7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-20 20:11:41 +00:00

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
micro_via
min
none
npth
opt
outer
@ -29,9 +30,13 @@ pth
rule
silk_clearance
skew
solid
text
text_height
text_thickness
thermal_reliefs
thermal_relief_gap
thermal_spoke_width
track
track_width
version
@ -39,3 +44,4 @@ via
via_count
via_diameter
zone
zone_connection

View File

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

View File

@ -243,6 +243,7 @@ set( PCBNEW_DRC_SRCS
drc/drc_test_provider_misc.cpp
drc/drc_test_provider_text_dims.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_silk_to_mask.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_pasteMarginRatio( aParent, m_pasteMarginRatioLabel, m_pasteMarginRatioCtrl,
m_pasteMarginRatioUnits ),
m_spokeWidth( aParent, m_spokeWidthLabel, m_spokeWidthCtrl, m_spokeWidthUnits ),
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 )
{
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.SetPrecision( 3 );
m_spokeAngle.SetUnits( EDA_UNITS::DEGREES );
m_pasteMargin.SetNegativeZero();
m_pasteMarginRatio.SetUnits( EDA_UNITS::PERCENT );
@ -558,6 +561,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
m_clearance.ChangeValue( m_dummyPad->GetLocalClearance() );
m_maskMargin.ChangeValue( m_dummyPad->GetLocalSolderMaskMargin() );
m_spokeWidth.ChangeValue( m_dummyPad->GetThermalSpokeWidth() );
m_spokeAngle.ChangeDoubleValue( m_dummyPad->GetThermalSpokeAngle() );
m_thermalGap.ChangeValue( m_dummyPad->GetThermalGap() );
m_pasteMargin.ChangeValue( m_dummyPad->GetLocalSolderPasteMargin() );
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)
if( m_dummyPad->GetRoundRectRadiusRatio() == 0.0 )
m_cornerRatio.ChangeValue( 25 );
m_cornerRatio.ChangeDoubleValue( 25.0 );
break;
}
@ -854,6 +858,19 @@ void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
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
wxSize size = m_shapePropsBook->GetSize();
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;
enablePrimitivePage( is_custom );
m_staticTextcps->Enable( is_custom );
m_ZoneCustomPadShape->Enable( is_custom );
transferDataToPad( m_dummyPad );
@ -1396,7 +1411,7 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK()
if( m_dummyPad->GetShape() == PAD_SHAPE::ROUNDRECT ||
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 )
error_msgs.Add( _( "Error: Negative corner size." ) );
@ -1607,6 +1622,7 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
m_currentPad->SetLocalSolderPasteMargin( m_padMaster->GetLocalSolderPasteMargin() );
m_currentPad->SetLocalSolderPasteMarginRatio( m_padMaster->GetLocalSolderPasteMarginRatio() );
m_currentPad->SetThermalSpokeWidth( m_padMaster->GetThermalSpokeWidth() );
m_currentPad->SetThermalSpokeAngle( m_padMaster->GetThermalSpokeAngle() );
m_currentPad->SetThermalGap( m_padMaster->GetThermalGap() );
m_currentPad->SetRoundRectRadiusRatio( m_padMaster->GetRoundRectRadiusRatio() );
m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() );
@ -1706,6 +1722,7 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
aPad->SetLocalSolderPasteMargin( m_pasteMargin.GetValue() );
aPad->SetLocalSolderPasteMarginRatio( m_pasteMarginRatio.GetDoubleValue() / 100.0 );
aPad->SetThermalSpokeWidth( m_spokeWidth.GetValue() );
aPad->SetThermalSpokeAngle( m_spokeAngle.GetDoubleValue() );
aPad->SetThermalGap( m_thermalGap.GetValue() );
// And rotation
@ -1832,13 +1849,15 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
// reference
if( aPad->GetAnchorPadShape() == PAD_SHAPE::CIRCLE )
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() )
{
case PAD_ATTRIB::PTH:

View File

@ -156,12 +156,16 @@ private:
UNIT_BINDER m_cornerRadius;
UNIT_BINDER m_cornerRatio;
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_clearance;
UNIT_BINDER m_maskMargin;
UNIT_BINDER m_pasteMargin, m_pasteMarginRatio;
UNIT_BINDER m_spokeWidth, m_thermalGap;
UNIT_BINDER m_pasteMargin;
UNIT_BINDER m_pasteMarginRatio;
UNIT_BINDER m_thermalGap;
UNIT_BINDER m_spokeWidth;
UNIT_BINDER m_spokeAngle;
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_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;
gbSizerHole = new wxGridBagSizer( 4, 0 );
@ -579,20 +579,20 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
bSizerClearance = new wxBoxSizer( wxVERTICAL );
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;
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 );
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->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->Wrap( -1 );
sbClearancesSizer->Add( m_staticTextInfoNegVal, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 );
sbClearancesSizer->Add( m_staticTextInfoNegVal, 0, wxBOTTOM|wxRIGHT, 10 );
wxFlexGridSizer* fgClearancesGridSizer;
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->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 );
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 );
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->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 );
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 );
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->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 );
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 );
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->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 );
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 );
sbClearancesSizer->Add( fgClearancesGridSizer, 0, wxEXPAND, 5 );
sbClearancesSizer->Add( fgClearancesGridSizer, 0, 0, 5 );
m_nonCopperWarningBook = new wxSimplebook( sbClearancesSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
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->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->Wrap( -1 );
bNoteSizer->Add( m_staticTextInfoPaste, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 );
bNoteSizer->Add( m_staticTextInfoPaste, 0, wxBOTTOM|wxRIGHT, 5 );
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 );
wxBoxSizer* bSizerLower;
bSizerLower = new wxBoxSizer( wxHORIZONTAL );
m_sbSizerZonesSettings = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Connection to Copper Zones") ), wxVERTICAL );
wxFlexGridSizer* fgSizerCopperZonesOpts;
fgSizerCopperZonesOpts = new wxFlexGridSizer( 0, 3, 0, 0 );
fgSizerCopperZonesOpts = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizerCopperZonesOpts->AddGrowableCol( 1 );
fgSizerCopperZonesOpts->SetFlexibleDirection( wxBOTH );
fgSizerCopperZonesOpts->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText40 = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Pad connection:"), wxDefaultPosition, wxDefaultSize, 0 );
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") };
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->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 );
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 = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Zone knockout:"), wxDefaultPosition, wxDefaultSize, 0 );
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 );
m_ZoneCustomPadShape = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneCustomPadShapeNChoices, m_ZoneCustomPadShapeChoices, 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 );

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;
wxStaticText* m_staticText40;
wxChoice* m_ZoneConnectionChoice;
wxStaticText* m_spokeWidthLabel;
wxTextCtrl* m_spokeWidthCtrl;
wxStaticText* m_spokeWidthUnits;
wxStaticText* m_staticTextcps;
wxChoice* m_ZoneCustomPadShape;
wxStaticText* m_thermalGapLabel;
wxTextCtrl* m_thermalGapCtrl;
wxStaticText* m_thermalGapUnits;
wxStaticText* m_staticTextcps;
wxChoice* m_ZoneCustomPadShape;
wxStaticText* m_spokeWidthLabel;
wxTextCtrl* m_spokeWidthCtrl;
wxStaticText* m_spokeWidthUnits;
wxStaticText* m_spokeAngleLabel;
wxTextCtrl* m_spokeAngleCtrl;
wxStaticText* m_spokeAngleUnits;
wxPanel* m_panelCustomShapePrimitives;
wxBoxSizer* m_bSizerPanelPrimitives;
wxStaticText* m_staticTextPrimitivesList;

View File

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

View File

@ -5,7 +5,7 @@
(rule <rule_name> <rule_clause> ...)
<br><br>
<br>
### Rule Clauses
@ -16,7 +16,7 @@
(layer "<layer_name>")
<br><br>
<br>
### Constraint Types
@ -34,19 +34,22 @@
* skew
* text\_height
* text\_thickness
* thermal\_relief\_gap
* thermal\_spoke\_width
* track\_width
* via\_count
* via\_diameter
* zone\_connection
<br><br>
<br>
### Item Types
* buried_via
* buried\_via
* graphic
* hole
* micro_via
* micro\_via
* pad
* text
* track
@ -55,6 +58,14 @@
<br>
### Zone Connections
* solid
* thermal\_reliefs
* none
<br>
### Examples
(version 1)
@ -79,6 +90,11 @@
(rule HV_unshielded
(constraint clearance (min 2mm))
(condition "A.NetClass == 'HV' && !A.insideArea('Shield*')"))
(rule heavy_thermals
(constraint thermal_spoke_width (min 0.5mm))
(condition "A.NetClass == 'HV'))
<br><br>
### Notes
@ -203,3 +219,9 @@ For the latter use a `(layer "layer_name")` clause in the rule.
(rule "dp clearance"
(constraint clearance (min "1.5mm"))
(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 },
{ TRACK_WIDTH_CONSTRAINT, "track_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 },
{ LENGTH_CONSTRAINT, "length", 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,
const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
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
* 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 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;
constraint.m_Type = aConstraintType;
@ -859,6 +915,64 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
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 =
[&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool
@ -1149,6 +1263,72 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
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() )
{
@ -1157,11 +1337,10 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
}
return constraint;
}
#undef REPORT
#undef UNITS
#undef REPORT_VALUE
}
bool DRC_ENGINE::IsErrorLimitExceeded( int error_code )

View File

@ -150,6 +150,9 @@ public:
const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
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 );
EDA_UNITS UserUnits() const { return m_userUnits; }

View File

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

View File

@ -23,6 +23,7 @@
#include <board.h>
#include <zones.h>
#include <drc/drc_rule_parser.h>
#include <drc/drc_rule_condition.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_annular_width: c.m_Type = ANNULAR_WIDTH_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_length: c.m_Type = LENGTH_CONSTRAINT; break;
case T_skew: c.m_Type = SKEW_CONSTRAINT; break;
@ -286,8 +290,10 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
default:
msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
"clearance, hole_clearance, edge_clearance, hole_size, hole_to_hole, "
"courtyard_clearance, silk_clearance, track_width, annular_width, via_diameter, "
"disallow, length, skew, diff_pair_gap or diff_pair_uncoupled." );
"courtyard_clearance, silk_clearance, text_height, text_thickness, "
"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 );
}
@ -336,6 +342,36 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
aRule->AddConstraint( c );
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() )
{

View File

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

View File

@ -21,7 +21,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
//#include <common.h>
#include <pcb_track.h>
#include <drc/drc_engine.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_localSolderPasteMargin = 0;
m_localSolderPasteMarginRatio = 0.0;
m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use zone setting by default
m_thermalWidth = 0; // Use zone setting by default
m_thermalGap = 0; // Use zone setting by default
m_zoneConnection = ZONE_CONNECTION::INHERITED;
// These are special and mandatory text fields
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_localSolderPasteMarginRatio = aFootprint.m_localSolderPasteMarginRatio;
m_zoneConnection = aFootprint.m_zoneConnection;
m_thermalWidth = aFootprint.m_thermalWidth;
m_thermalGap = aFootprint.m_thermalGap;
std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
@ -291,8 +287,6 @@ FOOTPRINT& FOOTPRINT::operator=( FOOTPRINT&& aOther )
m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
m_zoneConnection = aOther.m_zoneConnection;
m_thermalWidth = aOther.m_thermalWidth;
m_thermalGap = aOther.m_thermalGap;
// Move reference and value
m_reference = aOther.m_reference;
@ -391,8 +385,6 @@ FOOTPRINT& FOOTPRINT::operator=( const FOOTPRINT& aOther )
m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
m_zoneConnection = aOther.m_zoneConnection;
m_thermalWidth = aOther.m_thermalWidth;
m_thermalGap = aOther.m_thermalGap;
// Copy reference and value
*m_reference = *aOther.m_reference;
@ -2439,14 +2431,6 @@ static struct FOOTPRINT_DESC
double>( _HKI( "Solderpaste Margin Ratio Override" ),
&FOOTPRINT::SetLocalSolderPasteMarginRatio,
&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?
}
} _FOOTPRINT_DESC;

View File

@ -227,12 +227,6 @@ public:
void SetZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; }
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; }
void SetAttributes( int aAttributes ) { m_attributes = aAttributes; }
@ -772,8 +766,6 @@ private:
mutable int m_hullCacheTimeStamp;
ZONE_CONNECTION m_zoneConnection;
int m_thermalWidth;
int m_thermalGap;
int m_localClearance;
int m_localSolderMaskMargin; // Solder mask margin
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_chamferPositions = RECT_NO_CHAMFER; // No chamfered corner
m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use parent setting by default
m_thermalWidth = 0; // Use parent setting by default
m_thermalGap = 0; // Use parent setting by default
m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use parent setting by default
m_thermalSpokeWidth = 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;
@ -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 && 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 )
if( m_zoneConnection != ZONE_CONNECTION::INHERITED && aSource )
*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 )
{
if( aSource )
*aSource = _( "parent footprint" );
return m_thermalSpokeWidth;
}
return parentFootprint->GetThermalGap();
}
if( aSource )
int PAD::GetLocalThermalGapOverride( wxString* aSource ) const
{
if( m_thermalGap > 0 && aSource )
*aSource = _( "pad" );
return m_thermalGap;
@ -1488,8 +1457,9 @@ void PAD::ImportSettingsFrom( const PAD& aMasterPad )
SetLocalSolderPasteMargin( aMasterPad.GetLocalSolderPasteMargin() );
SetLocalSolderPasteMarginRatio( aMasterPad.GetLocalSolderPasteMarginRatio() );
SetZoneConnection( aMasterPad.GetEffectiveZoneConnection() );
SetZoneConnection( aMasterPad.GetZoneConnection() );
SetThermalSpokeWidth( aMasterPad.GetThermalSpokeWidth() );
SetThermalSpokeAngle( aMasterPad.GetThermalSpokeAngle() );
SetThermalGap( aMasterPad.GetThermalGap() );
SetCustomShapeInZoneOpt( aMasterPad.GetCustomShapeInZoneOpt() );
@ -1705,9 +1675,12 @@ static struct PAD_DESC
PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Solderpaste Margin Ratio Override" ),
&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,
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" ),
&PAD::SetThermalGap, &PAD::GetThermalGap,
PROPERTY_DISPLAY::DISTANCE ) );

View File

@ -475,33 +475,29 @@ public:
void SetZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; }
ZONE_CONNECTION GetZoneConnection() const { return m_zoneConnection; }
/**
* 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;
ZONE_CONNECTION GetLocalZoneConnectionOverride( wxString* aSource = nullptr ) const;
/**
* Set the width of the thermal spokes connecting the pad to a zone. If != 0 this will
* override similar settings in the parent footprint and zone.
*/
void SetThermalSpokeWidth( int aWidth ) { m_thermalWidth = aWidth; }
int GetThermalSpokeWidth() const { return m_thermalWidth; }
void SetThermalSpokeWidth( int aWidth ) { m_thermalSpokeWidth = aWidth; }
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; }
int GetThermalGap() const { return m_thermalGap; }
/**
* Return the effective thermal gap having resolved any inheritance.
*/
int GetEffectiveThermalGap( wxString* aSource = nullptr ) const;
int GetLocalThermalGapOverride( wxString* aSource = nullptr ) const;
/**
* Has meaning only for rounded rectangle pads.
@ -775,7 +771,9 @@ private:
// The final margin is the sum of these 2 values
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;
};

View File

@ -3402,12 +3402,9 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
break;
case T_thermal_width:
footprint->SetThermalWidth( parseBoardUnits( "thermal width value" ) );
NeedRIGHT();
break;
case T_thermal_gap:
footprint->SetThermalGap( parseBoardUnits( "thermal gap value" ) );
// Interestingly, these have never been exposed in the GUI
parseBoardUnits( token );
NeedRIGHT();
break;
@ -3526,7 +3523,7 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
"autoplace_cost90, autoplace_cost180, solder_mask_margin, "
"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, "
"zone, group, generator, version or model" );
}
@ -4102,6 +4099,13 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
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() )
{
if( token == T_locked )
@ -4289,13 +4293,20 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
NeedRIGHT();
break;
case T_thermal_width:
pad->SetThermalSpokeWidth( parseBoardUnits( T_thermal_width ) );
case T_thermal_width: // legacy token
case T_thermal_bridge_width:
pad->SetThermalSpokeWidth( parseBoardUnits( token ) );
NeedRIGHT();
break;
case T_thermal_bridge_angle:
pad->SetThermalSpokeAngle( parseAngle( "thermal spoke angle value" ) );
NeedRIGHT();
break;
case T_thermal_gap:
pad->SetThermalGap( parseBoardUnits( T_thermal_gap ) );
pad->SetThermalGap( parseBoardUnits( "thermal relief gap value" ) );
NeedRIGHT();
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",
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
if( aFootprint->GetAttributes() )
{
@ -1586,10 +1578,17 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
if( aPad->GetThermalSpokeWidth() != 0 )
{
StrPrintf( &output, " (thermal_width %s)",
StrPrintf( &output, " (thermal_bridge_width %s)",
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 )
{
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 20210925 // Locked flag for fp_text
//#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 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" ) )
{
BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) );
aFootprint->SetThermalWidth( tmp );
ignore_unused( tmp );
}
else if( TESTLINE( ".ThermalGap" ) )
{
BIU tmp = biuParse( line + SZ( ".ThermalGap" ) );
aFootprint->SetThermalGap( tmp );
ignore_unused( tmp );
}
else if( TESTLINE( "$EndMODULE" ) )
{

Some files were not shown because too many files have changed in this diff Show More