118 auto proj_bounds = [&] (
Dim2 d) {
124 auto const v =
Point(trans[d], trans[d + 2]);
125 auto const r = v.length();
126 auto const mid = trans[d + 4];
127 auto const angle =
Angle(v);
139 return { proj_bounds(
X), proj_bounds(
Y) };
152 for (
auto d : {
X,
Y }) {
154 auto const v =
Point(trans[d], trans[d + 2]);
155 auto const r = v.length();
156 auto const mid = trans[d + 4];
157 auto const interval =
Interval(mid - r, mid + r);
161 }
else if (!bbox[d].
contains(interval)) {
162 auto const angle =
Angle(v);
186 std::vector<Coord> sol;
203 double rxrotx =
ray(
X) * rotx;
204 double c_v =
center(d) - v;
206 double a = -rxrotx + c_v;
207 double b =
ray(
Y) * roty;
208 double c = rxrotx + c_v;
218 double s = 2 * std::atan(-
c/(2*b));
219 if ( s < 0 ) s += 2*M_PI;
225 double delta = b * b - a *
c;
228 double s = 2 * std::atan(-b/a);
229 if ( s < 0 ) s += 2*M_PI;
232 else if (
delta > 0 )
234 double sq = std::sqrt(
delta);
235 double s = 2 * std::atan( (-b - sq) / a );
236 if ( s < 0 ) s += 2*M_PI;
238 s = 2 * std::atan( (-b + sq) / a );
239 if ( s < 0 ) s += 2*M_PI;
244 std::vector<double> arc_sol;
245 for (
double & i : sol) {
250 arc_sol.push_back(i);
269 result->_angles.setInitial(
result->_angles.initialAngle() + M_PI/2);
270 result->_angles.setFinal(
result->_angles.finalAngle() + M_PI/2);
284 unsigned int nn = n+1;
285 std::vector<Point>
result;
289 ea->_ellipse.setCenter(0, 0);
290 unsigned int m = std::min(nn, 4u);
291 for (
unsigned int i = 0; i < m; ++i )
293 result.push_back( ea->pointAtAngle(angle) );
294 angle += (
sweep() ? M_PI/2 : -M_PI/2);
295 if ( !(angle < 2*M_PI) ) angle -= 2*M_PI;
298 for (
unsigned int i = 1; i < m; ++i )
300 for (
unsigned int j = 0; j < 4; ++j )
304 for (
unsigned int i = 0; i < m; ++i )
336 f = std::clamp(f, 0.0, 1.0);
337 t = std::clamp(t, 0.0, 1.0);
344 if (f == 0.0 && t == 1.0) {
347 if (f == 1.0 && t == 0.0) {
375 std::vector<double>
result;
377 if ( from > to ) std::swap(from, to);
378 if ( from < 0 || to > 1 )
380 THROW_RANGEERROR(
"[from,to] interval out of range");
423 THROW_INFINITESOLUTIONS(0);
465 double sinrot, cosrot;
467 double expr1 =
ray(
X) * (p_c[
X] * cosrot + p_c[
Y] * sinrot);
470 coeff[4] =
ray(
Y) * ( p_c[
Y] * cosrot - p_c[
X] * sinrot );
471 coeff[3] = 2 * ( rx2_ry2 + expr1 );
473 coeff[1] = 2 * ( -rx2_ry2 + expr1 );
474 coeff[0] = -coeff[4];
479 std::vector<double> real_sol;
484 real_sol.push_back(0);
487 double sq = -coeff[1] / coeff[3];
490 double s = std::sqrt(sq);
491 real_sol.push_back(s);
492 real_sol.push_back(-s);
501 for (
double & i : real_sol)
503 i = 2 * std::atan(i);
504 if ( i < 0 ) i += 2*M_PI;
508 if ( (real_sol.size() % 2) != 0 )
510 real_sol.push_back(M_PI);
513 double mindistsq1 = std::numeric_limits<double>::max();
514 double mindistsq2 = std::numeric_limits<double>::max();
516 unsigned int mi1 = 0, mi2 = 0;
517 for (
unsigned int i = 0; i < real_sol.size(); ++i )
520 if ( mindistsq1 > dsq )
522 mindistsq2 = mindistsq1;
527 else if ( mindistsq2 > dsq )
535 if ( !(t < from || t > to) )
540 bool second_sol =
false;
542 if ( real_sol.size() == 4 && !(t < from || t > to) )
556 if ( mindistsq2 > dsq1 )
562 else if (
are_near(mindistsq2, dsq) )
566 if ( mindistsq2 > dsq2 )
571 else if (
are_near(mindistsq2, dsq2) )
586 else if ( dsq2 > dsq1 )
607 std::vector<ShapeIntersection>
result;
608 result.reserve(xs.size());
609 for (
auto &xing : xs) {
611 result.emplace_back(std::move(xing));
628 static auto const UNIT_INTERVAL =
Interval(0, 1);
629 constexpr auto EPS = 1e-4;
658 if (
auto bez =
dynamic_cast<BezierCurve const *
>(&other)) {
662 if (
auto arc =
dynamic_cast<EllipticalArc const *
>(&other)) {
663 std::vector<CurveIntersection>
crossings;
690 std::vector<ShapeIntersection>
result;
693 auto const synthesize_intersection = [&](
Angle angle) {
697 return xing.first == time;
705 if (other_angles.contains(a)) {
706 synthesize_intersection(a);
709 for (
auto a : {other_angles.initialAngle(), other_angles.finalAngle()}) {
711 synthesize_intersection(a);
723 auto degenerate_ellipse = [&] {
732 degenerate_ellipse();
753 Point p = d / 2 * invrot;
757 Coord lambda = hypot(p[
X]/r[
X], p[
Y]/r[
Y]);
767 Coord const denominator = rxpy + pxry;
768 if (denominator == 0.0) {
769 degenerate_ellipse();
772 Coord rad = (rxry - pxry - rxpy) / denominator;
775 rad = std::sqrt(rad);
810 Coord cosrot, sinrot;
820 for (
int d = 0 ; d < 2 ; d++ ) {
887 if (
this == &
c)
return true;
889 if (!other)
return false;
894 if (
rays() != other->rays())
return false;
896 if (
_large_arc != other->_large_arc)
return false;
897 if (
sweep() != other->sweep())
return false;
922 if (moveto_initial) {
932 double sinrot, cosrot;
935 Angle ymin_a = std::atan2(
ray(
Y) * cosrot,
ray(
X) * sinrot );
936 Angle ymax_a = ymin_a + M_PI;
940 if (ymin[
Y] > ymax[
Y]) {
942 swap(ymin_a, ymax_a);
945 if (!
Interval(ymin[
Y], ymax[
Y]).lowerContains(p[
Y])) {
949 bool const left =
cross(ymax - ymin, p - ymin) > 0;
953 return sweep() ? 1 : -1;
961 larc(ymax_a, ymin_a,
true);
972 bool const initial_left = larc.
contains(ia);
973 bool const final_left = larc.
contains(fa);
975 bool intersects_left =
false, intersects_right =
false;
976 if (inside || left) {
995 (initial_left && final_left && includes_ymin && includes_ymax);
997 if (left && !inside) {
1016 (!initial_left && !final_left && includes_ymin && includes_ymax);
1019 int const winding_assuming_increasing_angles = (int)intersects_right - (
int)intersects_left;
1020 return sweep() ? winding_assuming_increasing_angles : -winding_assuming_increasing_angles;
1025 out <<
"EllipticalArc("
1029 <<
"large_arc=" << (ea.
largeArc() ?
"true" :
"false") <<
", "
1030 <<
"sweep=" << (ea.
sweep() ?
"true" :
"false") <<
", "
Various utility functions.
3x3 matrix representing an affine transformation.
Coord det() const
Calculate the determinant.
Directed angular interval.
void setSweep(bool s)
Set whether the interval goes in the direction of increasing angles.
void setAngles(Angle s, Angle e, bool prefer_full=false)
Set both angles at once.
Angle finalAngle() const
Get the end angle.
void setInitial(Angle a, bool prefer_full=false)
Set the initial angle.
bool isFull() const
Check whether the interval contains all angles.
void reverse()
Reverse the direction of the interval while keeping contained values the same.
Angle initialAngle() const
Get the start angle.
bool contains(Angle a) const
Check whether the interval includes the given angle.
void setFinal(Angle a, bool prefer_full=false)
Set the final angle.
Wrapper for angular values.
Coord radians() const
Get the angle as radians.
std::vector< CurveIntersection > intersect(Curve const &other, Coord eps=EPSILON) const override
Compute intersections with another curve.
Coord nearestTime(Point const &p, Coord from=0, Coord to=1) const override
Compute a time value at which the curve comes closest to a specified point.
Curve * derivative() const override
Create a derivative of this curve.
Two-dimensional Bezier curve of arbitrary order.
D2< SBasis > toSBasis() const override
Convert the curve to a symmetric power basis polynomial.
Coord valueAt(Coord t, Dim2 d) const override
Evaluate one of the coordinates at the specified time value.
Point pointAt(Coord t) const override
Evaluate the curve at a specified time value.
std::vector< Coord > roots(Coord v, Dim2 d) const override
Computes time values at which the curve intersects an axis-aligned line.
std::vector< Point > pointAndDerivatives(Coord t, unsigned n) const override
Evaluate the curve and its derivatives.
Abstract continuous curve on a plane defined on [0,1].
virtual Point initialPoint() const =0
Retrieve the start of the curve.
virtual Point finalPoint() const =0
Retrieve the end of the curve.
virtual std::vector< CurveIntersection > intersect(Curve const &other, Coord eps=EPSILON) const
Compute intersections with another curve.
virtual bool isLineSegment() const
Check whether the curve is a line segment.
void transform(Affine const &m)
Transform this curve by an affine transformation.
Adaptor that creates 2D functions from 1D ones.
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.
void setRays(Point const &p)
Set both rays of the ellipse.
void setRotationAngle(Angle a)
Set the angle the X ray makes with the +X axis.
bool contains(Point const &p) const
Check whether the ellipse contains the given point.
Coord timeAt(Point const &p) const
Find the time value of a point on an ellipse.
std::vector< ShapeIntersection > intersect(Line const &line) const
Compute intersections with an infinite line.
void setCenter(Point const &p)
Set the center.
Point pointAt(Coord t) const
Evaluate a point on the ellipse.
Rect boundsExact() const
Get the tight-fitting bounding box of the ellipse.
Coord timeAtAngle(Angle a) const
Compute the curve time value corresponding to the given angular value.
Angle initialAngle() const
std::vector< double > roots(double v, Dim2 d) const override
Computes time values at which the curve intersects an axis-aligned line.
D2< SBasis > toSBasis() const override
Convert the curve to a symmetric power basis polynomial.
bool isLineSegment() const override
Check whether the curve is a line segment.
Point pointAt(Coord t) const override
Evaluate the arc in the curve domain, i.e. .
AngleInterval angularInterval() const
Get the angular interval of the arc.
Point finalPoint() const override
Retrieve the end of the curve.
Coord valueAt(Coord t, Dim2 d) const override
Evaluate a single coordinate on the arc in the curve domain.
Curve * reverse() const override
Create a reversed version of this curve.
std::vector< Point > pointAndDerivatives(Coord t, unsigned int n) const override
int winding(Point const &p) const override
Compute the partial winding number of this curve.
Rect boundsExact() const override
Compute bounds of an elliptical arc.
Curve * duplicate() const override
Create an exact copy of this curve.
void _updateCenterAndAngles()
void feed(PathSink &sink, bool moveto_initial) const override
Feed the curve to a PathSink.
bool largeArc() const
Whether the arc is larger than half an ellipse.
Point initialPoint() const override
Retrieve the start of the curve.
Coord angularExtent() const
Get the elliptical angle spanned by the arc.
LineSegment chord() const
Get the line segment connecting the arc's endpoints.
Angle rotationAngle() const
Get the defining ellipse's rotation.
bool _validateIntersection(ShapeIntersection &xing, bool is_first) const
Convert the passed intersection to curve time and check whether the intersection is numerically sane.
std::vector< ShapeIntersection > _intersectSameEllipse(EllipticalArc const *other) const
Check if two arcs on the same ellipse intersect/overlap.
bool isChord() const
Check whether both rays are nonzero.
void expandToTransformed(Rect &bbox, Affine const &transform) const override
Expand the given rectangle to include the transformed curve, assuming it already contains its initial...
Point rays() const
Get both rays as a point.
Affine unitCircleTransform() const
Compute a transform that maps the unit circle to the arc's ellipse.
std::vector< CurveIntersection > intersect(Curve const &other, Coord eps=EPSILON) const override
Compute intersections with another curve.
Curve * derivative() const override
Create a derivative of this curve.
Angle angleAt(Coord t) const
Compute the angular domain value corresponding to the given time value.
void operator*=(Translate const &tr) override
Point pointAtAngle(Coord t) const
Evaluate the arc at the specified angular coordinate.
EllipticalArc()
Creates an arc with all variables set to zero.
Coord ray(Dim2 d) const
Get one of the ellipse's rays.
Coord sweepAngle() const
Compute the amount by which the angle parameter changes going from start to end.
bool _equalTo(Curve const &c) const override
bool sweep() const
Whether the arc turns clockwise.
Point center() const
Get the arc's center.
Coord valueAtAngle(Coord t, Dim2 d) const
Evaluate one of the arc's coordinates at the specified angle.
std::vector< ShapeIntersection > _filterIntersections(std::vector< ShapeIntersection > &&xs, bool is_first) const
Convert the passed intersections to curve time parametrization and filter out any invalid intersectio...
Curve * portion(double f, double t) const override
Create a curve that corresponds to a part of this curve.
std::vector< double > allNearestTimes(Point const &p, double from=0, double to=1) const override
Compute time values at which the curve comes closest to a specified point.
bool isNear(Curve const &other, Coord precision) const override
Test whether two curves are approximately the same.
constexpr bool contains(C val) const
Check whether the interval includes this number.
void expandTo(CPoint const &p)
Enlarge the rectangle to contain the given point.
void unionWith(CRect const &b)
Enlarge the rectangle to contain the argument.
Intersection between two shapes.
TimeB second
Second shape and time value.
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.
constexpr bool lowerContains(Coord val) const
Check whether the number is contained in the union of the interior and the lower boundary.
Function that interpolates linearly between two values.
Callback interface for processing path data.
virtual void arcTo(Coord rx, Coord ry, Coord angle, bool large_arc, bool sweep, Point const &p)=0
Output an elliptical arc segment.
virtual void moveTo(Point const &p)=0
Move to a different point without creating a segment.
Two-dimensional point that doubles as a vector.
Polynomial in canonical (monomial) basis.
Axis aligned, non-empty rectangle.
Rotation around the origin.
Polynomial in symmetric power basis.
Combination of a translation and uniform scale.
Dim2
2D axis enumeration (X or Y).
double Coord
Floating point type used to store coordinates.
Various utility functions.
SBasisN< n > cos(LinearN< n > bo, int k)
void sincos(double angle, double &sin_, double &cos_)
Simultaneously compute a sine and a cosine of the same angle.
std::ostream & operator<<(std::ostream &os, const Bezier &b)
bool are_near_rel(Point const &a, Point const &b, double eps=EPSILON)
Test whether the relative distance between two points is less than some threshold.
void transpose_in_place(std::vector< Intersection< T, T > > &xs)
Coord distanceSq(Point const &p, Rect const &rect)
double atan2(Point const &p)
double angle_between(Line const &l1, Line const &l2)
bool contains(Path const &p, Point const &i, bool evenodd=true)
std::vector< double > solve_reals(const Poly &p)
std::string format_coord_nice(Coord x)
Crossings crossings(Curve const &a, Curve const &b)
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
SBasis L2(D2< SBasis > const &a, unsigned k)
SBasisN< n > sin(LinearN< n > bo, int k)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Point middle_point(LineSegment const &_segment)
callback interface for SVG path data
two-dimensional geometric operators.