diff --git a/pcbnew/drc/drc_engine.cpp b/pcbnew/drc/drc_engine.cpp index d6fb214837..c029c727bb 100644 --- a/pcbnew/drc/drc_engine.cpp +++ b/pcbnew/drc/drc_engine.cpp @@ -762,53 +762,6 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aT } -bool isUnflashedNPTH( const BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ) -{ - if( !aItem || aItem->Type() != PCB_PAD_T ) - return false; - - const PAD* pad = static_cast( aItem ); - - if( pad->GetAttribute() != PAD_ATTRIB::NPTH ) - return false; - - // Run a couple of quick tests to see if there's any copper - - if( pad->GetShape() == PAD_SHAPE::CIRCLE && pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE - && pad->GetSizeX() <= pad->GetDrillSizeX() ) - { - return true; - } - - if( pad->GetShape() == PAD_SHAPE::OVAL && pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG - && pad->GetSizeX() <= pad->GetDrillSizeX() - && pad->GetSizeY() <= pad->GetDrillSizeY() ) - { - return true; - } - - if( !pad->FlashLayer( aLayer ) ) - return true; - - // We're out of optimizations. Do it the hard way. - - SHAPE_POLY_SET padOutline; - const SHAPE_SEGMENT* drillShape = pad->GetEffectiveHoleShape(); - const SEG drillSeg = drillShape->GetSeg(); - SHAPE_POLY_SET drillOutline; - - pad->TransformShapeWithClearanceToPolygon( padOutline, aLayer, 0, ARC_HIGH_DEF, - ERROR_LOC::ERROR_INSIDE ); - - TransformOvalToPolygon( drillOutline, (wxPoint) drillSeg.A, (wxPoint) drillSeg.B, - drillShape->GetWidth(), ARC_HIGH_DEF, ERROR_LOC::ERROR_INSIDE ); - - padOutline.BooleanSubtract( drillOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); - - return padOutline.IsEmpty(); -} - - DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM* a, const BOARD_ITEM* b, PCB_LAYER_ID aLayer, REPORTER* aReporter ) @@ -845,7 +798,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO REPORT( "" ) REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ), EscapeHTML( a->GetSelectMenuText( UNITS ) ), - EscapeHTML( REPORT_VALUE( overrideA ) ) ) ) + REPORT_VALUE( overrideA ) ) ) } if( bc && !a_is_non_copper && bc->GetLocalClearanceOverrides( nullptr ) > 0 ) @@ -855,34 +808,20 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO REPORT( "" ) REPORT( wxString::Format( _( "Local override on %s; clearance: %s." ), EscapeHTML( b->GetSelectMenuText( UNITS ) ), - EscapeHTML( REPORT_VALUE( overrideB ) ) ) ) + REPORT_VALUE( overrideB ) ) ) } if( overrideA || overrideB ) { int override = std::max( overrideA, overrideB ); - if( isUnflashedNPTH( a, aLayer ) || isUnflashedNPTH( b, aLayer ) ) + if( override < m_designSettings->m_MinClearance ) { - if( override < m_designSettings->m_HoleClearance ) - { - override = m_designSettings->m_HoleClearance; + override = m_designSettings->m_MinClearance; - REPORT( "" ) - REPORT( wxString::Format( _( "Board minimum hole clearance: %s." ), - EscapeHTML( REPORT_VALUE( override ) ) ) ) - } - } - else - { - if( override < m_designSettings->m_MinClearance ) - { - override = m_designSettings->m_MinClearance; - - REPORT( "" ) - REPORT( wxString::Format( _( "Board minimum clearance: %s." ), - EscapeHTML( REPORT_VALUE( override ) ) ) ) - } + REPORT( "" ) + REPORT( wxString::Format( _( "Board minimum clearance: %s." ), + REPORT_VALUE( override ) ) ) } DRC_CONSTRAINT constraint( aConstraintType, m_msg ); @@ -898,44 +837,73 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO REPORT( "" ) - if( c->constraint.m_Type == CLEARANCE_CONSTRAINT ) + switch( c->constraint.m_Type ) + { + case CLEARANCE_CONSTRAINT: + case COURTYARD_CLEARANCE_CONSTRAINT: + case SILK_CLEARANCE_CONSTRAINT: + case HOLE_CLEARANCE_CONSTRAINT: + case EDGE_CLEARANCE_CONSTRAINT: { int val = c->constraint.m_Value.Min(); REPORT( wxString::Format( _( "Checking %s clearance: %s." ), EscapeHTML( c->constraint.GetName() ), - EscapeHTML( REPORT_VALUE( val ) ) ) ) + REPORT_VALUE( val ) ) ) + break; } - else if( c->constraint.m_Type == COURTYARD_CLEARANCE_CONSTRAINT ) + + case TRACK_WIDTH_CONSTRAINT: + case ANNULAR_WIDTH_CONSTRAINT: + case VIA_DIAMETER_CONSTRAINT: { - int val = c->constraint.m_Value.Min(); - REPORT( wxString::Format( _( "Checking %s clearance: %s." ), - EscapeHTML( c->constraint.GetName() ), - EscapeHTML( REPORT_VALUE( val ) ) ) ) + if( aReporter ) + { + wxString min = wxT( "" ) + _( "undefined" ) + wxT( "" ); + wxString opt = wxT( "" ) + _( "undefined" ) + wxT( "" ); + wxString max = wxT( "" ) + _( "undefined" ) + wxT( "" ); + wxString msg; + + if( implicit ) + { + opt = StringFromValue( UNITS, c->constraint.m_Value.Opt(), true ); + + switch( c->constraint.m_Type ) + { + case TRACK_WIDTH_CONSTRAINT: msg = "track width"; break; + case ANNULAR_WIDTH_CONSTRAINT: msg = "annular width"; break; + case VIA_DIAMETER_CONSTRAINT: msg = "via diameter"; break; + default: msg = "constraint"; break; + } + + REPORT( wxString::Format( _( "Checking %s %s: %s." ), + EscapeHTML( c->constraint.GetName() ), + EscapeHTML( msg ), + opt ) ) + } + else + { + if( c->constraint.m_Value.HasMin() ) + min = StringFromValue( UNITS, c->constraint.m_Value.Min(), true ); + + if( c->constraint.m_Value.HasOpt() ) + opt = StringFromValue( UNITS, c->constraint.m_Value.Opt(), true ); + + if( c->constraint.m_Value.HasMax() ) + max = StringFromValue( UNITS, c->constraint.m_Value.Max(), true ); + + REPORT( wxString::Format( _( "Checking %s: min %s; opt %s; max %s." ), + EscapeHTML( c->constraint.GetName() ), + min, + opt, + max ) ) + } + } + break; } - else if( c->constraint.m_Type == SILK_CLEARANCE_CONSTRAINT ) - { - int val = c->constraint.m_Value.Min(); - REPORT( wxString::Format( _( "Checking %s clearance: %s." ), - EscapeHTML( c->constraint.GetName() ), - EscapeHTML( REPORT_VALUE( val ) ) ) ) - } - else if( c->constraint.m_Type == HOLE_CLEARANCE_CONSTRAINT ) - { - int val = c->constraint.m_Value.Min(); - REPORT( wxString::Format( _( "Checking %s clearance: %s." ), - EscapeHTML( c->constraint.GetName() ), - EscapeHTML( REPORT_VALUE( val ) ) ) ) - } - else if( c->constraint.m_Type == EDGE_CLEARANCE_CONSTRAINT ) - { - int val = c->constraint.m_Value.Min(); - REPORT( wxString::Format( _( "Checking %s clearance: %s." ), - EscapeHTML( c->constraint.GetName() ), - EscapeHTML( REPORT_VALUE( val ) ) ) ) - } - else - { - REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) ) + + default: + REPORT( wxString::Format( _( "Checking %s." ), + EscapeHTML( c->constraint.GetName() ) ) ) } if( c->constraint.m_Type == CLEARANCE_CONSTRAINT ) @@ -1086,17 +1054,9 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO } }; - DRC_CONSTRAINT_T effectiveConstraintType = aConstraintType; - - if( aConstraintType == CLEARANCE_CONSTRAINT - && ( isUnflashedNPTH( a, aLayer ) || isUnflashedNPTH( b, aLayer ) ) ) + if( m_constraintMap.count( aConstraintType ) ) { - effectiveConstraintType = HOLE_CLEARANCE_CONSTRAINT; - } - - if( m_constraintMap.count( effectiveConstraintType ) ) - { - std::vector* ruleset = m_constraintMap[ effectiveConstraintType ]; + std::vector* ruleset = m_constraintMap[ aConstraintType ]; if( aReporter ) { @@ -1134,7 +1094,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO REPORT( "" ) REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ), EscapeHTML( a->GetSelectMenuText( UNITS ) ), - EscapeHTML( REPORT_VALUE( localA ) ) ) ) + REPORT_VALUE( localA ) ) ) if( localA > clearance ) clearance = ac->GetLocalClearance( &m_msg ); @@ -1145,7 +1105,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO REPORT( "" ) REPORT( wxString::Format( _( "Local clearance on %s; clearance: %s." ), EscapeHTML( b->GetSelectMenuText( UNITS ) ), - EscapeHTML( REPORT_VALUE( localB ) ) ) ) + REPORT_VALUE( localB ) ) ) if( localB > clearance ) clearance = bc->GetLocalClearance( &m_msg ); diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index 7678f34888..5748a48e4d 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2004-2020 KiCad Developers. + * Copyright (C) 2004-2021 KiCad Developers. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,7 +22,6 @@ */ #include -#include #include #include #include @@ -32,7 +31,6 @@ #include #include -#include #include #include @@ -43,9 +41,12 @@ #include /* - Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their electrical clearance. + Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their + electrical clearance. + Errors generated: - DRCE_CLEARANCE + - DRCE_HOLE_CLEARANCE - DRCE_TRACKS_CROSSING - DRCE_ZONES_INTERSECT - DRCE_SHORTING_ITEMS @@ -90,7 +91,7 @@ private: void testPadClearances(); - void testZones(); + void testZonesToZones(); void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ); @@ -239,14 +240,14 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::Run() if( !reportPhase( _( "Checking copper zone clearances..." ) ) ) return false; // DRC cancelled - testZones(); + testZonesToZones(); } else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT ) ) { if( !reportPhase( _( "Checking zones..." ) ) ) return false; // DRC cancelled - testZones(); + testZonesToZones(); } reportRuleStatistics(); @@ -348,9 +349,9 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track, track->GetLayer() ); clearance = constraint.GetValue().Min(); - if( clearance >= 0 && trackShape->Collide( holeShape.get(), - std::max( 0, clearance - m_drcEpsilon ), - &actual, &pos ) ) + if( clearance > 0 && trackShape->Collide( holeShape.get(), + std::max( 0, clearance - m_drcEpsilon ), + &actual, &pos ) ) { std::shared_ptr drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); @@ -380,9 +381,6 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aItem { for( ZONE* zone : m_zones ) { - if( m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) ) - break; - if( !zone->GetLayerSet().test( aLayer ) ) continue; @@ -394,54 +392,111 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aItem if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) ) { - auto constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, zone, aLayer ); - int clearance = constraint.GetValue().Min(); + bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ); + bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ); - if( clearance < 0 ) - continue; + if( !testClearance && !testHoles ) + return; - int actual; - VECTOR2I pos; - DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get(); - EDA_RECT itemBBox = aItem->GetBoundingBox(); - std::shared_ptr itemShape = aItem->GetEffectiveShape( aLayer ); + DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get(); + EDA_RECT itemBBox = aItem->GetBoundingBox(); + DRC_CONSTRAINT constraint; + int clearance = -1; + int actual; + VECTOR2I pos; - if( aItem->Type() == PCB_PAD_T ) + if( testClearance ) { - PAD* pad = static_cast( aItem ); + constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, zone, aLayer ); + clearance = constraint.GetValue().Min(); + } - if( !pad->FlashLayer( aLayer ) ) + if( clearance >= 0 ) + { + std::shared_ptr itemShape = aItem->GetEffectiveShape( aLayer ); + + if( aItem->Type() == PCB_PAD_T ) { - if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) - continue; + PAD* pad = static_cast( aItem ); - const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape(); - int size = hole->GetWidth(); + if( !pad->FlashLayer( aLayer ) ) + { + if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 ) + continue; - // 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(); + const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape(); + int size = hole->GetWidth(); - itemShape = std::make_shared( hole->GetSeg(), size ); + // Note: drill size represents finish size, which means the actual hole + // size is the plating thickness larger. + if( pad->GetAttribute() == PAD_ATTRIB::PTH ) + size += m_board->GetDesignSettings().GetHolePlatingThickness(); + + itemShape = std::make_shared( hole->GetSeg(), size ); + } + } + + if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, + clearance - m_drcEpsilon, &actual, &pos ) ) + { + std::shared_ptr drce = DRC_ITEM::Create( DRCE_CLEARANCE ); + + m_msg.Printf( _( "(%s clearance %s; actual %s)" ), + constraint.GetName(), + MessageTextFromValue( userUnits(), clearance ), + MessageTextFromValue( userUnits(), actual ) ); + + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); + drce->SetItems( aItem, zone ); + drce->SetViolatingRule( constraint.GetParentRule() ); + + reportViolation( drce, (wxPoint) pos ); } } - if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, - clearance - m_drcEpsilon, &actual, &pos ) ) + if( testHoles && ( aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_PAD_T ) ) { - std::shared_ptr drce = DRC_ITEM::Create( DRCE_CLEARANCE ); + std::unique_ptr holeShape; - m_msg.Printf( _( "(%s clearance %s; actual %s)" ), - constraint.GetName(), - MessageTextFromValue( userUnits(), clearance ), - MessageTextFromValue( userUnits(), actual ) ); + if( aItem->Type() == PCB_VIA_T ) + { + PCB_VIA* via = static_cast( aItem ); + pos = via->GetPosition(); - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); - drce->SetItems( aItem, zone ); - drce->SetViolatingRule( constraint.GetParentRule() ); + if( via->GetLayerSet().Contains( aLayer ) ) + holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) ); + } + else if( aItem->Type() == PCB_PAD_T ) + { + PAD* pad = static_cast( aItem ); - reportViolation( drce, (wxPoint) pos ); + if( pad->GetDrillSize().x ) + holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) ); + } + + if( holeShape ) + { + constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, zone, + aLayer ); + clearance = constraint.GetValue().Min(); + + if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer, + clearance - m_drcEpsilon, &actual, &pos ) ) + { + std::shared_ptr drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); + + m_msg.Printf( _( "(%s clearance %s; actual %s)" ), + constraint.GetName(), + MessageTextFromValue( userUnits(), clearance ), + MessageTextFromValue( userUnits(), actual ) ); + + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); + drce->SetItems( aItem, zone ); + drce->SetViolatingRule( constraint.GetParentRule() ); + + reportViolation( drce, (wxPoint) pos ); + } + } } } } @@ -533,17 +588,43 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa testHoles = false; } + PAD* otherPad = nullptr; + PCB_VIA* otherVia = nullptr; + + if( other->Type() == PCB_PAD_T ) + otherPad = static_cast( other ); + + if( other->Type() == PCB_VIA_T ) + otherVia = static_cast( other ); + + if( !IsCopperLayer( layer ) ) + testClearance = false; + // A NPTH has no cylinder, but it may still have pads on some layers if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) ) testClearance = false; - if( !IsCopperLayer( layer ) ) + if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( layer ) ) testClearance = false; // Track clearances are tested in testTrackClearances() if( dynamic_cast( other) ) testClearance = false; + // Pads of the same (defined) net get a waiver on clearance and hole tests + if( otherPad && pad->GetNetCode() && otherPad->GetNetCode() == pad->GetNetCode() ) + { + testClearance = false; + testHoles = false; + } + + if( !( pad->GetDrillSize().x > 0 ) + && !( otherPad && otherPad->GetDrillSize().x > 0 ) + && !( otherVia && otherVia->GetDrill() > 0 ) ) + { + testHoles = false; + } + if( !testClearance && !testShorting && !testHoles ) return false; @@ -553,87 +634,28 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa int actual; VECTOR2I pos; - if( other->Type() == PCB_PAD_T ) + if( otherPad && pad->SameLogicalPadAs( otherPad ) ) { - PAD* otherPad = static_cast( other ); - // If pads are equivalent (ie: from the same footprint with the same pad number)... - if( pad->SameLogicalPadAs( otherPad ) ) + // ... and have nets... + // then they must be the same net + if( pad->GetNetCode() && otherPad->GetNetCode() + && pad->GetNetCode() != otherPad->GetNetCode() + && testShorting ) { - // ...and have nets, then they must be the same net - if( pad->GetNetCode() && otherPad->GetNetCode() - && pad->GetNetCode() != otherPad->GetNetCode() - && testShorting ) - { - std::shared_ptr drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS ); + std::shared_ptr drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS ); - m_msg.Printf( _( "(nets %s and %s)" ), - pad->GetNetname(), - otherPad->GetNetname() ); + m_msg.Printf( _( "(nets %s and %s)" ), + pad->GetNetname(), + otherPad->GetNetname() ); - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); - drce->SetItems( pad, otherPad ); + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); + drce->SetItems( pad, otherPad ); - reportViolation( drce, otherPad->GetPosition() ); - } - - return true; + reportViolation( drce, otherPad->GetPosition() ); } - if( testHoles && pad->FlashLayer( layer ) && otherPad->GetDrillSize().x ) - { - constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, otherPad, layer ); - clearance = constraint.GetValue().Min(); - - if( clearance >= 0 && padShape->Collide( otherPad->GetEffectiveHoleShape(), - std::max( 0, clearance - m_drcEpsilon ), - &actual, &pos ) ) - { - std::shared_ptr drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); - - m_msg.Printf( _( "(%s clearance %s; actual %s)" ), - constraint.GetName(), - MessageTextFromValue( userUnits(), clearance ), - MessageTextFromValue( userUnits(), actual ) ); - - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); - drce->SetItems( pad, other ); - drce->SetViolatingRule( constraint.GetParentRule() ); - - reportViolation( drce, (wxPoint) pos ); - } - } - - if( testHoles && otherPad->FlashLayer( layer ) && pad->GetDrillSize().x ) - { - constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, otherPad, layer ); - clearance = constraint.GetValue().Min(); - - if( clearance >= 0 && otherShape->Collide( pad->GetEffectiveHoleShape(), - std::max( 0, clearance - m_drcEpsilon ), - &actual, &pos ) ) - { - std::shared_ptr drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); - - m_msg.Printf( _( "(%s clearance %s; actual %s)" ), - constraint.GetName(), - MessageTextFromValue( userUnits(), clearance ), - MessageTextFromValue( userUnits(), actual ) ); - - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); - drce->SetItems( pad, other ); - drce->SetViolatingRule( constraint.GetParentRule() ); - - reportViolation( drce, (wxPoint) pos ); - } - } - - // Pads of the same (defined) net get a waiver on clearance tests - if( pad->GetNetCode() && otherPad->GetNetCode() == pad->GetNetCode() ) - testClearance = false; - - if( otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( layer ) ) - testClearance = false; + return true; } if( testClearance ) @@ -656,6 +678,81 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa drce->SetItems( pad, other ); drce->SetViolatingRule( constraint.GetParentRule() ); + reportViolation( drce, (wxPoint) pos ); + testHoles = false; // No need for multiple violations + } + } + + if( testHoles ) + { + constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, layer ); + clearance = constraint.GetValue().Min(); + } + + if( testHoles && otherPad && pad->FlashLayer( layer ) && otherPad->GetDrillSize().x ) + { + if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape(), + std::max( 0, clearance - m_drcEpsilon ), + &actual, &pos ) ) + { + std::shared_ptr drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); + + m_msg.Printf( _( "(%s clearance %s; actual %s)" ), + constraint.GetName(), + MessageTextFromValue( userUnits(), clearance ), + MessageTextFromValue( userUnits(), actual ) ); + + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); + drce->SetItems( pad, other ); + drce->SetViolatingRule( constraint.GetParentRule() ); + + reportViolation( drce, (wxPoint) pos ); + testHoles = false; // No need for multiple violations + } + } + + if( testHoles && otherPad && otherPad->FlashLayer( layer ) && pad->GetDrillSize().x ) + { + if( clearance >= 0 && otherShape->Collide( pad->GetEffectiveHoleShape(), + std::max( 0, clearance - m_drcEpsilon ), + &actual, &pos ) ) + { + std::shared_ptr drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); + + m_msg.Printf( _( "(%s clearance %s; actual %s)" ), + constraint.GetName(), + MessageTextFromValue( userUnits(), clearance ), + MessageTextFromValue( userUnits(), actual ) ); + + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); + drce->SetItems( pad, other ); + drce->SetViolatingRule( constraint.GetParentRule() ); + + reportViolation( drce, (wxPoint) pos ); + testHoles = false; // No need for multiple violations + } + } + + if( testHoles && otherVia && otherVia->IsOnLayer( layer ) ) + { + pos = otherVia->GetPosition(); + otherShape.reset( new SHAPE_SEGMENT( pos, pos, otherVia->GetDrill() ) ); + + if( clearance > 0 && padShape->Collide( otherShape.get(), + std::max( 0, clearance - m_drcEpsilon ), + &actual, &pos ) ) + { + std::shared_ptr drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE ); + + m_msg.Printf( _( "(%s clearance %s; actual %s)" ), + constraint.GetName(), + MessageTextFromValue( userUnits(), clearance ), + MessageTextFromValue( userUnits(), actual ) ); + + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg ); + drce->SetItems( pad, otherVia ); + drce->SetViolatingRule( constraint.GetParentRule() ); + reportViolation( drce, (wxPoint) pos ); } } @@ -725,7 +822,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadClearances( ) } -void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZones() +void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() { const int delta = 50; // This is the number of tests between 2 calls to the progress bar diff --git a/pcbnew/pcb_track.cpp b/pcbnew/pcb_track.cpp index 2e2f7455b5..01751da2bc 100644 --- a/pcbnew/pcb_track.cpp +++ b/pcbnew/pcb_track.cpp @@ -540,8 +540,7 @@ bool PCB_VIA::FlashLayer( int aLayer ) const if( m_keepTopBottomLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) ) return true; - return board->GetConnectivity()->IsConnectedOnLayer( this, static_cast( aLayer ), - types ); + return board->GetConnectivity()->IsConnectedOnLayer( this, static_cast( aLayer ), types ); } @@ -1031,9 +1030,18 @@ std::shared_ptr PCB_TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer ) const std::shared_ptr PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer ) const { if( FlashLayer( aLayer ) ) + { return std::make_shared( m_Start, m_Width / 2 ); + } else - return std::make_shared( m_Start, GetDrillValue() / 2 ); + { + int radius = GetDrillValue() / 2; + + if( GetBoard() ) + radius += GetBoard()->GetDesignSettings().GetHolePlatingThickness(); + + return std::make_shared( m_Start, radius ); + } } diff --git a/pcbnew/tools/board_inspection_tool.cpp b/pcbnew/tools/board_inspection_tool.cpp index 985480021f..44b47f057f 100644 --- a/pcbnew/tools/board_inspection_tool.cpp +++ b/pcbnew/tools/board_inspection_tool.cpp @@ -527,6 +527,33 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent ) } +wxString reportMin( EDA_UNITS aUnits, DRC_CONSTRAINT& aConstraint ) +{ + if( aConstraint.m_Value.HasMin() ) + return StringFromValue( aUnits, aConstraint.m_Value.Min(), true ); + else + return wxT( "" ) + _( "undefined" ) + wxT( "" ); +} + + +wxString reportOpt( EDA_UNITS aUnits, DRC_CONSTRAINT& aConstraint ) +{ + if( aConstraint.m_Value.HasOpt() ) + return StringFromValue( aUnits, aConstraint.m_Value.Opt(), true ); + else + return wxT( "" ) + _( "undefined" ) + wxT( "" ); +} + + +wxString reportMax( EDA_UNITS aUnits, DRC_CONSTRAINT& aConstraint ) +{ + if( aConstraint.m_Value.HasMax() ) + return StringFromValue( aUnits, aConstraint.m_Value.Max(), true ); + else + return wxT( "" ) + _( "undefined" ) + wxT( "" ); +} + + int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) { PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); @@ -580,7 +607,7 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) } WX_HTML_REPORT_BOX* r = nullptr; - + if( item->Type() == PCB_TRACE_T ) { r = m_inspectConstraintsDialog->AddPage( _( "Track Width" ) ); @@ -600,19 +627,11 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) auto constraint = drcEngine.EvalRules( TRACK_WIDTH_CONSTRAINT, item, nullptr, item->GetLayer(), r ); - wxString min = _( "undefined" ); - wxString max = _( "undefined" ); - - if( constraint.m_Value.HasMin() ) - min = StringFromValue( r->GetUnits(), constraint.m_Value.Min(), true ); - - if( constraint.m_Value.HasMax() ) - max = StringFromValue( r->GetUnits(), constraint.m_Value.Max(), true ); - r->Report( "" ); - r->Report( wxString::Format( _( "Width constraints: min %s max %s." ), - min, - max ) ); + r->Report( wxString::Format( _( "Width constraints: min %s; opt %s; max %s." ), + reportMin( r->GetUnits(), constraint ), + reportOpt( r->GetUnits(), constraint ), + reportMax( r->GetUnits(), constraint ) ) ); } r->Flush(); @@ -638,19 +657,11 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) auto constraint = drcEngine.EvalRules( VIA_DIAMETER_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r ); - wxString min = _( "undefined" ); - wxString max = _( "undefined" ); - - if( constraint.m_Value.HasMin() ) - min = StringFromValue( r->GetUnits(), constraint.m_Value.Min(), true ); - - if( constraint.m_Value.HasMax() ) - max = StringFromValue( r->GetUnits(), constraint.m_Value.Max(), true ); - r->Report( "" ); - r->Report( wxString::Format( _( "Diameter constraints: min %s max %s." ), - min, - max ) ); + r->Report( wxString::Format( _( "Diameter constraints: min %s; opt %s; max %s." ), + reportMin( r->GetUnits(), constraint ), + reportOpt( r->GetUnits(), constraint ), + reportMax( r->GetUnits(), constraint ) ) ); } r->Flush(); @@ -673,19 +684,11 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) auto constraint = drcEngine.EvalRules( ANNULAR_WIDTH_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r ); - wxString min = _( "undefined" ); - wxString max = _( "undefined" ); - - if( constraint.m_Value.HasMin() ) - min = StringFromValue( r->GetUnits(), constraint.m_Value.Min(), true ); - - if( constraint.m_Value.HasMax() ) - max = StringFromValue( r->GetUnits(), constraint.m_Value.Max(), true ); - r->Report( "" ); - r->Report( wxString::Format( _( "Annular width constraints: min %s max %s." ), - min, - max ) ); + r->Report( wxString::Format( _( "Annular width constraints: min %s; opt %s; max %s." ), + reportMin( r->GetUnits(), constraint ), + reportOpt( r->GetUnits(), constraint ), + reportMax( r->GetUnits(), constraint ) ) ); } r->Flush(); diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 5d1a242d65..44cfc07d55 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -569,6 +569,20 @@ void ZONE_FILLER::addKnockout( PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE_P } +/** + * Add a knockout for a pad's hole. + */ +void ZONE_FILLER::addHoleKnockout( PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles ) +{ + // Note: drill size represents finish size, which means the actual hole size is the plating + // thickness larger. + if( aPad->GetAttribute() == PAD_ATTRIB::PTH ) + aGap += aPad->GetBoard()->GetDesignSettings().GetHolePlatingThickness(); + + aPad->TransformHoleWithClearanceToPolygon( aHoles, aGap, m_maxError, ERROR_OUTSIDE ); +} + + /** * Add a knockout for a graphic item. The knockout is 'aGap' larger than the item (which * might be either the electrical clearance or the board edge clearance). @@ -691,34 +705,36 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa { if( aPad->GetBoundingBox().Intersects( zone_boundingbox ) ) { - int gap; + int gap = 0; + bool knockoutHoleClearance = true; - // For pads having the same netcode as the zone, the net clearance has no - // meaning so use the greater of the zone clearance and the thermal relief. if( aPad->GetNetCode() > 0 && aPad->GetNetCode() == aZone->GetNetCode() ) - gap = std::max( zone_clearance, aZone->GetThermalReliefGap( aPad ) ); - else - gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aPad, aLayer ); - - gap += extra_margin; - - // If the pad isn't on the current layer but has a hole, knock out the hole. - if( !aPad->FlashLayer( aLayer ) ) { - if( aPad->GetDrillSize().x == 0 && aPad->GetDrillSize().y == 0 ) - return; - - // Note: drill size represents finish size, which means the actual hole - // size is the plating thickness larger. - if( aPad->GetAttribute() == PAD_ATTRIB::PTH ) - gap += aPad->GetBoard()->GetDesignSettings().GetHolePlatingThickness(); - - aPad->TransformHoleWithClearanceToPolygon( aHoles, gap, m_maxError, - ERROR_OUTSIDE ); + // For pads having the same netcode as the zone, the net and hole + // clearances have no meanings. + // So just knock out the greater of the zone's local clearance and + // thermal relief. + gap = std::max( zone_clearance, aZone->GetThermalReliefGap( aPad ) ); + knockoutHoleClearance = false; } else { + gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aPad, aLayer ); + } + + gap += extra_margin; + + if( aPad->FlashLayer( aLayer ) ) addKnockout( aPad, aLayer, gap, aHoles ); + else if( aPad->GetDrillSize().x > 0 ) + addHoleKnockout( aPad, gap, aHoles ); + + if( knockoutHoleClearance && aPad->GetDrillSize().x > 0 ) + { + gap = evalRulesForItems( HOLE_CLEARANCE_CONSTRAINT, aZone, aPad, aLayer ); + gap += extra_margin; + + addHoleKnockout( aPad, gap, aHoles ); } } }; @@ -746,29 +762,50 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa { if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) ) { - int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer ); - - gap += extra_margin; - if( aTrack->Type() == PCB_VIA_T ) { PCB_VIA* via = static_cast( aTrack ); + int gap = 0; + bool checkHoleClearance = true; - if( !via->FlashLayer( aLayer ) && via->GetNetCode() != aZone->GetNetCode() ) + if( via->GetNetCode() > 0 && via->GetNetCode() == aZone->GetNetCode() ) { - int radius = via->GetDrillValue() / 2 + bds.GetHolePlatingThickness(); - TransformCircleToPolygon( aHoles, via->GetPosition(), radius + gap, - m_maxError, ERROR_OUTSIDE ); + // For pads having the same netcode as the zone, the net and hole + // clearances have no meanings. + // So just knock out the zone's local clearance. + gap = zone_clearance; + checkHoleClearance = false; } else { - via->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, + gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer ); + } + + if( via->FlashLayer( aLayer ) ) + { + via->TransformShapeWithClearanceToPolygon( aHoles, aLayer, + gap + extra_margin, m_maxError, ERROR_OUTSIDE ); } + + if( checkHoleClearance ) + { + gap = std::max( gap, evalRulesForItems( HOLE_CLEARANCE_CONSTRAINT, + aZone, via, aLayer ) ); + } + + int radius = via->GetDrillValue() / 2 + bds.GetHolePlatingThickness(); + + TransformCircleToPolygon( aHoles, via->GetPosition(), + radius + gap + extra_margin, + m_maxError, ERROR_OUTSIDE ); } else { - aTrack->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, + int gap = evalRulesForItems( CLEARANCE_CONSTRAINT, aZone, aTrack, aLayer ); + + aTrack->TransformShapeWithClearanceToPolygon( aHoles, aLayer, + gap + extra_margin, m_maxError, ERROR_OUTSIDE ); } } diff --git a/pcbnew/zone_filler.h b/pcbnew/zone_filler.h index 89c9a770b4..174396e326 100644 --- a/pcbnew/zone_filler.h +++ b/pcbnew/zone_filler.h @@ -62,6 +62,8 @@ private: void addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap, bool aIgnoreLineWidth, SHAPE_POLY_SET& aHoles ); + void addHoleKnockout( PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles ); + void knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aFill ); void buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLayer, diff --git a/qa/data/issue6945.kicad_pro b/qa/data/issue6945.kicad_pro index eb5026da3e..b91a40bf0c 100644 --- a/qa/data/issue6945.kicad_pro +++ b/qa/data/issue6945.kicad_pro @@ -48,7 +48,13 @@ "min_clearance": 0.508 } }, - "diff_pair_dimensions": [], + "diff_pair_dimensions": [ + { + "gap": 0.0, + "via_gap": 0.0, + "width": 0.0 + } + ], "drc_exclusions": [], "meta": { "version": 2 @@ -108,8 +114,15 @@ "min_via_diameter": 0.39999999999999997, "use_height_for_length_calcs": true }, - "track_widths": [], - "via_dimensions": [], + "track_widths": [ + 0.0 + ], + "via_dimensions": [ + { + "diameter": 0.0, + "drill": 0.0 + } + ], "zones_allow_external_fillets": false, "zones_use_no_outline": true }, diff --git a/qa/pcbnew/drc/test_drc_regressions.cpp b/qa/pcbnew/drc/test_drc_regressions.cpp index 20690c7a26..f881f05c1d 100644 --- a/qa/pcbnew/drc/test_drc_regressions.cpp +++ b/qa/pcbnew/drc/test_drc_regressions.cpp @@ -106,7 +106,7 @@ BOOST_FIXTURE_TEST_CASE( DRCFalseNegativeRegressions, DRC_REGRESSION_TEST_FIXTUR { "issue2528", 1 }, { "issue5750", 4 }, { "issue5854", 3 }, - { "issue6879", 7 }, + { "issue6879", 6 }, { "issue6945", 2 }, { "issue7241", 1 }, { "issue7267", 4 },