mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Bézier-rs: Add ids to manipulator groups (#1054)
* Add ids to manipulator groups * Fix tests * Rename trait from ManipulatorGroupId to Identifier * Rename EmptyManipulatorGroupId to EmptyId
This commit is contained in:
parent
7a52e50a94
commit
08b2782917
8 changed files with 93 additions and 27 deletions
|
@ -4,10 +4,10 @@ use crate::consts::*;
|
|||
use std::fmt::Write;
|
||||
|
||||
/// Functionality relating to core `Subpath` operations, such as constructors and `iter`.
|
||||
impl Subpath {
|
||||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
/// Create a new `Subpath` using a list of [ManipulatorGroup]s.
|
||||
/// A `Subpath` with less than 2 [ManipulatorGroup]s may not be closed.
|
||||
pub fn new(manipulator_groups: Vec<ManipulatorGroup>, closed: bool) -> Self {
|
||||
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 }
|
||||
}
|
||||
|
@ -20,11 +20,13 @@ impl Subpath {
|
|||
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,
|
||||
|
@ -59,7 +61,7 @@ impl Subpath {
|
|||
}
|
||||
|
||||
/// Returns an iterator of the [Bezier]s along the `Subpath`.
|
||||
pub fn iter(&self) -> SubpathIter {
|
||||
pub fn iter(&self) -> SubpathIter<ManipulatorGroupId> {
|
||||
SubpathIter { sub_path: self, index: 0 }
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::ProjectionOptions;
|
|||
use glam::DVec2;
|
||||
|
||||
/// Functionality relating to looking up properties of the `Subpath` or points along the `Subpath`.
|
||||
impl Subpath {
|
||||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
/// Return the sum of the approximation of the length of each `Bezier` curve along the `Subpath`.
|
||||
/// - `num_subdivisions` - Number of subdivisions used to approximate the curve. The default value is `1000`.
|
||||
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/bezier-rs-demos#subpath/length/solo" title="Length Demo"></iframe>
|
||||
|
@ -126,16 +126,19 @@ mod tests {
|
|||
anchor: start,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: middle,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle2),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: end,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle3),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
false,
|
||||
|
@ -165,16 +168,19 @@ mod tests {
|
|||
anchor: start,
|
||||
in_handle: Some(handle3),
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: middle,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: end,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle2),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
false,
|
||||
|
@ -191,6 +197,7 @@ mod tests {
|
|||
anchor: DVec2::new(0., 0.),
|
||||
in_handle: None,
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
};
|
||||
let open_subpath = Subpath {
|
||||
manipulator_groups: vec![mock_manipulator_group; 5],
|
||||
|
@ -212,6 +219,7 @@ mod tests {
|
|||
anchor: DVec2::new(0., 0.),
|
||||
in_handle: None,
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
};
|
||||
let closed_subpath = Subpath {
|
||||
manipulator_groups: vec![mock_manipulator_group; 5],
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::consts::MAX_ABSOLUTE_DIFFERENCE;
|
|||
use crate::utils::f64_compare;
|
||||
use crate::{SubpathTValue, TValue};
|
||||
|
||||
impl Subpath {
|
||||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
/// Inserts a `ManipulatorGroup` at a certain point along the subpath based on the parametric `t`-value provided.
|
||||
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
||||
pub fn insert(&mut self, t: SubpathTValue) {
|
||||
|
@ -22,6 +22,7 @@ impl Subpath {
|
|||
anchor: first.end(),
|
||||
in_handle: first.handle_end(),
|
||||
out_handle: second.handle_start(),
|
||||
id: ManipulatorGroupId::new(),
|
||||
};
|
||||
let number_of_groups = self.manipulator_groups.len() + 1;
|
||||
self.manipulator_groups.insert((segment_index) + 1, new_group);
|
||||
|
@ -37,7 +38,7 @@ mod tests {
|
|||
use super::*;
|
||||
use glam::DVec2;
|
||||
|
||||
fn set_up_open_subpath() -> Subpath {
|
||||
fn set_up_open_subpath() -> Subpath<EmptyId> {
|
||||
let start = DVec2::new(20., 30.);
|
||||
let middle1 = DVec2::new(80., 90.);
|
||||
let middle2 = DVec2::new(100., 100.);
|
||||
|
@ -53,28 +54,32 @@ mod tests {
|
|||
anchor: start,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: middle1,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle2),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: middle2,
|
||||
in_handle: None,
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: end,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle3),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
fn set_up_closed_subpath() -> Subpath {
|
||||
fn set_up_closed_subpath() -> Subpath<EmptyId> {
|
||||
let mut subpath = set_up_open_subpath();
|
||||
subpath.closed = true;
|
||||
subpath
|
||||
|
|
|
@ -12,19 +12,19 @@ use std::ops::{Index, IndexMut};
|
|||
|
||||
/// Structure used to represent a path composed of [Bezier] curves.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Subpath {
|
||||
manipulator_groups: Vec<ManipulatorGroup>,
|
||||
pub struct Subpath<ManipulatorGroupId: crate::Identifier> {
|
||||
manipulator_groups: Vec<ManipulatorGroup<ManipulatorGroupId>>,
|
||||
closed: bool,
|
||||
}
|
||||
|
||||
/// Iteration structure for iterating across each curve of a `Subpath`, using an intermediate `Bezier` representation.
|
||||
pub struct SubpathIter<'a> {
|
||||
pub struct SubpathIter<'a, ManipulatorGroupId: crate::Identifier> {
|
||||
index: usize,
|
||||
sub_path: &'a Subpath,
|
||||
sub_path: &'a Subpath<ManipulatorGroupId>,
|
||||
}
|
||||
|
||||
impl Index<usize> for Subpath {
|
||||
type Output = ManipulatorGroup;
|
||||
impl<ManipulatorGroupId: crate::Identifier> Index<usize> for Subpath<ManipulatorGroupId> {
|
||||
type Output = ManipulatorGroup<ManipulatorGroupId>;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
assert!(index < self.len(), "Index out of bounds in trait Index of SubPath.");
|
||||
|
@ -32,14 +32,14 @@ impl Index<usize> for Subpath {
|
|||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Subpath {
|
||||
impl<ManipulatorGroupId: crate::Identifier> IndexMut<usize> for Subpath<ManipulatorGroupId> {
|
||||
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 Iterator for SubpathIter<'_> {
|
||||
impl<ManipulatorGroupId: crate::Identifier> Iterator for SubpathIter<'_, ManipulatorGroupId> {
|
||||
type Item = Bezier;
|
||||
|
||||
// Returns the Bezier representation of each `Subpath` segment, defined between a pair of adjacent manipulator points.
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::TValue;
|
|||
|
||||
use glam::DVec2;
|
||||
|
||||
impl Subpath {
|
||||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
/// 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="400px" src="https://graphite.rs/bezier-rs-demos#subpath/evaluate/solo" title="Evaluate Demo"></iframe>
|
||||
|
@ -144,11 +144,13 @@ mod tests {
|
|||
anchor: start,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: end,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
false,
|
||||
|
@ -186,16 +188,19 @@ mod tests {
|
|||
anchor: start,
|
||||
in_handle: Some(handle3),
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: middle,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: end,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle2),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
false,
|
||||
|
@ -290,16 +295,19 @@ mod tests {
|
|||
anchor: cubic_start,
|
||||
in_handle: None,
|
||||
out_handle: Some(cubic_handle_1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: cubic_end,
|
||||
in_handle: Some(cubic_handle_2),
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: quadratic_end,
|
||||
in_handle: Some(quadratic_1_handle),
|
||||
out_handle: Some(quadratic_2_handle),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
true,
|
||||
|
@ -366,16 +374,19 @@ mod tests {
|
|||
anchor: cubic_start,
|
||||
in_handle: None,
|
||||
out_handle: Some(cubic_handle_1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: cubic_end,
|
||||
in_handle: Some(cubic_handle_2),
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: quadratic_end,
|
||||
in_handle: Some(quadratic_1_handle),
|
||||
out_handle: Some(quadratic_2_handle),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
true,
|
||||
|
@ -431,16 +442,19 @@ mod tests {
|
|||
anchor: cubic_start,
|
||||
in_handle: None,
|
||||
out_handle: Some(cubic_handle_1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: cubic_end,
|
||||
in_handle: Some(cubic_handle_2),
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: quadratic_end,
|
||||
in_handle: Some(quadratic_1_handle),
|
||||
out_handle: Some(quadratic_2_handle),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
true,
|
||||
|
|
|
@ -3,15 +3,33 @@ use super::Bezier;
|
|||
use glam::DVec2;
|
||||
use std::fmt::{Debug, Formatter, Result};
|
||||
|
||||
/// An id type used for each [ManipulatorGroup].
|
||||
pub trait Identifier: Sized + Clone + PartialEq {
|
||||
fn new() -> Self;
|
||||
}
|
||||
|
||||
/// An empty id type for use in tests
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg(test)]
|
||||
pub(crate) struct EmptyId;
|
||||
|
||||
#[cfg(test)]
|
||||
impl Identifier for EmptyId {
|
||||
fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure used to represent a single anchor with up to two optional associated handles along a `Subpath`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct ManipulatorGroup {
|
||||
pub struct ManipulatorGroup<ManipulatorGroupId: crate::Identifier> {
|
||||
pub anchor: DVec2,
|
||||
pub in_handle: Option<DVec2>,
|
||||
pub out_handle: Option<DVec2>,
|
||||
pub id: ManipulatorGroupId,
|
||||
}
|
||||
|
||||
impl Debug for ManipulatorGroup {
|
||||
impl<ManipulatorGroupId: crate::Identifier> Debug for ManipulatorGroup<ManipulatorGroupId> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
if self.in_handle.is_some() && self.out_handle.is_some() {
|
||||
write!(f, "anchor: {}, in: {}, out: {}", self.anchor, self.in_handle.unwrap(), self.out_handle.unwrap())
|
||||
|
@ -25,8 +43,8 @@ impl Debug for ManipulatorGroup {
|
|||
}
|
||||
}
|
||||
|
||||
impl ManipulatorGroup {
|
||||
pub fn to_bezier(&self, end_group: &ManipulatorGroup) -> Bezier {
|
||||
impl<ManipulatorGroupId: crate::Identifier> ManipulatorGroup<ManipulatorGroupId> {
|
||||
pub fn to_bezier(&self, end_group: &ManipulatorGroup<ManipulatorGroupId>) -> Bezier {
|
||||
let start = self.anchor;
|
||||
let end = end_group.anchor;
|
||||
let out_handle = self.out_handle;
|
||||
|
|
|
@ -3,12 +3,12 @@ use crate::utils::SubpathTValue;
|
|||
use crate::utils::TValue;
|
||||
|
||||
/// Functionality that transforms Subpaths, such as split, reduce, offset, etc.
|
||||
impl Subpath {
|
||||
impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
/// 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="400px" src="https://graphite.rs/bezier-rs-demos#subpath/split/solo" title="Split Demo"></iframe>
|
||||
pub fn split(&self, t: SubpathTValue) -> (Subpath, Option<Subpath>) {
|
||||
pub fn split(&self, t: SubpathTValue) -> (Subpath<ManipulatorGroupId>, Option<Subpath<ManipulatorGroupId>>) {
|
||||
let (segment_index, t) = self.t_value_to_parametric(t);
|
||||
let curve = self.get_segment(segment_index).unwrap();
|
||||
|
||||
|
@ -33,6 +33,7 @@ impl Subpath {
|
|||
anchor: first_bezier.end(),
|
||||
in_handle: last_curve.handle_end(),
|
||||
out_handle: None,
|
||||
id: ManipulatorGroupId::new(),
|
||||
});
|
||||
} else {
|
||||
if !first_split.is_empty() {
|
||||
|
@ -52,6 +53,7 @@ impl Subpath {
|
|||
anchor: first_bezier.end(),
|
||||
in_handle: first_bezier.handle_end(),
|
||||
out_handle: None,
|
||||
id: ManipulatorGroupId::new(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -62,6 +64,7 @@ impl Subpath {
|
|||
anchor: second_bezier.start(),
|
||||
in_handle: None,
|
||||
out_handle: second_bezier.handle_start(),
|
||||
id: ManipulatorGroupId::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -84,7 +87,7 @@ mod tests {
|
|||
use super::*;
|
||||
use glam::DVec2;
|
||||
|
||||
fn set_up_open_subpath() -> Subpath {
|
||||
fn set_up_open_subpath() -> Subpath<EmptyId> {
|
||||
let start = DVec2::new(20., 30.);
|
||||
let middle1 = DVec2::new(80., 90.);
|
||||
let middle2 = DVec2::new(100., 100.);
|
||||
|
@ -100,28 +103,32 @@ mod tests {
|
|||
anchor: start,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle1),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: middle1,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle2),
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: middle2,
|
||||
in_handle: None,
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
},
|
||||
ManipulatorGroup {
|
||||
anchor: end,
|
||||
in_handle: None,
|
||||
out_handle: Some(handle3),
|
||||
id: EmptyId,
|
||||
},
|
||||
],
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
fn set_up_closed_subpath() -> Subpath {
|
||||
fn set_up_closed_subpath() -> Subpath<EmptyId> {
|
||||
let mut subpath = set_up_open_subpath();
|
||||
subpath.closed = true;
|
||||
subpath
|
||||
|
@ -154,7 +161,8 @@ mod tests {
|
|||
ManipulatorGroup {
|
||||
anchor: location,
|
||||
in_handle: None,
|
||||
out_handle: None
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
}
|
||||
);
|
||||
assert_eq!(first.manipulator_groups.len(), 1);
|
||||
|
@ -177,7 +185,8 @@ mod tests {
|
|||
ManipulatorGroup {
|
||||
anchor: location,
|
||||
in_handle: None,
|
||||
out_handle: None
|
||||
out_handle: None,
|
||||
id: EmptyId,
|
||||
}
|
||||
);
|
||||
assert_eq!(second.manipulator_groups.len(), 1);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue