use path_builder::{ BaseBuilder, PathBuilder, SvgPathBuilder, FlatteningBuilder };
use path_iterator::PathStateIter;
use core::PathEvent;
use core::math::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Verb {
MoveTo,
LineTo,
QuadraticTo,
CubicTo,
Close,
}
#[derive(Clone, Debug)]
pub struct Path {
points: Vec<Point>,
verbs: Vec<Verb>,
}
#[derive(Copy, Clone, Debug)]
pub struct PathSlice<'l> {
points: &'l[Point],
verbs: &'l[Verb],
}
impl Path {
pub fn builder() -> Builder { Builder::new() }
pub fn new() -> Path {
Path {
points: Vec::new(),
verbs: Vec::new(),
}
}
pub fn with_capacity(cap: usize) -> Path {
Path {
points: Vec::with_capacity(cap),
verbs: Vec::with_capacity(cap),
}
}
pub fn as_slice(&self) -> PathSlice {
PathSlice { points: &self.points[..], verbs: &self.verbs[..] }
}
pub fn iter(&self) -> PathIter { PathIter::new(&self.points[..], &self.verbs[..]) }
pub fn path_iter(&self) -> PathStateIter<PathIter> { PathStateIter::new(self.iter()) }
pub fn points(&self) -> &[Point] { &self.points[..] }
pub fn mut_points(&mut self) -> &mut[Point] { &mut self.points[..] }
pub fn verbs(&self) -> &[Verb] { &self.verbs[..] }
}
impl<'l> PathSlice<'l> {
pub fn new(points: &'l[Point], verbs: &'l[Verb]) -> PathSlice<'l> {
PathSlice { points: points, verbs: verbs }
}
pub fn iter(&self) -> PathIter { PathIter::new(self.points, self.verbs) }
pub fn path_iter(&self) -> PathStateIter<PathIter> { PathStateIter::new(self.iter()) }
pub fn points(&self) -> &[Point] { self.points }
pub fn verbs(&self) -> &[Verb] { self.verbs }
}
pub struct Builder {
path: Path,
current_position: Point,
first_position: Point,
building: bool,
}
impl Builder {
pub fn new() -> Self { Builder::with_capacity(128) }
pub fn with_capacity(cap: usize) -> Self {
Builder {
path: Path::with_capacity(cap),
current_position: Point::new(0.0, 0.0),
first_position: Point::new(0.0, 0.0),
building: false,
}
}
pub fn with_svg(self) -> SvgPathBuilder<Self> { SvgPathBuilder::new(self) }
pub fn flattened(self, tolerance: f32) -> FlatteningBuilder<Self> {
FlatteningBuilder::new(self, tolerance)
}
}
#[inline]
fn nan_check(p: Point) {
debug_assert!(!p.x.is_nan());
debug_assert!(!p.y.is_nan());
}
impl BaseBuilder for Builder {
type PathType = Path;
fn move_to(&mut self, to: Point)
{
nan_check(to);
self.first_position = to;
self.current_position = to;
self.building = true;
self.path.points.push(to);
self.path.verbs.push(Verb::MoveTo);
}
fn line_to(&mut self, to: Point) {
nan_check(to);
self.path.points.push(to);
self.path.verbs.push(Verb::LineTo);
self.current_position = to;
}
fn close(&mut self) {
self.path.verbs.push(Verb::Close);
self.current_position = self.first_position;
self.building = false;
}
fn current_position(&self) -> Point { self.current_position }
fn build(self) -> Path { self.path }
fn build_and_reset(&mut self) -> Path {
self.current_position = Point::new(0.0, 0.0);
self.first_position = Point::new(0.0, 0.0);
self.building = false;
let mut tmp = Path::with_capacity(self.path.verbs.len());
::std::mem::swap(&mut self.path, &mut tmp);
return tmp;
}
}
impl PathBuilder for Builder {
fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
nan_check(ctrl);
nan_check(to);
self.path.points.push(ctrl);
self.path.points.push(to);
self.path.verbs.push(Verb::QuadraticTo);
self.current_position = to;
}
fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
nan_check(ctrl1);
nan_check(ctrl2);
nan_check(to);
self.path.points.push(ctrl1);
self.path.points.push(ctrl2);
self.path.points.push(to);
self.path.verbs.push(Verb::CubicTo);
self.current_position = to;
}
}
#[derive(Clone, Debug)]
pub struct PathIter<'l> {
points: ::std::slice::Iter<'l, Point>,
verbs: ::std::slice::Iter<'l, Verb>,
}
impl<'l> PathIter<'l> {
pub fn new(points: &'l[Point], verbs: &'l[Verb]) -> Self {
PathIter {
points: points.iter(),
verbs: verbs.iter(),
}
}
}
impl<'l> Iterator for PathIter<'l> {
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
return match self.verbs.next() {
Some(&Verb::MoveTo) => {
let to = *self.points.next().unwrap();
Some(PathEvent::MoveTo(to))
}
Some(&Verb::LineTo) => {
let to = *self.points.next().unwrap();
Some(PathEvent::LineTo(to))
}
Some(&Verb::QuadraticTo) => {
let ctrl = *self.points.next().unwrap();
let to = *self.points.next().unwrap();
Some(PathEvent::QuadraticTo(ctrl, to))
}
Some(&Verb::CubicTo) => {
let ctrl1 = *self.points.next().unwrap();
let ctrl2 = *self.points.next().unwrap();
let to = *self.points.next().unwrap();
Some(PathEvent::CubicTo(ctrl1, ctrl2, to))
}
Some(&Verb::Close) => {
Some(PathEvent::Close)
}
None => { None }
};
}
}
#[test]
fn test_path_builder_1() {
let mut p = Builder::with_capacity(0);
p.line_to(point(1.0, 0.0));
p.line_to(point(2.0, 0.0));
p.line_to(point(3.0, 0.0));
p.quadratic_bezier_to(point(4.0, 0.0), point(4.0, 1.0));
p.cubic_bezier_to(point(5.0, 0.0), point(5.0, 1.0), point(5.0, 2.0));
p.close();
p.move_to(point(10.0, 0.0));
p.line_to(point(11.0, 0.0));
p.line_to(point(12.0, 0.0));
p.line_to(point(13.0, 0.0));
p.quadratic_bezier_to(point(14.0, 0.0), point(14.0, 1.0));
p.cubic_bezier_to(point(15.0, 0.0), point(15.0, 1.0), point(15.0, 2.0));
p.close();
p.close();
p.move_to(point(1.0, 1.0));
p.move_to(point(2.0, 2.0));
p.move_to(point(3.0, 3.0));
p.line_to(point(4.0, 4.0));
let path = p.build();
let mut it = path.iter();
assert_eq!(it.next(), Some(PathEvent::LineTo(point(1.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(2.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(3.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::QuadraticTo(point(4.0, 0.0), point(4.0, 1.0))));
assert_eq!(it.next(), Some(PathEvent::CubicTo(point(5.0, 0.0), point(5.0, 1.0), point(5.0, 2.0))));
assert_eq!(it.next(), Some(PathEvent::Close));
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(10.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(11.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(12.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(13.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::QuadraticTo(point(14.0, 0.0), point(14.0, 1.0))));
assert_eq!(it.next(), Some(PathEvent::CubicTo(point(15.0, 0.0), point(15.0, 1.0), point(15.0, 2.0))));
assert_eq!(it.next(), Some(PathEvent::Close));
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(3.0, 3.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(4.0, 4.0))));
assert_eq!(it.next(), None);
assert_eq!(it.next(), None);
assert_eq!(it.next(), None);
}
#[test]
fn test_path_builder_empty() {
let path = Path::builder().build();
let mut it = path.iter();
assert_eq!(it.next(), None);
assert_eq!(it.next(), None);
}
#[test]
fn test_path_builder_empty_move_to() {
let mut p = Path::builder();
p.move_to(point(1.0, 2.0));
p.move_to(point(3.0, 4.0));
p.move_to(point(5.0, 6.0));
let path = p.build();
let mut it = path.iter();
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(5.0, 6.0))));
assert_eq!(it.next(), None);
assert_eq!(it.next(), None);
}
#[test]
fn test_path_builder_move_to_after_close() {
let mut p = Path::builder();
p.line_to(point(1.0, 0.0));
p.close();
p.line_to(point(2.0, 0.0));
let path = p.build();
let mut it = path.iter();
assert_eq!(it.next(), Some(PathEvent::LineTo(point(1.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::Close));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(2.0, 0.0))));
assert_eq!(it.next(), None);
}
pub type FlattenedPathBuilder = SvgPathBuilder<FlatteningBuilder<Builder>>;
pub fn flattened_path_builder(tolerance: f32) -> FlattenedPathBuilder {
SvgPathBuilder::new(FlatteningBuilder::new(Path::builder(), tolerance))
}