From 22cde88ba921093ac93ff4bf87259e1527f52027 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sat, 10 Oct 2020 23:09:00 +0100 Subject: [PATCH] Allow chamfering/filleting of zone/board edge intersections. Fixes https://gitlab.com/kicad/code/kicad/issues/5947 --- pcbnew/class_zone.cpp | 22 ++++++++++++++----- pcbnew/class_zone.h | 10 +++++---- .../drc_test_provider_copper_clearance.cpp | 8 ++++++- pcbnew/plot_board_layers.cpp | 17 +++++++++----- pcbnew/router/pns_kicad_iface.cpp | 15 +++++++++---- pcbnew/router/pns_kicad_iface.h | 2 +- pcbnew/zone_filler.cpp | 12 +++++----- 7 files changed, 58 insertions(+), 28 deletions(-) diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index ee259222c4..661f09c744 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -1159,11 +1159,19 @@ void ZONE_CONTAINER::GetInteractingZones( PCB_LAYER_ID aLayer, } -bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer ) const +bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer, + SHAPE_POLY_SET* aBoardOutline ) const { if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations will not like it ... return false; + if( GetIsRuleArea() ) + { + // We like keepouts just the way they are.... + aSmoothedPoly = *m_Poly; + return true; + } + BOARD* board = GetBoard(); int maxError = ARC_HIGH_DEF; bool keepExternalFillets = false; @@ -1215,6 +1223,9 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER for( ZONE_CONTAINER* zone : interactingZones ) aSmoothedPoly.BooleanAdd( *zone->Outline(), SHAPE_POLY_SET::PM_FAST ); + if( !GetIsRuleArea() && aBoardOutline ) + aSmoothedPoly.BooleanIntersection( *aBoardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + smooth( aSmoothedPoly ); aSmoothedPoly.BooleanIntersection( *maxExtents, SHAPE_POLY_SET::PM_FAST ); @@ -1247,16 +1258,17 @@ double ZONE_CONTAINER::CalculateFilledArea() /** - * Function TransformSmoothedOutlineWithClearanceToPolygon + * Function TransformSmoothedOutlineToPolygon * Convert the smoothed outline to polygons (optionally inflated by \a aClearance) and copy them * into \a aCornerBuffer. */ -void ZONE_CONTAINER::TransformSmoothedOutlineWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, - int aClearance ) const +void ZONE_CONTAINER::TransformSmoothedOutlineToPolygon( SHAPE_POLY_SET& aCornerBuffer, + int aClearance, + SHAPE_POLY_SET* aBoardOutline ) const { // Creates the zone outline polygon (with holes if any) SHAPE_POLY_SET polybuffer; - BuildSmoothedPoly( polybuffer, GetLayer() ); + BuildSmoothedPoly( polybuffer, GetLayer(), aBoardOutline ); // Calculate the polygon with clearance // holes are linked to the main outline, so only one polygon is created. diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index a25008f6d4..e0843b2a1b 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -361,7 +361,7 @@ public: int aError = ARC_HIGH_DEF ) const; /** - * Function TransformSmoothedOutlineWithClearanceToPolygon + * Function TransformSmoothedOutlineToPolygon * Convert the outlines shape to a polygon with no holes * inflated (optional) by max( aClearanceValue, the zone clearance) * (holes are linked to external outline by overlapping segments) @@ -369,9 +369,10 @@ public: * Circles (vias) and arcs (ends of tracks) are approximated by segments * @param aCornerBuffer = a buffer to store the polygon * @param aClearance = the min clearance around outlines + * @param aBoardOutline = the board outline (if a valid one exists; nullptr otherwise) */ - void TransformSmoothedOutlineWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, - int aClearance ) const; + void TransformSmoothedOutlineToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearance, + SHAPE_POLY_SET* aBoardOutline ) const; /** * Function TransformShapeWithClearanceToPolygon @@ -669,7 +670,8 @@ public: /** * Function GetSmoothedPoly */ - bool BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer ) const; + bool BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer, + SHAPE_POLY_SET* aBoardOutline ) const; void SetCornerSmoothingType( int aType ) { m_cornerSmoothingType = aType; }; diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index ae23921d54..9149d9d39b 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -758,6 +758,12 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZones() { const int delta = 50; // This is the number of tests between 2 calls to the progress bar + SHAPE_POLY_SET buffer; + SHAPE_POLY_SET* boardOutline = nullptr; + + if( m_board->GetBoardPolygonOutlines( buffer ) ) + boardOutline = &buffer; + // Test copper areas for valid netcodes -> fixme, goes to connectivity checks for( int layer_id = F_Cu; layer_id <= B_Cu; ++layer_id ) @@ -775,7 +781,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZones() ZONE_CONTAINER* zoneRef = m_board->GetArea( ii ); if( zoneRef->IsOnLayer( layer ) ) - zoneRef->BuildSmoothedPoly( smoothed_polys[ii], layer ); + zoneRef->BuildSmoothedPoly( smoothed_polys[ii], layer, boardOutline ); } // iterate through all areas diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index e8fca1cb16..797a0cdd02 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -757,6 +757,11 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness ) { PCB_LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask; + SHAPE_POLY_SET buffer; + SHAPE_POLY_SET* boardOutline = nullptr; + + if( aBoard->GetBoardPolygonOutlines( buffer ) ) + boardOutline = &buffer; // Set the current arc to segment max approx error int currMaxError = aBoard->GetDesignSettings().m_MaxError; @@ -776,9 +781,9 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, itemplotter.PlotBoardGraphicItems(); - for( auto module : aBoard->Modules() ) + for( MODULE* module : aBoard->Modules() ) { - for( auto item : module->GraphicalItems() ) + for( BOARD_ITEM* item : module->GraphicalItems() ) { itemplotter.PlotFootprintTextItems( module ); @@ -806,7 +811,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, #endif { // Plot pads - for( auto module : aBoard->Modules() ) + for( MODULE* module : aBoard->Modules() ) { // add shapes with their exact mask layer size in initialPolys module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0 ); @@ -821,7 +826,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin; int via_margin = via_clearance + inflate; - for( auto track : aBoard->Tracks() ) + for( TRACK* track : aBoard->Tracks() ) { const VIA* via = dyn_cast( track ); @@ -860,9 +865,9 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, continue; // add shapes inflated by aMinThickness/2 in areas - zone->TransformSmoothedOutlineWithClearanceToPolygon( areas, inflate + zone_margin ); + zone->TransformSmoothedOutlineToPolygon( areas, inflate + zone_margin, boardOutline ); // add shapes with their exact mask layer size in initialPolys - zone->TransformSmoothedOutlineWithClearanceToPolygon( initialPolys, zone_margin ); + zone->TransformSmoothedOutlineToPolygon( initialPolys, zone_margin, boardOutline ); } int maxError = aBoard->GetDesignSettings().m_MaxError; diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 97842cdb9a..169369137c 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -879,7 +879,8 @@ std::unique_ptr PNS_KICAD_IFACE_BASE::syncVia( VIA* aVia ) } -bool PNS_KICAD_IFACE_BASE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone ) +bool PNS_KICAD_IFACE_BASE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone, + SHAPE_POLY_SET* aBoardOutline ) { SHAPE_POLY_SET poly; @@ -894,7 +895,7 @@ bool PNS_KICAD_IFACE_BASE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone ) if( ! layers[ layer ] ) continue; - aZone->BuildSmoothedPoly( poly, ToLAYER_ID( layer ) ); + aZone->BuildSmoothedPoly( poly, ToLAYER_ID( layer ), aBoardOutline ); poly.CacheTriangulation(); if( !poly.IsTriangulationUpToDate() ) @@ -1143,9 +1144,15 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld ) } } + SHAPE_POLY_SET buffer; + SHAPE_POLY_SET* boardOutline = nullptr; + + if( m_board->GetBoardPolygonOutlines( buffer ) ) + boardOutline = &buffer; + for( ZONE_CONTAINER* zone : m_board->Zones() ) { - syncZone( aWorld, zone ); + syncZone( aWorld, zone, boardOutline ); } for( MODULE* module : m_board->Modules() ) @@ -1162,7 +1169,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld ) syncTextItem( aWorld, &module->Value(), module->Value().GetLayer() ); for( MODULE_ZONE_CONTAINER* zone : module->Zones() ) - syncZone( aWorld, zone ); + syncZone( aWorld, zone, boardOutline ); if( module->IsNetTie() ) continue; diff --git a/pcbnew/router/pns_kicad_iface.h b/pcbnew/router/pns_kicad_iface.h index 1b76828832..698f75d529 100644 --- a/pcbnew/router/pns_kicad_iface.h +++ b/pcbnew/router/pns_kicad_iface.h @@ -94,7 +94,7 @@ protected: std::unique_ptr syncVia( VIA* aVia ); bool syncTextItem( PNS::NODE* aWorld, EDA_TEXT* aText, PCB_LAYER_ID aLayer ); bool syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem ); - bool syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone ); + bool syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone, SHAPE_POLY_SET* aBoardOutline ); int inheritTrackWidth( PNS::ITEM* aItem ); diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 083a2fc67e..7bbb84b986 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -843,7 +843,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA || aZone->GetNetCode() == aKnockout->GetNetCode() ) { // Keepouts and same-net zones use outline with no clearance - aKnockout->TransformSmoothedOutlineWithClearanceToPolygon( aHoles, 0 ); + aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0, nullptr ); } else { @@ -852,7 +852,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA if( bds.m_ZoneFillVersion == 5 ) { // 5.x used outline with clearance - aKnockout->TransformSmoothedOutlineWithClearanceToPolygon( aHoles, gap ); + aKnockout->TransformSmoothedOutlineToPolygon( aHoles, gap, nullptr ); } else { @@ -1096,14 +1096,15 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys ) { - SHAPE_POLY_SET smoothedPoly; + SHAPE_POLY_SET* boardOutline = m_brdOutlinesValid ? &m_boardOutline : nullptr; + SHAPE_POLY_SET smoothedPoly; /* * convert outlines + holes to outlines without holes (adding extra segments if necessary) * m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building * this zone */ - if ( !aZone->BuildSmoothedPoly( smoothedPoly, aLayer ) ) + if ( !aZone->BuildSmoothedPoly( smoothedPoly, aLayer, boardOutline ) ) return false; if( m_progressReporter && m_progressReporter->IsCancelled() ) @@ -1122,9 +1123,6 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, int epsilon = Millimeter2iu( 0.001 ); int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 ); - if( m_brdOutlinesValid ) - smoothedPoly.BooleanIntersection( m_boardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - smoothedPoly.Deflate( half_min_width - epsilon, numSegs ); // Remove the non filled areas due to the hatch pattern