use crate::geom::traits::Transformation;
use crate::geom::{cubic_bezier, quadratic_bezier, CubicBezierSegment, QuadraticBezierSegment};
use crate::math::*;
use crate::{Attributes, Event, PathEvent};
pub trait PathIterator: Iterator<Item = PathEvent> + Sized {
fn flattened(self, tolerance: f32) -> Flattened<Self> {
Flattened::new(tolerance, self)
}
fn transformed<T: Transformation<f32>>(self, mat: &T) -> Transformed<Self, T> {
Transformed::new(mat, self)
}
}
impl<Iter> PathIterator for Iter where Iter: Iterator<Item = PathEvent> {}
pub struct NoAttributes<Iter>(pub(crate) Iter);
impl<'l, Iter> NoAttributes<Iter>
where
Iter: Iterator<Item = Event<(Point, Attributes<'l>), Point>>,
{
pub fn with_attributes(self) -> Iter {
self.0
}
}
impl<'l, Iter> Iterator for NoAttributes<Iter>
where
Iter: Iterator<Item = Event<(Point, Attributes<'l>), Point>>,
{
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
self.0.next().map(|event| event.with_points())
}
}
pub struct Flattened<Iter> {
it: Iter,
current_position: Point,
current_curve: TmpFlatteningIter,
tolerance: f32,
}
enum TmpFlatteningIter {
Quadratic(quadratic_bezier::Flattened<f32>),
Cubic(cubic_bezier::Flattened<f32>),
None,
}
impl<Iter: Iterator<Item = PathEvent>> Flattened<Iter> {
pub fn new(tolerance: f32, it: Iter) -> Self {
Flattened {
it,
current_position: point(0.0, 0.0),
current_curve: TmpFlatteningIter::None,
tolerance,
}
}
}
impl<Iter> Iterator for Flattened<Iter>
where
Iter: Iterator<Item = PathEvent>,
{
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
match self.current_curve {
TmpFlatteningIter::Quadratic(ref mut it) => {
if let Some(to) = it.next() {
let from = self.current_position;
self.current_position = to;
return Some(PathEvent::Line { from, to });
}
}
TmpFlatteningIter::Cubic(ref mut it) => {
if let Some(to) = it.next() {
let from = self.current_position;
self.current_position = to;
return Some(PathEvent::Line { from, to });
}
}
_ => {}
}
self.current_curve = TmpFlatteningIter::None;
match self.it.next() {
Some(PathEvent::Begin { at }) => Some(PathEvent::Begin { at }),
Some(PathEvent::Line { from, to }) => Some(PathEvent::Line { from, to }),
Some(PathEvent::End { last, first, close }) => {
Some(PathEvent::End { last, first, close })
}
Some(PathEvent::Quadratic { from, ctrl, to }) => {
self.current_position = from;
self.current_curve = TmpFlatteningIter::Quadratic(
QuadraticBezierSegment { from, ctrl, to }.flattened(self.tolerance),
);
self.next()
}
Some(PathEvent::Cubic {
from,
ctrl1,
ctrl2,
to,
}) => {
self.current_position = from;
self.current_curve = TmpFlatteningIter::Cubic(
CubicBezierSegment {
from,
ctrl1,
ctrl2,
to,
}
.flattened(self.tolerance),
);
self.next()
}
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let mut lo = self.it.size_hint().0;
match &self.current_curve {
TmpFlatteningIter::Quadratic(t) => {
lo += t.size_hint().0;
}
TmpFlatteningIter::Cubic(t) => {
lo += t.size_hint().0;
}
_ => {}
}
(lo, None)
}
}
pub struct Transformed<'l, I, T> {
it: I,
transform: &'l T,
}
impl<'l, I, T: Transformation<f32>> Transformed<'l, I, T>
where
I: Iterator<Item = PathEvent>,
{
#[inline]
pub fn new(transform: &'l T, it: I) -> Transformed<'l, I, T> {
Transformed { it, transform }
}
}
impl<'l, I, T> Iterator for Transformed<'l, I, T>
where
I: Iterator<Item = PathEvent>,
T: Transformation<f32>,
{
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
match self.it.next() {
None => None,
Some(ref evt) => Some(evt.transformed(self.transform)),
}
}
}
pub struct FromPolyline<Iter> {
iter: Iter,
current: Point,
first: Point,
is_first: bool,
done: bool,
close: bool,
}
impl<Iter: Iterator<Item = Point>> FromPolyline<Iter> {
pub fn new(close: bool, iter: Iter) -> Self {
FromPolyline {
iter,
current: point(0.0, 0.0),
first: point(0.0, 0.0),
is_first: true,
done: false,
close,
}
}
pub fn closed(iter: Iter) -> Self {
FromPolyline::new(true, iter)
}
pub fn open(iter: Iter) -> Self {
FromPolyline::new(false, iter)
}
}
impl<Iter> Iterator for FromPolyline<Iter>
where
Iter: Iterator<Item = Point>,
{
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
if self.done {
return None;
}
if let Some(next) = self.iter.next() {
debug_assert!(next.x.is_finite());
debug_assert!(next.y.is_finite());
let from = self.current;
self.current = next;
return if self.is_first {
self.is_first = false;
self.first = next;
Some(PathEvent::Begin { at: next })
} else {
Some(PathEvent::Line { from, to: next })
};
}
self.done = true;
Some(PathEvent::End {
last: self.current,
first: self.first,
close: self.close,
})
}
}
#[test]
fn test_from_polyline_open() {
let points = &[
point(1.0, 1.0),
point(3.0, 1.0),
point(4.0, 5.0),
point(5.0, 2.0),
];
let mut evts = FromPolyline::open(points.iter().cloned());
assert_eq!(
evts.next(),
Some(PathEvent::Begin {
at: point(1.0, 1.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::Line {
from: point(1.0, 1.0),
to: point(3.0, 1.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::Line {
from: point(3.0, 1.0),
to: point(4.0, 5.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::Line {
from: point(4.0, 5.0),
to: point(5.0, 2.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::End {
last: point(5.0, 2.0),
first: point(1.0, 1.0),
close: false
})
);
assert_eq!(evts.next(), None);
}
#[test]
fn test_from_polyline_closed() {
let points = &[
point(1.0, 1.0),
point(3.0, 1.0),
point(4.0, 5.0),
point(5.0, 2.0),
];
let mut evts = FromPolyline::closed(points.iter().cloned());
assert_eq!(
evts.next(),
Some(PathEvent::Begin {
at: point(1.0, 1.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::Line {
from: point(1.0, 1.0),
to: point(3.0, 1.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::Line {
from: point(3.0, 1.0),
to: point(4.0, 5.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::Line {
from: point(4.0, 5.0),
to: point(5.0, 2.0)
})
);
assert_eq!(
evts.next(),
Some(PathEvent::End {
last: point(5.0, 2.0),
first: point(1.0, 1.0),
close: true
})
);
assert_eq!(evts.next(), None);
}