diff --git a/pcbnew/drc/drc_test_provider_annular_width.cpp b/pcbnew/drc/drc_test_provider_annular_width.cpp index e3008c854c..f9518bbe66 100644 --- a/pcbnew/drc/drc_test_provider_annular_width.cpp +++ b/pcbnew/drc/drc_test_provider_annular_width.cpp @@ -88,8 +88,6 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() if( !reportPhase( _( "Checking pad & via annular rings..." ) ) ) return false; // DRC cancelled - int maxError = m_drcEngine->GetBoard()->GetDesignSettings().m_MaxError; - auto calcEffort = []( BOARD_ITEM* item ) -> size_t { @@ -143,6 +141,120 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() } }; + auto checkPadAnnularWidth = + []( PAD* pad, PCB_LAYER_ID aLayer, DRC_CONSTRAINT& constraint, + const std::vector& sameNumPads, + int* aMinAnnularWidth, int* aMaxAnnularWidth ) + { + int annularWidth = 0; + bool handled = false; + + if( pad->GetOffset( aLayer ) == VECTOR2I( 0, 0 ) ) + { + VECTOR2I padSize = pad->GetSize( aLayer ); + + switch( pad->GetShape( aLayer ) ) + { + case PAD_SHAPE::CIRCLE: + annularWidth = ( padSize.x - pad->GetDrillSizeX() ) / 2; + + // If there are more pads with the same number then we'll still need to + // run the more generalised checks below. + handled = sameNumPads.empty(); + + break; + + case PAD_SHAPE::CHAMFERED_RECT: + if( pad->GetChamferRectRatio( aLayer ) > 0.30 ) + break; + + KI_FALLTHROUGH; + + case PAD_SHAPE::OVAL: + case PAD_SHAPE::RECTANGLE: + case PAD_SHAPE::ROUNDRECT: + annularWidth = std::min( padSize.x - pad->GetDrillSizeX(), + padSize.y - pad->GetDrillSizeY() ) / 2; + + // If there are more pads with the same number then we'll still need to + // run the more generalised checks below. + handled = sameNumPads.empty(); + + break; + + default: + break; + } + } + + if( !handled ) + { + // Slow (but general purpose) method. + int maxError = pad->GetBoard()->GetDesignSettings().m_MaxError; + SEG::ecoord dist_sq; + SHAPE_POLY_SET padOutline; + std::shared_ptr slot = pad->GetEffectiveHoleShape(); + + pad->TransformShapeToPolygon( padOutline, aLayer, 0, maxError, ERROR_INSIDE ); + + if( sameNumPads.empty() ) + { + if( !padOutline.Collide( pad->GetPosition() ) ) + { + // Hole outside pad + annularWidth = 0; + } + else + { + // Disable is-inside test in SquaredDistance + padOutline.Outline( 0 ).SetClosed( false ); + + dist_sq = padOutline.SquaredDistanceToSeg( slot->GetSeg() ); + annularWidth = sqrt( dist_sq ) - slot->GetWidth() / 2; + } + } + else if( constraint.Value().HasMin() + && ( annularWidth < constraint.Value().Min() ) ) + { + SHAPE_POLY_SET aggregatePadOutline = padOutline; + SHAPE_POLY_SET otherPadHoles; + SHAPE_POLY_SET slotPolygon; + + slot->TransformToPolygon( slotPolygon, 0, ERROR_INSIDE ); + + for( const PAD* sameNumPad : sameNumPads ) + { + // Construct the full pad with outline and hole. + sameNumPad->TransformShapeToPolygon( aggregatePadOutline, + PADSTACK::ALL_LAYERS, 0, + maxError, ERROR_OUTSIDE ); + + sameNumPad->TransformHoleToPolygon( otherPadHoles, 0, maxError, + ERROR_INSIDE ); + } + + aggregatePadOutline.BooleanSubtract( otherPadHoles ); + + if( !aggregatePadOutline.Collide( pad->GetPosition() ) ) + { + // Hole outside pad + annularWidth = 0; + } + else + { + // Disable is-inside test in SquaredDistance + aggregatePadOutline.Outline( 0 ).SetClosed( false ); + + dist_sq = aggregatePadOutline.SquaredDistanceToSeg( slot->GetSeg() ); + annularWidth = sqrt( dist_sq ) - slot->GetWidth() / 2; + } + } + } + + *aMaxAnnularWidth = std::max( *aMaxAnnularWidth, annularWidth ); + *aMinAnnularWidth = std::min( *aMinAnnularWidth, annularWidth ); + }; + auto checkAnnularWidth = [&]( BOARD_ITEM* item ) -> bool { @@ -159,167 +271,41 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() bool fail_min = false; bool fail_max = false; - switch( item->Type() ) { case PCB_VIA_T: { PCB_VIA* via = static_cast( item ); - int drill = via->GetDrillValue(); + int drill = via->GetDrillValue(); via->Padstack().ForEachUniqueLayer( - [&]( PCB_LAYER_ID aLayer ) - { - int layerWidth = ( via->GetWidth( aLayer ) - drill ) / 2; - minAnnularWidth = std::min( minAnnularWidth, layerWidth ); - maxAnnularWidth = std::max( maxAnnularWidth, layerWidth ); - } ); + [&]( PCB_LAYER_ID aLayer ) + { + int layerWidth = ( via->GetWidth( aLayer ) - drill ) / 2; + minAnnularWidth = std::min( minAnnularWidth, layerWidth ); + maxAnnularWidth = std::max( maxAnnularWidth, layerWidth ); + } ); break; } case PCB_PAD_T: { PAD* pad = static_cast( item ); - bool handled = false; if( !pad->HasHole() || pad->GetAttribute() != PAD_ATTRIB::PTH ) return true; std::vector sameNumPads; - const FOOTPRINT* fp = static_cast( pad->GetParent() ); - - if( fp ) + if( const FOOTPRINT* fp = static_cast( pad->GetParent() ) ) sameNumPads = fp->GetPads( pad->GetNumber(), pad ); pad->Padstack().ForEachUniqueLayer( - [&]( PCB_LAYER_ID aLayer ) - { - int annularWidth = 0; - - if( pad->GetOffset( aLayer ) == VECTOR2I( 0, 0 ) ) - { - VECTOR2I padSize = pad->GetSize( aLayer ); - - switch( pad->GetShape( aLayer ) ) - { - case PAD_SHAPE::CIRCLE: - annularWidth = ( padSize.x - pad->GetDrillSizeX() ) / 2; - - // If there are more pads with the same number. Check to see if the - // pad is embedded inside another pad with the same number below. - if( sameNumPads.empty() ) - handled = true; - - break; - - case PAD_SHAPE::CHAMFERED_RECT: - if( pad->GetChamferRectRatio( aLayer ) > 0.30 ) - break; - - KI_FALLTHROUGH; - - case PAD_SHAPE::OVAL: - case PAD_SHAPE::RECTANGLE: - case PAD_SHAPE::ROUNDRECT: - annularWidth = std::min( padSize.x - pad->GetDrillSizeX(), - padSize.y - pad->GetDrillSizeY() ) / 2; - - // If there are more pads with the same number. Check to see if the - // pad is embedded inside another pad with the same number below. - if( sameNumPads.empty() ) - handled = true; - - break; - - default: - break; - } - } - - if( !handled ) + [&]( PCB_LAYER_ID aLayer ) { - // Slow (but general purpose) method. - SEG::ecoord dist_sq; - SHAPE_POLY_SET padOutline; - std::shared_ptr slot = pad->GetEffectiveHoleShape(); - - pad->TransformShapeToPolygon( padOutline, aLayer, 0, maxError, - ERROR_INSIDE ); - - if( sameNumPads.empty() ) - { - if( !padOutline.Collide( pad->GetPosition() ) ) - { - // Hole outside pad - annularWidth = 0; - } - else - { - // Disable is-inside test in SquaredDistance - padOutline.Outline( 0 ).SetClosed( false ); - - dist_sq = padOutline.SquaredDistanceToSeg( slot->GetSeg() ); - annularWidth = sqrt( dist_sq ) - slot->GetWidth() / 2; - } - } - else if( constraint.Value().HasMin() - && ( annularWidth < constraint.Value().Min() ) ) - { - SHAPE_POLY_SET otherPadOutline; - SHAPE_POLY_SET otherPadHoles; - SHAPE_POLY_SET slotPolygon; - - slot->TransformToPolygon( slotPolygon, 0, ERROR_INSIDE ); - - for( const PAD* sameNumPad : sameNumPads ) - { - // Construct the full pad with outline and hole. - sameNumPad->TransformShapeToPolygon( - otherPadOutline, PADSTACK::ALL_LAYERS, 0, maxError, - ERROR_OUTSIDE ); - - sameNumPad->TransformHoleToPolygon( - otherPadHoles, 0, maxError, ERROR_INSIDE ); - } - - otherPadOutline.BooleanSubtract( otherPadHoles ); - - // If the pad hole under test intersects with another pad outline, - // the annular width calculated above is used. - bool intersects = false; - - for( int i = 0; i < otherPadOutline.OutlineCount() && !intersects; i++ ) - { - intersects |= slotPolygon.COutline( 0 ).Intersects( otherPadOutline.COutline( i ) ); - - for( int j = 0; j < otherPadOutline.HoleCount( i ) && !intersects; j++ ) - { - intersects |= slotPolygon.COutline( 0 ).Intersects( otherPadOutline.CHole( i, j ) ); - } - } - - if( !intersects ) - { - // Determine the effective annular width if the pad hole under - // test lies withing the boundary of another pad outline. - int effectiveWidth = std::numeric_limits::max(); - - for( int ii = 0; ii < otherPadOutline.OutlineCount(); ii++ ) - { - if( slot->Collide( &otherPadOutline.Outline( ii ), 0 ) ) - { - if( effectiveWidth > annularWidth ) - annularWidth = effectiveWidth; - } - } - } - } - } - - maxAnnularWidth = std::max( maxAnnularWidth, annularWidth ); - minAnnularWidth = std::min( minAnnularWidth, annularWidth ); - } ); + checkPadAnnularWidth( pad, aLayer, constraint, sameNumPads, + &minAnnularWidth, &maxAnnularWidth ); + } ); break; }