use super::*;
use crate::consts::*;
use glam::DVec2;
use std::fmt::Write;
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
pub fn new(manipulator_groups: Vec<ManipulatorGroup<ManipulatorGroupId>>, closed: bool) -> Self {
assert!(!closed || manipulator_groups.len() > 1, "A closed Subpath must contain more than 1 ManipulatorGroup.");
Self { manipulator_groups, closed }
}
pub fn from_bezier(bezier: &Bezier) -> Self {
Subpath::new(
vec![
ManipulatorGroup {
anchor: bezier.start(),
in_handle: None,
out_handle: bezier.handle_start(),
id: ManipulatorGroupId::new(),
},
ManipulatorGroup {
anchor: bezier.end(),
in_handle: bezier.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
},
],
false,
)
}
pub fn from_beziers(beziers: &[Bezier], closed: bool) -> Self {
assert!(!closed || beziers.len() > 1, "A closed Subpath must contain at least 1 Bezier.");
if beziers.is_empty() {
return Subpath::new(vec![], closed);
}
let first = beziers.first().unwrap();
let mut manipulator_groups = vec![ManipulatorGroup {
anchor: first.start(),
in_handle: None,
out_handle: first.handle_start(),
id: ManipulatorGroupId::new(),
}];
let mut inner_groups: Vec<ManipulatorGroup<ManipulatorGroupId>> = beziers
.windows(2)
.map(|bezier_pair| ManipulatorGroup {
anchor: bezier_pair[1].start(),
in_handle: bezier_pair[0].handle_end(),
out_handle: bezier_pair[1].handle_start(),
id: ManipulatorGroupId::new(),
})
.collect::<Vec<ManipulatorGroup<ManipulatorGroupId>>>();
manipulator_groups.append(&mut inner_groups);
let last = beziers.last().unwrap();
if !closed {
manipulator_groups.push(ManipulatorGroup {
anchor: last.end(),
in_handle: last.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
});
return Subpath::new(manipulator_groups, false);
}
manipulator_groups[0].in_handle = last.handle_end();
Subpath::new(manipulator_groups, true)
}
pub fn is_empty(&self) -> bool {
self.manipulator_groups.is_empty()
}
pub fn len(&self) -> usize {
self.manipulator_groups.len()
}
pub fn len_segments(&self) -> usize {
let mut number_of_curves = self.len();
if !self.closed && number_of_curves > 0 {
number_of_curves -= 1
}
number_of_curves
}
pub fn get_segment(&self, segment_index: usize) -> Option<Bezier> {
if segment_index >= self.len_segments() {
return None;
}
Some(self[segment_index].to_bezier(&self[(segment_index + 1) % self.len()]))
}
pub fn iter(&self) -> SubpathIter<ManipulatorGroupId> {
SubpathIter { subpath: self, index: 0 }
}
pub fn manipulator_groups(&self) -> &[ManipulatorGroup<ManipulatorGroupId>] {
&self.manipulator_groups
}
pub fn manipulator_groups_mut(&mut self) -> &mut Vec<ManipulatorGroup<ManipulatorGroupId>> {
&mut self.manipulator_groups
}
pub fn anchors(&self) -> Vec<DVec2> {
self.manipulator_groups().iter().map(|group| group.anchor).collect()
}
pub fn is_point(&self) -> bool {
if self.is_empty() {
return false;
}
let point = self.manipulator_groups[0].anchor;
self.manipulator_groups
.iter()
.all(|manipulator_group| manipulator_group.anchor.abs_diff_eq(point, MAX_ABSOLUTE_DIFFERENCE))
}
pub fn curve_to_svg(&self, svg: &mut String, attributes: String) {
let curve_start_argument = format!("{SVG_ARG_MOVE}{} {}", self[0].anchor.x, self[0].anchor.y);
let mut curve_arguments: Vec<String> = self.iter().map(|bezier| bezier.svg_curve_argument()).collect();
if self.closed {
curve_arguments.push(String::from(SVG_ARG_CLOSED));
}
let _ = write!(svg, r#"<path d="{} {}" {attributes}/>"#, curve_start_argument, curve_arguments.join(" "));
}
pub fn subpath_to_svg(&self, svg: &mut String, transform: glam::DAffine2) -> std::fmt::Result {
if self.is_empty() {
return Ok(());
}
let start = transform.transform_point2(self[0].anchor);
write!(svg, "{SVG_ARG_MOVE}{:.6},{:.6}", start.x, start.y)?;
for bezier in self.iter() {
bezier.apply_transformation(|pos| transform.transform_point2(pos)).write_curve_argument(svg)?;
svg.push(' ');
}
if self.closed {
svg.push_str(SVG_ARG_CLOSED);
}
Ok(())
}
pub fn handle_lines_to_svg(&self, svg: &mut String, attributes: String) {
let handle_lines: Vec<String> = self.iter().filter_map(|bezier| bezier.svg_handle_line_argument()).collect();
let _ = write!(svg, r#"<path d="{}" {attributes}/>"#, handle_lines.join(" "));
}
pub fn anchors_to_svg(&self, svg: &mut String, attributes: String) {
let anchors = self
.manipulator_groups
.iter()
.map(|point| format!(r#"<circle cx="{}" cy="{}" {attributes}/>"#, point.anchor.x, point.anchor.y))
.collect::<Vec<String>>();
let _ = write!(svg, "{}", anchors.concat());
}
pub fn handles_to_svg(&self, svg: &mut String, attributes: String) {
let handles = self
.manipulator_groups
.iter()
.flat_map(|group| [group.in_handle, group.out_handle])
.flatten()
.map(|handle| format!(r#"<circle cx="{}" cy="{}" {attributes}/>"#, handle.x, handle.y))
.collect::<Vec<String>>();
let _ = write!(svg, "{}", handles.concat());
}
pub fn to_svg(&self, svg: &mut String, curve_attributes: String, anchor_attributes: String, handle_attributes: String, handle_line_attributes: String) {
if !curve_attributes.is_empty() {
self.curve_to_svg(svg, curve_attributes);
}
if !handle_line_attributes.is_empty() {
self.handle_lines_to_svg(svg, handle_line_attributes);
}
if !anchor_attributes.is_empty() {
self.anchors_to_svg(svg, anchor_attributes);
}
if !handle_attributes.is_empty() {
self.handles_to_svg(svg, handle_attributes);
}
}
pub fn from_anchors(anchor_positions: impl IntoIterator<Item = DVec2>, closed: bool) -> Self {
Self::new(anchor_positions.into_iter().map(|anchor| ManipulatorGroup::new_anchor(anchor)).collect(), closed)
}
pub fn new_rect(corner1: DVec2, corner2: DVec2) -> Self {
Self::from_anchors([corner1, DVec2::new(corner2.x, corner1.y), corner2, DVec2::new(corner1.x, corner2.y)], true)
}
pub fn new_ellipse(corner1: DVec2, corner2: DVec2) -> Self {
let size = (corner1 - corner2).abs();
let center = (corner1 + corner2) / 2.;
let top = DVec2::new(center.x, corner1.y);
let bottom = DVec2::new(center.x, corner2.y);
let left = DVec2::new(corner1.x, center.y);
let right = DVec2::new(corner2.x, center.y);
const HANDLE_OFFSET_FACTOR: f64 = 0.551784777779014;
let handle_offset = size * HANDLE_OFFSET_FACTOR * 0.5;
let manipulator_groups = vec![
ManipulatorGroup::new(top, Some(top - handle_offset * DVec2::X), Some(top + handle_offset * DVec2::X)),
ManipulatorGroup::new(right, Some(right - handle_offset * DVec2::Y), Some(right + handle_offset * DVec2::Y)),
ManipulatorGroup::new(bottom, Some(bottom + handle_offset * DVec2::X), Some(bottom - handle_offset * DVec2::X)),
ManipulatorGroup::new(left, Some(left + handle_offset * DVec2::Y), Some(left - handle_offset * DVec2::Y)),
];
Self::new(manipulator_groups, true)
}
pub fn new_regular_polygon(center: DVec2, sides: u64, radius: f64) -> Self {
let anchor_positions = (0..sides).map(|i| {
let angle = (i as f64) * std::f64::consts::TAU / (sides as f64);
let center = center + DVec2::ONE * radius;
DVec2::new(center.x + radius * f64::cos(angle), center.y + radius * f64::sin(angle)) * 0.5
});
Self::from_anchors(anchor_positions, true)
}
pub fn new_star_polygon(center: DVec2, sides: u64, radius: f64, inner_radius: f64) -> Self {
let anchor_positions = (0..sides * 2).map(|i| {
let angle = (i as f64) * 0.5 * std::f64::consts::TAU / (sides as f64);
let center = center + DVec2::ONE * radius;
let r = if i % 2 == 0 { radius } else { inner_radius };
DVec2::new(center.x + r * f64::cos(angle), center.y + r * f64::sin(angle)) * 0.5
});
Self::from_anchors(anchor_positions, true)
}
pub fn new_line(p1: DVec2, p2: DVec2) -> Self {
Self::from_anchors([p1, p2], false)
}
pub fn new_cubic_spline(points: Vec<DVec2>) -> Self {
if points.len() < 2 {
return Self::new(Vec::new(), false);
}
let len_points = points.len();
let mut b = vec![DVec2::new(4., 4.); len_points];
b[0] = DVec2::new(2., 2.);
b[len_points - 1] = DVec2::new(2., 2.);
let mut c = vec![DVec2::new(1., 1.); len_points];
let mut d = vec![DVec2::ZERO; len_points];
d[0] = DVec2::new(2. * points[1].x + points[0].x, 2. * points[1].y + points[0].y);
d[len_points - 1] = DVec2::new(3. * points[len_points - 1].x, 3. * points[len_points - 1].y);
for idx in 1..(len_points - 1) {
d[idx] = DVec2::new(4. * points[idx].x + 2. * points[idx + 1].x, 4. * points[idx].y + 2. * points[idx + 1].y);
}
c[0] /= -b[0];
d[0] /= -b[0];
#[allow(clippy::assign_op_pattern)]
for i in 1..len_points {
b[i] += c[i - 1];
d[i] = d[i] + d[i - 1];
c[i] /= -b[i];
d[i] /= -b[i];
}
d[len_points - 1] *= -1.;
#[allow(clippy::assign_op_pattern)]
for i in (0..len_points - 1).rev() {
d[i] = d[i] - (c[i] * d[i + 1]);
d[i] *= -1.; }
let mut subpath = Subpath::new(Vec::new(), false);
subpath.manipulator_groups.push(ManipulatorGroup::new(points[0], None, Some(d[0])));
for i in 1..len_points - 1 {
subpath.manipulator_groups.push(ManipulatorGroup::new(points[i], Some(2. * points[i] - d[i]), Some(d[i])));
}
subpath
.manipulator_groups
.push(ManipulatorGroup::new(points[len_points - 1], Some(2. * points[len_points - 1] - d[len_points - 1]), None));
subpath
}
}