42 int aError,
ERROR_LOC aErrorLoc,
int aMinSegCount )
46 numSegs = std::max( aMinSegCount, numSegs );
49 numSegs = ( numSegs + 7 ) / 8 * 8;
66 corner_position.
y = 0;
68 corner_position += aCenter;
69 aBuffer.
Append( corner_position.
x, corner_position.
y );
77 int aError,
ERROR_LOC aErrorLoc,
int aMinSegCount )
81 numSegs = std::max( aMinSegCount, numSegs );
84 numSegs = ( numSegs + 7 ) / 8 * 8;
103 corner_position.
y = 0;
105 corner_position += aCenter;
106 aBuffer.
Append( corner_position.
x, corner_position.
y );
111 corner_position.
y = 0;
113 corner_position += aCenter;
114 aBuffer.
Append( corner_position.
x, corner_position.
y );
119 int aWidth,
int aError,
ERROR_LOC aErrorLoc,
int aMinSegCount )
128 numSegs = std::max( aMinSegCount, numSegs );
131 numSegs = ( numSegs + 7 ) / 8 * 8;
157 endp = aStart - aEnd;
172 polyshape.
Append( corner.
x, corner.
y );
179 polyshape.
Append( corner.
x, corner.
y );
184 polyshape.
Append( corner.
x, corner.
y );
188 polyshape.
Append( corner.
x, corner.
y );
195 polyshape.
Append( corner.
x, corner.
y );
200 polyshape.
Append( corner.
x, corner.
y );
207 int halfwidth = aWidth / 2;
210 corner.
y = halfwidth;
212 corner.
y = -halfwidth;
214 corner.
x =
radius + seg_len + 2;
216 corner.
y = halfwidth;
225 polyshape.
Rotate( -delta_angle );
226 polyshape.
Move( startp );
228 aBuffer.
Append( polyshape);
243 int aInflate,
int aError,
ERROR_LOC aErrorLoc )
245 assert( aInflate >= 0 );
247 VECTOR2I incoming = aCorners[0].m_position - aCorners.back().m_position;
249 for(
int n = 0, count = aCorners.size(); n < count; n++ )
266 if( ( incoming.
x == 0 && outgoing.y == 0 ) || ( incoming.
y == 0 && outgoing.x == 0 ) )
273 double cosNum = (double) incoming.
x * outgoing.x + (
double) incoming.
y * outgoing.y;
274 double cosDen = (double) incoming.
EuclideanNorm() * outgoing.EuclideanNorm();
275 double angle = acos( cosNum / cosDen );
276 tanAngle2 = tan( (
M_PI - angle ) / 2 );
280 if( aInflate && tanAngle2 )
283 cornerPosition += incoming.
Resize( aInflate / tanAngle2 )
294 while( lastSeg > angDelta )
299 while( lastSeg < -angDelta )
303 EDA_ANGLE angPos = lastSeg.
IsZero() ? angDelta : ( angDelta + lastSeg ) / 2;
305 double arcTransitionDistance = ( tanAngle2 > 0 ) ? (
radius / tanAngle2 ) : 0;
306 VECTOR2I arcStart = cornerPosition - incoming.
Resize( arcTransitionDistance );
313 arcStartOrigin = arcStart - arcCenter;
314 outline.
Append( arcStart );
322 arcStartOrigin = arcStart - arcCenter;
326 SEG outlineIn( cornerPosition - incoming, cornerPosition );
328 arcEnd = cornerPosition;
330 while( angPos < endAngle )
337 if( outlineIn.
Side( pt ) > 0 )
341 wxCHECK_RET(
intersect, wxT(
"No solutions exist!" ) );
348 endAngle -= angDelta;
353 for( ; angPos < endAngle; angPos += angDelta )
357 outline.
Append( pt + arcCenter );
370 VECTOR2I prev = aCorners[0].m_position;
372 for(
int pos = aCorners.size() - 1; pos >= 0; pos-- )
374 if( aCorners[pos].m_position == prev )
375 aCorners.erase( aCorners.begin() + pos );
377 prev = aCorners[pos].m_position;
384 int aDeltaY,
int aInflate,
int aError,
ERROR_LOC aErrorLoc )
388 std::vector<ROUNDED_CORNER> corners;
392 if( !aDeltaX && !aDeltaY )
394 size.
x = std::max( 1, size.
x + aInflate );
395 size.
y = std::max( 1, size.
y + aInflate );
399 double slope = (double) aDeltaX / size.
x;
400 int yShrink =
KiROUND( ( std::hypot( size.
x, aDeltaX ) * aInflate ) / size.
x );
401 size.
y = std::max( 1, size.
y + yShrink );
402 size.
x = std::max( 1, size.
x + aInflate );
403 aDeltaX =
KiROUND( size.
x * slope );
405 if( aDeltaX > size.
y )
407 corners.reserve( 3 );
408 corners.emplace_back( -size.
x, -size.
y - aDeltaX );
409 corners.emplace_back(
KiROUND( size.
y / slope ), 0 );
410 corners.emplace_back( -size.
x, size.
y + aDeltaX );
415 double slope = (double) aDeltaY / size.
y;
416 int xShrink =
KiROUND( ( std::hypot( size.
y, aDeltaY ) * aInflate ) / size.
y );
417 size.
x = std::max( 1, size.
x + xShrink );
418 size.
y = std::max( 1, size.
y + aInflate );
419 aDeltaY =
KiROUND( size.
y * slope );
421 if( aDeltaY > size.
x )
423 corners.reserve( 3 );
424 corners.emplace_back( 0, -
KiROUND( size.
x / slope ) );
425 corners.emplace_back( size.
x + aDeltaY, size.
y );
426 corners.emplace_back( -size.
x - aDeltaY, size.
y );
433 if( corners.empty() )
435 corners.reserve( 4 );
436 corners.emplace_back( -size.
x + aDeltaY, -size.
y - aDeltaX );
437 corners.emplace_back( size.
x - aDeltaY, -size.
y + aDeltaX );
438 corners.emplace_back( size.
x + aDeltaY, size.
y - aDeltaX );
439 corners.emplace_back( -size.
x - aDeltaY, size.
y + aDeltaX );
448 outline.
Rotate( aRotation );
451 aBuffer.
Append( outline );
457 int aCornerRadius,
double aChamferRatio,
458 int aChamferCorners,
int aInflate,
int aError,
463 int chamferCnt = std::bitset<8>( aChamferCorners ).count();
464 double chamferDeduct = 0;
468 size.
x = std::max( 1, size.
x + aInflate );
469 size.
y = std::max( 1, size.
y + aInflate );
470 chamferDeduct = aInflate * ( 2.0 - M_SQRT2 );
471 aCornerRadius = std::max( 0, aCornerRadius + aInflate );
475 std::vector<ROUNDED_CORNER> corners;
476 corners.reserve( 4 + chamferCnt );
477 corners.emplace_back( -size.
x, -size.
y, aCornerRadius );
478 corners.emplace_back( size.
x, -size.
y, aCornerRadius );
479 corners.emplace_back( size.
x, size.
y, aCornerRadius );
480 corners.emplace_back( -size.
x, size.
y, aCornerRadius );
482 if( aChamferCorners )
484 int shorterSide = std::min( aSize.
x, aSize.
y );
485 int chamfer = std::max( 0,
KiROUND( aChamferRatio * shorterSide + chamferDeduct ) );
488 int sign[8] = { 0, 1, -1, 0, 0, -1, 1, 0 };
490 for(
int cc = 0, pos = 0; cc < 4; cc++, pos++ )
492 if( !( aChamferCorners & chamId[cc] ) )
495 corners[pos].m_radius = 0;
500 corners.insert( corners.begin() + pos + 1, corners[pos] );
501 corners[pos].m_position.x +=
sign[( 2 * cc ) & 7] *
chamfer;
502 corners[pos].m_position.y +=
sign[( 2 * cc - 2 ) & 7] *
chamfer;
503 corners[pos + 1].m_position.x +=
sign[( 2 * cc + 1 ) & 7] *
chamfer;
504 corners[pos + 1].m_position.y +=
sign[( 2 * cc - 1 ) & 7] *
chamfer;
508 if( chamferCnt > 1 && 2 *
chamfer >= shorterSide )
515 outline.
Rotate( aRotation );
517 outline.
Move( aPosition );
518 aBuffer.
Append( outline );
524 double aRadialOffset )
528 if( !params.
Compute( aStart, aMid, aEnd ) )
530 aPolyline.
Append( aStart );
540 if( arc_angle <= 0.0 )
542 aPolyline.
Append( aStart );
547 constexpr double max_coord =
static_cast<double>( std::numeric_limits<int>::max() );
549 auto append_point = [&](
double aAlpha,
double aEffectiveRadius ) ->
bool
551 const double u_offset = aEffectiveRadius * std::sin( aAlpha );
552 const double n_offset = params.
GetCenterOffset() - aEffectiveRadius * std::cos( aAlpha );
554 const double x = params.
GetMidX() + params.
GetUx() * u_offset + params.
GetNx() * n_offset;
555 const double y = params.
GetMidY() + params.
GetUy() * u_offset + params.
GetNy() * n_offset;
557 if( !std::isfinite( x ) || !std::isfinite( y )
560 wxLogDebug( wxT(
"Arc approximation overflow: falling back to straight segment." ) );
562 aPolyline.
Append( aStart );
571 double effective_radius = params.
GetRadius() + aRadialOffset;
575 int radius_for_seg =
KiROUND( std::min(
std::abs( effective_radius ), max_coord ) );
577 if( radius_for_seg >= aAccuracy )
580 const double half_angle = arc_angle / 2.0;
581 const double delta = arc_angle /
static_cast<double>( n );
585 for(
int i = 0; i <= n; ++i )
587 if( !append_point( -half_angle +
delta * i, effective_radius ) )
597 for(
int i = 0; i <= n; ++i )
599 if( !append_point( -half_angle +
delta * i, effective_radius ) )
607 double error_radius = effective_radius +
static_cast<double>( delta_radius );
609 if( !append_point( -half_angle, effective_radius ) )
612 for(
int i = 0; i < n; ++i )
614 if( !append_point( -half_angle +
delta * ( i + 0.5 ), error_radius ) )
618 if( !append_point( half_angle, effective_radius ) )
632 if( aRadius >= aAccuracy )
644 for(
int i = 0; i <= n; i++, rot +=
delta )
646 double x = aCenter.
x + aRadius * rot.
Cos();
647 double y = aCenter.
y + aRadius * rot.
Sin();
660 int errorRadius = aRadius + actual_delta_radius;
662 double x = aCenter.
x + aRadius * aStartAngle.
Cos();
663 double y = aCenter.
y + aRadius * aStartAngle.
Sin();
669 for(
int i = 0; i < n; i++, rot +=
delta )
671 x = aCenter.
x + errorRadius * rot.
Cos();
672 y = aCenter.
y + errorRadius * rot.
Sin();
677 x = aCenter.
x + aRadius * ( aStartAngle + aArcAngle ).Cos();
678 y = aCenter.
y + aRadius * ( aStartAngle + aArcAngle ).Sin();
690 SEG startToEnd( aStart, aEnd );
691 int distanceToMid = startToEnd.
Distance( aMid );
693 if( distanceToMid <= 1 )
702 VECTOR2I startToMid = aMid - aStart;
703 VECTOR2I startToEndVec = aEnd - aStart;
704 int64_t cross = (int64_t) startToMid.
x * startToEndVec.
y
705 - (int64_t) startToMid.
y * startToEndVec.
x;
715 if( !params.
Compute( p0, aMid, p1 ) )
724 int radial_offset = aWidth / 2;
725 double arc_inner_radius = params.
GetRadius() - radial_offset;
745 if( arc_inner_radius > 0 )
748 aBuffer.
Append( polyshape );
753 int aWidth,
int aError,
ERROR_LOC aErrorLoc )
755 int inner_radius = aRadius - ( aWidth / 2 );
756 int outer_radius = inner_radius + aWidth;
758 if( inner_radius <= 0 )
774 aError, inner_err_loc );
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Arc geometry computed from a chord-based coordinate system.
double GetRadius() const
Get the arc radius.
EDA_ANGLE GetEndAngle() const
Get the angle from arc center to the end point.
bool Compute(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Compute arc geometry from three points defining the arc.
double GetCenterOffset() const
Get the distance from chord midpoint to arc center along the normal.
double GetArcAngle() const
Get the arc angle (total angle swept by the arc) in radians.
EDA_ANGLE GetStartAngle() const
Get the angle from arc center to the start point.
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Clear()
Remove all points from the line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int NewHole(int aOutline=-1)
Creates a new hole in a given outline.
void Move(const VECTOR2I &aVector) override
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
void CornerListToPolygon(SHAPE_POLY_SET &outline, std::vector< ROUNDED_CORNER > &aCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
void CornerListRemoveDuplicates(std::vector< ROUNDED_CORNER > &aCorners)
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount)
Convert a oblong shape to a polygon, using multiple segments.
int ConvertArcToPolyline(SHAPE_LINE_CHAIN &aPolyline, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, double aAccuracy, ERROR_LOC aErrorLoc, double aRadialOffset)
Generate a polyline to approximate an arc defined by three points.
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount)
Convert a circle to a polygon, using multiple straight lines.
void TransformTrapezoidToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aDeltaX, int aDeltaY, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle or trapezoid to a polygon.
@ RECT_CHAMFER_BOTTOM_RIGHT
@ RECT_CHAMFER_BOTTOM_LEFT
static constexpr EDA_ANGLE ANGLE_0
static constexpr EDA_ANGLE ANGLE_90
static constexpr EDA_ANGLE FULL_CIRCLE
static constexpr EDA_ANGLE ANGLE_360
static constexpr EDA_ANGLE ANGLE_180
a few functions useful in geometry calculations.
int GetCircleToPolyCorrection(int aMaxError)
int CircleToEndSegmentDeltaRadius(int aInnerCircleRadius, int aSegCount)
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
static bool intersect(const SEGMENT_WITH_NORMALS &aSeg, const SFVEC2F &aStart, const SFVEC2F &aEnd)
constexpr double correction
std::optional< VECTOR2I > OPT_VECTOR2I
ROUNDED_CORNER(int x, int y, int radius)
ROUNDED_CORNER(int x, int y)
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
constexpr int sign(T val)
VECTOR2< int32_t > VECTOR2I