diff --git a/node-graph/gcore/src/graphic_element/renderer/quad.rs b/node-graph/gcore/src/graphic_element/renderer/quad.rs index 0e4483a3..e27a775d 100644 --- a/node-graph/gcore/src/graphic_element/renderer/quad.rs +++ b/node-graph/gcore/src/graphic_element/renderer/quad.rs @@ -26,11 +26,6 @@ impl Quad { self.edges().into_iter().map(|[start, end]| bezier_rs::Bezier::from_linear_dvec2(start, end)) } - /// Generates a [crate::vector::Subpath] of the quad - pub fn subpath(&self) -> crate::vector::Subpath { - crate::vector::Subpath::from_points(self.0.into_iter(), true) - } - /// Generates the axis aligned bounding box of the quad pub fn bounding_box(&self) -> [DVec2; 2] { [ diff --git a/node-graph/gcore/src/vector/consts.rs b/node-graph/gcore/src/vector/consts.rs deleted file mode 100644 index 68727490..00000000 --- a/node-graph/gcore/src/vector/consts.rs +++ /dev/null @@ -1,48 +0,0 @@ -use core::ops::{Index, IndexMut}; - -use serde::{Deserialize, Serialize}; - -#[repr(usize)] -#[derive(PartialEq, Eq, Clone, Debug, Copy, Serialize, Deserialize, specta::Type, Hash)] -pub enum ManipulatorType { - Anchor, - InHandle, - OutHandle, -} - -impl ManipulatorType { - pub fn from_index(index: usize) -> ManipulatorType { - match index { - 0 => ManipulatorType::Anchor, - 1 => ManipulatorType::InHandle, - 2 => ManipulatorType::OutHandle, - _ => ManipulatorType::Anchor, - } - } - - pub fn opposite_handle(self) -> ManipulatorType { - match self { - ManipulatorType::Anchor => ManipulatorType::Anchor, - ManipulatorType::InHandle => ManipulatorType::OutHandle, - ManipulatorType::OutHandle => ManipulatorType::InHandle, - } - } -} - -// Allows us to use ManipulatorType for indexing -impl Index for [T; 3] { - type Output = T; - fn index(&self, mt: ManipulatorType) -> &T { - &self[mt as usize] - } -} - -// Allows us to use ManipulatorType for indexing, mutably -impl IndexMut for [T; 3] { - fn index_mut(&mut self, mt: ManipulatorType) -> &mut T { - &mut self[mt as usize] - } -} - -// Remove when no longer needed -pub const SELECTION_THRESHOLD: f64 = 10.; diff --git a/node-graph/gcore/src/vector/id_vec.rs b/node-graph/gcore/src/vector/id_vec.rs deleted file mode 100644 index f34e361b..00000000 --- a/node-graph/gcore/src/vector/id_vec.rs +++ /dev/null @@ -1,183 +0,0 @@ -use alloc::vec; -use alloc::vec::Vec; -use core::iter::Zip; -use core::ops::{Deref, DerefMut}; -use core::slice::Iter; -use serde::{Deserialize, Serialize}; - -/// Brief description: A vec that allows indexing elements by both index and an assigned unique ID. -/// Goals of this data structure: -/// - Drop-in replacement for a Vec. -/// - Provide an auto-assigned Unique ID per element upon insertion. -/// - Add elements to the start or end. -/// - Insert element by Unique ID. Insert directly after an existing element by its Unique ID. -/// - Access data by providing Unique ID. -/// - Maintain ordering among the elements. -/// - Remove elements without changing Unique IDs. -/// This data structure is somewhat similar to a linked list in terms of invariants. -/// The downside is that currently it requires a lot of iteration. - -#[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct ElementId(pub u64); - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, specta::Type, Hash)] -pub struct IdBackedVec { - /// Contained elements - elements: Vec, - /// The IDs of the [Elements] contained within this - element_ids: Vec, - /// The ID that will be assigned to the next element that is added to this - next_id: ElementId, -} - -impl IdBackedVec { - /// Creates a new empty vector - pub const fn new() -> Self { - IdBackedVec { - elements: vec![], - element_ids: vec![], - next_id: ElementId(0), - } - } - - /// Push a new element to the start of the vector - pub fn push_front(&mut self, element: T) -> Option { - self.next_id = ElementId(self.next_id.0 + 1); - self.elements.insert(0, element); - self.element_ids.insert(0, self.next_id); - Some(self.next_id) - } - - /// Push an element to the end of the vector - pub fn push_end(&mut self, element: T) -> Option { - self.next_id = ElementId(self.next_id.0 + 1); - self.elements.push(element); - self.element_ids.push(self.next_id); - Some(self.next_id) - } - - /// Insert an element adjacent to the given ID - pub fn insert(&mut self, element: T, id: ElementId) -> Option { - if let Some(index) = self.index_from_id(id) { - self.next_id = ElementId(self.next_id.0 + 1); - self.elements.insert(index, element); - self.element_ids.insert(index, self.next_id); - return Some(self.next_id); - } - None - } - - /// Push an element to the end of the vector - /// Overridden from Vec, so adding values without creating an id cannot occur - pub fn push(&mut self, element: T) -> Option { - self.push_end(element) - } - - /// Add a range of elements of elements to the end of this vector - pub fn push_range(&mut self, elements: I) -> Vec - where - I: IntoIterator, - { - let mut ids = vec![]; - for element in elements { - if let Some(id) = self.push_end(element) { - ids.push(id); - } - } - ids - } - - /// Remove an element with a given element ID from the within this container. - /// This operation will return false if the element ID is not found. - /// Preserve unique ID lookup by using swap end and updating hashmap - pub fn remove(&mut self, to_remove_id: ElementId) -> Option { - if let Some(index) = self.index_from_id(to_remove_id) { - self.element_ids.remove(index); - return Some(self.elements.remove(index)); - } - None - } - - /// Get a single element with a given element ID from the within this container. - pub fn by_id(&self, id: ElementId) -> Option<&T> { - let index = self.index_from_id(id)?; - Some(&self.elements[index]) - } - - /// Get a mutable reference to a single element with a given element ID from the within this container. - pub fn by_id_mut(&mut self, id: ElementId) -> Option<&mut T> { - let index = self.index_from_id(id)?; - Some(&mut self.elements[index]) - } - - /// Get an element based on its index - pub fn by_index(&self, index: usize) -> Option<&T> { - self.elements.get(index) - } - - /// Get a mutable element based on its index - pub fn by_index_mut(&mut self, index: usize) -> Option<&mut T> { - self.elements.get_mut(index) - } - - /// Clear the elements and unique ids - pub fn clear(&mut self) { - self.elements.clear(); - self.element_ids.clear(); - } - - /// Enumerate the ids and elements in this container `(&ElementId, &T)` - pub fn enumerate(&self) -> Zip, Iter> { - self.element_ids.iter().zip(self.elements.iter()) - } - - /// Mutably Enumerate the ids and elements in this container `(&ElementId, &mut T)` - pub fn enumerate_mut(&mut self) -> impl Iterator { - self.element_ids.iter().zip(self.elements.iter_mut()) - } - - /// If this container contains an element with the given ID - pub fn contains(&self, id: ElementId) -> bool { - self.element_ids.contains(&id) - } - - /// Get the index of an element with the given ID - pub fn index_from_id(&self, element_id: ElementId) -> Option { - // Though this is a linear traversal, it is still likely faster than using a hashmap - self.element_ids.iter().position(|&id| id == element_id) - } -} - -impl Default for IdBackedVec { - fn default() -> Self { - Self::new() - } -} - -/// Allows for usage of UniqueElements as a Vec -impl Deref for IdBackedVec { - type Target = [T]; - fn deref(&self) -> &Self::Target { - &self.elements - } -} - -// TODO Consider removing this, it could allow for ElementIds and Elements to get out of sync -/// Allows for mutable usage of UniqueElements as a Vec -impl DerefMut for IdBackedVec { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.elements - } -} - -/// Allows use with iterators -/// Also allows constructing UniqueElements with collect -impl FromIterator for IdBackedVec { - fn from_iter>(iter: T) -> Self { - let mut new = IdBackedVec::default(); - // Add to the end of the existing elements - new.push_range(iter); - new - } -} diff --git a/node-graph/gcore/src/vector/manipulator_group.rs b/node-graph/gcore/src/vector/manipulator_group.rs deleted file mode 100644 index 1c2baddf..00000000 --- a/node-graph/gcore/src/vector/manipulator_group.rs +++ /dev/null @@ -1,307 +0,0 @@ -use super::consts::ManipulatorType; -use super::manipulator_point::ManipulatorPoint; - -use glam::{DAffine2, DVec2}; -use serde::{Deserialize, Serialize}; - -/// [ManipulatorGroup] is used to represent an anchor point + handles on the path that can be moved. -/// It contains 0-2 handles that are optionally available. -/// -/// Overview: -/// ```text -/// ManipulatorGroup <- Container for the anchor metadata and optional ManipulatorPoint -/// | -/// [Option; 3] <- [0] is the anchor's draggable point (but not metadata), [1] is the -/// / | \ InHandle's draggable point, [2] is the OutHandle's draggable point -/// / | \ -/// "Anchor" "InHandle" "OutHandle" <- These are ManipulatorPoints and the only editable "primitive" -/// ``` -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Default, specta::Type, Hash)] -pub struct ManipulatorGroup { - /// Editable points for the anchor and handles. - pub points: [Option; 3], - - #[serde(skip)] - // TODO: Remove this from Graphene, editor state should be stored in the frontend if possible. - /// The editor state of the anchor and handles. - pub editor_state: ManipulatorGroupEditorState, -} - -impl ManipulatorGroup { - /// Create a new anchor with the given position. - pub fn new_with_anchor(anchor_pos: DVec2) -> Self { - Self { - // An anchor and 2x None's which represent non-existent handles - points: [Some(ManipulatorPoint::new(anchor_pos, ManipulatorType::Anchor)), None, None], - editor_state: ManipulatorGroupEditorState::default(), - } - } - - /// Create a new anchor with the given anchor position and handles. - pub fn new_with_handles(anchor_pos: DVec2, handle_in_pos: Option, handle_out_pos: Option) -> Self { - Self { - points: match (handle_in_pos, handle_out_pos) { - (Some(pos1), Some(pos2)) => [ - Some(ManipulatorPoint::new(anchor_pos, ManipulatorType::Anchor)), - Some(ManipulatorPoint::new(pos1, ManipulatorType::InHandle)), - Some(ManipulatorPoint::new(pos2, ManipulatorType::OutHandle)), - ], - (None, Some(pos2)) => [ - Some(ManipulatorPoint::new(anchor_pos, ManipulatorType::Anchor)), - None, - Some(ManipulatorPoint::new(pos2, ManipulatorType::OutHandle)), - ], - (Some(pos1), None) => [ - Some(ManipulatorPoint::new(anchor_pos, ManipulatorType::Anchor)), - Some(ManipulatorPoint::new(pos1, ManipulatorType::InHandle)), - None, - ], - (None, None) => [Some(ManipulatorPoint::new(anchor_pos, ManipulatorType::Anchor)), None, None], - }, - editor_state: ManipulatorGroupEditorState::default(), - } - } - - // TODO Convert into bool in subpath - /// Create a [ManipulatorGroup] that represents a close path command. - pub fn closed() -> Self { - Self { - // An anchor (the first element) being `None` indicates a ClosePath (i.e. a path end command) - points: [None, None, None], - editor_state: ManipulatorGroupEditorState::default(), - } - } - - /// Answers whether this [ManipulatorGroup] represent a close shape command. - pub fn is_close(&self) -> bool { - self.points[ManipulatorType::Anchor].is_none() && self.points[ManipulatorType::InHandle].is_none() - } - - /// Finds the closest [ManipulatorPoint] owned by this [ManipulatorGroup]. This may return the anchor or either handle. - pub fn closest_point(&self, transform_space: &DAffine2, target: glam::DVec2, hide_handle_distance: f64) -> usize { - let mut closest_index: usize = 0; - let mut closest_distance_squared: f64 = f64::MAX; // Not ideal - for (index, point) in self.points.iter().enumerate() { - if let Some(point) = point { - let transfomed_point = transform_space.transform_point2(point.position); - - // Don't check handles under the anchor - if index != ManipulatorType::Anchor as usize { - if let Some(anchor) = &self.points[ManipulatorType::Anchor] { - if transfomed_point.distance_squared(transform_space.transform_point2(anchor.position)) < hide_handle_distance * hide_handle_distance { - continue; - } - } - } - - let distance_squared = transfomed_point.distance_squared(target); - if distance_squared < closest_distance_squared { - closest_distance_squared = distance_squared; - closest_index = index; - } - } - } - closest_index - } - - /// Move the selected points by the provided transform. - pub fn move_selected_points(&mut self, delta: DVec2, mirror_distance: bool) { - let mirror_angle = self.editor_state.mirror_angle_between_handles; - - // Move the point absolutely or relatively depending on if the point is under the cursor (the last selected point) - let move_point = |point: &mut ManipulatorPoint, delta: DVec2| { - point.position += delta; - assert!(point.position.is_finite(), "Point is not finite!") - }; - - // Find the correctly mirrored handle position based on mirroring settings - let move_symmetrical_handle = |position: DVec2, opposing_handle: Option<&mut ManipulatorPoint>, center: DVec2| { - // Early out for cases where we can't mirror - if !mirror_angle || opposing_handle.is_none() { - return; - } - let opposing_handle = opposing_handle.unwrap(); - - // Keep rotational similarity, but distance variable - let radius = if mirror_distance { center.distance(position) } else { center.distance(opposing_handle.position) }; - - if let Some(offset) = (position - center).try_normalize() { - opposing_handle.position = center - offset * radius; - assert!(opposing_handle.position.is_finite(), "Opposing handle not finite!") - } - }; - - // If no points are selected, why are we here at all? - if !self.any_points_selected() { - return; - } - - // If the anchor is selected, ignore any handle mirroring/dragging and drag all points - if self.is_anchor_selected() { - for point in self.points_mut() { - move_point(point, delta); - } - return; - } - - // If the anchor isn't selected, but both handles are, drag only handles - if self.both_handles_selected() { - self.editor_state.mirror_angle_between_handles = false; - for point in self.selected_handles_mut() { - move_point(point, delta); - } - return; - } - - // If the anchor isn't selected, and only one handle is selected - // Drag the single handle - let reflect_center = self.points[ManipulatorType::Anchor].as_ref().unwrap().position; - let selected_handle = self.selected_handles_mut().next().unwrap(); - move_point(selected_handle, delta); - - // Move the opposing handle symmetrically if our mirroring flags allow - let selected_handle = &selected_handle.clone(); - let opposing_handle = self.opposing_handle_mut(selected_handle); - move_symmetrical_handle(selected_handle.position, opposing_handle, reflect_center); - } - - /// Delete any [ManipulatorPoint] that are selected, this includes handles or the anchor. - pub fn delete_selected(&mut self) { - for point_option in self.points.iter_mut() { - if let Some(point) = point_option { - if point.editor_state.is_selected { - *point_option = None; - } - } - } - } - - /// Returns true if any points in this [ManipulatorGroup] are selected. - pub fn any_points_selected(&self) -> bool { - self.points.iter().flatten().any(|point| point.editor_state.is_selected) - } - - /// Returns true if the anchor point is selected. - pub fn is_anchor_selected(&self) -> bool { - if let Some(anchor) = &self.points[0] { - anchor.editor_state.is_selected - } else { - false - } - } - - /// Determines if the two handle points are selected. - pub fn both_handles_selected(&self) -> bool { - self.points.iter().skip(1).flatten().filter(|pnt| pnt.editor_state.is_selected).count() == 2 - } - - /// Set a point, given its [ManipulatorType] enum integer ID, to a chosen selected state. - pub fn select_point(&mut self, point_id: usize, selected: bool) -> Option<&mut ManipulatorPoint> { - if let Some(point) = self.points[point_id].as_mut() { - point.set_selected(selected); - } - self.points[point_id].as_mut() - } - - /// Clear the selected points for this [ManipulatorGroup]. - pub fn clear_selected_points(&mut self) { - for point in self.points.iter_mut().flatten() { - point.set_selected(false); - } - } - - /// Provides the points in this [ManipulatorGroup]. - pub fn points(&self) -> impl Iterator { - self.points.iter().flatten() - } - - /// Provides the points in this [ManipulatorGroup] as mutable. - pub fn points_mut(&mut self) -> impl Iterator { - self.points.iter_mut().flatten() - } - - /// Provides the selected points in this [ManipulatorGroup]. - pub fn selected_points(&self) -> impl Iterator { - self.points.iter().flatten().filter(|pnt| pnt.editor_state.is_selected) - } - - /// Provides mutable selected points in this [ManipulatorGroup]. - pub fn selected_points_mut(&mut self) -> impl Iterator { - self.points.iter_mut().flatten().filter(|pnt| pnt.editor_state.is_selected) - } - - /// Provides the selected handles attached to this [ManipulatorGroup]. - pub fn selected_handles(&self) -> impl Iterator { - self.points.iter().skip(1).flatten().filter(|pnt| pnt.editor_state.is_selected) - } - - /// Provides the mutable selected handles attached to this [ManipulatorGroup]. - pub fn selected_handles_mut(&mut self) -> impl Iterator { - self.points.iter_mut().skip(1).flatten().filter(|pnt| pnt.editor_state.is_selected) - } - - /// Angle between handles, in radians. - pub fn angle_between_handles(&self) -> f64 { - if let [Some(a1), Some(h1), Some(h2)] = &self.points { - (a1.position - h1.position).angle_between(a1.position - h2.position) - } else { - 0. - } - } - - /// Returns the opposing handle to the handle provided. - /// Returns [None] if the provided handle is of type [ManipulatorType::Anchor]. - /// Returns [None] if the opposing handle doesn't exist. - pub fn opposing_handle(&self, handle: &ManipulatorPoint) -> Option<&ManipulatorPoint> { - if handle.manipulator_type == ManipulatorType::Anchor { - return None; - } - self.points[handle.manipulator_type.opposite_handle()].as_ref() - } - - /// Returns the opposing handle to the handle provided, mutable. - /// Returns [None] if the provided handle is of type [ManipulatorType::Anchor]. - /// Returns [None] if the opposing handle doesn't exist. - pub fn opposing_handle_mut(&mut self, handle: &ManipulatorPoint) -> Option<&mut ManipulatorPoint> { - if handle.manipulator_type == ManipulatorType::Anchor { - return None; - } - self.points[handle.manipulator_type.opposite_handle()].as_mut() - } - - /// Set the mirroring state - pub fn toggle_mirroring(&mut self, toggle_angle: bool) { - if toggle_angle { - self.editor_state.mirror_angle_between_handles = !self.editor_state.mirror_angle_between_handles; - } - } - - /// Helper function to more easily set position of [ManipulatorPoints] - pub fn set_point_position(&mut self, point_index: usize, position: DVec2) { - assert!(position.is_finite(), "Tried to set_point_position to non finite"); - if let Some(point) = &mut self.points[point_index] { - point.position = position; - } else { - self.points[point_index] = Some(ManipulatorPoint::new(position, ManipulatorType::from_index(point_index))) - } - } - - /// Apply an affine transformation the points - pub fn transform(&mut self, transform: &DAffine2) { - for point in self.points_mut() { - point.transform(transform); - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ManipulatorGroupEditorState { - // Whether the angle between the handles should be maintained - pub mirror_angle_between_handles: bool, -} - -impl Default for ManipulatorGroupEditorState { - fn default() -> Self { - Self { mirror_angle_between_handles: true } - } -} diff --git a/node-graph/gcore/src/vector/manipulator_point.rs b/node-graph/gcore/src/vector/manipulator_point.rs deleted file mode 100644 index 06fb9acd..00000000 --- a/node-graph/gcore/src/vector/manipulator_point.rs +++ /dev/null @@ -1,90 +0,0 @@ -use core::hash::Hash; - -use super::consts::ManipulatorType; -use glam::{DAffine2, DVec2}; -use serde::{Deserialize, Serialize}; - -/// [ManipulatorPoint] represents any editable Bezier point, either an anchor or handle -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] -pub struct ManipulatorPoint { - /// The sibling element if this is a handle - pub position: glam::DVec2, - /// The type of manipulator this point is - pub manipulator_type: ManipulatorType, - - #[serde(skip)] - /// The state specific to the editor - // TODO Remove this from Graphene, editor state should be stored in the frontend if possible. - pub editor_state: ManipulatorPointEditorState, -} - -impl Default for ManipulatorPoint { - fn default() -> Self { - Self { - position: DVec2::ZERO, - manipulator_type: ManipulatorType::Anchor, - editor_state: ManipulatorPointEditorState::default(), - } - } -} - -#[allow(clippy::derived_hash_with_manual_eq)] -impl Hash for ManipulatorPoint { - fn hash(&self, state: &mut H) { - self.position.to_array().iter().for_each(|x| x.to_bits().hash(state)); - self.manipulator_type.hash(state); - - self.editor_state.hash(state); - } -} - -impl ManipulatorPoint { - /// Initialize a new [ManipulatorPoint]. - pub fn new(position: glam::DVec2, manipulator_type: ManipulatorType) -> Self { - assert!(position.is_finite(), "tried to create point with non finite position"); - Self { - position, - manipulator_type, - editor_state: ManipulatorPointEditorState::default(), - } - } - - /// Sets this [ManipulatorPoint] to a chosen selection state. - pub fn set_selected(&mut self, selected: bool) { - self.editor_state.is_selected = selected; - } - - /// Whether this [ManipulatorPoint] is currently selected. - pub fn is_selected(&self) -> bool { - self.editor_state.is_selected - } - - /// Apply given transform to this point - pub fn transform(&mut self, delta: &DAffine2) { - self.position = delta.transform_point2(self.position); - assert!(self.position.is_finite(), "tried to transform point to non finite position"); - } - - /// Move by a delta amount - pub fn move_by(&mut self, delta: &DVec2) { - self.position += *delta; - assert!(self.position.is_finite(), "tried to move point to non finite position"); - } -} - -#[derive(PartialEq, Eq, Clone, Debug, specta::Type, Hash)] -pub struct ManipulatorPointEditorState { - /// Whether or not this manipulator point can be selected. - pub can_be_selected: bool, - /// Whether or not this manipulator point is currently selected. - pub is_selected: bool, -} - -impl Default for ManipulatorPointEditorState { - fn default() -> Self { - Self { - can_be_selected: true, - is_selected: false, - } - } -} diff --git a/node-graph/gcore/src/vector/mod.rs b/node-graph/gcore/src/vector/mod.rs index ccf7ff6f..6b70aff2 100644 --- a/node-graph/gcore/src/vector/mod.rs +++ b/node-graph/gcore/src/vector/mod.rs @@ -1,21 +1,12 @@ pub mod brush_stroke; -pub mod consts; pub mod generator_nodes; -pub mod manipulator_group; -pub mod manipulator_point; pub mod style; pub use style::PathStyle; -pub mod subpath; -pub use subpath::Subpath; - mod vector_data; pub use vector_data::*; -mod id_vec; -pub use id_vec::IdBackedVec; - mod vector_nodes; pub use vector_nodes::*; diff --git a/node-graph/gcore/src/vector/subpath.rs b/node-graph/gcore/src/vector/subpath.rs deleted file mode 100644 index f7783d35..00000000 --- a/node-graph/gcore/src/vector/subpath.rs +++ /dev/null @@ -1,655 +0,0 @@ -use super::consts::ManipulatorType; -use super::id_vec::{ElementId, IdBackedVec}; -use super::manipulator_group::ManipulatorGroup; -use super::manipulator_point::ManipulatorPoint; -use crate::uuid::ManipulatorGroupId; - -use alloc::string::String; -use alloc::vec; -use alloc::vec::Vec; -use core::iter::Zip; -use core::slice::Iter; -use dyn_any::{DynAny, StaticType}; -use glam::{DAffine2, DVec2}; -use kurbo::{BezPath, PathEl, Shape}; -use serde::{Deserialize, Serialize}; - -/// [Subpath] represents a single vector path, containing many [ManipulatorGroups]. -/// For each closed shape we keep a [Subpath] which contains the [ManipulatorGroup]s (handles and anchors) that define that shape. -// TODO Add "closed" bool to subpath -#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize, DynAny, specta::Type, Hash)] -pub struct Subpath(IdBackedVec); - -impl Subpath { - // ** INITIALIZATION ** - - /// Create a new [Subpath] with no [ManipulatorGroup]s. - pub const fn new() -> Self { - Subpath(IdBackedVec::new()) - } - - /// Construct a [Subpath] from a point iterator - pub fn from_points(points: impl Iterator, closed: bool) -> Self { - let manipulator_groups = points.map(ManipulatorGroup::new_with_anchor); - - let mut p_line = Subpath(IdBackedVec::default()); - - p_line.0.push_range(manipulator_groups); - if closed { - p_line.0.push(ManipulatorGroup::closed()); - } - - p_line - } - - /// Create a new [Subpath] from a [kurbo Shape](Shape). - /// This exists to smooth the transition away from Kurbo - pub fn from_kurbo_shape(shape: &T) -> Self { - shape.path_elements(0.1).into() - } - - /// Convert to the legacy Subpath from the `bezier_rs::Subpath`. - pub fn from_bezier_rs<'a, Subpath: core::borrow::Borrow<&'a bezier_rs::Subpath>>(value: impl IntoIterator) -> Self { - let mut groups = IdBackedVec::new(); - for subpath in value.into_iter() { - for group in subpath.borrow().manipulator_groups() { - groups.push(ManipulatorGroup::new_with_handles(group.anchor, group.in_handle, group.out_handle)); - } - if subpath.borrow().closed() { - groups.push(ManipulatorGroup::closed()); - } - } - Self(groups) - } - - // ** PRIMITIVE CONSTRUCTION ** - - /// constructs a rectangle with `p1` as the lower left and `p2` as the top right - pub fn new_rect(p1: DVec2, p2: DVec2) -> Self { - Subpath( - vec![ - ManipulatorGroup::new_with_anchor(p1), - ManipulatorGroup::new_with_anchor(DVec2::new(p1.x, p2.y)), - ManipulatorGroup::new_with_anchor(p2), - ManipulatorGroup::new_with_anchor(DVec2::new(p2.x, p1.y)), - ManipulatorGroup::closed(), - ] - .into_iter() - .collect(), - ) - } - - pub fn new_ellipse(p1: DVec2, p2: DVec2) -> Self { - let x_height = DVec2::new((p2.x - p1.x).abs(), 0.); - let y_height = DVec2::new(0., (p2.y - p1.y).abs()); - let center = (p1 + p2) * 0.5; - let top = center + y_height * 0.5; - let bottom = center - y_height * 0.5; - let left = center + x_height * 0.5; - let right = center - x_height * 0.5; - - // Constant explained here https://stackoverflow.com/a/27863181 - let curve_constant = 0.55228_3; - let handle_offset_x = x_height * curve_constant * 0.5; - let handle_offset_y = y_height * curve_constant * 0.5; - - Subpath( - vec![ - ManipulatorGroup::new_with_handles(top, Some(top + handle_offset_x), Some(top - handle_offset_x)), - ManipulatorGroup::new_with_handles(right, Some(right + handle_offset_y), Some(right - handle_offset_y)), - ManipulatorGroup::new_with_handles(bottom, Some(bottom - handle_offset_x), Some(bottom + handle_offset_x)), - ManipulatorGroup::new_with_handles(left, Some(left - handle_offset_y), Some(left + handle_offset_y)), - ManipulatorGroup::closed(), - ] - .into_iter() - .collect(), - ) - } - - /// constructs an ngon - /// `radius` is the distance from the `center` to any vertex, or the radius of the circle the ngon may be inscribed inside - /// `sides` is the number of sides - pub fn new_ngon(center: DVec2, sides: u64, radius: f64) -> Self { - let mut manipulator_groups = vec![]; - for i in 0..sides { - let angle = (i as f64) * core::f64::consts::TAU / (sides as f64); - let center = center + DVec2::ONE * radius; - let position = ManipulatorGroup::new_with_anchor(DVec2::new(center.x + radius * f64::cos(angle), center.y + radius * f64::sin(angle)) * 0.5); - - manipulator_groups.push(position); - } - manipulator_groups.push(ManipulatorGroup::closed()); - - Subpath(manipulator_groups.into_iter().collect()) - } - - /// Constructs a line from `p1` to `p2` - pub fn new_line(p1: DVec2, p2: DVec2) -> Self { - Subpath(vec![ManipulatorGroup::new_with_anchor(p1), ManipulatorGroup::new_with_anchor(p2)].into_iter().collect()) - } - - /// Constructs a set of lines from `p1` to `pN` - pub fn new_poly_line>(points: Vec) -> Self { - let manipulator_groups = points.into_iter().map(|point| ManipulatorGroup::new_with_anchor(point.into())); - let mut p_line = Subpath(IdBackedVec::default()); - p_line.0.push_range(manipulator_groups); - p_line - } - - pub fn new_spline>(points: Vec) -> Self { - let mut new = Self::default(); - // shadow `points` - let points: Vec = points.into_iter().map(Into::::into).collect(); - - // Number of points = number of points to find handles for - let n = points.len(); - - // matrix coefficients a, b and c (see https://mathworld.wolfram.com/CubicSpline.html) - // because the 'a' coefficients are all 1 they need not be stored - // this algorithm does a variation of the above algorithm. - // Instead of using the traditional cubic: a + bt + ct^2 + dt^3, we use the bezier cubic. - - let mut b = vec![DVec2::new(4.0, 4.0); n]; - b[0] = DVec2::new(2.0, 2.0); - b[n - 1] = DVec2::new(2.0, 2.0); - - let mut c = vec![DVec2::new(1.0, 1.0); n]; - - // 'd' is the the second point in a cubic bezier, which is what we solve for - let mut d = vec![DVec2::ZERO; n]; - - d[0] = DVec2::new(2.0 * points[1].x + points[0].x, 2.0 * points[1].y + points[0].y); - d[n - 1] = DVec2::new(3.0 * points[n - 1].x, 3.0 * points[n - 1].y); - for idx in 1..(n - 1) { - d[idx] = DVec2::new(4.0 * points[idx].x + 2.0 * points[idx + 1].x, 4.0 * points[idx].y + 2.0 * points[idx + 1].y); - } - - // Solve with Thomas algorithm (see https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm) - // do row operations to eliminate `a` coefficients - c[0] /= -b[0]; - d[0] /= -b[0]; - #[allow(clippy::assign_op_pattern)] - for i in 1..n { - b[i] += c[i - 1]; - // for some reason the below line makes the borrow checker mad - //d[i] += d[i-1] - d[i] = d[i] + d[i - 1]; - c[i] /= -b[i]; - d[i] /= -b[i]; - } - - // at this point b[i] == -a[i + 1], a[i] == 0, - // do row operations to eliminate 'c' coefficients and solve - d[n - 1] *= -1.0; - #[allow(clippy::assign_op_pattern)] - for i in (0..n - 1).rev() { - d[i] = d[i] - (c[i] * d[i + 1]); - d[i] *= -1.0; //d[i] /= b[i] - } - - // given the second point in the n'th cubic bezier, the third point is given by 2 * points[n+1] - b[n+1]. - // to find 'handle1_pos' for the n'th point we need the n-1 cubic bezier - new.0.push_end(ManipulatorGroup::new_with_handles(points[0], None, Some(d[0]))); - for i in 1..n - 1 { - new.0.push_end(ManipulatorGroup::new_with_handles(points[i], Some(2.0 * points[i] - d[i]), Some(d[i]))); - } - new.0.push_end(ManipulatorGroup::new_with_handles(points[n - 1], Some(2.0 * points[n - 1] - d[n - 1]), None)); - - new - } - - /// Move the selected points by the delta vector - pub fn move_selected(&mut self, delta: DVec2, mirror_distance: bool) { - self.selected_manipulator_groups_any_points_mut() - .for_each(|manipulator_group| manipulator_group.move_selected_points(delta, mirror_distance)); - } - - /// Delete the selected points from the [Subpath] - pub fn delete_selected(&mut self) { - let mut ids_to_delete: Vec = vec![]; - for (id, manipulator_group) in self.manipulator_groups_mut().enumerate_mut() { - if manipulator_group.is_anchor_selected() { - ids_to_delete.push(*id); - } else { - manipulator_group.delete_selected(); - } - } - - for id in ids_to_delete { - self.manipulator_groups_mut().remove(id); - } - } - - // Apply a transformation to all of the Subpath points - pub fn apply_affine(&mut self, affine: DAffine2) { - for manipulator_group in self.manipulator_groups_mut().iter_mut() { - manipulator_group.transform(&affine); - } - } - - // ** SELECTION OF POINTS ** - - /// Set a single point to a chosen selection state by providing `(manipulator group ID, manipulator type)`. - pub fn select_point(&mut self, point: (ElementId, ManipulatorType), selected: bool) -> Option<&mut ManipulatorGroup> { - let (manipulator_group_id, point_id) = point; - if let Some(manipulator_group) = self.manipulator_groups_mut().by_id_mut(manipulator_group_id) { - manipulator_group.select_point(point_id as usize, selected); - - Some(manipulator_group) - } else { - None - } - } - - /// Set points in the [Subpath] to a chosen selection state, given by `(manipulator group ID, manipulator type)`. - pub fn select_points(&mut self, points: &[(ElementId, ManipulatorType)], selected: bool) { - points.iter().for_each(|point| { - self.select_point(*point, selected); - }); - } - - /// Select all the anchors in this shape - pub fn select_all_anchors(&mut self) { - for manipulator_group in self.manipulator_groups_mut().iter_mut() { - manipulator_group.select_point(ManipulatorType::Anchor as usize, true); - } - } - - /// Select an anchor by index - pub fn select_anchor_by_index(&mut self, manipulator_group_index: usize) -> Option<&mut ManipulatorGroup> { - if let Some(manipulator_group) = self.manipulator_groups_mut().by_index_mut(manipulator_group_index) { - manipulator_group.select_point(ManipulatorType::Anchor as usize, true); - - Some(manipulator_group) - } else { - None - } - } - - /// The last anchor in the shape - pub fn select_last_anchor(&mut self) -> Option<&mut ManipulatorGroup> { - if let Some(manipulator_group) = self.manipulator_groups_mut().last_mut() { - manipulator_group.select_point(ManipulatorType::Anchor as usize, true); - - Some(manipulator_group) - } else { - None - } - } - - /// Clear all the selected manipulator groups, i.e., clear the selected points inside the manipulator groups - pub fn clear_selected_manipulator_groups(&mut self) { - for manipulator_group in self.manipulator_groups_mut().iter_mut() { - manipulator_group.clear_selected_points(); - } - } - - // ** ACCESSING MANIPULATORGROUPS ** - - /// Return all the selected anchors, reference - pub fn selected_manipulator_groups(&self) -> impl Iterator { - self.manipulator_groups().iter().filter(|manipulator_group| manipulator_group.is_anchor_selected()) - } - - /// Return all the selected anchors, mutable - pub fn selected_manipulator_groups_mut(&mut self) -> impl Iterator { - self.manipulator_groups_mut().iter_mut().filter(|manipulator_group| manipulator_group.is_anchor_selected()) - } - - /// Return all the selected [ManipulatorPoint]s by reference - pub fn selected_manipulator_groups_any_points(&self) -> impl Iterator { - self.manipulator_groups().iter().filter(|manipulator_group| manipulator_group.any_points_selected()) - } - - /// Return all the selected [ManipulatorPoint]s by mutable reference - pub fn selected_manipulator_groups_any_points_mut(&mut self) -> impl Iterator { - self.manipulator_groups_mut().iter_mut().filter(|manipulator_group| manipulator_group.any_points_selected()) - } - - /// An alias for `self.0` - pub fn manipulator_groups(&self) -> &IdBackedVec { - &self.0 - } - - /// Returns a [ManipulatorPoint] from the last [ManipulatorGroup] in the [Subpath]. - pub fn last_point(&self, control_type: ManipulatorType) -> Option<&ManipulatorPoint> { - self.manipulator_groups().last().and_then(|manipulator_group| manipulator_group.points[control_type].as_ref()) - } - - /// Returns a [ManipulatorPoint] from the last [ManipulatorGroup], mutably - pub fn last_point_mut(&mut self, control_type: ManipulatorType) -> Option<&mut ManipulatorPoint> { - self.manipulator_groups_mut().last_mut().and_then(|manipulator_group| manipulator_group.points[control_type].as_mut()) - } - - /// Returns a [ManipulatorPoint] from the first [ManipulatorGroup] - pub fn first_point(&self, control_type: ManipulatorType) -> Option<&ManipulatorPoint> { - self.manipulator_groups().first().and_then(|manipulator_group| manipulator_group.points[control_type].as_ref()) - } - - /// Returns a [ManipulatorPoint] from the first [ManipulatorGroup] - pub fn first_point_mut(&mut self, control_type: ManipulatorType) -> Option<&mut ManipulatorPoint> { - self.manipulator_groups_mut().first_mut().and_then(|manipulator_group| manipulator_group.points[control_type].as_mut()) - } - - /// Should we close the shape? - pub fn should_close_shape(&self) -> bool { - if self.last_point(ManipulatorType::Anchor).is_none() { - return false; - } - - self.first_point(ManipulatorType::Anchor) - .unwrap() - .position - .distance(self.last_point(ManipulatorType::Anchor).unwrap().position) - < 0.001 // TODO Replace with constant, a small epsilon - } - - /// Close the shape if able - pub fn close_shape(&mut self) { - if self.should_close_shape() { - self.manipulator_groups_mut().push_end(ManipulatorGroup::closed()); - } - } - - /// An alias for `self.0` mutable - pub fn manipulator_groups_mut(&mut self) -> &mut IdBackedVec { - &mut self.0 - } - - /// Return the bounding box of the shape. - pub fn bounding_box(&self) -> Option<[DVec2; 2]> { - self.bezier_iter() - .map(|bezier| bezier.internal.bounding_box()) - .reduce(|[a_min, a_max], [b_min, b_max]| [a_min.min(b_min), a_max.max(b_max)]) - } - - /// Generate an SVG `path` elements's `d` attribute: ``. - pub fn to_svg(&mut self) -> String { - fn write_positions(result: &mut String, values: [Option; 3]) { - use core::fmt::Write; - let count = values.into_iter().flatten().count(); - for (index, pos) in values.into_iter().flatten().enumerate() { - write!(result, "{},{}", pos.x, pos.y).unwrap(); - if index != count - 1 { - result.push(' '); - } - } - } - - let mut result = String::new(); - // The out position from the previous ManipulatorGroup - let mut last_out_handle = None; - // The values from the last moveto (for closing the path) - let (mut first_in_handle, mut first_in_anchor) = (None, None); - // Should the next element be a moveto? - let mut start_new_contour = true; - for manipulator_group in self.manipulator_groups().iter() { - let in_handle = manipulator_group.points[ManipulatorType::InHandle].as_ref().map(|point| point.position); - let anchor = manipulator_group.points[ManipulatorType::Anchor].as_ref().map(|point| point.position); - let out_handle = manipulator_group.points[ManipulatorType::OutHandle].as_ref().map(|point| point.position); - - let command = match (last_out_handle.is_some(), in_handle.is_some(), anchor.is_some()) { - (_, _, true) if start_new_contour => 'M', - (true, false, true) | (false, true, true) => 'Q', - (true, true, true) => 'C', - (false, false, true) => 'L', - (_, false, false) => 'Z', - _ => panic!("Invalid shape {self:#?}"), - }; - - // Complete the last curve - if command == 'Z' { - if last_out_handle.is_some() && first_in_handle.is_some() { - result.push('C'); - write_positions(&mut result, [last_out_handle, first_in_handle, first_in_anchor]); - } else if last_out_handle.is_some() || first_in_handle.is_some() { - result.push('Q'); - write_positions(&mut result, [last_out_handle, first_in_handle, first_in_anchor]); - } - result.push('Z'); - } - // Update the last moveto position - else if command == 'M' { - (first_in_handle, first_in_anchor) = (in_handle, anchor); - result.push(command); - write_positions(&mut result, [None, None, anchor]); - } - // Write other path commands (line to/quadratic to/cubic to) - else { - result.push(command); - write_positions(&mut result, [last_out_handle, in_handle, anchor]); - } - - start_new_contour = command == 'Z'; - last_out_handle = out_handle; - } - result - } - - /// Convert to an iter over [`bezier_rs::Bezier`] segments. - pub fn bezier_iter(&self) -> PathIter { - PathIter { - path: self.manipulator_groups().enumerate(), - last_anchor: None, - last_out_handle: None, - last_id: None, - first_in_handle: None, - first_anchor: None, - first_id: None, - start_new_contour: true, - } - } -} - -/// A wrapper around [`bezier_rs::Bezier`] containing also the IDs for the [`ManipulatorGroup`]s where the points are from. -pub struct BezierId { - /// The internal [`bezier_rs::Bezier`]. - pub internal: bezier_rs::Bezier, - /// The ID of the [ManipulatorGroup] of the start point and, if cubic, the start handle. - pub start: u64, - /// The ID of the [ManipulatorGroup] of the end point and, if cubic, the end handle. - pub end: u64, - /// The ID of the [ManipulatorGroup] of the handle on a quadratic (if applicable). - pub mid: Option, -} - -impl BezierId { - /// Construct a `BezierId` by encapsulating a [`bezier_rs::Bezier`] and providing the IDs of the [`ManipulatorGroup`]s the constituent points belong to. - fn new(internal: bezier_rs::Bezier, start: u64, end: u64, mid: Option) -> Self { - Self { internal, start, end, mid } - } -} - -/// An iterator over [`bezier_rs::Bezier`] segments constructable via [`Subpath::bezier_iter`]. -pub struct PathIter<'a> { - path: Zip, Iter<'a, ManipulatorGroup>>, - - last_anchor: Option, - last_out_handle: Option, - last_id: Option, - - first_in_handle: Option, - first_anchor: Option, - first_id: Option, - - start_new_contour: bool, -} - -impl<'a> Iterator for PathIter<'a> { - type Item = BezierId; - - fn next(&mut self) -> Option { - use bezier_rs::Bezier; - - let mut result = None; - - while result.is_none() { - let (id, manipulator_group) = self.path.next()?; - let id = id.0; - - let in_handle = manipulator_group.points[ManipulatorType::InHandle].as_ref().map(|point| point.position); - let anchor = manipulator_group.points[ManipulatorType::Anchor].as_ref().map(|point| point.position); - let out_handle = manipulator_group.points[ManipulatorType::OutHandle].as_ref().map(|point| point.position); - - let mut start_new_contour = false; - - // Move to - if anchor.is_some() && self.start_new_contour { - // Update the last moveto position - (self.first_in_handle, self.first_anchor) = (in_handle, anchor); - self.first_id = Some(id); - } - // Cubic to - else if let (Some(p1), Some(p2), Some(p3), Some(p4), Some(last_id)) = (self.last_anchor, self.last_out_handle, in_handle, anchor, self.last_id) { - result = Some(BezierId::new(Bezier::from_cubic_dvec2(p1, p2, p3, p4), last_id, id, None)); - } - // Quadratic to - else if let (Some(p1), Some(p2), Some(p3), Some(last_id)) = (self.last_anchor, self.last_out_handle.or(in_handle), anchor, self.last_id) { - let mid = if self.last_out_handle.is_some() { last_id } else { id }; - result = Some(BezierId::new(Bezier::from_quadratic_dvec2(p1, p2, p3), last_id, id, Some(mid))); - } - // Line to - else if let (Some(p1), Some(p2), Some(last_id)) = (self.last_anchor, anchor, self.last_id) { - result = Some(BezierId::new(Bezier::from_linear_dvec2(p1, p2), last_id, id, None)); - } - // Close path - else if in_handle.is_none() && anchor.is_none() { - start_new_contour = true; - if let (Some(last_id), Some(first_id)) = (self.last_id, self.first_id) { - // Complete the last curve - if let (Some(p1), Some(p2), Some(p3), Some(p4)) = (self.last_anchor, self.last_out_handle, self.first_in_handle, self.first_anchor) { - result = Some(BezierId::new(Bezier::from_cubic_dvec2(p1, p2, p3, p4), last_id, first_id, None)); - } else if let (Some(p1), Some(p2), Some(p3)) = (self.last_anchor, self.last_out_handle.or(self.first_in_handle), self.first_anchor) { - let mid = if self.last_out_handle.is_some() { last_id } else { first_id }; - result = Some(BezierId::new(Bezier::from_quadratic_dvec2(p1, p2, p3), last_id, first_id, Some(mid))); - } else if let (Some(p1), Some(p2)) = (self.last_anchor, self.first_anchor) { - result = Some(BezierId::new(Bezier::from_linear_dvec2(p1, p2), last_id, first_id, None)); - } - } - } - - self.start_new_contour = start_new_contour; - self.last_out_handle = out_handle; - self.last_anchor = anchor; - self.last_id = Some(id); - } - result - } -} - -impl From<&Subpath> for BezPath { - /// Create a [BezPath] from a [Subpath]. - fn from(subpath: &Subpath) -> Self { - // Take manipulator groups and create path elements: line, quad or curve, or a close indicator - let manipulator_groups_to_path_el = |first: &ManipulatorGroup, second: &ManipulatorGroup| -> (PathEl, bool) { - match [ - &first.points[ManipulatorType::OutHandle], - &second.points[ManipulatorType::InHandle], - &second.points[ManipulatorType::Anchor], - ] { - [None, None, Some(anchor)] => (PathEl::LineTo(point_to_kurbo(anchor)), false), - [None, Some(in_handle), Some(anchor)] => (PathEl::QuadTo(point_to_kurbo(in_handle), point_to_kurbo(anchor)), false), - [Some(out_handle), None, Some(anchor)] => (PathEl::QuadTo(point_to_kurbo(out_handle), point_to_kurbo(anchor)), false), - [Some(out_handle), Some(in_handle), Some(anchor)] => (PathEl::CurveTo(point_to_kurbo(out_handle), point_to_kurbo(in_handle), point_to_kurbo(anchor)), false), - [Some(out_handle), None, None] => { - if let Some(first_anchor) = subpath.manipulator_groups().first() { - ( - if let Some(in_handle) = &first_anchor.points[ManipulatorType::InHandle] { - PathEl::CurveTo( - point_to_kurbo(out_handle), - point_to_kurbo(in_handle), - point_to_kurbo(first_anchor.points[ManipulatorType::Anchor].as_ref().unwrap()), - ) - } else { - PathEl::QuadTo(point_to_kurbo(out_handle), point_to_kurbo(first_anchor.points[ManipulatorType::Anchor].as_ref().unwrap())) - }, - true, - ) - } else { - (PathEl::ClosePath, true) - } - } - [None, None, None] => (PathEl::ClosePath, true), - _ => panic!("Invalid path element {subpath:#?}"), - } - }; - - if subpath.manipulator_groups().is_empty() { - return BezPath::new(); - } - - let mut bez_path = vec![]; - let mut start_new_shape = true; - - for elements in subpath.manipulator_groups().windows(2) { - let first = &elements[0]; - let second = &elements[1]; - - // Tell kurbo cursor to move to the first anchor - if start_new_shape { - if let Some(anchor) = &first.points[ManipulatorType::Anchor] { - bez_path.push(PathEl::MoveTo(point_to_kurbo(anchor))); - } - } - - // Create a path element from our first and second manipulator groups in the window - let (path_el, should_start_new_shape) = manipulator_groups_to_path_el(first, second); - start_new_shape = should_start_new_shape; - bez_path.push(path_el); - if should_start_new_shape && bez_path.last().filter(|&&el| el == PathEl::ClosePath).is_none() { - bez_path.push(PathEl::ClosePath) - } - } - - BezPath::from_vec(bez_path) - } -} - -impl> From for Subpath { - /// Create a Subpath from a [BezPath]. - fn from(path: T) -> Self { - let mut subpath = Subpath::new(); - let mut closed = false; - for path_el in path { - if closed { - warn!("Verbs appear after the close path in a subpath. This will probably cause crashes."); - } - match path_el { - PathEl::MoveTo(p) => { - subpath.manipulator_groups_mut().push_end(ManipulatorGroup::new_with_anchor(kurbo_point_to_dvec2(p))); - if !subpath.0.is_empty() { - warn!("A move to path element appears part way through a subpath. This will be treated as a line to verb."); - } - } - PathEl::LineTo(p) => { - subpath.manipulator_groups_mut().push_end(ManipulatorGroup::new_with_anchor(kurbo_point_to_dvec2(p))); - } - PathEl::QuadTo(p0, p1) => { - subpath.manipulator_groups_mut().push_end(ManipulatorGroup::new_with_anchor(kurbo_point_to_dvec2(p1))); - subpath.manipulator_groups_mut().last_mut().unwrap().points[ManipulatorType::InHandle] = Some(ManipulatorPoint::new(kurbo_point_to_dvec2(p0), ManipulatorType::InHandle)); - } - PathEl::CurveTo(p0, p1, p2) => { - subpath.manipulator_groups_mut().last_mut().unwrap().points[ManipulatorType::OutHandle] = Some(ManipulatorPoint::new(kurbo_point_to_dvec2(p0), ManipulatorType::OutHandle)); - subpath.manipulator_groups_mut().push_end(ManipulatorGroup::new_with_anchor(kurbo_point_to_dvec2(p2))); - subpath.manipulator_groups_mut().last_mut().unwrap().points[ManipulatorType::InHandle] = Some(ManipulatorPoint::new(kurbo_point_to_dvec2(p1), ManipulatorType::InHandle)); - } - PathEl::ClosePath => { - subpath.manipulator_groups_mut().push_end(ManipulatorGroup::closed()); - closed = true; - } - } - } - - subpath - } -} - -#[inline] -fn point_to_kurbo(point: &ManipulatorPoint) -> kurbo::Point { - kurbo::Point::new(point.position.x, point.position.y) -} - -#[inline] -fn kurbo_point_to_dvec2(point: kurbo::Point) -> DVec2 { - DVec2::new(point.x, point.y) -}