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;
}
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;
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
@ -307,55 +312,38 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
if( !testClearance && !testHoles )
return;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
EDA_RECT itemBBox = aItem->GetBoundingBox();
DRC_CONSTRAINT constraint;
DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
int clearance = -1;
int actual;
VECTOR2I pos;
bool unflashedPad = false;
bool platedHole = false;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
if( !zoneTree )
return;
DRC_CONSTRAINT constraint;
int clearance = -1;
int actual;
VECTOR2I pos;
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() )
return;
platedHole = static_cast<PAD*>( aItem )->GetAttribute() == PAD_ATTRIB::PTH;
if( !flashedPad && !platedHole )
testClearance = false;
}
if( zoneTree && testClearance )
if( testClearance )
{
if( unflashedPad && !platedHole )
constraintType = HOLE_CLEARANCE_CONSTRAINT;
constraint = m_drcEngine->EvalRules( constraintType, aItem, aZone, aLayer );
constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
clearance = constraint.GetValue().Min();
}
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 )
{
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 ) )
if( 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 );
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;
@ -433,7 +421,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
if( !reportProgress( ii++, m_board->Tracks().size(), progressDelta ) )
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 );

View File

@ -177,27 +177,46 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
continue;
}
BOX2I frontBBox = frontA.BBoxFromCaches();
BOX2I backBBox = backA.BBoxFromCaches();
BOX2I frontA_worstCaseBBox = frontA.BBoxFromCaches();
BOX2I backA_worstCaseBBox = backA.BBoxFromCaches();
frontBBox.Inflate( m_largestCourtyardClearance );
backBBox.Inflate( m_largestCourtyardClearance );
frontA_worstCaseBBox.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++ )
{
FOOTPRINT* fpB = *itB;
EDA_RECT fpBBBox = fpB->GetBoundingBox();
const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
DRC_CONSTRAINT constraint;
int clearance;
int actual;
VECTOR2I pos;
if( frontB.OutlineCount() == 0 && backB.OutlineCount() == 0
&& m_drcEngine->IsErrorLimitExceeded( DRCE_PTH_IN_COURTYARD )
&& 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
&& frontBBox.Intersects( frontB.BBoxFromCaches() ) )
&& frontA_worstCaseBBox.Intersects( frontB.BBoxFromCaches() ) )
{
constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
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
&& backBBox.Intersects( backB.BBoxFromCaches() ) )
&& backA_worstCaseBBox.Intersects( backB.BBoxFromCaches() ) )
{
constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
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 =
[&]( 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 ) )
|| ( backA.OutlineCount() > 0 && backA.BBoxFromCaches().Intersects( fpBBBox ) ) )
if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
|| ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
{
for( const PAD* padB : fpB->Pads() )
testPadAgainstCourtyards( padB, fpA );
}
if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpABBox ) )
|| ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpABBox ) ) )
if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
|| ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
{
for( const PAD* padA : fpA->Pads() )
testPadAgainstCourtyards( padA, fpB );

View File

@ -71,7 +71,7 @@ public:
}
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 );
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 );
//
// Generate a count for use in progress reporting.
//
forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
[&]( BOARD_ITEM* item ) -> bool
{
@ -126,6 +130,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
return true;
} );
//
// Generate a BOARD_ITEM RTree.
//
forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
[&]( BOARD_ITEM* item ) -> bool
{
@ -158,6 +166,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
progressDelta = 100;
ii = 0;
//
// Run clearance checks -between- items.
//
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE )
|| !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
{
@ -222,6 +234,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
count = 0;
ii = 0;
//
// Generate a count for progress reporting.
//
forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
LSET::AllCuMask(),
[&]( BOARD_ITEM* item ) -> bool
@ -236,6 +252,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
return true;
} );
//
// Run clearance checks -within- polygonal items.
//
forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
LSET::AllCuMask(),
[&]( 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 );
//
// Step one: outline to outline clearance violations
//
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;
}
//
// Step two: interior hole clearance violations
//
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,
SHAPE* itemShape,
PCB_LAYER_ID layer,
bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* aItem,
SHAPE* aItemShape,
PCB_LAYER_ID aLayer,
BOARD_ITEM* other )
{
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
@ -566,17 +590,17 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
int actual;
VECTOR2I pos;
std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( layer );
std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
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();
}
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 );
wxString msg;
@ -587,10 +611,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( item, other );
drce->SetItems( aItem, other );
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;
clearance = 0;
if( item->Type() == PCB_VIA_T )
if( aItem->Type() == PCB_VIA_T )
{
if( item->GetLayerSet().Contains( layer ) )
itemHoleShape = item->GetEffectiveHoleShape();
if( aItem->GetLayerSet().Contains( aLayer ) )
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->GetLayerSet().Contains( layer ) )
if( other->GetLayerSet().Contains( aLayer ) )
otherHoleShape = other->GetEffectiveHoleShape();
}
else if( other->HasHole() )
@ -622,8 +646,8 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
if( itemHoleShape || otherHoleShape )
{
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, other, item,
layer );
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, other, aItem,
aLayer );
clearance = constraint.GetValue().Min();
}
@ -640,13 +664,13 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( item, other );
drce->SetItems( aItem, other );
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 );
wxString msg;
@ -657,10 +681,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
MessageTextFromValue( userUnits(), actual ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( item, other );
drce->SetItems( aItem, other );
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 ) )
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 );
bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
aLayer );
clearance = constraint.GetValue().Min();
}
if( !testClearance && !testHoles )
return;
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
{
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
EDA_RECT itemBBox = aItem->GetBoundingBox();
DRC_CONSTRAINT constraint;
bool colliding;
int clearance = -1;
int actual;
VECTOR2I pos;
if( testClearance )
if( aItem->Type() == PCB_PAD_T )
{
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
aLayer );
clearance = constraint.GetValue().Min();
PAD* pad = static_cast<PAD*>( aItem );
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 );
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 );
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
wxString msg;
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 );
}
}
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() )

View File

@ -73,7 +73,7 @@ public:
}
private:
void addItemToRTrees( BOARD_ITEM* item );
void addItemToRTrees( BOARD_ITEM* aItem );
void buildRTrees();
void testSilkToMaskClearance();
@ -97,7 +97,7 @@ private:
int m_maxError;
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::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;
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 } )
{
@ -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 } )
{
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();
item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
clearance, m_maxError, ERROR_OUTSIDE );
aItem->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
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 } )
{
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();
via->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
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 } )
{
if( item->IsOnLayer( layer ) )
if( aItem->IsOnLayer( layer ) )
{
item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ),
layer, m_webWidth / 2, m_maxError,
ERROR_OUTSIDE );
aItem->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ),
layer, m_webWidth / 2, m_maxError,
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( B_Mask )->RemoveAllContours();
m_tesselatedTree = std::make_unique<DRC_RTREE>();
m_fullSolderMaskRTree = std::make_unique<DRC_RTREE>();
m_itemTree = std::make_unique<DRC_RTREE>();
forEachGeometryItem( s_allBasicItems, layers,
@ -221,8 +221,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
solderMask->CacheTriangulation();
m_tesselatedTree->Insert( solderMask, F_Mask );
m_tesselatedTree->Insert( solderMask, B_Mask );
m_fullSolderMaskRTree->Insert( solderMask, F_Mask );
m_fullSolderMaskRTree->Insert( solderMask, B_Mask );
m_checkedPairs.clear();
}
@ -272,8 +272,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testSilkToMaskClearance()
std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
if( m_tesselatedTree->QueryColliding( itemBBox, itemShape.get(), layer,
clearance, &actual, &pos ) )
if( m_fullSolderMaskRTree->QueryColliding( itemBBox, itemShape.get(), layer,
clearance, &actual, &pos ) )
{
auto drce = DRC_ITEM::Create( DRCE_SILK_CLEARANCE );
wxString msg;
@ -577,64 +577,59 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem,
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();
int clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance;
int actual;
VECTOR2I pos;
wxString msg;
BOARD_ITEM* colliding = nullptr;
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 );
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
if( checkMaskAperture( aItem, zone, aTargetLayer, zoneNet, &colliding ) )
{
auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
drce->SetErrorMessage( msg );
drce->SetItems( aItem, zone );
drce->SetItems( aItem, colliding, zone );
drce->SetViolatingRule( &m_bridgeRule );
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() )
@ -722,8 +717,8 @@ bool DRC_TEST_PROVIDER_SOLDER_MASK::Run()
m_largestClearance = std::max( m_largestClearance, pad->GetSolderMaskExpansion() );
}
// Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is maxed
// with the various SILK_CLEARANCE_CONSTRAINTS.
// Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is
// maxed with the various SILK_CLEARANCE_CONSTRAINTS.
m_largestClearance += m_largestClearance + m_webWidth;
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() )
return;
//
// Quick tests for "connected":
//
if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
continue;
@ -105,8 +107,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
if( !pad->FlashLayer( aLayer ) )
continue;
//
// If those passed, do a thorough test:
//
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer );
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
continue;
//
// See if there are any other manual spokes added:
//
for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
{
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:
//
if( spokes < minCount )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );