Fix a couple of DRC bugs where the bbox wasn't inflated for largestClearance.

Also removes a case of double-testing a pad with a non-plated hole.
This commit is contained in:
Jeff Young 2022-08-26 13:21:52 +01:00
parent 909358e643
commit 1a672aba56
5 changed files with 293 additions and 245 deletions

View File

@ -298,7 +298,12 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
return; return;
} }
if( !aItem->GetBoundingBox().Intersects( aZone->GetCachedBoundingBox() ) ) EDA_RECT itemBBox = aItem->GetBoundingBox();
EDA_RECT worstCaseBBox = itemBBox;
worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
if( !worstCaseBBox.Intersects( aZone->GetCachedBoundingBox() ) )
return; return;
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ); bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
@ -307,55 +312,38 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
if( !testClearance && !testHoles ) if( !testClearance && !testHoles )
return; return;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get(); DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
EDA_RECT itemBBox = aItem->GetBoundingBox();
DRC_CONSTRAINT constraint; if( !zoneTree )
DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT; return;
int clearance = -1;
int actual; DRC_CONSTRAINT constraint;
VECTOR2I pos; int clearance = -1;
bool unflashedPad = false; int actual;
bool platedHole = false; VECTOR2I pos;
if( aItem->Type() == PCB_PAD_T ) if( aItem->Type() == PCB_PAD_T )
{ {
unflashedPad = !static_cast<PAD*>( aItem )->FlashLayer( aLayer ); PAD* pad = static_cast<PAD*>( aItem );
bool flashedPad = pad->FlashLayer( aLayer );
bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
if( unflashedPad && !aItem->HasHole() ) if( !flashedPad && !platedHole )
return; testClearance = false;
platedHole = static_cast<PAD*>( aItem )->GetAttribute() == PAD_ATTRIB::PTH;
} }
if( zoneTree && testClearance ) if( testClearance )
{ {
if( unflashedPad && !platedHole ) constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
constraintType = HOLE_CLEARANCE_CONSTRAINT;
constraint = m_drcEngine->EvalRules( constraintType, aItem, aZone, aLayer );
clearance = constraint.GetValue().Min(); clearance = constraint.GetValue().Min();
} }
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 ) if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
{ {
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer ); std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
if( unflashedPad ) if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
{ std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
std::shared_ptr<SHAPE_SEGMENT> hole = aItem->GetEffectiveHoleShape();
int size = hole->GetWidth();
// Note: drill size represents finish size, which means the actual hole size is
// 2x the plating thickness larger.
if( platedHole )
size += 2 * m_board->GetDesignSettings().GetHolePlatingThickness();
itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
}
if( zoneTree && zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{ {
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE ); std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
wxString msg; wxString msg;
@ -373,7 +361,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
} }
} }
if( zoneTree && testHoles && aItem->HasHole() ) if( testHoles && aItem->HasHole() )
{ {
std::shared_ptr<SHAPE_SEGMENT> holeShape; std::shared_ptr<SHAPE_SEGMENT> holeShape;
@ -433,7 +421,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
if( !reportProgress( ii++, m_board->Tracks().size(), progressDelta ) ) if( !reportProgress( ii++, m_board->Tracks().size(), progressDelta ) )
break; break;
for( PCB_LAYER_ID layer : track->GetLayerSet().Seq() ) for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & LSET::AllCuMask() ).Seq() )
{ {
std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer ); std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );

View File

@ -177,27 +177,46 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
continue; continue;
} }
BOX2I frontBBox = frontA.BBoxFromCaches(); BOX2I frontA_worstCaseBBox = frontA.BBoxFromCaches();
BOX2I backBBox = backA.BBoxFromCaches(); BOX2I backA_worstCaseBBox = backA.BBoxFromCaches();
frontBBox.Inflate( m_largestCourtyardClearance ); frontA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
backBBox.Inflate( m_largestCourtyardClearance ); backA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
EDA_RECT fpABBox = fpA->GetBoundingBox(); EDA_RECT fpA_bbox = fpA->GetBoundingBox();
for( auto itB = itA + 1; itB != m_board->Footprints().end(); itB++ ) for( auto itB = itA + 1; itB != m_board->Footprints().end(); itB++ )
{ {
FOOTPRINT* fpB = *itB; FOOTPRINT* fpB = *itB;
EDA_RECT fpBBBox = fpB->GetBoundingBox();
const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd ); const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd ); const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
DRC_CONSTRAINT constraint;
int clearance; if( frontB.OutlineCount() == 0 && backB.OutlineCount() == 0
int actual; && m_drcEngine->IsErrorLimitExceeded( DRCE_PTH_IN_COURTYARD )
VECTOR2I pos; && m_drcEngine->IsErrorLimitExceeded( DRCE_NPTH_IN_COURTYARD ) )
{
// No courtyards defined and no hole testing against other footprint's courtyards
continue;
}
BOX2I frontB_worstCaseBBox = frontB.BBoxFromCaches();
BOX2I backB_worstCaseBBox = backB.BBoxFromCaches();
frontB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
backB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
EDA_RECT fpB_bbox = fpB->GetBoundingBox();
DRC_CONSTRAINT constraint;
int clearance;
int actual;
VECTOR2I pos;
//
// Check courtyard-to-courtyard collisions on front of board.
//
if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0 if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
&& frontBBox.Intersects( frontB.BBoxFromCaches() ) ) && frontA_worstCaseBBox.Intersects( frontB.BBoxFromCaches() ) )
{ {
constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu ); constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
clearance = constraint.GetValue().Min(); clearance = constraint.GetValue().Min();
@ -226,8 +245,12 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
} }
} }
//
// Check courtyard-to-courtyard collisions on back of board.
//
if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0 if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
&& backBBox.Intersects( backB.BBoxFromCaches() ) ) && backA_worstCaseBBox.Intersects( backB.BBoxFromCaches() ) )
{ {
constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu ); constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
clearance = constraint.GetValue().Min(); clearance = constraint.GetValue().Min();
@ -256,6 +279,13 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
} }
} }
//
// Check pad-hole-to-courtyard collisions on front and back of board.
//
// NB: via holes are not checked. There is a presumption that a physical object goes
// through a pad hole, which is not the case for via holes.
//
auto testPadAgainstCourtyards = auto testPadAgainstCourtyards =
[&]( const PAD* pad, const FOOTPRINT* fp ) [&]( const PAD* pad, const FOOTPRINT* fp )
{ {
@ -292,15 +322,15 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
} }
}; };
if( ( frontA.OutlineCount() > 0 && frontA.BBoxFromCaches().Intersects( fpBBBox ) ) if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
|| ( backA.OutlineCount() > 0 && backA.BBoxFromCaches().Intersects( fpBBBox ) ) ) || ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
{ {
for( const PAD* padB : fpB->Pads() ) for( const PAD* padB : fpB->Pads() )
testPadAgainstCourtyards( padB, fpA ); testPadAgainstCourtyards( padB, fpA );
} }
if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpABBox ) ) if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
|| ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpABBox ) ) ) || ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
{ {
for( const PAD* padA : fpA->Pads() ) for( const PAD* padA : fpA->Pads() )
testPadAgainstCourtyards( padA, fpB ); testPadAgainstCourtyards( padA, fpB );

View File

@ -71,7 +71,7 @@ public:
} }
private: private:
bool testItemAgainstItem( BOARD_ITEM* item, SHAPE* itemShape, PCB_LAYER_ID layer, bool testItemAgainstItem( BOARD_ITEM* aItem, SHAPE* aItemShape, PCB_LAYER_ID aLayer,
BOARD_ITEM* other ); BOARD_ITEM* other );
void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ); void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
@ -119,6 +119,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
static const LSET courtyards( 2, F_CrtYd, B_CrtYd ); static const LSET courtyards( 2, F_CrtYd, B_CrtYd );
//
// Generate a count for use in progress reporting.
//
forEachGeometryItem( itemTypes, LSET::AllLayersMask(), forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
[&]( BOARD_ITEM* item ) -> bool [&]( BOARD_ITEM* item ) -> bool
{ {
@ -126,6 +130,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
return true; return true;
} ); } );
//
// Generate a BOARD_ITEM RTree.
//
forEachGeometryItem( itemTypes, LSET::AllLayersMask(), forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
[&]( BOARD_ITEM* item ) -> bool [&]( BOARD_ITEM* item ) -> bool
{ {
@ -158,6 +166,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
progressDelta = 100; progressDelta = 100;
ii = 0; ii = 0;
//
// Run clearance checks -between- items.
//
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE )
|| !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) ) || !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
{ {
@ -222,6 +234,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
count = 0; count = 0;
ii = 0; ii = 0;
//
// Generate a count for progress reporting.
//
forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T }, forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
LSET::AllCuMask(), LSET::AllCuMask(),
[&]( BOARD_ITEM* item ) -> bool [&]( BOARD_ITEM* item ) -> bool
@ -236,6 +252,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
return true; return true;
} ); } );
//
// Run clearance checks -within- polygonal items.
//
forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T }, forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
LSET::AllCuMask(), LSET::AllCuMask(),
[&]( BOARD_ITEM* item ) -> bool [&]( BOARD_ITEM* item ) -> bool
@ -507,7 +527,9 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER
{ {
SHAPE_LINE_CHAIN* firstOutline = &fill.Outline( outlineIdx ); SHAPE_LINE_CHAIN* firstOutline = &fill.Outline( outlineIdx );
//
// Step one: outline to outline clearance violations // Step one: outline to outline clearance violations
//
for( int ii = outlineIdx + 1; ii < fill.OutlineCount(); ++ii ) for( int ii = outlineIdx + 1; ii < fill.OutlineCount(); ++ii )
{ {
@ -541,7 +563,9 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER
return; return;
} }
//
// Step two: interior hole clearance violations // Step two: interior hole clearance violations
//
for( int holeIdx = 0; holeIdx < fill.HoleCount( outlineIdx ); ++holeIdx ) for( int holeIdx = 0; holeIdx < fill.HoleCount( outlineIdx ); ++holeIdx )
{ {
@ -554,9 +578,9 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER
} }
bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item, bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* aItem,
SHAPE* itemShape, SHAPE* aItemShape,
PCB_LAYER_ID layer, PCB_LAYER_ID aLayer,
BOARD_ITEM* other ) BOARD_ITEM* other )
{ {
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ); bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
@ -566,17 +590,17 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
int actual; int actual;
VECTOR2I pos; VECTOR2I pos;
std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( layer ); std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
if( testClearance ) if( testClearance )
{ {
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, item, other, layer ); constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, other, aLayer );
clearance = constraint.GetValue().Min(); clearance = constraint.GetValue().Min();
} }
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 ) if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
{ {
if( itemShape->Collide( otherShape.get(), clearance, &actual, &pos ) ) if( aItemShape->Collide( otherShape.get(), clearance, &actual, &pos ) )
{ {
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE ); std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
wxString msg; wxString msg;
@ -587,10 +611,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
MessageTextFromValue( userUnits(), actual ) ); MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( item, other ); drce->SetItems( aItem, other );
drce->SetViolatingRule( constraint.GetParentRule() ); drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, pos, layer ); reportViolation( drce, pos, aLayer );
} }
} }
@ -600,19 +624,19 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
std::shared_ptr<SHAPE_SEGMENT> otherHoleShape; std::shared_ptr<SHAPE_SEGMENT> otherHoleShape;
clearance = 0; clearance = 0;
if( item->Type() == PCB_VIA_T ) if( aItem->Type() == PCB_VIA_T )
{ {
if( item->GetLayerSet().Contains( layer ) ) if( aItem->GetLayerSet().Contains( aLayer ) )
itemHoleShape = item->GetEffectiveHoleShape(); itemHoleShape = aItem->GetEffectiveHoleShape();
} }
else if( item->HasHole() ) else if( aItem->HasHole() )
{ {
itemHoleShape = item->GetEffectiveHoleShape(); itemHoleShape = aItem->GetEffectiveHoleShape();
} }
if( other->Type() == PCB_VIA_T ) if( other->Type() == PCB_VIA_T )
{ {
if( other->GetLayerSet().Contains( layer ) ) if( other->GetLayerSet().Contains( aLayer ) )
otherHoleShape = other->GetEffectiveHoleShape(); otherHoleShape = other->GetEffectiveHoleShape();
} }
else if( other->HasHole() ) else if( other->HasHole() )
@ -622,8 +646,8 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
if( itemHoleShape || otherHoleShape ) if( itemHoleShape || otherHoleShape )
{ {
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, other, item, constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, other, aItem,
layer ); aLayer );
clearance = constraint.GetValue().Min(); clearance = constraint.GetValue().Min();
} }
@ -640,13 +664,13 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
MessageTextFromValue( userUnits(), actual ) ); MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( item, other ); drce->SetItems( aItem, other );
drce->SetViolatingRule( constraint.GetParentRule() ); drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, pos, layer ); reportViolation( drce, pos, aLayer );
} }
if( otherHoleShape && otherHoleShape->Collide( itemShape, clearance, &actual, &pos ) ) if( otherHoleShape && otherHoleShape->Collide( aItemShape, clearance, &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 );
wxString msg; wxString msg;
@ -657,10 +681,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
MessageTextFromValue( userUnits(), actual ) ); MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( item, other ); drce->SetItems( aItem, other );
drce->SetViolatingRule( constraint.GetParentRule() ); drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, pos, layer ); reportViolation( drce, pos, aLayer );
} }
} }
} }
@ -677,68 +701,113 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
if( !zone->GetLayerSet().test( aLayer ) ) if( !zone->GetLayerSet().test( aLayer ) )
continue; continue;
if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) ) EDA_RECT itemBBox = aItem->GetBoundingBox();
EDA_RECT worstCaseBBox = itemBBox;
worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
if( !worstCaseBBox.Intersects( zone->GetCachedBoundingBox() ) )
continue;
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
if( !testClearance && !testHoles )
return;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
DRC_CONSTRAINT constraint;
bool colliding;
int clearance = -1;
int actual;
VECTOR2I pos;
if( testClearance )
{ {
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ); constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ); aLayer );
clearance = constraint.GetValue().Min();
}
if( !testClearance && !testHoles ) if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
return; {
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get(); if( aItem->Type() == PCB_PAD_T )
EDA_RECT itemBBox = aItem->GetBoundingBox();
DRC_CONSTRAINT constraint;
bool colliding;
int clearance = -1;
int actual;
VECTOR2I pos;
if( testClearance )
{ {
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone, PAD* pad = static_cast<PAD*>( aItem );
aLayer );
clearance = constraint.GetValue().Min(); if( !pad->FlashLayer( aLayer ) )
{
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
continue;
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
int size = hole->GetWidth();
// 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( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 ) if( zoneTree )
{ {
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer ); colliding = zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, clearance,
&actual, &pos );
}
else
{
colliding = zone->Outline()->Collide( itemShape.get(), clearance, &actual, &pos );
}
if( aItem->Type() == PCB_PAD_T ) if( colliding )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
wxString msg;
msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( aItem, zone );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, pos, aLayer );
}
}
if( testHoles )
{
std::shared_ptr<SHAPE_SEGMENT> holeShape;
if( aItem->Type() == PCB_VIA_T )
{
if( aItem->GetLayerSet().Contains( aLayer ) )
holeShape = aItem->GetEffectiveHoleShape();
}
else if( aItem->HasHole() )
{
holeShape = aItem->GetEffectiveHoleShape();
}
if( holeShape )
{
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aItem,
zone, aLayer );
clearance = constraint.GetValue().Min();
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
&& clearance > 0
&& zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer, clearance,
&actual, &pos ) )
{ {
PAD* pad = static_cast<PAD*>( aItem ); std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
if( !pad->FlashLayer( aLayer ) )
{
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
continue;
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
int size = hole->GetWidth();
// 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 )
{
colliding = zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
clearance, &actual, &pos );
}
else
{
colliding = zone->Outline()->Collide( itemShape.get(), clearance, &actual,
&pos );
}
if( colliding )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
wxString msg; wxString msg;
msg.Printf( _( "(%s clearance %s; actual %s)" ), msg.Printf( _( "(%s clearance %s; actual %s)" ),
@ -753,48 +822,6 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
reportViolation( drce, pos, aLayer ); reportViolation( drce, pos, aLayer );
} }
} }
if( testHoles )
{
std::shared_ptr<SHAPE_SEGMENT> holeShape;
if( aItem->Type() == PCB_VIA_T )
{
if( aItem->GetLayerSet().Contains( aLayer ) )
holeShape = aItem->GetEffectiveHoleShape();
}
else if( aItem->HasHole() )
{
holeShape = aItem->GetEffectiveHoleShape();
}
if( holeShape )
{
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aItem,
zone, aLayer );
clearance = constraint.GetValue().Min();
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
&& clearance > 0
&& zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
clearance, &actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
wxString msg;
msg.Printf( _( "(%s clearance %s; actual %s)" ),
constraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( aItem, zone );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, pos, aLayer );
}
}
}
} }
if( m_drcEngine->IsCancelled() ) if( m_drcEngine->IsCancelled() )

View File

@ -73,7 +73,7 @@ public:
} }
private: private:
void addItemToRTrees( BOARD_ITEM* item ); void addItemToRTrees( BOARD_ITEM* aItem );
void buildRTrees(); void buildRTrees();
void testSilkToMaskClearance(); void testSilkToMaskClearance();
@ -97,7 +97,7 @@ private:
int m_maxError; int m_maxError;
int m_largestClearance; int m_largestClearance;
std::unique_ptr<DRC_RTREE> m_tesselatedTree; std::unique_ptr<DRC_RTREE> m_fullSolderMaskRTree;
std::unique_ptr<DRC_RTREE> m_itemTree; std::unique_ptr<DRC_RTREE> m_itemTree;
std::unordered_map<PTR_PTR_CACHE_KEY, LSET> m_checkedPairs; std::unordered_map<PTR_PTR_CACHE_KEY, LSET> m_checkedPairs;
@ -109,13 +109,13 @@ private:
}; };
void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item ) void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* aItem )
{ {
ZONE* solderMask = m_board->m_SolderMask; ZONE* solderMask = m_board->m_SolderMask;
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T )
{ {
ZONE* zone = static_cast<ZONE*>( item ); ZONE* zone = static_cast<ZONE*>( aItem );
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
{ {
@ -126,35 +126,35 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
} }
} }
} }
else if( item->Type() == PCB_PAD_T ) else if( aItem->Type() == PCB_PAD_T )
{ {
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
{ {
if( item->IsOnLayer( layer ) ) if( aItem->IsOnLayer( layer ) )
{ {
PAD* pad = static_cast<PAD*>( item ); PAD* pad = static_cast<PAD*>( aItem );
int clearance = ( m_webWidth / 2 ) + pad->GetSolderMaskExpansion(); int clearance = ( m_webWidth / 2 ) + pad->GetSolderMaskExpansion();
item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer, aItem->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
clearance, m_maxError, ERROR_OUTSIDE ); clearance, m_maxError, ERROR_OUTSIDE );
m_itemTree->Insert( item, layer, m_largestClearance ); m_itemTree->Insert( aItem, layer, m_largestClearance );
} }
} }
} }
else if( item->Type() == PCB_VIA_T ) else if( aItem->Type() == PCB_VIA_T )
{ {
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
{ {
if( item->IsOnLayer( layer ) ) if( aItem->IsOnLayer( layer ) )
{ {
PCB_VIA* via = static_cast<PCB_VIA*>( item ); PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
int clearance = ( m_webWidth / 2 ) + via->GetSolderMaskExpansion(); int clearance = ( m_webWidth / 2 ) + via->GetSolderMaskExpansion();
via->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer, via->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
clearance, m_maxError, ERROR_OUTSIDE ); clearance, m_maxError, ERROR_OUTSIDE );
m_itemTree->Insert( item, layer, m_largestClearance ); m_itemTree->Insert( aItem, layer, m_largestClearance );
} }
} }
} }
@ -162,13 +162,13 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
{ {
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } ) for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
{ {
if( item->IsOnLayer( layer ) ) if( aItem->IsOnLayer( layer ) )
{ {
item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), aItem->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ),
layer, m_webWidth / 2, m_maxError, layer, m_webWidth / 2, m_maxError,
ERROR_OUTSIDE ); ERROR_OUTSIDE );
m_itemTree->Insert( item, layer, m_largestClearance ); m_itemTree->Insert( aItem, layer, m_largestClearance );
} }
} }
} }
@ -187,7 +187,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
solderMask->GetFill( F_Mask )->RemoveAllContours(); solderMask->GetFill( F_Mask )->RemoveAllContours();
solderMask->GetFill( B_Mask )->RemoveAllContours(); solderMask->GetFill( B_Mask )->RemoveAllContours();
m_tesselatedTree = std::make_unique<DRC_RTREE>(); m_fullSolderMaskRTree = std::make_unique<DRC_RTREE>();
m_itemTree = std::make_unique<DRC_RTREE>(); m_itemTree = std::make_unique<DRC_RTREE>();
forEachGeometryItem( s_allBasicItems, layers, forEachGeometryItem( s_allBasicItems, layers,
@ -221,8 +221,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
solderMask->CacheTriangulation(); solderMask->CacheTriangulation();
m_tesselatedTree->Insert( solderMask, F_Mask ); m_fullSolderMaskRTree->Insert( solderMask, F_Mask );
m_tesselatedTree->Insert( solderMask, B_Mask ); m_fullSolderMaskRTree->Insert( solderMask, B_Mask );
m_checkedPairs.clear(); m_checkedPairs.clear();
} }
@ -272,8 +272,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testSilkToMaskClearance()
std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer ); std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
if( m_tesselatedTree->QueryColliding( itemBBox, itemShape.get(), layer, if( m_fullSolderMaskRTree->QueryColliding( itemBBox, itemShape.get(), layer,
clearance, &actual, &pos ) ) clearance, &actual, &pos ) )
{ {
auto drce = DRC_ITEM::Create( DRCE_SILK_CLEARANCE ); auto drce = DRC_ITEM::Create( DRCE_SILK_CLEARANCE );
wxString msg; wxString msg;
@ -577,64 +577,59 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem,
continue; continue;
} }
if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) ) EDA_RECT inflatedBBox( aItemBBox );
int clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance;
if( aItem->Type() == PCB_PAD_T )
clearance += static_cast<PAD*>( aItem )->GetSolderMaskExpansion();
else if( aItem->Type() == PCB_VIA_T )
clearance += static_cast<PCB_VIA*>( aItem )->GetSolderMaskExpansion();
inflatedBBox.Inflate( clearance );
if( !inflatedBBox.Intersects( zone->GetCachedBoundingBox() ) )
continue;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
int actual;
VECTOR2I pos;
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aMaskLayer );
if( zoneTree && zoneTree->QueryColliding( aItemBBox, itemShape.get(), aTargetLayer,
clearance, &actual, &pos ) )
{ {
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get(); wxString msg;
int clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance; BOARD_ITEM* colliding = nullptr;
int actual;
VECTOR2I pos;
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aMaskLayer ); if( aMaskLayer == F_Mask )
msg = _( "Front solder mask aperture bridges items with different nets" );
else
msg = _( "Rear solder mask aperture bridges items with different nets" );
if( aItem->Type() == PCB_PAD_T ) // Simple mask apertures aren't associated with copper items, so they only constitute
// a bridge when they expose other copper items having at least two distinct nets.
if( isMaskAperture( aItem ) && zoneNet >= 0 )
{ {
PAD* pad = static_cast<PAD*>( aItem ); if( checkMaskAperture( aItem, zone, aTargetLayer, zoneNet, &colliding ) )
clearance += pad->GetSolderMaskExpansion();
}
else if( aItem->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
clearance += via->GetSolderMaskExpansion();
}
if( zoneTree && zoneTree->QueryColliding( aItemBBox, itemShape.get(), aTargetLayer,
clearance, &actual, &pos ) )
{
wxString msg;
BOARD_ITEM* colliding = nullptr;
if( aMaskLayer == F_Mask )
msg = _( "Front solder mask aperture bridges items with different nets" );
else
msg = _( "Rear solder mask aperture bridges items with different nets" );
// Simple mask apertures aren't associated with copper items, so they only
// constitute a bridge when they expose other copper items having at least
// two distinct nets.
if( isMaskAperture( aItem ) && zoneNet >= 0 )
{
if( checkMaskAperture( aItem, zone, aTargetLayer, zoneNet, &colliding ) )
{
auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
drce->SetErrorMessage( msg );
drce->SetItems( aItem, colliding, zone );
drce->SetViolatingRule( &m_bridgeRule );
reportViolation( drce, pos, aTargetLayer );
}
}
else
{ {
auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE ); auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
drce->SetErrorMessage( msg ); drce->SetErrorMessage( msg );
drce->SetItems( aItem, zone ); drce->SetItems( aItem, colliding, zone );
drce->SetViolatingRule( &m_bridgeRule ); drce->SetViolatingRule( &m_bridgeRule );
reportViolation( drce, pos, aTargetLayer ); reportViolation( drce, pos, aTargetLayer );
} }
} }
else
{
auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
drce->SetErrorMessage( msg );
drce->SetItems( aItem, zone );
drce->SetViolatingRule( &m_bridgeRule );
reportViolation( drce, pos, aTargetLayer );
}
} }
if( m_drcEngine->IsCancelled() ) if( m_drcEngine->IsCancelled() )
@ -722,8 +717,8 @@ bool DRC_TEST_PROVIDER_SOLDER_MASK::Run()
m_largestClearance = std::max( m_largestClearance, pad->GetSolderMaskExpansion() ); m_largestClearance = std::max( m_largestClearance, pad->GetSolderMaskExpansion() );
} }
// Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is maxed // Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is
// with the various SILK_CLEARANCE_CONSTRAINTS. // maxed with the various SILK_CLEARANCE_CONSTRAINTS.
m_largestClearance += m_largestClearance + m_webWidth; m_largestClearance += m_largestClearance + m_webWidth;
DRC_CONSTRAINT worstClearanceConstraint; DRC_CONSTRAINT worstClearanceConstraint;

View File

@ -92,8 +92,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
if( m_drcEngine->IsCancelled() ) if( m_drcEngine->IsCancelled() )
return; return;
//
// Quick tests for "connected": // Quick tests for "connected":
// //
if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 ) if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
continue; continue;
@ -105,8 +107,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
if( !pad->FlashLayer( aLayer ) ) if( !pad->FlashLayer( aLayer ) )
continue; continue;
//
// If those passed, do a thorough test: // If those passed, do a thorough test:
// //
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer ); constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer );
ZONE_CONNECTION conn = constraint.m_ZoneConnection; ZONE_CONNECTION conn = constraint.m_ZoneConnection;
@ -139,8 +143,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
if( spokes >= minCount ) // We already have enough if( spokes >= minCount ) // We already have enough
continue; continue;
//
// See if there are any other manual spokes added: // See if there are any other manual spokes added:
// //
for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) ) for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
{ {
if( padOutline.PointInside( track->GetStart() ) ) if( padOutline.PointInside( track->GetStart() ) )
@ -155,8 +161,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
} }
} }
//
// And finally report it if there aren't enough: // And finally report it if there aren't enough:
// //
if( spokes < minCount ) if( spokes < minCount )
{ {
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL ); std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );