From 17852b540843e750e3558ce3781e405e0df6814f Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Tue, 27 Dec 2022 19:50:20 +0100 Subject: [PATCH 01/10] Start implementing blur node --- node-graph/gcore/src/raster.rs | 274 +++++++++++++++++- .../interpreted-executor/src/node_registry.rs | 32 +- 2 files changed, 293 insertions(+), 13 deletions(-) diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 027b151e931..e6b980da9f1 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -6,24 +6,226 @@ pub use self::color::Color; #[derive(Debug, Clone, Copy)] pub struct GrayscaleColorNode; -impl Node for GrayscaleColorNode { +#[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, Clone, Copy)] +pub struct MapNode { + map_fn: MapFn, + _phantom: core::marker::PhantomData<(Iter, Item, Out)>, +} + +impl MapNode { + pub fn new(map_fn: MapFn) -> Self { + Self { + map_fn, + _phantom: core::marker::PhantomData, + } + } +} + +impl, MapFn: Node, Item, Out> Node for MapNode { + type Output = MapFnIterator; + + 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 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)] +pub struct WeightedAvgNode { + _phantom: core::marker::PhantomData, +} + +impl WeightedAvgNode { + pub fn new() -> Self { + Self { _phantom: core::marker::PhantomData } + } +} + +fn weighted_avg_node + Copy>(input: Iter) -> Color { + let total_weight: f32 = input.map(|(_, weight)| weight).sum(); + let total_r: f32 = input.map(|(color, weight)| color.r() * weight).sum(); + let total_g: f32 = input.map(|(color, weight)| color.g() * weight).sum(); + let total_b: f32 = input.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 + Copy> Node for WeightedAvgNode { 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()) + fn eval(self, input: Iter) -> Self::Output { + weighted_avg_node(input) } } -impl<'n> Node for &'n GrayscaleColorNode { +impl + Copy> Node for &WeightedAvgNode { 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()) + fn eval(self, input: Iter) -> Self::Output { + weighted_avg_node(input) } } -impl GrayscaleColorNode { - pub fn new() -> Self { - Self +#[derive(Debug, Clone, Copy)] +pub struct GaussianNode { + sigma: Sigma, +} + +#[node_macro::node_fn(GaussianNode)] +fn gaussian_node(input: f32, sigma: f32) -> 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: (i32, i32)) -> core::ops::Range { + let (width, height) = input; + 0..(width * height) as u32 +} + +#[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>; + 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>; + 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, (u32, u32)); + 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.x > max_x { + self.x = min_x; + self.y += 1; + } + if self.y > max_y { + return None; + } + Some((self.image.data[(self.x + self.y * self.image.width) as usize], (self.x, self.y))) + } +} + +#[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); + 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); + fn eval(self, input: (F, I)) -> Self::Output { + (input.0, self.map_fn.eval(input.1)) } } @@ -95,8 +297,39 @@ 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() + } +} + #[cfg(feature = "alloc")] -pub use image::Image; +pub use image::{CollectNode, Image}; #[cfg(feature = "alloc")] mod image { use super::Color; @@ -135,6 +368,23 @@ mod image { self.data.iter() } } + + #[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() + } + } } /*pub struct MutWrapper(pub N); diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index ab72d24411b..fba8b4d9b25 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -3,7 +3,7 @@ use glam::DVec2; use graphene_core::generic::FnNode; use graphene_core::ops::{AddNode, IdNode}; use graphene_core::raster::color::Color; -use graphene_core::raster::Image; +use graphene_core::raster::{Image, MapFnIterator}; use graphene_core::structural::{ConsNode, Then}; use graphene_core::vector::subpath::Subpath; use graphene_core::Node; @@ -452,6 +452,36 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ } }, ), + (NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), |proto_node, stack| { + let node_id = proto_node.input.unwrap_node() as usize; + use graphene_core::raster::{MapNode, MapSndNode}; + 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::<_, (), f32>::new(sigma); + let window = graphene_core::raster::WindowNode::new(radius, pre_node); + let pos_to_dist = MapSndNode::new(graphene_core::raster::DistanceNode); + let distance = window.then(MapNode::new(pos_to_dist)); + let map_gaussian = MapSndNode::new(graphene_core::raster::GaussianNode::new(sigma)); + let map_distances: MapNode<_, MapSndNode<_>, Color, Color> = MapNode::new(map_gaussian); + let gaussian_iter = distance.then(map_distances); + let avg = graphene_core::raster::WeightedAvgNode::new(); + let blur_iter = MapNode::new(avg); + let blur = graphene_core::raster::ImageIndexIterNode {}.then(blur_iter); + let collect = graphene_core::raster::CollectNode {}; + let vec = blur.then(collect); + let node: DynAnyNode<_, Vec<_>, _, _> = DynAnyNode::new(vec); + let node = (pre_node).then(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()) }), From be6c71c545ce3dd127fcdc920688cb453b3dc47f Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Tue, 27 Dec 2022 20:52:15 +0100 Subject: [PATCH 02/10] Further debugging --- node-graph/gcore/src/raster.rs | 39 +++++++++++-------- node-graph/gstd/src/any.rs | 11 ++++++ .../interpreted-executor/src/node_registry.rs | 26 ++++++------- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index e6b980da9f1..13eea2564e8 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -13,12 +13,12 @@ fn grayscale_color_node(input: Color) -> Color { } #[derive(Debug, Clone, Copy)] -pub struct MapNode { +pub struct MapNode, Item, Out> { map_fn: MapFn, _phantom: core::marker::PhantomData<(Iter, Item, Out)>, } -impl MapNode { +impl, Item, Out> MapNode { pub fn new(map_fn: MapFn) -> Self { Self { map_fn, @@ -27,7 +27,15 @@ impl MapNode { } } -impl, MapFn: Node, Item, Out> Node for MapNode { +impl, MapFn: Node, Item, Out> Node for MapNode { + type Output = MapFnIterator; + + 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; fn eval(self, input: Iter) -> Self::Output { @@ -65,7 +73,7 @@ where } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub struct WeightedAvgNode { _phantom: core::marker::PhantomData, } @@ -76,22 +84,22 @@ impl WeightedAvgNode { } } -fn weighted_avg_node + Copy>(input: Iter) -> Color { - let total_weight: f32 = input.map(|(_, weight)| weight).sum(); - let total_r: f32 = input.map(|(color, weight)| color.r() * weight).sum(); - let total_g: f32 = input.map(|(color, weight)| color.g() * weight).sum(); - let total_b: f32 = input.map(|(color, weight)| color.b() * weight).sum(); +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 + Copy> Node for WeightedAvgNode { +impl + Clone> Node for WeightedAvgNode { type Output = Color; fn eval(self, input: Iter) -> Self::Output { weighted_avg_node(input) } } -impl + Copy> Node for &WeightedAvgNode { +impl + Clone> Node for &WeightedAvgNode { type Output = Color; fn eval(self, input: Iter) -> Self::Output { weighted_avg_node(input) @@ -121,9 +129,8 @@ fn distance_node(input: (i32, i32)) -> f32 { pub struct ImageIndexIterNode; #[node_macro::node_fn(ImageIndexIterNode)] -fn image_index_iter_node(input: (i32, i32)) -> core::ops::Range { - let (width, height) = input; - 0..(width * height) as u32 +fn image_index_iter_node(input: ImageSlice<'static>) -> core::ops::Range { + 0..(input.width * input.height) } #[derive(Debug, Clone, Copy)] @@ -184,7 +191,7 @@ impl<'a> ImageWindowIterator<'a> { } impl<'a> Iterator for ImageWindowIterator<'a> { - type Item = (Color, (u32, u32)); + type Item = (Color, (i32, i32)); 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; @@ -201,7 +208,7 @@ impl<'a> Iterator for ImageWindowIterator<'a> { if self.y > max_y { return None; } - Some((self.image.data[(self.x + self.y * self.image.width) as usize], (self.x, self.y))) + 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))) } } diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index ebcecc4b36f..b063012560e 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -161,6 +161,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/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index fba8b4d9b25..43a570e5381 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -4,7 +4,7 @@ use graphene_core::generic::FnNode; use graphene_core::ops::{AddNode, IdNode}; use graphene_core::raster::color::Color; use graphene_core::raster::{Image, MapFnIterator}; -use graphene_core::structural::{ConsNode, Then}; +use graphene_core::structural::{ComposeNode, ConsNode, Then}; use graphene_core::vector::subpath::Subpath; use graphene_core::Node; use graphene_std::any::DowncastBothNode; @@ -454,28 +454,28 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ ), (NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), |proto_node, stack| { let node_id = proto_node.input.unwrap_node() as usize; - use graphene_core::raster::{MapNode, MapSndNode}; + use graphene_core::raster::{CollectNode, ImageSlice, ImageWindowIterator, MapNode, MapSndNode, WeightedAvgNode}; if let ConstructionArgs::Nodes(blur_args) = proto_node.construction_args { stack.push_fn(move |nodes| { - let pre_node = nodes.get(node_id).unwrap(); + let image = 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::<_, (), f32>::new(sigma); - let window = graphene_core::raster::WindowNode::new(radius, pre_node); + let image = DowncastBothNode::<_, (), ImageSlice<'static>>::new(image); + let window = graphene_core::raster::WindowNode::new(radius, image); let pos_to_dist = MapSndNode::new(graphene_core::raster::DistanceNode); - let distance = window.then(MapNode::new(pos_to_dist)); - let map_gaussian = MapSndNode::new(graphene_core::raster::GaussianNode::new(sigma)); - let map_distances: MapNode<_, MapSndNode<_>, Color, Color> = MapNode::new(map_gaussian); + let distance = window.then(&MapNode::new(pos_to_dist)); + let map_gaussian = &MapSndNode::new(graphene_core::raster::GaussianNode::new(sigma)); + let map_distances: &MapNode<_, &MapSndNode<_>, _, _> = &MapNode::new(&map_gaussian); let gaussian_iter = distance.then(map_distances); - let avg = graphene_core::raster::WeightedAvgNode::new(); + let avg = gaussian_iter.then(WeightedAvgNode::new()); let blur_iter = MapNode::new(avg); - let blur = graphene_core::raster::ImageIndexIterNode {}.then(blur_iter); - let collect = graphene_core::raster::CollectNode {}; - let vec = blur.then(collect); - let node: DynAnyNode<_, Vec<_>, _, _> = DynAnyNode::new(vec); - let node = (pre_node).then(node); + let blur = image.then(graphene_core::raster::ImageIndexIterNode).then(blur_iter); + let collect = CollectNode {}; + let vec: ComposeNode<&ComposeNode<_, _, _>, &CollectNode, ()> = ComposeNode::new(&blur, &collect); + let node: DynAnyNode<_, (), Vec, Vec> = DynAnyNode::new(vec); node.into_type_erased() }) } else { From 1959a1d066ce861bae4f4e7106cf3098f4a6f235 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Thu, 29 Dec 2022 12:27:20 +0100 Subject: [PATCH 03/10] Debug graph construction --- node-graph/gcore/src/lib.rs | 2 +- node-graph/gcore/src/ops.rs | 28 +++++++ node-graph/gcore/src/raster.rs | 73 ++++++++++++++++++- node-graph/gcore/src/raster/color.rs | 5 +- node-graph/gcore/src/structural.rs | 3 +- .../interpreted-executor/src/node_registry.rs | 16 ++-- 6 files changed, 115 insertions(+), 12 deletions(-) diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index 81246f8ad09..f87d8cf3e2e 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 7f7a3a27ec3..618fcf5994f 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -189,6 +189,34 @@ impl IdNode { } } +/// Ascribe the node types +#[derive(Debug, Copy, Clone, 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) + } +} + 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 13eea2564e8..ec57b78da50 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; @@ -27,9 +29,10 @@ impl, Item, Out> MapNode, MapFn: Node, Item, Out> Node for MapNode { +impl + core::fmt::Debug, MapFn: Node, Item, Out> Node for MapNode { type Output = MapFnIterator; + #[inline] fn eval(self, input: Iter) -> Self::Output { MapFnIterator::new(input, self.map_fn) } @@ -38,6 +41,7 @@ impl, MapFn: Node, Item, Out> No 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) } @@ -50,6 +54,14 @@ pub struct MapFnIterator { 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 } @@ -84,6 +96,7 @@ impl WeightedAvgNode { } } +#[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(); @@ -95,12 +108,16 @@ fn weighted_avg_node + Clone>(input: Iter) - 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) } @@ -147,6 +164,7 @@ impl WindowNode { 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(()); @@ -156,6 +174,7 @@ impl<'a, Radius: Node<(), Output = u32>, Image: Node<(), Output = ImageSlice<'a> } 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(()); @@ -192,6 +211,7 @@ impl<'a> ImageWindowIterator<'a> { 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; @@ -200,6 +220,7 @@ impl<'a> Iterator for ImageWindowIterator<'a> { 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; + self.x += 1; if self.x > max_x { self.x = min_x; @@ -225,12 +246,14 @@ impl MapSndNode { 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)) } @@ -408,7 +431,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() { @@ -418,4 +448,45 @@ 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(), 3); + /*let vec = window.eval(12); + assert_eq!(vec.count(), 9); + let vec = window.eval(5); + assert_eq!(vec.count(), 3); + */ + } + + #[test] + fn blur_node() { + let radius = ValueNode::new(5u32); + let sigma = ValueNode::new(3f32); + 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 aca21ce0611..c91b74ad39b 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 1676273c6f2..af1c2828ace 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/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 43a570e5381..c8083792365 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -1,7 +1,7 @@ use borrow_stack::FixedSizeStack; use glam::DVec2; use graphene_core::generic::FnNode; -use graphene_core::ops::{AddNode, IdNode}; +use graphene_core::ops::{AddNode, IdNode, TypeNode}; use graphene_core::raster::color::Color; use graphene_core::raster::{Image, MapFnIterator}; use graphene_core::structural::{ComposeNode, ConsNode, Then}; @@ -460,22 +460,28 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ let image = 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::<_, (), f32>::new(sigma); let image = DowncastBothNode::<_, (), ImageSlice<'static>>::new(image); let window = graphene_core::raster::WindowNode::new(radius, image); + let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window); let pos_to_dist = MapSndNode::new(graphene_core::raster::DistanceNode); let distance = window.then(&MapNode::new(pos_to_dist)); - let map_gaussian = &MapSndNode::new(graphene_core::raster::GaussianNode::new(sigma)); - let map_distances: &MapNode<_, &MapSndNode<_>, _, _> = &MapNode::new(&map_gaussian); + let map_gaussian = MapSndNode::new(graphene_core::raster::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 blur_iter = MapNode::new(avg); let blur = image.then(graphene_core::raster::ImageIndexIterNode).then(blur_iter); + let blur: TypeNode<_, (), MapFnIterator<_, _>> = TypeNode::new(blur); let collect = CollectNode {}; - let vec: ComposeNode<&ComposeNode<_, _, _>, &CollectNode, ()> = ComposeNode::new(&blur, &collect); + let vec = ComposeNode::new(blur, collect); + let vec: TypeNode<_, (), Vec> = TypeNode::new(vec); + let image = vec.eval(()); let node: DynAnyNode<_, (), Vec, Vec> = DynAnyNode::new(vec); + */ + let node = image; node.into_type_erased() }) } else { From e6c6e2b420505d23c7269e25a3912012cdfd8883 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Thu, 29 Dec 2022 15:28:02 +0100 Subject: [PATCH 04/10] Fix Copy implementation --- node-graph/gcore/src/ops.rs | 9 +++++++- node-graph/gcore/src/raster.rs | 22 ++++++++++++++----- .../interpreted-executor/src/node_registry.rs | 21 ++++++++---------- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index 618fcf5994f..0c41c43081b 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -190,7 +190,7 @@ impl IdNode { } /// Ascribe the node types -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[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; @@ -217,6 +217,13 @@ impl, I> TypeNode { } } +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 ec57b78da50..ef9f49a3134 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -14,13 +14,23 @@ fn grayscale_color_node(input: Color) -> Color { Color::from_rgbaf32_unchecked(avg, avg, avg, input.a()) } -#[derive(Debug, Clone, Copy)] -pub struct MapNode, Item, Out> { +#[derive(Debug)] +pub struct MapNode> { map_fn: MapFn, - _phantom: core::marker::PhantomData<(Iter, Item, Out)>, + _phantom: core::marker::PhantomData, +} + +impl + Clone> Clone for MapNode { + fn clone(&self) -> Self { + Self { + map_fn: self.map_fn.clone(), + _phantom: self._phantom, + } + } } +impl + Copy> Copy for MapNode {} -impl, Item, Out> MapNode { +impl> MapNode { pub fn new(map_fn: MapFn) -> Self { Self { map_fn, @@ -29,7 +39,7 @@ impl, Item, Out> MapNode + core::fmt::Debug, MapFn: Node, Item, Out> Node for MapNode { +impl, MapFn: Node, Item, Out> Node for MapNode { type Output = MapFnIterator; #[inline] @@ -38,7 +48,7 @@ impl + core::fmt::Debug, MapFn: Node, MapFn: Node + Copy, Item, Out> Node for &MapNode { +impl, MapFn: Node + Copy, Item, Out> Node for &MapNode { type Output = MapFnIterator; #[inline] diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index c8083792365..18d58cf265f 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -454,34 +454,31 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ ), (NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), |proto_node, stack| { let node_id = proto_node.input.unwrap_node() as usize; - use graphene_core::raster::{CollectNode, ImageSlice, ImageWindowIterator, MapNode, MapSndNode, WeightedAvgNode}; + use graphene_core::raster::*; if let ConstructionArgs::Nodes(blur_args) = proto_node.construction_args { stack.push_fn(move |nodes| { let image = 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::<_, (), f32>::new(sigma); let image = DowncastBothNode::<_, (), ImageSlice<'static>>::new(image); - let window = graphene_core::raster::WindowNode::new(radius, image); + let window = WindowNode::new(radius, image); let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window); - let pos_to_dist = MapSndNode::new(graphene_core::raster::DistanceNode); - let distance = window.then(&MapNode::new(pos_to_dist)); - let map_gaussian = MapSndNode::new(graphene_core::raster::GaussianNode::new(sigma)); - let map_distances: MapNode<_, MapSndNode<_>, _, _> = MapNode::new(map_gaussian); + 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(graphene_core::raster::ImageIndexIterNode).then(blur_iter); + let blur = image.then(ImageIndexIterNode).then(blur_iter); let blur: TypeNode<_, (), MapFnIterator<_, _>> = TypeNode::new(blur); let collect = CollectNode {}; - let vec = ComposeNode::new(blur, collect); + let vec = blur.then(collect); let vec: TypeNode<_, (), Vec> = TypeNode::new(vec); - let image = vec.eval(()); let node: DynAnyNode<_, (), Vec, Vec> = DynAnyNode::new(vec); - */ - let node = image; node.into_type_erased() }) } else { From 17b780038820c120031b7b9e12823fc91ad51bc9 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Thu, 29 Dec 2022 21:58:38 +0100 Subject: [PATCH 05/10] Bring node_registry implementation inline with design document --- node-graph/interpreted-executor/src/node_registry.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 18d58cf265f..92f668e57a6 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -464,12 +464,9 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ let sigma = DowncastBothNode::<_, (), f32>::new(sigma); let image = DowncastBothNode::<_, (), ImageSlice<'static>>::new(image); 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 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); From 2757ef8549a45336d49c1a91e977e496da88700e Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Fri, 30 Dec 2022 16:52:24 +0100 Subject: [PATCH 06/10] Add plumbing for ImageSlice --- .../document_node_types.rs | 20 ++++++ .../node_properties.rs | 7 ++ node-graph/gcore/src/raster.rs | 69 +++++++++++++++++-- node-graph/gstd/src/memo.rs | 35 +++++----- node-graph/gstd/src/raster.rs | 2 +- .../interpreted-executor/src/node_registry.rs | 33 +++++++-- 6 files changed, 133 insertions(+), 33 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 f1c44afc20a..13c056088e8 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 @@ -110,6 +110,26 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ outputs: &[FrontendGraphDataType::Raster], properties: node_properties::gpu_map_properties, }, + DocumentNodeType { + name: "Blur", + category: "Image Adjustments", + 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: "Image Adjustments", + 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 83addf11464..691fd44975f 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 @@ -175,6 +175,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(-255.).max(255.), true); + let sigma = number_widget(document_node, node_id, 2, "sigma", NumberInput::new().min(-255.).max(255.), 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/raster.rs b/node-graph/gcore/src/raster.rs index ef9f49a3134..3d34d0a985c 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -368,11 +368,44 @@ impl<'a> IntoIterator for &'a ImageSlice<'a> { } } +#[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::{CollectNode, 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)] @@ -391,6 +424,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 { @@ -401,11 +441,26 @@ 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() } } diff --git a/node-graph/gstd/src/memo.rs b/node-graph/gstd/src/memo.rs index 5660d9609d4..ea6a9601b5d 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(); +impl Node for CacheNode { + type Output = T; + fn eval(self, input: T) -> Self::Output { + input } - pub fn new(node: CachedNode) -> CacheNode { - CacheNode { node, cache: OnceCell::new() } +} + +impl CacheNode { + pub fn new() -> CacheNode { + CacheNode { cache: OnceCell::new() } } } -impl, I> Cache for CacheNode { +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 f88884e3934..121d63b2e82 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 92f668e57a6..cf1a89cb52e 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -16,6 +16,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)] = &[ @@ -452,30 +453,50 @@ 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::*; if let ConstructionArgs::Nodes(blur_args) = proto_node.construction_args { stack.push_fn(move |nodes| { - let image = nodes.get(node_id).unwrap(); + 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::<_, (), f32>::new(sigma); - let image = DowncastBothNode::<_, (), ImageSlice<'static>>::new(image); + let image = DowncastBothNode::<_, (), &Image>::new(pre_node); + let image = 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 = image.then(ImageIndexIterNode).then(blur_iter); - let blur: TypeNode<_, (), MapFnIterator<_, _>> = TypeNode::new(blur); + 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<_, (), Vec> = TypeNode::new(vec); - let node: DynAnyNode<_, (), Vec, Vec> = DynAnyNode::new(vec); + 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 image: TypeNode<_, (), Image> = TypeNode::new(image.then(new_image)); + let node: DynAnyNode<_, (), Image, Image> = DynAnyNode::new(image); + let node = node; node.into_type_erased() }) } else { From 23acb4946062fbf70e0e57302bd547f9b4271b94 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Fri, 30 Dec 2022 18:34:10 +0100 Subject: [PATCH 07/10] Fix BlurNode implementation --- node-graph/gcore/src/ops.rs | 6 ++++++ node-graph/gcore/src/raster.rs | 3 ++- node-graph/gstd/src/any.rs | 6 ++++-- .../interpreted-executor/src/node_registry.rs | 20 ++++++++++++------- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index 0c41c43081b..99e42ac93a8 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; diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 3d34d0a985c..1e14e3407c1 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -139,7 +139,8 @@ pub struct GaussianNode { } #[node_macro::node_fn(GaussianNode)] -fn gaussian_node(input: f32, sigma: f32) -> f32 { +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() } diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index b063012560e..e1dc6b4f909 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 \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 \n{node}").as_str()); Box::new((&self.0).eval_ref(*input)) } } diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index cf1a89cb52e..c9f576a0e55 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, TypeNode}; +use graphene_core::ops::{AddNode, CloneNode, IdNode, TypeNode}; use graphene_core::raster::color::Color; 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; @@ -460,6 +461,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ 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() }) @@ -470,15 +472,19 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ (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::<_, (), f32>::new(sigma); - let image = DowncastBothNode::<_, (), &Image>::new(pre_node); - let image = image.then(ImageRefNode::new()); + let sigma = DowncastBothNode::<_, (), f64>::new(sigma); + let image = DowncastBothNode::<_, Image, &Image>::new(pre_node); + // dirty hack + 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))); @@ -494,9 +500,9 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ 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 image: TypeNode<_, (), Image> = TypeNode::new(image.then(new_image)); - let node: DynAnyNode<_, (), Image, Image> = DynAnyNode::new(image); - let node = node; + //let image: TypeNode<_, (), Image> = TypeNode::new(then(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 { From 450a904f0d464288c97183e652f4967763f75d00 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Fri, 30 Dec 2022 19:18:53 +0100 Subject: [PATCH 08/10] Fix test cases --- node-graph/gcore/src/raster.rs | 26 +++++++++---------- .../interpreted-executor/src/node_registry.rs | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 1e14e3407c1..3ed356dbc39 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -231,16 +231,17 @@ impl<'a> Iterator for ImageWindowIterator<'a> { 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; - self.x += 1; + 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; } - if self.y > max_y { - return None; - } - 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))) + value } } @@ -522,18 +523,17 @@ mod test { let window = WindowNode::new(radius, image); //let window: TypeNode<_, u32, ImageWindowIterator<'static>> = TypeNode::new(window); let vec = window.eval(0); - assert_eq!(vec.count(), 3); - /*let vec = window.eval(12); - assert_eq!(vec.count(), 9); + assert_eq!(vec.count(), 4); let vec = window.eval(5); - assert_eq!(vec.count(), 3); - */ + assert_eq!(vec.count(), 6); + let vec = window.eval(12); + assert_eq!(vec.count(), 9); } #[test] fn blur_node() { - let radius = ValueNode::new(5u32); - let sigma = ValueNode::new(3f32); + 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); @@ -541,7 +541,7 @@ mod test { 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 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); diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index c9f576a0e55..7c14b4ace05 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -482,7 +482,8 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ let radius = DowncastBothNode::<_, (), u32>::new(radius); let sigma = DowncastBothNode::<_, (), f64>::new(sigma); let image = DowncastBothNode::<_, Image, &Image>::new(pre_node); - // dirty hack + // 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); @@ -500,7 +501,6 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ 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 image: TypeNode<_, (), Image> = TypeNode::new(then(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() From f6a83a1619eca250f6e693a0c5fc02384604e624 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sat, 31 Dec 2022 18:42:03 +0100 Subject: [PATCH 09/10] Address review comments --- .../node_graph_message_handler/document_node_types.rs | 4 ++-- .../node_graph/node_graph_message_handler/node_properties.rs | 4 ++-- node-graph/gstd/src/any.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 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 2149d25eba7..2b29618fb16 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 @@ -138,7 +138,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ }, DocumentNodeType { name: "Blur", - category: "Image Adjustments", + category: "Image Filters", identifier: NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), @@ -150,7 +150,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ }, DocumentNodeType { name: "Cache", - category: "Image Adjustments", + category: "Structural", identifier: NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], outputs: &[FrontendGraphDataType::Raster], 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 45a80288fcc..4ee296463ad 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 @@ -190,8 +190,8 @@ pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId, } 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(-255.).max(255.), true); - let sigma = number_widget(document_node, node_id, 2, "sigma", NumberInput::new().min(-255.).max(255.), true); + 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 }] } diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index e1dc6b4f909..4c785991b21 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -32,7 +32,7 @@ where type Output = Any<'n>; fn eval(self, input: Any<'n>) -> Self::Output { let node = core::any::type_name::(); - let input: Box = dyn_any::downcast(input).expect(format!("DynAnyNode Input \n{node}").as_str()); + let input: Box = dyn_any::downcast(input).expect(format!("DynAnyNode Input in:\n{node}").as_str()); Box::new(self.0.eval(*input)) } } @@ -43,7 +43,7 @@ where type Output = Any<'n>; fn eval(self, input: Any<'n>) -> Self::Output { let node = core::any::type_name::(); - let input: Box = dyn_any::downcast(input).expect(format!("DynAnyNode Input \n{node}").as_str()); + let input: Box = dyn_any::downcast(input).expect(format!("DynAnyNode Input in:\n{node}").as_str()); Box::new((&self.0).eval_ref(*input)) } } From 4baaa9f19900d951d5d41d8755820ae7089a1b37 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sat, 31 Dec 2022 19:22:26 +0100 Subject: [PATCH 10/10] Rename Blur -> Gaussian Blur --- .../node_graph_message_handler/document_node_types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2b29618fb16..d59feb6e300 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 @@ -137,7 +137,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ properties: node_properties::quantize_properties, }, DocumentNodeType { - name: "Blur", + name: "Gaussian Blur", category: "Image Filters", identifier: NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), inputs: &[