use crate::parser::{PathParser, PathSegment};
use crate::path::builder::*;
use crate::path::geom::Arc;
use crate::path::math::{point, vector, Angle, Point, Vector};
use crate::path::ArcFlags;
use std::f32::consts::PI;
#[derive(Clone, Debug, PartialEq)]
pub struct ParseError;
pub fn build_path<Builder>(mut builder: Builder, src: &str) -> Result<Builder::PathType, ParseError>
where
Builder: SvgPathBuilder + Build,
{
for item in PathParser::from(src) {
match item {
Ok(segment) => svg_event(&segment, &mut builder),
Err(_) => return Err(ParseError),
}
}
Ok(builder.build())
}
fn svg_event<Builder>(token: &PathSegment, builder: &mut Builder)
where
Builder: SvgPathBuilder,
{
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 {
PathSegment::MoveTo { abs: true, x, y } => {
builder.move_to(point2(x, y));
}
PathSegment::MoveTo { abs: false, x, y } => {
builder.relative_move_to(vec2(x, y));
}
PathSegment::LineTo { abs: true, x, y } => {
builder.line_to(point2(x, y));
}
PathSegment::LineTo { abs: false, x, y } => {
builder.relative_line_to(vec2(x, y));
}
PathSegment::HorizontalLineTo { abs: true, x } => {
builder.horizontal_line_to(x as f32);
}
PathSegment::HorizontalLineTo { abs: false, x } => {
builder.relative_horizontal_line_to(x as f32);
}
PathSegment::VerticalLineTo { abs: true, y } => {
builder.vertical_line_to(y as f32);
}
PathSegment::VerticalLineTo { abs: false, y } => {
builder.relative_vertical_line_to(y as f32);
}
PathSegment::CurveTo {
abs: true,
x1,
y1,
x2,
y2,
x,
y,
} => {
builder.cubic_bezier_to(point2(x1, y1), point2(x2, y2), point2(x, y));
}
PathSegment::CurveTo {
abs: false,
x1,
y1,
x2,
y2,
x,
y,
} => {
builder.relative_cubic_bezier_to(vec2(x1, y1), vec2(x2, y2), vec2(x, y));
}
PathSegment::SmoothCurveTo {
abs: true,
x2,
y2,
x,
y,
} => {
builder.smooth_cubic_bezier_to(point2(x2, y2), point2(x, y));
}
PathSegment::SmoothCurveTo {
abs: false,
x2,
y2,
x,
y,
} => {
builder.smooth_relative_cubic_bezier_to(vec2(x2, y2), vec2(x, y));
}
PathSegment::Quadratic {
abs: true,
x1,
y1,
x,
y,
} => {
builder.quadratic_bezier_to(point2(x1, y1), point2(x, y));
}
PathSegment::Quadratic {
abs: false,
x1,
y1,
x,
y,
} => {
builder.relative_quadratic_bezier_to(vec2(x1, y1), vec2(x, y));
}
PathSegment::SmoothQuadratic { abs: true, x, y } => {
builder.smooth_quadratic_bezier_to(point2(x, y));
}
PathSegment::SmoothQuadratic { abs: false, x, y } => {
builder.smooth_relative_quadratic_bezier_to(vec2(x, y));
}
PathSegment::EllipticalArc {
abs: true,
rx,
ry,
x_axis_rotation,
large_arc,
sweep,
x,
y,
} => {
builder.arc_to(
vec2(rx, ry),
Angle::degrees(x_axis_rotation as f32),
ArcFlags {
large_arc: large_arc,
sweep: sweep,
},
point2(x, y),
);
}
PathSegment::EllipticalArc {
abs: false,
rx,
ry,
x_axis_rotation,
large_arc,
sweep,
x,
y,
} => {
builder.relative_arc_to(
vec2(rx, ry),
Angle::degrees(x_axis_rotation as f32),
ArcFlags {
large_arc: large_arc,
sweep: sweep,
},
vec2(x, y),
);
}
PathSegment::ClosePath { .. } => {
builder.close();
}
}
}
pub struct PathSerializer {
path: String,
current: Point,
}
impl PathSerializer {
pub fn new() -> Self {
PathSerializer {
path: String::new(),
current: point(0.0, 0.0),
}
}
pub 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 Build for PathSerializer {
type PathType = String;
fn build(self) -> String {
self.path
}
}
impl SvgPathBuilder for PathSerializer {
fn move_to(&mut self, to: Point) {
self.path += &format!("M {} {} ", to.x, to.y);
self.current = to;
}
fn close(&mut self) {
self.path.push_str("Z");
}
fn line_to(&mut self, to: Point) {
self.path += &format!("L {} {} ", to.x, to.y);
self.current = to;
}
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 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
);
}
}
#[test]
fn test_parse_error() {
let invalid_commands = &"This is certainly not an SVG command string";
let svg_builder = lyon_path::Path::builder().with_svg();
assert!(build_path(svg_builder, invalid_commands).is_err());
}