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.

517 lines
13 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, double aValue, bool aUseMils )
  113. {
  114. wxString text;
  115. const wxChar* format;
  116. double value = To_User_Unit( aUnits, aValue, aUseMils );
  117. if( aUnits == INCHES )
  118. {
  119. if( aUseMils )
  120. {
  121. #if defined( EESCHEMA )
  122. format = wxT( "%.0f" );
  123. #else
  124. format = wxT( "%.1f" );
  125. #endif
  126. }
  127. else
  128. {
  129. #if defined( EESCHEMA )
  130. format = wxT( "%.3f" );
  131. #else
  132. format = wxT( "%.4f" );
  133. #endif
  134. }
  135. }
  136. else
  137. {
  138. #if defined( EESCHEMA )
  139. format = wxT( "%.2f" );
  140. #else
  141. format = wxT( "%.3f" );
  142. #endif
  143. }
  144. text.Printf( format, value );
  145. text += " ";
  146. text += GetAbbreviatedUnitsLabel( aUnits, aUseMils );
  147. return text;
  148. }
  149. /* Remove trailing 0 from a string containing a converted float number.
  150. * the trailing 0 are removed if the mantissa has more
  151. * than aTrailingZeroAllowed digits and some trailing 0
  152. */
  153. void StripTrailingZeros( wxString& aStringValue, unsigned aTrailingZeroAllowed )
  154. {
  155. struct lconv * lc = localeconv();
  156. char sep = lc->decimal_point[0];
  157. unsigned sep_pos = aStringValue.Find( sep );
  158. if( sep_pos > 0 )
  159. {
  160. // We want to keep at least aTrailingZeroAllowed digits after the separator
  161. unsigned min_len = sep_pos + aTrailingZeroAllowed + 1;
  162. while( aStringValue.Len() > min_len )
  163. {
  164. if( aStringValue.Last() == '0' )
  165. aStringValue.RemoveLast();
  166. else
  167. break;
  168. }
  169. }
  170. }
  171. /* Convert a value to a string using double notation.
  172. * For readability, the mantissa has 3 or more digits,
  173. * the trailing 0 are removed if the mantissa has more than 3 digits
  174. * and some trailing 0
  175. * This function should be used to display values in dialogs because a value
  176. * entered in mm (for instance 2.0 mm) could need up to 8 digits mantissa
  177. * if displayed in inch to avoid truncation or rounding made just by the printf function.
  178. * otherwise the actual value is rounded when read from dialog and converted
  179. * in internal units, and therefore modified.
  180. */
  181. wxString StringFromValue( EDA_UNITS_T aUnits, int aValue, bool aAddUnitSymbol, bool aUseMils )
  182. {
  183. double value_to_print = To_User_Unit( aUnits, aValue, aUseMils );
  184. #if defined( EESCHEMA )
  185. wxString stringValue = wxString::Format( wxT( "%.3f" ), value_to_print );
  186. // Strip trailing zeros. However, keep at least 3 digits in mantissa
  187. // For readability
  188. StripTrailingZeros( stringValue, 3 );
  189. #else
  190. char buf[50];
  191. int len;
  192. if( value_to_print != 0.0 && fabs( value_to_print ) <= 0.0001 )
  193. {
  194. len = sprintf( buf, "%.10f", value_to_print );
  195. while( --len > 0 && buf[len] == '0' )
  196. buf[len] = '\0';
  197. if( buf[len]=='.' || buf[len]==',' )
  198. buf[len] = '\0';
  199. else
  200. ++len;
  201. }
  202. else
  203. {
  204. if( aUnits == INCHES && aUseMils )
  205. len = sprintf( buf, "%.7g", value_to_print );
  206. else
  207. len = sprintf( buf, "%.10g", value_to_print );
  208. }
  209. wxString stringValue( buf, wxConvUTF8 );
  210. #endif
  211. if( aAddUnitSymbol )
  212. {
  213. switch( aUnits )
  214. {
  215. case INCHES:
  216. if( aUseMils )
  217. stringValue += wxT( " mils" );
  218. else
  219. stringValue += wxT( " in" );
  220. break;
  221. case MILLIMETRES:
  222. stringValue += wxT( " mm" );
  223. break;
  224. case DEGREES:
  225. stringValue += wxT( " deg" );
  226. break;
  227. case UNSCALED_UNITS:
  228. break;
  229. }
  230. }
  231. return stringValue;
  232. }
  233. double From_User_Unit( EDA_UNITS_T aUnits, double aValue, bool aUseMils )
  234. {
  235. switch( aUnits )
  236. {
  237. case MILLIMETRES:
  238. return MM_TO_IU( aValue );
  239. case INCHES:
  240. if( aUseMils )
  241. return MILS_TO_IU( aValue );
  242. else
  243. return IN_TO_IU( aValue );
  244. case DEGREES:
  245. // Convert to "decidegrees"
  246. return aValue * 10;
  247. default:
  248. case UNSCALED_UNITS:
  249. return aValue;
  250. }
  251. }
  252. double DoubleValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue, bool aUseMils )
  253. {
  254. double value;
  255. double dtmp = 0;
  256. // Acquire the 'right' decimal point separator
  257. const struct lconv* lc = localeconv();
  258. wxChar decimal_point = lc->decimal_point[0];
  259. wxString buf( aTextValue.Strip( wxString::both ) );
  260. // Convert the period in decimal point
  261. buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
  262. // Find the end of the numeric part
  263. unsigned brk_point = 0;
  264. while( brk_point < buf.Len() )
  265. {
  266. wxChar ch = buf[brk_point];
  267. if( !( (ch >= '0' && ch <='9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) )
  268. {
  269. break;
  270. }
  271. ++brk_point;
  272. }
  273. // Extract the numeric part
  274. buf.Left( brk_point );
  275. buf.ToDouble( &dtmp );
  276. // Check the optional unit designator (2 ch significant)
  277. wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
  278. if( aUnits == INCHES || aUnits == MILLIMETRES )
  279. {
  280. if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
  281. {
  282. aUnits = INCHES;
  283. aUseMils = false;
  284. }
  285. else if( unit == wxT( "mm" ) )
  286. {
  287. aUnits = MILLIMETRES;
  288. }
  289. else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // "mils" or "thou"
  290. {
  291. aUnits = INCHES;
  292. aUseMils = true;
  293. }
  294. }
  295. else if( aUnits == DEGREES )
  296. {
  297. if( unit == wxT( "ra" ) ) // Radians
  298. {
  299. dtmp *= 180.0f / M_PI;
  300. }
  301. }
  302. value = From_User_Unit( aUnits, dtmp, aUseMils );
  303. return value;
  304. }
  305. void FetchUnitsFromString( const wxString& aTextValue, EDA_UNITS_T& aUnits, bool& aUseMils )
  306. {
  307. wxString buf( aTextValue.Strip( wxString::both ) );
  308. unsigned brk_point = 0;
  309. while( brk_point < buf.Len() )
  310. {
  311. wxChar c = buf[brk_point];
  312. if( !( (c >= '0' && c <='9') || (c == '.') || (c == ',') || (c == '-') || (c == '+') ) )
  313. break;
  314. ++brk_point;
  315. }
  316. // Check the unit designator (2 ch significant)
  317. wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
  318. if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
  319. {
  320. aUnits = INCHES;
  321. aUseMils = false;
  322. }
  323. else if( unit == wxT( "mm" ) )
  324. {
  325. aUnits = MILLIMETRES;
  326. }
  327. else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // "mils" or "thou"
  328. {
  329. aUnits = INCHES;
  330. aUseMils = true;
  331. }
  332. else if( unit == wxT( "de" ) || unit == wxT( "ra" ) ) // "deg" or "rad"
  333. {
  334. aUnits = DEGREES;
  335. }
  336. }
  337. int ValueFromString( EDA_UNITS_T aUnits, const wxString& aTextValue, bool aUseMils )
  338. {
  339. double value = DoubleValueFromString( aUnits, aTextValue, aUseMils );
  340. return KiROUND( value );
  341. }
  342. /**
  343. * Function AngleToStringDegrees
  344. * is a helper to convert the \a double \a aAngle (in internal unit)
  345. * to a string in degrees
  346. */
  347. wxString AngleToStringDegrees( double aAngle )
  348. {
  349. wxString text;
  350. text.Printf( wxT( "%.3f" ), aAngle/10.0 );
  351. StripTrailingZeros( text, 1 );
  352. return text;
  353. }
  354. wxString GetAbbreviatedUnitsLabel( EDA_UNITS_T aUnit, bool aUseMils )
  355. {
  356. switch( aUnit )
  357. {
  358. case INCHES:
  359. if( aUseMils )
  360. return _( "mils" );
  361. else
  362. return _( "in" );
  363. case MILLIMETRES:
  364. return _( "mm" );
  365. case UNSCALED_UNITS:
  366. return wxEmptyString;
  367. case DEGREES:
  368. return _( "deg" );
  369. default:
  370. return wxT( "??" );
  371. }
  372. }
  373. std::string FormatInternalUnits( int aValue )
  374. {
  375. char buf[50];
  376. double engUnits = aValue;
  377. int len;
  378. #ifndef EESCHEMA
  379. engUnits /= IU_PER_MM;
  380. #endif
  381. if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 )
  382. {
  383. len = snprintf( buf, sizeof(buf), "%.10f", engUnits );
  384. while( --len > 0 && buf[len] == '0' )
  385. buf[len] = '\0';
  386. #ifndef EESCHEMA
  387. if( buf[len] == '.' )
  388. buf[len] = '\0';
  389. else
  390. #endif
  391. ++len;
  392. }
  393. else
  394. {
  395. len = snprintf( buf, sizeof(buf), "%.10g", engUnits );
  396. }
  397. return std::string( buf, len );
  398. }
  399. std::string FormatAngle( double aAngle )
  400. {
  401. char temp[50];
  402. int len;
  403. len = snprintf( temp, sizeof(temp), "%.10g", aAngle / 10.0 );
  404. return std::string( temp, len );
  405. }
  406. std::string FormatInternalUnits( const wxPoint& aPoint )
  407. {
  408. return FormatInternalUnits( aPoint.x ) + " " + FormatInternalUnits( aPoint.y );
  409. }
  410. std::string FormatInternalUnits( const VECTOR2I& aPoint )
  411. {
  412. return FormatInternalUnits( aPoint.x ) + " " + FormatInternalUnits( aPoint.y );
  413. }
  414. std::string FormatInternalUnits( const wxSize& aSize )
  415. {
  416. return FormatInternalUnits( aSize.GetWidth() ) + " " + FormatInternalUnits( aSize.GetHeight() );
  417. }