Clean up arc/circle polygonization.
1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
This commit is contained in:
@@ -344,7 +344,7 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
||||
// Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
|
||||
// which can create bad shapes if margin.x is < 0
|
||||
int maxError = aBoard->GetDesignSettings().m_MaxError;
|
||||
int numSegs = std::max( GetArcToSegmentCount( margin.x, maxError, 360.0 ), 6 );
|
||||
int numSegs = GetArcToSegmentCount( margin.x, maxError, 360.0 );
|
||||
shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST );
|
||||
dummy.DeletePrimitivesList();
|
||||
dummy.AddPrimitivePoly( shape, 0 );
|
||||
@@ -781,9 +781,6 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
||||
// They do not have a solder Mask margin, because they are graphic items
|
||||
// on this layer (like logos), not actually areas around pads.
|
||||
|
||||
// Normal mode to generate polygons from shapes with arcs, if any:
|
||||
DisableArcRadiusCorrection( false );
|
||||
|
||||
itemplotter.PlotBoardGraphicItems();
|
||||
|
||||
for( auto module : aBoard->Modules() )
|
||||
@@ -812,83 +809,81 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
||||
#if NEW_ALGO
|
||||
// Generate polygons with arcs inside the shape or exact shape
|
||||
// to minimize shape changes created by arc to segment size correction.
|
||||
DisableArcRadiusCorrection( true );
|
||||
DISABLE_ARC_RADIUS_CORRECTION disabler;
|
||||
#endif
|
||||
|
||||
// Plot pads
|
||||
for( auto module : aBoard->Modules() )
|
||||
{
|
||||
// add shapes with their exact mask layer size in initialPolys
|
||||
module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0 );
|
||||
// add shapes inflated by aMinThickness/2 in areas
|
||||
module->TransformPadsShapesWithClearanceToPolygon( layer, areas, inflate );
|
||||
}
|
||||
|
||||
// Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
|
||||
if( aPlotOpt.GetPlotViaOnMaskLayer() )
|
||||
{
|
||||
// The current layer is a solder mask, use the global mask clearance for vias
|
||||
int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
|
||||
int via_margin = via_clearance + inflate;
|
||||
|
||||
for( auto track : aBoard->Tracks() )
|
||||
// Plot pads
|
||||
for( auto module : aBoard->Modules() )
|
||||
{
|
||||
const VIA* via = dyn_cast<const VIA*>( track );
|
||||
|
||||
if( !via )
|
||||
continue;
|
||||
|
||||
// vias are plotted only if they are on the corresponding external copper layer
|
||||
LSET via_set = via->GetLayerSet();
|
||||
|
||||
if( via_set[B_Cu] )
|
||||
via_set.set( B_Mask );
|
||||
|
||||
if( via_set[F_Cu] )
|
||||
via_set.set( F_Mask );
|
||||
|
||||
if( !( via_set & aLayerMask ).any() )
|
||||
continue;
|
||||
|
||||
// add shapes with their exact mask layer size in initialPolys
|
||||
via->TransformShapeWithClearanceToPolygon( initialPolys, layer, via_clearance );
|
||||
module->TransformPadsShapesWithClearanceToPolygon( layer, initialPolys, 0 );
|
||||
// add shapes inflated by aMinThickness/2 in areas
|
||||
via->TransformShapeWithClearanceToPolygon( areas, layer, via_margin );
|
||||
module->TransformPadsShapesWithClearanceToPolygon( layer, areas, inflate );
|
||||
}
|
||||
}
|
||||
|
||||
// Add filled zone areas.
|
||||
// Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
|
||||
if( aPlotOpt.GetPlotViaOnMaskLayer() )
|
||||
{
|
||||
// The current layer is a solder mask, use the global mask clearance for vias
|
||||
int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
|
||||
int via_margin = via_clearance + inflate;
|
||||
|
||||
for( auto track : aBoard->Tracks() )
|
||||
{
|
||||
const VIA* via = dyn_cast<const VIA*>( track );
|
||||
|
||||
if( !via )
|
||||
continue;
|
||||
|
||||
// vias are plotted only if they are on the corresponding external copper layer
|
||||
LSET via_set = via->GetLayerSet();
|
||||
|
||||
if( via_set[B_Cu] )
|
||||
via_set.set( B_Mask );
|
||||
|
||||
if( via_set[F_Cu] )
|
||||
via_set.set( F_Mask );
|
||||
|
||||
if( !( via_set & aLayerMask ).any() )
|
||||
continue;
|
||||
|
||||
// add shapes with their exact mask layer size in initialPolys
|
||||
via->TransformShapeWithClearanceToPolygon( initialPolys, layer, via_clearance );
|
||||
// add shapes inflated by aMinThickness/2 in areas
|
||||
via->TransformShapeWithClearanceToPolygon( areas, layer, via_margin );
|
||||
}
|
||||
}
|
||||
|
||||
// Add filled zone areas.
|
||||
#if 0 // Set to 1 if a solder mask margin must be applied to zones on solder mask
|
||||
int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
|
||||
int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
|
||||
#else
|
||||
int zone_margin = 0;
|
||||
int zone_margin = 0;
|
||||
#endif
|
||||
|
||||
for( ZONE_CONTAINER* zone : aBoard->Zones() )
|
||||
{
|
||||
if( zone->GetLayer() != layer )
|
||||
continue;
|
||||
for( ZONE_CONTAINER* zone : aBoard->Zones() )
|
||||
{
|
||||
if( zone->GetLayer() != layer )
|
||||
continue;
|
||||
|
||||
// add shapes inflated by aMinThickness/2 in areas
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin );
|
||||
// add shapes with their exact mask layer size in initialPolys
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin );
|
||||
// add shapes inflated by aMinThickness/2 in areas
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin );
|
||||
// add shapes with their exact mask layer size in initialPolys
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin );
|
||||
}
|
||||
|
||||
int maxError = aBoard->GetDesignSettings().m_MaxError;
|
||||
int numSegs = GetArcToSegmentCount( inflate, maxError, 360.0 );
|
||||
|
||||
// Merge all polygons: After deflating, not merged (not overlapping) polygons
|
||||
// will have the initial shape (with perhaps small changes due to deflating transform)
|
||||
areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
areas.Deflate( inflate, numSegs );
|
||||
|
||||
// Restore initial settings:
|
||||
aBoard->GetDesignSettings().m_MaxError = currMaxError;
|
||||
}
|
||||
|
||||
int maxError = aBoard->GetDesignSettings().m_MaxError;
|
||||
int numSegs = std::max( GetArcToSegmentCount( inflate, maxError, 360.0 ), 12 );
|
||||
|
||||
// Merge all polygons: After deflating, not merged (not overlapping) polygons
|
||||
// will have the initial shape (with perhaps small changes due to deflating transform)
|
||||
areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
areas.Deflate( inflate, numSegs );
|
||||
|
||||
// Restore initial settings:
|
||||
aBoard->GetDesignSettings().m_MaxError = currMaxError;
|
||||
|
||||
// Restore normal option to build polygons from item shapes:
|
||||
DisableArcRadiusCorrection( false );
|
||||
|
||||
#if !NEW_ALGO
|
||||
// To avoid a lot of code, use a ZONE_CONTAINER to handle and plot polygons, because our
|
||||
// polygons look exactly like filled areas in zones.
|
||||
|
||||
Reference in New Issue
Block a user