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.

607 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 CERN
  5. * Copyright (C) 1992-2020 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @author Wayne Stambaugh <stambaughw@verizon.net>
  27. * @file base_units.cpp
  28. * @brief Code to handle objects that require both schematic and board internal units.
  29. * @note This file is an ugly hack to solve the problem of formatting the base units
  30. * for either schematics or boards in objects that are include in both domains.
  31. * At some point in the future. This code should be rolled back into the
  32. * appropriate object and build with the correct internal unit formatting
  33. * depending on the application.
  34. */
  35. #include <base_units.h>
  36. #include <common.h>
  37. #include <math/util.h> // for KiROUND
  38. #include <macros.h>
  39. #include <title_block.h>
  40. #if defined( PCBNEW ) || defined( CVPCB ) || defined( EESCHEMA ) || defined( GERBVIEW ) || defined( PL_EDITOR )
  41. #define IU_TO_MM( x ) ( x / IU_PER_MM )
  42. #define IU_TO_IN( x ) ( x / IU_PER_MILS / 1000 )
  43. #define IU_TO_MILS( x ) ( x / IU_PER_MILS )
  44. #define MM_TO_IU( x ) ( x * IU_PER_MM )
  45. #define IN_TO_IU( x ) ( x * IU_PER_MILS * 1000 )
  46. #define MILS_TO_IU( x ) ( x * IU_PER_MILS )
  47. #else
  48. #error "Cannot resolve internal units due to no definition of EESCHEMA, CVPCB or PCBNEW."
  49. #endif
  50. // Helper function to print a float number without using scientific notation
  51. // and no trailing 0
  52. // So we cannot always just use the %g or the %f format to print a fp number
  53. // this helper function uses the %f format when needed, or %g when %f is
  54. // not well working and then removes trailing 0
  55. std::string Double2Str( double aValue )
  56. {
  57. char buf[50];
  58. int len;
  59. if( aValue != 0.0 && fabs( aValue ) <= 0.0001 )
  60. {
  61. // For these small values, %f works fine,
  62. // and %g gives an exponent
  63. len = sprintf( buf, "%.16f", aValue );
  64. while( --len > 0 && buf[len] == '0' )
  65. buf[len] = '\0';
  66. if( buf[len] == '.' )
  67. buf[len] = '\0';
  68. else
  69. ++len;
  70. }
  71. else
  72. {
  73. // For these values, %g works fine, and sometimes %f
  74. // gives a bad value (try aValue = 1.222222222222, with %.16f format!)
  75. len = sprintf( buf, "%.16g", aValue );
  76. }
  77. return std::string( buf, len );
  78. }
  79. double To_User_Unit( EDA_UNITS aUnit, double aValue )
  80. {
  81. switch( aUnit )
  82. {
  83. case EDA_UNITS::MILLIMETRES:
  84. return IU_TO_MM( aValue );
  85. case EDA_UNITS::MILS:
  86. return IU_TO_MILS( aValue );
  87. case EDA_UNITS::INCHES:
  88. return IU_TO_IN( aValue );
  89. case EDA_UNITS::DEGREES:
  90. return aValue / 10.0f;
  91. default:
  92. return aValue;
  93. }
  94. }
  95. /* Convert a value to a string using double notation.
  96. * For readability, the mantissa has 0, 1, 3 or 4 digits, depending on units
  97. * for unit = inch the mantissa has 3 digits (Eeschema) or 4 digits
  98. * for unit = mil the mantissa has 0 digits (Eeschema) or 1 digits
  99. * for unit = mm the mantissa has 3 digits (Eeschema) or 4 digits
  100. * Should be used only to display info in status,
  101. * but not in dialogs, because 4 digits only
  102. * could truncate the actual value
  103. */
  104. // A lower-precision (for readability) version of StringFromValue()
  105. wxString MessageTextFromValue( EDA_UNITS aUnits, int aValue, bool aAddUnitLabel,
  106. EDA_DATA_TYPE aType )
  107. {
  108. return MessageTextFromValue( aUnits, double( aValue ), aAddUnitLabel, aType );
  109. }
  110. // A lower-precision (for readability) version of StringFromValue()
  111. wxString MessageTextFromValue( EDA_UNITS aUnits, long long int aValue, bool aAddUnitLabel,
  112. EDA_DATA_TYPE aType )
  113. {
  114. return MessageTextFromValue( aUnits, double( aValue ), aAddUnitLabel, aType );
  115. }
  116. // A lower-precision (for readability) version of StringFromValue()
  117. wxString MessageTextFromValue( EDA_UNITS aUnits, double aValue, bool aAddUnitLabel,
  118. EDA_DATA_TYPE aType )
  119. {
  120. wxString text;
  121. const wxChar* format;
  122. double value = aValue;
  123. switch( aType )
  124. {
  125. case EDA_DATA_TYPE::VOLUME:
  126. value = To_User_Unit( aUnits, value );
  127. // Fall through to continue computation
  128. KI_FALLTHROUGH;
  129. case EDA_DATA_TYPE::AREA:
  130. value = To_User_Unit( aUnits, value );
  131. // Fall through to continue computation
  132. KI_FALLTHROUGH;
  133. case EDA_DATA_TYPE::DISTANCE:
  134. value = To_User_Unit( aUnits, value );
  135. }
  136. switch( aUnits )
  137. {
  138. default:
  139. case EDA_UNITS::MILLIMETRES:
  140. #if defined( EESCHEMA )
  141. format = wxT( "%.2f" );
  142. #else
  143. format = wxT( "%.4f" );
  144. #endif
  145. break;
  146. case EDA_UNITS::MILS:
  147. #if defined( EESCHEMA )
  148. format = wxT( "%.0f" );
  149. #else
  150. format = wxT( "%.2f" );
  151. #endif
  152. break;
  153. case EDA_UNITS::INCHES:
  154. #if defined( EESCHEMA )
  155. format = wxT( "%.3f" );
  156. #else
  157. format = wxT( "%.4f" );
  158. #endif
  159. break;
  160. case EDA_UNITS::DEGREES:
  161. format = wxT( "%.1f" );
  162. break;
  163. case EDA_UNITS::UNSCALED:
  164. format = wxT( "%.0f" );
  165. break;
  166. }
  167. text.Printf( format, value );
  168. if( aAddUnitLabel )
  169. {
  170. text += " ";
  171. text += GetAbbreviatedUnitsLabel( aUnits, aType );
  172. }
  173. return text;
  174. }
  175. /* Remove trailing 0 from a string containing a converted float number.
  176. * the trailing 0 are removed if the mantissa has more
  177. * than aTrailingZeroAllowed digits and some trailing 0
  178. */
  179. void StripTrailingZeros( wxString& aStringValue, unsigned aTrailingZeroAllowed )
  180. {
  181. struct lconv * lc = localeconv();
  182. char sep = lc->decimal_point[0];
  183. unsigned sep_pos = aStringValue.Find( sep );
  184. if( sep_pos > 0 )
  185. {
  186. // We want to keep at least aTrailingZeroAllowed digits after the separator
  187. unsigned min_len = sep_pos + aTrailingZeroAllowed + 1;
  188. while( aStringValue.Len() > min_len )
  189. {
  190. if( aStringValue.Last() == '0' )
  191. aStringValue.RemoveLast();
  192. else
  193. break;
  194. }
  195. }
  196. }
  197. /* Convert a value to a string using double notation.
  198. * For readability, the mantissa has 3 or more digits,
  199. * the trailing 0 are removed if the mantissa has more than 3 digits
  200. * and some trailing 0
  201. * This function should be used to display values in dialogs because a value
  202. * entered in mm (for instance 2.0 mm) could need up to 8 digits mantissa
  203. * if displayed in inch to avoid truncation or rounding made just by the printf function.
  204. * otherwise the actual value is rounded when read from dialog and converted
  205. * in internal units, and therefore modified.
  206. */
  207. wxString StringFromValue( EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, EDA_DATA_TYPE aType )
  208. {
  209. double value_to_print = aValue;
  210. switch( aType )
  211. {
  212. case EDA_DATA_TYPE::VOLUME:
  213. value_to_print = To_User_Unit( aUnits, value_to_print );
  214. KI_FALLTHROUGH;
  215. case EDA_DATA_TYPE::AREA:
  216. value_to_print = To_User_Unit( aUnits, value_to_print );
  217. KI_FALLTHROUGH;
  218. case EDA_DATA_TYPE::DISTANCE:
  219. value_to_print = To_User_Unit( aUnits, value_to_print );
  220. }
  221. #if defined( EESCHEMA )
  222. wxString stringValue = wxString::Format( wxT( "%.3f" ), value_to_print );
  223. // Strip trailing zeros. However, keep at least 3 digits in mantissa
  224. // For readability
  225. StripTrailingZeros( stringValue, 3 );
  226. #else
  227. char buf[50];
  228. int len;
  229. if( value_to_print != 0.0 && fabs( value_to_print ) <= 0.0001 )
  230. {
  231. len = sprintf( buf, "%.10f", value_to_print );
  232. while( --len > 0 && buf[len] == '0' )
  233. buf[len] = '\0';
  234. if( buf[len]=='.' || buf[len]==',' )
  235. buf[len] = '\0';
  236. else
  237. ++len;
  238. }
  239. else
  240. {
  241. if( aUnits == EDA_UNITS::MILS )
  242. len = sprintf( buf, "%.7g", value_to_print );
  243. else
  244. len = sprintf( buf, "%.10g", value_to_print );
  245. }
  246. wxString stringValue( buf, wxConvUTF8 );
  247. #endif
  248. if( aAddUnitSymbol )
  249. {
  250. switch( aUnits )
  251. {
  252. case EDA_UNITS::MILLIMETRES:
  253. stringValue += wxT( " mm" );
  254. break;
  255. case EDA_UNITS::DEGREES:
  256. stringValue += wxT( " deg" );
  257. break;
  258. case EDA_UNITS::MILS:
  259. stringValue += wxT( " mils" );
  260. break;
  261. case EDA_UNITS::INCHES:
  262. stringValue += wxT( " in" );
  263. break;
  264. case EDA_UNITS::PERCENT:
  265. stringValue += wxT( "%" );
  266. break;
  267. case EDA_UNITS::UNSCALED:
  268. break;
  269. }
  270. }
  271. return stringValue;
  272. }
  273. double From_User_Unit( EDA_UNITS aUnits, double aValue )
  274. {
  275. switch( aUnits )
  276. {
  277. case EDA_UNITS::MILLIMETRES:
  278. return MM_TO_IU( aValue );
  279. case EDA_UNITS::MILS:
  280. return MILS_TO_IU( aValue );
  281. case EDA_UNITS::INCHES:
  282. return IN_TO_IU( aValue );
  283. case EDA_UNITS::DEGREES:
  284. // Convert to "decidegrees"
  285. return aValue * 10;
  286. default:
  287. case EDA_UNITS::UNSCALED:
  288. case EDA_UNITS::PERCENT:
  289. return aValue;
  290. }
  291. }
  292. double DoubleValueFromString( EDA_UNITS aUnits, const wxString& aTextValue, EDA_DATA_TYPE aType )
  293. {
  294. double dtmp = 0;
  295. // Acquire the 'right' decimal point separator
  296. const struct lconv* lc = localeconv();
  297. wxChar decimal_point = lc->decimal_point[0];
  298. wxString buf( aTextValue.Strip( wxString::both ) );
  299. // Convert the period in decimal point
  300. buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
  301. // Find the end of the numeric part
  302. unsigned brk_point = 0;
  303. while( brk_point < buf.Len() )
  304. {
  305. wxChar ch = buf[brk_point];
  306. if( !( (ch >= '0' && ch <='9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) )
  307. {
  308. break;
  309. }
  310. ++brk_point;
  311. }
  312. // Extract the numeric part
  313. buf.Left( brk_point );
  314. buf.ToDouble( &dtmp );
  315. // Check the optional unit designator (2 ch significant)
  316. wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
  317. if( aUnits == EDA_UNITS::MILLIMETRES || aUnits == EDA_UNITS::MILS || aUnits == EDA_UNITS::INCHES )
  318. {
  319. if( unit == wxT( "mm" ) )
  320. {
  321. aUnits = EDA_UNITS::MILLIMETRES;
  322. }
  323. else if( unit == wxT( "mi" ) || unit == wxT( "th" ) )
  324. {
  325. aUnits = EDA_UNITS::MILS;
  326. }
  327. else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
  328. {
  329. aUnits = EDA_UNITS::INCHES;
  330. }
  331. else if( unit == "oz" ) // 1 oz = 1.37 mils
  332. {
  333. aUnits = EDA_UNITS::MILS;
  334. dtmp *= 1.37;
  335. }
  336. }
  337. else if( aUnits == EDA_UNITS::DEGREES )
  338. {
  339. if( unit == wxT( "ra" ) ) // Radians
  340. {
  341. dtmp *= 180.0f / M_PI;
  342. }
  343. }
  344. switch( aType )
  345. {
  346. case EDA_DATA_TYPE::VOLUME:
  347. dtmp = From_User_Unit( aUnits, dtmp );
  348. KI_FALLTHROUGH;
  349. case EDA_DATA_TYPE::AREA:
  350. dtmp = From_User_Unit( aUnits, dtmp );
  351. KI_FALLTHROUGH;
  352. case EDA_DATA_TYPE::DISTANCE:
  353. dtmp = From_User_Unit( aUnits, dtmp );
  354. }
  355. return dtmp;
  356. }
  357. void FetchUnitsFromString( const wxString& aTextValue, EDA_UNITS& aUnits )
  358. {
  359. wxString buf( aTextValue.Strip( wxString::both ) );
  360. unsigned brk_point = 0;
  361. while( brk_point < buf.Len() )
  362. {
  363. wxChar c = buf[brk_point];
  364. if( !( (c >= '0' && c <='9') || (c == '.') || (c == ',') || (c == '-') || (c == '+') ) )
  365. break;
  366. ++brk_point;
  367. }
  368. // Check the unit designator (2 ch significant)
  369. wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
  370. if( unit == wxT( "mm" ) )
  371. aUnits = EDA_UNITS::MILLIMETRES;
  372. else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // "mils" or "thou"
  373. aUnits = EDA_UNITS::MILS;
  374. else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
  375. aUnits = EDA_UNITS::INCHES;
  376. else if( unit == wxT( "de" ) || unit == wxT( "ra" ) ) // "deg" or "rad"
  377. aUnits = EDA_UNITS::DEGREES;
  378. }
  379. long long int ValueFromString( EDA_UNITS aUnits, const wxString& aTextValue, EDA_DATA_TYPE aType )
  380. {
  381. double value = DoubleValueFromString( aUnits, aTextValue, aType );
  382. return KiROUND<double, long long int>( value );
  383. }
  384. /**
  385. * Function AngleToStringDegrees
  386. * is a helper to convert the \a double \a aAngle (in internal unit)
  387. * to a string in degrees
  388. */
  389. wxString AngleToStringDegrees( double aAngle )
  390. {
  391. wxString text;
  392. text.Printf( wxT( "%.3f" ), aAngle/10.0 );
  393. StripTrailingZeros( text, 1 );
  394. return text;
  395. }
  396. wxString GetAbbreviatedUnitsLabel( EDA_UNITS aUnit, EDA_DATA_TYPE aType )
  397. {
  398. switch( aUnit )
  399. {
  400. case EDA_UNITS::MILLIMETRES:
  401. switch( aType )
  402. {
  403. default:
  404. wxASSERT( 0 );
  405. KI_FALLTHROUGH;
  406. case EDA_DATA_TYPE::DISTANCE:
  407. return _( "mm" );
  408. case EDA_DATA_TYPE::AREA:
  409. return _( "sq. mm" );
  410. case EDA_DATA_TYPE::VOLUME:
  411. return _( "cu. mm" );
  412. }
  413. case EDA_UNITS::MILS:
  414. switch( aType )
  415. {
  416. default:
  417. wxASSERT( 0 );
  418. KI_FALLTHROUGH;
  419. case EDA_DATA_TYPE::DISTANCE:
  420. return _( "mils" );
  421. case EDA_DATA_TYPE::AREA:
  422. return _( "sq. mils" );
  423. case EDA_DATA_TYPE::VOLUME:
  424. return _( "cu. mils" );
  425. }
  426. case EDA_UNITS::INCHES:
  427. switch( aType )
  428. {
  429. default:
  430. wxASSERT( 0 );
  431. KI_FALLTHROUGH;
  432. case EDA_DATA_TYPE::DISTANCE:
  433. return _( "in" );
  434. case EDA_DATA_TYPE::AREA:
  435. return _( "sq. in" );
  436. case EDA_DATA_TYPE::VOLUME:
  437. return _( "cu. in" );
  438. }
  439. case EDA_UNITS::PERCENT:
  440. return _( "%" );
  441. case EDA_UNITS::UNSCALED:
  442. return wxEmptyString;
  443. case EDA_UNITS::DEGREES:
  444. return _( "deg" );
  445. default:
  446. return wxT( "??" );
  447. }
  448. }
  449. std::string FormatInternalUnits( int aValue )
  450. {
  451. char buf[50];
  452. double engUnits = aValue;
  453. int len;
  454. engUnits /= IU_PER_MM;
  455. if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 )
  456. {
  457. len = snprintf( buf, sizeof(buf), "%.10f", engUnits );
  458. while( --len > 0 && buf[len] == '0' )
  459. buf[len] = '\0';
  460. if( buf[len] == '.' )
  461. buf[len] = '\0';
  462. else
  463. ++len;
  464. }
  465. else
  466. {
  467. len = snprintf( buf, sizeof(buf), "%.10g", engUnits );
  468. }
  469. return std::string( buf, len );
  470. }
  471. std::string FormatAngle( double aAngle )
  472. {
  473. char temp[50];
  474. int len;
  475. len = snprintf( temp, sizeof(temp), "%.10g", aAngle / 10.0 );
  476. return std::string( temp, len );
  477. }
  478. std::string FormatInternalUnits( const wxPoint& aPoint )
  479. {
  480. return FormatInternalUnits( aPoint.x ) + " " + FormatInternalUnits( aPoint.y );
  481. }
  482. std::string FormatInternalUnits( const VECTOR2I& aPoint )
  483. {
  484. return FormatInternalUnits( aPoint.x ) + " " + FormatInternalUnits( aPoint.y );
  485. }
  486. std::string FormatInternalUnits( const wxSize& aSize )
  487. {
  488. return FormatInternalUnits( aSize.GetWidth() ) + " " + FormatInternalUnits( aSize.GetHeight() );
  489. }