46 int aError,
ERROR_LOC aErrorLoc,
int aMinSegCount )
50 numSegs = std::max( aMinSegCount, numSegs );
53 numSegs = ( numSegs + 7 ) / 8 * 8;
70 corner_position.
y = 0;
72 corner_position += aCenter;
73 aBuffer.
Append( corner_position.
x, corner_position.
y );
81 int aError,
ERROR_LOC aErrorLoc,
int aMinSegCount )
85 numSegs = std::max( aMinSegCount, numSegs );
88 numSegs = ( numSegs + 7 ) / 8 * 8;
107 corner_position.
y = 0;
109 corner_position += aCenter;
110 aBuffer.
Append( corner_position.
x, corner_position.
y );
115 corner_position.
y = 0;
117 corner_position += aCenter;
118 aBuffer.
Append( corner_position.
x, corner_position.
y );
123 int aWidth,
int aError,
ERROR_LOC aErrorLoc,
int aMinSegCount )
132 numSegs = std::max( aMinSegCount, numSegs );
135 numSegs = ( numSegs + 7 ) / 8 * 8;
161 endp = aStart - aEnd;
176 polyshape.
Append( corner.
x, corner.
y );
183 polyshape.
Append( corner.
x, corner.
y );
188 polyshape.
Append( corner.
x, corner.
y );
192 polyshape.
Append( corner.
x, corner.
y );
199 polyshape.
Append( corner.
x, corner.
y );
204 polyshape.
Append( corner.
x, corner.
y );
211 int halfwidth = aWidth / 2;
214 corner.
y = halfwidth;
216 corner.
y = -halfwidth;
218 corner.
x =
radius + seg_len + 2;
220 corner.
y = halfwidth;
229 polyshape.
Rotate( -delta_angle );
230 polyshape.
Move( startp );
232 aBuffer.
Append( polyshape);
247 int aInflate,
int aError,
ERROR_LOC aErrorLoc )
249 assert( aInflate >= 0 );
251 VECTOR2I incoming = aCorners[0].m_position - aCorners.back().m_position;
253 for(
int n = 0, count = aCorners.size(); n < count; n++ )
270 if( ( incoming.
x == 0 && outgoing.y == 0 ) || ( incoming.
y == 0 && outgoing.x == 0 ) )
277 double cosNum = (double) incoming.
x * outgoing.x + (
double) incoming.
y * outgoing.y;
278 double cosDen = (double) incoming.
EuclideanNorm() * outgoing.EuclideanNorm();
279 double angle = acos( cosNum / cosDen );
280 tanAngle2 = tan( (
M_PI - angle ) / 2 );
284 if( aInflate && tanAngle2 )
287 cornerPosition += incoming.
Resize( aInflate / tanAngle2 )
298 while( lastSeg > angDelta )
303 while( lastSeg < -angDelta )
307 EDA_ANGLE angPos = lastSeg.
IsZero() ? angDelta : ( angDelta + lastSeg ) / 2;
309 double arcTransitionDistance = ( tanAngle2 > 0 ) ? (
radius / tanAngle2 ) : 0;
310 VECTOR2I arcStart = cornerPosition - incoming.
Resize( arcTransitionDistance );
317 arcStartOrigin = arcStart - arcCenter;
318 outline.
Append( arcStart );
326 arcStartOrigin = arcStart - arcCenter;
330 SEG outlineIn( cornerPosition - incoming, cornerPosition );
332 arcEnd = cornerPosition;
334 while( angPos < endAngle )
341 if( outlineIn.
Side( pt ) > 0 )
345 wxCHECK_RET(
intersect, wxT(
"No solutions exist!" ) );
352 endAngle -= angDelta;
357 for( ; angPos < endAngle; angPos += angDelta )
361 outline.
Append( pt + arcCenter );
374 VECTOR2I prev = aCorners[0].m_position;
376 for(
int pos = aCorners.size() - 1; pos >= 0; pos-- )
378 if( aCorners[pos].m_position == prev )
379 aCorners.erase( aCorners.begin() + pos );
381 prev = aCorners[pos].m_position;
388 int aDeltaY,
int aInflate,
int aError,
ERROR_LOC aErrorLoc )
392 std::vector<ROUNDED_CORNER> corners;
396 if( !aDeltaX && !aDeltaY )
398 size.
x = std::max( 1, size.
x + aInflate );
399 size.
y = std::max( 1, size.
y + aInflate );
403 double slope = (double) aDeltaX / size.
x;
404 int yShrink =
KiROUND( ( std::hypot( size.
x, aDeltaX ) * aInflate ) / size.
x );
405 size.
y = std::max( 1, size.
y + yShrink );
406 size.
x = std::max( 1, size.
x + aInflate );
407 aDeltaX =
KiROUND( size.
x * slope );
409 if( aDeltaX > size.
y )
411 corners.reserve( 3 );
412 corners.emplace_back( -size.
x, -size.
y - aDeltaX );
413 corners.emplace_back(
KiROUND( size.
y / slope ), 0 );
414 corners.emplace_back( -size.
x, size.
y + aDeltaX );
419 double slope = (double) aDeltaY / size.
y;
420 int xShrink =
KiROUND( ( std::hypot( size.
y, aDeltaY ) * aInflate ) / size.
y );
421 size.
x = std::max( 1, size.
x + xShrink );
422 size.
y = std::max( 1, size.
y + aInflate );
423 aDeltaY =
KiROUND( size.
y * slope );
425 if( aDeltaY > size.
x )
427 corners.reserve( 3 );
428 corners.emplace_back( 0, -
KiROUND( size.
x / slope ) );
429 corners.emplace_back( size.
x + aDeltaY, size.
y );
430 corners.emplace_back( -size.
x - aDeltaY, size.
y );
437 if( corners.empty() )
439 corners.reserve( 4 );
440 corners.emplace_back( -size.
x + aDeltaY, -size.
y - aDeltaX );
441 corners.emplace_back( size.
x - aDeltaY, -size.
y + aDeltaX );
442 corners.emplace_back( size.
x + aDeltaY, size.
y - aDeltaX );
443 corners.emplace_back( -size.
x - aDeltaY, size.
y + aDeltaX );
452 outline.
Rotate( aRotation );
455 aBuffer.
Append( outline );
461 int aCornerRadius,
double aChamferRatio,
462 int aChamferCorners,
int aInflate,
int aError,
467 int chamferCnt = std::bitset<8>( aChamferCorners ).count();
468 double chamferDeduct = 0;
472 size.
x = std::max( 1, size.
x + aInflate );
473 size.
y = std::max( 1, size.
y + aInflate );
474 chamferDeduct = aInflate * ( 2.0 - M_SQRT2 );
475 aCornerRadius = std::max( 0, aCornerRadius + aInflate );
479 std::vector<ROUNDED_CORNER> corners;
480 corners.reserve( 4 + chamferCnt );
481 corners.emplace_back( -size.
x, -size.
y, aCornerRadius );
482 corners.emplace_back( size.
x, -size.
y, aCornerRadius );
483 corners.emplace_back( size.
x, size.
y, aCornerRadius );
484 corners.emplace_back( -size.
x, size.
y, aCornerRadius );
486 if( aChamferCorners )
488 int shorterSide = std::min( aSize.
x, aSize.
y );
489 int chamfer = std::max( 0,
KiROUND( aChamferRatio * shorterSide + chamferDeduct ) );
492 int sign[8] = { 0, 1, -1, 0, 0, -1, 1, 0 };
494 for(
int cc = 0, pos = 0; cc < 4; cc++, pos++ )
496 if( !( aChamferCorners & chamId[cc] ) )
499 corners[pos].m_radius = 0;
504 corners.insert( corners.begin() + pos + 1, corners[pos] );
505 corners[pos].m_position.x +=
sign[( 2 * cc ) & 7] *
chamfer;
506 corners[pos].m_position.y +=
sign[( 2 * cc - 2 ) & 7] *
chamfer;
507 corners[pos + 1].m_position.x +=
sign[( 2 * cc + 1 ) & 7] *
chamfer;
508 corners[pos + 1].m_position.y +=
sign[( 2 * cc - 1 ) & 7] *
chamfer;
512 if( chamferCnt > 1 && 2 *
chamfer >= shorterSide )
519 outline.
Rotate( aRotation );
521 outline.
Move( aPosition );
522 aBuffer.
Append( outline );
528 double aRadialOffset )
532 if( !params.
Compute( aStart, aMid, aEnd ) )
534 aPolyline.
Append( aStart );
544 if( arc_angle <= 0.0 )
546 aPolyline.
Append( aStart );
551 constexpr double max_coord =
static_cast<double>( std::numeric_limits<int>::max() );
553 auto append_point = [&](
double aAlpha,
double aEffectiveRadius ) ->
bool
555 const double u_offset = aEffectiveRadius * std::sin( aAlpha );
556 const double n_offset = params.
GetCenterOffset() - aEffectiveRadius * std::cos( aAlpha );
558 const double x = params.
GetMidX() + params.
GetUx() * u_offset + params.
GetNx() * n_offset;
559 const double y = params.
GetMidY() + params.
GetUy() * u_offset + params.
GetNy() * n_offset;
561 if( !std::isfinite( x ) || !std::isfinite( y )
564 wxLogDebug( wxT(
"Arc approximation overflow: falling back to straight segment." ) );
566 aPolyline.
Append( aStart );
575 double effective_radius = params.
GetRadius() + aRadialOffset;
579 int radius_for_seg =
KiROUND( std::min(
std::abs( effective_radius ), max_coord ) );
581 if( radius_for_seg >= aAccuracy )
584 const double half_angle = arc_angle / 2.0;
585 const double delta = arc_angle /
static_cast<double>( n );
589 for(
int i = 0; i <= n; ++i )
591 if( !append_point( -half_angle +
delta * i, effective_radius ) )
601 for(
int i = 0; i <= n; ++i )
603 if( !append_point( -half_angle +
delta * i, effective_radius ) )
611 double error_radius = effective_radius +
static_cast<double>( delta_radius );
613 if( !append_point( -half_angle, effective_radius ) )
616 for(
int i = 0; i < n; ++i )
618 if( !append_point( -half_angle +
delta * ( i + 0.5 ), error_radius ) )
622 if( !append_point( half_angle, effective_radius ) )
636 if( aRadius >= aAccuracy )
648 for(
int i = 0; i <= n; i++, rot +=
delta )
650 double x = aCenter.
x + aRadius * rot.
Cos();
651 double y = aCenter.
y + aRadius * rot.
Sin();
664 int errorRadius = aRadius + actual_delta_radius;
666 double x = aCenter.
x + aRadius * aStartAngle.
Cos();
667 double y = aCenter.
y + aRadius * aStartAngle.
Sin();
673 for(
int i = 0; i < n; i++, rot +=
delta )
675 x = aCenter.
x + errorRadius * rot.
Cos();
676 y = aCenter.
y + errorRadius * rot.
Sin();
681 x = aCenter.
x + aRadius * ( aStartAngle + aArcAngle ).Cos();
682 y = aCenter.
y + aRadius * ( aStartAngle + aArcAngle ).Sin();
694 SEG startToEnd( aStart, aEnd );
695 int distanceToMid = startToEnd.
Distance( aMid );
697 if( distanceToMid <= 1 )
706 VECTOR2I startToMid = aMid - aStart;
707 VECTOR2I startToEndVec = aEnd - aStart;
708 int64_t cross = (int64_t) startToMid.
x * startToEndVec.
y
709 - (int64_t) startToMid.
y * startToEndVec.
x;
719 if( !params.
Compute( p0, aMid, p1 ) )
728 int radial_offset = aWidth / 2;
729 double arc_inner_radius = params.
GetRadius() - radial_offset;
749 if( arc_inner_radius > 0 )
752 aBuffer.
Append( polyshape );
757 int aWidth,
int aError,
ERROR_LOC aErrorLoc )
759 int inner_radius = aRadius - ( aWidth / 2 );
760 int outer_radius = inner_radius + aWidth;
762 if( inner_radius <= 0 )
778 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.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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
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