KiCad PCB EDA Suite
base_units.cpp
Go to the documentation of this file.
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 
37 #include <base_units.h>
38 #include <common.h>
39 #include <kicad_string.h>
40 #include <math/util.h> // for KiROUND
41 #include <macros.h>
42 #include <title_block.h>
43 
44 #if defined( PCBNEW ) || defined( CVPCB ) || defined( EESCHEMA ) || defined( GERBVIEW ) || defined( PL_EDITOR )
45 #define IU_TO_MM( x ) ( x / IU_PER_MM )
46 #define IU_TO_IN( x ) ( x / IU_PER_MILS / 1000 )
47 #define IU_TO_MILS( x ) ( x / IU_PER_MILS )
48 #define MM_TO_IU( x ) ( x * IU_PER_MM )
49 #define IN_TO_IU( x ) ( x * IU_PER_MILS * 1000 )
50 #define MILS_TO_IU( x ) ( x * IU_PER_MILS )
51 #else
52 #error "Cannot resolve internal units due to no definition of EESCHEMA, CVPCB or PCBNEW."
53 #endif
54 
55 // Helper function to print a float number without using scientific notation
56 // and no trailing 0
57 // So we cannot always just use the %g or the %f format to print a fp number
58 // this helper function uses the %f format when needed, or %g when %f is
59 // not well working and then removes trailing 0
60 
61 std::string Double2Str( double aValue )
62 {
63  char buf[50];
64  int len;
65 
66  if( aValue != 0.0 && fabs( aValue ) <= 0.0001 )
67  {
68  // For these small values, %f works fine,
69  // and %g gives an exponent
70  len = sprintf( buf, "%.16f", aValue );
71 
72  while( --len > 0 && buf[len] == '0' )
73  buf[len] = '\0';
74 
75  if( buf[len] == '.' )
76  buf[len] = '\0';
77  else
78  ++len;
79  }
80  else
81  {
82  // For these values, %g works fine, and sometimes %f
83  // gives a bad value (try aValue = 1.222222222222, with %.16f format!)
84  len = sprintf( buf, "%.16g", aValue );
85  }
86 
87  return std::string( buf, len );
88 }
89 
90 
91 double To_User_Unit( EDA_UNITS aUnit, double aValue )
92 {
93  switch( aUnit )
94  {
96  return IU_TO_MM( aValue );
97 
98  case EDA_UNITS::MILS:
99  return IU_TO_MILS( aValue );
100 
101  case EDA_UNITS::INCHES:
102  return IU_TO_IN( aValue );
103 
104  case EDA_UNITS::DEGREES:
105  return aValue / 10.0f;
106 
107  default:
108  return aValue;
109  }
110 }
111 
124 // A lower-precision (for readability) version of StringFromValue()
125 wxString MessageTextFromValue( EDA_UNITS aUnits, int aValue, bool aAddUnitLabel,
126  EDA_DATA_TYPE aType )
127 {
128  return MessageTextFromValue( aUnits, double( aValue ), aAddUnitLabel, aType );
129 }
130 
131 
132 // A lower-precision (for readability) version of StringFromValue()
133 wxString MessageTextFromValue( EDA_UNITS aUnits, long long int aValue, bool aAddUnitLabel,
134  EDA_DATA_TYPE aType )
135 {
136  return MessageTextFromValue( aUnits, double( aValue ), aAddUnitLabel, aType );
137 }
138 
139 
140 // A lower-precision (for readability) version of StringFromValue()
141 wxString MessageTextFromValue( EDA_UNITS aUnits, double aValue, bool aAddUnitLabel,
142  EDA_DATA_TYPE aType )
143 {
144  wxString text;
145  const wxChar* format;
146  double value = aValue;
147 
148  switch( aType )
149  {
151  value = To_User_Unit( aUnits, value );
152  // Fall through to continue computation
154 
155  case EDA_DATA_TYPE::AREA:
156  value = To_User_Unit( aUnits, value );
157  // Fall through to continue computation
159 
161  value = To_User_Unit( aUnits, value );
162  }
163 
164  switch( aUnits )
165  {
166  default:
168 #if defined( EESCHEMA )
169  format = wxT( "%.2f" );
170 #else
171  format = wxT( "%.4f" );
172 #endif
173  break;
174 
175  case EDA_UNITS::MILS:
176 #if defined( EESCHEMA )
177  format = wxT( "%.0f" );
178 #else
179  format = wxT( "%.2f" );
180 #endif
181  break;
182 
183  case EDA_UNITS::INCHES:
184 #if defined( EESCHEMA )
185  format = wxT( "%.3f" );
186 #else
187  format = wxT( "%.4f" );
188 #endif
189  break;
190 
191  case EDA_UNITS::DEGREES:
192  // 3 digits in mantissa should be good for rotation in degree
193  format = wxT( "%.3f" );
194  break;
195 
196  case EDA_UNITS::UNSCALED:
197  format = wxT( "%.0f" );
198  break;
199  }
200 
201  text.Printf( format, value );
202 
203  if( aAddUnitLabel )
204  {
205  text += " ";
206  text += GetAbbreviatedUnitsLabel( aUnits, aType );
207  }
208 
209  return text;
210 }
211 
212 
225 wxString StringFromValue( EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol,
226  EDA_DATA_TYPE aType )
227 {
228  double value_to_print = aValue;
229 
230  switch( aType )
231  {
233  value_to_print = To_User_Unit( aUnits, value_to_print );
235 
236  case EDA_DATA_TYPE::AREA:
237  value_to_print = To_User_Unit( aUnits, value_to_print );
239 
241  value_to_print = To_User_Unit( aUnits, value_to_print );
242  }
243 
244 
245 #if defined( EESCHEMA )
246  wxString stringValue = wxString::Format( wxT( "%.3f" ), value_to_print );
247 
248  // Strip trailing zeros. However, keep at least 3 digits in mantissa for readability.
249  StripTrailingZeros( stringValue, 3 );
250 
251 #else
252 
253  char buf[50];
254  int len;
255 
256  if( value_to_print != 0.0 && fabs( value_to_print ) <= 0.0001 )
257  {
258  len = sprintf( buf, "%.10f", value_to_print );
259 
260  while( --len > 0 && buf[len] == '0' )
261  buf[len] = '\0';
262 
263  if( buf[len]=='.' || buf[len]==',' )
264  buf[len] = '\0';
265  else
266  ++len;
267  }
268  else
269  {
270  if( aUnits == EDA_UNITS::MILS )
271  len = sprintf( buf, "%.7g", value_to_print );
272  else
273  len = sprintf( buf, "%.10g", value_to_print );
274  }
275 
276  wxString stringValue( buf, wxConvUTF8 );
277 
278 #endif
279 
280  if( aAddUnitSymbol )
281  {
282  switch( aUnits )
283  {
285  stringValue += wxT( " mm" );
286  break;
287 
288  case EDA_UNITS::DEGREES:
289  stringValue += wxT( " deg" );
290  break;
291 
292  case EDA_UNITS::MILS:
293  stringValue += wxT( " mils" );
294  break;
295 
296  case EDA_UNITS::INCHES:
297  stringValue += wxT( " in" );
298  break;
299 
300  case EDA_UNITS::PERCENT:
301  stringValue += wxT( "%" );
302  break;
303 
304  case EDA_UNITS::UNSCALED:
305  break;
306  }
307  }
308 
309  return stringValue;
310 }
311 
312 
313 double From_User_Unit( EDA_UNITS aUnits, double aValue )
314 {
315  switch( aUnits )
316  {
318  return MM_TO_IU( aValue );
319 
320  case EDA_UNITS::MILS:
321  return MILS_TO_IU( aValue );
322 
323  case EDA_UNITS::INCHES:
324  return IN_TO_IU( aValue );
325 
326  case EDA_UNITS::DEGREES:
327  // Convert to "decidegrees"
328  return aValue * 10;
329 
330  default:
331  case EDA_UNITS::UNSCALED:
332  case EDA_UNITS::PERCENT:
333  return aValue;
334  }
335 }
336 
337 
338 double DoubleValueFromString( EDA_UNITS aUnits, const wxString& aTextValue, EDA_DATA_TYPE aType )
339 {
340  double dtmp = 0;
341 
342  // Acquire the 'right' decimal point separator
343  const struct lconv* lc = localeconv();
344 
345  wxChar decimal_point = lc->decimal_point[0];
346  wxString buf( aTextValue.Strip( wxString::both ) );
347 
348  // Convert any entered decimal point separators to the 'right' one
349  buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
350  buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) );
351 
352  // Find the end of the numeric part
353  unsigned brk_point = 0;
354 
355  while( brk_point < buf.Len() )
356  {
357  wxChar ch = buf[brk_point];
358 
359  if( !( (ch >= '0' && ch <= '9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) )
360  {
361  break;
362  }
363 
364  ++brk_point;
365  }
366 
367  // Extract the numeric part
368  buf.Left( brk_point );
369 
370  buf.ToDouble( &dtmp );
371 
372  // Check the optional unit designator (2 ch significant)
373  wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
374 
375  if( aUnits == EDA_UNITS::MILLIMETRES || 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 == "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  {
400  dtmp *= 180.0f / M_PI;
401  }
402  }
403 
404  switch( aType )
405  {
407  dtmp = From_User_Unit( aUnits, dtmp );
409 
410  case EDA_DATA_TYPE::AREA:
411  dtmp = From_User_Unit( aUnits, dtmp );
413 
415  dtmp = From_User_Unit( aUnits, dtmp );
416  }
417 
418  return dtmp;
419 }
420 
421 
422 void FetchUnitsFromString( const wxString& aTextValue, EDA_UNITS& aUnits )
423 {
424  wxString buf( aTextValue.Strip( wxString::both ) );
425  unsigned brk_point = 0;
426 
427  while( brk_point < buf.Len() )
428  {
429  wxChar c = buf[brk_point];
430 
431  if( !( (c >= '0' && c <='9') || (c == '.') || (c == ',') || (c == '-') || (c == '+') ) )
432  break;
433 
434  ++brk_point;
435  }
436 
437  // Check the unit designator (2 ch significant)
438  wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
439 
440  if( unit == wxT( "mm" ) )
441  aUnits = EDA_UNITS::MILLIMETRES;
442  else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // "mils" or "thou"
443  aUnits = EDA_UNITS::MILS;
444  else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
445  aUnits = EDA_UNITS::INCHES;
446  else if( unit == wxT( "de" ) || unit == wxT( "ra" ) ) // "deg" or "rad"
447  aUnits = EDA_UNITS::DEGREES;
448 }
449 
450 
451 long long int ValueFromString( EDA_UNITS aUnits, const wxString& aTextValue, EDA_DATA_TYPE aType )
452 {
453  double value = DoubleValueFromString( aUnits, aTextValue, aType );
454  return KiROUND<double, long long int>( value );
455 }
456 
457 
461 wxString AngleToStringDegrees( double aAngle )
462 {
463  wxString text;
464 
465  text.Printf( wxT( "%.3f" ), aAngle / 10.0 );
466  StripTrailingZeros( text, 1 );
467 
468  return text;
469 }
470 
471 
473 {
474  switch( aUnit )
475  {
477  switch( aType )
478  {
479  default:
480  wxASSERT( 0 );
483  return _( "mm" );
484  case EDA_DATA_TYPE::AREA:
485  return _( "sq. mm" );
487  return _( "cu. mm" );
488  }
489 
490  case EDA_UNITS::MILS:
491  switch( aType )
492  {
493  default:
494  wxASSERT( 0 );
497  return _( "mils" );
498  case EDA_DATA_TYPE::AREA:
499  return _( "sq. mils" );
501  return _( "cu. mils" );
502  }
503 
504  case EDA_UNITS::INCHES:
505  switch( aType )
506  {
507  default:
508  wxASSERT( 0 );
511  return _( "in" );
512  case EDA_DATA_TYPE::AREA:
513  return _( "sq. in" );
515  return _( "cu. in" );
516  }
517 
518  case EDA_UNITS::PERCENT:
519  return _( "%" );
520 
521  case EDA_UNITS::UNSCALED:
522  return wxEmptyString;
523 
524  case EDA_UNITS::DEGREES:
525  return _( "deg" );
526 
527  default:
528  return wxT( "??" );
529  }
530 }
531 
532 
533 std::string FormatInternalUnits( int aValue )
534 {
535  char buf[50];
536  double engUnits = aValue;
537  int len;
538 
539  engUnits /= IU_PER_MM;
540 
541  if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 )
542  {
543  len = snprintf( buf, sizeof(buf), "%.10f", engUnits );
544 
545  // Make sure snprintf() didn't fail and the locale numeric separator is correct.
546  wxCHECK( len >= 0 && len < 50 && strchr( buf, ',' ) == NULL, std::string( "" ) );
547 
548  while( --len > 0 && buf[len] == '0' )
549  buf[len] = '\0';
550 
551  if( buf[len] == '.' )
552  buf[len] = '\0';
553  else
554  ++len;
555  }
556  else
557  {
558  len = snprintf( buf, sizeof(buf), "%.10g", engUnits );
559 
560  // Make sure snprintf() didn't fail and the locale numeric separator is correct.
561  wxCHECK( len >= 0 && len < 50 && strchr( buf, ',' ) == NULL , std::string( "" ) );
562  }
563 
564  return std::string( buf, len );
565 }
566 
567 
568 std::string FormatAngle( double aAngle )
569 {
570  char temp[50];
571  int len;
572 
573  len = snprintf( temp, sizeof(temp), "%.10g", aAngle / 10.0 );
574 
575  return std::string( temp, len );
576 }
577 
578 
579 std::string FormatInternalUnits( const wxPoint& aPoint )
580 {
581  return FormatInternalUnits( aPoint.x ) + " " + FormatInternalUnits( aPoint.y );
582 }
583 
584 
585 std::string FormatInternalUnits( const VECTOR2I& aPoint )
586 {
587  return FormatInternalUnits( aPoint.x ) + " " + FormatInternalUnits( aPoint.y );
588 }
589 
590 
591 std::string FormatInternalUnits( const wxSize& aSize )
592 {
593  return FormatInternalUnits( aSize.GetWidth() ) + " " + FormatInternalUnits( aSize.GetHeight() );
594 }
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:125
Implementation of conversion functions that require both schematic and board internal units.
static constexpr double IU_PER_MM
Mock up a conversion function.
EDA_DATA_TYPE
The type of unit.
Definition: eda_units.h:31
Define a general 2D-vector/point.
Definition: vector2d.h:61
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
std::string Double2Str(double aValue)
Helper function Double2Str to print a float number without using scientific notation and no trailing ...
Definition: base_units.cpp:61
wxString GetAbbreviatedUnitsLabel(EDA_UNITS aUnit, EDA_DATA_TYPE aType)
Get the units string for a given units type.
Definition: base_units.cpp:472
void StripTrailingZeros(wxString &aStringValue, unsigned aTrailingZeroAllowed)
Remove trailing zeros from a string containing a converted float number.
Definition: string.cpp:830
This file contains miscellaneous commonly used macros and functions.
std::string FormatAngle(double aAngle)
Function FormatAngle converts aAngle from board units to a string appropriate for writing to file.
Definition: base_units.cpp:568
void FetchUnitsFromString(const wxString &aTextValue, EDA_UNITS &aUnits)
Function FetchUnitsFromString writes any unit info found in the string to aUnits.
Definition: base_units.cpp:422
#define NULL
long long int ValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: base_units.cpp:451
wxString AngleToStringDegrees(double aAngle)
A helper to convert aAngle in deci-degrees to a string in degrees.
Definition: base_units.cpp:461
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
EDA_UNITS
Definition: eda_units.h:38
double To_User_Unit(EDA_UNITS aUnit, double aValue)
Function To_User_Unit convert aValue in internal units to the appropriate user units defined by aUnit...
Definition: base_units.cpp:91
#define _(s)
Definition: 3d_actions.cpp:33
The common library.
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:225
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:338
double From_User_Unit(EDA_UNITS aUnits, double aValue)
Return in internal units the value "val" given in a real unit such as "in", "mm" or "deg".
Definition: base_units.cpp:313
std::string FormatInternalUnits(int aValue)
Function FormatInternalUnits converts aValue from internal units to a string appropriate for writing ...
Definition: base_units.cpp:533