7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-03-30 05:26:55 +00:00

Use std::optional for pad connection overrides.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/19555
This commit is contained in:
Jeff Young 2025-01-09 17:31:14 +00:00
parent 1ebcd24944
commit dd7c076bc9
16 changed files with 102 additions and 41 deletions

View File

@ -555,9 +555,17 @@ wxString PGPROPERTY_ANGLE::ValueToString( wxVariant& aVariant,
wxString PGPROPERTY_ANGLE::ValueToString( wxVariant& aVariant, int aArgFlags ) const
#endif
{
if( aVariant.GetType() == wxPG_VARIANT_TYPE_DOUBLE )
if( aVariant.GetType() == wxT( "std::optional<double>" ) )
{
auto* variantData = static_cast<STD_OPTIONAL_DOUBLE_VARIANT_DATA*>( aVariant.GetData() );
if( variantData->Value().has_value() )
return wxString::Format( wxS( "%g\u00B0" ), variantData->Value().value() / m_scale );
else
return wxEmptyString;
}
else if( aVariant.GetType() == wxPG_VARIANT_TYPE_DOUBLE )
{
// TODO(JE) Is this still needed?
return wxString::Format( wxS( "%g\u00B0" ), aVariant.GetDouble() / m_scale );
}
else if( aVariant.GetType() == wxS( "EDA_ANGLE" ) )

View File

@ -655,15 +655,20 @@ bool UNIT_BINDER::IsIndeterminate() const
bool UNIT_BINDER::IsNull() const
{
wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl );
if( te )
if( wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl ) )
return te->GetValue().IsEmpty();
return false;
}
void UNIT_BINDER::SetNull()
{
if( wxTextEntry* te = dynamic_cast<wxTextEntry*>( m_valueCtrl ) )
return te->SetValue( wxEmptyString );
}
void UNIT_BINDER::SetLabel( const wxString& aLabel )
{
m_label->SetLabel( aLabel );

View File

@ -112,9 +112,16 @@ public:
int val = 0;
if( aValue.CheckType<int>() )
{
val = aValue.As<int>();
}
else if( aValue.CheckType<std::optional<int>>() )
val = aValue.As<std::optional<int>>().value_or( 0 );
{
if( aValue.As<std::optional<int>>().has_value() )
val = aValue.As<std::optional<int>>().value();
else
return std::nullopt; // no value for a std::optional is always valid
}
if( val > Max )
return std::make_unique<VALIDATION_ERROR_TOO_LARGE<int>>( val, Max );
@ -132,9 +139,16 @@ public:
int val = 0;
if( aValue.CheckType<int>() )
{
val = aValue.As<int>();
}
else if( aValue.CheckType<std::optional<int>>() )
val = aValue.As<std::optional<int>>().value_or( 0 );
{
if( aValue.As<std::optional<int>>().has_value() )
val = aValue.As<std::optional<int>>().value();
else
return std::nullopt; // no value for a std::optional is always valid
}
if( val < 0 )
return std::make_unique<VALIDATION_ERROR_TOO_SMALL<int>>( val, 0 );

View File

@ -157,6 +157,7 @@ public:
* Return true if the control holds no value (ie: empty string, **not** 0).
*/
bool IsNull() const;
void SetNull();
/**
* Validate the control against the given range, informing the user of any errors found.

View File

@ -672,9 +672,17 @@ void DIALOG_PAD_PROPERTIES::initValues()
else
m_pasteMarginRatio.ChangeValue( wxEmptyString );
m_spokeWidth.ChangeValue( m_previewPad->GetThermalSpokeWidth() );
if( m_previewPad->GetLocalThermalSpokeWidthOverride().has_value() )
m_spokeWidth.ChangeValue( m_previewPad->GetLocalThermalSpokeWidthOverride().value() );
else
m_spokeWidth.SetNull();
if( m_previewPad->GetLocalThermalGapOverride().has_value() )
m_thermalGap.ChangeValue( m_previewPad->GetLocalThermalGapOverride().value() );
else
m_thermalGap.SetNull();
m_spokeAngle.ChangeAngleValue( m_previewPad->GetThermalSpokeAngle() );
m_thermalGap.ChangeValue( m_previewPad->GetThermalGap() );
m_pad_orientation.ChangeAngleValue( m_previewPad->GetOrientation() );
m_cbTeardrops->SetValue( m_previewPad->GetTeardropParams().m_Enabled );
@ -1734,9 +1742,13 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
else
aPad->SetLocalSolderPasteMarginRatio( m_pasteMarginRatio.GetDoubleValue() / 100.0 );
aPad->SetThermalSpokeWidth( m_spokeWidth.GetIntValue() );
if( !m_spokeWidth.IsNull() )
aPad->SetLocalThermalSpokeWidthOverride( m_spokeWidth.GetIntValue() );
if( !m_thermalGap.IsNull() )
aPad->SetLocalThermalGapOverride( m_thermalGap.GetIntValue() );
aPad->SetThermalSpokeAngle( m_spokeAngle.GetAngleValue() );
aPad->SetThermalGap( m_thermalGap.GetIntValue() );
// And rotation
aPad->SetOrientation( m_pad_orientation.GetAngleValue() );

View File

@ -233,13 +233,15 @@ bool padHasOverrides( const PAD* a, const PAD* b, REPORTER& aReporter )
REPORT_MSG( _( "%s has zone connection override." ), PAD_DESC( a ) );
}
if( a->GetThermalGap() != b->GetThermalGap() )
if( a->GetLocalThermalGapOverride().has_value()
&& a->GetThermalGap() != b->GetThermalGap() )
{
diff = true;
REPORT_MSG( _( "%s has thermal relief gap override." ), PAD_DESC( a ) );
}
if( a->GetThermalSpokeWidth() != b->GetThermalSpokeWidth() )
if( a->GetLocalThermalSpokeWidthOverride().has_value()
&& a->GetLocalThermalSpokeWidthOverride() != b->GetLocalThermalSpokeWidthOverride() )
{
diff = true;
REPORT_MSG( _( "%s has thermal relief spoke width override." ), PAD_DESC( a ) );

View File

@ -1265,7 +1265,7 @@ int PAD::GetLocalThermalGapOverride( wxString* aSource ) const
if( m_padStack.ThermalGap().has_value() && aSource )
*aSource = _( "pad" );
return m_padStack.ThermalGap().value_or( 0 );
return GetLocalThermalGapOverride().value_or( 0 );
}
@ -1877,9 +1877,9 @@ void PAD::ImportSettingsFrom( const PAD& aMasterPad )
SetLocalSolderPasteMarginRatio( aMasterPad.GetLocalSolderPasteMarginRatio() );
SetLocalZoneConnection( aMasterPad.GetLocalZoneConnection() );
SetThermalSpokeWidth( aMasterPad.GetThermalSpokeWidth() );
SetLocalThermalSpokeWidthOverride( aMasterPad.GetLocalThermalSpokeWidthOverride() );
SetThermalSpokeAngle( aMasterPad.GetThermalSpokeAngle() );
SetThermalGap( aMasterPad.GetThermalGap() );
SetLocalThermalGapOverride( aMasterPad.GetLocalThermalGapOverride() );
SetCustomShapeInZoneOpt( aMasterPad.GetCustomShapeInZoneOpt() );
@ -2720,17 +2720,20 @@ static struct PAD_DESC
constexpr int minZoneWidth = pcbIUScale.mmToIU( ZONE_THICKNESS_MIN_VALUE_MM );
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Spoke Width" ),
&PAD::SetThermalSpokeWidth, &PAD::GetThermalSpokeWidth,
propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>(
_HKI( "Thermal Relief Spoke Width" ),
&PAD::SetLocalThermalSpokeWidthOverride, &PAD::GetLocalThermalSpokeWidthOverride,
PROPERTY_DISPLAY::PT_SIZE ), groupOverrides )
.SetValidator( PROPERTY_VALIDATORS::RangeIntValidator<minZoneWidth, INT_MAX> );
propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Thermal Relief Spoke Angle" ),
propMgr.AddProperty( new PROPERTY<PAD, double>(
_HKI( "Thermal Relief Spoke Angle" ),
&PAD::SetThermalSpokeAngleDegrees, &PAD::GetThermalSpokeAngleDegrees,
PROPERTY_DISPLAY::PT_DEGREE ), groupOverrides );
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Gap" ),
&PAD::SetThermalGap, &PAD::GetThermalGap,
propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>(
_HKI( "Thermal Relief Gap" ),
&PAD::SetLocalThermalGapOverride, &PAD::GetLocalThermalGapOverride,
PROPERTY_DISPLAY::PT_SIZE ), groupOverrides )
.SetValidator( PROPERTY_VALIDATORS::PositiveIntValidator );

View File

@ -594,8 +594,14 @@ public:
* Set the width of the thermal spokes connecting the pad to a zone. If != 0 this will
* override similar settings in the parent footprint and zone.
*/
void SetThermalSpokeWidth( int aWidth ) { m_padStack.ThermalSpokeWidth() = aWidth; }
int GetThermalSpokeWidth() const { return m_padStack.ThermalSpokeWidth().value_or( 0 ); }
void SetLocalThermalSpokeWidthOverride( std::optional<int> aWidth )
{
m_padStack.ThermalSpokeWidth() = aWidth;
}
std::optional<int> GetLocalThermalSpokeWidthOverride() const
{
return m_padStack.ThermalSpokeWidth();
}
int GetLocalSpokeWidthOverride( wxString* aSource = nullptr ) const;
@ -626,7 +632,16 @@ public:
void SetThermalGap( int aGap ) { m_padStack.ThermalGap() = aGap; }
int GetThermalGap() const { return m_padStack.ThermalGap().value_or( 0 ); }
int GetLocalThermalGapOverride( wxString* aSource = nullptr ) const;
int GetLocalThermalGapOverride( wxString* aSource ) const;
std::optional<int> GetLocalThermalGapOverride() const
{
return m_padStack.ThermalGap();
}
void SetLocalThermalGapOverride( const std::optional<int>& aOverride )
{
m_padStack.ThermalGap() = aOverride;
}
/**
* Has meaning only for rounded rectangle pads.

View File

@ -40,9 +40,9 @@ PADSTACK::PADSTACK( BOARD_ITEM* aParent ) :
{
m_copperProps[PADSTACK::ALL_LAYERS].shape = SHAPE_PROPS();
m_copperProps[PADSTACK::ALL_LAYERS].zone_connection = ZONE_CONNECTION::INHERITED;
m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_width = 0;
m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_width = std::nullopt;
m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_angle = ANGLE_45;
m_copperProps[PADSTACK::ALL_LAYERS].thermal_gap = 0;
m_copperProps[PADSTACK::ALL_LAYERS].thermal_gap = std::nullopt;
m_drill.shape = PAD_DRILL_SHAPE::CIRCLE;
m_drill.start = F_Cu;
@ -247,8 +247,8 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
else
{
CopperLayer( ALL_LAYERS ).zone_connection = ZONE_CONNECTION::INHERITED;
CopperLayer( ALL_LAYERS ).thermal_gap = 0;
CopperLayer( ALL_LAYERS ).thermal_spoke_width = 0;
CopperLayer( ALL_LAYERS ).thermal_gap = std::nullopt;
CopperLayer( ALL_LAYERS ).thermal_spoke_width = std::nullopt;
CopperLayer( ALL_LAYERS ).thermal_spoke_angle = DefaultThermalSpokeAngleForShape( F_Cu );
}

View File

@ -1236,7 +1236,7 @@ PAD* CADSTAR_PCB_ARCHIVE_LOADER::getKiCadPad( const COMPONENT_PAD& aCadstarPad,
pad->SetThermalGap( getKiCadLength( csPadcode.ReliefClearance ) );
if( csPadcode.ReliefWidth != UNDEFINED_VALUE )
pad->SetThermalSpokeWidth( getKiCadLength( csPadcode.ReliefWidth ) );
pad->SetLocalThermalSpokeWidthOverride( getKiCadLength( csPadcode.ReliefWidth ) );
if( csPadcode.DrillDiameter != UNDEFINED_VALUE )
{

View File

@ -1567,12 +1567,12 @@ void PCB_IO_KICAD_LEGACY::loadPAD( FOOTPRINT* aFootprint )
else if( TESTLINE( ".ThermalWidth" ) )
{
BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) );
pad->SetThermalSpokeWidth( tmp );
pad->SetLocalThermalSpokeWidthOverride( tmp );
}
else if( TESTLINE( ".ThermalGap" ) )
{
BIU tmp = biuParse( line + SZ( ".ThermalGap" ) );
pad->SetThermalGap( tmp );
pad->SetLocalThermalGapOverride( tmp );
}
else if( TESTLINE( "$EndPAD" ) )
{

View File

@ -1668,10 +1668,10 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
static_cast<int>( aPad->GetLocalZoneConnection() ) );
}
if( aPad->GetThermalSpokeWidth() != 0 )
if( aPad->GetLocalThermalSpokeWidthOverride().has_value() )
{
m_out->Print( "(thermal_bridge_width %s)",
formatInternalUnits( aPad->GetThermalSpokeWidth() ).c_str() );
formatInternalUnits( aPad->GetLocalThermalSpokeWidthOverride().value() ).c_str() );
}
EDA_ANGLE defaultThermalSpokeAngle = ANGLE_90;
@ -1689,10 +1689,10 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
EDA_UNIT_UTILS::FormatAngle( aPad->GetThermalSpokeAngle() ).c_str() );
}
if( aPad->GetThermalGap() != 0 )
if( aPad->GetLocalThermalGapOverride().has_value() )
{
m_out->Print( "(thermal_gap %s)",
formatInternalUnits( aPad->GetThermalGap() ).c_str() );
formatInternalUnits( aPad->GetLocalThermalGapOverride().value() ).c_str() );
}
auto anchorShape =

View File

@ -5235,7 +5235,7 @@ PAD* PCB_IO_KICAD_SEXPR_PARSER::parsePAD( FOOTPRINT* aParent )
case T_thermal_width: // legacy token
case T_thermal_bridge_width:
pad->SetThermalSpokeWidth( parseBoardUnits( token ) );
pad->SetLocalThermalSpokeWidthOverride( parseBoardUnits( token ) );
NeedRIGHT();
break;

View File

@ -550,8 +550,8 @@ void PCB_SHAPE::SetIsProxyItem( bool aIsProxy )
{
if( GetShape() == SHAPE_T::SEGMENT )
{
if( parentPad && parentPad->GetThermalSpokeWidth() )
SetWidth( parentPad->GetThermalSpokeWidth() );
if( parentPad && parentPad->GetLocalThermalSpokeWidthOverride().has_value() )
SetWidth( parentPad->GetLocalThermalSpokeWidthOverride().value() );
else
SetWidth( pcbIUScale.mmToIU( ZONE_THERMAL_RELIEF_COPPER_WIDTH_MM ) );
}

View File

@ -896,8 +896,8 @@ void PAD_TOOL::explodePad( PAD* aPad, PCB_LAYER_ID* aLayer, BOARD_COMMIT& aCommi
if( shape->IsProxyItem() && shape->GetShape() == SHAPE_T::SEGMENT )
{
if( aPad->GetThermalSpokeWidth() )
shape->SetWidth( aPad->GetThermalSpokeWidth() );
if( aPad->GetLocalThermalSpokeWidthOverride().has_value() )
shape->SetWidth( aPad->GetLocalThermalSpokeWidthOverride().value() );
else
shape->SetWidth( pcbIUScale.mmToIU( ZONE_THERMAL_RELIEF_COPPER_WIDTH_MM ) );
}

View File

@ -367,7 +367,8 @@ void CheckFpPad( const PAD* expected, const PAD* pad )
BOOST_CHECK_EQUAL( expected->GetLocalClearance().value_or( 0 ),
pad->GetLocalClearance().value_or( 0 ) );
CHECK_ENUM_CLASS_EQUAL( expected->GetLocalZoneConnection(), pad->GetLocalZoneConnection() );
BOOST_CHECK_EQUAL( expected->GetThermalSpokeWidth(), pad->GetThermalSpokeWidth() );
BOOST_CHECK_EQUAL( expected->GetLocalThermalSpokeWidthOverride().value_or( 0 ),
pad->GetLocalThermalSpokeWidthOverride().value_or( 0 ) );
BOOST_CHECK_EQUAL( expected->GetThermalSpokeAngle(), pad->GetThermalSpokeAngle() );
BOOST_CHECK_EQUAL( expected->GetThermalGap(), pad->GetThermalGap() );
BOOST_CHECK_EQUAL( expected->GetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS ),