File size: 4,392 Bytes
2409829 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
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)))
}
|