makepad_vector/path/
path_iterator.rs

1use crate::path::{LinePathCommand, PathCommand};
2use crate::geometry::{CubicSegment, QuadraticSegment};
3use crate::internal_iter::InternalIterator;
4
5/// An extension trait for iterators over path commands.
6pub trait PathIterator: InternalIterator<Item = PathCommand> {
7    /// Returns an iterator over line path commands that approximate `self` with tolerance
8    /// `epsilon`.
9    fn linearize(self, epsilon: f64) -> Linearize<Self>
10    where
11        Self: Sized,
12    {
13        Linearize {
14            path: self,
15            epsilon,
16        }
17    }
18}
19
20impl<I> PathIterator for I where I: InternalIterator<Item = PathCommand> {}
21
22/// An iterator over line path commands that approximate `self` with tolerance `epsilon`.
23#[derive(Clone, Debug)]
24pub struct Linearize<P> {
25    path: P,
26    epsilon: f64,
27}
28
29impl<P> InternalIterator for Linearize<P>
30where
31    P: PathIterator,
32{
33    type Item = LinePathCommand;
34
35    fn for_each<F>(self, f: &mut F) -> bool
36    where
37        F: FnMut(LinePathCommand) -> bool,
38    {
39        let mut initial_point = None;
40        let mut current_point = None;
41        self.path.for_each({
42            let epsilon = self.epsilon;
43            &mut move |command| match command {
44                PathCommand::MoveTo(p) => {
45                    initial_point = Some(p);
46                    current_point = Some(p);
47                    f(LinePathCommand::MoveTo(p))
48                }
49                PathCommand::LineTo(p) => {
50                    current_point = Some(p);
51                    f(LinePathCommand::LineTo(p))
52                }
53                PathCommand::QuadraticTo(p1, p) => {
54                    QuadraticSegment::new(current_point.unwrap(), p1, p)
55                        .linearize(epsilon)
56                        .for_each(&mut |p| {
57                            current_point = Some(p);
58                            f(LinePathCommand::LineTo(p))
59                        })
60                }
61                PathCommand::CubicTo(p1, p2, p) => {
62                    CubicSegment::new(current_point.unwrap(), p1, p2, p)
63                        .linearize(epsilon)
64                        .for_each(&mut |p| {
65                            current_point = Some(p);
66                            f(LinePathCommand::LineTo(p))
67                        })
68                }
69                PathCommand::Close => {
70                    current_point = initial_point;
71                    f(LinePathCommand::Close)
72                }
73            }
74        })
75    }
76}