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