diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index bb3fd391..8b11a6d9 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -705,7 +705,7 @@ mod test_transform_layer { use crate::messages::portfolio::document::graph_operation::transform_utils; use crate::test_utils::test_prelude::*; // Use ModifyInputsContext to locate the transform node - use crate::messages::portfolio::document::graph_operation::utility_types::{ModifyInputsContext, TransformIn}; + use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::prelude::Message; use glam::DAffine2; use std::collections::VecDeque; diff --git a/node-graph/gcore/src/context.rs b/node-graph/gcore/src/context.rs index ae9fc660..10d2f19c 100644 --- a/node-graph/gcore/src/context.rs +++ b/node-graph/gcore/src/context.rs @@ -13,7 +13,7 @@ pub trait ExtractFootprint { fn footprint(&self) -> &Footprint { self.try_footprint().unwrap_or_else(|| { log::error!("Context did not have a footprint, called from: {}", Location::caller()); - &const { Footprint::empty() } + &Footprint::DEFAULT }) } } @@ -76,7 +76,7 @@ impl ExtractFootprint for Option { fn footprint(&self) -> &Footprint { self.try_footprint().unwrap_or_else(|| { log::warn!("trying to extract footprint from context None {} ", Location::caller()); - &const { Footprint::empty() } + &Footprint::DEFAULT }) } } @@ -193,7 +193,7 @@ impl ExtractFootprint for OwnedContextImpl { } impl ExtractTime for OwnedContextImpl { fn try_time(&self) -> Option { - self.time + self.real_time } } impl ExtractAnimationTime for OwnedContextImpl { @@ -245,10 +245,23 @@ pub struct OwnedContextImpl { parent: Option>, // This could be converted into a single enum to save extra bytes index: Option, - time: Option, + real_time: Option, animation_time: Option, } +impl core::fmt::Debug for OwnedContextImpl { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("OwnedContextImpl") + .field("footprint", &self.footprint) + .field("varargs", &self.varargs) + .field("parent", &self.parent.as_ref().map(|_| "")) + .field("index", &self.index) + .field("real_time", &self.real_time) + .field("animation_time", &self.animation_time) + .finish() + } +} + impl Default for OwnedContextImpl { #[track_caller] fn default() -> Self { @@ -259,10 +272,11 @@ impl Default for OwnedContextImpl { impl core::hash::Hash for OwnedContextImpl { fn hash(&self, state: &mut H) { self.footprint.hash(state); - self.index.hash(state); - self.time.map(|x| x.to_bits()).hash(state); - self.parent.as_ref().map(|x| Arc::as_ptr(x).addr()).hash(state); self.varargs.as_ref().map(|x| Arc::as_ptr(x).addr()).hash(state); + self.parent.as_ref().map(|x| Arc::as_ptr(x).addr()).hash(state); + self.index.hash(state); + self.real_time.map(|x| x.to_bits()).hash(state); + self.animation_time.map(|x| x.to_bits()).hash(state); } } @@ -273,13 +287,16 @@ impl OwnedContextImpl { let index = value.try_index(); let time = value.try_time(); let frame_time = value.try_animation_time(); - let parent = value.arc_clone(); + let parent = match value.varargs_len() { + Ok(x) if x > 0 => value.arc_clone(), + _ => None, + }; OwnedContextImpl { footprint, varargs: None, parent, index, - time, + real_time: time, animation_time: frame_time, } } @@ -289,7 +306,7 @@ impl OwnedContextImpl { varargs: None, parent: None, index: None, - time: None, + real_time: None, animation_time: None, } } @@ -303,8 +320,8 @@ impl OwnedContextImpl { self.footprint = Some(footprint); self } - pub fn with_time(mut self, time: f64) -> Self { - self.time = Some(time); + pub fn with_real_time(mut self, time: f64) -> Self { + self.real_time = Some(time); self } pub fn with_animation_time(mut self, animation_time: f64) -> Self { @@ -314,6 +331,10 @@ impl OwnedContextImpl { pub fn into_context(self) -> Option> { Some(Arc::new(self)) } + pub fn erase_parent(mut self) -> Self { + self.parent = None; + self + } } #[derive(Default, Clone, Copy, dyn_any::DynAny)] diff --git a/node-graph/gcore/src/memo.rs b/node-graph/gcore/src/memo.rs index cdea7b63..e7beb149 100644 --- a/node-graph/gcore/src/memo.rs +++ b/node-graph/gcore/src/memo.rs @@ -25,6 +25,7 @@ where let mut hasher = DefaultHasher::new(); input.hash(&mut hasher); let hash = hasher.finish(); + if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) { Box::pin(async move { data }) } else { diff --git a/node-graph/gcore/src/transform.rs b/node-graph/gcore/src/transform.rs index 35ceba75..509aaa57 100644 --- a/node-graph/gcore/src/transform.rs +++ b/node-graph/gcore/src/transform.rs @@ -4,7 +4,8 @@ use crate::raster::bbox::AxisAlignedBbox; use crate::raster::image::ImageFrameTable; use crate::vector::VectorDataTable; use crate::{Artboard, ArtboardGroupTable, CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicGroupTable, OwnedContextImpl}; -use glam::{DAffine2, DVec2}; +use core::f64; +use glam::{DAffine2, DMat2, DVec2}; pub trait Transform { fn transform(&self) -> DAffine2; @@ -94,18 +95,26 @@ pub struct Footprint { impl Default for Footprint { fn default() -> Self { - Self::empty() + Self::DEFAULT } } impl Footprint { - pub const fn empty() -> Self { - Self { - transform: DAffine2::IDENTITY, - resolution: glam::UVec2::new(1920, 1080), - quality: RenderQuality::Full, - } - } + pub const DEFAULT: Self = Self { + transform: DAffine2::IDENTITY, + resolution: glam::UVec2::new(1920, 1080), + quality: RenderQuality::Full, + }; + + pub const BOUNDLESS: Self = Self { + transform: DAffine2 { + matrix2: DMat2::from_diagonal(DVec2::splat(f64::INFINITY)), + translation: DVec2::ZERO, + }, + resolution: glam::UVec2::new(0, 0), + quality: RenderQuality::Full, + }; + pub fn viewport_bounds_in_local_space(&self) -> AxisAlignedBbox { let inverse = self.transform.inverse(); let start = inverse.transform_point2((0., 0.).into()); @@ -198,3 +207,38 @@ fn replace_transform( } data } + +#[node_macro::node(category("Debug"))] +async fn boundless_footprint( + ctx: impl Ctx + CloneVarArgs + ExtractAll, + #[implementations( + Context -> VectorDataTable, + Context -> GraphicGroupTable, + Context -> ImageFrameTable, + Context -> TextureFrameTable, + Context -> String, + Context -> f64, + )] + transform_target: impl Node, Output = T>, +) -> T { + let ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::BOUNDLESS); + + transform_target.eval(ctx.into_context()).await +} +#[node_macro::node(category("Debug"))] +async fn freeze_real_time( + ctx: impl Ctx + CloneVarArgs + ExtractAll, + #[implementations( + Context -> VectorDataTable, + Context -> GraphicGroupTable, + Context -> ImageFrameTable, + Context -> TextureFrameTable, + Context -> String, + Context -> f64, + )] + transform_target: impl Node, Output = T>, +) -> T { + let ctx = OwnedContextImpl::from(ctx).with_real_time(0.); + + transform_target.eval(ctx.into_context()).await +} diff --git a/node-graph/gstd/src/wasm_application_io.rs b/node-graph/gstd/src/wasm_application_io.rs index 964b5db4..74620cc6 100644 --- a/node-graph/gstd/src/wasm_application_io.rs +++ b/node-graph/gstd/src/wasm_application_io.rs @@ -237,7 +237,7 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>( let footprint = render_config.viewport; let ctx = OwnedContextImpl::default() .with_footprint(footprint) - .with_time(render_config.time.time) + .with_real_time(render_config.time.time) .with_animation_time(render_config.time.animation_time.as_secs_f64()) .into_context(); ctx.footprint(); @@ -246,10 +246,10 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>( let render_params = RenderParams::new(render_config.view_mode, None, false, hide_artboards, for_export); let data = data.eval(ctx.clone()).await; - let editor_api = editor_api.eval(ctx.clone()).await; + let editor_api = editor_api.eval(None).await; #[cfg(all(feature = "vello", target_arch = "wasm32"))] - let surface_handle = _surface_handle.eval(ctx.clone()).await; + let surface_handle = _surface_handle.eval(None).await; let use_vello = editor_api.editor_preferences.use_vello(); #[cfg(all(feature = "vello", target_arch = "wasm32"))] diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 36d72c39..29769235 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -307,6 +307,8 @@ fn node_registry() -> HashMap, input: Context, fn_params: [Context => wgpu_executor::WindowHandle]), async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::SurfaceFrame]), async_node!(graphene_core::memo::MemoNode<_, _>, input: UVec2, fn_params: [UVec2 => graphene_std::SurfaceFrame]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => f64]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => String]), async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]), async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicElement]), async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),