mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
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:
committed by
Keavon Chambers
parent
d142a9092c
commit
4ec600957c
@@ -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"
|
||||
|
||||
404
node-graph/graph-craft/src/document.rs
Normal file
404
node-graph/graph-craft/src/document.rs
Normal file
@@ -0,0 +1,404 @@
|
||||
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput, Type};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub mod value;
|
||||
|
||||
use rand_chacha::{
|
||||
rand_core::{RngCore, SeedableRng},
|
||||
ChaCha20Rng,
|
||||
};
|
||||
|
||||
pub type NodeId = u64;
|
||||
static RNG: Mutex<Option<ChaCha20Rng>> = Mutex::new(None);
|
||||
|
||||
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 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()
|
||||
}
|
||||
|
||||
type Fqn = NodeIdentifier<'static>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct DocumentNode {
|
||||
pub name: String,
|
||||
pub inputs: Vec<NodeInput>,
|
||||
pub 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_node(mut self) -> ProtoNode {
|
||||
let first = self.inputs.remove(0);
|
||||
if let DocumentNodeImplementation::Unresolved(fqn) = self.implementation {
|
||||
let (input, mut args) = match first {
|
||||
NodeInput::Value(value) => {
|
||||
assert_eq!(self.inputs.len(), 0);
|
||||
(ProtoNodeInput::None, ConstructionArgs::Value(value))
|
||||
}
|
||||
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");
|
||||
|
||||
if let ConstructionArgs::Nodes(nodes) = &mut args {
|
||||
nodes.extend(self.inputs.iter().map(|input| match input {
|
||||
NodeInput::Node(id) => *id,
|
||||
_ => unreachable!(),
|
||||
}));
|
||||
}
|
||||
ProtoNode {
|
||||
identifier: fqn,
|
||||
input,
|
||||
construction_args: args,
|
||||
}
|
||||
} else {
|
||||
unreachable!("tried to resolve not flattened node on resolved node");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NodeInput {
|
||||
Node(NodeId),
|
||||
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) {
|
||||
(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),
|
||||
Unresolved(Fqn),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct NodeNetwork {
|
||||
pub inputs: Vec<NodeId>,
|
||||
pub output: NodeId,
|
||||
pub nodes: HashMap<NodeId, DocumentNode>,
|
||||
}
|
||||
|
||||
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::<Vec<_>>();
|
||||
// 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::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic])),
|
||||
};
|
||||
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::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::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 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use value::IntoValue;
|
||||
|
||||
fn gen_node_id() -> NodeId {
|
||||
static mut NODE_ID: NodeId = 3;
|
||||
unsafe {
|
||||
NODE_ID += 1;
|
||||
NODE_ID
|
||||
}
|
||||
}
|
||||
|
||||
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::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
},
|
||||
),
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "add".into(),
|
||||
inputs: vec![NodeInput::Node(0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
|
||||
},
|
||||
),
|
||||
]
|
||||
.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::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
},
|
||||
),
|
||||
(
|
||||
2,
|
||||
DocumentNode {
|
||||
name: "add".into(),
|
||||
inputs: vec![NodeInput::Node(1)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
|
||||
},
|
||||
),
|
||||
]
|
||||
.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 document_node = DocumentNode {
|
||||
name: "cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Node(0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
};
|
||||
|
||||
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!(proto_node, reference);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_flatten_add_as_proto_network() {
|
||||
let construction_network = ProtoNetwork {
|
||||
inputs: vec![10],
|
||||
output: 1,
|
||||
nodes: [
|
||||
(
|
||||
1,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic]),
|
||||
input: ProtoNodeInput::Node(11),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
),
|
||||
(
|
||||
10,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic]),
|
||||
input: ProtoNodeInput::Network,
|
||||
construction_args: ConstructionArgs::Nodes(vec![14]),
|
||||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
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 network = flat_network();
|
||||
let resolved_network = network.into_proto_network();
|
||||
|
||||
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::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic])),
|
||||
},
|
||||
),
|
||||
(
|
||||
10,
|
||||
DocumentNode {
|
||||
name: "cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Node(14)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[Type::Generic, Type::Generic])),
|
||||
},
|
||||
),
|
||||
(
|
||||
14,
|
||||
DocumentNode {
|
||||
name: "Value: 2".into(),
|
||||
inputs: vec![NodeInput::Value(2_u32.into_any())],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic])),
|
||||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
DocumentNode {
|
||||
name: "add".into(),
|
||||
inputs: vec![NodeInput::Node(10)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Generic, Type::Generic])),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
74
node-graph/graph-craft/src/document/value.rs
Normal file
74
node-graph/graph-craft/src/document/value.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
231
node-graph/graph-craft/src/proto.rs
Normal file
231
node-graph/graph-craft/src/proto.rs
Normal 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]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user