lyon_path/
iterator.rs

1//! Tools to iterate over paths.
2//!
3//! # Lyon path iterators
4//!
5//! ## Overview
6//!
7//! This module provides a collection of traits to extend the `Iterator` trait when
8//! iterating over paths.
9//!
10//! ## Examples
11//!
12//! ```
13//! use lyon_path::iterator::*;
14//! use lyon_path::math::{point, vector};
15//! use lyon_path::{Path, PathEvent};
16//!
17//! // Start with a path.
18//! let mut builder = Path::builder();
19//! builder.begin(point(0.0, 0.0));
20//! builder.line_to(point(10.0, 0.0));
21//! builder.cubic_bezier_to(point(10.0, 10.0), point(0.0, 10.0), point(0.0, 5.0));
22//! builder.end(true);
23//! let path = builder.build();
24//!
25//! // A simple std::iter::Iterator<PathEvent>,
26//! let simple_iter = path.iter();
27//!
28//! // Make it an iterator over simpler primitives flattened events,
29//! // which do not contain any curve. To do so we approximate each curve
30//! // linear segments according to a tolerance threshold which controls
31//! // the tradeoff between fidelity of the approximation and amount of
32//! // generated events. Let's use a tolerance threshold of 0.01.
33//! // The beauty of this approach is that the flattening happens lazily
34//! // while iterating without allocating memory for the path.
35//! let flattened_iter = path.iter().flattened(0.01);
36//!
37//! for evt in flattened_iter {
38//!     match evt {
39//!         PathEvent::Begin { at } => { println!(" - move to {:?}", at); }
40//!         PathEvent::Line { from, to } => { println!(" - line {:?} -> {:?}", from, to); }
41//!         PathEvent::End { last, first, close } => {
42//!             if close {
43//!                 println!(" - close {:?} -> {:?}", last, first);
44//!             } else {
45//!                 println!(" - end");
46//!             }
47//!         }
48//!         _ => { panic!() }
49//!     }
50//! }
51//! ```
52//!
53//! Chaining the provided iterators allow performing some path manipulations lazily
54//! without allocating actual path objects to hold the result of the transformations.
55//!
56//! ```
57//! extern crate lyon_path;
58//! use lyon_path::iterator::*;
59//! use lyon_path::math::{point, Angle, Rotation};
60//! use lyon_path::Path;
61//!
62//! fn main() {
63//!     // In practice it is more common to iterate over Path objects than vectors
64//!     // of SVG commands (the former can be constructed from the latter).
65//!     let mut builder = Path::builder();
66//!     builder.begin(point(1.0, 1.0));
67//!     builder.line_to(point(2.0, 1.0));
68//!     builder.quadratic_bezier_to(point(2.0, 2.0), point(1.0, 2.0));
69//!     builder.cubic_bezier_to(point(0.0, 2.0), point(0.0, 0.0), point(1.0, 0.0));
70//!     builder.end(true);
71//!     let path = builder.build();
72//!
73//!     let transform = Rotation::new(Angle::radians(1.0));
74//!
75//!     for evt in path.iter().transformed(&transform).flattened(0.1) {
76//!         // ...
77//!     }
78//! }
79//! ```
80
81use crate::geom::traits::Transformation;
82use crate::geom::{cubic_bezier, quadratic_bezier, CubicBezierSegment, QuadraticBezierSegment};
83use crate::math::*;
84use crate::{Attributes, Event, PathEvent};
85
86// TODO: It would be great to add support for attributes in PathIterator.
87
88/// An extension trait for `PathEvent` iterators.
89pub trait PathIterator: Iterator<Item = PathEvent> + Sized {
90    /// Returns an iterator that turns curves into line segments.
91    fn flattened(self, tolerance: f32) -> Flattened<Self> {
92        Flattened::new(tolerance, self)
93    }
94
95    /// Returns an iterator applying a 2D transform to all of its events.
96    fn transformed<T: Transformation<f32>>(self, mat: &T) -> Transformed<Self, T> {
97        Transformed::new(mat, self)
98    }
99}
100
101impl<Iter> PathIterator for Iter where Iter: Iterator<Item = PathEvent> {}
102
103pub struct NoAttributes<Iter>(pub(crate) Iter);
104
105impl<'l, Iter> NoAttributes<Iter>
106where
107    Iter: Iterator<Item = Event<(Point, Attributes<'l>), Point>>,
108{
109    pub fn with_attributes(self) -> Iter {
110        self.0
111    }
112}
113
114impl<'l, Iter> Iterator for NoAttributes<Iter>
115where
116    Iter: Iterator<Item = Event<(Point, Attributes<'l>), Point>>,
117{
118    type Item = PathEvent;
119    fn next(&mut self) -> Option<PathEvent> {
120        self.0.next().map(|event| event.with_points())
121    }
122}
123
124/// An iterator that consumes `Event` iterator and yields flattened path events (with no curves).
125pub struct Flattened<Iter> {
126    it: Iter,
127    current_position: Point,
128    current_curve: TmpFlatteningIter,
129    tolerance: f32,
130}
131
132enum TmpFlatteningIter {
133    Quadratic(quadratic_bezier::Flattened<f32>),
134    Cubic(cubic_bezier::Flattened<f32>),
135    None,
136}
137
138impl<Iter: Iterator<Item = PathEvent>> Flattened<Iter> {
139    /// Create the iterator.
140    pub fn new(tolerance: f32, it: Iter) -> Self {
141        Flattened {
142            it,
143            current_position: point(0.0, 0.0),
144            current_curve: TmpFlatteningIter::None,
145            tolerance,
146        }
147    }
148}
149
150impl<Iter> Iterator for Flattened<Iter>
151where
152    Iter: Iterator<Item = PathEvent>,
153{
154    type Item = PathEvent;
155    fn next(&mut self) -> Option<PathEvent> {
156        match self.current_curve {
157            TmpFlatteningIter::Quadratic(ref mut it) => {
158                if let Some(to) = it.next() {
159                    let from = self.current_position;
160                    self.current_position = to;
161                    return Some(PathEvent::Line { from, to });
162                }
163            }
164            TmpFlatteningIter::Cubic(ref mut it) => {
165                if let Some(to) = it.next() {
166                    let from = self.current_position;
167                    self.current_position = to;
168                    return Some(PathEvent::Line { from, to });
169                }
170            }
171            _ => {}
172        }
173        self.current_curve = TmpFlatteningIter::None;
174        match self.it.next() {
175            Some(PathEvent::Begin { at }) => Some(PathEvent::Begin { at }),
176            Some(PathEvent::Line { from, to }) => Some(PathEvent::Line { from, to }),
177            Some(PathEvent::End { last, first, close }) => {
178                Some(PathEvent::End { last, first, close })
179            }
180            Some(PathEvent::Quadratic { from, ctrl, to }) => {
181                self.current_position = from;
182                self.current_curve = TmpFlatteningIter::Quadratic(
183                    QuadraticBezierSegment { from, ctrl, to }.flattened(self.tolerance),
184                );
185                self.next()
186            }
187            Some(PathEvent::Cubic {
188                from,
189                ctrl1,
190                ctrl2,
191                to,
192            }) => {
193                self.current_position = from;
194                self.current_curve = TmpFlatteningIter::Cubic(
195                    CubicBezierSegment {
196                        from,
197                        ctrl1,
198                        ctrl2,
199                        to,
200                    }
201                    .flattened(self.tolerance),
202                );
203                self.next()
204            }
205            None => None,
206        }
207    }
208
209    fn size_hint(&self) -> (usize, Option<usize>) {
210        // At minimum, the inner iterator size hint plus the flattening iterator size hint can form the lower
211        // bracket.
212        // We can't determine a maximum limit.
213        let mut lo = self.it.size_hint().0;
214        match &self.current_curve {
215            TmpFlatteningIter::Quadratic(t) => {
216                lo += t.size_hint().0;
217            }
218            TmpFlatteningIter::Cubic(t) => {
219                lo += t.size_hint().0;
220            }
221            _ => {}
222        }
223        (lo, None)
224    }
225}
226
227/// Applies a 2D transform to a path iterator and yields the resulting path iterator.
228pub struct Transformed<'l, I, T> {
229    it: I,
230    transform: &'l T,
231}
232
233impl<'l, I, T: Transformation<f32>> Transformed<'l, I, T>
234where
235    I: Iterator<Item = PathEvent>,
236{
237    /// Creates a new transformed path iterator from a path iterator.
238    #[inline]
239    pub fn new(transform: &'l T, it: I) -> Transformed<'l, I, T> {
240        Transformed { it, transform }
241    }
242}
243
244impl<'l, I, T> Iterator for Transformed<'l, I, T>
245where
246    I: Iterator<Item = PathEvent>,
247    T: Transformation<f32>,
248{
249    type Item = PathEvent;
250    fn next(&mut self) -> Option<PathEvent> {
251        match self.it.next() {
252            None => None,
253            Some(ref evt) => Some(evt.transformed(self.transform)),
254        }
255    }
256}
257
258/// An iterator that consumes an iterator of `Point`s and produces `Event`s.
259///
260/// # Example
261///
262/// ```
263/// # extern crate lyon_path;
264/// # use lyon_path::iterator::FromPolyline;
265/// # use lyon_path::math::point;
266/// # fn main() {
267/// let points = [
268///     point(1.0, 1.0),
269///     point(2.0, 1.0),
270///     point(1.0, 2.0)
271/// ];
272/// let iter = FromPolyline::closed(points.iter().cloned());
273/// # }
274/// ```
275pub struct FromPolyline<Iter> {
276    iter: Iter,
277    current: Point,
278    first: Point,
279    is_first: bool,
280    done: bool,
281    close: bool,
282}
283
284impl<Iter: Iterator<Item = Point>> FromPolyline<Iter> {
285    pub fn new(close: bool, iter: Iter) -> Self {
286        FromPolyline {
287            iter,
288            current: point(0.0, 0.0),
289            first: point(0.0, 0.0),
290            is_first: true,
291            done: false,
292            close,
293        }
294    }
295
296    pub fn closed(iter: Iter) -> Self {
297        FromPolyline::new(true, iter)
298    }
299
300    pub fn open(iter: Iter) -> Self {
301        FromPolyline::new(false, iter)
302    }
303}
304
305impl<Iter> Iterator for FromPolyline<Iter>
306where
307    Iter: Iterator<Item = Point>,
308{
309    type Item = PathEvent;
310
311    fn next(&mut self) -> Option<PathEvent> {
312        if self.done {
313            return None;
314        }
315
316        if let Some(next) = self.iter.next() {
317            debug_assert!(next.x.is_finite());
318            debug_assert!(next.y.is_finite());
319            let from = self.current;
320            self.current = next;
321            return if self.is_first {
322                self.is_first = false;
323                self.first = next;
324                Some(PathEvent::Begin { at: next })
325            } else {
326                Some(PathEvent::Line { from, to: next })
327            };
328        }
329
330        self.done = true;
331
332        Some(PathEvent::End {
333            last: self.current,
334            first: self.first,
335            close: self.close,
336        })
337    }
338}
339
340#[test]
341fn test_from_polyline_open() {
342    let points = &[
343        point(1.0, 1.0),
344        point(3.0, 1.0),
345        point(4.0, 5.0),
346        point(5.0, 2.0),
347    ];
348
349    let mut evts = FromPolyline::open(points.iter().cloned());
350
351    assert_eq!(
352        evts.next(),
353        Some(PathEvent::Begin {
354            at: point(1.0, 1.0)
355        })
356    );
357    assert_eq!(
358        evts.next(),
359        Some(PathEvent::Line {
360            from: point(1.0, 1.0),
361            to: point(3.0, 1.0)
362        })
363    );
364    assert_eq!(
365        evts.next(),
366        Some(PathEvent::Line {
367            from: point(3.0, 1.0),
368            to: point(4.0, 5.0)
369        })
370    );
371    assert_eq!(
372        evts.next(),
373        Some(PathEvent::Line {
374            from: point(4.0, 5.0),
375            to: point(5.0, 2.0)
376        })
377    );
378    assert_eq!(
379        evts.next(),
380        Some(PathEvent::End {
381            last: point(5.0, 2.0),
382            first: point(1.0, 1.0),
383            close: false
384        })
385    );
386    assert_eq!(evts.next(), None);
387}
388
389#[test]
390fn test_from_polyline_closed() {
391    let points = &[
392        point(1.0, 1.0),
393        point(3.0, 1.0),
394        point(4.0, 5.0),
395        point(5.0, 2.0),
396    ];
397
398    let mut evts = FromPolyline::closed(points.iter().cloned());
399
400    assert_eq!(
401        evts.next(),
402        Some(PathEvent::Begin {
403            at: point(1.0, 1.0)
404        })
405    );
406    assert_eq!(
407        evts.next(),
408        Some(PathEvent::Line {
409            from: point(1.0, 1.0),
410            to: point(3.0, 1.0)
411        })
412    );
413    assert_eq!(
414        evts.next(),
415        Some(PathEvent::Line {
416            from: point(3.0, 1.0),
417            to: point(4.0, 5.0)
418        })
419    );
420    assert_eq!(
421        evts.next(),
422        Some(PathEvent::Line {
423            from: point(4.0, 5.0),
424            to: point(5.0, 2.0)
425        })
426    );
427    assert_eq!(
428        evts.next(),
429        Some(PathEvent::End {
430            last: point(5.0, 2.0),
431            first: point(1.0, 1.0),
432            close: true
433        })
434    );
435    assert_eq!(evts.next(), None);
436}