From 1a672aba5607b6488b8e3151ed9fcf2ec03f5fa0 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 26 Aug 2022 13:21:52 +0100 Subject: [PATCH] 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. --- .../drc_test_provider_copper_clearance.cpp | 66 ++--- .../drc_test_provider_courtyard_clearance.cpp | 62 +++-- .../drc_test_provider_physical_clearance.cpp | 261 ++++++++++-------- pcbnew/drc/drc_test_provider_solder_mask.cpp | 141 +++++----- .../drc_test_provider_zone_connections.cpp | 8 + 5 files changed, 293 insertions(+), 245 deletions(-) diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index 8bbeb7dcf1..ded5baf1df 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -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( aItem )->FlashLayer( aLayer ); + PAD* pad = static_cast( aItem ); + bool flashedPad = pad->FlashLayer( aLayer ); + bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH; - if( unflashedPad && !aItem->HasHole() ) - return; - - platedHole = static_cast( 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 itemShape = aItem->GetEffectiveShape( aLayer ); + std::shared_ptr itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT ); - if( unflashedPad ) - { - std::shared_ptr 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( 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 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 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 trackShape = track->GetEffectiveShape( layer ); diff --git a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp index 9623f0dcce..9250ea1b00 100644 --- a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp @@ -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 ); diff --git a/pcbnew/drc/drc_test_provider_physical_clearance.cpp b/pcbnew/drc/drc_test_provider_physical_clearance.cpp index 64249d95d7..e5121b5fae 100644 --- a/pcbnew/drc/drc_test_provider_physical_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_physical_clearance.cpp @@ -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 otherShape = other->GetEffectiveShape( layer ); + std::shared_ptr 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 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 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 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 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( aItem ); + + if( !pad->FlashLayer( aLayer ) ) + { + if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) + continue; + + std::shared_ptr 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( hole->GetSeg(), size ); + } } - if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 ) + if( zoneTree ) { - std::shared_ptr 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 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 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( aItem ); - - if( !pad->FlashLayer( aLayer ) ) - { - if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) - continue; - - std::shared_ptr 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( 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 drce = DRC_ITEM::Create( DRCE_CLEARANCE ); + std::shared_ptr 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 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 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() ) diff --git a/pcbnew/drc/drc_test_provider_solder_mask.cpp b/pcbnew/drc/drc_test_provider_solder_mask.cpp index c87d3d4654..cf6696036e 100644 --- a/pcbnew/drc/drc_test_provider_solder_mask.cpp +++ b/pcbnew/drc/drc_test_provider_solder_mask.cpp @@ -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 m_tesselatedTree; + std::unique_ptr m_fullSolderMaskRTree; std::unique_ptr m_itemTree; std::unordered_map 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( item ); + ZONE* zone = static_cast( 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( item ); + PAD* pad = static_cast( 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( item ); + PCB_VIA* via = static_cast( 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(); + m_fullSolderMaskRTree = std::make_unique(); m_itemTree = std::make_unique(); 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 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( aItem )->GetSolderMaskExpansion(); + else if( aItem->Type() == PCB_VIA_T ) + clearance += static_cast( 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 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 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( aItem ); - - clearance += pad->GetSolderMaskExpansion(); - } - else if( aItem->Type() == PCB_VIA_T ) - { - PCB_VIA* via = static_cast( 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; diff --git a/pcbnew/drc/drc_test_provider_zone_connections.cpp b/pcbnew/drc/drc_test_provider_zone_connections.cpp index 7408f895b1..f9b2603561 100644 --- a/pcbnew/drc/drc_test_provider_zone_connections.cpp +++ b/pcbnew/drc/drc_test_provider_zone_connections.cpp @@ -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 drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );