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.

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