From 8c97a7b8c6c6b205589b92ffe1e63381a14f1239 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 2 Dec 2022 16:33:00 +0000 Subject: [PATCH] If fps contain complete board-edge holes then exclude those edges from main calc. Fixes https://gitlab.com/kicad/code/kicad/issues/12921 --- pcbnew/convert_shape_list_to_polygon.cpp | 115 +++++++++++++++++------ 1 file changed, 85 insertions(+), 30 deletions(-) diff --git a/pcbnew/convert_shape_list_to_polygon.cpp b/pcbnew/convert_shape_list_to_polygon.cpp index 4acdfee86e..d52ed40d2a 100644 --- a/pcbnew/convert_shape_list_to_polygon.cpp +++ b/pcbnew/convert_shape_list_to_polygon.cpp @@ -132,6 +132,34 @@ static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const VECTOR2I& aPoint, } +bool isCopperOutside( const FOOTPRINT* aFootprint, SHAPE_POLY_SET& aShape ) +{ + bool padOutside = false; + + for( PAD* pad : aFootprint->Pads() ) + { + SHAPE_POLY_SET poly = aShape.CloneDropTriangulation(); + + poly.BooleanIntersection( *pad->GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST ); + + if( poly.OutlineCount() == 0 ) + { + VECTOR2I padPos = pad->GetPosition(); + wxLogTrace( traceBoardOutline, wxT( "Tested pad (%d, %d): outside" ), + padPos.x, padPos.y ); + padOutside = true; + break; + } + + VECTOR2I padPos = pad->GetPosition(); + wxLogTrace( traceBoardOutline, wxT( "Tested pad (%d, %d): not outside" ), + padPos.x, padPos.y ); + } + + return padOutside; +} + + /** * Function ConvertOutlineToPolygon * Build a polygon (with holes) from a PCB_SHAPE list, which is expected to be a closed main @@ -579,16 +607,62 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aE PCB_TYPE_COLLECTOR items; bool success = false; + SHAPE_POLY_SET fpHoles; + // Get all the PCB and FP shapes into 'items', then keep only those on layer == Edge_Cuts. items.Collect( aBoard, { PCB_SHAPE_T, PCB_FP_SHAPE_T } ); + for( int ii = 0; ii < items.GetCount(); ++ii ) + items[ii]->ClearFlags( SKIP_STRUCT ); + + for( FOOTPRINT* fp : aBoard->Footprints() ) + { + PCB_TYPE_COLLECTOR fpItems; + fpItems.Collect( fp, { PCB_SHAPE_T, PCB_FP_SHAPE_T } ); + + std::vector fpSegList; + + for( int ii = 0; ii < fpItems.GetCount(); ii++ ) + { + PCB_SHAPE* fpSeg = static_cast( fpItems[ii] ); + + if( fpSeg->GetLayer() == Edge_Cuts ) + fpSegList.push_back( fpSeg ); + } + + if( !fpSegList.empty() ) + { + SHAPE_POLY_SET fpOutlines; + success = ConvertOutlineToPolygon( fpSegList, fpOutlines, aErrorMax, aChainingEpsilon, + false, aErrorHandler ); + + if( success && isCopperOutside( fp, fpOutlines ) ) + { + fpHoles.Append( fpOutlines ); + } + else + { + // If it wasn't a closed area, or wasn't a hole, the we want to keep the fpSegs + // in contention for the board outline builds. + for( int ii = 0; ii < fpItems.GetCount(); ++ii ) + fpItems[ii]->ClearFlags( SKIP_STRUCT ); + } + } + } + // Make a working copy of aSegList, because the list is modified during calculations std::vector segList; for( int ii = 0; ii < items.GetCount(); ii++ ) { - if( items[ii]->GetLayer() == Edge_Cuts ) - segList.push_back( static_cast( items[ii] ) ); + PCB_SHAPE* seg = static_cast( items[ii] ); + + // Skip anything already used to generate footprint holes (above) + if( seg->GetFlags() & SKIP_STRUCT ) + continue; + + if( seg->GetLayer() == Edge_Cuts ) + segList.push_back( seg ); } if( segList.size() ) @@ -630,6 +704,15 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aE aOutlines.Append( corner ); } + for( int ii = 0; ii < fpHoles.OutlineCount(); ++ii ) + { + for( int jj = 0; jj < aOutlines.OutlineCount(); ++jj ) + { + if( aOutlines.Outline( jj ).Intersects( fpHoles.Outline( ii ) ) ) + aOutlines.AddHole( fpHoles.Outline( ii ), jj ); + } + } + return success; } @@ -673,34 +756,6 @@ void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline ) } -bool isCopperOutside( const FOOTPRINT* aMod, SHAPE_POLY_SET& aShape ) -{ - bool padOutside = false; - - for( PAD* pad : aMod->Pads() ) - { - SHAPE_POLY_SET poly = aShape.CloneDropTriangulation(); - - poly.BooleanIntersection( *pad->GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST ); - - if( poly.OutlineCount() == 0 ) - { - VECTOR2I padPos = pad->GetPosition(); - wxLogTrace( traceBoardOutline, wxT( "Tested pad (%d, %d): outside" ), - padPos.x, padPos.y ); - padOutside = true; - break; - } - - VECTOR2I padPos = pad->GetPosition(); - wxLogTrace( traceBoardOutline, wxT( "Tested pad (%d, %d): not outside" ), - padPos.x, padPos.y ); - } - - return padOutside; -} - - VECTOR2I projectPointOnSegment( const VECTOR2I& aEndPoint, const SHAPE_POLY_SET& aOutline, int aOutlineNum = 0 ) {