Hook up zone-pad connections to custom rules.

ADDED zone_connection constraint.
ADDED thermal_relief_gap and thermal_spoke_width constraints.

ADDED angle override for thermal relief spokes in Pad Properties.

Fixes https://gitlab.com/kicad/code/kicad/issues/4067
This commit is contained in:
Jeff Young 2021-08-08 14:37:14 +01:00
parent 28b279cb2d
commit 32721755bf
34 changed files with 21888 additions and 21883 deletions

View File

@ -21,6 +21,7 @@ length
max max
micro_via micro_via
min min
none
npth npth
opt opt
outer outer
@ -29,9 +30,13 @@ pth
rule rule
silk_clearance silk_clearance
skew skew
solid
text text
text_height text_height
text_thickness text_thickness
thermal_reliefs
thermal_relief_gap
thermal_spoke_width
track track
track_width track_width
version version
@ -39,3 +44,4 @@ via
via_count via_count
via_diameter via_diameter
zone zone
zone_connection

View File

@ -267,6 +267,7 @@ text_frame
text_position_mode text_position_mode
thermal_width thermal_width
thermal_gap thermal_gap
thermal_bridge_angle
thermal_bridge_width thermal_bridge_width
thickness thickness
through_hole through_hole

View File

@ -243,6 +243,7 @@ set( PCBNEW_DRC_SRCS
drc/drc_test_provider_misc.cpp drc/drc_test_provider_misc.cpp
drc/drc_test_provider_text_dims.cpp drc/drc_test_provider_text_dims.cpp
drc/drc_test_provider_track_width.cpp drc/drc_test_provider_track_width.cpp
drc/drc_test_provider_zone_connections.cpp
drc/drc_test_provider_via_diameter.cpp drc/drc_test_provider_via_diameter.cpp
drc/drc_test_provider_silk_to_mask.cpp drc/drc_test_provider_silk_to_mask.cpp
drc/drc_test_provider_silk_clearance.cpp drc/drc_test_provider_silk_clearance.cpp

View File

@ -136,8 +136,9 @@ DIALOG_PAD_PROPERTIES::DIALOG_PAD_PROPERTIES( PCB_BASE_FRAME* aParent, PAD* aPad
m_pasteMargin( aParent, m_pasteMarginLabel, m_pasteMarginCtrl, m_pasteMarginUnits ), m_pasteMargin( aParent, m_pasteMarginLabel, m_pasteMarginCtrl, m_pasteMarginUnits ),
m_pasteMarginRatio( aParent, m_pasteMarginRatioLabel, m_pasteMarginRatioCtrl, m_pasteMarginRatio( aParent, m_pasteMarginRatioLabel, m_pasteMarginRatioCtrl,
m_pasteMarginRatioUnits ), m_pasteMarginRatioUnits ),
m_spokeWidth( aParent, m_spokeWidthLabel, m_spokeWidthCtrl, m_spokeWidthUnits ),
m_thermalGap( aParent, m_thermalGapLabel, m_thermalGapCtrl, m_thermalGapUnits ), m_thermalGap( aParent, m_thermalGapLabel, m_thermalGapCtrl, m_thermalGapUnits ),
m_spokeWidth( aParent, m_spokeWidthLabel, m_spokeWidthCtrl, m_spokeWidthUnits ),
m_spokeAngle( aParent, m_spokeAngleLabel, m_spokeAngleCtrl, m_spokeAngleUnits ),
m_pad_orientation( aParent, m_PadOrientText, m_cb_padrotation, m_orientationUnits ) m_pad_orientation( aParent, m_PadOrientText, m_cb_padrotation, m_orientationUnits )
{ {
SetName( PAD_PROPERTIES_DLG_NAME ); SetName( PAD_PROPERTIES_DLG_NAME );
@ -194,6 +195,8 @@ DIALOG_PAD_PROPERTIES::DIALOG_PAD_PROPERTIES( PCB_BASE_FRAME* aParent, PAD* aPad
m_pad_orientation.SetUnits( EDA_UNITS::DEGREES ); m_pad_orientation.SetUnits( EDA_UNITS::DEGREES );
m_pad_orientation.SetPrecision( 3 ); m_pad_orientation.SetPrecision( 3 );
m_spokeAngle.SetUnits( EDA_UNITS::DEGREES );
m_pasteMargin.SetNegativeZero(); m_pasteMargin.SetNegativeZero();
m_pasteMarginRatio.SetUnits( EDA_UNITS::PERCENT ); m_pasteMarginRatio.SetUnits( EDA_UNITS::PERCENT );
@ -558,6 +561,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
m_clearance.ChangeValue( m_dummyPad->GetLocalClearance() ); m_clearance.ChangeValue( m_dummyPad->GetLocalClearance() );
m_maskMargin.ChangeValue( m_dummyPad->GetLocalSolderMaskMargin() ); m_maskMargin.ChangeValue( m_dummyPad->GetLocalSolderMaskMargin() );
m_spokeWidth.ChangeValue( m_dummyPad->GetThermalSpokeWidth() ); m_spokeWidth.ChangeValue( m_dummyPad->GetThermalSpokeWidth() );
m_spokeAngle.ChangeDoubleValue( m_dummyPad->GetThermalSpokeAngle() );
m_thermalGap.ChangeValue( m_dummyPad->GetThermalGap() ); m_thermalGap.ChangeValue( m_dummyPad->GetThermalGap() );
m_pasteMargin.ChangeValue( m_dummyPad->GetLocalSolderPasteMargin() ); m_pasteMargin.ChangeValue( m_dummyPad->GetLocalSolderPasteMargin() );
m_pasteMarginRatio.ChangeDoubleValue( m_dummyPad->GetLocalSolderPasteMarginRatio() * 100.0 ); m_pasteMarginRatio.ChangeDoubleValue( m_dummyPad->GetLocalSolderPasteMarginRatio() * 100.0 );
@ -802,7 +806,7 @@ void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
// A reasonable default (from IPC-7351C) // A reasonable default (from IPC-7351C)
if( m_dummyPad->GetRoundRectRadiusRatio() == 0.0 ) if( m_dummyPad->GetRoundRectRadiusRatio() == 0.0 )
m_cornerRatio.ChangeValue( 25 ); m_cornerRatio.ChangeDoubleValue( 25.0 );
break; break;
} }
@ -854,6 +858,19 @@ void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
break; break;
} }
// Note: must do this before enabling/disabling m_sizeY as we're using that as a flag to see
// what the last shape was.
if( m_PadShapeSelector->GetSelection() == CHOICE_SHAPE_CIRCLE )
{
if( m_sizeYCtrl->IsEnabled() && m_spokeAngle.GetDoubleValue() == 900.0 )
m_spokeAngle.SetDoubleValue( 450.0 );
}
else
{
if( !m_sizeYCtrl->IsEnabled() && m_spokeAngle.GetDoubleValue() == 450.0 )
m_spokeAngle.SetDoubleValue( 900.0 );
}
// Readjust props book size // Readjust props book size
wxSize size = m_shapePropsBook->GetSize(); wxSize size = m_shapePropsBook->GetSize();
size.y = m_shapePropsBook->GetPage( m_shapePropsBook->GetSelection() )->GetBestSize().y; size.y = m_shapePropsBook->GetPage( m_shapePropsBook->GetSelection() )->GetBestSize().y;
@ -877,8 +894,6 @@ void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
|| m_PadShapeSelector->GetSelection() == CHOICE_SHAPE_CUSTOM_RECT_ANCHOR; || m_PadShapeSelector->GetSelection() == CHOICE_SHAPE_CUSTOM_RECT_ANCHOR;
enablePrimitivePage( is_custom ); enablePrimitivePage( is_custom );
m_staticTextcps->Enable( is_custom );
m_ZoneCustomPadShape->Enable( is_custom );
transferDataToPad( m_dummyPad ); transferDataToPad( m_dummyPad );
@ -1396,7 +1411,7 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK()
if( m_dummyPad->GetShape() == PAD_SHAPE::ROUNDRECT || if( m_dummyPad->GetShape() == PAD_SHAPE::ROUNDRECT ||
m_dummyPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT ) m_dummyPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT )
{ {
wxASSERT( m_cornerRatio.GetValue() == m_mixedCornerRatio.GetValue() ); wxASSERT( m_cornerRatio.GetDoubleValue() == m_mixedCornerRatio.GetDoubleValue() );
if( m_cornerRatio.GetDoubleValue() < 0.0 ) if( m_cornerRatio.GetDoubleValue() < 0.0 )
error_msgs.Add( _( "Error: Negative corner size." ) ); error_msgs.Add( _( "Error: Negative corner size." ) );
@ -1607,6 +1622,7 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
m_currentPad->SetLocalSolderPasteMargin( m_padMaster->GetLocalSolderPasteMargin() ); m_currentPad->SetLocalSolderPasteMargin( m_padMaster->GetLocalSolderPasteMargin() );
m_currentPad->SetLocalSolderPasteMarginRatio( m_padMaster->GetLocalSolderPasteMarginRatio() ); m_currentPad->SetLocalSolderPasteMarginRatio( m_padMaster->GetLocalSolderPasteMarginRatio() );
m_currentPad->SetThermalSpokeWidth( m_padMaster->GetThermalSpokeWidth() ); m_currentPad->SetThermalSpokeWidth( m_padMaster->GetThermalSpokeWidth() );
m_currentPad->SetThermalSpokeAngle( m_padMaster->GetThermalSpokeAngle() );
m_currentPad->SetThermalGap( m_padMaster->GetThermalGap() ); m_currentPad->SetThermalGap( m_padMaster->GetThermalGap() );
m_currentPad->SetRoundRectRadiusRatio( m_padMaster->GetRoundRectRadiusRatio() ); m_currentPad->SetRoundRectRadiusRatio( m_padMaster->GetRoundRectRadiusRatio() );
m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() ); m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() );
@ -1706,6 +1722,7 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
aPad->SetLocalSolderPasteMargin( m_pasteMargin.GetValue() ); aPad->SetLocalSolderPasteMargin( m_pasteMargin.GetValue() );
aPad->SetLocalSolderPasteMarginRatio( m_pasteMarginRatio.GetDoubleValue() / 100.0 ); aPad->SetLocalSolderPasteMarginRatio( m_pasteMarginRatio.GetDoubleValue() / 100.0 );
aPad->SetThermalSpokeWidth( m_spokeWidth.GetValue() ); aPad->SetThermalSpokeWidth( m_spokeWidth.GetValue() );
aPad->SetThermalSpokeAngle( m_spokeAngle.GetDoubleValue() );
aPad->SetThermalGap( m_thermalGap.GetValue() ); aPad->SetThermalGap( m_thermalGap.GetValue() );
// And rotation // And rotation
@ -1832,13 +1849,15 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
// reference // reference
if( aPad->GetAnchorPadShape() == PAD_SHAPE::CIRCLE ) if( aPad->GetAnchorPadShape() == PAD_SHAPE::CIRCLE )
aPad->SetSize( wxSize( m_sizeX.GetValue(), m_sizeX.GetValue() ) ); aPad->SetSize( wxSize( m_sizeX.GetValue(), m_sizeX.GetValue() ) );
// define the way the clearance area is defined in zones
aPad->SetCustomShapeInZoneOpt( m_ZoneCustomPadShape->GetSelection() == 0 ?
CUST_PAD_SHAPE_IN_ZONE_OUTLINE :
CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL );
} }
// Define the way the clearance area is defined in zones. Since all non-custom pad
// shapes are convex to begin with, this really only makes any difference for custom
// pad shapes.
aPad->SetCustomShapeInZoneOpt( m_ZoneCustomPadShape->GetSelection() == 0 ?
CUST_PAD_SHAPE_IN_ZONE_OUTLINE :
CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL );
switch( aPad->GetAttribute() ) switch( aPad->GetAttribute() )
{ {
case PAD_ATTRIB::PTH: case PAD_ATTRIB::PTH:

View File

@ -156,12 +156,16 @@ private:
UNIT_BINDER m_cornerRadius; UNIT_BINDER m_cornerRadius;
UNIT_BINDER m_cornerRatio; UNIT_BINDER m_cornerRatio;
UNIT_BINDER m_chamferRatio; UNIT_BINDER m_chamferRatio;
UNIT_BINDER m_mixedCornerRatio, m_mixedChamferRatio; UNIT_BINDER m_mixedCornerRatio;
UNIT_BINDER m_mixedChamferRatio;
UNIT_BINDER m_holeX, m_holeY; UNIT_BINDER m_holeX, m_holeY;
UNIT_BINDER m_clearance; UNIT_BINDER m_clearance;
UNIT_BINDER m_maskMargin; UNIT_BINDER m_maskMargin;
UNIT_BINDER m_pasteMargin, m_pasteMarginRatio; UNIT_BINDER m_pasteMargin;
UNIT_BINDER m_spokeWidth, m_thermalGap; UNIT_BINDER m_pasteMarginRatio;
UNIT_BINDER m_thermalGap;
UNIT_BINDER m_spokeWidth;
UNIT_BINDER m_spokeAngle;
UNIT_BINDER m_pad_orientation; UNIT_BINDER m_pad_orientation;
}; };

View File

@ -356,7 +356,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_LeftBoxSizer->Add( 0, 0, 1, wxEXPAND, 5 ); m_LeftBoxSizer->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticline6 = new wxStaticLine( m_panelGeneral, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); m_staticline6 = new wxStaticLine( m_panelGeneral, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
m_LeftBoxSizer->Add( m_staticline6, 0, wxBOTTOM|wxEXPAND|wxTOP, 5 ); m_LeftBoxSizer->Add( m_staticline6, 0, wxEXPAND|wxBOTTOM, 5 );
wxGridBagSizer* gbSizerHole; wxGridBagSizer* gbSizerHole;
gbSizerHole = new wxGridBagSizer( 4, 0 ); gbSizerHole = new wxGridBagSizer( 4, 0 );
@ -579,20 +579,20 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
bSizerClearance = new wxBoxSizer( wxVERTICAL ); bSizerClearance = new wxBoxSizer( wxVERTICAL );
wxStaticBoxSizer* sbClearancesSizer; wxStaticBoxSizer* sbClearancesSizer;
sbClearancesSizer = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Clearances") ), wxVERTICAL ); sbClearancesSizer = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Clearance Overrides") ), wxVERTICAL );
wxStaticText* m_staticTextHint; wxStaticText* m_staticTextHint;
m_staticTextHint = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Set values to 0 to use parent footprint or netclass values."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextHint = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Set values to 0 to use parent footprint or netclass values."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextHint->Wrap( -1 ); m_staticTextHint->Wrap( -1 );
sbClearancesSizer->Add( m_staticTextHint, 0, wxLEFT|wxRIGHT, 5 ); sbClearancesSizer->Add( m_staticTextHint, 0, wxRIGHT, 10 );
m_staticTextInfoPosValue = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Positive clearance means area bigger than the pad (usual for mask clearance)."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextInfoPosValue = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Positive clearance means area bigger than the pad (usual for mask clearance)."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextInfoPosValue->Wrap( -1 ); m_staticTextInfoPosValue->Wrap( -1 );
sbClearancesSizer->Add( m_staticTextInfoPosValue, 0, wxLEFT|wxRIGHT|wxTOP, 5 ); sbClearancesSizer->Add( m_staticTextInfoPosValue, 0, wxTOP|wxRIGHT, 10 );
m_staticTextInfoNegVal = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Negative clearance means area smaller than the pad (usual for paste clearance)."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextInfoNegVal = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Negative clearance means area smaller than the pad (usual for paste clearance)."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextInfoNegVal->Wrap( -1 ); m_staticTextInfoNegVal->Wrap( -1 );
sbClearancesSizer->Add( m_staticTextInfoNegVal, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); sbClearancesSizer->Add( m_staticTextInfoNegVal, 0, wxBOTTOM|wxRIGHT, 10 );
wxFlexGridSizer* fgClearancesGridSizer; wxFlexGridSizer* fgClearancesGridSizer;
fgClearancesGridSizer = new wxFlexGridSizer( 4, 3, 0, 0 ); fgClearancesGridSizer = new wxFlexGridSizer( 4, 3, 0, 0 );
@ -604,12 +604,12 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_clearanceLabel->Wrap( -1 ); m_clearanceLabel->Wrap( -1 );
m_clearanceLabel->SetToolTip( _("This is the local net clearance for this pad.\nIf 0, the footprint local value or the Netclass value is used.") ); m_clearanceLabel->SetToolTip( _("This is the local net clearance for this pad.\nIf 0, the footprint local value or the Netclass value is used.") );
fgClearancesGridSizer->Add( m_clearanceLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); fgClearancesGridSizer->Add( m_clearanceLabel, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_clearanceCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_clearanceCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgClearancesGridSizer->Add( m_clearanceCtrl, 0, wxEXPAND|wxTOP|wxLEFT, 5 ); fgClearancesGridSizer->Add( m_clearanceCtrl, 0, wxEXPAND|wxTOP|wxLEFT, 5 );
m_clearanceUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 ); m_clearanceUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_clearanceUnits->Wrap( -1 ); m_clearanceUnits->Wrap( -1 );
fgClearancesGridSizer->Add( m_clearanceUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 ); fgClearancesGridSizer->Add( m_clearanceUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 );
@ -617,12 +617,12 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_maskMarginLabel->Wrap( -1 ); m_maskMarginLabel->Wrap( -1 );
m_maskMarginLabel->SetToolTip( _("This is the local clearance between this pad and the solder mask.\nIf 0, the footprint local value or the global value is used.") ); m_maskMarginLabel->SetToolTip( _("This is the local clearance between this pad and the solder mask.\nIf 0, the footprint local value or the global value is used.") );
fgClearancesGridSizer->Add( m_maskMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_maskMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP, 5 );
m_maskMarginCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_maskMarginCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgClearancesGridSizer->Add( m_maskMarginCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_maskMarginCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 );
m_maskMarginUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 ); m_maskMarginUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_maskMarginUnits->Wrap( -1 ); m_maskMarginUnits->Wrap( -1 );
fgClearancesGridSizer->Add( m_maskMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_maskMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
@ -630,12 +630,12 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_pasteMarginLabel->Wrap( -1 ); m_pasteMarginLabel->Wrap( -1 );
m_pasteMarginLabel->SetToolTip( _("This is the local clearance between this pad and the solder paste.\nIf 0, the footprint value or the global value is used.\nThe final clearance value is the sum of this value and the clearance value ratio.\nA negative value means a smaller mask size than pad size.") ); m_pasteMarginLabel->SetToolTip( _("This is the local clearance between this pad and the solder paste.\nIf 0, the footprint value or the global value is used.\nThe final clearance value is the sum of this value and the clearance value ratio.\nA negative value means a smaller mask size than pad size.") );
fgClearancesGridSizer->Add( m_pasteMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_pasteMarginLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP, 5 );
m_pasteMarginCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginCtrl = new wxTextCtrl( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgClearancesGridSizer->Add( m_pasteMarginCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_pasteMarginCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 );
m_pasteMarginUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginUnits = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_pasteMarginUnits->Wrap( -1 ); m_pasteMarginUnits->Wrap( -1 );
fgClearancesGridSizer->Add( m_pasteMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_pasteMarginUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
@ -643,7 +643,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_pasteMarginRatioLabel->Wrap( -1 ); m_pasteMarginRatioLabel->Wrap( -1 );
m_pasteMarginRatioLabel->SetToolTip( _("This is the local clearance ratio in percent between this pad and the solder paste.\nA value of 10 means the clearance value is 10 percent of the pad size.\nIf 0, the footprint value or the global value is used.\nThe final clearance value is the sum of this value and the clearance value.\nA negative value means a smaller mask size than pad size.") ); m_pasteMarginRatioLabel->SetToolTip( _("This is the local clearance ratio in percent between this pad and the solder paste.\nA value of 10 means the clearance value is 10 percent of the pad size.\nIf 0, the footprint value or the global value is used.\nThe final clearance value is the sum of this value and the clearance value.\nA negative value means a smaller mask size than pad size.") );
fgClearancesGridSizer->Add( m_pasteMarginRatioLabel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_pasteMarginRatioLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 );
m_pasteMarginRatioCtrl = new TEXT_CTRL_EVAL( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_pasteMarginRatioCtrl = new TEXT_CTRL_EVAL( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgClearancesGridSizer->Add( m_pasteMarginRatioCtrl, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxTOP, 5 ); fgClearancesGridSizer->Add( m_pasteMarginRatioCtrl, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxTOP, 5 );
@ -653,7 +653,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
fgClearancesGridSizer->Add( m_pasteMarginRatioUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); fgClearancesGridSizer->Add( m_pasteMarginRatioUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
sbClearancesSizer->Add( fgClearancesGridSizer, 0, wxEXPAND, 5 ); sbClearancesSizer->Add( fgClearancesGridSizer, 0, 0, 5 );
m_nonCopperWarningBook = new wxSimplebook( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); m_nonCopperWarningBook = new wxSimplebook( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
wxPanel* notePanel; wxPanel* notePanel;
@ -663,11 +663,11 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_nonCopperNote = new wxStaticText( notePanel, wxID_ANY, _("Note: solder mask and paste values are used only for pads on copper layers."), wxDefaultPosition, wxDefaultSize, 0 ); m_nonCopperNote = new wxStaticText( notePanel, wxID_ANY, _("Note: solder mask and paste values are used only for pads on copper layers."), wxDefaultPosition, wxDefaultSize, 0 );
m_nonCopperNote->Wrap( -1 ); m_nonCopperNote->Wrap( -1 );
bNoteSizer->Add( m_nonCopperNote, 0, wxLEFT|wxRIGHT|wxTOP, 5 ); bNoteSizer->Add( m_nonCopperNote, 0, wxTOP|wxRIGHT, 5 );
m_staticTextInfoPaste = new wxStaticText( notePanel, wxID_ANY, _("Note: solder paste clearances (absolute and relative) are added to determine the final clearance."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextInfoPaste = new wxStaticText( notePanel, wxID_ANY, _("Note: solder paste clearances (absolute and relative) are added to determine the final clearance."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextInfoPaste->Wrap( -1 ); m_staticTextInfoPaste->Wrap( -1 );
bNoteSizer->Add( m_staticTextInfoPaste, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); bNoteSizer->Add( m_staticTextInfoPaste, 0, wxBOTTOM|wxRIGHT, 5 );
notePanel->SetSizer( bNoteSizer ); notePanel->SetSizer( bNoteSizer );
@ -700,64 +700,93 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
bSizerClearance->Add( sbClearancesSizer, 0, wxALL|wxEXPAND, 5 ); bSizerClearance->Add( sbClearancesSizer, 0, wxALL|wxEXPAND, 5 );
wxBoxSizer* bSizerLower;
bSizerLower = new wxBoxSizer( wxHORIZONTAL );
m_sbSizerZonesSettings = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Connection to Copper Zones") ), wxVERTICAL ); m_sbSizerZonesSettings = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Connection to Copper Zones") ), wxVERTICAL );
wxFlexGridSizer* fgSizerCopperZonesOpts; wxFlexGridSizer* fgSizerCopperZonesOpts;
fgSizerCopperZonesOpts = new wxFlexGridSizer( 0, 3, 0, 0 ); fgSizerCopperZonesOpts = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizerCopperZonesOpts->AddGrowableCol( 1 ); fgSizerCopperZonesOpts->AddGrowableCol( 1 );
fgSizerCopperZonesOpts->SetFlexibleDirection( wxBOTH ); fgSizerCopperZonesOpts->SetFlexibleDirection( wxBOTH );
fgSizerCopperZonesOpts->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); fgSizerCopperZonesOpts->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText40 = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Pad connection:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText40 = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Pad connection:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText40->Wrap( -1 ); m_staticText40->Wrap( -1 );
fgSizerCopperZonesOpts->Add( m_staticText40, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); fgSizerCopperZonesOpts->Add( m_staticText40, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM, 5 );
wxString m_ZoneConnectionChoiceChoices[] = { _("From parent footprint"), _("Solid"), _("Thermal relief"), _("None") }; wxString m_ZoneConnectionChoiceChoices[] = { _("From parent footprint"), _("Solid"), _("Thermal relief"), _("None") };
int m_ZoneConnectionChoiceNChoices = sizeof( m_ZoneConnectionChoiceChoices ) / sizeof( wxString ); int m_ZoneConnectionChoiceNChoices = sizeof( m_ZoneConnectionChoiceChoices ) / sizeof( wxString );
m_ZoneConnectionChoice = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneConnectionChoiceNChoices, m_ZoneConnectionChoiceChoices, 0 ); m_ZoneConnectionChoice = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneConnectionChoiceNChoices, m_ZoneConnectionChoiceChoices, 0 );
m_ZoneConnectionChoice->SetSelection( 0 ); m_ZoneConnectionChoice->SetSelection( 0 );
fgSizerCopperZonesOpts->Add( m_ZoneConnectionChoice, 0, wxEXPAND|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); fgSizerCopperZonesOpts->Add( m_ZoneConnectionChoice, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT, 5 );
m_staticTextcps = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Zone knockout:"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerCopperZonesOpts->Add( 0, 0, 1, wxEXPAND, 5 );
m_spokeWidthLabel = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Thermal relief spoke width:"), wxDefaultPosition, wxDefaultSize, 0 );
m_spokeWidthLabel->Wrap( -1 );
fgSizerCopperZonesOpts->Add( m_spokeWidthLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 );
m_spokeWidthCtrl = new wxTextCtrl( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerCopperZonesOpts->Add( m_spokeWidthCtrl, 0, wxEXPAND|wxLEFT|wxTOP|wxALIGN_CENTER_VERTICAL, 5 );
m_spokeWidthUnits = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 );
m_spokeWidthUnits->Wrap( -1 );
fgSizerCopperZonesOpts->Add( m_spokeWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
m_thermalGapLabel = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Thermal relief gap:"), wxDefaultPosition, wxDefaultSize, 0 );
m_thermalGapLabel->Wrap( -1 );
fgSizerCopperZonesOpts->Add( m_thermalGapLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 );
m_thermalGapCtrl = new wxTextCtrl( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerCopperZonesOpts->Add( m_thermalGapCtrl, 0, wxEXPAND|wxTOP|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
m_thermalGapUnits = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 );
m_thermalGapUnits->Wrap( -1 );
fgSizerCopperZonesOpts->Add( m_thermalGapUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5 );
m_staticTextcps = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Custom pad shape in zone:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextcps->Wrap( -1 ); m_staticTextcps->Wrap( -1 );
fgSizerCopperZonesOpts->Add( m_staticTextcps, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxRIGHT|wxTOP, 5 ); fgSizerCopperZonesOpts->Add( m_staticTextcps, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 );
wxString m_ZoneCustomPadShapeChoices[] = { _("Use pad shape"), _("Use pad convex hull") }; wxString m_ZoneCustomPadShapeChoices[] = { _("Pad shape"), _("Pad convex hull") };
int m_ZoneCustomPadShapeNChoices = sizeof( m_ZoneCustomPadShapeChoices ) / sizeof( wxString ); int m_ZoneCustomPadShapeNChoices = sizeof( m_ZoneCustomPadShapeChoices ) / sizeof( wxString );
m_ZoneCustomPadShape = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneCustomPadShapeNChoices, m_ZoneCustomPadShapeChoices, 0 ); m_ZoneCustomPadShape = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneCustomPadShapeNChoices, m_ZoneCustomPadShapeChoices, 0 );
m_ZoneCustomPadShape->SetSelection( 0 ); m_ZoneCustomPadShape->SetSelection( 0 );
fgSizerCopperZonesOpts->Add( m_ZoneCustomPadShape, 0, wxEXPAND|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); fgSizerCopperZonesOpts->Add( m_ZoneCustomPadShape, 1, wxEXPAND|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
m_sbSizerZonesSettings->Add( fgSizerCopperZonesOpts, 0, wxEXPAND, 5 ); m_sbSizerZonesSettings->Add( fgSizerCopperZonesOpts, 0, 0, 5 );
bSizerClearance->Add( m_sbSizerZonesSettings, 0, wxALL|wxEXPAND, 5 ); bSizerLower->Add( m_sbSizerZonesSettings, 1, wxALL|wxEXPAND, 5 );
wxStaticBoxSizer* sbSizerThermalReliefs;
sbSizerThermalReliefs = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Thermal Relief Overrides") ), wxVERTICAL );
wxFlexGridSizer* fgSizerThermalReliefs;
fgSizerThermalReliefs = new wxFlexGridSizer( 0, 3, 0, 0 );
fgSizerThermalReliefs->AddGrowableCol( 1 );
fgSizerThermalReliefs->SetFlexibleDirection( wxBOTH );
fgSizerThermalReliefs->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_thermalGapLabel = new wxStaticText( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, _("Relief gap:"), wxDefaultPosition, wxDefaultSize, 0 );
m_thermalGapLabel->Wrap( -1 );
fgSizerThermalReliefs->Add( m_thermalGapLabel, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_thermalGapCtrl = new wxTextCtrl( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerThermalReliefs->Add( m_thermalGapCtrl, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
m_thermalGapUnits = new wxStaticText( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_thermalGapUnits->Wrap( -1 );
fgSizerThermalReliefs->Add( m_thermalGapUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
m_spokeWidthLabel = new wxStaticText( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, _("Spoke width:"), wxDefaultPosition, wxDefaultSize, 0 );
m_spokeWidthLabel->Wrap( -1 );
fgSizerThermalReliefs->Add( m_spokeWidthLabel, 0, wxALIGN_CENTER_VERTICAL|wxTOP, 5 );
m_spokeWidthCtrl = new wxTextCtrl( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerThermalReliefs->Add( m_spokeWidthCtrl, 0, wxEXPAND|wxLEFT|wxTOP|wxALIGN_CENTER_VERTICAL, 5 );
m_spokeWidthUnits = new wxStaticText( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_spokeWidthUnits->Wrap( -1 );
fgSizerThermalReliefs->Add( m_spokeWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT, 5 );
m_spokeAngleLabel = new wxStaticText( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, _("Spoke angle:"), wxDefaultPosition, wxDefaultSize, 0 );
m_spokeAngleLabel->Wrap( -1 );
fgSizerThermalReliefs->Add( m_spokeAngleLabel, 0, wxTOP|wxBOTTOM|wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
m_spokeAngleCtrl = new wxTextCtrl( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerThermalReliefs->Add( m_spokeAngleCtrl, 0, wxEXPAND|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_spokeAngleUnits = new wxStaticText( sbSizerThermalReliefs->GetStaticBox(), wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 );
m_spokeAngleUnits->Wrap( -1 );
fgSizerThermalReliefs->Add( m_spokeAngleUnits, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
sbSizerThermalReliefs->Add( fgSizerThermalReliefs, 1, wxEXPAND, 5 );
bSizerLower->Add( sbSizerThermalReliefs, 1, wxEXPAND|wxALL, 5 );
bSizerClearance->Add( bSizerLower, 1, wxEXPAND, 5 );
bSizerPanelClearance->Add( bSizerClearance, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); bSizerPanelClearance->Add( bSizerClearance, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );

File diff suppressed because it is too large Load Diff

View File

@ -192,14 +192,17 @@ class DIALOG_PAD_PROPERTIES_BASE : public DIALOG_SHIM
wxStaticBoxSizer* m_sbSizerZonesSettings; wxStaticBoxSizer* m_sbSizerZonesSettings;
wxStaticText* m_staticText40; wxStaticText* m_staticText40;
wxChoice* m_ZoneConnectionChoice; wxChoice* m_ZoneConnectionChoice;
wxStaticText* m_spokeWidthLabel; wxStaticText* m_staticTextcps;
wxTextCtrl* m_spokeWidthCtrl; wxChoice* m_ZoneCustomPadShape;
wxStaticText* m_spokeWidthUnits;
wxStaticText* m_thermalGapLabel; wxStaticText* m_thermalGapLabel;
wxTextCtrl* m_thermalGapCtrl; wxTextCtrl* m_thermalGapCtrl;
wxStaticText* m_thermalGapUnits; wxStaticText* m_thermalGapUnits;
wxStaticText* m_staticTextcps; wxStaticText* m_spokeWidthLabel;
wxChoice* m_ZoneCustomPadShape; wxTextCtrl* m_spokeWidthCtrl;
wxStaticText* m_spokeWidthUnits;
wxStaticText* m_spokeAngleLabel;
wxTextCtrl* m_spokeAngleCtrl;
wxStaticText* m_spokeAngleUnits;
wxPanel* m_panelCustomShapePrimitives; wxPanel* m_panelCustomShapePrimitives;
wxBoxSizer* m_bSizerPanelPrimitives; wxBoxSizer* m_bSizerPanelPrimitives;
wxStaticText* m_staticTextPrimitivesList; wxStaticText* m_staticTextPrimitivesList;

View File

@ -305,9 +305,12 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
"skew|" "skew|"
"text_height|" "text_height|"
"text_thickness|" "text_thickness|"
"thermal_relief_gap|"
"thermal_spoke_width|"
"track_width|" "track_width|"
"via_count|" "via_count|"
"via_diameter"; "via_diameter|"
"zone_connection";
} }
else if( sexprs.top() == "disallow" || isDisallowToken( sexprs.top() ) ) else if( sexprs.top() == "disallow" || isDisallowToken( sexprs.top() ) )
{ {
@ -321,6 +324,12 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
"via|" "via|"
"zone"; "zone";
} }
else if( sexprs.top() == "zone_connection" )
{
tokens = "solid "
"thermal_relief "
"none";
}
else if( sexprs.top() == "layer" ) else if( sexprs.top() == "layer" )
{ {
tokens = "inner|outer|\"x\""; tokens = "inner|outer|\"x\"";

View File

@ -5,7 +5,7 @@
(rule <rule_name> <rule_clause> ...) (rule <rule_name> <rule_clause> ...)
<br><br> <br>
### Rule Clauses ### Rule Clauses
@ -16,7 +16,7 @@
(layer "<layer_name>") (layer "<layer_name>")
<br><br> <br>
### Constraint Types ### Constraint Types
@ -34,19 +34,22 @@
* skew * skew
* text\_height * text\_height
* text\_thickness * text\_thickness
* thermal\_relief\_gap
* thermal\_spoke\_width
* track\_width * track\_width
* via\_count * via\_count
* via\_diameter * via\_diameter
* zone\_connection
<br><br> <br>
### Item Types ### Item Types
* buried_via * buried\_via
* graphic * graphic
* hole * hole
* micro_via * micro\_via
* pad * pad
* text * text
* track * track
@ -55,6 +58,14 @@
<br> <br>
### Zone Connections
* solid
* thermal\_reliefs
* none
<br>
### Examples ### Examples
(version 1) (version 1)
@ -79,6 +90,11 @@
(rule HV_unshielded (rule HV_unshielded
(constraint clearance (min 2mm)) (constraint clearance (min 2mm))
(condition "A.NetClass == 'HV' && !A.insideArea('Shield*')")) (condition "A.NetClass == 'HV' && !A.insideArea('Shield*')"))
(rule heavy_thermals
(constraint thermal_spoke_width (min 0.5mm))
(condition "A.NetClass == 'HV'))
<br><br> <br><br>
### Notes ### Notes
@ -203,3 +219,9 @@ For the latter use a `(layer "layer_name")` clause in the rule.
(rule "dp clearance" (rule "dp clearance"
(constraint clearance (min "1.5mm")) (constraint clearance (min "1.5mm"))
(condition "A.inDiffPair('*') && !AB.isCoupledDiffPair()")) (condition "A.inDiffPair('*') && !AB.isCoupledDiffPair()"))
# Don't use thermal reliefs on heatsink pads
(rule heat_sink_pad
(constraint zone_connection solid)
(condition "A.Fabrication_Property == 'Heatsink pad'"))

View File

@ -509,7 +509,10 @@ static wxString formatConstraint( const DRC_CONSTRAINT& constraint )
{ TEXT_THICKNESS_CONSTRAINT, "text_thickness", formatMinMax }, { TEXT_THICKNESS_CONSTRAINT, "text_thickness", formatMinMax },
{ TRACK_WIDTH_CONSTRAINT, "track_width", formatMinMax }, { TRACK_WIDTH_CONSTRAINT, "track_width", formatMinMax },
{ ANNULAR_WIDTH_CONSTRAINT, "annular_width", formatMinMax }, { ANNULAR_WIDTH_CONSTRAINT, "annular_width", formatMinMax },
{ DISALLOW_CONSTRAINT, "disallow", nullptr }, { ZONE_CONNECTION_CONSTRAINT, "zone_connection", nullptr },
{ THERMAL_RELIEF_GAP_CONSTRAINT, "thermal_relief_gap", formatMinMax },
{ THERMAL_SPOKE_WIDTH_CONSTRAINT, "thermal_spoke_width", formatMinMax },
{ DISALLOW_CONSTRAINT, "disallow", nullptr },
{ VIA_DIAMETER_CONSTRAINT, "via_diameter", formatMinMax }, { VIA_DIAMETER_CONSTRAINT, "via_diameter", formatMinMax },
{ LENGTH_CONSTRAINT, "length", formatMinMax }, { LENGTH_CONSTRAINT, "length", formatMinMax },
{ SKEW_CONSTRAINT, "skew", formatMinMax }, { SKEW_CONSTRAINT, "skew", formatMinMax },
@ -768,13 +771,48 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aT
} }
#define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
#define UNITS aReporter ? aReporter->GetUnits() : EDA_UNITS::MILLIMETRES
#define REPORT_VALUE( v ) MessageTextFromValue( UNITS, v )
DRC_CONSTRAINT DRC_ENGINE::EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
PCB_LAYER_ID aLayer, REPORTER* aReporter )
{
DRC_CONSTRAINT constraint = EvalRules( ZONE_CONNECTION_CONSTRAINT, a, b, aLayer, aReporter );
REPORT( "" )
REPORT( wxString::Format( _( "Resolved zone connection type: %s." ),
EscapeHTML( PrintZoneConnection( constraint.m_ZoneConnection ) ) ) )
if( constraint.m_ZoneConnection == ZONE_CONNECTION::THT_THERMAL )
{
const PAD* pad = nullptr;
if( a->Type() == PCB_PAD_T )
pad = static_cast<const PAD*>( a );
else if( b->Type() == PCB_PAD_T )
pad = static_cast<const PAD*>( b );
if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
{
constraint.m_ZoneConnection = ZONE_CONNECTION::THERMAL;
}
else
{
REPORT( wxString::Format( _( "Pad is not a through hole pad; connection will be: %s." ),
EscapeHTML( PrintZoneConnection( ZONE_CONNECTION::FULL ) ) ) )
constraint.m_ZoneConnection = ZONE_CONNECTION::FULL;
}
}
return constraint;
}
DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM* a, DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM* a,
const BOARD_ITEM* b, PCB_LAYER_ID aLayer, const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
REPORTER* aReporter ) REPORTER* aReporter )
{ {
#define REPORT( s ) { if( aReporter ) { aReporter->Report( s ); } }
#define UNITS aReporter ? aReporter->GetUnits() : EDA_UNITS::MILLIMETRES
#define REPORT_VALUE( v ) MessageTextFromValue( UNITS, v )
/* /*
* NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely * NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
* kills performance when running bulk DRC tests (where aReporter is nullptr). * kills performance when running bulk DRC tests (where aReporter is nullptr).
@ -788,6 +826,24 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) ); bool a_is_non_copper = a && ( !a->IsOnCopperLayer() || isKeepoutZone( a, false ) );
bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) ); bool b_is_non_copper = b && ( !b->IsOnCopperLayer() || isKeepoutZone( b, false ) );
const PAD* pad = nullptr;
const ZONE* zone = nullptr;
if( aConstraintType == ZONE_CONNECTION_CONSTRAINT
|| aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT
|| aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
{
if( a && a->Type() == PCB_PAD_T )
pad = static_cast<const PAD*>( a );
else if( a && ( a->Type() == PCB_ZONE_T || a->Type() == PCB_FP_ZONE_T ) )
zone = static_cast<const ZONE*>( a );
if( b && b->Type() == PCB_PAD_T )
pad = static_cast<const PAD*>( b );
else if( b && ( b->Type() == PCB_ZONE_T || b->Type() == PCB_FP_ZONE_T ) )
zone = static_cast<const ZONE*>( b );
}
DRC_CONSTRAINT constraint; DRC_CONSTRAINT constraint;
constraint.m_Type = aConstraintType; constraint.m_Type = aConstraintType;
@ -859,6 +915,64 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
return constraint; return constraint;
} }
} }
else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
{
if( pad && pad->GetLocalZoneConnectionOverride( nullptr ) != ZONE_CONNECTION::INHERITED )
{
ZONE_CONNECTION override = pad->GetLocalZoneConnectionOverride( &m_msg );
REPORT( "" )
REPORT( wxString::Format( _( "Local override on %s; zone connection: %s." ),
EscapeHTML( pad->GetSelectMenuText( UNITS ) ),
EscapeHTML( PrintZoneConnection( override ) ) ) )
constraint.SetName( m_msg );
constraint.m_ZoneConnection = override;
return constraint;
}
}
else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
{
if( pad && pad->GetLocalThermalGapOverride( nullptr ) > 0 )
{
int override = pad->GetLocalThermalGapOverride( &m_msg );
REPORT( "" )
REPORT( wxString::Format( _( "Local override on %s; thermal relief gap: %s." ),
EscapeHTML( pad->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( override ) ) ) )
constraint.SetName( m_msg );
constraint.m_Value.SetMin( override );
return constraint;
}
}
else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
{
if( pad && pad->GetLocalSpokeWidthOverride( nullptr ) > 0 )
{
int override = pad->GetLocalSpokeWidthOverride( &m_msg );
REPORT( "" )
REPORT( wxString::Format( _( "Local override on %s; thermal spoke width: %s." ),
EscapeHTML( pad->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( override ) ) ) )
if( zone && zone->GetMinThickness() > override )
{
override = zone->GetMinThickness();
REPORT( "" )
REPORT( wxString::Format( _( "Zone %s min thickness: %s." ),
EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( override ) ) ) )
}
constraint.SetName( m_msg );
constraint.m_Value.SetMin( override );
return constraint;
}
}
auto processConstraint = auto processConstraint =
[&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool [&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool
@ -1149,6 +1263,72 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
return constraint; return constraint;
} }
} }
else if( aConstraintType == ZONE_CONNECTION_CONSTRAINT )
{
if( pad && pad->GetParent() )
{
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( pad->GetParent() );
ZONE_CONNECTION local = footprint->GetZoneConnection();
if( local != ZONE_CONNECTION::INHERITED )
{
REPORT( "" )
REPORT( wxString::Format( _( "Footprint %s zone connection: %s." ),
EscapeHTML( footprint->GetSelectMenuText( UNITS ) ),
EscapeHTML( PrintZoneConnection( local ) ) ) )
constraint.SetName( _( "footprint" ) );
constraint.m_ZoneConnection = local;
return constraint;
}
}
if( zone )
{
ZONE_CONNECTION local = zone->GetPadConnection();
REPORT( "" )
REPORT( wxString::Format( _( "Zone %s pad connection: %s." ),
EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
EscapeHTML( PrintZoneConnection( local ) ) ) )
constraint.SetName( _( "zone" ) );
constraint.m_ZoneConnection = local;
return constraint;
}
}
else if( aConstraintType == THERMAL_RELIEF_GAP_CONSTRAINT )
{
if( zone )
{
int local = zone->GetThermalReliefSpokeWidth();
REPORT( "" )
REPORT( wxString::Format( _( "Zone %s thermal relief gap: %s." ),
EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( local ) ) ) )
constraint.SetName( _( "zone" ) );
constraint.m_Value.SetMin( local );
return constraint;
}
}
else if( aConstraintType == THERMAL_SPOKE_WIDTH_CONSTRAINT )
{
if( zone )
{
int local = zone->GetThermalReliefSpokeWidth();
REPORT( "" )
REPORT( wxString::Format( _( "Zone %s thermal spoke width: %s." ),
EscapeHTML( zone->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( local ) ) ) )
constraint.SetName( _( "zone" ) );
constraint.m_Value.SetMin( local );
return constraint;
}
}
if( !constraint.GetParentRule() ) if( !constraint.GetParentRule() )
{ {
@ -1157,11 +1337,10 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
} }
return constraint; return constraint;
}
#undef REPORT #undef REPORT
#undef UNITS #undef UNITS
#undef REPORT_VALUE #undef REPORT_VALUE
}
bool DRC_ENGINE::IsErrorLimitExceeded( int error_code ) bool DRC_ENGINE::IsErrorLimitExceeded( int error_code )

View File

@ -150,6 +150,9 @@ public:
const BOARD_ITEM* b, PCB_LAYER_ID aLayer, const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
REPORTER* aReporter = nullptr ); REPORTER* aReporter = nullptr );
DRC_CONSTRAINT EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
PCB_LAYER_ID aLayer, REPORTER* aReporter = nullptr );
bool HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID ); bool HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID );
EDA_UNITS UserUnits() const { return m_userUnits; } EDA_UNITS UserUnits() const { return m_userUnits; }

View File

@ -29,6 +29,7 @@
#include <core/minoptmax.h> #include <core/minoptmax.h>
#include <layer_ids.h> #include <layer_ids.h>
#include <netclass.h> #include <netclass.h>
#include <zones.h>
#include <libeval_compiler/libeval_compiler.h> #include <libeval_compiler/libeval_compiler.h>
#include <wx/intl.h> #include <wx/intl.h>
@ -52,6 +53,9 @@ enum DRC_CONSTRAINT_T
TEXT_THICKNESS_CONSTRAINT, TEXT_THICKNESS_CONSTRAINT,
TRACK_WIDTH_CONSTRAINT, TRACK_WIDTH_CONSTRAINT,
ANNULAR_WIDTH_CONSTRAINT, ANNULAR_WIDTH_CONSTRAINT,
ZONE_CONNECTION_CONSTRAINT,
THERMAL_RELIEF_GAP_CONSTRAINT,
THERMAL_SPOKE_WIDTH_CONSTRAINT,
DISALLOW_CONSTRAINT, DISALLOW_CONSTRAINT,
VIA_DIAMETER_CONSTRAINT, VIA_DIAMETER_CONSTRAINT,
LENGTH_CONSTRAINT, LENGTH_CONSTRAINT,
@ -111,6 +115,7 @@ class DRC_CONSTRAINT
m_Type( aType ), m_Type( aType ),
m_Value(), m_Value(),
m_DisallowFlags( 0 ), m_DisallowFlags( 0 ),
m_ZoneConnection( ZONE_CONNECTION::INHERITED ),
m_name( aName ), m_name( aName ),
m_parentRule( nullptr ) m_parentRule( nullptr )
{ {
@ -146,6 +151,7 @@ public:
DRC_CONSTRAINT_T m_Type; DRC_CONSTRAINT_T m_Type;
MINOPTMAX<int> m_Value; MINOPTMAX<int> m_Value;
int m_DisallowFlags; int m_DisallowFlags;
ZONE_CONNECTION m_ZoneConnection;
private: private:
wxString m_name; // For just-in-time constraints wxString m_name; // For just-in-time constraints

View File

@ -23,6 +23,7 @@
#include <board.h> #include <board.h>
#include <zones.h>
#include <drc/drc_rule_parser.h> #include <drc/drc_rule_parser.h>
#include <drc/drc_rule_condition.h> #include <drc/drc_rule_condition.h>
#include <drc_rules_lexer.h> #include <drc_rules_lexer.h>
@ -277,6 +278,9 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
case T_track_width: c.m_Type = TRACK_WIDTH_CONSTRAINT; break; case T_track_width: c.m_Type = TRACK_WIDTH_CONSTRAINT; break;
case T_annular_width: c.m_Type = ANNULAR_WIDTH_CONSTRAINT; break; case T_annular_width: c.m_Type = ANNULAR_WIDTH_CONSTRAINT; break;
case T_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break; case T_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break;
case T_zone_connection: c.m_Type = ZONE_CONNECTION_CONSTRAINT; break;
case T_thermal_relief_gap: c.m_Type = THERMAL_RELIEF_GAP_CONSTRAINT; break;
case T_thermal_spoke_width: c.m_Type = THERMAL_SPOKE_WIDTH_CONSTRAINT; break;
case T_disallow: c.m_Type = DISALLOW_CONSTRAINT; break; case T_disallow: c.m_Type = DISALLOW_CONSTRAINT; break;
case T_length: c.m_Type = LENGTH_CONSTRAINT; break; case T_length: c.m_Type = LENGTH_CONSTRAINT; break;
case T_skew: c.m_Type = SKEW_CONSTRAINT; break; case T_skew: c.m_Type = SKEW_CONSTRAINT; break;
@ -286,8 +290,10 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
default: default:
msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(), msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
"clearance, hole_clearance, edge_clearance, hole_size, hole_to_hole, " "clearance, hole_clearance, edge_clearance, hole_size, hole_to_hole, "
"courtyard_clearance, silk_clearance, track_width, annular_width, via_diameter, " "courtyard_clearance, silk_clearance, text_height, text_thickness, "
"disallow, length, skew, diff_pair_gap or diff_pair_uncoupled." ); "track_width, annular_width, via_diameter, zone_connection, "
"thermal_relief_gap, thermal_spoke_width, disallow, length, skew, "
"diff_pair_gap or diff_pair_uncoupled." );
reportError( msg ); reportError( msg );
} }
@ -336,6 +342,36 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
aRule->AddConstraint( c ); aRule->AddConstraint( c );
return; return;
} }
else if( c.m_Type == ZONE_CONNECTION_CONSTRAINT )
{
token = NextTok();
if( (int) token == DSN_STRING )
token = GetCurStrAsToken();
switch( token )
{
case T_solid: c.m_ZoneConnection = ZONE_CONNECTION::FULL; break;
case T_thermal_reliefs: c.m_ZoneConnection = ZONE_CONNECTION::THERMAL; break;
case T_none: c.m_ZoneConnection = ZONE_CONNECTION::NONE; break;
case T_EOF:
reportError( _( "Missing ')'." ) );
return;
default:
msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
"'solid', 'thermal_reliefs' or 'none'." );
reportError( msg );
break;
}
if( (int) NextTok() != DSN_RIGHT )
reportError( _( "Missing ')'." ) );
aRule->AddConstraint( c );
return;
}
for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() ) for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
{ {

View File

@ -34,7 +34,6 @@
#include <drc/drc_engine.h> #include <drc/drc_engine.h>
#include <drc/drc_item.h> #include <drc/drc_item.h>
#include <drc/drc_test_provider.h> #include <drc/drc_test_provider.h>
#include <macros.h>
/* /*
Library parity test. Library parity test.
@ -171,6 +170,7 @@ bool padsNeedUpdate( const PAD* a, const PAD* b )
TEST( a->GetZoneConnection(), b->GetZoneConnection() ); TEST( a->GetZoneConnection(), b->GetZoneConnection() );
TEST( a->GetThermalGap(), b->GetThermalGap() ); TEST( a->GetThermalGap(), b->GetThermalGap() );
TEST( a->GetThermalSpokeWidth(), b->GetThermalSpokeWidth() ); TEST( a->GetThermalSpokeWidth(), b->GetThermalSpokeWidth() );
TEST( a->GetThermalSpokeAngle(), b->GetThermalSpokeAngle() );
TEST( a->GetCustomShapeInZoneOpt(), b->GetCustomShapeInZoneOpt() ); TEST( a->GetCustomShapeInZoneOpt(), b->GetCustomShapeInZoneOpt() );
TEST( a->GetPrimitives().size(), b->GetPrimitives().size() ); TEST( a->GetPrimitives().size(), b->GetPrimitives().size() );

View File

@ -21,7 +21,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
//#include <common.h>
#include <pcb_track.h> #include <pcb_track.h>
#include <drc/drc_engine.h> #include <drc/drc_engine.h>
#include <drc/drc_item.h> #include <drc/drc_item.h>

View File

@ -0,0 +1,70 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 KiCad Developers.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <drc/drc_rule.h>
#include <drc/drc_test_provider.h>
/*
This doesn't actually run any tests; it just loads the various zone connectionrules for
the ZONE_FILLER.
*/
class DRC_TEST_PROVIDER_ZONE_CONNECTIONS : public DRC_TEST_PROVIDER
{
public:
DRC_TEST_PROVIDER_ZONE_CONNECTIONS()
{
}
virtual ~DRC_TEST_PROVIDER_ZONE_CONNECTIONS()
{
}
virtual bool Run() override
{
return true;
}
virtual const wxString GetName() const override
{
return "zone connections";
};
virtual const wxString GetDescription() const override
{
return "Compiles zone connection rules for the zone filler";
}
virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override
{
return { ZONE_CONNECTION_CONSTRAINT, THERMAL_RELIEF_GAP_CONSTRAINT,
THERMAL_SPOKE_WIDTH_CONSTRAINT };
}
};
namespace detail
{
static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_ZONE_CONNECTIONS> dummy;
}

View File

@ -68,9 +68,7 @@ FOOTPRINT::FOOTPRINT( BOARD* parent ) :
m_localSolderMaskMargin = 0; m_localSolderMaskMargin = 0;
m_localSolderPasteMargin = 0; m_localSolderPasteMargin = 0;
m_localSolderPasteMarginRatio = 0.0; m_localSolderPasteMarginRatio = 0.0;
m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use zone setting by default m_zoneConnection = ZONE_CONNECTION::INHERITED;
m_thermalWidth = 0; // Use zone setting by default
m_thermalGap = 0; // Use zone setting by default
// These are special and mandatory text fields // These are special and mandatory text fields
m_reference = new FP_TEXT( this, FP_TEXT::TEXT_is_REFERENCE ); m_reference = new FP_TEXT( this, FP_TEXT::TEXT_is_REFERENCE );
@ -108,8 +106,6 @@ FOOTPRINT::FOOTPRINT( const FOOTPRINT& aFootprint ) :
m_localSolderPasteMargin = aFootprint.m_localSolderPasteMargin; m_localSolderPasteMargin = aFootprint.m_localSolderPasteMargin;
m_localSolderPasteMarginRatio = aFootprint.m_localSolderPasteMarginRatio; m_localSolderPasteMarginRatio = aFootprint.m_localSolderPasteMarginRatio;
m_zoneConnection = aFootprint.m_zoneConnection; m_zoneConnection = aFootprint.m_zoneConnection;
m_thermalWidth = aFootprint.m_thermalWidth;
m_thermalGap = aFootprint.m_thermalGap;
std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap; std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
@ -291,8 +287,6 @@ FOOTPRINT& FOOTPRINT::operator=( FOOTPRINT&& aOther )
m_localSolderPasteMargin = aOther.m_localSolderPasteMargin; m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio; m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
m_zoneConnection = aOther.m_zoneConnection; m_zoneConnection = aOther.m_zoneConnection;
m_thermalWidth = aOther.m_thermalWidth;
m_thermalGap = aOther.m_thermalGap;
// Move reference and value // Move reference and value
m_reference = aOther.m_reference; m_reference = aOther.m_reference;
@ -391,8 +385,6 @@ FOOTPRINT& FOOTPRINT::operator=( const FOOTPRINT& aOther )
m_localSolderPasteMargin = aOther.m_localSolderPasteMargin; m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio; m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
m_zoneConnection = aOther.m_zoneConnection; m_zoneConnection = aOther.m_zoneConnection;
m_thermalWidth = aOther.m_thermalWidth;
m_thermalGap = aOther.m_thermalGap;
// Copy reference and value // Copy reference and value
*m_reference = *aOther.m_reference; *m_reference = *aOther.m_reference;
@ -2439,14 +2431,6 @@ static struct FOOTPRINT_DESC
double>( _HKI( "Solderpaste Margin Ratio Override" ), double>( _HKI( "Solderpaste Margin Ratio Override" ),
&FOOTPRINT::SetLocalSolderPasteMarginRatio, &FOOTPRINT::SetLocalSolderPasteMarginRatio,
&FOOTPRINT::GetLocalSolderPasteMarginRatio ) ); &FOOTPRINT::GetLocalSolderPasteMarginRatio ) );
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Thermal Relief Width" ),
&FOOTPRINT::SetThermalWidth,
&FOOTPRINT::GetThermalWidth,
PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Thermal Relief Gap" ),
&FOOTPRINT::SetThermalGap,
&FOOTPRINT::GetThermalGap,
PROPERTY_DISPLAY::DISTANCE ) );
// TODO zone connection, FPID? // TODO zone connection, FPID?
} }
} _FOOTPRINT_DESC; } _FOOTPRINT_DESC;

View File

@ -227,12 +227,6 @@ public:
void SetZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; } void SetZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; }
ZONE_CONNECTION GetZoneConnection() const { return m_zoneConnection; } ZONE_CONNECTION GetZoneConnection() const { return m_zoneConnection; }
void SetThermalWidth( int aWidth ) { m_thermalWidth = aWidth; }
int GetThermalWidth() const { return m_thermalWidth; }
void SetThermalGap( int aGap ) { m_thermalGap = aGap; }
int GetThermalGap() const { return m_thermalGap; }
int GetAttributes() const { return m_attributes; } int GetAttributes() const { return m_attributes; }
void SetAttributes( int aAttributes ) { m_attributes = aAttributes; } void SetAttributes( int aAttributes ) { m_attributes = aAttributes; }
@ -772,8 +766,6 @@ private:
mutable int m_hullCacheTimeStamp; mutable int m_hullCacheTimeStamp;
ZONE_CONNECTION m_zoneConnection; ZONE_CONNECTION m_zoneConnection;
int m_thermalWidth;
int m_thermalGap;
int m_localClearance; int m_localClearance;
int m_localSolderMaskMargin; // Solder mask margin int m_localSolderMaskMargin; // Solder mask margin
int m_localSolderPasteMargin; // Solder paste margin absolute value int m_localSolderPasteMargin; // Solder paste margin absolute value

View File

@ -85,9 +85,10 @@ PAD::PAD( FOOTPRINT* parent ) :
m_chamferScale = 0.2; // Size of chamfer: ratio of smallest of X,Y size m_chamferScale = 0.2; // Size of chamfer: ratio of smallest of X,Y size
m_chamferPositions = RECT_NO_CHAMFER; // No chamfered corner m_chamferPositions = RECT_NO_CHAMFER; // No chamfered corner
m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use parent setting by default m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use parent setting by default
m_thermalWidth = 0; // Use parent setting by default m_thermalSpokeWidth = 0; // Use parent setting by default
m_thermalGap = 0; // Use parent setting by default m_thermalSpokeAngle = 450.0; // Default for circular pads
m_thermalGap = 0; // Use parent setting by default
m_customShapeClearanceArea = CUST_PAD_SHAPE_IN_ZONE_OUTLINE; m_customShapeClearanceArea = CUST_PAD_SHAPE_IN_ZONE_OUTLINE;
@ -819,59 +820,27 @@ wxSize PAD::GetSolderPasteMargin() const
} }
ZONE_CONNECTION PAD::GetEffectiveZoneConnection( wxString* aSource ) const ZONE_CONNECTION PAD::GetLocalZoneConnectionOverride( wxString* aSource ) const
{ {
FOOTPRINT* parentFootprint = GetParent(); if( m_zoneConnection != ZONE_CONNECTION::INHERITED && aSource )
if( m_zoneConnection == ZONE_CONNECTION::INHERITED && parentFootprint )
{
if( aSource )
*aSource = _( "parent footprint" );
return parentFootprint->GetZoneConnection();
}
else
{
if( aSource )
*aSource = _( "pad" );
return m_zoneConnection;
}
}
int PAD::GetEffectiveThermalSpokeWidth( wxString* aSource ) const
{
FOOTPRINT* parentFootprint = GetParent();
if( m_thermalWidth == 0 && parentFootprint )
{
if( aSource )
*aSource = _( "parent footprint" );
return parentFootprint->GetThermalWidth();
}
if( aSource )
*aSource = _( "pad" ); *aSource = _( "pad" );
return m_thermalWidth; return m_zoneConnection;
} }
int PAD::GetEffectiveThermalGap( wxString* aSource ) const int PAD::GetLocalSpokeWidthOverride( wxString* aSource ) const
{ {
FOOTPRINT* parentFootprint = GetParent(); if( m_thermalSpokeWidth > 0 && aSource )
*aSource = _( "pad" );
if( m_thermalGap == 0 && parentFootprint ) return m_thermalSpokeWidth;
{ }
if( aSource )
*aSource = _( "parent footprint" );
return parentFootprint->GetThermalGap();
}
if( aSource ) int PAD::GetLocalThermalGapOverride( wxString* aSource ) const
{
if( m_thermalGap > 0 && aSource )
*aSource = _( "pad" ); *aSource = _( "pad" );
return m_thermalGap; return m_thermalGap;
@ -1488,8 +1457,9 @@ void PAD::ImportSettingsFrom( const PAD& aMasterPad )
SetLocalSolderPasteMargin( aMasterPad.GetLocalSolderPasteMargin() ); SetLocalSolderPasteMargin( aMasterPad.GetLocalSolderPasteMargin() );
SetLocalSolderPasteMarginRatio( aMasterPad.GetLocalSolderPasteMarginRatio() ); SetLocalSolderPasteMarginRatio( aMasterPad.GetLocalSolderPasteMarginRatio() );
SetZoneConnection( aMasterPad.GetEffectiveZoneConnection() ); SetZoneConnection( aMasterPad.GetZoneConnection() );
SetThermalSpokeWidth( aMasterPad.GetThermalSpokeWidth() ); SetThermalSpokeWidth( aMasterPad.GetThermalSpokeWidth() );
SetThermalSpokeAngle( aMasterPad.GetThermalSpokeAngle() );
SetThermalGap( aMasterPad.GetThermalGap() ); SetThermalGap( aMasterPad.GetThermalGap() );
SetCustomShapeInZoneOpt( aMasterPad.GetCustomShapeInZoneOpt() ); SetCustomShapeInZoneOpt( aMasterPad.GetCustomShapeInZoneOpt() );
@ -1705,9 +1675,12 @@ static struct PAD_DESC
PROPERTY_DISPLAY::DISTANCE ) ); PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Solderpaste Margin Ratio Override" ), propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Solderpaste Margin Ratio Override" ),
&PAD::SetLocalSolderPasteMarginRatio, &PAD::GetLocalSolderPasteMarginRatio ) ); &PAD::SetLocalSolderPasteMarginRatio, &PAD::GetLocalSolderPasteMarginRatio ) );
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Width" ), propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Spoke Width" ),
&PAD::SetThermalSpokeWidth, &PAD::GetThermalSpokeWidth, &PAD::SetThermalSpokeWidth, &PAD::GetThermalSpokeWidth,
PROPERTY_DISPLAY::DISTANCE ) ); PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Thermal Relief Spoke Angle" ),
&PAD::SetThermalSpokeAngle, &PAD::GetThermalSpokeAngle,
PROPERTY_DISPLAY::DEGREE ) );
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Gap" ), propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Gap" ),
&PAD::SetThermalGap, &PAD::GetThermalGap, &PAD::SetThermalGap, &PAD::GetThermalGap,
PROPERTY_DISPLAY::DISTANCE ) ); PROPERTY_DISPLAY::DISTANCE ) );

View File

@ -475,33 +475,29 @@ public:
void SetZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; } void SetZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; }
ZONE_CONNECTION GetZoneConnection() const { return m_zoneConnection; } ZONE_CONNECTION GetZoneConnection() const { return m_zoneConnection; }
/** ZONE_CONNECTION GetLocalZoneConnectionOverride( wxString* aSource = nullptr ) const;
* Return the zone connection in effect (either locally overridden or overridden in the
* parent footprint).
*
* Optionally reports on the source of the property (pad, parent footprint or zone).
*/
ZONE_CONNECTION GetEffectiveZoneConnection( wxString* aSource = nullptr ) const;
/** /**
* Set the width of the thermal spokes connecting the pad to a zone. If != 0 this will * 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. * override similar settings in the parent footprint and zone.
*/ */
void SetThermalSpokeWidth( int aWidth ) { m_thermalWidth = aWidth; } void SetThermalSpokeWidth( int aWidth ) { m_thermalSpokeWidth = aWidth; }
int GetThermalSpokeWidth() const { return m_thermalWidth; } int GetThermalSpokeWidth() const { return m_thermalSpokeWidth; }
int GetLocalSpokeWidthOverride( wxString* aSource = nullptr ) const;
/** /**
* Return the effective thermal spoke width having resolved any inheritance. * The orientation of the thermal spokes (in decidegrees). 450 will produce an X (the
* default for circular pads and circular-anchored custom shaped pads), while 900 will
* produce a + (the default for all other shapes).
*/ */
int GetEffectiveThermalSpokeWidth( wxString* aSource = nullptr ) const; void SetThermalSpokeAngle( double aAngle ) { m_thermalSpokeAngle = aAngle; }
double GetThermalSpokeAngle() const { return m_thermalSpokeAngle; }
void SetThermalGap( int aGap ) { m_thermalGap = aGap; } void SetThermalGap( int aGap ) { m_thermalGap = aGap; }
int GetThermalGap() const { return m_thermalGap; } int GetThermalGap() const { return m_thermalGap; }
/** int GetLocalThermalGapOverride( wxString* aSource = nullptr ) const;
* Return the effective thermal gap having resolved any inheritance.
*/
int GetEffectiveThermalGap( wxString* aSource = nullptr ) const;
/** /**
* Has meaning only for rounded rectangle pads. * Has meaning only for rounded rectangle pads.
@ -775,7 +771,9 @@ private:
// The final margin is the sum of these 2 values // The final margin is the sum of these 2 values
ZONE_CONNECTION m_zoneConnection; // No connection, thermal relief, etc. ZONE_CONNECTION m_zoneConnection; // No connection, thermal relief, etc.
int m_thermalWidth; // Thermal spoke width. int m_thermalSpokeWidth; // Thermal spoke width.
double m_thermalSpokeAngle; // Rotation of the spokes, in deci-degrees. 450
// will produce an X, while 900 will produce a +.
int m_thermalGap; int m_thermalGap;
}; };

View File

@ -3402,12 +3402,9 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
break; break;
case T_thermal_width: case T_thermal_width:
footprint->SetThermalWidth( parseBoardUnits( "thermal width value" ) );
NeedRIGHT();
break;
case T_thermal_gap: case T_thermal_gap:
footprint->SetThermalGap( parseBoardUnits( "thermal gap value" ) ); // Interestingly, these have never been exposed in the GUI
parseBoardUnits( token );
NeedRIGHT(); NeedRIGHT();
break; break;
@ -3526,7 +3523,7 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, " Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
"autoplace_cost90, autoplace_cost180, solder_mask_margin, " "autoplace_cost90, autoplace_cost180, solder_mask_margin, "
"solder_paste_margin, solder_paste_ratio, clearance, " "solder_paste_margin, solder_paste_ratio, clearance, "
"zone_connect, thermal_width, thermal_gap, attr, fp_text, " "zone_connect, thermal_gap, attr, fp_text, "
"fp_arc, fp_circle, fp_curve, fp_line, fp_poly, fp_rect, pad, " "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, fp_rect, pad, "
"zone, group, generator, version or model" ); "zone, group, generator, version or model" );
} }
@ -4102,6 +4099,13 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" ); Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
} }
if( pad->GetShape() == PAD_SHAPE::CIRCLE )
pad->SetThermalSpokeAngle( 450 );
else if( pad->GetShape() == PAD_SHAPE::CUSTOM && pad->GetAnchorPadShape() == PAD_SHAPE::CIRCLE )
pad->SetThermalSpokeAngle( 450 );
else
pad->SetThermalSpokeAngle( 900 );
for( token = NextTok(); token != T_RIGHT; token = NextTok() ) for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{ {
if( token == T_locked ) if( token == T_locked )
@ -4289,13 +4293,20 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
NeedRIGHT(); NeedRIGHT();
break; break;
case T_thermal_width: case T_thermal_width: // legacy token
pad->SetThermalSpokeWidth( parseBoardUnits( T_thermal_width ) ); case T_thermal_bridge_width:
pad->SetThermalSpokeWidth( parseBoardUnits( token ) );
NeedRIGHT(); NeedRIGHT();
break; break;
case T_thermal_bridge_angle:
pad->SetThermalSpokeAngle( parseAngle( "thermal spoke angle value" ) );
NeedRIGHT();
break;
case T_thermal_gap: case T_thermal_gap:
pad->SetThermalGap( parseBoardUnits( T_thermal_gap ) ); pad->SetThermalGap( parseBoardUnits( "thermal relief gap value" ) );
NeedRIGHT(); NeedRIGHT();
break; break;

View File

@ -1196,14 +1196,6 @@ void PCB_PLUGIN::format( const FOOTPRINT* aFootprint, int aNestLevel ) const
m_out->Print( aNestLevel+1, "(zone_connect %d)\n", m_out->Print( aNestLevel+1, "(zone_connect %d)\n",
static_cast<int>( aFootprint->GetZoneConnection() ) ); static_cast<int>( aFootprint->GetZoneConnection() ) );
if( aFootprint->GetThermalWidth() != 0 )
m_out->Print( aNestLevel+1, "(thermal_width %s)\n",
FormatInternalUnits( aFootprint->GetThermalWidth() ).c_str() );
if( aFootprint->GetThermalGap() != 0 )
m_out->Print( aNestLevel+1, "(thermal_gap %s)\n",
FormatInternalUnits( aFootprint->GetThermalGap() ).c_str() );
// Attributes // Attributes
if( aFootprint->GetAttributes() ) if( aFootprint->GetAttributes() )
{ {
@ -1586,10 +1578,17 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
if( aPad->GetThermalSpokeWidth() != 0 ) if( aPad->GetThermalSpokeWidth() != 0 )
{ {
StrPrintf( &output, " (thermal_width %s)", StrPrintf( &output, " (thermal_bridge_width %s)",
FormatInternalUnits( aPad->GetThermalSpokeWidth() ).c_str() ); FormatInternalUnits( aPad->GetThermalSpokeWidth() ).c_str() );
} }
if( ( aPad->GetShape() == PAD_SHAPE::CIRCLE && aPad->GetThermalSpokeAngle() != 450.0 )
|| ( aPad->GetShape() != PAD_SHAPE::CIRCLE && aPad->GetThermalSpokeAngle() != 900.0 ) )
{
StrPrintf( &output, " (thermal_bridge_angle %s)",
FormatAngle( aPad->GetThermalSpokeAngle() ).c_str() );
}
if( aPad->GetThermalGap() != 0 ) if( aPad->GetThermalGap() != 0 )
{ {
StrPrintf( &output, " (thermal_gap %s)", StrPrintf( &output, " (thermal_gap %s)",

View File

@ -103,7 +103,8 @@ class PCB_TEXT;
//#define SEXPR_BOARD_FILE_VERSION 20210824 // Opacity in 3D colors //#define SEXPR_BOARD_FILE_VERSION 20210824 // Opacity in 3D colors
//#define SEXPR_BOARD_FILE_VERSION 20210925 // Locked flag for fp_text //#define SEXPR_BOARD_FILE_VERSION 20210925 // Locked flag for fp_text
//#define SEXPR_BOARD_FILE_VERSION 20211014 // Arc formatting //#define SEXPR_BOARD_FILE_VERSION 20211014 // Arc formatting
#define SEXPR_BOARD_FILE_VERSION 20211226 // Add radial dimension //#define SEXPR_BOARD_FILE_VERSION 20211226 // Add radial dimension
#define SEXPR_BOARD_FILE_VERSION 20211227 // Add thermal relief spoke angle overrides
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag #define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting #define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting

View File

@ -1302,12 +1302,12 @@ void LEGACY_PLUGIN::loadFOOTPRINT( FOOTPRINT* aFootprint )
else if( TESTLINE( ".ThermalWidth" ) ) else if( TESTLINE( ".ThermalWidth" ) )
{ {
BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) ); BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) );
aFootprint->SetThermalWidth( tmp ); ignore_unused( tmp );
} }
else if( TESTLINE( ".ThermalGap" ) ) else if( TESTLINE( ".ThermalGap" ) )
{ {
BIU tmp = biuParse( line + SZ( ".ThermalGap" ) ); BIU tmp = biuParse( line + SZ( ".ThermalGap" ) );
aFootprint->SetThermalGap( tmp ); ignore_unused( tmp );
} }
else if( TESTLINE( "$EndMODULE" ) ) else if( TESTLINE( "$EndMODULE" ) )
{ {

View File

@ -373,7 +373,6 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
if( copperIntersection.any() && zone && pad && zone->GetNetCode() == pad->GetNetCode() ) if( copperIntersection.any() && zone && pad && zone->GetNetCode() == pad->GetNetCode() )
{ {
PCB_LAYER_ID layer = active; PCB_LAYER_ID layer = active;
wxString source;
if( !zone->IsOnLayer( active ) ) if( !zone->IsOnLayer( active ) )
layer = zone->GetLayerSet().Seq().front(); layer = zone->GetLayerSet().Seq().front();
@ -387,44 +386,41 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
EscapeHTML( getItemDescription( a ) ), EscapeHTML( getItemDescription( a ) ),
EscapeHTML( getItemDescription( b ) ) ) ); EscapeHTML( getItemDescription( b ) ) ) );
ZONE_CONNECTION zoneConnection = zone->GetPadConnection( pad ); constraint = drcEngine.EvalZoneConnection( pad, zone, layer, r );
if( zoneConnection == ZONE_CONNECTION::THERMAL ) if( constraint.m_ZoneConnection == ZONE_CONNECTION::THERMAL )
{ {
int gap = zone->GetThermalReliefGap(); constraint = drcEngine.EvalRules( THERMAL_RELIEF_GAP_CONSTRAINT, pad, zone, layer, r );
int gap = constraint.m_Value.Min();
r->Report( wxString::Format( _( "Resolved thermal relief gap: %s." ),
StringFromValue( units, gap, true ) ) );
constraint = drcEngine.EvalRules( THERMAL_SPOKE_WIDTH_CONSTRAINT, pad, zone, layer, r );
int width = constraint.m_Value.Opt();
if( compileError )
reportCompileError( r );
r->Report( "" ); r->Report( "" );
r->Report( wxString::Format( _( "Zone thermal relief: %s." ), r->Report( wxString::Format( _( "Resolved thermal spoke width: %s." ),
StringFromValue( r->GetUnits(), gap, true ) ) ); StringFromValue( units, width, true ) ) );
gap = zone->GetThermalReliefGap( pad, &source );
if( source != _( "zone" ) )
{
r->Report( wxString::Format( _( "Overridden by %s; thermal relief: %s." ),
source,
StringFromValue( r->GetUnits(), gap, true ) ) );
}
} }
else if( zoneConnection == ZONE_CONNECTION::NONE ) else if( constraint.m_ZoneConnection == ZONE_CONNECTION::NONE )
{ {
clearance = zone->GetLocalClearance(); clearance = zone->GetLocalClearance();
r->Report( "" );
r->Report( wxString::Format( _( "Zone clearance: %s." ), r->Report( wxString::Format( _( "Zone clearance: %s." ),
StringFromValue( r->GetUnits(), clearance, true ) ) ); StringFromValue( units, clearance, true ) ) );
if( zone->GetThermalReliefGap( pad ) > clearance ) constraint = drcEngine.EvalRules( THERMAL_RELIEF_GAP_CONSTRAINT, pad, zone, layer, r );
if( constraint.m_Value.Min() > clearance )
{ {
clearance = zone->GetThermalReliefGap( pad, &source ); clearance = constraint.m_Value.Min();
r->Report( wxString::Format( _( "Overridden by larger thermal relief from %s;"
if( source != _( "zone" ) ) "clearance: %s." ),
{ EscapeHTML( constraint.GetName() ),
r->Report( wxString::Format( _( "Overridden by larger thermal relief from %s;" StringFromValue( units, clearance, true ) ) );
"clearance: %s." ),
source,
StringFromValue( r->GetUnits(), clearance, true ) ) );
}
} }
if( compileError ) if( compileError )

View File

@ -41,37 +41,21 @@
#include <trigo.h> #include <trigo.h>
#include <i18n_utility.h> #include <i18n_utility.h>
ZONE::ZONE( BOARD_ITEM_CONTAINER* aParent, bool aInFP ) : ZONE::ZONE( BOARD_ITEM_CONTAINER* aParent, bool aInFP ) :
BOARD_CONNECTED_ITEM( aParent, aInFP ? PCB_FP_ZONE_T : PCB_ZONE_T ), BOARD_CONNECTED_ITEM( aParent, aInFP ? PCB_FP_ZONE_T : PCB_ZONE_T ),
m_area( 0.0 ) m_area( 0.0 )
{ {
m_CornerSelection = nullptr; // no corner is selected m_CornerSelection = nullptr; // no corner is selected
m_isFilled = false; // fill status : true when the zone is filled m_isFilled = false; // fill status : true when the zone is filled
m_fillMode = ZONE_FILL_MODE::POLYGONS;
m_borderStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; m_borderStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE;
m_borderHatchPitch = GetDefaultHatchPitch(); m_borderHatchPitch = GetDefaultHatchPitch();
m_hv45 = false;
m_hatchThickness = 0;
m_hatchGap = 0;
m_hatchOrientation = 0.0;
m_hatchSmoothingLevel = 0; // Grid pattern smoothing type. 0 = no smoothing
m_hatchSmoothingValue = 0.1; // Grid pattern chamfer value relative to the gap value
// used only if m_hatchSmoothingLevel > 0
m_hatchHoleMinArea = 0.3; // Min size before holes are dropped (ratio of hole size)
m_hatchBorderAlgorithm = 1; // 0 = use zone min thickness; 1 = use hatch width
m_priority = 0; m_priority = 0;
m_cornerSmoothingType = ZONE_SETTINGS::SMOOTHING_NONE; SetIsRuleArea( aInFP ); // Zones living in footprints have the rule area option
SetIsRuleArea( aInFP ); // Zones living in footprints have the rule area option SetLocalFlags( 0 ); // flags temporary used in zone calculations
SetDoNotAllowCopperPour( false ); // has meaning only if m_isRuleArea == true m_Poly = new SHAPE_POLY_SET(); // Outlines
SetDoNotAllowVias( true ); // has meaning only if m_isRuleArea == true m_fillVersion = 5; // set the "old" way to build filled polygon areas (< 6.0.x)
SetDoNotAllowTracks( true ); // has meaning only if m_isRuleArea == true
SetDoNotAllowPads( true ); // has meaning only if m_isRuleArea == true
SetDoNotAllowFootprints( false ); // has meaning only if m_isRuleArea == true
m_cornerRadius = 0;
SetLocalFlags( 0 ); // flags temporary used in zone calculations
m_Poly = new SHAPE_POLY_SET(); // Outlines
m_fillVersion = 5; // set the "old" way to build filled polygon areas (< 6.0.x)
m_islandRemovalMode = ISLAND_REMOVAL_MODE::ALWAYS;
aParent->GetZoneSettings().ExportSetting( *this ); aParent->GetZoneSettings().ExportSetting( *this );
m_ZoneMinThickness = Mils2iu( ZONE_THICKNESS_MIL ); m_ZoneMinThickness = Mils2iu( ZONE_THICKNESS_MIL );
@ -335,7 +319,7 @@ const EDA_RECT ZONE::GetBoundingBox() const
int ZONE::GetThermalReliefGap( PAD* aPad, wxString* aSource ) const int ZONE::GetThermalReliefGap( PAD* aPad, wxString* aSource ) const
{ {
if( aPad->GetEffectiveThermalGap() == 0 ) if( aPad->GetLocalThermalGapOverride() == 0 )
{ {
if( aSource ) if( aSource )
*aSource = _( "zone" ); *aSource = _( "zone" );
@ -343,21 +327,8 @@ int ZONE::GetThermalReliefGap( PAD* aPad, wxString* aSource ) const
return m_thermalReliefGap; return m_thermalReliefGap;
} }
return aPad->GetEffectiveThermalGap( aSource ); return aPad->GetLocalThermalGapOverride( aSource );
}
int ZONE::GetThermalReliefSpokeWidth( PAD* aPad, wxString* aSource ) const
{
if( aPad->GetEffectiveThermalSpokeWidth() == 0 )
{
if( aSource )
*aSource = _( "zone" );
return m_thermalReliefSpokeWidth;
}
return aPad->GetEffectiveThermalSpokeWidth( aSource );
} }
@ -784,22 +755,6 @@ void ZONE::Mirror( const wxPoint& aMirrorRef, bool aMirrorLeftRight )
} }
ZONE_CONNECTION ZONE::GetPadConnection( PAD* aPad, wxString* aSource ) const
{
if( aPad == nullptr || aPad->GetEffectiveZoneConnection() == ZONE_CONNECTION::INHERITED )
{
if( aSource )
*aSource = _( "zone" );
return m_PadConnection;
}
else
{
return aPad->GetEffectiveZoneConnection( aSource );
}
}
void ZONE::RemoveCutout( int aOutlineIdx, int aHoleIdx ) void ZONE::RemoveCutout( int aOutlineIdx, int aHoleIdx )
{ {
// Ensure the requested cutout is valid // Ensure the requested cutout is valid
@ -1464,7 +1419,7 @@ static struct ZONE_DESC
.Map( ZONE_CONNECTION::NONE, _HKI( "None" ) ) .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
.Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) ) .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
.Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) ) .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
.Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Reliefs for PTH" ) ); .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance(); PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( ZONE ); REGISTER_TYPE( ZONE );
@ -1486,7 +1441,7 @@ static struct ZONE_DESC
propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Gap" ), propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Gap" ),
&ZONE::SetThermalReliefGap, &ZONE::GetThermalReliefGap, &ZONE::SetThermalReliefGap, &ZONE::GetThermalReliefGap,
PROPERTY_DISPLAY::DISTANCE ) ); PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Width" ), propMgr.AddProperty( new PROPERTY<ZONE, int>( _HKI( "Thermal Relief Spoke Width" ),
&ZONE::SetThermalReliefSpokeWidth, &ZONE::GetThermalReliefSpokeWidth, &ZONE::SetThermalReliefSpokeWidth, &ZONE::GetThermalReliefSpokeWidth,
PROPERTY_DISPLAY::DISTANCE ) ); PROPERTY_DISPLAY::DISTANCE ) );
} }

View File

@ -200,7 +200,6 @@ public:
} }
int GetThermalReliefSpokeWidth() const { return m_thermalReliefSpokeWidth; } int GetThermalReliefSpokeWidth() const { return m_thermalReliefSpokeWidth; }
int GetThermalReliefSpokeWidth( PAD* aPad, wxString* aSource = nullptr ) const;
/** /**
* Compute the area currently occupied by the zone fill. * Compute the area currently occupied by the zone fill.
@ -237,7 +236,6 @@ public:
bool NeedRefill() const { return m_needRefill; } bool NeedRefill() const { return m_needRefill; }
void SetNeedRefill( bool aNeedRefill ) { m_needRefill = aNeedRefill; } void SetNeedRefill( bool aNeedRefill ) { m_needRefill = aNeedRefill; }
ZONE_CONNECTION GetPadConnection( PAD* aPad, wxString* aSource = nullptr ) const;
ZONE_CONNECTION GetPadConnection() const { return m_PadConnection; } ZONE_CONNECTION GetPadConnection() const { return m_PadConnection; }
void SetPadConnection( ZONE_CONNECTION aPadConnection ) { m_PadConnection = aPadConnection; } void SetPadConnection( ZONE_CONNECTION aPadConnection ) { m_PadConnection = aPadConnection; }

View File

@ -47,8 +47,6 @@
#include <math/util.h> // for KiROUND #include <math/util.h> // for KiROUND
#include "zone_filler.h" #include "zone_filler.h"
static const double s_RoundPadThermalSpokeAngle = 450; // in deci-degrees
ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) : ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) :
m_board( aBoard ), m_board( aBoard ),
@ -503,35 +501,6 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
} }
/**
* Return true if the given pad has a thermal connection with the given zone.
*/
bool hasThermalConnection( PAD* pad, const ZONE* aZone )
{
// Rejects non-standard pads with tht-only thermal reliefs
if( aZone->GetPadConnection( pad ) == ZONE_CONNECTION::THT_THERMAL
&& pad->GetAttribute() != PAD_ATTRIB::PTH )
{
return false;
}
if( aZone->GetPadConnection( pad ) != ZONE_CONNECTION::THERMAL
&& aZone->GetPadConnection( pad ) != ZONE_CONNECTION::THT_THERMAL )
{
return false;
}
if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
return false;
EDA_RECT item_boundingbox = pad->GetBoundingBox();
int thermalGap = aZone->GetThermalReliefGap( pad );
item_boundingbox.Inflate( thermalGap, thermalGap );
return item_boundingbox.Intersects( aZone->GetCachedBoundingBox() );
}
/** /**
* Add a knockout for a pad. The knockout is 'aGap' larger than the pad (which might be * Add a knockout for a pad. The knockout is 'aGap' larger than the pad (which might be
* either the thermal clearance or the electrical clearance). * either the thermal clearance or the electrical clearance).
@ -619,32 +588,63 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap,
* in spokes, which must be done later. * in spokes, which must be done later.
*/ */
void ZONE_FILLER::knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer, void ZONE_FILLER::knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aFill ) SHAPE_POLY_SET& aFill,
std::vector<PAD*>& aThermalConnectionPads,
std::vector<PAD*>& aNoConnectionPads )
{ {
SHAPE_POLY_SET holes; BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
DRC_CONSTRAINT constraint;
SHAPE_POLY_SET holes;
for( FOOTPRINT* footprint : m_board->Footprints() ) for( FOOTPRINT* footprint : m_board->Footprints() )
{ {
for( PAD* pad : footprint->Pads() ) for( PAD* pad : footprint->Pads() )
{ {
if( !hasThermalConnection( pad, aZone ) ) if( !pad->IsOnLayer( aLayer ) )
continue; continue;
int gap = aZone->GetThermalReliefGap( pad ); if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
// If the pad is flashed to the current layer, or is on the same layer and shares a netcode, then
// we need to knock out the thermal relief.
if( pad->FlashLayer( aLayer ) || ( pad->IsOnLayer( aLayer ) && pad->GetNetCode() == aZone->GetNetCode() ) )
{ {
aNoConnectionPads.push_back( pad );
continue;
}
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer );
ZONE_CONNECTION conn = constraint.m_ZoneConnection;
if( conn == ZONE_CONNECTION::FULL )
continue;
constraint = bds.m_DRCEngine->EvalRules( THERMAL_RELIEF_GAP_CONSTRAINT, pad, aZone,
aLayer );
int gap = constraint.GetValue().Min();
EDA_RECT item_boundingbox = pad->GetBoundingBox();
item_boundingbox.Inflate( gap, gap );
if( !item_boundingbox.Intersects( aZone->GetCachedBoundingBox() ) )
continue;
// If the pad is flashed to the current layer, or is on the same layer and shares a
// netcode, then we need to knock out the thermal relief.
if( pad->FlashLayer( aLayer ) )
{
if( conn == ZONE_CONNECTION::THERMAL )
aThermalConnectionPads.push_back( pad );
else if( conn == ZONE_CONNECTION::NONE )
aNoConnectionPads.push_back( pad );
addKnockout( pad, aLayer, gap, holes ); addKnockout( pad, aLayer, gap, holes );
} }
else else
{ {
// If the pad isn't on the current layer but has a hole, knock out a thermal relief // If the pad isn't flashed on the current layer but has a hole, knock out a
// for the hole. // thermal relief for the hole.
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
continue; continue;
aNoConnectionPads.push_back( pad );
// Note: drill size represents finish size, which means the actual holes size is // Note: drill size represents finish size, which means the actual holes size is
// the plating thickness larger. // the plating thickness larger.
if( pad->GetAttribute() == PAD_ATTRIB::PTH ) if( pad->GetAttribute() == PAD_ATTRIB::PTH )
@ -664,9 +664,11 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer
* not connected to it. * not connected to it.
*/ */
void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLayer, void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLayer,
const std::vector<PAD*> aNoConnectionPads,
SHAPE_POLY_SET& aHoles ) SHAPE_POLY_SET& aHoles )
{ {
long ticker = 0; BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
long ticker = 0;
auto checkForCancel = auto checkForCancel =
[&ticker]( PROGRESS_REPORTER* aReporter ) -> bool [&ticker]( PROGRESS_REPORTER* aReporter ) -> bool
@ -677,11 +679,8 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
// A small extra clearance to be sure actual track clearances are not smaller than // A small extra clearance to be sure actual track clearances are not smaller than
// requested clearance due to many approximations in calculations, like arc to segment // requested clearance due to many approximations in calculations, like arc to segment
// approx, rounding issues, etc. // approx, rounding issues, etc.
int extra_margin = Millimeter2iu( ADVANCED_CFG::GetCfg().m_ExtraClearance ); EDA_RECT zone_boundingbox = aZone->GetCachedBoundingBox();
int extra_margin = Millimeter2iu( ADVANCED_CFG::GetCfg().m_ExtraClearance );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
int zone_clearance = aZone->GetLocalClearance();
EDA_RECT zone_boundingbox = aZone->GetCachedBoundingBox();
// Items outside the zone bounding box are skipped, so it needs to be inflated by the // Items outside the zone bounding box are skipped, so it needs to be inflated by the
// largest clearance value found in the netclasses and rules // largest clearance value found in the netclasses and rules
@ -692,7 +691,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
PCB_LAYER_ID aEvalLayer ) -> int PCB_LAYER_ID aEvalLayer ) -> int
{ {
auto c = bds.m_DRCEngine->EvalRules( aConstraint, a, b, aEvalLayer ); auto c = bds.m_DRCEngine->EvalRules( aConstraint, a, b, aEvalLayer );
return c.Value().Min(); return c.GetValue().Min();
}; };
// Add non-connected pad clearances // Add non-connected pad clearances
@ -711,7 +710,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
// clearances have no meanings. // clearances have no meanings.
// So just knock out the greater of the zone's local clearance and // So just knock out the greater of the zone's local clearance and
// thermal relief. // thermal relief.
gap = std::max( zone_clearance, aZone->GetThermalReliefGap( aPad ) ); gap = std::max( aZone->GetLocalClearance(), aZone->GetThermalReliefGap() );
knockoutHoleClearance = false; knockoutHoleClearance = false;
} }
else else
@ -736,20 +735,12 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
} }
}; };
for( FOOTPRINT* footprint : m_board->Footprints() ) for( PAD* pad : aNoConnectionPads )
{ {
for( PAD* pad : footprint->Pads() ) if( checkForCancel( m_progressReporter ) )
{ return;
if( checkForCancel( m_progressReporter ) )
return;
if( pad->GetNetCode() != aZone->GetNetCode() knockoutPadClearance( pad );
|| pad->GetNetCode() <= 0
|| aZone->GetPadConnection( pad ) == ZONE_CONNECTION::NONE )
{
knockoutPadClearance( pad );
}
}
} }
// Add non-connected track clearances // Add non-connected track clearances
@ -770,7 +761,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
// For pads having the same netcode as the zone, the net and hole // For pads having the same netcode as the zone, the net and hole
// clearances have no meanings. // clearances have no meanings.
// So just knock out the zone's local clearance. // So just knock out the zone's local clearance.
gap = zone_clearance; gap = aZone->GetLocalClearance();
checkHoleClearance = false; checkHoleClearance = false;
} }
else else
@ -1080,8 +1071,10 @@ bool ZONE_FILLER::computeRawFilledArea( const ZONE* aZone,
SHAPE_POLY_SET::CORNER_STRATEGY fastCornerStrategy = SHAPE_POLY_SET::CHAMFER_ALL_CORNERS; SHAPE_POLY_SET::CORNER_STRATEGY fastCornerStrategy = SHAPE_POLY_SET::CHAMFER_ALL_CORNERS;
SHAPE_POLY_SET::CORNER_STRATEGY cornerStrategy = SHAPE_POLY_SET::ROUND_ALL_CORNERS; SHAPE_POLY_SET::CORNER_STRATEGY cornerStrategy = SHAPE_POLY_SET::ROUND_ALL_CORNERS;
std::vector<PAD*> thermalConnectionPads;
std::vector<PAD*> noConnectionPads;
std::deque<SHAPE_LINE_CHAIN> thermalSpokes; std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
SHAPE_POLY_SET clearanceHoles; SHAPE_POLY_SET clearanceHoles;
aRawPolys = aSmoothedOutline; aRawPolys = aSmoothedOutline;
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In1_Cu, "smoothed-outline" ); DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In1_Cu, "smoothed-outline" );
@ -1089,19 +1082,19 @@ bool ZONE_FILLER::computeRawFilledArea( const ZONE* aZone,
if( m_progressReporter && m_progressReporter->IsCancelled() ) if( m_progressReporter && m_progressReporter->IsCancelled() )
return false; return false;
knockoutThermalReliefs( aZone, aLayer, aRawPolys ); knockoutThermalReliefs( aZone, aLayer, aRawPolys, thermalConnectionPads, noConnectionPads );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In2_Cu, "minus-thermal-reliefs" ); DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In2_Cu, "minus-thermal-reliefs" );
if( m_progressReporter && m_progressReporter->IsCancelled() ) if( m_progressReporter && m_progressReporter->IsCancelled() )
return false; return false;
buildCopperItemClearances( aZone, aLayer, clearanceHoles ); buildCopperItemClearances( aZone, aLayer, noConnectionPads, clearanceHoles );
DUMP_POLYS_TO_COPPER_LAYER( clearanceHoles, In3_Cu, "clearance-holes" ); DUMP_POLYS_TO_COPPER_LAYER( clearanceHoles, In3_Cu, "clearance-holes" );
if( m_progressReporter && m_progressReporter->IsCancelled() ) if( m_progressReporter && m_progressReporter->IsCancelled() )
return false; return false;
buildThermalSpokes( aZone, aLayer, thermalSpokes ); buildThermalSpokes( aZone, aLayer, thermalConnectionPads, thermalSpokes );
if( m_progressReporter && m_progressReporter->IsCancelled() ) if( m_progressReporter && m_progressReporter->IsCancelled() )
return false; return false;
@ -1298,118 +1291,118 @@ bool ZONE_FILLER::fillSingleZone( ZONE* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_S
* Function buildThermalSpokes * Function buildThermalSpokes
*/ */
void ZONE_FILLER::buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer, void ZONE_FILLER::buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer,
const std::vector<PAD*>& aSpokedPadsList,
std::deque<SHAPE_LINE_CHAIN>& aSpokesList ) std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
{ {
auto zoneBB = aZone->GetCachedBoundingBox(); BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
int zone_clearance = aZone->GetLocalClearance(); EDA_RECT zoneBB = aZone->GetCachedBoundingBox();
int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); DRC_CONSTRAINT constraint;
biggest_clearance = std::max( biggest_clearance, zone_clearance );
zoneBB.Inflate( biggest_clearance ); zoneBB.Inflate( std::max( bds.GetBiggestClearanceValue(), aZone->GetLocalClearance() ) );
// Is a point on the boundary of the polygon inside or outside? This small epsilon lets // Is a point on the boundary of the polygon inside or outside? This small epsilon lets
// us avoid the question. // us avoid the question.
int epsilon = KiROUND( IU_PER_MM * 0.04 ); // about 1.5 mil int epsilon = KiROUND( IU_PER_MM * 0.04 ); // about 1.5 mil
for( FOOTPRINT* footprint : m_board->Footprints() ) for( PAD* pad : aSpokedPadsList )
{ {
for( PAD* pad : footprint->Pads() ) // We currently only connect to pads, not pad holes
if( !pad->IsOnLayer( aLayer ) )
continue;
constraint = bds.m_DRCEngine->EvalRules( THERMAL_RELIEF_GAP_CONSTRAINT, pad, aZone, aLayer );
int thermalReliefGap = constraint.GetValue().Min();
constraint = bds.m_DRCEngine->EvalRules( THERMAL_SPOKE_WIDTH_CONSTRAINT, pad, aZone, aLayer );
int spoke_w = constraint.GetValue().Opt();
// Spoke width should ideally be smaller than the pad minor axis.
spoke_w = std::min( spoke_w, pad->GetSize().x );
spoke_w = std::min( spoke_w, pad->GetSize().y );
spoke_w = std::max( spoke_w, constraint.Value().Min() );
spoke_w = std::min( spoke_w, constraint.Value().Max() );
// Cannot create stubs having a width < zone min thickness
if( spoke_w < aZone->GetMinThickness() )
continue;
int spoke_half_w = spoke_w / 2;
// Quick test here to possibly save us some work
BOX2I itemBB = pad->GetBoundingBox();
itemBB.Inflate( thermalReliefGap + epsilon );
if( !( itemBB.Intersects( zoneBB ) ) )
continue;
// Thermal spokes consist of segments from the pad center to points just outside
// the thermal relief.
wxPoint shapePos = pad->ShapePos();
double spokesAngle = pad->GetOrientation() + pad->GetThermalSpokeAngle();
while( spokesAngle >= 900.0 )
spokesAngle -= 900.0;
while( spokesAngle < 0.0 )
spokesAngle += 900.0;
// We use the bounding-box to lay out the spokes, but for this to work the
// bounding box has to be built at the same rotation as the spokes.
// We have to use a dummy pad to avoid dirtying the cached shapes
PAD dummy_pad( *pad );
dummy_pad.SetOrientation( spokesAngle );
// Spokes are from center of pad, not from hole
dummy_pad.SetPosition( -pad->GetOffset() );
BOX2I reliefBB = dummy_pad.GetBoundingBox();
reliefBB.Inflate( thermalReliefGap + epsilon );
for( int i = 0; i < 4; i++ )
{ {
if( !hasThermalConnection( pad, aZone ) ) SHAPE_LINE_CHAIN spoke;
continue; switch( i )
// We currently only connect to pads, not pad holes
if( !pad->IsOnLayer( aLayer ) )
continue;
int thermalReliefGap = aZone->GetThermalReliefGap( pad );
// Calculate thermal bridge half width
int spoke_w = aZone->GetThermalReliefSpokeWidth( pad );
// Avoid spoke_w bigger than the smaller pad size, because
// it is not possible to create stubs bigger than the pad.
// Possible refinement: have a separate size for vertical and horizontal stubs
spoke_w = std::min( spoke_w, pad->GetSize().x );
spoke_w = std::min( spoke_w, pad->GetSize().y );
// Cannot create stubs having a width < zone min thickness
if( spoke_w < aZone->GetMinThickness() )
continue;
int spoke_half_w = spoke_w / 2;
// Quick test here to possibly save us some work
BOX2I itemBB = pad->GetBoundingBox();
itemBB.Inflate( thermalReliefGap + epsilon );
if( !( itemBB.Intersects( zoneBB ) ) )
continue;
// Thermal spokes consist of segments from the pad center to points just outside
// the thermal relief.
//
// We use the bounding-box to lay out the spokes, but for this to work the
// bounding box has to be built at the same rotation as the spokes.
// We have to use a dummy pad to avoid dirtying the cached shapes
wxPoint shapePos = pad->ShapePos();
double padAngle = pad->GetOrientation();
PAD dummy_pad( *pad );
dummy_pad.SetOrientation( 0.0 );
// Spokes are from center of pad, not from hole
dummy_pad.SetPosition( -pad->GetOffset() );
BOX2I reliefBB = dummy_pad.GetBoundingBox();
reliefBB.Inflate( thermalReliefGap + epsilon );
// For circle pads, the thermal spoke orientation is 45 deg
if( pad->GetShape() == PAD_SHAPE::CIRCLE )
padAngle = s_RoundPadThermalSpokeAngle;
for( int i = 0; i < 4; i++ )
{ {
SHAPE_LINE_CHAIN spoke; case 0: // lower stub
switch( i ) spoke.Append( +spoke_half_w, -spoke_half_w );
{ spoke.Append( -spoke_half_w, -spoke_half_w );
case 0: // lower stub spoke.Append( -spoke_half_w, reliefBB.GetBottom() );
spoke.Append( +spoke_half_w, -spoke_half_w ); spoke.Append( 0, reliefBB.GetBottom() ); // test pt
spoke.Append( -spoke_half_w, -spoke_half_w ); spoke.Append( +spoke_half_w, reliefBB.GetBottom() );
spoke.Append( -spoke_half_w, reliefBB.GetBottom() ); break;
spoke.Append( 0, reliefBB.GetBottom() ); // test pt
spoke.Append( +spoke_half_w, reliefBB.GetBottom() );
break;
case 1: // upper stub case 1: // upper stub
spoke.Append( +spoke_half_w, spoke_half_w ); spoke.Append( +spoke_half_w, spoke_half_w );
spoke.Append( -spoke_half_w, spoke_half_w ); spoke.Append( -spoke_half_w, spoke_half_w );
spoke.Append( -spoke_half_w, reliefBB.GetTop() ); spoke.Append( -spoke_half_w, reliefBB.GetTop() );
spoke.Append( 0, reliefBB.GetTop() ); // test pt spoke.Append( 0, reliefBB.GetTop() ); // test pt
spoke.Append( +spoke_half_w, reliefBB.GetTop() ); spoke.Append( +spoke_half_w, reliefBB.GetTop() );
break; break;
case 2: // right stub case 2: // right stub
spoke.Append( -spoke_half_w, spoke_half_w ); spoke.Append( -spoke_half_w, spoke_half_w );
spoke.Append( -spoke_half_w, -spoke_half_w ); spoke.Append( -spoke_half_w, -spoke_half_w );
spoke.Append( reliefBB.GetRight(), -spoke_half_w ); spoke.Append( reliefBB.GetRight(), -spoke_half_w );
spoke.Append( reliefBB.GetRight(), 0 ); // test pt spoke.Append( reliefBB.GetRight(), 0 ); // test pt
spoke.Append( reliefBB.GetRight(), spoke_half_w ); spoke.Append( reliefBB.GetRight(), spoke_half_w );
break; break;
case 3: // left stub case 3: // left stub
spoke.Append( spoke_half_w, spoke_half_w ); spoke.Append( spoke_half_w, spoke_half_w );
spoke.Append( spoke_half_w, -spoke_half_w ); spoke.Append( spoke_half_w, -spoke_half_w );
spoke.Append( reliefBB.GetLeft(), -spoke_half_w ); spoke.Append( reliefBB.GetLeft(), -spoke_half_w );
spoke.Append( reliefBB.GetLeft(), 0 ); // test pt spoke.Append( reliefBB.GetLeft(), 0 ); // test pt
spoke.Append( reliefBB.GetLeft(), spoke_half_w ); spoke.Append( reliefBB.GetLeft(), spoke_half_w );
break; break;
}
spoke.Rotate( -DECIDEG2RAD( padAngle ) );
spoke.Move( shapePos );
spoke.SetClosed( true );
spoke.GenerateBBoxCache();
aSpokesList.push_back( std::move( spoke ) );
} }
spoke.Rotate( -DECIDEG2RAD( pad->GetOrientation() + spokesAngle ) );
spoke.Move( shapePos );
spoke.SetClosed( true );
spoke.GenerateBBoxCache();
aSpokesList.push_back( std::move( spoke ) );
} }
} }
} }

View File

@ -63,9 +63,12 @@ private:
void addHoleKnockout( PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles ); void addHoleKnockout( PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles );
void knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aFill ); void knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aFill,
std::vector<PAD*>& aThermalConnectionPads,
std::vector<PAD*>& aNoConnectionPads );
void buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLayer, void buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLayer,
const std::vector<PAD*> aNoConnectionPads,
SHAPE_POLY_SET& aHoles ); SHAPE_POLY_SET& aHoles );
void subtractHigherPriorityZones( const ZONE* aZone, PCB_LAYER_ID aLayer, void subtractHigherPriorityZones( const ZONE* aZone, PCB_LAYER_ID aLayer,
@ -91,6 +94,7 @@ private:
* Constructs a list of all thermal spokes for the given zone. * Constructs a list of all thermal spokes for the given zone.
*/ */
void buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer, void buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer,
const std::vector<PAD*>& aSpokedPadsList,
std::deque<SHAPE_LINE_CHAIN>& aSpokes ); std::deque<SHAPE_LINE_CHAIN>& aSpokes );
/** /**

View File

@ -105,7 +105,7 @@ ZONE_SETTINGS& ZONE_SETTINGS::operator << ( const ZONE& aSource )
m_Name = aSource.GetZoneName(); m_Name = aSource.GetZoneName();
m_ZoneBorderDisplayStyle = aSource.GetHatchStyle(); m_ZoneBorderDisplayStyle = aSource.GetHatchStyle();
m_ThermalReliefGap = aSource.GetThermalReliefGap(); m_ThermalReliefGap = aSource.GetThermalReliefGap();
m_ThermalReliefSpokeWidth = aSource.GetThermalReliefSpokeWidth(); m_ThermalReliefSpokeWidth = aSource.GetThermalReliefSpokeWidth();
m_padConnection = aSource.GetPadConnection(); m_padConnection = aSource.GetPadConnection();
m_cornerSmoothingType = aSource.GetCornerSmoothingType(); m_cornerSmoothingType = aSource.GetCornerSmoothingType();
m_cornerRadius = aSource.GetCornerRadius(); m_cornerRadius = aSource.GetCornerRadius();

View File

@ -99,8 +99,8 @@ public:
/// Option to show the zone area (outlines only, short hatches or full hatches /// Option to show the zone area (outlines only, short hatches or full hatches
ZONE_BORDER_DISPLAY_STYLE m_ZoneBorderDisplayStyle; ZONE_BORDER_DISPLAY_STYLE m_ZoneBorderDisplayStyle;
long m_ThermalReliefGap; // thickness of the gap in thermal reliefs long m_ThermalReliefGap; // thickness of the gap in thermal reliefs
long m_ThermalReliefSpokeWidth; // thickness of the copper bridge in thermal reliefs long m_ThermalReliefSpokeWidth; // thickness of the copper bridge in thermal reliefs
bool m_Zone_45_Only; bool m_Zone_45_Only;
bool m_Locked; bool m_Locked;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2008-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2008-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -25,6 +25,8 @@
#ifndef ZONES_H_ #ifndef ZONES_H_
#define ZONES_H_ #define ZONES_H_
#include <wx/translation.h>
// Default values in mils for parameters in ZONE // Default values in mils for parameters in ZONE
#define ZONE_THERMAL_RELIEF_GAP_MIL 20 // default value for ZONE_SETTINGS::m_ThermalReliefGap #define ZONE_THERMAL_RELIEF_GAP_MIL 20 // default value for ZONE_SETTINGS::m_ThermalReliefGap
#define ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL 20 // default value for ZONE_SETTINGS::m_ThermalReliefCopperBridge #define ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL 20 // default value for ZONE_SETTINGS::m_ThermalReliefCopperBridge
@ -37,6 +39,7 @@
#define ZONE_EXPORT_VALUES 1004 // Copper zone dialog reports wxID_OK, wxID_CANCEL or #define ZONE_EXPORT_VALUES 1004 // Copper zone dialog reports wxID_OK, wxID_CANCEL or
// ZONE_EXPORT_VALUES // ZONE_EXPORT_VALUES
/// How pads are covered by copper in zone /// How pads are covered by copper in zone
enum class ZONE_CONNECTION enum class ZONE_CONNECTION
{ {
@ -47,6 +50,20 @@ enum class ZONE_CONNECTION
THT_THERMAL ///< Thermal relief only for THT pads THT_THERMAL ///< Thermal relief only for THT pads
}; };
inline wxString PrintZoneConnection( ZONE_CONNECTION aConnection )
{
switch( aConnection )
{
case ZONE_CONNECTION::INHERITED: return _( "inherited" );
case ZONE_CONNECTION::NONE: return _( "none" );
case ZONE_CONNECTION::THERMAL: return _( "thermal reliefs" );
case ZONE_CONNECTION::FULL: return _( "solid" );
case ZONE_CONNECTION::THT_THERMAL: return _( "thermal reliefs for PTH" );
}
}
class ZONE; class ZONE;
class ZONE_SETTINGS; class ZONE_SETTINGS;
class PCB_BASE_FRAME; class PCB_BASE_FRAME;

File diff suppressed because it is too large Load Diff