mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-03 21:08:18 +00: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:
parent
d142a9092c
commit
4ec600957c
12 changed files with 662 additions and 384 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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])),
|
||||
},
|
||||
),
|
||||
]
|
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]);
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -8,8 +8,6 @@ pub mod raster;
|
|||
|
||||
pub mod any;
|
||||
|
||||
pub mod document;
|
||||
|
||||
pub use graphene_core::*;
|
||||
|
||||
use quote::quote;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue