From 68d1a36938b903969921ee018b689141511e6f8e Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sun, 25 Jan 2026 22:11:16 -0800 Subject: [PATCH] Add the "Rate" multiplier parameter to the Animation Time node --- .../messages/portfolio/document_migration.rs | 14 ++++++++++++ node-graph/nodes/gcore/src/animation.rs | 12 +++++++--- node-graph/nodes/graphic/src/graphic.rs | 22 +++++++++---------- node-graph/nodes/vector/src/vector_nodes.rs | 11 ++++++++-- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/editor/src/messages/portfolio/document_migration.rs b/editor/src/messages/portfolio/document_migration.rs index aa4ccaa174..da6ed7745f 100644 --- a/editor/src/messages/portfolio/document_migration.rs +++ b/editor/src/messages/portfolio/document_migration.rs @@ -1632,6 +1632,20 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], } } + // Upgrade the "Animation" node to add the "Rate" input + if reference == DefinitionIdentifier::ProtoNode(graphene_std::animation::animation_time::IDENTIFIER) && inputs_count < 2 { + let mut node_template = resolve_document_node_type(&reference)?.default_node_template(); + document.network_interface.replace_implementation(node_id, network_path, &mut node_template); + let _ = document.network_interface.replace_inputs(node_id, network_path, &mut node_template); + + document + .network_interface + .set_input(&InputConnector::node(*node_id, 0), NodeInput::value(TaggedValue::None, false), network_path); + document + .network_interface + .set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::F64(1.), false), network_path); + } + // Migrate from the old source/target "Morph" node to the new vector table based "Morph" node. // This doesn't produce exactly equivalent results in cases involving input vector tables with multiple rows. // The old version would zip the source and target table rows, interpoleating each pair together. diff --git a/node-graph/nodes/gcore/src/animation.rs b/node-graph/nodes/gcore/src/animation.rs index b2aca80802..da987c2a9d 100644 --- a/node-graph/nodes/gcore/src/animation.rs +++ b/node-graph/nodes/gcore/src/animation.rs @@ -30,13 +30,13 @@ fn real_time( component: RealTimeMode, ) -> f64 { let real_time = ctx.try_real_time().unwrap_or_default(); + // TODO: Implement proper conversion using and existing time implementation match component { RealTimeMode::Utc => real_time, RealTimeMode::Year => (real_time / DAY / 365.25).floor() + 1970., // TODO: Factor in a chosen timezone RealTimeMode::Hour => (real_time / 1000. / 3600.).floor() % 24., // TODO: Factor in a chosen timezone RealTimeMode::Minute => (real_time / 1000. / 60.).floor() % 60., // TODO: Factor in a chosen timezone - RealTimeMode::Second => (real_time / 1000.).floor() % 60., RealTimeMode::Millisecond => real_time % 1000., } @@ -44,8 +44,14 @@ fn real_time( /// Produces the time, in seconds on the timeline, since the beginning of animation playback. #[node_macro::node(category("Animation"))] -fn animation_time(ctx: impl Ctx + ExtractAnimationTime) -> f64 { - ctx.try_animation_time().unwrap_or_default() +fn animation_time( + ctx: impl Ctx + ExtractAnimationTime, + _primary: (), + #[default(1)] + #[unit("/sec")] + rate: f64, +) -> f64 { + ctx.try_animation_time().unwrap_or_default() * rate } /// Produces the current position of the user's pointer within the document canvas. diff --git a/node-graph/nodes/graphic/src/graphic.rs b/node-graph/nodes/graphic/src/graphic.rs index ac2d1ebe93..ef76f5e4c7 100644 --- a/node-graph/nodes/graphic/src/graphic.rs +++ b/node-graph/nodes/graphic/src/graphic.rs @@ -13,7 +13,7 @@ use vector_types::GradientStops; /// This node associates the ID of the network's parent layer to every element of output data. /// This technical detail may be ignored by users, and will be phased out in the future. #[node_macro::node(category(""))] -pub async fn source_node_id( +pub async fn source_node_id( _: impl Ctx, #[implementations( Table, @@ -24,9 +24,9 @@ pub async fn source_node_id( Table, Table, )] - content: Table, + content: Table, node_path: Vec, -) -> Table { +) -> Table { // Get the penultimate element of the node path, or None if the path is too short // This is used to get the ID of the user-facing parent layer node (whose network contains this internal node). let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied(); @@ -41,16 +41,16 @@ pub async fn source_node_id( /// Joins two tables of the same type, extending the base table with the rows of the new table. #[node_macro::node(category("General"))] -pub async fn extend( +pub async fn extend( _: impl Ctx, /// The table whose rows will appear at the start of the extended table. #[implementations(Table, Table, Table, Table>, Table>, Table, Table)] - base: Table, + base: Table, /// The table whose rows will appear at the end of the extended table. #[expose] #[implementations(Table, Table, Table, Table>, Table>, Table, Table)] - new: Table, -) -> Table { + new: Table, +) -> Table { let mut base = base; base.extend(new); @@ -61,14 +61,14 @@ pub async fn extend( /// Performs an obsolete function as part of a migration from an older document format. /// Users are advised to delete this node and replace it with a new one. #[node_macro::node(category(""))] -pub async fn legacy_layer_extend( +pub async fn legacy_layer_extend( _: impl Ctx, - #[implementations(Table, Table, Table, Table>, Table>, Table, Table)] base: Table, + #[implementations(Table, Table, Table, Table>, Table>, Table, Table)] base: Table, #[expose] #[implementations(Table, Table, Table, Table>, Table>, Table, Table)] - new: Table, + new: Table, nested_node_path: Vec, -) -> Table { +) -> Table { // Get the penultimate element of the node path, or None if the path is too short // This is used to get the ID of the user-facing parent layer-style node (which encapsulates this internal node). let source_node_id = nested_node_path.get(nested_node_path.len().wrapping_sub(2)).copied(); diff --git a/node-graph/nodes/vector/src/vector_nodes.rs b/node-graph/nodes/vector/src/vector_nodes.rs index b68067d789..778b9eeab5 100644 --- a/node-graph/nodes/vector/src/vector_nodes.rs +++ b/node-graph/nodes/vector/src/vector_nodes.rs @@ -1226,9 +1226,16 @@ async fn instance_map(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: Table< } #[node_macro::node(category("Vector"), path(graphene_core::vector))] -async fn flatten_path(_: impl Ctx, #[implementations(Table, Table)] content: Table) -> Table +async fn flatten_path( + _: impl Ctx, + #[implementations( + Table, + Table, + )] + content: Table, +) -> Table where - Graphic: From>, + Graphic: From>, { // NOTE(AdamGerhant): // A node-based solution to support passing through vector data could be a network node with a cache node