1use crate::cubic_bezier_intersections::cubic_bezier_intersections_t;
2use crate::scalar::Scalar;
3use crate::segment::{BoundingBox, Segment};
4use crate::traits::Transformation;
5use crate::utils::{cubic_polynomial_roots, min_max};
6use crate::{point, Box2D, Point, Vector};
7use crate::{Line, LineEquation, LineSegment, QuadraticBezierSegment};
8use arrayvec::ArrayVec;
9
10use core::cmp::Ordering::{Equal, Greater, Less};
11use core::ops::Range;
12
13#[cfg(test)]
14use std::vec::Vec;
15
16/// A 2d curve segment defined by four points: the beginning of the segment, two control
17/// points and the end of the segment.
18///
19/// The curve is defined by equation:²
20/// ```∀ t ∈ [0..1], P(t) = (1 - t)³ * from + 3 * (1 - t)² * t * ctrl1 + 3 * t² * (1 - t) * ctrl2 + t³ * to```
21#[derive(Copy, Clone, Debug, PartialEq)]
22#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
23pub struct CubicBezierSegment<S> {
24 pub from: Point<S>,
25 pub ctrl1: Point<S>,
26 pub ctrl2: Point<S>,
27 pub to: Point<S>,
28}
29
30impl<S: Scalar> CubicBezierSegment<S> {
31 /// Sample the curve at t (expecting t between 0 and 1).
32 pub fn sample(&self, t: S) -> Point<S> {
33 let t2 = t * t;
34 let t3 = t2 * t;
35 let one_t = S::ONE - t;
36 let one_t2 = one_t * one_t;
37 let one_t3 = one_t2 * one_t;
38
39 self.from * one_t3
40 + self.ctrl1.to_vector() * S::THREE * one_t2 * t
41 + self.ctrl2.to_vector() * S::THREE * one_t * t2
42 + self.to.to_vector() * t3
43 }
44
45 /// Sample the x coordinate of the curve at t (expecting t between 0 and 1).
46 pub fn x(&self, t: S) -> S {
47 let t2 = t * t;
48 let t3 = t2 * t;
49 let one_t = S::ONE - t;
50 let one_t2 = one_t * one_t;
51 let one_t3 = one_t2 * one_t;
52
53 self.from.x * one_t3
54 + self.ctrl1.x * S::THREE * one_t2 * t
55 + self.ctrl2.x * S::THREE * one_t * t2
56 + self.to.x * t3
57 }
58
59 /// Sample the y coordinate of the curve at t (expecting t between 0 and 1).
60 pub fn y(&self, t: S) -> S {
61 let t2 = t * t;
62 let t3 = t2 * t;
63 let one_t = S::ONE - t;
64 let one_t2 = one_t * one_t;
65 let one_t3 = one_t2 * one_t;
66
67 self.from.y * one_t3
68 + self.ctrl1.y * S::THREE * one_t2 * t
69 + self.ctrl2.y * S::THREE * one_t * t2
70 + self.to.y * t3
71 }
72
73 /// Return the parameter values corresponding to a given x coordinate.
74 pub fn solve_t_for_x(&self, x: S) -> ArrayVec<S, 3> {
75 let (min, max) = self.fast_bounding_range_x();
76 if min > x || max < x {
77 return ArrayVec::new();
78 }
79
80 self.parameters_for_xy_value(x, self.from.x, self.ctrl1.x, self.ctrl2.x, self.to.x)
81 }
82
83 /// Return the parameter values corresponding to a given y coordinate.
84 pub fn solve_t_for_y(&self, y: S) -> ArrayVec<S, 3> {
85 let (min, max) = self.fast_bounding_range_y();
86 if min > y || max < y {
87 return ArrayVec::new();
88 }
89
90 self.parameters_for_xy_value(y, self.from.y, self.ctrl1.y, self.ctrl2.y, self.to.y)
91 }
92
93 fn parameters_for_xy_value(
94 &self,
95 value: S,
96 from: S,
97 ctrl1: S,
98 ctrl2: S,
99 to: S,
100 ) -> ArrayVec<S, 3> {
101 let mut result = ArrayVec::new();
102
103 let a = -from + S::THREE * ctrl1 - S::THREE * ctrl2 + to;
104 let b = S::THREE * from - S::SIX * ctrl1 + S::THREE * ctrl2;
105 let c = -S::THREE * from + S::THREE * ctrl1;
106 let d = from - value;
107
108 let roots = cubic_polynomial_roots(a, b, c, d);
109 for root in roots {
110 if root > S::ZERO && root < S::ONE {
111 result.push(root);
112 }
113 }
114
115 result
116 }
117
118 #[inline]
119 fn derivative_coefficients(&self, t: S) -> (S, S, S, S) {
120 let t2 = t * t;
121 (
122 -S::THREE * t2 + S::SIX * t - S::THREE,
123 S::NINE * t2 - S::value(12.0) * t + S::THREE,
124 -S::NINE * t2 + S::SIX * t,
125 S::THREE * t2,
126 )
127 }
128
129 /// Sample the curve's derivative at t (expecting t between 0 and 1).
130 pub fn derivative(&self, t: S) -> Vector<S> {
131 let (c0, c1, c2, c3) = self.derivative_coefficients(t);
132 self.from.to_vector() * c0
133 + self.ctrl1.to_vector() * c1
134 + self.ctrl2.to_vector() * c2
135 + self.to.to_vector() * c3
136 }
137
138 /// Sample the x coordinate of the curve's derivative at t (expecting t between 0 and 1).
139 pub fn dx(&self, t: S) -> S {
140 let (c0, c1, c2, c3) = self.derivative_coefficients(t);
141 self.from.x * c0 + self.ctrl1.x * c1 + self.ctrl2.x * c2 + self.to.x * c3
142 }
143
144 /// Sample the y coordinate of the curve's derivative at t (expecting t between 0 and 1).
145 pub fn dy(&self, t: S) -> S {
146 let (c0, c1, c2, c3) = self.derivative_coefficients(t);
147 self.from.y * c0 + self.ctrl1.y * c1 + self.ctrl2.y * c2 + self.to.y * c3
148 }
149
150 /// Return the sub-curve inside a given range of t.
151 ///
152 /// This is equivalent to splitting at the range's end points.
153 pub fn split_range(&self, t_range: Range<S>) -> Self {
154 let (t0, t1) = (t_range.start, t_range.end);
155 let from = self.sample(t0);
156 let to = self.sample(t1);
157
158 let d = QuadraticBezierSegment {
159 from: (self.ctrl1 - self.from).to_point(),
160 ctrl: (self.ctrl2 - self.ctrl1).to_point(),
161 to: (self.to - self.ctrl2).to_point(),
162 };
163
164 let dt = t1 - t0;
165 let ctrl1 = from + d.sample(t0).to_vector() * dt;
166 let ctrl2 = to - d.sample(t1).to_vector() * dt;
167
168 CubicBezierSegment {
169 from,
170 ctrl1,
171 ctrl2,
172 to,
173 }
174 }
175
176 /// Split this curve into two sub-curves.
177 pub fn split(&self, t: S) -> (CubicBezierSegment<S>, CubicBezierSegment<S>) {
178 let ctrl1a = self.from + (self.ctrl1 - self.from) * t;
179 let ctrl2a = self.ctrl1 + (self.ctrl2 - self.ctrl1) * t;
180 let ctrl1aa = ctrl1a + (ctrl2a - ctrl1a) * t;
181 let ctrl3a = self.ctrl2 + (self.to - self.ctrl2) * t;
182 let ctrl2aa = ctrl2a + (ctrl3a - ctrl2a) * t;
183 let ctrl1aaa = ctrl1aa + (ctrl2aa - ctrl1aa) * t;
184
185 (
186 CubicBezierSegment {
187 from: self.from,
188 ctrl1: ctrl1a,
189 ctrl2: ctrl1aa,
190 to: ctrl1aaa,
191 },
192 CubicBezierSegment {
193 from: ctrl1aaa,
194 ctrl1: ctrl2aa,
195 ctrl2: ctrl3a,
196 to: self.to,
197 },
198 )
199 }
200
201 /// Return the curve before the split point.
202 pub fn before_split(&self, t: S) -> CubicBezierSegment<S> {
203 let ctrl1a = self.from + (self.ctrl1 - self.from) * t;
204 let ctrl2a = self.ctrl1 + (self.ctrl2 - self.ctrl1) * t;
205 let ctrl1aa = ctrl1a + (ctrl2a - ctrl1a) * t;
206 let ctrl3a = self.ctrl2 + (self.to - self.ctrl2) * t;
207 let ctrl2aa = ctrl2a + (ctrl3a - ctrl2a) * t;
208 let ctrl1aaa = ctrl1aa + (ctrl2aa - ctrl1aa) * t;
209
210 CubicBezierSegment {
211 from: self.from,
212 ctrl1: ctrl1a,
213 ctrl2: ctrl1aa,
214 to: ctrl1aaa,
215 }
216 }
217
218 /// Return the curve after the split point.
219 pub fn after_split(&self, t: S) -> CubicBezierSegment<S> {
220 let ctrl1a = self.from + (self.ctrl1 - self.from) * t;
221 let ctrl2a = self.ctrl1 + (self.ctrl2 - self.ctrl1) * t;
222 let ctrl1aa = ctrl1a + (ctrl2a - ctrl1a) * t;
223 let ctrl3a = self.ctrl2 + (self.to - self.ctrl2) * t;
224 let ctrl2aa = ctrl2a + (ctrl3a - ctrl2a) * t;
225
226 CubicBezierSegment {
227 from: ctrl1aa + (ctrl2aa - ctrl1aa) * t,
228 ctrl1: ctrl2a + (ctrl3a - ctrl2a) * t,
229 ctrl2: ctrl3a,
230 to: self.to,
231 }
232 }
233
234 #[inline]
235 pub fn baseline(&self) -> LineSegment<S> {
236 LineSegment {
237 from: self.from,
238 to: self.to,
239 }
240 }
241
242 /// Returns true if the curve can be approximated with a single line segment, given
243 /// a tolerance threshold.
244 pub fn is_linear(&self, tolerance: S) -> bool {
245 // Similar to Line::square_distance_to_point, except we keep
246 // the sign of c1 and c2 to compute tighter upper bounds as we
247 // do in fat_line_min_max.
248 let baseline = self.to - self.from;
249 let v1 = self.ctrl1 - self.from;
250 let v2 = self.ctrl2 - self.from;
251 let c1 = baseline.cross(v1);
252 let c2 = baseline.cross(v2);
253 // TODO: it would be faster to multiply the threshold with baseline_len2
254 // instead of dividing d1 and d2, but it changes the behavior when the
255 // baseline length is zero in ways that breaks some of the cubic intersection
256 // tests.
257 let inv_baseline_len2 = S::ONE / baseline.square_length();
258 let d1 = (c1 * c1) * inv_baseline_len2;
259 let d2 = (c2 * c2) * inv_baseline_len2;
260
261 let factor = if (c1 * c2) > S::ZERO {
262 S::THREE / S::FOUR
263 } else {
264 S::FOUR / S::NINE
265 };
266
267 let f2 = factor * factor;
268 let threshold = tolerance * tolerance;
269
270 d1 * f2 <= threshold && d2 * f2 <= threshold
271 }
272
273 /// Returns whether the curve can be approximated with a single point, given
274 /// a tolerance threshold.
275 pub(crate) fn is_a_point(&self, tolerance: S) -> bool {
276 let tolerance_squared = tolerance * tolerance;
277 // Use <= so that tolerance can be zero.
278 (self.from - self.to).square_length() <= tolerance_squared
279 && (self.from - self.ctrl1).square_length() <= tolerance_squared
280 && (self.to - self.ctrl2).square_length() <= tolerance_squared
281 }
282
283 /// Computes the signed distances (min <= 0 and max >= 0) from the baseline of this
284 /// curve to its two "fat line" boundary lines.
285 ///
286 /// A fat line is two conservative lines between which the segment
287 /// is fully contained.
288 pub(crate) fn fat_line_min_max(&self) -> (S, S) {
289 let baseline = self.baseline().to_line().equation();
290 let (d1, d2) = min_max(
291 baseline.signed_distance_to_point(&self.ctrl1),
292 baseline.signed_distance_to_point(&self.ctrl2),
293 );
294
295 let factor = if (d1 * d2) > S::ZERO {
296 S::THREE / S::FOUR
297 } else {
298 S::FOUR / S::NINE
299 };
300
301 let d_min = factor * S::min(d1, S::ZERO);
302 let d_max = factor * S::max(d2, S::ZERO);
303
304 (d_min, d_max)
305 }
306
307 /// Computes a "fat line" of this segment.
308 ///
309 /// A fat line is two conservative lines between which the segment
310 /// is fully contained.
311 pub fn fat_line(&self) -> (LineEquation<S>, LineEquation<S>) {
312 let baseline = self.baseline().to_line().equation();
313 let (d1, d2) = self.fat_line_min_max();
314
315 (baseline.offset(d1), baseline.offset(d2))
316 }
317
318 /// Applies the transform to this curve and returns the results.
319 #[inline]
320 pub fn transformed<T: Transformation<S>>(&self, transform: &T) -> Self {
321 CubicBezierSegment {
322 from: transform.transform_point(self.from),
323 ctrl1: transform.transform_point(self.ctrl1),
324 ctrl2: transform.transform_point(self.ctrl2),
325 to: transform.transform_point(self.to),
326 }
327 }
328
329 /// Swap the beginning and the end of the segment.
330 pub fn flip(&self) -> Self {
331 CubicBezierSegment {
332 from: self.to,
333 ctrl1: self.ctrl2,
334 ctrl2: self.ctrl1,
335 to: self.from,
336 }
337 }
338
339 /// Approximate the curve with a single quadratic bézier segment.
340 ///
341 /// This is terrible as a general approximation but works if the cubic
342 /// curve does not have inflection points and is "flat" enough. Typically
343 /// usable after subdividing the curve a few times.
344 pub fn to_quadratic(&self) -> QuadraticBezierSegment<S> {
345 let c1 = (self.ctrl1 * S::THREE - self.from) * S::HALF;
346 let c2 = (self.ctrl2 * S::THREE - self.to) * S::HALF;
347 QuadraticBezierSegment {
348 from: self.from,
349 ctrl: ((c1 + c2) * S::HALF).to_point(),
350 to: self.to,
351 }
352 }
353
354 /// Evaluates an upper bound on the maximum distance between the curve
355 /// and its quadratic approximation obtained using `to_quadratic`.
356 pub fn to_quadratic_error(&self) -> S {
357 // See http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
358 S::sqrt(S::THREE) / S::value(36.0)
359 * ((self.to - self.ctrl2 * S::THREE) + (self.ctrl1 * S::THREE - self.from)).length()
360 }
361
362 /// Returns true if the curve can be safely approximated with a single quadratic bézier
363 /// segment given the provided tolerance threshold.
364 ///
365 /// Equivalent to comparing `to_quadratic_error` with the tolerance threshold, avoiding
366 /// the cost of two square roots.
367 pub fn is_quadratic(&self, tolerance: S) -> bool {
368 S::THREE / S::value(1296.0)
369 * ((self.to - self.ctrl2 * S::THREE) + (self.ctrl1 * S::THREE - self.from))
370 .square_length()
371 <= tolerance * tolerance
372 }
373
374 /// Computes the number of quadratic bézier segments required to approximate this cubic curve
375 /// given a tolerance threshold.
376 ///
377 /// Derived by Raph Levien from section 10.6 of Sedeberg's CAGD notes
378 /// <https://scholarsarchive.byu.edu/cgi/viewcontent.cgi?article=1000&context=facpub#section.10.6>
379 /// and the error metric from the caffein owl blog post <http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html>
380 pub fn num_quadratics(&self, tolerance: S) -> u32 {
381 self.num_quadratics_impl(tolerance).to_u32().unwrap_or(1)
382 }
383
384 fn num_quadratics_impl(&self, tolerance: S) -> S {
385 debug_assert!(tolerance > S::ZERO);
386
387 let x = self.from.x - S::THREE * self.ctrl1.x + S::THREE * self.ctrl2.x - self.to.x;
388 let y = self.from.y - S::THREE * self.ctrl1.y + S::THREE * self.ctrl2.y - self.to.y;
389
390 let err = x * x + y * y;
391
392 (err / (S::value(432.0) * tolerance * tolerance))
393 .powf(S::ONE / S::SIX)
394 .ceil()
395 .max(S::ONE)
396 }
397
398 /// Returns the flattened representation of the curve as an iterator, starting *after* the
399 /// current point.
400 pub fn flattened(&self, tolerance: S) -> Flattened<S> {
401 Flattened::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 extrema: ArrayVec<S, 4> = ArrayVec::new();
410 self.for_each_local_x_extremum_t(&mut |t| extrema.push(t));
411 self.for_each_local_y_extremum_t(&mut |t| extrema.push(t));
412 extrema.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
413
414 let mut t0 = S::ZERO;
415 for &t in &extrema {
416 if t != t0 {
417 cb(t0..t);
418 t0 = t;
419 }
420 }
421
422 cb(t0..S::ONE);
423 }
424
425 /// Invokes a callback for each monotonic part of the segment.
426 pub fn for_each_monotonic<F>(&self, cb: &mut F)
427 where
428 F: FnMut(&CubicBezierSegment<S>),
429 {
430 self.for_each_monotonic_range(&mut |range| {
431 let mut sub = self.split_range(range);
432 // Due to finite precision the split may actually result in sub-curves
433 // that are almost but not-quite monotonic. Make sure they actually are.
434 let min_x = sub.from.x.min(sub.to.x);
435 let max_x = sub.from.x.max(sub.to.x);
436 let min_y = sub.from.y.min(sub.to.y);
437 let max_y = sub.from.y.max(sub.to.y);
438 sub.ctrl1.x = sub.ctrl1.x.max(min_x).min(max_x);
439 sub.ctrl1.y = sub.ctrl1.y.max(min_y).min(max_y);
440 sub.ctrl2.x = sub.ctrl2.x.max(min_x).min(max_x);
441 sub.ctrl2.y = sub.ctrl2.y.max(min_y).min(max_y);
442 cb(&sub);
443 });
444 }
445
446 /// Invokes a callback for each y-monotonic part of the segment.
447 pub fn for_each_y_monotonic_range<F>(&self, cb: &mut F)
448 where
449 F: FnMut(Range<S>),
450 {
451 let mut t0 = S::ZERO;
452 self.for_each_local_y_extremum_t(&mut |t| {
453 cb(t0..t);
454 t0 = t;
455 });
456
457 cb(t0..S::ONE);
458 }
459
460 /// Invokes a callback for each y-monotonic part of the segment.
461 pub fn for_each_y_monotonic<F>(&self, cb: &mut F)
462 where
463 F: FnMut(&CubicBezierSegment<S>),
464 {
465 self.for_each_y_monotonic_range(&mut |range| {
466 let mut sub = self.split_range(range);
467 // Due to finite precision the split may actually result in sub-curves
468 // that are almost but not-quite monotonic. Make sure they actually are.
469 let min_y = sub.from.y.min(sub.to.y);
470 let max_y = sub.from.y.max(sub.to.y);
471 sub.ctrl1.y = sub.ctrl1.y.max(min_y).min(max_y);
472 sub.ctrl2.y = sub.ctrl2.y.max(min_y).min(max_y);
473 cb(&sub);
474 });
475 }
476
477 /// Invokes a callback for each x-monotonic part of the segment.
478 pub fn for_each_x_monotonic_range<F>(&self, cb: &mut F)
479 where
480 F: FnMut(Range<S>),
481 {
482 let mut t0 = S::ZERO;
483 self.for_each_local_x_extremum_t(&mut |t| {
484 cb(t0..t);
485 t0 = t;
486 });
487
488 cb(t0..S::ONE);
489 }
490
491 /// Invokes a callback for each x-monotonic part of the segment.
492 pub fn for_each_x_monotonic<F>(&self, cb: &mut F)
493 where
494 F: FnMut(&CubicBezierSegment<S>),
495 {
496 self.for_each_x_monotonic_range(&mut |range| {
497 let mut sub = self.split_range(range);
498 // Due to finite precision the split may actually result in sub-curves
499 // that are almost but not-quite monotonic. Make sure they actually are.
500 let min_x = sub.from.x.min(sub.to.x);
501 let max_x = sub.from.x.max(sub.to.x);
502 sub.ctrl1.x = sub.ctrl1.x.max(min_x).min(max_x);
503 sub.ctrl2.x = sub.ctrl2.x.max(min_x).min(max_x);
504 cb(&sub);
505 });
506 }
507
508 /// Approximates the cubic bézier curve with sequence of quadratic ones,
509 /// invoking a callback at each step.
510 pub fn for_each_quadratic_bezier<F>(&self, tolerance: S, cb: &mut F)
511 where
512 F: FnMut(&QuadraticBezierSegment<S>),
513 {
514 self.for_each_quadratic_bezier_with_t(tolerance, &mut |quad, _range| cb(quad));
515 }
516
517 /// Approximates the cubic bézier curve with sequence of quadratic ones,
518 /// invoking a callback at each step.
519 pub fn for_each_quadratic_bezier_with_t<F>(&self, tolerance: S, cb: &mut F)
520 where
521 F: FnMut(&QuadraticBezierSegment<S>, Range<S>),
522 {
523 debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
524
525 let num_quadratics = self.num_quadratics_impl(tolerance);
526 let step = S::ONE / num_quadratics;
527 let n = num_quadratics.to_u32().unwrap_or(1);
528 let mut t0 = S::ZERO;
529 for _ in 0..(n - 1) {
530 let t1 = t0 + step;
531
532 let quad = self.split_range(t0..t1).to_quadratic();
533 cb(&quad, t0..t1);
534
535 t0 = t1;
536 }
537
538 // Do the last step manually to make sure we finish at t = 1.0 exactly.
539 let quad = self.split_range(t0..S::ONE).to_quadratic();
540 cb(&quad, t0..S::ONE)
541 }
542
543 /// Approximates the curve with sequence of line segments.
544 ///
545 /// The `tolerance` parameter defines the maximum distance between the curve and
546 /// its approximation.
547 pub fn for_each_flattened<F: FnMut(&LineSegment<S>)>(&self, tolerance: S, callback: &mut F) {
548 debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
549 let quadratics_tolerance = tolerance * S::value(0.4);
550 let flattening_tolerance = tolerance * S::value(0.8);
551
552 self.for_each_quadratic_bezier(quadratics_tolerance, &mut |quad| {
553 quad.for_each_flattened(flattening_tolerance, &mut |segment| {
554 callback(segment);
555 });
556 });
557 }
558
559 /// Approximates the curve with sequence of line segments.
560 ///
561 /// The `tolerance` parameter defines the maximum distance between the curve and
562 /// its approximation.
563 ///
564 /// The end of the t parameter range at the final segment is guaranteed to be equal to `1.0`.
565 pub fn for_each_flattened_with_t<F: FnMut(&LineSegment<S>, Range<S>)>(
566 &self,
567 tolerance: S,
568 callback: &mut F,
569 ) {
570 debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
571 let quadratics_tolerance = tolerance * S::value(0.4);
572 let flattening_tolerance = tolerance * S::value(0.8);
573
574 let mut t_from = S::ZERO;
575 self.for_each_quadratic_bezier_with_t(quadratics_tolerance, &mut |quad, range| {
576 let last_quad = range.end == S::ONE;
577 let range_len = range.end - range.start;
578 quad.for_each_flattened_with_t(flattening_tolerance, &mut |segment, range_sub| {
579 let last_seg = range_sub.end == S::ONE;
580 let t = if last_quad && last_seg {
581 S::ONE
582 } else {
583 range_sub.end * range_len + range.start
584 };
585 callback(segment, t_from..t);
586 t_from = t;
587 });
588 });
589 }
590
591 /// Compute the length of the segment using a flattened approximation.
592 pub fn approximate_length(&self, tolerance: S) -> S {
593 let mut length = S::ZERO;
594
595 self.for_each_quadratic_bezier(tolerance, &mut |quad| {
596 length += quad.length();
597 });
598
599 length
600 }
601
602 /// Invokes a callback at each inflection point if any.
603 pub fn for_each_inflection_t<F>(&self, cb: &mut F)
604 where
605 F: FnMut(S),
606 {
607 // Find inflection points.
608 // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
609 // of this approach.
610 let pa = self.ctrl1 - self.from;
611 let pb = self.ctrl2.to_vector() - (self.ctrl1.to_vector() * S::TWO) + self.from.to_vector();
612 let pc = self.to.to_vector() - (self.ctrl2.to_vector() * S::THREE)
613 + (self.ctrl1.to_vector() * S::THREE)
614 - self.from.to_vector();
615
616 let a = pb.cross(pc);
617 let b = pa.cross(pc);
618 let c = pa.cross(pb);
619
620 if S::abs(a) < S::EPSILON {
621 // Not a quadratic equation.
622 if S::abs(b) < S::EPSILON {
623 // Instead of a linear acceleration change we have a constant
624 // acceleration change. This means the equation has no solution
625 // and there are no inflection points, unless the constant is 0.
626 // In that case the curve is a straight line, essentially that means
627 // the easiest way to deal with is is by saying there's an inflection
628 // point at t == 0. The inflection point approximation range found will
629 // automatically extend into infinity.
630 if S::abs(c) < S::EPSILON {
631 cb(S::ZERO);
632 }
633 } else {
634 let t = -c / b;
635 if in_range(t) {
636 cb(t);
637 }
638 }
639
640 return;
641 }
642
643 fn in_range<S: Scalar>(t: S) -> bool {
644 t >= S::ZERO && t < S::ONE
645 }
646
647 let discriminant = b * b - S::FOUR * a * c;
648
649 if discriminant < S::ZERO {
650 return;
651 }
652
653 if discriminant < S::EPSILON {
654 let t = -b / (S::TWO * a);
655
656 if in_range(t) {
657 cb(t);
658 }
659
660 return;
661 }
662
663 // This code is derived from https://www2.units.it/ipl/students_area/imm2/files/Numerical_Recipes.pdf page 184.
664 // Computing the roots this way avoids precision issues when a, c or both are small.
665 let discriminant_sqrt = S::sqrt(discriminant);
666 let sign_b = if b >= S::ZERO { S::ONE } else { -S::ONE };
667 let q = -S::HALF * (b + sign_b * discriminant_sqrt);
668 let mut first_inflection = q / a;
669 let mut second_inflection = c / q;
670
671 if first_inflection > second_inflection {
672 core::mem::swap(&mut first_inflection, &mut second_inflection);
673 }
674
675 if in_range(first_inflection) {
676 cb(first_inflection);
677 }
678
679 if in_range(second_inflection) {
680 cb(second_inflection);
681 }
682 }
683
684 /// Return local x extrema or None if this curve is monotonic.
685 ///
686 /// This returns the advancements along the curve, not the actual x position.
687 pub fn for_each_local_x_extremum_t<F>(&self, cb: &mut F)
688 where
689 F: FnMut(S),
690 {
691 Self::for_each_local_extremum(self.from.x, self.ctrl1.x, self.ctrl2.x, self.to.x, cb)
692 }
693
694 /// Return local y extrema or None if this curve is monotonic.
695 ///
696 /// This returns the advancements along the curve, not the actual y position.
697 pub fn for_each_local_y_extremum_t<F>(&self, cb: &mut F)
698 where
699 F: FnMut(S),
700 {
701 Self::for_each_local_extremum(self.from.y, self.ctrl1.y, self.ctrl2.y, self.to.y, cb)
702 }
703
704 fn for_each_local_extremum<F>(p0: S, p1: S, p2: S, p3: S, cb: &mut F)
705 where
706 F: FnMut(S),
707 {
708 // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
709 // The derivative of a cubic bezier curve is a curve representing a second degree polynomial function
710 // f(x) = a * x² + b * x + c such as :
711
712 let a = S::THREE * (p3 + S::THREE * (p1 - p2) - p0);
713 let b = S::SIX * (p2 - S::TWO * p1 + p0);
714 let c = S::THREE * (p1 - p0);
715
716 fn in_range<S: Scalar>(t: S) -> bool {
717 t > S::ZERO && t < S::ONE
718 }
719
720 // If the derivative is a linear function
721 if a == S::ZERO {
722 if b != S::ZERO {
723 let t = -c / b;
724 if in_range(t) {
725 cb(t);
726 }
727 }
728 return;
729 }
730
731 let discriminant = b * b - S::FOUR * a * c;
732
733 // There is no Real solution for the equation
734 if discriminant < S::ZERO {
735 return;
736 }
737
738 // There is one Real solution for the equation
739 if discriminant == S::ZERO {
740 let t = -b / (S::TWO * a);
741 if in_range(t) {
742 cb(t);
743 }
744 return;
745 }
746
747 // There are two Real solutions for the equation
748 let discriminant_sqrt = discriminant.sqrt();
749
750 let mut first_extremum = (-b - discriminant_sqrt) / (S::TWO * a);
751 let mut second_extremum = (-b + discriminant_sqrt) / (S::TWO * a);
752 if first_extremum > second_extremum {
753 core::mem::swap(&mut first_extremum, &mut second_extremum);
754 }
755
756 if in_range(first_extremum) {
757 cb(first_extremum);
758 }
759
760 if in_range(second_extremum) {
761 cb(second_extremum);
762 }
763 }
764
765 /// Find the advancement of the y-most position in the curve.
766 ///
767 /// This returns the advancement along the curve, not the actual y position.
768 pub fn y_maximum_t(&self) -> S {
769 let mut max_t = S::ZERO;
770 let mut max_y = self.from.y;
771 if self.to.y > max_y {
772 max_t = S::ONE;
773 max_y = self.to.y;
774 }
775 self.for_each_local_y_extremum_t(&mut |t| {
776 let y = self.y(t);
777 if y > max_y {
778 max_t = t;
779 max_y = y;
780 }
781 });
782
783 max_t
784 }
785
786 /// Find the advancement of the y-least position in the curve.
787 ///
788 /// This returns the advancement along the curve, not the actual y position.
789 pub fn y_minimum_t(&self) -> S {
790 let mut min_t = S::ZERO;
791 let mut min_y = self.from.y;
792 if self.to.y < min_y {
793 min_t = S::ONE;
794 min_y = self.to.y;
795 }
796 self.for_each_local_y_extremum_t(&mut |t| {
797 let y = self.y(t);
798 if y < min_y {
799 min_t = t;
800 min_y = y;
801 }
802 });
803
804 min_t
805 }
806
807 /// Find the advancement of the x-most position in the curve.
808 ///
809 /// This returns the advancement along the curve, not the actual x position.
810 pub fn x_maximum_t(&self) -> S {
811 let mut max_t = S::ZERO;
812 let mut max_x = self.from.x;
813 if self.to.x > max_x {
814 max_t = S::ONE;
815 max_x = self.to.x;
816 }
817 self.for_each_local_x_extremum_t(&mut |t| {
818 let x = self.x(t);
819 if x > max_x {
820 max_t = t;
821 max_x = x;
822 }
823 });
824
825 max_t
826 }
827
828 /// Find the x-least position in the curve.
829 pub fn x_minimum_t(&self) -> S {
830 let mut min_t = S::ZERO;
831 let mut min_x = self.from.x;
832 if self.to.x < min_x {
833 min_t = S::ONE;
834 min_x = self.to.x;
835 }
836 self.for_each_local_x_extremum_t(&mut |t| {
837 let x = self.x(t);
838 if x < min_x {
839 min_t = t;
840 min_x = x;
841 }
842 });
843
844 min_t
845 }
846
847 /// Returns a conservative rectangle the curve is contained in.
848 ///
849 /// This method is faster than `bounding_box` but more conservative.
850 pub fn fast_bounding_box(&self) -> Box2D<S> {
851 let (min_x, max_x) = self.fast_bounding_range_x();
852 let (min_y, max_y) = self.fast_bounding_range_y();
853
854 Box2D {
855 min: point(min_x, min_y),
856 max: point(max_x, max_y),
857 }
858 }
859
860 /// Returns a conservative range of x that contains this curve.
861 #[inline]
862 pub fn fast_bounding_range_x(&self) -> (S, S) {
863 let min_x = self
864 .from
865 .x
866 .min(self.ctrl1.x)
867 .min(self.ctrl2.x)
868 .min(self.to.x);
869 let max_x = self
870 .from
871 .x
872 .max(self.ctrl1.x)
873 .max(self.ctrl2.x)
874 .max(self.to.x);
875
876 (min_x, max_x)
877 }
878
879 /// Returns a conservative range of y that contains this curve.
880 #[inline]
881 pub fn fast_bounding_range_y(&self) -> (S, S) {
882 let min_y = self
883 .from
884 .y
885 .min(self.ctrl1.y)
886 .min(self.ctrl2.y)
887 .min(self.to.y);
888 let max_y = self
889 .from
890 .y
891 .max(self.ctrl1.y)
892 .max(self.ctrl2.y)
893 .max(self.to.y);
894
895 (min_y, max_y)
896 }
897
898 /// Returns a conservative rectangle that contains the curve.
899 #[inline]
900 pub fn bounding_box(&self) -> Box2D<S> {
901 let (min_x, max_x) = self.bounding_range_x();
902 let (min_y, max_y) = self.bounding_range_y();
903
904 Box2D {
905 min: point(min_x, min_y),
906 max: point(max_x, max_y),
907 }
908 }
909
910 /// Returns the smallest range of x that contains this curve.
911 #[inline]
912 pub fn bounding_range_x(&self) -> (S, S) {
913 let min_x = self.x(self.x_minimum_t());
914 let max_x = self.x(self.x_maximum_t());
915
916 (min_x, max_x)
917 }
918
919 /// Returns the smallest range of y that contains this curve.
920 #[inline]
921 pub fn bounding_range_y(&self) -> (S, S) {
922 let min_y = self.y(self.y_minimum_t());
923 let max_y = self.y(self.y_maximum_t());
924
925 (min_y, max_y)
926 }
927
928 /// Returns whether this segment is monotonic on the x axis.
929 pub fn is_x_monotonic(&self) -> bool {
930 let mut found = false;
931 self.for_each_local_x_extremum_t(&mut |_| {
932 found = true;
933 });
934 !found
935 }
936
937 /// Returns whether this segment is monotonic on the y axis.
938 pub fn is_y_monotonic(&self) -> bool {
939 let mut found = false;
940 self.for_each_local_y_extremum_t(&mut |_| {
941 found = true;
942 });
943 !found
944 }
945
946 /// Returns whether this segment is fully monotonic.
947 pub fn is_monotonic(&self) -> bool {
948 self.is_x_monotonic() && self.is_y_monotonic()
949 }
950
951 /// Computes the intersections (if any) between this segment and another one.
952 ///
953 /// The result is provided in the form of the `t` parameters of each point along the curves. To
954 /// get the intersection points, sample the curves at the corresponding values.
955 ///
956 /// Returns endpoint intersections where an endpoint intersects the interior of the other curve,
957 /// but not endpoint/endpoint intersections.
958 ///
959 /// Returns no intersections if either curve is a point.
960 pub fn cubic_intersections_t(&self, curve: &CubicBezierSegment<S>) -> ArrayVec<(S, S), 9> {
961 cubic_bezier_intersections_t(self, curve)
962 }
963
964 /// Computes the intersection points (if any) between this segment and another one.
965 pub fn cubic_intersections(&self, curve: &CubicBezierSegment<S>) -> ArrayVec<Point<S>, 9> {
966 let intersections = self.cubic_intersections_t(curve);
967
968 let mut result_with_repeats = ArrayVec::<_, 9>::new();
969 for (t, _) in intersections {
970 result_with_repeats.push(self.sample(t));
971 }
972
973 // We can have up to nine "repeated" values here (for example: two lines, each of which
974 // overlaps itself 3 times, intersecting in their 3-fold overlaps). We make an effort to
975 // dedupe the results, but that's hindered by not having predictable control over how far
976 // the repeated intersections can be from each other (and then by the fact that true
977 // intersections can be arbitrarily close), so the results will never be perfect.
978
979 let pair_cmp = |s: &Point<S>, t: &Point<S>| {
980 if s.x < t.x || (s.x == t.x && s.y < t.y) {
981 Less
982 } else if s.x == t.x && s.y == t.y {
983 Equal
984 } else {
985 Greater
986 }
987 };
988 result_with_repeats.sort_unstable_by(pair_cmp);
989 if result_with_repeats.len() <= 1 {
990 return result_with_repeats;
991 }
992
993 #[inline]
994 fn dist_sq<S: Scalar>(p1: &Point<S>, p2: &Point<S>) -> S {
995 (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)
996 }
997
998 let epsilon_squared = S::EPSILON * S::EPSILON;
999 let mut result = ArrayVec::new();
1000 let mut reference_intersection = &result_with_repeats[0];
1001 result.push(*reference_intersection);
1002 for i in 1..result_with_repeats.len() {
1003 let intersection = &result_with_repeats[i];
1004 if dist_sq(reference_intersection, intersection) < epsilon_squared {
1005 continue;
1006 } else {
1007 result.push(*intersection);
1008 reference_intersection = intersection;
1009 }
1010 }
1011
1012 result
1013 }
1014
1015 /// Computes the intersections (if any) between this segment a quadratic bézier segment.
1016 ///
1017 /// The result is provided in the form of the `t` parameters of each point along the curves. To
1018 /// get the intersection points, sample the curves at the corresponding values.
1019 ///
1020 /// Returns endpoint intersections where an endpoint intersects the interior of the other curve,
1021 /// but not endpoint/endpoint intersections.
1022 ///
1023 /// Returns no intersections if either curve is a point.
1024 pub fn quadratic_intersections_t(
1025 &self,
1026 curve: &QuadraticBezierSegment<S>,
1027 ) -> ArrayVec<(S, S), 9> {
1028 self.cubic_intersections_t(&curve.to_cubic())
1029 }
1030
1031 /// Computes the intersection points (if any) between this segment and a quadratic bézier segment.
1032 pub fn quadratic_intersections(
1033 &self,
1034 curve: &QuadraticBezierSegment<S>,
1035 ) -> ArrayVec<Point<S>, 9> {
1036 self.cubic_intersections(&curve.to_cubic())
1037 }
1038
1039 /// Computes the intersections (if any) between this segment and a line.
1040 ///
1041 /// The result is provided in the form of the `t` parameters of each
1042 /// point along curve. To get the intersection points, sample the curve
1043 /// at the corresponding values.
1044 pub fn line_intersections_t(&self, line: &Line<S>) -> ArrayVec<S, 3> {
1045 if line.vector.square_length() < S::EPSILON {
1046 return ArrayVec::new();
1047 }
1048
1049 let from = self.from.to_vector();
1050 let ctrl1 = self.ctrl1.to_vector();
1051 let ctrl2 = self.ctrl2.to_vector();
1052 let to = self.to.to_vector();
1053
1054 let p1 = to - from + (ctrl1 - ctrl2) * S::THREE;
1055 let p2 = from * S::THREE + (ctrl2 - ctrl1 * S::TWO) * S::THREE;
1056 let p3 = (ctrl1 - from) * S::THREE;
1057 let p4 = from;
1058
1059 let c = line.point.y * line.vector.x - line.point.x * line.vector.y;
1060
1061 let roots = cubic_polynomial_roots(
1062 line.vector.y * p1.x - line.vector.x * p1.y,
1063 line.vector.y * p2.x - line.vector.x * p2.y,
1064 line.vector.y * p3.x - line.vector.x * p3.y,
1065 line.vector.y * p4.x - line.vector.x * p4.y + c,
1066 );
1067
1068 let mut result = ArrayVec::new();
1069
1070 for root in roots {
1071 if root >= S::ZERO && root <= S::ONE {
1072 result.push(root);
1073 }
1074 }
1075
1076 // TODO: sort the intersections?
1077
1078 result
1079 }
1080
1081 /// Computes the intersection points (if any) between this segment and a line.
1082 pub fn line_intersections(&self, line: &Line<S>) -> ArrayVec<Point<S>, 3> {
1083 let intersections = self.line_intersections_t(line);
1084
1085 let mut result = ArrayVec::new();
1086 for t in intersections {
1087 result.push(self.sample(t));
1088 }
1089
1090 result
1091 }
1092
1093 /// Computes the intersections (if any) between this segment and a line segment.
1094 ///
1095 /// The result is provided in the form of the `t` parameters of each
1096 /// point along curve and segment. To get the intersection points, sample
1097 /// the segments at the corresponding values.
1098 pub fn line_segment_intersections_t(&self, segment: &LineSegment<S>) -> ArrayVec<(S, S), 3> {
1099 if !self
1100 .fast_bounding_box()
1101 .inflate(S::EPSILON, S::EPSILON)
1102 .intersects(&segment.bounding_box().inflate(S::EPSILON, S::EPSILON))
1103 {
1104 return ArrayVec::new();
1105 }
1106
1107 let intersections = self.line_intersections_t(&segment.to_line());
1108
1109 let mut result = ArrayVec::new();
1110 if intersections.is_empty() {
1111 return result;
1112 }
1113
1114 let seg_is_mostly_vertical =
1115 S::abs(segment.from.y - segment.to.y) >= S::abs(segment.from.x - segment.to.x);
1116 let (seg_long_axis_min, seg_long_axis_max) = if seg_is_mostly_vertical {
1117 segment.bounding_range_y()
1118 } else {
1119 segment.bounding_range_x()
1120 };
1121
1122 for t in intersections {
1123 let intersection_xy = if seg_is_mostly_vertical {
1124 self.y(t)
1125 } else {
1126 self.x(t)
1127 };
1128 if intersection_xy >= seg_long_axis_min && intersection_xy <= seg_long_axis_max {
1129 let t2 = (self.sample(t) - segment.from).length() / segment.length();
1130 // Don't take intersections that are on endpoints of both curves at the same time.
1131 if (t != S::ZERO && t != S::ONE) || (t2 != S::ZERO && t2 != S::ONE) {
1132 result.push((t, t2));
1133 }
1134 }
1135 }
1136
1137 result
1138 }
1139
1140 #[inline]
1141 pub fn from(&self) -> Point<S> {
1142 self.from
1143 }
1144
1145 #[inline]
1146 pub fn to(&self) -> Point<S> {
1147 self.to
1148 }
1149
1150 pub fn line_segment_intersections(&self, segment: &LineSegment<S>) -> ArrayVec<Point<S>, 3> {
1151 let intersections = self.line_segment_intersections_t(segment);
1152
1153 let mut result = ArrayVec::new();
1154 for (t, _) in intersections {
1155 result.push(self.sample(t));
1156 }
1157
1158 result
1159 }
1160
1161 fn baseline_projection(&self, t: S) -> S {
1162 // See https://pomax.github.io/bezierinfo/#abc
1163 // We are computing the interpolation factor between
1164 // `from` and `to` to get the position of C.
1165 let one_t = S::ONE - t;
1166 let one_t3 = one_t * one_t * one_t;
1167 let t3 = t * t * t;
1168
1169 t3 / (t3 + one_t3)
1170 }
1171
1172 fn abc_ratio(&self, t: S) -> S {
1173 // See https://pomax.github.io/bezierinfo/#abc
1174 let one_t = S::ONE - t;
1175 let one_t3 = one_t * one_t * one_t;
1176 let t3 = t * t * t;
1177
1178 ((t3 + one_t3 - S::ONE) / (t3 + one_t3)).abs()
1179 }
1180
1181 // Returns a quadratic bézier curve built by dragging this curve's point at `t`
1182 // to a new position, without moving the endpoints.
1183 //
1184 // The relative effect on control points is chosen to give a similar "feel" to
1185 // most vector graphics editors: dragging from near the first endpoint will affect
1186 // the first control point more than the second control point, etc.
1187 pub fn drag(&self, t: S, new_position: Point<S>) -> Self {
1188 // A lot of tweaking could go into making the weight feel as natural as possible.
1189 let min = S::value(0.1);
1190 let max = S::value(0.9);
1191 let weight = if t < min {
1192 S::ZERO
1193 } else if t > max {
1194 S::ONE
1195 } else {
1196 (t - min) / (max - min)
1197 };
1198
1199 self.drag_with_weight(t, new_position, weight)
1200 }
1201
1202 // Returns a quadratic bézier curve built by dragging this curve's point at `t`
1203 // to a new position, without moving the endpoints.
1204 //
1205 // The provided weight specifies the relative effect on control points.
1206 // - with `weight = 0.5`, `ctrl1` and `ctrl2` are equally affected,
1207 // - with `weight = 0.0`, only `ctrl1` is affected,
1208 // - with `weight = 1.0`, only `ctrl2` is affected,
1209 // - etc.
1210 pub fn drag_with_weight(&self, t: S, new_position: Point<S>, weight: S) -> Self {
1211 // See https://pomax.github.io/bezierinfo/#abc
1212 //
1213 // From-----------Ctrl1
1214 // | \ d1 \
1215 // C-------P--------A \ d12
1216 // | \d2 \
1217 // | \ \
1218 // To-----------------Ctrl2
1219 //
1220 // The ABC relation means we can place the new control points however we like
1221 // as long as the ratios CA/CP, d1/d12 and d2/d12 remain constant.
1222 //
1223 // we use the weight to guide our decisions. A weight of 0.5 would be a uniform
1224 // displacement (d1 and d2 do not change and both control points are moved by the
1225 // same amount).
1226 // The approach is to use the weight interpolate the most constrained control point
1227 // between it's old position and the position it would have with uniform displacement.
1228 // then we determine the position of the least constrained control point such that
1229 // the ratios mentioned earlier remain constant.
1230
1231 let c = self.from.lerp(self.to, self.baseline_projection(t));
1232 let cacp_ratio = self.abc_ratio(t);
1233
1234 let old_pos = self.sample(t);
1235 // Construct A before and after drag using the constance ca/cp ratio
1236 let old_a = old_pos + (old_pos - c) / cacp_ratio;
1237 let new_a = new_position + (new_position - c) / cacp_ratio;
1238
1239 // Sort ctrl1 and ctrl2 such ctrl1 is the least affected (or most constrained).
1240 let mut ctrl1 = self.ctrl1;
1241 let mut ctrl2 = self.ctrl2;
1242 if t < S::HALF {
1243 core::mem::swap(&mut ctrl1, &mut ctrl2);
1244 }
1245
1246 // Move the most constrained control point by a subset of the uniform displacement
1247 // depending on the weight.
1248 let uniform_displacement = new_a - old_a;
1249 let f = if t < S::HALF {
1250 S::TWO * weight
1251 } else {
1252 S::TWO * (S::ONE - weight)
1253 };
1254 let mut new_ctrl1 = ctrl1 + uniform_displacement * f;
1255
1256 // Now that the most constrained control point is placed there is only one position
1257 // for the least constrained control point that satisfies the constant ratios.
1258 let d1_pre = (old_a - ctrl1).length();
1259 let d12_pre = (self.ctrl2 - self.ctrl1).length();
1260
1261 let mut new_ctrl2 = new_ctrl1 + (new_a - new_ctrl1) * (d12_pre / d1_pre);
1262
1263 if t < S::HALF {
1264 core::mem::swap(&mut new_ctrl1, &mut new_ctrl2);
1265 }
1266
1267 CubicBezierSegment {
1268 from: self.from,
1269 ctrl1: new_ctrl1,
1270 ctrl2: new_ctrl2,
1271 to: self.to,
1272 }
1273 }
1274
1275 pub fn to_f32(&self) -> CubicBezierSegment<f32> {
1276 CubicBezierSegment {
1277 from: self.from.to_f32(),
1278 ctrl1: self.ctrl1.to_f32(),
1279 ctrl2: self.ctrl2.to_f32(),
1280 to: self.to.to_f32(),
1281 }
1282 }
1283
1284 pub fn to_f64(&self) -> CubicBezierSegment<f64> {
1285 CubicBezierSegment {
1286 from: self.from.to_f64(),
1287 ctrl1: self.ctrl1.to_f64(),
1288 ctrl2: self.ctrl2.to_f64(),
1289 to: self.to.to_f64(),
1290 }
1291 }
1292}
1293
1294impl<S: Scalar> Segment for CubicBezierSegment<S> {
1295 impl_segment!(S);
1296
1297 fn for_each_flattened_with_t(
1298 &self,
1299 tolerance: Self::Scalar,
1300 callback: &mut dyn FnMut(&LineSegment<S>, Range<S>),
1301 ) {
1302 self.for_each_flattened_with_t(tolerance, &mut |s: &LineSegment, t: Range| callback(s, t));
1303 }
1304}
1305
1306impl<S: Scalar> BoundingBox for CubicBezierSegment<S> {
1307 type Scalar = S;
1308 fn bounding_range_x(&self) -> (S, S) {
1309 self.bounding_range_x()
1310 }
1311 fn bounding_range_y(&self) -> (S, S) {
1312 self.bounding_range_y()
1313 }
1314 fn fast_bounding_range_x(&self) -> (S, S) {
1315 self.fast_bounding_range_x()
1316 }
1317 fn fast_bounding_range_y(&self) -> (S, S) {
1318 self.fast_bounding_range_y()
1319 }
1320}
1321
1322use crate::quadratic_bezier::FlattenedT as FlattenedQuadraticSegment;
1323
1324pub struct Flattened<S: Scalar> {
1325 curve: CubicBezierSegment<S>,
1326 current_curve: FlattenedQuadraticSegment<S>,
1327 remaining_sub_curves: i32,
1328 tolerance: S,
1329 range_step: S,
1330 range_start: S,
1331}
1332
1333impl<S: Scalar> Flattened<S> {
1334 pub(crate) fn new(curve: &CubicBezierSegment<S>, tolerance: S) -> Self {
1335 debug_assert!(tolerance >= S::EPSILON * S::EPSILON);
1336
1337 let quadratics_tolerance: S = tolerance * S::value(0.4);
1338 let flattening_tolerance: S = tolerance * S::value(0.8);
1339
1340 let num_quadratics: S = curve.num_quadratics_impl(quadratics_tolerance);
1341
1342 let range_step: S = S::ONE / num_quadratics;
1343
1344 let quadratic: QuadraticBezierSegment = curve.split_range(S::ZERO..range_step).to_quadratic();
1345 let current_curve: FlattenedT = FlattenedQuadraticSegment::new(&quadratic, flattening_tolerance);
1346
1347 Flattened {
1348 curve: *curve,
1349 current_curve,
1350 remaining_sub_curves: num_quadratics.to_i32().unwrap() - 1,
1351 tolerance: flattening_tolerance,
1352 range_start: S::ZERO,
1353 range_step,
1354 }
1355 }
1356}
1357
1358impl<S: Scalar> Iterator for Flattened<S> {
1359 type Item = Point<S>;
1360
1361 fn next(&mut self) -> Option<Point<S>> {
1362 if let Some(t_inner) = self.current_curve.next() {
1363 let t = self.range_start + t_inner * self.range_step;
1364 return Some(self.curve.sample(t));
1365 }
1366
1367 if self.remaining_sub_curves <= 0 {
1368 return None;
1369 }
1370
1371 self.range_start += self.range_step;
1372 let t0 = self.range_start;
1373 let t1 = self.range_start + self.range_step;
1374 self.remaining_sub_curves -= 1;
1375
1376 let quadratic = self.curve.split_range(t0..t1).to_quadratic();
1377 self.current_curve = FlattenedQuadraticSegment::new(&quadratic, self.tolerance);
1378
1379 let t_inner = self.current_curve.next().unwrap_or(S::ONE);
1380 let t = t0 + t_inner * self.range_step;
1381
1382 Some(self.curve.sample(t))
1383 }
1384
1385 fn size_hint(&self) -> (usize, Option<usize>) {
1386 (
1387 self.remaining_sub_curves as usize * self.current_curve.size_hint().0,
1388 None,
1389 )
1390 }
1391}
1392
1393#[cfg(test)]
1394fn print_arrays(a: &[Point<f32>], b: &[Point<f32>]) {
1395 std::println!("left: {a:?}");
1396 std::println!("right: {b:?}");
1397}
1398
1399#[cfg(test)]
1400fn assert_approx_eq(a: &[Point<f32>], b: &[Point<f32>]) {
1401 if a.len() != b.len() {
1402 print_arrays(a, b);
1403 panic!("Lengths differ ({} != {})", a.len(), b.len());
1404 }
1405 for i in 0..a.len() {
1406 let threshold = 0.029;
1407 let dx = f32::abs(a[i].x - b[i].x);
1408 let dy = f32::abs(a[i].y - b[i].y);
1409 if dx > threshold || dy > threshold {
1410 print_arrays(a, b);
1411 std::println!("diff = {dx:?} {dy:?}");
1412 panic!("The arrays are not equal");
1413 }
1414 }
1415}
1416
1417#[test]
1418fn test_iterator_builder_1() {
1419 let tolerance: f64 = 0.01;
1420 let c1: CubicBezierSegment = CubicBezierSegment {
1421 from: Point::new(x:0.0, y:0.0),
1422 ctrl1: Point::new(x:1.0, y:0.0),
1423 ctrl2: Point::new(x:1.0, y:1.0),
1424 to: Point::new(x:0.0, y:1.0),
1425 };
1426 let iter_points: Vec<Point<f32>> = c1.flattened(tolerance).collect();
1427 let mut builder_points = Vec::new();
1428 c1.for_each_flattened(tolerance, &mut |s: &LineSegment| {
1429 builder_points.push(s.to);
1430 });
1431
1432 assert!(iter_points.len() > 2);
1433 assert_approx_eq(&iter_points[..], &builder_points[..]);
1434}
1435
1436#[test]
1437fn test_iterator_builder_2() {
1438 let tolerance: f64 = 0.01;
1439 let c1: CubicBezierSegment = CubicBezierSegment {
1440 from: Point::new(x:0.0, y:0.0),
1441 ctrl1: Point::new(x:1.0, y:0.0),
1442 ctrl2: Point::new(x:0.0, y:1.0),
1443 to: Point::new(x:1.0, y:1.0),
1444 };
1445 let iter_points: Vec<Point<f32>> = c1.flattened(tolerance).collect();
1446 let mut builder_points = Vec::new();
1447 c1.for_each_flattened(tolerance, &mut |s: &LineSegment| {
1448 builder_points.push(s.to);
1449 });
1450
1451 assert!(iter_points.len() > 2);
1452 assert_approx_eq(&iter_points[..], &builder_points[..]);
1453}
1454
1455#[test]
1456fn test_iterator_builder_3() {
1457 let tolerance: f64 = 0.01;
1458 let c1: CubicBezierSegment = CubicBezierSegment {
1459 from: Point::new(x:141.0, y:135.0),
1460 ctrl1: Point::new(x:141.0, y:130.0),
1461 ctrl2: Point::new(x:140.0, y:130.0),
1462 to: Point::new(x:131.0, y:130.0),
1463 };
1464 let iter_points: Vec<Point<f32>> = c1.flattened(tolerance).collect();
1465 let mut builder_points = Vec::new();
1466 c1.for_each_flattened(tolerance, &mut |s: &LineSegment| {
1467 builder_points.push(s.to);
1468 });
1469
1470 assert!(iter_points.len() > 2);
1471 assert_approx_eq(&iter_points[..], &builder_points[..]);
1472}
1473
1474#[test]
1475fn test_issue_19() {
1476 let tolerance: f64 = 0.15;
1477 let c1: CubicBezierSegment = CubicBezierSegment {
1478 from: Point::new(x:11.71726, y:9.07143),
1479 ctrl1: Point::new(x:1.889879, y:13.22917),
1480 ctrl2: Point::new(x:18.142855, y:19.27679),
1481 to: Point::new(x:18.142855, y:19.27679),
1482 };
1483 let iter_points: Vec<Point<f32>> = c1.flattened(tolerance).collect();
1484 let mut builder_points = Vec::new();
1485 c1.for_each_flattened(tolerance, &mut |s: &LineSegment| {
1486 builder_points.push(s.to);
1487 });
1488
1489 assert_approx_eq(&iter_points[..], &builder_points[..]);
1490
1491 assert!(iter_points.len() > 1);
1492}
1493
1494#[test]
1495fn test_issue_194() {
1496 let segment: CubicBezierSegment = CubicBezierSegment {
1497 from: Point::new(x:0.0, y:0.0),
1498 ctrl1: Point::new(x:0.0, y:0.0),
1499 ctrl2: Point::new(x:50.0, y:70.0),
1500 to: Point::new(x:100.0, y:100.0),
1501 };
1502
1503 let mut points = Vec::new();
1504 segment.for_each_flattened(tolerance:0.1, &mut |s: &LineSegment| {
1505 points.push(s.to);
1506 });
1507
1508 assert!(points.len() > 2);
1509}
1510
1511#[test]
1512fn flatten_with_t() {
1513 let segment = CubicBezierSegment {
1514 from: Point::new(0.0f32, 0.0),
1515 ctrl1: Point::new(0.0, 0.0),
1516 ctrl2: Point::new(50.0, 70.0),
1517 to: Point::new(100.0, 100.0),
1518 };
1519
1520 for tolerance in &[0.1, 0.01, 0.001, 0.0001] {
1521 let tolerance = *tolerance;
1522
1523 let mut a = Vec::new();
1524 segment.for_each_flattened(tolerance, &mut |s| {
1525 a.push(*s);
1526 });
1527
1528 let mut b = Vec::new();
1529 let mut ts = Vec::new();
1530 segment.for_each_flattened_with_t(tolerance, &mut |s, t| {
1531 b.push(*s);
1532 ts.push(t);
1533 });
1534
1535 assert_eq!(a, b);
1536
1537 for i in 0..b.len() {
1538 let sampled = segment.sample(ts[i].start);
1539 let point = b[i].from;
1540 let dist = (sampled - point).length();
1541 assert!(dist <= tolerance);
1542
1543 let sampled = segment.sample(ts[i].end);
1544 let point = b[i].to;
1545 let dist = (sampled - point).length();
1546 assert!(dist <= tolerance);
1547 }
1548 }
1549}
1550
1551#[test]
1552fn test_flatten_end() {
1553 let segment: CubicBezierSegment = CubicBezierSegment {
1554 from: Point::new(x:0.0, y:0.0),
1555 ctrl1: Point::new(x:100.0, y:0.0),
1556 ctrl2: Point::new(x:100.0, y:100.0),
1557 to: Point::new(x:100.0, y:200.0),
1558 };
1559
1560 let mut last: Point2D = segment.from;
1561 segment.for_each_flattened(tolerance:0.0001, &mut |s: &LineSegment| {
1562 last = s.to;
1563 });
1564
1565 assert_eq!(last, segment.to);
1566}
1567
1568#[test]
1569fn test_flatten_point() {
1570 let segment: CubicBezierSegment = CubicBezierSegment {
1571 from: Point::new(x:0.0, y:0.0),
1572 ctrl1: Point::new(x:0.0, y:0.0),
1573 ctrl2: Point::new(x:0.0, y:0.0),
1574 to: Point::new(x:0.0, y:0.0),
1575 };
1576
1577 let mut last: Point2D = segment.from;
1578 segment.for_each_flattened(tolerance:0.0001, &mut |s: &LineSegment| {
1579 last = s.to;
1580 });
1581
1582 assert_eq!(last, segment.to);
1583}
1584
1585#[test]
1586fn issue_652() {
1587 use crate::point;
1588
1589 let curve: CubicBezierSegment = CubicBezierSegment {
1590 from: point(x:-1061.0, y:-3327.0),
1591 ctrl1: point(x:-1061.0, y:-3177.0),
1592 ctrl2: point(x:-1061.0, y:-3477.0),
1593 to: point(x:-1061.0, y:-3327.0),
1594 };
1595
1596 for _ in curve.flattened(tolerance:1.0) {}
1597 for _ in curve.flattened(tolerance:0.1) {}
1598 for _ in curve.flattened(tolerance:0.01) {}
1599
1600 curve.for_each_flattened(tolerance:1.0, &mut |_| {});
1601 curve.for_each_flattened(tolerance:0.1, &mut |_| {});
1602 curve.for_each_flattened(tolerance:0.01, &mut |_| {});
1603}
1604
1605#[test]
1606fn fast_bounding_box_for_cubic_bezier_segment() {
1607 let a: CubicBezierSegment = CubicBezierSegment {
1608 from: Point::new(x:0.0, y:0.0),
1609 ctrl1: Point::new(x:0.5, y:1.0),
1610 ctrl2: Point::new(x:1.5, y:-1.0),
1611 to: Point::new(x:2.0, y:0.0),
1612 };
1613
1614 let expected_aabb: Box2D = Box2D {
1615 min: point(x:0.0, y:-1.0),
1616 max: point(x:2.0, y:1.0),
1617 };
1618
1619 let actual_aabb: Box2D = a.fast_bounding_box();
1620
1621 assert_eq!(expected_aabb, actual_aabb)
1622}
1623
1624#[test]
1625fn minimum_bounding_box_for_cubic_bezier_segment() {
1626 let a: CubicBezierSegment = CubicBezierSegment {
1627 from: Point::new(x:0.0, y:0.0),
1628 ctrl1: Point::new(x:0.5, y:2.0),
1629 ctrl2: Point::new(x:1.5, y:-2.0),
1630 to: Point::new(x:2.0, y:0.0),
1631 };
1632
1633 let expected_bigger_aabb: Box2D<f32> = Box2D {
1634 min: point(x:0.0, y:-0.6),
1635 max: point(x:2.0, y:0.6),
1636 };
1637 let expected_smaller_aabb: Box2D<f32> = Box2D {
1638 min: point(x:0.1, y:-0.5),
1639 max: point(x:2.0, y:0.5),
1640 };
1641
1642 let actual_minimum_aabb: Box2D = a.bounding_box();
1643
1644 assert!(expected_bigger_aabb.contains_box(&actual_minimum_aabb));
1645 assert!(actual_minimum_aabb.contains_box(&expected_smaller_aabb));
1646}
1647
1648#[test]
1649fn y_maximum_t_for_simple_cubic_segment() {
1650 let a: CubicBezierSegment = CubicBezierSegment {
1651 from: Point::new(x:0.0, y:0.0),
1652 ctrl1: Point::new(x:0.5, y:1.0),
1653 ctrl2: Point::new(x:1.5, y:1.0),
1654 to: Point::new(x:2.0, y:2.0),
1655 };
1656
1657 let expected_y_maximum: f64 = 1.0;
1658
1659 let actual_y_maximum: f64 = a.y_maximum_t();
1660
1661 assert_eq!(expected_y_maximum, actual_y_maximum)
1662}
1663
1664#[test]
1665fn y_minimum_t_for_simple_cubic_segment() {
1666 let a: CubicBezierSegment = CubicBezierSegment {
1667 from: Point::new(x:0.0, y:0.0),
1668 ctrl1: Point::new(x:0.5, y:1.0),
1669 ctrl2: Point::new(x:1.5, y:1.0),
1670 to: Point::new(x:2.0, y:0.0),
1671 };
1672
1673 let expected_y_minimum: f64 = 0.0;
1674
1675 let actual_y_minimum: f64 = a.y_minimum_t();
1676
1677 assert_eq!(expected_y_minimum, actual_y_minimum)
1678}
1679
1680#[test]
1681fn y_extrema_for_simple_cubic_segment() {
1682 let a: CubicBezierSegment = CubicBezierSegment {
1683 from: Point::new(x:0.0, y:0.0),
1684 ctrl1: Point::new(x:1.0, y:2.0),
1685 ctrl2: Point::new(x:2.0, y:2.0),
1686 to: Point::new(x:3.0, y:0.0),
1687 };
1688
1689 let mut n: u32 = 0;
1690 a.for_each_local_y_extremum_t(&mut |t: f64| {
1691 assert_eq!(t, 0.5);
1692 n += 1;
1693 });
1694 assert_eq!(n, 1);
1695}
1696
1697#[test]
1698fn x_extrema_for_simple_cubic_segment() {
1699 let a: CubicBezierSegment = CubicBezierSegment {
1700 from: Point::new(x:0.0, y:0.0),
1701 ctrl1: Point::new(x:1.0, y:2.0),
1702 ctrl2: Point::new(x:1.0, y:2.0),
1703 to: Point::new(x:0.0, y:0.0),
1704 };
1705
1706 let mut n: u32 = 0;
1707 a.for_each_local_x_extremum_t(&mut |t: f64| {
1708 assert_eq!(t, 0.5);
1709 n += 1;
1710 });
1711 assert_eq!(n, 1);
1712}
1713
1714#[test]
1715fn x_maximum_t_for_simple_cubic_segment() {
1716 let a: CubicBezierSegment = CubicBezierSegment {
1717 from: Point::new(x:0.0, y:0.0),
1718 ctrl1: Point::new(x:0.5, y:1.0),
1719 ctrl2: Point::new(x:1.5, y:1.0),
1720 to: Point::new(x:2.0, y:0.0),
1721 };
1722 let expected_x_maximum: f64 = 1.0;
1723
1724 let actual_x_maximum: f64 = a.x_maximum_t();
1725
1726 assert_eq!(expected_x_maximum, actual_x_maximum)
1727}
1728
1729#[test]
1730fn x_minimum_t_for_simple_cubic_segment() {
1731 let a: CubicBezierSegment = CubicBezierSegment {
1732 from: Point::new(x:0.0, y:0.0),
1733 ctrl1: Point::new(x:0.5, y:1.0),
1734 ctrl2: Point::new(x:1.5, y:1.0),
1735 to: Point::new(x:2.0, y:0.0),
1736 };
1737
1738 let expected_x_minimum: f64 = 0.0;
1739
1740 let actual_x_minimum: f64 = a.x_minimum_t();
1741
1742 assert_eq!(expected_x_minimum, actual_x_minimum)
1743}
1744
1745#[test]
1746fn derivatives() {
1747 let c1: CubicBezierSegment = CubicBezierSegment {
1748 from: Point::new(x:1.0, y:1.0),
1749 ctrl1: Point::new(x:1.0, y:2.0),
1750 ctrl2: Point::new(x:2.0, y:1.0),
1751 to: Point::new(x:2.0, y:2.0),
1752 };
1753
1754 assert_eq!(c1.dx(0.0), 0.0);
1755 assert_eq!(c1.dx(1.0), 0.0);
1756 assert_eq!(c1.dy(0.5), 0.0);
1757}
1758
1759#[test]
1760fn fat_line() {
1761 use crate::point;
1762
1763 let c1 = CubicBezierSegment {
1764 from: point(1.0f32, 2.0),
1765 ctrl1: point(1.0, 3.0),
1766 ctrl2: point(11.0, 11.0),
1767 to: point(11.0, 12.0),
1768 };
1769
1770 let (l1, l2) = c1.fat_line();
1771
1772 for i in 0..100 {
1773 let t = i as f32 / 99.0;
1774 assert!(l1.signed_distance_to_point(&c1.sample(t)) >= -0.000001);
1775 assert!(l2.signed_distance_to_point(&c1.sample(t)) <= 0.000001);
1776 }
1777
1778 let c2 = CubicBezierSegment {
1779 from: point(1.0f32, 2.0),
1780 ctrl1: point(1.0, 3.0),
1781 ctrl2: point(11.0, 14.0),
1782 to: point(11.0, 12.0),
1783 };
1784
1785 let (l1, l2) = c2.fat_line();
1786
1787 for i in 0..100 {
1788 let t = i as f32 / 99.0;
1789 assert!(l1.signed_distance_to_point(&c2.sample(t)) >= -0.000001);
1790 assert!(l2.signed_distance_to_point(&c2.sample(t)) <= 0.000001);
1791 }
1792
1793 let c3 = CubicBezierSegment {
1794 from: point(0.0f32, 1.0),
1795 ctrl1: point(0.5, 0.0),
1796 ctrl2: point(0.5, 0.0),
1797 to: point(1.0, 1.0),
1798 };
1799
1800 let (l1, l2) = c3.fat_line();
1801
1802 for i in 0..100 {
1803 let t = i as f32 / 99.0;
1804 assert!(l1.signed_distance_to_point(&c3.sample(t)) >= -0.000001);
1805 assert!(l2.signed_distance_to_point(&c3.sample(t)) <= 0.000001);
1806 }
1807}
1808
1809#[test]
1810fn is_linear() {
1811 let mut angle: f64 = 0.0;
1812 let center: Point2D = Point::new(x:1000.0, y:-700.0);
1813 for _ in 0..100 {
1814 for i: i32 in 0..10 {
1815 for j: i32 in 0..10 {
1816 let (sin: f64, cos: f64) = f64::sin_cos(self:angle);
1817 let endpoint: Vector2D = Vector::new(x:cos * 100.0, y:sin * 100.0);
1818 let curve: CubicBezierSegment = CubicBezierSegment {
1819 from: center - endpoint,
1820 ctrl1: center + endpoint.lerp(-endpoint, t:i as f64 / 9.0),
1821 ctrl2: center + endpoint.lerp(-endpoint, t:j as f64 / 9.0),
1822 to: center + endpoint,
1823 };
1824 assert!(curve.is_linear(1e-10));
1825 }
1826 }
1827 angle += 0.001;
1828 }
1829}
1830
1831#[test]
1832fn test_monotonic() {
1833 use crate::point;
1834 let curve: CubicBezierSegment = CubicBezierSegment {
1835 from: point(x:1.0, y:1.0),
1836 ctrl1: point(x:10.0, y:2.0),
1837 ctrl2: point(x:1.0, y:3.0),
1838 to: point(x:10.0, y:4.0),
1839 };
1840
1841 curve.for_each_monotonic_range(&mut |range: Range| {
1842 let sub_curve: CubicBezierSegment = curve.split_range(t_range:range);
1843 assert!(sub_curve.is_monotonic());
1844 });
1845}
1846
1847#[test]
1848fn test_line_segment_intersections() {
1849 use crate::point;
1850 fn assert_approx_eq(a: ArrayVec<(f32, f32), 3>, b: &[(f32, f32)], epsilon: f32) {
1851 for i in 0..a.len() {
1852 if f32::abs(a[i].0 - b[i].0) > epsilon || f32::abs(a[i].1 - b[i].1) > epsilon {
1853 std::println!("{a:?} != {b:?}");
1854 }
1855 assert!((a[i].0 - b[i].0).abs() <= epsilon && (a[i].1 - b[i].1).abs() <= epsilon);
1856 }
1857 assert_eq!(a.len(), b.len());
1858 }
1859
1860 let epsilon = 0.0001;
1861
1862 // Make sure we find intersections with horizontal and vertical lines.
1863
1864 let curve1 = CubicBezierSegment {
1865 from: point(-1.0, -1.0),
1866 ctrl1: point(0.0, 4.0),
1867 ctrl2: point(10.0, -4.0),
1868 to: point(11.0, 1.0),
1869 };
1870 let seg1 = LineSegment {
1871 from: point(0.0, 0.0),
1872 to: point(10.0, 0.0),
1873 };
1874 assert_approx_eq(
1875 curve1.line_segment_intersections_t(&seg1),
1876 &[(0.5, 0.5)],
1877 epsilon,
1878 );
1879
1880 let curve2 = CubicBezierSegment {
1881 from: point(-1.0, 0.0),
1882 ctrl1: point(0.0, 5.0),
1883 ctrl2: point(0.0, 5.0),
1884 to: point(1.0, 0.0),
1885 };
1886 let seg2 = LineSegment {
1887 from: point(0.0, 0.0),
1888 to: point(0.0, 5.0),
1889 };
1890 assert_approx_eq(
1891 curve2.line_segment_intersections_t(&seg2),
1892 &[(0.5, 0.75)],
1893 epsilon,
1894 );
1895}
1896
1897#[test]
1898fn test_parameters_for_value() {
1899 use crate::point;
1900 fn assert_approx_eq(a: ArrayVec<f32, 3>, b: &[f32], epsilon: f32) {
1901 for i in 0..a.len() {
1902 if f32::abs(a[i] - b[i]) > epsilon {
1903 std::println!("{a:?} != {b:?}");
1904 }
1905 assert!((a[i] - b[i]).abs() <= epsilon);
1906 }
1907 assert_eq!(a.len(), b.len());
1908 }
1909
1910 {
1911 let curve = CubicBezierSegment {
1912 from: point(0.0, 0.0),
1913 ctrl1: point(0.0, 8.0),
1914 ctrl2: point(10.0, 8.0),
1915 to: point(10.0, 0.0),
1916 };
1917
1918 let epsilon = 1e-4;
1919 assert_approx_eq(curve.solve_t_for_x(5.0), &[0.5], epsilon);
1920 assert_approx_eq(curve.solve_t_for_y(6.0), &[0.5], epsilon);
1921 }
1922 {
1923 let curve = CubicBezierSegment {
1924 from: point(0.0, 10.0),
1925 ctrl1: point(0.0, 10.0),
1926 ctrl2: point(10.0, 10.0),
1927 to: point(10.0, 10.0),
1928 };
1929
1930 assert_approx_eq(curve.solve_t_for_y(10.0), &[], 0.0);
1931 }
1932}
1933
1934#[test]
1935fn test_cubic_intersection_deduping() {
1936 use crate::point;
1937
1938 let epsilon = 0.0001;
1939
1940 // Two "line segments" with 3-fold overlaps, intersecting in their overlaps for a total of nine
1941 // parameter intersections.
1942 let line1 = CubicBezierSegment {
1943 from: point(-1_000_000.0, 0.0),
1944 ctrl1: point(2_000_000.0, 3_000_000.0),
1945 ctrl2: point(-2_000_000.0, -1_000_000.0),
1946 to: point(1_000_000.0, 2_000_000.0),
1947 };
1948 let line2 = CubicBezierSegment {
1949 from: point(-1_000_000.0, 2_000_000.0),
1950 ctrl1: point(2_000_000.0, -1_000_000.0),
1951 ctrl2: point(-2_000_000.0, 3_000_000.0),
1952 to: point(1_000_000.0, 0.0),
1953 };
1954 let intersections = line1.cubic_intersections(&line2);
1955 // (If you increase the coordinates above to 10s of millions, you get two returned intersection
1956 // points; i.e. the test fails.)
1957 assert_eq!(intersections.len(), 1);
1958 assert!(f64::abs(intersections[0].x) < epsilon);
1959 assert!(f64::abs(intersections[0].y - 1_000_000.0) < epsilon);
1960
1961 // Two self-intersecting curves that intersect in their self-intersections, for a total of four
1962 // parameter intersections.
1963 let curve1 = CubicBezierSegment {
1964 from: point(-10.0, -13.636363636363636),
1965 ctrl1: point(15.0, 11.363636363636363),
1966 ctrl2: point(-15.0, 11.363636363636363),
1967 to: point(10.0, -13.636363636363636),
1968 };
1969 let curve2 = CubicBezierSegment {
1970 from: point(13.636363636363636, -10.0),
1971 ctrl1: point(-11.363636363636363, 15.0),
1972 ctrl2: point(-11.363636363636363, -15.0),
1973 to: point(13.636363636363636, 10.0),
1974 };
1975 let intersections = curve1.cubic_intersections(&curve2);
1976 assert_eq!(intersections.len(), 1);
1977 assert!(f64::abs(intersections[0].x) < epsilon);
1978 assert!(f64::abs(intersections[0].y) < epsilon);
1979}
1980
1981#[test]
1982fn cubic_line_intersection_on_endpoint() {
1983 let l1 = LineSegment {
1984 from: Point::new(0.0, -100.0),
1985 to: Point::new(0.0, 100.0),
1986 };
1987
1988 let cubic = CubicBezierSegment {
1989 from: Point::new(0.0, 0.0),
1990 ctrl1: Point::new(20.0, 20.0),
1991 ctrl2: Point::new(20.0, 40.0),
1992 to: Point::new(0.0, 60.0),
1993 };
1994
1995 let intersections = cubic.line_segment_intersections_t(&l1);
1996
1997 assert_eq!(intersections.len(), 2);
1998 assert_eq!(intersections[0], (1.0, 0.8));
1999 assert_eq!(intersections[1], (0.0, 0.5));
2000
2001 let l2 = LineSegment {
2002 from: Point::new(0.0, 0.0),
2003 to: Point::new(0.0, 60.0),
2004 };
2005
2006 let intersections = cubic.line_segment_intersections_t(&l2);
2007
2008 assert!(intersections.is_empty());
2009
2010 let c1 = CubicBezierSegment {
2011 from: Point::new(0.0, 0.0),
2012 ctrl1: Point::new(20.0, 0.0),
2013 ctrl2: Point::new(20.0, 20.0),
2014 to: Point::new(0.0, 60.0),
2015 };
2016
2017 let c2 = CubicBezierSegment {
2018 from: Point::new(0.0, 60.0),
2019 ctrl1: Point::new(-40.0, 4.0),
2020 ctrl2: Point::new(-20.0, 20.0),
2021 to: Point::new(0.0, 00.0),
2022 };
2023
2024 let intersections = c1.cubic_intersections_t(&c2);
2025
2026 assert!(intersections.is_empty());
2027}
2028
2029#[test]
2030fn test_cubic_to_quadratics() {
2031 use euclid::approxeq::ApproxEq;
2032
2033 let quadratic = QuadraticBezierSegment {
2034 from: point(1.0, 2.0),
2035 ctrl: point(10.0, 5.0),
2036 to: point(0.0, 1.0),
2037 };
2038
2039 let mut count = 0;
2040 assert_eq!(quadratic.to_cubic().num_quadratics(0.0001), 1);
2041 quadratic
2042 .to_cubic()
2043 .for_each_quadratic_bezier(0.0001, &mut |c| {
2044 assert_eq!(count, 0);
2045 assert!(c.from.approx_eq(&quadratic.from));
2046 assert!(c.ctrl.approx_eq(&quadratic.ctrl));
2047 assert!(c.to.approx_eq(&quadratic.to));
2048 count += 1;
2049 });
2050
2051 let cubic = CubicBezierSegment {
2052 from: point(1.0f32, 1.0),
2053 ctrl1: point(10.0, 2.0),
2054 ctrl2: point(1.0, 3.0),
2055 to: point(10.0, 4.0),
2056 };
2057
2058 let mut prev = cubic.from;
2059 let mut count = 0;
2060 cubic.for_each_quadratic_bezier(0.01, &mut |c| {
2061 assert!(c.from.approx_eq(&prev));
2062 prev = c.to;
2063 count += 1;
2064 });
2065 assert!(prev.approx_eq(&cubic.to));
2066 assert!(count < 10);
2067 assert!(count > 4);
2068}
2069