| 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))) |
| } |
|
|