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:
Jeff Young 2020-09-11 00:05:20 +01:00
parent f493e270ea
commit e2bc7557cc
22 changed files with 269 additions and 420 deletions

View File

@ -256,16 +256,7 @@ unsigned int BOARD_ADAPTER::GetNrSegmentsCircle( int aDiameterBIU ) const
{
wxASSERT( aDiameterBIU > 0 );
// Require at least 3 segments for a circle
return std::max( GetArcToSegmentCount( aDiameterBIU / 2, ARC_HIGH_DEF, 360.0 ), 3 );
}
double BOARD_ADAPTER::GetCircleCorrectionFactor( int aNrSides ) const
{
wxASSERT( aNrSides >= 3 );
return GetCircletoPolyCorrectionFactor( aNrSides );
return GetArcToSegmentCount( aDiameterBIU / 2, ARC_HIGH_DEF, 360.0 );
}

View File

@ -529,14 +529,6 @@ class BOARD_ADAPTER
*/
unsigned int GetNrSegmentsCircle( int aDiameterBIU ) const;
/**
* @brief GetCircleCorrectionFactor - computes a angle correction
* factor used when creating circles
* @param aNrSides: the number of segments sides of the circle
* @return a factor to apply to contour creation
*/
double GetCircleCorrectionFactor( int aNrSides ) const;
/**
* @brief GetPolyMap - Get maps of polygons's layers
* @return the map with polygons's layers

View File

@ -54,8 +54,7 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
const VECTOR2I& a = path.CPoint( ii );
const VECTOR2I& b = path.CPoint( ii + 1 );
TransformSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ),
ARC_HIGH_DEF, aWidth );
TransformOvalToPolygon( aCornerBuffer, (wxPoint) a, (wxPoint) b, aWidth, ARC_HIGH_DEF );
}
}

View File

@ -744,8 +744,7 @@ void C3D_RENDER_OGL_LEGACY::generate_3D_Vias_and_Pads()
const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3Dunits();
const float thickness = m_boardAdapter.GetCopperThickness3DU();
const int nrSegments = m_boardAdapter.GetNrSegmentsCircle( via->GetDrillValue() );
const double correctionFactor = m_boardAdapter.GetCircleCorrectionFactor( nrSegments );
const float hole_inner_radius = ( holediameter / 2.0f ) * correctionFactor;
const float hole_inner_radius = holediameter / 2.0f;
const SFVEC2F via_center( via->GetStart().x * m_boardAdapter.BiuTo3Dunits(),
-via->GetStart().y * m_boardAdapter.BiuTo3Dunits() );
@ -805,14 +804,11 @@ void C3D_RENDER_OGL_LEGACY::generate_3D_Vias_and_Pads()
// for slots, the diameter is the smaller of (drillsize.x, drillsize.y)
int copperThickness = m_boardAdapter.GetCopperThicknessBIU();
int radius = std::min( drillsize.x, drillsize.y ) / 2 + copperThickness;
int nrSegments = m_boardAdapter.GetNrSegmentsCircle( radius * 2 );
double correctionFactor = m_boardAdapter.GetCircleCorrectionFactor( nrSegments );
int correction = radius * ( correctionFactor - 1 );
pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly,
copperThickness + correction );
copperThickness + radius );
pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, correction );
pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, radius );
}
}
}

View File

@ -533,8 +533,8 @@ void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
// enter outline as polygon:
for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
{
TransformSegmentToPolygon( bufferOutline,
aCornerList[ ii - 1 ], aCornerList[ ii ], GetPlotterArcHighDef(), aWidth );
TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ],
aWidth, GetPlotterArcHighDef() );
}
// enter the initial polygon:

View File

@ -121,22 +121,6 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer,
double aRotation, int aCornerRadius,
double aChamferRatio, int aChamferCorners, int aError );
/**
* Function TransformRoundedEndsSegmentToPolygon
* convert a segment with rounded ends to a polygon
* Convert arcs to multiple straight lines
* @param aCornerBuffer = a buffer to store the polygon
* @param aStart = the segment start point coordinate
* @param aEnd = the segment end point coordinate
* @param aError = the IU allowed for error in approximation
* @param aWidth = the segment width
* Note: the polygon is inside the arc ends, so if you want to have the polygon
* outside the circle, you should give aStart and aEnd calculated with a correction factor
*/
void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd,
int aError, int aWidth );
/**
* Function TransformArcToPolygon
* Creates a polygon from an Arc

View File

@ -47,36 +47,33 @@ class EDA_RECT;
*/
int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree );
/** When creating polygons to create a clearance polygonal area, the polygon must
/**
* When creating polygons to create a clearance polygonal area, the polygon must
* be same or bigger than the original shape.
* Polygons are bigger if the original shape has arcs (round rectangles, ovals, circles...)
* In some cases (in fact only one: when building layer solder mask) modifying
* shapes when converting them to polygons is not acceptable (the modification
* can break calculations)
* so one can disable the shape expansion by calling DisableArcRadiusCorrection( true )
* Important: calling DisableArcRadiusCorrection( false ) after calculations is
* mandatory to break oher calculations
* @param aDisable = false to create polygons same or outside the original shape
* = true to create polygons same or inside the original shape and minimize
* shape geometric changes
* Polygons are bigger if the original shape has arcs (round rectangles, ovals,
* circles...). However, when building the solder mask layer modifying the shapes
* when converting them to polygons is not acceptable (the modification can break
* calculations).
* So one can disable the shape expansion within a particular scope by allocating
* a DISABLE_ARC_CORRECTION.
*/
void DisableArcRadiusCorrection( bool aDisable );
class DISABLE_ARC_RADIUS_CORRECTION
{
public:
DISABLE_ARC_RADIUS_CORRECTION();
~DISABLE_ARC_RADIUS_CORRECTION();
};
/**
* @return the correction factor to approximate a circle by segments or 1.0
* depending on the last call to DisableArcRadiusCorrection()
* @param aSegCountforCircle is the number of segments to approximate the circle
* @return the radius correction to approximate a circle.
* @param aMaxError is the same error value used to calculate the number of segments.
*
* When creating a polygon from a circle, the polygon is inside the circle.
* Only corners are on the circle.
* This is incorrect when building clearance areas of circles, that need to build
* the equivalent polygon outside the circle
* The correction factor is a scaling factor to apply to the radius to build a
* polygon outside the circle (only the middle of each segment is on the circle
*
*
* the equivalent polygon outside the circle.
*/
double GetCircletoPolyCorrectionFactor( int aSegCountforCircle );
int GetCircleToPolyCorrection( int aMaxError );
/**
* Snap a vector onto the nearest 0, 45 or 90 degree line.

View File

@ -42,17 +42,16 @@ void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aCornerBuffer, wxPoint aCenter,
int aError )
{
wxPoint corner_position;
int numSegs = std::max( GetArcToSegmentCount( aRadius, aError, 360.0 ), 6 );
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
double correction = GetCircletoPolyCorrectionFactor( numSegs );
int radius = aRadius * correction; // make segments outside the circles
double halfstep = delta/2.0; // the starting value for rot angles
int numSegs = GetArcToSegmentCount( aRadius, aError, 360.0 );
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
int correction = GetCircleToPolyCorrection( aError );
int radius = aRadius + correction; // make segments outside the circles
double halfstep = delta / 2.0; // the starting value for rot angles
for( int ii = 0; ii < numSegs; ii++ )
for( int angle = 0; angle < 3600; angle += delta )
{
corner_position.x = radius;
corner_position.y = 0;
double angle = (ii * delta) + halfstep;
RotatePoint( &corner_position, angle );
corner_position += aCenter;
aCornerBuffer.Append( corner_position.x, corner_position.y );
@ -66,23 +65,27 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCenter, i
int aError )
{
wxPoint corner_position;
int numSegs = std::max( GetArcToSegmentCount( aRadius, aError, 360.0 ), 6 );
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
double correction = GetCircletoPolyCorrectionFactor( numSegs );
int radius = aRadius * correction; // make segments outside the circles
double halfstep = delta/2.0; // the starting value for rot angles
int numSegs = GetArcToSegmentCount( aRadius, aError, 360.0 );
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
int correction = GetCircleToPolyCorrection( aError );
int radius = aRadius + correction; // make segments outside the circles
aCornerBuffer.NewOutline();
for( int ii = 0; ii < numSegs; ii++ )
for( int angle = 0; angle < 3600; angle += delta )
{
corner_position.x = radius;
corner_position.y = 0;
double angle = (ii * delta) + halfstep;
corner_position.x = radius;
corner_position.y = 0;
RotatePoint( &corner_position, angle );
corner_position += aCenter;
aCornerBuffer.Append( corner_position.x, corner_position.y );
}
// Finish circle
corner_position.x = radius;
corner_position.y = 0;
corner_position += aCenter;
aCornerBuffer.Append( corner_position.x, corner_position.y );
}
@ -94,23 +97,17 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
// However, the width of the segment is too big.
// so, later, we will clamp the polygonal shape with the bounding box
// of the segment.
int radius = aWidth / 2;
int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 );
int radius = aWidth / 2;
int numSegs = GetArcToSegmentCount( radius, aError, 360.0 );
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
int correction = GetCircleToPolyCorrection( aError );
// Because we want to create 2 arcs (one at each segment end) numSegs must be
// a even value (we will used numSegs/2 later)
if( numSegs % 2 != 0 )
numSegs++;
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
double correction = GetCircletoPolyCorrectionFactor( numSegs );
radius = radius * correction; // make segments outside the circles
radius += correction; // make segments outside the circles
// end point is the coordinate relative to aStart
wxPoint endp = aEnd - aStart;
wxPoint startp = aStart;
wxPoint corner;
wxPoint endp = aEnd - aStart;
wxPoint startp = aStart;
wxPoint corner;
SHAPE_POLY_SET polyshape;
polyshape.NewOutline();
@ -125,18 +122,19 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
// delta_angle is in radian
double delta_angle = atan2( (double)endp.y, (double)endp.x );
int seg_len = KiROUND( EuclideanNorm( endp ) );
int seg_len = KiROUND( EuclideanNorm( endp ) );
// Compute the outlines of the segment, and creates a polygon
// Note: the polygonal shape is built from the equivalent horizontal
// segment starting ar 0,0, and ending at seg_len,0
// segment starting at {0,0}, and ending at {seg_len,0}
// add right rounded end:
for( int ii = 0; ii < numSegs / 2; ii++ )
for( int angle = 0; angle < 1800; angle += delta )
{
corner = wxPoint( 0, radius );
RotatePoint( &corner, delta * ii );
RotatePoint( &corner, angle );
corner.x += seg_len;
polyshape.Append( corner.x, corner.y );
}
@ -146,10 +144,10 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
polyshape.Append( corner.x, corner.y );
// add left rounded end:
for( int ii = 0; ii < numSegs / 2; ii++ )
for( int angle = 0; angle < 1800; angle += delta )
{
corner = wxPoint( 0, -radius );
RotatePoint( &corner, delta * ii );
RotatePoint( &corner, angle );
polyshape.Append( corner.x, corner.y );
}
@ -157,31 +155,27 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
corner = wxPoint( 0, radius );
polyshape.Append( corner.x, corner.y );
// Now, clamp the polygonal shape (too big) with the segment bounding box
// the polygonal shape bbox equivalent to the segment has a too big height,
// and the right width
if( correction > 1.0 )
{
SHAPE_POLY_SET bbox;
bbox.NewOutline();
// Build the bbox (a horizontal rectangle).
int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height
corner.x = -radius - 2; // use a bbox width slightly bigger to avoid
// creating useless corner at segment ends
corner.y = halfwidth;
bbox.Append( corner.x, corner.y );
corner.y = -halfwidth;
bbox.Append( corner.x, corner.y );
corner.x = radius + seg_len + 2;
bbox.Append( corner.x, corner.y );
corner.y = halfwidth;
bbox.Append( corner.x, corner.y );
// Now trim the edges of the polygonal shape which will be slightly outside the
// track width.
SHAPE_POLY_SET bbox;
bbox.NewOutline();
// Build the bbox (a horizontal rectangle).
int halfwidth = aWidth / 2; // Use the exact segment width for the bbox height
corner.x = -radius - 2; // use a bbox width slightly bigger to avoid
// creating useless corner at segment ends
corner.y = halfwidth;
bbox.Append( corner.x, corner.y );
corner.y = -halfwidth;
bbox.Append( corner.x, corner.y );
corner.x = radius + seg_len + 2;
bbox.Append( corner.x, corner.y );
corner.y = halfwidth;
bbox.Append( corner.x, corner.y );
// Now, clamp the shape
polyshape.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
// Note the final polygon is a simple, convex polygon with no hole
// due to the shape of initial polygons
}
// Now, clamp the shape
polyshape.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
// Note the final polygon is a simple, convex polygon with no hole
// due to the shape of initial polygons
// Rotate and move the polygon to its right location
polyshape.Rotate( delta_angle, VECTOR2I( 0, 0 ) );
@ -240,18 +234,13 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const
for( const wxPoint& corner : corners)
outline.Append( corner );
// These are small radius corners (of which there may be many), so peg the segs-per-circle
// to no more than 16.
int numSegs = std::max( GetArcToSegmentCount( aCornerRadius, aError, 360.0 ), 16 );
int numSegs = GetArcToSegmentCount( aCornerRadius, aError, 360.0 );
// To build the polygonal shape outside the actual shape, we use a bigger
// radius to build rounded corners.
// However, the size of the shape is too large.
// so, we also clamp the polygonal shape with the bounding box
// of the initial shape.
double correction = GetCircletoPolyCorrectionFactor( numSegs );
int radius = aCornerRadius * correction; // make segments outside the circles
int correction = GetCircleToPolyCorrection( aError );
int radius = aCornerRadius + correction; // make segments outside the circles
outline.Inflate( radius, numSegs );
if( correction > 1.0 )
@ -340,82 +329,12 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const
}
void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd,
int aError, int aWidth )
{
int radius = aWidth / 2;
wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0)
wxPoint startp = aStart;
wxPoint corner;
VECTOR2I polypoint;
int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 );
double correction = GetCircletoPolyCorrectionFactor( numSegs );
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
radius = KiROUND( radius * correction );
aCornerBuffer.NewOutline();
// normalize the position in order to have endp.x >= 0;
if( endp.x < 0 )
{
endp = aStart - aEnd;
startp = aEnd;
}
double delta_angle = ArcTangente( endp.y, endp.x ); // delta_angle is in 0.1 degrees
int seg_len = KiROUND( EuclideanNorm( endp ) );
// Compute the outlines of the segment, and creates a polygon
// add right rounded end:
for( int ii = 0; ii < 1800; ii += delta )
{
corner = wxPoint( 0, radius );
RotatePoint( &corner, ii );
corner.x += seg_len;
RotatePoint( &corner, -delta_angle );
corner += startp;
polypoint.x = corner.x;
polypoint.y = corner.y;
aCornerBuffer.Append( polypoint.x, polypoint.y );
}
// Finish arc:
corner = wxPoint( seg_len, -radius );
RotatePoint( &corner, -delta_angle );
corner += startp;
polypoint.x = corner.x;
polypoint.y = corner.y;
aCornerBuffer.Append( polypoint.x, polypoint.y );
// add left rounded end:
for( int ii = 0; ii < 1800; ii += delta )
{
corner = wxPoint( 0, -radius );
RotatePoint( &corner, ii );
RotatePoint( &corner, -delta_angle );
corner += startp;
polypoint.x = corner.x;
polypoint.y = corner.y;
aCornerBuffer.Append( polypoint.x, polypoint.y );
}
// Finish arc:
corner = wxPoint( 0, radius );
RotatePoint( &corner, -delta_angle );
corner += startp;
polypoint.x = corner.x;
polypoint.y = corner.y;
aCornerBuffer.Append( polypoint.x, polypoint.y );
}
void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, wxPoint aStart,
double aArcAngle, int aError, int aWidth )
{
wxPoint arc_start, arc_end;
int dist = EuclideanNorm( aCentre - aStart );
int numSegs = std::max( GetArcToSegmentCount( dist, aError, 360.0 ), 6 );
int numSegs = GetArcToSegmentCount( dist, aError, 360.0 );
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
arc_end = arc_start = aStart;
@ -437,12 +356,12 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, wxPo
{
curr_end = arc_start;
RotatePoint( &curr_end, aCentre, -ii );
TransformSegmentToPolygon( aCornerBuffer, curr_start, curr_end, aError, aWidth );
TransformOvalToPolygon( aCornerBuffer, curr_start, curr_end, aWidth, aError );
curr_start = curr_end;
}
if( curr_end != arc_end )
TransformSegmentToPolygon( aCornerBuffer, curr_end, arc_end, aError, aWidth );
TransformOvalToPolygon( aCornerBuffer, curr_end, arc_end, aWidth, aError );
}

View File

@ -34,8 +34,11 @@
#include <geometry/geometry_utils.h>
#include <math/util.h> // for KiROUND
// To approximate a circle by segments, a minimal seg count is mandatory
#define MIN_SEGCOUNT_FOR_CIRCLE 6
// To approximate a circle by segments, a minimal seg count is mandatory.
// Note that this is rarely used as the maxError constraint will yield a higher
// segment count on everything but very small circles. (Even a 0.125mm track
// with a 0.01mm maximum deviation yields 11 segments.)
#define MIN_SEGCOUNT_FOR_CIRCLE 8
int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree )
{
@ -56,39 +59,35 @@ int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree )
int segCount = KiROUND( fabs( aArcAngleDegree ) / arc_increment );
// Ensure at least one segment is used (can happen for small arcs)
return std::max( segCount, 1 );
// Ensure at least two segments are used for algorithmic safety
return std::max( segCount, 2 );
}
// When creating polygons to create a clearance polygonal area, the polygon must
// be same or bigger than the original shape.
// Polygons are bigger if the original shape has arcs (round rectangles, ovals, circles...)
// In some cases (in fact only one: when building layer solder mask) modifying
// shapes when converting them to polygons is not acceptable (the modification
// can break calculations)
// so one can disable the shape expansion by calling KeepPolyInsideShape( true )
// Important: calling KeepPolyInsideShape( false ) after calculations is
// mandatory to break oher calculations
// Polygons are bigger if the original shape has arcs (round rectangles, ovals,
// circles...). However, when building the solder mask layer modifying the shapes
// when converting them to polygons is not acceptable (the modification can break
// calculations).
// So one can disable the shape expansion within a particular scope by allocating
// a DISABLE_ARC_CORRECTION.
static bool s_disable_arc_correction = false;
// Enable (aInside = false) or disable (aInside = true) polygonal shape expansion
// when converting pads shapes and other items shapes to polygons:
void DisableArcRadiusCorrection( bool aDisable )
DISABLE_ARC_RADIUS_CORRECTION::DISABLE_ARC_RADIUS_CORRECTION()
{
s_disable_arc_correction = aDisable;
s_disable_arc_correction = true;
}
double GetCircletoPolyCorrectionFactor( int aSegCountforCircle )
DISABLE_ARC_RADIUS_CORRECTION::~DISABLE_ARC_RADIUS_CORRECTION()
{
/* calculates the coeff to compensate radius reduction of circle
* due to the segment approx.
* For a circle the min radius is radius * cos( 2PI / aSegCountforCircle / 2)
* this is the distance between the center and the middle of the segment.
* therefore, to move the middle of the segment to the circle (distance = radius)
* the correctionFactor is 1 /cos( PI/aSegCountforCircle )
*/
aSegCountforCircle = std::max( MIN_SEGCOUNT_FOR_CIRCLE, aSegCountforCircle );
return s_disable_arc_correction ? 1.0 : 1.0 / cos( M_PI / aSegCountforCircle );
s_disable_arc_correction = false;
}
int GetCircleToPolyCorrection( int aMaxError )
{
// Push all the error to the outside by increasing the radius
return s_disable_arc_correction ? 0 : aMaxError;
}

View File

@ -59,9 +59,8 @@ TSEGM_2_POLY_PRMS prms;
static void addTextSegmToPoly( int x0, int y0, int xf, int yf, void* aData )
{
TSEGM_2_POLY_PRMS* prm = static_cast<TSEGM_2_POLY_PRMS*>( aData );
TransformSegmentToPolygon( *prm->m_cornerBuffer,
wxPoint( x0, y0 ), wxPoint( xf, yf ),
prm->m_error, prm->m_textWidth );
TransformOvalToPolygon( *prm->m_cornerBuffer, wxPoint( x0, y0 ), wxPoint( xf, yf ),
prm->m_textWidth, prm->m_error );
}
@ -296,7 +295,7 @@ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygon( PCB_LAYER_ID aLayer,
if( board )
maxError = board->GetDesignSettings().m_MaxError;
int numSegs = std::max( GetArcToSegmentCount( GetMinThickness(), maxError, 360.0 ), 12 );
int numSegs = GetArcToSegmentCount( GetMinThickness(), maxError, 360.0 );
polys.InflateWithLinkedHoles( GetMinThickness()/2, numSegs, SHAPE_POLY_SET::PM_FAST );
@ -420,10 +419,10 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
if( width > 0 )
{
// Add in segments
TransformSegmentToPolygon( aCornerBuffer, pts[0], pts[1], aError, width );
TransformSegmentToPolygon( aCornerBuffer, pts[1], pts[2], aError, width );
TransformSegmentToPolygon( aCornerBuffer, pts[2], pts[3], aError, width );
TransformSegmentToPolygon( aCornerBuffer, pts[3], pts[0], aError, width );
TransformOvalToPolygon( aCornerBuffer, pts[0], pts[1], width, aError );
TransformOvalToPolygon( aCornerBuffer, pts[1], pts[2], width, aError );
TransformOvalToPolygon( aCornerBuffer, pts[2], pts[3], width, aError );
TransformOvalToPolygon( aCornerBuffer, pts[3], pts[0], width, aError );
}
}
break;
@ -473,7 +472,7 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
for( wxPoint pt2 : poly )
{
if( pt2 != pt1 )
TransformSegmentToPolygon( aCornerBuffer, pt1, pt2, aError, width );
TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError );
pt1 = pt2;
}
@ -491,7 +490,7 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
if( width != 0 )
{
for( unsigned ii = 1; ii < poly.size(); ii++ )
TransformSegmentToPolygon( aCornerBuffer, poly[ii-1], poly[ii], aError, width );
TransformOvalToPolygon( aCornerBuffer, poly[ii-1], poly[ii], width, aError );
}
}
break;
@ -511,29 +510,33 @@ void TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
{
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for tracks." );
int width = m_Width + ( 2 * aClearanceValue );
switch( Type() )
{
case PCB_VIA_T:
{
{
int radius = ( m_Width / 2 ) + aClearanceValue;
TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aError );
}
}
break;
case PCB_ARC_T:
{
{
const ARC* arc = static_cast<const ARC*>( this );
VECTOR2D center( arc->GetCenter() );
double arc_angle = arc->GetAngle();
TransformArcToPolygon( aCornerBuffer, wxPoint( center.x, center.y ),
GetStart(), arc_angle, aError, width );
}
int width = m_Width + ( 2 * aClearanceValue );
VECTOR2D center( arc->GetCenter() );
double angle = arc->GetAngle();
TransformArcToPolygon( aCornerBuffer, (wxPoint) center, GetStart(), angle, aError, width );
}
break;
default:
{
int width = m_Width + ( 2 * aClearanceValue );
TransformOvalToPolygon( aCornerBuffer, m_Start, m_End, width, aError );
}
break;
}
}
@ -602,10 +605,9 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
if( aClearanceValue )
{
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
pad_min_seg_per_circle_count );
double correction = GetCircletoPolyCorrectionFactor( numSegs );
int clearance = KiROUND( aClearanceValue * correction );
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
pad_min_seg_per_circle_count );
int clearance = aClearanceValue + GetCircleToPolyCorrection( aError );
outline.Inflate( clearance, numSegs );
// TODO: clamp the inflated polygon, because it is slightly too big:
// it was inflated by a value slightly too big to keep rounded corners
@ -620,13 +622,10 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
case PAD_SHAPE_ROUNDRECT:
{
int radius = GetRoundRectCornerRadius() + aClearanceValue;
int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ),
pad_min_seg_per_circle_count );
double correction = GetCircletoPolyCorrectionFactor( numSegs );
int clearance = KiROUND( aClearanceValue * correction );
int clearance = aClearanceValue + GetCircleToPolyCorrection( aError );
wxSize shapesize( m_size );
radius = KiROUND( radius * correction );
radius = radius + GetCircleToPolyCorrection( aError );
shapesize.x += clearance * 2;
shapesize.y += clearance * 2;
bool doChamfer = GetShape() == PAD_SHAPE_CHAMFERED_RECT;
@ -652,10 +651,9 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
if( aClearanceValue )
{
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
pad_min_seg_per_circle_count );
double correction = GetCircletoPolyCorrectionFactor( numSegs );
int clearance = KiROUND( aClearanceValue * correction );
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
pad_min_seg_per_circle_count );
int clearance = aClearanceValue + GetCircleToPolyCorrection( aError );
outline.Inflate( clearance, numSegs );
}
@ -684,8 +682,8 @@ bool D_PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
const SHAPE_SEGMENT* seg = GetEffectiveHoleShape();
TransformSegmentToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
aError, seg->GetWidth() + aInflateValue * 2 );
TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
seg->GetWidth() + aInflateValue * 2, aError );
return true;
}

View File

@ -1325,7 +1325,7 @@ void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SE
if( board )
maxError = board->GetDesignSettings().m_MaxError;
int segCount = std::max( GetArcToSegmentCount( aClearance, maxError, 360.0 ), 3 );
int segCount = GetArcToSegmentCount( aClearance, maxError, 360.0 );
polybuffer.Inflate( aClearance, segCount );
}

View File

@ -536,7 +536,7 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
double angle = 3600.0;
wxPoint start = center;
int radius = graphic->GetRadius();
int steps = std::max<int>( 4, GetArcToSegmentCount( radius, aTolerance, 360.0 ) );
int steps = GetArcToSegmentCount( radius, aTolerance, 360.0 );
wxPoint nextPt;
start.x += radius;

View File

@ -1252,39 +1252,45 @@ void DRC::setTransitions()
const int UI_EPSILON = Mils2iu( 5 );
wxPoint DRC::GetLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone )
wxPoint DRC::GetLocation( PCB_LAYER_ID aLayer, TRACK* aTrack, ZONE_CONTAINER* aZone )
{
SHAPE_POLY_SET* conflictOutline = nullptr;
SHAPE_POLY_SET* zonePoly = nullptr;
PCB_LAYER_ID l = aTrack->GetLayer();
if( aZone->IsFilled() && aZone->HasFilledPolysForLayer( aLayer ) )
zonePoly = const_cast<SHAPE_POLY_SET*>( &aZone->GetFilledPolysList( aLayer ) );
if( aConflictZone->IsFilled() && aConflictZone->HasFilledPolysForLayer( l ) )
conflictOutline = const_cast<SHAPE_POLY_SET*>( &aConflictZone->GetFilledPolysList( l ) );
if( !zonePoly || zonePoly->IsEmpty() )
zonePoly = aZone->Outline();
if( !conflictOutline || conflictOutline->IsEmpty() )
conflictOutline = aConflictZone->Outline();
SEG trackSeg( aTrack->GetStart(), aTrack->GetEnd() );
SEG::ecoord closestDist_sq = VECTOR2I::ECOORD_MAX;
SEG closestSeg;
wxPoint pt1 = aTrack->GetPosition();
wxPoint pt2 = aTrack->GetEnd();
// If the mid-point is in the zone, then that's a fine place for the marker
if( conflictOutline->SquaredDistance( ( pt1 + pt2 ) / 2 ) == 0 )
return ( pt1 + pt2 ) / 2;
// Otherwise do a binary search for a "good enough" marker location
else
for( auto it = zonePoly->CIterateSegments( 0, -1, true ); it; it++ )
{
while( GetLineLength( pt1, pt2 ) > UI_EPSILON )
{
if( conflictOutline->SquaredDistance( pt1 ) < conflictOutline->SquaredDistance( pt2 ) )
pt2 = ( pt1 + pt2 ) / 2;
else
pt1 = ( pt1 + pt2 ) / 2;
}
SEG::ecoord dist_sq = trackSeg.SquaredDistance( *it );
// Once we're within UI_EPSILON pt1 and pt2 are "equivalent"
return pt1;
if( dist_sq < closestDist_sq )
{
closestDist_sq = dist_sq;
closestSeg = *it;
}
}
VECTOR2I pt1 = closestSeg.A;
VECTOR2I pt2 = closestSeg.B;
// Do a binary search for a "good enough" marker location
while( GetLineLength( (wxPoint) pt1, (wxPoint) pt2 ) > UI_EPSILON )
{
if( trackSeg.SquaredDistance( pt1 ) < trackSeg.SquaredDistance( pt2 ) )
pt2 = ( pt1 + pt2 ) / 2;
else
pt1 = ( pt1 + pt2 ) / 2;
}
// Once we're within UI_EPSILON pt1 and pt2 are "equivalent"
return (wxPoint) pt1;
}

View File

@ -246,8 +246,8 @@ public:
/**
* Fetches a reasonable point for marking a violoation between two non-point objects.
*/
static wxPoint GetLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone );
static wxPoint GetLocation( TRACK* aTrack, const SEG& aConflictSeg );
static wxPoint GetLocation( PCB_LAYER_ID aLayer, TRACK* aTrack, ZONE_CONTAINER* aZone );
static wxPoint GetLocation( TRACK* aTrack, const SEG& aSeg );
/**
* Open a dialog and prompts the user, then if a test run button is

View File

@ -443,9 +443,9 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
/* Phase 3: test DRC with copper zones */
/***************************************/
// Can be *very* time consumming.
if( aTestZones )
{
for( ZONE_CONTAINER* zone : m_pcb->Zones() )
{
if( !zone->GetLayerSet().test( aLayer ) || zone->GetIsKeepout() )
@ -474,7 +474,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, zone );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, zone ) );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aLayer, aRefSeg, zone ) );
addMarkerToPcb( aCommit, marker );
}
}

View File

@ -88,7 +88,9 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias()
if( segm->Type() == PCB_TRACE_T && ( m_keepoutFlags & DISALLOW_TRACKS ) != 0 )
{
// Ignore if the keepout zone is not on the same layer
if( !m_zone->IsOnLayer( segm->GetLayer() ) )
PCB_LAYER_ID layer = segm->GetLayer();
if( !m_zone->IsOnLayer( layer ) )
continue;
SEG trackSeg( segm->GetStart(), segm->GetEnd() );
@ -103,7 +105,7 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias()
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( segm, m_zone );
HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( segm, m_zone ) ) );
HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( layer, segm, m_zone ) ) );
success = false;
}
}
@ -143,7 +145,7 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias()
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( segm, m_zone );
HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( segm, m_zone ) ) );
HandleMarker( new MARKER_PCB( drcItem, via->GetPosition() ) );
success = false;
}
}

View File

@ -1295,11 +1295,8 @@ ZONE_CONTAINER* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode )
double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
+ pow( center.y - kicad_y( v1.y ), 2 ) );
// If we are curving, we need at least 2 segments otherwise
// delta_angle == angle
double delta_angle = angle / std::max(
2, GetArcToSegmentCount( KiROUND( radius ),
ARC_HIGH_DEF, *v1.curve ) - 1 );
int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
double delta_angle = angle / segCount;
for( double a = end_angle + angle;
fabs( a - end_angle ) > fabs( delta_angle );
@ -1959,12 +1956,7 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const
if( KiROUND( radius ) == 0 )
radius = 1.0;
int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve ) - 1;
// If we are curving, we need at least 2 segments otherwise delta == angle
if( segCount < 2 )
segCount = 2;
int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *v1.curve );
double delta = angle / segCount;
for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
@ -2295,12 +2287,8 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
pow( center.y - kicad_y( w.y1 ), 2 ) );
// If we are curving, we need at least 2 segments otherwise
// delta_angle == angle
int segments = std::max( 2, GetArcToSegmentCount( KiROUND( radius ),
ARC_HIGH_DEF,
*w.curve ) - 1 );
delta_angle = angle / segments;
int segs = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF, *w.curve );
delta_angle = angle / segs;
}
while( fabs( angle ) > fabs( delta_angle ) )

View File

@ -671,8 +671,7 @@ static void export_vrml_polygon( MODEL_VRML& aModel, LAYER_NUM layer,
if( aOutline->GetWidth() )
{
int numSegs = std::max(
GetArcToSegmentCount( aOutline->GetWidth() / 2, ARC_HIGH_DEF, 360.0 ), 6 );
int numSegs = GetArcToSegmentCount( aOutline->GetWidth() / 2, ARC_HIGH_DEF, 360.0 );
shape.Inflate( aOutline->GetWidth() / 2, numSegs );
shape.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}

View File

@ -55,7 +55,7 @@ static void gen_arc( std::vector <wxPoint>& aBuffer,
{
auto first_point = aStartPoint - aCenter;
auto radius = KiROUND( EuclideanNorm( first_point ) );
int seg_count = std::max( GetArcToSegmentCount( radius, ARC_HIGH_DEF, a_ArcAngle / 10.0 ), 3 );
int seg_count = GetArcToSegmentCount( radius, ARC_HIGH_DEF, a_ArcAngle / 10.0 );
double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;

View File

@ -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.

View File

@ -78,12 +78,11 @@ static const bool s_DumpZonesWhenFilling = false;
ZONE_FILLER::ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit ) :
m_board( aBoard ),
m_brdOutlinesValid( false ),
m_commit( aCommit ),
m_progressReporter( nullptr ),
m_high_def( 9 ),
m_low_def( 6 )
m_board( aBoard ),
m_brdOutlinesValid( false ),
m_commit( aCommit ),
m_progressReporter( nullptr ),
m_maxError( ARC_HIGH_DEF )
{
}
@ -470,7 +469,7 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE
if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
SHAPE_POLY_SET poly;
aPad->TransformShapeWithClearanceToPolygon( poly, aLayer, aGap, m_high_def );
aPad->TransformShapeWithClearanceToPolygon( poly, aLayer, aGap, m_maxError );
// the pad shape in zone can be its convex hull or the shape itself
if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
@ -488,14 +487,7 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE
}
else
{
// Optimizing polygon vertex count: the high definition is used for round
// and oval pads (pads with large arcs) but low def for other shapes (with
// small arcs)
if( aPad->GetShape() == PAD_SHAPE_CIRCLE || aPad->GetShape() == PAD_SHAPE_OVAL ||
( aPad->GetShape() == PAD_SHAPE_ROUNDRECT && aPad->GetRoundRectRadiusRatio() > 0.4 ) )
aPad->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_high_def );
else
aPad->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_low_def );
aPad->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError );
}
}
@ -512,7 +504,7 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap,
case PCB_LINE_T:
{
DRAWSEGMENT* seg = (DRAWSEGMENT*) aItem;
seg->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_high_def,
seg->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError,
aIgnoreLineWidth );
break;
}
@ -525,7 +517,7 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap,
case PCB_MODULE_EDGE_T:
{
EDGE_MODULE* edge = (EDGE_MODULE*) aItem;
edge->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_high_def,
edge->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError,
aIgnoreLineWidth );
break;
}
@ -674,17 +666,17 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
if( !via->IsPadOnLayer( aLayer ) )
{
TransformCircleToPolygon( aHoles, via->GetPosition(),
( via->GetDrillValue() + 1 ) / 2 + gap, m_low_def );
int radius = via->GetDrillValue() / 2 + bds.GetHolePlatingThickness() + gap;
TransformCircleToPolygon( aHoles, via->GetPosition(), radius, m_maxError );
}
else
{
via->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_low_def );
via->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_maxError );
}
}
else
{
track->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_low_def );
track->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap, m_maxError );
}
}
}
@ -780,15 +772,14 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
SHAPE_POLY_SET& aRawPolys,
SHAPE_POLY_SET& aFinalPolys )
{
m_high_def = m_board->GetDesignSettings().m_MaxError;
m_low_def = std::min( ARC_LOW_DEF, int( m_high_def * 1.5 ) ); // Reasonable value
m_maxError = m_board->GetDesignSettings().m_MaxError;
// Features which are min_width should survive pruning; features that are *less* than
// min_width should not. Therefore we subtract epsilon from the min_width when
// deflating/inflating.
int half_min_width = aZone->GetMinThickness() / 2;
int epsilon = Millimeter2iu( 0.001 );
int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 );
SHAPE_POLY_SET::CORNER_STRATEGY cornerStrategy;
@ -974,7 +965,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
// deflating/inflating.
int half_min_width = aZone->GetMinThickness() / 2;
int epsilon = Millimeter2iu( 0.001 );
int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, 360.0 );
if( m_brdOutlinesValid )
smoothedPoly.BooleanIntersection( m_boardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );

View File

@ -115,14 +115,7 @@ private:
WX_PROGRESS_REPORTER* m_progressReporter;
std::unique_ptr<WX_PROGRESS_REPORTER> m_uniqueReporter;
// m_high_def can be used to define a high definition arc to polygon approximation
int m_high_def;
// m_low_def can be used to define a low definition arc to polygon approximation
// Used when converting some pad shapes that can accept lower resolution, vias and track ends.
// Rect pads use m_low_def to reduce the number of segments. For these shapes a low def
// gives a good shape, because the arc is small (90 degrees) and a small part of the shape.
int m_low_def;
int m_maxError;
};
#endif