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 crate::messages::layout::utility_types::layout_widget::LayoutGroup;
use graph_craft::concrete; use graph_craft::concrete;
use graph_craft::document::value::*;
use graph_craft::document::*; use graph_craft::document::*;
use graph_craft::document::{value::*, DocumentNodeImplementation};
use graph_craft::imaginate_input::ImaginateSamplingMethod; use graph_craft::imaginate_input::ImaginateSamplingMethod;
use graph_craft::proto::{NodeIdentifier, Type}; use graph_craft::proto::{NodeIdentifier, Type};
use graphene_core::raster::Image; use graphene_core::raster::Image;
@ -40,21 +40,85 @@ pub struct NodePropertiesContext<'a> {
pub nested_path: &'a [NodeId], 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 struct DocumentNodeType {
pub name: &'static str, pub name: &'static str,
pub category: &'static str, pub category: &'static str,
pub identifier: NodeIdentifier, pub identifier: NodeImplementation,
pub inputs: &'static [DocumentInputType], pub inputs: &'static [DocumentInputType],
pub outputs: &'static [FrontendGraphDataType], pub outputs: &'static [FrontendGraphDataType],
pub properties: fn(&DocumentNode, NodeId, &mut NodePropertiesContext) -> Vec<LayoutGroup>, 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 // TODO: Dynamic node library
static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ static STATIC_NODES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Identity", name: "Identity",
category: "General", category: "General",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType { inputs: &[DocumentInputType {
name: "In", name: "In",
data_type: FrontendGraphDataType::General, data_type: FrontendGraphDataType::General,
@ -66,7 +130,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Image", name: "Image",
category: "Ignore", 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)], inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), false)],
outputs: &[FrontendGraphDataType::Raster], outputs: &[FrontendGraphDataType::Raster],
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"), 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 { DocumentNodeType {
name: "Input", name: "Input",
category: "Ignore", category: "Ignore",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType { inputs: &[DocumentInputType {
name: "In", name: "In",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
@ -86,7 +150,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Output", name: "Output",
category: "Ignore", category: "Ignore",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType { inputs: &[DocumentInputType {
name: "In", name: "In",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
@ -98,7 +162,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Grayscale", name: "Grayscale",
category: "Image Adjustments", 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)], inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)],
outputs: &[FrontendGraphDataType::Raster], outputs: &[FrontendGraphDataType::Raster],
properties: node_properties::no_properties, properties: node_properties::no_properties,
@ -107,7 +171,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "GpuImage", name: "GpuImage",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType { DocumentInputType {
@ -123,7 +187,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "QuantizeImage", name: "QuantizeImage",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType { DocumentInputType {
name: "Image", name: "Image",
@ -144,22 +208,10 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
outputs: &[FrontendGraphDataType::Raster], outputs: &[FrontendGraphDataType::Raster],
properties: node_properties::quantize_properties, 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 { DocumentNodeType {
name: "Cache", name: "Cache",
category: "Structural", 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)], inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)],
outputs: &[FrontendGraphDataType::Raster], outputs: &[FrontendGraphDataType::Raster],
properties: node_properties::no_properties, properties: node_properties::no_properties,
@ -167,7 +219,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Invert RGB", name: "Invert RGB",
category: "Image Adjustments", 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)], inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)],
outputs: &[FrontendGraphDataType::Raster], outputs: &[FrontendGraphDataType::Raster],
properties: node_properties::no_properties, properties: node_properties::no_properties,
@ -175,7 +227,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Hue/Saturation", name: "Hue/Saturation",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Hue Shift", TaggedValue::F64(0.), false), DocumentInputType::new("Hue Shift", TaggedValue::F64(0.), false),
@ -188,7 +240,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Brightness/Contrast", name: "Brightness/Contrast",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Brightness", TaggedValue::F64(0.), false), DocumentInputType::new("Brightness", TaggedValue::F64(0.), false),
@ -200,7 +252,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Gamma", name: "Gamma",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Gamma", TaggedValue::F64(1.), false), DocumentInputType::new("Gamma", TaggedValue::F64(1.), false),
@ -211,7 +263,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Opacity", name: "Opacity",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Factor", TaggedValue::F64(1.), false), DocumentInputType::new("Factor", TaggedValue::F64(1.), false),
@ -222,7 +274,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Posterize", name: "Posterize",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Value", TaggedValue::F64(5.), false), DocumentInputType::new("Value", TaggedValue::F64(5.), false),
@ -233,7 +285,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Exposure", name: "Exposure",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeIdentifier::new("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Value", TaggedValue::F64(0.), false), DocumentInputType::new("Value", TaggedValue::F64(0.), false),
@ -245,7 +297,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Add", name: "Add",
category: "Math", category: "Math",
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Input", TaggedValue::F64(0.), true), DocumentInputType::new("Input", TaggedValue::F64(0.), true),
DocumentInputType::new("Addend", TaggedValue::F64(0.), true), DocumentInputType::new("Addend", TaggedValue::F64(0.), true),
@ -256,7 +308,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
/*DocumentNodeType { /*DocumentNodeType {
name: "Unit Circle Generator", name: "Unit Circle Generator",
category: "Vector", category: "Vector",
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]),
inputs: &[DocumentInputType::none()], inputs: &[DocumentInputType::none()],
outputs: &[FrontendGraphDataType::Subpath], outputs: &[FrontendGraphDataType::Subpath],
properties: node_properties::no_properties, properties: node_properties::no_properties,
@ -264,7 +316,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Unit Square Generator", name: "Unit Square Generator",
category: "Vector", category: "Vector",
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]),
inputs: &[DocumentInputType::none()], inputs: &[DocumentInputType::none()],
outputs: &[FrontendGraphDataType::Subpath], outputs: &[FrontendGraphDataType::Subpath],
properties: node_properties::no_properties, properties: node_properties::no_properties,
@ -272,7 +324,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Path Generator", name: "Path Generator",
category: "Vector", category: "Vector",
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]), identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
inputs: &[DocumentInputType { inputs: &[DocumentInputType {
name: "Path Data", name: "Path Data",
data_type: FrontendGraphDataType::Subpath, data_type: FrontendGraphDataType::Subpath,
@ -284,7 +336,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Transform Subpath", name: "Transform Subpath",
category: "Vector", category: "Vector",
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]),
inputs: &[ inputs: &[
DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true), DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true),
DocumentInputType::new("Translation", TaggedValue::DVec2(DVec2::ZERO), false), DocumentInputType::new("Translation", TaggedValue::DVec2(DVec2::ZERO), false),
@ -298,7 +350,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
DocumentNodeType { DocumentNodeType {
name: "Blit Subpath", name: "Blit Subpath",
category: "Vector", category: "Vector",
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::BlitSubpath", &[]),
inputs: &[ inputs: &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::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 { pub const IMAGINATE_NODE: DocumentNodeType = DocumentNodeType {
name: "Imaginate", name: "Imaginate",
category: "Image Synthesis", category: "Image Synthesis",
identifier: NodeIdentifier::new("graphene_std::raster::ImaginateNode", &[concrete!("&TypeErasedNode")]), identifier: NodeImplementation::proto("graphene_std::raster::ImaginateNode", &[concrete!("&TypeErasedNode")]),
inputs: &[ inputs: &[
DocumentInputType::new("Input Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::new("Input Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Seed", TaggedValue::F64(0.), false), DocumentInputType::new("Seed", TaggedValue::F64(0.), false),
@ -353,24 +405,30 @@ pub fn collect_node_types() -> Vec<FrontendNodeType> {
impl DocumentNodeType { impl DocumentNodeType {
/// Generate a [`DocumentNodeImplementation`] from this node type, using a nested network. /// Generate a [`DocumentNodeImplementation`] from this node type, using a nested network.
pub fn generate_implementation(&self) -> DocumentNodeImplementation { pub fn generate_implementation(&self) -> DocumentNodeImplementation {
let number_of_inputs = self.inputs.len(); let num_inputs = self.inputs.len();
let network = NodeNetwork {
inputs: (0..number_of_inputs).map(|_| 0).collect(), let inner_network = match &self.identifier {
output: 0, NodeImplementation::ProtoNode(ident) => {
nodes: [( NodeNetwork {
0, inputs: (0..num_inputs).map(|_| 0).collect(),
DocumentNode { output: 0,
name: format!("{}_impl", self.name), nodes: [(
// TODO: Allow inserting nodes that contain other nodes. 0,
implementation: DocumentNodeImplementation::Unresolved(self.identifier.clone()), DocumentNode {
inputs: (0..number_of_inputs).map(|_| NodeInput::Network).collect(), name: format!("{}_impl", self.name),
metadata: DocumentNodeMetadata::default(), // TODO: Allow inserting nodes that contain other nodes.
}, implementation: DocumentNodeImplementation::Unresolved(ident.clone()),
)] inputs: (0..num_inputs).map(|_| NodeInput::Network).collect(),
.into_iter() metadata: DocumentNodeMetadata::default(),
.collect(), },
..Default::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::frontend::utility_types::MouseCursorIcon;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion}; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
use crate::messages::layout::utility_types::layout_widget::PropertyHolder; 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::prelude::*;
use crate::messages::tool::common_functionality::resize::Resize; use crate::messages::tool::common_functionality::resize::Resize;
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType}; use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};