From a9601ab164b32628e210bfe6384157a302b4dbe2 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sat, 31 Dec 2022 21:12:02 +0100 Subject: [PATCH] Implement the Gaussian Blur node (#933) --- .../document_node_types.rs | 20 + .../node_properties.rs | 7 + node-graph/gcore/src/lib.rs | 2 +- node-graph/gcore/src/ops.rs | 41 ++ node-graph/gcore/src/raster.rs | 432 +++++++++++++++++- node-graph/gcore/src/raster/color.rs | 5 +- node-graph/gcore/src/structural.rs | 3 +- node-graph/gstd/src/any.rs | 17 +- node-graph/gstd/src/memo.rs | 37 +- node-graph/gstd/src/raster.rs | 2 +- .../interpreted-executor/src/node_registry.rs | 63 ++- 11 files changed, 578 insertions(+), 51 deletions(-) diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index d1109e2d..d59feb6e 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -136,6 +136,26 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ outputs: &[FrontendGraphDataType::Raster], properties: node_properties::quantize_properties, }, + DocumentNodeType { + name: "Gaussian Blur", + category: "Image Filters", + identifier: NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), + inputs: &[ + DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), + DocumentInputType::new("Radius", TaggedValue::U32(3), false), + DocumentInputType::new("Sigma", TaggedValue::F64(1.), false), + ], + outputs: &[FrontendGraphDataType::Raster], + properties: node_properties::blur_image_properties, + }, + DocumentNodeType { + name: "Cache", + category: "Structural", + identifier: NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), + inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], + outputs: &[FrontendGraphDataType::Raster], + properties: node_properties::no_properties, + }, DocumentNodeType { name: "Invert RGB", category: "Image Adjustments", diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs index fb7651de..4ee29646 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs @@ -189,6 +189,13 @@ pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId, vec![LayoutGroup::Row { widgets: brightness }, LayoutGroup::Row { widgets: contrast }] } +pub fn blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let radius = number_widget(document_node, node_id, 1, "Radius", NumberInput::new().min(0.).max(20.).int(), true); + let sigma = number_widget(document_node, node_id, 2, "Sigma", NumberInput::new().min(0.).max(10000.), true); + + vec![LayoutGroup::Row { widgets: radius }, LayoutGroup::Row { widgets: sigma }] +} + pub fn adjust_gamma_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let gamma = number_widget(document_node, node_id, 1, "Gamma", NumberInput::new().min(0.01), true); diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index 81246f8a..f87d8cf3 100644 --- a/node-graph/gcore/src/lib.rs +++ b/node-graph/gcore/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index 7f7a3a27..99e42ac9 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -95,6 +95,12 @@ impl<'n, O: Clone> Node<&'n O> for CloneNode { input.clone() } } +impl<'n, O: Clone> Node<&'n O> for &CloneNode { + type Output = O; + fn eval(self, input: &'n O) -> Self::Output { + input.clone() + } +} #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FstNode; @@ -189,6 +195,41 @@ impl IdNode { } } +/// Ascribe the node types +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct TypeNode(pub N, pub PhantomData<(I, O)>); +impl, I> Node for TypeNode { + type Output = N::Output; + fn eval(self, input: I) -> Self::Output { + self.0.eval(input) + } +} +impl + Copy, I> Node for &TypeNode { + type Output = N::Output; + fn eval(self, input: I) -> Self::Output { + self.0.eval(input) + } +} /* + impl, I> Node for &TypeNode { + type Output = N::Output; + fn eval(self, input: I) -> Self::Output { + self.0.eval_ref(input) + } + }*/ + +impl, I> TypeNode { + pub fn new(node: N) -> Self { + Self(node, PhantomData) + } +} + +impl + Clone, I> Clone for TypeNode { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1) + } +} +impl + Copy, I> Copy for TypeNode {} + pub struct MapResultNode(pub MN, pub PhantomData<(I, E)>); impl, I, E> Node> for MapResultNode { diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index fe2ea09a..60142a0f 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -1,3 +1,5 @@ +use core::fmt::Debug; + use crate::Node; pub mod color; @@ -6,24 +8,266 @@ pub use self::color::Color; #[derive(Debug, Clone, Copy, Default)] pub struct GrayscaleColorNode; -impl Node for GrayscaleColorNode { - type Output = Color; - fn eval(self, color: Color) -> Color { - let avg = (color.r() + color.g() + color.b()) / 3.0; - Color::from_rgbaf32_unchecked(avg, avg, avg, color.a()) +#[node_macro::node_fn(GrayscaleColorNode)] +fn grayscale_color_node(input: Color) -> Color { + let avg = (input.r() + input.g() + input.b()) / 3.0; + Color::from_rgbaf32_unchecked(avg, avg, avg, input.a()) +} + +#[derive(Debug)] +pub struct MapNode> { + map_fn: MapFn, + _phantom: core::marker::PhantomData, +} + +impl + Clone> Clone for MapNode { + fn clone(&self) -> Self { + Self { + map_fn: self.map_fn.clone(), + _phantom: self._phantom, + } } } -impl<'n> Node for &'n GrayscaleColorNode { - type Output = Color; - fn eval(self, color: Color) -> Color { - let avg = (color.r() + color.g() + color.b()) / 3.0; - Color::from_rgbaf32_unchecked(avg, avg, avg, color.a()) +impl + Copy> Copy for MapNode {} + +impl> MapNode { + pub fn new(map_fn: MapFn) -> Self { + Self { + map_fn, + _phantom: core::marker::PhantomData, + } } } -impl GrayscaleColorNode { +impl, MapFn: Node, Item, Out> Node for MapNode { + type Output = MapFnIterator; + + #[inline] + fn eval(self, input: Iter) -> Self::Output { + MapFnIterator::new(input, self.map_fn) + } +} + +impl, MapFn: Node + Copy, Item, Out> Node for &MapNode { + type Output = MapFnIterator; + + #[inline] + fn eval(self, input: Iter) -> Self::Output { + MapFnIterator::new(input, self.map_fn) + } +} + +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct MapFnIterator { + iter: Iter, + map_fn: MapFn, +} + +impl Debug for MapFnIterator { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("MapFnIterator").field("iter", &self.iter).field("map_fn", &"MapFn").finish() + } +} + +impl Copy for MapFnIterator {} + +impl MapFnIterator { + pub fn new(iter: Iter, map_fn: MapFn) -> Self { + Self { iter, map_fn } + } +} + +impl Iterator for MapFnIterator +where + F: Node + Copy, +{ + type Item = B; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|x| self.map_fn.eval(x)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct WeightedAvgNode { + _phantom: core::marker::PhantomData, +} + +impl WeightedAvgNode { pub fn new() -> Self { - Self + Self { _phantom: core::marker::PhantomData } + } +} + +#[inline] +fn weighted_avg_node + Clone>(input: Iter) -> Color { + let total_weight: f32 = input.clone().map(|(_, weight)| weight).sum(); + let total_r: f32 = input.clone().map(|(color, weight)| color.r() * weight).sum(); + let total_g: f32 = input.clone().map(|(color, weight)| color.g() * weight).sum(); + let total_b: f32 = input.clone().map(|(color, weight)| color.b() * weight).sum(); + let total_a: f32 = input.map(|(color, weight)| color.a() * weight).sum(); + Color::from_rgbaf32_unchecked(total_r / total_weight, total_g / total_weight, total_b / total_weight, total_a / total_weight) +} + +impl + Clone> Node for WeightedAvgNode { + type Output = Color; + + #[inline] + fn eval(self, input: Iter) -> Self::Output { + weighted_avg_node(input) + } +} +impl + Clone> Node for &WeightedAvgNode { + type Output = Color; + + #[inline] + fn eval(self, input: Iter) -> Self::Output { + weighted_avg_node(input) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct GaussianNode { + sigma: Sigma, +} + +#[node_macro::node_fn(GaussianNode)] +fn gaussian_node(input: f32, sigma: f64) -> f32 { + let sigma = sigma as f32; + (1.0 / (2.0 * core::f32::consts::PI * sigma * sigma).sqrt()) * (-input * input / (2.0 * sigma * sigma)).exp() +} + +#[derive(Debug, Clone, Copy)] +pub struct DistanceNode; + +#[node_macro::node_fn(DistanceNode)] +fn distance_node(input: (i32, i32)) -> f32 { + let (x, y) = input; + ((x * x + y * y) as f32).sqrt() +} + +#[derive(Debug, Clone, Copy)] +pub struct ImageIndexIterNode; + +#[node_macro::node_fn(ImageIndexIterNode)] +fn image_index_iter_node(input: ImageSlice<'static>) -> core::ops::Range { + 0..(input.width * input.height) +} + +#[derive(Debug, Clone, Copy)] +pub struct WindowNode { + radius: Radius, + image: Image, +} + +impl WindowNode { + pub fn new(radius: Radius, image: Image) -> Self { + Self { radius, image } + } +} + +impl<'a, Radius: Node<(), Output = u32>, Image: Node<(), Output = ImageSlice<'a>>> Node for WindowNode { + type Output = ImageWindowIterator<'a>; + #[inline] + fn eval(self, input: u32) -> Self::Output { + let radius = self.radius.eval(()); + let image = self.image.eval(()); + let iter = ImageWindowIterator::new(image, radius, input); + iter + } +} +impl<'a, 'b: 'a, Radius: Node<(), Output = u32> + Copy, Index: Node<(), Output = ImageSlice<'b>> + Copy> Node for &'a WindowNode { + type Output = ImageWindowIterator<'a>; + #[inline] + fn eval(self, input: u32) -> Self::Output { + let radius = self.radius.eval(()); + let image = self.image.eval(()); + let iter = ImageWindowIterator::new(image, radius, input); + iter + } +} + +#[derive(Debug, Clone, Copy)] +pub struct ImageWindowIterator<'a> { + image: ImageSlice<'a>, + radius: u32, + index: u32, + x: u32, + y: u32, +} + +impl<'a> ImageWindowIterator<'a> { + fn new(image: ImageSlice<'a>, radius: u32, index: u32) -> Self { + let start_x = index as i32 % image.width as i32; + let start_y = index as i32 / image.width as i32; + let min_x = (start_x - radius as i32).max(0) as u32; + let min_y = (start_y - radius as i32).max(0) as u32; + + Self { + image, + radius, + index, + x: min_x, + y: min_y, + } + } +} + +impl<'a> Iterator for ImageWindowIterator<'a> { + type Item = (Color, (i32, i32)); + #[inline] + fn next(&mut self) -> Option { + let start_x = self.index as i32 % self.image.width as i32; + let start_y = self.index as i32 / self.image.width as i32; + let radius = self.radius as i32; + + let min_x = (start_x - radius).max(0) as u32; + let max_x = (start_x + radius).min(self.image.width as i32 - 1) as u32; + let max_y = (start_y + radius).min(self.image.height as i32 - 1) as u32; + if self.y > max_y { + return None; + } + let value = Some((self.image.data[(self.x + self.y * self.image.width) as usize], (self.x as i32 - start_x, self.y as i32 - start_y))); + + self.x += 1; + if self.x > max_x { + self.x = min_x; + self.y += 1; + } + value + } +} + +#[derive(Debug, Clone, Copy)] +pub struct MapSndNode { + map_fn: MapFn, +} + +impl MapSndNode { + pub fn new(map_fn: MapFn) -> Self { + Self { map_fn } + } +} + +impl, I, F> Node<(F, I)> for MapSndNode { + type Output = (F, MapFn::Output); + #[inline] + fn eval(self, input: (F, I)) -> Self::Output { + (input.0, self.map_fn.eval(input.1)) + } +} +impl + Copy, I, F> Node<(F, I)> for &MapSndNode { + type Output = (F, MapFn::Output); + #[inline] + fn eval(self, input: (F, I)) -> Self::Output { + (input.0, self.map_fn.eval(input.1)) } } @@ -121,11 +365,75 @@ where } } +use dyn_any::{DynAny, StaticType}; +#[derive(Clone, Debug, PartialEq, DynAny, Default, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +pub struct ImageSlice<'a> { + pub width: u32, + pub height: u32, + pub data: &'a [Color], +} + +impl ImageSlice<'_> { + pub const fn empty() -> Self { + Self { width: 0, height: 0, data: &[] } + } +} + +impl<'a> IntoIterator for ImageSlice<'a> { + type Item = &'a Color; + type IntoIter = core::slice::Iter<'a, Color>; + fn into_iter(self) -> Self::IntoIter { + self.data.iter() + } +} + +impl<'a> IntoIterator for &'a ImageSlice<'a> { + type Item = &'a Color; + type IntoIter = core::slice::Iter<'a, Color>; + fn into_iter(self) -> Self::IntoIter { + self.data.iter() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct MapImageSliceNode(MapFn); + +impl MapImageSliceNode { + pub fn new(map_fn: MapFn) -> Self { + Self(map_fn) + } +} + +impl<'a, MapFn: Node, Output = Vec>> Node> for MapImageSliceNode { + type Output = Image; + fn eval(self, image: ImageSlice<'a>) -> Self::Output { + let data = self.0.eval(image); + Image { + width: image.width, + height: image.height, + data, + } + } +} + +impl<'a, MapFn: Copy + Node, Output = Vec>> Node> for &MapImageSliceNode { + type Output = Image; + fn eval(self, image: ImageSlice<'a>) -> Self::Output { + let data = self.0.eval(image); + Image { + width: image.width, + height: image.height, + data, + } + } +} + #[cfg(feature = "alloc")] -pub use image::Image; +pub use image::{CollectNode, Image, ImageRefNode}; #[cfg(feature = "alloc")] mod image { - use super::Color; + use super::{Color, ImageSlice}; use alloc::vec::Vec; use dyn_any::{DynAny, StaticType}; #[derive(Clone, Debug, PartialEq, DynAny, Default)] @@ -144,6 +452,13 @@ mod image { data: Vec::new(), } } + pub fn as_slice(&self) -> ImageSlice { + ImageSlice { + width: self.width, + height: self.height, + data: self.data.as_slice(), + } + } } impl IntoIterator for Image { @@ -154,11 +469,43 @@ mod image { } } - impl<'a> IntoIterator for &'a Image { - type Item = &'a Color; - type IntoIter = alloc::slice::Iter<'a, Color>; - fn into_iter(self) -> Self::IntoIter { - self.data.iter() + #[derive(Debug, Clone, Copy, Default)] + pub struct ImageRefNode; + + impl ImageRefNode { + pub fn new() -> Self { + Self + } + } + + impl<'a> Node<&'a Image> for ImageRefNode { + type Output = ImageSlice<'a>; + fn eval(self, image: &'a Image) -> Self::Output { + image.as_slice() + } + } + + impl<'a> Node<&'a Image> for &ImageRefNode { + type Output = ImageSlice<'a>; + fn eval(self, image: &'a Image) -> Self::Output { + image.as_slice() + } + } + + #[derive(Debug, Clone, Copy)] + pub struct CollectNode; + + use crate::Node; + impl Node for CollectNode { + type Output = Vec; + fn eval(self, iter: Iter) -> Self::Output { + iter.collect() + } + } + impl Node for &CollectNode { + type Output = Vec; + fn eval(self, iter: Iter) -> Self::Output { + iter.collect() } } } @@ -177,7 +524,14 @@ where #[cfg(test)] mod test { + use crate::{ + ops::TypeNode, + structural::{ComposeNode, Then}, + value::ValueNode, + }; + use super::*; + use alloc::vec::Vec; #[test] fn map_node() { @@ -187,4 +541,44 @@ mod test { (&map).eval(array.iter_mut()); assert_eq!(array[0], Color::from_rgbaf32(0.33333334, 0.33333334, 0.33333334, 1.0).unwrap());*/ } + #[test] + fn window_node() { + let radius = ValueNode::new(1u32); + static data: &[Color] = &[Color::from_rgbf32_unchecked(1., 0., 0.); 25]; + let image = ValueNode::<_>::new(ImageSlice { width: 5, height: 5, data }); + let window = WindowNode::new(radius, image); + //let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window); + let vec = window.eval(0); + assert_eq!(vec.count(), 4); + let vec = window.eval(5); + assert_eq!(vec.count(), 6); + let vec = window.eval(12); + assert_eq!(vec.count(), 9); + } + + #[test] + fn blur_node() { + let radius = ValueNode::new(1u32); + let sigma = ValueNode::new(3f64); + static data: &[Color] = &[Color::from_rgbf32_unchecked(1., 0., 0.); 20]; + let image = ValueNode::<_>::new(ImageSlice { width: 10, height: 2, data }); + let window = WindowNode::new(radius, image); + let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window); + let pos_to_dist = MapSndNode::new(DistanceNode); + let distance = window.then(MapNode::new(pos_to_dist)); + let map_gaussian = MapSndNode::new(GaussianNode::new(sigma)); + let map_distances: MapNode<_, MapSndNode<_>> = MapNode::new(map_gaussian); + let gaussian_iter = distance.then(map_distances); + let avg = gaussian_iter.then(WeightedAvgNode::new()); + let avg: TypeNode<_, u32, Color> = TypeNode::new(avg); + let blur_iter = MapNode::new(avg); + let blur = image.then(ImageIndexIterNode).then(blur_iter); + let blur: TypeNode<_, (), MapFnIterator<_, _>> = TypeNode::new(blur); + let collect = CollectNode {}; + let vec = collect.eval(0..10); + assert_eq!(vec.len(), 10); + let vec = ComposeNode::new(blur, collect); + let vec: TypeNode<_, (), Vec> = TypeNode::new(vec); + let image = vec.eval(()); + } } diff --git a/node-graph/gcore/src/raster/color.rs b/node-graph/gcore/src/raster/color.rs index aca21ce0..c91b74ad 100644 --- a/node-graph/gcore/src/raster/color.rs +++ b/node-graph/gcore/src/raster/color.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "std")] use dyn_any::{DynAny, StaticType}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -17,9 +16,9 @@ use bytemuck::{Pod, Zeroable}; /// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`, /// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color. #[repr(C)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, DynAny))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "gpu", derive(Pod, Zeroable))] -#[derive(Debug, Clone, Copy, PartialEq, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Default, DynAny)] pub struct Color { red: f32, green: f32, diff --git a/node-graph/gcore/src/structural.rs b/node-graph/gcore/src/structural.rs index 1676273c..af1c2828 100644 --- a/node-graph/gcore/src/structural.rs +++ b/node-graph/gcore/src/structural.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use crate::{AsRefNode, Node, RefNode}; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct ComposeNode { first: First, second: Second, @@ -56,7 +56,6 @@ where (self.second).eval_ref(arg) } } -#[cfg(feature = "std")] impl dyn_any::StaticType for ComposeNode { type Static = ComposeNode; } diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index ebcecc4b..4c785991 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -31,7 +31,8 @@ where { type Output = Any<'n>; fn eval(self, input: Any<'n>) -> Self::Output { - let input: Box = dyn_any::downcast(input).expect("DynAnyNode Input"); + let node = core::any::type_name::(); + let input: Box = dyn_any::downcast(input).expect(format!("DynAnyNode Input in:\n{node}").as_str()); Box::new(self.0.eval(*input)) } } @@ -41,7 +42,8 @@ where { type Output = Any<'n>; fn eval(self, input: Any<'n>) -> Self::Output { - let input: Box = dyn_any::downcast(input).expect("DynAnyNode Input"); + let node = core::any::type_name::(); + let input: Box = dyn_any::downcast(input).expect(format!("DynAnyNode Input in:\n{node}").as_str()); Box::new((&self.0).eval_ref(*input)) } } @@ -161,6 +163,17 @@ where *dyn_any::downcast(output).expect("DowncastBothNode Output") } } +impl<'n, N, I: 'n + StaticType, O: 'n + StaticType> Node for &DowncastBothNode +where + N: Node, Output = Any<'n>> + Copy, +{ + type Output = O; + fn eval(self, input: I) -> Self::Output { + let input = Box::new(input) as Box; + let output = self.0.eval(input); + *dyn_any::downcast(output).expect("DowncastBothNode Output") + } +} impl<'n, N, I: StaticType, O: StaticType> DowncastBothNode where N: Node>, diff --git a/node-graph/gstd/src/memo.rs b/node-graph/gstd/src/memo.rs index 5660d960..ea6a9601 100644 --- a/node-graph/gstd/src/memo.rs +++ b/node-graph/gstd/src/memo.rs @@ -2,35 +2,32 @@ use graphene_core::{Cache, Node}; use once_cell::sync::OnceCell; /// Caches the output of a given Node and acts as a proxy -pub struct CacheNode, I> { - node: CachedNode, - cache: OnceCell, +pub struct CacheNode { + cache: OnceCell, } -impl<'n, CashedNode: Node + Copy, I> Node for &'n CacheNode { - type Output = &'n CashedNode::Output; - fn eval(self, input: I) -> Self::Output { +impl<'n, T> Node for &'n CacheNode { + type Output = &'n T; + fn eval(self, input: T) -> Self::Output { self.cache.get_or_init(|| { trace!("Creating new cache node"); - self.node.eval(input) + input }) } } - -impl<'n, CachedNode: Node, I> CacheNode { - pub fn clear(&'n mut self) { - self.cache = OnceCell::new(); - } - pub fn new(node: CachedNode) -> CacheNode { - CacheNode { node, cache: OnceCell::new() } +impl Node for CacheNode { + type Output = T; + fn eval(self, input: T) -> Self::Output { + input } } -impl, I> Cache for CacheNode { + +impl CacheNode { + pub fn new() -> CacheNode { + CacheNode { cache: OnceCell::new() } + } +} +impl Cache for CacheNode { fn clear(&mut self) { self.cache = OnceCell::new(); } } - -/*use dyn_any::{DynAny, StaticType}; -#[derive(DynAny)] -struct Boo<'a>(&'a u8); -*/ diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index f88884e3..121d63b2 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -132,7 +132,7 @@ pub fn export_image_node<'n>() -> impl Node<(Image, &'n str), Output = Result<() FnNode::new(|input: (Image, &str)| { let (image, path) = input; let mut new_image = image::ImageBuffer::new(image.width, image.height); - for ((x, y, pixel), color) in new_image.enumerate_pixels_mut().zip((&image).into_iter()) { + for ((x, y, pixel), color) in new_image.enumerate_pixels_mut().zip((&image).data.iter()) { let color: Color = *color; assert!(x < image.width); assert!(y < image.height); diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 342581ac..22c40270 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -1,10 +1,11 @@ use borrow_stack::FixedSizeStack; use glam::DVec2; use graphene_core::generic::FnNode; -use graphene_core::ops::{AddNode, IdNode}; +use graphene_core::ops::{AddNode, CloneNode, IdNode, TypeNode}; use graphene_core::raster::color::Color; -use graphene_core::raster::Image; -use graphene_core::structural::{ConsNode, Then}; +use graphene_core::raster::{Image, MapFnIterator}; +use graphene_core::structural::{ComposeNode, ConsNode, Then}; +use graphene_core::value::ValueNode; use graphene_core::vector::subpath::Subpath; use graphene_core::Node; use graphene_std::any::DowncastBothNode; @@ -16,6 +17,7 @@ use graph_craft::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeI type NodeConstructor = fn(ProtoNode, &FixedSizeStack>); use graph_craft::{concrete, generic}; +use graphene_std::memo::CacheNode; //TODO: turn into hasmap static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ @@ -478,6 +480,61 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ } }, ), + (NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), |proto_node, stack| { + let node_id = proto_node.input.unwrap_node() as usize; + use graphene_core::raster::*; + if let ConstructionArgs::Nodes(image_args) = proto_node.construction_args { + stack.push_fn(move |nodes| { + let image = nodes.get(node_id).unwrap(); + let node: DynAnyNode<_, Image, Image, &Image> = DynAnyNode::new(CacheNode::new()); + + let node = image.then(node); + node.into_type_erased() + }) + } else { + unimplemented!() + } + }), + (NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), |proto_node, stack| { + let node_id = proto_node.input.unwrap_node() as usize; + use graphene_core::raster::*; + + static EMPTY_IMAGE: ValueNode = ValueNode::new(Image::empty()); + if let ConstructionArgs::Nodes(blur_args) = proto_node.construction_args { + stack.push_fn(move |nodes| { + let pre_node = nodes.get(node_id).unwrap(); + let radius = nodes.get(blur_args[0] as usize).unwrap(); + let sigma = nodes.get(blur_args[1] as usize).unwrap(); + let radius = DowncastBothNode::<_, (), u32>::new(radius); + let sigma = DowncastBothNode::<_, (), f64>::new(sigma); + let image = DowncastBothNode::<_, Image, &Image>::new(pre_node); + // dirty hack: we abuse that the cache node will ignore the input if it is + // evaluated a second time + let empty: TypeNode<_, (), Image> = TypeNode::new((&EMPTY_IMAGE).then(CloneNode)); + let image = empty.then(image).then(ImageRefNode::new()); + let window = WindowNode::new(radius, image); + let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window); + let map_gaussian = MapSndNode::new(DistanceNode.then(GaussianNode::new(sigma))); + let map_distances = MapNode::new(map_gaussian); + let gaussian_iter = window.then(map_distances); + let avg = gaussian_iter.then(WeightedAvgNode::new()); + let avg: TypeNode<_, u32, Color> = TypeNode::new(avg); + let blur_iter = MapNode::new(avg); + let blur = ImageIndexIterNode.then(blur_iter); + let blur: TypeNode<_, ImageSlice<'_>, MapFnIterator<_, _>> = TypeNode::new(blur); + let collect = CollectNode {}; + let vec = blur.then(collect); + let vec: TypeNode<_, ImageSlice<'_>, Vec> = TypeNode::new(vec); + let new_image = MapImageSliceNode::new(vec); + let new_image: TypeNode<_, ImageSlice<'_>, Image> = TypeNode::new(new_image); + let node: DynAnyNode<_, &Image, Image, Image> = DynAnyNode::new(ImageRefNode.then(new_image)); + let node = ComposeNode::new(pre_node, node); + node.into_type_erased() + }) + } else { + unimplemented!() + } + }), (NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| { stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased()) }),