use crate::geom::traits::Transformation;
use crate::geom::{
cubic_bezier, quadratic_bezier, BezierSegment, CubicBezierSegment, LineSegment,
QuadraticBezierSegment,
};
use crate::math::*;
use crate::PathEvent;
pub trait PathIterator: Iterator<Item = PathEvent> + Sized {
fn flattened(self, tolerance: f32) -> Flattened<Self> {
Flattened::new(tolerance, self)
}
fn transformed<'l, T: Transformation<f32>>(self, mat: &'l T) -> Transformed<'l, Self, T> {
Transformed::new(mat, self)
}
fn bezier_segments(self) -> BezierSegments<Self> {
BezierSegments { iter: self }
}
}
impl<Iter> PathIterator for Iter where Iter: Iterator<Item = PathEvent> {}
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,
}
}
}
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,
})
}
}
pub struct BezierSegments<Iter> {
iter: Iter,
}
impl<Iter> Iterator for BezierSegments<Iter>
where
Iter: Iterator<Item = PathEvent>,
{
type Item = BezierSegment<f32>;
fn next(&mut self) -> Option<BezierSegment<f32>> {
match self.iter.next() {
Some(PathEvent::Line { from, to }) => {
Some(BezierSegment::Linear(LineSegment { from, to }))
}
Some(PathEvent::End {
last,
first,
close: true,
}) => Some(BezierSegment::Linear(LineSegment {
from: last,
to: first,
})),
Some(PathEvent::End { close: false, .. }) => self.next(),
Some(PathEvent::Quadratic { from, ctrl, to }) => {
Some(BezierSegment::Quadratic(QuadraticBezierSegment {
from,
ctrl,
to,
}))
}
Some(PathEvent::Cubic {
from,
ctrl1,
ctrl2,
to,
}) => Some(BezierSegment::Cubic(CubicBezierSegment {
from,
ctrl1,
ctrl2,
to,
})),
Some(PathEvent::Begin { .. }) => self.next(),
None => None,
}
}
}
#[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);
}