Enforce hole clearance on NPTHs in zones.

Fixes https://gitlab.com/kicad/code/kicad/issues/8924
This commit is contained in:
Jeff Young 2021-08-06 13:15:01 +01:00
parent a10f2b284c
commit bafa105ed0
3 changed files with 135 additions and 78 deletions

View File

@ -40,6 +40,7 @@
#include <geometry/shape.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_null.h>
#include <convert_basic_shapes_to_polygon.h>
void drcPrintDebugMessage( int level, const wxString& msg, const char *function, int line )
{
@ -163,32 +164,35 @@ void DRC_ENGINE::loadImplicitRules()
diameterConstraint.Value().SetMin( bds.m_ViasMinSize );
rule->AddConstraint( diameterConstraint );
DRC_CONSTRAINT edgeClearanceConstraint( EDGE_CLEARANCE_CONSTRAINT );
edgeClearanceConstraint.Value().SetMin( bds.m_CopperEdgeClearance );
rule->AddConstraint( edgeClearanceConstraint );
DRC_CONSTRAINT holeClearanceConstraint( HOLE_CLEARANCE_CONSTRAINT );
holeClearanceConstraint.Value().SetMin( bds.m_HoleClearance );
rule->AddConstraint( holeClearanceConstraint );
DRC_CONSTRAINT holeToHoleConstraint( HOLE_TO_HOLE_CONSTRAINT );
holeToHoleConstraint.Value().SetMin( bds.m_HoleToHoleMin );
rule->AddConstraint( holeToHoleConstraint );
DRC_CONSTRAINT courtyardClearanceConstraint( COURTYARD_CLEARANCE_CONSTRAINT );
holeToHoleConstraint.Value().SetMin( 0 );
rule->AddConstraint( courtyardClearanceConstraint );
DRC_CONSTRAINT diffPairGapConstraint( DIFF_PAIR_GAP_CONSTRAINT );
diffPairGapConstraint.Value().SetMin( bds.m_MinClearance );
rule->AddConstraint( diffPairGapConstraint );
rule = createImplicitRule( _( "board setup constraints" ) );
rule = createImplicitRule( _( "board setup constraints silk" ) );
rule->m_LayerCondition = LSET( 2, F_SilkS, B_SilkS );
DRC_CONSTRAINT silkClearanceConstraint( SILK_CLEARANCE_CONSTRAINT );
silkClearanceConstraint.Value().SetMin( bds.m_SilkClearance );
rule->AddConstraint( silkClearanceConstraint );
rule = createImplicitRule( _( "board setup constraints hole" ) );
DRC_CONSTRAINT holeClearanceConstraint( HOLE_CLEARANCE_CONSTRAINT );
holeClearanceConstraint.Value().SetMin( bds.m_HoleClearance );
rule->AddConstraint( holeClearanceConstraint );
rule = createImplicitRule( _( "board setup constraints edge" ) );
DRC_CONSTRAINT edgeClearanceConstraint( EDGE_CLEARANCE_CONSTRAINT );
edgeClearanceConstraint.Value().SetMin( bds.m_CopperEdgeClearance );
rule->AddConstraint( edgeClearanceConstraint );
rule = createImplicitRule( _( "board setup constraints courtyard" ) );
DRC_CONSTRAINT courtyardClearanceConstraint( COURTYARD_CLEARANCE_CONSTRAINT );
holeToHoleConstraint.Value().SetMin( 0 );
rule->AddConstraint( courtyardClearanceConstraint );
// 2) micro-via specific defaults (new DRC doesn't treat microvias in any special way)
DRC_RULE* uViaRule = createImplicitRule( _( "board setup micro-via constraints" ) );
@ -758,12 +762,60 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aT
}
DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOARD_ITEM* a,
bool isUnflashedNPTH( const BOARD_ITEM* aItem, PCB_LAYER_ID aLayer )
{
if( !aItem || aItem->Type() != PCB_PAD_T )
return false;
const PAD* pad = static_cast<const PAD*>( aItem );
if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
return false;
// Run a couple of quick tests to see if there's any copper
if( pad->GetShape() == PAD_SHAPE::CIRCLE && pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE
&& pad->GetSizeX() <= pad->GetDrillSizeX() )
{
return true;
}
if( pad->GetShape() == PAD_SHAPE::OVAL && pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG
&& pad->GetSizeX() <= pad->GetDrillSizeX()
&& pad->GetSizeY() <= pad->GetDrillSizeY() )
{
return true;
}
if( !pad->FlashLayer( aLayer ) )
return true;
// We're out of optimizations. Do it the hard way.
SHAPE_POLY_SET padOutline;
const SHAPE_SEGMENT* drillShape = pad->GetEffectiveHoleShape();
const SEG drillSeg = drillShape->GetSeg();
SHAPE_POLY_SET drillOutline;
pad->TransformShapeWithClearanceToPolygon( padOutline, aLayer, 0, ARC_HIGH_DEF,
ERROR_LOC::ERROR_INSIDE );
TransformOvalToPolygon( drillOutline, (wxPoint) drillSeg.A, (wxPoint) drillSeg.B,
drillShape->GetWidth(), ARC_HIGH_DEF, ERROR_LOC::ERROR_INSIDE );
padOutline.BooleanSubtract( drillOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
return padOutline.IsEmpty();
}
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).
@ -781,7 +833,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
bool implicit = false;
// Local overrides take precedence over everything *except* board min clearance
if( aConstraintId == CLEARANCE_CONSTRAINT )
if( aConstraintType == CLEARANCE_CONSTRAINT )
{
int overrideA = 0;
int overrideB = 0;
@ -793,7 +845,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
REPORT( "" )
REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
EscapeHTML( a->GetSelectMenuText( UNITS ) ),
EscapeHTML( MessageTextFromValue( UNITS, overrideA ) ) ) )
EscapeHTML( REPORT_VALUE( overrideA ) ) ) )
}
if( bc && !a_is_non_copper && bc->GetLocalClearanceOverrides( nullptr ) > 0 )
@ -803,23 +855,37 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
REPORT( "" )
REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
EscapeHTML( b->GetSelectMenuText( UNITS ) ),
EscapeHTML( MessageTextFromValue( UNITS, overrideB ) ) ) )
EscapeHTML( REPORT_VALUE( overrideB ) ) ) )
}
if( overrideA || overrideB )
{
int override = std::max( overrideA, overrideB );
if( override < m_designSettings->m_MinClearance )
if( isUnflashedNPTH( a, aLayer ) || isUnflashedNPTH( b, aLayer ) )
{
override = m_designSettings->m_MinClearance;
if( override < m_designSettings->m_HoleClearance )
{
override = m_designSettings->m_HoleClearance;
REPORT( "" )
REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
EscapeHTML( MessageTextFromValue( UNITS, override ) ) ) )
REPORT( "" )
REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ),
EscapeHTML( REPORT_VALUE( override ) ) ) )
}
}
else
{
if( override < m_designSettings->m_MinClearance )
{
override = m_designSettings->m_MinClearance;
REPORT( "" )
REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
EscapeHTML( REPORT_VALUE( override ) ) ) )
}
}
DRC_CONSTRAINT constraint( aConstraintId, m_msg );
DRC_CONSTRAINT constraint( aConstraintType, m_msg );
constraint.m_Value.SetMin( override );
return constraint;
}
@ -832,47 +898,47 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
REPORT( "" )
if( aConstraintId == CLEARANCE_CONSTRAINT )
if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
{
int val = c->constraint.m_Value.Min();
REPORT( wxString::Format( _( "Checking %s; clearance: %s." ),
REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( MessageTextFromValue( UNITS, val ) ) ) )
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else if( aConstraintId == COURTYARD_CLEARANCE_CONSTRAINT )
else if( c->constraint.m_Type == COURTYARD_CLEARANCE_CONSTRAINT )
{
int val = c->constraint.m_Value.Min();
REPORT( wxString::Format( _( "Checking %s; courtyard clearance: %s." ),
REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( MessageTextFromValue( UNITS, val ) ) ) )
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else if( aConstraintId == SILK_CLEARANCE_CONSTRAINT )
else if( c->constraint.m_Type == SILK_CLEARANCE_CONSTRAINT )
{
int val = c->constraint.m_Value.Min();
REPORT( wxString::Format( _( "Checking %s; silk clearance: %s." ),
REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( MessageTextFromValue( UNITS, val ) ) ) )
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else if( aConstraintId == HOLE_CLEARANCE_CONSTRAINT )
else if( c->constraint.m_Type == HOLE_CLEARANCE_CONSTRAINT )
{
int val = c->constraint.m_Value.Min();
REPORT( wxString::Format( _( "Checking %s; hole clearance: %s." ),
REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( MessageTextFromValue( UNITS, val ) ) ) )
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else if( aConstraintId == EDGE_CLEARANCE_CONSTRAINT )
else if( c->constraint.m_Type == EDGE_CLEARANCE_CONSTRAINT )
{
int val = c->constraint.m_Value.Min();
REPORT( wxString::Format( _( "Checking %s; edge clearance: %s." ),
REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( MessageTextFromValue( UNITS, val ) ) ) )
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else
{
REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
}
if( aConstraintId == CLEARANCE_CONSTRAINT )
if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
{
if( implicit && ( a_is_non_copper || b_is_non_copper ) )
{
@ -881,7 +947,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
return true;
}
}
else if( aConstraintId == DISALLOW_CONSTRAINT )
else if( c->constraint.m_Type == DISALLOW_CONSTRAINT )
{
int mask;
@ -891,12 +957,14 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
}
else if( a->Type() == PCB_VIA_T )
{
if( static_cast<const PCB_VIA*>( a )->GetViaType() == VIATYPE::BLIND_BURIED )
mask = DRC_DISALLOW_VIAS | DRC_DISALLOW_BB_VIAS;
else if( static_cast<const PCB_VIA*>( a )->GetViaType() == VIATYPE::MICROVIA )
mask = DRC_DISALLOW_VIAS | DRC_DISALLOW_MICRO_VIAS;
else
mask = DRC_DISALLOW_VIAS;
mask = DRC_DISALLOW_VIAS;
switch( static_cast<const PCB_VIA*>( a )->GetViaType() )
{
case VIATYPE::BLIND_BURIED: mask |= DRC_DISALLOW_BB_VIAS; break;
case VIATYPE::MICROVIA: mask |= DRC_DISALLOW_MICRO_VIAS; break;
default: break;
}
}
else
{
@ -1018,9 +1086,17 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
}
};
if( m_constraintMap.count( aConstraintId ) )
DRC_CONSTRAINT_T effectiveConstraintType = aConstraintType;
if( aConstraintType == CLEARANCE_CONSTRAINT
&& ( isUnflashedNPTH( a, aLayer ) || isUnflashedNPTH( b, aLayer ) ) )
{
std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintId ];
effectiveConstraintType = HOLE_CLEARANCE_CONSTRAINT;
}
if( m_constraintMap.count( effectiveConstraintType ) )
{
std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ effectiveConstraintType ];
if( aReporter )
{
@ -1046,7 +1122,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
// Unfortunately implicit rules don't work for local clearances (such as zones) because
// they have to be max'ed with netclass values (which are already implicit rules), and our
// rule selection paradigm is "winner takes all".
if( aConstraintId == CLEARANCE_CONSTRAINT && !explicitConstraintFound )
if( aConstraintType == CLEARANCE_CONSTRAINT && !explicitConstraintFound )
{
int global = constraintRef ? constraintRef->m_Value.Min() : 0;
int localA = ac ? ac->GetLocalClearance( nullptr ) : 0;
@ -1058,7 +1134,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
REPORT( "" )
REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ),
EscapeHTML( a->GetSelectMenuText( UNITS ) ),
EscapeHTML( MessageTextFromValue( UNITS, localA ) ) ) )
EscapeHTML( REPORT_VALUE( localA ) ) ) )
if( localA > clearance )
clearance = ac->GetLocalClearance( &m_msg );
@ -1069,7 +1145,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
REPORT( "" )
REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ),
EscapeHTML( b->GetSelectMenuText( UNITS ) ),
EscapeHTML( MessageTextFromValue( UNITS, localB ) ) ) )
EscapeHTML( REPORT_VALUE( localB ) ) ) )
if( localB > clearance )
clearance = bc->GetLocalClearance( &m_msg );
@ -1090,6 +1166,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOAR
#undef REPORT
#undef UNITS
#undef REPORT_VALUE
}

View File

@ -146,7 +146,7 @@ public:
bool IsErrorLimitExceeded( int error_code );
DRC_CONSTRAINT EvalRules( DRC_CONSTRAINT_T aConstraintId, const BOARD_ITEM* a,
DRC_CONSTRAINT EvalRules( DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM* a,
const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
REPORTER* aReporter = nullptr );

View File

@ -343,7 +343,6 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
{
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
const PCB_SELECTION& selection = selTool->GetSelection();
PCB_LAYER_ID layer = m_frame->GetActiveLayer();
if( selection.Size() != 2 )
{
@ -406,32 +405,13 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
// a and b could be null after group tests above.
wxCHECK( a && b, 0 );
if( a->Type() == PCB_TRACE_T || a->Type() == PCB_ARC_T )
{
layer = a->GetLayer();
}
else if( b->Type() == PCB_TRACE_T || b->Type() == PCB_ARC_T )
{
layer = b->GetLayer();
}
else if( a->Type() == PCB_PAD_T && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
{
PAD* pad = static_cast<PAD*>( a );
PCB_LAYER_ID layer;
LSET intersection = a->GetLayerSet() & b->GetLayerSet();
if( pad->IsOnLayer( F_Cu ) )
layer = F_Cu;
else
layer = B_Cu;
}
else if( b->Type() == PCB_PAD_T && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
{
PAD* pad = static_cast<PAD*>( b );
if( pad->IsOnLayer( F_Cu ) )
layer = F_Cu;
else
layer = B_Cu;
}
if( intersection.any() && !intersection.Contains( m_frame->GetActiveLayer() ) )
layer = intersection.Seq().front();
else
layer = m_frame->GetActiveLayer();
if( a->Type() != PCB_ZONE_T && b->Type() == PCB_ZONE_T )
std::swap( a, b );