Skip to content

Commit 269c572

Browse files
indierustyKeavon
andauthored
Refactor the 'Morph' node to use Kurbo instead of Bezier-rs (#2696)
* fix morph node * it worked! * cleanup * add comments --------- Co-authored-by: Keavon Chambers <[email protected]>
1 parent e6a7325 commit 269c572

File tree

1 file changed

+121
-47
lines changed

1 file changed

+121
-47
lines changed

node-graph/gcore/src/vector/vector_nodes.rs

Lines changed: 121 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ use crate::vector::PointDomain;
1212
use crate::vector::misc::dvec2_to_point;
1313
use crate::vector::style::{LineCap, LineJoin};
1414
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicElement, GraphicGroupTable, OwnedContextImpl};
15-
use bezier_rs::{Join, ManipulatorGroup, Subpath, SubpathTValue};
15+
use bezier_rs::{Join, ManipulatorGroup, Subpath};
1616
use core::f64::consts::PI;
1717
use core::hash::{Hash, Hasher};
1818
use glam::{DAffine2, DVec2};
19-
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, Shape};
19+
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, ParamCurve, PathEl, PathSeg, Point, Shape};
2020
use rand::{Rng, SeedableRng};
2121
use std::collections::hash_map::DefaultHasher;
2222
use std::f64::consts::TAU;
@@ -1526,6 +1526,46 @@ async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)]
15261526

15271527
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
15281528
async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDataTable, #[default(0.5)] time: Fraction) -> VectorDataTable {
1529+
/// Subdivides the last segment of the bezpath to until it appends 'count' number of segments.
1530+
fn make_new_segments(bezpath: &mut BezPath, count: usize) {
1531+
let bezpath_segment_count = bezpath.segments().count();
1532+
1533+
if count == 0 || bezpath_segment_count == 0 {
1534+
return;
1535+
}
1536+
1537+
// Initially push the last segment of the bezpath
1538+
let mut new_segments = vec![bezpath.get_seg(bezpath_segment_count).unwrap()];
1539+
1540+
// Generate new segments by subdividing last segment
1541+
for _ in 0..count {
1542+
let last = new_segments.pop().unwrap();
1543+
let (first, second) = last.subdivide();
1544+
new_segments.push(first);
1545+
new_segments.push(second);
1546+
}
1547+
1548+
// Append the new segments.
1549+
if count != 0 {
1550+
// Remove the last segment as it is already appended to the new_segments.
1551+
let mut is_closed = false;
1552+
if let Some(last_element) = bezpath.pop() {
1553+
if last_element == PathEl::ClosePath {
1554+
is_closed = true;
1555+
_ = bezpath.pop();
1556+
}
1557+
}
1558+
1559+
for segment in new_segments {
1560+
bezpath.push(segment.as_path_el());
1561+
}
1562+
1563+
if is_closed {
1564+
bezpath.close_path();
1565+
}
1566+
}
1567+
}
1568+
15291569
let time = time.clamp(0., 1.);
15301570

15311571
let mut result_table = VectorDataTable::default();
@@ -1542,65 +1582,99 @@ async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDat
15421582
let target_transform = target_instance.transform;
15431583

15441584
// Before and after paths
1545-
let source_paths = source_instance.instance.stroke_bezier_paths();
1546-
let target_paths = target_instance.instance.stroke_bezier_paths();
1547-
for (mut source_path, mut target_path) in source_paths.zip(target_paths) {
1548-
source_path.apply_transform(source_transform);
1549-
target_path.apply_transform(target_transform);
1550-
1551-
// Align point counts by inserting mid‐segment points until their counts match
1552-
while source_path.manipulator_groups().len() < target_path.manipulator_groups().len() {
1553-
let last = source_path.len() - 1;
1554-
source_path.insert(SubpathTValue::Parametric { segment_index: last, t: 0.5 });
1555-
}
1556-
while target_path.manipulator_groups().len() < source_path.manipulator_groups().len() {
1557-
let last = target_path.len() - 1;
1558-
target_path.insert(SubpathTValue::Parametric { segment_index: last, t: 0.5 });
1585+
let source_bezpaths = source_instance.instance.stroke_bezpath_iter();
1586+
let target_bezpaths = target_instance.instance.stroke_bezpath_iter();
1587+
1588+
for (mut source_bezpath, mut target_bezpath) in source_bezpaths.zip(target_bezpaths) {
1589+
if source_bezpath.elements().is_empty() || target_bezpath.elements().is_empty() {
1590+
continue;
15591591
}
15601592

1593+
source_bezpath.apply_affine(Affine::new(source_transform.to_cols_array()));
1594+
target_bezpath.apply_affine(Affine::new(target_transform.to_cols_array()));
1595+
1596+
let target_segment_len = target_bezpath.segments().count();
1597+
let source_segment_len = source_bezpath.segments().count();
1598+
1599+
// Insert new segments to align the number of segments in sorce_bezpath and target_bezpath.
1600+
make_new_segments(&mut source_bezpath, target_segment_len.max(source_segment_len) - source_segment_len);
1601+
make_new_segments(&mut target_bezpath, source_segment_len.max(target_segment_len) - target_segment_len);
1602+
1603+
let source_segments = source_bezpath.segments().collect::<Vec<PathSeg>>();
1604+
let target_segments = target_bezpath.segments().collect::<Vec<PathSeg>>();
1605+
15611606
// Interpolate anchors and handles
1562-
for (source_manipulators, target_manipulators) in source_path.manipulator_groups_mut().iter_mut().zip(target_path.manipulator_groups()) {
1563-
let source_anchor = source_manipulators.anchor;
1564-
let target_anchor = target_manipulators.anchor;
1565-
source_manipulators.anchor = source_anchor.lerp(target_anchor, time);
1566-
1567-
let source_in_handle = source_manipulators.in_handle.unwrap_or(source_anchor);
1568-
let target_in_handle = target_manipulators.in_handle.unwrap_or(target_anchor);
1569-
source_manipulators.in_handle = Some(source_in_handle.lerp(target_in_handle, time));
1570-
1571-
let source_out_handle = source_manipulators.out_handle.unwrap_or(source_anchor);
1572-
let target_out_handle = target_manipulators.out_handle.unwrap_or(target_anchor);
1573-
source_manipulators.out_handle = Some(source_out_handle.lerp(target_out_handle, time));
1607+
for (i, (source_element, target_element)) in source_bezpath.elements_mut().iter_mut().zip(target_bezpath.elements_mut().iter_mut()).enumerate() {
1608+
match source_element {
1609+
PathEl::MoveTo(point) => *point = point.lerp(target_element.end_point().unwrap(), time),
1610+
PathEl::ClosePath => {}
1611+
elm => {
1612+
let mut source_segment = source_segments.get(i - 1).unwrap().to_cubic();
1613+
let target_segment = target_segments.get(i - 1).unwrap().to_cubic();
1614+
source_segment.p0 = source_segment.p0.lerp(target_segment.p0, time);
1615+
source_segment.p1 = source_segment.p1.lerp(target_segment.p1, time);
1616+
source_segment.p2 = source_segment.p2.lerp(target_segment.p2, time);
1617+
source_segment.p3 = source_segment.p3.lerp(target_segment.p3, time);
1618+
*elm = PathSeg::Cubic(source_segment).as_path_el();
1619+
}
1620+
}
15741621
}
15751622

1576-
vector_data_instance.append_subpath(source_path.clone(), true);
1623+
vector_data_instance.append_bezpath(source_bezpath.clone());
15771624
}
15781625

15791626
// Deal with unmatched extra paths by collapsing them
1580-
let source_paths_count = source_instance.instance.stroke_bezier_paths().count();
1581-
let target_paths_count = target_instance.instance.stroke_bezier_paths().count();
1582-
let source_paths = source_instance.instance.stroke_bezier_paths().skip(target_paths_count);
1583-
let target_paths = target_instance.instance.stroke_bezier_paths().skip(source_paths_count);
1627+
let source_paths_count = source_instance.instance.stroke_bezpath_iter().count();
1628+
let target_paths_count = target_instance.instance.stroke_bezpath_iter().count();
1629+
let source_paths = source_instance.instance.stroke_bezpath_iter().skip(target_paths_count);
1630+
let target_paths = target_instance.instance.stroke_bezpath_iter().skip(source_paths_count);
15841631

15851632
for mut source_path in source_paths {
1586-
source_path.apply_transform(source_transform);
1587-
let end = source_path.manipulator_groups().last().map(|group| group.anchor).unwrap_or_default();
1588-
for group in source_path.manipulator_groups_mut() {
1589-
group.anchor = group.anchor.lerp(end, time);
1590-
group.in_handle = group.in_handle.map(|handle| handle.lerp(end, time));
1591-
group.out_handle = group.out_handle.map(|handle| handle.lerp(end, time));
1633+
source_path.apply_affine(Affine::new(source_transform.to_cols_array()));
1634+
1635+
let end: Point = source_path.elements().last().and_then(|element| element.end_point()).unwrap_or_default();
1636+
1637+
for element in source_path.elements_mut() {
1638+
match element {
1639+
PathEl::MoveTo(point) => *point = point.lerp(end, time),
1640+
PathEl::LineTo(point) => *point = point.lerp(end, time),
1641+
PathEl::QuadTo(point, point1) => {
1642+
*point = point.lerp(end, time);
1643+
*point1 = point1.lerp(end, time);
1644+
}
1645+
PathEl::CurveTo(point, point1, point2) => {
1646+
*point = point.lerp(end, time);
1647+
*point1 = point1.lerp(end, time);
1648+
*point2 = point2.lerp(end, time);
1649+
}
1650+
PathEl::ClosePath => {}
1651+
}
15921652
}
1593-
vector_data_instance.append_subpath(source_path, true);
1653+
vector_data_instance.append_bezpath(source_path);
15941654
}
1655+
15951656
for mut target_path in target_paths {
1596-
target_path.apply_transform(target_transform);
1597-
let start = target_path.manipulator_groups().first().map(|group| group.anchor).unwrap_or_default();
1598-
for group in target_path.manipulator_groups_mut() {
1599-
group.anchor = start.lerp(group.anchor, time);
1600-
group.in_handle = group.in_handle.map(|handle| start.lerp(handle, time));
1601-
group.out_handle = group.out_handle.map(|handle| start.lerp(handle, time));
1657+
target_path.apply_affine(Affine::new(source_transform.to_cols_array()));
1658+
1659+
let end: Point = target_path.elements().last().and_then(|element| element.end_point()).unwrap_or_default();
1660+
1661+
for element in target_path.elements_mut() {
1662+
match element {
1663+
PathEl::MoveTo(point) => *point = point.lerp(end, time),
1664+
PathEl::LineTo(point) => *point = point.lerp(end, time),
1665+
PathEl::QuadTo(point, point1) => {
1666+
*point = point.lerp(end, time);
1667+
*point1 = point1.lerp(end, time);
1668+
}
1669+
PathEl::CurveTo(point, point1, point2) => {
1670+
*point = point.lerp(end, time);
1671+
*point1 = point1.lerp(end, time);
1672+
*point2 = point2.lerp(end, time);
1673+
}
1674+
PathEl::ClosePath => {}
1675+
}
16021676
}
1603-
vector_data_instance.append_subpath(target_path, true);
1677+
vector_data_instance.append_bezpath(target_path);
16041678
}
16051679

16061680
result_table.push(Instance {

0 commit comments

Comments
 (0)