mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Add type checking to the node graph (#1025)
* Implement type inference Add type hints to node trait Add type annotation infrastructure Refactor type ascription infrastructure Run cargo fix Insert infer types stub Remove types from node identifier * Implement covariance * Disable rejection of generic inputs + parameters * Fix lints * Extend type checking to cover Network inputs * Implement generic specialization * Relax covariance rules * Fix type annotations for TypErasedComposeNode * Fix type checking errors * Keep connection information during node resolution * Fix TypeDescriptor PartialEq implementation * Apply review suggestions * Add documentation to type inference * Add Imaginate node to document node types * Fix whitespace in macros * Add types to imaginate node * Fix type declaration for imaginate node + add console logging * Use fully qualified type names as fallback during comparison --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
a64c856ec4
commit
5dab7de68d
@@ -1,8 +1,10 @@
|
||||
use gpu_compiler_bin_wrapper::CompileRequest;
|
||||
use graph_craft::concrete;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::*;
|
||||
use graph_craft::proto::*;
|
||||
use graph_craft::{concrete, generic};
|
||||
|
||||
use graph_craft::*;
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn main() {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
@@ -17,7 +19,7 @@ fn main() {
|
||||
DocumentNode {
|
||||
name: "Inc Node".into(),
|
||||
inputs: vec![
|
||||
NodeInput::Network,
|
||||
NodeInput::Network(concrete!(u32)),
|
||||
NodeInput::Value {
|
||||
tagged_value: TaggedValue::U32(1),
|
||||
exposed: false,
|
||||
@@ -47,9 +49,9 @@ fn add_network() -> NodeNetwork {
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(u32))],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), concrete!("u32")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode")),
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -58,7 +60,7 @@ fn add_network() -> NodeNetwork {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::node(0, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode")),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
std = ["dyn-any", "dyn-any/std"]
|
||||
std = ["dyn-any", "dyn-any/std", "alloc"]
|
||||
default = ["async", "serde", "kurbo", "log", "std"]
|
||||
log = ["dep:log"]
|
||||
serde = ["dep:serde", "glam/serde"]
|
||||
@@ -17,9 +17,10 @@ gpu = ["spirv-std", "bytemuck", "glam/bytemuck", "dyn-any"]
|
||||
async = ["async-trait", "alloc"]
|
||||
nightly = []
|
||||
alloc = ["dyn-any", "bezier-rs"]
|
||||
type_id_logging = []
|
||||
|
||||
[dependencies]
|
||||
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"], optional = true, default-features = false }
|
||||
dyn-any = {path = "../../libraries/dyn-any", features = ["derive", "glam"], optional = true, default-features = false }
|
||||
|
||||
spirv-std = { git = "https://github.com/EmbarkStudios/rust-gpu", features = ["glam"] , optional = true}
|
||||
bytemuck = {version = "1.8", features = ["derive"], optional = true}
|
||||
@@ -34,4 +35,5 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
|
||||
glam = { version = "^0.22", default-features = false, features = ["scalar-math", "libm"]}
|
||||
node-macro = {path = "../node-macro"}
|
||||
specta.workspace = true
|
||||
once_cell = { version = "1.17.0", default-features = false }
|
||||
# forma = { version = "0.1.0", package = "forma-render" }
|
||||
|
||||
@@ -21,12 +21,52 @@ pub mod raster;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod vector;
|
||||
|
||||
use core::any::TypeId;
|
||||
|
||||
// pub trait Node: for<'n> NodeIO<'n> {
|
||||
pub trait Node<'i, Input: 'i>: 'i {
|
||||
type Output: 'i;
|
||||
fn eval<'s: 'i>(&'s self, input: Input) -> Self::Output;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod types;
|
||||
pub use types::*;
|
||||
|
||||
pub trait NodeIO<'i, Input: 'i>: 'i + Node<'i, Input>
|
||||
where
|
||||
Self::Output: 'i + StaticType,
|
||||
Input: 'i + StaticType,
|
||||
{
|
||||
fn input_type(&self) -> TypeId {
|
||||
TypeId::of::<Input::Static>()
|
||||
}
|
||||
fn input_type_name(&self) -> &'static str {
|
||||
core::any::type_name::<Input>()
|
||||
}
|
||||
fn output_type(&self) -> core::any::TypeId {
|
||||
TypeId::of::<<Self::Output as StaticType>::Static>()
|
||||
}
|
||||
fn output_type_name(&self) -> &'static str {
|
||||
core::any::type_name::<Self::Output>()
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
fn to_node_io(&self, parameters: Vec<Type>) -> NodeIOTypes {
|
||||
NodeIOTypes {
|
||||
input: concrete!(<Input as StaticType>::Static),
|
||||
output: concrete!(<Self::Output as StaticType>::Static),
|
||||
parameters,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, N: Node<'i, I>, I> NodeIO<'i, I> for N
|
||||
where
|
||||
N::Output: 'i + StaticType,
|
||||
I: 'i + StaticType,
|
||||
{
|
||||
}
|
||||
|
||||
/*impl<'i, I: 'i, O: 'i> Node<'i, I> for &'i dyn for<'n> Node<'n, I, Output = O> {
|
||||
type Output = O;
|
||||
|
||||
@@ -42,6 +82,8 @@ impl<'i, 'n: 'i, I: 'i, O: 'i> Node<'i, I> for &'n dyn for<'a> Node<'a, I, Outpu
|
||||
}
|
||||
}
|
||||
use core::pin::Pin;
|
||||
|
||||
use dyn_any::StaticType;
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<Box<dyn for<'a> Node<'a, I, Output = O> + 'i>> {
|
||||
type Output = O;
|
||||
|
||||
108
node-graph/gcore/src/types.rs
Normal file
108
node-graph/gcore/src/types.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use core::any::TypeId;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub use alloc::borrow::Cow;
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::borrow::Cow;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct NodeIOTypes {
|
||||
pub input: Type,
|
||||
pub output: Type,
|
||||
pub parameters: Vec<Type>,
|
||||
}
|
||||
|
||||
impl NodeIOTypes {
|
||||
pub fn new(input: Type, output: Type, parameters: Vec<Type>) -> Self {
|
||||
Self { input, output, parameters }
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! concrete {
|
||||
($type:ty) => {
|
||||
Type::Concrete(TypeDescriptor {
|
||||
id: Some(core::any::TypeId::of::<$type>()),
|
||||
name: Cow::Borrowed(core::any::type_name::<$type>()),
|
||||
})
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! generic {
|
||||
($type:ty) => {{
|
||||
Type::Generic(Cow::Borrowed(stringify!($type)))
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeIdentifier {
|
||||
pub name: Cow<'static, str>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct TypeDescriptor {
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
#[specta(skip)]
|
||||
pub id: Option<TypeId>,
|
||||
pub name: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl core::hash::Hash for TypeDescriptor {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.id.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for TypeDescriptor {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self.id, other.id) {
|
||||
(Some(id), Some(other_id)) => id == other_id,
|
||||
_ => {
|
||||
warn!("TypeDescriptor::eq: comparing types without ids based on name");
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Type {
|
||||
Generic(Cow<'static, str>),
|
||||
Concrete(TypeDescriptor),
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Type {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::Generic(arg0) => f.write_fmt(format_args!("Generic({})", arg0)),
|
||||
#[cfg(feature = "type_id_logging")]
|
||||
Self::Concrete(arg0) => f.write_fmt(format_args!("Concrete({}, {:?}))", arg0.name, arg0.id)),
|
||||
#[cfg(not(feature = "type_id_logging"))]
|
||||
Self::Concrete(arg0) => f.write_fmt(format_args!("Concrete({})", arg0.name)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Type::Generic(name) => write!(f, "{}", name),
|
||||
Type::Concrete(ty) => write!(f, "{}", ty.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for NodeIdentifier {
|
||||
fn from(s: &'static str) -> Self {
|
||||
NodeIdentifier { name: Cow::Borrowed(s) }
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeIdentifier {
|
||||
pub const fn new(name: &'static str) -> Self {
|
||||
NodeIdentifier { name: Cow::Borrowed(name) }
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,19 @@ pub fn compile_spirv(network: &graph_craft::document::NodeNetwork, input_type: &
|
||||
.arg("--release")
|
||||
.arg("--manifest-path")
|
||||
.arg(manifest_path)
|
||||
.current_dir(manifest_path.replace("Cargo.toml", ""))
|
||||
.env_clear()
|
||||
.envs(non_cargo_env_vars)
|
||||
.current_dir(manifest_path.replace("Cargo.toml", ""))
|
||||
.env_clear()
|
||||
.envs(non_cargo_env_vars)
|
||||
.arg("--features")
|
||||
.arg(features)
|
||||
.arg("--")
|
||||
.arg(input_type)
|
||||
.arg(output_type)
|
||||
// TODO: handle None case properly
|
||||
.arg(compile_dir.unwrap())
|
||||
.arg("--")
|
||||
.arg(input_type)
|
||||
.arg(output_type)
|
||||
// TODO: handle None case properly
|
||||
.arg(compile_dir.unwrap())
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.spawn()?;
|
||||
.spawn()?;
|
||||
|
||||
cargo_command.stdin.as_mut().unwrap().write_all(serialized_graph.as_bytes())?;
|
||||
let output = cargo_command.wait_with_output()?;
|
||||
|
||||
@@ -88,16 +88,16 @@ pub fn serialize_gpu(network: &ProtoNetwork, input_type: &str, output_type: &str
|
||||
|
||||
use spirv_builder::{MetadataPrintout, SpirvBuilder, SpirvMetadata};
|
||||
pub fn compile(dir: &Path) -> Result<spirv_builder::CompileResult, spirv_builder::SpirvBuilderError> {
|
||||
dbg!(&dir);
|
||||
dbg!(&dir);
|
||||
let result = SpirvBuilder::new(dir, "spirv-unknown-spv1.5")
|
||||
.print_metadata(MetadataPrintout::DependencyOnly)
|
||||
.multimodule(false)
|
||||
.preserve_bindings(true)
|
||||
.release(true)
|
||||
//.relax_struct_store(true)
|
||||
//.relax_block_layout(true)
|
||||
.spirv_metadata(SpirvMetadata::Full)
|
||||
.build()?;
|
||||
.print_metadata(MetadataPrintout::DependencyOnly)
|
||||
.multimodule(false)
|
||||
.preserve_bindings(true)
|
||||
.release(true)
|
||||
//.relax_struct_store(true)
|
||||
//.relax_block_layout(true)
|
||||
.spirv_metadata(SpirvMetadata::Full)
|
||||
.build()?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use graph_craft::document::NodeNetwork;
|
||||
use gpu_compiler as compiler;
|
||||
use graph_craft::document::NodeNetwork;
|
||||
use std::io::Write;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
println!("Starting Gpu Compiler!");
|
||||
fn main() -> anyhow::Result<()> {
|
||||
println!("Starting GPU Compiler!");
|
||||
let mut stdin = std::io::stdin();
|
||||
let mut stdout = std::io::stdout();
|
||||
let input_type = std::env::args().nth(1).expect("input type arg missing");
|
||||
let output_type = std::env::args().nth(2).expect("output type arg missing");
|
||||
let compile_dir = std::env::args().nth(3).map(|x| std::path::PathBuf::from(&x)).unwrap_or(tempfile::tempdir()?.into_path());
|
||||
let network: NodeNetwork = serde_json::from_reader(&mut stdin)?;
|
||||
let compiler = graph_craft::executor::Compiler{};
|
||||
let compiler = graph_craft::executor::Compiler {};
|
||||
let proto_network = compiler.compile(network, true);
|
||||
dbg!(&compile_dir);
|
||||
dbg!(&compile_dir);
|
||||
|
||||
let metadata = compiler::Metadata::new("project".to_owned(), vec!["test@example.com".to_owned()]);
|
||||
|
||||
|
||||
@@ -7,32 +7,32 @@ extern crate spirv_std;
|
||||
|
||||
#[cfg(target_arch = "spirv")]
|
||||
pub mod gpu {
|
||||
use super::*;
|
||||
use spirv_std::spirv;
|
||||
use spirv_std::glam::UVec3;
|
||||
use super::*;
|
||||
use spirv_std::spirv;
|
||||
use spirv_std::glam::UVec3;
|
||||
|
||||
#[allow(unused)]
|
||||
#[spirv(compute(threads({{compute_threads}})))]
|
||||
pub fn eval (
|
||||
#[spirv(global_invocation_id)] global_id: UVec3,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] a: &[{{input_type}}],
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] y: &mut [{{output_type}}],
|
||||
#[spirv(push_constant)] push_consts: &graphene_core::gpu::PushConstants,
|
||||
) {
|
||||
let gid = global_id.x as usize;
|
||||
// Only process up to n, which is the length of the buffers.
|
||||
if global_id.x < push_consts.n {
|
||||
y[gid] = node_graph(a[gid]);
|
||||
}
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[spirv(compute(threads({{compute_threads}})))]
|
||||
pub fn eval (
|
||||
#[spirv(global_invocation_id)] global_id: UVec3,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] a: &[{{input_type}}],
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] y: &mut [{{output_type}}],
|
||||
#[spirv(push_constant)] push_consts: &graphene_core::gpu::PushConstants,
|
||||
) {
|
||||
let gid = global_id.x as usize;
|
||||
// Only process up to n, which is the length of the buffers.
|
||||
if global_id.x < push_consts.n {
|
||||
y[gid] = node_graph(a[gid]);
|
||||
}
|
||||
}
|
||||
|
||||
fn node_graph(input: {{input_type}}) -> {{output_type}} {
|
||||
use graphene_core::Node;
|
||||
fn node_graph(input: {{input_type}}) -> {{output_type}} {
|
||||
use graphene_core::Node;
|
||||
|
||||
{% for node in nodes %}
|
||||
let {{node.id}} = {{node.fqn}}::new({% for arg in node.args %}{{arg}}, {% endfor %});
|
||||
{% endfor %}
|
||||
{% for node in nodes %}
|
||||
let {{node.id}} = {{node.fqn}}::new({% for arg in node.args %}{{arg}}, {% endfor %});
|
||||
{% endfor %}
|
||||
{{last_node}}.eval(input)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ serde = ["dep:serde", "graphene-core/serde", "glam/serde"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
graphene-core = { path = "../gcore", features = ["alloc"] }
|
||||
graphene-core = { path = "../gcore", features = ["std"] }
|
||||
dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types", "rc", "glam"] }
|
||||
num-traits = "0.2"
|
||||
dyn-clone = "1.0"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::document::value::TaggedValue;
|
||||
use crate::generic;
|
||||
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput, Type};
|
||||
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use graphene_core::{NodeIdentifier, Type};
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use glam::IVec2;
|
||||
use graphene_core::TypeDescriptor;
|
||||
use rand_chacha::{
|
||||
rand_core::{RngCore, SeedableRng},
|
||||
ChaCha20Rng,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Mutex;
|
||||
|
||||
@@ -59,7 +61,7 @@ impl DocumentNode {
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, input)| matches!(input, NodeInput::Network))
|
||||
.filter(|(_, input)| matches!(input, NodeInput::Network(_)))
|
||||
.nth(offset)
|
||||
.expect("no network input");
|
||||
|
||||
@@ -80,9 +82,9 @@ impl DocumentNode {
|
||||
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode.");
|
||||
(ProtoNodeInput::Node(node_id), ConstructionArgs::Nodes(vec![]))
|
||||
}
|
||||
NodeInput::Network => (ProtoNodeInput::Network, ConstructionArgs::Nodes(vec![])),
|
||||
NodeInput::Network(ty) => (ProtoNodeInput::Network(ty), 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::Network(_))), "recieved non resolved parameter");
|
||||
assert!(
|
||||
!self.inputs.iter().any(|input| matches!(input, NodeInput::Value { .. })),
|
||||
"recieved value as parameter. inupts: {:#?}, construction_args: {:#?}",
|
||||
@@ -129,12 +131,12 @@ impl DocumentNode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, specta::Type)]
|
||||
#[derive(Debug, Clone, PartialEq, Hash, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum NodeInput {
|
||||
Node { node_id: NodeId, output_index: usize },
|
||||
Value { tagged_value: value::TaggedValue, exposed: bool },
|
||||
Network,
|
||||
Network(Type),
|
||||
}
|
||||
|
||||
impl NodeInput {
|
||||
@@ -153,17 +155,14 @@ impl NodeInput {
|
||||
match self {
|
||||
NodeInput::Node { .. } => true,
|
||||
NodeInput::Value { exposed, .. } => *exposed,
|
||||
NodeInput::Network => false,
|
||||
NodeInput::Network(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NodeInput {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (&self, &other) {
|
||||
(Self::Node { node_id: n0, output_index: o0 }, Self::Node { node_id: n1, output_index: o1 }) => n0 == n1 && o0 == o1,
|
||||
(Self::Value { tagged_value: v1, .. }, Self::Value { tagged_value: v2, .. }) => v1 == v2,
|
||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||
pub fn ty(&self) -> Type {
|
||||
match self {
|
||||
NodeInput::Node { .. } => unreachable!("ty() called on NodeInput::Node"),
|
||||
NodeInput::Value { tagged_value, .. } => tagged_value.ty(),
|
||||
NodeInput::Network(ty) => ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,7 +356,7 @@ impl NodeNetwork {
|
||||
.unwrap_or_else(|| panic!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self));
|
||||
|
||||
if self.disabled.contains(&id) {
|
||||
node.implementation = DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]));
|
||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
|
||||
node.inputs.drain(1..);
|
||||
self.nodes.insert(id, node);
|
||||
return;
|
||||
@@ -394,7 +393,7 @@ impl NodeNetwork {
|
||||
let value_node = DocumentNode {
|
||||
name,
|
||||
inputs: vec![NodeInput::Value { tagged_value, exposed }],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[generic!("T")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
};
|
||||
assert!(!self.nodes.contains_key(&new_id));
|
||||
@@ -402,7 +401,7 @@ impl NodeNetwork {
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(new_id, 0, *offset);
|
||||
}
|
||||
NodeInput::Network => {
|
||||
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;
|
||||
@@ -410,7 +409,7 @@ impl NodeNetwork {
|
||||
}
|
||||
}
|
||||
}
|
||||
node.implementation = DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]));
|
||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
|
||||
node.inputs = inner_network
|
||||
.outputs
|
||||
.iter()
|
||||
@@ -419,6 +418,7 @@ impl NodeNetwork {
|
||||
output_index: node_output_index,
|
||||
})
|
||||
.collect();
|
||||
|
||||
for node_id in new_nodes {
|
||||
self.flatten_with_fns(node_id, map_ids, gen_id);
|
||||
}
|
||||
@@ -456,8 +456,8 @@ impl NodeNetwork {
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "Input".into(),
|
||||
inputs: vec![NodeInput::Network],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
inputs: vec![NodeInput::Network(concrete!(u32))],
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
|
||||
metadata: DocumentNodeMetadata { position: (8, 4).into() },
|
||||
},
|
||||
),
|
||||
@@ -466,7 +466,7 @@ impl NodeNetwork {
|
||||
DocumentNode {
|
||||
name: "Output".into(),
|
||||
inputs: vec![NodeInput::node(output_node_id, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
|
||||
metadata: DocumentNodeMetadata { position: (output_offset, 4).into() },
|
||||
},
|
||||
),
|
||||
@@ -553,7 +553,8 @@ impl NodeNetwork {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use graphene_core::NodeIdentifier;
|
||||
|
||||
fn gen_node_id() -> NodeId {
|
||||
static mut NODE_ID: NodeId = 3;
|
||||
@@ -572,9 +573,9 @@ mod test {
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(u32))],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -583,7 +584,7 @@ mod test {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::node(0, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::AddNode".into()),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -605,9 +606,9 @@ mod test {
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(u32))],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -616,7 +617,7 @@ mod test {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::node(1, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::AddNode".into()),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -637,7 +638,7 @@ mod test {
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![
|
||||
NodeInput::Network,
|
||||
NodeInput::Network(concrete!(u32)),
|
||||
NodeInput::Value {
|
||||
tagged_value: value::TaggedValue::U32(2),
|
||||
exposed: false,
|
||||
@@ -663,15 +664,15 @@ mod test {
|
||||
fn resolve_proto_node_add() {
|
||||
let document_node = DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::node(0, 0)],
|
||||
inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::node(0, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
|
||||
};
|
||||
|
||||
let proto_node = document_node.resolve_proto_node();
|
||||
let reference = ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")]),
|
||||
input: ProtoNodeInput::Network,
|
||||
identifier: "graphene_core::structural::ConsNode".into(),
|
||||
input: ProtoNodeInput::Network(concrete!(u32)),
|
||||
construction_args: ConstructionArgs::Nodes(vec![0]),
|
||||
};
|
||||
assert_eq!(proto_node, reference);
|
||||
@@ -686,7 +687,7 @@ mod test {
|
||||
(
|
||||
1,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]),
|
||||
identifier: "graphene_core::ops::IdNode".into(),
|
||||
input: ProtoNodeInput::Node(11),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
@@ -694,15 +695,15 @@ mod test {
|
||||
(
|
||||
10,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")]),
|
||||
input: ProtoNodeInput::Network,
|
||||
identifier: "graphene_core::structural::ConsNode".into(),
|
||||
input: ProtoNodeInput::Network(concrete!(u32)),
|
||||
construction_args: ConstructionArgs::Nodes(vec![14]),
|
||||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")]),
|
||||
identifier: "graphene_core::ops::AddNode".into(),
|
||||
input: ProtoNodeInput::Node(10),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
},
|
||||
@@ -731,16 +732,16 @@ mod test {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![NodeInput::node(11, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
|
||||
},
|
||||
),
|
||||
(
|
||||
10,
|
||||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::node(14, 0)],
|
||||
inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::node(14, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -752,7 +753,7 @@ mod test {
|
||||
exposed: false,
|
||||
}],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[generic!("T")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -761,7 +762,7 @@ mod test {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::node(10, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::AddNode".into()),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -780,18 +781,18 @@ mod test {
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Identity 1".into(),
|
||||
inputs: vec![NodeInput::Network],
|
||||
inputs: vec![NodeInput::Network(concrete!(u32))],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
|
||||
},
|
||||
),
|
||||
(
|
||||
2,
|
||||
DocumentNode {
|
||||
name: "Identity 2".into(),
|
||||
inputs: vec![NodeInput::Network],
|
||||
inputs: vec![NodeInput::Network(concrete!(u32))],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -821,7 +822,7 @@ mod test {
|
||||
name: "Result".into(),
|
||||
inputs: vec![result_node_input],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ use dyn_any::{DynAny, Upcast};
|
||||
use dyn_clone::DynClone;
|
||||
pub use glam::{DAffine2, DVec2};
|
||||
use graphene_core::raster::LuminanceCalculation;
|
||||
use graphene_core::Node;
|
||||
use graphene_core::{Node, Type};
|
||||
use std::hash::Hash;
|
||||
pub use std::sync::Arc;
|
||||
|
||||
@@ -142,6 +142,32 @@ impl<'a> TaggedValue {
|
||||
TaggedValue::LayerPath(x) => Box::new(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> Type {
|
||||
use graphene_core::TypeDescriptor;
|
||||
use std::borrow::Cow;
|
||||
match self {
|
||||
TaggedValue::None => concrete!(()),
|
||||
TaggedValue::String(_) => concrete!(String),
|
||||
TaggedValue::U32(_) => concrete!(u32),
|
||||
TaggedValue::F32(_) => concrete!(f32),
|
||||
TaggedValue::F64(_) => concrete!(f64),
|
||||
TaggedValue::Bool(_) => concrete!(bool),
|
||||
TaggedValue::DVec2(_) => concrete!(DVec2),
|
||||
TaggedValue::OptionalDVec2(_) => concrete!(Option<DVec2>),
|
||||
TaggedValue::Image(_) => concrete!(graphene_core::raster::Image),
|
||||
TaggedValue::RcImage(_) => concrete!(Option<Arc<graphene_core::raster::Image>>),
|
||||
TaggedValue::Color(_) => concrete!(graphene_core::raster::Color),
|
||||
TaggedValue::Subpath(_) => concrete!(graphene_core::vector::subpath::Subpath),
|
||||
TaggedValue::RcSubpath(_) => concrete!(Arc<graphene_core::vector::subpath::Subpath>),
|
||||
TaggedValue::ImaginateSamplingMethod(_) => concrete!(ImaginateSamplingMethod),
|
||||
TaggedValue::ImaginateMaskStartingFill(_) => concrete!(ImaginateMaskStartingFill),
|
||||
TaggedValue::ImaginateStatus(_) => concrete!(ImaginateStatus),
|
||||
TaggedValue::LayerPath(_) => concrete!(Option<Vec<u64>>),
|
||||
TaggedValue::DAffine2(_) => concrete!(DAffine2),
|
||||
TaggedValue::LuminanceCalculation(_) => concrete!(LuminanceCalculation),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UpcastNode {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
extern crate graphene_core;
|
||||
pub use graphene_core::{concrete, generic, NodeIdentifier, Type, TypeDescriptor};
|
||||
|
||||
pub mod document;
|
||||
pub mod proto;
|
||||
|
||||
|
||||
@@ -4,87 +4,16 @@ use std::hash::Hash;
|
||||
|
||||
use crate::document::value;
|
||||
use crate::document::NodeId;
|
||||
use dyn_any::DynAny;
|
||||
use graphene_core::*;
|
||||
use std::pin::Pin;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! concrete {
|
||||
($type:expr) => {
|
||||
Type::Concrete(std::borrow::Cow::Borrowed($type))
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! generic {
|
||||
($type:expr) => {
|
||||
Type::Generic(std::borrow::Cow::Borrowed($type))
|
||||
};
|
||||
}
|
||||
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
|
||||
pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync;
|
||||
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync)>;
|
||||
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync>>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeIdentifier {
|
||||
pub name: std::borrow::Cow<'static, str>,
|
||||
pub types: std::borrow::Cow<'static, [Type]>,
|
||||
}
|
||||
|
||||
impl NodeIdentifier {
|
||||
pub fn fully_qualified_name(&self) -> String {
|
||||
let mut name = String::new();
|
||||
name.push_str(self.name.as_ref());
|
||||
name.push('<');
|
||||
for t in self.types.as_ref() {
|
||||
name.push_str(t.to_string().as_str());
|
||||
name.push_str(", ");
|
||||
}
|
||||
name.pop();
|
||||
name.pop();
|
||||
name.push('>');
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Type {
|
||||
Generic(std::borrow::Cow<'static, str>),
|
||||
Concrete(std::borrow::Cow<'static, str>),
|
||||
}
|
||||
|
||||
impl From<&'static str> for Type {
|
||||
fn from(s: &'static str) -> Self {
|
||||
Type::Concrete(std::borrow::Cow::Borrowed(s))
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Type::Generic(name) => write!(f, "{}", name),
|
||||
Type::Concrete(name) => write!(f, "{}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub const fn from_str(concrete: &'static str) -> Self {
|
||||
Type::Concrete(std::borrow::Cow::Borrowed(concrete))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for NodeIdentifier {
|
||||
fn from(s: &'static str) -> Self {
|
||||
NodeIdentifier {
|
||||
name: std::borrow::Cow::Borrowed(s),
|
||||
types: std::borrow::Cow::Borrowed(&[]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeIdentifier {
|
||||
pub const fn new(name: &'static str, types: &'static [Type]) -> Self {
|
||||
NodeIdentifier {
|
||||
name: std::borrow::Cow::Borrowed(name),
|
||||
types: std::borrow::Cow::Borrowed(types),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type NodeConstructor = for<'a> fn(Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'static>;
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct ProtoNetwork {
|
||||
@@ -140,11 +69,10 @@ pub struct ProtoNode {
|
||||
pub identifier: NodeIdentifier,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum ProtoNodeInput {
|
||||
None,
|
||||
#[default]
|
||||
Network,
|
||||
Network(Type),
|
||||
Node(NodeId),
|
||||
}
|
||||
|
||||
@@ -161,11 +89,14 @@ impl ProtoNode {
|
||||
pub fn stable_node_id(&self) -> Option<NodeId> {
|
||||
use std::hash::Hasher;
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
self.identifier.fully_qualified_name().hash(&mut hasher);
|
||||
self.identifier.name.hash(&mut hasher);
|
||||
self.construction_args.hash(&mut hasher);
|
||||
match self.input {
|
||||
ProtoNodeInput::None => "none".hash(&mut hasher),
|
||||
ProtoNodeInput::Network => "network".hash(&mut hasher),
|
||||
ProtoNodeInput::Network(ref ty) => {
|
||||
"network".hash(&mut hasher);
|
||||
ty.hash(&mut hasher);
|
||||
}
|
||||
ProtoNodeInput::Node(id) => id.hash(&mut hasher),
|
||||
};
|
||||
Some(hasher.finish() as NodeId)
|
||||
@@ -173,7 +104,7 @@ impl ProtoNode {
|
||||
|
||||
pub fn value(value: ConstructionArgs) -> Self {
|
||||
Self {
|
||||
identifier: NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic(Cow::Borrowed("T"))]),
|
||||
identifier: NodeIdentifier::new("graphene_core::value::ValueNode"),
|
||||
construction_args: value,
|
||||
input: ProtoNodeInput::None,
|
||||
}
|
||||
@@ -260,20 +191,22 @@ impl ProtoNetwork {
|
||||
}
|
||||
|
||||
pub fn resolve_inputs(&mut self) {
|
||||
while !self.resolve_inputs_impl() {}
|
||||
let mut resolved = HashSet::new();
|
||||
while !self.resolve_inputs_impl(&mut resolved) {}
|
||||
}
|
||||
fn resolve_inputs_impl(&mut self) -> bool {
|
||||
fn resolve_inputs_impl(&mut self, resolved: &mut HashSet<NodeId>) -> bool {
|
||||
self.reorder_ids();
|
||||
|
||||
let mut lookup = self.nodes.iter().map(|(id, _)| (*id, *id)).collect::<HashMap<_, _>>();
|
||||
let compose_node_id = self.nodes.len() as NodeId;
|
||||
let inputs = self.nodes.iter().map(|(_, node)| node.input).collect::<Vec<_>>();
|
||||
let inputs = self.nodes.iter().map(|(_, node)| node.input.clone()).collect::<Vec<_>>();
|
||||
|
||||
if let Some((input_node, id, input)) = self.nodes.iter_mut().find_map(|(id, node)| {
|
||||
let resolved_lookup = resolved.clone();
|
||||
if let Some((input_node, id, input)) = self.nodes.iter_mut().filter(|(id, _)| !resolved_lookup.contains(id)).find_map(|(id, node)| {
|
||||
if let ProtoNodeInput::Node(input_node) = node.input {
|
||||
node.input = ProtoNodeInput::None;
|
||||
resolved.insert(*id);
|
||||
let pre_node_input = inputs.get(input_node as usize).expect("input node should exist");
|
||||
Some((input_node, *id, *pre_node_input))
|
||||
Some((input_node, *id, pre_node_input.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -283,7 +216,7 @@ impl ProtoNetwork {
|
||||
self.nodes.push((
|
||||
compose_node_id,
|
||||
ProtoNode {
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>", &[generic!("T"), generic!("U")]),
|
||||
identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
|
||||
construction_args: ConstructionArgs::Nodes(vec![input_node, id]),
|
||||
input,
|
||||
},
|
||||
@@ -373,6 +306,172 @@ impl ProtoNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `TypingContext` is used to store the types of the nodes indexed by their stable node id.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct TypingContext {
|
||||
lookup: Cow<'static, HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>>,
|
||||
inferred: HashMap<NodeId, NodeIOTypes>,
|
||||
constructor: HashMap<NodeId, NodeConstructor>,
|
||||
}
|
||||
|
||||
impl TypingContext {
|
||||
/// Creates a new `TypingContext` with the given lookup table.
|
||||
pub fn new(lookup: &'static HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>) -> Self {
|
||||
Self {
|
||||
lookup: Cow::Borrowed(lookup),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the `TypingContext` wtih a given proto network. This will infer the types of the nodes
|
||||
/// and store them in the `inferred` field. The proto network has to be topologically sorted
|
||||
/// and contain fully resolved stable node ids.
|
||||
pub fn update(&mut self, network: &ProtoNetwork) -> Result<(), String> {
|
||||
for (id, node) in network.nodes.iter() {
|
||||
self.infer(*id, node)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the node constructor for a given node id.
|
||||
pub fn constructor(&self, node_id: NodeId) -> Option<NodeConstructor> {
|
||||
self.constructor.get(&node_id).copied()
|
||||
}
|
||||
|
||||
/// Returns the inferred types for a given node id.
|
||||
pub fn infer(&mut self, node_id: NodeId, node: &ProtoNode) -> Result<NodeIOTypes, String> {
|
||||
let identifier = node.identifier.name.clone();
|
||||
|
||||
// Return the inferred type if it is already known
|
||||
if let Some(infered) = self.inferred.get(&node_id) {
|
||||
return Ok(infered.clone());
|
||||
}
|
||||
|
||||
let parameters = match node.construction_args {
|
||||
// If the node has a value parameter we can infer the return type from it
|
||||
ConstructionArgs::Value(ref v) => {
|
||||
assert!(matches!(node.input, ProtoNodeInput::None));
|
||||
let types = NodeIOTypes::new(concrete!(()), v.ty(), vec![]);
|
||||
self.inferred.insert(node_id, types.clone());
|
||||
return Ok(types);
|
||||
}
|
||||
// If the node has nodes as parameters we can infer the types from the node outputs
|
||||
ConstructionArgs::Nodes(ref nodes) => nodes
|
||||
.iter()
|
||||
.map(|id| {
|
||||
self.inferred
|
||||
.get(id)
|
||||
.ok_or(format!("Inferring type of {node_id} depends on {id} which is not present in the typing context"))
|
||||
.map(|node| node.output.clone())
|
||||
})
|
||||
.collect::<Result<Vec<Type>, String>>()?,
|
||||
};
|
||||
|
||||
// Get the node input type from the proto node declaration
|
||||
let input = match node.input {
|
||||
ProtoNodeInput::None => concrete!(()),
|
||||
ProtoNodeInput::Network(ref ty) => ty.clone(),
|
||||
ProtoNodeInput::Node(id) => {
|
||||
let input = self
|
||||
.inferred
|
||||
.get(&id)
|
||||
.ok_or(format!("Inferring type of {node_id} depends on {id} which is not present in the typing context"))?;
|
||||
input.output.clone()
|
||||
}
|
||||
};
|
||||
let impls = self.lookup.get(&node.identifier).ok_or(format!("No implementations found for {:?}", node.identifier))?;
|
||||
|
||||
if matches!(input, Type::Generic(_)) {
|
||||
return Err(format!("Generic types are not supported as inputs yet {:?} occured in {:?}", &input, node.identifier));
|
||||
}
|
||||
if parameters.iter().any(|p| matches!(p, Type::Generic(_))) {
|
||||
return Err(format!("Generic types are not supported in parameters: {:?} occured in {:?}", parameters, node.identifier));
|
||||
}
|
||||
let covariant = |output, input| match (&output, &input) {
|
||||
(Type::Concrete(t1), Type::Concrete(t2)) => t1 == t2,
|
||||
(Type::Concrete(_), Type::Generic(_)) => true,
|
||||
// TODO: relax this requirement when allowing generic types as inputs
|
||||
(Type::Generic(_), _) => false,
|
||||
};
|
||||
|
||||
// List of all implementations that match the input and parameter types
|
||||
let valid_output_types = impls
|
||||
.keys()
|
||||
.filter(|node_io| covariant(input.clone(), node_io.input.clone()) && parameters.iter().zip(node_io.parameters.iter()).all(|(p1, p2)| covariant(p1.clone(), p2.clone())))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Attempt to substitute generic types with concrete types and save the list of results
|
||||
let substitution_results = valid_output_types
|
||||
.iter()
|
||||
.map(|node_io| {
|
||||
collect_generics(node_io)
|
||||
.iter()
|
||||
.try_for_each(|generic| check_generic(node_io, &input, ¶meters, generic).map(|_| ()))
|
||||
.map(|_| {
|
||||
if let Type::Generic(out) = &node_io.output {
|
||||
((*node_io).clone(), check_generic(node_io, &input, ¶meters, out).unwrap())
|
||||
} else {
|
||||
((*node_io).clone(), node_io.output.clone())
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Collect all substitutions that are valid
|
||||
let valid_impls = substitution_results.iter().filter_map(|result| result.as_ref().ok()).collect::<Vec<_>>();
|
||||
|
||||
match valid_impls.as_slice() {
|
||||
[] => {
|
||||
dbg!(&self.inferred);
|
||||
Err(format!(
|
||||
"No implementations found for {identifier} with \ninput: {input:?} and \nparameters: {parameters:?}.\nOther Implementations found: {:?}",
|
||||
impls,
|
||||
))
|
||||
}
|
||||
[(org_nio, output)] => {
|
||||
let node_io = NodeIOTypes::new(input, (*output).clone(), parameters);
|
||||
|
||||
// Save the inferred type
|
||||
self.inferred.insert(node_id, node_io.clone());
|
||||
self.constructor.insert(node_id, impls[org_nio]);
|
||||
Ok(node_io)
|
||||
}
|
||||
_ => Err(format!(
|
||||
"Multiple implementations found for {identifier} with input {input:?} and parameters {parameters:?} (valid types: {valid_output_types:?}"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of all generic types used in the node
|
||||
fn collect_generics(types: &NodeIOTypes) -> Vec<Cow<'static, str>> {
|
||||
let inputs = [&types.input].into_iter().chain(types.parameters.iter());
|
||||
let mut generics = inputs
|
||||
.filter_map(|t| match t {
|
||||
Type::Generic(out) => Some(out.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if let Type::Generic(out) = &types.output {
|
||||
generics.push(out.clone());
|
||||
}
|
||||
generics.dedup();
|
||||
generics
|
||||
}
|
||||
|
||||
/// Checks if a generic type can be substituted with a concrete type and returns the concrete type
|
||||
fn check_generic(types: &NodeIOTypes, input: &Type, parameters: &[Type], generic: &str) -> Result<Type, String> {
|
||||
let inputs = [(&types.input, input)].into_iter().chain(types.parameters.iter().zip(parameters.iter()));
|
||||
let mut concrete_inputs = inputs.filter(|(ni, _)| matches!(ni, Type::Generic(input) if generic == input));
|
||||
let (_, out_ty) = concrete_inputs
|
||||
.next()
|
||||
.ok_or_else(|| format!("Generic output type {generic} is not dependent on input {input:?} or parameters {parameters:?}",))?;
|
||||
if concrete_inputs.any(|(_, ty)| ty != out_ty) {
|
||||
return Err(format!("Generic output type {generic} is dependent on multiple inputs or parameters",));
|
||||
}
|
||||
Ok(out_ty.clone())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -436,12 +535,12 @@ mod test {
|
||||
assert_eq!(
|
||||
ids,
|
||||
vec![
|
||||
17495035641492238530,
|
||||
14931179783740213471,
|
||||
2268573767208263092,
|
||||
14616574692620381527,
|
||||
12110007198416821768,
|
||||
11185814750012198757
|
||||
15907139529964845467,
|
||||
17186311536944112733,
|
||||
1674503539363691855,
|
||||
10408773954839245246,
|
||||
1677533587730447846,
|
||||
6826908746727711035
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -471,7 +570,7 @@ mod test {
|
||||
10,
|
||||
ProtoNode {
|
||||
identifier: "cons".into(),
|
||||
input: ProtoNodeInput::Network,
|
||||
input: ProtoNodeInput::Network(concrete!(u32)),
|
||||
construction_args: ConstructionArgs::Nodes(vec![14]),
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use dyn_any::StaticType;
|
||||
pub use graph_craft::proto::{Any, TypeErasedNode, TypeErasedPinned, TypeErasedPinnedRef};
|
||||
use graphene_core::NodeIO;
|
||||
pub use graphene_core::{generic, ops, Node};
|
||||
use std::{marker::PhantomData, pin::Pin};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct DynAnyNode<I, O, Node> {
|
||||
node: Node,
|
||||
@@ -39,17 +41,13 @@ impl<_I, _O, S0> DynAnyRefNode<_I, _O, S0> {
|
||||
}
|
||||
}
|
||||
|
||||
pub type TypeErasedNode<'n> = dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync;
|
||||
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync)>;
|
||||
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync>>;
|
||||
|
||||
pub trait IntoTypeErasedNode<'n> {
|
||||
fn into_type_erased(self) -> TypeErasedPinned<'n>;
|
||||
}
|
||||
|
||||
impl<'n, N: 'n> IntoTypeErasedNode<'n> for N
|
||||
where
|
||||
N: for<'i> Node<'i, Any<'i>, Output = Any<'i>> + Send + Sync + 'n,
|
||||
N: for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + Send + Sync + 'n,
|
||||
{
|
||||
fn into_type_erased(self) -> TypeErasedPinned<'n> {
|
||||
Box::pin(self)
|
||||
@@ -70,7 +68,7 @@ impl<N: Copy, O: StaticType> Copy for DowncastNode<O, N> {}
|
||||
#[node_macro::node_fn(DowncastNode<_O>)]
|
||||
fn downcast<N, _O: StaticType>(input: Any<'input>, node: &'input N) -> _O
|
||||
where
|
||||
N: Node<'input, Any<'input>, Output = Any<'input>>,
|
||||
N: for<'any_input> Node<'any_input, Any<'any_input>, Output = Any<'any_input>> + 'input,
|
||||
{
|
||||
let node_name = core::any::type_name::<N>();
|
||||
let out = dyn_any::downcast(node.eval(input)).unwrap_or_else(|e| panic!("DowncastNode Input {e} in:\n{node_name}"));
|
||||
@@ -148,8 +146,6 @@ impl<'a> ComposeTypeErased<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
|
||||
|
||||
pub fn input_node<O: StaticType>(n: TypeErasedPinnedRef) -> DowncastBothNode<(), O> {
|
||||
DowncastBothNode::new(n)
|
||||
}
|
||||
|
||||
@@ -24,3 +24,4 @@ rand_chacha = "0.3.1"
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
glam = { version = "0.22" }
|
||||
once_cell = "1.17.0"
|
||||
|
||||
@@ -6,28 +6,44 @@ use dyn_any::StaticType;
|
||||
use graph_craft::document::value::UpcastNode;
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::executor::Executor;
|
||||
use graph_craft::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
use graph_craft::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, TypingContext};
|
||||
use graphene_std::any::{Any, TypeErasedPinned, TypeErasedPinnedRef};
|
||||
|
||||
use crate::node_registry::constrcut_node;
|
||||
use crate::node_registry;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DynamicExecutor {
|
||||
output: NodeId,
|
||||
tree: BorrowTree,
|
||||
typing_context: TypingContext,
|
||||
}
|
||||
|
||||
impl Default for DynamicExecutor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
output: Default::default(),
|
||||
tree: Default::default(),
|
||||
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DynamicExecutor {
|
||||
pub fn new(proto_network: ProtoNetwork) -> Self {
|
||||
pub fn new(proto_network: ProtoNetwork) -> Result<Self, String> {
|
||||
let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY);
|
||||
typing_context.update(&proto_network)?;
|
||||
let output = proto_network.output;
|
||||
let tree = BorrowTree::new(proto_network);
|
||||
Self { tree, output }
|
||||
let tree = BorrowTree::new(proto_network, &typing_context)?;
|
||||
|
||||
Ok(Self { tree, output, typing_context })
|
||||
}
|
||||
|
||||
pub fn update(&mut self, proto_network: ProtoNetwork) {
|
||||
pub fn update(&mut self, proto_network: ProtoNetwork) -> Result<(), String> {
|
||||
self.output = proto_network.output;
|
||||
self.typing_context.update(&proto_network)?;
|
||||
trace!("setting output to {}", self.output);
|
||||
self.tree.update(proto_network);
|
||||
self.tree.update(proto_network, &self.typing_context)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +54,7 @@ impl Executor for DynamicExecutor {
|
||||
}
|
||||
|
||||
pub struct NodeContainer<'n> {
|
||||
node: TypeErasedPinned<'n>,
|
||||
pub node: TypeErasedPinned<'n>,
|
||||
// the dependencies are only kept to ensure that the nodes are not dropped while still in use
|
||||
_dependencies: Vec<Arc<NodeContainer<'static>>>,
|
||||
}
|
||||
@@ -50,6 +66,10 @@ impl<'a> core::fmt::Debug for NodeContainer<'a> {
|
||||
}
|
||||
|
||||
impl<'a> NodeContainer<'a> {
|
||||
pub fn new(node: TypeErasedPinned<'a>, _dependencies: Vec<Arc<NodeContainer<'static>>>) -> Self {
|
||||
Self { node, _dependencies }
|
||||
}
|
||||
|
||||
/// Return a static reference to the TypeErasedNode
|
||||
/// # Safety
|
||||
/// This is unsafe because the returned reference is only valid as long as the NodeContainer is alive
|
||||
@@ -58,7 +78,7 @@ impl<'a> NodeContainer<'a> {
|
||||
}
|
||||
}
|
||||
impl NodeContainer<'static> {
|
||||
unsafe fn static_ref(&self) -> TypeErasedPinnedRef<'static> {
|
||||
pub unsafe fn static_ref(&self) -> TypeErasedPinnedRef<'static> {
|
||||
let s = &*(self as *const Self);
|
||||
*(&s.node.as_ref() as *const TypeErasedPinnedRef<'static>)
|
||||
}
|
||||
@@ -70,24 +90,24 @@ pub struct BorrowTree {
|
||||
}
|
||||
|
||||
impl BorrowTree {
|
||||
pub fn new(proto_network: ProtoNetwork) -> Self {
|
||||
pub fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Self, String> {
|
||||
let mut nodes = BorrowTree::default();
|
||||
for (id, node) in proto_network.nodes {
|
||||
nodes.push_node(id, node)
|
||||
nodes.push_node(id, node, typing_context)?
|
||||
}
|
||||
nodes
|
||||
Ok(nodes)
|
||||
}
|
||||
|
||||
/// Pushes new nodes into the tree and return orphaned nodes
|
||||
pub fn update(&mut self, proto_network: ProtoNetwork) -> Vec<NodeId> {
|
||||
pub fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Vec<NodeId>, String> {
|
||||
let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect();
|
||||
for (id, node) in proto_network.nodes {
|
||||
if !self.nodes.contains_key(&id) {
|
||||
self.push_node(id, node);
|
||||
self.push_node(id, node, typing_context)?;
|
||||
old_nodes.remove(&id);
|
||||
}
|
||||
}
|
||||
old_nodes.into_iter().collect()
|
||||
Ok(old_nodes.into_iter().collect())
|
||||
}
|
||||
|
||||
fn node_refs(&self, nodes: &[NodeId]) -> Vec<TypeErasedPinnedRef<'static>> {
|
||||
@@ -120,16 +140,8 @@ impl BorrowTree {
|
||||
self.nodes.remove(&id);
|
||||
}
|
||||
|
||||
pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode) {
|
||||
let ProtoNode { input, construction_args, identifier } = proto_node;
|
||||
|
||||
assert!(
|
||||
!matches!(&input, &ProtoNodeInput::Node(_)),
|
||||
"Only nodes without inputs are supported. Any inputs should already be resolved by placing ComposeNodes {:?}, {:?}, {:?}",
|
||||
identifier,
|
||||
construction_args,
|
||||
input,
|
||||
);
|
||||
pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> {
|
||||
let ProtoNode { construction_args, identifier, .. } = proto_node;
|
||||
|
||||
match construction_args {
|
||||
ConstructionArgs::Value(value) => {
|
||||
@@ -141,7 +153,8 @@ impl BorrowTree {
|
||||
}
|
||||
ConstructionArgs::Nodes(ids) => {
|
||||
let construction_nodes = self.node_refs(&ids);
|
||||
let node = constrcut_node(identifier, construction_nodes);
|
||||
let constructor = typing_context.constructor(id).ok_or(format!("No constructor found for node {:?}", identifier))?;
|
||||
let node = constructor(construction_nodes);
|
||||
let node = NodeContainer {
|
||||
node,
|
||||
_dependencies: self.node_deps(&ids),
|
||||
@@ -149,7 +162,8 @@ impl BorrowTree {
|
||||
let node = unsafe { node.erase_lifetime() };
|
||||
self.store_node(Arc::new(node), id);
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +177,7 @@ mod test {
|
||||
fn push_node() {
|
||||
let mut tree = BorrowTree::default();
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32)));
|
||||
tree.push_node(0, val_1_protonode);
|
||||
tree.push_node(0, val_1_protonode, &TypingContext::default()).unwrap();
|
||||
let _node = tree.get(0).unwrap();
|
||||
assert_eq!(tree.eval(0, ()), Some(2u32));
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ pub mod node_registry;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use dyn_any::IntoDynAny;
|
||||
use graphene_core::*;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/*
|
||||
#[test]
|
||||
@@ -46,7 +47,7 @@ mod tests {
|
||||
#[test]
|
||||
fn execute_add() {
|
||||
use graph_craft::document::*;
|
||||
use graph_craft::proto::*;
|
||||
|
||||
use graph_craft::*;
|
||||
|
||||
fn add_network() -> NodeNetwork {
|
||||
@@ -58,8 +59,8 @@ mod tests {
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode<_, _>", &[concrete!("u32"), concrete!("u32")])),
|
||||
inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(&u32))],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode<_, _>")),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
@@ -68,7 +69,7 @@ mod tests {
|
||||
DocumentNode {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::node(0, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("(u32, u32)")])),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode")),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
@@ -87,9 +88,9 @@ mod tests {
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![
|
||||
NodeInput::Network,
|
||||
NodeInput::Network(concrete!(u32)),
|
||||
NodeInput::Value {
|
||||
tagged_value: value::TaggedValue::U32(1),
|
||||
tagged_value: graph_craft::document::value::TaggedValue::U32(1u32),
|
||||
exposed: false,
|
||||
},
|
||||
],
|
||||
@@ -108,7 +109,7 @@ mod tests {
|
||||
let compiler = Compiler {};
|
||||
let protograph = compiler.compile_single(network, true).expect("Graph should be generated");
|
||||
|
||||
let exec = DynamicExecutor::new(protograph);
|
||||
let exec = DynamicExecutor::new(protograph).unwrap_or_else(|e| panic!("Failed to create executor: {}", e));
|
||||
|
||||
let result = exec.execute(32_u32.into_dyn()).unwrap();
|
||||
let val = *dyn_any::downcast::<u32>(result).unwrap();
|
||||
|
||||
@@ -1,296 +1,369 @@
|
||||
use glam::DAffine2;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus};
|
||||
use graphene_core::ops::{CloneNode, IdNode, TypeNode};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use graphene_core::raster::color::Color;
|
||||
use graphene_core::raster::*;
|
||||
use graphene_core::structural::Then;
|
||||
use graphene_core::value::{ForgetNode, ValueNode};
|
||||
use graphene_core::{Node, NodeIO, NodeIOTypes};
|
||||
|
||||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyNode, IntoTypeErasedNode, TypeErasedPinned, TypeErasedPinnedRef};
|
||||
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyNode, IntoTypeErasedNode};
|
||||
|
||||
use graph_craft::proto::NodeIdentifier;
|
||||
use graph_craft::proto::Type;
|
||||
use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor};
|
||||
|
||||
type NodeConstructor = for<'a> fn(Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'static>;
|
||||
use graph_craft::proto::NodeConstructor;
|
||||
|
||||
use graph_craft::{concrete, generic};
|
||||
use graphene_core::{concrete, generic};
|
||||
use graphene_std::memo::CacheNode;
|
||||
|
||||
use crate::executor::NodeContainer;
|
||||
|
||||
use dyn_any::StaticType;
|
||||
|
||||
macro_rules! register_node {
|
||||
($path:ty, input: $input:ty, params: [$($type:ty),*]) => {
|
||||
( {NodeIdentifier::new(stringify!($path), &[concrete!(stringify!($input)), $(concrete!(stringify!($type))),*])},
|
||||
|args| {
|
||||
let mut args = args.clone();
|
||||
args.reverse();
|
||||
let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("not enough arguments provided to construct node"))),*);
|
||||
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
Box::pin(any) as TypeErasedPinned
|
||||
})
|
||||
(
|
||||
NodeIdentifier::new(stringify!($path)),
|
||||
|args| {
|
||||
let mut args = args.clone();
|
||||
args.reverse();
|
||||
let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("not enough arguments provided to construct node"))),*);
|
||||
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
Box::pin(any)
|
||||
},{
|
||||
let node = IdNode::new().into_type_erased();
|
||||
let node = NodeContainer::new(node, vec![]);
|
||||
let _node = unsafe { node.erase_lifetime().static_ref() };
|
||||
let node = <$path>::new($(graphene_std::any::input_node::<$type>(_node)),*);
|
||||
let params = vec![$(concrete!($type)),*];
|
||||
let mut node_io = <$path as NodeIO<'_, $input>>::to_node_io(&node, params);
|
||||
node_io.input = concrete!(<$input as StaticType>::Static);
|
||||
node_io
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
macro_rules! raster_node {
|
||||
($path:ty, params: [$($type:ty),*]) => {
|
||||
( {NodeIdentifier::new(stringify!($path), &[concrete!("Image"), $(concrete!(stringify!($type))),*])},
|
||||
|args| {
|
||||
let mut args = args.clone();
|
||||
args.reverse();
|
||||
let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"))),*);
|
||||
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node));
|
||||
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node));
|
||||
Box::pin(any) as TypeErasedPinned
|
||||
})
|
||||
(
|
||||
NodeIdentifier::new(stringify!($path)),
|
||||
|args| {
|
||||
let mut args = args.clone();
|
||||
args.reverse();
|
||||
let node = <$path>::new($(
|
||||
graphene_core::value::ClonedNode::new(
|
||||
graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"))
|
||||
.eval(()))
|
||||
),*);
|
||||
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node));
|
||||
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node));
|
||||
Box::pin(any)
|
||||
}, {
|
||||
let params = vec![$(concrete!($type)),*];
|
||||
NodeIOTypes::new(concrete!(Image), concrete!(Image), params)
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
//TODO: turn into hashmap
|
||||
static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
//register_node!(graphene_core::ops::IdNode, input: Any<'_>, params: []),
|
||||
(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()),
|
||||
// TODO: create macro to impl for all types
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [&u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [&u32]),
|
||||
register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [&u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]),
|
||||
(NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>", &[generic!("T"), generic!("U")]), |args| {
|
||||
let node = ComposeTypeErased::new(args[0], args[1]);
|
||||
node.into_type_erased()
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()),
|
||||
// Filters
|
||||
raster_node!(graphene_core::raster::LuminanceNode<_>, params: [LuminanceCalculation]),
|
||||
raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]),
|
||||
raster_node!(graphene_core::raster::HueSaturationNode<_, _, _>, params: [f64, f64, f64]),
|
||||
raster_node!(graphene_core::raster::InvertRGBNode, params: []),
|
||||
raster_node!(graphene_core::raster::ThresholdNode<_, _>, params: [LuminanceCalculation, f64]),
|
||||
raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]),
|
||||
raster_node!(graphene_core::raster::BrightnessContrastNode< _, _>, params: [f64, f64]),
|
||||
raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]),
|
||||
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
|
||||
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
|
||||
(NodeIdentifier::new("graphene_core::structural::MapImageNode", &[]), |args| {
|
||||
let map_fn: DowncastBothNode<Color, Color> = DowncastBothNode::new(args[0]);
|
||||
let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn));
|
||||
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
any.into_type_erased()
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>", &[concrete!("Image"), concrete!("Option<std::sync::Arc<Image>>")]),
|
||||
|args| {
|
||||
let cached = graphene_std::any::input_node::<Option<std::sync::Arc<Image>>>(args[16]);
|
||||
let node = graphene_std::raster::ImaginateNode::new(cached);
|
||||
let any = DynAnyNode::new(ValueNode::new(node));
|
||||
any.into_type_erased()
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_core::raster::BlurNode", &[concrete!("Image")]), |args| {
|
||||
let radius = DowncastBothNode::<(), u32>::new(args[0]);
|
||||
let sigma = DowncastBothNode::<(), f64>::new(args[1]);
|
||||
let image = DowncastBothRefNode::<Image, Image>::new(args[2]);
|
||||
let empty_image: ValueNode<Image> = ValueNode::new(Image::empty());
|
||||
let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new()));
|
||||
|
||||
//let image = &image as &dyn for<'a> Node<'a, (), Output = &'a Image>;
|
||||
// dirty hack: we abuse that the cache node will ignore the input if it is evaluated a second time
|
||||
let image = empty.then(image).then(ImageRefNode::new());
|
||||
|
||||
let window = WindowNode::new(radius, image.clone());
|
||||
let map_gaussian = MapSndNode::new(ValueNode::new(DistanceNode.then(GaussianNode::new(sigma))));
|
||||
let map_distances = MapNode::new(ValueNode::new(map_gaussian));
|
||||
let gaussian_iter = window.then(map_distances);
|
||||
let avg = gaussian_iter.then(WeightedAvgNode::new());
|
||||
let avg: TypeNode<_, u32, Color> = TypeNode::new(avg);
|
||||
let blur_iter = MapNode::new(ValueNode::new(avg));
|
||||
let pixel_iter = image.clone().then(ImageIndexIterNode::new());
|
||||
let blur = pixel_iter.then(blur_iter);
|
||||
let collect = CollectNode {};
|
||||
let vec = blur.then(collect);
|
||||
let new_image = MapImageSliceNode::new(vec);
|
||||
let dimensions = image.then(ImageDimensionsNode::new());
|
||||
let dimensions: TypeNode<_, (), (u32, u32)> = TypeNode::new(dimensions);
|
||||
let new_image = dimensions.then(new_image);
|
||||
let new_image = ForgetNode::new().then(new_image);
|
||||
let node: DynAnyNode<&Image, _, _> = DynAnyNode::new(ValueNode::new(new_image));
|
||||
node.into_type_erased()
|
||||
}),
|
||||
//register_node!(graphene_std::memo::CacheNode<_>, input: Image, params: []),
|
||||
(NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), |_| {
|
||||
let node: CacheNode<Image> = graphene_std::memo::CacheNode::new();
|
||||
let any = graphene_std::any::DynAnyRefNode::new(node);
|
||||
any.into_type_erased()
|
||||
}),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]),
|
||||
register_node!(graphene_std::raster::ImageFrameNode<_>, input: Image, params: [DAffine2]),
|
||||
/*
|
||||
(NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| {
|
||||
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
|
||||
let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::ExportImageNode", &[concrete!("&str")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
|
||||
let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap());
|
||||
let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
|
||||
let node_types: Vec<(NodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![
|
||||
//register_node!(graphene_core::ops::IdNode, input: Any<'_>, params: []),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("Image"), concrete!("&str")]),
|
||||
|proto_node, stack| {
|
||||
let node_id = proto_node.input.unwrap_node() as usize;
|
||||
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
let pre_node = nodes.get(node_id).unwrap();
|
||||
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
|
||||
NodeIdentifier::new("graphene_core::ops::IdNode"),
|
||||
|_| IdNode::new().into_type_erased(),
|
||||
NodeIOTypes::new(generic!(I), generic!(I), vec![]),
|
||||
),
|
||||
// TODO: create macro to impl for all types
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [&u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [u32]),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [&u32]),
|
||||
register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []),
|
||||
register_node!(graphene_core::ops::AddNode, input: (u32, &u32), params: []),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [&u32]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]),
|
||||
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
|
||||
|args| {
|
||||
let node = ComposeTypeErased::new(args[0], args[1]);
|
||||
node.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(generic!(T), generic!(U), vec![generic!(V), generic!(U)]),
|
||||
),
|
||||
// Filters
|
||||
raster_node!(graphene_core::raster::LuminanceNode<_>, params: [LuminanceCalculation]),
|
||||
raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]),
|
||||
raster_node!(graphene_core::raster::HueSaturationNode<_, _, _>, params: [f64, f64, f64]),
|
||||
raster_node!(graphene_core::raster::InvertRGBNode, params: []),
|
||||
raster_node!(graphene_core::raster::ThresholdNode<_, _>, params: [LuminanceCalculation, f64]),
|
||||
raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]),
|
||||
raster_node!(graphene_core::raster::BrightnessContrastNode< _, _>, params: [f64, f64]),
|
||||
raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]),
|
||||
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
|
||||
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::MapImageNode"),
|
||||
|args| {
|
||||
let map_fn: DowncastBothNode<Color, Color> = DowncastBothNode::new(args[0]);
|
||||
let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn));
|
||||
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![]),
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>"),
|
||||
|args| {
|
||||
let cached = graphene_std::any::input_node::<Option<std::sync::Arc<Image>>>(args[16]);
|
||||
let node = graphene_std::raster::ImaginateNode::new(cached);
|
||||
let any = DynAnyNode::new(ValueNode::new(node));
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(
|
||||
concrete!(Image),
|
||||
concrete!(Image),
|
||||
vec![
|
||||
concrete!(DAffine2),
|
||||
concrete!(f64),
|
||||
concrete!(Option<DVec2>),
|
||||
concrete!(f64),
|
||||
concrete!(ImaginateSamplingMethod),
|
||||
concrete!(f64),
|
||||
concrete!(String),
|
||||
concrete!(String),
|
||||
concrete!(bool),
|
||||
concrete!(f64),
|
||||
concrete!(Option<Vec<u64>>),
|
||||
concrete!(bool),
|
||||
concrete!(f64),
|
||||
concrete!(ImaginateMaskStartingFill),
|
||||
concrete!(bool),
|
||||
concrete!(bool),
|
||||
concrete!(Option<std::sync::Arc<Image>>),
|
||||
concrete!(f64),
|
||||
concrete!(ImaginateStatus),
|
||||
],
|
||||
),
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::raster::BlurNode"),
|
||||
|args| {
|
||||
let radius = DowncastBothNode::<(), u32>::new(args[0]);
|
||||
let sigma = DowncastBothNode::<(), f64>::new(args[1]);
|
||||
let image = DowncastBothRefNode::<Image, Image>::new(args[2]);
|
||||
let empty_image: ValueNode<Image> = ValueNode::new(Image::empty());
|
||||
let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new()));
|
||||
|
||||
let cons_node = ConsNode::new(DowncastNode::<_, &str>::new(cons_node_arg));
|
||||
let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node);
|
||||
let node = (pre_node).then(node);
|
||||
//let image = &image as &dyn for<'a> Node<'a, (), Output = &'a Image>;
|
||||
// dirty hack: we abuse that the cache node will ignore the input if it is evaluated a second time
|
||||
let image = empty.then(image).then(ImageRefNode::new());
|
||||
|
||||
let window = WindowNode::new(radius, image.clone());
|
||||
let map_gaussian = MapSndNode::new(ValueNode::new(DistanceNode.then(GaussianNode::new(sigma))));
|
||||
let map_distances = MapNode::new(ValueNode::new(map_gaussian));
|
||||
let gaussian_iter = window.then(map_distances);
|
||||
let avg = gaussian_iter.then(WeightedAvgNode::new());
|
||||
let avg: TypeNode<_, u32, Color> = TypeNode::new(avg);
|
||||
let blur_iter = MapNode::new(ValueNode::new(avg));
|
||||
let pixel_iter = image.clone().then(ImageIndexIterNode::new());
|
||||
let blur = pixel_iter.then(blur_iter);
|
||||
let collect = CollectNode {};
|
||||
let vec = blur.then(collect);
|
||||
let new_image = MapImageSliceNode::new(vec);
|
||||
let dimensions = image.then(ImageDimensionsNode::new());
|
||||
let dimensions: TypeNode<_, (), (u32, u32)> = TypeNode::new(dimensions);
|
||||
let new_image = dimensions.then(new_image);
|
||||
let new_image = ForgetNode::new().then(new_image);
|
||||
let node: DynAnyNode<&Image, _, _> = DynAnyNode::new(ValueNode::new(new_image));
|
||||
node.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![concrete!(u32), concrete!(f64)]),
|
||||
),
|
||||
//register_node!(graphene_std::memo::CacheNode<_>, input: Image, params: []),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|
||||
|_| {
|
||||
let node: CacheNode<Image> = graphene_std::memo::CacheNode::new();
|
||||
let any = graphene_std::any::DynAnyRefNode::new(node);
|
||||
any.into_type_erased()
|
||||
},
|
||||
NodeIOTypes::new(concrete!(Image), concrete!(&Image), vec![]),
|
||||
),
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]),
|
||||
register_node!(graphene_std::raster::ImageFrameNode<_>, input: Image, params: [DAffine2]),
|
||||
/*
|
||||
(NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| {
|
||||
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
|
||||
let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::raster::ExportImageNode", &[concrete!("&str")]), |proto_node, stack| {
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
|
||||
|
||||
let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap());
|
||||
let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("Image"), concrete!("&str")]),
|
||||
|proto_node, stack| {
|
||||
let node_id = proto_node.input.unwrap_node() as usize;
|
||||
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
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::<_, &str>::new(cons_node_arg));
|
||||
let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node);
|
||||
let node = (pre_node).then(node);
|
||||
node.into_type_erased()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitSquareGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BlitSubpath without subpath input") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), Subpath> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::BlitSubpath::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitSquareGenerator).into_type_erased())
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BlitSubpath without subpath input") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), Subpath> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::BlitSubpath::new(input_node));
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("TransformSubpathNode without subpath input") };
|
||||
let translate_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let rotate_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let scale_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
|
||||
let shear_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[3] as usize).unwrap());
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("TransformSubpathNode without subpath input") };
|
||||
let translate_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
|
||||
let rotate_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
|
||||
let scale_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
|
||||
let shear_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[3] as usize).unwrap());
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::TransformSubpathNode::new(translate_node, rotate_node, scale_node, shear_node));
|
||||
|
||||
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::TransformSubpathNode::new(translate_node, rotate_node, scale_node, shear_node));
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::executor::MapGpuNode", &[concrete!("&TypeErasedNode"), concrete!("Color"), concrete!("Color")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
info!("Map image Depending upon id {:?}", operation_node_id);
|
||||
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), &graph_craft::document::NodeNetwork> = DowncastBothNode::new(operation_node);
|
||||
let map_node: graphene_std::executor::MapGpuNode<_, Vec<u32>, u32, u32> = graphene_std::executor::MapGpuNode::new(input_node);
|
||||
let map_node = DynAnyNode::new(map_node);
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::executor::MapGpuNode", &[concrete!("&TypeErasedNode"), concrete!("Color"), concrete!("Color")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
info!("Map image Depending upon id {:?}", operation_node_id);
|
||||
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), &graph_craft::document::NodeNetwork> = DowncastBothNode::new(operation_node);
|
||||
let map_node: graphene_std::executor::MapGpuNode<_, Vec<u32>, u32, u32> = graphene_std::executor::MapGpuNode::new(input_node);
|
||||
let map_node = DynAnyNode::new(map_node);
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(map_node).into_type_erased()
|
||||
} else {
|
||||
map_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
info!("Map image Depending upon id {:?}", operation_node_id);
|
||||
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), String> = DowncastBothNode::new(operation_node);
|
||||
let map_node = graphene_std::executor::MapGpuSingleImageNode(input_node);
|
||||
let map_node = DynAnyNode::new(map_node);
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(map_node).into_type_erased()
|
||||
} else {
|
||||
map_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
#[cfg(feature = "gpu")]
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
info!("Map image Depending upon id {:?}", operation_node_id);
|
||||
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), String> = DowncastBothNode::new(operation_node);
|
||||
let map_node = graphene_std::executor::MapGpuSingleImageNode(input_node);
|
||||
let map_node = DynAnyNode::new(map_node);
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(map_node).into_type_erased()
|
||||
} else {
|
||||
map_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
#[cfg(feature = "quantization")]
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
info!("Quantization Depending upon id {:?}", operation_node_id);
|
||||
let samples_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||
let index_node = nodes.get(operation_node_id[1] as usize).unwrap();
|
||||
let samples_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(samples_node);
|
||||
let index_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(index_node);
|
||||
let map_node = graphene_std::quantization::GenerateQuantizationNode::new(samples_node, index_node);
|
||||
let map_node = DynAnyNode::new(map_node);
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(map_node).into_type_erased()
|
||||
} else {
|
||||
map_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
#[cfg(feature = "quantization")]
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]),
|
||||
|proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
info!("Quantization Depending upon id {:?}", operation_node_id);
|
||||
let samples_node = nodes.get(operation_node_id[0] as usize).unwrap();
|
||||
let index_node = nodes.get(operation_node_id[1] as usize).unwrap();
|
||||
let samples_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(samples_node);
|
||||
let index_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(index_node);
|
||||
let map_node = graphene_std::quantization::GenerateQuantizationNode::new(samples_node, index_node);
|
||||
let map_node = DynAnyNode::new(map_node);
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(map_node).into_type_erased()
|
||||
} else {
|
||||
map_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
<<<<<<< HEAD
|
||||
*/
|
||||
];
|
||||
|
||||
pub fn constrcut_node<'a>(ident: NodeIdentifier, construction_args: Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'a> {
|
||||
if let Some((_id, f)) = NODE_REGISTRY.iter().find(|(id, _)| *id == ident) {
|
||||
f(construction_args)
|
||||
} else {
|
||||
let other_types = NODE_REGISTRY.iter().map(|(id, _)| id).filter(|id| id.name.as_ref() == ident.name.as_ref()).collect::<Vec<_>>();
|
||||
panic!("NodeImplementation: {:?} not found in Registry. Types for which the node is implemented:\n {:#?}", ident, other_types);
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(map_node).into_type_erased()
|
||||
} else {
|
||||
map_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
),
|
||||
<<<<<<< HEAD
|
||||
*/
|
||||
];
|
||||
let mut map: HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
|
||||
for (id, c, types) in node_types {
|
||||
map.entry(id).or_default().insert(types.clone(), c);
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
pub static NODE_REGISTRY: Lazy<HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>> = Lazy::new(|| node_registry());
|
||||
|
||||
/*
|
||||
#[cfg(test)]
|
||||
mod protograph_testing {
|
||||
|
||||
Reference in New Issue
Block a user