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
+3
View File
@@ -356,5 +356,8 @@ mod tests {
let bezier2 = Bezier::from_quadratic_coordinates(0., 0., 0., 100., 100., 100.);
assert_eq!(bezier2.project(DVec2::new(100., 0.)), 0.);
let bezier3 = Bezier::from_cubic_coordinates(-50.0, -50.0, -50.0, -50.0, 50.0, -50.0, 50.0, -50.0);
assert_eq!(DVec2::new(0., -50.), bezier3.evaluate(TValue::Parametric(bezier3.project(DVec2::new(0., -50.)))));
}
}
+31
View File
@@ -48,6 +48,14 @@ impl BezierHandles {
matches!(self, Self::Cubic { .. })
}
pub fn is_finite(&self) -> bool {
match self {
BezierHandles::Linear => true,
BezierHandles::Quadratic { handle } => handle.is_finite(),
BezierHandles::Cubic { handle_start, handle_end } => handle_start.is_finite() && handle_end.is_finite(),
}
}
/// Get the coordinates of the bezier segment's first handle point. This represents the only handle in a quadratic segment.
pub fn start(&self) -> Option<DVec2> {
match *self {
@@ -64,6 +72,18 @@ impl BezierHandles {
}
}
pub fn move_start(&mut self, delta: DVec2) {
if let BezierHandles::Cubic { handle_start, .. } | BezierHandles::Quadratic { handle: handle_start } = self {
*handle_start += delta
}
}
pub fn move_end(&mut self, delta: DVec2) {
if let BezierHandles::Cubic { handle_end, .. } = self {
*handle_end += delta
}
}
/// Returns a Bezier curve that results from applying the transformation function to each handle point in the Bezier.
#[must_use]
pub fn apply_transformation(&self, transformation_function: impl Fn(DVec2) -> DVec2) -> Self {
@@ -80,6 +100,17 @@ impl BezierHandles {
}
}
}
#[must_use]
pub fn flipped(self) -> Self {
match self {
BezierHandles::Cubic { handle_start, handle_end } => Self::Cubic {
handle_start: handle_end,
handle_end: handle_start,
},
_ => self,
}
}
}
#[cfg(feature = "dyn-any")]
+6 -6
View File
@@ -347,7 +347,7 @@ impl Bezier {
/// - `distance` - The offset's distance from the curve. Positive values will offset the curve in the same direction as the endpoint normals,
/// while negative values will offset in the opposite direction.
/// <iframe frameBorder="0" width="100%" height="325px" src="https://graphite.rs/libraries/bezier-rs#bezier/offset/solo" title="Offset Demo"></iframe>
pub fn offset<ManipulatorGroupId: crate::Identifier>(&self, distance: f64) -> Subpath<ManipulatorGroupId> {
pub fn offset<PointId: crate::Identifier>(&self, distance: f64) -> Subpath<PointId> {
if self.is_point() {
return Subpath::from_bezier(self);
}
@@ -375,7 +375,7 @@ impl Bezier {
/// Version of the `offset` function which scales the offset such that the start of the offset is `start_distance` from the original curve, while the end of
/// of the offset is `end_distance` from the original curve. The curve transitions from `start_distance` to `end_distance` gradually, proportional to the
/// distance along the equation (`t`-value) of the curve. Similarly to the `offset` function, the returned result is an approximation.
pub fn graduated_offset<ManipulatorGroupId: crate::Identifier>(&self, start_distance: f64, end_distance: f64) -> Subpath<ManipulatorGroupId> {
pub fn graduated_offset<PointId: crate::Identifier>(&self, start_distance: f64, end_distance: f64) -> Subpath<PointId> {
let reduced = self.reduce(None);
let mut next_start_distance = start_distance;
let distance_difference = end_distance - start_distance;
@@ -414,7 +414,7 @@ impl Bezier {
/// Outline takes the following parameter:
/// - `distance` - The outline's distance from the curve.
/// <iframe frameBorder="0" width="100%" height="350px" src="https://graphite.rs/libraries/bezier-rs#bezier/outline/solo" title="Outline Demo"></iframe>
pub fn outline<ManipulatorGroupId: crate::Identifier>(&self, distance: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
pub fn outline<PointId: crate::Identifier>(&self, distance: f64, cap: Cap) -> Subpath<PointId> {
let (pos_offset, neg_offset) = if self.is_point() {
(
Subpath::new(vec![ManipulatorGroup::new_anchor(self.start() + DVec2::NEG_Y * distance)], false),
@@ -434,13 +434,13 @@ impl Bezier {
/// Version of the `outline` function which draws the outline at the specified distances away from the curve.
/// The outline begins `start_distance` away, and gradually move to being `end_distance` away.
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/libraries/bezier-rs#bezier/graduated-outline/solo" title="Graduated Outline Demo"></iframe>
pub fn graduated_outline<ManipulatorGroupId: crate::Identifier>(&self, start_distance: f64, end_distance: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
pub fn graduated_outline<PointId: crate::Identifier>(&self, start_distance: f64, end_distance: f64, cap: Cap) -> Subpath<PointId> {
self.skewed_outline(start_distance, end_distance, end_distance, start_distance, cap)
}
/// Version of the `graduated_outline` function that allows for the 4 corners of the outline to be different distances away from the curve.
/// <iframe frameBorder="0" width="100%" height="475px" src="https://graphite.rs/libraries/bezier-rs#bezier/skewed-outline/solo" title="Skewed Outline Demo"></iframe>
pub fn skewed_outline<ManipulatorGroupId: crate::Identifier>(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64, cap: Cap) -> Subpath<ManipulatorGroupId> {
pub fn skewed_outline<PointId: crate::Identifier>(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64, cap: Cap) -> Subpath<PointId> {
let (pos_offset, neg_offset) = if self.is_point() {
(
Subpath::new(vec![ManipulatorGroup::new_anchor(self.start() + DVec2::NEG_Y * distance1)], false),
@@ -776,7 +776,7 @@ mod tests {
.all(|(curve, t_pair)| curve.abs_diff_eq(&bezier.trim(TValue::Parametric(t_pair[0]), TValue::Parametric(t_pair[1])), MAX_ABSOLUTE_DIFFERENCE)))
}
fn assert_valid_offset<ManipulatorGroupId: crate::Identifier>(bezier: &Bezier, offset: &Subpath<ManipulatorGroupId>, expected_distance: f64) {
fn assert_valid_offset<PointId: crate::Identifier>(bezier: &Bezier, offset: &Subpath<PointId>, expected_distance: f64) {
// Verify that the offset is smooth
if offset.len() > 1 {
offset.iter().take(offset.len() - 2).zip(offset.iter().skip(1)).for_each(|beziers_pair| {