diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index c0a62305..11d3fe5f 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -2,8 +2,8 @@ use super::{node_properties, FrontendGraphDataType, FrontendNodeType}; use crate::messages::layout::utility_types::layout_widget::LayoutGroup; use graph_craft::concrete; -use graph_craft::document::value::*; use graph_craft::document::*; +use graph_craft::document::{value::*, DocumentNodeImplementation}; use graph_craft::imaginate_input::ImaginateSamplingMethod; use graph_craft::proto::{NodeIdentifier, Type}; use graphene_core::raster::Image; @@ -40,21 +40,85 @@ pub struct NodePropertiesContext<'a> { pub nested_path: &'a [NodeId], } +#[derive(Clone)] +pub enum NodeImplementation { + ProtoNode(NodeIdentifier), + DocumentNode(NodeNetwork), +} + +impl NodeImplementation { + pub const fn proto(name: &'static str, types: &'static [Type]) -> Self { + Self::ProtoNode(NodeIdentifier::new(name, types)) + } +} + +#[derive(Clone)] pub struct DocumentNodeType { pub name: &'static str, pub category: &'static str, - pub identifier: NodeIdentifier, + pub identifier: NodeImplementation, pub inputs: &'static [DocumentInputType], pub outputs: &'static [FrontendGraphDataType], pub properties: fn(&DocumentNode, NodeId, &mut NodePropertiesContext) -> Vec, } +fn document_node_types() -> Vec { + let mut vec: Vec<_> = STATIC_NODES.to_vec(); + + const INPUTS: &[DocumentInputType] = &[ + DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), + DocumentInputType::new("Radius", TaggedValue::U32(3), false), + DocumentInputType::new("Sigma", TaggedValue::F64(1.), false), + ]; + + let blur = DocumentNodeType { + name: "Gaussian Blur", + category: "Image Filters", + identifier: NodeImplementation::DocumentNode(NodeNetwork { + inputs: vec![0, 1, 1], + output: 1, + nodes: vec![ + ( + 0, + DocumentNode { + name: "CacheNode".to_string(), + inputs: vec![NodeInput::Network], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")])), + metadata: Default::default(), + }, + ), + ( + 1, + DocumentNode { + name: "BlurNode".to_string(), + inputs: vec![NodeInput::Node(0), NodeInput::Network, NodeInput::Network], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::raster::BlurNode", &[])), + metadata: Default::default(), + }, + ), + ] + .into_iter() + .collect(), + ..Default::default() + }), + inputs: INPUTS, + outputs: &[FrontendGraphDataType::Raster], + properties: node_properties::blur_image_properties, + }; + vec.push(blur); + vec +} + +// We use the once cell for lazy initialization to avoid the overhead of reconstructing the node list every time. +// TODO: make document nodes not require a `'static` lifetime to avoid having to split the construction into const and non-const parts. +static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(document_node_types); + // TODO: Dynamic node library -static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ +static STATIC_NODES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Identity", category: "General", - identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), + identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), inputs: &[DocumentInputType { name: "In", data_type: FrontendGraphDataType::General, @@ -66,7 +130,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Image", category: "Ignore", - identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), + identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), false)], outputs: &[FrontendGraphDataType::Raster], properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"), @@ -74,7 +138,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Input", category: "Ignore", - identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), + identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), inputs: &[DocumentInputType { name: "In", data_type: FrontendGraphDataType::Raster, @@ -86,7 +150,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Output", category: "Ignore", - identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), + identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), inputs: &[DocumentInputType { name: "In", data_type: FrontendGraphDataType::Raster, @@ -98,7 +162,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Grayscale", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::GrayscaleNode", &[]), + identifier: NodeImplementation::proto("graphene_std::raster::GrayscaleNode", &[]), inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::no_properties, @@ -107,7 +171,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "GpuImage", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType { @@ -123,7 +187,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "QuantizeImage", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType { name: "Image", @@ -144,22 +208,10 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ outputs: &[FrontendGraphDataType::Raster], properties: node_properties::quantize_properties, }, - DocumentNodeType { - name: "Gaussian Blur", - category: "Image Filters", - identifier: NodeIdentifier::new("graphene_core::raster::BlurNode", &[]), - inputs: &[ - DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), - DocumentInputType::new("Radius", TaggedValue::U32(3), false), - DocumentInputType::new("Sigma", TaggedValue::F64(1.), false), - ], - outputs: &[FrontendGraphDataType::Raster], - properties: node_properties::blur_image_properties, - }, DocumentNodeType { name: "Cache", category: "Structural", - identifier: NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), + identifier: NodeImplementation::proto("graphene_std::memo::CacheNode", &[concrete!("Image")]), inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::no_properties, @@ -167,7 +219,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Invert RGB", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::InvertRGBNode", &[]), + identifier: NodeImplementation::proto("graphene_std::raster::InvertRGBNode", &[]), inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::no_properties, @@ -175,7 +227,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Hue/Saturation", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Hue Shift", TaggedValue::F64(0.), false), @@ -188,7 +240,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Brightness/Contrast", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Brightness", TaggedValue::F64(0.), false), @@ -200,7 +252,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Gamma", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Gamma", TaggedValue::F64(1.), false), @@ -211,7 +263,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Opacity", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Factor", TaggedValue::F64(1.), false), @@ -222,7 +274,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Posterize", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Value", TaggedValue::F64(5.), false), @@ -233,7 +285,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Exposure", category: "Image Adjustments", - identifier: NodeIdentifier::new("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Value", TaggedValue::F64(0.), false), @@ -245,7 +297,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Add", category: "Math", - identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Input", TaggedValue::F64(0.), true), DocumentInputType::new("Addend", TaggedValue::F64(0.), true), @@ -256,7 +308,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ /*DocumentNodeType { name: "Unit Circle Generator", category: "Vector", - identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), + identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), inputs: &[DocumentInputType::none()], outputs: &[FrontendGraphDataType::Subpath], properties: node_properties::no_properties, @@ -264,7 +316,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Unit Square Generator", category: "Vector", - identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), + identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), inputs: &[DocumentInputType::none()], outputs: &[FrontendGraphDataType::Subpath], properties: node_properties::no_properties, @@ -272,7 +324,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Path Generator", category: "Vector", - identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), + identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), inputs: &[DocumentInputType { name: "Path Data", data_type: FrontendGraphDataType::Subpath, @@ -284,7 +336,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Transform Subpath", category: "Vector", - identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), + identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), inputs: &[ DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true), DocumentInputType::new("Translation", TaggedValue::DVec2(DVec2::ZERO), false), @@ -298,7 +350,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentNodeType { name: "Blit Subpath", category: "Vector", - identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), + identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::BlitSubpath", &[]), inputs: &[ DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true), @@ -311,7 +363,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ pub const IMAGINATE_NODE: DocumentNodeType = DocumentNodeType { name: "Imaginate", category: "Image Synthesis", - identifier: NodeIdentifier::new("graphene_std::raster::ImaginateNode", &[concrete!("&TypeErasedNode")]), + identifier: NodeImplementation::proto("graphene_std::raster::ImaginateNode", &[concrete!("&TypeErasedNode")]), inputs: &[ DocumentInputType::new("Input Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Seed", TaggedValue::F64(0.), false), @@ -353,24 +405,30 @@ pub fn collect_node_types() -> Vec { impl DocumentNodeType { /// Generate a [`DocumentNodeImplementation`] from this node type, using a nested network. pub fn generate_implementation(&self) -> DocumentNodeImplementation { - let number_of_inputs = self.inputs.len(); - let network = NodeNetwork { - inputs: (0..number_of_inputs).map(|_| 0).collect(), - output: 0, - nodes: [( - 0, - DocumentNode { - name: format!("{}_impl", self.name), - // TODO: Allow inserting nodes that contain other nodes. - implementation: DocumentNodeImplementation::Unresolved(self.identifier.clone()), - inputs: (0..number_of_inputs).map(|_| NodeInput::Network).collect(), - metadata: DocumentNodeMetadata::default(), - }, - )] - .into_iter() - .collect(), - ..Default::default() + let num_inputs = self.inputs.len(); + + let inner_network = match &self.identifier { + NodeImplementation::ProtoNode(ident) => { + NodeNetwork { + inputs: (0..num_inputs).map(|_| 0).collect(), + output: 0, + nodes: [( + 0, + DocumentNode { + name: format!("{}_impl", self.name), + // TODO: Allow inserting nodes that contain other nodes. + implementation: DocumentNodeImplementation::Unresolved(ident.clone()), + inputs: (0..num_inputs).map(|_| NodeInput::Network).collect(), + metadata: DocumentNodeMetadata::default(), + }, + )] + .into_iter() + .collect(), + ..Default::default() + } + } + NodeImplementation::DocumentNode(network) => network.clone(), }; - DocumentNodeImplementation::Network(network) + DocumentNodeImplementation::Network(inner_network) } } diff --git a/editor/src/messages/tool/tool_messages/imaginate_tool.rs b/editor/src/messages/tool/tool_messages/imaginate_tool.rs index 0b12dfc1..aa67ea93 100644 --- a/editor/src/messages/tool/tool_messages/imaginate_tool.rs +++ b/editor/src/messages/tool/tool_messages/imaginate_tool.rs @@ -2,7 +2,7 @@ use crate::consts::DRAG_THRESHOLD; use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion}; use crate::messages::layout::utility_types::layout_widget::PropertyHolder; -use crate::messages::portfolio::document::node_graph::IMAGINATE_NODE; +use crate::messages::portfolio::document::node_graph::{NodeImplementation, IMAGINATE_NODE}; use crate::messages::prelude::*; use crate::messages::tool::common_functionality::resize::Resize; use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};