Skip to content

Commit 1907486

Browse files
committed
Add InterpolationDistribution parameter to Morph with weighted progression, swap parameter orders, and rename shear to skew
1 parent d393654 commit 1907486

9 files changed

Lines changed: 141 additions & 63 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,9 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
188188
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
189189
network_interface.move_layer_to_stack(blend_path_layer, parent, insert_index + 1, &[]);
190190

191-
// Connect the Path node's output to the Blend Shapes node's Path parameter input (input 1).
191+
// Connect the Path node's output to the Blend Shapes node's Path parameter input (input 2).
192192
// Done after move_layer_to_stack so chain nodes have correct positions when converted to absolute.
193-
network_interface.set_input(&InputConnector::node(blend_shapes_node_id, 1), NodeInput::node(path_node_id, 0), &[]);
193+
network_interface.set_input(&InputConnector::node(blend_shapes_node_id, 2), NodeInput::node(path_node_id, 0), &[]);
194194

195195
responses.add(NodeGraphMessage::SetDisplayNameImpl {
196196
node_id: id,

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ impl<'a> ModifyInputsContext<'a> {
159159
pub fn insert_blend_shapes_data(&mut self, layer: LayerNodeIdentifier, count: f64) -> NodeId {
160160
let blend_shapes = resolve_network_node_type("Blend Shapes").expect("Blend Shapes node does not exist").node_template_input_override([
161161
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
162-
Some(NodeInput::value(TaggedValue::Vector(Default::default()), true)),
162+
None,
163+
None,
163164
Some(NodeInput::value(TaggedValue::F64(count), false)),
164165
]);
165166

@@ -176,6 +177,7 @@ impl<'a> ModifyInputsContext<'a> {
176177
.node_template_input_override([
177178
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
178179
Some(NodeInput::value(TaggedValue::F64(0.5), false)),
180+
None,
179181
Some(NodeInput::value(TaggedValue::Vector(Default::default()), false)),
180182
]);
181183

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
472472
// 0: Separate Subpaths (split path into individual subpaths)
473473
DocumentNode {
474474
implementation: DocumentNodeImplementation::ProtoNode(vector::separate_subpaths::IDENTIFIER),
475-
inputs: vec![NodeInput::import(generic!(T), 1)],
475+
inputs: vec![NodeInput::import(generic!(T), 2)],
476476
..Default::default()
477477
},
478478
// 1: Count Elements (number of subpaths)
@@ -490,7 +490,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
490490
// 3: Floor (integer count per subpath)
491491
DocumentNode {
492492
implementation: DocumentNodeImplementation::ProtoNode(math_nodes::floor::IDENTIFIER),
493-
inputs: vec![NodeInput::import(concrete!(f64), 2)],
493+
inputs: vec![NodeInput::import(concrete!(f64), 3)],
494494
..Default::default()
495495
},
496496
// 4: Multiply (total_instances = count × subpath_count)
@@ -532,7 +532,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
532532
// 10: Path Is Closed (check if current subpath is closed)
533533
DocumentNode {
534534
implementation: DocumentNodeImplementation::ProtoNode(vector::path_is_closed::IDENTIFIER),
535-
inputs: vec![NodeInput::import(generic!(T), 1), NodeInput::node(NodeId(8), 0)],
535+
inputs: vec![NodeInput::import(generic!(T), 2), NodeInput::node(NodeId(8), 0)],
536536
..Default::default()
537537
},
538538
// 11: Switch (closed → count, open → count - 1 as denominator)
@@ -559,16 +559,21 @@ 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, path)
562+
// 15: Morph (content, progression, distribution, path)
563563
DocumentNode {
564564
implementation: DocumentNodeImplementation::ProtoNode(vector::morph::IDENTIFIER),
565-
inputs: vec![NodeInput::import(generic!(T), 0), NodeInput::node(NodeId(14), 0), NodeInput::import(generic!(T), 1)],
565+
inputs: vec![
566+
NodeInput::import(generic!(T), 0),
567+
NodeInput::node(NodeId(14), 0),
568+
NodeInput::import(concrete!(vector::misc::InterpolationDistribution), 1),
569+
NodeInput::import(generic!(T), 2),
570+
],
566571
..Default::default()
567572
},
568573
// 16: Repeat
569574
DocumentNode {
570575
implementation: DocumentNodeImplementation::ProtoNode(repeat_nodes::repeat::IDENTIFIER),
571-
inputs: vec![NodeInput::node(NodeId(15), 0), NodeInput::node(NodeId(4), 0), NodeInput::import(generic!(T), 3)],
576+
inputs: vec![NodeInput::node(NodeId(15), 0), NodeInput::node(NodeId(4), 0), NodeInput::import(generic!(T), 4)],
572577
..Default::default()
573578
},
574579
// 17: Max (clamp count to at least 1)
@@ -586,14 +591,21 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
586591
}),
587592
inputs: vec![
588593
NodeInput::value(TaggedValue::Vector(Default::default()), true),
594+
NodeInput::value(TaggedValue::InterpolationDistribution(Default::default()), false),
589595
NodeInput::value(TaggedValue::Vector(Default::default()), false),
590596
NodeInput::value(TaggedValue::F64(10.), false),
591597
NodeInput::value(TaggedValue::Bool(Default::default()), false),
592598
],
593599
..Default::default()
594600
},
595601
persistent_node_metadata: DocumentNodePersistentMetadata {
596-
input_metadata: vec![("Content", "TODO").into(), ("Path", "TODO").into(), ("Count", "TODO").into(), ("Reverse", "TODO").into()],
602+
input_metadata: vec![
603+
("Content", "TODO").into(),
604+
("Distribution", "TODO").into(),
605+
("Path", "TODO").into(),
606+
("Count", "TODO").into(),
607+
("Reverse", "TODO").into(),
608+
],
597609
output_names: vec!["Out".to_string()],
598610
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
599611
network_metadata: Some(NodeNetworkMetadata {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use graphene_std::text::{Font, TextAlign};
2626
use graphene_std::transform::{Footprint, ReferencePoint, ScaleType, Transform};
2727
use graphene_std::vector::QRCodeErrorCorrectionLevel;
2828
use graphene_std::vector::misc::BooleanOperation;
29-
use graphene_std::vector::misc::{ArcType, CentroidType, ExtrudeJoiningAlgorithm, GridType, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType};
29+
use graphene_std::vector::misc::{ArcType, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType};
3030
use graphene_std::vector::style::{Fill, FillChoice, FillType, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
3131

3232
pub(crate) fn string_properties(text: &str) -> Vec<LayoutGroup> {
@@ -266,6 +266,7 @@ pub(crate) fn property_from_type(
266266
Some(x) if x == TypeId::of::<LuminanceCalculation>() => enum_choice::<LuminanceCalculation>().for_socket(default_info).property_row(),
267267
Some(x) if x == TypeId::of::<QRCodeErrorCorrectionLevel>() => enum_choice::<QRCodeErrorCorrectionLevel>().for_socket(default_info).property_row(),
268268
Some(x) if x == TypeId::of::<ScaleType>() => enum_choice::<ScaleType>().for_socket(default_info).property_row(),
269+
Some(x) if x == TypeId::of::<InterpolationDistribution>() => enum_choice::<InterpolationDistribution>().for_socket(default_info).property_row(),
269270
// =====
270271
// OTHER
271272
// =====

editor/src/messages/portfolio/document_migration.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,9 +1669,10 @@ 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 3 inputs too, so we distinguish by checking if input 2 is an f64 (old `time` param) vs a Table<Vector> (new `path` param).
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).
16731673
let is_old_morph = reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::morph::IDENTIFIER)
1674-
&& (inputs_count == 4 || (inputs_count == 3 && node.inputs.get(2).and_then(|i| i.as_value()).is_some_and(|v| matches!(v, TaggedValue::F64(_)))));
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(_)))));
16751676
if is_old_morph {
16761677
// 3 inputs - old signature (#3405):
16771678
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction) -> Table<Vector> { ... }
@@ -1680,7 +1681,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
16801681
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction, #[min(0.)] start_index: IntegerCount) -> Table<Vector> { ... }
16811682
//
16821683
// New signature:
1683-
// async fn morph<I: IntoGraphicTable>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: I, progression: Progression, path: Table<Vector>) -> Table<Vector> { ... }
1684+
// async fn morph<I: IntoGraphicTable>(_: impl Ctx, content: I, progression: Progression, distribution: InterpolationDistribution, path: Table<Vector>) -> Table<Vector> { ... }
16841685

16851686
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
16861687
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;

node-graph/graph-craft/src/document/value.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ tagged_value! {
260260
ExtrudeJoiningAlgorithm(vector::misc::ExtrudeJoiningAlgorithm),
261261
PointSpacingType(vector::misc::PointSpacingType),
262262
SpiralType(vector::misc::SpiralType),
263+
InterpolationDistribution(vector::misc::InterpolationDistribution),
263264
#[serde(alias = "LineCap")]
264265
StrokeCap(vector::style::StrokeCap),
265266
#[serde(alias = "LineJoin")]

node-graph/interpreted-executor/src/node_registry.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
135135
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
136136
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text::TextAlign]),
137137
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ScaleType]),
138+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::InterpolationDistribution]),
138139
// Context nullification
139140
#[cfg(feature = "gpu")]
140141
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => &WasmEditorApi, Context => graphene_std::ContextFeatures]),
@@ -229,6 +230,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
229230
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::BooleanOperation]),
230231
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::text::TextAlign]),
231232
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::ScaleType]),
233+
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::InterpolationDistribution]),
232234
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderIntermediate]),
233235
];
234236
// =============

node-graph/libraries/vector-types/src/vector/misc.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,21 @@ pub enum SpiralType {
554554
Archimedean,
555555
Logarithmic,
556556
}
557+
558+
/// Controls how the morph/blend progression spends its time along the interpolation path, allowing for constant speed/spacing with respect to different parameters of change.
559+
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
560+
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, node_macro::ChoiceType)]
561+
#[widget(Dropdown)]
562+
pub enum InterpolationDistribution {
563+
/// All objects occupy an equal portion of the progression range, regardless of their changing distances, angles, sizes, or slants.
564+
#[default]
565+
Objects,
566+
/// All distances along the interpolation path are covered at a constant rate, meaning more time is spent traversing further distances.
567+
Distances,
568+
/// All angles of rotation between objects are covered at a constant rate, meaning more time is spent turning through larger angles.
569+
Angles,
570+
/// All sizes of expansion/contraction between objects are covered at a constant rate, meaning more time is spent scaling through larger (logarithmic) changes in size.
571+
Sizes,
572+
/// All slants (changes in skew angle) between objects are covered at a constant rate, meaning more time is spent skewing through larger changes in slant.
573+
Slants,
574+
}

0 commit comments

Comments
 (0)