KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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#include <charconv>
29#include <wx/translation.h>
30
32{
33 switch( aUnit )
34 {
36 case EDA_UNITS::MILS:
37 return true;
38
39 default:
40 return false;
41 }
42}
43
44
46{
47 switch( aUnit )
48 {
52 return true;
53
54 default:
55 return false;
56 }
57}
58
59
60int EDA_UNIT_UTILS::Mm2mils( double aVal )
61{
62 return KiROUND( aVal * 1000. / 25.4 );
63}
64
65
66int EDA_UNIT_UTILS::Mils2mm( double aVal )
67{
68 return KiROUND( aVal * 25.4 / 1000. );
69}
70
71
72bool EDA_UNIT_UTILS::FetchUnitsFromString( const wxString& aTextValue, EDA_UNITS& aUnits )
73{
74 wxString buf( aTextValue.Strip( wxString::both ) );
75 unsigned brk_point = 0;
76
77 while( brk_point < buf.Len() )
78 {
79 wxChar c = buf[brk_point];
80
81 if( !( ( c >= '0' && c <= '9' ) || ( c == '.' ) || ( c == ',' ) || ( c == '-' )
82 || ( c == '+' ) ) )
83 break;
84
85 ++brk_point;
86 }
87
88 // Check the unit designator (2 ch significant)
89 wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
90
91 //check for um, μm (µ is MICRO SIGN) and µm (µ is GREEK SMALL LETTER MU) for micrometre
92 if( unit == wxT( "um" ) || unit == wxT( "\u00B5m" ) || unit == wxT( "\u03BCm" ) )
94 else if( unit == wxT( "mm" ) )
96 if( unit == wxT( "cm" ) )
98 else if( unit == wxT( "mi" ) || unit == wxT( "th" ) ) // "mils" or "thou"
99 aUnits = EDA_UNITS::MILS;
100 else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
101 aUnits = EDA_UNITS::INCHES;
102 else if( unit == wxT( "de" ) || unit == wxT( "ra" ) ) // "deg" or "rad"
103 aUnits = EDA_UNITS::DEGREES;
104 else
105 return false;
106 return true;
107}
108
109
111{
112 wxString label;
113
114 switch( aUnits )
115 {
116 case EDA_UNITS::MICROMETRES: label = wxT( " \u00B5m" ); break; //00B5 for µ
117 case EDA_UNITS::MILLIMETRES: label = wxT( " mm" ); break;
118 case EDA_UNITS::CENTIMETRES: label = wxT( " cm" ); break;
119 case EDA_UNITS::DEGREES: label = wxT( "°" ); break;
120 case EDA_UNITS::MILS: label = wxT( " mils" ); break;
121 case EDA_UNITS::INCHES: label = wxT( " in" ); break;
122 case EDA_UNITS::PERCENT: label = wxT( "%" ); break;
123 case EDA_UNITS::UNSCALED: break;
124 default: UNIMPLEMENTED_FOR( wxS( "Unknown units" ) ); break;
125 }
126
127 switch( aType )
128 {
129 case EDA_DATA_TYPE::VOLUME: label += wxT( "³" ); break;
130 case EDA_DATA_TYPE::AREA: label += wxT( "²" ); break;
131 case EDA_DATA_TYPE::DISTANCE: break;
132 default: UNIMPLEMENTED_FOR( wxS( "Unknown measurement" ) ); break;
133 }
134
135 return label;
136}
137
138
140{
141 return GetText( aUnits, aType ).Trim( false );
142}
143
144
145std::string EDA_UNIT_UTILS::FormatAngle( const EDA_ANGLE& aAngle )
146{
147 std::string temp = fmt::format( "{:.10g}", aAngle.AsDegrees() );
148
149 return temp;
150}
151
152
153std::string EDA_UNIT_UTILS::FormatInternalUnits( const EDA_IU_SCALE& aIuScale, int aValue )
154{
155 std::string buf;
156 double engUnits = aValue;
157
158 engUnits /= aIuScale.IU_PER_MM;
159
160 if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 )
161 {
162 buf = fmt::format( "{:.10f}", engUnits );
163
164 // remove trailing zeros
165 while( !buf.empty() && buf[buf.size() - 1] == '0' )
166 {
167 buf.pop_back();
168 }
169
170 // if the value was really small
171 // we may have just stripped all the zeros after the decimal
172 if( buf[buf.size() - 1] == '.' )
173 {
174 buf.pop_back();
175 }
176 }
177 else
178 {
179 buf = fmt::format( "{:.10g}", engUnits );
180 }
181
182 return buf;
183}
184
185
187 const VECTOR2I& aPoint )
188{
189 return FormatInternalUnits( aIuScale, aPoint.x ) + " "
190 + FormatInternalUnits( aIuScale, aPoint.y );
191}
192
193
194#if 0 // No support for std::from_chars on MacOS yet
195
196bool EDA_UNIT_UTILS::ParseInternalUnits( const std::string& aInput, const EDA_IU_SCALE& aIuScale,
197 int& aOut )
198{
199 double value;
200
201 if( std::from_chars( aInput.data(), aInput.data() + aInput.size(), value ).ec != std::errc() )
202 return false;
203
204 aOut = value * aIuScale.IU_PER_MM;
205 return true;
206}
207
208
209bool EDA_UNIT_UTILS::ParseInternalUnits( const std::string& aInput, const EDA_IU_SCALE& aIuScale,
210 VECTOR2I& aOut )
211{
212 size_t pos = aInput.find( ' ' );
213
214 if( pos == std::string::npos )
215 return false;
216
217 std::string first = aInput.substr( 0, pos );
218 std::string second = aInput.substr( pos + 1 );
219
220 VECTOR2I vec;
221
222 if( !ParseInternalUnits( first, aIuScale, vec.x ) )
223 return false;
224
225 if( !ParseInternalUnits( second, aIuScale, vec.y ) )
226 return false;
227
228 aOut = vec;
229
230 return true;
231}
232
233#endif
234
235
236#define IU_TO_MM( x, scale ) ( x / scale.IU_PER_MM )
237#define IU_TO_IN( x, scale ) ( x / scale.IU_PER_MILS / 1000 )
238#define IU_TO_MILS( x, scale ) ( x / scale.IU_PER_MILS )
239#define MM_TO_IU( x, scale ) ( x * scale.IU_PER_MM )
240#define IN_TO_IU( x, scale ) ( x * scale.IU_PER_MILS * 1000 )
241#define MILS_TO_IU( x, scale ) ( x * scale.IU_PER_MILS )
242
244 double aValue )
245{
246 switch( aUnit )
247 {
249 return IU_TO_MM( aValue, aIuScale ) * 1000;
250
252 return IU_TO_MM( aValue, aIuScale );
253
255 return IU_TO_MM( aValue, aIuScale ) / 10;
256
257 case EDA_UNITS::MILS:
258 return IU_TO_MILS( aValue, aIuScale );
259
261 return IU_TO_IN( aValue, aIuScale );
262
264 return aValue;
265
266 default:
267 return aValue;
268 }
269}
270
271
285 double aValue, bool aAddUnitsText,
286 EDA_DATA_TYPE aType )
287{
288 double value_to_print = aValue;
289
290 switch( aType )
291 {
293 value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
295
297 value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
299
301 value_to_print = ToUserUnit( aIuScale, aUnits, value_to_print );
302 break;
303
305 break;
306 }
307
308 char buf[50];
309
310 if( value_to_print != 0.0 && fabs( value_to_print ) <= 0.0001 )
311 {
312 int len = snprintf( buf, sizeof( buf ) - 1, "%.10f", value_to_print );
313
314 while( --len > 0 && buf[len] == '0' )
315 buf[len] = '\0';
316
317 if( len >= 0 && ( buf[len] == '.' || buf[len] == ',' ) )
318 buf[len] = '\0';
319 }
320 else
321 {
322 snprintf( buf, sizeof( buf ) - 1, "%.10g", value_to_print );
323 }
324
325 wxString stringValue( buf, wxConvUTF8 );
326
327 if( aAddUnitsText )
328 stringValue += EDA_UNIT_UTILS::GetText( aUnits, aType );
329
330 return stringValue;
331}
332
333
334
348// A lower-precision (for readability) version of StringFromValue()
350 int aValue,
351 bool aAddUnitLabel,
352 EDA_DATA_TYPE aType )
353{
354 return MessageTextFromValue( aIuScale, aUnits, double( aValue ), aAddUnitLabel, aType );
355}
356
357
358// A lower-precision (for readability) version of StringFromValue()
360 long long int aValue,
361 bool aAddUnitLabel,
362 EDA_DATA_TYPE aType )
363{
364 return MessageTextFromValue( aIuScale, aUnits, double( aValue ), aAddUnitLabel, aType );
365}
366
367
368wxString EDA_UNIT_UTILS::UI::MessageTextFromValue( EDA_ANGLE aValue, bool aAddUnitLabel )
369{
370 if( aAddUnitLabel )
371 return wxString::Format( wxT( "%.1f°" ), aValue.AsDegrees() );
372 else
373 return wxString::Format( wxT( "%.1f" ), aValue.AsDegrees() );
374}
375
376
377// A lower-precision (for readability) version of StringFromValue()
379 double aValue, bool aAddUnitsText,
380 EDA_DATA_TYPE aType )
381{
382 wxString text;
383 const wxChar* format;
384 double value = aValue;
385
386 switch( aType )
387 {
389 value = ToUserUnit( aIuScale, aUnits, value );
390 // Fall through to continue computation
392
394 value = ToUserUnit( aIuScale, aUnits, value );
395 // Fall through to continue computation
397
399 value = ToUserUnit( aIuScale, aUnits, value );
400 break;
401
403 break;
404 }
405
406 switch( aUnits )
407 {
408 default:
410#if defined( EESCHEMA )
411 format = wxT( "%.0f" );
412#else
413 format = wxT( "%.1f" );
414#endif
415 break;
416
418#if defined( EESCHEMA )
419 format = wxT( "%.2f" );
420#else
421 format = wxT( "%.4f" );
422#endif
423 break;
424
426#if defined( EESCHEMA )
427 format = wxT( "%.3f" );
428#else
429 format = wxT( "%.5f" );
430#endif
431 break;
432
433 case EDA_UNITS::MILS:
434#if defined( EESCHEMA )
435 format = wxT( "%.0f" );
436#else
437 format = wxT( "%.2f" );
438#endif
439 break;
440
442#if defined( EESCHEMA )
443 format = wxT( "%.3f" );
444#else
445 format = wxT( "%.4f" );
446#endif
447 break;
448
450 // 3 digits in mantissa should be good for rotation in degree
451 format = wxT( "%.3f" );
452 break;
453
455 format = wxT( "%.0f" );
456 break;
457 }
458
459 text.Printf( format, value );
460
461 if( aAddUnitsText )
462 text += EDA_UNIT_UTILS::GetText( aUnits, aType );
463
464 return text;
465}
466
467
469 EDA_UNITS aUnits,
470 const MINOPTMAX<int>& aValue )
471{
472 wxString msg;
473
474 if( aValue.HasMin() && aValue.Min() > 0 )
475 {
476 msg += _( "min" ) + wxS( " " ) + MessageTextFromValue( aIuScale, aUnits, aValue.Min() );
477 }
478
479 if( aValue.HasOpt() )
480 {
481 if( !msg.IsEmpty() )
482 msg += wxS( "; " );
483
484 msg += _( "opt" ) + wxS( " " ) + MessageTextFromValue( aIuScale, aUnits, aValue.Opt() );
485 }
486
487 if( aValue.HasMax() )
488 {
489 if( !msg.IsEmpty() )
490 msg += wxS( "; " );
491
492 msg += _( "max" ) + wxS( " " ) + MessageTextFromValue( aIuScale, aUnits, aValue.Max() );
493 }
494
495 return msg;
496};
497
498
500 double aValue )
501{
502 switch( aUnits )
503 {
505 return MM_TO_IU( aValue / 1000.0, aIuScale );
506
508 return MM_TO_IU( aValue, aIuScale );
509
511 return MM_TO_IU( aValue * 10, aIuScale );
512
513 case EDA_UNITS::MILS:
514 return MILS_TO_IU( aValue, aIuScale );
515
517 return IN_TO_IU( aValue, aIuScale );
518
519 default:
523 return aValue;
524 }
525}
526
527
528double EDA_UNIT_UTILS::UI::DoubleValueFromString( const wxString& aTextValue )
529{
530 double dtmp = 0;
531
532 // Acquire the 'right' decimal point separator
533 const struct lconv* lc = localeconv();
534
535 wxChar decimal_point = lc->decimal_point[0];
536 wxString buf( aTextValue.Strip( wxString::both ) );
537
538 // Convert any entered decimal point separators to the 'right' one
539 buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
540 buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) );
541
542 // Find the end of the numeric part
543 unsigned brk_point = 0;
544
545 while( brk_point < buf.Len() )
546 {
547 wxChar ch = buf[brk_point];
548
549 if( !( ( ch >= '0' && ch <= '9' ) || ( ch == decimal_point ) || ( ch == '-' )
550 || ( ch == '+' ) ) )
551 {
552 break;
553 }
554
555 ++brk_point;
556 }
557
558 // Extract the numeric part
559 buf.Left( brk_point ).ToDouble( &dtmp );
560
561 return dtmp;
562}
563
564
566 const wxString& aTextValue, EDA_DATA_TYPE aType )
567{
568 double dtmp = 0;
569
570 // Acquire the 'right' decimal point separator
571 const struct lconv* lc = localeconv();
572
573 wxChar decimal_point = lc->decimal_point[0];
574 wxString buf( aTextValue.Strip( wxString::both ) );
575
576 // Convert any entered decimal point separators to the 'right' one
577 buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) );
578 buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) );
579
580 // Find the end of the numeric part
581 unsigned brk_point = 0;
582
583 while( brk_point < buf.Len() )
584 {
585 wxChar ch = buf[brk_point];
586
587 if( !( (ch >= '0' && ch <= '9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) )
588 break;
589
590 ++brk_point;
591 }
592
593 // Extract the numeric part
594 buf.Left( brk_point ).ToDouble( &dtmp );
595
596 // Check the optional unit designator (2 ch significant)
597 wxString unit( buf.Mid( brk_point ).Strip( wxString::leading ).Left( 2 ).Lower() );
598
599 if( aUnits == EDA_UNITS::MICROMETRES
600 || aUnits == EDA_UNITS::MILLIMETRES
601 || aUnits == EDA_UNITS::CENTIMETRES
602 || aUnits == EDA_UNITS::MILS
603 || aUnits == EDA_UNITS::INCHES )
604 {
605 //check for um, μm (µ is MICRO SIGN) and µm (µ is GREEK SMALL LETTER MU) for micrometre
606 if( unit == wxT( "um" ) || unit == wxT( "\u00B5m" ) || unit == wxT( "\u03BCm" ) )
607 {
608 aUnits = EDA_UNITS::MICROMETRES;
609 }
610 else if( unit == wxT( "mm" ) )
611 {
612 aUnits = EDA_UNITS::MILLIMETRES;
613 }
614 else if( unit == wxT( "cm" ) )
615 {
616 aUnits = EDA_UNITS::CENTIMETRES;
617 }
618 else if( unit == wxT( "mi" ) || unit == wxT( "th" ) )
619 {
620 aUnits = EDA_UNITS::MILS;
621 }
622 else if( unit == wxT( "in" ) || unit == wxT( "\"" ) )
623 {
624 aUnits = EDA_UNITS::INCHES;
625 }
626 else if( unit == wxT( "oz" ) ) // 1 oz = 1.37 mils
627 {
628 aUnits = EDA_UNITS::MILS;
629 dtmp *= 1.37;
630 }
631 }
632 else if( aUnits == EDA_UNITS::DEGREES )
633 {
634 if( unit == wxT( "ra" ) ) // Radians
635 dtmp *= 180.0f / M_PI;
636 }
637
638 switch( aType )
639 {
641 dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
643
645 dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
647
649 dtmp = FromUserUnit( aIuScale, aUnits, dtmp );
650 break;
651
653 break;
654 }
655
656 return dtmp;
657}
658
659
660long long int EDA_UNIT_UTILS::UI::ValueFromString( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
661 const wxString& aTextValue, EDA_DATA_TYPE aType )
662{
663 double value = DoubleValueFromString( aIuScale, aUnits, aTextValue, aType );
664
665 return KiROUND<double, long long int>( value );
666}
667
668
669long long int EDA_UNIT_UTILS::UI::ValueFromString( const wxString& aTextValue )
670{
671 double value = DoubleValueFromString( aTextValue );
672
673 return KiROUND<double, long long int>( value );
674}
double AsDegrees() const
Definition: eda_angle.h:155
T Min() const
Definition: minoptmax.h:33
bool HasMax() const
Definition: minoptmax.h:38
bool HasMin() const
Definition: minoptmax.h:37
T Max() const
Definition: minoptmax.h:34
T Opt() const
Definition: minoptmax.h:35
bool HasOpt() const
Definition: minoptmax.h:39
#define _(s)
#define IU_TO_IN(x, scale)
Definition: eda_units.cpp:237
#define IN_TO_IU(x, scale)
Definition: eda_units.cpp:240
#define IU_TO_MILS(x, scale)
Definition: eda_units.cpp:238
#define MM_TO_IU(x, scale)
Definition: eda_units.cpp:239
#define IU_TO_MM(x, scale)
Definition: eda_units.cpp:236
#define MILS_TO_IU(x, scale)
Definition: eda_units.cpp:241
EDA_DATA_TYPE
The type of unit.
Definition: eda_units.h:38
EDA_UNITS
Definition: eda_units.h:46
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:96
KICOMMON_API 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:378
KICOMMON_API double FromUserUnit(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnit, double aValue)
Return in internal units the value aValue given in a real unit such as "in", "mm",...
Definition: eda_units.cpp:499
KICOMMON_API 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:660
KICOMMON_API 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:284
KICOMMON_API wxString MessageTextFromMinOptMax(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const MINOPTMAX< int > &aValue)
Definition: eda_units.cpp:468
KICOMMON_API 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:565
KICOMMON_API 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:243
KICOMMON_API 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:110
KICOMMON_API bool FetchUnitsFromString(const wxString &aTextValue, EDA_UNITS &aUnits)
Writes any unit info found in the string to aUnits.
Definition: eda_units.cpp:72
KICOMMON_API bool IsImperialUnit(EDA_UNITS aUnit)
Definition: eda_units.cpp:31
KICOMMON_API bool IsMetricUnit(EDA_UNITS aUnit)
Definition: eda_units.cpp:45
KICOMMON_API 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:139
KICOMMON_API int Mm2mils(double aVal)
Convert mm to mils.
Definition: eda_units.cpp:60
KICOMMON_API 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:153
KICOMMON_API std::string FormatAngle(const EDA_ANGLE &aAngle)
Converts aAngle from board units to a string appropriate for writing to file.
Definition: eda_units.cpp:145
KICOMMON_API int Mils2mm(double aVal)
Convert mils to mm.
Definition: eda_units.cpp:66
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