44double adaptiveSimpson(
F f,
double a,
double b,
double tol,
int maxDepth );
47double adaptiveSimpsonRec(
F f,
double a,
double b,
double tol,
double whole,
double fa,
double fb,
double fm,
50 const double m = 0.5 * ( a + b );
51 const double lm = 0.5 * ( a + m );
52 const double rm = 0.5 * ( m + b );
53 const double flm = f( lm );
54 const double frm = f( rm );
56 const double left = ( m - a ) * ( fa + 4.0 * flm + fm ) / 6.0;
57 const double right = ( b - m ) * ( fm + 4.0 * frm + fb ) / 6.0;
60 if( depth <= 0 ||
std::abs( diff ) < 15.0 * tol )
63 return adaptiveSimpsonRec( f, a, m, 0.5 * tol,
left, fa, fm, flm, depth - 1 )
64 + adaptiveSimpsonRec( f, m, b, 0.5 * tol,
right, fm, fb, frm, depth - 1 );
68double adaptiveSimpson(
F f,
double a,
double b,
double tol,
int maxDepth )
70 const double fa = f( a );
71 const double fb = f( b );
72 const double fm = f( 0.5 * ( a + b ) );
73 const double whole = ( b - a ) * ( fa + 4.0 * fm + fb ) / 6.0;
74 return adaptiveSimpsonRec( f, a, b, tol, whole, fa, fb, fm, maxDepth );
84template <
typename Eval>
85void subdivideEllipseArc(
double t0,
const VECTOR2I& p0,
double t1,
const VECTOR2I& p1,
double aMaxErrSq,
int aDepth,
94 const double tm = 0.5 * ( t0 + t1 );
97 const double mx = 0.5 * (
static_cast<double>( p0.
x ) + p1.
x );
98 const double my = 0.5 * (
static_cast<double>( p0.
y ) + p1.
y );
99 const double ex = pm.
x - mx;
100 const double ey = pm.
y - my;
102 if( ex * ex + ey * ey <= aMaxErrSq )
108 subdivideEllipseArc( t0, p0, tm, pm, aMaxErrSq, aDepth - 1, aEval, aOut );
109 subdivideEllipseArc( tm, pm, t1, p1, aMaxErrSq, aDepth - 1, aEval, aOut );
130 m_ellipse( aCenter, aMajorRadius, aMinorRadius, aRotation ),
140 m_ellipse( aCenter, aMajorRadius, aMinorRadius, aRotation, aStartAngle, aEndAngle ),
149 m_ellipse( aCenter, aMajorEndpoint, aRatio ),
159 m_ellipse( aCenter, aMajorEndpoint, aRatio, aStartAngle, aEndAngle ),
228 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
229 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
236 const double dx = std::sqrt( a * a * cos2 + b * b * sin2 );
237 const double dy = std::sqrt( a * a * sin2 + b * b * cos2 );
239 const int idx =
static_cast<int>( std::ceil( dx ) ) + aClearance;
240 const int idy =
static_cast<int>( std::ceil( dy ) ) + aClearance;
245 auto eval = [&](
double theta ) ->
VECTOR2D
247 const double ct = std::cos( theta );
248 const double st = std::sin( theta );
255 double minX = std::min( p0.
x, p1.
x );
256 double maxX = std::max( p0.
x, p1.
x );
257 double minY = std::min( p0.
y, p1.
y );
258 double maxY = std::max( p0.
y, p1.
y );
263 const double candidates[4] = { thetaX, thetaX +
M_PI, thetaY, thetaY +
M_PI };
265 for(
double c : candidates )
271 minX = std::min( minX, p.
x );
272 maxX = std::max( maxX, p.
x );
273 minY = std::min( minY, p.
y );
274 maxY = std::max( maxY, p.
y );
277 const int iMinX =
static_cast<int>( std::floor( minX ) ) - aClearance;
278 const int iMaxX =
static_cast<int>( std::ceil( maxX ) ) + aClearance;
279 const int iMinY =
static_cast<int>( std::floor( minY ) ) - aClearance;
280 const int iMaxY =
static_cast<int>( std::ceil( maxY ) ) + aClearance;
283 VECTOR2I( iMaxX - iMinX, iMaxY - iMinY ) );
289 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
290 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
296 const double h = ( a - b ) / ( a + b );
297 const double h2 = h * h;
298 return M_PI * ( a + b ) * ( 1.0 + 3.0 * h2 / ( 10.0 + std::sqrt( 4.0 - 3.0 * h2 ) ) );
301 auto integrand = [a, b](
double theta ) ->
double
303 const double s = std::sin( theta );
304 const double c = std::cos( theta );
305 return std::sqrt( a * a * s * s + b * b * c * c );
311 return adaptiveSimpson( integrand, t0, t1, 1e-9, 20 );
317 if( aSeg.
A == aSeg.
B )
322 if( dSq == 0 || dSq < clearSq )
325 *aActual =
static_cast<int>( std::round( std::sqrt(
static_cast<double>( dSq ) ) ) );
336 const double dx = p.x -
m_ellipse.Center.x;
337 const double dy = p.y -
m_ellipse.Center.y;
346 m_ellipse.Center.y +
static_cast<int>( std::round( wy ) ) );
353 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
354 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
355 const double aSq = a * a;
356 const double bSq = b * b;
358 const double alpha =
D.x *
D.x / aSq +
D.y *
D.y / bSq;
359 const double beta = 2.0 * ( Aloc.
x *
D.x / aSq + Aloc.
y *
D.y / bSq );
360 const double gamma = Aloc.
x * Aloc.
x / aSq + Aloc.
y * Aloc.
y / bSq - 1.0;
363 const double valB = alpha + beta + gamma;
386 const double disc = beta * beta - 4.0 * alpha * gamma;
388 if( disc >= 0.0 && alpha > 0.0 )
390 const double sqrtDisc = std::sqrt( disc );
391 const double twoAlpha = 2.0 * alpha;
392 const double t0 = ( -beta - sqrtDisc ) / twoAlpha;
393 const double t1 = ( -beta + sqrtDisc ) / twoAlpha;
394 const double roots[2] = { t0, t1 };
396 for(
double t : roots )
398 if( t < 0.0 || t > 1.0 )
401 const VECTOR2D hit( Aloc.
x + t *
D.x, Aloc.
y + t *
D.y );
406 const double angle = std::atan2( hit.
y / b, hit.
x / a );
414 *aLocation = toWorld( hit );
419 double minDistSq = std::numeric_limits<double>::max();
429 bestOnSegment = Aloc;
434 bestOnSegment = Bloc;
438 const double dDotD =
D.x *
D.x +
D.y *
D.y;
443 const double theta0 = std::atan2( -b *
D.x, a *
D.y );
444 const double thetas[2] = { theta0, theta0 +
M_PI };
446 for(
double theta : thetas )
451 const double ex = a * std::cos( theta );
452 const double ey = b * std::sin( theta );
455 const double pDotD = ( ex - Aloc.
x ) *
D.x + ( ey - Aloc.
y ) *
D.y;
456 const double t = std::clamp( pDotD / dDotD, 0.0, 1.0 );
457 const double qx = Aloc.
x + t *
D.x;
458 const double qy = Aloc.
y + t *
D.y;
460 const double distSq = ( ex - qx ) * ( ex - qx ) + ( ey - qy ) * ( ey - qy );
462 if( distSq < minDistSq )
475 for(
const EDA_ANGLE& endAngle : endAngles )
477 const double angleRad = endAngle.AsRadians();
478 const double ex = a * std::cos( angleRad );
479 const double ey = b * std::sin( angleRad );
481 const double pDotD = ( ex - Aloc.
x ) *
D.x + ( ey - Aloc.
y ) *
D.y;
482 const double t = std::clamp( pDotD / dDotD, 0.0, 1.0 );
483 const double qx = Aloc.
x + t *
D.x;
484 const double qy = Aloc.
y + t *
D.y;
486 const double distSq = ( ex - qx ) * ( ex - qx ) + ( ey - qy ) * ( ey - qy );
488 if( distSq < minDistSq )
497 const double thresholdSq =
static_cast<double>( aClearance ) *
static_cast<double>( aClearance );
499 if( minDistSq > 0.0 && minDistSq >= thresholdSq )
503 *aActual =
static_cast<int>( std::round( std::sqrt( minDistSq ) ) );
505 *aLocation = toWorld( bestOnSegment );
517 chain.SetClosed(
true );
532 m_ellipse.Mirror( aRef, aFlipDirection );
539 std::stringstream ss;
543 ss <<
"SHAPE_ELLIPSE( VECTOR2I( " <<
m_ellipse.Center.x <<
", " <<
m_ellipse.Center.y <<
" ), "
545 <<
m_ellipse.Rotation.AsDegrees() <<
", DEGREES_T )";
549 ss <<
", EDA_ANGLE( " <<
m_ellipse.StartAngle.AsDegrees() <<
", DEGREES_T )"
550 <<
", EDA_ANGLE( " <<
m_ellipse.EndAngle.AsDegrees() <<
", DEGREES_T )";
563 ss <<
" " <<
m_ellipse.StartAngle.AsDegrees() <<
" " <<
m_ellipse.EndAngle.AsDegrees();
579 const double rotRad =
m_ellipse.Rotation.AsRadians();
583 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
584 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
604 const double a =
static_cast<double>(
m_ellipse.MajorRadius ) + aAccuracy;
605 const double b =
static_cast<double>(
m_ellipse.MinorRadius ) + aAccuracy;
606 return ( lx * lx ) / ( a * a ) + ( ly * ly ) / ( b * b ) < 1.0;
621 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
622 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
625 if( !
m_isArc && !aOutlineOnly )
641 double x0Local = 0.0;
642 double x1Local = 0.0;
648 const double z0 = y0 / a;
649 const double z1 = y1 / b;
650 const double g = z0 * z0 + z1 * z1 - 1.0;
654 const double r0 = ( a / b ) * ( a / b );
655 const double n0 = r0 * z0;
657 double s0 = z1 - 1.0;
658 double s1 = ( g < 0.0 ) ? 0.0 : std::sqrt( n0 * n0 + z1 * z1 ) - 1.0;
661 for(
int iter = 0; iter < 64; ++iter )
663 s = 0.5 * ( s0 + s1 );
665 if( s == s0 || s == s1 )
668 const double ratio0 = n0 / ( s + r0 );
669 const double ratio1 = z1 / ( s + 1.0 );
670 const double gs = ratio0 * ratio0 + ratio1 * ratio1 - 1.0;
680 x0Local = r0 * y0 / ( s + r0 );
681 x1Local = y1 / ( s + 1.0 );
700 const double numer0 = a * y0;
701 const double denom0 = a * a - b * b;
703 if( numer0 < denom0 )
705 const double xde0 = numer0 / denom0;
707 x1Local = b * std::sqrt( std::max( 0.0, 1.0 - xde0 * xde0 ) );
716 const double closestX = ( lx < 0.0 ) ? -x0Local : x0Local;
717 const double closestY = ( ly < 0.0 ) ? -x1Local : x1Local;
722 const double closestTheta = std::atan2( closestY / b, closestX / a );
726 const double s0 =
m_ellipse.StartAngle.AsRadians();
727 const double e0 =
m_ellipse.EndAngle.AsRadians();
728 const double ex0 = a * std::cos( s0 );
729 const double ey0 = b * std::sin( s0 );
730 const double ex1 = a * std::cos( e0 );
731 const double ey1 = b * std::sin( e0 );
733 const double d0 = ( lx - ex0 ) * ( lx - ex0 ) + ( ly - ey0 ) * ( ly - ey0 );
734 const double d1 = ( lx - ex1 ) * ( lx - ex1 ) + ( ly - ey1 ) * ( ly - ey1 );
736 return static_cast<SEG::ecoord>( std::min( d0, d1 ) );
740 const double dxE = closestX - lx;
741 const double dyE = closestY - ly;
742 return static_cast<SEG::ecoord>( dxE * dxE + dyE * dyE );
751 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
752 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
758 auto eval = [=](
double theta ) ->
VECTOR2I
760 const double ct = std::cos( theta );
761 const double st = std::sin( theta );
762 const double lx = a * ct;
763 const double ly = b * st;
764 const double wx = lx * cosRot - ly * sinRot;
765 const double wy = lx * sinRot + ly * cosRot;
766 return VECTOR2I(
static_cast<int>( std::round( cx + wx ) ),
static_cast<int>( std::round( cy + wy ) ) );
772 const double maxErrSq =
static_cast<double>( aMaxError ) * aMaxError;
775 const VECTOR2I pStart = eval( tStart );
779 subdivideEllipseArc( tStart, pStart, tEnd, pEnd, maxErrSq, 20, eval, out );
795 const double twoPi = 2.0 *
M_PI;
804 aStart =
m_ellipse.StartAngle.AsRadians();
807 const double sweep = aEnd - aStart;
809 if( sweep >= twoPi || sweep <= -twoPi )
810 aEnd = aStart + twoPi;
811 else if( aEnd < aStart )
818 const double twoPi = 2.0 *
M_PI;
823 double t = aAngleRad;
826 while( t >= tStart + twoPi )
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
VECTOR2I::extended_type ecoord
void SetRotation(const EDA_ANGLE &aAngle)
void updateCache()
Recompute cached sin/cos and inverse-radius-squared values.
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
void SetMajorRadius(int aRadius)
ELLIPSE< int > m_ellipse
Wrapped geometric data (from geometry/ellipse.h)
bool isAngleInSweep(double aAngleRad) const
Return true if aAngleRad falls between StartAngle and EndAngle (counter-clockwise sweep).
SEG::ecoord SquaredDistance(const VECTOR2I &aP, bool aOutlineOnly=false) const override
double m_invMinorRSq
1 / MinorRadius ^ 2
void SetStartAngle(const EDA_ANGLE &aAngle)
double m_cosRot
cos(Rotation)
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
const std::string Format(bool aCplusPlus=true) const override
Serialize the ellipse.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the ellipse across a horizontal or vertical axis passing through aRef.
void sweepRange(double &aStart, double &aEnd) const
Canonical CCW sweep in radians; aEnd >= aStart. Used by all sweep-aware paths.
void normalize()
If major < minor, swap them and add 90 degrees to rotation.
void SetCenter(const VECTOR2I &aCenter)
void SetMinorRadius(int aRadius)
double m_invMajorRSq
1 / MajorRadius ^ 2
double m_sinRot
sin(Rotation)
void Move(const VECTOR2I &aVector) override
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
bool m_isArc
true if open elliptical arc, false if closed ellipse
void SetEndAngle(const EDA_ANGLE &aAngle)
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.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
SHAPE(SHAPE_TYPE aType)
Create an empty shape of type aType.
virtual const std::string Format(bool aCplusPlus=true) const
static constexpr EDA_ANGLE ANGLE_90
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ SH_ELLIPSE
ellipse or elliptical arc
const SHAPE_LINE_CHAIN chain
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.
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D