Add nondestructive vector editing (#1676)

* Initial vector modify node

* Initial extraction of data from monitor nodes

* Migrate to point id

* Start converting to modify node

* Non destructive spline tool (tout le reste est cassé)

* Fix unconnected modify node

* Fix freehand tool

* Pen tool

* Migrate demo art

* Select points

* Fix the demo artwork

* Fix the X and Y inputs for path tool

* G1 continous toggle

* Delete points

* Fix test

* Insert point

* Improve robustness of handles

* Fix GRS shortcuts on path

* Dragging points

* Fix build

* Preserve opposing handle lengths

* Update demo art and snapping

* Fix polygon tool

* Double click end anchor

* Improve dragging

* Fix text shifting

* Select only connected verts

* Colinear alt

* Cleanup

* Fix imports

* Improve pen tool avoiding handle placement

* Improve disolve

* Remove pivot widget from Transform node properties

* Fix demo art

* Fix bugs

* Re-save demo artwork

* Code review

* Serialize hashmap as tuple vec to enable deserialize_inputs

* Fix migrate

* Add document upgrade function to editor_api.rs

* Finalize document upgrading

* Rename to the Path node

* Remove smoothing from Freehand tool

* Upgrade demo artwork

* Propertly disable raw-rs tests

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
Co-authored-by: Adam <adamgerhant@gmail.com>
Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
James Lindsay
2024-07-05 13:42:40 -07:00
committed by GitHub
co-authored by Keavon Chambers Adam Dennis Kobert
parent fd3613018a
commit 1652c713a6
96 changed files with 3343 additions and 2622 deletions
+18 -16
View File
@@ -5,11 +5,11 @@ use glam::DVec2;
use std::fmt::Write;
/// Functionality relating to core `Subpath` operations, such as constructors and `iter`.
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Subpath<PointId> {
/// Create a new `Subpath` using a list of [ManipulatorGroup]s.
/// A `Subpath` with less than 2 [ManipulatorGroup]s may not be closed.
#[track_caller]
pub fn new(manipulator_groups: Vec<ManipulatorGroup<ManipulatorGroupId>>, closed: bool) -> Self {
pub fn new(manipulator_groups: Vec<ManipulatorGroup<PointId>>, closed: bool) -> Self {
assert!(!closed || manipulator_groups.len() > 1, "A closed Subpath must contain more than 1 ManipulatorGroup.");
Self { manipulator_groups, closed }
}
@@ -22,13 +22,13 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: bezier.start(),
in_handle: None,
out_handle: bezier.handle_start(),
id: ManipulatorGroupId::new(),
id: PointId::new(),
},
ManipulatorGroup {
anchor: bezier.end(),
in_handle: bezier.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
},
],
false,
@@ -48,17 +48,17 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: first.start(),
in_handle: None,
out_handle: first.handle_start(),
id: ManipulatorGroupId::new(),
id: PointId::new(),
}];
let mut inner_groups: Vec<ManipulatorGroup<ManipulatorGroupId>> = beziers
let mut inner_groups: Vec<ManipulatorGroup<PointId>> = 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(),
id: PointId::new(),
})
.collect::<Vec<ManipulatorGroup<ManipulatorGroupId>>>();
.collect::<Vec<ManipulatorGroup<PointId>>>();
manipulator_groups.append(&mut inner_groups);
let last = beziers.last().unwrap();
@@ -67,7 +67,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: last.end(),
in_handle: last.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
});
return Subpath::new(manipulator_groups, false);
}
@@ -104,7 +104,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
}
/// Returns an iterator of the [Bezier]s along the `Subpath`.
pub fn iter(&self) -> SubpathIter<ManipulatorGroupId> {
pub fn iter(&self) -> SubpathIter<PointId> {
SubpathIter {
subpath: self,
index: 0,
@@ -113,7 +113,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
}
/// Returns an iterator of the [Bezier]s along the `Subpath` always considering it as a closed subpath.
pub fn iter_closed(&self) -> SubpathIter<ManipulatorGroupId> {
pub fn iter_closed(&self) -> SubpathIter<PointId> {
SubpathIter {
subpath: self,
index: 0,
@@ -122,12 +122,12 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
}
/// Returns a slice of the [ManipulatorGroup]s in the `Subpath`.
pub fn manipulator_groups(&self) -> &[ManipulatorGroup<ManipulatorGroupId>] {
pub fn manipulator_groups(&self) -> &[ManipulatorGroup<PointId>] {
&self.manipulator_groups
}
/// Returns a mutable reference to the [ManipulatorGroup]s in the `Subpath`.
pub fn manipulator_groups_mut(&mut self) -> &mut Vec<ManipulatorGroup<ManipulatorGroupId>> {
pub fn manipulator_groups_mut(&mut self) -> &mut Vec<ManipulatorGroup<PointId>> {
&mut self.manipulator_groups
}
@@ -232,9 +232,13 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// Constructs a rounded rectangle with `corner1` and `corner2` as the two corners and `corner_radii` as the radii of the corners: `[top_left, top_right, bottom_right, bottom_left]`.
pub fn new_rounded_rect(corner1: DVec2, corner2: DVec2, corner_radii: [f64; 4]) -> Self {
if corner_radii.iter().all(|radii| radii.abs() < f64::EPSILON * 100.) {
return Self::new_rect(corner1, corner2);
}
use std::f64::consts::{FRAC_1_SQRT_2, PI};
let new_arc = |center: DVec2, corner: DVec2, radius: f64| -> Vec<ManipulatorGroup<ManipulatorGroupId>> {
let new_arc = |center: DVec2, corner: DVec2, radius: f64| -> Vec<ManipulatorGroup<PointId>> {
let point1 = center + DVec2::from_angle(-PI * 0.25).rotate(corner - center) * FRAC_1_SQRT_2;
let point2 = center + DVec2::from_angle(PI * 0.25).rotate(corner - center) * FRAC_1_SQRT_2;
if radius == 0. {
@@ -245,10 +249,8 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
const HANDLE_OFFSET_FACTOR: f64 = 0.551784777779014;
let handle_offset = radius * HANDLE_OFFSET_FACTOR;
vec![
ManipulatorGroup::new_anchor(point1),
ManipulatorGroup::new(point1, None, Some(point1 + handle_offset * (corner - point1).normalize())),
ManipulatorGroup::new(point2, Some(point2 + handle_offset * (corner - point2).normalize()), None),
ManipulatorGroup::new_anchor(point2),
]
};
Self::new(
+1 -1
View File
@@ -4,7 +4,7 @@ use crate::utils::{SubpathTValue, TValue, TValueType};
use glam::DVec2;
/// Functionality relating to looking up properties of the `Subpath` or points along the `Subpath`.
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Subpath<PointId> {
/// Return a selection of equidistant points on the bezier curve.
/// If no value is provided for `steps`, then the function will default `steps` to be 10.
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/libraries/bezier-rs#subpath/lookup-table/solo" title="Lookup-Table Demo"></iframe>
+14 -14
View File
@@ -3,7 +3,7 @@ use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
use crate::utils::f64_compare;
use crate::{SubpathTValue, TValue};
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Subpath<PointId> {
/// Get whether the subpath is closed.
pub fn closed(&self) -> bool {
self.closed
@@ -14,40 +14,40 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
self.closed = new_closed;
}
/// Access a [ManipulatorGroup] from a ManipulatorGroupId.
pub fn manipulator_from_id(&self, id: ManipulatorGroupId) -> Option<&ManipulatorGroup<ManipulatorGroupId>> {
/// Access a [ManipulatorGroup] from a PointId.
pub fn manipulator_from_id(&self, id: PointId) -> Option<&ManipulatorGroup<PointId>> {
self.manipulator_groups.iter().find(|manipulator_group| manipulator_group.id == id)
}
/// Access a mutable [ManipulatorGroup] from a ManipulatorGroupId.
pub fn manipulator_mut_from_id(&mut self, id: ManipulatorGroupId) -> Option<&mut ManipulatorGroup<ManipulatorGroupId>> {
/// Access a mutable [ManipulatorGroup] from a PointId.
pub fn manipulator_mut_from_id(&mut self, id: PointId) -> Option<&mut ManipulatorGroup<PointId>> {
self.manipulator_groups.iter_mut().find(|manipulator_group| manipulator_group.id == id)
}
/// Access the index of a [ManipulatorGroup] from a ManipulatorGroupId.
pub fn manipulator_index_from_id(&self, id: ManipulatorGroupId) -> Option<usize> {
/// Access the index of a [ManipulatorGroup] from a PointId.
pub fn manipulator_index_from_id(&self, id: PointId) -> Option<usize> {
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id)
}
/// Insert a manipulator group at an index.
pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup<ManipulatorGroupId>) {
pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup<PointId>) {
assert!(group.is_finite(), "Inserting non finite manipulator group");
self.manipulator_groups.insert(index, group)
}
/// Push a manipulator group to the end.
pub fn push_manipulator_group(&mut self, group: ManipulatorGroup<ManipulatorGroupId>) {
pub fn push_manipulator_group(&mut self, group: ManipulatorGroup<PointId>) {
assert!(group.is_finite(), "Pushing non finite manipulator group");
self.manipulator_groups.push(group)
}
/// Get a mutable reference to the last manipulator
pub fn last_manipulator_group_mut(&mut self) -> Option<&mut ManipulatorGroup<ManipulatorGroupId>> {
pub fn last_manipulator_group_mut(&mut self) -> Option<&mut ManipulatorGroup<PointId>> {
self.manipulator_groups.last_mut()
}
/// Remove a manipulator group at an index.
pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup<ManipulatorGroupId> {
pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup<PointId> {
self.manipulator_groups.remove(index)
}
@@ -69,7 +69,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: first.end(),
in_handle: first.handle_end(),
out_handle: second.handle_start(),
id: ManipulatorGroupId::new(),
id: PointId::new(),
};
let number_of_groups = self.manipulator_groups.len() + 1;
self.manipulator_groups.insert((segment_index) + 1, new_group);
@@ -89,7 +89,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: bezier.start(),
in_handle: None,
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
}];
}
let mut last_index = self.manipulator_groups.len() - 1;
@@ -114,7 +114,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: bezier.end(),
in_handle: bezier.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
});
}
}
+11 -11
View File
@@ -15,25 +15,25 @@ use std::ops::{Index, IndexMut};
/// Structure used to represent a path composed of [Bezier] curves.
#[derive(Clone, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Subpath<ManipulatorGroupId: crate::Identifier> {
manipulator_groups: Vec<ManipulatorGroup<ManipulatorGroupId>>,
pub struct Subpath<PointId: crate::Identifier> {
manipulator_groups: Vec<ManipulatorGroup<PointId>>,
pub closed: bool,
}
#[cfg(feature = "dyn-any")]
unsafe impl<ManipulatorGroupId: crate::Identifier> dyn_any::StaticType for Subpath<ManipulatorGroupId> {
type Static = Subpath<ManipulatorGroupId>;
unsafe impl<PointId: crate::Identifier> dyn_any::StaticType for Subpath<PointId> {
type Static = Subpath<PointId>;
}
/// Iteration structure for iterating across each curve of a `Subpath`, using an intermediate `Bezier` representation.
pub struct SubpathIter<'a, ManipulatorGroupId: crate::Identifier> {
pub struct SubpathIter<'a, PointId: crate::Identifier> {
index: usize,
subpath: &'a Subpath<ManipulatorGroupId>,
subpath: &'a Subpath<PointId>,
is_always_closed: bool,
}
impl<ManipulatorGroupId: crate::Identifier> Index<usize> for Subpath<ManipulatorGroupId> {
type Output = ManipulatorGroup<ManipulatorGroupId>;
impl<PointId: crate::Identifier> Index<usize> for Subpath<PointId> {
type Output = ManipulatorGroup<PointId>;
fn index(&self, index: usize) -> &Self::Output {
assert!(index < self.len(), "Index out of bounds in trait Index of SubPath.");
@@ -41,14 +41,14 @@ impl<ManipulatorGroupId: crate::Identifier> Index<usize> for Subpath<Manipulator
}
}
impl<ManipulatorGroupId: crate::Identifier> IndexMut<usize> for Subpath<ManipulatorGroupId> {
impl<PointId: crate::Identifier> IndexMut<usize> for Subpath<PointId> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
assert!(index < self.len(), "Index out of bounds in trait IndexMut of SubPath.");
&mut self.manipulator_groups[index]
}
}
impl<ManipulatorGroupId: crate::Identifier> Iterator for SubpathIter<'_, ManipulatorGroupId> {
impl<PointId: crate::Identifier> Iterator for SubpathIter<'_, PointId> {
type Item = Bezier;
// Returns the Bezier representation of each `Subpath` segment, defined between a pair of adjacent manipulator points.
@@ -73,7 +73,7 @@ impl<ManipulatorGroupId: crate::Identifier> Iterator for SubpathIter<'_, Manipul
}
}
impl<ManipulatorGroupId: crate::Identifier> Debug for Subpath<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Debug for Subpath<PointId> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("Subpath").field("closed", &self.closed).field("manipulator_groups", &self.manipulator_groups).finish()
}
+19 -7
View File
@@ -6,7 +6,7 @@ use crate::TValue;
use glam::{DAffine2, DMat2, DVec2};
use std::f64::consts::PI;
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Subpath<PointId> {
/// Calculate the point on the subpath based on the parametric `t`-value provided.
/// Expects `t` to be within the inclusive range `[0, 1]`.
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/libraries/bezier-rs#subpath/evaluate/solo" title="Evaluate Demo"></iframe>
@@ -39,7 +39,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// This function expects the following:
/// - other: a [Bezier] curve to check intersections against
/// - error: an optional f64 value to provide an error bound
pub fn subpath_intersections(&self, other: &Subpath<ManipulatorGroupId>, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
pub fn subpath_intersections(&self, other: &Subpath<PointId>, error: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
let mut intersection_t_values: Vec<(usize, f64)> = other.iter().flat_map(|bezier| self.intersections(&bezier, error, minimum_separation)).collect();
intersection_t_values.sort_by(|a, b| a.partial_cmp(b).unwrap());
intersection_t_values
@@ -302,6 +302,18 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
self.iter().map(|bezier| bezier.winding(target_point)).sum::<i32>() != 0
}
/// Does a path contain a point? Based on the non zero winding. Automatically adds a linear segment if the subpath is not closed.
pub fn contains_point_autoclose(&self, target_point: DVec2) -> bool {
let mut winding = self.iter().map(|bezier| bezier.winding(target_point)).sum::<i32>();
if !self.closed {
if let [Some(first), Some(last)] = [self.manipulator_groups.first(), self.manipulator_groups.last()] {
winding += Bezier::from_linear_dvec2(first.anchor, last.anchor).winding(target_point);
}
}
winding != 0
}
/// Randomly places points across the filled surface of this subpath (which is assumed to be closed).
/// The `separation_disk_diameter` determines the minimum distance between all points from one another.
/// Conceptually, this works by "throwing a dart" at the subpath's bounding box and keeping the dart only if:
@@ -342,7 +354,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// Alternatively, this can be interpreted as limiting the angle that the miter can form.
/// When the limit is exceeded, no manipulator group will be returned.
/// This value should be at least 1. If not, the default of 4 will be used.
pub(crate) fn miter_line_join(&self, other: &Subpath<ManipulatorGroupId>, miter_limit: Option<f64>) -> Option<ManipulatorGroup<ManipulatorGroupId>> {
pub(crate) fn miter_line_join(&self, other: &Subpath<PointId>, miter_limit: Option<f64>) -> Option<ManipulatorGroup<PointId>> {
let miter_limit = match miter_limit {
Some(miter_limit) if miter_limit >= 1. => miter_limit,
_ => 4.,
@@ -371,7 +383,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: intersection,
in_handle: None,
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
});
}
}
@@ -384,7 +396,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// - The `out_handle` for the last manipulator group of `self`
/// - The new manipulator group to be added
/// - The `in_handle` for the first manipulator group of `other`
pub(crate) fn round_line_join(&self, other: &Subpath<ManipulatorGroupId>, center: DVec2) -> (DVec2, ManipulatorGroup<ManipulatorGroupId>, DVec2) {
pub(crate) fn round_line_join(&self, other: &Subpath<PointId>, center: DVec2) -> (DVec2, ManipulatorGroup<PointId>, DVec2) {
let left = self.manipulator_groups[self.len() - 1].anchor;
let right = other.manipulator_groups[0].anchor;
@@ -410,7 +422,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// - The `out_handle` for the last manipulator group of `self`
/// - The new manipulator group to be added
/// - The `in_handle` for the first manipulator group of `other`
pub(crate) fn round_cap(&self, other: &Subpath<ManipulatorGroupId>) -> (DVec2, ManipulatorGroup<ManipulatorGroupId>, DVec2) {
pub(crate) fn round_cap(&self, other: &Subpath<PointId>) -> (DVec2, ManipulatorGroup<PointId>, DVec2) {
let left = self.manipulator_groups[self.len() - 1].anchor;
let right = other.manipulator_groups[0].anchor;
@@ -423,7 +435,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
}
/// Returns the two manipulator groups that create a square cap between the end of `self` and the beginning of `other`.
pub(crate) fn square_cap(&self, other: &Subpath<ManipulatorGroupId>) -> [ManipulatorGroup<ManipulatorGroupId>; 2] {
pub(crate) fn square_cap(&self, other: &Subpath<PointId>) -> [ManipulatorGroup<PointId>; 2] {
let left = self.manipulator_groups[self.len() - 1].anchor;
let right = other.manipulator_groups[0].anchor;
+11 -11
View File
@@ -26,15 +26,15 @@ impl Identifier for EmptyId {
/// Structure used to represent a single anchor with up to two optional associated handles along a `Subpath`
#[derive(Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ManipulatorGroup<ManipulatorGroupId: crate::Identifier> {
pub struct ManipulatorGroup<PointId: crate::Identifier> {
pub anchor: DVec2,
pub in_handle: Option<DVec2>,
pub out_handle: Option<DVec2>,
pub id: ManipulatorGroupId,
pub id: PointId,
}
// TODO: Remove once we no longer need to hash floats in Graphite
impl<ManipulatorGroupId: crate::Identifier> Hash for ManipulatorGroup<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Hash for ManipulatorGroup<PointId> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.anchor.to_array().iter().for_each(|x| x.to_bits().hash(state));
self.in_handle.is_some().hash(state);
@@ -50,11 +50,11 @@ impl<ManipulatorGroupId: crate::Identifier> Hash for ManipulatorGroup<Manipulato
}
#[cfg(feature = "dyn-any")]
unsafe impl<ManipulatorGroupId: crate::Identifier> dyn_any::StaticType for ManipulatorGroup<ManipulatorGroupId> {
type Static = ManipulatorGroup<ManipulatorGroupId>;
unsafe impl<PointId: crate::Identifier> dyn_any::StaticType for ManipulatorGroup<PointId> {
type Static = ManipulatorGroup<PointId>;
}
impl<ManipulatorGroupId: crate::Identifier> Debug for ManipulatorGroup<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Debug for ManipulatorGroup<PointId> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("ManipulatorGroup")
.field("anchor", &self.anchor)
@@ -64,10 +64,10 @@ impl<ManipulatorGroupId: crate::Identifier> Debug for ManipulatorGroup<Manipulat
}
}
impl<ManipulatorGroupId: crate::Identifier> ManipulatorGroup<ManipulatorGroupId> {
impl<PointId: crate::Identifier> ManipulatorGroup<PointId> {
/// Construct a new manipulator group from an anchor, in handle and out handle
pub fn new(anchor: DVec2, in_handle: Option<DVec2>, out_handle: Option<DVec2>) -> Self {
let id = ManipulatorGroupId::new();
let id = PointId::new();
Self { anchor, in_handle, out_handle, id }
}
@@ -77,17 +77,17 @@ impl<ManipulatorGroupId: crate::Identifier> ManipulatorGroup<ManipulatorGroupId>
}
/// Construct a new manipulator group from an anchor, in handle, out handle and an id
pub fn new_with_id(anchor: DVec2, in_handle: Option<DVec2>, out_handle: Option<DVec2>, id: ManipulatorGroupId) -> Self {
pub fn new_with_id(anchor: DVec2, in_handle: Option<DVec2>, out_handle: Option<DVec2>, id: PointId) -> Self {
Self { anchor, in_handle, out_handle, id }
}
/// Construct a new manipulator point with just an anchor position and an id
pub fn new_anchor_with_id(anchor: DVec2, id: ManipulatorGroupId) -> Self {
pub fn new_anchor_with_id(anchor: DVec2, id: PointId) -> Self {
Self::new_with_id(anchor, Some(anchor), Some(anchor), id)
}
/// Create a bezier curve that starts at the current manipulator group and finishes in the `end_group` manipulator group.
pub fn to_bezier(&self, end_group: &ManipulatorGroup<ManipulatorGroupId>) -> Bezier {
pub fn to_bezier(&self, end_group: &ManipulatorGroup<PointId>) -> Bezier {
let start = self.anchor;
let end = end_group.anchor;
let out_handle = self.out_handle;
+21 -25
View File
@@ -18,12 +18,12 @@ fn map_index_within_range(index: usize, t: f64, max_size: usize) -> (usize, f64)
}
/// Functionality that transforms Subpaths, such as split, reduce, offset, etc.
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
impl<PointId: crate::Identifier> Subpath<PointId> {
/// Returns either one or two Subpaths that result from splitting the original Subpath at the point corresponding to `t`.
/// If the original Subpath was closed, a single open Subpath will be returned.
/// If the original Subpath was open, two open Subpaths will be returned.
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/libraries/bezier-rs#subpath/split/solo" title="Split Demo"></iframe>
pub fn split(&self, t: SubpathTValue) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
pub fn split(&self, t: SubpathTValue) -> (Subpath<PointId>, Option<Subpath<PointId>>) {
let (segment_index, t) = self.t_value_to_parametric(t);
let curve = self.get_segment(segment_index).unwrap();
@@ -48,7 +48,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: first_bezier.end(),
in_handle: last_curve.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
});
} else {
if !first_split.is_empty() {
@@ -68,7 +68,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: first_bezier.end(),
in_handle: first_bezier.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
});
}
@@ -79,7 +79,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: second_bezier.start(),
in_handle: None,
out_handle: second_bezier.handle_start(),
id: ManipulatorGroupId::new(),
id: PointId::new(),
},
);
}
@@ -95,7 +95,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
}
/// Returns [ManipulatorGroup]s with a reversed winding order.
fn reverse_manipulator_groups(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>]) -> Vec<ManipulatorGroup<ManipulatorGroupId>> {
fn reverse_manipulator_groups(manipulator_groups: &[ManipulatorGroup<PointId>]) -> Vec<ManipulatorGroup<PointId>> {
manipulator_groups
.iter()
.rev()
@@ -103,14 +103,14 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: group.anchor,
in_handle: group.out_handle,
out_handle: group.in_handle,
id: ManipulatorGroupId::new(),
id: PointId::new(),
})
.collect::<Vec<ManipulatorGroup<ManipulatorGroupId>>>()
.collect::<Vec<ManipulatorGroup<PointId>>>()
}
/// Returns a [Subpath] with a reversed winding order.
/// Note that a reversed closed subpath will start on the same manipulator group and simply wind the other direction
pub fn reverse(&self) -> Subpath<ManipulatorGroupId> {
pub fn reverse(&self) -> Subpath<PointId> {
let mut reversed = Subpath::reverse_manipulator_groups(self.manipulator_groups());
if self.closed {
reversed.rotate_right(1);
@@ -127,7 +127,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// That means, if the value of `t1` > `t2`, it will cross the break between endpoints from `t1` to `t = 1 = 0` to `t2`.
/// If a path winding in the reverse direction is desired, call `trim` on the `Subpath` returned from `Subpath::reverse`.
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/libraries/bezier-rs#subpath/trim/solo" title="Trim Demo"></iframe>
pub fn trim(&self, t1: SubpathTValue, t2: SubpathTValue) -> Subpath<ManipulatorGroupId> {
pub fn trim(&self, t1: SubpathTValue, t2: SubpathTValue) -> Subpath<PointId> {
// Return a clone of the Subpath if it is not long enough to be a valid Bezier
if self.manipulator_groups.is_empty() {
return Subpath {
@@ -196,7 +196,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
cloned_manipulator_groups
.drain(range_start..range_end.min(cloned_manipulator_groups.len()))
.collect::<Vec<ManipulatorGroup<ManipulatorGroupId>>>()
.collect::<Vec<ManipulatorGroup<PointId>>>()
};
// Adjust curve indices to match the cloned list
@@ -255,7 +255,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: front_split.start(),
in_handle: None,
out_handle: front_split.handle_start(),
id: ManipulatorGroupId::new(),
id: PointId::new(),
};
// Update the last two manipulator groups to match the back_split
@@ -264,7 +264,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
anchor: back_split.end(),
in_handle: back_split.handle_end(),
out_handle: None,
id: ManipulatorGroupId::new(),
id: PointId::new(),
};
Subpath {
@@ -313,7 +313,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
// at the incorrect location. This can be avoided by first trimming the two Subpaths at any extrema, effectively ignoring loopbacks.
/// Helper function to clip overlap of two intersecting open Subpaths. Returns an optional, as intersections may not exist for certain arrangements and distances.
/// Assumes that the Subpaths represents simple Bezier segments, and clips the Subpaths at the last intersection of the first Subpath, and first intersection of the last Subpath.
fn clip_simple_subpaths(subpath1: &Subpath<ManipulatorGroupId>, subpath2: &Subpath<ManipulatorGroupId>) -> Option<(Subpath<ManipulatorGroupId>, Subpath<ManipulatorGroupId>)> {
fn clip_simple_subpaths(subpath1: &Subpath<PointId>, subpath2: &Subpath<PointId>) -> Option<(Subpath<PointId>, Subpath<PointId>)> {
// Split the first subpath at its last intersection
let intersections1 = subpath1.subpath_intersections(subpath2, None, None);
if intersections1.is_empty() {
@@ -335,7 +335,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// Returns a subpath that results from rotating this subpath around the origin by the given angle (in radians).
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/libraries/bezier-rs#subpath/rotate/solo" title="Rotate Demo"></iframe>
pub fn rotate(&self, angle: f64) -> Subpath<ManipulatorGroupId> {
pub fn rotate(&self, angle: f64) -> Subpath<PointId> {
let mut rotated_subpath = self.clone();
let affine_transform: DAffine2 = DAffine2::from_angle(angle);
@@ -345,7 +345,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
}
/// Returns a subpath that results from rotating this subpath around the provided point by the given angle (in radians).
pub fn rotate_about_point(&self, angle: f64, pivot: DVec2) -> Subpath<ManipulatorGroupId> {
pub fn rotate_about_point(&self, angle: f64, pivot: DVec2) -> Subpath<PointId> {
// Translate before and after the rotation to account for the pivot
let translate: DAffine2 = DAffine2::from_translation(pivot);
let rotate: DAffine2 = DAffine2::from_angle(angle);
@@ -359,7 +359,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// Reduces the segments of the subpath into simple subcurves, then scales each subcurve a set `distance` away.
/// The intersections of segments of the subpath are joined using the method specified by the `join` argument.
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/libraries/bezier-rs#subpath/offset/solo" title="Offset Demo"></iframe>
pub fn offset(&self, distance: f64, join: Join) -> Subpath<ManipulatorGroupId> {
pub fn offset(&self, distance: f64, join: Join) -> Subpath<PointId> {
assert!(self.len_segments() > 1, "Cannot offset an empty Subpath.");
// An offset at a distance 0 from the curve is simply the same curve
@@ -368,11 +368,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
return self.clone();
}
let mut subpaths = self
.iter()
.filter(|bezier| !bezier.is_point())
.map(|bezier| bezier.offset(distance))
.collect::<Vec<Subpath<ManipulatorGroupId>>>();
let mut subpaths = self.iter().filter(|bezier| !bezier.is_point()).map(|bezier| bezier.offset(distance)).collect::<Vec<Subpath<PointId>>>();
let mut drop_common_point = vec![true; self.len()];
// Clip or join consecutive Subpaths
@@ -489,8 +485,8 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
}
/// Helper function to combine the two offsets that make up an outline.
pub(crate) fn combine_outline(&self, other: &Subpath<ManipulatorGroupId>, cap: Cap) -> Subpath<ManipulatorGroupId> {
let mut result_manipulator_groups: Vec<ManipulatorGroup<ManipulatorGroupId>> = vec![];
pub(crate) fn combine_outline(&self, other: &Subpath<PointId>, cap: Cap) -> Subpath<PointId> {
let mut result_manipulator_groups: Vec<ManipulatorGroup<PointId>> = vec![];
result_manipulator_groups.extend_from_slice(self.manipulator_groups());
match cap {
Cap::Butt => {
@@ -527,7 +523,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
/// - `distance` - The outline's distance from the curve.
/// - `join` - The join type used to cap the endpoints of open bezier curves, and join successive subpath segments.
/// <iframe frameBorder="0" width="100%" height="425px" src="https://graphite.rs/libraries/bezier-rs#subpath/outline/solo" title="Outline Demo"></iframe>
pub fn outline(&self, distance: f64, join: Join, cap: Cap) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
pub fn outline(&self, distance: f64, join: Join, cap: Cap) -> (Subpath<PointId>, Option<Subpath<PointId>>) {
let is_point = self.is_point();
let (pos_offset, neg_offset) = if is_point {
let point = self.manipulator_groups[0].anchor;