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-std",
"num-traits",
"rand_chacha",
]
[[package]]
@ -395,7 +396,6 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"rand_chacha",
"syn",
]

View file

@ -30,17 +30,21 @@ impl<'n, L: Add<R, Output = O> + 'n + Copy, R: Copy, O: 'n> Node<&'n (L, R)> for
}
}
// 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`
pub struct DynamicAddNode;
#[cfg(feature = "std")]
pub mod dynamic {
use super::*;
// Alias for a dynamic type
pub type Dynamic<'a> = alloc::boxed::Box<dyn dyn_any::DynAny<'a> + 'a>;
// 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`
pub struct DynamicAddNode;
/// Resolves the dynamic types for a dynamic node.
///
/// Macro uses format `BaseNode => (arg1: u32) (arg1: i32)`
macro_rules! resolve_dynamic_types {
// Alias for a dynamic type
pub type Dynamic<'a> = alloc::boxed::Box<dyn dyn_any::DynAny<'a> + 'a>;
/// Resolves the dynamic types for a dynamic node.
///
/// Macro uses format `BaseNode => (arg1: u32) (arg1: i32)`
macro_rules! resolve_dynamic_types {
($node:ident => $(($($arg:ident : $t:ty),*))*) => {
$(
// Check for each possible set of arguments if their types match the arguments given
@ -55,24 +59,25 @@ macro_rules! resolve_dynamic_types {
};
}
impl<'n> Node<(Dynamic<'n>, Dynamic<'n>)> for DynamicAddNode {
type Output = Dynamic<'n>;
fn eval(self, (left, right): (Dynamic, Dynamic)) -> Self::Output {
resolve_dynamic_types! { AddNode =>
(left: usize, right: usize)
(left: u8, right: u8)
(left: u16, right: u16)
(left: u32, right: u32)
(left: u64, right: u64)
(left: u128, right: u128)
(left: isize, right: isize)
(left: i8, right: i8)
(left: i16, right: i16)
(left: i32, right: i32)
(left: i64, right: i64)
(left: i128, right: i128)
(left: f32, right: f32)
(left: f64, right: f64) }
impl<'n> Node<(Dynamic<'n>, Dynamic<'n>)> for DynamicAddNode {
type Output = Dynamic<'n>;
fn eval(self, (left, right): (Dynamic, Dynamic)) -> Self::Output {
resolve_dynamic_types! { AddNode =>
(left: usize, right: usize)
(left: u8, right: u8)
(left: u16, right: u16)
(left: u32, right: u32)
(left: u64, right: u64)
(left: u128, right: u128)
(left: isize, right: isize)
(left: i8, right: i8)
(left: i16, right: i16)
(left: i32, right: i32)
(left: i64, right: i64)
(left: i128, right: i128)
(left: f32, right: f32)
(left: f64, right: f64) }
}
}
}

View file

@ -1,3 +1,4 @@
#[cfg(feature = "std")]
use dyn_any::{DynAny, StaticType};
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 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, 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 {
red: f32,
green: f32,

View file

@ -13,3 +13,4 @@ dyn-any = { path = "../../libraries/dyn-any" }
num-traits = "0.2"
borrow_stack = { path = "../borrow_stack" }
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::fmt::Display;
use std::sync::Mutex;
use dyn_any::{DynAny, StaticType};
use dyn_clone::DynClone;
pub mod value;
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha20Rng,
};
type NodeId = u64;
pub type NodeId = u64;
static RNG: Mutex<Option<ChaCha20Rng>> = Mutex::new(None);
pub fn generate_uuid() -> u64 {
@ -20,14 +20,6 @@ pub fn generate_uuid() -> u64 {
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 {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
@ -36,6 +28,8 @@ fn merge_ids(a: u64, b: u64) -> u64 {
hasher.finish()
}
type Fqn = NodeIdentifier<'static>;
#[derive(Debug, PartialEq)]
pub struct DocumentNode {
pub name: String,
@ -57,35 +51,33 @@ impl DocumentNode {
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);
if let DocumentNodeImplementation::ProtoNode(proto) = &mut self.implementation {
match first {
if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation {
let (input, mut args) = match first {
NodeInput::Value(value) => {
proto.input = ProtoNodeInput::None;
proto.construction_args = ConstructionArgs::Value(value);
assert_eq!(self.inputs.len(), 0);
return;
(ProtoNodeInput::None, ConstructionArgs::Value(value))
}
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");
NodeInput::Node(id) => (ProtoNodeInput::Node(id), ConstructionArgs::Nodes(vec![])),
NodeInput::Network => (ProtoNodeInput::Network, ConstructionArgs::Nodes(vec![])),
};
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");
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),
if let ConstructionArgs::Nodes(nodes) = &mut args {
nodes.extend(self.inputs.iter().map(|input| match input {
NodeInput::Node(id) => *id,
_ => unreachable!(),
}));
}
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)]
pub enum NodeInput {
Node(NodeId),
Value(Value),
Value(value::Value),
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 {
fn eq(&self, other: &Self) -> bool {
match (&self, &other) {
@ -110,7 +110,7 @@ impl PartialEq for NodeInput {
#[derive(Debug, PartialEq)]
pub enum DocumentNodeImplementation {
Network(NodeNetwork),
ProtoNode(ProtoNode),
Unresolved(Fqn),
}
#[derive(Debug, Default, PartialEq)]
@ -119,127 +119,6 @@ pub struct NodeNetwork {
pub output: NodeId,
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 {
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) {
@ -286,7 +165,7 @@ impl NodeNetwork {
let value_node = DocumentNode {
name: name.clone(),
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));
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)];
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);
}
DocumentNodeImplementation::Unresolved(_) => (),
}
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();
pub fn into_proto_network(self) -> ProtoNetwork {
let mut nodes: Vec<_> = self.nodes.into_iter().map(|(id, node)| (id, node.resolve_proto_node())).collect();
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)]
mod test {
use super::*;
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput};
use value::IntoValue;
#[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());
fn gen_node_id() -> NodeId {
static mut NODE_ID: NodeId = 3;
unsafe {
NODE_ID += 1;
NODE_ID
}
}
fn add_network() -> NodeNetwork {
@ -357,7 +227,7 @@ mod test {
DocumentNode {
name: "cons".into(),
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 {
name: "add".into(),
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 {
name: "cons".into(),
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 {
name: "add".into(),
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]
fn resolve_proto_node_add() {
let mut d_node = DocumentNode {
let document_node = DocumentNode {
name: "cons".into(),
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 reference = DocumentNode {
name: "cons".into(),
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode {
name: "cons".into(),
input: ProtoNodeInput::Network,
construction_args: ConstructionArgs::Nodes(vec![0]),
}),
let proto_node = document_node.resolve_proto_node();
let reference = ProtoNode {
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic]),
input: ProtoNodeInput::Network,
construction_args: ConstructionArgs::Nodes(vec![0]),
};
assert_eq!(d_node, reference);
assert_eq!(proto_node, reference);
}
#[test]
fn resolve_flatten_add() {
let construction_network = NodeNetwork {
fn resolve_flatten_add_as_proto_network() {
let construction_network = ProtoNetwork {
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,
}),
ProtoNode {
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic]),
input: ProtoNodeInput::Node(11),
construction_args: ConstructionArgs::Nodes(vec![]),
},
),
(
10,
DocumentNode {
name: "cons".into(),
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode {
name: "cons".into(),
input: ProtoNodeInput::Network,
construction_args: ConstructionArgs::Nodes(vec![14]),
}),
ProtoNode {
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic]),
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()),
}),
ProtoNode {
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic]),
input: ProtoNodeInput::Node(10),
construction_args: ConstructionArgs::Nodes(vec![]),
},
),
(14, ProtoNode::value(ConstructionArgs::Value(2_u32.into_any()))),
]
.into_iter()
.collect(),
};
let mut resolved_network = flat_network();
resolved_network.resolve_proto_nodes();
let network = flat_network();
let resolved_network = network.into_proto_network();
println!("{:#?}", resolved_network);
println!("{:#?}", construction_network);
@ -526,7 +369,7 @@ mod test {
DocumentNode {
name: "Inc".into(),
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 {
name: "cons".into(),
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 {
name: "Value: 2".into(),
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 {
name: "add".into(),
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)]
pub mod node_registry;
pub mod document;
pub mod proto;
#[cfg(test)]
mod tests {
@ -9,7 +12,9 @@ mod tests {
use graphene_core::value::ValueNode;
use graphene_core::{structural::*, RefNode};
use crate::document::value::IntoValue;
use borrow_stack::BorrowStack;
use borrow_stack::FixedSizeStack;
use dyn_any::{downcast, IntoDynAny};
use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode};
use graphene_std::ops::AddNode;
@ -27,7 +32,6 @@ mod tests {
let dynanynode: DynAnyNode<ConsNode<_, Any<'_>>, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData));
dynanynode.into_box()
});
/*
stack.push_fn(|_| {
let dynanynode: DynAnyNode<_, (u32, &u32), _, _> = DynAnyNode::new(AddNode);
dynanynode.into_box()
@ -35,79 +39,81 @@ mod tests {
stack.push_fn(|nodes| {
let compose_node = nodes[1].after(&nodes[2]);
TypeErasedNode(Box::new(compose_node))
});}*/
});
let result = unsafe { &stack.get()[0] }.eval_ref(().into_dyn());
assert_eq!(*downcast::<&u32>(result).unwrap(), &2_u32);
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
assert_eq!(*downcast::<(u32, &u32)>(result).unwrap(), (4_u32, &2_u32));
/*
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
let add = unsafe { &stack.get()[2] }.eval_ref(result);
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
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]
fn craft_from_flattened() {
use graphene_std::document::*;
// This is input and evaluated
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 {
fn execute_add() {
use crate::document::*;
use crate::node_registry::push_node;
use crate::proto::*;
use graphene_core::Node;
fn add_network() -> NodeNetwork {
NodeNetwork {
inputs: vec![0, 0],
output: 1,
nodes: [
(
0,
DocumentNode {
name: "cons".into(),
input: ProtoNodeInput::Network,
construction_args: ConstructionArgs::Nodes(vec![14]),
}),
},
),
(
11,
DocumentNode {
name: "add".into(),
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNode {
inputs: vec![NodeInput::Network, NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Concrete("u32"), Type::Concrete("u32")])),
},
),
(
1,
DocumentNode {
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()),
}),
},
),
]
inputs: vec![NodeInput::Node(0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete("u32"), Type::Concrete("u32")])),
},
),
]
.into_iter()
.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 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::any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode};
use graphene_std::raster::Image;
use graphene_std::{
any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode},
document::{ConstructionArgs, ProtoNode, ProtoNodeInput},
};
struct NodeIdentifier {
name: &'static str,
types: &'static [&'static str],
}
use crate::proto::Type;
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeInput, Type::Concrete};
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 {
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| {
stack.push_fn(|nodes| {
@ -36,7 +46,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
(
NodeIdentifier {
name: "graphene_core::ops::AddNode",
types: &["u32", "u32"],
types: &[Concrete("u32"), Concrete("u32")],
},
|proto_node, stack| {
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 {
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| {
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 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 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()
})
@ -73,7 +168,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
(
NodeIdentifier {
name: "graphene_core::any::DowncastNode",
types: &["&TypeErasedNode", "&u32"],
types: &[Concrete("&u32")],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
@ -86,7 +181,23 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
(
NodeIdentifier {
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| {
stack.push_fn(|_nodes| {
@ -102,7 +213,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
(
NodeIdentifier {
name: "graphene_core::raster::GrayscaleNode",
types: &["Color"],
types: &[],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
@ -120,7 +231,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
(
NodeIdentifier {
name: "graphene_std::raster::MapImageNode",
types: &["Image"],
types: &[],
},
|proto_node, stack| {
let node_id = proto_node.input.unwrap_node() as usize;
@ -143,8 +254,8 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
),
(
NodeIdentifier {
name: "graphene_std::raster::image_node",
types: &["&str"],
name: "graphene_std::raster::ImageNode",
types: &[Concrete("&str")],
},
|_proto_node, stack| {
stack.push_fn(|_nodes| {
@ -156,8 +267,8 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
),
(
NodeIdentifier {
name: "graphene_std::raster::export_image_node",
types: &["Image", "&str"],
name: "graphene_std::raster::ExportImageNode",
types: &[Concrete("&str")],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
@ -173,7 +284,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErase
(
NodeIdentifier {
name: "graphene_core::structural::ConsNode",
types: &["Image", "&str"],
types: &[Concrete("Image"), Concrete("&str")],
},
|proto_node, stack| {
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)]
mod protograph_testing {
use borrow_stack::BorrowStack;
@ -208,25 +327,25 @@ mod protograph_testing {
#[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_1_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(2u32)));
push_node(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 val_2_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(40u32)));
push_node(val_2_protonode, &stack);
let cons_protonode = ProtoNode {
construction_args: ConstructionArgs::Nodes(vec![1]),
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 {
construction_args: ConstructionArgs::None,
construction_args: ConstructionArgs::Nodes(vec![]),
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 val = *dyn_any::downcast::<u32>(result).unwrap();
@ -236,15 +355,15 @@ mod protograph_testing {
#[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 val_protonode = ProtoNode::value(ConstructionArgs::Value(Box::new(Color::from_rgb8(10, 20, 30))));
push_node(val_protonode, &stack);
let greyscale_protonode = ProtoNode {
construction_args: ConstructionArgs::None,
construction_args: ConstructionArgs::Nodes(vec![]),
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 val = *dyn_any::downcast::<Color>(result).unwrap();
@ -255,11 +374,11 @@ mod protograph_testing {
fn load_image() {
let stack = FixedSizeStack::new(256);
let image_protonode = ProtoNode {
construction_args: ConstructionArgs::None,
construction_args: ConstructionArgs::Nodes(vec![]),
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 image = *dyn_any::downcast::<Image>(result).unwrap();
@ -270,25 +389,25 @@ mod protograph_testing {
fn greyscale_map_image() {
let stack = FixedSizeStack::new(256);
let image_protonode = ProtoNode {
construction_args: ConstructionArgs::None,
construction_args: ConstructionArgs::Nodes(vec![]),
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 {
construction_args: ConstructionArgs::None,
construction_args: ConstructionArgs::Nodes(vec![]),
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 {
construction_args: ConstructionArgs::Nodes(vec![1]),
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 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"]}
quote = {version = "1.0", default-features = false }
image = "*"
rand_chacha = "0.3.1"
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 first: Box<dyn Node<(), Output = Dynamic<'a>>>,
pub second: Second,

View file

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