From 2c102a62e042811097f446ea24a197e03c50304c Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 12 Sep 2025 20:34:54 +0100 Subject: [PATCH] Auto-convert numeric values in fields when referencing them in expressions. Fixes https://gitlab.com/kicad/code/kicad/-/issues/21723 --- common/eda_units.cpp | 98 ++++++++++++++++++++++++++++++++++++ include/eda_units.h | 3 ++ pcbnew/pcbexpr_evaluator.cpp | 39 +++++++------- 3 files changed, 123 insertions(+), 17 deletions(-) diff --git a/common/eda_units.cpp b/common/eda_units.cpp index a835cebcd5..7ea9ef5ece 100644 --- a/common/eda_units.cpp +++ b/common/eda_units.cpp @@ -708,6 +708,104 @@ double EDA_UNIT_UTILS::UI::DoubleValueFromString( const EDA_IU_SCALE& aIuScale, } +bool EDA_UNIT_UTILS::UI::DoubleValueFromString( const EDA_IU_SCALE& aIuScale, const wxString& aTextValue, + double& aDoubleValue ) +{ + double dtmp = 0; + + // Acquire the 'right' decimal point separator + const struct lconv* lc = localeconv(); + + wxChar decimal_point = lc->decimal_point[0]; + wxString buf( aTextValue.Strip( wxString::both ) ); + + // Convert any entered decimal point separators to the 'right' one + buf.Replace( wxT( "." ), wxString( decimal_point, 1 ) ); + buf.Replace( wxT( "," ), wxString( decimal_point, 1 ) ); + + // Find the end of the numeric part + unsigned brk_point = 0; + + while( brk_point < buf.Len() ) + { + wxChar ch = buf[brk_point]; + + if( !( (ch >= '0' && ch <= '9') || (ch == decimal_point) || (ch == '-') || (ch == '+') ) ) + break; + + ++brk_point; + } + + if( brk_point == 0 ) + return false; + + // Extract the numeric part + buf.Left( brk_point ).ToDouble( &dtmp ); + + // Check the unit designator + wxString unit( buf.Mid( brk_point ).Strip( wxString::both ).Lower() ); + EDA_UNITS units; + + //check for um, μm (µ is MICRO SIGN) and µm (µ is GREEK SMALL LETTER MU) for micrometre + if( unit == wxT( "um" ) || unit == wxT( "\u00B5m" ) || unit == wxT( "\u03BCm" ) ) + { + units = EDA_UNITS::UM; + } + else if( unit == wxT( "mm" ) ) + { + units = EDA_UNITS::MM; + } + else if( unit == wxT( "cm" ) ) + { + units = EDA_UNITS::CM; + } + else if( unit == wxT( "mil" ) || unit == wxT( "mils" ) || unit == wxT( "thou" ) ) + { + units = EDA_UNITS::MILS; + } + else if( unit == wxT( "in" ) || unit == wxT( "\"" ) ) + { + units = EDA_UNITS::INCH; + } + else if( unit == wxT( "oz" ) ) // 1 oz = 1.37 mils + { + units = EDA_UNITS::MILS; + dtmp *= 1.37; + } + else if( unit == wxT( "ra" ) ) // Radians + { + dtmp *= 180.0f / M_PI; + } + else if( unit == wxT( "fs" ) ) + { + units = EDA_UNITS::FS; + } + else if( unit == wxT( "ps" ) ) + { + units = EDA_UNITS::PS; + } + else if( unit == wxT( "ps/in" ) ) + { + units = EDA_UNITS::PS_PER_INCH; + } + else if( unit == wxT( "ps/cm" ) ) + { + units = EDA_UNITS::PS_PER_CM; + } + else if( unit == wxT( "ps/mm" ) ) + { + units = EDA_UNITS::PS_PER_MM; + } + else + { + return false; + } + + aDoubleValue = FromUserUnit( aIuScale, units, dtmp ); + return true; +} + + long long int EDA_UNIT_UTILS::UI::ValueFromString( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits, const wxString& aTextValue, EDA_DATA_TYPE aType ) { diff --git a/include/eda_units.h b/include/eda_units.h index d70a5e8c58..5820b2f7d8 100644 --- a/include/eda_units.h +++ b/include/eda_units.h @@ -256,6 +256,9 @@ namespace EDA_UNIT_UTILS KICOMMON_API double DoubleValueFromString( const wxString& aTextValue ); + KICOMMON_API bool DoubleValueFromString( const EDA_IU_SCALE& aIuScale, const wxString& aTextValue, + double& aDoubleValue ); + /** * Convert \a aTextValue in \a aUnits to internal units used by the application. * diff --git a/pcbnew/pcbexpr_evaluator.cpp b/pcbnew/pcbexpr_evaluator.cpp index 0089de2e0a..78b2524b53 100644 --- a/pcbnew/pcbexpr_evaluator.cpp +++ b/pcbnew/pcbexpr_evaluator.cpp @@ -447,28 +447,33 @@ LIBEVAL::VALUE* PCBEXPR_VAR_REF::GetValue( LIBEVAL::CONTEXT* aCtx ) if( it->second->Name() == wxT( "Pin Type" ) ) return new PCBEXPR_PINTYPE_VALUE( str ); - else - return new LIBEVAL::VALUE( str ); + + // If it quacks like a duck, it is a duck + double doubleVal; + + if( EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, str, doubleVal ) ) + return new LIBEVAL::VALUE( doubleVal ); + + return new LIBEVAL::VALUE( str ); } - else + else if( it->second->Name() == wxT( "Layer" ) + || it->second->Name() == wxT( "Layer Top" ) + || it->second->Name() == wxT( "Layer Bottom" ) ) { const wxAny& any = item->Get( it->second ); PCB_LAYER_ID layer; - if( it->second->Name() == wxT( "Layer" ) - || it->second->Name() == wxT( "Layer Top" ) - || it->second->Name() == wxT( "Layer Bottom" ) ) - { - if( any.GetAs( &layer ) ) - return new PCBEXPR_LAYER_VALUE( layer ); - else if( any.GetAs( &str ) ) - return new PCBEXPR_LAYER_VALUE( context->GetBoard()->GetLayerID( str ) ); - } - else - { - if( any.GetAs( &str ) ) - return new LIBEVAL::VALUE( str ); - } + if( any.GetAs( &layer ) ) + return new PCBEXPR_LAYER_VALUE( layer ); + else if( any.GetAs( &str ) ) + return new PCBEXPR_LAYER_VALUE( context->GetBoard()->GetLayerID( str ) ); + } + else + { + const wxAny& any = item->Get( it->second ); + + if( any.GetAs( &str ) ) + return new LIBEVAL::VALUE( str ); } return new LIBEVAL::VALUE();