Integrate the node graph as a Node Graph Frame layer type (#812)

* Add node graph frame tool

* Add a brighten

* Use the node graph

* Fix topological_sort

* Update UI

* Add icons for the tool and layer type

* Avoid serde & use bitmaps to improve performance

* Allow serialising a node graph

* Fix missing ..Default::default()

* Fix incorrect comments

* Cache node graph output image

* Suppress no-cycle import warning

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2022-11-05 21:38:14 +00:00
committed by GitHub
parent 596b9f4531
commit 9cdebfb1f0
33 changed files with 1018 additions and 258 deletions

View File

@@ -1,11 +1,9 @@
use std::marker::PhantomData;
use borrow_stack::FixedSizeStack;
use graphene_core::generic::FnNode;
use graphene_core::ops::{AddNode, IdNode};
use graphene_core::ops::AddNode;
use graphene_core::raster::color::Color;
use graphene_core::structural::{ConsNode, Then};
use graphene_core::{AsRefNode, Node};
use graphene_core::Node;
use graphene_std::any::DowncastBothNode;
use graphene_std::any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode};
use graphene_std::raster::Image;
@@ -13,17 +11,12 @@ use graphene_std::raster::Image;
use crate::proto::Type;
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeInput, Type::Concrete};
use dyn_any::Upcast;
type NodeConstructor = fn(ProtoNode, &FixedSizeStack<TypeErasedNode<'static>>);
//TODO: turn into hasmap
static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
(
NodeIdentifier {
name: "graphene_core::ops::IdNode",
types: &[Concrete("Any<'_>")],
},
NodeIdentifier::new("graphene_core::ops::IdNode", &[Concrete(std::borrow::Cow::Borrowed("Any<'_>"))]),
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
@@ -32,24 +25,18 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
})
},
),
(NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Generic]), |proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
let node = pre_node.then(graphene_core::ops::IdNode);
node.into_type_erased()
})
}),
(
NodeIdentifier {
name: "graphene_core::ops::IdNode",
types: &[Type::Generic],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
let node = pre_node.then(graphene_core::ops::IdNode);
node.into_type_erased()
})
},
),
(
NodeIdentifier {
name: "graphene_core::ops::AddNode",
types: &[Concrete("u32"), Concrete("u32")],
},
NodeIdentifier::new(
"graphene_core::ops::AddNode",
&[Concrete(std::borrow::Cow::Borrowed("u32")), Concrete(std::borrow::Cow::Borrowed("u32"))],
),
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
@@ -61,10 +48,10 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_core::ops::AddNode",
types: &[Concrete("&u32"), Concrete("&u32")],
},
NodeIdentifier::new(
"graphene_core::ops::AddNode",
&[Concrete(std::borrow::Cow::Borrowed("&u32")), Concrete(std::borrow::Cow::Borrowed("&u32"))],
),
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
@@ -76,10 +63,10 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_core::ops::AddNode",
types: &[Concrete("&u32"), Concrete("u32")],
},
NodeIdentifier::new(
"graphene_core::ops::AddNode",
&[Concrete(std::borrow::Cow::Borrowed("&u32")), Concrete(std::borrow::Cow::Borrowed("u32"))],
),
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
@@ -91,10 +78,10 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_core::structural::ConsNode",
types: &[Concrete("&u32"), Concrete("u32")],
},
NodeIdentifier::new(
"graphene_core::structural::ConsNode",
&[Concrete(std::borrow::Cow::Borrowed("&u32")), Concrete(std::borrow::Cow::Borrowed("u32"))],
),
|proto_node, stack| {
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
stack.push_fn(move |nodes| {
@@ -118,10 +105,10 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_core::structural::ConsNode",
types: &[Concrete("u32"), Concrete("u32")],
},
NodeIdentifier::new(
"graphene_core::structural::ConsNode",
&[Concrete(std::borrow::Cow::Borrowed("u32")), Concrete(std::borrow::Cow::Borrowed("u32"))],
),
|proto_node, stack| {
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
stack.push_fn(move |nodes| {
@@ -146,10 +133,10 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
),
// TODO: create macro to impl for all types
(
NodeIdentifier {
name: "graphene_core::structural::ConsNode",
types: &[Concrete("&u32"), Concrete("&u32")],
},
NodeIdentifier::new(
"graphene_core::structural::ConsNode",
&[Concrete(std::borrow::Cow::Borrowed("&u32")), Concrete(std::borrow::Cow::Borrowed("&u32"))],
),
|proto_node, stack| {
let node_id = proto_node.input.unwrap_node() as usize;
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
@@ -168,10 +155,7 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_core::any::DowncastNode",
types: &[Concrete("&u32")],
},
NodeIdentifier::new("graphene_core::any::DowncastNode", &[Concrete(std::borrow::Cow::Borrowed("&u32"))]),
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
@@ -181,10 +165,7 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_core::value::ValueNode",
types: &[Concrete("Any<'_>")],
},
NodeIdentifier::new("graphene_core::value::ValueNode", &[Concrete(std::borrow::Cow::Borrowed("Any<'_>"))]),
|proto_node, stack| {
stack.push_fn(|_nodes| {
if let ConstructionArgs::Value(value) = proto_node.construction_args {
@@ -197,69 +178,48 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
})
},
),
(
NodeIdentifier {
name: "graphene_core::value::ValueNode",
types: &[Type::Generic],
},
|proto_node, stack| {
stack.push_fn(|_nodes| {
if let ConstructionArgs::Value(value) = proto_node.construction_args {
let node = FnNode::new(move |_| value.clone().up_box() as Any<'static>);
node.into_type_erased()
} else {
unreachable!()
}
})
},
),
(
NodeIdentifier {
name: "graphene_core::raster::GrayscaleNode",
types: &[],
},
|proto_node, stack| {
stack.push_fn(|nodes| {
let node = DynAnyNode::new(graphene_core::raster::GrayscaleNode);
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
let pre_node = nodes.get(pre_id as usize).unwrap();
(pre_node).then(node).into_type_erased()
} else {
node.into_type_erased()
}
})
},
),
(
NodeIdentifier {
name: "graphene_std::raster::MapImageNode",
types: &[],
},
|proto_node, stack| {
let node_id = proto_node.input.unwrap_node() as usize;
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
stack.push_fn(move |nodes| {
let pre_node = nodes.get(node_id).unwrap();
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
let operation_node: DowncastBothNode<_, Color, Color> = DowncastBothNode::new(operation_node);
let map_node = DynAnyNode::new(graphene_std::raster::MapImageNode::new(operation_node));
let node = (pre_node).then(map_node);
node.into_type_erased()
})
(NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic]), |proto_node, stack| {
stack.push_fn(|_nodes| {
if let ConstructionArgs::Value(value) = proto_node.construction_args {
let node = FnNode::new(move |_| value.clone().up_box() as Any<'static>);
node.into_type_erased()
} else {
unimplemented!()
unreachable!()
}
},
),
})
}),
(NodeIdentifier::new("graphene_core::raster::GrayscaleNode", &[]), |proto_node, stack| {
stack.push_fn(|nodes| {
let node = DynAnyNode::new(graphene_core::raster::GrayscaleNode);
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
let pre_node = nodes.get(pre_id as usize).unwrap();
(pre_node).then(node).into_type_erased()
} else {
node.into_type_erased()
}
})
}),
(NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]), |proto_node, stack| {
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
stack.push_fn(move |nodes| {
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
let operation_node: DowncastBothNode<_, Color, Color> = DowncastBothNode::new(operation_node);
let map_node = DynAnyNode::new(graphene_std::raster::MapImageNode::new(operation_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!()
}
}),
(
NodeIdentifier {
name: "graphene_std::raster::ImageNode",
types: &[Concrete("&str")],
},
NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete(std::borrow::Cow::Borrowed("&str"))]),
|_proto_node, stack| {
stack.push_fn(|_nodes| {
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
@@ -269,10 +229,7 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_std::raster::ExportImageNode",
types: &[Concrete("&str")],
},
NodeIdentifier::new("graphene_std::raster::ExportImageNode", &[Concrete(std::borrow::Cow::Borrowed("&str"))]),
|proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
@@ -285,10 +242,10 @@ static NODE_REGISTRY: &[(NodeIdentifier<'static>, NodeConstructor)] = &[
},
),
(
NodeIdentifier {
name: "graphene_core::structural::ConsNode",
types: &[Concrete("Image"), Concrete("&str")],
},
NodeIdentifier::new(
"graphene_core::structural::ConsNode",
&[Concrete(std::borrow::Cow::Borrowed("Image")), Concrete(std::borrow::Cow::Borrowed("&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 {
@@ -322,11 +279,6 @@ mod protograph_testing {
use super::*;
/// Lookup a node by th suffix of the name (for testing only)
fn simple_lookup(suffix: &str) -> &(NodeIdentifier, fn(ProtoNode, &FixedSizeStack<TypeErasedNode<'static>>)) {
NODE_REGISTRY.iter().find(|node| node.0.name.ends_with(suffix)).unwrap()
}
#[test]
fn add_values() {
let stack = FixedSizeStack::new(256);
@@ -339,14 +291,20 @@ mod protograph_testing {
let cons_protonode = ProtoNode {
construction_args: ConstructionArgs::Nodes(vec![1]),
input: ProtoNodeInput::Node(0),
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[Concrete("u32"), Concrete("u32")]),
identifier: NodeIdentifier::new(
"graphene_core::structural::ConsNode",
&[Concrete(std::borrow::Cow::Borrowed("u32")), Concrete(std::borrow::Cow::Borrowed("u32"))],
),
};
push_node(cons_protonode, &stack);
let add_protonode = ProtoNode {
construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::Node(2),
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Concrete("u32"), Concrete("u32")]),
identifier: NodeIdentifier::new(
"graphene_core::ops::AddNode",
&[Concrete(std::borrow::Cow::Borrowed("u32")), Concrete(std::borrow::Cow::Borrowed("u32"))],
),
};
push_node(add_protonode, &stack);
@@ -379,7 +337,7 @@ mod protograph_testing {
let image_protonode = ProtoNode {
construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::None,
identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete("&str")]),
identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete(std::borrow::Cow::Borrowed("&str"))]),
};
push_node(image_protonode, &stack);
@@ -394,7 +352,7 @@ mod protograph_testing {
let image_protonode = ProtoNode {
construction_args: ConstructionArgs::Nodes(vec![]),
input: ProtoNodeInput::None,
identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete("&str")]),
identifier: NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete(std::borrow::Cow::Borrowed("&str"))]),
};
push_node(image_protonode, &stack);