use builder::{FlatPathBuilder, PathBuilder, SvgPathBuilder, FlatteningBuilder};
use iterator::PathIter;
use PathEvent;
use math::*;
use std::iter::IntoIterator;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum Verb {
MoveTo,
LineTo,
QuadraticTo,
CubicTo,
Arc,
Close,
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
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) -> Iter { Iter::new(&self.points[..], &self.verbs[..]) }
pub fn path_iter(&self) -> PathIter<Iter> { PathIter::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[..] }
pub fn merge(mut self, other: Self) -> Self {
if other.verbs.is_empty() {
return self;
}
if other.verbs[0] != Verb::MoveTo {
self.verbs.push(Verb::MoveTo);
self.points.push(point(0.0, 0.0));
}
self.verbs.extend(other.verbs);
self.points.extend(other.points);
self
}
}
impl<'l> IntoIterator for &'l Path {
type Item = PathEvent;
type IntoIter = Iter<'l>;
fn into_iter(self) -> Iter<'l> { self.iter() }
}
impl<'l> PathSlice<'l> {
pub fn new(points: &'l [Point], verbs: &'l [Verb]) -> PathSlice<'l> {
PathSlice {
points,
verbs,
}
}
pub fn iter(&self) -> Iter { Iter::new(self.points, self.verbs) }
pub fn path_iter(&self) -> PathIter<Iter> { PathIter::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 FlatPathBuilder 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);
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;
}
fn arc(
&mut self,
center: Point,
radii: Vector,
sweep_angle: Angle,
x_rotation: Angle
) {
nan_check(center);
nan_check(radii.to_point());
debug_assert!(!sweep_angle.get().is_nan());
debug_assert!(!x_rotation.get().is_nan());
self.path.points.push(center);
self.path.points.push(radii.to_point());
self.path.points.push(point(
sweep_angle.get(),
x_rotation.get(),
));
self.path.verbs.push(Verb::Arc);
}
}
#[derive(Clone, Debug)]
pub struct Iter<'l> {
points: ::std::slice::Iter<'l, Point>,
verbs: ::std::slice::Iter<'l, Verb>,
}
impl<'l> Iter<'l> {
pub fn new(points: &'l [Point], verbs: &'l [Verb]) -> Self {
Iter {
points: points.iter(),
verbs: verbs.iter(),
}
}
}
impl<'l> Iterator for Iter<'l> {
type Item = PathEvent;
fn next(&mut self) -> Option<PathEvent> {
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::Arc) => {
let center = *self.points.next().unwrap();
let radii = self.points.next().unwrap().to_vector();
let sweep_angle_x_rot = *self.points.next().unwrap();
Some(PathEvent::Arc(
center,
radii,
Angle::radians(sweep_angle_x_rot.x),
Angle::radians(sweep_angle_x_rot.y),
))
}
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::Close));
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(1.0, 1.0))));
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(2.0, 2.0))));
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(1.0, 2.0))));
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(3.0, 4.0))));
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))
}
#[test]
fn test_merge_paths() {
let mut builder = Path::builder();
builder.move_to(point(0.0, 0.0));
builder.line_to(point(5.0, 0.0));
builder.line_to(point(5.0, 5.0));
builder.close();
let path1 = builder.build();
let mut builder = Path::builder();
builder.move_to(point(1.0, 1.0));
builder.line_to(point(4.0, 0.0));
builder.line_to(point(4.0, 4.0));
builder.close();
let path2 = builder.build();
let path = path1.merge(path2);
let mut it = path.iter();
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(0.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(5.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(5.0, 5.0))));
assert_eq!(it.next(), Some(PathEvent::Close));
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(1.0, 1.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(4.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(4.0, 4.0))));
assert_eq!(it.next(), Some(PathEvent::Close));
assert_eq!(it.next(), None);
}
#[test]
fn test_merge_missing_moveto() {
let mut builder = Path::builder();
builder.move_to(point(0.0, 0.0));
builder.line_to(point(5.0, 0.0));
builder.line_to(point(5.0, 5.0));
let path1 = builder.build();
let mut builder = Path::builder();
builder.line_to(point(4.0, 0.0));
builder.line_to(point(4.0, 4.0));
let path2 = builder.build();
let path = path1.merge(path2);
let mut it = path.iter();
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(0.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(5.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(5.0, 5.0))));
assert_eq!(it.next(), Some(PathEvent::MoveTo(point(0.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(4.0, 0.0))));
assert_eq!(it.next(), Some(PathEvent::LineTo(point(4.0, 4.0))));
assert_eq!(it.next(), None);
}