From d142a9092c0f300d9f8f8cadfdaccab5a3b69819 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Fri, 21 Oct 2022 17:22:06 +0100 Subject: [PATCH] Add some raster protonodes --- Cargo.lock | 8 + node-graph/gcore/src/raster/color.rs | 3 +- node-graph/graph-craft/Cargo.toml | 1 + node-graph/graph-craft/src/node_registry.rs | 221 ++++++++++++++++++-- node-graph/gstd/Cargo.toml | 1 + node-graph/gstd/src/any.rs | 30 +++ node-graph/gstd/src/raster.rs | 16 +- 7 files changed, 263 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ab9e410..78c7a618 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,12 @@ dependencies = [ "syn", ] +[[package]] +name = "dyn-clone" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" + [[package]] name = "either" version = "1.8.0" @@ -349,6 +355,7 @@ version = "0.1.0" dependencies = [ "borrow_stack", "dyn-any", + "dyn-clone", "graphene-core", "graphene-std", "num-traits", @@ -381,6 +388,7 @@ version = "0.1.0" dependencies = [ "borrow_stack", "dyn-any", + "dyn-clone", "graph-proc-macros", "graphene-core", "image", diff --git a/node-graph/gcore/src/raster/color.rs b/node-graph/gcore/src/raster/color.rs index ef735ec5..2eb011cd 100644 --- a/node-graph/gcore/src/raster/color.rs +++ b/node-graph/gcore/src/raster/color.rs @@ -1,3 +1,4 @@ +use dyn_any::{DynAny, StaticType}; use serde::{Deserialize, Serialize}; /// Structure that represents a color. @@ -5,7 +6,7 @@ use serde::{Deserialize, Serialize}; /// 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)] -#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, DynAny)] pub struct Color { red: f32, green: f32, diff --git a/node-graph/graph-craft/Cargo.toml b/node-graph/graph-craft/Cargo.toml index 7b29a687..03bb6145 100644 --- a/node-graph/graph-craft/Cargo.toml +++ b/node-graph/graph-craft/Cargo.toml @@ -12,3 +12,4 @@ graphene-std = { path = "../gstd" } dyn-any = { path = "../../libraries/dyn-any" } num-traits = "0.2" borrow_stack = { path = "../borrow_stack" } +dyn-clone = "1.0" diff --git a/node-graph/graph-craft/src/node_registry.rs b/node-graph/graph-craft/src/node_registry.rs index 75688549..0d82aa56 100644 --- a/node-graph/graph-craft/src/node_registry.rs +++ b/node-graph/graph-craft/src/node_registry.rs @@ -4,8 +4,11 @@ use borrow_stack::FixedSizeStack; use dyn_clone::DynClone; use graphene_core::generic::FnNode; use graphene_core::ops::{AddNode, IdNode}; +use graphene_core::raster::color::Color; use graphene_core::structural::{ConsNode, Then}; use graphene_core::{AsRefNode, Node}; +use graphene_std::any::DowncastBothNode; +use graphene_std::raster::Image; use graphene_std::{ any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode}, document::{ConstructionArgs, ProtoNode, ProtoNodeInput}, @@ -52,12 +55,19 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack = DowncastNode::new(pre_node); - let dynanynode: DynAnyNode<_, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData)); - dynanynode.into_box() - }) + if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args { + stack.push_fn(move |nodes| { + let pre_node = nodes.get(node_id).unwrap(); + let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap(); + + let cons_node = ConsNode::new(DowncastNode::<_, u32>::new(cons_node_arg)); + let node: DynAnyNode<_, u32, _, _> = DynAnyNode::new(cons_node); + let node = (pre_node).then(node); + node.into_type_erased() + }) + } else { + unimplemented!() + } }, ), ( @@ -79,7 +89,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack"], }, |proto_node, stack| { - stack.push_fn(|nodes| { + stack.push_fn(|_nodes| { if let ConstructionArgs::Value(value) = proto_node.construction_args { let node = FnNode::new(move |_| value.clone() as Any<'static>); node.into_type_erased() @@ -89,16 +99,199 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack = DowncastBothNode::new(operation_node); + let map_node = DynAnyNode::new(graphene_std::raster::MapImageNode::new(operation_node)); + + let node = (pre_node).then(map_node); + + node.into_type_erased() + }) + } else { + unimplemented!() + } + }, + ), + ( + NodeIdentifier { + name: "graphene_std::raster::image_node", + types: &["&str"], + }, + |_proto_node, stack| { + stack.push_fn(|_nodes| { + let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap()); + let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image); + node.into_type_erased() + }) + }, + ), + ( + NodeIdentifier { + name: "graphene_std::raster::export_image_node", + types: &["Image", "&str"], + }, + |proto_node, stack| { + stack.push_fn(|nodes| { + let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap(); + + let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap()); + let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image); + let node = (pre_node).then(node); + node.into_type_erased() + }) + }, + ), + ( + NodeIdentifier { + name: "graphene_core::structural::ConsNode", + types: &["Image", "&str"], + }, + |proto_node, stack| { + let node_id = proto_node.input.unwrap_node() as usize; + if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args { + stack.push_fn(move |nodes| { + let pre_node = nodes.get(node_id).unwrap(); + let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap(); + + let cons_node = ConsNode::new(DowncastNode::<_, &str>::new(cons_node_arg)); + let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node); + let node = (pre_node).then(node); + node.into_type_erased() + }) + } else { + unimplemented!() + } + }, + ), ]; #[cfg(test)] -mod test { +mod protograph_testing { + use borrow_stack::BorrowStack; + use super::*; - /*#[test] - fn test() { - let nodes = [TypeErasedNode(Box::new(42u32))]; - let node = NODE_REGISTRY[0].1(node, &nodes); - assert_eq!(node.eval(()), 42); - }*/ + /// Lookup a node by th suffix of the name (for testing only) + fn simple_lookup(suffix: &str) -> &(NodeIdentifier, fn(ProtoNode, &FixedSizeStack>)) { + NODE_REGISTRY.iter().find(|node| node.0.name.ends_with(suffix)).unwrap() + } + + #[test] + fn add_values() { + let stack = FixedSizeStack::new(256); + let val_1_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(2u32))); + simple_lookup("ValueNode").1(val_1_protonode, &stack); + + let val_2_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(40u32))); + simple_lookup("ValueNode").1(val_2_protonode, &stack); + + let cons_protonode = ProtoNode { + construction_args: ConstructionArgs::Nodes(vec![1]), + input: ProtoNodeInput::Node(0), + name: "todo!()".to_string(), + }; + simple_lookup("ConsNode").1(cons_protonode, &stack); + + let add_protonode = ProtoNode { + construction_args: ConstructionArgs::None, + input: ProtoNodeInput::Node(2), + name: "todo!()".to_string(), + }; + simple_lookup("AddNode").1(add_protonode, &stack); + + let result = unsafe { stack.get()[3].eval(Box::new(())) }; + let val = *dyn_any::downcast::(result).unwrap(); + assert_eq!(val, 42); + } + + #[test] + fn greyscale_colour() { + let stack = FixedSizeStack::new(256); + let val_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(Color::from_rgb8(10, 20, 30)))); + simple_lookup("ValueNode").1(val_protonode, &stack); + + let greyscale_protonode = ProtoNode { + construction_args: ConstructionArgs::None, + input: ProtoNodeInput::Node(0), + name: "todo!()".to_string(), + }; + simple_lookup("GrayscaleNode").1(greyscale_protonode, &stack); + + let result = unsafe { stack.get()[1].eval(Box::new(())) }; + let val = *dyn_any::downcast::(result).unwrap(); + assert_eq!(val, Color::from_rgb8(20, 20, 20)); + } + + #[test] + fn load_image() { + let stack = FixedSizeStack::new(256); + let image_protonode = ProtoNode { + construction_args: ConstructionArgs::None, + input: ProtoNodeInput::None, + name: "todo!()".to_string(), + }; + simple_lookup("image_node").1(image_protonode, &stack); + + let result = unsafe { stack.get()[0].eval(Box::new("../gstd/test-image-1.png")) }; + let image = *dyn_any::downcast::(result).unwrap(); + assert_eq!(image.height, 240); + } + + #[test] + fn greyscale_map_image() { + let stack = FixedSizeStack::new(256); + let image_protonode = ProtoNode { + construction_args: ConstructionArgs::None, + input: ProtoNodeInput::None, + name: "todo!()".to_string(), + }; + simple_lookup("image_node").1(image_protonode, &stack); + + let greyscale_protonode = ProtoNode { + construction_args: ConstructionArgs::None, + input: ProtoNodeInput::None, + name: "todo!()".to_string(), + }; + simple_lookup("GrayscaleNode").1(greyscale_protonode, &stack); + + let image_map_protonode = ProtoNode { + construction_args: ConstructionArgs::Nodes(vec![1]), + input: ProtoNodeInput::Node(0), + name: "todo!()".to_string(), + }; + simple_lookup("MapImageNode").1(image_map_protonode, &stack); + + let result = unsafe { stack.get()[2].eval(Box::new("../gstd/test-image-1.png")) }; + let image = *dyn_any::downcast::(result).unwrap(); + assert!(!image.data.iter().any(|c| c.r() != c.b() || c.b() != c.g())); + } } diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 99f8609e..2d5064dd 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -26,3 +26,4 @@ proc-macro2 = {version = "1.0", default-features = false, features = ["proc-macr quote = {version = "1.0", default-features = false } image = "*" rand_chacha = "0.3.1" +dyn-clone = "1.0" diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index aeef23f9..4fc14f91 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -144,6 +144,36 @@ where } } +/// Boxes the input and downcasts the output. +/// Wraps around a node taking Box and returning Box +pub struct DowncastBothNode(pub N, pub PhantomData<(I, O)>); +impl Clone for DowncastBothNode { + fn clone(&self) -> Self { + Self(self.0, self.1) + } +} +impl Copy for DowncastBothNode {} + +impl<'n, N, I: 'n + StaticType, O: 'n + StaticType> Node for DowncastBothNode +where + N: Node, Output = Any<'n>>, +{ + 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).unwrap_or_else(|| panic!("DowncastBothNode Output: {}", fmt_error::())) + } +} +impl<'n, N, I: StaticType, O: StaticType> DowncastBothNode +where + N: Node>, +{ + pub fn new(n: N) -> Self { + DowncastBothNode(n, PhantomData) + } +} + /* /// If we store a `Box` in the stack then the origional DynAnyNode is dropped (because it is not stored by reference) /// This trait is implemented directly by `DynAnyNode` so this means the borrow stack will hold by value diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 0ba7c7ce..57355351 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -1,4 +1,5 @@ use core::marker::PhantomData; +use dyn_any::{DynAny, StaticType}; use graphene_core::ops::FlatMapResultNode; use graphene_core::raster::color::Color; use graphene_core::structural::{ComposeNode, ConsNode}; @@ -23,6 +24,17 @@ impl, MN: Node, S> MapNode { pub struct MapImageNode + Copy>(pub MN); +impl + Copy> Node for MapImageNode { + type Output = Image; + fn eval(self, input: Image) -> Self::Output { + Image { + width: input.width, + height: input.height, + data: input.data.iter().map(|x| self.0.eval(*x)).collect(), + } + } +} + impl<'n, MN: Node + Copy> Node for &'n MapImageNode { type Output = Image; fn eval(self, input: Image) -> Self::Output { @@ -40,7 +52,7 @@ impl + Copy> MapImageNode { } } -#[derive(Debug)] +#[derive(Debug, DynAny)] pub enum Error { IO(std::io::Error), Image(image::ImageError), @@ -86,7 +98,7 @@ impl Node for BufferNode { } } -#[derive(Clone)] +#[derive(Clone, DynAny)] pub struct Image { pub width: u32, pub height: u32,