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