diff --git a/pcbnew/dialogs/dialog_pad_properties.cpp b/pcbnew/dialogs/dialog_pad_properties.cpp index 788e2a8ee4..1a29dde175 100644 --- a/pcbnew/dialogs/dialog_pad_properties.cpp +++ b/pcbnew/dialogs/dialog_pad_properties.cpp @@ -1242,13 +1242,11 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK() // Test hole size against pad size if( m_dummyPad->IsOnCopperLayer() ) { - LSET lset = m_dummyPad->GetLayerSet() & LSET::AllCuMask(); - PCB_LAYER_ID layer = lset.Seq().at( 0 ); int maxError = m_board->GetDesignSettings().m_MaxError; SHAPE_POLY_SET padOutline; - m_dummyPad->TransformShapeWithClearanceToPolygon( padOutline, layer, 0, maxError, - ERROR_LOC::ERROR_INSIDE ); + m_dummyPad->TransformShapeWithClearanceToPolygon( padOutline, UNDEFINED_LAYER, 0, + maxError, ERROR_INSIDE ); const SHAPE_SEGMENT* drillShape = m_dummyPad->GetEffectiveHoleShape(); const SEG drillSeg = drillShape->GetSeg(); diff --git a/pcbnew/drc/drc_test_provider_annular_width.cpp b/pcbnew/drc/drc_test_provider_annular_width.cpp index 25a7b051eb..7d61a1b874 100644 --- a/pcbnew/drc/drc_test_provider_annular_width.cpp +++ b/pcbnew/drc/drc_test_provider_annular_width.cpp @@ -23,10 +23,15 @@ #include #include +#include +#include #include #include #include #include +#include +#include +#include /* Via/pad annular ring width test. Checks if there's sufficient copper ring around @@ -87,24 +92,138 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() if( !reportPhase( _( "Checking via annular rings..." ) ) ) return false; // DRC cancelled + int maxError = m_drcEngine->GetBoard()->GetDesignSettings().m_MaxError; + + auto calcEffort = + []( BOARD_ITEM* item ) + { + switch( item->Type() ) + { + case PCB_VIA_T: + return 1; + + case PCB_PAD_T: + { + PAD* pad = static_cast( item ); + + if( !pad->HasHole() || pad->GetAttribute() != PAD_ATTRIB::PTH ) + return 0; + + if( pad->GetOffset() == wxPoint( 0, 0 ) ) + { + switch( pad->GetShape() ) + { + case PAD_SHAPE::CHAMFERED_RECT: + if( pad->GetChamferRectRatio() > 0.30 ) + break; + + KI_FALLTHROUGH; + + case PAD_SHAPE::CIRCLE: + case PAD_SHAPE::OVAL: + case PAD_SHAPE::RECT: + case PAD_SHAPE::ROUNDRECT: + return 1; + + default: + break; + } + } + + return 100; + } + + default: + return 0; + } + }; + auto checkAnnularWidth = [&]( BOARD_ITEM* item ) -> bool { if( m_drcEngine->IsErrorLimitExceeded( DRCE_ANNULAR_WIDTH ) ) return false; - int v_min = 0; - int v_max = 0; - PCB_VIA* via = dyn_cast( item ); + int annularWidth = 0; - // fixme: check minimum IAR/OAR ring for THT pads too - if( !via ) + switch( item->Type() ) + { + case PCB_VIA_T: + { + PCB_VIA* via = static_cast( item ); + annularWidth = ( via->GetWidth() - via->GetDrillValue() ) / 2; + break; + } + + case PCB_PAD_T: + { + PAD* pad = static_cast( item ); + bool handled = false; + + if( !pad->HasHole() || pad->GetAttribute() != PAD_ATTRIB::PTH ) + return true; + + if( pad->GetOffset() == wxPoint( 0, 0 ) ) + { + switch( pad->GetShape() ) + { + case PAD_SHAPE::CHAMFERED_RECT: + if( pad->GetChamferRectRatio() > 0.30 ) + break; + + KI_FALLTHROUGH; + + case PAD_SHAPE::CIRCLE: + case PAD_SHAPE::OVAL: + case PAD_SHAPE::RECT: + case PAD_SHAPE::ROUNDRECT: + annularWidth = std::min( pad->GetSizeX() - pad->GetDrillSizeX(), + pad->GetSizeY() - pad->GetDrillSizeY() ) / 2; + handled = true; + break; + + default: + break; + } + } + + if( !handled ) + { + // Slow (but general purpose) method. + SHAPE_POLY_SET padOutline; + + pad->TransformShapeWithClearanceToPolygon( padOutline, UNDEFINED_LAYER, 0, + maxError, ERROR_INSIDE ); + + if( !padOutline.Collide( pad->GetPosition() ) ) + { + // Hole outside pad + annularWidth = 0; + } + else + { + const SHAPE_SEGMENT* slot = pad->GetEffectiveHoleShape(); + + // Disable is-inside test in SquaredDistance + padOutline.Outline( 0 ).SetClosed( false ); + + SEG::ecoord dist_sq = padOutline.SquaredDistance( slot->GetSeg() ); + annularWidth = sqrt( dist_sq ) - slot->GetWidth() / 2; + } + } + + break; + } + + default: return true; + } // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer.... - auto constraint = m_drcEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, via, nullptr, + auto constraint = m_drcEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, item, nullptr, UNDEFINED_LAYER ); - int annularWidth = ( via->GetWidth() - via->GetDrillValue() ) / 2; + int v_min = 0; + int v_max = 0; bool fail_min = false; bool fail_max = false; @@ -126,39 +245,69 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() wxString msg; if( fail_min ) + { msg.Printf( _( "(%s min annular width %s; actual %s)" ), constraint.GetName(), MessageTextFromValue( userUnits(), v_min ), MessageTextFromValue( userUnits(), annularWidth ) ); + } if( fail_max ) + { msg.Printf( _( "(%s max annular width %s; actual %s)" ), constraint.GetName(), MessageTextFromValue( userUnits(), v_max ), MessageTextFromValue( userUnits(), annularWidth ) ); + } drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg ); drcItem->SetItems( item ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, via->GetPosition() ); + reportViolation( drcItem, item->GetPosition() ); } return true; }; BOARD* board = m_drcEngine->GetBoard(); - int ii = 0; + size_t ii = 0; + size_t total = 0; + + for( PCB_TRACK* item : board->Tracks() ) + total += calcEffort( item ); + + for( FOOTPRINT* footprint : board->Footprints() ) + { + for( PAD* pad : footprint->Pads() ) + total += calcEffort( pad ); + } for( PCB_TRACK* item : board->Tracks() ) { - if( !reportProgress( ii++, board->Tracks().size(), delta ) ) + ii += calcEffort( item ); + + if( !reportProgress( ii, total, delta ) ) return false; // DRC cancelled if( !checkAnnularWidth( item ) ) break; } + for( FOOTPRINT* footprint : board->Footprints() ) + { + for( PAD* pad : footprint->Pads() ) + { + ii += calcEffort( pad ); + + if( !reportProgress( ii, total, delta ) ) + return false; // DRC cancelled + + if( !checkAnnularWidth( pad ) ) + break; + } + } + reportRuleStatistics(); return true; diff --git a/pcbnew/pad.h b/pcbnew/pad.h index 3f36610284..8931545346 100644 --- a/pcbnew/pad.h +++ b/pcbnew/pad.h @@ -102,6 +102,11 @@ public: return false; } + bool HasHole() const + { + return GetDrillSizeX() > 0 && GetDrillSizeY() > 0; + } + FOOTPRINT* GetParent() const; wxString GetParentAsString() const { return m_parent->m_Uuid.AsString(); }