Allow chamfering/filleting of zone/board edge intersections.

Fixes https://gitlab.com/kicad/code/kicad/issues/5947
This commit is contained in:
Jeff Young 2020-10-10 23:09:00 +01:00
parent a8bd0a9b84
commit 22cde88ba9
7 changed files with 58 additions and 28 deletions

View File

@ -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 ... if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations will not like it ...
return false; return false;
if( GetIsRuleArea() )
{
// We like keepouts just the way they are....
aSmoothedPoly = *m_Poly;
return true;
}
BOARD* board = GetBoard(); BOARD* board = GetBoard();
int maxError = ARC_HIGH_DEF; int maxError = ARC_HIGH_DEF;
bool keepExternalFillets = false; bool keepExternalFillets = false;
@ -1215,6 +1223,9 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER
for( ZONE_CONTAINER* zone : interactingZones ) for( ZONE_CONTAINER* zone : interactingZones )
aSmoothedPoly.BooleanAdd( *zone->Outline(), SHAPE_POLY_SET::PM_FAST ); aSmoothedPoly.BooleanAdd( *zone->Outline(), SHAPE_POLY_SET::PM_FAST );
if( !GetIsRuleArea() && aBoardOutline )
aSmoothedPoly.BooleanIntersection( *aBoardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
smooth( aSmoothedPoly ); smooth( aSmoothedPoly );
aSmoothedPoly.BooleanIntersection( *maxExtents, SHAPE_POLY_SET::PM_FAST ); 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 * Convert the smoothed outline to polygons (optionally inflated by \a aClearance) and copy them
* into \a aCornerBuffer. * into \a aCornerBuffer.
*/ */
void ZONE_CONTAINER::TransformSmoothedOutlineWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, void ZONE_CONTAINER::TransformSmoothedOutlineToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int aClearance ) const int aClearance,
SHAPE_POLY_SET* aBoardOutline ) const
{ {
// Creates the zone outline polygon (with holes if any) // Creates the zone outline polygon (with holes if any)
SHAPE_POLY_SET polybuffer; SHAPE_POLY_SET polybuffer;
BuildSmoothedPoly( polybuffer, GetLayer() ); BuildSmoothedPoly( polybuffer, GetLayer(), aBoardOutline );
// Calculate the polygon with clearance // Calculate the polygon with clearance
// holes are linked to the main outline, so only one polygon is created. // holes are linked to the main outline, so only one polygon is created.

View File

@ -361,7 +361,7 @@ public:
int aError = ARC_HIGH_DEF ) const; int aError = ARC_HIGH_DEF ) const;
/** /**
* Function TransformSmoothedOutlineWithClearanceToPolygon * Function TransformSmoothedOutlineToPolygon
* Convert the outlines shape to a polygon with no holes * Convert the outlines shape to a polygon with no holes
* inflated (optional) by max( aClearanceValue, the zone clearance) * inflated (optional) by max( aClearanceValue, the zone clearance)
* (holes are linked to external outline by overlapping segments) * (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 * Circles (vias) and arcs (ends of tracks) are approximated by segments
* @param aCornerBuffer = a buffer to store the polygon * @param aCornerBuffer = a buffer to store the polygon
* @param aClearance = the min clearance around outlines * @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, void TransformSmoothedOutlineToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearance,
int aClearance ) const; SHAPE_POLY_SET* aBoardOutline ) const;
/** /**
* Function TransformShapeWithClearanceToPolygon * Function TransformShapeWithClearanceToPolygon
@ -669,7 +670,8 @@ public:
/** /**
* Function GetSmoothedPoly * 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; }; void SetCornerSmoothingType( int aType ) { m_cornerSmoothingType = aType; };

View File

@ -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 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 // Test copper areas for valid netcodes -> fixme, goes to connectivity checks
for( int layer_id = F_Cu; layer_id <= B_Cu; ++layer_id ) 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 ); ZONE_CONTAINER* zoneRef = m_board->GetArea( ii );
if( zoneRef->IsOnLayer( layer ) ) if( zoneRef->IsOnLayer( layer ) )
zoneRef->BuildSmoothedPoly( smoothed_polys[ii], layer ); zoneRef->BuildSmoothedPoly( smoothed_polys[ii], layer, boardOutline );
} }
// iterate through all areas // iterate through all areas

View File

@ -757,6 +757,11 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness ) const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness )
{ {
PCB_LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask; 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 // Set the current arc to segment max approx error
int currMaxError = aBoard->GetDesignSettings().m_MaxError; int currMaxError = aBoard->GetDesignSettings().m_MaxError;
@ -776,9 +781,9 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
itemplotter.PlotBoardGraphicItems(); 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 ); itemplotter.PlotFootprintTextItems( module );
@ -806,7 +811,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
#endif #endif
{ {
// Plot pads // Plot pads
for( auto module : aBoard->Modules() ) for( MODULE* module : aBoard->Modules() )
{ {
// add shapes with their exact mask layer size in initialPolys // add shapes with their exact mask layer size in initialPolys
module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0 ); 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_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
int via_margin = via_clearance + inflate; int via_margin = via_clearance + inflate;
for( auto track : aBoard->Tracks() ) for( TRACK* track : aBoard->Tracks() )
{ {
const VIA* via = dyn_cast<const VIA*>( track ); const VIA* via = dyn_cast<const VIA*>( track );
@ -860,9 +865,9 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
continue; continue;
// add shapes inflated by aMinThickness/2 in areas // 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 // 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; int maxError = aBoard->GetDesignSettings().m_MaxError;

View File

@ -879,7 +879,8 @@ std::unique_ptr<PNS::VIA> 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; SHAPE_POLY_SET poly;
@ -894,7 +895,7 @@ bool PNS_KICAD_IFACE_BASE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone )
if( ! layers[ layer ] ) if( ! layers[ layer ] )
continue; continue;
aZone->BuildSmoothedPoly( poly, ToLAYER_ID( layer ) ); aZone->BuildSmoothedPoly( poly, ToLAYER_ID( layer ), aBoardOutline );
poly.CacheTriangulation(); poly.CacheTriangulation();
if( !poly.IsTriangulationUpToDate() ) 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() ) for( ZONE_CONTAINER* zone : m_board->Zones() )
{ {
syncZone( aWorld, zone ); syncZone( aWorld, zone, boardOutline );
} }
for( MODULE* module : m_board->Modules() ) 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() ); syncTextItem( aWorld, &module->Value(), module->Value().GetLayer() );
for( MODULE_ZONE_CONTAINER* zone : module->Zones() ) for( MODULE_ZONE_CONTAINER* zone : module->Zones() )
syncZone( aWorld, zone ); syncZone( aWorld, zone, boardOutline );
if( module->IsNetTie() ) if( module->IsNetTie() )
continue; continue;

View File

@ -94,7 +94,7 @@ protected:
std::unique_ptr<PNS::VIA> syncVia( VIA* aVia ); std::unique_ptr<PNS::VIA> syncVia( VIA* aVia );
bool syncTextItem( PNS::NODE* aWorld, EDA_TEXT* aText, PCB_LAYER_ID aLayer ); bool syncTextItem( PNS::NODE* aWorld, EDA_TEXT* aText, PCB_LAYER_ID aLayer );
bool syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem ); 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 ); int inheritTrackWidth( PNS::ITEM* aItem );

View File

@ -843,7 +843,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
|| aZone->GetNetCode() == aKnockout->GetNetCode() ) || aZone->GetNetCode() == aKnockout->GetNetCode() )
{ {
// Keepouts and same-net zones use outline with no clearance // Keepouts and same-net zones use outline with no clearance
aKnockout->TransformSmoothedOutlineWithClearanceToPolygon( aHoles, 0 ); aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0, nullptr );
} }
else else
{ {
@ -852,7 +852,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
if( bds.m_ZoneFillVersion == 5 ) if( bds.m_ZoneFillVersion == 5 )
{ {
// 5.x used outline with clearance // 5.x used outline with clearance
aKnockout->TransformSmoothedOutlineWithClearanceToPolygon( aHoles, gap ); aKnockout->TransformSmoothedOutlineToPolygon( aHoles, gap, nullptr );
} }
else 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, bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys ) 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) * 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 * m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building
* this zone * this zone
*/ */
if ( !aZone->BuildSmoothedPoly( smoothedPoly, aLayer ) ) if ( !aZone->BuildSmoothedPoly( smoothedPoly, aLayer, boardOutline ) )
return false; return false;
if( m_progressReporter && m_progressReporter->IsCancelled() ) 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 epsilon = Millimeter2iu( 0.001 );
int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 ); 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 ); smoothedPoly.Deflate( half_min_width - epsilon, numSegs );
// Remove the non filled areas due to the hatch pattern // Remove the non filled areas due to the hatch pattern