Skip to content

Commit 80b8df8

Browse files
committed
FIx the 'Morph' node to not fail on topology changes like holes
1 parent 0c3cae2 commit 80b8df8

File tree

4 files changed

+79
-63
lines changed

4 files changed

+79
-63
lines changed

demo-artwork/changing-seasons.graphite

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editor/src/messages/portfolio/portfolio_message_handler.rs

+17
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,23 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
905905
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
906906
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
907907
}
908+
909+
if reference == "Morph" && inputs_count == 4 {
910+
let node_definition = resolve_document_node_type(reference).unwrap();
911+
let new_node_template = node_definition.default_node_template();
912+
let document_node = new_node_template.document_node;
913+
document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone());
914+
document
915+
.network_interface
916+
.replace_implementation_metadata(node_id, network_path, new_node_template.persistent_node_metadata);
917+
918+
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
919+
920+
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
921+
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
922+
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
923+
// We have removed the last input, so we don't add index 3
924+
}
908925
}
909926

910927
// TODO: Eventually remove this document upgrade code

node-graph/gcore/src/ops.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,22 @@ fn multiply<U: Mul<T>, T>(
114114
}
115115

116116
/// The division operation (÷) calculates the quotient of two numbers.
117+
///
118+
/// Produces 0 if the denominator is 0.
117119
#[node_macro::node(category("Math: Arithmetic"))]
118-
fn divide<U: Div<T>, T>(
120+
fn divide<U: Div<T> + Default + PartialEq, T: Default + PartialEq>(
119121
_: impl Ctx,
120-
#[implementations(f64, &f64, f64, &f64, f32, &f32, f32, &f32, u32, &u32, u32, &u32, DVec2, DVec2, f64)] numerator: U,
122+
#[implementations(f64, f64, f32, f32, u32, u32, DVec2, DVec2, f64)] numerator: U,
121123
#[default(1.)]
122-
#[implementations(f64, f64, &f64, &f64, f32, f32, &f32, &f32, u32, u32, &u32, &u32, DVec2, f64, DVec2)]
124+
#[implementations(f64, f64, f32, f32, u32, u32, DVec2, f64, DVec2)]
123125
denominator: T,
124-
) -> <U as Div<T>>::Output {
126+
) -> <U as Div<T>>::Output
127+
where
128+
<U as Div<T>>::Output: Default,
129+
{
130+
if denominator == T::default() {
131+
return <U as Div<T>>::Output::default();
132+
}
125133
numerator / denominator
126134
}
127135

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

+49-58
Original file line numberDiff line numberDiff line change
@@ -1525,96 +1525,87 @@ async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)]
15251525
}
15261526

15271527
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
1528-
async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDataTable, #[default(0.5)] time: Fraction, #[min(0.)] start_index: IntegerCount) -> VectorDataTable {
1528+
async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDataTable, #[default(0.5)] time: Fraction) -> VectorDataTable {
1529+
let mut source = source;
1530+
let mut target = target;
1531+
15291532
let time = time.clamp(0., 1.);
15301533

1534+
let mut result_table = VectorDataTable::default();
1535+
1536+
// Lerp styles
15311537
let source_alpha_blending = source.one_instance_ref().alpha_blending;
15321538
let target_alpha_blending = target.one_instance_ref().alpha_blending;
1539+
*result_table.one_instance_mut().alpha_blending = if time < 0.5 { *source_alpha_blending } else { *target_alpha_blending };
1540+
result_table.one_instance_mut().instance.style = source.one_instance_ref().instance.style.lerp(&target.one_instance_ref().instance.style, time);
15331541

1534-
let source_transform = source.transform();
1535-
let target_transform = target.transform();
1536-
1537-
let source = source.one_instance_ref().instance;
1538-
let target = target.one_instance_ref().instance;
1542+
// Before and after transforms
1543+
let source_transform = *source.one_instance_ref().transform;
1544+
let target_transform = *target.one_instance_ref().transform;
15391545

1540-
let mut result = VectorDataTable::default();
1541-
1542-
// Lerp styles
1543-
*result.one_instance_mut().alpha_blending = if time < 0.5 { *source_alpha_blending } else { *target_alpha_blending };
1544-
result.one_instance_mut().instance.style = source.style.lerp(&target.style, time);
1545-
1546-
let mut source_paths = source.stroke_bezier_paths();
1547-
let mut target_paths = target.stroke_bezier_paths();
1548-
for (mut source_path, mut target_path) in (&mut source_paths).zip(&mut target_paths) {
1549-
// Deal with mismatched transforms
1546+
// Before and after paths
1547+
let source_paths = source.one_instance_mut().instance.stroke_bezier_paths();
1548+
let target_paths = target.one_instance_mut().instance.stroke_bezier_paths();
1549+
for (mut source_path, mut target_path) in source_paths.zip(target_paths) {
15501550
source_path.apply_transform(source_transform);
15511551
target_path.apply_transform(target_transform);
15521552

1553-
// Deal with mismatched start index
1554-
for _ in 0..start_index {
1555-
let first = target_path.remove_manipulator_group(0);
1556-
target_path.push_manipulator_group(first);
1557-
}
1558-
1559-
// Deal with mismatched closed state
1560-
if source_path.closed() && !target_path.closed() {
1561-
source_path.set_closed(false);
1562-
source_path.push_manipulator_group(source_path.manipulator_groups()[0].flip());
1553+
// Align point counts by inserting mid‐segment points until their counts match
1554+
while source_path.manipulator_groups().len() < target_path.manipulator_groups().len() {
1555+
let last = source_path.len() - 1;
1556+
source_path.insert(SubpathTValue::Parametric { segment_index: last, t: 0.5 });
15631557
}
1564-
if !source_path.closed() && target_path.closed() {
1565-
target_path.set_closed(false);
1566-
target_path.push_manipulator_group(target_path.manipulator_groups()[0].flip());
1558+
while target_path.manipulator_groups().len() < source_path.manipulator_groups().len() {
1559+
let last = target_path.len() - 1;
1560+
target_path.insert(SubpathTValue::Parametric { segment_index: last, t: 0.5 });
15671561
}
15681562

1569-
// Mismatched subpath items
1570-
'outer: loop {
1571-
for segment_index in (0..(source_path.len() - 1)).rev() {
1572-
if target_path.len() <= source_path.len() {
1573-
break 'outer;
1574-
}
1575-
source_path.insert(SubpathTValue::Parametric { segment_index, t: 0.5 })
1576-
}
1577-
}
1578-
'outer: loop {
1579-
for segment_index in (0..(target_path.len() - 1)).rev() {
1580-
if source_path.len() <= target_path.len() {
1581-
break 'outer;
1582-
}
1583-
target_path.insert(SubpathTValue::Parametric { segment_index, t: 0.5 })
1584-
}
1585-
}
1563+
// Interpolate anchors and handles
1564+
for (source_manipulators, target_manipulators) in source_path.manipulator_groups_mut().iter_mut().zip(target_path.manipulator_groups()) {
1565+
let source_anchor = source_manipulators.anchor;
1566+
let target_anchor = target_manipulators.anchor;
1567+
source_manipulators.anchor = source_anchor.lerp(target_anchor, time);
1568+
1569+
let source_in_handle = source_manipulators.in_handle.unwrap_or(source_anchor);
1570+
let target_in_handle = target_manipulators.in_handle.unwrap_or(target_anchor);
1571+
source_manipulators.in_handle = Some(source_in_handle.lerp(target_in_handle, time));
15861572

1587-
// Lerp points
1588-
for (manipulator, target) in source_path.manipulator_groups_mut().iter_mut().zip(target_path.manipulator_groups()) {
1589-
manipulator.in_handle = Some(manipulator.in_handle.unwrap_or(manipulator.anchor).lerp(target.in_handle.unwrap_or(target.anchor), time));
1590-
manipulator.out_handle = Some(manipulator.out_handle.unwrap_or(manipulator.anchor).lerp(target.out_handle.unwrap_or(target.anchor), time));
1591-
manipulator.anchor = manipulator.anchor.lerp(target.anchor, time);
1573+
let source_out_handle = source_manipulators.out_handle.unwrap_or(source_anchor);
1574+
let target_out_handle = target_manipulators.out_handle.unwrap_or(target_anchor);
1575+
source_manipulators.out_handle = Some(source_out_handle.lerp(target_out_handle, time));
15921576
}
15931577

1594-
result.one_instance_mut().instance.append_subpath(source_path, true);
1578+
result_table.one_instance_mut().instance.append_subpath(source_path.clone(), true);
15951579
}
15961580

1597-
// Mismatched subpath count
1581+
// Deal with unmatched extra paths by collapsing them
1582+
let source_paths_count = source.one_instance_ref().instance.stroke_bezier_paths().count();
1583+
let target_paths_count = target.one_instance_ref().instance.stroke_bezier_paths().count();
1584+
let source_paths = source.one_instance_mut().instance.stroke_bezier_paths().skip(target_paths_count);
1585+
let target_paths = target.one_instance_mut().instance.stroke_bezier_paths().skip(source_paths_count);
1586+
15981587
for mut source_path in source_paths {
15991588
source_path.apply_transform(source_transform);
1600-
let end = source_path.manipulator_groups().first().map(|group| group.anchor).unwrap_or_default();
1589+
let end = source_path.manipulator_groups().last().map(|group| group.anchor).unwrap_or_default();
16011590
for group in source_path.manipulator_groups_mut() {
16021591
group.anchor = group.anchor.lerp(end, time);
16031592
group.in_handle = group.in_handle.map(|handle| handle.lerp(end, time));
1604-
group.out_handle = group.in_handle.map(|handle| handle.lerp(end, time));
1593+
group.out_handle = group.out_handle.map(|handle| handle.lerp(end, time));
16051594
}
1595+
result_table.one_instance_mut().instance.append_subpath(source_path, true);
16061596
}
16071597
for mut target_path in target_paths {
16081598
target_path.apply_transform(target_transform);
16091599
let start = target_path.manipulator_groups().first().map(|group| group.anchor).unwrap_or_default();
16101600
for group in target_path.manipulator_groups_mut() {
16111601
group.anchor = start.lerp(group.anchor, time);
16121602
group.in_handle = group.in_handle.map(|handle| start.lerp(handle, time));
1613-
group.out_handle = group.in_handle.map(|handle| start.lerp(handle, time));
1603+
group.out_handle = group.out_handle.map(|handle| start.lerp(handle, time));
16141604
}
1605+
result_table.one_instance_mut().instance.append_subpath(target_path, true);
16151606
}
16161607

1617-
result
1608+
result_table
16181609
}
16191610

16201611
fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2, distance: f64) -> VectorData {
@@ -1978,7 +1969,7 @@ mod test {
19781969
async fn morph() {
19791970
let source = Subpath::new_rect(DVec2::ZERO, DVec2::ONE * 100.);
19801971
let target = Subpath::new_ellipse(DVec2::NEG_ONE * 100., DVec2::ZERO);
1981-
let sample_points = super::morph(Footprint::default(), vector_node(source), vector_node(target), 0.5, 0).await;
1972+
let sample_points = super::morph(Footprint::default(), vector_node(source), vector_node(target), 0.5).await;
19821973
let sample_points = sample_points.instance_ref_iter().next().unwrap().instance;
19831974
assert_eq!(
19841975
&sample_points.point_domain.positions()[..4],

0 commit comments

Comments
 (0)