diff --git a/pcbnew/dialogs/dialog_pad_properties.cpp b/pcbnew/dialogs/dialog_pad_properties.cpp index 20fb53495b..4aab7312f3 100644 --- a/pcbnew/dialogs/dialog_pad_properties.cpp +++ b/pcbnew/dialogs/dialog_pad_properties.cpp @@ -1266,13 +1266,11 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK() // Test hole against pad shape if( m_dummyPad->IsOnCopperLayer() && m_dummyPad->GetDrillSize().x > 0 ) { - 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 ); if( !padOutline.Collide( m_dummyPad->GetPosition() ) ) { @@ -1284,9 +1282,9 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK() SHAPE_POLY_SET slotOutline; TransformOvalToPolygon( slotOutline, slot->GetSeg().A, slot->GetSeg().B, - slot->GetWidth(), maxError, ERROR_LOC::ERROR_INSIDE ); + slot->GetWidth(), maxError, ERROR_INSIDE ); - padOutline.BooleanSubtract( slotOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + padOutline.BooleanSubtract( slotOutline, SHAPE_POLY_SET::PM_FAST ); if( padOutline.IsEmpty() ) warning_msgs.Add( _( "Warning: Pad hole will leave no copper." ) ); diff --git a/pcbnew/drc/drc_test_provider_annular_width.cpp b/pcbnew/drc/drc_test_provider_annular_width.cpp index 8987a78969..a14b78a1ea 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 @@ -80,27 +85,141 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() return true; // continue with other tests } - if( !reportPhase( _( "Checking via annular rings..." ) ) ) + if( !reportPhase( _( "Checking pad & 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() == VECTOR2I( 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() == VECTOR2I( 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 + { + std::shared_ptr 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; @@ -125,39 +244,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(), via->GetLayer() ); + reportViolation( drcItem, item->GetPosition(), item->GetLayer() ); } 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 !m_drcEngine->IsCancelled();