KiCad PCB EDA Suite
eda_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) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <eda_units.h>
25#include <fmt/core.h>
26#include <math/util.h> // for KiROUND
27#include <macros.h>
28
30{
31 switch( aUnit )
32 {
34 case EDA_UNITS::MILS:
35 return true;
36
37 default:
38 return false;
39 }
40}
41
42
44{
45 switch( aUnit )
46 {
48 return true;
49
50 default:
51 return false;
52 }
53}
54
55
56int EDA_UNIT_UTILS::Mm2mils( double aVal )
57{
58 return KiROUND( aVal * 1000. / 25.4 );
59}
60
61
62int EDA_UNIT_UTILS::Mils2mm( double aVal )
63{
64 return KiROUND( aVal * 25.4 / 1000. );
65}
66
67
68bool EDA_UNIT_UTILS::FetchUnitsFromString( const wxString& aTextValue, EDA_UNITS& aUnits )
69{
70 wxString buf( aTextValue.Strip( wxString::both ) );
71 unsigned brk_point = 0;
72
73 while( brk_point < buf.Len() )
74 {
75 wxChar c = buf[brk_point];
76
77 if( !( ( c >= '0' && c <= '9' ) || ( c == '.' ) || ( c == ',' ) || ( c == '-' )
78 || ( c == '+' ) ) )
79 break;
80
81 ++brk_point;
82 }
83
84 // Check the unit designator (2 ch significant)
85 wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
86
87 if( unit == wxT( "mm" ) )
89 else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // "mils" or "thou"
90 aUnits = EDA_UNITS::MILS;
91 else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
92 aUnits = EDA_UNITS::INCHES;
93 else if( unit == wxT( "de" ) || unit == wxT( "ra" ) ) // "deg" or "rad"
94 aUnits = EDA_UNITS::DEGREES;
95 else
96 return false;
97 return true;
98}
99
100
102{
103 wxString label;
104
105 switch( aUnits )
106 {
107 case EDA_UNITS::MILLIMETRES: label = wxT( " mm" ); break;
108 case EDA_UNITS::DEGREES: label = wxT( "°" ); break;
109 case EDA_UNITS::MILS: label = wxT( " mils" ); break;
110 case EDA_UNITS::INCHES: label = wxT( " in" ); break;
111 case EDA_UNITS::PERCENT: label = wxT( "%" ); break;
112 case EDA_UNITS::UNSCALED: break;
113 default: UNIMPLEMENTED_FOR( wxS( "Unknown units" ) ); break;
114 }
115
116 switch( aType )
117 {
118 case EDA_DATA_TYPE::VOLUME: label += wxT( "³" ); break;
119 case EDA_DATA_TYPE::AREA: label += wxT( "²" ); break;
120 case EDA_DATA_TYPE::DISTANCE: break;
121 default: UNIMPLEMENTED_FOR( wxS( "Unknown measurement" ) ); break;
122 }
123
124 return label;
125}
126
127
129{
130 return GetText( aUnits, aType ).Trim( false );
131}
132
133
134std::string EDA_UNIT_UTILS::FormatAngle( const EDA_ANGLE& aAngle )
135{
136 std::string temp = fmt::format( "{:.10g}", aAngle.AsDegrees() );
137
138 return temp;
139}
140
141
142std::string EDA_UNIT_UTILS::FormatInternalUnits( const EDA_IU_SCALE& aIuScale, int aValue )
143{
144 std::string buf;
145 double engUnits = aValue;
146
147 engUnits /= aIuScale.IU_PER_MM;
148
149 if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 )
150 {
151 buf = fmt::format( "{:.10f}", engUnits );
152
153 // remove trailing zeros
154 while( !buf.empty() && buf[buf.size() - 1] == '0' )
155 {
156 buf.pop_back();
157 }
158
159 // if the value was really small
160 // we may have just stripped all the zeros after the decimal
161 if( buf[buf.size() - 1] == '.' )
162 {
163 buf.pop_back();
164 }
165 }
166 else
167 {
168 buf = fmt::format( "{:.10g}", engUnits );
169 }
170
171 return buf;
172}
173
174
176 const VECTOR2I& aPoint )
177{
178 return FormatInternalUnits( aIuScale, aPoint.x ) + " "
179 + FormatInternalUnits( aIuScale, aPoint.y );
180}
181
182
183#define IU_TO_MM( x, scale ) ( x / scale.IU_PER_MM )
184#define IU_TO_IN( x, scale ) ( x / scale.IU_PER_MILS / 1000 )
185#define IU_TO_MILS( x, scale ) ( x / scale.IU_PER_MILS )
186#define MM_TO_IU( x, scale ) ( x * scale.IU_PER_MM )
187#define IN_TO_IU( x, scale ) ( x * scale.IU_PER_MILS * 1000 )
188#define MILS_TO_IU( x, scale ) ( x * scale.IU_PER_MILS )
189
191 double aValue )
192{
193 switch( aUnit )
194 {
196 return IU_TO_MM( aValue, aIuScale );
197
198 case EDA_UNITS::MILS:
199 return IU_TO_MILS( aValue, aIuScale );
200
202 return IU_TO_IN( aValue, aIuScale );
203
205 return aValue;
206
207 default:
208 return aValue;
209 }
210}
211
212
226 double aValue, bool aAddUnitsText,
227 EDA_DATA_TYPE aType )
228{
229 double value_to_print = aValue;
230
231 switch( aType )
232 {
234 value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
236
238 value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
240
242 value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
243 }
244
245 char buf[50];
246
247 if( value_to_print != 0.0 && fabs( value_to_print ) <= 0.0001 )
248 {
249 int len = snprintf( buf, sizeof( buf ) - 1, "%.10f", value_to_print );
250
251 while( --len > 0 && buf[len] == '0' )
252 buf[len] = '\0';
253
254 if( len >= 0 && ( buf[len] == '.' || buf[len] == ',' ) )
255 buf[len] = '\0';
256 }
257 else
258 {
259 snprintf( buf, sizeof( buf ) - 1, "%.10g", value_to_print );
260 }
261
262 wxString stringValue( buf, wxConvUTF8 );
263
264 if( aAddUnitsText )
265 stringValue += EDA_UNIT_UTILS::GetText( aUnits, aType );
266
267 return stringValue;
268}
269
270
271
285// A lower-precision (for readability) version of StringFromValue()
287 int aValue,
288 bool aAddUnitLabel,
289 EDA_DATA_TYPE aType )
290{
291 return MessageTextFromValue( aIuScale, aUnits, double( aValue ), aAddUnitLabel, aType );
292}
293
294
295// A lower-precision (for readability) version of StringFromValue()
297 long long int aValue,
298 bool aAddUnitLabel,
299 EDA_DATA_TYPE aType )
300{
301 return MessageTextFromValue( aIuScale, aUnits, double( aValue ), aAddUnitLabel, aType );
302}
303
304
305wxString EDA_UNIT_UTILS::UI::MessageTextFromValue( EDA_ANGLE aValue, bool aAddUnitLabel )
306{
307 if( aAddUnitLabel )
308 return wxString::Format( wxT( "%.1f°" ), aValue.AsDegrees() );
309 else
310 return wxString::Format( wxT( "%.1f" ), aValue.AsDegrees() );
311}
312
313
314// A lower-precision (for readability) version of StringFromValue()
316 double aValue, bool aAddUnitsText,
317 EDA_DATA_TYPE aType )
318{
319 wxString text;
320 const wxChar* format;
321 double value = aValue;
322
323 switch( aType )
324 {
326 value = ToUserUnit( aIuScale, aUnits, value );
327 // Fall through to continue computation
329
331 value = ToUserUnit( aIuScale, aUnits, value );
332 // Fall through to continue computation
334
336 value = ToUserUnit( aIuScale, aUnits, value );
337 }
338
339 switch( aUnits )
340 {
341 default:
343#if defined( EESCHEMA )
344 format = wxT( "%.2f" );
345#else
346 format = wxT( "%.4f" );
347#endif
348 break;
349
350 case EDA_UNITS::MILS:
351#if defined( EESCHEMA )
352 format = wxT( "%.0f" );
353#else
354 format = wxT( "%.2f" );
355#endif
356 break;
357
359#if defined( EESCHEMA )
360 format = wxT( "%.3f" );
361#else
362 format = wxT( "%.4f" );
363#endif
364 break;
365
367 // 3 digits in mantissa should be good for rotation in degree
368 format = wxT( "%.3f" );
369 break;
370
372 format = wxT( "%.0f" );
373 break;
374 }
375
376 text.Printf( format, value );
377
378 if( aAddUnitsText )
379 text += EDA_UNIT_UTILS::GetText( aUnits, aType );
380
381 return text;
382}
383
384
386 double aValue )
387{
388 switch( aUnits )
389 {
391 return MM_TO_IU( aValue, aIuScale );
392
393 case EDA_UNITS::MILS:
394 return MILS_TO_IU( aValue, aIuScale );
395
397 return IN_TO_IU( aValue, aIuScale );
398
399 default:
403 return aValue;
404 }
405}
406
407
408double EDA_UNIT_UTILS::UI::DoubleValueFromString( const wxString& aTextValue )
409{
410 double dtmp = 0;
411
412 // Acquire the 'right' decimal point separator
413 const struct lconv* lc = localeconv();
414
415 wxChar decimal_point = lc->decimal_point[0];
416 wxString buf( aTextValue.Strip( wxString::both ) );
417
418 // Convert any entered decimal point separators to the 'right' one
419 buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
420 buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) );
421
422 // Find the end of the numeric part
423 unsigned brk_point = 0;
424
425 while( brk_point < buf.Len() )
426 {
427 wxChar ch = buf[brk_point];
428
429 if( !( ( ch >= '0' && ch <= '9' ) || ( ch == decimal_point ) || ( ch == '-' )
430 || ( ch == '+' ) ) )
431 {
432 break;
433 }
434
435 ++brk_point;
436 }
437
438 // Extract the numeric part
439 buf.Left( brk_point ).ToDouble( &dtmp );
440
441 return dtmp;
442}
443
444
446 const wxString& aTextValue, EDA_DATA_TYPE aType )
447{
448 double dtmp = 0;
449
450 // Acquire the 'right' decimal point separator
451 const struct lconv* lc = localeconv();
452
453 wxChar decimal_point = lc->decimal_point[0];
454 wxString buf( aTextValue.Strip( wxString::both ) );
455
456 // Convert any entered decimal point separators to the 'right' one
457 buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
458 buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) );
459
460 // Find the end of the numeric part
461 unsigned brk_point = 0;
462
463 while( brk_point < buf.Len() )
464 {
465 wxChar ch = buf[brk_point];
466
467 if( !( (ch >= '0' && ch <= '9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) )
468 break;
469
470 ++brk_point;
471 }
472
473 // Extract the numeric part
474 buf.Left( brk_point ).ToDouble( &dtmp );
475
476 // Check the optional unit designator (2 ch significant)
477 wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
478
479 if( aUnits == EDA_UNITS::MILLIMETRES
480 || aUnits == EDA_UNITS::MILS
481 || aUnits == EDA_UNITS::INCHES )
482 {
483 if( unit == wxT( "mm" ) )
484 {
485 aUnits = EDA_UNITS::MILLIMETRES;
486 }
487 else if( unit == wxT( "mi" ) || unit == wxT( "th" ) )
488 {
489 aUnits = EDA_UNITS::MILS;
490 }
491 else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
492 {
493 aUnits = EDA_UNITS::INCHES;
494 }
495 else if( unit == wxT( "oz" ) ) // 1 oz = 1.37 mils
496 {
497 aUnits = EDA_UNITS::MILS;
498 dtmp *= 1.37;
499 }
500 }
501 else if( aUnits == EDA_UNITS::DEGREES )
502 {
503 if( unit == wxT( "ra" ) ) // Radians
504 dtmp *= 180.0f / M_PI;
505 }
506
507 switch( aType )
508 {
510 dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
512
514 dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
516
518 dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
519 }
520
521 return dtmp;
522}
523
524
525long long int EDA_UNIT_UTILS::UI::ValueFromString( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
526 const wxString& aTextValue, EDA_DATA_TYPE aType )
527{
528 double value = DoubleValueFromString( aIuScale, aUnits, aTextValue, aType );
529
530 return KiROUND<double, long long int>( value );
531}
532
533
534long long int EDA_UNIT_UTILS::UI::ValueFromString( const wxString& aTextValue )
535{
536 double value = DoubleValueFromString( aTextValue );
537
538 return KiROUND<double, long long int>( value );
539}
double AsDegrees() const
Definition: eda_angle.h:149
#define IU_TO_IN(x, scale)
Definition: eda_units.cpp:184
#define IN_TO_IU(x, scale)
Definition: eda_units.cpp:187
#define IU_TO_MILS(x, scale)
Definition: eda_units.cpp:185
#define MM_TO_IU(x, scale)
Definition: eda_units.cpp:186
#define IU_TO_MM(x, scale)
Definition: eda_units.cpp:183
#define MILS_TO_IU(x, scale)
Definition: eda_units.cpp:188
EDA_DATA_TYPE
The type of unit.
Definition: eda_units.h:36
EDA_UNITS
Definition: eda_units.h:43
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
Definition: eda_units.cpp:315
double FromUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Return in internal units the value "val" given in a real unit such as "in", "mm" or "deg".
Definition: eda_units.cpp:385
long long int ValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: eda_units.cpp:525
wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Returns the string from aValue according to aUnits (inch, mm ...) for display.
Definition: eda_units.cpp:225
double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:445
double ToUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Function To_User_Unit convert aValue in internal units to the appropriate user units defined by aUnit...
Definition: eda_units.cpp:190
wxString GetText(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
Definition: eda_units.cpp:101
bool FetchUnitsFromString(const wxString &aTextValue, EDA_UNITS &aUnits)
Writes any unit info found in the string to aUnits.
Definition: eda_units.cpp:68
bool IsImperialUnit(EDA_UNITS aUnit)
Definition: eda_units.cpp:29
bool IsMetricUnit(EDA_UNITS aUnit)
Definition: eda_units.cpp:43
wxString GetLabel(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
Definition: eda_units.cpp:128
int Mm2mils(double aVal)
Convert mm to mils.
Definition: eda_units.cpp:56
std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue)
Converts aValue from internal units to a string appropriate for writing to file.
Definition: eda_units.cpp:142
std::string FormatAngle(const EDA_ANGLE &aAngle)
Converts aAngle from board units to a string appropriate for writing to file.
Definition: eda_units.cpp:134
int Mils2mm(double aVal)
Convert mils to mm.
Definition: eda_units.cpp:62
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
const double IU_PER_MM
Definition: base_units.h:77
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85