zeno/
segment.rs

1//! Path segmentation.
2
3#![allow(clippy::excessive_precision)]
4
5use super::command::Command;
6use super::geometry::*;
7#[allow(unused)]
8use super::F32Ext;
9
10use core::borrow::Borrow;
11use core::f32;
12
13/// Represents the time parameter for a specific distance along
14/// a segment.
15#[derive(Copy, Clone, Debug)]
16pub struct SegmentTime {
17    pub distance: f32,
18    pub time: f32,
19}
20
21/// Line segment.
22#[derive(Copy, Clone, PartialEq, Debug)]
23pub struct Line {
24    pub a: Point,
25    pub b: Point,
26}
27
28impl Line {
29    /// Creates a new line segment.
30    pub fn new(a: impl Into<Vector>, b: impl Into<Vector>) -> Self {
31        Self {
32            a: a.into(),
33            b: b.into(),
34        }
35    }
36
37    /// Returns the length of the line segment.
38    pub fn length(&self) -> f32 {
39        (self.b - self.a).length()
40    }
41
42    /// Returns a slice of the line segment described by the specified start and end times.
43    #[allow(unused)]
44    pub fn slice(&self, start: f32, end: f32) -> Self {
45        let dir = self.b - self.a;
46        Self::new(self.a + dir * start, self.a + dir * end)
47    }
48
49    #[allow(unused)]
50    pub fn time(&self, distance: f32) -> SegmentTime {
51        let len = (self.b - self.a).length();
52        if distance > len {
53            return SegmentTime {
54                distance: len,
55                time: 1.,
56            };
57        }
58        SegmentTime {
59            distance,
60            time: distance / len,
61        }
62    }
63
64    #[allow(unused)]
65    pub fn reverse(&self) -> Self {
66        Self::new(self.b, self.a)
67    }
68}
69
70/// Cubic bezier curve.
71#[derive(Copy, Clone, PartialEq, Default, Debug)]
72pub struct Curve {
73    pub a: Point,
74    pub b: Point,
75    pub c: Point,
76    pub d: Point,
77}
78
79impl Curve {
80    /// Creates a new curve.
81    pub fn new(
82        a: impl Into<Point>,
83        b: impl Into<Point>,
84        c: impl Into<Point>,
85        d: impl Into<Point>,
86    ) -> Self {
87        Curve {
88            a: a.into(),
89            b: b.into(),
90            c: c.into(),
91            d: d.into(),
92        }
93    }
94
95    /// Creates a new curve from a quadratic bezier curve.
96    pub fn from_quadratic(a: impl Into<Point>, b: impl Into<Point>, c: impl Into<Point>) -> Self {
97        let a = a.into();
98        let b = b.into();
99        let c = c.into();
100        Self {
101            a,
102            b: Point::new(a.x + 2. / 3. * (b.x - a.x), a.y + 2. / 3. * (b.y - a.y)),
103            c: Point::new(c.x + 2. / 3. * (b.x - c.x), c.y + 2. / 3. * (b.y - c.y)),
104            d: c,
105        }
106    }
107
108    /// Returns the length of the curve.
109    pub fn length(&self) -> f32 {
110        let mut len = 0.;
111        let mut prev = self.a;
112        let steps = 64;
113        let step = 1. / steps as f32;
114        let mut t = 0.;
115        for _ in 0..=steps {
116            t += step;
117            let next = self.evaluate(t);
118            len += (next - prev).length();
119            prev = next;
120        }
121        len
122    }
123
124    /// Returns a slice of the curve described by the specified start and end times.
125    pub fn slice(&self, start: f32, end: f32) -> Self {
126        let t0 = start;
127        let t1 = end;
128        let u0 = 1. - t0;
129        let u1 = 1. - t1;
130        let v0 = self.a;
131        let v1 = self.b;
132        let v2 = self.c;
133        let v3 = self.d;
134        Self::new(
135            (v0 * (u0 * u0 * u0))
136                + (v1 * (t0 * u0 * u0 + u0 * t0 * u0 + u0 * u0 * t0))
137                + (v2 * (t0 * t0 * u0 + u0 * t0 * t0 + t0 * u0 * t0))
138                + (v3 * (t0 * t0 * t0)),
139            (v0 * (u0 * u0 * u1))
140                + (v1 * (t0 * u0 * u1 + u0 * t0 * u1 + u0 * u0 * t1))
141                + (v2 * (t0 * t0 * u1 + u0 * t0 * t1 + t0 * u0 * t1))
142                + (v3 * (t0 * t0 * t1)),
143            (v0 * (u0 * u1 * u1))
144                + (v1 * (t0 * u1 * u1 + u0 * t1 * u1 + u0 * u1 * t1))
145                + (v2 * (t0 * t1 * u1 + u0 * t1 * t1 + t0 * u1 * t1))
146                + (v3 * (t0 * t1 * t1)),
147            (v0 * (u1 * u1 * u1))
148                + (v1 * (t1 * u1 * u1 + u1 * t1 * u1 + u1 * u1 * t1))
149                + (v2 * (t1 * t1 * u1 + u1 * t1 * t1 + t1 * u1 * t1))
150                + (v3 * (t1 * t1 * t1)),
151        )
152    }
153
154    /// Returns a curve with the direction reversed.
155    #[allow(unused)]
156    pub fn reverse(&self) -> Self {
157        Self::new(self.d, self.c, self.b, self.a)
158    }
159
160    /// Returns the time parameter for the specified linear distance along
161    /// the curve.
162    #[allow(unused)]
163    pub fn time(&self, distance: f32, tolerance: f32) -> SegmentTime {
164        let (distance, time) = self.time_impl(distance, tolerance, 1., 0);
165        SegmentTime { distance, time }
166    }
167
168    /// Returns true if the curve can be represented as a line within some
169    /// tolerance.
170    pub fn is_line(&self, tolerance: f32) -> bool {
171        let degen_ab = self.a.nearly_eq_by(self.b, tolerance);
172        let degen_bc = self.b.nearly_eq_by(self.c, tolerance);
173        let degen_cd = self.c.nearly_eq_by(self.d, tolerance);
174        degen_ab as u8 + degen_bc as u8 + degen_cd as u8 >= 2
175    }
176
177    /// Evaluates the curve at the specified time.
178    pub fn evaluate(&self, time: f32) -> Point {
179        let t = time;
180        let t0 = 1. - t;
181        (self.a * (t0 * t0 * t0))
182            + (self.b * (3. * t0 * t0 * t))
183            + (self.c * (3. * t0 * t * t))
184            + (self.d * (t * t * t))
185    }
186
187    #[allow(clippy::wrong_self_convention)]
188    fn to_segment(&self, id: SegmentId) -> Option<Segment> {
189        if self.is_line(MERGE_EPSILON) {
190            if self.a.nearly_eq_by(self.d, MERGE_EPSILON) {
191                None
192            } else {
193                Some(Segment::Line(id, Line::new(self.a, self.d)))
194            }
195        } else {
196            Some(Segment::Curve(id, *self))
197        }
198    }
199
200    fn split_at_max_curvature(&self, splits: &mut [Curve; 4]) -> usize {
201        let mut tmp = [0f32; 3];
202        let count1 = self.max_curvature(&mut tmp);
203        let mut count = 0;
204        let mut ts = [0f32; 4];
205        for &t in &tmp[..count1] {
206            if t > 0. && t < 1. {
207                ts[count] = t;
208                count += 1;
209            }
210        }
211        if count == 0 {
212            splits[0] = *self;
213        } else {
214            let mut i = 0;
215            let mut last_t = 0.;
216            for &t in &ts[..count] {
217                splits[i] = self.slice(last_t, t);
218                i += 1;
219                last_t = t;
220            }
221            splits[i] = self.slice(last_t, 1.);
222        }
223        count + 1
224    }
225
226    fn split(&self, t: f32) -> (Self, Self) {
227        (self.slice(0., t), self.slice(t, 1.))
228    }
229
230    fn time_impl(&self, distance: f32, tolerance: f32, t: f32, level: u8) -> (f32, f32) {
231        if level < 5 && self.too_curvy(tolerance) {
232            let c0 = self.slice(0., 0.5);
233            let (dist0, t0) = c0.time_impl(distance, tolerance, t * 0.5, level + 1);
234            if dist0 < distance {
235                let c1 = self.slice(0.5, 1.);
236                let (dist1, t1) = c1.time_impl(distance - dist0, tolerance, t * 0.5, level + 1);
237                (dist0 + dist1, t0 + t1)
238            } else {
239                (dist0, t0)
240            }
241        } else {
242            let dist = (self.d - self.a).length();
243            if dist >= distance {
244                let s = distance / dist;
245                (distance, t * s)
246            } else {
247                (dist, t)
248            }
249        }
250    }
251
252    fn max_curvature(&self, ts: &mut [f32; 3]) -> usize {
253        let comps_x = [self.a.x, self.b.x, self.c.x, self.d.x];
254        let comps_y = [self.a.y, self.b.y, self.c.y, self.d.y];
255        fn get_coeffs(src: [f32; 4]) -> [f32; 4] {
256            let a = src[1] - src[0];
257            let b = src[2] - 2. * src[1] + src[0];
258            let c = src[3] + 3. * (src[1] - src[2]) - src[0];
259            [c * c, 3. * b * c, 2. * b * b + c * a, a * b]
260        }
261        let mut coeffs = get_coeffs(comps_x);
262        let coeffs_y = get_coeffs(comps_y);
263        for i in 0..4 {
264            coeffs[i] += coeffs_y[i];
265        }
266        Self::solve(coeffs, ts)
267    }
268
269    fn solve(coeff: [f32; 4], ts: &mut [f32; 3]) -> usize {
270        const PI: f32 = core::f32::consts::PI;
271        let i = 1. / coeff[0];
272        let a = coeff[1] * i;
273        let b = coeff[2] * i;
274        let c = coeff[3] * i;
275        let q = (a * a - b * 3.) / 9.;
276        let r = (2. * a * a * a - 9. * a * b + 27. * c) / 54.;
277        let q3 = q * q * q;
278        let r2_sub_q3 = r * r - q3;
279        let adiv3 = a / 3.;
280        if r2_sub_q3 < 0. {
281            let theta = satf32(r / q3.sqrt()).acos();
282            let neg2_root_q = -2. * q.sqrt();
283            ts[0] = satf32(neg2_root_q * (theta / 3.).cos() - adiv3);
284            ts[1] = satf32(neg2_root_q * ((theta + 2. * PI) / 3.).cos() - adiv3);
285            ts[2] = satf32(neg2_root_q * ((theta - 2. * PI) / 3.).cos() - adiv3);
286            ts.sort_unstable_by(|x, y| x.partial_cmp(y).unwrap_or(core::cmp::Ordering::Less));
287            let mut count = 3;
288            if ts[0] == ts[1] {
289                ts[1] = ts[2];
290                count -= 1;
291            }
292            if ts[1] == ts[2] {
293                count -= 1;
294            }
295            count
296        } else {
297            let mut a = r.abs() + r2_sub_q3.sqrt();
298            a = a.powf(0.3333333);
299            if r > 0. {
300                a = -a;
301            }
302            if a != 0. {
303                a += q / a;
304            }
305            ts[0] = satf32(a - adiv3);
306            1
307        }
308    }
309
310    fn too_curvy(&self, tolerance: f32) -> bool {
311        (2. * self.d.x - 3. * self.c.x + self.a.x).abs() > tolerance
312            || (2. * self.d.y - 3. * self.c.y + self.a.y).abs() > tolerance
313            || (self.d.x - 3. * self.b.x + 2. * self.a.x).abs() > tolerance
314            || (self.d.y - 3. * self.b.y + 2. * self.a.y).abs() > tolerance
315    }
316
317    fn needs_split(&self) -> bool {
318        if self.b.nearly_eq_by(self.c, MERGE_EPSILON) {
319            return true;
320        }
321        let normal_ab = normal(self.a, self.b);
322        let normal_bc = normal(self.b, self.c);
323        fn too_curvy(n0: Vector, n1: Vector) -> bool {
324            const FLAT_ENOUGH: f32 = f32::consts::SQRT_2 / 2. + 1. / 10.;
325            n0.dot(n1) <= FLAT_ENOUGH
326        }
327        too_curvy(normal_ab, normal_bc) || too_curvy(normal_bc, normal(self.c, self.d))
328    }
329}
330
331fn satf32(x: f32) -> f32 {
332    x.clamp(0., 1.)
333}
334
335/// Marker that allows regrouping of previously split segments due to simplification.
336pub type SegmentId = u8;
337
338/// Segment of a path.
339#[derive(Copy, Clone, PartialEq, Debug)]
340pub enum Segment {
341    /// Line segment..
342    Line(SegmentId, Line),
343    /// Cubic bezier segment.
344    Curve(SegmentId, Curve),
345    /// Marks the end of a subpath. Contains the value `true` if the subpath
346    /// is closed.
347    End(bool),
348}
349
350impl Segment {
351    pub fn length(&self) -> f32 {
352        match self {
353            Self::Line(_, line) => line.length(),
354            Self::Curve(_, curve) => curve.length(),
355            _ => 0.,
356        }
357    }
358
359    #[allow(unused)]
360    pub fn slice(&self, start: f32, end: f32) -> Self {
361        match self {
362            Self::Line(id, line) => Self::Line(*id, line.slice(start, end)),
363            Self::Curve(id, curve) => Self::Curve(*id, curve.slice(start, end)),
364            Self::End(..) => *self,
365        }
366    }
367
368    #[allow(unused)]
369    pub fn reverse(&self) -> Self {
370        match self {
371            Self::Line(id, line) => Self::Line(*id, line.reverse()),
372            Self::Curve(id, curve) => Self::Curve(*id, curve.reverse()),
373            Self::End(..) => *self,
374        }
375    }
376
377    #[allow(unused)]
378    pub fn time(&self, distance: f32, tolerance: f32) -> SegmentTime {
379        match self {
380            Self::Line(_, line) => line.time(distance),
381            Self::Curve(_, curve) => curve.time(distance, tolerance),
382            _ => SegmentTime {
383                distance: 0.,
384                time: 0.,
385            },
386        }
387    }
388
389    #[allow(unused)]
390    pub fn point_normal(&self, time: f32) -> (Point, Vector) {
391        match self {
392            Self::Line(_, line) => {
393                let dir = line.b - line.a;
394                let p = line.a + dir * time;
395                let n = normal(line.a, line.b);
396                (p, n)
397            }
398            Self::Curve(_, curve) => {
399                let p = curve.evaluate(time);
400                let a = curve.evaluate(time - 0.05);
401                let b = curve.evaluate(time + 0.05);
402                let n = normal(a, b);
403                (p, n)
404            }
405            Self::End(..) => (Point::ZERO, Vector::ZERO),
406        }
407    }
408}
409
410impl Default for Segment {
411    fn default() -> Self {
412        Self::End(false)
413    }
414}
415
416// This large epsilon trades fidelity for performance, visual continuity
417// and numeric stability.
418const MERGE_EPSILON: f32 = 0.01;
419
420/// Creates a segment iterator from a command iterator, optionally producing
421/// simplified curves.
422pub fn segments<I>(commands: I, simplify_curves: bool) -> Segments<I>
423where
424    I: Iterator + Clone,
425    I::Item: Borrow<Command>,
426{
427    Segments::new(simplify_curves, commands)
428}
429
430/// Iterator over path segments.
431#[derive(Clone)]
432pub struct Segments<I> {
433    commands: I,
434    start: Vector,
435    prev: Vector,
436    close: bool,
437    split: bool,
438    splits: [Curve; 16],
439    split_count: usize,
440    split_index: usize,
441    last_was_end: bool,
442    id: u8,
443    count: u32,
444}
445
446impl<I> Segments<I>
447where
448    I: Iterator + Clone,
449    I::Item: Borrow<Command>,
450{
451    fn new(split: bool, commands: I) -> Self {
452        Self {
453            commands,
454            start: Vector::ZERO,
455            prev: Vector::ZERO,
456            close: false,
457            split,
458            splits: [Curve::default(); 16],
459            split_count: 0,
460            split_index: 0,
461            last_was_end: true,
462            id: 0,
463            count: 0,
464        }
465    }
466
467    #[allow(clippy::needless_range_loop)]
468    fn split_curve(&mut self, id: SegmentId, c: &Curve) -> Option<Segment> {
469        if c.is_line(MERGE_EPSILON) {
470            if c.a.nearly_eq_by(c.d, MERGE_EPSILON) {
471                return None;
472            }
473            return Some(Segment::Line(id, Line::new(c.a, c.d)));
474        }
475        let mut splits = [Curve::default(); 4];
476        let count = c.split_at_max_curvature(&mut splits);
477        let mut i = 0;
478        for j in 0..count {
479            let curve = splits[j];
480            if curve.needs_split() {
481                let (a, b) = curve.split(0.5);
482                if a.needs_split() {
483                    let (c, d) = a.split(0.5);
484                    self.splits[i] = c;
485                    self.splits[i + 1] = d;
486                    i += 2;
487                } else {
488                    self.splits[i] = a;
489                    i += 1;
490                }
491                if b.needs_split() {
492                    let (c, d) = b.split(0.5);
493                    self.splits[i] = c;
494                    self.splits[i + 1] = d;
495                    i += 2;
496                } else {
497                    self.splits[i] = b;
498                    i += 1;
499                }
500            } else {
501                self.splits[i] = curve;
502                i += 1;
503            }
504        }
505        self.split_count = i;
506        self.split_index = 1;
507        self.splits[0].to_segment(id)
508    }
509
510    fn inc_id(&mut self) {
511        if self.id == 254 {
512            self.id = 0;
513        } else {
514            self.id += 1;
515        }
516    }
517}
518
519impl<I> Iterator for Segments<I>
520where
521    I: Iterator + Clone,
522    I::Item: Borrow<Command>,
523{
524    type Item = Segment;
525
526    fn next(&mut self) -> Option<Self::Item> {
527        use Command::*;
528        if self.close {
529            self.close = false;
530            self.last_was_end = true;
531            return Some(Segment::End(true));
532        }
533        if self.split {
534            loop {
535                if self.split_index < self.split_count {
536                    let curve = self.splits[self.split_index];
537                    self.split_index += 1;
538                    if let Some(segment) = curve.to_segment(self.id) {
539                        self.count += 1;
540                        self.last_was_end = false;
541                        self.prev = curve.d;
542                        return Some(segment);
543                    }
544                    continue;
545                }
546                self.inc_id();
547                let id = self.id;
548                let from = self.prev;
549                match *self.commands.next()?.borrow() {
550                    MoveTo(to) => {
551                        self.start = to;
552                        self.prev = to;
553                        self.count = 0;
554                        if !self.last_was_end {
555                            self.last_was_end = true;
556                            return Some(Segment::End(false));
557                        }
558                    }
559                    LineTo(to) => {
560                        if !from.nearly_eq_by(to, MERGE_EPSILON) {
561                            self.count += 1;
562                            self.prev = to;
563                            self.last_was_end = false;
564                            return Some(Segment::Line(id, Line::new(from, to)));
565                        }
566                    }
567                    CurveTo(c1, c2, to) => {
568                        if let Some(segment) = self.split_curve(id, &Curve::new(from, c1, c2, to)) {
569                            self.count += 1;
570                            self.prev = to;
571                            self.last_was_end = false;
572                            return Some(segment);
573                        }
574                    }
575                    QuadTo(c, to) => {
576                        if let Some(segment) =
577                            self.split_curve(id, &Curve::from_quadratic(from, c, to))
578                        {
579                            self.count += 1;
580                            self.prev = to;
581                            self.last_was_end = false;
582                            return Some(segment);
583                        }
584                    }
585                    Close => {
586                        self.prev = self.start;
587                        if self.count == 0 || !from.nearly_eq_by(self.start, MERGE_EPSILON) {
588                            self.close = true;
589                            return Some(Segment::Line(id, Line::new(from, self.start)));
590                        } else {
591                            self.count = 0;
592                            self.last_was_end = true;
593                            return Some(Segment::End(true));
594                        }
595                    }
596                }
597            }
598        } else {
599            let id = self.id;
600            self.inc_id();
601            loop {
602                let from = self.prev;
603                match *self.commands.next()?.borrow() {
604                    MoveTo(to) => {
605                        self.start = to;
606                        self.prev = to;
607                        self.count = 0;
608                        if !self.last_was_end {
609                            self.last_was_end = true;
610                            return Some(Segment::End(false));
611                        }
612                    }
613                    LineTo(to) => {
614                        if !from.nearly_eq_by(to, MERGE_EPSILON) {
615                            self.count += 1;
616                            self.prev = to;
617                            self.last_was_end = false;
618                            return Some(Segment::Line(id, Line::new(from, to)));
619                        }
620                    }
621                    CurveTo(c1, c2, to) => {
622                        let segment = Segment::Curve(id, Curve::new(from, c1, c2, to));
623                        self.count += 1;
624                        self.prev = to;
625                        self.last_was_end = false;
626                        return Some(segment);
627                    }
628                    QuadTo(c, to) => {
629                        let segment = Segment::Curve(id, Curve::from_quadratic(from, c, to));
630                        self.count += 1;
631                        self.prev = to;
632                        self.last_was_end = false;
633                        return Some(segment);
634                    }
635                    Close => {
636                        self.prev = self.start;
637                        if self.count == 0 || !from.nearly_eq_by(self.start, 0.01) {
638                            self.close = true;
639                            return Some(Segment::Line(id, Line::new(from, self.start)));
640                        } else {
641                            self.count = 0;
642                            self.last_was_end = true;
643                            return Some(Segment::End(true));
644                        }
645                    }
646                }
647            }
648        }
649    }
650}