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.

538 lines
15 KiB

3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2022 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. #include <eda_units.h>
  24. #include <fmt/core.h>
  25. #include <math/util.h> // for KiROUND
  26. #include <macros.h>
  27. bool EDA_UNIT_UTILS::IsImperialUnit( EDA_UNITS aUnit )
  28. {
  29. switch( aUnit )
  30. {
  31. case EDA_UNITS::INCHES:
  32. case EDA_UNITS::MILS:
  33. return true;
  34. default:
  35. return false;
  36. }
  37. }
  38. bool EDA_UNIT_UTILS::IsMetricUnit( EDA_UNITS aUnit )
  39. {
  40. switch( aUnit )
  41. {
  42. case EDA_UNITS::MILLIMETRES:
  43. return true;
  44. default:
  45. return false;
  46. }
  47. }
  48. int EDA_UNIT_UTILS::Mm2mils( double aVal )
  49. {
  50. return KiROUND( aVal * 1000. / 25.4 );
  51. }
  52. int EDA_UNIT_UTILS::Mils2mm( double aVal )
  53. {
  54. return KiROUND( aVal * 25.4 / 1000. );
  55. }
  56. bool EDA_UNIT_UTILS::FetchUnitsFromString( const wxString& aTextValue, EDA_UNITS& aUnits )
  57. {
  58. wxString buf( aTextValue.Strip( wxString::both ) );
  59. unsigned brk_point = 0;
  60. while( brk_point < buf.Len() )
  61. {
  62. wxChar c = buf[brk_point];
  63. if( !( ( c >= '0' && c <= '9' ) || ( c == '.' ) || ( c == ',' ) || ( c == '-' )
  64. || ( c == '+' ) ) )
  65. break;
  66. ++brk_point;
  67. }
  68. // Check the unit designator (2 ch significant)
  69. wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
  70. if( unit == wxT( "mm" ) )
  71. aUnits = EDA_UNITS::MILLIMETRES;
  72. else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // "mils" or "thou"
  73. aUnits = EDA_UNITS::MILS;
  74. else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
  75. aUnits = EDA_UNITS::INCHES;
  76. else if( unit == wxT( "de" ) || unit == wxT( "ra" ) ) // "deg" or "rad"
  77. aUnits = EDA_UNITS::DEGREES;
  78. else
  79. return false;
  80. return true;
  81. }
  82. wxString EDA_UNIT_UTILS::GetText( EDA_UNITS aUnits, EDA_DATA_TYPE aType )
  83. {
  84. wxString label;
  85. switch( aUnits )
  86. {
  87. case EDA_UNITS::MILLIMETRES: label = wxT( " mm" ); break;
  88. case EDA_UNITS::DEGREES: label = wxT( "°" ); break;
  89. case EDA_UNITS::MILS: label = wxT( " mils" ); break;
  90. case EDA_UNITS::INCHES: label = wxT( " in" ); break;
  91. case EDA_UNITS::PERCENT: label = wxT( "%" ); break;
  92. case EDA_UNITS::UNSCALED: break;
  93. default: UNIMPLEMENTED_FOR( wxS( "Unknown units" ) ); break;
  94. }
  95. switch( aType )
  96. {
  97. case EDA_DATA_TYPE::VOLUME: label += wxT( "³" ); break;
  98. case EDA_DATA_TYPE::AREA: label += wxT( "²" ); break;
  99. case EDA_DATA_TYPE::DISTANCE: break;
  100. default: UNIMPLEMENTED_FOR( wxS( "Unknown measurement" ) ); break;
  101. }
  102. return label;
  103. }
  104. wxString EDA_UNIT_UTILS::GetLabel( EDA_UNITS aUnits, EDA_DATA_TYPE aType )
  105. {
  106. return GetText( aUnits, aType ).Trim( false );
  107. }
  108. std::string EDA_UNIT_UTILS::FormatAngle( const EDA_ANGLE& aAngle )
  109. {
  110. std::string temp = fmt::format( "{:.10g}", aAngle.AsDegrees() );
  111. return temp;
  112. }
  113. std::string EDA_UNIT_UTILS::FormatInternalUnits( const EDA_IU_SCALE& aIuScale, int aValue )
  114. {
  115. std::string buf;
  116. double engUnits = aValue;
  117. engUnits /= aIuScale.IU_PER_MM;
  118. if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 )
  119. {
  120. buf = fmt::format( "{:.10f}", engUnits );
  121. // remove trailing zeros
  122. while( !buf.empty() && buf[buf.size() - 1] == '0' )
  123. {
  124. buf.pop_back();
  125. }
  126. // if the value was really small
  127. // we may have just stripped all the zeros after the decimal
  128. if( buf[buf.size() - 1] == '.' )
  129. {
  130. buf.pop_back();
  131. }
  132. }
  133. else
  134. {
  135. buf = fmt::format( "{:.10g}", engUnits );
  136. }
  137. return buf;
  138. }
  139. std::string EDA_UNIT_UTILS::FormatInternalUnits( const EDA_IU_SCALE& aIuScale,
  140. const VECTOR2I& aPoint )
  141. {
  142. return FormatInternalUnits( aIuScale, aPoint.x ) + " "
  143. + FormatInternalUnits( aIuScale, aPoint.y );
  144. }
  145. #define IU_TO_MM( x, scale ) ( x / scale.IU_PER_MM )
  146. #define IU_TO_IN( x, scale ) ( x / scale.IU_PER_MILS / 1000 )
  147. #define IU_TO_MILS( x, scale ) ( x / scale.IU_PER_MILS )
  148. #define MM_TO_IU( x, scale ) ( x * scale.IU_PER_MM )
  149. #define IN_TO_IU( x, scale ) ( x * scale.IU_PER_MILS * 1000 )
  150. #define MILS_TO_IU( x, scale ) ( x * scale.IU_PER_MILS )
  151. double EDA_UNIT_UTILS::UI::ToUserUnit( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnit,
  152. double aValue )
  153. {
  154. switch( aUnit )
  155. {
  156. case EDA_UNITS::MILLIMETRES:
  157. return IU_TO_MM( aValue, aIuScale );
  158. case EDA_UNITS::MILS:
  159. return IU_TO_MILS( aValue, aIuScale );
  160. case EDA_UNITS::INCHES:
  161. return IU_TO_IN( aValue, aIuScale );
  162. case EDA_UNITS::DEGREES:
  163. return aValue;
  164. default:
  165. return aValue;
  166. }
  167. }
  168. /**
  169. * Convert a value to a string using double notation.
  170. *
  171. * For readability, the mantissa has 3 or more digits,
  172. * the trailing 0 are removed if the mantissa has more than 3 digits
  173. * and some trailing 0
  174. * This function should be used to display values in dialogs because a value
  175. * entered in mm (for instance 2.0 mm) could need up to 8 digits mantissa
  176. * if displayed in inch to avoid truncation or rounding made just by the printf function.
  177. * otherwise the actual value is rounded when read from dialog and converted
  178. * in internal units, and therefore modified.
  179. */
  180. wxString EDA_UNIT_UTILS::UI::StringFromValue( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
  181. double aValue, bool aAddUnitsText,
  182. EDA_DATA_TYPE aType )
  183. {
  184. double value_to_print = aValue;
  185. switch( aType )
  186. {
  187. case EDA_DATA_TYPE::VOLUME:
  188. value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
  189. KI_FALLTHROUGH;
  190. case EDA_DATA_TYPE::AREA:
  191. value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
  192. KI_FALLTHROUGH;
  193. case EDA_DATA_TYPE::DISTANCE:
  194. value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
  195. }
  196. char buf[50];
  197. if( value_to_print != 0.0 && fabs( value_to_print ) <= 0.0001 )
  198. {
  199. int len = snprintf( buf, sizeof( buf ) - 1, "%.10f", value_to_print );
  200. while( --len > 0 && buf[len] == '0' )
  201. buf[len] = '\0';
  202. if( len >= 0 && ( buf[len] == '.' || buf[len] == ',' ) )
  203. buf[len] = '\0';
  204. }
  205. else
  206. {
  207. snprintf( buf, sizeof( buf ) - 1, "%.10g", value_to_print );
  208. }
  209. wxString stringValue( buf, wxConvUTF8 );
  210. if( aAddUnitsText )
  211. stringValue += EDA_UNIT_UTILS::GetText( aUnits, aType );
  212. return stringValue;
  213. }
  214. /**
  215. * Convert a value to a string using double notation.
  216. *
  217. * For readability, the mantissa has 0, 1, 3 or 4 digits, depending on units
  218. * for unit = inch the mantissa has 3 digits (Eeschema) or 4 digits
  219. * for unit = mil the mantissa has 0 digits (Eeschema) or 1 digits
  220. * for unit = mm the mantissa has 3 digits (Eeschema) or 4 digits
  221. * Should be used only to display info in status,
  222. * but not in dialogs, because 4 digits only
  223. * could truncate the actual value
  224. */
  225. // A lower-precision (for readability) version of StringFromValue()
  226. wxString EDA_UNIT_UTILS::UI::MessageTextFromValue( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
  227. int aValue,
  228. bool aAddUnitLabel,
  229. EDA_DATA_TYPE aType )
  230. {
  231. return MessageTextFromValue( aIuScale, aUnits, double( aValue ), aAddUnitLabel, aType );
  232. }
  233. // A lower-precision (for readability) version of StringFromValue()
  234. wxString EDA_UNIT_UTILS::UI::MessageTextFromValue( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
  235. long long int aValue,
  236. bool aAddUnitLabel,
  237. EDA_DATA_TYPE aType )
  238. {
  239. return MessageTextFromValue( aIuScale, aUnits, double( aValue ), aAddUnitLabel, aType );
  240. }
  241. wxString EDA_UNIT_UTILS::UI::MessageTextFromValue( EDA_ANGLE aValue, bool aAddUnitLabel )
  242. {
  243. if( aAddUnitLabel )
  244. return wxString::Format( wxT( "%.1f°" ), aValue.AsDegrees() );
  245. else
  246. return wxString::Format( wxT( "%.1f" ), aValue.AsDegrees() );
  247. }
  248. // A lower-precision (for readability) version of StringFromValue()
  249. wxString EDA_UNIT_UTILS::UI::MessageTextFromValue( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
  250. double aValue, bool aAddUnitsText,
  251. EDA_DATA_TYPE aType )
  252. {
  253. wxString text;
  254. const wxChar* format;
  255. double value = aValue;
  256. switch( aType )
  257. {
  258. case EDA_DATA_TYPE::VOLUME:
  259. value = ToUserUnit( aIuScale, aUnits, value );
  260. // Fall through to continue computation
  261. KI_FALLTHROUGH;
  262. case EDA_DATA_TYPE::AREA:
  263. value = ToUserUnit( aIuScale, aUnits, value );
  264. // Fall through to continue computation
  265. KI_FALLTHROUGH;
  266. case EDA_DATA_TYPE::DISTANCE:
  267. value = ToUserUnit( aIuScale, aUnits, value );
  268. }
  269. switch( aUnits )
  270. {
  271. default:
  272. case EDA_UNITS::MILLIMETRES:
  273. #if defined( EESCHEMA )
  274. format = wxT( "%.2f" );
  275. #else
  276. format = wxT( "%.4f" );
  277. #endif
  278. break;
  279. case EDA_UNITS::MILS:
  280. #if defined( EESCHEMA )
  281. format = wxT( "%.0f" );
  282. #else
  283. format = wxT( "%.2f" );
  284. #endif
  285. break;
  286. case EDA_UNITS::INCHES:
  287. #if defined( EESCHEMA )
  288. format = wxT( "%.3f" );
  289. #else
  290. format = wxT( "%.4f" );
  291. #endif
  292. break;
  293. case EDA_UNITS::DEGREES:
  294. // 3 digits in mantissa should be good for rotation in degree
  295. format = wxT( "%.3f" );
  296. break;
  297. case EDA_UNITS::UNSCALED:
  298. format = wxT( "%.0f" );
  299. break;
  300. }
  301. text.Printf( format, value );
  302. if( aAddUnitsText )
  303. text += EDA_UNIT_UTILS::GetText( aUnits, aType );
  304. return text;
  305. }
  306. double EDA_UNIT_UTILS::UI::FromUserUnit( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
  307. double aValue )
  308. {
  309. switch( aUnits )
  310. {
  311. case EDA_UNITS::MILLIMETRES:
  312. return MM_TO_IU( aValue, aIuScale );
  313. case EDA_UNITS::MILS:
  314. return MILS_TO_IU( aValue, aIuScale );
  315. case EDA_UNITS::INCHES:
  316. return IN_TO_IU( aValue, aIuScale );
  317. default:
  318. case EDA_UNITS::DEGREES:
  319. case EDA_UNITS::UNSCALED:
  320. case EDA_UNITS::PERCENT:
  321. return aValue;
  322. }
  323. }
  324. double EDA_UNIT_UTILS::UI::DoubleValueFromString( const wxString& aTextValue )
  325. {
  326. double dtmp = 0;
  327. // Acquire the 'right' decimal point separator
  328. const struct lconv* lc = localeconv();
  329. wxChar decimal_point = lc->decimal_point[0];
  330. wxString buf( aTextValue.Strip( wxString::both ) );
  331. // Convert any entered decimal point separators to the 'right' one
  332. buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
  333. buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) );
  334. // Find the end of the numeric part
  335. unsigned brk_point = 0;
  336. while( brk_point < buf.Len() )
  337. {
  338. wxChar ch = buf[brk_point];
  339. if( !( ( ch >= '0' && ch <= '9' ) || ( ch == decimal_point ) || ( ch == '-' )
  340. || ( ch == '+' ) ) )
  341. {
  342. break;
  343. }
  344. ++brk_point;
  345. }
  346. // Extract the numeric part
  347. buf.Left( brk_point ).ToDouble( &dtmp );
  348. return dtmp;
  349. }
  350. double EDA_UNIT_UTILS::UI::DoubleValueFromString( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
  351. const wxString& aTextValue, EDA_DATA_TYPE aType )
  352. {
  353. double dtmp = 0;
  354. // Acquire the 'right' decimal point separator
  355. const struct lconv* lc = localeconv();
  356. wxChar decimal_point = lc->decimal_point[0];
  357. wxString buf( aTextValue.Strip( wxString::both ) );
  358. // Convert any entered decimal point separators to the 'right' one
  359. buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
  360. buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) );
  361. // Find the end of the numeric part
  362. unsigned brk_point = 0;
  363. while( brk_point < buf.Len() )
  364. {
  365. wxChar ch = buf[brk_point];
  366. if( !( (ch >= '0' && ch <= '9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) )
  367. break;
  368. ++brk_point;
  369. }
  370. // Extract the numeric part
  371. buf.Left( brk_point ).ToDouble( &dtmp );
  372. // Check the optional unit designator (2 ch significant)
  373. wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
  374. if( aUnits == EDA_UNITS::MILLIMETRES
  375. || aUnits == EDA_UNITS::MILS
  376. || aUnits == EDA_UNITS::INCHES )
  377. {
  378. if( unit == wxT( "mm" ) )
  379. {
  380. aUnits = EDA_UNITS::MILLIMETRES;
  381. }
  382. else if( unit == wxT( "mi" ) || unit == wxT( "th" ) )
  383. {
  384. aUnits = EDA_UNITS::MILS;
  385. }
  386. else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
  387. {
  388. aUnits = EDA_UNITS::INCHES;
  389. }
  390. else if( unit == wxT( "oz" ) ) // 1 oz = 1.37 mils
  391. {
  392. aUnits = EDA_UNITS::MILS;
  393. dtmp *= 1.37;
  394. }
  395. }
  396. else if( aUnits == EDA_UNITS::DEGREES )
  397. {
  398. if( unit == wxT( "ra" ) ) // Radians
  399. dtmp *= 180.0f / M_PI;
  400. }
  401. switch( aType )
  402. {
  403. case EDA_DATA_TYPE::VOLUME:
  404. dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
  405. KI_FALLTHROUGH;
  406. case EDA_DATA_TYPE::AREA:
  407. dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
  408. KI_FALLTHROUGH;
  409. case EDA_DATA_TYPE::DISTANCE:
  410. dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
  411. }
  412. return dtmp;
  413. }
  414. long long int EDA_UNIT_UTILS::UI::ValueFromString( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
  415. const wxString& aTextValue, EDA_DATA_TYPE aType )
  416. {
  417. double value = DoubleValueFromString( aIuScale, aUnits, aTextValue, aType );
  418. return KiROUND<double, long long int>( value );
  419. }
  420. long long int EDA_UNIT_UTILS::UI::ValueFromString( const wxString& aTextValue )
  421. {
  422. double value = DoubleValueFromString( aTextValue );
  423. return KiROUND<double, long long int>( value );
  424. }