diff --git a/Cargo.lock b/Cargo.lock index 31197f1e..d4c45d62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -373,6 +373,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", + "rand_chacha", "syn", ] diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 82db125e..99f8609e 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -25,3 +25,4 @@ syn = {version = "1.0", default-features = false, features = ["parsing", "printi proc-macro2 = {version = "1.0", default-features = false, features = ["proc-macro"]} quote = {version = "1.0", default-features = false } image = "*" +rand_chacha = "0.3.1" diff --git a/node-graph/gstd/src/document.rs b/node-graph/gstd/src/document.rs index 309d2158..014c331f 100644 --- a/node-graph/gstd/src/document.rs +++ b/node-graph/gstd/src/document.rs @@ -1,94 +1,537 @@ -/* -use core::marker::PhantomData; +use std::collections::HashMap; +use std::fmt::Display; +use std::sync::Mutex; -use graphene_core::{structural::After, structural::ComposeNode, value::ValueNode, Node, RefNode}; +use dyn_any::{DynAny, StaticType}; +use rand_chacha::{ + rand_core::{RngCore, SeedableRng}, + ChaCha20Rng, +}; -use crate::any::Any; -use crate::any::DynAnyNode; +type NodeId = u64; +static RNG: Mutex> = Mutex::new(None); -pub trait DocumentNode: RefNode { - fn input_hints(&self) -> &'static [&'static str]; - fn input_types(&self) -> &'static [&'static str]; - fn inputs(&self) -> Vec { - self.input_hints().iter().zip(self.input_types()).map(|(a, b)| format!("{}{}", a, b)).collect() +pub fn generate_uuid() -> u64 { + let mut lock = RNG.lock().expect("uuid mutex poisoned"); + if lock.is_none() { + *lock = Some(ChaCha20Rng::seed_from_u64(0)); + } + lock.as_mut().map(ChaCha20Rng::next_u64).unwrap() +} + +fn gen_node_id() -> NodeId { + static mut NODE_ID: NodeId = 3; + unsafe { + NODE_ID += 1; + NODE_ID } } -struct InjectNode + Copy, I>(N, PhantomData); +fn merge_ids(a: u64, b: u64) -> u64 { + use std::hash::{Hash, Hasher}; + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + a.hash(&mut hasher); + b.hash(&mut hasher); + hasher.finish() +} -impl<'n, N: Node + Copy, I> Node<&'n [&'n AnyNode<'n>]> for &'n InjectNode { - type Output = Box, Output = Any<'n>> + 'n>; - fn eval(self, input: &'n [&'n AnyNode<'n>]) -> Self::Output { - assert_eq!(input.len(), 1); - Box::new(ComposeNode::new(&DynAnyNode(input[0]), &DynAnyNode(self.0))) - } -} - -impl + Copy, I> InjectNode { - const TYPES: &'static [&'static str] = &[core::any::type_name::()]; - const HINTS: &'static [&'static str] = &["input: "]; -} -impl<'n, N: Node + Copy, I> DocumentNode<&'n [&'n AnyNode<'n>]> for &'n InjectNode { - fn input_hints(&self) -> &'static [&'static str] { - InjectNode::::HINTS - } - fn input_types(&self) -> &'static [&'static str] { - InjectNode::::TYPES - } -} - -pub type NodeId = u32; - -type AnyNode<'n> = dyn RefNode, Output = Any<'n>>; - -pub struct DocumentGraphNode<'n> { - pub id: NodeId, - pub inputs: Vec, - pub node: NodeWrapper<'n>, -} - -impl<'n> DocumentGraphNode<'n> { - pub fn new(id: NodeId, inputs: Vec, node: NodeWrapper<'n>) -> Self { - Self { id, inputs, node } - } -} - -pub struct NodeWrapper<'n> { - pub node: &'n (dyn Node, Output = Any<'n>> + 'n), - - pub path: &'static str, -} - -impl<'n> NodeWrapper<'n> { - pub fn new(node: &'n (dyn Node, Output = Any<'n>> + 'n), path: &'static str) -> Self { - Self { node, path } +#[derive(Debug, PartialEq)] +pub struct DocumentNode { + name: String, + inputs: Vec, + implementation: DocumentNodeImplementation, +} + +impl DocumentNode { + pub fn populate_first_network_input(&mut self, node: NodeId, offset: usize) { + let input = self + .inputs + .iter() + .enumerate() + .filter(|(_, input)| matches!(input, NodeInput::Network)) + .nth(offset) + .expect("no network input"); + + let index = input.0; + self.inputs[index] = NodeInput::Node(node); + } + + fn resolve_proto_nodes(&mut self) { + let first = self.inputs.remove(0); + if let DocumentNodeImplementation::ProtoNode(proto) = &mut self.implementation { + match first { + NodeInput::Value(value) => { + proto.input = ProtoNodeInput::None; + proto.construction_args = ConstructionArgs::Value(value); + assert_eq!(self.inputs.len(), 0); + return; + } + NodeInput::Node(id) => proto.input = ProtoNodeInput::Node(id), + NodeInput::Network => proto.input = ProtoNodeInput::Network, + } + assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network)), "recived non resolved parameter"); + assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Value(_))), "recieved value as parameter"); + + let nodes: Vec<_> = self + .inputs + .iter() + .filter_map(|input| match input { + NodeInput::Node(id) => Some(*id), + _ => None, + }) + .collect(); + match nodes { + vec if vec.is_empty() => proto.construction_args = ConstructionArgs::None, + vec => proto.construction_args = ConstructionArgs::Nodes(vec), + } + self.inputs = vec![]; + } } } +#[derive(Debug)] pub enum NodeInput { Node(NodeId), - Default(ValueNode>), + Value(Value), + Network, +} + +impl PartialEq for NodeInput { + fn eq(&self, other: &Self) -> bool { + match (&self, &other) { + (Self::Node(n1), Self::Node(n2)) => n1 == n2, + (Self::Value(v1), Self::Value(v2)) => v1 == v2, + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum DocumentNodeImplementation { + Network(NodeNetwork), + ProtoNode(ProtoNode), +} + +#[derive(Debug, Default, PartialEq)] +pub struct NodeNetwork { + inputs: Vec, + output: NodeId, + nodes: HashMap, +} +pub type Value = Box; +pub trait ValueTrait: DynAny<'static> + std::fmt::Debug {} + +pub trait IntoValue: Sized + ValueTrait + 'static { + fn into_any(self) -> Value { + Box::new(self) + } +} +impl ValueTrait for T {} +impl IntoValue for T {} + +#[repr(C)] +struct Vtable { + destructor: unsafe fn(*mut ()), + size: usize, + align: usize, +} + +#[repr(C)] +struct TraitObject { + self_ptr: *mut u8, + vtable: &'static Vtable, +} + +impl PartialEq for Box { + fn eq(&self, other: &Self) -> bool { + if self.type_id() != other.type_id() { + return false; + } + let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) }; + let other_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(other.as_ref()) }; + let size = self_trait_object.vtable.size; + let self_mem = unsafe { std::slice::from_raw_parts(self_trait_object.self_ptr, size) }; + let other_mem = unsafe { std::slice::from_raw_parts(other_trait_object.self_ptr, size) }; + self_mem == other_mem + } +} + +#[derive(Debug, Default)] +pub enum ConstructionArgs { + None, + #[default] + Unresolved, + Value(Value), + Nodes(Vec), +} + +impl PartialEq for ConstructionArgs { + fn eq(&self, other: &Self) -> bool { + match (&self, &other) { + (Self::Nodes(n1), Self::Nodes(n2)) => n1 == n2, + (Self::Value(v1), Self::Value(v2)) => v1 == v2, + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +#[derive(Debug, Default, PartialEq)] +pub struct ProtoNode { + construction_args: ConstructionArgs, + input: ProtoNodeInput, + name: String, +} + +#[derive(Debug, Default, PartialEq, Eq)] +pub enum ProtoNodeInput { + None, + #[default] + Network, + Node(NodeId), +} + +impl NodeInput { + fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) { + if let NodeInput::Node(id) = self { + *self = NodeInput::Node(f(*id)) + } + } +} + +impl ProtoNode { + pub fn id() -> Self { + Self { + name: "id".into(), + ..Default::default() + } + } + pub fn unresolved(name: String) -> Self { + Self { name, ..Default::default() } + } + pub fn value(name: String, value: ConstructionArgs) -> Self { + Self { + name, + construction_args: value, + ..Default::default() + } + } +} + +impl NodeNetwork { + pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) { + self.inputs.iter_mut().for_each(|id| *id = f(*id)); + self.output = f(self.output); + let mut empty = HashMap::new(); + std::mem::swap(&mut self.nodes, &mut empty); + self.nodes = empty + .into_iter() + .map(|(id, mut node)| { + node.inputs.iter_mut().for_each(|input| input.map_ids(f)); + (f(id), node) + }) + .collect(); + } + + pub fn flatten(&mut self, node: NodeId) { + self.flatten_with_fns(node, merge_ids, generate_uuid) + } + + /// Recursively dissolve non primitive document nodes and return a single flattened network of nodes. + pub fn flatten_with_fns(&mut self, node: NodeId, map_ids: impl Fn(NodeId, NodeId) -> NodeId + Copy, gen_id: impl Fn() -> NodeId + Copy) { + let (id, mut node) = self.nodes.remove_entry(&node).expect("The node which was supposed to be flattened does not exist in the network"); + + match node.implementation { + DocumentNodeImplementation::Network(mut inner_network) => { + // Connect all network inputs to either the parent network nodes, or newly created value nodes. + inner_network.map_ids(|inner_id| map_ids(id, inner_id)); + let new_nodes = inner_network.nodes.keys().cloned().collect::>(); + // Copy nodes from the inner network into the parent network + self.nodes.extend(inner_network.nodes); + + let mut network_offsets = HashMap::new(); + for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) { + let offset = network_offsets.entry(network_input).or_insert(0); + match document_input { + NodeInput::Node(node) => { + let network_input = self.nodes.get_mut(network_input).unwrap(); + network_input.populate_first_network_input(node, *offset); + } + NodeInput::Value(value) => { + let name = format!("Value: {:?}", value); + let new_id = map_ids(id, gen_id()); + let value_node = DocumentNode { + name: name.clone(), + inputs: vec![NodeInput::Value(value)], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("value".into())), + }; + assert!(!self.nodes.contains_key(&new_id)); + self.nodes.insert(new_id, value_node); + let network_input = self.nodes.get_mut(network_input).unwrap(); + network_input.populate_first_network_input(new_id, *offset); + } + NodeInput::Network => { + *network_offsets.get_mut(network_input).unwrap() += 1; + if let Some(index) = self.inputs.iter().position(|i| *i == id) { + self.inputs[index] = *network_input; + } + } + } + } + node.implementation = DocumentNodeImplementation::ProtoNode(ProtoNode::id()); + node.inputs = vec![NodeInput::Node(inner_network.output)]; + for node_id in new_nodes { + self.flatten_with_fns(node_id, map_ids, gen_id); + } + } + DocumentNodeImplementation::ProtoNode(proto_node) => { + node.implementation = DocumentNodeImplementation::ProtoNode(proto_node); + } + } + assert!(!self.nodes.contains_key(&id), "Trying to insert a node into the network caused an id conflict"); + self.nodes.insert(id, node); + } + + pub fn resolve_proto_nodes(&mut self) { + for node in self.nodes.values_mut() { + node.resolve_proto_nodes(); + } + } +} + +struct Map(core::marker::PhantomData<(I, O)>); + +impl Display for Map<(), O> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "Map") + } +} + +impl Display for Map { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "Map") + } } #[cfg(test)] mod test { - use crate::any::DynAnyNode; - use super::*; - use graphene_core::value::ValueNode; #[test] - fn inject_node() { - let inject_node = InjectNode(&ValueNode(4u32), PhantomData); - use super::DocumentNode; - /*assert_eq!( - (&inject_node as &dyn DocumentNode<&[&AnyNode], Output = ComposeNode<&AnyNode, ValueNode, ()>>).inputs(), - vec!["input: ()"] - );*/ - let any_inject = DynAnyNode(&inject_node, PhantomData); - let any_inject = Box::leak(Box::new(any_inject)); - let wrapped = NodeWrapper::new((&any_inject) as &(dyn Node<&[&AnyNode], Output = Any>), "grahpene_core::document::InjectNode"); - let document_node = DocumentGraphNode::new(0, vec![], wrapped); + fn test_any_src() { + assert!(2_u32.into_any() == 2_u32.into_any()); + assert!(2_u32.into_any() != 3_u32.into_any()); + assert!(2_u32.into_any() != 3_i32.into_any()); + } + + fn add_network() -> NodeNetwork { + NodeNetwork { + inputs: vec![0, 0], + output: 1, + nodes: [ + ( + 0, + DocumentNode { + name: "cons".into(), + inputs: vec![NodeInput::Network, NodeInput::Network], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("cons".into())), + }, + ), + ( + 1, + DocumentNode { + name: "add".into(), + inputs: vec![NodeInput::Node(0)], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("add".into())), + }, + ), + ] + .into_iter() + .collect(), + } + } + + #[test] + fn map_ids() { + let mut network = add_network(); + network.map_ids(|id| id + 1); + let maped_add = NodeNetwork { + inputs: vec![1, 1], + output: 2, + nodes: [ + ( + 1, + DocumentNode { + name: "cons".into(), + inputs: vec![NodeInput::Network, NodeInput::Network], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::value("cons".into(), ConstructionArgs::Unresolved)), + }, + ), + ( + 2, + DocumentNode { + name: "add".into(), + inputs: vec![NodeInput::Node(1)], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::value("add".into(), ConstructionArgs::Unresolved)), + }, + ), + ] + .into_iter() + .collect(), + }; + assert_eq!(network, maped_add); + } + + #[test] + fn flatten_add() { + let mut network = NodeNetwork { + inputs: vec![1], + output: 1, + nodes: [( + 1, + DocumentNode { + name: "Inc".into(), + inputs: vec![NodeInput::Network, NodeInput::Value(2_u32.into_any())], + implementation: DocumentNodeImplementation::Network(add_network()), + }, + )] + .into_iter() + .collect(), + }; + network.flatten_with_fns(1, |self_id, inner_id| self_id * 10 + inner_id, gen_node_id); + let flat_network = flat_network(); + + println!("{:#?}", network); + println!("{:#?}", flat_network); + assert_eq!(flat_network, network); + } + + #[test] + fn resolve_proto_node_add() { + let mut d_node = DocumentNode { + name: "cons".into(), + inputs: vec![NodeInput::Network, NodeInput::Node(0)], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::value("cons".into(), ConstructionArgs::Unresolved)), + }; + + d_node.resolve_proto_nodes(); + let reference = DocumentNode { + name: "cons".into(), + inputs: vec![], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode { + name: "cons".into(), + input: ProtoNodeInput::Network, + construction_args: ConstructionArgs::Nodes(vec![0]), + }), + }; + assert_eq!(d_node, reference); + } + + #[test] + fn resolve_flatten_add() { + let construction_network = NodeNetwork { + inputs: vec![10], + output: 1, + nodes: [ + ( + 1, + DocumentNode { + name: "Inc".into(), + inputs: vec![], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode { + name: "id".into(), + input: ProtoNodeInput::Node(11), + construction_args: ConstructionArgs::None, + }), + }, + ), + ( + 10, + DocumentNode { + name: "cons".into(), + inputs: vec![], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode { + name: "cons".into(), + input: ProtoNodeInput::Network, + construction_args: ConstructionArgs::Nodes(vec![14]), + }), + }, + ), + ( + 11, + DocumentNode { + name: "add".into(), + inputs: vec![], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode { + name: "add".into(), + input: ProtoNodeInput::Node(10), + construction_args: ConstructionArgs::None, + }), + }, + ), + ( + 14, + DocumentNode { + name: "Value: 2".into(), + inputs: vec![], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode { + name: "value".into(), + input: ProtoNodeInput::None, + construction_args: ConstructionArgs::Value(2_u32.into_any()), + }), + }, + ), + ] + .into_iter() + .collect(), + }; + let mut resolved_network = flat_network(); + resolved_network.resolve_proto_nodes(); + + println!("{:#?}", resolved_network); + println!("{:#?}", construction_network); + assert_eq!(resolved_network, construction_network); + } + + fn flat_network() -> NodeNetwork { + NodeNetwork { + inputs: vec![10], + output: 1, + nodes: [ + ( + 1, + DocumentNode { + name: "Inc".into(), + inputs: vec![NodeInput::Node(11)], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::id()), + }, + ), + ( + 10, + DocumentNode { + name: "cons".into(), + inputs: vec![NodeInput::Network, NodeInput::Node(14)], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("cons".into())), + }, + ), + ( + 14, + DocumentNode { + name: "Value: 2".into(), + inputs: vec![NodeInput::Value(2_u32.into_any())], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("value".into())), + }, + ), + ( + 11, + DocumentNode { + name: "add".into(), + inputs: vec![NodeInput::Node(10)], + implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("add".into())), + }, + ), + ] + .into_iter() + .collect(), + } } } -*/