114 if (pts.size() < 2) {
115 THROW_RANGEERROR(
"Bezier curve must have at least 2 control points");
121 for (
unsigned d = 0; d < 2; ++d) {
123 for (
unsigned i = 1; i <
size(); ++i) {
124 if (
inner[d][i] != ic)
return false;
133 auto const last_idx =
size() - 1;
139 for (
unsigned i = 1; i < last_idx; ++i) {
169 return bezier_length(pts[0], pts[1], pts[2], pts[3], tolerance);
176std::vector<CurveIntersection>
179 std::vector<CurveIntersection>
result;
192 std::vector<std::pair<double, double> > xs;
194 for (
auto & i : xs) {
209 if (
this == &
c)
return true;
212 if (!other)
return false;
218 for (
unsigned i = 1; i <
order(); ++i) {
227 for (
size_t dim = 0; dim < 2; dim++) {
228 unsigned const our_degree =
inner[dim].degree();
229 unsigned const other_degree = other->
inner[dim].degree();
231 if (our_degree < other_degree) {
233 elevated_this.
inner[dim] =
inner[dim].elevate_to_degree(other_degree);
234 elevated_other.
inner[dim] = other->
inner[dim];
235 }
else if (our_degree > other_degree) {
238 elevated_other.
inner[dim] = other->
inner[dim].elevate_to_degree(our_degree);
242 elevated_other.
inner[dim] = other->
inner[dim];
245 assert(elevated_other.
size() == elevated_this.
size());
246 return elevated_this.
isNear(elevated_other, precision);
252 if (f == 0.0 && t == 1.0) {
255 if (f == 1.0 && t == 0.0) {
263 if (
this == &
c)
return true;
265 if (!other)
return false;
267 if (
size() != other->size())
return false;
269 for (
unsigned i = 0; i <
size(); ++i) {
270 if (
controlPoint(i) != other->controlPoint(i))
return false;
289 if (moveto_initial) {
312 switch (pts.size()) {
315 THROW_LOGICALERROR(
"BezierCurve::create: too few points in vector");
322 return new CubicBezier(pts[0], pts[1], pts[2], pts[3]);
342 if (this->
size() <= 2) {
346 std::vector<Coord> res;
352 auto const c0 = (dx * ddy - ddx * dy) * radius;
353 auto const c1 = dx * dx + dy * dy;
354 auto const p = c0 * c0 - c1 * c1 * c1;
355 auto const candidates = p.roots();
358 for (
Coord const candidate : candidates) {
359 if (c0.valueAt(candidate) > 0) {
360 res.push_back(candidate);
380 if ( from > to ) swap(from, to);
381 Point ip = pointAt(from);
382 Point fp = pointAt(to);
385 if (l2v == 0)
return 0;
387 if ( t <= 0 )
return from;
388 else if ( t >= 1 )
return to;
389 else return from + t*(to-from);
401 std::vector<CurveIntersection>
result;
411 Point const u = finalPoint() - initialPoint();
413 if (u.
isZero() || v.isZero()) {
418 bool const segments_are_parallel = std::abs(u_cross_v) < eps * uv;
420 if (segments_are_parallel) {
424 if (distance_between_lines > eps) {
432 auto const time_of_passage = [&](
Point const &point_on_line) ->
Coord {
433 return dot(u.
normalized(), point_on_line - initialPoint()) * ulen_inverse;
438 Coord const eps_utime = eps * ulen_inverse;
439 if (time_in_other.min() > 1 + eps_utime || time_in_other.max() < -eps_utime) {
445 for (
Coord t : {time_in_other.min(), time_in_other.max()}) {
446 t = std::clamp(t, 0.0, 1.0);
447 if (t == last_time) {
451 auto const point = pointAt(t);
452 Coord const other_t = std::clamp(
dot(v.normalized(), other.
initialPoint() - point) / v.length(), 0.0, 1.0);
453 auto const other_pt = other.
pointAt(other_t);
454 if (
distance(point, other_pt) > eps) {
464 Coord candidate_time_this =
cross(
w, v) / u_cross_v;
468 auto const is_outside_seg = [eps](
Coord t,
Coord len) ->
bool {
470 return arclen_coord < -eps || arclen_coord >
len + eps;
473 if (is_outside_seg(candidate_time_this, u.
length())) {
477 Coord candidate_time_othr =
cross(u,
w) / u_cross_v;
478 if (is_outside_seg(candidate_time_othr, v.length())) {
485 candidate_time_this = std::clamp(candidate_time_this, 0.0, 1.0);
486 candidate_time_othr = std::clamp(candidate_time_othr, 0.0, 1.0);
488 auto const pt_this = pointAt(candidate_time_this);
489 auto const pt_othr = other.
pointAt(candidate_time_othr);
490 if (
distance(pt_this, pt_othr) > eps) {
508template <
unsigned degree>
511 static_assert(
degree == 2 ||
degree == 3,
"bezier_line_intersections<degree>() error: degree must be 2 or 3.");
517 std::vector<CurveIntersection>
result;
524 std::vector<double>
roots;
528 double const c2 = y[0] + y[2] - 2.0 * y[1];
529 double const c1 = y[1] - y[0];
530 double const c0 = y[0];
532 if constexpr (
degree == 2) {
534 }
else if constexpr (
degree == 3) {
535 double const c3 = y[3] - y[0] + 3.0 * (y[1] - y[2]);
542 if (root < 0.0 || root > 1.0) {
546 if (x < 0.0 || x >
length) {
558 if (
auto other_bezier =
dynamic_cast<BezierCurve const *
>(&other)) {
559 auto const other_degree = other_bezier->order();
560 if (other_degree == 1) {
562 auto line =
Line(other_bezier->initialPoint(), other_bezier->finalPoint());
563 return bezier_line_intersections<2>(*
this, line);
574 if (
auto other_bezier =
dynamic_cast<BezierCurve const *
>(&other)) {
575 if (other_bezier->order() == 1) {
577 auto line =
Line(other_bezier->initialPoint(), other_bezier->finalPoint());
578 return bezier_line_intersections<3>(*
this, line);
588 if (p[
Y] == std::max(ip[
Y], fp[
Y]))
return 0;
593 assert(t >= 0 && t <= 1);
596 return v[
Y] > 0 ? 1 : -1;
604 if (moveto_initial) {
605 sink.
moveTo(controlPoint(0));
607 sink.
lineTo(controlPoint(1));
613 if (moveto_initial) {
614 sink.
moveTo(controlPoint(0));
616 sink.
quadTo(controlPoint(1), controlPoint(2));
622 if (moveto_initial) {
623 sink.
moveTo(controlPoint(0));
625 sink.
curveTo(controlPoint(1), controlPoint(2), controlPoint(3));
630 for (
auto i : {
X,
Y }) {
637 for (
auto i : {
X,
Y }) {
645 bbox.
expandTo(finalPoint() * transform);
652 controlPoint(1) * transform,
653 controlPoint(2) * transform);
660 controlPoint(1) * transform,
661 controlPoint(2) * transform,
662 controlPoint(3) * transform);
679 for (
size_t i = 0; i < v1.size() - 1; ++i) {
682 if (upper - lower <= 2*tolerance || level >= 8) {
683 return (lower + upper) / 2;
687 std::vector<Point> v2 = v1;
733 for (
size_t i = 1; i < v1.size(); ++i) {
734 for (
size_t j = i; j > 0; --j) {
735 v1[j-1] = 0.5 * (v1[j-1] + v1[j]);
748 if (points.size() < 2)
return 0.0;
749 std::vector<Point> v1 = points;
758 if (upper - lower <= 2*tolerance || level >= 8) {
759 return (lower + upper) / 2;
784 if (upper - lower <= 2*tolerance || level >= 8) {
785 return (lower + upper) / 2;
Basic intersection routines.
3x3 matrix representing an affine transformation.
void feed(PathSink &sink, bool moveto_initial) const override
Feed the curve to a PathSink.
std::vector< CurveIntersection > intersect(Curve const &other, Coord eps=EPSILON) const override
Compute intersections with another curve.
int winding(Point const &p) const override
Compute the partial winding number of this 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.
void expandToTransformed(Rect &bbox, Affine const &transform) const override
Expand the given rectangle to include the transformed curve, assuming it already contains its initial...
Two-dimensional Bezier curve of arbitrary order.
Curve * duplicate() const override
Create an exact copy of this curve.
bool isLineSegment() const override
Return false if there are at least 3 distinct control points, true otherwise.
bool isDegenerate() const override
Check whether the curve has exactly zero length.
std::vector< Coord > timesWithRadiusOfCurvature(double radius) const
Computes the times where the radius of curvature of the bezier curve equals the given radius.
unsigned size() const
Get the number of control points.
Coord bezier_length(std::vector< Point > const &points, Coord tolerance)
Compute the length of a bezier curve given by a vector of its control points.
Coord length(Coord tolerance) const override
Compute the arc length of this curve.
bool _equalTo(Curve const &c) const override
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.
Point finalPoint() const override
Retrieve the end of the curve.
bool isNear(Curve const &c, Coord precision) const override
Test whether two curves are approximately the same.
void feed(PathSink &sink, bool) const override
Feed the curve to a PathSink.
Point initialPoint() const override
Retrieve the start of the curve.
std::vector< CurveIntersection > intersect(Curve const &other, Coord eps=EPSILON) const override
Compute intersections with another curve.
Curve * reverse() const override
Create a reversed version of this curve.
Point controlPoint(unsigned ix) const
Access control points of the curve.
std::vector< Point > controlPoints() const
Get the control points.
Curve * portion(Coord f, Coord t) const override
Create a curve that corresponds to a part of this curve.
unsigned order() const
Get the order of the Bezier curve.
static BezierCurve * create(std::vector< Point > const &pts)
Construct a curve from a vector of control points.
void expandToTransformed(Rect &bbox, Affine const &transform) const override
Expand the given rectangle to include the transformed curve, assuming it already contains its initial...
Polynomial in Bernstein-Bezier basis.
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 void feed(PathSink &sink, bool moveto_initial) const
Feed the curve to a PathSink.
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.
virtual Point pointAt(Coord t) const
Evaluate the curve at a specified time value.
void transform(Affine const &m)
Transform this curve by an affine transformation.
void expandTo(CPoint const &p)
Enlarge the rectangle to contain the given point.
Intersection between two shapes.
Range of real numbers that is never empty.
Infinite line on a plane.
Affine rotationToZero(Dim2 d) const
Compute an affine which transforms all points on the line to zero X or Y coordinate.
Point initialPoint() const
Callback interface for processing path data.
virtual void lineTo(Point const &p)=0
Output a line segment.
virtual void quadTo(Point const &c, Point const &p)=0
Output a cubic Bezier segment.
virtual void curveTo(Point const &c0, Point const &c1, Point const &p)=0
Output a quadratic Bezier 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.
constexpr bool isZero() const
Check whether both coordinates are zero.
Coord length() const
Compute the distance from origin.
Axis aligned, non-empty rectangle.
double inner(valarray< double > const &x, valarray< double > const &y)
BezierCurveN< 3 > CubicBezier
Cubic (order 3) Bezier curve.
BezierCurveN< 1 > LineSegment
Line segment.
BezierCurveN< 2 > QuadraticBezier
Quadratic (order 2) Bezier curve.
constexpr Coord lerp(Coord t, Coord a, Coord b)
Numerically stable linear interpolation.
constexpr Coord infinity()
Get a value representing infinity.
double Coord
Floating point type used to store coordinates.
Various utility functions.
Coord length(LineSegment const &seg)
Intersection CurveIntersection
OptInterval bounds_exact(Bezier const &b)
void transpose_in_place(std::vector< Intersection< T, T > > &xs)
Coord nearest_time(Point const &p, Curve const &c)
Angle distance(Angle const &a, Angle const &b)
Coord bezier_length(std::vector< Point > const &points, Coord tolerance=0.01)
Compute the length of a bezier curve given by a vector of its control points.
std::vector< Coord > solve_quadratic(Coord a, Coord b, Coord c)
Analytically solve quadratic equation.
std::vector< Coord > solve_cubic(Coord a, Coord b, Coord c, Coord d)
Analytically solve cubic equation.
std::vector< double > roots(SBasis const &s)
void find_intersections(std::vector< std::pair< double, double > > &xs, D2< Bezier > const &A, D2< Bezier > const &B, double precision=EPSILON)
Bezier portion(const Bezier &a, double from, double to)
Bezier derivative(Bezier const &a)
static Coord bezier_length_internal(std::vector< Point > &v1, Coord tolerance, int level)
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
T dot(D2< T > const &a, D2< T > const &b)
static std::vector< CurveIntersection > bezier_line_intersections(BezierCurveN< degree > const &curve, Line const &line)
Find intersections of a low-degree Bézier curve with a line segment.
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
void bezier_expand_to_image(Interval &range, Coord x0, Coord x1, Coord x2)
Expand an interval to the image of a Bézier-Bernstein polynomial, assuming it already contains the in...
Point middle_point(LineSegment const &_segment)
Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>>
callback interface for SVG path data
Polynomial in canonical (monomial) basis.