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:
Dennis Kobert
2023-02-15 23:31:30 +01:00
committed by Keavon Chambers
parent a64c856ec4
commit 5dab7de68d
25 changed files with 1365 additions and 1008 deletions

View File

@@ -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"

View File

@@ -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));
}

View File

@@ -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();

View File

@@ -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 {