diff --git a/node-graph/interpreted-executor/tests/graphene_spike.rs b/node-graph/interpreted-executor/tests/graphene_spike.rs new file mode 100644 index 0000000000..ad5524acb0 --- /dev/null +++ b/node-graph/interpreted-executor/tests/graphene_spike.rs @@ -0,0 +1,291 @@ +use std::any::Any; +use std::mem::MaybeUninit; +use std::ops::Add; + +use core_types::arena::{Arena, ArenaCell}; +use core_types::context::{ContextImpl, Ctx, EvalScope, ExtractArena, InjectIndex}; +use core_types::gnode::{BatchStatus, GNode, StatusCell}; +use core_types::gpoll::{ErrorKind, Finality, GPoll, Interrupt}; + +fn add, B, C: Ctx>(_ctx: &C, augend: A, addend: B) -> >::Output { + augend + addend +} + +struct AddNode { + augend: Node0, + addend: Node1, +} + +impl AddNode { + fn new(augend: Node0, addend: Node1) -> Self { + Self { augend, addend } + } +} + +impl GNode for AddNode +where + A: Add, + Input: Ctx, + Node0: GNode, + Node1: GNode, +{ + type Output = >::Output; + + fn eval(&self, input: &Input) -> GPoll { + let cell = StatusCell::new(); + let augend = match cell.eval_input(0, &self.augend, input) { + Ok(value) => value, + Err(interrupt) => return interrupt.into(), + }; + let addend = match cell.eval_input(1, &self.addend, input) { + Ok(value) => value, + Err(interrupt) => return interrupt.into(), + }; + cell.finish(add(input, augend, addend)) + } +} + +struct ValueNode(T); + +impl GNode for ValueNode { + type Output = T; + + fn eval(&self, _input: &Input) -> GPoll { + GPoll::Final(self.0.clone()) + } +} + +struct ReadIndexNode; + +impl GNode for ReadIndexNode { + type Output = f64; + + fn eval(&self, input: &Input) -> GPoll { + GPoll::Final(input.index_value() as f64) + } +} + +trait ExtractIndexValue { + fn index_value(&self) -> u64; +} + +impl ExtractIndexValue for ContextImpl<'_> { + fn index_value(&self) -> u64 { + self.index_head().index + } +} + +fn string_length(_ctx: &C, value: &String) -> f64 { + value.len() as f64 +} + +struct LendStringNode { + value: String, + cell: ArenaCell, +} + +impl LendStringNode { + fn new(value: String) -> Self { + Self { + value, + cell: ArenaCell::new(), + } + } +} + +impl<'e, Input> GNode for LendStringNode +where + Input: Ctx + ExtractArena, +{ + type Output = &'e String; + + fn eval(&self, input: &Input) -> GPoll<&'e String> { + let arena = input.arena(); + if let Some(value) = self.cell.load(arena) { + return GPoll::Final(value); + } + match arena.alloc(self.value.clone()) { + Some((value, weak)) => { + self.cell.store(weak); + GPoll::Final(value) + } + None => GPoll::arena_exhausted(), + } + } +} + +struct StringLengthNode { + value: Node0, +} + +impl StringLengthNode { + fn new(value: Node0) -> Self { + Self { value } + } +} + +impl<'e, Input, Node0> GNode for StringLengthNode +where + Input: Ctx, + Node0: GNode, +{ + type Output = f64; + + fn eval(&self, input: &Input) -> GPoll { + let cell = StatusCell::new(); + let value = match cell.eval_input(0, &self.value, input) { + Ok(value) => value, + Err(interrupt) => return interrupt.into(), + }; + cell.finish(string_length(input, value)) + } +} + +type ErasedGNode = dyn for<'c> GNode, Output = T>; +type ErasedLendEdge = dyn for<'c> GNode, Output = &'c String>; + +fn string_length_constructor(args: Vec>) -> Result>, &'static str> { + let mut args = args.into_iter(); + let value = *args.next().ok_or("arity")?.downcast::>().map_err(|_| "type")?; + Ok(Box::new(StringLengthNode::new(value))) +} + +fn add_constructor_f64(args: Vec>) -> Result>, &'static str> { + let mut args = args.into_iter(); + let augend = *args.next().ok_or("arity")?.downcast::>>().map_err(|_| "type")?; + let addend = *args.next().ok_or("arity")?.downcast::>>().map_err(|_| "type")?; + Ok(Box::new(AddNode::new(augend, addend))) +} + +fn scope_fixture<'a>(generations: &'a [(u64, u64)], arena: &'a Arena) -> EvalScope<'a> { + EvalScope::new(Some(0.5), None, None, generations, arena) +} + +#[test] +fn hand_expansion_evaluates_through_typed_erased_edges() { + let arena = Arena::new(1024); + let generations = []; + let scope = scope_fixture(&generations, &arena); + let ctx = ContextImpl::root(&scope); + + let augend: Box = Box::new(Box::new(ValueNode(1.0f64)) as Box>); + let addend: Box = Box::new(Box::new(ValueNode(2.0f64)) as Box>); + let wired = add_constructor_f64(vec![augend, addend]).unwrap(); + + assert_eq!(wired.eval(&ctx), GPoll::Final(3.0)); +} + +#[test] +fn wiring_rejects_type_and_arity_mismatches() { + let augend: Box = Box::new(Box::new(ValueNode(1.0f64)) as Box>); + let addend: Box = Box::new(Box::new(ValueNode(2u32)) as Box>); + assert_eq!(add_constructor_f64(vec![augend, addend]).map(|_| ()), Err("type")); + + let augend: Box = Box::new(Box::new(ValueNode(1.0f64)) as Box>); + assert_eq!(add_constructor_f64(vec![augend]).map(|_| ()), Err("arity")); +} + +#[test] +fn spec_loop_batches_through_the_erased_edge() { + let arena = Arena::new(1024); + let generations = []; + let scope = scope_fixture(&generations, &arena); + let ctx = ContextImpl::root(&scope); + + let graph: Box> = Box::new(AddNode::new(ReadIndexNode, ValueNode(10.0f64))); + let mut scratch = [const { MaybeUninit::uninit() }; 4]; + let status = graph.eval_batch(&ctx, 2..6, Some(&mut scratch)); + let BatchStatus::Filled(lanes, finality) = status else { + panic!("expected filled, got {status:?}"); + }; + assert_eq!(lanes, &[12.0, 13.0, 14.0, 15.0]); + assert_eq!(finality, Finality::AllFinal); +} + +#[test] +fn lending_kernel_clones_once_per_generation_and_lends_after() { + let arena = Arena::new(1024); + let generations = []; + let scope = scope_fixture(&generations, &arena); + let ctx = ContextImpl::root(&scope); + + let node = LendStringNode::new("lend me".to_string()); + let GPoll::Final(first) = node.eval(&ctx) else { + panic!("first eval must clone into the arena and lend"); + }; + let GPoll::Final(second) = node.eval(&ctx) else { + panic!("second eval must hit the cell"); + }; + assert_eq!(first, "lend me"); + assert!(std::ptr::eq(first, second)); +} + +#[test] +fn exhausted_arena_reports_the_operational_error() { + let arena = Arena::new(0); + let generations = []; + let scope = scope_fixture(&generations, &arena); + let ctx = ContextImpl::root(&scope); + + let node = LendStringNode::new("too big".to_string()); + let GPoll::Error(error) = node.eval(&ctx) else { + panic!("exhaustion must surface as an operational error"); + }; + assert_eq!(error.kind, ErrorKind::ArenaExhausted); +} + +#[test] +fn lending_edges_erase_and_wire_like_owned_edges() { + let arena = Arena::new(1024); + let generations = []; + let scope = scope_fixture(&generations, &arena); + let ctx = ContextImpl::root(&scope); + + let value: Box = Box::new(Box::new(LendStringNode::new("across the boundary".to_string())) as Box); + let wired = string_length_constructor(vec![value]).unwrap(); + + assert_eq!(wired.eval(&ctx), GPoll::Final(19.0)); +} + +#[test] +fn spec_loop_batches_through_the_erased_lending_edge() { + let arena = Arena::new(1024); + let generations = []; + let scope = scope_fixture(&generations, &arena); + let ctx = ContextImpl::root(&scope); + + let graph: Box = Box::new(LendStringNode::new("batched".to_string())); + let mut scratch = [const { MaybeUninit::uninit() }; 3]; + let status = graph.eval_batch(&ctx, 0..3, Some(&mut scratch)); + let BatchStatus::Filled(lanes, finality) = status else { + panic!("expected filled, got {status:?}"); + }; + assert_eq!(lanes.len(), 3); + assert!(lanes.iter().all(|lane| std::ptr::eq(*lane, lanes[0]))); + assert_eq!(*lanes[0], "batched"); + assert_eq!(finality, Finality::AllFinal); +} + +#[test] +fn fallback_input_records_partiality_invisibly() { + struct FallbackNode; + impl GNode for FallbackNode { + type Output = f64; + fn eval(&self, _input: &Input) -> GPoll { + GPoll::fallback(0.0, "upstream failed") + } + } + + let arena = Arena::new(1024); + let generations = []; + let scope = scope_fixture(&generations, &arena); + let ctx = ContextImpl::root(&scope); + + let graph = AddNode::new(FallbackNode, ValueNode(5.0f64)); + let GPoll::Fallback(boxed) = graph.eval(&ctx) else { + panic!("fallback must propagate with the computed stand-in"); + }; + assert_eq!(boxed.0, 5.0); + assert!(boxed.1.kind == "upstream failed"); + assert_eq!(boxed.1.trace, vec![0]); +} diff --git a/node-graph/nodes/brush/src/brush.rs b/node-graph/nodes/brush/src/brush.rs index 53b0ea0052..64ef3bc3f7 100644 --- a/node-graph/nodes/brush/src/brush.rs +++ b/node-graph/nodes/brush/src/brush.rs @@ -3,15 +3,12 @@ use crate::brush_stroke::{BrushStroke, BrushStyle}; use core_types::blending::BlendMode; use core_types::bounds::{BoundingBox, RenderBoundingBox}; use core_types::color::{Alpha, Color, Pixel, Sample}; -use core_types::generic::FnNode; use core_types::list::{Item, List}; use core_types::math::bbox::{AxisAlignedBbox, Bbox}; -use core_types::registry::FutureWrapperNode; use core_types::transform::Transform; use core_types::uuid::NodeId; -use core_types::value::ClonedNode; use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM}; -use core_types::{Ctx, Node}; +use core_types::Ctx; use glam::{DAffine2, DVec2}; use raster_nodes::blending_nodes::blend_colors; use raster_nodes::std_nodes::{empty_image, extend_image_to_bounds}; @@ -63,7 +60,7 @@ impl Sample for BrushStampGenerator

{ /// The feather exponent is calculated from hardness to determine edge softness. /// Used internally to create the brush texture before stamping it repeatedly along a stroke path. #[node_macro::node(category(""), skip_impl)] -fn brush_stamp_generator(#[unit(" px")] diameter: f64, color: Color, hardness: f64, flow: f64) -> BrushStampGenerator { +fn brush_stamp_generator(_: impl Ctx, #[unit(" px")] diameter: f64, color: Color, hardness: f64, flow: f64) -> BrushStampGenerator { // Diameter let radius = diameter / 2.; @@ -83,9 +80,9 @@ fn brush_stamp_generator(#[unit(" px")] diameter: f64, color: Color, hardness: f /// Used to efficiently paint brush strokes. Applies the same texture repeatedly at different positions with proper blending and boundary handling. #[node_macro::node(category(""), skip_impl)] -fn blit(mut target: List>, texture: Raster, positions: Vec, blend_mode: BlendFn) -> List> +fn blit(_: impl Ctx, mut target: List>, texture: Raster, positions: Vec, blend_mode: BlendFn) -> List> where - BlendFn: for<'any_input> Node<'any_input, (Color, Color), Output = Color>, + BlendFn: Fn(Color, Color) -> Color, { if positions.is_empty() { return target; @@ -125,7 +122,7 @@ where for x in blit_area_offset.x..blit_area_offset.x + blit_area_dimensions.x { let src_pixel = texture.data[texture_index(x, y)]; let dst_pixel = &mut element.data_mut().data[target_index(x + clamp_start.x, y + clamp_start.y)]; - *dst_pixel = blend_mode.eval((src_pixel, *dst_pixel)); + *dst_pixel = blend_mode(src_pixel, *dst_pixel); } } } @@ -134,10 +131,10 @@ where target } -pub async fn create_brush_texture(brush_style: &BrushStyle) -> Raster { - let stamp = brush_stamp_generator(brush_style.diameter, brush_style.color, brush_style.hardness, brush_style.flow); +pub fn create_brush_texture(brush_style: &BrushStyle) -> Raster { + let stamp = brush_stamp_generator(&(), brush_style.diameter, brush_style.color, brush_style.hardness, brush_style.flow); let transform = DAffine2::from_scale_angle_translation(DVec2::splat(brush_style.diameter), 0., -DVec2::splat(brush_style.diameter / 2.)); - let blank_texture = empty_image((), transform, List::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap_or_default(); + let blank_texture = empty_image(&(), transform, List::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap_or_default(); let image = blend_stamp_closure(stamp, blank_texture, |a, b| blend_colors(a, b, BlendMode::Normal, 1.)); image.into_element() @@ -188,7 +185,7 @@ pub fn blend_with_mode(background: Item>, foreground: Item>, @@ -224,7 +221,7 @@ async fn brush( let mut brush_plan = cache.compute_brush_plan(list_item, &draw_strokes); // TODO: Find a way to handle more than one item - let Some(mut actual_image) = extend_image_to_bounds((), List::new_from_item(brush_plan.background), background_bounds).into_iter().next() else { + let Some(mut actual_image) = extend_image_to_bounds(&(), List::new_from_item(brush_plan.background), background_bounds).into_iter().next() else { return List::new(); }; @@ -234,7 +231,7 @@ async fn brush( // TODO: apply rotation from layer to stamp for non-rotationally-symmetric brushes. let mut brush_texture = cache.get_cached_brush(&stroke.style); if brush_texture.is_none() { - let tex = create_brush_texture(&stroke.style).await; + let tex = create_brush_texture(&stroke.style); cache.store_brush(stroke.style.clone(), tex.clone()); brush_texture = Some(tex); } @@ -255,21 +252,14 @@ async fn brush( let stroke_origin_in_layer = bbox.start - snap_offset - DVec2::splat(stroke.style.diameter / 2.); let stroke_to_layer = DAffine2::from_translation(stroke_origin_in_layer) * DAffine2::from_scale(stroke_size); - let normal_blend = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::Normal, 1.)); - let blit_node = BlitNode::new( - FutureWrapperNode::new(ClonedNode::new(brush_texture)), - FutureWrapperNode::new(ClonedNode::new(positions)), - FutureWrapperNode::new(ClonedNode::new(normal_blend)), - ); let blit_target = if idx == 0 { let target = core::mem::take(&mut brush_plan.first_stroke_texture); - extend_image_to_bounds((), List::new_from_item(target), stroke_to_layer) + extend_image_to_bounds(&(), List::new_from_item(target), stroke_to_layer) } else { - empty_image((), stroke_to_layer, List::new_from_element(Color::TRANSPARENT)) - // EmptyImageNode::new(CopiedNode::new(stroke_to_layer), CopiedNode::new(Color::TRANSPARENT)).eval(()) + empty_image(&(), stroke_to_layer, List::new_from_element(Color::TRANSPARENT)) }; - let list = blit_node.eval(blit_target).await; + let list = blit(&(), blit_target, brush_texture, positions, |a, b| blend_colors(a, b, BlendMode::Normal, 1.)); assert_eq!(list.len(), 1); list.into_iter().next().unwrap_or_default() }; @@ -291,7 +281,7 @@ async fn brush( for stroke in trace.into_iter().map(|row| row.into_element()) { let mut brush_texture = cache.get_cached_brush(&stroke.style); if brush_texture.is_none() { - let tex = create_brush_texture(&stroke.style).await; + let tex = create_brush_texture(&stroke.style); cache.store_brush(stroke.style.clone(), tex.clone()); brush_texture = Some(tex); } @@ -305,17 +295,13 @@ async fn brush( _ => BlendMode::Restore, }; - let blend_params = FnNode::new(move |(a, b)| blend_colors(a, b, mask_blend_mode, 1.)); - let blit_node = BlitNode::new( - FutureWrapperNode::new(ClonedNode::new(brush_texture)), - FutureWrapperNode::new(ClonedNode::new(positions)), - FutureWrapperNode::new(ClonedNode::new(blend_params)), - ); - erase_restore_mask = blit_node.eval(List::new_from_item(erase_restore_mask)).await.into_iter().next().unwrap_or_default(); + erase_restore_mask = blit(&(), List::new_from_item(erase_restore_mask), brush_texture, positions, move |a, b| blend_colors(a, b, mask_blend_mode, 1.)) + .into_iter() + .next() + .unwrap_or_default(); } - let blend_params = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::MultiplyAlpha, 1.)); - actual_image = blend_image_closure(erase_restore_mask, actual_image, |a, b| blend_params.eval((a, b))); + actual_image = blend_image_closure(erase_restore_mask, actual_image, |a, b| blend_colors(a, b, BlendMode::MultiplyAlpha, 1.)); } let transform: DAffine2 = actual_image.attribute_cloned_or_default(ATTR_TRANSFORM); @@ -410,16 +396,16 @@ mod test { #[test] fn test_brush_texture() { let size = 20.; - let image = brush_stamp_generator(size, Color::BLACK, 100., 100.); + let image = brush_stamp_generator(&(), size, Color::BLACK, 100., 100.); assert_eq!(image.transform(), DAffine2::from_scale_angle_translation(DVec2::splat(size.ceil()), 0., -DVec2::splat(size / 2.))); // center pixel should be BLACK assert_eq!(image.sample(DVec2::splat(0.), DVec2::ONE), Some(Color::BLACK)); } - #[tokio::test] - async fn test_brush_output_size() { + #[test] + fn test_brush_output_size() { let image = brush( - (), + &(), &BrushCache::default(), List::new_from_element(Raster::new_cpu(Image::::default())), List::new_from_element(BrushStroke { @@ -433,8 +419,7 @@ mod test { blend_mode: BlendMode::Normal, }, }), - ) - .await; + ); assert_eq!(image.element(0).unwrap().width, 20); } } diff --git a/node-graph/nodes/gcore/src/context.rs b/node-graph/nodes/gcore/src/context.rs index 8b11d3d657..e697f5c851 100644 --- a/node-graph/nodes/gcore/src/context.rs +++ b/node-graph/nodes/gcore/src/context.rs @@ -47,7 +47,7 @@ fn read_gradient(ctx: impl Ctx + ExtractVarArgs) -> List { } #[node_macro::node(category("Context"), path(core_types::vector))] -async fn read_position( +fn read_position( ctx: impl Ctx + ExtractPosition, _primary: (), /// The number of nested loops to traverse outwards (from the innermost loop) to get the position from. The most upstream loop is level 0, and downstream loops add levels. @@ -64,7 +64,7 @@ async fn read_position( /// /// Nested loops can enable 2D or higher-dimensional iteration by using the *Loop Level* parameter to read the index from outer levels of loops. #[node_macro::node(category("Context"), path(core_types::vector))] -async fn read_index( +fn read_index( ctx: impl Ctx + ExtractIndex, _primary: (), /// The number of nested loops to traverse outwards (from the innermost loop) to get the index from. The most upstream loop is level 0, and downstream loops add levels. diff --git a/node-graph/nodes/graphic/src/graphic.rs b/node-graph/nodes/graphic/src/graphic.rs index 8698375f6b..f610c0154f 100644 --- a/node-graph/nodes/graphic/src/graphic.rs +++ b/node-graph/nodes/graphic/src/graphic.rs @@ -569,7 +569,7 @@ pub fn wrap_graphic>( /// Converts a list of graphical content into a `Graphic[]` by placing it into an element of a new wrapper `Graphic[]`. /// If it is already a `Graphic[]`, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired. #[node_macro::node(category("General"))] -pub async fn to_graphic( +pub fn to_graphic( _: impl Ctx, #[implementations( List, @@ -587,7 +587,7 @@ pub async fn to_graphic( /// Removes a level of nesting from a `Graphic[]`, or all nesting if "Fully Flatten" is enabled. #[node_macro::node(category("General"))] -pub async fn flatten_graphic(_: impl Ctx, content: List, fully_flatten: bool) -> List { +pub fn flatten_graphic(_: impl Ctx, content: List, fully_flatten: bool) -> List { // TODO: Avoid mutable reference, instead return a new List? fn flatten_list(output_graphic_list: &mut List, current_graphic_list: List, fully_flatten: bool, recursion_depth: usize) { for index in 0..current_graphic_list.len() { @@ -624,7 +624,7 @@ pub async fn flatten_graphic(_: impl Ctx, content: List, fully_flatten: /// Converts a `Graphic[]` into a `Vector[]` by deeply flattening any vector content it contains, and discarding any non-vector content. #[node_macro::node(category("Vector"))] -pub async fn flatten_vector(_: impl Ctx, #[implementations(List, List)] content: T) -> List { +pub fn flatten_vector(_: impl Ctx, #[implementations(List, List)] content: T) -> List { let graphic_list = content.into_graphic_list(); let mut output: List = graphic_list.clone().into_flattened_list(); @@ -657,19 +657,19 @@ pub async fn flatten_vector(_: impl Ctx, #[implementations(L /// Converts a `Graphic[]` into a `Raster[]` by deeply flattening any raster content it contains, and discarding any non-raster content. #[node_macro::node(category("Raster"))] -pub async fn flatten_raster(_: impl Ctx, #[implementations(List, List>)] content: T) -> List> { +pub fn flatten_raster(_: impl Ctx, #[implementations(List, List>)] content: T) -> List> { content.into_flattened_list() } /// Converts a `Graphic[]` into a `Color[]` by deeply flattening any color content it contains, and discarding any non-color content. #[node_macro::node(category("General"))] -pub async fn flatten_color(_: impl Ctx, #[implementations(List, List)] content: T) -> List { +pub fn flatten_color(_: impl Ctx, #[implementations(List, List)] content: T) -> List { content.into_flattened_list() } /// Converts a `Graphic[]` into a `GradientStops[]` by deeply flattening any gradient content it contains, and discarding any non-gradient content. #[node_macro::node(category("General"))] -pub async fn flatten_gradient(_: impl Ctx, #[implementations(List, List)] content: T) -> List { +pub fn flatten_gradient(_: impl Ctx, #[implementations(List, List)] content: T) -> List { content.into_flattened_list() } diff --git a/node-graph/nodes/gstd/src/platform_application_io.rs b/node-graph/nodes/gstd/src/platform_application_io.rs index d71f48aa52..7e0362766a 100644 --- a/node-graph/nodes/gstd/src/platform_application_io.rs +++ b/node-graph/nodes/gstd/src/platform_application_io.rs @@ -137,7 +137,7 @@ fn image_to_bytes(_: impl Ctx, image: List>) -> List { /// Loads binary from URLs and local asset paths. Returns a transparent placeholder if the resource fails to load, allowing rendering to continue. #[node_macro::node(category("Web Request"))] -async fn load_resource<'a: 'n>(_: impl Ctx, _primary: (), #[name("URL")] url: String) -> Arc<[u8]> { +async fn load_resource(_: impl Ctx, _primary: (), #[name("URL")] url: String) -> Arc<[u8]> { let placeholder = || -> Arc<[u8]> { Arc::from(Vec::::new()) }; let response = match reqwest::Client::new().get(&url).send().await { @@ -185,14 +185,14 @@ fn decode_image(_: impl Ctx, data: Arc<[u8]>) -> List> { #[cfg(target_family = "wasm")] #[node_macro::node(category(""))] -async fn create_canvas(_: impl Ctx) -> CanvasHandle { +fn create_canvas(_: impl Ctx) -> CanvasHandle { CanvasHandle::new() } /// Renders a view of the input graphic within an area defined by the *Footprint*. #[cfg(target_family = "wasm")] #[node_macro::node(category(""))] -async fn rasterize( +fn rasterize( _: impl Ctx, #[implementations( List, @@ -262,12 +262,12 @@ where } #[node_macro::node(category(""), inject_scope)] -pub async fn editor_api<'a: 'n>(_: impl Ctx, #[scope("editor-api")] editor_api: &'a PlatformEditorApi) -> &'a PlatformEditorApi { +pub fn editor_api<'a>(_: impl Ctx, #[scope("editor-api")] editor_api: &'a PlatformEditorApi) -> &'a PlatformEditorApi { editor_api } #[node_macro::node(category(""))] -pub async fn resource<'a: 'n>(_: impl Ctx, hash: ResourceHash, #[scope(editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi) -> Resource { +pub async fn resource<'a>(_: impl Ctx, hash: ResourceHash, #[scope(editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi) -> Resource { let application_io = editor_api.application_io.as_ref().expect("ApplicationIo must be available when using resources"); application_io.load_resource(hash).await.unwrap_or_else(|| { panic!("Resource {hash} not found"); @@ -275,7 +275,7 @@ pub async fn resource<'a: 'n>(_: impl Ctx, hash: ResourceHash, #[scope(editor_ap } #[node_macro::node(category(""), inject_scope)] -pub async fn wgpu_executor<'a: 'n>(_: impl Ctx, #[scope(editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi) -> &'a ::wgpu_executor::WgpuExecutor { +pub fn wgpu_executor<'a>(_: impl Ctx, #[scope(editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi) -> &'a ::wgpu_executor::WgpuExecutor { editor_api .application_io .as_ref() @@ -285,6 +285,16 @@ pub async fn wgpu_executor<'a: 'n>(_: impl Ctx, #[scope(editor_api::IDENTIFIER)] } #[node_macro::node(category(""), inject_scope)] -pub async fn try_wgpu_executor<'a: 'n>(_: impl Ctx, #[scope(editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi) -> Option<&'a ::wgpu_executor::WgpuExecutor> { +pub fn try_wgpu_executor<'a>(_: impl Ctx, #[scope(editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi) -> Option<&'a ::wgpu_executor::WgpuExecutor> { editor_api.application_io.as_ref()?.gpu_executor() } + +#[node_macro::node(category(""), inject_scope)] +pub fn wgpu_executor_arc<'a>(_: impl Ctx, #[scope(editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi) -> std::sync::Arc<::wgpu_executor::WgpuExecutor> { + editor_api + .application_io + .as_ref() + .expect("ApplicationIo not available") + .gpu_executor_arc() + .expect("GPU executor not available") +} diff --git a/node-graph/nodes/gstd/src/render_background.rs b/node-graph/nodes/gstd/src/render_background.rs index 8b239a47f1..a416d83d34 100644 --- a/node-graph/nodes/gstd/src/render_background.rs +++ b/node-graph/nodes/gstd/src/render_background.rs @@ -12,7 +12,7 @@ use wgpu::util::DeviceExt; use wgpu_executor::{AsyncWgpuPipeline, WgpuExecutor, WgpuPipelineCache}; #[node_macro::node(category(""))] -async fn render_background<'a: 'n>( +fn render_background<'a>( ctx: impl Ctx + ExtractFootprint + ExtractVarArgs, #[scope(composite_background_pipeline::IDENTIFIER)] pipeline: WgpuPipelineCache, data: RenderOutput, @@ -121,7 +121,7 @@ async fn render_background<'a: 'n>( } #[node_macro::node(category(""), inject_scope)] -async fn composite_background_pipeline<'a: 'n>( +fn composite_background_pipeline<'a>( _ctx: impl Ctx, #[scope(crate::platform_application_io::try_wgpu_executor::IDENTIFIER)] executor: Option<&'a WgpuExecutor>, #[data] pipeline: WgpuPipelineCache, diff --git a/node-graph/nodes/gstd/src/render_cache.rs b/node-graph/nodes/gstd/src/render_cache.rs index c21377e6a2..27f27cceb4 100644 --- a/node-graph/nodes/gstd/src/render_cache.rs +++ b/node-graph/nodes/gstd/src/render_cache.rs @@ -321,7 +321,7 @@ fn flood_fill(start: &TileCoord, tile_set: &HashSet, visited: &mut Ha } #[node_macro::node(category(""))] -pub async fn render_output_cache<'a: 'n>( +pub async fn render_output_cache<'a>( ctx: impl Ctx + ExtractAll + CloneVarArgs + ExtractRealTime + ExtractAnimationTime + ExtractPointerPosition + Sync, #[scope(crate::platform_application_io::try_wgpu_executor::IDENTIFIER)] executor: Option<&'a WgpuExecutor>, #[scope(crate::platform_application_io::editor_api::IDENTIFIER)] editor_api: &'a PlatformEditorApi, diff --git a/node-graph/nodes/gstd/src/render_node.rs b/node-graph/nodes/gstd/src/render_node.rs index f53aa69fdc..a8f40074e8 100644 --- a/node-graph/nodes/gstd/src/render_node.rs +++ b/node-graph/nodes/gstd/src/render_node.rs @@ -1,7 +1,7 @@ +use core_types::gpoll::Interrupt; use core_types::list::List; use core_types::transform::{Footprint, Transform}; -use core_types::{CloneVarArgs, ExtractAll, ExtractVarArgs}; -use core_types::{Color, Context, Ctx, ExtractFootprint, OwnedContextImpl, WasmNotSend}; +use core_types::{Color, Context, Ctx, DeriveCtx, ExtractFootprint, ExtractVarArgs, OwnedContextImpl, WasmNotSend}; use graph_craft::document::value::{RenderOutput, RenderOutputType}; use graphene_application_io::{ExportFormat, RenderConfig}; use graphic_types::raster_types::{CPU, Raster}; @@ -23,8 +23,8 @@ pub struct RenderIntermediate { } #[node_macro::node(category(""))] -async fn render_intermediate<'a: 'n, T: 'static + Render + WasmNotSend + Send + Sync>( - ctx: impl Ctx + ExtractVarArgs + ExtractAll + CloneVarArgs, +fn render_intermediate( + ctx: impl Ctx + ExtractVarArgs + DeriveCtx, #[implementations( Context -> List, Context -> List, @@ -35,20 +35,18 @@ async fn render_intermediate<'a: 'n, T: 'static + Render + WasmNotSend + Send + Context -> List, )] data: impl Node, Output = T>, -) -> RenderIntermediate { +) -> Result { + let data = data.eval(&ctx.derived())?; let render_params = ctx .vararg(0) .expect("Did not find var args") .downcast_ref::() .expect("Downcasting render params yielded invalid type"); - let ctx = OwnedContextImpl::from(ctx.clone()).into_context(); - let data = data.eval(ctx).await; - let footprint = Footprint::default(); let mut metadata = RenderMetadata::default(); data.collect_metadata(&mut metadata, footprint, None); - match &render_params.render_output_type { + Ok(match &render_params.render_output_type { RenderOutputTypeRequest::Vello => { let mut scene = vello::Scene::new(); @@ -70,11 +68,11 @@ async fn render_intermediate<'a: 'n, T: 'static + Render + WasmNotSend + Send + metadata, } } - } + }) } #[node_macro::node(category(""))] -async fn render<'a: 'n>( +fn render<'a>( ctx: impl Ctx + ExtractFootprint + ExtractVarArgs, #[scope(crate::platform_application_io::try_wgpu_executor::IDENTIFIER)] executor: Option<&'a WgpuExecutor>, data: RenderIntermediate, @@ -144,7 +142,7 @@ async fn render<'a: 'n>( } #[node_macro::node(category(""))] -async fn create_context<'a: 'n>( +fn create_context<'a>( // Context injections are defined in the wrap_network_in_scope function render_config: RenderConfig, data: impl Node, Output = RenderOutput>, diff --git a/node-graph/nodes/gstd/src/render_pixel_preview.rs b/node-graph/nodes/gstd/src/render_pixel_preview.rs index 549633362d..3247c11114 100644 --- a/node-graph/nodes/gstd/src/render_pixel_preview.rs +++ b/node-graph/nodes/gstd/src/render_pixel_preview.rs @@ -8,7 +8,7 @@ use vector_types::vector::style::RenderMode; use wgpu_executor::{AsyncWgpuPipeline, WgpuExecutor, WgpuPipelineCache}; #[node_macro::node(category(""))] -pub async fn render_pixel_preview<'a: 'n>( +pub async fn render_pixel_preview<'a>( ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync, #[scope(pixel_preview_pipeline::IDENTIFIER)] pipeline: WgpuPipelineCache, data: impl Node, Output = RenderOutput> + Send + Sync, @@ -75,7 +75,7 @@ pub async fn render_pixel_preview<'a: 'n>( } #[node_macro::node(category(""), inject_scope)] -async fn pixel_preview_pipeline<'a: 'n>( +fn pixel_preview_pipeline<'a>( _ctx: impl Ctx, #[scope(crate::platform_application_io::try_wgpu_executor::IDENTIFIER)] executor: Option<&'a WgpuExecutor>, #[data] pipeline: WgpuPipelineCache, diff --git a/node-graph/nodes/path-bool/src/lib.rs b/node-graph/nodes/path-bool/src/lib.rs index 14e2270f98..cc4dc9fbd1 100644 --- a/node-graph/nodes/path-bool/src/lib.rs +++ b/node-graph/nodes/path-bool/src/lib.rs @@ -23,7 +23,7 @@ pub use vector_types::vector::misc::BooleanOperation; /// Combines the geometric forms of one or more closed paths into a new vector path that results from cutting or joining the paths by the chosen method. #[node_macro::node(category("Vector: Modifier"), memoize)] -async fn boolean_operation( +fn boolean_operation( _: impl Ctx, /// The `List` of vector paths to perform the boolean operation on. Nested `List`s are automatically flattened. #[implementations(List, List)] diff --git a/node-graph/nodes/raster/src/dehaze.rs b/node-graph/nodes/raster/src/dehaze.rs index b236dafa52..711fb47b0f 100644 --- a/node-graph/nodes/raster/src/dehaze.rs +++ b/node-graph/nodes/raster/src/dehaze.rs @@ -8,7 +8,7 @@ use raster_types::{CPU, Raster}; use std::cmp::{max, min}; #[node_macro::node(category("Raster: Filter"))] -async fn dehaze(_: impl Ctx, image_frame: List>, strength: Percentage) -> List> { +fn dehaze(_: impl Ctx, image_frame: List>, strength: Percentage) -> List> { image_frame .into_iter() .map(|mut row| { diff --git a/node-graph/nodes/raster/src/filter.rs b/node-graph/nodes/raster/src/filter.rs index 373298a016..cfa29435d0 100644 --- a/node-graph/nodes/raster/src/filter.rs +++ b/node-graph/nodes/raster/src/filter.rs @@ -87,7 +87,7 @@ fn unpremultiply_gamma_to_linear(buffer: Image) -> Imag /// Blurs the image with a Gaussian or box blur kernel filter. #[node_macro::node(category("Raster: Filter"))] -async fn blur( +fn blur( _: impl Ctx, /// The image to be blurred. image_frame: List>, @@ -124,7 +124,7 @@ async fn blur( /// Applies a median filter to reduce noise while preserving edges. #[node_macro::node(category("Raster: Filter"))] -async fn median_filter( +fn median_filter( _: impl Ctx, /// The image to be filtered. image_frame: List>, diff --git a/node-graph/nodes/raster/src/gradient_map.rs b/node-graph/nodes/raster/src/gradient_map.rs index db3b949447..c314332a8c 100644 --- a/node-graph/nodes/raster/src/gradient_map.rs +++ b/node-graph/nodes/raster/src/gradient_map.rs @@ -10,7 +10,7 @@ use vector_types::GradientStops; // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27grdm%27%20%3D%20Gradient%20Map // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Gradient%20settings%20(Photoshop%206.0) #[node_macro::node(category("Raster: Adjustment"))] -async fn gradient_map>( +fn gradient_map>( _: impl Ctx, #[implementations( List>, diff --git a/node-graph/nodes/raster/src/image_color_palette.rs b/node-graph/nodes/raster/src/image_color_palette.rs index 240e51d5ff..9bcd4b7e86 100644 --- a/node-graph/nodes/raster/src/image_color_palette.rs +++ b/node-graph/nodes/raster/src/image_color_palette.rs @@ -4,7 +4,7 @@ use core_types::list::{Item, List}; use raster_types::{CPU, Raster}; #[node_macro::node(category("Color"))] -async fn image_color_palette( +fn image_color_palette( _: impl Ctx, image: List>, #[default(4)] diff --git a/node-graph/nodes/transform/src/transform_nodes.rs b/node-graph/nodes/transform/src/transform_nodes.rs index ee85f409a2..7439f7bfa7 100644 --- a/node-graph/nodes/transform/src/transform_nodes.rs +++ b/node-graph/nodes/transform/src/transform_nodes.rs @@ -108,7 +108,7 @@ fn replace_transform( // TODO: Figure out how this node should behave once #2982 is implemented. /// Obtains the transform of the first item in the input `List`, if present. #[node_macro::node(category("Math: Transform"), path(core_types::vector))] -async fn extract_transform(_: impl Ctx, content: ListDyn) -> DAffine2 { +fn extract_transform(_: impl Ctx, content: ListDyn) -> DAffine2 { content.attribute::(ATTR_TRANSFORM, 0).copied().unwrap_or_default() } diff --git a/node-graph/nodes/vector/src/vector_modification_nodes.rs b/node-graph/nodes/vector/src/vector_modification_nodes.rs index e473c8184f..5123466c4e 100644 --- a/node-graph/nodes/vector/src/vector_modification_nodes.rs +++ b/node-graph/nodes/vector/src/vector_modification_nodes.rs @@ -7,7 +7,7 @@ use vector_types::vector::VectorModification; /// Applies a differential modification to a vector path, associating changes made by the Pen and Path tools to indices of edited points and segments. #[node_macro::node(category(""))] -async fn path_modify(_ctx: impl Ctx, mut vector: List, modification: Box, node_path: List) -> List { +fn path_modify(_ctx: impl Ctx, mut vector: List, modification: Box, node_path: List) -> List { use core_types::list::Item; if vector.is_empty() { @@ -35,7 +35,7 @@ async fn path_modify(_ctx: impl Ctx, mut vector: List, modification: Box /// Applies the vector path's local transformation to its geometry and resets the transform to the identity. #[node_macro::node(category("Vector"))] -async fn apply_transform(_ctx: impl Ctx, mut vector: List) -> List { +fn apply_transform(_ctx: impl Ctx, mut vector: List) -> List { let (elements, transforms) = vector.element_and_attribute_slices_mut::(ATTR_TRANSFORM); for (element, transform) in elements.iter_mut().zip(transforms.iter_mut()) { for (_, point) in element.point_domain.positions_mut() { diff --git a/node-graph/nodes/vector/src/vector_nodes.rs b/node-graph/nodes/vector/src/vector_nodes.rs index 113d38c94b..5b7a51ad83 100644 --- a/node-graph/nodes/vector/src/vector_nodes.rs +++ b/node-graph/nodes/vector/src/vector_nodes.rs @@ -89,7 +89,7 @@ impl VectorListIterMut for List { /// Uniquely sets the fill and/or stroke style of every vector element to individual colors sampled along a chosen gradient. #[node_macro::node(category("Vector: Style"), path(graphene_core::vector))] -async fn assign_colors( +fn assign_colors( _: impl Ctx, /// The content with vector paths to apply the fill and/or stroke style to. #[implementations(List, List)] @@ -157,7 +157,7 @@ where /// Applies a fill style to the vector content, giving an appearance to the area within the interior of the geometry. #[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))] -async fn fill( +fn fill( _: impl Ctx, /// The content with vector paths to apply the fill style to. #[implementations( @@ -252,7 +252,7 @@ impl IntoF64Vec for String { /// Applies a stroke style to the vector content, giving an appearance to the area within the outline of the geometry. #[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("stroke_properties"))] -async fn stroke( +fn stroke( _: impl Ctx, /// The content with vector paths to apply the stroke style to. #[implementations( @@ -357,7 +357,7 @@ where } #[node_macro::node(name("Copy to Points"), category("Repeat"), path(core_types::vector))] -async fn copy_to_points( +fn copy_to_points( _: impl Ctx, points: List, /// Artwork to be copied and placed at each point. @@ -441,7 +441,7 @@ async fn copy_to_points( } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn round_corners( +fn round_corners( _: impl Ctx, source: List, #[hard(0..)] @@ -778,7 +778,7 @@ pub mod extrude_algorithms { } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn extrude(_: impl Ctx, mut source: List, direction: DVec2, joining_algorithm: ExtrudeJoiningAlgorithm) -> List { +fn extrude(_: impl Ctx, mut source: List, direction: DVec2, joining_algorithm: ExtrudeJoiningAlgorithm) -> List { for vector in source.iter_element_values_mut() { extrude_algorithms::extrude(vector, direction, joining_algorithm); } @@ -786,7 +786,7 @@ async fn extrude(_: impl Ctx, mut source: List, direction: DVec2, joinin } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn box_warp(_: impl Ctx, content: List, #[expose] rectangle: List) -> List { +fn box_warp(_: impl Ctx, content: List, #[expose] rectangle: List) -> List { let Some(target) = rectangle.element(0).cloned() else { return content }; let target_transform: DAffine2 = rectangle.attribute_cloned_or_default(ATTR_TRANSFORM, 0); @@ -871,7 +871,7 @@ fn bilinear_interpolate(t: DVec2, quad: &[DVec2; 4]) -> DVec2 { } #[node_macro::node(category("Vector"), path(graphene_core::vector))] -async fn pack_strips( +fn pack_strips( _: impl Ctx, #[implementations( List, @@ -992,7 +992,7 @@ where /// Automatically constructs tangents (Bézier handles) for anchor points in a vector path. #[node_macro::node(category("Vector: Modifier"), name("Auto-Tangents"), path(core_types::vector))] -async fn auto_tangents( +fn auto_tangents( _: impl Ctx, source: List, /// The amount of spread for the auto-tangents, from 0 (sharp corner) to 1 (full spread). @@ -1146,7 +1146,7 @@ async fn auto_tangents( } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn bounding_box(_: impl Ctx, content: List) -> List { +fn bounding_box(_: impl Ctx, content: List) -> List { content .into_iter() .map(|mut row| { @@ -1171,7 +1171,7 @@ async fn bounding_box(_: impl Ctx, content: List) -> List { } #[node_macro::node(category("Vector: Measure"), path(core_types::vector))] -async fn dimensions(_: impl Ctx, content: List) -> DVec2 { +fn dimensions(_: impl Ctx, content: List) -> DVec2 { (0..content.len()) .filter_map(|index| content.element(index).unwrap().bounding_box_with_transform(content.attribute_cloned_or_default(ATTR_TRANSFORM, index))) .reduce(|[acc_top_left, acc_bottom_right], [top_left, bottom_right]| [acc_top_left.min(top_left), acc_bottom_right.max(bottom_right)]) @@ -1187,7 +1187,7 @@ fn as_vector(_: impl Ctx, value: List) -> List { /// Creates a polyline from a series of vector points, replacing any existing segments and regions that may already exist. #[node_macro::node(category("Vector"), name("Points to Polyline"), path(core_types::vector))] -async fn points_to_polyline(_: impl Ctx, mut points: List, #[default(true)] closed: bool) -> List { +fn points_to_polyline(_: impl Ctx, mut points: List, #[default(true)] closed: bool) -> List { for vector in points.iter_element_values_mut() { let mut segment_domain = SegmentDomain::new(); let mut next_id = SegmentId::ZERO; @@ -1215,7 +1215,7 @@ async fn points_to_polyline(_: impl Ctx, mut points: List, #[default(tru } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector), properties("offset_path_properties"))] -async fn offset_path(_: impl Ctx, content: List, distance: f64, join: StrokeJoin, #[default(4.)] miter_limit: f64) -> List { +fn offset_path(_: impl Ctx, content: List, distance: f64, join: StrokeJoin, #[default(4.)] miter_limit: f64) -> List { content .into_iter() .map(|mut row| { @@ -1259,7 +1259,7 @@ async fn offset_path(_: impl Ctx, content: List, distance: f64, join: St } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn solidify_stroke(_: impl Ctx, #[implementations(List, List)] content: T) -> List { +fn solidify_stroke(_: impl Ctx, #[implementations(List, List)] content: T) -> List { // TODO: Make this node support stroke align, which it currently ignores let graphic_list = content.into_graphic_list(); @@ -1367,7 +1367,7 @@ async fn solidify_stroke(_: impl Ctx, #[implementations(List } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn separate_subpaths(_: impl Ctx, content: List) -> List { +fn separate_subpaths(_: impl Ctx, content: List) -> List { content .into_iter() .flat_map(|row| { @@ -1398,7 +1398,7 @@ async fn separate_subpaths(_: impl Ctx, content: List) -> List { /// Determines if the subpath at the given index (across all vector element subpaths) is closed, meaning its ends are connected together forming a loop. #[node_macro::node(name("Path is Closed"), category("Vector: Measure"), path(core_types::vector))] -async fn path_is_closed( +fn path_is_closed( _: impl Ctx, /// The vector content whose subpaths are inspected. content: List, @@ -1431,7 +1431,7 @@ fn map_points(ctx: impl Ctx + DeriveCtx, content: List, mapped: impl Nod // TODO: Rename to "Combine Paths" and make this happen per-element instead of flattening every element into a single path. The migration for this should then become a Flatten Vector -> Combine Paths pair of nodes. #[node_macro::node(category("Vector"), path(graphene_core::vector))] -pub async fn flatten_path(_: impl Ctx, #[implementations(List, List)] content: T) -> List { +pub fn flatten_path(_: impl Ctx, #[implementations(List, List)] content: T) -> List { let graphic_list = content.into_graphic_list(); let flattened = graphic_list.clone().into_flattened_list::(); @@ -1487,7 +1487,7 @@ pub async fn flatten_path(_: impl Ctx, #[implementations(Lis /// Convert vector geometry into a polyline composed of evenly spaced points. #[node_macro::node(category("Vector: Modifier"), path(core_types::vector), properties("sample_polyline_properties"), memoize)] -async fn sample_polyline( +fn sample_polyline( _: impl Ctx, content: List, spacing: PointSpacingType, @@ -1573,7 +1573,7 @@ async fn sample_polyline( /// Simplifies vector paths by reducing the number of curve segments while preserving the overall shape within the given tolerance. #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn simplify( +fn simplify( _: impl Ctx, /// The vector paths to simplify. content: List, @@ -1617,7 +1617,7 @@ async fn simplify( /// Decimates vector paths into polylines by sampling any curves into line segments, then removing points that don't significantly contribute to the shape using the Ramer-Douglas-Peucker algorithm. #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn decimate( +fn decimate( _: impl Ctx, /// The vector paths to decimate. content: List, @@ -1745,7 +1745,7 @@ async fn decimate( /// /// If multiple subpaths make up the path, the whole number part of the progression value selects the subpath and the decimal part determines the position along it. #[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))] -async fn cut_path( +fn cut_path( _: impl Ctx, /// The path to insert a cut into. mut content: List, @@ -1796,7 +1796,7 @@ async fn cut_path( /// Cuts path segments into separate disconnected pieces where each is a distinct subpath. #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn cut_segments(_: impl Ctx, mut content: List) -> List { +fn cut_segments(_: impl Ctx, mut content: List) -> List { // Iterate through every segment and make a copy of each of its endpoints, then reassign each segment's endpoints to its own unique point copy for vector in content.iter_element_values_mut() { let points_count = vector.point_domain.ids().len(); @@ -1855,7 +1855,7 @@ async fn cut_segments(_: impl Ctx, mut content: List) -> List { /// /// If multiple subpaths make up the path, the whole number part of the progression value selects the subpath and the decimal part determines the position along it. #[node_macro::node(name("Position on Path"), category("Vector: Measure"), path(graphene_core::vector))] -async fn position_on_path( +fn position_on_path( _: impl Ctx, /// The path to traverse. content: List, @@ -1893,7 +1893,7 @@ async fn position_on_path( /// /// If multiple subpaths make up the path, the whole number part of the progression value selects the subpath and the decimal part determines the position along it. #[node_macro::node(name("Tangent on Path"), category("Vector: Measure"), path(graphene_core::vector))] -async fn tangent_on_path( +fn tangent_on_path( _: impl Ctx, /// The path to traverse. content: List, @@ -1941,7 +1941,7 @@ async fn tangent_on_path( } #[node_macro::node(category("Vector: Modifier"), path(core_types::vector), memoize)] -async fn scatter_points( +fn scatter_points( _: impl Ctx, content: List, #[unit(" px")] @@ -1991,7 +1991,7 @@ async fn scatter_points( } #[node_macro::node(name("Spline"), category("Vector: Modifier"), path(core_types::vector))] -async fn spline(_: impl Ctx, content: List) -> List { +fn spline(_: impl Ctx, content: List) -> List { content .into_iter() .filter_map(|mut row| { @@ -2091,7 +2091,7 @@ fn apply_point_deltas(element: &mut Vector, deltas: &[DVec2], transform: DAffine /// Perturbs the positions of anchor points in vector geometry by random amounts and directions. #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn jitter_points( +fn jitter_points( _: impl Ctx, /// The vector geometry with points to be jittered. content: List, @@ -2141,7 +2141,7 @@ async fn jitter_points( /// Displaces anchor points along their normal direction (perpendicular to the path) by a set distance. /// Points with 0 or 3+ segment connections have no well-defined normal and are left in place. #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn offset_points( +fn offset_points( _: impl Ctx, /// The vector geometry with points to be offset. content: List, @@ -2178,7 +2178,7 @@ async fn offset_points( /// /// *Progression* morphs through all objects. Interpolation is linear unless *Path* geometry is provided to control the trajectory between key objects. The **Origins to Polyline** node may be used to create a path with anchor points corresponding to each object. Other nodes can modify its path segments. #[node_macro::node(category("Vector: Modifier"), path(core_types::vector))] -async fn morph( +fn morph( _: impl Ctx, /// The vector objects to interpolate between. Mixed graphic content is deeply flattened to keep only vector elements. #[implementations(List, List)] @@ -3125,19 +3125,19 @@ fn point_inside(_: impl Ctx, source: List, point: DVec2) -> bool { // TODO: Return u32, u64, or usize instead of f64 after #1621 is resolved and has allowed us to implement automatic type conversion in the node graph for nodes with generic type inputs. // TODO: (Currently automatic type conversion only works for concrete types, via the Graphene preprocessor and not the full Graphene type system.) #[node_macro::node(category("General"), path(graphene_core::vector))] -async fn count_elements(_: impl Ctx, content: ListDyn) -> f64 { +fn count_elements(_: impl Ctx, content: ListDyn) -> f64 { content.len() as f64 } #[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))] -async fn count_points(_: impl Ctx, content: List) -> f64 { +fn count_points(_: impl Ctx, content: List) -> f64 { content.iter_element_values().map(|vector| vector.point_domain.positions().len() as f64).sum() } /// Retrieves the vec2 position (in local space) of the anchor point at the specified index in a `List` of vector elements. /// If no value exists at that index, the position (0, 0) is returned. #[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))] -async fn index_points( +fn index_points( _: impl Ctx, /// The vector element or elements containing the anchor points to be retrieved. content: List, @@ -3171,7 +3171,7 @@ async fn index_points( } #[node_macro::node(category("Vector: Measure"), path(core_types::vector))] -async fn path_length(_: impl Ctx, source: List) -> f64 { +fn path_length(_: impl Ctx, source: List) -> f64 { (0..source.len()) .map(|index| { let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);