Convert hole clearance tests from NPTH holes to all holes.

This commit is contained in:
Jeff Young 2021-08-09 22:25:16 +01:00
parent 6b48825aa0
commit a208dac8d8
8 changed files with 426 additions and 306 deletions

View File

@ -762,53 +762,6 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aT
} }
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, 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 )
@ -845,7 +798,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
REPORT( "" ) REPORT( "" )
REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ), REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
EscapeHTML( a->GetSelectMenuText( UNITS ) ), EscapeHTML( a->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( overrideA ) ) ) ) REPORT_VALUE( overrideA ) ) )
} }
if( bc && !a_is_non_copper && bc->GetLocalClearanceOverrides( nullptr ) > 0 ) if( bc && !a_is_non_copper && bc->GetLocalClearanceOverrides( nullptr ) > 0 )
@ -855,34 +808,20 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
REPORT( "" ) REPORT( "" )
REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ), REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ),
EscapeHTML( b->GetSelectMenuText( UNITS ) ), EscapeHTML( b->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( overrideB ) ) ) ) REPORT_VALUE( overrideB ) ) )
} }
if( overrideA || overrideB ) if( overrideA || overrideB )
{ {
int override = std::max( overrideA, overrideB ); int override = std::max( overrideA, overrideB );
if( isUnflashedNPTH( a, aLayer ) || isUnflashedNPTH( b, aLayer ) ) if( override < m_designSettings->m_MinClearance )
{ {
if( override < m_designSettings->m_HoleClearance ) override = m_designSettings->m_MinClearance;
{
override = m_designSettings->m_HoleClearance;
REPORT( "" ) REPORT( "" )
REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ), REPORT( wxString::Format( _( "Board minimum clearance: %s." ),
EscapeHTML( REPORT_VALUE( override ) ) ) ) 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( aConstraintType, m_msg ); DRC_CONSTRAINT constraint( aConstraintType, m_msg );
@ -898,44 +837,73 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
REPORT( "" ) REPORT( "" )
if( c->constraint.m_Type == CLEARANCE_CONSTRAINT ) switch( c->constraint.m_Type )
{
case CLEARANCE_CONSTRAINT:
case COURTYARD_CLEARANCE_CONSTRAINT:
case SILK_CLEARANCE_CONSTRAINT:
case HOLE_CLEARANCE_CONSTRAINT:
case EDGE_CLEARANCE_CONSTRAINT:
{ {
int val = c->constraint.m_Value.Min(); 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( c->constraint.GetName() ),
EscapeHTML( REPORT_VALUE( val ) ) ) ) REPORT_VALUE( val ) ) )
break;
} }
else if( c->constraint.m_Type == COURTYARD_CLEARANCE_CONSTRAINT )
case TRACK_WIDTH_CONSTRAINT:
case ANNULAR_WIDTH_CONSTRAINT:
case VIA_DIAMETER_CONSTRAINT:
{ {
int val = c->constraint.m_Value.Min(); if( aReporter )
REPORT( wxString::Format( _( "Checking %s clearance: %s." ), {
EscapeHTML( c->constraint.GetName() ), wxString min = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
EscapeHTML( REPORT_VALUE( val ) ) ) ) wxString opt = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
wxString max = wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
wxString msg;
if( implicit )
{
opt = StringFromValue( UNITS, c->constraint.m_Value.Opt(), true );
switch( c->constraint.m_Type )
{
case TRACK_WIDTH_CONSTRAINT: msg = "track width"; break;
case ANNULAR_WIDTH_CONSTRAINT: msg = "annular width"; break;
case VIA_DIAMETER_CONSTRAINT: msg = "via diameter"; break;
default: msg = "constraint"; break;
}
REPORT( wxString::Format( _( "Checking %s %s: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( msg ),
opt ) )
}
else
{
if( c->constraint.m_Value.HasMin() )
min = StringFromValue( UNITS, c->constraint.m_Value.Min(), true );
if( c->constraint.m_Value.HasOpt() )
opt = StringFromValue( UNITS, c->constraint.m_Value.Opt(), true );
if( c->constraint.m_Value.HasMax() )
max = StringFromValue( UNITS, c->constraint.m_Value.Max(), true );
REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ),
EscapeHTML( c->constraint.GetName() ),
min,
opt,
max ) )
}
}
break;
} }
else if( c->constraint.m_Type == SILK_CLEARANCE_CONSTRAINT )
{ default:
int val = c->constraint.m_Value.Min(); REPORT( wxString::Format( _( "Checking %s." ),
REPORT( wxString::Format( _( "Checking %s clearance: %s." ), EscapeHTML( c->constraint.GetName() ) ) )
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else if( c->constraint.m_Type == HOLE_CLEARANCE_CONSTRAINT )
{
int val = c->constraint.m_Value.Min();
REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else if( c->constraint.m_Type == EDGE_CLEARANCE_CONSTRAINT )
{
int val = c->constraint.m_Value.Min();
REPORT( wxString::Format( _( "Checking %s clearance: %s." ),
EscapeHTML( c->constraint.GetName() ),
EscapeHTML( REPORT_VALUE( val ) ) ) )
}
else
{
REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
} }
if( c->constraint.m_Type == CLEARANCE_CONSTRAINT ) if( c->constraint.m_Type == CLEARANCE_CONSTRAINT )
@ -1086,17 +1054,9 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
} }
}; };
DRC_CONSTRAINT_T effectiveConstraintType = aConstraintType; if( m_constraintMap.count( aConstraintType ) )
if( aConstraintType == CLEARANCE_CONSTRAINT
&& ( isUnflashedNPTH( a, aLayer ) || isUnflashedNPTH( b, aLayer ) ) )
{ {
effectiveConstraintType = HOLE_CLEARANCE_CONSTRAINT; std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ aConstraintType ];
}
if( m_constraintMap.count( effectiveConstraintType ) )
{
std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ effectiveConstraintType ];
if( aReporter ) if( aReporter )
{ {
@ -1134,7 +1094,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
REPORT( "" ) REPORT( "" )
REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ), REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ),
EscapeHTML( a->GetSelectMenuText( UNITS ) ), EscapeHTML( a->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( localA ) ) ) ) REPORT_VALUE( localA ) ) )
if( localA > clearance ) if( localA > clearance )
clearance = ac->GetLocalClearance( &m_msg ); clearance = ac->GetLocalClearance( &m_msg );
@ -1145,7 +1105,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
REPORT( "" ) REPORT( "" )
REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ), REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ),
EscapeHTML( b->GetSelectMenuText( UNITS ) ), EscapeHTML( b->GetSelectMenuText( UNITS ) ),
EscapeHTML( REPORT_VALUE( localB ) ) ) ) REPORT_VALUE( localB ) ) )
if( localB > clearance ) if( localB > clearance )
clearance = bc->GetLocalClearance( &m_msg ); clearance = bc->GetLocalClearance( &m_msg );

View File

@ -1,7 +1,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) 2004-2020 KiCad Developers. * Copyright (C) 2004-2021 KiCad Developers.
* *
* 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
@ -22,7 +22,6 @@
*/ */
#include <common.h> #include <common.h>
#include <board.h>
#include <board_design_settings.h> #include <board_design_settings.h>
#include <footprint.h> #include <footprint.h>
#include <pcb_shape.h> #include <pcb_shape.h>
@ -32,7 +31,6 @@
#include <geometry/seg.h> #include <geometry/seg.h>
#include <geometry/shape_poly_set.h> #include <geometry/shape_poly_set.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h> #include <geometry/shape_segment.h>
#include <drc/drc_engine.h> #include <drc/drc_engine.h>
@ -43,9 +41,12 @@
#include <pcb_dimension.h> #include <pcb_dimension.h>
/* /*
Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their electrical clearance. Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their
electrical clearance.
Errors generated: Errors generated:
- DRCE_CLEARANCE - DRCE_CLEARANCE
- DRCE_HOLE_CLEARANCE
- DRCE_TRACKS_CROSSING - DRCE_TRACKS_CROSSING
- DRCE_ZONES_INTERSECT - DRCE_ZONES_INTERSECT
- DRCE_SHORTING_ITEMS - DRCE_SHORTING_ITEMS
@ -90,7 +91,7 @@ private:
void testPadClearances(); void testPadClearances();
void testZones(); void testZonesToZones();
void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ); void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
@ -239,14 +240,14 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::Run()
if( !reportPhase( _( "Checking copper zone clearances..." ) ) ) if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
return false; // DRC cancelled return false; // DRC cancelled
testZones(); testZonesToZones();
} }
else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT ) ) else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT ) )
{ {
if( !reportPhase( _( "Checking zones..." ) ) ) if( !reportPhase( _( "Checking zones..." ) ) )
return false; // DRC cancelled return false; // DRC cancelled
testZones(); testZonesToZones();
} }
reportRuleStatistics(); reportRuleStatistics();
@ -348,9 +349,9 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track,
track->GetLayer() ); track->GetLayer() );
clearance = constraint.GetValue().Min(); clearance = constraint.GetValue().Min();
if( clearance >= 0 && trackShape->Collide( holeShape.get(), if( clearance > 0 && trackShape->Collide( holeShape.get(),
std::max( 0, clearance - m_drcEpsilon ), std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) ) &actual, &pos ) )
{ {
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
@ -380,9 +381,6 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aItem
{ {
for( ZONE* zone : m_zones ) for( ZONE* zone : m_zones )
{ {
if( m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
break;
if( !zone->GetLayerSet().test( aLayer ) ) if( !zone->GetLayerSet().test( aLayer ) )
continue; continue;
@ -394,54 +392,111 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aItem
if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) ) if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) )
{ {
auto constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, zone, aLayer ); bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
int clearance = constraint.GetValue().Min(); bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
if( clearance < 0 ) if( !testClearance && !testHoles )
continue; return;
int actual; DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get();
VECTOR2I pos; EDA_RECT itemBBox = aItem->GetBoundingBox();
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get(); DRC_CONSTRAINT constraint;
EDA_RECT itemBBox = aItem->GetBoundingBox(); int clearance = -1;
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer ); int actual;
VECTOR2I pos;
if( aItem->Type() == PCB_PAD_T ) if( testClearance )
{ {
PAD* pad = static_cast<PAD*>( aItem ); constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, zone, aLayer );
clearance = constraint.GetValue().Min();
}
if( !pad->FlashLayer( aLayer ) ) if( clearance >= 0 )
{
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
if( aItem->Type() == PCB_PAD_T )
{ {
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) PAD* pad = static_cast<PAD*>( aItem );
continue;
const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape(); if( !pad->FlashLayer( aLayer ) )
int size = hole->GetWidth(); {
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
continue;
// Note: drill size represents finish size, which means the actual hole const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
// size is the plating thickness larger. int size = hole->GetWidth();
if( pad->GetAttribute() == PAD_ATTRIB::PTH )
size += m_board->GetDesignSettings().GetHolePlatingThickness();
itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size ); // Note: drill size represents finish size, which means the actual hole
// size is the plating thickness larger.
if( pad->GetAttribute() == PAD_ATTRIB::PTH )
size += m_board->GetDesignSettings().GetHolePlatingThickness();
itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
}
}
if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
clearance - m_drcEpsilon, &actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( aItem, zone );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos );
} }
} }
if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, if( testHoles && ( aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_PAD_T ) )
clearance - m_drcEpsilon, &actual, &pos ) )
{ {
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE ); std::unique_ptr<SHAPE_SEGMENT> holeShape;
m_msg.Printf( _( "(%s clearance %s; actual %s)" ), if( aItem->Type() == PCB_VIA_T )
constraint.GetName(), {
MessageTextFromValue( userUnits(), clearance ), PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
MessageTextFromValue( userUnits(), actual ) ); pos = via->GetPosition();
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); if( via->GetLayerSet().Contains( aLayer ) )
drce->SetItems( aItem, zone ); holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
drce->SetViolatingRule( constraint.GetParentRule() ); }
else if( aItem->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( aItem );
reportViolation( drce, (wxPoint) pos ); if( pad->GetDrillSize().x )
holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
}
if( holeShape )
{
constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, zone,
aLayer );
clearance = constraint.GetValue().Min();
if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
clearance - m_drcEpsilon, &actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( aItem, zone );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos );
}
}
} }
} }
} }
@ -533,17 +588,43 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa
testHoles = false; testHoles = false;
} }
PAD* otherPad = nullptr;
PCB_VIA* otherVia = nullptr;
if( other->Type() == PCB_PAD_T )
otherPad = static_cast<PAD*>( other );
if( other->Type() == PCB_VIA_T )
otherVia = static_cast<PCB_VIA*>( other );
if( !IsCopperLayer( layer ) )
testClearance = false;
// A NPTH has no cylinder, but it may still have pads on some layers // A NPTH has no cylinder, but it may still have pads on some layers
if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) ) if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) )
testClearance = false; testClearance = false;
if( !IsCopperLayer( layer ) ) if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( layer ) )
testClearance = false; testClearance = false;
// Track clearances are tested in testTrackClearances() // Track clearances are tested in testTrackClearances()
if( dynamic_cast<PCB_TRACK*>( other) ) if( dynamic_cast<PCB_TRACK*>( other) )
testClearance = false; testClearance = false;
// Pads of the same (defined) net get a waiver on clearance and hole tests
if( otherPad && pad->GetNetCode() && otherPad->GetNetCode() == pad->GetNetCode() )
{
testClearance = false;
testHoles = false;
}
if( !( pad->GetDrillSize().x > 0 )
&& !( otherPad && otherPad->GetDrillSize().x > 0 )
&& !( otherVia && otherVia->GetDrill() > 0 ) )
{
testHoles = false;
}
if( !testClearance && !testShorting && !testHoles ) if( !testClearance && !testShorting && !testHoles )
return false; return false;
@ -553,87 +634,28 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa
int actual; int actual;
VECTOR2I pos; VECTOR2I pos;
if( other->Type() == PCB_PAD_T ) if( otherPad && pad->SameLogicalPadAs( otherPad ) )
{ {
PAD* otherPad = static_cast<PAD*>( other );
// If pads are equivalent (ie: from the same footprint with the same pad number)... // If pads are equivalent (ie: from the same footprint with the same pad number)...
if( pad->SameLogicalPadAs( otherPad ) ) // ... and have nets...
// then they must be the same net
if( pad->GetNetCode() && otherPad->GetNetCode()
&& pad->GetNetCode() != otherPad->GetNetCode()
&& testShorting )
{ {
// ...and have nets, then they must be the same net std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
if( pad->GetNetCode() && otherPad->GetNetCode()
&& pad->GetNetCode() != otherPad->GetNetCode()
&& testShorting )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
m_msg.Printf( _( "(nets %s and %s)" ), m_msg.Printf( _( "(nets %s and %s)" ),
pad->GetNetname(), pad->GetNetname(),
otherPad->GetNetname() ); otherPad->GetNetname() );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( pad, otherPad ); drce->SetItems( pad, otherPad );
reportViolation( drce, otherPad->GetPosition() ); reportViolation( drce, otherPad->GetPosition() );
}
return true;
} }
if( testHoles && pad->FlashLayer( layer ) && otherPad->GetDrillSize().x ) return true;
{
constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, otherPad, layer );
clearance = constraint.GetValue().Min();
if( clearance >= 0 && padShape->Collide( otherPad->GetEffectiveHoleShape(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( pad, other );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos );
}
}
if( testHoles && otherPad->FlashLayer( layer ) && pad->GetDrillSize().x )
{
constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, otherPad, layer );
clearance = constraint.GetValue().Min();
if( clearance >= 0 && otherShape->Collide( pad->GetEffectiveHoleShape(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( pad, other );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos );
}
}
// Pads of the same (defined) net get a waiver on clearance tests
if( pad->GetNetCode() && otherPad->GetNetCode() == pad->GetNetCode() )
testClearance = false;
if( otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( layer ) )
testClearance = false;
} }
if( testClearance ) if( testClearance )
@ -656,6 +678,81 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa
drce->SetItems( pad, other ); drce->SetItems( pad, other );
drce->SetViolatingRule( constraint.GetParentRule() ); drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos );
testHoles = false; // No need for multiple violations
}
}
if( testHoles )
{
constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, layer );
clearance = constraint.GetValue().Min();
}
if( testHoles && otherPad && pad->FlashLayer( layer ) && otherPad->GetDrillSize().x )
{
if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( pad, other );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos );
testHoles = false; // No need for multiple violations
}
}
if( testHoles && otherPad && otherPad->FlashLayer( layer ) && pad->GetDrillSize().x )
{
if( clearance >= 0 && otherShape->Collide( pad->GetEffectiveHoleShape(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( pad, other );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos );
testHoles = false; // No need for multiple violations
}
}
if( testHoles && otherVia && otherVia->IsOnLayer( layer ) )
{
pos = otherVia->GetPosition();
otherShape.reset( new SHAPE_SEGMENT( pos, pos, otherVia->GetDrill() ) );
if( clearance > 0 && padShape->Collide( otherShape.get(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
drce->SetItems( pad, otherVia );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, (wxPoint) pos ); reportViolation( drce, (wxPoint) pos );
} }
} }
@ -725,7 +822,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadClearances( )
} }
void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZones() void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
{ {
const int delta = 50; // This is the number of tests between 2 calls to the progress bar const int delta = 50; // This is the number of tests between 2 calls to the progress bar

View File

@ -540,8 +540,7 @@ bool PCB_VIA::FlashLayer( int aLayer ) const
if( m_keepTopBottomLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) ) if( m_keepTopBottomLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) )
return true; return true;
return board->GetConnectivity()->IsConnectedOnLayer( this, static_cast<int>( aLayer ), return board->GetConnectivity()->IsConnectedOnLayer( this, static_cast<int>( aLayer ), types );
types );
} }
@ -1031,9 +1030,18 @@ std::shared_ptr<SHAPE> PCB_TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
std::shared_ptr<SHAPE> PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer ) const std::shared_ptr<SHAPE> PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
{ {
if( FlashLayer( aLayer ) ) if( FlashLayer( aLayer ) )
{
return std::make_shared<SHAPE_CIRCLE>( m_Start, m_Width / 2 ); return std::make_shared<SHAPE_CIRCLE>( m_Start, m_Width / 2 );
}
else else
return std::make_shared<SHAPE_CIRCLE>( m_Start, GetDrillValue() / 2 ); {
int radius = GetDrillValue() / 2;
if( GetBoard() )
radius += GetBoard()->GetDesignSettings().GetHolePlatingThickness();
return std::make_shared<SHAPE_CIRCLE>( m_Start, radius );
}
} }

View File

@ -527,6 +527,33 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
} }
wxString reportMin( EDA_UNITS aUnits, DRC_CONSTRAINT& aConstraint )
{
if( aConstraint.m_Value.HasMin() )
return StringFromValue( aUnits, aConstraint.m_Value.Min(), true );
else
return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
}
wxString reportOpt( EDA_UNITS aUnits, DRC_CONSTRAINT& aConstraint )
{
if( aConstraint.m_Value.HasOpt() )
return StringFromValue( aUnits, aConstraint.m_Value.Opt(), true );
else
return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
}
wxString reportMax( EDA_UNITS aUnits, DRC_CONSTRAINT& aConstraint )
{
if( aConstraint.m_Value.HasMax() )
return StringFromValue( aUnits, aConstraint.m_Value.Max(), true );
else
return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
}
int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
{ {
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>(); PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
@ -600,19 +627,11 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
auto constraint = drcEngine.EvalRules( TRACK_WIDTH_CONSTRAINT, item, nullptr, auto constraint = drcEngine.EvalRules( TRACK_WIDTH_CONSTRAINT, item, nullptr,
item->GetLayer(), r ); item->GetLayer(), r );
wxString min = _( "undefined" );
wxString max = _( "undefined" );
if( constraint.m_Value.HasMin() )
min = StringFromValue( r->GetUnits(), constraint.m_Value.Min(), true );
if( constraint.m_Value.HasMax() )
max = StringFromValue( r->GetUnits(), constraint.m_Value.Max(), true );
r->Report( "" ); r->Report( "" );
r->Report( wxString::Format( _( "Width constraints: min %s max %s." ), r->Report( wxString::Format( _( "Width constraints: min %s; opt %s; max %s." ),
min, reportMin( r->GetUnits(), constraint ),
max ) ); reportOpt( r->GetUnits(), constraint ),
reportMax( r->GetUnits(), constraint ) ) );
} }
r->Flush(); r->Flush();
@ -638,19 +657,11 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
auto constraint = drcEngine.EvalRules( VIA_DIAMETER_CONSTRAINT, item, nullptr, auto constraint = drcEngine.EvalRules( VIA_DIAMETER_CONSTRAINT, item, nullptr,
UNDEFINED_LAYER, r ); UNDEFINED_LAYER, r );
wxString min = _( "undefined" );
wxString max = _( "undefined" );
if( constraint.m_Value.HasMin() )
min = StringFromValue( r->GetUnits(), constraint.m_Value.Min(), true );
if( constraint.m_Value.HasMax() )
max = StringFromValue( r->GetUnits(), constraint.m_Value.Max(), true );
r->Report( "" ); r->Report( "" );
r->Report( wxString::Format( _( "Diameter constraints: min %s max %s." ), r->Report( wxString::Format( _( "Diameter constraints: min %s; opt %s; max %s." ),
min, reportMin( r->GetUnits(), constraint ),
max ) ); reportOpt( r->GetUnits(), constraint ),
reportMax( r->GetUnits(), constraint ) ) );
} }
r->Flush(); r->Flush();
@ -673,19 +684,11 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
auto constraint = drcEngine.EvalRules( ANNULAR_WIDTH_CONSTRAINT, item, nullptr, auto constraint = drcEngine.EvalRules( ANNULAR_WIDTH_CONSTRAINT, item, nullptr,
UNDEFINED_LAYER, r ); UNDEFINED_LAYER, r );
wxString min = _( "undefined" );
wxString max = _( "undefined" );
if( constraint.m_Value.HasMin() )
min = StringFromValue( r->GetUnits(), constraint.m_Value.Min(), true );
if( constraint.m_Value.HasMax() )
max = StringFromValue( r->GetUnits(), constraint.m_Value.Max(), true );
r->Report( "" ); r->Report( "" );
r->Report( wxString::Format( _( "Annular width constraints: min %s max %s." ), r->Report( wxString::Format( _( "Annular width constraints: min %s; opt %s; max %s." ),
min, reportMin( r->GetUnits(), constraint ),
max ) ); reportOpt( r->GetUnits(), constraint ),
reportMax( r->GetUnits(), constraint ) ) );
} }
r->Flush(); r->Flush();

View File

@ -569,6 +569,20 @@ void ZONE_FILLER::addKnockout( PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE_P
} }
/**
* Add a knockout for a pad's hole.
*/
void ZONE_FILLER::addHoleKnockout( PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles )
{
// Note: drill size represents finish size, which means the actual hole size is the plating
// thickness larger.
if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
aGap += aPad->GetBoard()->GetDesignSettings().GetHolePlatingThickness();
aPad->TransformHoleWithClearanceToPolygon( aHoles, aGap, m_maxError, ERROR_OUTSIDE );
}
/** /**
* Add a knockout for a graphic item. The knockout is 'aGap' larger than the item (which * Add a knockout for a graphic item. The knockout is 'aGap' larger than the item (which
* might be either the electrical clearance or the board edge clearance). * might be either the electrical clearance or the board edge clearance).
@ -691,34 +705,36 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
{ {
if( aPad->GetBoundingBox().Intersects( zone_boundingbox ) ) if( aPad->GetBoundingBox().Intersects( zone_boundingbox ) )
{ {
int gap; int gap = 0;
bool knockoutHoleClearance = true;
// For pads having the same netcode as the zone, the net clearance has no
// meaning so use the greater of the zone clearance and the thermal relief.
if( aPad->GetNetCode() > 0 && aPad->GetNetCode() == aZone->GetNetCode() ) if( aPad->GetNetCode() > 0 && aPad->GetNetCode() == aZone->GetNetCode() )
gap = std::max( zone_clearance, aZone->GetThermalReliefGap( aPad ) );
else
gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aPad, aLayer );
gap += extra_margin;
// If the pad isn't on the current layer but has a hole, knock out the hole.
if( !aPad->FlashLayer( aLayer ) )
{ {
if( aPad->GetDrillSize().x == 0 && aPad->GetDrillSize().y == 0 ) // For pads having the same netcode as the zone, the net and hole
return; // clearances have no meanings.
// So just knock out the greater of the zone's local clearance and
// Note: drill size represents finish size, which means the actual hole // thermal relief.
// size is the plating thickness larger. gap = std::max( zone_clearance, aZone->GetThermalReliefGap( aPad ) );
if( aPad->GetAttribute() == PAD_ATTRIB::PTH ) knockoutHoleClearance = false;
gap += aPad->GetBoard()->GetDesignSettings().GetHolePlatingThickness();
aPad->TransformHoleWithClearanceToPolygon( aHoles, gap, m_maxError,
ERROR_OUTSIDE );
} }
else else
{ {
gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aPad, aLayer );
}
gap += extra_margin;
if( aPad->FlashLayer( aLayer ) )
addKnockout( aPad, aLayer, gap, aHoles ); addKnockout( aPad, aLayer, gap, aHoles );
else if( aPad->GetDrillSize().x > 0 )
addHoleKnockout( aPad, gap, aHoles );
if( knockoutHoleClearance && aPad->GetDrillSize().x > 0 )
{
gap = evalRulesForItems( HOLE_CLEARANCE_CONSTRAINT, aZone, aPad, aLayer );
gap += extra_margin;
addHoleKnockout( aPad, gap, aHoles );
} }
} }
}; };
@ -746,29 +762,50 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
{ {
if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) ) if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
{ {
int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer );
gap += extra_margin;
if( aTrack->Type() == PCB_VIA_T ) if( aTrack->Type() == PCB_VIA_T )
{ {
PCB_VIA* via = static_cast<PCB_VIA*>( aTrack ); PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
int gap = 0;
bool checkHoleClearance = true;
if( !via->FlashLayer( aLayer ) && via->GetNetCode() != aZone->GetNetCode() ) if( via->GetNetCode() > 0 && via->GetNetCode() == aZone->GetNetCode() )
{ {
int radius = via->GetDrillValue() / 2 + bds.GetHolePlatingThickness(); // For pads having the same netcode as the zone, the net and hole
TransformCircleToPolygon( aHoles, via->GetPosition(), radius + gap, // clearances have no meanings.
m_maxError, ERROR_OUTSIDE ); // So just knock out the zone's local clearance.
gap = zone_clearance;
checkHoleClearance = false;
} }
else else
{ {
via->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer );
}
if( via->FlashLayer( aLayer ) )
{
via->TransformShapeWithClearanceToPolygon( aHoles, aLayer,
gap + extra_margin,
m_maxError, ERROR_OUTSIDE ); m_maxError, ERROR_OUTSIDE );
} }
if( checkHoleClearance )
{
gap = std::max( gap, evalRulesForItems( HOLE_CLEARANCE_CONSTRAINT,
aZone, via, aLayer ) );
}
int radius = via->GetDrillValue() / 2 + bds.GetHolePlatingThickness();
TransformCircleToPolygon( aHoles, via->GetPosition(),
radius + gap + extra_margin,
m_maxError, ERROR_OUTSIDE );
} }
else else
{ {
aTrack->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer );
aTrack->TransformShapeWithClearanceToPolygon( aHoles, aLayer,
gap + extra_margin,
m_maxError, ERROR_OUTSIDE ); m_maxError, ERROR_OUTSIDE );
} }
} }

View File

@ -62,6 +62,8 @@ private:
void addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap, bool aIgnoreLineWidth, void addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap, bool aIgnoreLineWidth,
SHAPE_POLY_SET& aHoles ); 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 );
void buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLayer, void buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLayer,

View File

@ -48,7 +48,13 @@
"min_clearance": 0.508 "min_clearance": 0.508
} }
}, },
"diff_pair_dimensions": [], "diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [], "drc_exclusions": [],
"meta": { "meta": {
"version": 2 "version": 2
@ -108,8 +114,15 @@
"min_via_diameter": 0.39999999999999997, "min_via_diameter": 0.39999999999999997,
"use_height_for_length_calcs": true "use_height_for_length_calcs": true
}, },
"track_widths": [], "track_widths": [
"via_dimensions": [], 0.0
],
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false, "zones_allow_external_fillets": false,
"zones_use_no_outline": true "zones_use_no_outline": true
}, },

View File

@ -106,7 +106,7 @@ BOOST_FIXTURE_TEST_CASE( DRCFalseNegativeRegressions, DRC_REGRESSION_TEST_FIXTUR
{ "issue2528", 1 }, { "issue2528", 1 },
{ "issue5750", 4 }, { "issue5750", 4 },
{ "issue5854", 3 }, { "issue5854", 3 },
{ "issue6879", 7 }, { "issue6879", 6 },
{ "issue6945", 2 }, { "issue6945", 2 },
{ "issue7241", 1 }, { "issue7241", 1 },
{ "issue7267", 4 }, { "issue7267", 4 },