openfree's picture
Deploy from GitHub repository
2409829 verified
pub(crate) mod intersection_path_segment;
pub(crate) mod line_segment;
pub(crate) mod line_segment_aabb;
pub(crate) mod path_cubic_segment_self_intersection;
pub(crate) mod path_segment;
use glam::DVec2;
#[cfg(feature = "parsing")]
use crate::path_command::{AbsolutePathCommand, PathCommand, to_absolute_commands};
use crate::path_segment::PathSegment;
pub type Path = Vec<PathSegment>;
fn reflect_control_point(point: DVec2, control_point: DVec2) -> DVec2 {
point * 2. - control_point
}
#[cfg(feature = "parsing")]
pub fn path_from_commands<I>(commands: I) -> impl Iterator<Item = PathSegment>
where
I: IntoIterator<Item = PathCommand>,
{
let mut first_point: Option<DVec2> = None;
let mut last_point: Option<DVec2> = None;
let mut last_control_point: Option<DVec2> = None;
to_absolute_commands(commands).filter_map(move |cmd| match cmd {
AbsolutePathCommand::M(point) => {
last_point = Some(point);
first_point = Some(point);
last_control_point = None;
None
}
AbsolutePathCommand::L(point) => {
let start = last_point.unwrap();
last_point = Some(point);
last_control_point = None;
Some(PathSegment::Line(start, point))
}
AbsolutePathCommand::H(x) => {
let start = last_point.unwrap();
let point = DVec2::new(x, start.y);
last_point = Some(point);
last_control_point = None;
Some(PathSegment::Line(start, point))
}
AbsolutePathCommand::V(y) => {
let start = last_point.unwrap();
let point = DVec2::new(start.x, y);
last_point = Some(point);
last_control_point = None;
Some(PathSegment::Line(start, point))
}
AbsolutePathCommand::C(c1, c2, end) => {
let start = last_point.unwrap();
last_point = Some(end);
last_control_point = Some(c2);
Some(PathSegment::Cubic(start, c1, c2, end))
}
AbsolutePathCommand::S(c2, end) => {
let start = last_point.unwrap();
let c1 = reflect_control_point(start, last_control_point.unwrap_or(start));
last_point = Some(end);
last_control_point = Some(c2);
Some(PathSegment::Cubic(start, c1, c2, end))
}
AbsolutePathCommand::Q(c, end) => {
let start = last_point.unwrap();
last_point = Some(end);
last_control_point = Some(c);
Some(PathSegment::Quadratic(start, c, end))
}
AbsolutePathCommand::T(end) => {
let start = last_point.unwrap();
let c = reflect_control_point(start, last_control_point.unwrap_or(start));
last_point = Some(end);
last_control_point = Some(c);
Some(PathSegment::Quadratic(start, c, end))
}
AbsolutePathCommand::A(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, end) => {
let start = last_point.unwrap();
last_point = Some(end);
last_control_point = None;
Some(PathSegment::Arc(start, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, end))
}
AbsolutePathCommand::Z => {
let start = last_point.unwrap();
let end = first_point.unwrap();
last_point = Some(end);
last_control_point = None;
Some(PathSegment::Line(start, end))
}
})
}
#[cfg(feature = "parsing")]
pub fn path_to_commands<'a, I>(segments: I, eps: f64) -> impl Iterator<Item = PathCommand> + 'a
where
I: IntoIterator<Item = &'a PathSegment> + 'a,
{
let mut last_point: Option<DVec2> = None;
segments
.into_iter()
.flat_map(move |seg| {
let start = seg.start();
let mut commands = Vec::new();
if last_point.is_none_or(|lp| !start.abs_diff_eq(lp, eps)) {
if last_point.is_some() {
commands.push(PathCommand::Absolute(AbsolutePathCommand::Z));
}
commands.push(PathCommand::Absolute(AbsolutePathCommand::M(start)));
}
match seg {
PathSegment::Line(_, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::L(*end)));
last_point = Some(*end);
}
PathSegment::Cubic(_, c1, c2, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::C(*c1, *c2, *end)));
last_point = Some(*end);
}
PathSegment::Quadratic(_, c, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::Q(*c, *end)));
last_point = Some(*end);
}
PathSegment::Arc(_, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, end) => {
commands.push(PathCommand::Absolute(AbsolutePathCommand::A(*rx, *ry, *x_axis_rotation, *large_arc_flag, *sweep_flag, *end)));
last_point = Some(*end);
}
}
commands
})
.chain(std::iter::once(PathCommand::Absolute(AbsolutePathCommand::Z)))
}