beziercurve_wkt/
intersection.rs

1#![allow(non_snake_case)]
2
3//! Module for calculating curve-curve
4
5use crate::{Point, Line, QuadraticCurve, CubicCurve};
6
7type OptionTuple<T> = (Option<T>, Option<T>, Option<T>);
8
9#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
10pub struct BezierNormalVector { pub x: f64, pub y: f64 }
11
12#[derive(Debug, Clone, PartialEq, PartialOrd)]
13pub enum IntersectionResult {
14    NoIntersection,
15    FoundIntersection(Intersection),
16    Infinite(InfiniteIntersections)
17}
18
19#[derive(Debug, Clone, PartialEq, PartialOrd)]
20pub enum Intersection {
21    LineLine(LineLineIntersection),
22    LineQuad(LineQuadIntersection),
23    LineCubic(LineCubicIntersection),
24    QuadLine(QuadLineIntersection),
25    QuadQuad(Vec<QuadQuadIntersection>),
26    QuadCubic(Vec<QuadCubicIntersection>),
27    CubicLine(CubicLineIntersection),
28    CubicQuad(Vec<CubicQuadIntersection>),
29    CubicCubic(Vec<CubicCubicIntersection>),
30}
31
32/// When two curves are the same, they have infinite intersections
33/// Currently, this is treated the same as having no intersections
34#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
35pub enum InfiniteIntersections {
36    LineLine(Line, Line),
37    QuadQuad(QuadraticCurve, QuadraticCurve),
38    QuadCubic(QuadraticCurve, CubicCurve),
39    CubicQuad(CubicCurve, QuadraticCurve),
40    CubicCubic(CubicCurve, CubicCurve),
41}
42
43#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
44pub struct LineLineIntersection {
45    pub t1: f64,
46    pub line1: Line,
47    pub t2: f64,
48    pub line2: Line,
49}
50
51impl LineLineIntersection {
52
53    #[inline]
54    pub fn get_intersection_point_1(&self) -> Point {
55        // lerp(line.0, line.1, t1)
56        let new_x = (1.0 - self.t1) * self.line1.0.x + self.t1 * self.line1.1.x;
57        let new_y = (1.0 - self.t1) * self.line1.0.y + self.t1 * self.line1.1.y;
58        Point::new(new_x, new_y)
59    }
60
61    #[inline]
62    pub fn get_intersection_point_2(&self) -> Point {
63        // lerp(line.0, line.1, t2)
64        let new_x = (1.0 - self.t2) * self.line2.0.x + self.t2 * self.line2.1.x;
65        let new_y = (1.0 - self.t2) * self.line2.0.y + self.t2 * self.line2.1.y;
66        Point::new(new_x, new_y)
67    }
68}
69
70macro_rules! cubic_line {($structname:ident, $curvetype:ident) => (
71    // A line-curve intersection can intersect in up to 3 points
72    #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
73    pub enum $structname {
74        Intersect1 {
75            curve: $curvetype,
76            line: Line,
77            t_curve_1: f64,
78            t_line_1: f64,
79        },
80        Intersect2 {
81            curve: $curvetype,
82            line: Line,
83            t_curve_1: f64,
84            t_line_1: f64,
85            t_curve_2: f64,
86            t_line_2: f64,
87        },
88        Intersect3 {
89            curve: $curvetype,
90            line: Line,
91            t_curve_1: f64,
92            t_line_1: f64,
93            t_curve_2: f64,
94            t_line_2: f64,
95            t_curve_3: f64,
96            t_line_3: f64,
97        }
98    }
99
100    impl $structname {
101
102        #[inline]
103        pub fn get_curve_t1(&self) -> f64 {
104            use self::$structname::*;
105            match self {
106                Intersect1 { t_curve_1, .. } => *t_curve_1,
107                Intersect2 { t_curve_1, .. } => *t_curve_1,
108                Intersect3 { t_curve_1, .. } => *t_curve_1,
109            }
110        }
111
112        #[inline]
113        pub fn get_curve_t2(&self) -> Option<f64> {
114            use self::$structname::*;
115            match self {
116                Intersect1 { .. } => None,
117                Intersect2 { t_curve_2, .. } => Some(*t_curve_2),
118                Intersect3 { t_curve_2, .. } => Some(*t_curve_2),
119            }
120        }
121
122        #[inline]
123        pub fn get_curve_t3(&self) -> Option<f64> {
124            use self::$structname::*;
125            match self {
126                Intersect1 { .. } => None,
127                Intersect2 { .. } => None,
128                Intersect3 { t_curve_3, .. } => Some(*t_curve_3),
129            }
130        }
131
132        #[inline]
133        pub fn get_line_t1(&self) -> f64 {
134            use self::$structname::*;
135            match self {
136                Intersect1 { t_line_1, .. } => *t_line_1,
137                Intersect2 { t_line_1, .. } => *t_line_1,
138                Intersect3 { t_line_1, .. } => *t_line_1,
139            }
140        }
141
142        #[inline]
143        pub fn get_line_t2(&self) -> Option<f64> {
144            use self::$structname::*;
145            match self {
146                Intersect1 { .. } => None,
147                Intersect2 { t_line_2, .. } => Some(*t_line_2),
148                Intersect3 { t_line_2, .. } => Some(*t_line_2),
149            }
150        }
151
152        #[inline]
153        pub fn get_line_t3(&self) -> Option<f64> {
154            use self::$structname::*;
155            match self {
156                Intersect1 { .. } => None,
157                Intersect2 { .. } => None,
158                Intersect3 { t_line_3, .. } => Some(*t_line_3),
159            }
160        }
161
162        #[inline]
163        pub fn get_intersection_point_1(&self) -> Point {
164            use self::$structname::*;
165            match self {
166                Intersect1 { line, t_line_1, .. } => lerp(line.0, line.1, *t_line_1),
167                Intersect2 { line, t_line_1, .. } => lerp(line.0, line.1, *t_line_1),
168                Intersect3 { line, t_line_1, .. } => lerp(line.0, line.1, *t_line_1),
169            }
170        }
171
172        #[inline]
173        pub fn get_intersection_point_2(&self) -> Option<Point> {
174            use self::$structname::*;
175            match self {
176                Intersect1 { .. } => None,
177                Intersect2 { line, t_line_2, .. } => Some(lerp(line.0, line.1, *t_line_2)),
178                Intersect3 { line, t_line_2, .. } => Some(lerp(line.0, line.1, *t_line_2)),
179            }
180        }
181
182        #[inline]
183        pub fn get_intersection_point_3(&self) -> Option<Point> {
184            use self::$structname::*;
185            match self {
186                Intersect1 { .. } => None,
187                Intersect2 { .. } => None,
188                Intersect3 { line, t_line_3, .. } => Some(lerp(line.0, line.1, *t_line_3)),
189            }
190        }
191    }
192)}
193
194cubic_line!(LineQuadIntersection, QuadraticCurve);
195cubic_line!(LineCubicIntersection, CubicCurve);
196
197macro_rules! cubic_cubic {($structname:ident, $curvetype:ident, $evaluate_fn:ident) => (
198    #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
199    pub struct $structname {
200        pub t1: f64,
201        pub curve1: $curvetype,
202        pub t2: f64,
203        pub curve2: $curvetype,
204    }
205
206    impl $structname {
207        pub fn get_intersection_point_1(&self) -> Point {
208            $evaluate_fn(self.curve1, self.t1)
209        }
210
211        pub fn get_intersection_point_2(&self) -> Point {
212            $evaluate_fn(self.curve2, self.t2)
213        }
214    }
215)}
216
217cubic_line!(QuadLineIntersection, QuadraticCurve);
218cubic_cubic!(QuadQuadIntersection, QuadraticCurve, quadratic_evaluate);
219
220#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
221pub struct QuadCubicIntersection {
222    pub t1: f64,
223    pub curve1: QuadraticCurve,
224    pub t2: f64,
225    pub curve2: CubicCurve,
226}
227
228impl QuadCubicIntersection {
229    pub fn get_intersection_point_1(&self) -> Point {
230        quadratic_evaluate(self.curve1, self.t1)
231    }
232
233    pub fn get_intersection_point_2(&self) -> Point {
234        evaluate(self.curve2, self.t2)
235    }
236}
237
238cubic_line!(CubicLineIntersection, CubicCurve);
239
240#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
241pub struct CubicQuadIntersection {
242    pub t1: f64,
243    pub curve1: CubicCurve,
244    pub t2: f64,
245    pub curve2: QuadraticCurve,
246}
247
248impl CubicQuadIntersection {
249    pub fn get_intersection_point_1(&self) -> Point {
250        evaluate(self.curve1, self.t1)
251    }
252
253    pub fn get_intersection_point_2(&self) -> Point {
254        quadratic_evaluate(self.curve2, self.t2)
255    }
256}
257
258cubic_cubic!(CubicCubicIntersection, CubicCurve, evaluate);
259
260pub(crate) fn line_line_intersect(line1: Line, line2: Line) -> IntersectionResult {
261    use self::{Intersection::*, IntersectionResult::*};
262
263    if line1 == line2 {
264        return Infinite(InfiniteIntersections::LineLine(line1, line2));
265    }
266
267    if line1.0 == line1.1 || line2.0 == line2.1 {
268        return NoIntersection;
269    }
270
271    match do_line_line_intersect(line1, line2) {
272        None => NoIntersection,
273        Some(s) => FoundIntersection(LineLine(s)),
274    }
275}
276
277pub(crate) fn line_quad_intersect(line1: Line, curve1: QuadraticCurve) -> IntersectionResult {
278    use self::{Intersection::*, LineQuadIntersection::*, IntersectionResult::*};
279
280    if line1.0 == line1.1 || (curve1.0 == curve1.1 && curve1.1 == curve1.2) {
281        return NoIntersection;
282    }
283
284    match do_curve_line_intersect(quadratic_to_cubic_curve(curve1), line1) {
285        (Some((t_line_1, t_curve_1)), None, None) =>
286            FoundIntersection(LineQuad(Intersect1 {
287                curve: curve1,
288                line: line1,
289                t_curve_1,
290                t_line_1,
291            })),
292        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), None) =>
293            FoundIntersection(LineQuad(Intersect2 {
294                curve: curve1,
295                line: line1,
296                t_curve_1,
297                t_line_1,
298                t_curve_2,
299                t_line_2,
300            })),
301        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), Some((t_line_3, t_curve_3))) =>
302            FoundIntersection(LineQuad(Intersect3 {
303                curve: curve1,
304                line: line1,
305                t_curve_1,
306                t_line_1,
307                t_curve_2,
308                t_line_2,
309                t_curve_3,
310                t_line_3,
311            })),
312        _ => NoIntersection,
313    }
314}
315
316pub(crate) fn line_cubic_intersect(line1: Line, curve1: CubicCurve) -> IntersectionResult {
317    use self::{Intersection::*, LineCubicIntersection::*, IntersectionResult::*};
318
319    if line1.0 == line1.1 || (curve1.0 == curve1.1 && curve1.1 == curve1.2 && curve1.2 == curve1.3) {
320        return NoIntersection;
321    }
322
323    match do_curve_line_intersect(curve1, line1) {
324        (Some((t_line_1, t_curve_1)), None, None) =>
325            FoundIntersection(LineCubic(Intersect1 {
326                curve: curve1,
327                line: line1,
328                t_curve_1,
329                t_line_1,
330            })),
331        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), None) =>
332            FoundIntersection(LineCubic(Intersect2 {
333                curve: curve1,
334                line: line1,
335                t_curve_1,
336                t_line_1,
337                t_curve_2,
338                t_line_2,
339            })),
340        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), Some((t_line_3, t_curve_3))) =>
341            FoundIntersection(LineCubic(Intersect3 {
342                curve: curve1,
343                line: line1,
344                t_curve_1,
345                t_line_1,
346                t_curve_2,
347                t_line_2,
348                t_curve_3,
349                t_line_3,
350            })),
351        _ => NoIntersection,
352    }
353}
354
355pub(crate) fn quad_line_intersect(curve1: QuadraticCurve, line1: Line) -> IntersectionResult {
356    use self::{Intersection::*, QuadLineIntersection::*, IntersectionResult::*};
357
358    if line1.0 == line1.1 || (curve1.0 == curve1.1 && curve1.1 == curve1.2) {
359        return NoIntersection;
360    }
361
362    match do_curve_line_intersect(quadratic_to_cubic_curve(curve1), line1) {
363        (Some((t_line_1, t_curve_1)), None, None) =>
364            FoundIntersection(QuadLine(Intersect1 {
365                curve: curve1,
366                line: line1,
367                t_curve_1,
368                t_line_1,
369            })),
370        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), None) =>
371            FoundIntersection(QuadLine(Intersect2 {
372                curve: curve1,
373                line: line1,
374                t_curve_1,
375                t_line_1,
376                t_curve_2,
377                t_line_2,
378            })),
379        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), Some((t_line_3, t_curve_3))) =>
380            FoundIntersection(QuadLine(Intersect3 {
381                curve: curve1,
382                line: line1,
383                t_curve_1,
384                t_line_1,
385                t_curve_2,
386                t_line_2,
387                t_curve_3,
388                t_line_3,
389            })),
390        _ => NoIntersection,
391    }
392}
393
394pub(crate) fn quad_quad_intersect(curve1: QuadraticCurve, curve2:  QuadraticCurve) -> IntersectionResult {
395    use self::{Intersection::*, IntersectionResult::*};
396
397    if curve1 == curve2 {
398        return Infinite(InfiniteIntersections::QuadQuad(curve1, curve2));
399    }
400
401    if (curve1.0 == curve1.1 && curve1.1 == curve1.2) || (curve2.0 == curve2.1 && curve2.1 == curve2.2) {
402        return NoIntersection;
403    }
404
405    match do_curve_curve_intersect(quadratic_to_cubic_curve(curve1), quadratic_to_cubic_curve(curve2)) {
406        None => NoIntersection,
407        Some(s) => {
408            FoundIntersection(QuadQuad(s
409                .into_iter()
410                .map(|c| QuadQuadIntersection { curve1, curve2, t1: c.t1, t2: c.t2 })
411                .collect())
412            )
413        }
414    }
415}
416
417pub(crate) fn quad_cubic_intersect(curve1: QuadraticCurve, curve2: CubicCurve) -> IntersectionResult {
418    use self::{Intersection::*, IntersectionResult::*};
419
420    if (curve1.0 == curve1.1 && curve1.1 == curve1.2) || (curve2.0 == curve2.1 && curve2.1 == curve2.2 && curve2.2 == curve2.3) {
421        return NoIntersection;
422    }
423
424    let curve1_new = quadratic_to_cubic_curve(curve1);
425
426    if curve1_new == curve2 {
427        return Infinite(InfiniteIntersections::QuadCubic(curve1, curve2));
428    }
429
430    match do_curve_curve_intersect(curve1_new, curve2) {
431        None => NoIntersection,
432        Some(s) => {
433            FoundIntersection(QuadCubic(s
434                .into_iter()
435                .map(|c| QuadCubicIntersection { curve1, curve2, t1: c.t1, t2: c.t2 })
436                .collect())
437            )
438        }
439    }
440}
441
442pub(crate) fn cubic_line_intersect(curve1: CubicCurve, line1: Line) -> IntersectionResult {
443
444    use self::{Intersection::*, CubicLineIntersection::*, IntersectionResult::*};
445
446    if line1.0 == line1.1 || (curve1.0 == curve1.1 && curve1.1 == curve1.2 && curve1.2 == curve1.3) {
447        return NoIntersection;
448    }
449
450    match do_curve_line_intersect(curve1, line1) {
451        (Some((t_line_1, t_curve_1)), None, None) =>
452            FoundIntersection(CubicLine(Intersect1 {
453                curve: curve1,
454                line: line1,
455                t_curve_1,
456                t_line_1,
457            })),
458        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), None) =>
459            FoundIntersection(CubicLine(Intersect2 {
460                curve: curve1,
461                line: line1,
462                t_curve_1,
463                t_line_1,
464                t_curve_2,
465                t_line_2,
466            })),
467        (Some((t_line_1, t_curve_1)), Some((t_line_2, t_curve_2)), Some((t_line_3, t_curve_3))) =>
468            FoundIntersection(CubicLine(Intersect3 {
469                curve: curve1,
470                line: line1,
471                t_curve_1,
472                t_line_1,
473                t_curve_2,
474                t_line_2,
475                t_curve_3,
476                t_line_3,
477            })),
478        _ => NoIntersection,
479    }
480}
481
482pub(crate) fn cubic_quad_intersect(curve1: CubicCurve, curve2: QuadraticCurve) -> IntersectionResult {
483    use self::{Intersection::*, IntersectionResult::*};
484
485    if (curve1.0 == curve1.1 && curve1.1 == curve1.2 && curve1.2 == curve1.3) || (curve2.0 == curve2.1 && curve2.1 == curve2.2) {
486        return NoIntersection;
487    }
488
489    let curve2_new = quadratic_to_cubic_curve(curve2);
490
491    if curve2_new == curve1 {
492        return Infinite(InfiniteIntersections::CubicQuad(curve1, curve2));
493    }
494
495    match do_curve_curve_intersect(curve1, curve2_new) {
496        None => NoIntersection,
497        Some(s) => {
498            FoundIntersection(CubicQuad(s
499                .into_iter()
500                .map(|c| CubicQuadIntersection { curve1, curve2, t1: c.t1, t2: c.t2 })
501                .collect())
502            )
503        }
504    }
505}
506
507pub(crate) fn cubic_cubic_intersect(curve1: CubicCurve, curve2: CubicCurve) -> IntersectionResult {
508    use self::{Intersection::*, IntersectionResult::*};
509
510    if curve1 == curve2 {
511        return Infinite(InfiniteIntersections::CubicCubic(curve1, curve2));
512    }
513
514    if (curve1.0 == curve1.1 && curve1.1 == curve1.2) || (curve2.0 == curve2.1 && curve2.1 == curve2.2) {
515        return NoIntersection;
516    }
517
518    match do_curve_curve_intersect(curve1, curve2) {
519        None => NoIntersection,
520        Some(s) => FoundIntersection(CubicCubic(s)),
521    }
522}
523
524#[inline]
525pub(crate) fn split_line(line: Line, t: f64) -> (Line, Line) {
526    let t = t.max(0.0).min(1.0);
527    let split_point = lerp(line.0, line.1, t);
528    (
529        (line.0, split_point),
530        (split_point, line.1),
531    )
532}
533
534#[inline]
535pub(crate) fn split_quad(curve: QuadraticCurve, t: f64) -> (QuadraticCurve, QuadraticCurve) {
536    let t = t.max(0.0).min(1.0);
537    let p = quad_hull_points(curve, t);
538    ((p[0], p[3], p[5]), (p[5], p[4], p[2]))
539}
540
541#[inline]
542pub(crate) fn split_cubic(curve: CubicCurve, t: f64) -> (CubicCurve, CubicCurve) {
543    let t = t.max(0.0).min(1.0);
544    subdivide(curve, t)
545}
546
547//  Determines the intersection point of the line defined by points A and B with the
548//  line defined by points C and D.
549//
550//  Returns YES if the intersection point was found, and stores that point in X,Y.
551//  Returns NO if there is no determinable intersection point, in which case X,Y will
552//  be unmodified.
553#[inline]
554fn do_line_line_intersect(
555    (a, b): Line,
556    (c, d): Line,
557) -> Option<LineLineIntersection> {
558
559    let (original_a, original_b) = (a, b);
560    let (original_c, original_d) = (c, d);
561
562    //  (1) Translate the system so that point A is on the origin.
563    let b = Point::new(b.x - a.x, b.y - a.y);
564    let mut c = Point::new(c.x - a.x, c.y - a.y);
565    let mut d = Point::new(d.x - a.x, d.y - a.y);
566
567    // Get the length from a to b
568    let dist_ab = (b.x*b.x + b.y*b.y).sqrt();
569
570    // Rotate the system so that point B is on the positive X axis.
571    let cos_b = b.x / dist_ab;
572    let sin_b = b.y / dist_ab;
573
574    // Rotate c and d around b
575    let new_x = c.x * cos_b + c.y * sin_b;
576    c.y = c.y * cos_b - c.x * sin_b;
577    c.x = new_x;
578
579    let new_x = d.x * cos_b + d.y * sin_b;
580    d.y = d.y * cos_b - d.x * sin_b;
581    d.x = new_x;
582
583    // Fail if the lines are parallel
584    if c.y == d.y {
585        return None;
586    }
587
588    // Calculate the position of the intersection point along line A-B.
589    let t = d.x + (c.x - d.x) * d.y / (d.y - c.y);
590
591    let new_x = original_a.x + t * cos_b;
592    let new_y = original_a.y + t * sin_b;
593
594    // The t projected onto the line a - b
595    let t1 = (
596        ((new_x - original_a.x) / (original_b.x - original_a.x)) +
597        ((new_y - original_a.y) / (original_b.y - original_a.y))
598    ) / 2.0;
599
600    // The t projected onto the line b - c
601    let t2 = (
602        ((new_x - original_c.x) / (original_d.x - original_c.x)) +
603        ((new_y - original_c.y) / (original_d.y - original_c.y))
604    ) / 2.0;
605
606    Some(LineLineIntersection {
607        t1,
608        line1: (original_a, original_b),
609        t2,
610        line2: (original_c, original_d),
611    })
612}
613
614/// Intersect a cubic curve with a line.
615///
616/// Based on http://www.particleincell.com/blog/2013/cubic-line-intersection/
617// https://jsbin.com/nawoxemopa/1/edit?js,console
618#[inline]
619fn do_curve_line_intersect(
620    (a1, a2, a3, a4): CubicCurve,
621    (b1, b2): Line,
622) -> OptionTuple<(f64, f64)> {
623
624    // If the numbers are below 10.0, the algorithm has
625    // problems with precision, multiply by 100
626    // also, round to 3 decimals to avoid precision issues
627    let numbers = [a1.x, a1.y, a2.x, a2.y, a3.x, a3.y, a4.x, a4.y, b1.x, b1.y, b2.x, b2.y];
628    let mut lowest_number = 0.0_f64;
629    for n in &numbers { lowest_number = lowest_number.min(*n); }
630    let smallest_number_abs = lowest_number.abs();
631    let multiplier = if smallest_number_abs != 0.0 { 100.0 / smallest_number_abs } else { 100.0 };
632
633    let a1 = Point::new(round_3(a1.x * multiplier), round_3(a1.y * multiplier));
634    let a2 = Point::new(round_3(a2.x * multiplier), round_3(a2.y * multiplier));
635    let a3 = Point::new(round_3(a3.x * multiplier), round_3(a3.y * multiplier));
636    let a4 = Point::new(round_3(a4.x * multiplier), round_3(a4.y * multiplier));
637    let b1 = Point::new(round_3(b1.x * multiplier), round_3(b1.y * multiplier));
638    let b2 = Point::new(round_3(b2.x * multiplier), round_3(b2.y * multiplier));
639
640    #[inline]
641    fn round_3(input: f64) -> f64 {
642        ((input * 1000.0_f64) as u64) as f64 / 1000.0_f64
643    }
644
645    let A = b2.y - b1.y; // A = y2 - y1
646    let B = b1.x - b2.x; // B = x1 - x2
647    let C = b1.x * (b1.y - b2.y) + b1.y * (b2.x - b1.x); // C = x1*(y1-y2)+y1*(x2-x1)
648
649    let bx = bezier_coeffs(a1.x, a2.x, a3.x, a4.x);
650    let by = bezier_coeffs(a1.y, a2.y, a3.y, a4.y);
651
652    let p_0 = A * bx.0 + B * by.0;     // t^3
653    let p_1 = A * bx.1 + B * by.1;     // t^2
654    let p_2 = A * bx.2 + B * by.2;     // t
655    let p_3 = A * bx.3 + B * by.3 + C; // 1
656
657    let r = cubic_roots(p_0, p_1, p_2, p_3);
658
659    let mut intersections = (None, None, None);
660
661    // for root in r
662    macro_rules! unroll_loop {($index:tt) => ({
663        if let Some(t) = r.$index {
664
665            let final_x = bx.0* t * t * t + bx.1 * t * t + bx.2 * t + bx.3;
666            let final_y = by.0* t * t * t + by.1 * t * t + by.2 * t + by.3;
667
668            // (final_x, final_y) is intersection point assuming infinitely long line segment,
669            // make sure we are also in bounds of the line
670
671            let x_dist = b2.x - b1.x;
672            let y_dist = b2.y - b1.y;
673
674            let t_line = if x_dist != 0.0 {
675                // if not vertical line
676                (final_x - b1.x) / x_dist
677            } else {
678                (final_y - b1.y) / y_dist
679            };
680
681            intersections.$index = if !t.is_sign_positive() || t > 1.0 || !t_line.is_sign_positive() || t_line > 1.0 {
682                None
683            } else {
684                Some((t_line as f64, t as f64))
685            }
686        }
687    })}
688
689    unroll_loop!(0);
690    unroll_loop!(1);
691    unroll_loop!(2);
692
693    intersections
694}
695
696// Intersect a quadratic with another quadratic curve
697fn do_curve_curve_intersect(a: CubicCurve, b: CubicCurve) -> Option<Vec<CubicCubicIntersection>> {
698
699    let intersections = curve_intersections_inner(a, b, 0.0, 1.0, 0.0, 1.0, 1.0, false, 0, 32, 0.8);
700
701    if intersections.is_empty() {
702        None
703    } else {
704        Some(intersections)
705    }
706}
707
708
709
710// --- helper functions
711
712
713// Generates all hull points, at all iterations, for an on-curve point
714// at the specified t-value. This generates a point[6], where the first iteration is
715// [0,1,2], the second iteration is [3,4], the third iteration is [5]
716// (the on-curve point)
717#[inline(always)]
718fn quad_hull_points(curve: QuadraticCurve, t: f64) -> [Point;6] {
719    let (p0, p1, p2) = curve;
720
721    // 2nd iteration
722    let p3 = lerp(p0, p1, t);
723    let p4 = lerp(p1, p2, t);
724
725    // 3rd iteration
726    let p5 = lerp(p3, p4, t);
727
728    [p0, p1, p2, p3, p4, p5]
729}
730
731/// Calculates the normal vector at a certain point (perpendicular to the curve)
732#[inline]
733pub(crate) fn cubic_bezier_normal(curve: CubicCurve, t: f64) -> BezierNormalVector {
734
735    // 1. Calculate the derivative of the bezier curve
736    //
737    // This means, we go from 4 control points to 3 control points and redistribute
738    // the weights of the control points according to the formula:
739    //
740    // w'0 = 3(w1-w0)
741    // w'1 = 3(w2-w1)
742    // w'2 = 3(w3-w2)
743
744    let weight_1_x = 3.0 * (curve.1.x - curve.0.x);
745    let weight_1_y = 3.0 * (curve.1.y - curve.0.y);
746
747    let weight_2_x = 3.0 * (curve.2.x - curve.1.x);
748    let weight_2_y = 3.0 * (curve.2.y - curve.1.y);
749
750    let weight_3_x = 3.0 * (curve.3.x - curve.2.x);
751    let weight_3_y = 3.0 * (curve.3.y - curve.2.y);
752
753    // The first derivative of a cubic bezier curve is a quadratic bezier curve
754    // Luckily, the first derivative is also the tangent vector. So all we need to do
755    // is to get the quadratic bezier
756    let mut tangent = quadratic_evaluate((
757        Point { x: weight_1_x, y: weight_1_y },
758        Point { x: weight_2_x, y: weight_2_y },
759        Point { x: weight_3_x, y: weight_3_y },
760    ), t);
761
762    // We normalize the tangent to have a lenght of 1
763    let tangent_length = (tangent.x.powi(2) + tangent.y.powi(2)).sqrt();
764    tangent.x /= tangent_length;
765    tangent.y /= tangent_length;
766
767    // The tangent is the vector that runs "along" the curve at a specific point.
768    // To get the normal (to calcuate the rotation of the characters), we need to
769    // rotate the tangent vector by 90 degrees.
770    //
771    // Rotating by 90 degrees is very simple, as we only need to flip the x and y axis
772
773    BezierNormalVector {
774        x: -tangent.y,
775        y: tangent.x,
776    }
777}
778
779/// Calculates the normal vector at a certain point (perpendicular to the curve)
780#[inline]
781pub(crate) fn quadratic_bezier_normal(curve: QuadraticCurve, t: f64) -> BezierNormalVector {
782    cubic_bezier_normal(quadratic_to_cubic_curve(curve), t)
783}
784
785/// Calculates the normal vector at a certain point (perpendicular to the curve)
786#[inline]
787pub(crate) fn line_normal(line: Line, _t: f64) -> BezierNormalVector {
788    // calculate the rise / run, then simply
789    // inverse the axis to rotate by 90 degrees
790    let diff_x = line.1.x - line.0.x;
791    let diff_y = line.1.y - line.0.y;
792    let line_length = (diff_x.powi(2) + diff_y.powi(2)).sqrt();
793    BezierNormalVector {
794        x: -diff_y / line_length,
795        y: diff_x / line_length,
796    }
797}
798
799#[inline]
800fn quadratic_evaluate(curve: QuadraticCurve, t: f64) -> Point {
801    let one_minus = 1.0 - t;
802    let one_minus_square = one_minus.powi(2);
803
804    let t_pow2 = t.powi(2);
805
806    let x =         one_minus_square *             curve.0.x
807            + 2.0 * one_minus        * t         * curve.1.x
808            + 3.0                    * t_pow2    * curve.2.x;
809
810    let y =         one_minus_square *             curve.0.y
811            + 2.0 * one_minus        * t         * curve.1.y
812            + 3.0                    * t_pow2    * curve.2.y;
813
814    Point { x, y }
815}
816
817// based on http://mysite.verizon.net/res148h4j/javascript/script_exact_cubic.html#the%20source%20code
818#[inline(always)]
819fn cubic_roots(a: f64, b: f64, c: f64, d: f64) -> (Option<f64>, Option<f64>, Option<f64>) {
820
821    use std::f64::consts::PI;
822
823    // special case for linear and quadratic case
824    if is_zero(a) {
825        if is_zero(b) {
826           // linear formula
827
828           let p = -1.0 * (d / c);
829
830           let ret = (
831               if !p.is_sign_positive() || p > 1.0 { None } else { Some(p) },
832               None,
833               None
834           );
835
836           let ret = sort_special(ret);
837
838           return ret;
839        } else {
840            // quadratic discriminant
841            let d_q = c.powi(2) - 4.0 * b * d;
842
843            if d_q.is_sign_positive() {
844                let d_q = d_q.sqrt();
845
846                let m = -1.0 * (d_q + c) / (2.0 * b);
847                let n = (d_q - c) / (2.0 * b);
848
849                let ret = (
850                    if !m.is_sign_positive() || m > 1.0 { None } else { Some(m) },
851                    if !n.is_sign_positive() || n > 1.0 { None } else { Some(n) },
852                    None,
853                );
854
855                let ret = sort_special(ret);
856
857                return ret;
858            }
859        }
860    }
861
862    let A = b / a;
863    let B = c / a;
864    let C = d / a;
865
866    let Q = (3.0 * B - (A*A)) / 9.0;
867    let R = (9.0 * A * B - 27.0 * C - 2.0 * (A*A*A)) / 54.0;
868    let D = Q*Q*Q + R*R; // polynomial discriminant
869
870    let ret = if D.is_sign_positive() {
871
872        // complex or duplicate roots
873        const ONE_THIRD: f64 = 1.0 / 3.0;
874
875        let D_sqrt = D.sqrt();
876        let S = sign(R + D_sqrt) * (R + D_sqrt).abs().powf(ONE_THIRD);
877        let T = sign(R - D_sqrt) * (R - D_sqrt).abs().powf(ONE_THIRD);
878
879        let m = -A / 3.0 + (S + T);         // real root
880        let n = -A / 3.0 - (S + T) / 2.0;   // real part of complex root
881        let p = -A / 3.0 - (S + T) / 2.0;   // real part of complex root
882
883        let mut ret = (
884            if !m.is_sign_positive() || m > 1.0 { None } else { Some(m) },
885            if !n.is_sign_positive() || n > 1.0 { None } else { Some(n) },
886            if !p.is_sign_positive() || p > 1.0 { None } else { Some(p) },
887        );
888
889        let imaginary = (3.0_f64.sqrt() * (S - T) / 2.0).abs(); // complex part of root pair
890
891        // discard complex roots
892        if !is_zero(imaginary) {
893            ret.1 = None;
894            ret.2 = None;
895        }
896
897        ret
898    } else {
899
900        let th = (R / (-1.0 * Q.powi(3)).sqrt()).acos();
901        let minus_q_sqrt = (-1.0 * Q).sqrt();
902
903        let m = 2.0 * minus_q_sqrt * (th / 3.0).cos() - A / 3.0;
904        let n = 2.0 * minus_q_sqrt * ((th + 2.0 * PI) / 3.0).cos() - A / 3.0;
905        let p = 2.0 * minus_q_sqrt * ((th + 4.0 * PI) / 3.0).cos() - A / 3.0;
906
907        // discard out of spec roots
908        (
909            if !m.is_sign_positive() || m > 1.0 { None } else { Some(m) },
910            if !n.is_sign_positive() || n > 1.0 { None } else { Some(n) },
911            if !p.is_sign_positive() || p > 1.0 { None } else { Some(p) },
912        )
913    };
914
915    // sort but place None at the end
916    let ret = sort_special(ret);
917
918    ret
919}
920
921#[inline]
922fn sign(a: f64) -> f64 {
923    if a.is_sign_positive() { 1.0 } else { -1.0 }
924}
925
926#[inline]
927fn bezier_coeffs(a: f64, b: f64, c: f64, d: f64) -> (f64, f64, f64, f64) {
928    (
929        -a + 3.0*b + -3.0*c + d,
930        3.0*a - 6.0*b + 3.0*c,
931        -3.0*a + 3.0*b,
932        a
933    )
934}
935
936// Sort so that the None values are at the end
937#[inline]
938fn sort_special(a: OptionTuple<f64>) -> OptionTuple<f64> {
939    match a {
940        (None, None, None) => (None, None, None),
941        (Some(a), None, None) |
942        (None, Some(a), None) |
943        (None, None, Some(a)) => (Some(a), None, None),
944        (Some(a), Some(b), None) |
945        (None, Some(a), Some(b)) |
946        (Some(b), None, Some(a)) => (Some(a.min(b)), Some(a.max(b)), None),
947        (Some(a), Some(b), Some(c)) => {
948            let new_a = a.min(b).min(c);
949            let new_b = if a < b && b < c { b } else if b < c && c < a { c } else { a };
950            let new_c = a.max(b).max(c);
951            (Some(new_a), Some(new_b), Some(new_c))
952        }
953    }
954}
955
956// Convert a quadratic bezier into a cubic bezier
957#[inline]
958fn quadratic_to_cubic_curve(c: QuadraticCurve) -> CubicCurve {
959    const TWO_THIRDS: f64 = 2.0 / 3.0;
960
961    let c1_x = c.0.x + TWO_THIRDS * (c.1.x - c.0.x);
962    let c1_y = c.0.y + TWO_THIRDS * (c.1.y - c.0.y);
963
964    let c2_x = c.2.x + TWO_THIRDS * (c.1.x - c.2.x);
965    let c2_y = c.2.y + TWO_THIRDS * (c.1.y - c.2.y);
966
967    (c.0, Point::new(c1_x, c1_y), Point::new(c2_x, c2_y), c.2)
968}
969
970/// Bezier curve intersection algorithm and utilities
971/// Directly extracted from PaperJS's implementation bezier curve fat-line clipping
972/// The original source code is available under the MIT license at
973///
974/// https://github.com/paperjs/paper.js/
975
976const TOLERANCE:f64 = 1e-5;
977const EPSILON: f64 = 1e-10;
978
979#[inline]
980fn is_zero(val: f64) -> bool {
981  val.abs() <= EPSILON
982}
983
984/// Computes the signed distance of (x, y) between (px, py) and (vx, vy)
985#[inline]
986fn signed_distance(px: f64, py: f64, mut vx: f64, mut vy: f64, x: f64, y: f64) -> f64 {
987    vx -= px;
988    vy -= py;
989    if is_zero(vx) {
990        if vy.is_sign_positive() { px - x } else { x - px }
991    } else if is_zero(vy) {
992        if vx.is_sign_positive() { y - py } else { py - y }
993    } else {
994        (vx * (y - py) - vy * (x - px)) / (vx * vx + vy * vy).sqrt()
995    }
996}
997
998/// Calculate the convex hull for the non-parametric bezier curve D(ti, di(t)).
999///
1000/// The ti is equally spaced across [0..1] — [0, 1/3, 2/3, 1] for
1001/// di(t), [dq0, dq1, dq2, dq3] respectively. In other words our CVs for the
1002/// curve are already sorted in the X axis in the increasing order.
1003/// Calculating convex-hull is much easier than a set of arbitrary points.
1004///
1005/// The convex-hull is returned as two parts [TOP, BOTTOM]. Both are in a
1006/// coordinate space where y increases upwards with origin at bottom-left
1007///
1008/// - TOP: The part that lies above the 'median' (line connecting end points of the curve)
1009/// - BOTTOM: The part that lies below the median.
1010#[inline]
1011fn convex_hull(dq0: f64, dq1: f64, dq2: f64, dq3: f64) -> [Vec<[f64;2]>;2] {
1012
1013    let p0 = [0.0, dq0];
1014    let p1 = [1.0 / 3.0, dq1];
1015    let p2 = [2.0 / 3.0, dq2];
1016    let p3 = [1.0, dq3];
1017
1018    // Find signed distance of p1 and p2 from line [ p0, p3 ]
1019    let dist1 = signed_distance(0.0, dq0, 1.0, dq3, 1.0 / 3.0, dq1);
1020    let dist2 = signed_distance(0.0, dq0, 1.0, dq3, 2.0 / 3.0, dq2);
1021
1022    // Check if p1 and p2 are on the same side of the line [ p0, p3 ]
1023    let (mut hull, flip) = if dist1 * dist2 < 0.0 {
1024        // p1 and p2 lie on different sides of [ p0, p3 ]. The hull is a
1025        // quadrilateral and line [ p0, p3 ] is NOT part of the hull so we
1026        // are pretty much done here.
1027        // The top part includes p1,
1028        // we will reverse it later if that is not the case
1029        let hull = [vec![p0, p1, p3], vec![p0, p2, p3]];
1030        let flip = dist1 < 0.0;
1031        (hull, flip)
1032    } else {
1033        // p1 and p2 lie on the same sides of [ p0, p3 ]. The hull can be
1034        // a triangle or a quadrilateral and line [ p0, p3 ] is part of the
1035        // hull. Check if the hull is a triangle or a quadrilateral.
1036        // Also, if at least one of the distances for p1 or p2, from line
1037        // [p0, p3] is zero then hull must at most have 3 vertices.
1038        let (cross, pmax) = if dist1.abs() > dist2.abs() {
1039            // apex is dq3 and the other apex point is dq0 vector dqapex ->
1040            // dqapex2 or base vector which is already part of the hull.
1041            let cross = (dq3 - dq2 - (dq3 - dq0) / 3.0) * (2.0 * (dq3 - dq2) - dq3 + dq1) / 3.0;
1042            (cross, p1)
1043        } else {
1044            // apex is dq0 in this case, and the other apex point is dq3
1045            // vector dqapex -> dqapex2 or base vector which is already part
1046            // of the hull.
1047            let cross = (dq1 - dq0 + (dq0 - dq3) / 3.0) * (-2.0 * (dq0 - dq1) + dq0 - dq2) / 3.0;
1048            (cross, p2)
1049        };
1050
1051        let distZero = is_zero(dist1) || is_zero(dist2);
1052
1053        // Compare cross products of these vectors to determine if the point
1054        // is in the triangle [ p3, pmax, p0 ], or if it is a quadrilateral.
1055        let hull = if cross < 0.0 || distZero {
1056            [vec![p0, pmax, p3], vec![p0, p3]]
1057        } else {
1058            [vec![p0, p1, p2, p3], vec![p0, p3]]
1059        };
1060
1061        let flip = if is_zero(dist1) { !dist2.is_sign_positive() } else {  !dist1.is_sign_positive() };
1062
1063        (hull, flip)
1064    };
1065
1066    if flip {
1067      hull.reverse();
1068    }
1069
1070    hull
1071}
1072
1073/// Clips the convex-hull and returns [tMin, tMax] for the curve contained.
1074#[inline]
1075fn clip_convex_hull(hullTop: &[[f64;2]], hullBottom: &[[f64;2]], dMin: f64, dMax: f64) -> Option<f64> {
1076    if hullTop[0][1] < dMin {
1077        // Left of hull is below dMin, walk through the hull until it
1078        // enters the region between dMin and dMax
1079        clip_convex_hull_part(hullTop, true, dMin)
1080    } else if hullBottom[0][1] > dMax {
1081        // Left of hull is above dMax, walk through the hull until it
1082        // enters the region between dMin and dMax
1083        clip_convex_hull_part(hullBottom, false, dMax)
1084    } else {
1085        // Left of hull is between dMin and dMax, no clipping possible
1086        Some(hullTop[0][0])
1087    }
1088}
1089
1090#[inline]
1091fn clip_convex_hull_part(part: &[[f64;2]], top: bool, threshold: f64) -> Option<f64> {
1092    let mut pxpy = part[0];
1093
1094    for [qx, qy] in part.iter().copied() {
1095        let [px, py] = pxpy;
1096        let a = if top { qy >= threshold } else { qy <= threshold };
1097        if a {
1098            return Some(px + (threshold - py) * (qx - px) / (qy - py));
1099        }
1100        pxpy = [qx, qy];
1101    }
1102
1103    // All points of hull are above / below the threshold
1104    None
1105}
1106
1107/// Calculates the fat line of a curve and returns the maximum and minimum offset widths
1108/// for the fatline of a curve
1109#[inline]
1110fn get_fatline((p0, p1, p2, p3): CubicCurve) -> (f64, f64) {
1111
1112    // Calculate the fat-line L, for Q is the baseline l and two
1113    // offsets which completely encloses the curve P.
1114    let d1 = signed_distance(p0.x, p0.y, p3.x, p3.y, p1.x, p1.y);
1115    let d2 = signed_distance(p0.x, p0.y, p3.x, p3.y, p2.x, p2.y);
1116    let factor = if (d1 * d2).is_sign_positive() { 3.0 / 4.0 } else { 4.0 / 9.0 }; // Get a tighter fit
1117    let dMin = factor * 0.0_f64.min(d1).min(d2);
1118    let dMax = factor * 0.0_f64.max(d1).max(d2);
1119
1120    // The width of the 'fatline' is |dMin| + |dMax|
1121    (dMin, dMax)
1122}
1123
1124#[inline]
1125fn subdivide((p1, c1, c2, p2): CubicCurve, t: f64) -> (CubicCurve, CubicCurve) {
1126
1127    // Triangle computation, with loops unrolled.
1128    let u = 1.0 - t;
1129
1130    // Interpolate from 4 to 3 points
1131    let p3x = u * p1.x + t * c1.x;
1132    let p3y = u * p1.y + t * c1.y;
1133    let p4x = u * c1.x + t * c2.x;
1134    let p4y = u * c1.y + t * c2.y;
1135    let p5x = u * c2.x + t * p2.x;
1136    let p5y = u * c2.y + t * p2.y;
1137
1138    // Interpolate from 3 to 2 points
1139    let p6x = u * p3x + t * p4x;
1140    let p6y = u * p3y + t * p4y;
1141    let p7x = u * p4x + t * p5x;
1142    let p7y = u * p4y + t * p5y;
1143
1144    // Interpolate from 2 points to 1 point
1145    let p8x = u * p6x + t * p7x;
1146    let p8y = u * p6y + t * p7y;
1147
1148    // We now have all the values we need to build the sub-curves [left, right]:
1149    (
1150      (p1, Point::new(p3x, p3y), Point::new(p6x, p6y), Point::new(p8x, p8y)),
1151      (Point::new(p8x, p8y), Point::new(p7x, p7y), Point::new(p5x, p5y), p2)
1152    )
1153}
1154
1155/// Returns the part of a curve between t1 and t2
1156#[inline]
1157fn get_part(mut v: CubicCurve, t1: f64, t2: f64) -> CubicCurve {
1158
1159    if t1.is_sign_positive() {
1160        v = subdivide(v, t1).1; // right
1161    }
1162
1163    // Interpolate the parameter at 't2' in the new curve and cut there.
1164    if t2 < 1.0 {
1165        v = subdivide(v, (t2 - t1) / (1.0 - t1)).0; // left
1166    }
1167
1168    v
1169}
1170
1171/// Calculates the coordinates of the point on a bezier curve at a given t
1172#[inline]
1173fn evaluate((p1, c1, c2, p2): CubicCurve, t: f64) -> Point {
1174
1175  // Handle special case at beginning / end of curve
1176  if t < TOLERANCE || t > (1.0 - TOLERANCE) {
1177        let is_zero = t < TOLERANCE;
1178        let x = if is_zero { p1.x } else { p2.x };
1179        let y = if is_zero { p1.y } else { p2.y };
1180        Point::new(x, y)
1181  } else {
1182        // Calculate the polynomial coefficients.
1183        let cx = 3.0 * (c1.x - p1.x);
1184        let bx = 3.0 * (c2.x - c1.x) - cx;
1185        let ax = p2.x - p1.x - cx - bx;
1186
1187        let cy = 3.0 * (c1.y - p1.y);
1188        let by = 3.0 * (c2.y - c1.y) - cy;
1189        let ay = p2.y - p1.y - cy - by;
1190
1191        // Calculate the curve point at parameter value t
1192        let x = ((ax * t + bx) * t + cx) * t + p1.x;
1193        let y = ((ay * t + by) * t + cy) * t + p1.y;
1194
1195        Point::new(x, y)
1196    }
1197}
1198
1199/// Computes the intersections of two bezier curves
1200#[inline]
1201fn curve_intersections_inner(
1202    mut v1: CubicCurve,
1203    v2: CubicCurve,
1204    tMin: f64,
1205    tMax: f64,
1206    uMin: f64,
1207    uMax: f64,
1208    oldTDiff: f64,
1209    reverse: bool,
1210    recursion: usize,
1211    recursionLimit: usize,
1212    tLimit: f64
1213) -> Vec<CubicCubicIntersection> {
1214
1215    // Avoid deeper recursion.
1216    // NOTE: @iconexperience determined that more than 20 recursions are
1217    // needed sometimes, depending on the tDiff threshold values further
1218    // below when determining which curve converges the least. He also
1219    // recommended a threshold of 0.5 instead of the initial 0.8
1220    // See: https:#github.com/paperjs/paper.js/issues/565
1221    if recursion > recursionLimit {
1222      return Vec::new();
1223    }
1224
1225    // Let P be the first curve and Q be the second
1226
1227    // Calculate the fat-line L for Q is the baseline l and two
1228    // offsets which completely encloses the curve P.
1229    let (dMin, dMax) = get_fatline(v2);
1230
1231    // Calculate non-parametric bezier curve D(ti, di(t)) - di(t) is the
1232    // distance of P from the baseline l of the fat-line, ti is equally
1233    // spaced in [0, 1]
1234    let dp0 = signed_distance(v2.0.x, v2.0.y, v2.3.x, v2.3.y, v1.0.x, v1.0.y);
1235    let dp1 = signed_distance(v2.0.x, v2.0.y, v2.3.x, v2.3.y, v1.1.x, v1.1.y);
1236    let dp2 = signed_distance(v2.0.x, v2.0.y, v2.3.x, v2.3.y, v1.2.x, v1.2.y);
1237    let dp3 = signed_distance(v2.0.x, v2.0.y, v2.3.x, v2.3.y, v1.3.x, v1.3.y);
1238
1239    // NOTE: the recursion threshold of 4 is needed to prevent issue #571
1240    // from occurring: https://github.com/paperjs/paper.js/issues/571
1241    let (tMinNew, tMaxNew, tDiff) = if v2.0.x == v2.3.x && uMax - uMin <= EPSILON && recursion > 4 {
1242        // The fatline of Q has converged to a point, the clipping is not
1243        // reliable. Return the value we have even though we will miss the
1244        // precision.
1245        let tNew = (tMax + tMin) / 2.0;
1246        (tNew, tNew, 0.0)
1247    } else {
1248        // Get the top and bottom parts of the convex-hull
1249        let [mut top, mut bottom] = convex_hull(dp0, dp1, dp2, dp3);
1250
1251        // Clip the convex-hull with dMin and dMax
1252        let tMinClip = clip_convex_hull(&top, &bottom, dMin, dMax);
1253        top.reverse();
1254        bottom.reverse();
1255        let tMaxClip = clip_convex_hull(&top, &bottom, dMin, dMax);
1256
1257        // No intersections if one of the tvalues are null or 'undefined'
1258        let (tMinClip, tMaxClip) = match (tMinClip, tMaxClip) {
1259            (Some(min), Some(max)) => (min, max),
1260            _ => return Vec::new(),
1261        };
1262
1263        // Clip P with the fatline for Q
1264        v1 = get_part(v1, tMinClip, tMaxClip);
1265
1266        // tMin and tMax are within the range (0, 1). We need to project it
1267        // to the original parameter range for v2.
1268        let tDiff = tMaxClip - tMinClip;
1269        let tMinNew = tMax * tMinClip + tMin * (1.0 - tMinClip);
1270        let tMaxNew = tMax * tMaxClip + tMin * (1.0 - tMaxClip);
1271
1272        (tMinNew, tMaxNew, tDiff)
1273    };
1274
1275    // Check if we need to subdivide the curves
1276    if oldTDiff > tLimit && tDiff > tLimit {
1277        // Subdivide the curve which has converged the least.
1278        if tMaxNew - tMinNew > uMax - uMin {
1279            let parts = subdivide(v1, 0.5);
1280            let t = tMinNew + (tMaxNew - tMinNew) / 2.0;
1281            let mut intersections = Vec::new();
1282            intersections.append(&mut curve_intersections_inner(v2, parts.0, uMin, uMax, tMinNew, t, tDiff, !reverse, recursion + 1, recursionLimit, tLimit));
1283            intersections.append(&mut curve_intersections_inner(v2, parts.1, uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion + 1, recursionLimit, tLimit));
1284            intersections
1285        } else {
1286            let parts = subdivide(v2, 0.5);
1287            let t = uMin + (uMax - uMin) / 2.0;
1288            let mut intersections = Vec::new();
1289            intersections.append(&mut curve_intersections_inner(parts.0, v1, uMin, t, tMinNew, tMaxNew, tDiff, !reverse, recursion + 1, recursionLimit, tLimit));
1290            intersections.append(&mut curve_intersections_inner(parts.1, v1, t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion + 1, recursionLimit, tLimit));
1291            intersections
1292        }
1293    } else if (uMax - uMin).max(tMaxNew - tMinNew) < TOLERANCE {
1294        // We have isolated the intersection with sufficient precision
1295        let t1 = tMinNew + (tMaxNew - tMinNew) / 2.0;
1296        let t2 = uMin + (uMax - uMin) / 2.0;
1297        if reverse {
1298            vec![CubicCubicIntersection {
1299                t1: t2,
1300                curve1: v2,
1301                t2: t1,
1302                curve2: v1,
1303             }]
1304        } else {
1305            vec![CubicCubicIntersection {
1306                t1,
1307                curve1: v1,
1308                t2,
1309                curve2: v2,
1310            }]
1311        }
1312    } else {
1313        // Recurse
1314        curve_intersections_inner(v2, v1, uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion + 1, recursionLimit, tLimit)
1315    }
1316}
1317
1318#[inline(always)]
1319fn lerp(p1: Point, p2: Point, t: f64) -> Point {
1320    let new_x = (1.0 - t) * p1.x + t * p2.x;
1321    let new_y = (1.0 - t) * p1.y + t * p2.y;
1322    Point::new(new_x, new_y)
1323}