use super::FuzzyEq;
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum PathCommand {
MoveTo,
LineTo,
HorizontalLineTo,
VerticalLineTo,
CurveTo,
SmoothCurveTo,
Quadratic,
SmoothQuadratic,
EllipticalArc,
ClosePath,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PathSegment {
MoveTo {
abs: bool,
x: f64,
y: f64,
},
LineTo {
abs: bool,
x: f64,
y: f64,
},
HorizontalLineTo {
abs: bool,
x: f64,
},
VerticalLineTo {
abs: bool,
y: f64,
},
CurveTo {
abs: bool,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
x: f64,
y: f64,
},
SmoothCurveTo {
abs: bool,
x2: f64,
y2: f64,
x: f64,
y: f64,
},
Quadratic {
abs: bool,
x1: f64,
y1: f64,
x: f64,
y: f64,
},
SmoothQuadratic {
abs: bool,
x: f64,
y: f64,
},
EllipticalArc {
abs: bool,
rx: f64,
ry: f64,
x_axis_rotation: f64,
large_arc: bool,
sweep: bool,
x: f64,
y: f64,
},
ClosePath {
abs: bool,
},
}
impl PathSegment {
pub fn set_absolute(&mut self, new_abs: bool) {
match *self {
PathSegment::MoveTo { ref mut abs, .. }
| PathSegment::LineTo { ref mut abs, .. }
| PathSegment::HorizontalLineTo { ref mut abs, .. }
| PathSegment::VerticalLineTo { ref mut abs, .. }
| PathSegment::CurveTo { ref mut abs, .. }
| PathSegment::SmoothCurveTo { ref mut abs, .. }
| PathSegment::Quadratic { ref mut abs, .. }
| PathSegment::SmoothQuadratic { ref mut abs, .. }
| PathSegment::EllipticalArc { ref mut abs, .. }
| PathSegment::ClosePath { ref mut abs, .. } => {
*abs = new_abs;
}
}
}
pub fn cmd(&self) -> PathCommand {
match *self {
PathSegment::MoveTo { .. } => PathCommand::MoveTo,
PathSegment::LineTo { .. } => PathCommand::LineTo,
PathSegment::HorizontalLineTo { .. } => PathCommand::HorizontalLineTo,
PathSegment::VerticalLineTo { .. } => PathCommand::VerticalLineTo,
PathSegment::CurveTo { .. } => PathCommand::CurveTo,
PathSegment::SmoothCurveTo { .. } => PathCommand::SmoothCurveTo,
PathSegment::Quadratic { .. } => PathCommand::Quadratic,
PathSegment::SmoothQuadratic { .. } => PathCommand::SmoothQuadratic,
PathSegment::EllipticalArc { .. } => PathCommand::EllipticalArc,
PathSegment::ClosePath { .. } => PathCommand::ClosePath,
}
}
#[inline]
pub fn is_absolute(&self) -> bool {
match *self {
PathSegment::MoveTo { abs, .. }
| PathSegment::LineTo { abs, .. }
| PathSegment::HorizontalLineTo { abs, .. }
| PathSegment::VerticalLineTo { abs, .. }
| PathSegment::CurveTo { abs, .. }
| PathSegment::SmoothCurveTo { abs, .. }
| PathSegment::Quadratic { abs, .. }
| PathSegment::SmoothQuadratic { abs, .. }
| PathSegment::EllipticalArc { abs, .. }
| PathSegment::ClosePath { abs, .. } => abs,
}
}
#[inline]
pub fn is_relative(&self) -> bool {
!self.is_absolute()
}
pub fn x(&self) -> Option<f64> {
match *self {
PathSegment::MoveTo { x, .. }
| PathSegment::LineTo { x, .. }
| PathSegment::HorizontalLineTo { x, .. }
| PathSegment::CurveTo { x, .. }
| PathSegment::SmoothCurveTo { x, .. }
| PathSegment::Quadratic { x, .. }
| PathSegment::SmoothQuadratic { x, .. }
| PathSegment::EllipticalArc { x, .. } => Some(x),
PathSegment::VerticalLineTo { .. } | PathSegment::ClosePath { .. } => None,
}
}
pub fn y(&self) -> Option<f64> {
match *self {
PathSegment::MoveTo { y, .. }
| PathSegment::LineTo { y, .. }
| PathSegment::VerticalLineTo { y, .. }
| PathSegment::CurveTo { y, .. }
| PathSegment::SmoothCurveTo { y, .. }
| PathSegment::Quadratic { y, .. }
| PathSegment::SmoothQuadratic { y, .. }
| PathSegment::EllipticalArc { y, .. } => Some(y),
PathSegment::HorizontalLineTo { .. } | PathSegment::ClosePath { .. } => None,
}
}
}
impl FuzzyEq for PathSegment {
fn fuzzy_eq(&self, other: &Self) -> bool {
use self::PathSegment as Seg;
match (*self, *other) {
(
Seg::MoveTo { abs, x, y },
Seg::MoveTo {
abs: oabs,
x: ox,
y: oy,
},
)
| (
Seg::LineTo { abs, x, y },
Seg::LineTo {
abs: oabs,
x: ox,
y: oy,
},
)
| (
Seg::SmoothQuadratic { abs, x, y },
Seg::SmoothQuadratic {
abs: oabs,
x: ox,
y: oy,
},
) => abs == oabs && x.fuzzy_eq(&ox) && y.fuzzy_eq(&oy),
(Seg::HorizontalLineTo { abs, x }, Seg::HorizontalLineTo { abs: oabs, x: ox }) => {
abs == oabs && x.fuzzy_eq(&ox)
}
(Seg::VerticalLineTo { abs, y }, Seg::VerticalLineTo { abs: oabs, y: oy }) => {
abs == oabs && y.fuzzy_eq(&oy)
}
(
Seg::CurveTo {
abs,
x1,
y1,
x2,
y2,
x,
y,
},
Seg::CurveTo {
abs: oabs,
x1: ox1,
y1: oy1,
x2: ox2,
y2: oy2,
x: ox,
y: oy,
},
) => {
abs == oabs
&& x.fuzzy_eq(&ox)
&& y.fuzzy_eq(&oy)
&& x1.fuzzy_eq(&ox1)
&& y1.fuzzy_eq(&oy1)
&& x2.fuzzy_eq(&ox2)
&& y2.fuzzy_eq(&oy2)
}
(
Seg::SmoothCurveTo { abs, x2, y2, x, y },
Seg::SmoothCurveTo {
abs: oabs,
x2: ox2,
y2: oy2,
x: ox,
y: oy,
},
) => {
abs == oabs
&& x.fuzzy_eq(&ox)
&& y.fuzzy_eq(&oy)
&& x2.fuzzy_eq(&ox2)
&& y2.fuzzy_eq(&oy2)
}
(
Seg::Quadratic { abs, x1, y1, x, y },
Seg::Quadratic {
abs: oabs,
x1: ox1,
y1: oy1,
x: ox,
y: oy,
},
) => {
abs == oabs
&& x.fuzzy_eq(&ox)
&& y.fuzzy_eq(&oy)
&& x1.fuzzy_eq(&ox1)
&& y1.fuzzy_eq(&oy1)
}
(
Seg::EllipticalArc {
abs,
rx,
ry,
x_axis_rotation,
large_arc,
sweep,
x,
y,
},
Seg::EllipticalArc {
abs: oabs,
rx: orx,
ry: ory,
x_axis_rotation: ox_axis_rotation,
large_arc: olarge_arc,
sweep: osweep,
x: ox,
y: oy,
},
) => {
abs == oabs
&& x.fuzzy_eq(&ox)
&& y.fuzzy_eq(&oy)
&& rx.fuzzy_eq(&orx)
&& ry.fuzzy_eq(&ory)
&& x_axis_rotation.fuzzy_eq(&ox_axis_rotation)
&& large_arc == olarge_arc
&& sweep == osweep
}
(Seg::ClosePath { abs }, Seg::ClosePath { abs: oabs }) => abs == oabs,
_ => false,
}
}
}
#[cfg(test)]
mod fuzzy_eq_tests {
use super::*;
macro_rules! test {
($name:ident, $seg1:expr, $seg2:expr) => {
#[test]
fn $name() {
assert!($seg1 != $seg2);
assert!($seg1.fuzzy_eq(&$seg2));
}
};
}
test!(
m,
PathSegment::MoveTo {
abs: true,
x: 10.0,
y: 10.1 + 10.2
},
PathSegment::MoveTo {
abs: true,
x: 10.0,
y: 20.3
}
);
test!(
l,
PathSegment::LineTo {
abs: true,
x: 10.0,
y: 10.1 + 10.2
},
PathSegment::LineTo {
abs: true,
x: 10.0,
y: 20.3
}
);
test!(
h,
PathSegment::HorizontalLineTo {
abs: true,
x: 10.1 + 10.2
},
PathSegment::HorizontalLineTo { abs: true, x: 20.3 }
);
test!(
v,
PathSegment::VerticalLineTo {
abs: true,
y: 10.1 + 10.2
},
PathSegment::VerticalLineTo { abs: true, y: 20.3 }
);
test!(
c,
PathSegment::CurveTo {
abs: true,
x1: 10.0,
y1: 10.1 + 10.2,
x2: 10.0,
y2: 10.0,
x: 10.0,
y: 10.0
},
PathSegment::CurveTo {
abs: true,
x1: 10.0,
y1: 20.3,
x2: 10.0,
y2: 10.0,
x: 10.0,
y: 10.0
}
);
test!(
s,
PathSegment::SmoothCurveTo {
abs: true,
x2: 10.0,
y2: 10.1 + 10.2,
x: 10.0,
y: 10.0
},
PathSegment::SmoothCurveTo {
abs: true,
x2: 10.0,
y2: 20.3,
x: 10.0,
y: 10.0
}
);
test!(
q,
PathSegment::Quadratic {
abs: true,
x1: 10.0,
y1: 10.1 + 10.2,
x: 10.0,
y: 10.0
},
PathSegment::Quadratic {
abs: true,
x1: 10.0,
y1: 20.3,
x: 10.0,
y: 10.0
}
);
test!(
t,
PathSegment::SmoothQuadratic {
abs: true,
x: 10.0,
y: 10.1 + 10.2
},
PathSegment::SmoothQuadratic {
abs: true,
x: 10.0,
y: 20.3
}
);
test!(
a,
PathSegment::EllipticalArc {
abs: true,
rx: 100.0,
ry: 100.0,
x_axis_rotation: 0.0,
large_arc: true,
sweep: true,
x: 10.1 + 10.2,
y: 10.0,
},
PathSegment::EllipticalArc {
abs: true,
rx: 100.0,
ry: 100.0,
x_axis_rotation: 0.0,
large_arc: true,
sweep: true,
x: 20.3,
y: 10.0,
}
);
}