48double adaptiveSimpson(
F f,
double a,
double b,
double tol,
int maxDepth );
51double adaptiveSimpsonRec(
F f,
double a,
double b,
double tol,
double whole,
double fa,
double fb,
double fm,
54 const double m = 0.5 * ( a + b );
55 const double lm = 0.5 * ( a + m );
56 const double rm = 0.5 * ( m + b );
57 const double flm = f( lm );
58 const double frm = f( rm );
60 const double left = ( m - a ) * ( fa + 4.0 * flm + fm ) / 6.0;
61 const double right = ( b - m ) * ( fm + 4.0 * frm + fb ) / 6.0;
64 if( depth <= 0 ||
std::abs( diff ) < 15.0 * tol )
67 return adaptiveSimpsonRec( f, a, m, 0.5 * tol,
left, fa, fm, flm, depth - 1 )
68 + adaptiveSimpsonRec( f, m, b, 0.5 * tol,
right, fm, fb, frm, depth - 1 );
72double adaptiveSimpson(
F f,
double a,
double b,
double tol,
int maxDepth )
74 const double fa = f( a );
75 const double fb = f( b );
76 const double fm = f( 0.5 * ( a + b ) );
77 const double whole = ( b - a ) * ( fa + 4.0 * fm + fb ) / 6.0;
78 return adaptiveSimpsonRec( f, a, b, tol, whole, fa, fb, fm, maxDepth );
88template <
typename Eval>
89void subdivideEllipseArc(
double t0,
const VECTOR2I& p0,
double t1,
const VECTOR2I& p1,
double aMaxErrSq,
int aDepth,
98 const double tm = 0.5 * ( t0 + t1 );
101 const double mx = 0.5 * (
static_cast<double>( p0.
x ) + p1.
x );
102 const double my = 0.5 * (
static_cast<double>( p0.
y ) + p1.
y );
103 const double ex = pm.
x - mx;
104 const double ey = pm.
y - my;
106 if( ex * ex + ey * ey <= aMaxErrSq )
112 subdivideEllipseArc( t0, p0, tm, pm, aMaxErrSq, aDepth - 1, aEval, aOut );
113 subdivideEllipseArc( tm, pm, t1, p1, aMaxErrSq, aDepth - 1, aEval, aOut );
134 m_ellipse( aCenter, aMajorRadius, aMinorRadius, aRotation ),
144 m_ellipse( aCenter, aMajorRadius, aMinorRadius, aRotation, aStartAngle, aEndAngle ),
153 m_ellipse( aCenter, aMajorEndpoint, aRatio ),
163 m_ellipse( aCenter, aMajorEndpoint, aRatio, aStartAngle, aEndAngle ),
232 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
233 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
240 const double dx = std::sqrt( a * a * cos2 + b * b * sin2 );
241 const double dy = std::sqrt( a * a * sin2 + b * b * cos2 );
243 const int idx =
static_cast<int>( std::ceil( dx ) ) + aClearance;
244 const int idy =
static_cast<int>( std::ceil( dy ) ) + aClearance;
249 auto eval = [&](
double theta ) ->
VECTOR2D
251 const double ct = std::cos( theta );
252 const double st = std::sin( theta );
259 double minX = std::min( p0.
x, p1.
x );
260 double maxX = std::max( p0.
x, p1.
x );
261 double minY = std::min( p0.
y, p1.
y );
262 double maxY = std::max( p0.
y, p1.
y );
267 const double candidates[4] = { thetaX, thetaX +
M_PI, thetaY, thetaY +
M_PI };
269 for(
double c : candidates )
275 minX = std::min( minX, p.
x );
276 maxX = std::max( maxX, p.
x );
277 minY = std::min( minY, p.
y );
278 maxY = std::max( maxY, p.
y );
281 const int iMinX =
static_cast<int>( std::floor( minX ) ) - aClearance;
282 const int iMaxX =
static_cast<int>( std::ceil( maxX ) ) + aClearance;
283 const int iMinY =
static_cast<int>( std::floor( minY ) ) - aClearance;
284 const int iMaxY =
static_cast<int>( std::ceil( maxY ) ) + aClearance;
287 VECTOR2I( iMaxX - iMinX, iMaxY - iMinY ) );
293 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
294 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
300 const double h = ( a - b ) / ( a + b );
301 const double h2 = h * h;
302 return M_PI * ( a + b ) * ( 1.0 + 3.0 * h2 / ( 10.0 + std::sqrt( 4.0 - 3.0 * h2 ) ) );
305 auto integrand = [a, b](
double theta ) ->
double
307 const double s = std::sin( theta );
308 const double c = std::cos( theta );
309 return std::sqrt( a * a * s * s + b * b * c * c );
315 return adaptiveSimpson( integrand, t0, t1, 1e-9, 20 );
321 if( aSeg.
A == aSeg.
B )
326 if( dSq == 0 || dSq < clearSq )
329 *aActual =
static_cast<int>( std::round( std::sqrt(
static_cast<double>( dSq ) ) ) );
340 const double dx = p.x -
m_ellipse.Center.x;
341 const double dy = p.y -
m_ellipse.Center.y;
350 m_ellipse.Center.y +
static_cast<int>( std::round( wy ) ) );
357 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
358 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
359 const double aSq = a * a;
360 const double bSq = b * b;
362 const double alpha =
D.x *
D.x / aSq +
D.y *
D.y / bSq;
363 const double beta = 2.0 * ( Aloc.
x *
D.x / aSq + Aloc.
y *
D.y / bSq );
364 const double gamma = Aloc.
x * Aloc.
x / aSq + Aloc.
y * Aloc.
y / bSq - 1.0;
367 const double valB = alpha + beta + gamma;
390 const double disc = beta * beta - 4.0 * alpha * gamma;
392 if( disc >= 0.0 && alpha > 0.0 )
394 const double sqrtDisc = std::sqrt( disc );
395 const double twoAlpha = 2.0 * alpha;
396 const double t0 = ( -beta - sqrtDisc ) / twoAlpha;
397 const double t1 = ( -beta + sqrtDisc ) / twoAlpha;
398 const double roots[2] = { t0, t1 };
400 for(
double t : roots )
402 if( t < 0.0 || t > 1.0 )
405 const VECTOR2D hit( Aloc.
x + t *
D.x, Aloc.
y + t *
D.y );
410 const double angle = std::atan2( hit.
y / b, hit.
x / a );
418 *aLocation = toWorld( hit );
423 double minDistSq = std::numeric_limits<double>::max();
433 bestOnSegment = Aloc;
438 bestOnSegment = Bloc;
442 const double dDotD =
D.x *
D.x +
D.y *
D.y;
447 const double theta0 = std::atan2( -b *
D.x, a *
D.y );
448 const double thetas[2] = { theta0, theta0 +
M_PI };
450 for(
double theta : thetas )
455 const double ex = a * std::cos( theta );
456 const double ey = b * std::sin( theta );
459 const double pDotD = ( ex - Aloc.
x ) *
D.x + ( ey - Aloc.
y ) *
D.y;
460 const double t = std::clamp( pDotD / dDotD, 0.0, 1.0 );
461 const double qx = Aloc.
x + t *
D.x;
462 const double qy = Aloc.
y + t *
D.y;
464 const double distSq = ( ex - qx ) * ( ex - qx ) + ( ey - qy ) * ( ey - qy );
466 if( distSq < minDistSq )
479 for(
const EDA_ANGLE& endAngle : endAngles )
481 const double angleRad = endAngle.AsRadians();
482 const double ex = a * std::cos( angleRad );
483 const double ey = b * std::sin( angleRad );
485 const double pDotD = ( ex - Aloc.
x ) *
D.x + ( ey - Aloc.
y ) *
D.y;
486 const double t = std::clamp( pDotD / dDotD, 0.0, 1.0 );
487 const double qx = Aloc.
x + t *
D.x;
488 const double qy = Aloc.
y + t *
D.y;
490 const double distSq = ( ex - qx ) * ( ex - qx ) + ( ey - qy ) * ( ey - qy );
492 if( distSq < minDistSq )
501 const double thresholdSq =
static_cast<double>( aClearance ) *
static_cast<double>( aClearance );
503 if( minDistSq > 0.0 && minDistSq >= thresholdSq )
507 *aActual =
static_cast<int>( std::round( std::sqrt( minDistSq ) ) );
509 *aLocation = toWorld( bestOnSegment );
521 chain.SetClosed(
true );
536 m_ellipse.Mirror( aRef, aFlipDirection );
543 std::stringstream ss;
547 ss <<
"SHAPE_ELLIPSE( VECTOR2I( " <<
m_ellipse.Center.x <<
", " <<
m_ellipse.Center.y <<
" ), "
549 <<
m_ellipse.Rotation.AsDegrees() <<
", DEGREES_T )";
553 ss <<
", EDA_ANGLE( " <<
m_ellipse.StartAngle.AsDegrees() <<
", DEGREES_T )"
554 <<
", EDA_ANGLE( " <<
m_ellipse.EndAngle.AsDegrees() <<
", DEGREES_T )";
567 ss <<
" " <<
m_ellipse.StartAngle.AsDegrees() <<
" " <<
m_ellipse.EndAngle.AsDegrees();
583 const double rotRad =
m_ellipse.Rotation.AsRadians();
587 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
588 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
608 const double a =
static_cast<double>(
m_ellipse.MajorRadius ) + aAccuracy;
609 const double b =
static_cast<double>(
m_ellipse.MinorRadius ) + aAccuracy;
610 return ( lx * lx ) / ( a * a ) + ( ly * ly ) / ( b * b ) < 1.0;
625 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
626 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
629 if( !
m_isArc && !aOutlineOnly )
645 double x0Local = 0.0;
646 double x1Local = 0.0;
652 const double z0 = y0 / a;
653 const double z1 = y1 / b;
654 const double g = z0 * z0 + z1 * z1 - 1.0;
658 const double r0 = ( a / b ) * ( a / b );
659 const double n0 = r0 * z0;
661 double s0 = z1 - 1.0;
662 double s1 = ( g < 0.0 ) ? 0.0 : std::sqrt( n0 * n0 + z1 * z1 ) - 1.0;
665 for(
int iter = 0; iter < 64; ++iter )
667 s = 0.5 * ( s0 + s1 );
669 if( s == s0 || s == s1 )
672 const double ratio0 = n0 / ( s + r0 );
673 const double ratio1 = z1 / ( s + 1.0 );
674 const double gs = ratio0 * ratio0 + ratio1 * ratio1 - 1.0;
684 x0Local = r0 * y0 / ( s + r0 );
685 x1Local = y1 / ( s + 1.0 );
704 const double numer0 = a * y0;
705 const double denom0 = a * a - b * b;
707 if( numer0 < denom0 )
709 const double xde0 = numer0 / denom0;
711 x1Local = b * std::sqrt( std::max( 0.0, 1.0 - xde0 * xde0 ) );
720 const double closestX = ( lx < 0.0 ) ? -x0Local : x0Local;
721 const double closestY = ( ly < 0.0 ) ? -x1Local : x1Local;
726 const double closestTheta = std::atan2( closestY / b, closestX / a );
730 const double s0 =
m_ellipse.StartAngle.AsRadians();
731 const double e0 =
m_ellipse.EndAngle.AsRadians();
732 const double ex0 = a * std::cos( s0 );
733 const double ey0 = b * std::sin( s0 );
734 const double ex1 = a * std::cos( e0 );
735 const double ey1 = b * std::sin( e0 );
737 const double d0 = ( lx - ex0 ) * ( lx - ex0 ) + ( ly - ey0 ) * ( ly - ey0 );
738 const double d1 = ( lx - ex1 ) * ( lx - ex1 ) + ( ly - ey1 ) * ( ly - ey1 );
740 return static_cast<SEG::ecoord>( std::min( d0, d1 ) );
744 const double dxE = closestX - lx;
745 const double dyE = closestY - ly;
746 return static_cast<SEG::ecoord>( dxE * dxE + dyE * dyE );
755 const double a =
static_cast<double>(
m_ellipse.MajorRadius );
756 const double b =
static_cast<double>(
m_ellipse.MinorRadius );
762 auto eval = [=](
double theta ) ->
VECTOR2I
764 const double ct = std::cos( theta );
765 const double st = std::sin( theta );
766 const double lx = a * ct;
767 const double ly = b * st;
768 const double wx = lx * cosRot - ly * sinRot;
769 const double wy = lx * sinRot + ly * cosRot;
770 return VECTOR2I(
static_cast<int>( std::round( cx + wx ) ),
static_cast<int>( std::round( cy + wy ) ) );
776 const double maxErrSq =
static_cast<double>( aMaxError ) * aMaxError;
779 const VECTOR2I pStart = eval( tStart );
783 subdivideEllipseArc( tStart, pStart, tEnd, pEnd, maxErrSq, 20, eval, out );
799 const double twoPi = 2.0 *
M_PI;
808 aStart =
m_ellipse.StartAngle.AsRadians();
811 const double sweep = aEnd - aStart;
813 if( sweep >= twoPi || sweep <= -twoPi )
814 aEnd = aStart + twoPi;
815 else if( aEnd < aStart )
822 const double twoPi = 2.0 *
M_PI;
827 double t = aAngleRad;
830 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