You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

255 lines
10 KiB

10 months ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #ifndef UNITS_PROVIDER_H
  24. #define UNITS_PROVIDER_H
  25. #include <eda_units.h>
  26. #include <origin_transforms.h>
  27. #include <core/minoptmax.h>
  28. #include <optional>
  29. #include <utility>
  30. class UNITS_PROVIDER
  31. {
  32. public:
  33. UNITS_PROVIDER( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits ) :
  34. m_iuScale( aIuScale ),
  35. m_userUnits( aUnits )
  36. {}
  37. virtual ~UNITS_PROVIDER()
  38. {}
  39. EDA_UNITS GetUserUnits() const { return m_userUnits; }
  40. void SetUserUnits( EDA_UNITS aUnits ) { m_userUnits = aUnits; }
  41. virtual void GetUnitPair( EDA_UNITS& aPrimaryUnit, EDA_UNITS& aSecondaryUnits )
  42. {
  43. aPrimaryUnit = GetUserUnits();
  44. aSecondaryUnits = EDA_UNIT_UTILS::IsImperialUnit( aPrimaryUnit ) ? EDA_UNITS::MM
  45. : EDA_UNITS::MILS;
  46. }
  47. const EDA_IU_SCALE& GetIuScale() const { return m_iuScale; }
  48. // No SetIuScale(); scale is invariant
  49. virtual ORIGIN_TRANSFORMS& GetOriginTransforms()
  50. {
  51. static ORIGIN_TRANSFORMS identityTransform;
  52. return identityTransform;
  53. }
  54. /**
  55. * Converts \a aValue in internal units into a united string.
  56. *
  57. * For readability, trailing 0s are removed if the mantissa has 3 or more digits.
  58. * This function should be used to display values in dialogs because a value entered in mm
  59. * (for instance 2.0 mm) could need up to 8 digits mantissa if displayed in inch to avoid
  60. * truncation or rounding made just by the printf function.
  61. *
  62. * @param aValue = value in internal units
  63. * @param aAddUnitLabel = true to add symbol unit to the string value
  64. * @param aType is the type of this value, and controls the way the value is converted
  65. * to a string, and the suitable unit
  66. * @return A wxString object containing value and optionally the symbol unit (like 2.000 mm)
  67. */
  68. wxString StringFromValue( double aValue, bool aAddUnitLabel = false,
  69. EDA_DATA_TYPE aType = EDA_DATA_TYPE::DISTANCE ) const
  70. {
  71. return EDA_UNIT_UTILS::UI::StringFromValue( GetIuScale(), GetUnitsFromType( aType ), aValue,
  72. aAddUnitLabel, aType );
  73. }
  74. /**
  75. * Converts an optional \a aValue in internal units into a united string.
  76. *
  77. * For readability, trailing 0s are removed if the mantissa has 3 or more digits.
  78. * This function should be used to display values in dialogs because a value entered in mm
  79. * (for instance 2.0 mm) could need up to 8 digits mantissa if displayed in inch to avoid
  80. * truncation or rounding made just by the printf function.
  81. *
  82. * @param aValue = optional value in internal units
  83. * @param aAddUnitLabel = true to add symbol unit to the string value
  84. * @param aType is the type of this value, and controls the way the value is converted
  85. * to a string, and the suitable unit
  86. * @return A wxString object containing value and optionally the symbol unit (like 2.000 mm)
  87. */
  88. wxString StringFromOptionalValue( std::optional<int> aValue, bool aAddUnitLabel = false,
  89. EDA_DATA_TYPE aType = EDA_DATA_TYPE::DISTANCE ) const
  90. {
  91. if( !aValue )
  92. return NullUiString;
  93. else
  94. return EDA_UNIT_UTILS::UI::StringFromValue( GetIuScale(), GetUnitsFromType( aType ),
  95. aValue.value(), aAddUnitLabel, aType );
  96. }
  97. wxString StringFromValue( const EDA_ANGLE& aValue, bool aAddUnitLabel = false ) const
  98. {
  99. return EDA_UNIT_UTILS::UI::StringFromValue( unityScale, EDA_UNITS::DEGREES,
  100. aValue.AsDegrees(), aAddUnitLabel,
  101. EDA_DATA_TYPE::DISTANCE );
  102. }
  103. /**
  104. * A lower-precision version of StringFromValue().
  105. *
  106. * Should ONLY be used for status text and messages. Not suitable for dialogs, files, etc.
  107. * where the loss of precision matters.
  108. */
  109. wxString MessageTextFromValue( double aValue, bool aAddUnitLabel = true,
  110. EDA_DATA_TYPE aType = EDA_DATA_TYPE::DISTANCE ) const
  111. {
  112. return EDA_UNIT_UTILS::UI::MessageTextFromValue( GetIuScale(), GetUnitsFromType( aType ),
  113. aValue, aAddUnitLabel, aType );
  114. }
  115. wxString MessageTextFromValue( const EDA_ANGLE& aValue, bool aAddUnitLabel = true ) const
  116. {
  117. return EDA_UNIT_UTILS::UI::MessageTextFromValue( unityScale, EDA_UNITS::DEGREES,
  118. aValue.AsDegrees(), aAddUnitLabel,
  119. EDA_DATA_TYPE::DISTANCE );
  120. }
  121. wxString MessageTextFromMinOptMax( const MINOPTMAX<int>& aValue,
  122. EDA_DATA_TYPE aType = EDA_DATA_TYPE::DISTANCE ) const
  123. {
  124. return EDA_UNIT_UTILS::UI::MessageTextFromMinOptMax( GetIuScale(), GetUnitsFromType( aType ), aValue );
  125. };
  126. /**
  127. * Converts \a aTextValue in \a aUnits to internal units used by the frame.
  128. * @warning This utilizes the current locale and will break if decimal formats differ
  129. * @param aType is the type of this value, and controls the way the string is converted
  130. * to a value
  131. *
  132. * @param aTextValue A reference to a wxString object containing the string to convert.
  133. * @return internal units value
  134. */
  135. int ValueFromString( const wxString& aTextValue,
  136. EDA_DATA_TYPE aType = EDA_DATA_TYPE::DISTANCE ) const
  137. {
  138. double value = EDA_UNIT_UTILS::UI::DoubleValueFromString(
  139. GetIuScale(), GetUnitsFromType( aType ), aTextValue, aType );
  140. return KiROUND<double, int>( value );
  141. }
  142. /**
  143. * Converts \a aTextValue in \a aUnits to internal units used by the frame. Allows the return
  144. * of an empty optional if the string represents a null value (currently empty string)
  145. * @warning This utilizes the current locale and will break if decimal formats differ
  146. * @param aType is the type of this value, and controls the way the string is converted
  147. * to a value
  148. *
  149. * @param aTextValue A reference to a wxString object containing the string to convert.
  150. * @return internal units value
  151. */
  152. std::optional<int>
  153. OptionalValueFromString( const wxString& aTextValue,
  154. EDA_DATA_TYPE aType = EDA_DATA_TYPE::DISTANCE ) const
  155. {
  156. // Handle null (empty) values
  157. if( aTextValue == NullUiString )
  158. return {};
  159. double value = EDA_UNIT_UTILS::UI::DoubleValueFromString(
  160. GetIuScale(), GetUnitsFromType( aType ), aTextValue, aType );
  161. return KiROUND<double, int>( value );
  162. }
  163. EDA_ANGLE AngleValueFromString( const wxString& aTextValue ) const
  164. {
  165. double angle = EDA_UNIT_UTILS::UI::DoubleValueFromString( GetIuScale(), EDA_UNITS::DEGREES,
  166. aTextValue );
  167. return EDA_ANGLE( angle, DEGREES_T );
  168. }
  169. /**
  170. * Gets the units to use in the conversion based on the underlying user units
  171. */
  172. EDA_UNITS GetUnitsFromType( EDA_DATA_TYPE aType ) const
  173. {
  174. // Get the unit type depending on the requested unit type and the underlying user setting
  175. if( aType == EDA_DATA_TYPE::TIME )
  176. {
  177. return EDA_UNITS::PS;
  178. }
  179. else if( aType == EDA_DATA_TYPE::LENGTH_DELAY )
  180. {
  181. if( EDA_UNIT_UTILS::IsMetricUnit( GetUserUnits() ) )
  182. return EDA_UNITS::PS_PER_CM;
  183. else
  184. return EDA_UNITS::PS_PER_INCH;
  185. }
  186. return GetUserUnits();
  187. }
  188. /**
  189. * Gets the inferred type from the given units. Note: will always return the most simple
  190. * type (e.g. a DISTANCE rather than AREA or VOLUME for a measurement unit).
  191. */
  192. static EDA_DATA_TYPE GetTypeFromUnits( const EDA_UNITS aUnits )
  193. {
  194. switch( aUnits )
  195. {
  196. case EDA_UNITS::INCH:
  197. case EDA_UNITS::MM:
  198. case EDA_UNITS::UM:
  199. case EDA_UNITS::CM:
  200. case EDA_UNITS::MILS:
  201. return EDA_DATA_TYPE::DISTANCE;
  202. case EDA_UNITS::DEGREES:
  203. case EDA_UNITS::PERCENT:
  204. case EDA_UNITS::UNSCALED:
  205. return EDA_DATA_TYPE::UNITLESS;
  206. case EDA_UNITS::FS:
  207. case EDA_UNITS::PS:
  208. return EDA_DATA_TYPE::TIME;
  209. case EDA_UNITS::PS_PER_INCH:
  210. case EDA_UNITS::PS_PER_CM:
  211. case EDA_UNITS::PS_PER_MM:
  212. return EDA_DATA_TYPE::LENGTH_DELAY;
  213. }
  214. wxString msg = wxString::Format( wxT( "Unhandled unit data type %d" ), static_cast<int>( aUnits ) );
  215. wxCHECK_MSG( false, EDA_DATA_TYPE::UNITLESS, msg );
  216. return EDA_DATA_TYPE::UNITLESS; // Note that this is unreachable but g++-12 doesn't know that.
  217. }
  218. /// @brief The string that is used in the UI to represent a null value
  219. static inline const wxString NullUiString = "";
  220. private:
  221. const EDA_IU_SCALE& m_iuScale;
  222. EDA_UNITS m_userUnits;
  223. };
  224. #endif // UNITS_PROVIDER_H