Skip to content

Commit 862811b

Browse files
committed
Add the Reverse parameter to the Morph node
1 parent 1907486 commit 862811b

4 files changed

Lines changed: 11 additions & 9 deletions

File tree

editor/src/messages/portfolio/document/graph_operation/utility_types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ impl<'a> ModifyInputsContext<'a> {
178178
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
179179
Some(NodeInput::value(TaggedValue::F64(0.5), false)),
180180
None,
181+
None,
181182
Some(NodeInput::value(TaggedValue::Vector(Default::default()), false)),
182183
]);
183184

editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,12 +559,13 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
559559
inputs: vec![NodeInput::node(NodeId(8), 0), NodeInput::node(NodeId(13), 0)],
560560
..Default::default()
561561
},
562-
// 15: Morph (content, progression, distribution, path)
562+
// 15: Morph (content, progression, reverse, distribution, path)
563563
DocumentNode {
564564
implementation: DocumentNodeImplementation::ProtoNode(vector::morph::IDENTIFIER),
565565
inputs: vec![
566566
NodeInput::import(generic!(T), 0),
567567
NodeInput::node(NodeId(14), 0),
568+
NodeInput::value(TaggedValue::Bool(false), false),
568569
NodeInput::import(concrete!(vector::misc::InterpolationDistribution), 1),
569570
NodeInput::import(generic!(T), 2),
570571
],

editor/src/messages/portfolio/document_migration.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,19 +1669,16 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
16691669
// The old version would zip the source and target table rows, interpoleating each pair together.
16701670
// The migrated version will instead deeply flatten both merged tables and morph sequentially between all source vectors and all target vector elements.
16711671
// This migration assumes most usages didn't involve multiple parallel vector elements, and instead morphed from a single source to a single target vector element.
1672-
// The new signature has 4 inputs too, so we distinguish by checking if input 2 is an f64 (old `time` param) vs an InterpolationSpacing (new `distribution` param).
1673-
let is_old_morph = reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::morph::IDENTIFIER)
1674-
&& ((inputs_count == 3 && node.inputs.get(2).and_then(|i| i.as_value()).is_some_and(|v| matches!(v, TaggedValue::F64(_))))
1675-
|| (inputs_count == 4 && node.inputs.get(2).and_then(|i| i.as_value()).is_some_and(|v| matches!(v, TaggedValue::F64(_)))));
1676-
if is_old_morph {
1672+
// The new v3 signature has 5 inputs, so 3 or 4 inputs can only be old v1 versions.
1673+
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::morph::IDENTIFIER) && (inputs_count == 3 || inputs_count == 4) {
16771674
// 3 inputs - old signature (#3405):
16781675
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction) -> Table<Vector> { ... }
16791676
//
16801677
// 4 inputs - even older signature (commit 80b8df8d4298b6669f124b929ce61bfabfc44e41):
16811678
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction, #[min(0.)] start_index: IntegerCount) -> Table<Vector> { ... }
16821679
//
16831680
// New signature:
1684-
// async fn morph<I: IntoGraphicTable>(_: impl Ctx, content: I, progression: Progression, distribution: InterpolationDistribution, path: Table<Vector>) -> Table<Vector> { ... }
1681+
// async fn morph<I: IntoGraphicTable>(_: impl Ctx, content: I, progression: Progression, reverse: bool, distribution: InterpolationDistribution, path: Table<Vector>) -> Table<Vector> { ... }
16851682

16861683
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
16871684
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1720,7 +1717,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
17201717
inputs_count = 3;
17211718
}
17221719

1723-
// Migrate from the v2 "Morph" node (2 inputs: content, progression) to the v3 "Morph" node (3 inputs: content, progression, path).
1720+
// Migrate from the v2 "Morph" node (2 inputs: content, progression) to the v3 "Morph" node (5 inputs: content, progression, reverse, distribution, path).
17241721
// The old progression used integer part for pair selection (range 0..N-1 where N is the number of content objects).
17251722
// The new progression uses fractional 0..1 for euclidean traversal through all objects.
17261723
// We insert Count Elements → Subtract 1 → Divide to remap: new_progression = old_progression / max(N - 1, 1).

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1949,6 +1949,8 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
19491949
content: I,
19501950
/// The fractional part [0, 1) traverses the morph uniformly along the path. If the control path has multiple subpaths, each added integer selects the next subpath.
19511951
progression: Progression,
1952+
/// Swap the direction of the progression between objects or along the control path.
1953+
reverse: bool,
19521954
/// The parameter of change that influences the interpolation speed between each object. Equal slices in this parameter correspond to the rate of progression through the morph. This must be set to a parameter that changes.
19531955
///
19541956
/// "Objects" morphs through each group element at an equal rate. "Distances" keeps constant speed with time between objects proportional to their distances. "Angles" keeps constant rotational speed. "Sizes" keeps constant shrink/growth speed. "Slants" keeps constant shearing angle speed.
@@ -2102,6 +2104,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
21022104
// Select which subpath to use based on the integer part of progression (like the 'Position on Path' node)
21032105
let progression = progression.max(0.);
21042106
let subpath_count = control_bezpaths.len() as f64;
2107+
let progression = if reverse { subpath_count - progression } else { progression };
21052108
let clamped_progression = progression.clamp(0., subpath_count);
21062109
let subpath_index = if clamped_progression >= subpath_count { subpath_count - 1. } else { clamped_progression } as usize;
21072110
let fractional_progression = if clamped_progression >= subpath_count { 1. } else { clamped_progression.fract() };
@@ -3023,7 +3026,7 @@ mod test {
30233026
second_rectangle.transform *= DAffine2::from_translation((-100., -100.).into());
30243027
rectangles.push(second_rectangle);
30253028

3026-
let morphed = super::morph(Footprint::default(), rectangles, 0.5, InterpolationDistribution::default(), Table::default()).await;
3029+
let morphed = super::morph(Footprint::default(), rectangles, 0.5, false, InterpolationDistribution::default(), Table::default()).await;
30273030
let row = morphed.iter().next().unwrap();
30283031
// Geometry stays in local space (original rectangle coordinates)
30293032
assert_eq!(

0 commit comments

Comments
 (0)