Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
bezier-curve.cpp
Go to the documentation of this file.
1/* Bezier curve implementation
2 *
3 * Authors:
4 * MenTaLguY <mental@rydia.net>
5 * Marco Cecchetti <mrcekets at gmail.com>
6 * Krzysztof Kosiński <tweenk.pl@gmail.com>
7 *
8 * Copyright 2007-2009 Authors
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it either under the terms of the GNU Lesser General Public
12 * License version 2.1 as published by the Free Software Foundation
13 * (the "LGPL") or, at your option, under the terms of the Mozilla
14 * Public License Version 1.1 (the "MPL"). If you do not alter this
15 * notice, a recipient may use your version of this file under either
16 * the MPL or the LGPL.
17 *
18 * You should have received a copy of the LGPL along with this library
19 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * You should have received a copy of the MPL along with this library
22 * in the file COPYING-MPL-1.1
23 *
24 * The contents of this file are subject to the Mozilla Public License
25 * Version 1.1 (the "License"); you may not use this file except in
26 * compliance with the License. You may obtain a copy of the License at
27 * http://www.mozilla.org/MPL/
28 *
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
30 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
31 * the specific language governing rights and limitations.
32 */
33
34#include <2geom/bezier-curve.h>
35#include <2geom/path-sink.h>
37#include <2geom/nearest-time.h>
38#include <2geom/polynomial.h>
39
40namespace Geom
41{
42
111BezierCurve::BezierCurve(std::vector<Point> const &pts)
112 : inner(pts)
113{
114 if (pts.size() < 2) {
115 THROW_RANGEERROR("Bezier curve must have at least 2 control points");
116 }
117}
118
120{
121 for (unsigned d = 0; d < 2; ++d) {
122 Coord ic = inner[d][0];
123 for (unsigned i = 1; i < size(); ++i) {
124 if (inner[d][i] != ic) return false;
125 }
126 }
127 return true;
128}
129
132{
133 auto const last_idx = size() - 1;
134 if (last_idx == 1) {
135 return true;
136 }
137 auto const start = controlPoint(0);
138 auto const end = controlPoint(last_idx);
139 for (unsigned i = 1; i < last_idx; ++i) {
140 auto const pi = controlPoint(i);
141 if (pi != start && pi != end) {
142 return false;
143 }
144 }
145 return true;
146}
147
148void BezierCurve::expandToTransformed(Rect &bbox, Affine const &transform) const
149{
150 bbox |= bounds_exact(inner * transform);
151}
152
154{
155 switch (order())
156 {
157 case 0:
158 return 0.0;
159 case 1:
160 return distance(initialPoint(), finalPoint());
161 case 2:
162 {
163 std::vector<Point> pts = controlPoints();
164 return bezier_length(pts[0], pts[1], pts[2], tolerance);
165 }
166 case 3:
167 {
168 std::vector<Point> pts = controlPoints();
169 return bezier_length(pts[0], pts[1], pts[2], pts[3], tolerance);
170 }
171 default:
172 return bezier_length(controlPoints(), tolerance);
173 }
174}
175
176std::vector<CurveIntersection>
177BezierCurve::intersect(Curve const &other, Coord eps) const
178{
179 std::vector<CurveIntersection> result;
180
181 // in case we encounter an order-1 curve created from a vector
182 // or a degenerate elliptical arc
183 if (isLineSegment()) {
185 result = ls.intersect(other);
186 return result;
187 }
188
189 // here we are sure that this curve is at least a quadratic Bezier
190 BezierCurve const *bez = dynamic_cast<BezierCurve const *>(&other);
191 if (bez) {
192 std::vector<std::pair<double, double> > xs;
193 find_intersections(xs, inner, bez->inner, eps);
194 for (auto & i : xs) {
195 CurveIntersection x(*this, other, i.first, i.second);
196 result.push_back(x);
197 }
198 return result;
199 }
200
201 // pass other intersection types to the other curve
202 result = other.intersect(*this, eps);
204 return result;
205}
206
207bool BezierCurve::isNear(Curve const &c, Coord precision) const
208{
209 if (this == &c) return true;
210
211 BezierCurve const *other = dynamic_cast<BezierCurve const *>(&c);
212 if (!other) return false;
213
214 if (!are_near(inner.at0(), other->inner.at0(), precision)) return false;
215 if (!are_near(inner.at1(), other->inner.at1(), precision)) return false;
216
217 if (size() == other->size()) {
218 for (unsigned i = 1; i < order(); ++i) {
219 if (!are_near(inner.point(i), other->inner.point(i), precision)) {
220 return false;
221 }
222 }
223 return true;
224 } else {
225 // Must equalize the degrees before comparing
226 BezierCurve elevated_this, elevated_other;
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();
230
231 if (our_degree < other_degree) {
232 // Elevate our 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) {
236 // Elevate the other's degree
237 elevated_this.inner[dim] = inner[dim];
238 elevated_other.inner[dim] = other->inner[dim].elevate_to_degree(our_degree);
239 } else {
240 // Equal degrees: just copy
241 elevated_this.inner[dim] = inner[dim];
242 elevated_other.inner[dim] = other->inner[dim];
243 }
244 }
245 assert(elevated_other.size() == elevated_this.size());
246 return elevated_this.isNear(elevated_other, precision);
247 }
248}
249
251{
252 if (f == 0.0 && t == 1.0) {
253 return duplicate();
254 }
255 if (f == 1.0 && t == 0.0) {
256 return reverse();
257 }
258 return new BezierCurve(Geom::portion(inner, f, t));
259}
260
261bool BezierCurve::_equalTo(Curve const &c) const
262{
263 if (this == &c) return true;
264 auto other = dynamic_cast<BezierCurve const *>(&c);
265 if (!other) return false;
266
267 if (size() != other->size()) return false;
268
269 for (unsigned i = 0; i < size(); ++i) {
270 if (controlPoint(i) != other->controlPoint(i)) return false;
271 }
272
273 return true;
274}
275
277{
278 return nearest_time(p, inner, from, to);
279}
280
281void BezierCurve::feed(PathSink &sink, bool moveto_initial) const
282{
283 if (size() > 4) {
284 Curve::feed(sink, moveto_initial);
285 return;
286 }
287
288 Point ip = controlPoint(0);
289 if (moveto_initial) {
290 sink.moveTo(ip);
291 }
292 switch (size()) {
293 case 2:
294 sink.lineTo(controlPoint(1));
295 break;
296 case 3:
297 sink.quadTo(controlPoint(1), controlPoint(2));
298 break;
299 case 4:
301 break;
302 default:
303 // TODO: add a path sink method that accepts a vector of control points
304 // and converts to cubic spline by default
305 assert(false);
306 break;
307 }
308}
309
310BezierCurve *BezierCurve::create(std::vector<Point> const &pts)
311{
312 switch (pts.size()) {
313 case 0:
314 case 1:
315 THROW_LOGICALERROR("BezierCurve::create: too few points in vector");
316 return NULL;
317 case 2:
318 return new LineSegment(pts[0], pts[1]);
319 case 3:
320 return new QuadraticBezier(pts[0], pts[1], pts[2]);
321 case 4:
322 return new CubicBezier(pts[0], pts[1], pts[2], pts[3]);
323 default:
324 return new BezierCurve(pts);
325 }
326}
327
331std::vector<Coord> BezierCurve::timesWithRadiusOfCurvature(double radius) const
332{
342 if (this->size() <= 2) {
343 return {};
344 }
345
346 std::vector<Coord> res;
347
348 auto const dx = Geom::derivative(inner[X]);
349 auto const dy = Geom::derivative(inner[Y]);
350 auto const ddx = Geom::derivative(dx);
351 auto const ddy = Geom::derivative(dy);
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();
356 // check which candidates have positive nominator
357 // as squaring also give negative (spurious) results
358 for (Coord const candidate : candidates) {
359 if (c0.valueAt(candidate) > 0) {
360 res.push_back(candidate);
361 }
362 }
363
364 return res;
365}
366
367// optimized specializations for LineSegment
368
369template <>
371 double dx = inner[X][1] - inner[X][0], dy = inner[Y][1] - inner[Y][0];
372 return new BezierCurveN<1>(Point(dx,dy),Point(dx,dy));
373}
374
375template<>
377{
378 using std::swap;
379
380 if ( from > to ) swap(from, to);
381 Point ip = pointAt(from);
382 Point fp = pointAt(to);
383 Point v = fp - ip;
384 Coord l2v = L2sq(v);
385 if (l2v == 0) return 0;
386 Coord t = dot( p - ip, v ) / l2v;
387 if ( t <= 0 ) return from;
388 else if ( t >= 1 ) return to;
389 else return from + t*(to-from);
390}
391
392/* Specialized intersection routine for line segments.
393 *
394 * NOTE: if the segments overlap in part or in full, the function returns the start and end
395 * of the overlapping subsegment as intersections. This behavior is more useful than throwing
396 * Geom::InfinitelyManySolutions.
397 */
398template <>
399std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &other, Coord eps) const
400{
401 std::vector<CurveIntersection> result;
402
403 // only handle intersections with other LineSegments here
404 if (!other.isLineSegment()) {
405 // pass all other types to the other curve
406 result = other.intersect(*this, eps);
408 return result;
409 }
410
411 Point const u = finalPoint() - initialPoint();
412 Point const v = other.initialPoint() - other.finalPoint();
413 if (u.isZero() || v.isZero()) {
414 return {};
415 }
416 Coord const uv = u.length() * v.length();
417 Coord const u_cross_v = cross(u, v);
418 bool const segments_are_parallel = std::abs(u_cross_v) < eps * uv;
419
420 if (segments_are_parallel) {
421 // We check if the segments lie on the same line.
422 Coord const distance_between_lines = std::abs(cross(u.normalized(),
423 other.initialPoint() - initialPoint()));
424 if (distance_between_lines > eps) {
425 // Segments are parallel but aren't part of the same line => no intersections.
426 return {};
427 }
428 // The segments are on the same line, so they may overlap in part or in full.
429 // Look for the times on this segment's line at which the line passes through
430 // the initial and final points of the other segment.
431 Coord const ulen_inverse = 1.0 / u.length();
432 auto const time_of_passage = [&](Point const &point_on_line) -> Coord {
433 return dot(u.normalized(), point_on_line - initialPoint()) * ulen_inverse;
434 };
435 // Find the range of times on our segment where we travel through the other segment.
436 auto time_in_other = Interval(time_of_passage(other.initialPoint()),
437 time_of_passage(other.finalPoint()));
438 Coord const eps_utime = eps * ulen_inverse;
439 if (time_in_other.min() > 1 + eps_utime || time_in_other.max() < -eps_utime) {
440 return {};
441 }
442
443 // Create two intersections, one at each end of the overlap interval.
444 Coord last_time = infinity();
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) {
448 continue;
449 }
450 last_time = t;
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) {
455 continue;
456 }
457 result.emplace_back(t, other_t, middle_point(point, other_pt));
458 }
459 return result;
460 } else {
461 // Segments are not collinear - there is 0 or 1 intersection.
462 // This is the typical case so it's important for it to be fast.
463 Point const w = other.initialPoint() - initialPoint();
464 Coord candidate_time_this = cross(w, v) / u_cross_v;
465
468 auto const is_outside_seg = [eps](Coord t, Coord len) -> bool {
469 Coord const arclen_coord = t * len;
470 return arclen_coord < -eps || arclen_coord > len + eps;
471 };
472
473 if (is_outside_seg(candidate_time_this, u.length())) {
474 return {};
475 }
476
477 Coord candidate_time_othr = cross(u, w) / u_cross_v;
478 if (is_outside_seg(candidate_time_othr, v.length())) {
479 return {};
480 }
481
482 // Even if there was some fuzz in the interval, we must now restrict to [0, 1] and verify
483 // that the intersection found is still within epsilon precision (the fuzz may have been too
484 // large, depending on the angle between the intervals).
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);
487
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) {
491 return {};
492 }
493 return { CurveIntersection(candidate_time_this, candidate_time_othr, middle_point(pt_this, pt_othr)) };
494 }
495}
496
508template <unsigned degree>
509static std::vector<CurveIntersection> bezier_line_intersections(BezierCurveN<degree> const &curve, Line const &line)
510{
511 static_assert(degree == 2 || degree == 3, "bezier_line_intersections<degree>() error: degree must be 2 or 3.");
512
513 auto const length = distance(line.initialPoint(), line.finalPoint());
514 if (length == 0) {
515 return {};
516 }
517 std::vector<CurveIntersection> result;
518
519 // Find the isometry mapping the line to the x-axis, taking the initial point to the origin
520 // and the final point to (length, 0). Apply this transformation to the Bézier curve and
521 // extract the y-coordinate polynomial.
522 auto const transform = line.rotationToZero(Y);
523 Bezier const y = (curve.fragment() * transform)[Y];
524 std::vector<double> roots;
525
526 // Find roots of the polynomial y.
527 {
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];
531
532 if constexpr (degree == 2) {
533 roots = solve_quadratic(c2, 2.0 * c1, c0);
534 } else if constexpr (degree == 3) {
535 double const c3 = y[3] - y[0] + 3.0 * (y[1] - y[2]);
536 roots = solve_cubic(c3, 3.0 * c2, 3.0 * c1 , c0);
537 }
538 }
539
540 // Filter the roots and assemble intersections.
541 for (double root : roots) {
542 if (root < 0.0 || root > 1.0) {
543 continue;
544 }
545 Coord x = (curve.pointAt(root) * transform)[X];
546 if (x < 0.0 || x > length) {
547 continue;
548 }
549 result.emplace_back(curve, line, root, x / length);
550 }
551 return result;
552}
553
554/* Specialized intersection routine for quadratic Bézier curves. */
555template <>
556std::vector<CurveIntersection> BezierCurveN<2>::intersect(Curve const &other, Coord eps) const
557{
558 if (auto other_bezier = dynamic_cast<BezierCurve const *>(&other)) {
559 auto const other_degree = other_bezier->order();
560 if (other_degree == 1) {
561 // Use the exact method to intersect a quadratic Bézier with a line segment.
562 auto line = Line(other_bezier->initialPoint(), other_bezier->finalPoint());
563 return bezier_line_intersections<2>(*this, line);
564 }
565 // TODO: implement exact intersection of two quadratic Béziers using the method of resultants.
566 }
567 return BezierCurve::intersect(other, eps);
568}
569
570/* Specialized intersection routine for cubic Bézier curves. */
571template <>
572std::vector<CurveIntersection> BezierCurveN<3>::intersect(Curve const &other, Coord eps) const
573{
574 if (auto other_bezier = dynamic_cast<BezierCurve const *>(&other)) {
575 if (other_bezier->order() == 1) {
576 // Use the exact method to intersect a cubic Bézier with a line segment.
577 auto line = Line(other_bezier->initialPoint(), other_bezier->finalPoint());
578 return bezier_line_intersections<3>(*this, line);
579 }
580 }
581 return BezierCurve::intersect(other, eps);
582}
583
584template <>
586{
587 Point ip = inner.at0(), fp = inner.at1();
588 if (p[Y] == std::max(ip[Y], fp[Y])) return 0;
589
590 Point v = fp - ip;
591 assert(v[Y] != 0);
592 Coord t = (p[Y] - ip[Y]) / v[Y];
593 assert(t >= 0 && t <= 1);
594 Coord xcross = lerp(t, ip[X], fp[X]);
595 if (xcross > p[X]) {
596 return v[Y] > 0 ? 1 : -1;
597 }
598 return 0;
599}
600
601template <>
602void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const
603{
604 if (moveto_initial) {
605 sink.moveTo(controlPoint(0));
606 }
607 sink.lineTo(controlPoint(1));
608}
609
610template <>
611void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const
612{
613 if (moveto_initial) {
614 sink.moveTo(controlPoint(0));
615 }
616 sink.quadTo(controlPoint(1), controlPoint(2));
617}
618
619template <>
620void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const
621{
622 if (moveto_initial) {
623 sink.moveTo(controlPoint(0));
624 }
625 sink.curveTo(controlPoint(1), controlPoint(2), controlPoint(3));
626}
627
628static void bezier_expand_to_image(Rect &range, Point const &x0, Point const &x1, Point const &x2)
629{
630 for (auto i : { X, Y }) {
631 bezier_expand_to_image(range[i], x0[i], x1[i], x2[i]);
632 }
633}
634
635static void bezier_expand_to_image(Rect &range, Point const &x0, Point const &x1, Point const &x2, Point const &x3)
636{
637 for (auto i : { X, Y }) {
638 bezier_expand_to_image(range[i], x0[i], x1[i], x2[i], x3[i]);
639 }
640}
641
642template <>
643void BezierCurveN<1>::expandToTransformed(Rect &bbox, Affine const &transform) const
644{
645 bbox.expandTo(finalPoint() * transform);
646}
647
648template <>
649void BezierCurveN<2>::expandToTransformed(Rect &bbox, Affine const &transform) const
650{
651 bezier_expand_to_image(bbox, controlPoint(0) * transform,
652 controlPoint(1) * transform,
653 controlPoint(2) * transform);
654}
655
656template <>
657void BezierCurveN<3>::expandToTransformed(Rect &bbox, Affine const &transform) const
658{
659 bezier_expand_to_image(bbox, controlPoint(0) * transform,
660 controlPoint(1) * transform,
661 controlPoint(2) * transform,
662 controlPoint(3) * transform);
663}
664
665static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance, int level)
666{
667 /* The Bezier length algorithm used in 2Geom utilizes a simple fact:
668 * the Bezier curve is longer than the distance between its endpoints
669 * but shorter than the length of the polyline formed by its control
670 * points. When the difference between the two values is smaller than the
671 * error tolerance, we can be sure that the true value is no further than
672 * 0.5 * tolerance from their arithmetic mean. When it's larger, we recursively
673 * subdivide the Bezier curve into two parts and add their lengths.
674 *
675 * We cap the maximum number of subdivisions at 256, which corresponds to 8 levels.
676 */
677 Coord lower = distance(v1.front(), v1.back());
678 Coord upper = 0.0;
679 for (size_t i = 0; i < v1.size() - 1; ++i) {
680 upper += distance(v1[i], v1[i+1]);
681 }
682 if (upper - lower <= 2*tolerance || level >= 8) {
683 return (lower + upper) / 2;
684 }
685
686
687 std::vector<Point> v2 = v1;
688
689 /* Compute the right subdivision directly in v1 and the left one in v2.
690 * Explanation of the algorithm used:
691 * We have to compute the left and right edges of this triangle in which
692 * the top row are the control points of the Bezier curve, and each cell
693 * is equal to the arithmetic mean of the cells directly above it
694 * to the right and left. This corresponds to subdividing the Bezier curve
695 * at time value 0.5: the left edge has the control points of the first
696 * portion of the Bezier curve and the right edge - the second one.
697 * In the example we subdivide a curve with 5 control points (order 4).
698 *
699 * Start:
700 * 0 1 2 3 4
701 * ? ? ? ?
702 * ? ? ?
703 * ? ?
704 * ?
705 * # means we have overwritten the value, ? means we don't know
706 * the value yet. Numbers mean the value is at i-th position in the vector.
707 *
708 * After loop with i==1
709 * # 1 2 3 4
710 * 0 ? ? ? -> write 0 to v2[1]
711 * ? ? ?
712 * ? ?
713 * ?
714 *
715 * After loop with i==2
716 * # # 2 3 4
717 * # 1 ? ?
718 * 0 ? ? -> write 0 to v2[2]
719 * ? ?
720 * ?
721 *
722 * After loop with i==3
723 * # # # 3 4
724 * # # 2 ?
725 * # 1 ?
726 * 0 ? -> write 0 to v2[3]
727 * ?
728 *
729 * After loop with i==4, we have the right edge of the triangle in v1,
730 * and we write the last value needed for the left edge in v2[4].
731 */
732
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]);
736 }
737 v2[i] = v1[0];
738 }
739
740 return bezier_length_internal(v1, 0.5 * tolerance, level + 1) +
741 bezier_length_internal(v2, 0.5 * tolerance, level + 1);
742}
743
746Coord bezier_length(std::vector<Point> const &points, Coord tolerance)
747{
748 if (points.size() < 2) return 0.0;
749 std::vector<Point> v1 = points;
750 return bezier_length_internal(v1, tolerance, 0);
751}
752
753static Coord bezier_length_internal(Point a0, Point a1, Point a2, Coord tolerance, int level)
754{
755 Coord lower = distance(a0, a2);
756 Coord upper = distance(a0, a1) + distance(a1, a2);
757
758 if (upper - lower <= 2*tolerance || level >= 8) {
759 return (lower + upper) / 2;
760 }
761
762 Point // Casteljau subdivision
763 // b0 = a0,
764 // c0 = a2,
765 b1 = 0.5*(a0 + a1),
766 c1 = 0.5*(a1 + a2),
767 b2 = 0.5*(b1 + c1); // == c2
768 return bezier_length_internal(a0, b1, b2, 0.5 * tolerance, level + 1) +
769 bezier_length_internal(b2, c1, a2, 0.5 * tolerance, level + 1);
770}
771
774Coord bezier_length(Point a0, Point a1, Point a2, Coord tolerance)
775{
776 return bezier_length_internal(a0, a1, a2, tolerance, 0);
777}
778
779static Coord bezier_length_internal(Point a0, Point a1, Point a2, Point a3, Coord tolerance, int level)
780{
781 Coord lower = distance(a0, a3);
782 Coord upper = distance(a0, a1) + distance(a1, a2) + distance(a2, a3);
783
784 if (upper - lower <= 2*tolerance || level >= 8) {
785 return (lower + upper) / 2;
786 }
787
788 Point // Casteljau subdivision
789 // b0 = a0,
790 // c0 = a3,
791 b1 = 0.5*(a0 + a1),
792 t0 = 0.5*(a1 + a2),
793 c1 = 0.5*(a2 + a3),
794 b2 = 0.5*(b1 + t0),
795 c2 = 0.5*(t0 + c1),
796 b3 = 0.5*(b2 + c2); // == c3
797 return bezier_length_internal(a0, b1, b2, b3, 0.5 * tolerance, level + 1) +
798 bezier_length_internal(b3, c2, c1, a3, 0.5 * tolerance, level + 1);
799}
800
803Coord bezier_length(Point a0, Point a1, Point a2, Point a3, Coord tolerance)
804{
805 return bezier_length_internal(a0, a1, a2, a3, tolerance, 0);
806}
807
808} // end namespace Geom
809
810/*
811 Local Variables:
812 mode:c++
813 c-file-style:"stroustrup"
814 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
815 indent-tabs-mode:nil
816 fill-column:99
817 End:
818*/
819// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Basic intersection routines.
Bezier curve.
3x3 matrix representing an affine transformation.
Definition affine.h:70
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.
D2< Bezier > inner
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.
Definition bezier.h:126
Abstract continuous curve on a plane defined on [0,1].
Definition curve.h:78
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.
Definition curve.cpp:214
virtual std::vector< CurveIntersection > intersect(Curve const &other, Coord eps=EPSILON) const
Compute intersections with another curve.
Definition curve.cpp:97
virtual bool isLineSegment() const
Check whether the curve is a line segment.
Definition curve.h:98
virtual Point pointAt(Coord t) const
Evaluate the curve at a specified time value.
Definition curve.h:110
void transform(Affine const &m)
Transform this curve by an affine transformation.
Definition curve.h:193
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.
Definition interval.h:59
Infinite line on a plane.
Definition line.h:53
Affine rotationToZero(Dim2 d) const
Compute an affine which transforms all points on the line to zero X or Y coordinate.
Definition line.h:350
Point finalPoint() const
Definition line.h:228
Point initialPoint() const
Definition line.h:225
Callback interface for processing path data.
Definition path-sink.h:56
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.
Definition point.h:66
constexpr bool isZero() const
Check whether both coordinates are zero.
Definition point.h:227
Point normalized() const
Definition point.h:121
Coord length() const
Compute the distance from origin.
Definition point.h:118
Axis aligned, non-empty rectangle.
Definition rect.h:92
const double w
Definition conic-4.cpp:19
double inner(valarray< double > const &x, valarray< double > const &y)
RootCluster root
Css & result
double c[8][4]
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.
Definition coord.h:97
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Geom::Point start
Geom::Point end
Various utility functions.
Definition affine.h:22
Coord length(LineSegment const &seg)
Intersection CurveIntersection
Definition curve.h:54
OptInterval bounds_exact(Bezier const &b)
Definition bezier.cpp:310
void transpose_in_place(std::vector< Intersection< T, T > > &xs)
Coord nearest_time(Point const &p, Curve const &c)
Definition curve.h:354
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
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)
Definition bezier.cpp:250
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
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)
Definition d2.h:355
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...
Definition bezier.cpp:347
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.
auto len
Definition safe-printf.h:21
size_t degree
Definition curve.h:24