44 , _rays(
c.radius(),
c.radius())
50 double den = 4*A*C - B*B;
52 THROW_RANGEERROR(
"den == 0, while computing ellipse centre");
67 _angle = std::atan2( -B, -(A - C) )/2;
70 double sinrot, cosrot;
72 double cos2 = cosrot * cosrot;
73 double sin2 = sinrot * sinrot;
74 double cossin = cosrot * sinrot;
76 den = A * cos2 + B * cossin + C * sin2;
78 THROW_RANGEERROR(
"den == 0, while computing 'rx' coefficient");
80 double rx2 =
num / den;
82 THROW_RANGEERROR(
"rx2 < 0, while computing 'rx' coefficient");
86 den = C * cos2 - B * cossin + A * sin2;
88 THROW_RANGEERROR(
"den == 0, while computing 'ry' coefficient");
90 double ry2 =
num / den;
92 THROW_RANGEERROR(
"ry2 < 0, while computing 'rx' coefficient");
103 Coord sinrot, cosrot;
120 THROW_RANGEERROR(
"a degenerate ellipse doesn't have an inverse unit circle transform");
129 Point a(0, 0), b(0, 0);
139 Point a(0, 0), b(0, 0);
150 auto proj_bounds = [&] (
Dim2 d) {
154 auto const r = std::hypot(trans[d], trans[d + 2]);
155 auto const mid = trans[d + 4];
159 return { proj_bounds(
X), proj_bounds(
Y) };
168 assert(larger_ray >= 0.0);
169 auto const rr =
Point(larger_ray, larger_ray);
175 std::vector<double>
c(6);
183 THROW_RANGEERROR(
"a degenerate ellipse doesn't have an implicit form");
186 double cosrot, sinrot;
188 double cos2 = cosrot * cosrot;
189 double sin2 = sinrot * sinrot;
190 double cossin = cosrot * sinrot;
191 double invrx2 = 1 / (
ray(
X) *
ray(
X));
192 double invry2 = 1 / (
ray(
Y) *
ray(
Y));
194 A = invrx2 * cos2 + invry2 * sin2;
195 B = 2 * (invrx2 - invry2) * cossin;
196 C = invrx2 * sin2 + invry2 * cos2;
208 size_t sz = points.size();
210 THROW_RANGEERROR(
"fitting error: too few points passed");
215 for (
size_t i = 0; i < sz; ++i) {
216 fitter.append(points[i]);
221 model.
instance(*
this, fitter.result(z));
231 bool large_arc_flag =
false;
232 bool sweep_flag =
false;
246 double ifcp =
cross(fv, iv);
248 if (ifcp != 0 && (
sgn(
cross(fv, innerv)) !=
sgn(ifcp) ||
251 large_arc_flag =
true;
275 if ((ifcp < 0) ^ large_arc_flag) {
280 large_arc_flag, sweep_flag, fp);
301 angle = std::atan2(am[2], am[0]);
302 }
else if (am[1] != 0) {
303 angle = std::atan2(am[3], am[1]);
307 Point v = Point::polar(angle) * am;
314 _rays[
X] *= std::abs(mwot[0]);
315 _rays[
Y] *= std::abs(mwot[3]);
321 Affine q( coeff[0], coeff[1]/2,
322 coeff[1]/2, coeff[2],
327 std::swap(invm[1], invm[2]);
360 Point p = Point::polar(t);
367 Coord sinrot, cosrot, cost, sint;
372 return ray(
X) * cosrot * cost
373 -
ray(
Y) * sinrot * sint
376 return ray(
X) * sinrot * cost
377 +
ray(
Y) * cosrot * sint
388 }
else if (
ray(
Y) != 0) {
400 Point p = Point::polar(t + M_PI/2);
422 Coord const to_unit = std::clamp(2.0 * t - 1.0, -1.0, 1.0);
424 double const arcsin = std::asin(to_unit);
425 return {arcsin, M_PI - arcsin};
427 double const arccos = std::acos(to_unit);
428 return {arccos, -arccos};
443 if (axis_intersections.empty()) {
446 std::vector<ShapeIntersection>
result;
447 result.reserve(2 * axis_intersections.size());
449 for (
auto const &x : axis_intersections) {
451 result.emplace_back(a, x.first, x.point());
452 if (x.second == 0.0 || x.second == 1.0) {
462 std::vector<ShapeIntersection>
result;
472 std::array<Coord, 6> coeffs;
473 coefficients(coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5]);
475 auto [A, B, C,
D, E, F] = coeffs;
479 std::array<Coord, 3> line_coeffs;
480 line.
coefficients(line_coeffs[0], line_coeffs[1], line_coeffs[2]);
482 auto [a, b,
c] = line_coeffs;
485 if (fabs(lv[
X]) > fabs(lv[
Y])) {
491 Coord I = A + B*q + C*q*q;
492 Coord J = B*r + C*2*q*r +
D + E*q;
493 Coord K = C*r*r + E*r + F;
496 for (
double x : xs) {
504 Coord I = A*q*q + B*q + C;
505 Coord J = A*2*q*r + B*r +
D*q + E;
509 for (
double x : xs) {
535 result.reserve(xings.size());
537 for (
auto const &x : xings) {
538 if (x.second < -param_prec || x.second > 1.0 + param_prec) {
541 result.emplace_back(x.first, std::clamp(x.second, 0.0, 1.0), x.point());
552 if (*
this == other) {
553 THROW_INFINITELY_MANY_SOLUTIONS(
"The two ellipses are identical.");
561 std::array<double, 6> coeffs;
562 coefficients(coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5]);
564 auto [A, B, C,
D, E, F] = coeffs;
566 std::array<double, 6> otheffs;
567 other.
coefficients(otheffs[0], otheffs[1], otheffs[2], otheffs[3], otheffs[4], otheffs[5]);
569 auto [a, b,
c, d, e, f] = otheffs;
610 I = (-B*B*F + 4*A*C*F +
D*E*B - A*E*E - C*
D*
D) / 4;
611 J = -((B*B - 4*A*C) * f + (2*B*F -
D*E) * b + (2*A*E -
D*B) * e +
612 (2*C*
D - E*B) * d + (
D*
D - 4*A*F) *
c + (E*E - 4*C*F) * a) / 4;
613 K = -((b*b - 4*a*
c) * F + (2*b*f - d*e) * B + (2*a*e - d*b) * E +
614 (2*
c*d - e*b) *
D + (d*d - 4*a*f) * C + (e*e - 4*
c*f) * A) / 4;
615 L = (-b*b*f + 4*a*
c*f + d*e*b - a*e*e -
c*d*d) / 4;
630 if (mus.size() == 3) {
631 std::swap(mus[1], mus[0]);
634 static Coord const discriminant_precision = 1e-10;
636 for (
Coord candidate_mu : mus) {
637 Coord const aa = std::fma(candidate_mu, A, a);
638 Coord const bb = std::fma(candidate_mu, B, b);
639 Coord const cc = std::fma(candidate_mu, C,
c);
641 if (
delta < -discriminant_precision) {
654 std::array<double, 6>
degen = {std::fma(mu, A, a), std::fma(mu, B, b), std::fma(mu, C,
c),
655 std::fma(mu,
D, d), std::fma(mu, E, e), std::fma(mu, F, f)};
661 std::vector<ShapeIntersection>
result;
662 for (
auto const &line : lines) {
663 if (line.isDegenerate()) {
671 if (as.empty() ||
bs.empty()) {
687 if (as.size() == 2) {
688 if (
bs.size() == 2) {
689 synthesize_intersection(as[0],
bs[0]);
690 synthesize_intersection(as[1],
bs[1]);
691 }
else if (
bs.size() == 1) {
692 synthesize_intersection(intersection_average(as[0], as[1]),
bs[0]);
694 }
else if (as.size() == 1) {
695 if (
bs.size() == 2) {
696 synthesize_intersection(as[0], intersection_average(
bs[0],
bs[1]));
697 }
else if (
bs.size() == 1) {
698 synthesize_intersection(as[0],
bs[0]);
711 Bezier x = A*b[
X]*b[
X] + B*b[
X]*b[
Y] + C*b[
Y]*b[
Y] +
D*b[
X] + E*b[
Y] + F;
712 std::vector<Coord> r = x.
roots();
714 std::vector<ShapeIntersection>
result;
715 for (
double & i : r) {
760 for (
auto & tp : tps) {
762 tp * bc.unitCircleTransform(),
771 out <<
"Ellipse(" << e.
center() <<
", " << e.
rays()
3x3 matrix representing an affine transformation.
void setTranslation(Point const &loc)
Sets the translation imparted by the Affine.
Coord descrim() const
Calculate the descriminant.
bool isScale(Coord eps=EPSILON) const
Check whether this matrix represents pure scaling.
Affine inverse() const
Compute the inverse matrix.
Affine withoutTranslation() const
Wrapper for angular values.
Coord radians0() const
Get the angle as positive radians.
Coord radians() const
Get the angle as radians.
Rect boundsFast() const override
Quickly compute the curve's approximate bounding box.
Coord length(Coord tolerance) const override
Compute the arc length of this curve.
Polynomial in Bernstein-Bezier basis.
std::vector< Coord > roots() const
Set of all points at a fixed distance from the center.
void transform(Affine const &m)
Transform this curve by an affine transformation.
Adaptor that creates 2D functions from 1D ones.
Point valueAt(double t) const
Set of points with a constant sum of distances from two foci.
Coord valueAt(Coord t, Dim2 d) const
Evaluate a single coordinate of a point on the ellipse.
LineSegment semiaxis(Dim2 d, int sign=1) const
void setRays(Point const &p)
Set both rays of the ellipse.
LineSegment axis(Dim2 d) const
Point initialPoint() const
Get the point corresponding to the +X ray of the ellipse.
void setRotationAngle(Angle a)
Set the angle the X ray makes with the +X axis.
Point unitTangentAt(Coord t) const
Get the value of the derivative at time t normalized to unit length.
std::vector< double > coefficients() const
Get the coefficients of the ellipse's implicit equation.
bool contains(Point const &p) const
Check whether the ellipse contains the given point.
Ellipse canonicalForm() const
Return an ellipse with less degrees of freedom.
Rect boundsFast() const
Get a fast to compute bounding box which contains the ellipse.
Coord timeAt(Point const &p) const
Find the time value of a point on an ellipse.
Angle rotationAngle() const
Get the angle the X ray makes with the +X axis.
std::vector< ShapeIntersection > intersect(Line const &line) const
Compute intersections with an infinite line.
Affine unitCircleTransform() const
Compute the transform that maps the unit circle to this ellipse.
void fit(std::vector< Point > const &points)
Create an ellipse passing through the specified points At least five points have to be specified.
EllipticalArc * arc(Point const &ip, Point const &inner, Point const &fp)
Create an elliptical arc from a section of the ellipse.
Affine inverseUnitCircleTransform() const
Compute the transform that maps this ellipse to the unit circle.
bool are_near(Ellipse const &a, Ellipse const &b, Coord precision=EPSILON)
Test whether two ellipses are approximately the same.
Point pointAt(Coord t) const
Evaluate a point on the ellipse.
Ellipse & operator*=(Translate const &t)
Coord ray(Dim2 d) const
Get one ray of the ellipse.
Rect boundsExact() const
Get the tight-fitting bounding box of the ellipse.
Point rays() const
Get both rays as a point.
bool operator==(Ellipse const &other) const
Compare ellipses for exact equality.
LineSegment majorAxis() const
void setCoefficients(double A, double B, double C, double D, double E, double F)
Set an ellipse by solving its implicit equation.
Intersection between two shapes.
Point point() const
Intersection point, as calculated by the intersection algorithm.
TimeA first
First shape and time value.
Range of real numbers that is never empty.
Infinite line on a plane.
std::vector< double > coefficients() const
Get the coefficients of the line equation as a vector.
Coord timeAt(Point const &p) const
Get a time value corresponding to a point.
bool isDegenerate() const
Check if the line has more than one point.
Point vector() const
Get the line's raw direction vector.
std::vector< ShapeIntersection > intersect(Line const &other) const
void instance(Ellipse &e, ConstVectorView const &coeff) const
Two-dimensional point that doubles as a vector.
void normalize()
Normalize the vector representing the point.
Coord length() const
Compute the distance from origin.
Axis aligned, non-empty rectangle.
Rotation around the origin.
std::array< Line, 2 > decompose_df(Coord epsilon=EPSILON) const
Division-free decomposition of a degenerate conic section, without degeneration test.
double inner(valarray< double > const &x, valarray< double > const &y)
Dim2
2D axis enumeration (X or Y).
constexpr Coord infinity()
Get a value representing infinity.
double Coord
Floating point type used to store coordinates.
constexpr Coord EPSILON
Default "acceptably small" value.
Various utility functions.
void sincos(double angle, double &sin_, double &cos_)
Simultaneously compute a sine and a cosine of the same angle.
static std::vector< ShapeIntersection > double_axis_intersections(std::vector< ShapeIntersection > &&axis_intersections, bool vertical)
For each intersection of some shape with the major axis of an ellipse, produce one or two intersectio...
std::ostream & operator<<(std::ostream &os, const Bezier &b)
int sgn(const T &x)
Sign function - indicates the sign of a numeric type.
Intersection ShapeIntersection
Angle distance(Angle const &a, Angle const &b)
static float sign(double number)
Returns +1 for positive numbers, -1 for negative numbers, and 0 otherwise.
double atan2(Point const &p)
std::vector< Coord > solve_quadratic(Coord a, Coord b, Coord c)
Analytically solve quadratic equation.
static std::array< Coord, 2 > axis_time_to_angles(Coord t, bool vertical)
Convert curve time on the major axis to the corresponding angle parameters on a degenerate ellipse co...
std::vector< Coord > solve_cubic(Coord a, Coord b, Coord c, Coord d)
Analytically solve cubic equation.
std::string format_coord_nice(Coord x)
int rescale_homogenous(std::array< double, N > &values)
Scale the doubles in the passed array to make them "reasonably large".
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
SBasis L2(D2< SBasis > const &a, unsigned k)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Point middle_point(LineSegment const &_segment)