use parser::xmlparser::{StrSpan, FromSpan};
use parser::path::{Tokenizer, Token};
use path::geom::Arc;
use path::math::{Vector, vector, Point, point, Angle};
use path::{SvgEvent, ArcFlags};
use path::builder::*;
use std::f32::consts::PI;
use std::mem;
#[derive(Clone, Debug, PartialEq)]
pub struct ParseError;
pub fn build_path<Builder>(mut builder: Builder, src: &str) -> Result<Builder::PathType, ParseError>
where
Builder: SvgBuilder + Build
{
for item in PathTokenizer::new(src) {
match item {
Ok(event) => { builder.svg_event(event); }
Err(err) => { return Err(err); }
}
}
Ok(builder.build())
}
pub struct PathTokenizer<'l> {
tokenizer: Tokenizer<'l>
}
impl<'l> PathTokenizer<'l> {
pub fn new(text: &str) -> PathTokenizer {
PathTokenizer {
tokenizer: Tokenizer::from_str(text)
}
}
pub fn from_span(span: StrSpan) -> PathTokenizer {
PathTokenizer {
tokenizer: Tokenizer::from_span(span)
}
}
}
impl<'l> Iterator for PathTokenizer<'l> {
type Item = Result<SvgEvent, ParseError>;
fn next(&mut self) -> Option<Result<SvgEvent, ParseError>> {
self.tokenizer.next().map(|token| { Ok(svg_event(&token)) })
}
}
fn svg_event(token: &Token) -> SvgEvent {
fn vec2(x: f64, y: f64) -> Vector { vector(x as f32, y as f32) }
fn point2(x: f64, y: f64) -> Point { point(x as f32, y as f32) }
match *token {
Token::MoveTo { abs, x, y } => {
if abs {
SvgEvent::MoveTo(point2(x, y))
} else {
SvgEvent::RelativeMoveTo(vec2(x, y))
}
},
Token::LineTo { abs, x, y } => {
if abs {
SvgEvent::LineTo(point2(x, y))
} else {
SvgEvent::RelativeLineTo(vec2(x, y))
}
},
Token::HorizontalLineTo { abs, x } => {
if abs {
SvgEvent::HorizontalLineTo(x as f32)
} else {
SvgEvent::RelativeHorizontalLineTo(x as f32)
}
},
Token::VerticalLineTo { abs, y } => {
if abs {
SvgEvent::VerticalLineTo(y as f32)
} else {
SvgEvent::RelativeVerticalLineTo(y as f32)
}
},
Token::CurveTo { abs, x1, y1, x2, y2, x, y } => {
if abs {
SvgEvent::CubicTo(point2(x1, y1), point2(x2, y2), point2(x, y))
} else {
SvgEvent::RelativeCubicTo(vec2(x1, y1), vec2(x2, y2), vec2(x, y))
}
},
Token::SmoothCurveTo { abs, x2, y2, x, y } => {
if abs {
SvgEvent::SmoothCubicTo(point2(x2, y2), point2(x, y))
} else {
SvgEvent::SmoothRelativeCubicTo(vec2(x2, y2), vec2(x, y))
}
},
Token::Quadratic { abs, x1, y1, x, y } => {
if abs {
SvgEvent::QuadraticTo(point2(x1, y1), point2(x, y))
} else {
SvgEvent::RelativeQuadraticTo(vec2(x1, y1), vec2(x, y))
}
},
Token::SmoothQuadratic { abs, x, y } => {
if abs {
SvgEvent::SmoothQuadraticTo(point2(x, y))
} else {
SvgEvent::SmoothRelativeQuadraticTo(vec2(x, y))
}
},
Token::EllipticalArc { abs, rx, ry, x_axis_rotation, large_arc, sweep, x, y } => {
if abs {
SvgEvent::ArcTo(
vec2(rx, ry),
Angle::degrees(x_axis_rotation as f32),
ArcFlags { large_arc: large_arc, sweep: sweep },
point2(x, y),
)
} else {
SvgEvent::RelativeArcTo(
vec2(rx, ry),
Angle::degrees(x_axis_rotation as f32),
ArcFlags { large_arc: large_arc, sweep: sweep },
vec2(x, y),
)
}
},
Token::ClosePath { .. } => { SvgEvent::Close },
}
}
pub struct PathSerializer {
path: String,
current: Point,
}
impl PathSerializer {
pub fn new() -> Self {
PathSerializer {
path: String::new(),
current: point(0.0, 0.0),
}
}
}
impl Build for PathSerializer {
type PathType = String;
fn build(self) -> String { self.path }
fn build_and_reset(&mut self) -> String {
self.current = point(0.0, 0.0);
mem::replace(&mut self.path, String::new())
}
}
impl FlatPathBuilder for PathSerializer {
fn move_to(&mut self, to: Point) {
self.path += &format!("M {} {} ", to.x, to.y);
self.current = to;
}
fn line_to(&mut self, to: Point) {
self.path += &format!("L {} {} ", to.x, to.y);
self.current = to;
}
fn close(&mut self) {
self.path.push_str("Z");
}
fn current_position(&self) -> Point {
self.current
}
}
impl PathBuilder for PathSerializer {
fn quadratic_bezier_to(&mut self, ctrl: Point, to: Point) {
self.path += &format!("Q {} {} {} {}", ctrl.x, ctrl.y, to.x, to.y);
}
fn cubic_bezier_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
self.path += &format!("C {} {} {} {} {} {}", ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, to.x, to.y);
}
fn arc(
&mut self,
center: Point,
radii: Vector,
sweep_angle: Angle,
x_rotation: Angle
) {
let start_angle = (self.current - center).angle_from_x_axis() - x_rotation;
let svg = Arc {
center, radii, start_angle, sweep_angle, x_rotation
}.to_svg_arc();
self.path += &format!(
"A {} {} {} {} {} {} {}",
radii.x, radii.y, svg.x_rotation.get(),
svg.flags.large_arc, svg.flags.sweep,
svg.to.x, svg.to.y
);
}
}
impl SvgBuilder for PathSerializer {
fn relative_move_to(&mut self, to: Vector) {
self.path += &format!("m {} {} ", to.x, to.y);
}
fn relative_line_to(&mut self, to: Vector) {
self.path += &format!("l {} {} ", to.x, to.y);
}
fn relative_quadratic_bezier_to(&mut self, ctrl: Vector, to: Vector) {
self.path += &format!("q {} {} {} {}", ctrl.x, ctrl.y, to.x, to.y);
}
fn relative_cubic_bezier_to(&mut self, ctrl1: Vector, ctrl2: Vector, to: Vector) {
self.path += &format!("c {} {} {} {} {} {}", ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, to.x, to.y);
}
fn smooth_cubic_bezier_to(&mut self, ctrl2: Point, to: Point) {
self.path += &format!("S {} {} {} {}", ctrl2.x, ctrl2.y, to.x, to.y);
}
fn smooth_relative_cubic_bezier_to(&mut self, ctrl2: Vector, to: Vector) {
self.path += &format!("s {} {} {} {}", ctrl2.x, ctrl2.y, to.x, to.y);
}
fn smooth_quadratic_bezier_to(&mut self, to: Point) {
self.path += &format!("T {} {} ", to.x, to.y);
}
fn smooth_relative_quadratic_bezier_to(&mut self, to: Vector) {
self.path += &format!("t {} {} ", to.x, to.y);
}
fn horizontal_line_to(&mut self, x: f32) {
self.path += &format!("H {} ", x);
}
fn relative_horizontal_line_to(&mut self, dx: f32) {
self.path += &format!("h {} ", dx);
}
fn vertical_line_to(&mut self, y: f32) {
self.path += &format!("V {} ", y);
}
fn relative_vertical_line_to(&mut self, dy: f32) {
self.path += &format!("v {} ", dy);
}
fn arc_to(
&mut self,
radii: Vector,
x_rotation: Angle,
flags: ArcFlags,
to: Point
) {
self.path += &format!(
"A {} {} {} {} {} {} {} ",
radii.x, radii.y, x_rotation.get() * 180.0 / PI,
if flags.large_arc { 1u32 } else { 0 },
if flags.sweep { 1u32 } else { 0 },
to.x, to.y
);
}
fn relative_arc_to(
&mut self,
radii: Vector,
x_rotation: Angle,
flags: ArcFlags,
to: Vector,
) {
self.path += &format!(
"a {} {} {} {} {} {} {} ",
radii.x, radii.y, x_rotation.get() * 180.0 / PI,
if flags.large_arc { 1u32 } else { 0 },
if flags.sweep { 1u32 } else { 0 },
to.x, to.y
);
}
}
impl PolygonBuilder for PathSerializer {
fn polygon(&mut self, points: &[Point]) {
build_polygon(self, points);
}
}