Node graph dynamic execution (#824)

Restructure document node implementation

* Implement topological sort

* Enforce the usage of type annotations

* Add complete test case
This commit is contained in:
TrueDoctor 2022-10-26 00:45:42 +02:00 committed by Keavon Chambers
parent d142a9092c
commit 4ec600957c
12 changed files with 662 additions and 384 deletions

2
Cargo.lock generated
View File

@ -359,6 +359,7 @@ dependencies = [
"graphene-core", "graphene-core",
"graphene-std", "graphene-std",
"num-traits", "num-traits",
"rand_chacha",
] ]
[[package]] [[package]]
@ -395,7 +396,6 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand_chacha",
"syn", "syn",
] ]

View File

@ -30,6 +30,10 @@ impl<'n, L: Add<R, Output = O> + 'n + Copy, R: Copy, O: 'n> Node<&'n (L, R)> for
} }
} }
#[cfg(feature = "std")]
pub mod dynamic {
use super::*;
// Unfortunatly we can't impl the AddNode as we get // Unfortunatly we can't impl the AddNode as we get
// `upstream crates may add a new impl of trait `core::ops::Add` for type `alloc::boxed::Box<(dyn dyn_any::DynAny<'_> + 'static)>` in future versions` // `upstream crates may add a new impl of trait `core::ops::Add` for type `alloc::boxed::Box<(dyn dyn_any::DynAny<'_> + 'static)>` in future versions`
pub struct DynamicAddNode; pub struct DynamicAddNode;
@ -75,6 +79,7 @@ impl<'n> Node<(Dynamic<'n>, Dynamic<'n>)> for DynamicAddNode {
(left: f64, right: f64) } (left: f64, right: f64) }
} }
} }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CloneNode; pub struct CloneNode;

View File

@ -1,3 +1,4 @@
#[cfg(feature = "std")]
use dyn_any::{DynAny, StaticType}; use dyn_any::{DynAny, StaticType};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -6,7 +7,8 @@ use serde::{Deserialize, Serialize};
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`, /// 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. /// 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)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, DynAny)] #[cfg_attr(feature = "std", derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, DynAny))]
#[cfg_attr(not(feature = "std"), derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize))]
pub struct Color { pub struct Color {
red: f32, red: f32,
green: f32, green: f32,

View File

@ -13,3 +13,4 @@ dyn-any = { path = "../../libraries/dyn-any" }
num-traits = "0.2" num-traits = "0.2"
borrow_stack = { path = "../borrow_stack" } borrow_stack = { path = "../borrow_stack" }
dyn-clone = "1.0" dyn-clone = "1.0"
rand_chacha = "0.3.1"

View File

@ -1,15 +1,15 @@
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput, Type};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display;
use std::sync::Mutex; use std::sync::Mutex;
use dyn_any::{DynAny, StaticType}; pub mod value;
use dyn_clone::DynClone;
use rand_chacha::{ use rand_chacha::{
rand_core::{RngCore, SeedableRng}, rand_core::{RngCore, SeedableRng},
ChaCha20Rng, ChaCha20Rng,
}; };
type NodeId = u64; pub type NodeId = u64;
static RNG: Mutex<Option<ChaCha20Rng>> = Mutex::new(None); static RNG: Mutex<Option<ChaCha20Rng>> = Mutex::new(None);
pub fn generate_uuid() -> u64 { pub fn generate_uuid() -> u64 {
@ -20,14 +20,6 @@ pub fn generate_uuid() -> u64 {
lock.as_mut().map(ChaCha20Rng::next_u64).unwrap() 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
}
}
fn merge_ids(a: u64, b: u64) -> u64 { fn merge_ids(a: u64, b: u64) -> u64 {
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new(); let mut hasher = std::collections::hash_map::DefaultHasher::new();
@ -36,6 +28,8 @@ fn merge_ids(a: u64, b: u64) -> u64 {
hasher.finish() hasher.finish()
} }
type Fqn = NodeIdentifier<'static>;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct DocumentNode { pub struct DocumentNode {
pub name: String, pub name: String,
@ -57,35 +51,33 @@ impl DocumentNode {
self.inputs[index] = NodeInput::Node(node); self.inputs[index] = NodeInput::Node(node);
} }
fn resolve_proto_nodes(&mut self) { fn resolve_proto_node(mut self) -> ProtoNode {
let first = self.inputs.remove(0); let first = self.inputs.remove(0);
if let DocumentNodeImplementation::ProtoNode(proto) = &mut self.implementation { if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation {
match first { let (input, mut args) = match first {
NodeInput::Value(value) => { NodeInput::Value(value) => {
proto.input = ProtoNodeInput::None;
proto.construction_args = ConstructionArgs::Value(value);
assert_eq!(self.inputs.len(), 0); assert_eq!(self.inputs.len(), 0);
return; (ProtoNodeInput::None, ConstructionArgs::Value(value))
} }
NodeInput::Node(id) => proto.input = ProtoNodeInput::Node(id), NodeInput::Node(id) => (ProtoNodeInput::Node(id), ConstructionArgs::Nodes(vec![])),
NodeInput::Network => proto.input = ProtoNodeInput::Network, NodeInput::Network => (ProtoNodeInput::Network, ConstructionArgs::Nodes(vec![])),
} };
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network)), "recived non resolved parameter"); assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network)), "recieved non resolved parameter");
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Value(_))), "recieved value as parameter"); assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Value(_))), "recieved value as parameter");
let nodes: Vec<_> = self if let ConstructionArgs::Nodes(nodes) = &mut args {
.inputs nodes.extend(self.inputs.iter().map(|input| match input {
.iter() NodeInput::Node(id) => *id,
.filter_map(|input| match input { _ => unreachable!(),
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![]; ProtoNode {
identifier: fqn,
input,
construction_args: args,
}
} else {
unreachable!("tried to resolve not flattened node on resolved node");
} }
} }
} }
@ -93,10 +85,18 @@ impl DocumentNode {
#[derive(Debug)] #[derive(Debug)]
pub enum NodeInput { pub enum NodeInput {
Node(NodeId), Node(NodeId),
Value(Value), Value(value::Value),
Network, Network,
} }
impl NodeInput {
fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
if let NodeInput::Node(id) = self {
*self = NodeInput::Node(f(*id))
}
}
}
impl PartialEq for NodeInput { impl PartialEq for NodeInput {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (&self, &other) { match (&self, &other) {
@ -110,7 +110,7 @@ impl PartialEq for NodeInput {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DocumentNodeImplementation { pub enum DocumentNodeImplementation {
Network(NodeNetwork), Network(NodeNetwork),
ProtoNode(ProtoNode), Unresolved(Fqn),
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]
@ -119,127 +119,6 @@ pub struct NodeNetwork {
pub output: NodeId, pub output: NodeId,
pub nodes: HashMap<NodeId, DocumentNode>, pub nodes: HashMap<NodeId, DocumentNode>,
} }
pub type Value = Box<dyn ValueTrait>;
pub trait ValueTrait: DynAny<'static> + std::fmt::Debug + DynClone {}
pub trait IntoValue: Sized + ValueTrait + 'static {
fn into_any(self) -> Value {
Box::new(self)
}
}
impl<T: 'static + StaticType + std::fmt::Debug + PartialEq + Clone> ValueTrait for T {}
impl<T: 'static + ValueTrait> 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<dyn ValueTrait> {
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
}
}
impl Clone for Value {
fn clone(&self) -> Self {
let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) };
let size = self_trait_object.vtable.size;
let self_mem = unsafe { std::slice::from_raw_parts(self_trait_object.self_ptr, size) }.to_owned();
let ptr = Vec::leak(self_mem);
unsafe {
std::mem::transmute(TraitObject {
self_ptr: ptr as *mut [u8] as *mut u8,
vtable: self_trait_object.vtable,
})
}
}
}
#[derive(Debug, Default)]
pub enum ConstructionArgs {
None,
#[default]
Unresolved,
Value(Value),
Nodes(Vec<NodeId>),
}
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 {
pub construction_args: ConstructionArgs,
pub input: ProtoNodeInput,
pub 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 ProtoNodeInput {
pub fn unwrap_node(self) -> NodeId {
match self {
ProtoNodeInput::Node(id) => id,
_ => panic!("tried to unwrap id from non node input"),
}
}
}
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 { impl NodeNetwork {
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) { pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) {
@ -286,7 +165,7 @@ impl NodeNetwork {
let value_node = DocumentNode { let value_node = DocumentNode {
name: name.clone(), name: name.clone(),
inputs: vec![NodeInput::Value(value)], inputs: vec![NodeInput::Value(value)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("value".into())), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic])),
}; };
assert!(!self.nodes.contains_key(&new_id)); assert!(!self.nodes.contains_key(&new_id));
self.nodes.insert(new_id, value_node); self.nodes.insert(new_id, value_node);
@ -301,50 +180,41 @@ impl NodeNetwork {
} }
} }
} }
node.implementation = DocumentNodeImplementation::ProtoNode(ProtoNode::id()); node.implementation = DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic]));
node.inputs = vec![NodeInput::Node(inner_network.output)]; node.inputs = vec![NodeInput::Node(inner_network.output)];
for node_id in new_nodes { for node_id in new_nodes {
self.flatten_with_fns(node_id, map_ids, gen_id); self.flatten_with_fns(node_id, map_ids, gen_id);
} }
} }
DocumentNodeImplementation::ProtoNode(proto_node) => { DocumentNodeImplementation::Unresolved(_) => (),
node.implementation = DocumentNodeImplementation::ProtoNode(proto_node);
}
} }
assert!(!self.nodes.contains_key(&id), "Trying to insert a node into the network caused an id conflict"); assert!(!self.nodes.contains_key(&id), "Trying to insert a node into the network caused an id conflict");
self.nodes.insert(id, node); self.nodes.insert(id, node);
} }
pub fn resolve_proto_nodes(&mut self) { pub fn into_proto_network(self) -> ProtoNetwork {
for node in self.nodes.values_mut() { let mut nodes: Vec<_> = self.nodes.into_iter().map(|(id, node)| (id, node.resolve_proto_node())).collect();
node.resolve_proto_nodes(); nodes.sort_unstable_by_key(|(i, _)| *i);
ProtoNetwork {
inputs: self.inputs,
output: self.output,
nodes,
} }
} }
} }
struct Map<I, O>(core::marker::PhantomData<(I, O)>);
impl<O> Display for Map<(), O> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "Map")
}
}
impl Display for Map<i32, String> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "Map<String>")
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput};
use value::IntoValue;
#[test] fn gen_node_id() -> NodeId {
fn test_any_src() { static mut NODE_ID: NodeId = 3;
assert!(2_u32.into_any() == 2_u32.into_any()); unsafe {
assert!(2_u32.into_any() != 3_u32.into_any()); NODE_ID += 1;
assert!(2_u32.into_any() != 3_i32.into_any()); NODE_ID
}
} }
fn add_network() -> NodeNetwork { fn add_network() -> NodeNetwork {
@ -357,7 +227,7 @@ mod test {
DocumentNode { DocumentNode {
name: "cons".into(), name: "cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network], inputs: vec![NodeInput::Network, NodeInput::Network],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("cons".into())), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
}, },
), ),
( (
@ -365,7 +235,7 @@ mod test {
DocumentNode { DocumentNode {
name: "add".into(), name: "add".into(),
inputs: vec![NodeInput::Node(0)], inputs: vec![NodeInput::Node(0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("add".into())), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
}, },
), ),
] ]
@ -387,7 +257,7 @@ mod test {
DocumentNode { DocumentNode {
name: "cons".into(), name: "cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network], inputs: vec![NodeInput::Network, NodeInput::Network],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::value("cons".into(), ConstructionArgs::Unresolved)), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
}, },
), ),
( (
@ -395,7 +265,7 @@ mod test {
DocumentNode { DocumentNode {
name: "add".into(), name: "add".into(),
inputs: vec![NodeInput::Node(1)], inputs: vec![NodeInput::Node(1)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::value("add".into(), ConstructionArgs::Unresolved)), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
}, },
), ),
] ]
@ -431,85 +301,58 @@ mod test {
#[test] #[test]
fn resolve_proto_node_add() { fn resolve_proto_node_add() {
let mut d_node = DocumentNode { let document_node = DocumentNode {
name: "cons".into(), name: "cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Node(0)], inputs: vec![NodeInput::Network, NodeInput::Node(0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::value("cons".into(), ConstructionArgs::Unresolved)), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
}; };
d_node.resolve_proto_nodes(); let proto_node = document_node.resolve_proto_node();
let reference = DocumentNode { let reference = ProtoNode {
name: "cons".into(), identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic]),
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode {
name: "cons".into(),
input: ProtoNodeInput::Network, input: ProtoNodeInput::Network,
construction_args: ConstructionArgs::Nodes(vec![0]), construction_args: ConstructionArgs::Nodes(vec![0]),
}),
}; };
assert_eq!(d_node, reference); assert_eq!(proto_node, reference);
} }
#[test] #[test]
fn resolve_flatten_add() { fn resolve_flatten_add_as_proto_network() {
let construction_network = NodeNetwork { let construction_network = ProtoNetwork {
inputs: vec![10], inputs: vec![10],
output: 1, output: 1,
nodes: [ nodes: [
( (
1, 1,
DocumentNode { ProtoNode {
name: "Inc".into(), identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic]),
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode {
name: "id".into(),
input: ProtoNodeInput::Node(11), input: ProtoNodeInput::Node(11),
construction_args: ConstructionArgs::None, construction_args: ConstructionArgs::Nodes(vec![]),
}),
}, },
), ),
( (
10, 10,
DocumentNode { ProtoNode {
name: "cons".into(), identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic]),
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode {
name: "cons".into(),
input: ProtoNodeInput::Network, input: ProtoNodeInput::Network,
construction_args: ConstructionArgs::Nodes(vec![14]), construction_args: ConstructionArgs::Nodes(vec![14]),
}),
}, },
), ),
( (
11, 11,
DocumentNode { ProtoNode {
name: "add".into(), identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic]),
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode {
name: "add".into(),
input: ProtoNodeInput::Node(10), input: ProtoNodeInput::Node(10),
construction_args: ConstructionArgs::None, construction_args: ConstructionArgs::Nodes(vec![]),
}),
},
),
(
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()),
}),
}, },
), ),
(14, ProtoNode::value(ConstructionArgs::Value(2_u32.into_any()))),
] ]
.into_iter() .into_iter()
.collect(), .collect(),
}; };
let mut resolved_network = flat_network(); let network = flat_network();
resolved_network.resolve_proto_nodes(); let resolved_network = network.into_proto_network();
println!("{:#?}", resolved_network); println!("{:#?}", resolved_network);
println!("{:#?}", construction_network); println!("{:#?}", construction_network);
@ -526,7 +369,7 @@ mod test {
DocumentNode { DocumentNode {
name: "Inc".into(), name: "Inc".into(),
inputs: vec![NodeInput::Node(11)], inputs: vec![NodeInput::Node(11)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::id()), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic])),
}, },
), ),
( (
@ -534,7 +377,7 @@ mod test {
DocumentNode { DocumentNode {
name: "cons".into(), name: "cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Node(14)], inputs: vec![NodeInput::Network, NodeInput::Node(14)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("cons".into())), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
}, },
), ),
( (
@ -542,7 +385,7 @@ mod test {
DocumentNode { DocumentNode {
name: "Value: 2".into(), name: "Value: 2".into(),
inputs: vec![NodeInput::Value(2_u32.into_any())], inputs: vec![NodeInput::Value(2_u32.into_any())],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("value".into())), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic])),
}, },
), ),
( (
@ -550,7 +393,7 @@ mod test {
DocumentNode { DocumentNode {
name: "add".into(), name: "add".into(),
inputs: vec![NodeInput::Node(10)], inputs: vec![NodeInput::Node(10)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode::unresolved("add".into())), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
}, },
), ),
] ]

View File

@ -0,0 +1,74 @@
use dyn_any::StaticType;
use dyn_clone::DynClone;
use dyn_any::DynAny;
pub type Value = Box<dyn ValueTrait>;
pub trait ValueTrait: DynAny<'static> + std::fmt::Debug + DynClone {}
pub trait IntoValue: Sized + ValueTrait + 'static {
fn into_any(self) -> Value {
Box::new(self)
}
}
impl<T: 'static + StaticType + std::fmt::Debug + PartialEq + Clone> ValueTrait for T {}
impl<T: 'static + ValueTrait> IntoValue for T {}
#[repr(C)]
pub(crate) struct Vtable {
pub(crate) destructor: unsafe fn(*mut ()),
pub(crate) size: usize,
pub(crate) align: usize,
}
#[repr(C)]
pub(crate) struct TraitObject {
pub(crate) self_ptr: *mut u8,
pub(crate) vtable: &'static Vtable,
}
impl PartialEq for Box<dyn ValueTrait> {
#[cfg_attr(miri, ignore)]
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
}
}
impl Clone for Value {
fn clone(&self) -> Self {
let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) };
let size = self_trait_object.vtable.size;
let self_mem = unsafe { std::slice::from_raw_parts(self_trait_object.self_ptr, size) }.to_owned();
let ptr = Vec::leak(self_mem);
unsafe {
std::mem::transmute(TraitObject {
self_ptr: ptr as *mut [u8] as *mut u8,
vtable: self_trait_object.vtable,
})
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
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());
}
}

View File

@ -1,6 +1,9 @@
#![feature(trait_upcasting)] #![feature(trait_upcasting)]
pub mod node_registry; pub mod node_registry;
pub mod document;
pub mod proto;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -9,7 +12,9 @@ mod tests {
use graphene_core::value::ValueNode; use graphene_core::value::ValueNode;
use graphene_core::{structural::*, RefNode}; use graphene_core::{structural::*, RefNode};
use crate::document::value::IntoValue;
use borrow_stack::BorrowStack; use borrow_stack::BorrowStack;
use borrow_stack::FixedSizeStack;
use dyn_any::{downcast, IntoDynAny}; use dyn_any::{downcast, IntoDynAny};
use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode}; use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode};
use graphene_std::ops::AddNode; use graphene_std::ops::AddNode;
@ -27,7 +32,6 @@ mod tests {
let dynanynode: DynAnyNode<ConsNode<_, Any<'_>>, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData)); let dynanynode: DynAnyNode<ConsNode<_, Any<'_>>, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData));
dynanynode.into_box() dynanynode.into_box()
}); });
/*
stack.push_fn(|_| { stack.push_fn(|_| {
let dynanynode: DynAnyNode<_, (u32, &u32), _, _> = DynAnyNode::new(AddNode); let dynanynode: DynAnyNode<_, (u32, &u32), _, _> = DynAnyNode::new(AddNode);
dynanynode.into_box() dynanynode.into_box()
@ -35,79 +39,81 @@ mod tests {
stack.push_fn(|nodes| { stack.push_fn(|nodes| {
let compose_node = nodes[1].after(&nodes[2]); let compose_node = nodes[1].after(&nodes[2]);
TypeErasedNode(Box::new(compose_node)) TypeErasedNode(Box::new(compose_node))
});}*/ });
let result = unsafe { &stack.get()[0] }.eval_ref(().into_dyn()); let result = unsafe { &stack.get()[0] }.eval_ref(().into_dyn());
assert_eq!(*downcast::<&u32>(result).unwrap(), &2_u32); assert_eq!(*downcast::<&u32>(result).unwrap(), &2_u32);
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn()); let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
assert_eq!(*downcast::<(u32, &u32)>(result).unwrap(), (4_u32, &2_u32)); assert_eq!(*downcast::<(u32, &u32)>(result).unwrap(), (4_u32, &2_u32));
/*
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn()); let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
let add = unsafe { &stack.get()[2] }.eval_ref(result); let add = unsafe { &stack.get()[2] }.eval_ref(result);
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32); assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn()); let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn());
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);*/ assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
} }
#[test] #[test]
fn craft_from_flattened() { fn execute_add() {
use graphene_std::document::*; use crate::document::*;
// This is input and evaluated use crate::node_registry::push_node;
let construction_network = NodeNetwork { use crate::proto::*;
inputs: vec![10], use graphene_core::Node;
fn add_network() -> NodeNetwork {
NodeNetwork {
inputs: vec![0, 0],
output: 1, output: 1,
nodes: [ nodes: [
(
0,
DocumentNode {
name: "cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Concrete("u32"), Type::Concrete("u32")])),
},
),
( (
1, 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 { DocumentNode {
name: "add".into(), name: "add".into(),
inputs: vec![], inputs: vec![NodeInput::Node(0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode { implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete("u32"), Type::Concrete("u32")])),
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() .into_iter()
.collect(), .collect(),
}
}
let mut network = NodeNetwork {
inputs: vec![0],
output: 0,
nodes: [(
0,
DocumentNode {
name: "Inc".into(),
inputs: vec![NodeInput::Network, NodeInput::Value(1_u32.into_any())],
implementation: DocumentNodeImplementation::Network(add_network()),
},
)]
.into_iter()
.collect(),
}; };
let stack = FixedSizeStack::new(256);
println!("flattening");
network.flatten(0);
//println!("flat_network: {:#?}", network);
let mut proto_network = network.into_proto_network();
proto_network.reorder_ids();
//println!("reordered_ides: {:#?}", proto_network);
for (_id, node) in proto_network.nodes {
push_node(node, &stack);
}
let result = unsafe { stack.get().last().unwrap().eval(32_u32.into_dyn()) };
let val = *dyn_any::downcast::<u32>(result).unwrap();
assert_eq!(val, 33_u32);
} }
} }

View File

@ -1,29 +1,39 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use borrow_stack::FixedSizeStack; use borrow_stack::FixedSizeStack;
use dyn_clone::DynClone;
use graphene_core::generic::FnNode; use graphene_core::generic::FnNode;
use graphene_core::ops::{AddNode, IdNode}; use graphene_core::ops::{AddNode, IdNode};
use graphene_core::raster::color::Color; use graphene_core::raster::color::Color;
use graphene_core::structural::{ConsNode, Then}; use graphene_core::structural::{ConsNode, Then};
use graphene_core::{AsRefNode, Node}; use graphene_core::{AsRefNode, Node};
use graphene_std::any::DowncastBothNode; use graphene_std::any::DowncastBothNode;
use graphene_std::any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode};
use graphene_std::raster::Image; use graphene_std::raster::Image;
use graphene_std::{
any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode},
document::{ConstructionArgs, ProtoNode, ProtoNodeInput},
};
struct NodeIdentifier { use crate::proto::Type;
name: &'static str, use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeInput, Type::Concrete};
types: &'static [&'static str],
}
static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErasedNode<'static>>))] = &[ type NodeConstructor = fn(ProtoNode, &FixedSizeStack<TypeErasedNode<'static>>);
//TODO: turn into hasmap
static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_core::ops::IdNode", name: "graphene_core::ops::IdNode",
types: &["Any<'n>"], types: &[Concrete("Any<'_>")],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
let node = pre_node.then(graphene_core::ops::IdNode);
node.into_type_erased()
})
},
),
(
NodeIdentifier {
name: "graphene_core::ops::IdNode",
types: &[Type::Generic],
}, },
|proto_node, stack| { |proto_node, stack| {
stack.push_fn(|nodes| { stack.push_fn(|nodes| {
@ -36,7 +46,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_core::ops::AddNode", name: "graphene_core::ops::AddNode",
types: &["u32", "u32"], types: &[Concrete("u32"), Concrete("u32")],
}, },
|proto_node, stack| { |proto_node, stack| {
stack.push_fn(|nodes| { stack.push_fn(|nodes| {
@ -48,10 +58,95 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
}) })
}, },
), ),
(
NodeIdentifier {
name: "graphene_core::ops::AddNode",
types: &[Concrete("&u32"), Concrete("&u32")],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
let node: DynAnyNode<AddNode, (&u32, &u32), _, _> = DynAnyNode::new(graphene_core::ops::AddNode);
let node = (pre_node).then(node);
node.into_type_erased()
})
},
),
(
NodeIdentifier {
name: "graphene_core::ops::AddNode",
types: &[Concrete("&u32"), Concrete("u32")],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
let node: DynAnyNode<AddNode, (&u32, u32), _, _> = DynAnyNode::new(graphene_core::ops::AddNode);
let node = (pre_node).then(node);
node.into_type_erased()
})
},
),
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_core::structural::ConsNode", name: "graphene_core::structural::ConsNode",
types: &["&TypeErasedNode", "&u32", "u32"], types: &[Concrete("&u32"), Concrete("u32")],
},
|proto_node, stack| {
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
stack.push_fn(move |nodes| {
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 = match proto_node.input {
ProtoNodeInput::Network => node.into_type_erased(),
ProtoNodeInput::Node(node_id) => {
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(node).into_type_erased()
}
ProtoNodeInput::None => unreachable!(),
};
node
})
} else {
unimplemented!()
}
},
),
(
NodeIdentifier {
name: "graphene_core::structural::ConsNode",
types: &[Concrete("u32"), Concrete("u32")],
},
|proto_node, stack| {
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
stack.push_fn(move |nodes| {
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 = match proto_node.input {
ProtoNodeInput::Network => node.into_type_erased(),
ProtoNodeInput::Node(node_id) => {
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(node).into_type_erased()
}
ProtoNodeInput::None => unreachable!(),
};
node
})
} else {
unimplemented!()
}
},
),
// TODO: create macro to impl for all types
(
NodeIdentifier {
name: "graphene_core::structural::ConsNode",
types: &[Concrete("&u32"), Concrete("&u32")],
}, },
|proto_node, stack| { |proto_node, stack| {
let node_id = proto_node.input.unwrap_node() as usize; let node_id = proto_node.input.unwrap_node() as usize;
@ -60,8 +155,8 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
let pre_node = nodes.get(node_id).unwrap(); let pre_node = nodes.get(node_id).unwrap();
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).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 cons_node = ConsNode::new(DowncastNode::<_, &u32>::new(cons_node_arg));
let node: DynAnyNode<_, u32, _, _> = DynAnyNode::new(cons_node); let node: DynAnyNode<_, &u32, _, _> = DynAnyNode::new(cons_node);
let node = (pre_node).then(node); let node = (pre_node).then(node);
node.into_type_erased() node.into_type_erased()
}) })
@ -73,7 +168,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_core::any::DowncastNode", name: "graphene_core::any::DowncastNode",
types: &["&TypeErasedNode", "&u32"], types: &[Concrete("&u32")],
}, },
|proto_node, stack| { |proto_node, stack| {
stack.push_fn(|nodes| { stack.push_fn(|nodes| {
@ -86,7 +181,23 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_core::value::ValueNode", name: "graphene_core::value::ValueNode",
types: &["Any<'n>"], types: &[Concrete("Any<'_>")],
},
|proto_node, stack| {
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()
} else {
unreachable!()
}
})
},
),
(
NodeIdentifier {
name: "graphene_core::value::ValueNode",
types: &[Type::Generic],
}, },
|proto_node, stack| { |proto_node, stack| {
stack.push_fn(|_nodes| { stack.push_fn(|_nodes| {
@ -102,7 +213,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_core::raster::GrayscaleNode", name: "graphene_core::raster::GrayscaleNode",
types: &["Color"], types: &[],
}, },
|proto_node, stack| { |proto_node, stack| {
stack.push_fn(|nodes| { stack.push_fn(|nodes| {
@ -120,7 +231,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_std::raster::MapImageNode", name: "graphene_std::raster::MapImageNode",
types: &["Image"], types: &[],
}, },
|proto_node, stack| { |proto_node, stack| {
let node_id = proto_node.input.unwrap_node() as usize; let node_id = proto_node.input.unwrap_node() as usize;
@ -143,8 +254,8 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
), ),
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_std::raster::image_node", name: "graphene_std::raster::ImageNode",
types: &["&str"], types: &[Concrete("&str")],
}, },
|_proto_node, stack| { |_proto_node, stack| {
stack.push_fn(|_nodes| { stack.push_fn(|_nodes| {
@ -156,8 +267,8 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
), ),
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_std::raster::export_image_node", name: "graphene_std::raster::ExportImageNode",
types: &["Image", "&str"], types: &[Concrete("&str")],
}, },
|proto_node, stack| { |proto_node, stack| {
stack.push_fn(|nodes| { stack.push_fn(|nodes| {
@ -173,7 +284,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
( (
NodeIdentifier { NodeIdentifier {
name: "graphene_core::structural::ConsNode", name: "graphene_core::structural::ConsNode",
types: &["Image", "&str"], types: &[Concrete("Image"), Concrete("&str")],
}, },
|proto_node, stack| { |proto_node, stack| {
let node_id = proto_node.input.unwrap_node() as usize; let node_id = proto_node.input.unwrap_node() as usize;
@ -194,6 +305,14 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
), ),
]; ];
pub fn push_node(proto_node: ProtoNode, stack: &FixedSizeStack<TypeErasedNode<'static>>) {
if let Some((_id, f)) = NODE_REGISTRY.iter().find(|(id, _)| *id == proto_node.identifier) {
f(proto_node, stack);
} else {
panic!("NodeImplementation: {:?} not found in Registry", proto_node.identifier);
}
}
#[cfg(test)] #[cfg(test)]
mod protograph_testing { mod protograph_testing {
use borrow_stack::BorrowStack; use borrow_stack::BorrowStack;
@ -208,25 +327,25 @@ mod protograph_testing {
#[test] #[test]
fn add_values() { fn add_values() {
let stack = FixedSizeStack::new(256); let stack = FixedSizeStack::new(256);
let val_1_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(2u32))); let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(2u32)));
simple_lookup("ValueNode").1(val_1_protonode, &stack); push_node(val_1_protonode, &stack);
let val_2_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(40u32))); let val_2_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(40u32)));
simple_lookup("ValueNode").1(val_2_protonode, &stack); push_node(val_2_protonode, &stack);
let cons_protonode = ProtoNode { let cons_protonode = ProtoNode {
construction_args: ConstructionArgs::Nodes(vec![1]), construction_args: ConstructionArgs::Nodes(vec![1]),
input: ProtoNodeInput::Node(0), input: ProtoNodeInput::Node(0),
name: "todo!()".to_string(), identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[Concrete("u32"), Concrete("u32")]),
}; };
simple_lookup("ConsNode").1(cons_protonode, &stack); push_node(cons_protonode, &stack);
let add_protonode = ProtoNode { let add_protonode = ProtoNode {
construction_args: ConstructionArgs::None, construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::Node(2), input: ProtoNodeInput::Node(2),
name: "todo!()".to_string(), identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Concrete("u32"), Concrete("u32")]),
}; };
simple_lookup("AddNode").1(add_protonode, &stack); push_node(add_protonode, &stack);
let result = unsafe { stack.get()[3].eval(Box::new(())) }; let result = unsafe { stack.get()[3].eval(Box::new(())) };
let val = *dyn_any::downcast::<u32>(result).unwrap(); let val = *dyn_any::downcast::<u32>(result).unwrap();
@ -236,15 +355,15 @@ mod protograph_testing {
#[test] #[test]
fn greyscale_colour() { fn greyscale_colour() {
let stack = FixedSizeStack::new(256); let stack = FixedSizeStack::new(256);
let val_protonode = ProtoNode::value("name".to_string(), ConstructionArgs::Value(Box::new(Color::from_rgb8(10, 20, 30)))); let val_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(Color::from_rgb8(10, 20, 30))));
simple_lookup("ValueNode").1(val_protonode, &stack); push_node(val_protonode, &stack);
let greyscale_protonode = ProtoNode { let greyscale_protonode = ProtoNode {
construction_args: ConstructionArgs::None, construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::Node(0), input: ProtoNodeInput::Node(0),
name: "todo!()".to_string(), identifier: NodeIdentifier::new("graphene_core::raster::GrayscaleNode", &[]),
}; };
simple_lookup("GrayscaleNode").1(greyscale_protonode, &stack); push_node(greyscale_protonode, &stack);
let result = unsafe { stack.get()[1].eval(Box::new(())) }; let result = unsafe { stack.get()[1].eval(Box::new(())) };
let val = *dyn_any::downcast::<Color>(result).unwrap(); let val = *dyn_any::downcast::<Color>(result).unwrap();
@ -255,11 +374,11 @@ mod protograph_testing {
fn load_image() { fn load_image() {
let stack = FixedSizeStack::new(256); let stack = FixedSizeStack::new(256);
let image_protonode = ProtoNode { let image_protonode = ProtoNode {
construction_args: ConstructionArgs::None, construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::None, input: ProtoNodeInput::None,
name: "todo!()".to_string(), identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete("&str")]),
}; };
simple_lookup("image_node").1(image_protonode, &stack); push_node(image_protonode, &stack);
let result = unsafe { stack.get()[0].eval(Box::new("../gstd/test-image-1.png")) }; let result = unsafe { stack.get()[0].eval(Box::new("../gstd/test-image-1.png")) };
let image = *dyn_any::downcast::<Image>(result).unwrap(); let image = *dyn_any::downcast::<Image>(result).unwrap();
@ -270,25 +389,25 @@ mod protograph_testing {
fn greyscale_map_image() { fn greyscale_map_image() {
let stack = FixedSizeStack::new(256); let stack = FixedSizeStack::new(256);
let image_protonode = ProtoNode { let image_protonode = ProtoNode {
construction_args: ConstructionArgs::None, construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::None, input: ProtoNodeInput::None,
name: "todo!()".to_string(), identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete("&str")]),
}; };
simple_lookup("image_node").1(image_protonode, &stack); push_node(image_protonode, &stack);
let greyscale_protonode = ProtoNode { let greyscale_protonode = ProtoNode {
construction_args: ConstructionArgs::None, construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::None, input: ProtoNodeInput::None,
name: "todo!()".to_string(), identifier: NodeIdentifier::new("graphene_core::raster::GrayscaleNode", &[]),
}; };
simple_lookup("GrayscaleNode").1(greyscale_protonode, &stack); push_node(greyscale_protonode, &stack);
let image_map_protonode = ProtoNode { let image_map_protonode = ProtoNode {
construction_args: ConstructionArgs::Nodes(vec![1]), construction_args: ConstructionArgs::Nodes(vec![1]),
input: ProtoNodeInput::Node(0), input: ProtoNodeInput::Node(0),
name: "todo!()".to_string(), identifier: NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]),
}; };
simple_lookup("MapImageNode").1(image_map_protonode, &stack); push_node(image_map_protonode, &stack);
let result = unsafe { stack.get()[2].eval(Box::new("../gstd/test-image-1.png")) }; let result = unsafe { stack.get()[2].eval(Box::new("../gstd/test-image-1.png")) };
let image = *dyn_any::downcast::<Image>(result).unwrap(); let image = *dyn_any::downcast::<Image>(result).unwrap();

View File

@ -0,0 +1,231 @@
use std::collections::HashMap;
use std::collections::HashSet;
use crate::document::value;
use crate::document::NodeId;
#[derive(Debug, PartialEq)]
pub struct NodeIdentifier<'a> {
pub name: &'a str,
pub types: &'a [Type<'a>],
}
#[derive(Debug, PartialEq)]
pub enum Type<'a> {
Generic,
Concrete(&'a str),
}
impl<'a> From<&'a str> for Type<'a> {
fn from(s: &'a str) -> Self {
Type::Concrete(s)
}
}
impl<'a> From<&'a str> for NodeIdentifier<'a> {
fn from(s: &'a str) -> Self {
NodeIdentifier { name: s, types: &[] }
}
}
impl<'a> NodeIdentifier<'a> {
pub fn new(name: &'a str, types: &'a [Type<'a>]) -> Self {
NodeIdentifier { name, types }
}
}
#[derive(Debug, Default, PartialEq)]
pub struct ProtoNetwork {
pub inputs: Vec<NodeId>,
pub output: NodeId,
pub nodes: Vec<(NodeId, ProtoNode)>,
}
#[derive(Debug)]
pub enum ConstructionArgs {
Value(value::Value),
Nodes(Vec<NodeId>),
}
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, PartialEq)]
pub struct ProtoNode {
pub construction_args: ConstructionArgs,
pub input: ProtoNodeInput,
pub identifier: NodeIdentifier<'static>,
}
#[derive(Debug, Default, PartialEq, Eq)]
pub enum ProtoNodeInput {
None,
#[default]
Network,
Node(NodeId),
}
impl ProtoNodeInput {
pub fn unwrap_node(self) -> NodeId {
match self {
ProtoNodeInput::Node(id) => id,
_ => panic!("tried to unwrap id from non node input \n node: {:#?}", self),
}
}
}
impl ProtoNode {
pub fn value(value: ConstructionArgs) -> Self {
Self {
identifier: NodeIdentifier {
name: "graphene_core::value::ValueNode",
types: &[Type::Generic],
},
construction_args: value,
input: ProtoNodeInput::None,
}
}
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
if let ProtoNodeInput::Node(id) = self.input {
self.input = ProtoNodeInput::Node(f(id))
}
if let ConstructionArgs::Nodes(ids) = &mut self.construction_args {
ids.iter_mut().for_each(|id| *id = f(*id));
}
}
pub fn unwrap_construction_nodes(&self) -> Vec<NodeId> {
match &self.construction_args {
ConstructionArgs::Nodes(nodes) => nodes.clone(),
_ => panic!("tried to unwrap nodes from non node construction args \n node: {:#?}", self),
}
}
}
impl ProtoNetwork {
fn reverse_edges(&self) -> HashMap<NodeId, Vec<NodeId>> {
let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for (id, node) in &self.nodes {
if let ProtoNodeInput::Node(ref_id) = &node.input {
edges.entry(*id).or_default().push(*ref_id)
}
if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args {
for ref_id in ref_nodes {
edges.entry(*id).or_default().push(*ref_id)
}
}
}
edges
}
pub fn topological_sort(&self) -> Vec<NodeId> {
let mut visited = HashSet::new();
let mut stack = Vec::new();
let mut sorted = Vec::new();
let graph = self.reverse_edges();
// TODO: remove
println!("{:#?}", graph);
for (id, _) in &self.nodes {
if !visited.contains(id) {
stack.push(*id);
while let Some(id) = stack.pop() {
//TODO remove
println!("{:?}", stack);
if !visited.contains(&id) {
visited.insert(id);
if let Some(refs) = graph.get(&id) {
for ref_id in refs {
if !visited.contains(ref_id) {
stack.push(id);
stack.push(*ref_id);
break;
}
}
}
sorted.push(id);
}
}
}
}
sorted.reverse();
sorted
}
pub fn reorder_ids(&mut self) {
let order = self.topological_sort();
let lookup = self
.nodes
.iter()
.map(|(id, _)| (*id, order.iter().position(|x| x == id).unwrap() as u64))
.collect::<HashMap<u64, u64>>();
self.nodes.sort_by_key(|(id, _)| lookup.get(id).unwrap());
self.nodes.iter_mut().for_each(|(id, node)| {
node.map_ids(|id| *lookup.get(&id).unwrap());
*id = *lookup.get(id).unwrap()
});
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
use value::IntoValue;
#[test]
fn topological_sort() {
let construction_network = ProtoNetwork {
inputs: vec![10],
output: 1,
nodes: [
(
1,
ProtoNode {
identifier: "id".into(),
input: ProtoNodeInput::Node(11),
construction_args: ConstructionArgs::Nodes(vec![]),
},
),
(
10,
ProtoNode {
identifier: "cons".into(),
input: ProtoNodeInput::Network,
construction_args: ConstructionArgs::Nodes(vec![14]),
},
),
(
11,
ProtoNode {
identifier: "add".into(),
input: ProtoNodeInput::Node(10),
construction_args: ConstructionArgs::Nodes(vec![]),
},
),
(
14,
ProtoNode {
identifier: "value".into(),
input: ProtoNodeInput::None,
construction_args: ConstructionArgs::Value(2_u32.into_any()),
},
),
]
.into_iter()
.collect(),
};
let sorted = construction_network.topological_sort();
println!("{:#?}", sorted);
assert_eq!(sorted, vec![14, 10, 11, 1]);
}
}

View File

@ -25,5 +25,4 @@ syn = {version = "1.0", default-features = false, features = ["parsing", "printi
proc-macro2 = {version = "1.0", default-features = false, features = ["proc-macro"]} proc-macro2 = {version = "1.0", default-features = false, features = ["proc-macro"]}
quote = {version = "1.0", default-features = false } quote = {version = "1.0", default-features = false }
image = "*" image = "*"
rand_chacha = "0.3.1"
dyn-clone = "1.0" dyn-clone = "1.0"

View File

@ -189,7 +189,7 @@ where
} }
}*/ }*/
use graphene_core::{ops::Dynamic, AsRefNode}; use graphene_core::{ops::dynamic::Dynamic, AsRefNode};
pub struct BoxedComposition<'a, Second> { pub struct BoxedComposition<'a, Second> {
pub first: Box<dyn Node<(), Output = Dynamic<'a>>>, pub first: Box<dyn Node<(), Output = Dynamic<'a>>>,
pub second: Second, pub second: Second,

View File

@ -8,8 +8,6 @@ pub mod raster;
pub mod any; pub mod any;
pub mod document;
pub use graphene_core::*; pub use graphene_core::*;
use quote::quote; use quote::quote;