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; fn reflect_control_point(point: DVec2, control_point: DVec2) -> DVec2 { point * 2. - control_point } #[cfg(feature = "parsing")] pub fn path_from_commands(commands: I) -> impl Iterator where I: IntoIterator, { let mut first_point: Option = None; let mut last_point: Option = None; let mut last_control_point: Option = 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 + 'a where I: IntoIterator + 'a, { let mut last_point: Option = 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))) }