Implement blur as a document node (#987)

* Implement blur as a document node

* Reuse node node registry

* Add comment explaining the use of once cell

* Fix rebase error
This commit is contained in:
Dennis Kobert 2023-01-27 21:47:47 +01:00 committed by Keavon Chambers
parent 898b0bb582
commit 0531df18d5
2 changed files with 114 additions and 56 deletions

View File

@ -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<LayoutGroup>,
}
fn document_node_types() -> Vec<DocumentNodeType> {
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<Vec<DocumentNodeType>> = 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<FrontendNodeType> {
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)
}
}

View File

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