Skip to main content

lyon_geom/
quadratic_bezier.rs

1use crate::scalar::Scalar;
2use crate::segment::Segment;
3use crate::traits::Transformation;
4use crate::{point, Box2D, Point, Vector};
5use crate::{CubicBezierSegment, Line, LineEquation, LineSegment, Triangle};
6use arrayvec::ArrayVec;
7use num_traits::NumCast;
8
9use core::mem;
10use core::ops::Range;
11
12/// A 2d curve segment defined by three points: the beginning of the segment, a control
13/// point and the end of the segment.
14///
15/// The curve is defined by equation:
16/// ```∀ t ∈ [0..1],  P(t) = (1 - t)² * from + 2 * (1 - t) * t * ctrl + t² * to```
17#[derive(Copy, Clone, Debug, PartialEq)]
18#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
19pub struct QuadraticBezierSegment<S> {
20    pub from: Point<S>,
21    pub ctrl: Point<S>,
22    pub to: Point<S>,
23}
24
25impl<S: Scalar> QuadraticBezierSegment<S> {
26    pub fn cast<NewS: NumCast>(self) -> QuadraticBezierSegment<NewS> {
27        QuadraticBezierSegment {
28            from: self.from.cast(),
29            ctrl: self.ctrl.cast(),
30            to: self.to.cast(),
31        }
32    }
33
34    /// Sample the curve at t (expecting t between 0 and 1).
35    pub fn sample(&self, t: S) -> Point<S> {
36        let t2 = t * t;
37        let one_t = S::ONE - t;
38        let one_t2 = one_t * one_t;
39
40        self.from * one_t2 + self.ctrl.to_vector() * S::TWO * one_t * t + self.to.to_vector() * t2
41    }
42
43    /// Sample the x coordinate of the curve at t (expecting t between 0 and 1).
44    pub fn x(&self, t: S) -> S {
45        let t2 = t * t;
46        let one_t = S::ONE - t;
47        let one_t2 = one_t * one_t;
48
49        self.from.x * one_t2 + self.ctrl.x * S::TWO * one_t * t + self.to.x * t2
50    }
51
52    /// Sample the y coordinate of the curve at t (expecting t between 0 and 1).
53    pub fn y(&self, t: S) -> S {
54        let t2 = t * t;
55        let one_t = S::ONE - t;
56        let one_t2 = one_t * one_t;
57
58        self.from.y * one_t2 + self.ctrl.y * S::TWO * one_t * t + self.to.y * t2
59    }
60
61    #[inline]
62    fn derivative_coefficients(&self, t: S) -> (S, S, S) {
63        (S::TWO * t - S::TWO, -S::FOUR * t + S::TWO, S::TWO * t)
64    }
65
66    /// Sample the curve's derivative at t (expecting t between 0 and 1).
67    pub fn derivative(&self, t: S) -> Vector<S> {
68        let (c0, c1, c2) = self.derivative_coefficients(t);
69        self.from.to_vector() * c0 + self.ctrl.to_vector() * c1 + self.to.to_vector() * c2
70    }
71
72    /// Sample the x coordinate of the curve's derivative at t (expecting t between 0 and 1).
73    pub fn dx(&self, t: S) -> S {
74        let (c0, c1, c2) = self.derivative_coefficients(t);
75        self.from.x * c0 + self.ctrl.x * c1 + self.to.x * c2
76    }
77
78    /// Sample the y coordinate of the curve's derivative at t (expecting t between 0 and 1).
79    pub fn dy(&self, t: S) -> S {
80        let (c0, c1, c2) = self.derivative_coefficients(t);
81        self.from.y * c0 + self.ctrl.y * c1 + self.to.y * c2
82    }
83
84    /// Swap the beginning and the end of the segment.
85    pub fn flip(&self) -> Self {
86        QuadraticBezierSegment {
87            from: self.to,
88            ctrl: self.ctrl,
89            to: self.from,
90        }
91    }
92
93    /// Find the advancement of the y-most position in the curve.
94    ///
95    /// This returns the advancement along the curve, not the actual y position.
96    pub fn y_maximum_t(&self) -> S {
97        if let Some(t) = self.local_y_extremum_t() {
98            let y = self.y(t);
99            if y > self.from.y && y > self.to.y {
100                return t;
101            }
102        }
103
104        if self.from.y > self.to.y {
105            S::ZERO
106        } else {
107            S::ONE
108        }
109    }
110
111    /// Find the advancement of the y-least position in the curve.
112    ///
113    /// This returns the advancement along the curve, not the actual y position.
114    pub fn y_minimum_t(&self) -> S {
115        if let Some(t) = self.local_y_extremum_t() {
116            let y = self.y(t);
117            if y < self.from.y && y < self.to.y {
118                return t;
119            }
120        }
121
122        if self.from.y < self.to.y {
123            S::ZERO
124        } else {
125            S::ONE
126        }
127    }
128
129    /// Return the y inflection point or None if this curve is y-monotonic.
130    pub fn local_y_extremum_t(&self) -> Option<S> {
131        let div = self.from.y - S::TWO * self.ctrl.y + self.to.y;
132        if div == S::ZERO {
133            return None;
134        }
135        let t = (self.from.y - self.ctrl.y) / div;
136        if t > S::ZERO && t < S::ONE {
137            return Some(t);
138        }
139
140        None
141    }
142
143    /// Find the advancement of the x-most position in the curve.
144    ///
145    /// This returns the advancement along the curve, not the actual x position.
146    pub fn x_maximum_t(&self) -> S {
147        if let Some(t) = self.local_x_extremum_t() {
148            let x = self.x(t);
149            if x > self.from.x && x > self.to.x {
150                return t;
151            }
152        }
153
154        if self.from.x > self.to.x {
155            S::ZERO
156        } else {
157            S::ONE
158        }
159    }
160
161    /// Find the advancement of the x-least position in the curve.
162    ///
163    /// This returns the advancement along the curve, not the actual x position.
164    pub fn x_minimum_t(&self) -> S {
165        if let Some(t) = self.local_x_extremum_t() {
166            let x = self.x(t);
167            if x < self.from.x && x < self.to.x {
168                return t;
169            }
170        }
171
172        if self.from.x < self.to.x {
173            S::ZERO
174        } else {
175            S::ONE
176        }
177    }
178
179    /// Return the x inflection point or None if this curve is x-monotonic.
180    pub fn local_x_extremum_t(&self) -> Option<S> {
181        let div = self.from.x - S::TWO * self.ctrl.x + self.to.x;
182        if div == S::ZERO {
183            return None;
184        }
185        let t = (self.from.x - self.ctrl.x) / div;
186        if t > S::ZERO && t < S::ONE {
187            return Some(t);
188        }
189
190        None
191    }
192
193    /// Return the sub-curve inside a given range of t.
194    ///
195    /// This is equivalent splitting at the range's end points.
196    pub fn split_range(&self, t_range: Range<S>) -> Self {
197        let t0 = t_range.start;
198        let t1 = t_range.end;
199
200        let from = self.sample(t0);
201        let to = self.sample(t1);
202        let ctrl = from + (self.ctrl - self.from).lerp(self.to - self.ctrl, t0) * (t1 - t0);
203
204        QuadraticBezierSegment { from, ctrl, to }
205    }
206
207    /// Split this curve into two sub-curves.
208    pub fn split(&self, t: S) -> (QuadraticBezierSegment<S>, QuadraticBezierSegment<S>) {
209        let split_point = self.sample(t);
210
211        (
212            QuadraticBezierSegment {
213                from: self.from,
214                ctrl: self.from.lerp(self.ctrl, t),
215                to: split_point,
216            },
217            QuadraticBezierSegment {
218                from: split_point,
219                ctrl: self.ctrl.lerp(self.to, t),
220                to: self.to,
221            },
222        )
223    }
224
225    /// Return the curve before the split point.
226    pub fn before_split(&self, t: S) -> QuadraticBezierSegment<S> {
227        QuadraticBezierSegment {
228            from: self.from,
229            ctrl: self.from.lerp(self.ctrl, t),
230            to: self.sample(t),
231        }
232    }
233
234    /// Return the curve after the split point.
235    pub fn after_split(&self, t: S) -> QuadraticBezierSegment<S> {
236        QuadraticBezierSegment {
237            from: self.sample(t),
238            ctrl: self.ctrl.lerp(self.to, t),
239            to: self.to,
240        }
241    }
242
243    /// Elevate this curve to a third order bézier.
244    pub fn to_cubic(&self) -> CubicBezierSegment<S> {
245        CubicBezierSegment {
246            from: self.from,
247            ctrl1: (self.from + self.ctrl.to_vector() * S::TWO) / S::THREE,
248            ctrl2: (self.to + self.ctrl.to_vector() * S::TWO) / S::THREE,
249            to: self.to,
250        }
251    }
252
253    #[inline]
254    pub fn baseline(&self) -> LineSegment<S> {
255        LineSegment {
256            from: self.from,
257            to: self.to,
258        }
259    }
260
261    /// Returns whether the curve can be approximated with a single point, given
262    /// a tolerance threshold.
263    pub fn is_a_point(&self, tolerance: S) -> bool {
264        let tol2 = tolerance * tolerance;
265        (self.from - self.to).square_length() <= tol2
266            && (self.from - self.ctrl).square_length() <= tol2
267    }
268
269    /// Returns true if the curve can be approximated with a single line segment
270    /// given a tolerance threshold.
271    pub fn is_linear(&self, tolerance: S) -> bool {
272        if self.from == self.to {
273            return true;
274        }
275
276        let d = self
277            .baseline()
278            .to_line()
279            .square_distance_to_point(self.ctrl);
280
281        d <= (tolerance * tolerance * S::FOUR)
282    }
283
284    /// Computes a "fat line" of this segment.
285    ///
286    /// A fat line is two conservative lines between which the segment
287    /// is fully contained.
288    pub fn fat_line(&self) -> (LineEquation<S>, LineEquation<S>) {
289        let l1 = self.baseline().to_line().equation();
290        let d = S::HALF * l1.signed_distance_to_point(&self.ctrl);
291        let l2 = l1.offset(d);
292        if d >= S::ZERO {
293            (l1, l2)
294        } else {
295            (l2, l1)
296        }
297    }
298
299    /// Applies the transform to this curve and returns the results.
300    #[inline]
301    pub fn transformed<T: Transformation<S>>(&self, transform: &T) -> Self {
302        QuadraticBezierSegment {
303            from: transform.transform_point(self.from),
304            ctrl: transform.transform_point(self.ctrl),
305            to: transform.transform_point(self.to),
306        }
307    }
308
309    /// Find the interval of the beginning of the curve that can be approximated with a
310    /// line segment.
311    pub fn flattening_step(&self, tolerance: S) -> S {
312        let v1 = self.ctrl - self.from;
313        let v2 = self.to - self.from;
314
315        let v1_cross_v2 = v2.x * v1.y - v2.y * v1.x;
316        let h = S::sqrt(v1.x * v1.x + v1.y * v1.y);
317
318        if S::abs(v1_cross_v2 * h) <= S::EPSILON {
319            return S::ONE;
320        }
321
322        let s2inv = h / v1_cross_v2;
323
324        let t = S::TWO * S::sqrt(tolerance * S::abs(s2inv) / S::THREE);
325
326        if t > S::ONE {
327            return S::ONE;
328        }
329
330        t
331    }
332
333    /// Approximates the curve with sequence of line segments.
334    ///
335    /// The `tolerance` parameter defines the maximum distance between the curve and
336    /// its approximation.
337    pub fn for_each_flattened<F>(&self, tolerance: S, callback: &mut F)
338    where
339        F: FnMut(&LineSegment<S>),
340    {
341        debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
342
343        let n = flattened_segments_wang(self, tolerance);
344        let step = S::ONE / n;
345        let mut t = step;
346
347        let curve = self.polynomial_form();
348
349        let n = n.to_u32().unwrap_or(1) - 1;
350        let mut from = self.from;
351        for _ in 0..n {
352            let to = curve.sample(t);
353            callback(&LineSegment { from, to });
354            from = to;
355            t += step;
356        }
357
358        callback(&LineSegment { from, to: self.to });
359    }
360
361    /// Compute a flattened approximation of the curve, invoking a callback at
362    /// each step.
363    ///
364    /// The `tolerance` parameter defines the maximum distance between the curve and
365    /// its approximation.
366    ///
367    /// The end of the t parameter range at the final segment is guaranteed to be equal to `1.0`.
368    pub fn for_each_flattened_with_t<F>(&self, tolerance: S, callback: &mut F)
369    where
370        F: FnMut(&LineSegment<S>, Range<S>),
371    {
372        debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
373
374        let n = flattened_segments_wang(self, tolerance);
375        let step = S::ONE / n;
376        let mut t0 = S::ZERO;
377
378        let curve = self.polynomial_form();
379
380        let n = n.to_u32().unwrap_or(1) - 1;
381        let mut from = self.from;
382        for _ in 0..n {
383            let t1 = t0 + step;
384            let to = curve.sample(t1);
385            callback(&LineSegment { from, to }, t0..t1);
386            from = to;
387            t0 = t1;
388        }
389
390        callback(&LineSegment { from, to: self.to }, t0..S::ONE);
391    }
392
393    /// Returns the flattened representation of the curve as an iterator, starting *after* the
394    /// current point.
395    pub fn flattened(&self, tolerance: S) -> Flattened<S> {
396        Flattened::new(self, tolerance)
397    }
398    /// Returns the flattened representation of the curve as an iterator, starting *after* the
399    /// current point.
400    pub fn flattened_t(&self, tolerance: S) -> FlattenedT<S> {
401        FlattenedT::new(self, tolerance)
402    }
403
404    /// Invokes a callback for each monotonic part of the segment.
405    pub fn for_each_monotonic_range<F>(&self, cb: &mut F)
406    where
407        F: FnMut(Range<S>),
408    {
409        let mut t0 = self.local_x_extremum_t();
410        let mut t1 = self.local_y_extremum_t();
411
412        let swap = match (t0, t1) {
413            (Some(tx), Some(ty)) => tx > ty,
414            _ => false,
415        };
416
417        if swap {
418            mem::swap(&mut t0, &mut t1);
419        }
420
421        let mut start = S::ZERO;
422
423        if let Some(t) = t0 {
424            cb(start..t);
425            start = t;
426        }
427
428        if let Some(t) = t1 {
429            // In extreme cases the same point can be an x and y inflection point.
430            if t != start {
431                cb(start..t);
432                start = t
433            }
434        }
435
436        cb(start..S::ONE);
437    }
438
439    /// Invokes a callback for each monotonic part of the segment.
440    pub fn for_each_monotonic<F>(&self, cb: &mut F)
441    where
442        F: FnMut(&QuadraticBezierSegment<S>),
443    {
444        self.for_each_monotonic_range(&mut |range| {
445            let mut sub = self.split_range(range);
446            // Due to finite precision the split may actually result in sub-curves
447            // that are almost but not-quite monotonic. Make sure they actually are.
448            let min_x = sub.from.x.min(sub.to.x);
449            let max_x = sub.from.x.max(sub.to.x);
450            let min_y = sub.from.y.min(sub.to.y);
451            let max_y = sub.from.y.max(sub.to.y);
452            sub.ctrl.x = sub.ctrl.x.max(min_x).min(max_x);
453            sub.ctrl.y = sub.ctrl.y.max(min_y).min(max_y);
454            cb(&sub);
455        });
456    }
457
458    /// Invokes a callback for each y-monotonic part of the segment.
459    pub fn for_each_y_monotonic_range<F>(&self, cb: &mut F)
460    where
461        F: FnMut(Range<S>),
462    {
463        match self.local_y_extremum_t() {
464            Some(t) => {
465                cb(S::ZERO..t);
466                cb(t..S::ONE);
467            }
468            None => {
469                cb(S::ZERO..S::ONE);
470            }
471        }
472    }
473
474    /// Invokes a callback for each y-monotonic part of the segment.
475    pub fn for_each_y_monotonic<F>(&self, cb: &mut F)
476    where
477        F: FnMut(&QuadraticBezierSegment<S>),
478    {
479        match self.local_y_extremum_t() {
480            Some(t) => {
481                let (a, b) = self.split(t);
482                cb(&a);
483                cb(&b);
484            }
485            None => {
486                cb(self);
487            }
488        }
489    }
490
491    /// Invokes a callback for each x-monotonic part of the segment.
492    pub fn for_each_x_monotonic_range<F>(&self, cb: &mut F)
493    where
494        F: FnMut(Range<S>),
495    {
496        match self.local_x_extremum_t() {
497            Some(t) => {
498                cb(S::ZERO..t);
499                cb(t..S::ONE);
500            }
501            None => {
502                cb(S::ZERO..S::ONE);
503            }
504        }
505    }
506
507    /// Invokes a callback for each x-monotonic part of the segment.
508    pub fn for_each_x_monotonic<F>(&self, cb: &mut F)
509    where
510        F: FnMut(&QuadraticBezierSegment<S>),
511    {
512        match self.local_x_extremum_t() {
513            Some(t) => {
514                let (mut a, mut b) = self.split(t);
515                // Due to finite precision the split may actually result in sub-curves
516                // that are almost but not-quite monotonic. Make sure they actually are.
517                let a_min = a.from.x.min(a.to.x);
518                let a_max = a.from.x.max(a.to.x);
519                let b_min = b.from.x.min(b.to.x);
520                let b_max = b.from.x.max(b.to.x);
521                a.ctrl.x = a.ctrl.x.max(a_min).min(a_max);
522                b.ctrl.x = b.ctrl.x.max(b_min).min(b_max);
523                cb(&a);
524                cb(&b);
525            }
526            None => {
527                cb(self);
528            }
529        }
530    }
531
532    /// Returns a triangle containing this curve segment.
533    pub fn bounding_triangle(&self) -> Triangle<S> {
534        Triangle {
535            a: self.from,
536            b: self.ctrl,
537            c: self.to,
538        }
539    }
540
541    /// Returns a conservative rectangle that contains the curve.
542    pub fn fast_bounding_box(&self) -> Box2D<S> {
543        let (min_x, max_x) = self.fast_bounding_range_x();
544        let (min_y, max_y) = self.fast_bounding_range_y();
545
546        Box2D {
547            min: point(min_x, min_y),
548            max: point(max_x, max_y),
549        }
550    }
551
552    /// Returns a conservative range of x that contains this curve.
553    pub fn fast_bounding_range_x(&self) -> (S, S) {
554        let min_x = self.from.x.min(self.ctrl.x).min(self.to.x);
555        let max_x = self.from.x.max(self.ctrl.x).max(self.to.x);
556
557        (min_x, max_x)
558    }
559
560    /// Returns a conservative range of y that contains this curve.
561    pub fn fast_bounding_range_y(&self) -> (S, S) {
562        let min_y = self.from.y.min(self.ctrl.y).min(self.to.y);
563        let max_y = self.from.y.max(self.ctrl.y).max(self.to.y);
564
565        (min_y, max_y)
566    }
567
568    /// Returns the smallest rectangle the curve is contained in
569    pub fn bounding_box(&self) -> Box2D<S> {
570        let (min_x, max_x) = self.bounding_range_x();
571        let (min_y, max_y) = self.bounding_range_y();
572
573        Box2D {
574            min: point(min_x, min_y),
575            max: point(max_x, max_y),
576        }
577    }
578
579    /// Returns the smallest range of x that contains this curve.
580    pub fn bounding_range_x(&self) -> (S, S) {
581        let min_x = self.x(self.x_minimum_t());
582        let max_x = self.x(self.x_maximum_t());
583
584        (min_x, max_x)
585    }
586
587    /// Returns the smallest range of y that contains this curve.
588    pub fn bounding_range_y(&self) -> (S, S) {
589        let min_y = self.y(self.y_minimum_t());
590        let max_y = self.y(self.y_maximum_t());
591
592        (min_y, max_y)
593    }
594
595    /// Returns whether this segment is monotonic on the x axis.
596    pub fn is_x_monotonic(&self) -> bool {
597        self.local_x_extremum_t().is_none()
598    }
599
600    /// Returns whether this segment is monotonic on the y axis.
601    pub fn is_y_monotonic(&self) -> bool {
602        self.local_y_extremum_t().is_none()
603    }
604
605    /// Returns whether this segment is fully monotonic.
606    pub fn is_monotonic(&self) -> bool {
607        self.is_x_monotonic() && self.is_y_monotonic()
608    }
609
610    /// Computes the intersections (if any) between this segment a line.
611    ///
612    /// The result is provided in the form of the `t` parameters of each
613    /// point along curve. To get the intersection points, sample the curve
614    /// at the corresponding values.
615    pub fn line_intersections_t(&self, line: &Line<S>) -> ArrayVec<S, 2> {
616        // Take the quadratic bezier formulation and inject it in
617        // the line equation ax + by + c = 0.
618        let eqn = line.equation();
619        let i = eqn.a() * self.from.x + eqn.b() * self.from.y;
620        let j = eqn.a() * self.ctrl.x + eqn.b() * self.ctrl.y;
621        let k = eqn.a() * self.to.x + eqn.b() * self.to.y;
622        // Solve "(i - 2j + k)t² + (2j - 2i)t + (i + c) = 0"
623        let a = i - j - j + k;
624        let b = j + j - i - i;
625        let c = i + eqn.c();
626
627        let mut result = ArrayVec::new();
628
629        if a == S::ZERO {
630            // Linear equation bt + c = 0.
631            let t = c / b;
632            // Note: if b is zero, then t is NaN and we return without any
633            // intersection. This is on purpose although debatable. See the
634            // comment in `line_intersections_degenerate_curve_on_line`.
635            // This happens if the the curve is a line segment colinear with
636            // `line`.
637            if t >= S::ZERO && t <= S::ONE {
638                result.push(t);
639                return result;
640            }
641        }
642
643        let delta = b * b - S::FOUR * a * c;
644        if delta >= S::ZERO {
645            // To avoid potential float precision issues when b is close to
646            // sqrt_delta, we exploit the fact that given the roots t1 and t2,
647            // t2 = c / (a * t1) and t1 = c / (a * t2).
648            let sqrt_delta = S::sqrt(delta);
649            let s_sqrt_delta = -b.signum() * sqrt_delta;
650            let mut t1 = (-b + s_sqrt_delta) / (S::TWO * a);
651            let mut t2 = c / (a * t1);
652
653            if t1 > t2 {
654                mem::swap(&mut t1, &mut t2);
655            }
656
657            if t1 >= S::ZERO && t1 <= S::ONE {
658                result.push(t1);
659            }
660
661            if t2 >= S::ZERO && t2 <= S::ONE && t1 != t2 {
662                result.push(t2);
663            }
664        }
665
666        result
667    }
668
669    /// Computes the intersection points (if any) between this segment a line.
670    pub fn line_intersections(&self, line: &Line<S>) -> ArrayVec<Point<S>, 2> {
671        let intersections = self.line_intersections_t(line);
672
673        let mut result = ArrayVec::new();
674        for t in intersections {
675            result.push(self.sample(t));
676        }
677
678        result
679    }
680
681    /// Computes the intersections (if any) between this segment and a line segment.
682    ///
683    /// The result is provided in the form of the `t` parameters of each
684    /// point along curve and segment. To get the intersection points, sample
685    /// the segments at the corresponding values.
686    pub fn line_segment_intersections_t(&self, segment: &LineSegment<S>) -> ArrayVec<(S, S), 2> {
687        if !self
688            .fast_bounding_box()
689            .inflate(S::EPSILON, S::EPSILON)
690            .intersects(&segment.bounding_box().inflate(S::EPSILON, S::EPSILON))
691        {
692            return ArrayVec::new();
693        }
694
695        let intersections = self.line_intersections_t(&segment.to_line());
696
697        let mut result = ArrayVec::new();
698        if intersections.is_empty() {
699            return result;
700        }
701
702        let seg_is_mostly_vertical =
703            S::abs(segment.from.y - segment.to.y) >= S::abs(segment.from.x - segment.to.x);
704        let (seg_long_axis_min, seg_long_axis_max) = if seg_is_mostly_vertical {
705            segment.bounding_range_y()
706        } else {
707            segment.bounding_range_x()
708        };
709
710        for t in intersections {
711            let intersection_xy = if seg_is_mostly_vertical {
712                self.y(t)
713            } else {
714                self.x(t)
715            };
716            if intersection_xy >= seg_long_axis_min && intersection_xy <= seg_long_axis_max {
717                let t2 = (self.sample(t) - segment.from).length() / segment.length();
718                // Don't take intersections that are on endpoints of both curves at the same time.
719                if (t != S::ZERO && t != S::ONE) || (t2 != S::ZERO && t2 != S::ONE) {
720                    result.push((t, t2));
721                }
722            }
723        }
724
725        result
726    }
727
728    #[inline]
729    pub fn from(&self) -> Point<S> {
730        self.from
731    }
732
733    #[inline]
734    pub fn to(&self) -> Point<S> {
735        self.to
736    }
737
738    /// Computes the intersection points (if any) between this segment a line segment.
739    pub fn line_segment_intersections(&self, segment: &LineSegment<S>) -> ArrayVec<Point<S>, 2> {
740        let intersections = self.line_segment_intersections_t(segment);
741
742        let mut result = ArrayVec::new();
743        for (t, _) in intersections {
744            result.push(self.sample(t));
745        }
746
747        result
748    }
749
750    /// Analytic solution to finding the closest point on the curve to `pos`.
751    pub fn closest_point(&self, pos: Point<S>) -> S {
752        // We are looking for the points in the curve where the line passing through pos
753        // and these points are perpendicular to the curve.
754        let a = self.from - pos;
755        let b = self.ctrl - self.from;
756        let c = self.from + self.to.to_vector() - self.ctrl * S::TWO;
757
758        // Polynomial coefficients
759        let c0 = c.dot(c);
760        let c1 = b.dot(c) * S::THREE;
761        let c2 = b.dot(b) * S::TWO + a.dot(c);
762        let c3 = a.dot(b);
763
764        let roots = crate::utils::cubic_polynomial_roots(c0, c1, c2, c3);
765
766        let mut sq_dist = a.square_length();
767        let mut t = S::ZERO;
768        let to_dist = (self.to - pos).square_length();
769        if to_dist < sq_dist {
770            sq_dist = to_dist;
771            t = S::ONE
772        }
773        for root in roots {
774            if root >= S::ZERO && root <= S::ONE {
775                let p = self.sample(root);
776                let d = (pos - p).square_length();
777                if d < sq_dist {
778                    sq_dist = d;
779                    t = root;
780                }
781            }
782        }
783
784        t
785    }
786
787    /// Returns the shortest distance between this segment and a point.
788    pub fn distance_to_point(&self, pos: Point<S>) -> S {
789        (self.sample(self.closest_point(pos)) - pos).length()
790    }
791
792    /// Returns the shortest squared distance between this segment and a point.
793    ///
794    /// May be useful to avoid the cost of a square root when comparing against a distance
795    /// that can be squared instead.
796    pub fn square_distance_to_point(&self, pos: Point<S>) -> S {
797        (self.sample(self.closest_point(pos)) - pos).square_length()
798    }
799
800    // Returns a quadratic bézier curve built by dragging this curve's point at `t`
801    // to a new position without moving the endpoints.
802    pub fn drag(&self, t: S, new_position: Point<S>) -> Self {
803        let t2 = t * t;
804        let one_t = S::ONE - t;
805        let one_t2 = one_t * one_t;
806
807        let u = t2 / (t2 + one_t2);
808        let c = self.from.lerp(self.to, u);
809
810        let inv_r = S::abs((t2 + one_t2) / (t2 + one_t2 - S::ONE));
811
812        QuadraticBezierSegment {
813            from: self.from,
814            ctrl: new_position + (new_position - c) * inv_r,
815            to: self.to,
816        }
817    }
818
819    /// Computes the length of this segment.
820    ///
821    /// Implements Raph Levien's analytical approach described in
822    /// https://raphlinus.github.io/curves/2018/12/28/bezier-arclength.html
823    pub fn length(&self) -> S {
824        // This is ported from kurbo's implementation.
825        // https://github.com/linebender/kurbo/blob/d0b956b47f219ba2303b4e2f2d904ea7b946e783/src/quadbez.rs#L239
826        let d2 = self.from - self.ctrl * S::TWO + self.to.to_vector();
827        let d1 = self.ctrl - self.from;
828        let a = d2.square_length();
829        let c = d1.square_length();
830        if a < S::value(1e-4) * c {
831            // The segment is almost straight.
832            //
833            // Legendre-Gauss quadrature using formula from Behdad
834            // in https://github.com/Pomax/BezierInfo-2/issues/77
835            let v0 = (self.from.to_vector() * S::value(-0.492943519233745)
836                + self.ctrl.to_vector() * S::value(0.430331482911935)
837                + self.to.to_vector() * S::value(0.0626120363218102))
838            .length();
839            let v1 = ((self.to - self.from) * S::value(0.4444444444444444)).length();
840            let v2 = (self.from.to_vector() * S::value(-0.0626120363218102)
841                + self.ctrl.to_vector() * S::value(-0.430331482911935)
842                + self.to.to_vector() * S::value(0.492943519233745))
843            .length();
844            return v0 + v1 + v2;
845        }
846
847        let b = S::TWO * d2.dot(d1);
848
849        let sqr_abc = (a + b + c).sqrt();
850        let a2 = a.powf(-S::HALF);
851        let a32 = a2.powi(3);
852        let c2 = S::TWO * c.sqrt();
853        let ba_c2 = b * a2 + c2;
854
855        let v0 = S::HALF * S::HALF * a2 * a2 * b * (S::TWO * sqr_abc - c2) + sqr_abc;
856
857        if ba_c2 < S::EPSILON {
858            // The curve has a sharp turns.
859            v0
860        } else {
861            v0 + S::HALF
862                * S::HALF
863                * a32
864                * (S::FOUR * c * a - b * b)
865                * (((S::TWO * a + b) * a2 + S::TWO * sqr_abc) / ba_c2).ln()
866        }
867    }
868
869    // This is to conform to the `impl_segment!` macro
870    fn approximate_length(&self, _tolerance: S) -> S {
871        self.length()
872    }
873
874    pub fn to_f32(&self) -> QuadraticBezierSegment<f32> {
875        QuadraticBezierSegment {
876            from: self.from.to_f32(),
877            ctrl: self.ctrl.to_f32(),
878            to: self.to.to_f32(),
879        }
880    }
881
882    pub fn to_f64(&self) -> QuadraticBezierSegment<f64> {
883        QuadraticBezierSegment {
884            from: self.from.to_f64(),
885            ctrl: self.ctrl.to_f64(),
886            to: self.to.to_f64(),
887        }
888    }
889
890    #[inline]
891    pub fn polynomial_form(&self) -> QuadraticBezierPolynomial<S> {
892        let from = self.from.to_vector();
893        let ctrl = self.ctrl.to_vector();
894        let to = self.to.to_vector();
895        QuadraticBezierPolynomial {
896            a0: from,
897            a1: (ctrl - from) * S::TWO,
898            a2: from + to - ctrl * S::TWO,
899        }
900    }
901}
902
903// TODO(breaking change): This should not have been a public struct.
904// It's not useful to users of the crate but removing it is a breaking change so it
905// stays empty for now.
906pub struct FlatteningParameters<S> {
907    _marker: core::marker::PhantomData<S>
908}
909
910impl<S: Scalar> FlatteningParameters<S> {
911    pub fn new(_curve: &QuadraticBezierSegment<S>, _tolerance: S) -> Self {
912        Self { _marker: core::marker::PhantomData }
913    }
914}
915
916/// The polynomial form of a quadratic bézier segment.
917///
918/// The `sample` implementation uses Horner's method and tends to be faster than
919/// `QuadraticBezierSegment::sample`.
920#[derive(Copy, Clone, Debug, PartialEq)]
921pub struct QuadraticBezierPolynomial<S> {
922    pub a0: Vector<S>,
923    pub a1: Vector<S>,
924    pub a2: Vector<S>,
925}
926
927impl<S: Scalar> QuadraticBezierPolynomial<S> {
928    #[inline(always)]
929    pub fn sample(&self, t: S) -> Point<S> {
930        // Horner's method.
931        let mut v = self.a0;
932        let mut t2 = t;
933        v += self.a1 * t2;
934        t2 *= t;
935        v += self.a2 * t2;
936
937        v.to_point()
938    }
939}
940
941/// Computes the number of line segments required to build a flattened approximation
942/// of the curve with segments placed at regular `t` intervals.
943fn flattened_segments_wang<S: Scalar>(curve: &QuadraticBezierSegment<S>, tolerance: S) -> S {
944    let from = curve.from.to_vector();
945    let ctrl = curve.ctrl.to_vector();
946    let to = curve.to.to_vector();
947    let l = (from - ctrl * S::TWO + to) * S::TWO;
948    let d = S::ONE / (S::EIGHT * tolerance);
949    let err4 = l.dot(l) * d * d;
950    let err4_f32 = err4.to_f32().unwrap_or(1.0);
951
952    // Avoid two square roots using a lookup table that contains
953    // i^4 for  i in 1..25.
954    const N: usize = 24;
955    const LUT: [f32; N] = [
956        1.0, 16.0, 81.0, 256.0, 625.0, 1296.0, 2401.0, 4096.0, 6561.0,
957        10000.0, 14641.0, 20736.0, 28561.0, 38416.0, 50625.0, 65536.0,
958        83521.0, 104976.0, 130321.0, 160000.0, 194481.0, 234256.0,
959        279841.0, 331776.0
960    ];
961
962    // If the value we are looking for is within the LUT, take the fast path
963    if err4_f32 <= 331776.0 {
964        #[allow(clippy::needless_range_loop)]
965        for i in 0..N {
966            if err4_f32 <= LUT[i] {
967                return S::from(i + 1).unwrap_or(S::ONE);
968            }
969        }
970    }
971
972    // Otherwise fall back to computing via two square roots.
973    err4.sqrt().sqrt().max(S::ONE)
974}
975
976/// A flattening iterator for quadratic bézier segments.
977///
978/// Yields points at each iteration.
979pub struct Flattened<S> {
980    curve: QuadraticBezierPolynomial<S>,
981    to: Point<S>,
982    segments: u32,
983    step: S,
984    t: S,
985}
986
987impl<S: Scalar> Flattened<S> {
988    #[inline]
989    pub(crate) fn new(curve: &QuadraticBezierSegment<S>, tolerance: S) -> Self {
990        debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
991
992        let n = flattened_segments_wang(curve, tolerance);
993
994        let step = S::ONE / n;
995
996        Flattened {
997            curve: curve.polynomial_form(),
998            to: curve.to,
999            segments: n.to_u32().unwrap_or(1),
1000            step,
1001            t: step
1002        }
1003    }
1004}
1005
1006impl<S: Scalar> Iterator for Flattened<S> {
1007    type Item = Point<S>;
1008
1009    #[inline]
1010    fn next(&mut self) -> Option<Point<S>> {
1011        if self.segments == 0 {
1012            return None;
1013        }
1014
1015        self.segments -= 1;
1016        if self.segments == 0 {
1017            return Some(self.to)
1018        }
1019
1020        let p = self.curve.sample(self.t);
1021        self.t += self.step;
1022
1023        Some(p)
1024    }
1025
1026    #[inline]
1027    fn size_hint(&self) -> (usize, Option<usize>) {
1028        let n = self.segments as usize;
1029        (n, Some(n))
1030    }
1031}
1032
1033// TODO(breaking change): is this useful? Either remove
1034// it or add the equivalent for cubic curves.
1035/// A flattening iterator for quadratic bézier segments.
1036///
1037/// Yields the curve parameter at each iteration.
1038pub struct FlattenedT<S> {
1039    segments: u32,
1040    step: S,
1041    t: S,
1042}
1043
1044impl<S: Scalar> FlattenedT<S> {
1045    #[inline]
1046    pub(crate) fn new(curve: &QuadraticBezierSegment<S>, tolerance: S) -> Self {
1047        debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
1048
1049        let n = flattened_segments_wang(curve, tolerance);
1050
1051        let step = S::ONE / n;
1052
1053        FlattenedT {
1054            segments: n.to_u32().unwrap_or(1),
1055            step,
1056            t: step
1057        }
1058    }
1059}
1060
1061impl<S: Scalar> Iterator for FlattenedT<S> {
1062    type Item = S;
1063
1064    #[inline]
1065    fn next(&mut self) -> Option<S> {
1066        if self.segments == 0 {
1067            return None;
1068        }
1069
1070        self.segments -= 1;
1071        if self.segments == 0 {
1072            return Some(S::ONE)
1073        }
1074
1075        let t = self.t;
1076        self.t += self.step;
1077
1078        Some(t)
1079    }
1080
1081    #[inline]
1082    fn size_hint(&self) -> (usize, Option<usize>) {
1083        let n = self.segments as usize;
1084        (n, Some(n))
1085    }
1086}
1087
1088impl<S: Scalar> Segment for QuadraticBezierSegment<S> {
1089    impl_segment!(S);
1090
1091    fn for_each_flattened_with_t(
1092        &self,
1093        tolerance: Self::Scalar,
1094        callback: &mut dyn FnMut(&LineSegment<S>, Range<S>),
1095    ) {
1096        self.for_each_flattened_with_t(tolerance, &mut |s, t| callback(s, t));
1097    }
1098}
1099
1100#[test]
1101fn bounding_box_for_monotonic_quadratic_bezier_segment() {
1102    let a = QuadraticBezierSegment {
1103        from: Point::new(0.0, 0.0),
1104        ctrl: Point::new(0.0, 0.0),
1105        to: Point::new(2.0, 0.0),
1106    };
1107
1108    let expected_aabb = Box2D {
1109        min: point(0.0, 0.0),
1110        max: point(2.0, 0.0),
1111    };
1112
1113    let actual_aabb = a.bounding_box();
1114
1115    assert_eq!(expected_aabb, actual_aabb)
1116}
1117
1118#[test]
1119fn fast_bounding_box_for_quadratic_bezier_segment() {
1120    let a = QuadraticBezierSegment {
1121        from: Point::new(0.0, 0.0),
1122        ctrl: Point::new(1.0, 1.0),
1123        to: Point::new(2.0, 0.0),
1124    };
1125
1126    let expected_aabb = Box2D {
1127        min: point(0.0, 0.0),
1128        max: point(2.0, 1.0),
1129    };
1130
1131    let actual_aabb = a.fast_bounding_box();
1132
1133    assert_eq!(expected_aabb, actual_aabb)
1134}
1135
1136#[test]
1137fn minimum_bounding_box_for_quadratic_bezier_segment() {
1138    let a = QuadraticBezierSegment {
1139        from: Point::new(0.0, 0.0),
1140        ctrl: Point::new(1.0, 1.0),
1141        to: Point::new(2.0, 0.0),
1142    };
1143
1144    let expected_aabb = Box2D {
1145        min: point(0.0, 0.0),
1146        max: point(2.0, 0.5),
1147    };
1148
1149    let actual_aabb = a.bounding_box();
1150
1151    assert_eq!(expected_aabb, actual_aabb)
1152}
1153
1154#[test]
1155fn y_maximum_t_for_simple_segment() {
1156    let a = QuadraticBezierSegment {
1157        from: Point::new(0.0, 0.0),
1158        ctrl: Point::new(1.0, 1.0),
1159        to: Point::new(2.0, 0.0),
1160    };
1161
1162    let expected_y_maximum = 0.5;
1163
1164    let actual_y_maximum = a.y_maximum_t();
1165
1166    assert_eq!(expected_y_maximum, actual_y_maximum)
1167}
1168
1169#[test]
1170fn local_y_extremum_for_simple_segment() {
1171    let a = QuadraticBezierSegment {
1172        from: Point::new(0.0, 0.0),
1173        ctrl: Point::new(1.0, 1.0),
1174        to: Point::new(2.0, 0.0),
1175    };
1176
1177    let expected_y_inflection = 0.5;
1178
1179    match a.local_y_extremum_t() {
1180        Some(actual_y_inflection) => assert_eq!(expected_y_inflection, actual_y_inflection),
1181        None => panic!(),
1182    }
1183}
1184
1185#[test]
1186fn y_minimum_t_for_simple_segment() {
1187    let a = QuadraticBezierSegment {
1188        from: Point::new(0.0, 0.0),
1189        ctrl: Point::new(1.0, -1.0),
1190        to: Point::new(2.0, 0.0),
1191    };
1192
1193    let expected_y_minimum = 0.5;
1194
1195    let actual_y_minimum = a.y_minimum_t();
1196
1197    assert_eq!(expected_y_minimum, actual_y_minimum)
1198}
1199
1200#[test]
1201fn x_maximum_t_for_simple_segment() {
1202    let a = QuadraticBezierSegment {
1203        from: Point::new(0.0, 0.0),
1204        ctrl: Point::new(1.0, 1.0),
1205        to: Point::new(0.0, 2.0),
1206    };
1207
1208    let expected_x_maximum = 0.5;
1209
1210    let actual_x_maximum = a.x_maximum_t();
1211
1212    assert_eq!(expected_x_maximum, actual_x_maximum)
1213}
1214
1215#[test]
1216fn local_x_extremum_for_simple_segment() {
1217    let a = QuadraticBezierSegment {
1218        from: Point::new(0.0, 0.0),
1219        ctrl: Point::new(1.0, 1.0),
1220        to: Point::new(0.0, 2.0),
1221    };
1222
1223    let expected_x_inflection = 0.5;
1224
1225    match a.local_x_extremum_t() {
1226        Some(actual_x_inflection) => assert_eq!(expected_x_inflection, actual_x_inflection),
1227        None => panic!(),
1228    }
1229}
1230
1231#[test]
1232fn x_minimum_t_for_simple_segment() {
1233    let a = QuadraticBezierSegment {
1234        from: Point::new(2.0, 0.0),
1235        ctrl: Point::new(1.0, 1.0),
1236        to: Point::new(2.0, 2.0),
1237    };
1238
1239    let expected_x_minimum = 0.5;
1240
1241    let actual_x_minimum = a.x_minimum_t();
1242
1243    assert_eq!(expected_x_minimum, actual_x_minimum)
1244}
1245
1246#[test]
1247fn length_straight_line() {
1248    // Sanity check: aligned points so both these curves are straight lines
1249    // that go form (0.0, 0.0) to (2.0, 0.0).
1250
1251    let len = QuadraticBezierSegment {
1252        from: Point::new(0.0f64, 0.0),
1253        ctrl: Point::new(1.0, 0.0),
1254        to: Point::new(2.0, 0.0),
1255    }
1256    .length();
1257    assert!((len - 2.0).abs() < 0.000001);
1258
1259    let len = CubicBezierSegment {
1260        from: Point::new(0.0f64, 0.0),
1261        ctrl1: Point::new(1.0, 0.0),
1262        ctrl2: Point::new(1.0, 0.0),
1263        to: Point::new(2.0, 0.0),
1264    }
1265    .approximate_length(0.0001);
1266    assert!((len - 2.0).abs() < 0.000001);
1267}
1268
1269#[test]
1270fn derivatives() {
1271    let c1 = QuadraticBezierSegment {
1272        from: Point::new(1.0, 1.0),
1273        ctrl: Point::new(2.0, 1.0),
1274        to: Point::new(2.0, 2.0),
1275    };
1276
1277    assert_eq!(c1.dy(0.0), 0.0);
1278    assert_eq!(c1.dx(1.0), 0.0);
1279    assert_eq!(c1.dy(0.5), c1.dx(0.5));
1280}
1281
1282#[test]
1283fn fat_line() {
1284    use crate::point;
1285
1286    let c1 = QuadraticBezierSegment {
1287        from: point(1.0f32, 2.0),
1288        ctrl: point(1.0, 3.0),
1289        to: point(11.0, 12.0),
1290    };
1291
1292    let (l1, l2) = c1.fat_line();
1293
1294    for i in 0..100 {
1295        let t = i as f32 / 99.0;
1296        assert!(l1.signed_distance_to_point(&c1.sample(t)) >= -0.000001);
1297        assert!(l2.signed_distance_to_point(&c1.sample(t)) <= 0.000001);
1298    }
1299}
1300
1301#[test]
1302fn is_linear() {
1303    let mut angle = 0.0;
1304    let center = Point::new(1000.0, -700.0);
1305    for _ in 0..100 {
1306        for i in 0..10 {
1307            let (sin, cos) = f64::sin_cos(angle);
1308            let endpoint = Vector::new(cos * 100.0, sin * 100.0);
1309            let curve = QuadraticBezierSegment {
1310                from: center - endpoint,
1311                ctrl: center + endpoint.lerp(-endpoint, i as f64 / 9.0),
1312                to: center + endpoint,
1313            };
1314
1315            assert!(curve.is_linear(1e-10));
1316        }
1317        angle += 0.001;
1318    }
1319}
1320
1321#[test]
1322fn test_flattening() {
1323    use crate::point;
1324
1325    let c1 = QuadraticBezierSegment {
1326        from: point(0.0, 0.0),
1327        ctrl: point(5.0, 0.0),
1328        to: point(5.0, 5.0),
1329    };
1330
1331    let c2 = QuadraticBezierSegment {
1332        from: point(0.0, 0.0),
1333        ctrl: point(50.0, 0.0),
1334        to: point(50.0, 50.0),
1335    };
1336
1337    let c3 = QuadraticBezierSegment {
1338        from: point(0.0, 0.0),
1339        ctrl: point(100.0, 100.0),
1340        to: point(5.0, 0.0),
1341    };
1342
1343    fn check_tolerance(curve: &QuadraticBezierSegment<f64>, tolerance: f64) {
1344        let mut c = curve.clone();
1345        loop {
1346            let t = c.flattening_step(tolerance);
1347            if t >= 1.0 {
1348                break;
1349            }
1350            let (before, after) = c.split(t);
1351            let mid_point = before.sample(0.5);
1352            let distance = before
1353                .baseline()
1354                .to_line()
1355                .equation()
1356                .distance_to_point(&mid_point);
1357            assert!(distance <= tolerance);
1358            c = after;
1359        }
1360    }
1361
1362    check_tolerance(&c1, 1.0);
1363    check_tolerance(&c1, 0.1);
1364    check_tolerance(&c1, 0.01);
1365    check_tolerance(&c1, 0.001);
1366    check_tolerance(&c1, 0.0001);
1367
1368    check_tolerance(&c2, 1.0);
1369    check_tolerance(&c2, 0.1);
1370    check_tolerance(&c2, 0.01);
1371    check_tolerance(&c2, 0.001);
1372    check_tolerance(&c2, 0.0001);
1373
1374    check_tolerance(&c3, 1.0);
1375    check_tolerance(&c3, 0.1);
1376    check_tolerance(&c3, 0.01);
1377    check_tolerance(&c3, 0.001);
1378    check_tolerance(&c3, 0.0001);
1379}
1380
1381#[test]
1382fn test_flattening_empty_curve() {
1383    use crate::point;
1384
1385    let curve = QuadraticBezierSegment {
1386        from: point(0.0, 0.0),
1387        ctrl: point(0.0, 0.0),
1388        to: point(0.0, 0.0),
1389    };
1390
1391    let mut iter = FlattenedT::new(&curve, 0.1);
1392
1393    assert_eq!(iter.next(), Some(1.0));
1394    assert_eq!(iter.next(), None);
1395
1396    let mut count: u32 = 0;
1397    curve.for_each_flattened(0.1, &mut |_| count += 1);
1398    assert_eq!(count, 1);
1399}
1400
1401#[test]
1402fn test_flattening_straight_line() {
1403    use crate::point;
1404
1405    let curve = QuadraticBezierSegment {
1406        from: point(0.0, 0.0),
1407        ctrl: point(10.0, 0.0),
1408        to: point(20.0, 0.0),
1409    };
1410
1411    let mut iter = FlattenedT::new(&curve, 0.1);
1412
1413    assert_eq!(iter.next(), Some(1.0));
1414    assert!(iter.next().is_none());
1415
1416    let mut count: u32 = 0;
1417    curve.for_each_flattened(0.1, &mut |_| count += 1);
1418    assert_eq!(count, 1);
1419}
1420
1421#[test]
1422fn test_flattened_t_values_are_monotonic() {
1423    use crate::point;
1424
1425    // A curve with enough curvature to require several segments.
1426    let curve = QuadraticBezierSegment {
1427        from: point(0.0f32, 0.0),
1428        ctrl: point(50.0, 100.0),
1429        to: point(100.0, 0.0),
1430    };
1431
1432    let iter = FlattenedT::new(&curve, 0.01);
1433    let count = iter.segments;
1434    assert!(count >= 3, "need at least 3 segments for this test, got {}", count);
1435
1436    let step = 1.0 / count as f32;
1437    let mut iter = FlattenedT::new(&curve, 0.01);
1438    let mut prev = 0.0f32;
1439    let mut n = 0u32;
1440
1441    while let Some(t) = iter.next() {
1442        // Values must be strictly monotonically increasing.
1443        assert!(t > prev, "t values must be strictly increasing: {} <= {}", t, prev);
1444        prev = t;
1445        n += 1;
1446    }
1447
1448    // Must produce exactly `count` values.
1449    assert_eq!(n, count);
1450
1451    // First value must be close to 1/count (not 2/count).
1452    let first = FlattenedT::new(&curve, 0.01).next().unwrap();
1453    assert!(
1454        (first - step).abs() < 1e-3,
1455        "first t value should be ~{}, got {}",
1456        step, first,
1457    );
1458
1459    // Last value must be exactly 1.0.
1460    assert_eq!(prev, 1.0);
1461}
1462
1463#[test]
1464fn issue_678() {
1465    let points = [
1466        [-7768.80859375f32, -35563.80859375],
1467        [-38463.125, -10941.41796875],
1468        [-21846.12890625, -13518.1953125],
1469        [-11727.439453125, -22080.33203125],
1470    ];
1471
1472    let quadratic = QuadraticBezierSegment {
1473        from: Point::new(points[0][0], points[0][1]),
1474        ctrl: Point::new(points[1][0], points[1][1]),
1475        to: Point::new(points[2][0], points[2][1]),
1476    };
1477
1478    let line = Line {
1479        point: Point::new(points[3][0], points[3][1]),
1480        vector: Vector::new(-0.5, -0.5).normalize(),
1481    };
1482
1483    let intersections = quadratic.line_intersections(&line);
1484    std::println!("{intersections:?}");
1485
1486    assert_eq!(intersections.len(), 1);
1487}
1488
1489#[test]
1490fn line_intersections_t() {
1491    let curve = QuadraticBezierSegment {
1492        from: point(0.0f64, 0.0),
1493        ctrl: point(100.0, 0.0),
1494        to: point(100.0, 500.0),
1495    };
1496    let cubic = curve.to_cubic();
1497
1498    let line = Line {
1499        point: point(0.0, -50.0),
1500        vector: crate::vector(100.0, 500.0),
1501    };
1502
1503    let mut i1 = curve.line_intersections_t(&line);
1504    let mut i2 = curve.to_cubic().line_intersections_t(&line);
1505
1506    use std::cmp::Ordering::{Equal, Greater, Less};
1507    i1.sort_by(|a, b| {
1508        if a == b {
1509            Equal
1510        } else if a > b {
1511            Greater
1512        } else {
1513            Less
1514        }
1515    });
1516    i2.sort_by(|a, b| {
1517        if a == b {
1518            Equal
1519        } else if a > b {
1520            Greater
1521        } else {
1522            Less
1523        }
1524    });
1525
1526    for (t1, t2) in i1.iter().zip(i2.iter()) {
1527        use euclid::approxeq::ApproxEq;
1528        let p1 = curve.sample(*t1);
1529        let p2 = cubic.sample(*t2);
1530        assert!(p1.approx_eq(&p2), "{:?} == {:?}", p1, p2);
1531    }
1532    assert_eq!(i2.len(), 2);
1533    assert_eq!(i1.len(), 2);
1534}
1535
1536#[test]
1537fn line_intersections_degenerate_curve_on_line() {
1538    // A degenerate quadratic bézier where all three control points are
1539    // collinear and lie on the line. This makes both the quadratic (a)
1540    // and linear (b) coefficients zero in the intersection equation,
1541    // so the code path `c / b` computes 0/0 = NaN.
1542    // The function should return valid t values (not NaN/Inf), since
1543    // every point of the curve lies on the line.
1544
1545    // All points on the x-axis.
1546    let curve = QuadraticBezierSegment {
1547        from: point(0.0f64, 0.0),
1548        ctrl: point(1.0, 0.0),
1549        to: point(2.0, 0.0),
1550    };
1551
1552    // The x-axis itself.
1553    let line = Line {
1554        point: point(0.0, 0.0),
1555        vector: crate::vector(1.0, 0.0),
1556    };
1557
1558    let intersections = curve.line_intersections_t(&line);
1559
1560    // The curve lies entirely on the line, so no returned t should be NaN or Inf.
1561    for &t in intersections.iter() {
1562        assert!(
1563            t.is_finite(),
1564            "Expected finite t value, got {:?}", t
1565        );
1566        assert!(
1567            t >= 0.0 && t <= 1.0,
1568            "Expected t in [0, 1], got {:?}", t
1569        );
1570    }
1571
1572    // This is contentious: the curve lies entirely on the line so we could
1573    // consider that all points are intersection points, or just pick one point
1574    // (for example the mid point) as the intersection, but instead we consider
1575    // that the curve does not intersect the line.
1576    // We could decide to change this behavior in the future. For now this is
1577    // consistent with the line-line intersection code.
1578    assert!(intersections.is_empty());
1579}
1580
1581#[test]
1582fn drag() {
1583    let curve = QuadraticBezierSegment {
1584        from: point(0.0f32, 0.0),
1585        ctrl: point(100.0, 0.0),
1586        to: point(100.0, 100.0),
1587    };
1588
1589    for t in [0.5, 0.25, 0.1, 0.4, 0.7] {
1590        let target = point(0.0, 10.0);
1591
1592        let dragged = curve.drag(t, target);
1593
1594        use euclid::approxeq::ApproxEq;
1595        let p1 = dragged.sample(t);
1596        assert!(
1597            p1.approx_eq_eps(&target, &point(0.001, 0.001)),
1598            "{:?} == {:?}",
1599            p1,
1600            target
1601        );
1602    }
1603}
1604
1605#[test]
1606fn arc_length() {
1607    let curves = [
1608        QuadraticBezierSegment {
1609            from: point(0.0f64, 0.0),
1610            ctrl: point(100.0, 0.0),
1611            to: point(0.0, 100.0),
1612        },
1613        QuadraticBezierSegment {
1614            from: point(0.0, 0.0),
1615            ctrl: point(100.0, 0.0),
1616            to: point(200.0, 0.0),
1617        },
1618        QuadraticBezierSegment {
1619            from: point(100.0, 0.0),
1620            ctrl: point(0.0, 0.0),
1621            to: point(50.0, 1.0),
1622        },
1623    ];
1624
1625    for (idx, curve) in curves.iter().enumerate() {
1626        let length = curve.length();
1627        let mut accum = 0.0;
1628        curve.for_each_flattened(0.00000001, &mut |line| {
1629            accum += line.length();
1630        });
1631
1632        assert!(
1633            (length - accum).abs() < 0.00001,
1634            "curve {:?}, {:?} == {:?}",
1635            idx,
1636            length,
1637            accum
1638        );
1639    }
1640}