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:
parent
f493e270ea
commit
e2bc7557cc
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ) )
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue