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(transform: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<(Point2D, …), …>| 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(x:0.0, y: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: &Event, …>) => 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(x:0.0, y:0.0),
289 first: point(x:0.0, y:0.0),
290 is_first: true,
291 done: false,
292 close,
293 }
294 }
295
296 pub fn closed(iter: Iter) -> Self {
297 FromPolyline::new(close:true, iter)
298 }
299
300 pub fn open(iter: Iter) -> Self {
301 FromPolyline::new(close: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}
437