Replace node definition string-based lookups with DefinitionIdentifier instances (#3451)

* create definition identifier and integrate it

* Bug fixes and code review

* formatting

* Fix migrations

* Fix remove handles migration

* formatting

* Fix test

* Fix tests 2

* fix deserialization

* Code review

* Small fixes

* Consolidate 'Morph' node migrations

* Add old SamplePointsNode name to migrations list

* Fix tests

* Unrelated small fix

* Fix migration crashes

* Fix tests

* Final code review

* fmt

* Add metadata

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Adam Gerhant
2026-01-12 23:09:43 -08:00
committed by GitHub
co-authored by Keavon Chambers
parent 4fea2b0fe7
commit a6052c5819
63 changed files with 843 additions and 802 deletions
@@ -2,6 +2,7 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform; use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
use crate::messages::input_mapper::utility_types::misc::ActionShortcut; use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::node_graph::utility_types::{ use crate::messages::portfolio::document::node_graph::utility_types::{
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphErrorDiagnostic, Transform, BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphErrorDiagnostic, Transform,
}; };
@@ -60,7 +61,7 @@ pub enum FrontendMessage {
// Send prefix: Send global, static data to the frontend that is never updated // Send prefix: Send global, static data to the frontend that is never updated
SendUIMetadata { SendUIMetadata {
#[serde(rename = "nodeDescriptions")] #[serde(rename = "nodeDescriptions")]
node_descriptions: Vec<(String, String)>, node_descriptions: Vec<(DefinitionIdentifier, String)>,
#[serde(rename = "nodeTypes")] #[serde(rename = "nodeTypes")]
node_types: Vec<FrontendNodeType>, node_types: Vec<FrontendNodeType>,
}, },
@@ -287,11 +287,7 @@ impl LayoutMessageHandler {
responses.add(callback_message); responses.add(callback_message);
} }
WidgetValueAction::Update => { WidgetValueAction::Update => {
let Some(value) = value.as_str().map(|s| s.to_string()) else { let callback_message = (node_type_input.on_update.callback)(&value.into());
error!("NodeCatalog update was not of type String");
return;
};
let callback_message = (node_type_input.on_update.callback)(&value);
responses.add(callback_message); responses.add(callback_message);
} }
}, },
@@ -790,23 +790,19 @@ impl DiffUpdate {
}; };
// Recursively fill menu list entries with their realized shortcut keys specific to the current bindings and platform // Recursively fill menu list entries with their realized shortcut keys specific to the current bindings and platform
let apply_action_shortcut_to_menu_lists = |entry_sections: &mut MenuListEntrySections| { fn apply_action_shortcut_to_menu_lists(entry_sections: &mut MenuListEntrySections, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
struct RecursiveWrapper<'a>(&'a dyn Fn(&mut MenuListEntrySections, &RecursiveWrapper)); for entries in entry_sections {
let recursive_wrapper = RecursiveWrapper(&|entry_sections: &mut MenuListEntrySections, recursive_wrapper| { for entry in entries {
for entries in entry_sections { // Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
for entry in entries { if let Some(tooltip_shortcut) = &mut entry.tooltip_shortcut {
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut` tooltip_shortcut.realize_shortcut(action_input_mapping);
if let Some(tooltip_shortcut) = &mut entry.tooltip_shortcut {
tooltip_shortcut.realize_shortcut(action_input_mapping);
}
// Recursively call this inner closure on the menu's children
(recursive_wrapper.0)(&mut entry.children, recursive_wrapper);
} }
// Recursively call this function on the menu's children
apply_action_shortcut_to_menu_lists(&mut entry.children, action_input_mapping);
} }
}); }
(recursive_wrapper.0)(entry_sections, &recursive_wrapper) }
};
// Hash the menu list entry sections for caching purposes // Hash the menu list entry sections for caching purposes
let hash_menu_list_entry_sections = |entry_sections: &MenuListEntrySections| { let hash_menu_list_entry_sections = |entry_sections: &MenuListEntrySections| {
@@ -833,11 +829,11 @@ impl DiffUpdate {
// Apply shortcut conversions to all widgets that have menu lists // Apply shortcut conversions to all widgets that have menu lists
let convert_menu_lists = |widget_instance: &mut WidgetInstance| match &mut *widget_instance.widget { let convert_menu_lists = |widget_instance: &mut WidgetInstance| match &mut *widget_instance.widget {
Widget::DropdownInput(dropdown_input) => { Widget::DropdownInput(dropdown_input) => {
apply_action_shortcut_to_menu_lists(&mut dropdown_input.entries); apply_action_shortcut_to_menu_lists(&mut dropdown_input.entries, action_input_mapping);
dropdown_input.entries_hash = hash_menu_list_entry_sections(&dropdown_input.entries); dropdown_input.entries_hash = hash_menu_list_entry_sections(&dropdown_input.entries);
} }
Widget::TextButton(text_button) => { Widget::TextButton(text_button) => {
apply_action_shortcut_to_menu_lists(&mut text_button.menu_list_children); apply_action_shortcut_to_menu_lists(&mut text_button.menu_list_children, action_input_mapping);
text_button.menu_list_children_hash = hash_menu_list_entry_sections(&text_button.menu_list_children); text_button.menu_list_children_hash = hash_menu_list_entry_sections(&text_button.menu_list_children);
} }
_ => {} _ => {}
@@ -1,5 +1,6 @@
use crate::messages::input_mapper::utility_types::misc::ActionShortcut; use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use derivative::*; use derivative::*;
use graphene_std::Color; use graphene_std::Color;
use graphene_std::raster::curve::Curve; use graphene_std::raster::curve::Curve;
@@ -273,7 +274,7 @@ pub struct NodeCatalog {
// Callbacks // Callbacks
#[serde(skip)] #[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")] #[derivative(Debug = "ignore", PartialEq = "ignore")]
pub on_update: WidgetCallback<String>, pub on_update: WidgetCallback<DefinitionIdentifier>,
#[serde(skip)] #[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")] #[derivative(Debug = "ignore", PartialEq = "ignore")]
pub on_commit: WidgetCallback<()>, pub on_commit: WidgetCallback<()>,
@@ -11,6 +11,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler}; use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler};
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::NodeGraphMessageContext; use crate::messages::portfolio::document::node_graph::NodeGraphMessageContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType; use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options}; use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings, Pivot}; use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings, Pivot};
@@ -1548,7 +1549,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
// Create an artboard and set its dimensions to the bounding box size and location // Create an artboard and set its dimensions to the bounding box size and location
let node_id = NodeId::new(); let node_id = NodeId::new();
let node_layer_id = LayerNodeIdentifier::new_unchecked(node_id); let node_layer_id = LayerNodeIdentifier::new_unchecked(node_id);
let new_artboard_node = document_node_definitions::resolve_document_node_type("Artboard") let new_artboard_node = document_node_definitions::resolve_network_node_type("Artboard")
.expect("Failed to create artboard node") .expect("Failed to create artboard node")
.default_node_template(); .default_node_template();
responses.add(NodeGraphMessage::InsertNode { responses.add(NodeGraphMessage::InsertNode {
@@ -2133,8 +2134,7 @@ impl DocumentMessageHandler {
network_interface.upstream_flow_back_from_nodes(vec![selected_id.to_node()], &[], FlowType::HorizontalFlow).find(|id| { network_interface.upstream_flow_back_from_nodes(vec![selected_id.to_node()], &[], FlowType::HorizontalFlow).find(|id| {
network_interface network_interface
.reference(id, &[]) .reference(id, &[])
.map(|name| name.as_deref().unwrap_or_default() == "Boolean Operation") .is_some_and(|reference| reference == DefinitionIdentifier::Network("Boolean Operation".into()))
.unwrap_or_default()
}) })
}); });
@@ -382,7 +382,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
modify_inputs.insert_vector(subpaths, layer, true, path.fill().is_some(), path.stroke().is_some()); modify_inputs.insert_vector(subpaths, layer, true, path.fill().is_some(), path.stroke().is_some());
if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform", true) { if let Some(transform_node_id) = modify_inputs.existing_network_node_id("Transform", true) {
transform_utils::update_transform(modify_inputs.network_interface, &transform_node_id, transform * usvg_transform(node.abs_transform())); transform_utils::update_transform(modify_inputs.network_interface, &transform_node_id, transform * usvg_transform(node.abs_transform()));
} }
@@ -1,12 +1,12 @@
use super::transform_utils; use super::transform_utils;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type, resolve_network_node_type, resolve_proto_node_type};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{self, InputConnector, NodeNetworkInterface, OutputConnector}; use crate::messages::portfolio::document::utility_types::network_interface::{self, InputConnector, NodeNetworkInterface, OutputConnector};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use glam::{DAffine2, IVec2}; use glam::{DAffine2, IVec2};
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput}; use graph_craft::document::{NodeId, NodeInput};
use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::Artboard; use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke; use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::raster::BlendMode; use graphene_std::raster::BlendMode;
@@ -123,14 +123,14 @@ impl<'a> ModifyInputsContext<'a> {
/// Creates a new layer and adds it to the document network. network_interface.move_layer_to_stack should be called after /// Creates a new layer and adds it to the document network. network_interface.move_layer_to_stack should be called after
pub fn create_layer(&mut self, new_id: NodeId) -> LayerNodeIdentifier { pub fn create_layer(&mut self, new_id: NodeId) -> LayerNodeIdentifier {
let new_merge_node = resolve_document_node_type("Merge").expect("Merge node").default_node_template(); let new_merge_node = resolve_network_node_type("Merge").expect("Merge node").default_node_template();
self.network_interface.insert_node(new_id, new_merge_node, &[]); self.network_interface.insert_node(new_id, new_merge_node, &[]);
LayerNodeIdentifier::new(new_id, self.network_interface) LayerNodeIdentifier::new(new_id, self.network_interface)
} }
/// Creates an artboard as the primary export for the document network /// Creates an artboard as the primary export for the document network
pub fn create_artboard(&mut self, new_id: NodeId, artboard: Artboard) -> LayerNodeIdentifier { pub fn create_artboard(&mut self, new_id: NodeId, artboard: Artboard) -> LayerNodeIdentifier {
let artboard_node_template = resolve_document_node_type("Artboard").expect("Node").node_template_input_override([ let artboard_node_template = resolve_network_node_type("Artboard").expect("Node").node_template_input_override([
Some(NodeInput::value(TaggedValue::Artboard(Default::default()), true)), Some(NodeInput::value(TaggedValue::Artboard(Default::default()), true)),
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)), Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
Some(NodeInput::value(TaggedValue::DVec2(artboard.location.into()), false)), Some(NodeInput::value(TaggedValue::DVec2(artboard.location.into()), false)),
@@ -143,7 +143,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
pub fn insert_boolean_data(&mut self, operation: graphene_std::path_bool::BooleanOperation, layer: LayerNodeIdentifier) { pub fn insert_boolean_data(&mut self, operation: graphene_std::path_bool::BooleanOperation, layer: LayerNodeIdentifier) {
let boolean = resolve_document_node_type("Boolean Operation").expect("Boolean node does not exist").node_template_input_override([ let boolean = resolve_network_node_type("Boolean Operation").expect("Boolean node does not exist").node_template_input_override([
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)), Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)), Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),
]); ]);
@@ -156,7 +156,7 @@ impl<'a> ModifyInputsContext<'a> {
pub fn insert_vector(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier, include_transform: bool, include_fill: bool, include_stroke: bool) { pub fn insert_vector(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier, include_transform: bool, include_fill: bool, include_stroke: bool) {
let vector = Table::new_from_element(Vector::from_subpaths(subpaths, true)); let vector = Table::new_from_element(Vector::from_subpaths(subpaths, true));
let shape = resolve_document_node_type("Path") let shape = resolve_network_node_type("Path")
.expect("Path node does not exist") .expect("Path node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::Vector(vector), false))]); .node_template_input_override([Some(NodeInput::value(TaggedValue::Vector(vector), false))]);
let shape_id = NodeId::new(); let shape_id = NodeId::new();
@@ -164,21 +164,25 @@ impl<'a> ModifyInputsContext<'a> {
self.network_interface.move_node_to_chain_start(&shape_id, layer, &[]); self.network_interface.move_node_to_chain_start(&shape_id, layer, &[]);
if include_transform { if include_transform {
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template(); let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let transform_id = NodeId::new(); let transform_id = NodeId::new();
self.network_interface.insert_node(transform_id, transform, &[]); self.network_interface.insert_node(transform_id, transform, &[]);
self.network_interface.move_node_to_chain_start(&transform_id, layer, &[]); self.network_interface.move_node_to_chain_start(&transform_id, layer, &[]);
} }
if include_fill { if include_fill {
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template(); let fill = resolve_proto_node_type(graphene_std::vector_nodes::fill::IDENTIFIER)
.expect("Fill node does not exist")
.default_node_template();
let fill_id = NodeId::new(); let fill_id = NodeId::new();
self.network_interface.insert_node(fill_id, fill, &[]); self.network_interface.insert_node(fill_id, fill, &[]);
self.network_interface.move_node_to_chain_start(&fill_id, layer, &[]); self.network_interface.move_node_to_chain_start(&fill_id, layer, &[]);
} }
if include_stroke { if include_stroke {
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template(); let stroke = resolve_proto_node_type(graphene_std::vector_nodes::stroke::IDENTIFIER)
.expect("Stroke node does not exist")
.default_node_template();
let stroke_id = NodeId::new(); let stroke_id = NodeId::new();
self.network_interface.insert_node(stroke_id, stroke, &[]); self.network_interface.insert_node(stroke_id, stroke, &[]);
self.network_interface.move_node_to_chain_start(&stroke_id, layer, &[]); self.network_interface.move_node_to_chain_start(&stroke_id, layer, &[]);
@@ -186,21 +190,27 @@ impl<'a> ModifyInputsContext<'a> {
} }
pub fn insert_text(&mut self, text: String, font: Font, typesetting: TypesettingConfig, layer: LayerNodeIdentifier) { pub fn insert_text(&mut self, text: String, font: Font, typesetting: TypesettingConfig, layer: LayerNodeIdentifier) {
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template(); let stroke = resolve_proto_node_type(graphene_std::vector_nodes::stroke::IDENTIFIER)
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template(); .expect("Stroke node does not exist")
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template(); .default_node_template();
let text = resolve_document_node_type("Text").expect("Text node does not exist").node_template_input_override([ let fill = resolve_proto_node_type(graphene_std::vector_nodes::fill::IDENTIFIER)
Some(NodeInput::scope("editor-api")), .expect("Fill node does not exist")
Some(NodeInput::value(TaggedValue::String(text), false)), .default_node_template();
Some(NodeInput::value(TaggedValue::Font(font), false)), let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
Some(NodeInput::value(TaggedValue::F64(typesetting.font_size), false)), let text = resolve_proto_node_type(graphene_std::text::text::IDENTIFIER)
Some(NodeInput::value(TaggedValue::F64(typesetting.line_height_ratio), false)), .expect("Text node does not exist")
Some(NodeInput::value(TaggedValue::F64(typesetting.character_spacing), false)), .node_template_input_override([
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_width), false)), Some(NodeInput::scope("editor-api")),
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_height), false)), Some(NodeInput::value(TaggedValue::String(text), false)),
Some(NodeInput::value(TaggedValue::F64(typesetting.tilt), false)), Some(NodeInput::value(TaggedValue::Font(font), false)),
Some(NodeInput::value(TaggedValue::TextAlign(typesetting.align), false)), Some(NodeInput::value(TaggedValue::F64(typesetting.font_size), false)),
]); Some(NodeInput::value(TaggedValue::F64(typesetting.line_height_ratio), false)),
Some(NodeInput::value(TaggedValue::F64(typesetting.character_spacing), false)),
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_width), false)),
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_height), false)),
Some(NodeInput::value(TaggedValue::F64(typesetting.tilt), false)),
Some(NodeInput::value(TaggedValue::TextAlign(typesetting.align), false)),
]);
let text_id = NodeId::new(); let text_id = NodeId::new();
self.network_interface.insert_node(text_id, text, &[]); self.network_interface.insert_node(text_id, text, &[]);
@@ -220,8 +230,8 @@ impl<'a> ModifyInputsContext<'a> {
} }
pub fn insert_image_data(&mut self, image_frame: Table<Raster<CPU>>, layer: LayerNodeIdentifier) { pub fn insert_image_data(&mut self, image_frame: Table<Raster<CPU>>, layer: LayerNodeIdentifier) {
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template(); let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let image = resolve_document_node_type("Image Value") let image = resolve_proto_node_type(graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER)
.expect("ImageValue node does not exist") .expect("ImageValue node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::Raster(image_frame), false))]); .node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::Raster(image_frame), false))]);
@@ -245,18 +255,26 @@ impl<'a> ModifyInputsContext<'a> {
}) })
} }
/// Gets the node id of a node with a specific reference that is upstream from the layer node, and optionally creates it if it does not exist. /// Gets the node id of a network node with a specific reference that is upstream from the layer node, and optionally creates it if it does not exist.
/// The returned node is based on the selection dots in the layer. The right most dot will always insert/access the path that flows directly into the layer. pub fn existing_network_node_id(&mut self, reference: &str, create_if_nonexistent: bool) -> Option<NodeId> {
/// Each dot after that represents an existing path node. If there is an existing upstream node, then it will always be returned first. self.existing_node_id(&DefinitionIdentifier::Network(reference.into()), create_if_nonexistent)
pub fn existing_node_id(&mut self, reference_name: &'static str, create_if_nonexistent: bool) -> Option<NodeId> { }
/// Gets the node id of a proto node with a specific reference that is upstream from the layer node, and optionally creates it if it does not exist.
pub fn existing_proto_node_id(&mut self, reference: ProtoNodeIdentifier, create_if_nonexistent: bool) -> Option<NodeId> {
self.existing_node_id(&DefinitionIdentifier::ProtoNode(reference), create_if_nonexistent)
}
/// Gets the node id of a document node with a specific reference that is upstream from the layer node, and optionally creates it if it does not exist.
fn existing_node_id(&mut self, reference: &DefinitionIdentifier, create_if_nonexistent: bool) -> Option<NodeId> {
// Start from the layer node or export // Start from the layer node or export
let output_layer = self.get_output_layer()?; let output_layer = self.get_output_layer()?;
let existing_node_id = Self::locate_node_in_layer_chain(reference_name, output_layer, self.network_interface); let existing_node_id = Self::locate_node_in_layer_chain(reference, output_layer, self.network_interface);
// Create a new node if the node does not exist and update its inputs // Create a new node if the node does not exist and update its inputs
if create_if_nonexistent { if create_if_nonexistent {
return existing_node_id.or_else(|| self.create_node(reference_name)); return existing_node_id.or_else(|| self.create_node(reference));
} }
existing_node_id existing_node_id
@@ -265,16 +283,13 @@ impl<'a> ModifyInputsContext<'a> {
/// Gets the node id of a node with a specific reference (name) that is upstream (leftward) from the layer node, but before reaching another upstream layer stack. /// Gets the node id of a node with a specific reference (name) that is upstream (leftward) from the layer node, but before reaching another upstream layer stack.
/// For example, if given a parent layer, this would find a requested "Transform" or "Boolean Operation" node in its chain, between the parent layer and its layer stack child contents. /// For example, if given a parent layer, this would find a requested "Transform" or "Boolean Operation" node in its chain, between the parent layer and its layer stack child contents.
/// It would also travel up an entire layer that's not fed by a stack until reaching the generator node, such as a "Rectangle" or "Path" layer. /// It would also travel up an entire layer that's not fed by a stack until reaching the generator node, such as a "Rectangle" or "Path" layer.
pub fn locate_node_in_layer_chain(reference_name: &str, left_of_layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn locate_node_in_layer_chain(reference: &DefinitionIdentifier, left_of_layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
let upstream = network_interface.upstream_flow_back_from_nodes(vec![left_of_layer.to_node()], &[], network_interface::FlowType::HorizontalFlow); let upstream = network_interface.upstream_flow_back_from_nodes(vec![left_of_layer.to_node()], &[], network_interface::FlowType::HorizontalFlow);
// Look at all of the upstream nodes // Look at all of the upstream nodes
for upstream_node in upstream { for upstream_node in upstream {
// Check if this is the node we have been searching for. // Check if this is the node we have been searching for.
if network_interface if network_interface.reference(&upstream_node, &[]).is_some_and(|node_reference| node_reference == *reference) {
.reference(&upstream_node, &[])
.is_some_and(|node_reference| *node_reference == Some(reference_name.to_string()))
{
if !network_interface.is_visible(&upstream_node, &[]) { if !network_interface.is_visible(&upstream_node, &[]) {
continue; continue;
} }
@@ -293,10 +308,10 @@ impl<'a> ModifyInputsContext<'a> {
} }
/// Create a new node inside the layer /// Create a new node inside the layer
pub fn create_node(&mut self, reference: &str) -> Option<NodeId> { pub fn create_node(&mut self, reference: &DefinitionIdentifier) -> Option<NodeId> {
let output_layer = self.get_output_layer()?; let output_layer = self.get_output_layer()?;
let Some(node_definition) = resolve_document_node_type(reference) else { let Some(node_definition) = resolve_document_node_type(reference) else {
log::error!("Node type {reference} does not exist in ModifyInputsContext::existing_node_id"); log::error!("Node {reference:?} does not exist in ModifyInputsContext::existing_node_id");
return None; return None;
}; };
@@ -305,7 +320,7 @@ impl<'a> ModifyInputsContext<'a> {
if node_definition.identifier == "Path" { if node_definition.identifier == "Path" {
let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]); let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]);
if layer_input_type.compiled_nested_type() == Some(&concrete!(Table<Graphic>)) { if layer_input_type.compiled_nested_type() == Some(&concrete!(Table<Graphic>)) {
let Some(flatten_path_definition) = resolve_document_node_type("Flatten Path") else { let Some(flatten_path_definition) = resolve_proto_node_type(graphene_std::vector_nodes::flatten_path::IDENTIFIER) else {
log::error!("Flatten Path does not exist in ModifyInputsContext::existing_node_id"); log::error!("Flatten Path does not exist in ModifyInputsContext::existing_node_id");
return None; return None;
}; };
@@ -325,7 +340,9 @@ impl<'a> ModifyInputsContext<'a> {
let backup_color_index = 2; let backup_color_index = 2;
let backup_gradient_index = 3; let backup_gradient_index = 3;
let Some(fill_node_id) = self.existing_node_id("Fill", true) else { return }; let Some(fill_node_id) = self.existing_proto_node_id(graphene_std::vector_nodes::fill::IDENTIFIER, true) else {
return;
};
match &fill { match &fill {
Fill::None => { Fill::None => {
let input_connector = InputConnector::node(fill_node_id, backup_color_index); let input_connector = InputConnector::node(fill_node_id, backup_color_index);
@@ -345,32 +362,42 @@ impl<'a> ModifyInputsContext<'a> {
} }
pub fn blend_mode_set(&mut self, blend_mode: BlendMode) { pub fn blend_mode_set(&mut self, blend_mode: BlendMode) {
let Some(blend_node_id) = self.existing_node_id("Blending", true) else { return }; let Some(blend_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::blending::IDENTIFIER, true) else {
return;
};
let input_connector = InputConnector::node(blend_node_id, 1); let input_connector = InputConnector::node(blend_node_id, 1);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::BlendMode(blend_mode), false), false); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::BlendMode(blend_mode), false), false);
} }
pub fn opacity_set(&mut self, opacity: f64) { pub fn opacity_set(&mut self, opacity: f64) {
let Some(blend_node_id) = self.existing_node_id("Blending", true) else { return }; let Some(blend_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::blending::IDENTIFIER, true) else {
return;
};
let input_connector = InputConnector::node(blend_node_id, 2); let input_connector = InputConnector::node(blend_node_id, 2);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(opacity * 100.), false), false); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(opacity * 100.), false), false);
} }
pub fn blending_fill_set(&mut self, fill: f64) { pub fn blending_fill_set(&mut self, fill: f64) {
let Some(blend_node_id) = self.existing_node_id("Blending", true) else { return }; let Some(blend_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::blending::IDENTIFIER, true) else {
return;
};
let input_connector = InputConnector::node(blend_node_id, 3); let input_connector = InputConnector::node(blend_node_id, 3);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(fill * 100.), false), false); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(fill * 100.), false), false);
} }
pub fn clip_mode_toggle(&mut self, clip_mode: Option<bool>) { pub fn clip_mode_toggle(&mut self, clip_mode: Option<bool>) {
let clip = !clip_mode.unwrap_or(false); let clip = !clip_mode.unwrap_or(false);
let Some(clip_node_id) = self.existing_node_id("Blending", true) else { return }; let Some(clip_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::blending::IDENTIFIER, true) else {
return;
};
let input_connector = InputConnector::node(clip_node_id, 4); let input_connector = InputConnector::node(clip_node_id, 4);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Bool(clip), false), false); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Bool(clip), false), false);
} }
pub fn stroke_set(&mut self, stroke: Stroke) { pub fn stroke_set(&mut self, stroke: Stroke) {
let Some(stroke_node_id) = self.existing_node_id("Stroke", true) else { return }; let Some(stroke_node_id) = self.existing_proto_node_id(graphene_std::vector::stroke::IDENTIFIER, true) else {
return;
};
let stroke_color = if let Some(color) = stroke.color { Table::new_from_element(color) } else { Table::new() }; let stroke_color = if let Some(color) = stroke.color { Table::new_from_element(color) } else { Table::new() };
@@ -399,7 +426,7 @@ impl<'a> ModifyInputsContext<'a> {
pub fn transform_change_with_parent(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, skip_rerender: bool) { pub fn transform_change_with_parent(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, skip_rerender: bool) {
// Get the existing upstream Transform node and its transform, if present, otherwise use the identity transform // Get the existing upstream Transform node and its transform, if present, otherwise use the identity transform
let (layer_transform, transform_node_id) = self let (layer_transform, transform_node_id) = self
.existing_node_id("Transform", false) .existing_network_node_id("Transform", false)
.and_then(|transform_node_id| { .and_then(|transform_node_id| {
let document_node = self.network_interface.document_network().nodes.get(&transform_node_id)?; let document_node = self.network_interface.document_network().nodes.get(&transform_node_id)?;
Some((transform_utils::get_current_transform(&document_node.inputs), transform_node_id)) Some((transform_utils::get_current_transform(&document_node.inputs), transform_node_id))
@@ -423,7 +450,7 @@ impl<'a> ModifyInputsContext<'a> {
/// A new Transform node is created if one does not exist, unless it would be given the identity transform. /// A new Transform node is created if one does not exist, unless it would be given the identity transform.
pub fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, skip_rerender: bool) { pub fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, skip_rerender: bool) {
// Get the existing upstream Transform node, if present // Get the existing upstream Transform node, if present
let transform_node_id = self.existing_node_id("Transform", false); let transform_node_id = self.existing_network_node_id("Transform", false);
// Get a transform appropriate for the requested space // Get a transform appropriate for the requested space
let to_transform = match transform_in { let to_transform = match transform_in {
@@ -448,7 +475,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
// Create the Transform node // Create the Transform node
self.existing_node_id("Transform", true) self.existing_network_node_id("Transform", true)
}) else { }) else {
return; return;
}; };
@@ -464,19 +491,23 @@ impl<'a> ModifyInputsContext<'a> {
} }
pub fn vector_modify(&mut self, modification_type: VectorModificationType) { pub fn vector_modify(&mut self, modification_type: VectorModificationType) {
let Some(path_node_id) = self.existing_node_id("Path", true) else { return }; let Some(path_node_id) = self.existing_network_node_id("Path", true) else {
return;
};
self.network_interface.vector_modify(&path_node_id, modification_type); self.network_interface.vector_modify(&path_node_id, modification_type);
self.responses.add(PropertiesPanelMessage::Refresh); self.responses.add(PropertiesPanelMessage::Refresh);
self.responses.add(NodeGraphMessage::RunDocumentGraph); self.responses.add(NodeGraphMessage::RunDocumentGraph);
} }
pub fn brush_modify(&mut self, strokes: Vec<BrushStroke>) { pub fn brush_modify(&mut self, strokes: Vec<BrushStroke>) {
let Some(brush_node_id) = self.existing_node_id("Brush", true) else { return }; let Some(brush_node_id) = self.existing_network_node_id("Brush", true) else {
return;
};
self.set_input_with_refresh(InputConnector::node(brush_node_id, 1), NodeInput::value(TaggedValue::BrushStrokes(strokes), false), false); self.set_input_with_refresh(InputConnector::node(brush_node_id, 1), NodeInput::value(TaggedValue::BrushStrokes(strokes), false), false);
} }
pub fn resize_artboard(&mut self, location: IVec2, dimensions: IVec2) { pub fn resize_artboard(&mut self, location: IVec2, dimensions: IVec2) {
let Some(artboard_node_id) = self.existing_node_id("Artboard", true) else { let Some(artboard_node_id) = self.existing_network_node_id("Artboard", true) else {
return; return;
}; };
@@ -26,7 +26,8 @@ use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::transform::Footprint; use graphene_std::transform::Footprint;
use graphene_std::vector::Vector; use graphene_std::vector::Vector;
use graphene_std::*; use graphene_std::*;
use std::collections::{HashMap, HashSet, VecDeque}; use serde_json::Value;
use std::collections::{HashMap, VecDeque};
pub struct NodePropertiesContext<'a> { pub struct NodePropertiesContext<'a> {
pub persistent_data: &'a PersistentData, pub persistent_data: &'a PersistentData,
@@ -54,10 +55,63 @@ impl NodePropertiesContext<'_> {
} }
} }
/// The key used to access definitions for a network node or proto node.
/// For proto nodes, this is their [`ProtoNodeIdentifier`].
/// For network nodes, it doesn't necessarily have to be the same as the network's display name, but it often is.
#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
#[serde(tag = "type", content = "data")]
pub enum DefinitionIdentifier {
ProtoNode(ProtoNodeIdentifier),
Network(String),
}
impl DefinitionIdentifier {
pub fn implementation_name_from_identifier(&self) -> String {
match self {
DefinitionIdentifier::Network(name) => name.clone(),
DefinitionIdentifier::ProtoNode(proto_node_identifier) => registry::NODE_METADATA
.lock()
.unwrap()
.get(proto_node_identifier)
.map(|metadata| metadata.display_name.to_string())
.unwrap_or_else(|| {
let mut last_segment = proto_node_identifier.as_str().split("::").last().unwrap_or_default().to_string();
last_segment = last_segment.strip_suffix("Node").unwrap_or(&last_segment).to_string();
last_segment
}),
}
}
}
impl From<Value> for DefinitionIdentifier {
fn from(value: Value) -> Self {
match value {
Value::Object(mut map) => {
let ty = map.remove("type").unwrap().as_str().unwrap().to_owned();
match ty.as_ref() {
"Network" => {
let data = map.remove("data").unwrap().as_str().unwrap().to_owned();
DefinitionIdentifier::Network(data)
}
"ProtoNode" => {
let value = map.remove("data").unwrap();
let proto: ProtoNodeIdentifier = serde_json::from_value(value).unwrap();
DefinitionIdentifier::ProtoNode(proto)
}
_ => panic!("Unknown `DefinitionIdentifier` type: {:?}", ty),
}
}
_ => panic!("Expected a JSON object to convert to `DefinitionIdentifier`"),
}
}
}
/// Acts as a description for a [DocumentNode] before it gets instantiated as one. /// Acts as a description for a [DocumentNode] before it gets instantiated as one.
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct DocumentNodeDefinition { pub struct DocumentNodeDefinition {
/// Used by the reference field in [`DocumentNodeMetadata`] to prevent storing a copy of the implementation, if it is unchanged from the definition. /// Used to create the [`DefinitionIdentifier::Network`] identifier.
pub identifier: &'static str, pub identifier: &'static str,
/// All data required to construct a [`DocumentNode`] and [`DocumentNodeMetadata`] /// All data required to construct a [`DocumentNode`] and [`DocumentNodeMetadata`]
@@ -75,14 +129,14 @@ pub struct DocumentNodeDefinition {
pub properties: Option<&'static str>, pub properties: Option<&'static str>,
} }
// We use the once cell for lazy initialization to avoid the overhead of reconstructing the node list every time. // We use the once_cell to use the document node definitions throughout the editor without passing a reference
// TODO: make document nodes not require a `'static` lifetime to avoid having to split the construction into const and non-const parts. // TODO: If dynamic node library is required, use a Mutex as well
static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy<Vec<DocumentNodeDefinition>> = once_cell::sync::Lazy::new(static_nodes); static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy<HashMap<DefinitionIdentifier, DocumentNodeDefinition>> = once_cell::sync::Lazy::new(document_node_definitions);
// TODO: Dynamic node library
/// Defines the "signature" or "header file"-like metadata for the document nodes, but not the implementation (which is defined in the node registry). /// Defines the "signature" or "header file"-like metadata for the document nodes, but not the implementation (which is defined in the node registry).
/// The [`DocumentNode`] is the instance while these [`DocumentNodeDefinition`]s are the "classes" or "blueprints" from which the instances are built. /// The [`DocumentNode`] is the instance while these [`DocumentNodeDefinition`]s are the "classes" or "blueprints" from which the instances are built.
fn static_nodes() -> Vec<DocumentNodeDefinition> { /// Only the position can be set for protonodes within a definition. The rest of the metadata comes from the node macro in NODE_METADATA
fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefinition> {
let custom = vec![ let custom = vec![
// TODO: Auto-generate this from its proto node macro // TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition { DocumentNodeDefinition {
@@ -100,7 +154,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default() ..Default::default()
}, },
}, },
description: Cow::Borrowed("Returns the input value without changing it. This is useful for rerouting wires for organization purposes."), description: Cow::Borrowed("Passes-through the input value without changing it. This is useful for rerouting wires for organization purposes."),
properties: None, properties: None,
}, },
// TODO: Auto-generate this from its proto node macro // TODO: Auto-generate this from its proto node macro
@@ -140,6 +194,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("An empty node network you can use to create your own custom nodes."), description: Cow::Borrowed("An empty node network you can use to create your own custom nodes."),
properties: None, properties: None,
}, },
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition { DocumentNodeDefinition {
identifier: "Cache", identifier: "Cache",
category: "General", category: "General",
@@ -224,7 +279,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "To Graphic".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)),
..Default::default() ..Default::default()
}, },
@@ -232,7 +286,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Wrap Graphic".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -1)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -1)),
..Default::default() ..Default::default()
}, },
@@ -240,7 +293,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Source Node ID".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -1)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -1)),
..Default::default() ..Default::default()
}, },
@@ -248,7 +300,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-7, -1)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-7, -1)),
..Default::default() ..Default::default()
}, },
@@ -256,7 +307,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extend".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -3)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -3)),
..Default::default() ..Default::default()
}, },
@@ -375,7 +425,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Create Artboard".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)),
..Default::default() ..Default::default()
}, },
@@ -383,7 +432,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Source Node ID".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -3)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -3)),
..Default::default() ..Default::default()
}, },
@@ -391,7 +439,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-7, -3)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-7, -3)),
..Default::default() ..Default::default()
}, },
@@ -399,7 +446,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extend".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -4)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -4)),
..Default::default() ..Default::default()
}, },
@@ -989,7 +1035,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Load Resource".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -997,7 +1042,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Decode Image".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default() ..Default::default()
}, },
@@ -1071,7 +1115,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Create Surface".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)),
..Default::default() ..Default::default()
}, },
@@ -1079,7 +1122,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Cache".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 2)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 2)),
..Default::default() ..Default::default()
}, },
@@ -1087,7 +1129,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Rasterize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
..Default::default() ..Default::default()
}, },
@@ -1108,12 +1149,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("TODO"), description: Cow::Borrowed("TODO"),
properties: None, properties: None,
}, },
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition { DocumentNodeDefinition {
identifier: "Noise Pattern", identifier: "Noise Pattern",
category: "Raster: Pattern", category: "Raster: Pattern",
node_template: NodeTemplate { node_template: NodeTemplate {
document_node: DocumentNode { document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::std_nodes::noise_pattern::IDENTIFIER), implementation: DocumentNodeImplementation::ProtoNode(graphene_std::raster_nodes::std_nodes::noise_pattern::IDENTIFIER),
inputs: vec![ inputs: vec![
NodeInput::value(TaggedValue::None, false), NodeInput::value(TaggedValue::None, false),
NodeInput::value(TaggedValue::Bool(true), false), NodeInput::value(TaggedValue::Bool(true), false),
@@ -1228,7 +1270,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -1236,7 +1277,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)),
..Default::default() ..Default::default()
}, },
@@ -1244,7 +1284,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 4)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 4)),
..Default::default() ..Default::default()
}, },
@@ -1252,7 +1291,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 6)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 6)),
..Default::default() ..Default::default()
}, },
@@ -1312,7 +1350,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract XY".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -1320,7 +1357,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract XY".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)),
..Default::default() ..Default::default()
}, },
@@ -1382,7 +1418,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
persistent_metadata: NodeNetworkPersistentMetadata { persistent_metadata: NodeNetworkPersistentMetadata {
node_metadata: [DocumentNodeMetadata { node_metadata: [DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Brush".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -1402,6 +1437,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("TODO"), description: Cow::Borrowed("TODO"),
properties: None, properties: None,
}, },
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition { DocumentNodeDefinition {
identifier: "Memoize", identifier: "Memoize",
category: "Debug", category: "Debug",
@@ -1463,7 +1499,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Executor".to_string(), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-7, 0)),
..Default::default()
},
..Default::default()
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -1471,20 +1513,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Upload Texture".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default() ..Default::default()
}, },
..Default::default() ..Default::default()
}, },
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Cache".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
..Default::default()
},
..Default::default()
},
] ]
.into_iter() .into_iter()
.enumerate() .enumerate()
@@ -1592,7 +1625,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -1600,7 +1632,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Path Modify".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default() ..Default::default()
}, },
@@ -1621,6 +1652,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("TODO"), description: Cow::Borrowed("TODO"),
properties: None, properties: None,
}, },
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition { DocumentNodeDefinition {
identifier: "Text", identifier: "Text",
category: "Text", category: "Text",
@@ -1788,7 +1820,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -1796,15 +1827,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Transform".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
input_metadata: vec![
("Value", "TODO").into(),
("Translation", "TODO").into(),
("Rotation", "TODO").into(),
("Scale", "TODO").into(),
("Skew", "TODO").into(),
],
..Default::default() ..Default::default()
}, },
..Default::default() ..Default::default()
@@ -1891,7 +1914,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Boolean Operation".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -1899,7 +1921,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Memoize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default() ..Default::default()
}, },
@@ -1981,7 +2002,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Subpath Segment Lengths".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 7)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 7)),
..Default::default() ..Default::default()
}, },
@@ -1989,7 +2009,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Sample Polyline".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default() ..Default::default()
}, },
@@ -1997,7 +2016,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Memoize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
..Default::default() ..Default::default()
}, },
@@ -2104,7 +2122,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [ node_metadata: [
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Poisson-Disk Points".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default() ..Default::default()
}, },
@@ -2112,7 +2129,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}, },
DocumentNodeMetadata { DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata { persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Memoize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default() ..Default::default()
}, },
@@ -2637,102 +2653,48 @@ fn static_input_properties() -> InputProperties {
map map
} }
pub fn resolve_document_node_type(identifier: &str) -> Option<&DocumentNodeDefinition> { pub fn resolve_network_node_type(identifier: &str) -> Option<&'static DocumentNodeDefinition> {
DOCUMENT_NODE_TYPES.iter().find(|definition| definition.identifier == identifier) resolve_document_node_type(&DefinitionIdentifier::Network(identifier.into()))
}
pub fn resolve_proto_node_type(identifier: ProtoNodeIdentifier) -> Option<&'static DocumentNodeDefinition> {
resolve_document_node_type(&DefinitionIdentifier::ProtoNode(identifier))
}
pub fn resolve_document_node_type(identifier: &DefinitionIdentifier) -> Option<&'static DocumentNodeDefinition> {
DOCUMENT_NODE_TYPES.get(identifier)
} }
pub fn collect_node_types() -> Vec<FrontendNodeType> { pub fn collect_node_types() -> Vec<FrontendNodeType> {
// Create a mapping from registry ID to document node identifier DOCUMENT_NODE_TYPES
let id_to_identifier_map: HashMap<ProtoNodeIdentifier, &'static str> = DOCUMENT_NODE_TYPES
.iter() .iter()
.filter_map(|definition| { .filter(|(_, definition)| !definition.category.is_empty())
if let DocumentNodeImplementation::ProtoNode(name) = &definition.node_template.document_node.implementation { .map(|(identifier, definition)| {
Some((name.clone(), definition.identifier))
} else {
None
}
})
.collect();
let mut extracted_node_types = Vec::new();
let node_registry = registry::NODE_REGISTRY.lock().unwrap();
let node_metadata = registry::NODE_METADATA.lock().unwrap();
for (id, metadata) in node_metadata.iter() {
if let Some(implementations) = node_registry.get(id) {
let identifier = match id_to_identifier_map.get(id) {
Some(&id) => id,
None => continue,
};
// Extract category from metadata (already creates an owned String)
let category = metadata.category.unwrap_or_default();
// Extract input types (already creates owned Strings)
let input_types = implementations
.iter()
.flat_map(|(_, node_io)| node_io.inputs.iter().map(|ty| ty.nested_type().to_cow_string()))
.collect::<HashSet<Cow<'static, str>>>()
.into_iter()
.collect::<Vec<Cow<'static, str>>>();
// Create a FrontendNodeType
let node_type = FrontendNodeType::with_input_types(identifier, category, input_types);
// Store the created node_type
extracted_node_types.push(node_type);
}
}
let node_types: Vec<FrontendNodeType> = DOCUMENT_NODE_TYPES
.iter()
.filter(|definition| !definition.category.is_empty())
.map(|definition| {
let input_types = definition let input_types = definition
.node_template .node_template
.document_node .document_node
.inputs .inputs
.iter() .iter()
.filter_map(|node_input| node_input.as_value().map(|node_value| node_value.ty().nested_type().to_cow_string())) .map(|node_input| node_input.as_value().map(|node_value| node_value.ty().nested_type().to_string()).unwrap_or_default())
.collect::<Vec<Cow<'static, str>>>(); .collect::<Vec<String>>();
let mut name = definition.node_template.persistent_node_metadata.display_name.clone();
FrontendNodeType::with_input_types(definition.identifier, definition.category, input_types) if name.is_empty() {
}) name = identifier.implementation_name_from_identifier()
.collect();
// Update categories in extracted_node_types from node_types
for extracted_node in &mut extracted_node_types {
if extracted_node.category.is_empty() {
// Find matching node in node_types and update category if found
if let Some(matching_node) = node_types.iter().find(|node_type| node_type.name == extracted_node.name) {
extracted_node.category = matching_node.category.clone();
} }
} FrontendNodeType {
} identifier: identifier.clone(),
let missing_nodes: Vec<FrontendNodeType> = node_types name,
.iter() category: definition.category.to_string(),
.filter(|node| !extracted_node_types.iter().any(|extracted| extracted.name == node.name)) input_types,
.cloned() }
.collect(); })
.collect()
// Add the missing nodes to extracted_node_types
for node in missing_nodes {
extracted_node_types.push(node);
}
// Remove entries with empty categories
extracted_node_types.retain(|node| !node.category.is_empty());
extracted_node_types
} }
pub fn collect_node_descriptions() -> Vec<(String, String)> { pub fn collect_node_descriptions() -> Vec<(DefinitionIdentifier, String)> {
DOCUMENT_NODE_TYPES DOCUMENT_NODE_TYPES
.iter() .iter()
.map(|definition| { .map(|(identifier, definition)| (identifier.clone(), if definition.description != "TODO" { definition.description.to_string() } else { String::new() }))
(
definition.identifier.to_string(),
if definition.description != "TODO" { definition.description.to_string() } else { String::new() },
)
})
.collect() .collect()
} }
@@ -2744,8 +2706,9 @@ impl DocumentNodeDefinition {
// TODO: Replace the .enumerate() with changing the iterator to take a tuple of (index, input) so the user is forced to provide the correct index // TODO: Replace the .enumerate() with changing the iterator to take a tuple of (index, input) so the user is forced to provide the correct index
input_override.into_iter().enumerate().for_each(|(index, input_override)| { input_override.into_iter().enumerate().for_each(|(index, input_override)| {
if let Some(input_override) = input_override { if let Some(input_override) = input_override {
// Only value inputs can be overridden, since node inputs change graph structure and must be handled by the network interface // Only value inputs should be overridden, since node inputs change graph structure and must be handled by the network interface.
// assert!(matches!(input_override, NodeInput::Value { .. }), "Only value inputs are supported for input overrides"); // However, this would require changing some tooling which creates multiple nodes at once, before they are inserted into the network.
// debug_assert!(input_override.as_node().is_none(), "Node inputs are not supported in input overrides");
template.document_node.inputs[index] = input_override; template.document_node.inputs[index] = input_override;
} }
}); });
@@ -2806,12 +2769,6 @@ impl DocumentNodeDefinition {
} }
populate_input_properties(&mut template, Vec::new()); populate_input_properties(&mut template, Vec::new());
// Set the reference to the node definition
template.persistent_node_metadata.reference = Some(self.identifier.to_string());
// If the display name is empty and it is not a merge node, then set it to the reference
if template.persistent_node_metadata.display_name.is_empty() && self.identifier != "Merge" {
template.persistent_node_metadata.display_name = self.identifier.to_string();
}
template template
} }
@@ -1,72 +1,34 @@
use super::DocumentNodeDefinition; use super::DocumentNodeDefinition;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{DocumentNodePersistentMetadata, InputMetadata, NodeTemplate, WidgetOverride}; use crate::messages::portfolio::document::utility_types::network_interface::{DocumentNodePersistentMetadata, InputMetadata, NodeTemplate, WidgetOverride};
use graph_craft::ProtoNodeIdentifier;
use graph_craft::document::*; use graph_craft::document::*;
use graphene_std::registry::*; use graphene_std::registry::*;
use graphene_std::*; use graphene_std::*;
use std::collections::HashSet; use std::collections::{HashMap, HashSet};
/// Traverses a document node template and metadata in parallel to link the protonodes to their reference pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap<DefinitionIdentifier, DocumentNodeDefinition> {
fn traverse_node(node: &DocumentNode, node_metadata: &mut DocumentNodePersistentMetadata) { // Create hashmap for the protonodes added by the macro.
match &node.implementation { let mut definitions_map = HashMap::new();
DocumentNodeImplementation::Network(node_network) => { // First remove the custom protonodes and add them to the definitions map since they contain different metadata
for (nested_node_id, nested_node) in node_network.nodes.iter() { // from the macro and must be inserted first so that network nodes which reference them use the correct metadata.
let nested_metadata = node_metadata let network_nodes = custom
.network_metadata .into_iter()
.as_mut() .filter_map(|definition| {
.expect("Network node must have network metadata") if let DocumentNodeImplementation::ProtoNode(proto_node_identifier) = &definition.node_template.document_node.implementation {
.persistent_metadata definitions_map.insert(DefinitionIdentifier::ProtoNode(proto_node_identifier.clone()), definition);
.node_metadata return None;
.get_mut(nested_node_id) };
.expect("Network metadata must have corresponding node id"); Some(definition)
traverse_node(nested_node, &mut nested_metadata.persistent_metadata); })
} .collect::<Vec<_>>();
}
DocumentNodeImplementation::ProtoNode(proto_node_identifier) => {
if let Some(metadata) = NODE_METADATA.lock().unwrap().get(proto_node_identifier) {
node_metadata.reference = Some(metadata.display_name.to_string());
}
}
DocumentNodeImplementation::Extract => {}
}
}
pub(super) fn post_process_nodes(mut custom: Vec<DocumentNodeDefinition>) -> Vec<DocumentNodeDefinition> {
// Link the protonodes with custom networks to their reference
for node in custom.iter_mut() {
traverse_node(&node.node_template.document_node, &mut node.node_template.persistent_node_metadata);
}
// Remove struct generics
for DocumentNodeDefinition { node_template, .. } in custom.iter_mut() {
let NodeTemplate {
document_node: DocumentNode { implementation, .. },
..
} = node_template;
if let DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier { name }) = implementation
&& let Some((new_name, _suffix)) = name.rsplit_once("<")
{
*name = Cow::Owned(new_name.to_string())
};
}
// Add the rest of the protonodes from the macro
let node_registry = NODE_REGISTRY.lock().unwrap(); let node_registry = NODE_REGISTRY.lock().unwrap();
'outer: for (id, metadata) in NODE_METADATA.lock().unwrap().iter() { for (id, metadata) in NODE_METADATA.lock().unwrap().iter() {
for node in custom.iter() { let identifier = DefinitionIdentifier::ProtoNode(id.clone());
let DocumentNodeDefinition { if definitions_map.contains_key(&identifier) {
node_template: NodeTemplate { continue;
document_node: DocumentNode { implementation, .. }, };
..
},
..
} = node;
match implementation {
DocumentNodeImplementation::ProtoNode(name) if name == id => continue 'outer,
_ => (),
}
}
let NodeMetadata { let NodeMetadata {
display_name, display_name,
category, category,
@@ -78,48 +40,96 @@ pub(super) fn post_process_nodes(mut custom: Vec<DocumentNodeDefinition>) -> Vec
let Some(implementations) = &node_registry.get(id) else { continue }; let Some(implementations) = &node_registry.get(id) else { continue };
let valid_inputs: HashSet<_> = implementations.iter().map(|(_, node_io)| node_io.call_argument.clone()).collect();
let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() }); let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() });
let valid_inputs: HashSet<_> = implementations.iter().map(|(_, node_io)| node_io.call_argument.clone()).collect();
let input_type = if valid_inputs.len() > 1 { &const { generic!(D) } } else { &first_node_io.call_argument }; let input_type = if valid_inputs.len() > 1 { &const { generic!(D) } } else { &first_node_io.call_argument };
let output_type = &first_node_io.return_value;
let inputs = preprocessor::node_inputs(fields, first_node_io); let inputs = preprocessor::node_inputs(fields, first_node_io);
let node = DocumentNodeDefinition { definitions_map.insert(
identifier: display_name, identifier,
node_template: NodeTemplate { DocumentNodeDefinition {
document_node: DocumentNode { identifier: display_name,
inputs, node_template: NodeTemplate {
call_argument: input_type.clone(), document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(id.clone()), inputs,
visible: true, call_argument: input_type.clone(),
skip_deduplication: false, implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
context_features: ContextDependencies::from(context_features.as_slice()), visible: true,
..Default::default() skip_deduplication: false,
}, context_features: ContextDependencies::from(context_features.as_slice()),
persistent_node_metadata: DocumentNodePersistentMetadata { ..Default::default()
// TODO: Store information for input overrides in the node macro },
input_metadata: fields persistent_node_metadata: DocumentNodePersistentMetadata {
.iter() // TODO: Store information for input overrides in the node macro
.map(|f| match f.widget_override { input_metadata: fields
RegistryWidgetOverride::None => (f.name, f.description).into(), .iter()
RegistryWidgetOverride::Hidden => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::Hidden), .map(|f| match f.widget_override {
RegistryWidgetOverride::String(str) => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::String(str.to_string())), RegistryWidgetOverride::None => (f.name, f.description).into(),
RegistryWidgetOverride::Custom(str) => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::Custom(str.to_string())), RegistryWidgetOverride::Hidden => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::Hidden),
}) RegistryWidgetOverride::String(str) => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::String(str.to_string())),
.collect(), RegistryWidgetOverride::Custom(str) => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::Custom(str.to_string())),
output_names: vec![output_type.to_string()], })
locked: false, .collect(),
..Default::default() locked: false,
..Default::default()
},
}, },
category: category.unwrap_or("UNCATEGORIZED"),
description: Cow::Borrowed(description),
properties: *properties,
}, },
category: category.unwrap_or("UNCATEGORIZED"), );
description: Cow::Borrowed(description),
properties: *properties,
};
custom.push(node);
} }
custom // If any protonode does not have metadata then set its display name to its identifier string
for definition in definitions_map.values_mut() {
let metadata = NODE_METADATA.lock().unwrap();
if let DocumentNodeImplementation::ProtoNode(id) = &definition.node_template.document_node.implementation
&& !metadata.contains_key(id)
{
definition.node_template.persistent_node_metadata.display_name = definition.identifier.to_string();
}
}
// Add the rest of the network nodes to the map and add the metadata for their internal protonodes
for mut network_node in network_nodes {
traverse_node(&network_node.node_template.document_node, &mut network_node.node_template.persistent_node_metadata, &definitions_map);
// Set the reference to the node identifier
if let Some(nested_metadata) = network_node.node_template.persistent_node_metadata.network_metadata.as_mut() {
nested_metadata.persistent_metadata.reference = Some(network_node.identifier.to_string());
// If it is not a merge node, then set the display name to the identifier/reference
if network_node.identifier != "Merge" {
network_node.node_template.persistent_node_metadata.display_name = network_node.identifier.to_string();
}
}
definitions_map.insert(DefinitionIdentifier::Network(network_node.identifier.to_string()), network_node);
}
definitions_map
}
/// Traverses a document node template and metadata in parallel to add metadata to the protonodes
fn traverse_node(node: &DocumentNode, node_metadata: &mut DocumentNodePersistentMetadata, definitions_map: &HashMap<DefinitionIdentifier, DocumentNodeDefinition>) {
match &node.implementation {
DocumentNodeImplementation::Network(node_network) => {
for (nested_node_id, nested_node) in node_network.nodes.iter() {
let nested_metadata = node_metadata.network_metadata.as_mut().unwrap().persistent_metadata.node_metadata.get_mut(nested_node_id).unwrap();
traverse_node(nested_node, &mut nested_metadata.persistent_metadata, definitions_map);
}
}
DocumentNodeImplementation::ProtoNode(id) => {
// Set all the metadata except the position to the proto node information from the macro
// TODO: Use options in the template to specify what you want to default and what you want to override
// If this fails then the proto node id in the definition doesn't match what is generated by the macro
let Some(definition) = definitions_map.get(&DefinitionIdentifier::ProtoNode(id.clone())) else {
// log::error!("Could not get definition for id {} when filling in protonode metadata for a custom node", id.clone());
return;
};
let mut new_metadata = definition.node_template.persistent_node_metadata.clone();
new_metadata.node_type_metadata = node_metadata.node_type_metadata.clone();
*node_metadata = new_metadata
}
DocumentNodeImplementation::Extract => {}
}
} }
@@ -1,5 +1,6 @@
use super::utility_types::Direction; use super::utility_types::Direction;
use crate::messages::input_mapper::utility_types::input_keyboard::Key; use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector}; use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector};
use crate::messages::prelude::*; use crate::messages::prelude::*;
@@ -27,16 +28,16 @@ pub enum NodeGraphMessage {
SelectedNodesUpdated, SelectedNodesUpdated,
Copy, Copy,
CreateNodeInLayerNoTransaction { CreateNodeInLayerNoTransaction {
node_type: String, node_type: DefinitionIdentifier,
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
}, },
CreateNodeInLayerWithTransaction { CreateNodeInLayerWithTransaction {
node_type: String, node_type: DefinitionIdentifier,
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
}, },
CreateNodeFromContextMenu { CreateNodeFromContextMenu {
node_id: Option<NodeId>, node_id: Option<NodeId>,
node_type: String, node_type: DefinitionIdentifier,
xy: Option<(i32, i32)>, xy: Option<(i32, i32)>,
add_transaction: bool, add_transaction: bool,
}, },
@@ -1,12 +1,14 @@
use super::node_properties;
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode}; use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode};
use super::{document_node_definitions, node_properties};
use crate::consts::GRID_SIZE; use crate::consts::GRID_SIZE;
use crate::messages::clipboard::utility_types::ClipboardContent; use crate::messages::clipboard::utility_types::ClipboardContent;
use crate::messages::input_mapper::utility_types::macros::{action_shortcut, action_shortcut_manual}; use crate::messages::input_mapper::utility_types::macros::{action_shortcut, action_shortcut_manual};
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::document_message_handler::navigation_controls; use crate::messages::portfolio::document::document_message_handler::navigation_controls;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext; use crate::messages::portfolio::document::node_graph::document_node_definitions::{
DefinitionIdentifier, NodePropertiesContext, resolve_document_node_type, resolve_network_node_type, resolve_proto_node_type,
};
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, NodeGraphErrorDiagnostic}; use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, NodeGraphErrorDiagnostic};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
@@ -131,7 +133,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
} }
NodeGraphMessage::AddPathNode => { NodeGraphMessage::AddPathNode => {
if let Some(layer) = make_path_editable_is_allowed(network_interface) { if let Some(layer) = make_path_editable_is_allowed(network_interface) {
responses.add(NodeGraphMessage::CreateNodeInLayerWithTransaction { node_type: "Path".to_string(), layer }); responses.add(NodeGraphMessage::CreateNodeInLayerWithTransaction {
node_type: DefinitionIdentifier::Network("Path".into()),
layer,
});
responses.add(EventMessage::SelectionChanged); responses.add(EventMessage::SelectionChanged);
} }
} }
@@ -208,7 +213,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let mid_point = (network_interface.get_output_center(&output_connector, breadcrumb_network_path).unwrap() let mid_point = (network_interface.get_output_center(&output_connector, breadcrumb_network_path).unwrap()
+ network_interface.get_input_center(&input_connector, breadcrumb_network_path).unwrap()) + network_interface.get_input_center(&input_connector, breadcrumb_network_path).unwrap())
/ 2.; / 2.;
let node_template = Box::new(document_node_definitions::resolve_document_node_type("Passthrough").unwrap().default_node_template()); let node_template = Box::new(resolve_proto_node_type(graphene_core::ops::identity::IDENTIFIER).unwrap().default_node_template());
let node_id = NodeId::new(); let node_id = NodeId::new();
responses.add(NodeGraphMessage::InsertNode { node_id, node_template }); responses.add(NodeGraphMessage::InsertNode { node_id, node_template });
@@ -274,10 +279,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let node_id = node_id.unwrap_or_else(NodeId::new); let node_id = node_id.unwrap_or_else(NodeId::new);
let Some(document_node_type) = document_node_definitions::resolve_document_node_type(&node_type) else { let Some(document_node_type) = resolve_document_node_type(&node_type) else {
responses.add(DialogMessage::DisplayDialogError { responses.add(DialogMessage::DisplayDialogError {
title: "Cannot insert node".to_string(), title: "Cannot insert node".to_string(),
description: format!("The document node '{node_type}' does not exist in the document node list"), description: format!("The document node '{node_type:?}' does not exist in the document node list"),
}); });
return; return;
}; };
@@ -639,9 +644,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
// Use the network interface to add a default node, then set the imports, exports, paste the nodes inside, and connect them to the imports/exports // Use the network interface to add a default node, then set the imports, exports, paste the nodes inside, and connect them to the imports/exports
let encapsulating_node_id = NodeId::new(); let encapsulating_node_id = NodeId::new();
let mut default_node_template = document_node_definitions::resolve_document_node_type("Default Network") let mut default_node_template = resolve_network_node_type("Default Network").expect("Default Network node should exist").default_node_template();
.expect("Default Network node should exist")
.default_node_template();
let Some(center_of_selected_nodes) = network_interface.selected_nodes_bounding_box(breadcrumb_network_path).map(|[a, b]| (a + b) / 2.) else { let Some(center_of_selected_nodes) = network_interface.selected_nodes_bounding_box(breadcrumb_network_path).map(|[a, b]| (a + b) / 2.) else {
log::error!("Could not get center of selected_nodes"); log::error!("Could not get center of selected_nodes");
return; return;
@@ -1701,7 +1704,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
input, input,
}); });
responses.add(PropertiesPanelMessage::Refresh); responses.add(PropertiesPanelMessage::Refresh);
if !(network_interface.reference(&node_id, selection_network_path).is_none() || input_index == 0) && network_interface.connected_to_output(&node_id, selection_network_path) { if network_interface.connected_to_output(&node_id, selection_network_path) {
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
} }
@@ -2596,8 +2599,9 @@ impl NodeGraphMessageHandler {
.node_metadata(&node_id, breadcrumb_network_path) .node_metadata(&node_id, breadcrumb_network_path)
.is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()), .is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
can_be_layer: network_interface.is_eligible_to_be_layer(&node_id, breadcrumb_network_path), can_be_layer: network_interface.is_eligible_to_be_layer(&node_id, breadcrumb_network_path),
reference: network_interface.reference(&node_id, breadcrumb_network_path).cloned().unwrap_or_default(), reference: network_interface.reference(&node_id, breadcrumb_network_path),
display_name: network_interface.display_name(&node_id, breadcrumb_network_path), display_name: network_interface.display_name(&node_id, breadcrumb_network_path),
implementation_name: network_interface.implementation_name(&node_id, breadcrumb_network_path),
primary_input, primary_input,
exposed_inputs, exposed_inputs,
primary_output, primary_output,
@@ -2711,10 +2715,16 @@ impl NodeGraphMessageHandler {
} }
}); });
let Some(reference) = network_interface.reference(&node_id, &[]) else {
log::error!("Could not get reference for layer {node_id} in update_layer_panel");
continue;
};
let clippable = layer.can_be_clipped(network_interface.document_metadata()); let clippable = layer.can_be_clipped(network_interface.document_metadata());
let data = LayerPanelEntry { let data = LayerPanelEntry {
id: node_id, id: node_id,
reference: network_interface.reference(&node_id, &[]).and_then(|x| x.as_ref()).cloned().unwrap_or_default(), reference,
alias: network_interface.display_name(&node_id, &[]), alias: network_interface.display_name(&node_id, &[]),
in_selected_network: selection_network_path.is_empty(), in_selected_network: selection_network_path.is_empty(),
children_allowed, children_allowed,
@@ -3,6 +3,7 @@
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext}; use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
use super::utility_types::FrontendGraphDataType; use super::utility_types::FrontendGraphDataType;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector; use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::portfolio::utility_types::{FontCatalogStyle, PersistentData}; use crate::messages::portfolio::utility_types::{FontCatalogStyle, PersistentData};
use crate::messages::prelude::*; use crate::messages::prelude::*;
@@ -1705,10 +1706,8 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
if let Some(properties_override) = context if let Some(properties_override) = context
.network_interface .network_interface
.reference(&node_id, context.selection_network_path) .reference(&node_id, context.selection_network_path)
.cloned()
.unwrap_or_default()
.as_ref() .as_ref()
.and_then(|reference| super::document_node_definitions::resolve_document_node_type(reference)) .and_then(|identifier| resolve_document_node_type(identifier))
.and_then(|definition| definition.properties) .and_then(|definition| definition.properties)
.and_then(|properties| NODE_OVERRIDES.get(properties)) .and_then(|properties| NODE_OVERRIDES.get(properties))
{ {
@@ -1773,25 +1772,20 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
if layout.is_empty() { if layout.is_empty() {
layout = node_no_properties(node_id, context); layout = node_no_properties(node_id, context);
} }
let name = context let name = context.network_interface.implementation_name(&node_id, context.selection_network_path);
let description = context
.network_interface .network_interface
.reference(&node_id, context.selection_network_path) .reference(&node_id, context.selection_network_path)
.cloned() .as_ref()
.unwrap_or_default() // If there is an error getting the reference, default to empty string .and_then(|identifier| resolve_document_node_type(identifier))
.or_else(|| { .map(|definition| definition.description.to_string())
// If there is no reference, try to get the proto node name .filter(|string| string != "TODO")
context.network_interface.implementation(&node_id, context.selection_network_path).and_then(|implementation|{ .unwrap_or_default();
if let DocumentNodeImplementation::ProtoNode(protonode) = implementation {
Some(protonode.name.clone().into_owned())
} else {
None
}
})
})
.unwrap_or("Custom Node".to_string());
let description = context.network_interface.description(&node_id, context.selection_network_path);
let visible = context.network_interface.is_visible(&node_id, context.selection_network_path); let visible = context.network_interface.is_visible(&node_id, context.selection_network_path);
let pinned = context.network_interface.is_pinned(&node_id, context.selection_network_path); let pinned = context.network_interface.is_pinned(&node_id, context.selection_network_path);
LayoutGroup::Section { LayoutGroup::Section {
name, name,
description, description,
@@ -1,8 +1,8 @@
use super::document_node_definitions::DefinitionIdentifier;
use glam::{DVec2, IVec2}; use glam::{DVec2, IVec2};
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graphene_std::Type; use graphene_std::Type;
use std::borrow::Cow;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType { pub enum FrontendGraphDataType {
@@ -79,9 +79,11 @@ pub struct FrontendNode {
pub is_layer: bool, pub is_layer: bool,
#[serde(rename = "canBeLayer")] #[serde(rename = "canBeLayer")]
pub can_be_layer: bool, pub can_be_layer: bool,
pub reference: Option<String>, pub reference: Option<DefinitionIdentifier>,
#[serde(rename = "displayName")] #[serde(rename = "displayName")]
pub display_name: String, pub display_name: String,
#[serde(rename = "implementationName")]
pub implementation_name: String,
#[serde(rename = "primaryInput")] #[serde(rename = "primaryInput")]
pub primary_input: Option<FrontendGraphInput>, pub primary_input: Option<FrontendGraphInput>,
#[serde(rename = "exposedInputs")] #[serde(rename = "exposedInputs")]
@@ -102,29 +104,13 @@ pub struct FrontendNode {
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeType { pub struct FrontendNodeType {
pub name: Cow<'static, str>, pub identifier: DefinitionIdentifier,
pub category: Cow<'static, str>, pub name: String,
pub category: String,
#[serde(rename = "inputTypes")] #[serde(rename = "inputTypes")]
pub input_types: Option<Vec<Cow<'static, str>>>, pub input_types: Vec<String>,
} }
impl FrontendNodeType {
pub fn new(name: impl Into<Cow<'static, str>>, category: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
category: category.into(),
input_types: None,
}
}
pub fn with_input_types(name: impl Into<Cow<'static, str>>, category: impl Into<Cow<'static, str>>, input_types: Vec<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
category: category.into(),
input_types: Some(input_types),
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct DragStart { pub struct DragStart {
pub start_x: f64, pub start_x: f64,
@@ -1,6 +1,7 @@
use super::network_interface::NodeNetworkInterface; use super::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::document::graph_operation::transform_utils; use crate::messages::portfolio::document::graph_operation::transform_utils;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::FlowType; use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
@@ -91,7 +92,8 @@ impl DocumentMetadata {
let mut use_local = true; let mut use_local = true;
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface); let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface);
if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer("Path") let identifier = DefinitionIdentifier::Network("Path".into());
if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer(&identifier)
&& let Some(&source) = self.first_element_source_ids.get(&layer.to_node()) && let Some(&source) = self.first_element_source_ids.get(&layer.to_node())
&& !network_interface && !network_interface
.upstream_flow_back_from_nodes(vec![path_node], &[], FlowType::HorizontalFlow) .upstream_flow_back_from_nodes(vec![path_node], &[], FlowType::HorizontalFlow)
@@ -114,7 +116,7 @@ impl DocumentMetadata {
let local_transform = self.local_transforms.get(&layer.to_node()).copied(); let local_transform = self.local_transforms.get(&layer.to_node()).copied();
let transform = local_transform.unwrap_or_else(|| { let transform = local_transform.unwrap_or_else(|| {
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain("Transform", layer, network_interface); let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain(&DefinitionIdentifier::Network("Transform".into()), layer, network_interface);
let transform_node = transform_node_id.and_then(|id| network_interface.document_node(&id, &[])); let transform_node = transform_node_id.and_then(|id| network_interface.document_node(&id, &[]));
transform_node.map(|node| transform_utils::get_current_transform(node.inputs.as_slice())).unwrap_or_default() transform_node.map(|node| transform_utils::get_current_transform(node.inputs.as_slice())).unwrap_or_default()
}); });
@@ -7,7 +7,7 @@ use super::misc::PTZ;
use super::nodes::SelectedNodes; use super::nodes::SelectedNodes;
use crate::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}; use crate::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP};
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type}; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type};
use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput}; use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput};
use crate::messages::portfolio::document::overlays::utility_functions::text_width; use crate::messages::portfolio::document::overlays::utility_functions::text_width;
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::ResolvedDocumentNodeTypes; use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::ResolvedDocumentNodeTypes;
@@ -79,7 +79,7 @@ impl NodeNetworkInterface {
fix_network(network); fix_network(network);
} }
if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation
&& protonode.name.contains("PathModifyNode") && protonode.as_str().contains("PathModifyNode")
&& node.inputs.len() < 3 && node.inputs.len() < 3
{ {
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)); node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath));
@@ -470,12 +470,6 @@ impl NodeNetworkInterface {
node_template node_template
} }
/// Try and get the [`DocumentNodeDefinition`] for a node
pub fn get_node_definition(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeDefinition> {
let metadata = self.node_metadata(node_id, network_path)?;
resolve_document_node_type(metadata.persistent_metadata.reference.as_ref()?)
}
pub fn input_from_connector(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<&NodeInput> { pub fn input_from_connector(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<&NodeInput> {
let Some(network) = self.nested_network(network_path) else { let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get network in input_from_connector"); log::error!("Could not get network in input_from_connector");
@@ -916,12 +910,30 @@ impl NodeNetworkInterface {
} }
} }
pub fn reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&Option<String>> { pub fn reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<DefinitionIdentifier> {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else { let Some(document_node) = self.document_node(node_id, network_path) else {
log::error!("Could not get reference for node: {node_id:?}"); log::error!("Could not get document_node for node in reference: {node_id:?}");
return None; return None;
}; };
Some(&node_metadata.persistent_metadata.reference) match &document_node.implementation {
DocumentNodeImplementation::Network(_) => {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get reference for node in reference: {node_id:?}");
return None;
};
node_metadata
.persistent_metadata
.network_metadata
.as_ref()
.expect("Network metadata must exist for network node in reference")
.persistent_metadata
.reference
.clone()
.map(DefinitionIdentifier::Network)
}
DocumentNodeImplementation::ProtoNode(protonode_id) => Some(DefinitionIdentifier::ProtoNode(protonode_id.clone())),
_ => None,
}
} }
pub fn implementation(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeImplementation> { pub fn implementation(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeImplementation> {
@@ -982,11 +994,6 @@ impl NodeNetworkInterface {
pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
let is_layer = self.is_layer(node_id, network_path); let is_layer = self.is_layer(node_id, network_path);
let Some(reference) = self.reference(node_id, network_path) else {
log::error!("Could not get reference in untitled_layer_label");
return "".to_string();
};
let display_name = if let Some(node_metadata) = self.node_metadata(node_id, network_path) { let display_name = if let Some(node_metadata) = self.node_metadata(node_id, network_path) {
node_metadata.persistent_metadata.display_name.clone() node_metadata.persistent_metadata.display_name.clone()
} else { } else {
@@ -998,18 +1005,19 @@ impl NodeNetworkInterface {
if is_layer { if is_layer {
"Untitled Layer".to_string() "Untitled Layer".to_string()
} else { } else {
reference.clone().unwrap_or("Untitled Node".to_string()) // TODO: Have this displayed in italics in the UI
self.implementation_name(node_id, network_path)
} }
} else { } else {
display_name display_name
} }
} }
/// Returns the description of the node, or an empty string if it is not set. /// The uneditable name in the Properties panel which represents the function name of the node implementation.
pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { pub fn implementation_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
self.get_node_definition(node_id, network_path) self.reference(node_id, network_path)
.map(|node_definition| node_definition.description.to_string()) .map(|identifier| identifier.implementation_name_from_identifier())
.unwrap_or_default() .unwrap_or("Custom Node".to_string())
} }
pub fn is_locked(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { pub fn is_locked(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
@@ -1125,7 +1133,7 @@ impl NodeNetworkInterface {
pub fn is_artboard(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { pub fn is_artboard(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
self.reference(node_id, network_path) self.reference(node_id, network_path)
.is_some_and(|reference| *reference == Some("Artboard".to_string()) && self.connected_to_output(node_id, &[])) .is_some_and(|reference| reference == DefinitionIdentifier::Network("Artboard".into()) && self.connected_to_output(node_id, &[]))
} }
pub fn all_artboards(&self) -> HashSet<LayerNodeIdentifier> { pub fn all_artboards(&self) -> HashSet<LayerNodeIdentifier> {
@@ -1134,12 +1142,13 @@ impl NodeNetworkInterface {
.node_metadata .node_metadata
.iter() .iter()
.filter_map(|(node_id, node_metadata)| { .filter_map(|(node_id, node_metadata)| {
if node_metadata if node_metadata.persistent_metadata.network_metadata.as_ref().is_some_and(|network_metadata| {
.persistent_metadata network_metadata
.reference .persistent_metadata
.as_ref() .reference
.is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[]) && self.is_layer(node_id, &[])) .as_ref()
{ .is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[]) && self.is_layer(node_id, &[]))
}) {
Some(LayerNodeIdentifier::new(*node_id, self)) Some(LayerNodeIdentifier::new(*node_id, self))
} else { } else {
None None
@@ -1345,7 +1354,6 @@ impl NodeNetworkInterface {
node.skip_deduplication = old_node.skip_deduplication; node.skip_deduplication = old_node.skip_deduplication;
node.original_location = old_node.original_location; node.original_location = old_node.original_location;
node_metadata.persistent_metadata.display_name = old_node.alias; node_metadata.persistent_metadata.display_name = old_node.alias;
node_metadata.persistent_metadata.reference = if old_node.name.is_empty() { None } else { Some(old_node.name) };
node_metadata.persistent_metadata.locked = old_node.locked; node_metadata.persistent_metadata.locked = old_node.locked;
node_metadata.persistent_metadata.node_type_metadata = if old_node.is_layer { node_metadata.persistent_metadata.node_type_metadata = if old_node.is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
@@ -3047,7 +3055,7 @@ impl NodeNetworkInterface {
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> { pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self); let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self);
if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer("Path") if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer(&DefinitionIdentifier::Network("Path".into()))
&& let Some(vector) = self.document_metadata.vector_modify.get(&path_node) && let Some(vector) = self.document_metadata.vector_modify.get(&path_node)
{ {
let mut modified = vector.clone(); let mut modified = vector.clone();
@@ -3200,14 +3208,6 @@ impl NodeNetworkInterface {
} }
} }
pub fn set_reference(&mut self, node_id: &NodeId, network_path: &[NodeId], reference: Option<String>) {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node_metadata in set_reference");
return;
};
node_metadata.persistent_metadata.reference = reference;
}
pub fn set_transform(&mut self, transform: DAffine2, network_path: &[NodeId]) { pub fn set_transform(&mut self, transform: DAffine2, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network in set_transform"); log::error!("Could not get nested network in set_transform");
@@ -3278,7 +3278,9 @@ impl NodeNetworkInterface {
encapsulating_node_metadata.persistent_metadata.output_names.insert(insert_index as usize, output_name.to_string()); encapsulating_node_metadata.persistent_metadata.output_names.insert(insert_index as usize, output_name.to_string());
} }
// Clear the reference to the nodes definition // Clear the reference to the nodes definition
encapsulating_node_metadata.persistent_metadata.reference = None; if let Some(network_metadata) = encapsulating_node_metadata.persistent_metadata.network_metadata.as_mut() {
network_metadata.persistent_metadata.reference = None
}
}; };
// Update the export ports and outward wires for the current network // Update the export ports and outward wires for the current network
@@ -3347,7 +3349,9 @@ impl NodeNetworkInterface {
} }
// Clear the reference to the nodes definition // Clear the reference to the nodes definition
node_metadata.persistent_metadata.reference = None; if let Some(network_metadata) = node_metadata.persistent_metadata.network_metadata.as_mut() {
network_metadata.persistent_metadata.reference = None
}
// Update the metadata for the encapsulating node // Update the metadata for the encapsulating node
self.unload_node_click_targets(&node_id, &encapsulating_network_path); self.unload_node_click_targets(&node_id, &encapsulating_network_path);
@@ -3403,7 +3407,9 @@ impl NodeNetworkInterface {
return; return;
}; };
encapsulating_node_metadata.persistent_metadata.output_names.remove(export_index); encapsulating_node_metadata.persistent_metadata.output_names.remove(export_index);
encapsulating_node_metadata.persistent_metadata.reference = None; if let Some(network_metadata) = encapsulating_node_metadata.persistent_metadata.network_metadata.as_mut() {
network_metadata.persistent_metadata.reference = None;
}
// Update the metadata for the encapsulating node // Update the metadata for the encapsulating node
self.unload_outward_wires(&encapsulating_network_path); self.unload_outward_wires(&encapsulating_network_path);
@@ -3477,7 +3483,9 @@ impl NodeNetworkInterface {
return; return;
}; };
encapsulating_node_metadata.persistent_metadata.input_metadata.remove(import_index); encapsulating_node_metadata.persistent_metadata.input_metadata.remove(import_index);
encapsulating_node_metadata.persistent_metadata.reference = None; if let Some(network_metadata) = encapsulating_node_metadata.persistent_metadata.network_metadata.as_mut() {
network_metadata.persistent_metadata.reference = None;
}
// Update the metadata for the encapsulating node // Update the metadata for the encapsulating node
self.unload_outward_wires(encapsulating_network_path); self.unload_outward_wires(encapsulating_network_path);
@@ -3523,7 +3531,9 @@ impl NodeNetworkInterface {
let name = encapsulating_node_metadata.persistent_metadata.output_names.remove(start_index); let name = encapsulating_node_metadata.persistent_metadata.output_names.remove(start_index);
encapsulating_node_metadata.persistent_metadata.output_names.insert(end_index, name); encapsulating_node_metadata.persistent_metadata.output_names.insert(end_index, name);
encapsulating_node_metadata.persistent_metadata.reference = None; if let Some(network_metadata) = encapsulating_node_metadata.persistent_metadata.network_metadata.as_mut() {
network_metadata.persistent_metadata.reference = None;
}
// Update the metadata for the encapsulating network // Update the metadata for the encapsulating network
self.unload_outward_wires(&encapsulating_network_path); self.unload_outward_wires(&encapsulating_network_path);
@@ -3615,7 +3625,9 @@ impl NodeNetworkInterface {
let properties_row = encapsulating_node_metadata.persistent_metadata.input_metadata.remove(start_index); let properties_row = encapsulating_node_metadata.persistent_metadata.input_metadata.remove(start_index);
encapsulating_node_metadata.persistent_metadata.input_metadata.insert(end_index, properties_row); encapsulating_node_metadata.persistent_metadata.input_metadata.insert(end_index, properties_row);
encapsulating_node_metadata.persistent_metadata.reference = None; if let Some(network_metadata) = encapsulating_node_metadata.persistent_metadata.network_metadata.as_mut() {
network_metadata.persistent_metadata.reference = None;
}
// Update the metadata for the outer network // Update the metadata for the outer network
self.unload_outward_wires(&encapsulating_network_path); self.unload_outward_wires(&encapsulating_network_path);
@@ -3721,28 +3733,17 @@ impl NodeNetworkInterface {
let number_of_inputs = node.inputs.len(); let number_of_inputs = node.inputs.len();
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else { return }; let Some(metadata) = self.node_metadata_mut(node_id, network_path) else { return };
for added_input_index in metadata.persistent_metadata.input_metadata.len()..number_of_inputs { for added_input_index in metadata.persistent_metadata.input_metadata.len()..number_of_inputs {
let reference = metadata.persistent_metadata.reference.as_ref(); let input_metadata = self
let definition = reference.and_then(|reference| resolve_document_node_type(reference)); .reference(node_id, network_path)
let input_metadata = definition .as_ref()
.and_then(resolve_document_node_type)
.and_then(|definition| definition.node_template.persistent_node_metadata.input_metadata.get(added_input_index)) .and_then(|definition| definition.node_template.persistent_node_metadata.input_metadata.get(added_input_index))
.cloned(); .cloned();
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else { return };
metadata.persistent_metadata.input_metadata.push(input_metadata.unwrap_or_default()); metadata.persistent_metadata.input_metadata.push(input_metadata.unwrap_or_default());
} }
} }
/// Used to ensure the display name is the reference name in case it is empty.
pub fn validate_display_name_metadata(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else { return };
if metadata.persistent_metadata.display_name.is_empty()
&& let Some(reference) = metadata.persistent_metadata.reference.clone()
{
// Keep the name for merge nodes as empty
if reference != "Merge" {
metadata.persistent_metadata.display_name = reference;
}
}
}
// When opening an old document to ensure the output names match the number of exports // When opening an old document to ensure the output names match the number of exports
pub fn validate_output_names(&mut self, node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId]) { pub fn validate_output_names(&mut self, node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId]) {
if let DocumentNodeImplementation::Network(network) = &node.implementation { if let DocumentNodeImplementation::Network(network) = &node.implementation {
@@ -3755,13 +3756,17 @@ impl NodeNetworkInterface {
} }
} }
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts /// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts.
pub fn replace_reference_name(&mut self, node_id: &NodeId, network_path: &[NodeId], reference_name: String) { /// Only works with network nodes. Proto nodes use their ID as the reference.
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { pub fn set_reference(&mut self, node_id: &NodeId, network_path: &[NodeId], reference_name: Option<String>) {
log::error!("Could not get node metadata in replace_reference_name"); let Some(node_network_metadata) = self
.node_metadata_mut(node_id, network_path)
.and_then(|node_metadata| node_metadata.persistent_metadata.network_metadata.as_mut())
else {
log::error!("Could not get network metadata in replace_reference_name");
return; return;
}; };
node_metadata.persistent_metadata.reference = Some(reference_name); node_network_metadata.persistent_metadata.reference = reference_name;
} }
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts /// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts
@@ -4397,7 +4402,10 @@ impl NodeNetworkInterface {
node_metadata.persistent_metadata.display_name.clone_from(&display_name); node_metadata.persistent_metadata.display_name.clone_from(&display_name);
// Keep the alias in sync with the `ToArtboard` name input // Keep the alias in sync with the `ToArtboard` name input
if node_metadata.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Artboard") { if self
.reference(node_id, network_path)
.is_some_and(|reference| reference == DefinitionIdentifier::Network("Artboard".into()))
{
let Some(nested_network) = self.network_mut(network_path) else { let Some(nested_network) = self.network_mut(network_path) else {
return; return;
}; };
@@ -5382,7 +5390,9 @@ impl NodeNetworkInterface {
// If a non artboard layer is attempted to be connected to the exports, and there is already an artboard connected, then connect the layer to the artboard. // If a non artboard layer is attempted to be connected to the exports, and there is already an artboard connected, then connect the layer to the artboard.
if let Some(first_layer) = LayerNodeIdentifier::ROOT_PARENT.children(&self.document_metadata).next() if let Some(first_layer) = LayerNodeIdentifier::ROOT_PARENT.children(&self.document_metadata).next()
&& parent == LayerNodeIdentifier::ROOT_PARENT && parent == LayerNodeIdentifier::ROOT_PARENT
&& self.reference(&layer.to_node(), network_path).is_none_or(|reference| *reference != Some("Artboard".to_string())) && self
.reference(&layer.to_node(), network_path)
.is_none_or(|reference| reference != DefinitionIdentifier::Network("Artboard".into()))
&& self.is_artboard(&first_layer.to_node(), network_path) && self.is_artboard(&first_layer.to_node(), network_path)
{ {
parent = first_layer; parent = first_layer;
@@ -5974,6 +5984,12 @@ impl NodeNetworkMetadata {
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct NodeNetworkPersistentMetadata { pub struct NodeNetworkPersistentMetadata {
/// The identifier for the node definition created for custom network nodes in [`DocumentNodeDefinition`].
/// It is only used to associate network nodes with their definition. Protonodes use their ProtonodeIdentifier.
/// The reference is removed once the node is modified, since the node now stores its own implementation and inputs.
/// TODO: Used during serialization/deserialization to prevent storing implementation or inputs (and possible other fields) if they are the same as the definition.
/// TODO: Implement node versioning so that references to old nodes can be updated to the new node definition.
pub reference: Option<String>,
/// Node metadata must exist for every document node in the network /// Node metadata must exist for every document node in the network
#[serde(serialize_with = "graphene_std::vector::serialize_hashmap", deserialize_with = "graphene_std::vector::deserialize_hashmap")] #[serde(serialize_with = "graphene_std::vector::serialize_hashmap", deserialize_with = "graphene_std::vector::deserialize_hashmap")]
pub node_metadata: HashMap<NodeId, DocumentNodeMetadata>, pub node_metadata: HashMap<NodeId, DocumentNodeMetadata>,
@@ -6218,13 +6234,9 @@ struct InputTransientMetadata {
/// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node. /// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node.
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DocumentNodePersistentMetadata { pub struct DocumentNodePersistentMetadata {
/// The name of the node definition, as originally set by [`DocumentNodeDefinition`], used to display in the UI and to display the appropriate properties if no display name is set. /// A name chosen by the user for this instance of the node. Empty indicates no given name, in which case the implementation name is displayed to the user in italics.
// TODO: Used during serialization/deserialization to prevent storing implementation or inputs (and possible other fields) if they are the same as the definition.
// TODO: The reference is removed once the node is modified, since the node now stores its own implementation and inputs.
// TODO: Implement node versioning so that references to old nodes can be updated to the new node definition.
pub reference: Option<String>,
/// A name chosen by the user for this instance of the node. Empty indicates no given name, in which case the reference name is displayed to the user in italics.
#[serde(default)] #[serde(default)]
pub display_name: String, pub display_name: String,
/// Stores metadata to override the properties in the properties panel for each input. These can either be generated automatically based on the type, or with a custom function. /// Stores metadata to override the properties in the properties panel for each input. These can either be generated automatically based on the type, or with a custom function.
@@ -6463,7 +6475,9 @@ mod network_interface_tests {
async fn copy_isolated_node() { async fn copy_isolated_node() {
let mut editor = EditorTestUtils::create(); let mut editor = EditorTestUtils::create();
editor.new_document().await; editor.new_document().await;
let rectangle = editor.create_node_by_name("Rectangle").await; let rectangle = editor
.create_node_by_name(DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::rectangle::IDENTIFIER))
.await;
editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![rectangle] }).await; editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![rectangle] }).await;
let frontend_messages = editor.handle_message(NodeGraphMessage::Copy).await; let frontend_messages = editor.handle_message(NodeGraphMessage::Copy).await;
let serialized_nodes = frontend_messages let serialized_nodes = frontend_messages
@@ -19,13 +19,14 @@ pub struct DocumentNodePersistentMetadataInputNames {
pub network_metadata: Option<NodeNetworkMetadata>, pub network_metadata: Option<NodeNetworkMetadata>,
} }
impl From<DocumentNodePersistentMetadataInputNames> for DocumentNodePersistentMetadata { impl From<DocumentNodePersistentMetadataInputNames> for DocumentNodePersistentMetadataPropertiesRow {
fn from(old: DocumentNodePersistentMetadataInputNames) -> Self { fn from(old: DocumentNodePersistentMetadataInputNames) -> Self {
DocumentNodePersistentMetadata { DocumentNodePersistentMetadataPropertiesRow {
input_metadata: Vec::new(),
reference: old.reference, reference: old.reference,
input_properties: Vec::new(),
display_name: old.display_name, display_name: old.display_name,
output_names: old.output_names, output_names: old.output_names,
has_primary_output: old.has_primary_output,
locked: old.locked, locked: old.locked,
pinned: old.pinned, pinned: old.pinned,
node_type_metadata: old.node_type_metadata, node_type_metadata: old.node_type_metadata,
@@ -60,7 +61,7 @@ pub struct PropertiesRow {
pub input_description: String, pub input_description: String,
} }
impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersistentMetadata { impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersistentMetadataHasPrimaryOutput {
fn from(old: DocumentNodePersistentMetadataPropertiesRow) -> Self { fn from(old: DocumentNodePersistentMetadataPropertiesRow) -> Self {
let mut input_metadata = Vec::new(); let mut input_metadata = Vec::new();
for properties_row in old.input_properties { for properties_row in old.input_properties {
@@ -74,11 +75,12 @@ impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersisten
..Default::default() ..Default::default()
}) })
} }
DocumentNodePersistentMetadata { DocumentNodePersistentMetadataHasPrimaryOutput {
reference: old.reference, reference: old.reference,
display_name: old.display_name, display_name: old.display_name,
input_metadata: Vec::new(), input_metadata: Vec::new(),
output_names: old.output_names, output_names: old.output_names,
has_primary_output: old.has_primary_output,
locked: old.locked, locked: old.locked,
pinned: old.pinned, pinned: old.pinned,
node_type_metadata: old.node_type_metadata, node_type_metadata: old.node_type_metadata,
@@ -104,9 +106,9 @@ pub struct DocumentNodePersistentMetadataHasPrimaryOutput {
pub network_metadata: Option<NodeNetworkMetadata>, pub network_metadata: Option<NodeNetworkMetadata>,
} }
impl From<DocumentNodePersistentMetadataHasPrimaryOutput> for DocumentNodePersistentMetadata { impl From<DocumentNodePersistentMetadataHasPrimaryOutput> for DocumentNodePersistentMetadataStringReference {
fn from(old: DocumentNodePersistentMetadataHasPrimaryOutput) -> Self { fn from(old: DocumentNodePersistentMetadataHasPrimaryOutput) -> Self {
DocumentNodePersistentMetadata { DocumentNodePersistentMetadataStringReference {
reference: old.reference, reference: old.reference,
display_name: old.display_name, display_name: old.display_name,
input_metadata: old.input_metadata, input_metadata: old.input_metadata,
@@ -119,6 +121,49 @@ impl From<DocumentNodePersistentMetadataHasPrimaryOutput> for DocumentNodePersis
} }
} }
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct DocumentNodePersistentMetadataStringReference {
pub reference: Option<String>,
#[serde(default)]
pub display_name: String,
pub input_metadata: Vec<InputMetadata>,
pub output_names: Vec<String>,
#[serde(default)]
pub locked: bool,
#[serde(default)]
pub pinned: bool,
pub node_type_metadata: NodeTypePersistentMetadata,
pub network_metadata: Option<NodeNetworkMetadata>,
}
impl From<DocumentNodePersistentMetadataStringReference> for DocumentNodePersistentMetadata {
fn from(mut old: DocumentNodePersistentMetadataStringReference) -> Self {
if let Some(metadata) = old.network_metadata.as_mut() {
metadata.persistent_metadata.reference = old.reference;
}
DocumentNodePersistentMetadata {
display_name: old.display_name,
input_metadata: old.input_metadata,
output_names: old.output_names,
locked: old.locked,
pinned: old.pinned,
node_type_metadata: old.node_type_metadata,
network_metadata: old.network_metadata,
}
}
}
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum DocumentNodePersistentMetadataVersioned {
// Newest first
Current(DocumentNodePersistentMetadata),
StringReference(DocumentNodePersistentMetadataStringReference),
HasPrimaryOutput(DocumentNodePersistentMetadataHasPrimaryOutput),
PropertiesRow(DocumentNodePersistentMetadataPropertiesRow),
InputNames(DocumentNodePersistentMetadataInputNames),
}
pub fn deserialize_node_persistent_metadata<'de, D>(deserializer: D) -> Result<DocumentNodePersistentMetadata, D::Error> pub fn deserialize_node_persistent_metadata<'de, D>(deserializer: D) -> Result<DocumentNodePersistentMetadata, D::Error>
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
@@ -126,17 +171,31 @@ where
use serde::Deserialize; use serde::Deserialize;
let value = Value::deserialize(deserializer)?; let value = Value::deserialize(deserializer)?;
if let Ok(document) = serde_json::from_value::<DocumentNodePersistentMetadataHasPrimaryOutput>(value.clone()) {
return Ok(document.into()); let versioned_document = serde_json::from_value::<DocumentNodePersistentMetadataVersioned>(value).map_err(serde::de::Error::custom)?;
let current: DocumentNodePersistentMetadata = match versioned_document {
DocumentNodePersistentMetadataVersioned::Current(v) => v,
DocumentNodePersistentMetadataVersioned::StringReference(v) => {
let v: DocumentNodePersistentMetadataStringReference = v;
v.into()
}
DocumentNodePersistentMetadataVersioned::HasPrimaryOutput(v) => {
let v: DocumentNodePersistentMetadataStringReference = v.into();
v.into()
}
DocumentNodePersistentMetadataVersioned::PropertiesRow(v) => {
let v: DocumentNodePersistentMetadataHasPrimaryOutput = v.into();
let v: DocumentNodePersistentMetadataStringReference = v.into();
v.into()
}
DocumentNodePersistentMetadataVersioned::InputNames(v) => {
let v: DocumentNodePersistentMetadataPropertiesRow = v.into();
let v: DocumentNodePersistentMetadataHasPrimaryOutput = v.into();
let v: DocumentNodePersistentMetadataStringReference = v.into();
v.into()
}
}; };
if let Ok(document) = serde_json::from_value::<DocumentNodePersistentMetadata>(value.clone()) {
return Ok(document); Ok(current)
};
if let Ok(document) = serde_json::from_value::<DocumentNodePersistentMetadataPropertiesRow>(value.clone()) {
return Ok(document.into());
};
match serde_json::from_value::<DocumentNodePersistentMetadataInputNames>(value.clone()) {
Ok(document) => Ok(document.into()),
Err(e) => Err(serde::de::Error::custom(e)),
}
} }
@@ -1,5 +1,6 @@
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use super::network_interface::NodeNetworkInterface; use super::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
use glam::DVec2; use glam::DVec2;
use graph_craft::document::{NodeId, NodeNetwork}; use graph_craft::document::{NodeId, NodeNetwork};
@@ -34,7 +35,7 @@ impl serde::Serialize for JsRawBuffer {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct LayerPanelEntry { pub struct LayerPanelEntry {
pub id: NodeId, pub id: NodeId,
pub reference: String, pub reference: DefinitionIdentifier,
pub alias: String, pub alias: String,
#[serde(rename = "inSelectedNetwork")] #[serde(rename = "inSelectedNetwork")]
pub in_selected_network: bool, pub in_selected_network: bool,
@@ -2,6 +2,7 @@ use super::network_interface::NodeNetworkInterface;
use crate::consts::{ROTATE_INCREMENT, SCALE_INCREMENT}; use crate::consts::{ROTATE_INCREMENT, SCALE_INCREMENT};
use crate::messages::portfolio::document::graph_operation::transform_utils; use crate::messages::portfolio::document::graph_operation::transform_utils;
use crate::messages::portfolio::document::graph_operation::utility_types::{ModifyInputsContext, TransformIn}; use crate::messages::portfolio::document::graph_operation::utility_types::{ModifyInputsContext, TransformIn};
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::shape_editor::ShapeState;
@@ -53,7 +54,7 @@ impl OriginalTransforms {
/// Gets the transform from the most downstream transform node /// Gets the transform from the most downstream transform node
fn get_layer_transform(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DAffine2> { fn get_layer_transform(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DAffine2> {
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain("Transform", layer, network_interface)?; let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain(&DefinitionIdentifier::Network("Transform".into()), layer, network_interface)?;
let document_node = network_interface.document_network().nodes.get(&transform_node_id)?; let document_node = network_interface.document_network().nodes.get(&transform_node_id)?;
Some(transform_utils::get_current_transform(&document_node.inputs)) Some(transform_utils::get_current_transform(&document_node.inputs))
@@ -1,7 +1,7 @@
// TODO: Eventually remove this document upgrade code // TODO: Eventually remove this document upgrade code
// This file contains lots of hacky code for upgrading old documents to the new format // This file contains lots of hacky code for upgrading old documents to the new format
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type, resolve_network_node_type, resolve_proto_node_type};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate, OutputConnector}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate, OutputConnector};
use crate::messages::prelude::DocumentMessageHandler; use crate::messages::prelude::DocumentMessageHandler;
@@ -734,12 +734,7 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
}, },
NodeReplacement { NodeReplacement {
node: graphene_std::vector::auto_tangents::IDENTIFIER, node: graphene_std::vector::auto_tangents::IDENTIFIER,
aliases: &[ aliases: &["graphene_core::vector::vector_nodes::AutoTangentsNode", "graphene_core::vector::AutoTangentsNode"],
"graphene_core::vector::vector_nodes::AutoTangentsNode",
"graphene_core::vector::AutoTangentsNode",
"graphene_core::vector::GenerateHandlesNode",
"graphene_core::vector::RemoveHandlesNode",
],
}, },
NodeReplacement { NodeReplacement {
node: graphene_std::vector::bevel::IDENTIFIER, node: graphene_std::vector::bevel::IDENTIFIER,
@@ -919,7 +914,11 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
}, },
NodeReplacement { NodeReplacement {
node: graphene_std::vector::sample_polyline::IDENTIFIER, node: graphene_std::vector::sample_polyline::IDENTIFIER,
aliases: &["graphene_core::vector::SamplePolylineNode"], aliases: &[
"graphene_core::vector::SamplePolylineNode",
"graphene_core::vector::SamplePointsNode",
"graphene_core::vector::vector_nodes::SamplePointsNode",
],
}, },
NodeReplacement { NodeReplacement {
node: graphene_std::vector::separate_subpaths::IDENTIFIER, node: graphene_std::vector::separate_subpaths::IDENTIFIER,
@@ -1000,7 +999,7 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
for (node_id, node, network_path) in network.recursive_nodes() { for (node_id, node, network_path) in network.recursive_nodes() {
if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation { if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation {
let node_path_without_type_args = protonode_id.name.split('<').next(); let node_path_without_type_args = protonode_id.as_str().split('<').next();
if let Some(new) = node_path_without_type_args.and_then(|node_path| replacements.get(node_path)) { if let Some(new) = node_path_without_type_args.and_then(|node_path| replacements.get(node_path)) {
let mut default_template = NodeTemplate::default(); let mut default_template = NodeTemplate::default();
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.clone()); default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.clone());
@@ -1023,8 +1022,8 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
} }
fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> { fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> {
if reset_node_definitions_on_open && let Some(Some(reference)) = document.network_interface.reference(node_id, network_path) { if reset_node_definitions_on_open && let Some(reference) = document.network_interface.reference(node_id, network_path) {
let node_definition = resolve_document_node_type(reference)?; let node_definition = resolve_document_node_type(&reference)?;
document.network_interface.replace_implementation(node_id, network_path, &mut node_definition.default_node_template()); document.network_interface.replace_implementation(node_id, network_path, &mut node_definition.default_node_template());
} }
@@ -1034,14 +1033,13 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Only nodes that have not been modified and still refer to a definition can be updated // Only nodes that have not been modified and still refer to a definition can be updated
let reference = document.network_interface.reference(node_id, network_path).cloned().flatten()?; let reference = document.network_interface.reference(node_id, network_path)?;
let reference = &reference;
let inputs_count = node.inputs.len(); let inputs_count = node.inputs.len();
// Upgrade Stroke node to reorder parameters and add "Align" and "Paint Order" (#2644) // Upgrade Stroke node to reorder parameters and add "Align" and "Paint Order" (#2644)
if reference == "Stroke" && inputs_count == 8 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER) && inputs_count == 8 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
let align_input = NodeInput::value(TaggedValue::StrokeAlign(StrokeAlign::Center), false); let align_input = NodeInput::value(TaggedValue::StrokeAlign(StrokeAlign::Center), false);
@@ -1059,19 +1057,18 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document.network_interface.set_input(&InputConnector::node(*node_id, 9), old_inputs[4].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 9), old_inputs[4].clone(), network_path);
} }
// Rename the old "Splines from Points" node to "Spline" and upgrade it to the new "Spline" node
if reference == "Splines from Points" {
document.network_interface.set_reference(node_id, network_path, Some("Spline".to_string()));
}
// Upgrade the old "Spline" node to the new "Spline" node // Upgrade the old "Spline" node to the new "Spline" node
if reference == "Spline" { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::spline::IDENTIFIER)
|| reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::SplineNode"))
|| reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::SplineNode"))
|| reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::SplinesFromPointsNode"))
{
// Retrieve the proto node identifier and verify it is the old "Spline" node, otherwise skip it if this is the new "Spline" node // Retrieve the proto node identifier and verify it is the old "Spline" node, otherwise skip it if this is the new "Spline" node
let identifier = document let identifier = document
.network_interface .network_interface
.implementation(node_id, network_path) .implementation(node_id, network_path)
.and_then(|implementation| implementation.get_proto_node()); .and_then(|implementation| implementation.get_proto_node());
if identifier.map(|identifier| &identifier.name) != Some(&"graphene_core::vector::generator_nodes::SplineNode".into()) { if identifier.map(|identifier| identifier.as_str()) != Some("graphene_core::vector::generator_nodes::SplineNode") {
return None; return None;
} }
@@ -1096,7 +1093,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
}; };
// Get the "Path" node definition and fill it in with the Vector path and default vector modification // Get the "Path" node definition and fill it in with the Vector path and default vector modification
let Some(path_node_type) = resolve_document_node_type("Path") else { let Some(path_node_type) = resolve_network_node_type("Path") else {
log::error!("Path node does not exist."); log::error!("Path node does not exist.");
return None; return None;
}; };
@@ -1106,7 +1103,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
]); ]);
// Get the "Spline" node definition and wire it up with the "Path" node as input // Get the "Spline" node definition and wire it up with the "Path" node as input
let Some(spline_node_type) = resolve_document_node_type("Spline") else { let Some(spline_node_type) = resolve_proto_node_type(graphene_std::vector::spline::IDENTIFIER) else {
log::error!("Spline node does not exist."); log::error!("Spline node does not exist.");
return None; return None;
}; };
@@ -1137,8 +1134,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016 // Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016
if reference == "Text" && inputs_count != 11 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER) && inputs_count != 11 {
let mut template = resolve_document_node_type(reference)?.default_node_template(); let mut template: NodeTemplate = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut template); document.network_interface.replace_implementation(node_id, network_path, &mut template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut template)?;
@@ -1212,8 +1209,12 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Upgrade Sine, Cosine, and Tangent nodes to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default // Upgrade Sine, Cosine, and Tangent nodes to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default
if (reference == "Sine" || reference == "Cosine" || reference == "Tangent") && inputs_count == 1 { if inputs_count == 1
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); && (reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::sine::IDENTIFIER)
|| reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::cosine::IDENTIFIER)
|| reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::tangent::IDENTIFIER))
{
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1225,8 +1226,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Upgrade the 'Tangent on Path' node to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default // Upgrade the 'Tangent on Path' node to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default
if (reference == "Tangent on Path") && inputs_count == 4 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::tangent_on_path::IDENTIFIER) && inputs_count == 4 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1241,8 +1242,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Upgrade the Modulo node to include a boolean input for whether the output should be always positive, which was previously not an option // Upgrade the Modulo node to include a boolean input for whether the output should be always positive, which was previously not an option
if reference == "Modulo" && inputs_count == 2 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::modulo::IDENTIFIER) && inputs_count == 2 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1255,8 +1256,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Upgrade the Mirror node to add the `keep_original` boolean input // Upgrade the Mirror node to add the `keep_original` boolean input
if reference == "Mirror" && inputs_count == 3 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::mirror::IDENTIFIER) && inputs_count == 3 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1270,8 +1271,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Upgrade the Mirror node to add the `reference_point` input and change `offset` from `DVec2` to `f64` // Upgrade the Mirror node to add the `reference_point` input and change `offset` from `DVec2` to `f64`
if reference == "Mirror" && inputs_count == 4 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::mirror::IDENTIFIER) && inputs_count == 4 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1293,24 +1294,23 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Upgrade artboard name being passed as hidden value input to "Create Artboard" // Upgrade artboard name being passed as hidden value input to "Create Artboard"
if reference == "Artboard" && reset_node_definitions_on_open { if reference == DefinitionIdentifier::Network("Artboard".into()) && reset_node_definitions_on_open {
let label = document.network_interface.display_name(node_id, network_path); let label = document.network_interface.display_name(node_id, network_path);
document document
.network_interface .network_interface
.set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]); .set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]);
} }
if reference == "Image" && inputs_count == 1 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER) && inputs_count == 1 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
// Insert a new empty input for the image // Insert a new empty input for the image
document.network_interface.add_import(TaggedValue::None, false, 0, "Empty", "", &[*node_id]); document.network_interface.add_import(TaggedValue::None, false, 0, "Empty", "", &[*node_id]);
document.network_interface.set_reference(node_id, network_path, Some("Image".to_string()));
} }
if reference == "Noise Pattern" && inputs_count == 15 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::noise_pattern::IDENTIFIER) && inputs_count == 15 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1323,8 +1323,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
} }
if reference == "Instance on Points" && inputs_count == 2 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::instance_on_points::IDENTIFIER) && inputs_count == 2 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1333,20 +1333,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
} }
if reference == "Morph" && inputs_count == 4 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::brush::brush::brush::IDENTIFIER) && inputs_count == 4 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
// We have removed the last input, so we don't add index 3
}
if reference == "Brush" && inputs_count == 4 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1357,19 +1345,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[3].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[3].clone(), network_path);
} }
if reference == "Flatten Vector Elements" { if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::RemoveHandlesNode")) {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::auto_tangents::IDENTIFIER))?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
document.network_interface.replace_reference_name(node_id, network_path, "Flatten Path".to_string());
}
if reference == "Remove Handles" {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1381,12 +1358,10 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document document
.network_interface .network_interface
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), network_path); .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), network_path);
document.network_interface.replace_reference_name(node_id, network_path, "Auto-Tangents".to_string());
} }
if reference == "Generate Handles" { if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::GenerateHandlesNode")) {
let mut node_template = resolve_document_node_type("Auto-Tangents")?.default_node_template(); let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::auto_tangents::IDENTIFIER))?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1396,12 +1371,10 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document document
.network_interface .network_interface
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(true), false), network_path); .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(true), false), network_path);
document.network_interface.replace_reference_name(node_id, network_path, "Auto-Tangents".to_string());
} }
if reference == "Merge by Distance" && inputs_count == 2 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::merge_by_distance::IDENTIFIER) && inputs_count == 2 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1415,8 +1388,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
); );
} }
if reference == "Spatial Merge by Distance" { if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::SpatialMergeByDistanceNode")) {
let mut node_template = resolve_document_node_type("Merge by Distance")?.default_node_template(); let mut node_template = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::vector::merge_by_distance::IDENTIFIER))?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1428,12 +1401,10 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
NodeInput::value(TaggedValue::MergeByDistanceAlgorithm(graphene_std::vector::misc::MergeByDistanceAlgorithm::Spatial), false), NodeInput::value(TaggedValue::MergeByDistanceAlgorithm(graphene_std::vector::misc::MergeByDistanceAlgorithm::Spatial), false),
network_path, network_path,
); );
document.network_interface.replace_reference_name(node_id, network_path, "Merge by Distance".to_string());
} }
if reference == "Sample Points" && inputs_count == 5 { if reference == DefinitionIdentifier::Network("Sample Points".into()) && inputs_count == 5 {
let mut node_template = resolve_document_node_type("Sample Polyline")?.default_node_template(); let mut node_template = resolve_network_node_type("Sample Polyline")?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1447,12 +1418,10 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[2].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[2].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 6), old_inputs[4].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 6), old_inputs[4].clone(), network_path);
document.network_interface.replace_reference_name(node_id, network_path, "Sample Polyline".to_string());
} }
// Make the "Quantity" parameter a u32 instead of f64 // Make the "Quantity" parameter a u32 instead of f64
if reference == "Sample Polyline" { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::sample_polyline::IDENTIFIER) {
// Get the inputs, obtain the quantity value, and put the inputs back // Get the inputs, obtain the quantity value, and put the inputs back
let quantity_value = document let quantity_value = document
.network_interface .network_interface
@@ -1467,8 +1436,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Make the "Grid" node, if its input of index 3 is a DVec2 for "angles" instead of a u32 for the "columns" input that now succeeds "angles", move the angle to index 5 (after "columns" and "rows") // Make the "Grid" node, if its input of index 3 is a DVec2 for "angles" instead of a u32 for the "columns" input that now succeeds "angles", move the angle to index 5 (after "columns" and "rows")
if reference == "Grid" && inputs_count == 6 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::grid::IDENTIFIER) && inputs_count == 6 {
let node_definition = resolve_document_node_type(reference)?; let node_definition = resolve_document_node_type(&reference)?;
let mut new_node_template = node_definition.default_node_template(); let mut new_node_template = node_definition.default_node_template();
let mut current_node_template = document.network_interface.create_node_template(node_id, network_path)?; let mut current_node_template = document.network_interface.create_node_template(node_id, network_path)?;
@@ -1497,8 +1466,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Add the "Depth" parameter to the "Instance Index" node // Add the "Depth" parameter to the "Instance Index" node
if reference == "Instance Index" && inputs_count == 0 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::instance_index::IDENTIFIER) && inputs_count == 0 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
document.network_interface.set_display_name(node_id, "Instance Index".to_string(), network_path); document.network_interface.set_display_name(node_id, "Instance Index".to_string(), network_path);
@@ -1510,8 +1479,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
// Migrate the Transform node to use degrees instead of radians // Migrate the Transform node to use degrees instead of radians
if reference == "Transform" && node.inputs.get(6).is_none() { if reference == DefinitionIdentifier::Network("Transform".into()) && node.inputs.get(6).is_none() {
let mut node_template = resolve_document_node_type("Transform")?.default_node_template(); let mut node_template = resolve_network_node_type("Transform")?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template); document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
@@ -1547,7 +1516,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
} }
NodeInput::Node { .. } => { NodeInput::Node { .. } => {
// Construct a new Multiply node for converting from degrees to radians // Construct a new Multiply node for converting from degrees to radians
let Some(multiply_node) = resolve_document_node_type("Multiply") else { let Some(multiply_node) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::multiply::IDENTIFIER)) else {
log::error!("Could not get multiply node from definition when upgrading transform"); log::error!("Could not get multiply node from definition when upgrading transform");
return None; return None;
}; };
@@ -1587,18 +1556,21 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
// The old version would zip the source and target table rows, interpoleating each pair together. // The old version would zip the source and target table rows, interpoleating each pair together.
// The migrated version will instead deeply flatten both merged tables and morph sequentially between all source vectors and all target vector elements. // The migrated version will instead deeply flatten both merged tables and morph sequentially between all source vectors and all target vector elements.
// This migration assumes most usages didn't involve multiple parallel vector elements, and instead morphed from a single source to a single target vector element. // This migration assumes most usages didn't involve multiple parallel vector elements, and instead morphed from a single source to a single target vector element.
if reference == "Morph" && inputs_count == 3 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::morph::IDENTIFIER) && (inputs_count == 3 || inputs_count == 4) {
// Old signature: // 3 inputs - old signature (#3405):
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction) -> Table<Vector> { ... } // async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction) -> Table<Vector> { ... }
// //
// 4 inputs - even older signature (commit 80b8df8d4298b6669f124b929ce61bfabfc44e41):
// async fn morph(_: impl Ctx, source: Table<Vector>, #[expose] target: Table<Vector>, #[default(0.5)] time: Fraction, #[min(0.)] start_index: IntegerCount) -> Table<Vector> { ... }
//
// New signature: // New signature:
// async fn morph<I: IntoGraphicTable>(_: impl Ctx, content: #[implementations(Table<Graphic>, Table<Vector>)] content: I, progression: Progression) -> Table<Vector> { ... } // async fn morph<I: IntoGraphicTable>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>)] content: I, progression: Progression) -> Table<Vector> { ... }
let mut node_template = resolve_document_node_type(reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
// Create a new Merge node // Create a new Merge node
let Some(merge_node_type) = resolve_document_node_type("Merge") else { let Some(merge_node_type) = resolve_network_node_type("Merge") else {
log::error!("Could not get merge node from definition when upgrading morph"); log::error!("Could not get merge node from definition when upgrading morph");
return None; return None;
}; };
@@ -1631,7 +1603,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
// Add context features to nodes that don't have them (fine-grained context caching migration) // Add context features to nodes that don't have them (fine-grained context caching migration)
if node.context_features == graphene_std::ContextDependencies::default() if node.context_features == graphene_std::ContextDependencies::default()
&& let Some(reference) = document.network_interface.reference(node_id, network_path).cloned().flatten() && let Some(reference) = document.network_interface.reference(node_id, network_path).clone()
&& let Some(node_definition) = resolve_document_node_type(&reference) && let Some(node_definition) = resolve_document_node_type(&reference)
{ {
let context_features = node_definition.node_template.document_node.context_features; let context_features = node_definition.node_template.document_node.context_features;
@@ -1678,7 +1650,7 @@ mod tests {
NODE_REPLACEMENTS.iter().for_each(|node| { NODE_REPLACEMENTS.iter().for_each(|node| {
*hashmap.entry(node.node.clone()).or_default() += 1; *hashmap.entry(node.node.clone()).or_default() += 1;
}); });
let duplicates = hashmap.iter().filter(|(_, count)| **count > 1).map(|(node, _)| &node.name).collect::<Vec<_>>(); let duplicates = hashmap.iter().filter(|(_, count)| **count > 1).map(|(node, _)| node.as_str()).collect::<Vec<_>>();
if !duplicates.is_empty() { if !duplicates.is_empty() {
panic!("Duplicate entries in `NODE_REPLACEMENTS`: {duplicates:?}"); panic!("Duplicate entries in `NODE_REPLACEMENTS`: {duplicates:?}");
} }
@@ -12,8 +12,7 @@ use crate::messages::input_mapper::utility_types::macros::{action_shortcut, acti
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::DocumentMessageContext; use crate::messages::portfolio::document::DocumentMessageContext;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions; use crate::messages::portfolio::document::node_graph::document_node_definitions::{self, resolve_network_node_type};
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
use crate::messages::portfolio::document::utility_types::network_interface::OutputConnector; use crate::messages::portfolio::document::utility_types::network_interface::OutputConnector;
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
@@ -522,7 +521,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// Ensure each node has the metadata for its inputs // Ensure each node has the metadata for its inputs
for (node_id, node, path) in document.network_interface.document_network().clone().recursive_nodes() { for (node_id, node, path) in document.network_interface.document_network().clone().recursive_nodes() {
document.network_interface.validate_input_metadata(node_id, node, &path); document.network_interface.validate_input_metadata(node_id, node, &path);
document.network_interface.validate_display_name_metadata(node_id, &path);
document.network_interface.validate_output_names(node_id, node, &path); document.network_interface.validate_output_names(node_id, node, &path);
} }
@@ -664,7 +662,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let mut layers = Vec::new(); let mut layers = Vec::new();
for (_, new_vector, transform) in data { for (_, new_vector, transform) in data {
let Some(node_type) = resolve_document_node_type("Path") else { let Some(node_type) = resolve_network_node_type("Path") else {
error!("Path node does not exist"); error!("Path node does not exist");
continue; continue;
}; };
@@ -2,6 +2,7 @@ use crate::consts::GIZMO_HIDE_THRESHOLD;
use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD}; use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD};
use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message; use crate::messages::message::Message;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector}; use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector};
use crate::messages::prelude::FrontendMessage; use crate::messages::prelude::FrontendMessage;
@@ -331,7 +332,8 @@ impl PointRadiusHandle {
fn calculate_snap_radii(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, radius_index: usize) -> Vec<f64> { fn calculate_snap_radii(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, radius_index: usize) -> Vec<f64> {
let mut snap_radii = Vec::new(); let mut snap_radii = Vec::new();
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star") else { let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::star::IDENTIFIER))
else {
return snap_radii; return snap_radii;
}; };
@@ -449,7 +451,8 @@ impl PointRadiusHandle {
} }
fn check_if_radius_flipped(&mut self, original_radius: f64, new_radius: f64, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, radius_index: usize) { fn check_if_radius_flipped(&mut self, original_radius: f64, new_radius: f64, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, radius_index: usize) {
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star") else { let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::star::IDENTIFIER))
else {
return; return;
}; };
@@ -1,5 +1,5 @@
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions; use crate::messages::portfolio::document::node_graph::document_node_definitions::{self, DefinitionIdentifier};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeNetworkInterface, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeNetworkInterface, NodeTemplate};
use crate::messages::prelude::*; use crate::messages::prelude::*;
@@ -24,9 +24,13 @@ pub fn find_spline(document: &DocumentMessageHandler, layer: LayerNodeIdentifier
document document
.network_interface .network_interface
.upstream_flow_back_from_nodes([layer.to_node()].to_vec(), &[], FlowType::HorizontalFlow) .upstream_flow_back_from_nodes([layer.to_node()].to_vec(), &[], FlowType::HorizontalFlow)
.map(|node_id| (document.network_interface.reference(&node_id, &[]).unwrap(), node_id)) .map(|node_id| (document.network_interface.reference(&node_id, &[]), node_id))
.take_while(|(reference, _)| reference.as_ref().is_some_and(|node_ref| node_ref != "Path")) .take_while(|(reference, _)| reference.as_ref().is_some_and(|node_ref| node_ref != &DefinitionIdentifier::Network("Path".into())))
.find(|(reference, _)| reference.as_ref().is_some_and(|node_ref| node_ref == "Spline")) .find(|(reference, _)| {
reference
.as_ref()
.is_some_and(|node_ref| *node_ref == DefinitionIdentifier::ProtoNode(graphene_std::vector::spline::IDENTIFIER))
})
.map(|node| node.1) .map(|node| node.1)
} }
@@ -73,7 +77,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
// Merge the inputs of the two layers // Merge the inputs of the two layers
let merge_node_id = NodeId::new(); let merge_node_id = NodeId::new();
let merge_node = document_node_definitions::resolve_document_node_type("Merge") let merge_node = document_node_definitions::resolve_network_node_type("Merge")
.expect("Failed to create merge node") .expect("Failed to create merge node")
.default_node_template(); .default_node_template();
responses.add(NodeGraphMessage::InsertNode { responses.add(NodeGraphMessage::InsertNode {
@@ -99,7 +103,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
// Add a Flatten Path node after the merge // Add a Flatten Path node after the merge
let flatten_node_id = NodeId::new(); let flatten_node_id = NodeId::new();
let flatten_node = document_node_definitions::resolve_document_node_type("Flatten Path") let flatten_node = document_node_definitions::resolve_proto_node_type(graphene_std::vector::flatten_path::IDENTIFIER)
.expect("Failed to create flatten node") .expect("Failed to create flatten node")
.default_node_template(); .default_node_template();
responses.add(NodeGraphMessage::InsertNode { responses.add(NodeGraphMessage::InsertNode {
@@ -113,7 +117,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
// Add a path node after the flatten node // Add a path node after the flatten node
let path_node_id = NodeId::new(); let path_node_id = NodeId::new();
let path_node = document_node_definitions::resolve_document_node_type("Path") let path_node = document_node_definitions::resolve_network_node_type("Path")
.expect("Failed to create path node") .expect("Failed to create path node")
.default_node_template(); .default_node_template();
responses.add(NodeGraphMessage::InsertNode { responses.add(NodeGraphMessage::InsertNode {
@@ -128,7 +132,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
// Add a Spline node after the Path node if both the layers we are merging is spline. // Add a Spline node after the Path node if both the layers we are merging is spline.
if current_and_other_layer_is_spline { if current_and_other_layer_is_spline {
let spline_node_id = NodeId::new(); let spline_node_id = NodeId::new();
let spline_node = document_node_definitions::resolve_document_node_type("Spline") let spline_node = document_node_definitions::resolve_proto_node_type(graphene_std::vector::spline::IDENTIFIER)
.expect("Failed to create Spline node") .expect("Failed to create Spline node")
.default_node_template(); .default_node_template();
responses.add(NodeGraphMessage::InsertNode { responses.add(NodeGraphMessage::InsertNode {
@@ -143,7 +147,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
// Add a transform node to ensure correct tooling modifications // Add a transform node to ensure correct tooling modifications
let transform_node_id = NodeId::new(); let transform_node_id = NodeId::new();
let transform_node = document_node_definitions::resolve_document_node_type("Transform") let transform_node = document_node_definitions::resolve_network_node_type("Transform")
.expect("Failed to create transform node") .expect("Failed to create transform node")
.default_node_template(); .default_node_template();
responses.add(NodeGraphMessage::InsertNode { responses.add(NodeGraphMessage::InsertNode {
@@ -251,7 +255,7 @@ pub fn new_custom(id: NodeId, nodes: Vec<(NodeId, NodeTemplate)>, parent: LayerN
pub fn get_origin(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DVec2> { pub fn get_origin(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DVec2> {
use graphene_std::transform_nodes::transform::*; use graphene_std::transform_nodes::transform::*;
if let TaggedValue::DVec2(origin) = NodeGraphLayer::new(layer, network_interface).find_input("Transform", TranslationInput::INDEX)? { if let TaggedValue::DVec2(origin) = NodeGraphLayer::new(layer, network_interface).find_input(&DefinitionIdentifier::Network("Transform".into()), TranslationInput::INDEX)? {
Some(*origin) Some(*origin)
} else { } else {
None None
@@ -273,7 +277,7 @@ pub fn get_viewport_center(layer: LayerNodeIdentifier, network_interface: &NodeN
pub fn get_gradient(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Gradient> { pub fn get_gradient(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Gradient> {
let fill_index = 1; let fill_index = 1;
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER))?;
let TaggedValue::Fill(Fill::Gradient(gradient)) = inputs.get(fill_index)?.as_value()? else { let TaggedValue::Fill(Fill::Gradient(gradient)) = inputs.get(fill_index)?.as_value()? else {
return None; return None;
}; };
@@ -284,7 +288,7 @@ pub fn get_gradient(layer: LayerNodeIdentifier, network_interface: &NodeNetworkI
pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Color> { pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Color> {
let fill_index = 1; let fill_index = 1;
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER))?;
let TaggedValue::Fill(Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else { let TaggedValue::Fill(Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else {
return None; return None;
}; };
@@ -293,7 +297,7 @@ pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetwor
/// Get the current blend mode of a layer from the closest "Blending" node. /// Get the current blend mode of a layer from the closest "Blending" node.
pub fn get_blend_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<BlendMode> { pub fn get_blend_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<BlendMode> {
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Blending")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::blending_nodes::blending::IDENTIFIER))?;
let TaggedValue::BlendMode(blend_mode) = inputs.get(1)?.as_value()? else { let TaggedValue::BlendMode(blend_mode) = inputs.get(1)?.as_value()? else {
return None; return None;
}; };
@@ -309,7 +313,7 @@ pub fn get_blend_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetwor
/// ///
/// With those limitations in mind, the intention of this function is to show just the value already present in an upstream Opacity node so that value can be directly edited. /// With those limitations in mind, the intention of this function is to show just the value already present in an upstream Opacity node so that value can be directly edited.
pub fn get_opacity(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> { pub fn get_opacity(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Blending")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::blending_nodes::blending::IDENTIFIER))?;
let TaggedValue::F64(opacity) = inputs.get(2)?.as_value()? else { let TaggedValue::F64(opacity) = inputs.get(2)?.as_value()? else {
return None; return None;
}; };
@@ -317,7 +321,7 @@ pub fn get_opacity(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn
} }
pub fn get_clip_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<bool> { pub fn get_clip_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<bool> {
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Blending")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::blending_nodes::blending::IDENTIFIER))?;
let TaggedValue::Bool(clip) = inputs.get(4)?.as_value()? else { let TaggedValue::Bool(clip) = inputs.get(4)?.as_value()? else {
return None; return None;
}; };
@@ -325,7 +329,7 @@ pub fn get_clip_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetwork
} }
pub fn get_fill(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> { pub fn get_fill(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Blending")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::blending_nodes::blending::IDENTIFIER))?;
let TaggedValue::F64(fill) = inputs.get(3)?.as_value()? else { let TaggedValue::F64(fill) = inputs.get(3)?.as_value()? else {
return None; return None;
}; };
@@ -333,56 +337,56 @@ pub fn get_fill(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInter
} }
pub fn get_fill_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_fill_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Fill") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::fill::IDENTIFIER))
} }
pub fn get_circle_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_circle_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Circle") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::circle::IDENTIFIER))
} }
pub fn get_ellipse_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_ellipse_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Ellipse") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::ellipse::IDENTIFIER))
} }
pub fn get_line_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_line_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Line") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::line::IDENTIFIER))
} }
pub fn get_polygon_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_polygon_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Regular Polygon") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::regular_polygon::IDENTIFIER))
} }
pub fn get_rectangle_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_rectangle_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Rectangle") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::rectangle::IDENTIFIER))
} }
pub fn get_star_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_star_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Star") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::star::IDENTIFIER))
} }
pub fn get_arc_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_arc_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Arc") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::arc::IDENTIFIER))
} }
pub fn get_arrow_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_arrow_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Arrow") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::arrow::IDENTIFIER))
} }
pub fn get_spiral_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_spiral_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Spiral") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::spiral::IDENTIFIER))
} }
pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER))
} }
pub fn get_grid_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> { pub fn get_grid_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Grid") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::grid::IDENTIFIER))
} }
/// Gets properties from the Text node /// Gets properties from the Text node
pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> { pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> {
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER))?;
let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None }; let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None };
let Some(TaggedValue::Font(font)) = &inputs[2].as_value() else { return None }; let Some(TaggedValue::Font(font)) = &inputs[2].as_value() else { return None };
@@ -409,7 +413,7 @@ pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInter
pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> { pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
let weight_node_input_index = graphene_std::vector::stroke::WeightInput::INDEX; let weight_node_input_index = graphene_std::vector::stroke::WeightInput::INDEX;
if let TaggedValue::F64(width) = NodeGraphLayer::new(layer, network_interface).find_input("Stroke", weight_node_input_index)? { if let TaggedValue::F64(width) = NodeGraphLayer::new(layer, network_interface).find_input(&DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER), weight_node_input_index)? {
Some(*width) Some(*width)
} else { } else {
None None
@@ -417,8 +421,8 @@ pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetw
} }
/// Checks if a specified layer uses an upstream node matching the given name. /// Checks if a specified layer uses an upstream node matching the given name.
pub fn is_layer_fed_by_node_of_name(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, node_name: &str) -> bool { pub fn is_layer_fed_by_node_of_name(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, identifier: &DefinitionIdentifier) -> bool {
NodeGraphLayer::new(layer, network_interface).find_node_inputs(node_name).is_some() NodeGraphLayer::new(layer, network_interface).find_node_inputs(identifier).is_some()
} }
/// An immutable reference to a layer within the document node graph for easy access. /// An immutable reference to a layer within the document node graph for easy access.
@@ -443,19 +447,19 @@ impl<'a> NodeGraphLayer<'a> {
} }
/// Node id of a node if it exists in the layer's primary flow /// Node id of a node if it exists in the layer's primary flow
pub fn upstream_node_id_from_name(&self, node_name: &str) -> Option<NodeId> { pub fn upstream_node_id_from_name(&self, identifier: &DefinitionIdentifier) -> Option<NodeId> {
self.horizontal_layer_flow() self.horizontal_layer_flow()
.find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| *reference == Some(node_name.to_string()))) .find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| reference == *identifier))
} }
/// Node id of a visible node if it exists in the layer's primary flow until another layer /// Node id of a visible node if it exists in the layer's primary flow until another layer
pub fn upstream_visible_node_id_from_name_in_layer(&self, node_name: &str) -> Option<NodeId> { pub fn upstream_visible_node_id_from_name_in_layer(&self, identifier: &DefinitionIdentifier) -> Option<NodeId> {
// `.skip(1)` is used to skip self // `.skip(1)` is used to skip self
self.horizontal_layer_flow() self.horizontal_layer_flow()
.skip(1) .skip(1)
.take_while(|node_id| !self.network_interface.is_layer(node_id, &[])) .take_while(|node_id| !self.network_interface.is_layer(node_id, &[]))
.filter(|node_id| self.network_interface.is_visible(node_id, &[])) .filter(|node_id| self.network_interface.is_visible(node_id, &[]))
.find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| *reference == Some(node_name.to_string()))) .find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| reference == *identifier))
} }
/// Node id of a protonode if it exists in the layer's primary flow /// Node id of a protonode if it exists in the layer's primary flow
@@ -471,19 +475,19 @@ impl<'a> NodeGraphLayer<'a> {
} }
/// Find all of the inputs of a specific node within the layer's primary flow, up until the next layer is reached. /// Find all of the inputs of a specific node within the layer's primary flow, up until the next layer is reached.
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> { pub fn find_node_inputs(&self, identifier: &DefinitionIdentifier) -> Option<&'a Vec<NodeInput>> {
// `.skip(1)` is used to skip self // `.skip(1)` is used to skip self
self.horizontal_layer_flow() self.horizontal_layer_flow()
.skip(1) .skip(1)
.take_while(|node_id| !self.network_interface.is_layer(node_id, &[])) .take_while(|node_id| !self.network_interface.is_layer(node_id, &[]))
.find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| *reference == Some(node_name.to_string()))) .find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| reference == *identifier))
.and_then(|node_id| self.network_interface.document_network().nodes.get(&node_id).map(|node| &node.inputs)) .and_then(|node_id| self.network_interface.document_network().nodes.get(&node_id).map(|node| &node.inputs))
} }
/// Find a specific input of a node within the layer's primary flow /// Find a specific input of a node within the layer's primary flow
pub fn find_input(&self, node_name: &str, index: usize) -> Option<&'a TaggedValue> { pub fn find_input(&self, identifier: &DefinitionIdentifier, index: usize) -> Option<&'a TaggedValue> {
// TODO: Find a better way to accept a node input rather than using its index (which is quite unclear and fragile) // TODO: Find a better way to accept a node input rather than using its index (which is quite unclear and fragile)
self.find_node_inputs(node_name)?.get(index)?.as_value() self.find_node_inputs(identifier)?.get(index)?.as_value()
} }
/// Check if a layer is a raster layer /// Check if a layer is a raster layer
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey; use super::shape_utility::ShapeToolModifierKey;
use super::*; use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState};
@@ -133,7 +133,7 @@ pub struct Arc;
impl Arc { impl Arc {
pub fn create_node(arc_type: ArcType) -> NodeTemplate { pub fn create_node(arc_type: ArcType) -> NodeTemplate {
let node_type = resolve_document_node_type("Arc").expect("Ellipse node does not exist"); let node_type = resolve_proto_node_type(graphene_std::vector::generator_nodes::arc::IDENTIFIER).expect("Ellipse node does not exist");
node_type.node_template_input_override([ node_type.node_template_input_override([
None, None,
Some(NodeInput::value(TaggedValue::F64(0.5), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false)),
@@ -1,6 +1,6 @@
use super::shape_utility::ShapeToolModifierKey; use super::shape_utility::ShapeToolModifierKey;
use super::*; use super::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -16,7 +16,7 @@ pub struct Arrow;
impl Arrow { impl Arrow {
pub fn create_node(document: &DocumentMessageHandler, drag_start: DVec2) -> NodeTemplate { pub fn create_node(document: &DocumentMessageHandler, drag_start: DVec2) -> NodeTemplate {
let node_type = resolve_document_node_type("Arrow").expect("Arrow node does not exist"); let node_type = resolve_proto_node_type(graphene_std::vector_nodes::arrow::IDENTIFIER).expect("Arrow node does not exist");
let viewport_pos = document.metadata().document_to_viewport.transform_point2(drag_start); let viewport_pos = document.metadata().document_to_viewport.transform_point2(drag_start);
node_type.node_template_input_override([ node_type.node_template_input_override([
None, None,
@@ -1,5 +1,5 @@
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -82,7 +82,7 @@ pub struct Circle;
impl Circle { impl Circle {
pub fn create_node() -> NodeTemplate { pub fn create_node() -> NodeTemplate {
let node_type = resolve_document_node_type("Circle").expect("Circle can't be found"); let node_type = resolve_proto_node_type(graphene_std::vector::generator_nodes::circle::IDENTIFIER).expect("Circle can't be found");
node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(0.), false))]) node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(0.), false))])
} }
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey; use super::shape_utility::ShapeToolModifierKey;
use super::*; use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
@@ -16,7 +16,7 @@ pub struct Ellipse;
impl Ellipse { impl Ellipse {
pub fn create_node() -> NodeTemplate { pub fn create_node() -> NodeTemplate {
let node_type = resolve_document_node_type("Ellipse").expect("Ellipse node can't be found"); let node_type = resolve_proto_node_type(graphene_std::vector::generator_nodes::ellipse::IDENTIFIER).expect("Ellipse node can't be found");
node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(0.5), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false))]) node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(0.5), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false))])
} }
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey; use super::shape_utility::ShapeToolModifierKey;
use super::*; use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -86,7 +86,7 @@ pub struct Grid;
impl Grid { impl Grid {
pub fn create_node(grid_type: GridType) -> NodeTemplate { pub fn create_node(grid_type: GridType) -> NodeTemplate {
let node_type = resolve_document_node_type("Grid").expect("Grid can't be found"); let node_type = resolve_proto_node_type(graphene_std::vector::generator_nodes::grid::IDENTIFIER).expect("Grid can't be found");
node_type.node_template_input_override([ node_type.node_template_input_override([
None, None,
Some(NodeInput::value(TaggedValue::GridType(grid_type), false)), Some(NodeInput::value(TaggedValue::GridType(grid_type), false)),
@@ -1,6 +1,6 @@
use super::shape_utility::ShapeToolModifierKey; use super::shape_utility::ShapeToolModifierKey;
use crate::consts::{BOUNDS_SELECT_THRESHOLD, LINE_ROTATE_SNAP_ANGLE}; use crate::consts::{BOUNDS_SELECT_THRESHOLD, LINE_ROTATE_SNAP_ANGLE};
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type};
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -37,7 +37,8 @@ pub struct Line;
impl Line { impl Line {
pub fn create_node(document: &DocumentMessageHandler, drag_start: DVec2) -> NodeTemplate { pub fn create_node(document: &DocumentMessageHandler, drag_start: DVec2) -> NodeTemplate {
let node_type = resolve_document_node_type("Line").expect("Line node can't be found"); let identifier = DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::line::IDENTIFIER);
let node_type = resolve_document_node_type(&identifier).expect("Line node can't be found");
node_type.node_template_input_override([ node_type.node_template_input_override([
None, None,
Some(NodeInput::value(TaggedValue::DVec2(document.metadata().document_to_viewport.transform_point2(drag_start)), false)), Some(NodeInput::value(TaggedValue::DVec2(document.metadata().document_to_viewport.transform_point2(drag_start)), false)),
@@ -88,7 +89,8 @@ impl Line {
.selected_nodes() .selected_nodes()
.selected_visible_and_unlocked_layers(&document.network_interface) .selected_visible_and_unlocked_layers(&document.network_interface)
.filter_map(|layer| { .filter_map(|layer| {
let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line")?; let node_inputs =
NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::line::IDENTIFIER))?;
let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else { let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else {
return None; return None;
@@ -171,7 +173,7 @@ fn generate_line(tool_data: &mut ShapeToolData, snap_data: SnapData, lock_angle:
} }
pub fn clicked_on_line_endpoints(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, shape_tool_data: &mut ShapeToolData) -> bool { pub fn clicked_on_line_endpoints(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, shape_tool_data: &mut ShapeToolData) -> bool {
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line") else { let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::line::IDENTIFIER)) else {
return false; return false;
}; };
@@ -216,7 +218,7 @@ mod test_line_tool {
.selected_nodes() .selected_nodes()
.selected_visible_and_unlocked_layers(network_interface) .selected_visible_and_unlocked_layers(network_interface)
.filter_map(|layer| { .filter_map(|layer| {
let node_inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Line")?; let node_inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::line::IDENTIFIER))?;
let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else { let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else {
return None; return None;
}; };
@@ -1,7 +1,7 @@
use super::shape_utility::{ShapeToolModifierKey, update_radius_sign}; use super::shape_utility::{ShapeToolModifierKey, update_radius_sign};
use super::*; use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type};
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -109,7 +109,8 @@ pub struct Polygon;
impl Polygon { impl Polygon {
pub fn create_node(vertices: u32) -> NodeTemplate { pub fn create_node(vertices: u32) -> NodeTemplate {
let node_type = resolve_document_node_type("Regular Polygon").expect("Regular Polygon can't be found"); let identifier = DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER);
let node_type = resolve_document_node_type(&identifier).expect("Regular Polygon can't be found");
node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::U32(vertices), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false))]) node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::U32(vertices), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false))])
} }
@@ -167,8 +168,8 @@ impl Polygon {
}; };
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface) let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface)
.find_node_inputs("Regular Polygon") .find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER))
.or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star")) .or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::star::IDENTIFIER)))
else { else {
return; return;
}; };
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey; use super::shape_utility::ShapeToolModifierKey;
use super::*; use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
@@ -16,7 +16,7 @@ pub struct Rectangle;
impl Rectangle { impl Rectangle {
pub fn create_node() -> NodeTemplate { pub fn create_node() -> NodeTemplate {
let node_type = resolve_document_node_type("Rectangle").expect("Rectangle node can't be found"); let node_type = resolve_proto_node_type(graphene_std::vector::generator_nodes::rectangle::IDENTIFIER).expect("Rectangle node can't be found");
node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(1.), false)), Some(NodeInput::value(TaggedValue::F64(1.), false))]) node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(1.), false)), Some(NodeInput::value(TaggedValue::F64(1.), false))])
} }
@@ -2,6 +2,7 @@ use super::ShapeToolData;
use crate::consts::{ARC_SWEEP_GIZMO_RADIUS, ARC_SWEEP_GIZMO_TEXT_HEIGHT}; use crate::consts::{ARC_SWEEP_GIZMO_RADIUS, ARC_SWEEP_GIZMO_TEXT_HEIGHT};
use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message; use crate::messages::message::Message;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector; use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
@@ -157,8 +158,15 @@ pub fn update_radius_sign(end: DVec2, start: DVec2, layer: LayerNodeIdentifier,
let sign_num = if end[1] > start[1] { 1. } else { -1. }; let sign_num = if end[1] > start[1] { 1. } else { -1. };
let new_layer = NodeGraphLayer::new(layer, &document.network_interface); let new_layer = NodeGraphLayer::new(layer, &document.network_interface);
if new_layer.find_input("Regular Polygon", 1).unwrap_or(&TaggedValue::U32(0)).to_u32() % 2 == 1 { if new_layer
let Some(polygon_node_id) = new_layer.upstream_node_id_from_name("Regular Polygon") else { return }; .find_input(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER), 1)
.unwrap_or(&TaggedValue::U32(0))
.to_u32()
% 2 == 1
{
let Some(polygon_node_id) = new_layer.upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::regular_polygon::IDENTIFIER)) else {
return;
};
responses.add(NodeGraphMessage::SetInput { responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(polygon_node_id, 2), input_connector: InputConnector::node(polygon_node_id, 2),
@@ -167,8 +175,15 @@ pub fn update_radius_sign(end: DVec2, start: DVec2, layer: LayerNodeIdentifier,
return; return;
} }
if new_layer.find_input("Star", 1).unwrap_or(&TaggedValue::U32(0)).to_u32() % 2 == 1 { if new_layer
let Some(star_node_id) = new_layer.upstream_node_id_from_name("Star") else { return }; .find_input(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::star::IDENTIFIER), 1)
.unwrap_or(&TaggedValue::U32(0))
.to_u32()
% 2 == 1
{
let Some(star_node_id) = new_layer.upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::star::IDENTIFIER)) else {
return;
};
responses.add(NodeGraphMessage::SetInput { responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(star_node_id, 2), input_connector: InputConnector::node(star_node_id, 2),
@@ -237,7 +252,7 @@ pub fn anchor_overlays(document: &DocumentMessageHandler, overlay_context: &mut
/// Extract the node input values of Star. /// Extract the node input values of Star.
/// Returns an option of (sides, radius1, radius2). /// Returns an option of (sides, radius1, radius2).
pub fn extract_star_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(u32, f64, f64)> { pub fn extract_star_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(u32, f64, f64)> {
let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs("Star")?; let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::star::IDENTIFIER))?;
let (Some(&TaggedValue::U32(sides)), Some(&TaggedValue::F64(radius_1)), Some(&TaggedValue::F64(radius_2))) = let (Some(&TaggedValue::U32(sides)), Some(&TaggedValue::F64(radius_1)), Some(&TaggedValue::F64(radius_2))) =
(node_inputs.get(1)?.as_value(), node_inputs.get(2)?.as_value(), node_inputs.get(3)?.as_value()) (node_inputs.get(1)?.as_value(), node_inputs.get(2)?.as_value(), node_inputs.get(3)?.as_value())
@@ -251,7 +266,8 @@ pub fn extract_star_parameters(layer: Option<LayerNodeIdentifier>, document: &Do
/// Extract the node input values of Polygon. /// Extract the node input values of Polygon.
/// Returns an option of (sides, radius). /// Returns an option of (sides, radius).
pub fn extract_polygon_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(u32, f64)> { pub fn extract_polygon_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(u32, f64)> {
let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs("Regular Polygon")?; let node_inputs =
NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER))?;
let (Some(&TaggedValue::U32(n)), Some(&TaggedValue::F64(radius))) = (node_inputs.get(1)?.as_value(), node_inputs.get(2)?.as_value()) else { let (Some(&TaggedValue::U32(n)), Some(&TaggedValue::F64(radius))) = (node_inputs.get(1)?.as_value(), node_inputs.get(2)?.as_value()) else {
return None; return None;
@@ -263,7 +279,7 @@ pub fn extract_polygon_parameters(layer: Option<LayerNodeIdentifier>, document:
/// Extract the node input values of an arc. /// Extract the node input values of an arc.
/// Returns an option of (radius, start angle, sweep angle, arc type). /// Returns an option of (radius, start angle, sweep angle, arc type).
pub fn extract_arc_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(f64, f64, f64, ArcType)> { pub fn extract_arc_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(f64, f64, f64, ArcType)> {
let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs("Arc")?; let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::arc::IDENTIFIER))?;
let (Some(&TaggedValue::F64(radius)), Some(&TaggedValue::F64(start_angle)), Some(&TaggedValue::F64(sweep_angle)), Some(&TaggedValue::ArcType(arc_type))) = ( let (Some(&TaggedValue::F64(radius)), Some(&TaggedValue::F64(start_angle)), Some(&TaggedValue::F64(sweep_angle)), Some(&TaggedValue::ArcType(arc_type))) = (
node_inputs.get(1)?.as_value(), node_inputs.get(1)?.as_value(),
@@ -303,7 +319,7 @@ pub fn arc_end_points_ignore_layer(radius: f64, start_angle: f64, sweep_angle: f
/// Extract the node input values of Circle. /// Extract the node input values of Circle.
/// Returns an option of (radius). /// Returns an option of (radius).
pub fn extract_circle_radius(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<f64> { pub fn extract_circle_radius(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<f64> {
let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Circle")?; let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::circle::IDENTIFIER))?;
let Some(&TaggedValue::F64(radius)) = node_inputs.get(1)?.as_value() else { let Some(&TaggedValue::F64(radius)) = node_inputs.get(1)?.as_value() else {
return None; return None;
@@ -499,7 +515,7 @@ pub fn calculate_arc_text_transform(angle: f64, offset_angle: f64, center: DVec2
pub fn extract_grid_parameters(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<(GridType, DVec2, u32, u32, DVec2)> { pub fn extract_grid_parameters(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<(GridType, DVec2, u32, u32, DVec2)> {
use graphene_std::vector::generator_nodes::grid::*; use graphene_std::vector::generator_nodes::grid::*;
let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Grid")?; let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::grid::IDENTIFIER))?;
let (Some(&TaggedValue::GridType(grid_type)), Some(&TaggedValue::DVec2(spacing)), Some(&TaggedValue::U32(columns)), Some(&TaggedValue::U32(rows)), Some(&TaggedValue::DVec2(angles))) = ( let (Some(&TaggedValue::GridType(grid_type)), Some(&TaggedValue::DVec2(spacing)), Some(&TaggedValue::U32(columns)), Some(&TaggedValue::U32(rows)), Some(&TaggedValue::DVec2(angles))) = (
node_inputs.get(GridTypeInput::INDEX)?.as_value(), node_inputs.get(GridTypeInput::INDEX)?.as_value(),
@@ -1,6 +1,6 @@
use super::*; use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer};
@@ -24,7 +24,8 @@ impl Spiral {
SpiralType::Logarithmic => 0.1, SpiralType::Logarithmic => 0.1,
}; };
let node_type = resolve_document_node_type("Spiral").expect("Spiral node can't be found"); let identifier = DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::spiral::IDENTIFIER);
let node_type = resolve_document_node_type(&identifier).expect("Spiral node can't be found");
node_type.node_template_input_override([ node_type.node_template_input_override([
None, None,
Some(NodeInput::value(TaggedValue::SpiralType(spiral_type), false)), Some(NodeInput::value(TaggedValue::SpiralType(spiral_type), false)),
@@ -62,7 +63,8 @@ impl Spiral {
return; return;
}; };
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Spiral") else { let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::spiral::IDENTIFIER))
else {
return; return;
}; };
@@ -93,7 +95,8 @@ impl Spiral {
pub fn update_turns(decrease: bool, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { pub fn update_turns(decrease: bool, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
use graphene_std::vector::generator_nodes::spiral::*; use graphene_std::vector::generator_nodes::spiral::*;
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Spiral") else { let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::spiral::IDENTIFIER))
else {
return; return;
}; };
@@ -1,7 +1,7 @@
use super::shape_utility::{ShapeToolModifierKey, update_radius_sign}; use super::shape_utility::{ShapeToolModifierKey, update_radius_sign};
use super::*; use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type};
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -109,7 +109,8 @@ pub struct Star;
impl Star { impl Star {
pub fn create_node(vertices: u32) -> NodeTemplate { pub fn create_node(vertices: u32) -> NodeTemplate {
let node_type = resolve_document_node_type("Star").expect("Star node can't be found"); let identifier = DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::star::IDENTIFIER);
let node_type = resolve_document_node_type(&identifier).expect("Star node can't be found");
node_type.node_template_input_override([ node_type.node_template_input_override([
None, None,
Some(NodeInput::value(TaggedValue::U32(vertices), false)), Some(NodeInput::value(TaggedValue::U32(vertices), false)),
@@ -1,6 +1,7 @@
use super::snapping::{SnapCandidatePoint, SnapData, SnapManager}; use super::snapping::{SnapCandidatePoint, SnapData, SnapManager};
use super::transformation_cage::{BoundingBoxManager, SizeSnapData}; use super::transformation_cage::{BoundingBoxManager, SizeSnapData};
use crate::consts::ROTATE_INCREMENT; use crate::consts::ROTATE_INCREMENT;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{NodeNetworkInterface, OutputConnector}; use crate::messages::portfolio::document::utility_types::network_interface::{NodeNetworkInterface, OutputConnector};
use crate::messages::portfolio::document::utility_types::transformation::Selected; use crate::messages::portfolio::document::utility_types::transformation::Selected;
@@ -581,7 +582,7 @@ pub fn make_path_editable_is_allowed(network_interface: &mut NodeNetworkInterfac
// Must not already have an existing Path node, in the right-most part of the layer chain, which has an empty set of modifications // Must not already have an existing Path node, in the right-most part of the layer chain, which has an empty set of modifications
// (otherwise users could repeatedly keep running this command and stacking up empty Path nodes) // (otherwise users could repeatedly keep running this command and stacking up empty Path nodes)
if let Some(TaggedValue::VectorModification(modifications)) = NodeGraphLayer::new(first_layer, network_interface).find_input("Path", 1) if let Some(TaggedValue::VectorModification(modifications)) = NodeGraphLayer::new(first_layer, network_interface).find_input(&DefinitionIdentifier::Network("Path".into()), 1)
&& modifications.as_ref() == &VectorModification::default() && modifications.as_ref() == &VectorModification::default()
{ {
return None; return None;
@@ -1,7 +1,7 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::DEFAULT_BRUSH_SIZE; use crate::consts::DEFAULT_BRUSH_SIZE;
use crate::messages::portfolio::document::graph_operation::transform_utils::get_current_transform; use crate::messages::portfolio::document::graph_operation::transform_utils::get_current_transform;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_network_node_type};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::FlowType; use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
@@ -316,7 +316,7 @@ impl BrushToolData {
continue; continue;
}; };
if *reference == Some("Brush".to_string()) && node_id != layer.to_node() { if reference == DefinitionIdentifier::Network("Brush".into()) && node_id != layer.to_node() {
let points_input = node.inputs.get(1)?; let points_input = node.inputs.get(1)?;
let Some(TaggedValue::BrushStrokes(strokes)) = points_input.as_value() else { continue }; let Some(TaggedValue::BrushStrokes(strokes)) = points_input.as_value() else { continue };
self.strokes.clone_from(strokes); self.strokes.clone_from(strokes);
@@ -324,7 +324,7 @@ impl BrushToolData {
return Some(layer); return Some(layer);
} }
if *reference == Some("Transform".to_string()) { if reference == DefinitionIdentifier::Network("Transform".into()) {
self.transform = get_current_transform(&node.inputs) * self.transform; self.transform = get_current_transform(&node.inputs) * self.transform;
} }
} }
@@ -475,7 +475,7 @@ impl Fsm for BrushToolFsmState {
fn new_brush_layer(document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier { fn new_brush_layer(document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
responses.add(DocumentMessage::DeselectAllLayers); responses.add(DocumentMessage::DeselectAllLayers);
let brush_node = resolve_document_node_type("Brush").expect("Brush node does not exist").default_node_template(); let brush_node = resolve_network_node_type("Brush").expect("Brush node does not exist").default_node_template();
let id = NodeId::new(); let id = NodeId::new();
responses.add(GraphOperationMessage::NewCustomLayer { responses.add(GraphOperationMessage::NewCustomLayer {
@@ -1,6 +1,6 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::DEFAULT_STROKE_WIDTH; use crate::consts::DEFAULT_STROKE_WIDTH;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_network_node_type;
use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays; use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@@ -285,7 +285,7 @@ impl Fsm for FreehandToolFsmState {
let parent = document.new_layer_bounding_artboard(input, viewport); let parent = document.new_layer_bounding_artboard(input, viewport);
let node_type = resolve_document_node_type("Path").expect("Path node does not exist"); let node_type = resolve_network_node_type("Path").expect("Path node does not exist");
let node = node_type.default_node_template(); let node = node_type.default_node_template();
let nodes = vec![(NodeId(0), node)]; let nodes = vec![(NodeId(0), node)];
@@ -402,7 +402,7 @@ mod test_freehand {
.filter_map(|layer| { .filter_map(|layer| {
let graph_layer = NodeGraphLayer::new(layer, &document.network_interface); let graph_layer = NodeGraphLayer::new(layer, &document.network_interface);
// Only get layers with path nodes // Only get layers with path nodes
let _ = graph_layer.upstream_visible_node_id_from_name_in_layer("Path")?; let _ = graph_layer.upstream_visible_node_id_from_name_in_layer(&DefinitionIdentifier::Network("Path".into()))?;
let vector = document.network_interface.compute_modified_vector(layer)?; let vector = document.network_interface.compute_modified_vector(layer)?;
let transform = document.metadata().transform_to_viewport(layer); let transform = document.metadata().transform_to_viewport(layer);
@@ -7,7 +7,7 @@ use crate::consts::{
use crate::messages::clipboard::utility_types::ClipboardContent; use crate::messages::clipboard::utility_types::ClipboardContent;
use crate::messages::input_mapper::utility_types::macros::action_shortcut_manual; use crate::messages::input_mapper::utility_types::macros::action_shortcut_manual;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_network_node_type;
use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments}; use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments};
use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext}; use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext};
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
@@ -2826,7 +2826,7 @@ impl Fsm for PathToolFsmState {
let layer = if shape_editor.selected_shape_state.contains_key(&layer) { let layer = if shape_editor.selected_shape_state.contains_key(&layer) {
layer layer
} else { } else {
let Some(node_type) = resolve_document_node_type("Path") else { let Some(node_type) = resolve_network_node_type("Path") else {
error!("Could not resolve node type for Path"); error!("Could not resolve node type for Path");
continue; continue;
}; };
@@ -1,7 +1,7 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::{COLOR_OVERLAY_BLUE, DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE}; use crate::consts::{COLOR_OVERLAY_BLUE, DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, SEGMENT_OVERLAY_SIZE};
use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_network_node_type;
use crate::messages::portfolio::document::overlays::utility_functions::path_overlays; use crate::messages::portfolio::document::overlays::utility_functions::path_overlays;
use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext}; use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@@ -1282,7 +1282,7 @@ impl PenToolData {
} }
// New path layer // New path layer
let node_type = resolve_document_node_type("Path").expect("Path node does not exist"); let node_type = resolve_network_node_type("Path").expect("Path node does not exist");
let nodes = vec![(NodeId(0), node_type.default_node_template())]; let nodes = vec![(NodeId(0), node_type.default_node_template())];
let parent = document.new_layer_bounding_artboard(input, viewport); let parent = document.new_layer_bounding_artboard(input, viewport);
@@ -4,6 +4,7 @@ use super::tool_prelude::*;
use crate::consts::*; use crate::consts::*;
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition; use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType}; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
@@ -624,7 +625,7 @@ impl Fsm for SelectToolFsmState {
let layer_to_viewport = document.metadata().transform_to_viewport(layer); let layer_to_viewport = document.metadata().transform_to_viewport(layer);
overlay_context.outline(document.metadata().layer_with_free_points_outline(layer), layer_to_viewport, None); overlay_context.outline(document.metadata().layer_with_free_points_outline(layer), layer_to_viewport, None);
if is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") { if is_layer_fed_by_node_of_name(layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)) {
let transformed_quad = layer_to_viewport * text_bounding_box(layer, document, &persistent_data.font_cache); let transformed_quad = layer_to_viewport * text_bounding_box(layer, document, &persistent_data.font_cache);
overlay_context.dashed_quad(transformed_quad, None, None, Some(7.), Some(5.), None); overlay_context.dashed_quad(transformed_quad, None, None, Some(7.), Some(5.), None);
} }
@@ -1579,7 +1580,7 @@ impl Fsm for SelectToolFsmState {
if let Some(layer) = selected_layers.next() { if let Some(layer) = selected_layers.next() {
// Check that only one layer is selected // Check that only one layer is selected
if selected_layers.next().is_none() && is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") { if selected_layers.next().is_none() && is_layer_fed_by_node_of_name(layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)) {
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text }); responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text });
responses.add(TextToolMessage::EditSelected); responses.add(TextToolMessage::EditSelected);
} }
@@ -1933,7 +1934,7 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer:
/// Called when a double click on a layer in deep select mode. /// Called when a double click on a layer in deep select mode.
/// If the layer is text, the text tool is selected. /// If the layer is text, the text tool is selected.
fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, responses: &mut VecDeque<Message>) { fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, responses: &mut VecDeque<Message>) {
if is_layer_fed_by_node_of_name(layer, network_interface, "Text") { if is_layer_fed_by_node_of_name(layer, network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)) {
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text }); responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text });
responses.add(TextToolMessage::EditSelected); responses.add(TextToolMessage::EditSelected);
} }
@@ -1,7 +1,7 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::{DEFAULT_STROKE_WIDTH, DRAG_THRESHOLD, PATH_JOIN_THRESHOLD, SNAP_POINT_TOLERANCE}; use crate::consts::{DEFAULT_STROKE_WIDTH, DRAG_THRESHOLD, PATH_JOIN_THRESHOLD, SNAP_POINT_TOLERANCE};
use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::node_graph::document_node_definitions::{resolve_network_node_type, resolve_proto_node_type};
use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays; use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@@ -388,9 +388,9 @@ impl Fsm for SplineToolFsmState {
let parent = document.new_layer_bounding_artboard(input, viewport); let parent = document.new_layer_bounding_artboard(input, viewport);
let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist"); let path_node_type = resolve_network_node_type("Path").expect("Path node does not exist");
let path_node = path_node_type.default_node_template(); let path_node = path_node_type.default_node_template();
let spline_node_type = resolve_document_node_type("Spline").expect("Spline node does not exist"); let spline_node_type = resolve_proto_node_type(graphene_std::vector::spline::IDENTIFIER).expect("Spline node does not exist");
let spline_node = spline_node_type.node_template_input_override([Some(NodeInput::node(NodeId(1), 0))]); let spline_node = spline_node_type.node_template_input_override([Some(NodeInput::node(NodeId(1), 0))]);
let nodes = vec![(NodeId(1), path_node), (NodeId(0), spline_node)]; let nodes = vec![(NodeId(1), path_node), (NodeId(0), spline_node)];
@@ -3,6 +3,7 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_RED, DRAG_THRESHOLD}; use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_RED, DRAG_THRESHOLD};
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector; use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
@@ -512,7 +513,7 @@ impl TextToolData {
document document
.metadata() .metadata()
.all_layers() .all_layers()
.filter(|&layer| is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text")) .filter(|&layer| is_layer_fed_by_node_of_name(layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)))
.find(|&layer| { .find(|&layer| {
let transformed_quad = document.metadata().transform_to_viewport(layer) * text_bounding_box(layer, document, font_cache); let transformed_quad = document.metadata().transform_to_viewport(layer) * text_bounding_box(layer, document, font_cache);
let mouse = DVec2::new(input.mouse.position.x, input.mouse.position.y); let mouse = DVec2::new(input.mouse.position.x, input.mouse.position.y);
@@ -541,7 +542,7 @@ fn can_edit_selected(document: &DocumentMessageHandler) -> Option<LayerNodeIdent
return None; return None;
} }
if !is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") { if !is_layer_fed_by_node_of_name(layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)) {
return None; return None;
} }
@@ -610,7 +611,7 @@ impl Fsm for TextToolFsmState {
// TODO: implement bounding box for multiple layers // TODO: implement bounding box for multiple layers
let selected = document.network_interface.selected_nodes(); let selected = document.network_interface.selected_nodes();
let mut all_layers = selected.selected_visible_and_unlocked_layers(&document.network_interface); let mut all_layers = selected.selected_visible_and_unlocked_layers(&document.network_interface);
let layer = all_layers.find(|layer| is_layer_fed_by_node_of_name(*layer, &document.network_interface, "Text")); let layer = all_layers.find(|layer| is_layer_fed_by_node_of_name(*layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)));
let bounds = layer.map(|layer| text_bounding_box(layer, document, font_cache)); let bounds = layer.map(|layer| text_bounding_box(layer, document, font_cache));
let layer_transform = layer.map(|layer| document.metadata().transform_to_viewport(layer)).unwrap_or(DAffine2::IDENTITY); let layer_transform = layer.map(|layer| document.metadata().transform_to_viewport(layer)).unwrap_or(DAffine2::IDENTITY);
@@ -671,7 +672,7 @@ impl Fsm for TextToolFsmState {
let selected = document.network_interface.selected_nodes(); let selected = document.network_interface.selected_nodes();
let mut all_selected = selected.selected_visible_and_unlocked_layers(&document.network_interface); let mut all_selected = selected.selected_visible_and_unlocked_layers(&document.network_interface);
let selected = all_selected.find(|layer| is_layer_fed_by_node_of_name(*layer, &document.network_interface, "Text")); let selected = all_selected.find(|layer| is_layer_fed_by_node_of_name(*layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)));
if dragging_bounds.is_some() { if dragging_bounds.is_some() {
responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::StartTransaction);
@@ -710,7 +711,7 @@ impl Fsm for TextToolFsmState {
// This ensures the cursor only changes if a layer is selected // This ensures the cursor only changes if a layer is selected
let selected = document.network_interface.selected_nodes(); let selected = document.network_interface.selected_nodes();
let mut all_selected = selected.selected_visible_and_unlocked_layers(&document.network_interface); let mut all_selected = selected.selected_visible_and_unlocked_layers(&document.network_interface);
let layer = all_selected.find(|&layer| is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text")); let layer = all_selected.find(|&layer| is_layer_fed_by_node_of_name(layer, &document.network_interface, &DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER)));
let mut cursor = tool_data let mut cursor = tool_data
.bounding_box_manager .bounding_box_manager
@@ -754,6 +754,7 @@ fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &D
mod test_transform_layer { mod test_transform_layer {
use crate::messages::portfolio::document::graph_operation::transform_utils; use crate::messages::portfolio::document::graph_operation::transform_utils;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
use crate::messages::prelude::Message; use crate::messages::prelude::Message;
use crate::messages::tool::transform_layer::transform_layer_message_handler::VectorModificationType; use crate::messages::tool::transform_layer::transform_layer_message_handler::VectorModificationType;
@@ -766,7 +767,7 @@ mod test_transform_layer {
let document = editor.active_document(); let document = editor.active_document();
let network_interface = &document.network_interface; let network_interface = &document.network_interface;
let _responses: VecDeque<Message> = VecDeque::new(); let _responses: VecDeque<Message> = VecDeque::new();
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain("Transform", layer, network_interface)?; let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain(&DefinitionIdentifier::Network("Transform".into()), layer, network_interface)?;
let document_node = network_interface.document_network().nodes.get(&transform_node_id)?; let document_node = network_interface.document_network().nodes.get(&transform_node_id)?;
Some(transform_utils::get_current_transform(&document_node.inputs)) Some(transform_utils::get_current_transform(&document_node.inputs))
} }
+4 -2
View File
@@ -2,6 +2,7 @@ use crate::application::Editor;
use crate::application::set_uuid_seed; use crate::application::set_uuid_seed;
use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys; use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition}; use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition};
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::utility_types::Platform; use crate::messages::portfolio::utility_types::Platform;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::Key; use crate::messages::tool::tool_messages::tool_prelude::Key;
@@ -307,11 +308,11 @@ impl EditorTestUtils {
.await; .await;
} }
pub async fn create_node_by_name(&mut self, name: impl Into<String>) -> NodeId { pub async fn create_node_by_name(&mut self, node_type: DefinitionIdentifier) -> NodeId {
let node_id = NodeId::new(); let node_id = NodeId::new();
self.handle_message(NodeGraphMessage::CreateNodeFromContextMenu { self.handle_message(NodeGraphMessage::CreateNodeFromContextMenu {
node_id: Some(node_id), node_id: Some(node_id),
node_type: name.into(), node_type,
xy: None, xy: None,
add_transaction: true, add_transaction: true,
}) })
@@ -340,6 +341,7 @@ pub mod test_prelude {
pub use crate::float_eq; pub use crate::float_eq;
pub use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys}; pub use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
pub use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; pub use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys;
pub use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
pub use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; pub use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
pub use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; pub use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
pub use crate::messages::prelude::*; pub use crate::messages::prelude::*;
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher, getContext, onMount } from "svelte"; import { createEventDispatcher, getContext, onMount } from "svelte";
import type { FrontendNodeType } from "@graphite/messages"; import type { DefinitionIdentifier, FrontendNodeType } from "@graphite/messages";
import type { NodeGraphState } from "@graphite/state-providers/node-graph"; import type { NodeGraphState } from "@graphite/state-providers/node-graph";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte"; import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
@@ -9,7 +9,7 @@
import TextInput from "@graphite/components/widgets/inputs/TextInput.svelte"; import TextInput from "@graphite/components/widgets/inputs/TextInput.svelte";
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte"; import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
const dispatch = createEventDispatcher<{ selectNodeType: string }>(); const dispatch = createEventDispatcher<{ selectNodeType: DefinitionIdentifier }>();
const nodeGraph = getContext<NodeGraphState>("nodeGraph"); const nodeGraph = getContext<NodeGraphState>("nodeGraph");
// Content // Content
@@ -125,8 +125,8 @@
{disabled} {disabled}
label={nodeType.name} label={nodeType.name}
tooltipLabel={nodeType.name} tooltipLabel={nodeType.name}
tooltipDescription={$nodeGraph.nodeDescriptions.get(nodeType.name)} tooltipDescription={$nodeGraph.nodeDescriptions.get(nodeType.identifier)}
action={() => dispatch("selectNodeType", nodeType.name)} action={() => dispatch("selectNodeType", nodeType.identifier)}
/> />
{/each} {/each}
</details> </details>
+2 -2
View File
@@ -644,7 +644,7 @@
{@html $nodeGraph.thumbnails.get(listing.entry.id)} {@html $nodeGraph.thumbnails.get(listing.entry.id)}
{/if} {/if}
</div> </div>
{#if listing.entry.reference === "Artboard"} {#if listing.entry.reference.type === "Network" && listing.entry.reference.data === "Artboard"}
<IconLabel icon="Artboard" class="layer-type-icon" tooltipLabel="Artboard" /> <IconLabel icon="Artboard" class="layer-type-icon" tooltipLabel="Artboard" />
{/if} {/if}
<LayoutRow class="layer-name" on:dblclick={() => onEditLayerName(listing)}> <LayoutRow class="layer-name" on:dblclick={() => onEditLayerName(listing)}>
@@ -652,7 +652,7 @@
data-text-input data-text-input
type="text" type="text"
value={listing.entry.alias} value={listing.entry.alias}
placeholder={listing.entry.reference} placeholder={listing.entry.reference.data}
disabled={!listing.editingName} disabled={!listing.editingName}
on:blur={() => onEditLayerNameDeselect(listing)} on:blur={() => onEditLayerNameDeselect(listing)}
on:keydown={(e) => e.key === "Escape" && onEditLayerNameDeselect(listing)} on:keydown={(e) => e.key === "Escape" && onEditLayerNameDeselect(listing)}
+9 -16
View File
@@ -4,8 +4,7 @@
import { fade } from "svelte/transition"; import { fade } from "svelte/transition";
import type { Editor } from "@graphite/editor"; import type { Editor } from "@graphite/editor";
import type { IconName } from "@graphite/icons"; import type { DefinitionIdentifier, FrontendGraphInput, FrontendGraphOutput, FrontendNode } from "@graphite/messages";
import type { FrontendGraphInput, FrontendGraphOutput } from "@graphite/messages";
import type { NodeGraphState } from "@graphite/state-providers/node-graph"; import type { NodeGraphState } from "@graphite/state-providers/node-graph";
import NodeCatalog from "@graphite/components/floating-menus/NodeCatalog.svelte"; import NodeCatalog from "@graphite/components/floating-menus/NodeCatalog.svelte";
@@ -13,7 +12,6 @@
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte"; import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte"; import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte"; import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte"; import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
const GRID_COLLAPSE_SPACING = 10; const GRID_COLLAPSE_SPACING = 10;
@@ -102,18 +100,10 @@
return sparse; return sparse;
} }
function nodeIcon(icon?: string): IconName { function createNode(identifier: DefinitionIdentifier) {
if (!icon) return "NodeNodes";
const iconMap: Record<string, IconName> = {
Output: "NodeOutput",
};
return iconMap[icon] || "NodeNodes";
}
function createNode(nodeType: string) {
if ($nodeGraph.contextMenuInformation === undefined) return; if ($nodeGraph.contextMenuInformation === undefined) return;
editor.handle.createNode(nodeType, $nodeGraph.contextMenuInformation.contextMenuCoordinates.x, $nodeGraph.contextMenuInformation.contextMenuCoordinates.y); editor.handle.createNode(identifier, $nodeGraph.contextMenuInformation.contextMenuCoordinates.x, $nodeGraph.contextMenuInformation.contextMenuCoordinates.y);
} }
function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, exposedSecondaryInputs: number, primaryOutputExists: boolean, exposedSecondaryOutputs: number): string { function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, exposedSecondaryInputs: number, primaryOutputExists: boolean, exposedSecondaryOutputs: number): string {
@@ -166,6 +156,10 @@
return `Data Type: ${value.resolvedType}`; return `Data Type: ${value.resolvedType}`;
} }
function nodeNameTooltipLabel(node: FrontendNode): string {
return node.displayName === node.implementationName ? node.displayName : `${node.displayName} (${node.implementationName})`;
}
function validTypesText(value: FrontendGraphInput): string { function validTypesText(value: FrontendGraphInput): string {
const validTypes = value.validTypes.length > 0 ? value.validTypes.map((x) => `• ${x}`).join("\n") : "None"; const validTypes = value.validTypes.length > 0 ? value.validTypes.map((x) => `• ${x}`).join("\n") : "None";
return `Valid Types:\n${validTypes}`; return `Valid Types:\n${validTypes}`;
@@ -501,7 +495,7 @@
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
style:--layer-area-width={layerAreaWidth} style:--layer-area-width={layerAreaWidth}
style:--node-chain-area-left-extension={layerChainWidth !== 0 ? layerChainWidth + 0.5 : 0} style:--node-chain-area-left-extension={layerChainWidth !== 0 ? layerChainWidth + 0.5 : 0}
data-tooltip-label={node.displayName === node.reference || !node.reference ? node.displayName : `${node.displayName} (${node.reference})`} data-tooltip-label={nodeNameTooltipLabel(node)}
data-tooltip-description={` data-tooltip-description={`
${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position.x}, ${node.position.y}).` : ""} ${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position.x}, ${node.position.y}).` : ""}
`.trim()} `.trim()}
@@ -651,7 +645,7 @@
style:--clip-path-id={`url(#${clipPathId})`} style:--clip-path-id={`url(#${clipPathId})`}
style:--data-color={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()})`} style:--data-color={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
data-tooltip-label={node.displayName === node.reference || !node.reference ? node.displayName : `${node.displayName} (${node.reference})`} data-tooltip-label={nodeNameTooltipLabel(node)}
data-tooltip-description={` data-tooltip-description={`
${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position.x}, ${node.position.y}).` : ""} ${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position.x}, ${node.position.y}).` : ""}
`.trim()} `.trim()}
@@ -659,7 +653,6 @@
> >
<!-- Primary row --> <!-- Primary row -->
<div class="primary" class:in-selected-network={$nodeGraph.inSelectedNetwork} class:no-secondary-section={exposedInputsOutputs.length === 0}> <div class="primary" class:in-selected-network={$nodeGraph.inSelectedNetwork} class:no-secondary-section={exposedInputsOutputs.length === 0}>
<IconLabel icon={nodeIcon(node.reference)} />
<!-- TODO: Allow the user to edit the name, just like in the Layers panel --> <!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
<TextLabel>{node.displayName}</TextLabel> <TextLabel>{node.displayName}</TextLabel>
</div> </div>
@@ -24,7 +24,7 @@
</tbody> </tbody>
</table> </table>
<style lang="scss"> <style lang="scss" global>
table:not(.unstyled) { table:not(.unstyled) {
background: var(--color-3-darkgray); background: var(--color-3-darkgray);
border: none; border: none;
@@ -120,6 +120,8 @@
<style lang="scss" global> <style lang="scss" global>
.shortcut-label { .shortcut-label {
align-items: center;
.key-label { .key-label {
display: flex; display: flex;
align-items: center; align-items: center;
+11 -7
View File
@@ -100,12 +100,10 @@ export class UpdateNodeGraphTransform extends JsMessage {
readonly transform!: NodeGraphTransform; readonly transform!: NodeGraphTransform;
} }
const NodeDescriptions = Transform(({ obj }) => new Map(obj.nodeDescriptions));
export class SendUIMetadata extends JsMessage { export class SendUIMetadata extends JsMessage {
@NodeDescriptions @Transform(({ obj }) => new Map(obj.nodeDescriptions))
readonly nodeDescriptions!: Map<string, string>; readonly nodeDescriptions!: Map<DefinitionIdentifier, string>;
@Type(() => FrontendNode)
readonly nodeTypes!: FrontendNodeType[]; readonly nodeTypes!: FrontendNodeType[];
} }
@@ -222,10 +220,12 @@ export class FrontendNode {
readonly canBeLayer!: boolean; readonly canBeLayer!: boolean;
readonly reference!: string | undefined; readonly reference!: DefinitionIdentifier | undefined;
readonly displayName!: string; readonly displayName!: string;
readonly implementationName!: string;
readonly primaryInput!: FrontendGraphInput | undefined; readonly primaryInput!: FrontendGraphInput | undefined;
readonly exposedInputs!: FrontendGraphInput[]; readonly exposedInputs!: FrontendGraphInput[];
@@ -251,6 +251,8 @@ export class FrontendNode {
} }
export class FrontendNodeType { export class FrontendNodeType {
readonly identifier!: DefinitionIdentifier;
readonly name!: string; readonly name!: string;
readonly category!: string; readonly category!: string;
@@ -258,6 +260,8 @@ export class FrontendNodeType {
readonly inputTypes!: string[]; readonly inputTypes!: string[];
} }
export type DefinitionIdentifier = { type: "Network" | "ProtoNode"; data: string };
export class NodeGraphTransform { export class NodeGraphTransform {
readonly scale!: number; readonly scale!: number;
readonly x!: number; readonly x!: number;
@@ -827,7 +831,7 @@ export class UpdateDocumentLayerDetails extends JsMessage {
export class LayerPanelEntry { export class LayerPanelEntry {
id!: bigint; id!: bigint;
reference!: string; reference!: DefinitionIdentifier;
alias!: string; alias!: string;
+2 -1
View File
@@ -9,6 +9,7 @@ import {
type FrontendNode, type FrontendNode,
type FrontendNodeType, type FrontendNodeType,
type WirePath, type WirePath,
type DefinitionIdentifier,
ClearAllNodeGraphWires, ClearAllNodeGraphWires,
SendUIMetadata, SendUIMetadata,
UpdateBox, UpdateBox,
@@ -44,7 +45,7 @@ export function createNodeGraphState(editor: Editor) {
/// The index is the exposed input index. The exports have a first key value of u32::MAX. /// The index is the exposed input index. The exports have a first key value of u32::MAX.
wires: new Map<bigint, Map<number, WirePath>>(), wires: new Map<bigint, Map<number, WirePath>>(),
wirePathInProgress: undefined as WirePath | undefined, wirePathInProgress: undefined as WirePath | undefined,
nodeDescriptions: new Map<string, string>(), nodeDescriptions: new Map<DefinitionIdentifier, string>(),
nodeTypes: [] as FrontendNodeType[], nodeTypes: [] as FrontendNodeType[],
thumbnails: new Map<bigint, string>(), thumbnails: new Map<bigint, string>(),
selected: [] as bigint[], selected: [] as bigint[],
+4 -2
View File
@@ -721,11 +721,13 @@ impl EditorHandle {
/// Creates a new document node in the node graph /// Creates a new document node in the node graph
#[wasm_bindgen(js_name = createNode)] #[wasm_bindgen(js_name = createNode)]
pub fn create_node(&self, node_type: String, x: i32, y: i32) { pub fn create_node(&self, node_type: JsValue, x: i32, y: i32) {
let value: serde_json::Value = serde_wasm_bindgen::from_value(node_type).unwrap();
let id = NodeId::new(); let id = NodeId::new();
let message = NodeGraphMessage::CreateNodeFromContextMenu { let message = NodeGraphMessage::CreateNodeFromContextMenu {
node_id: Some(id), node_id: Some(id),
node_type, node_type: value.into(),
xy: Some((x / 24, y / 24)), xy: Some((x / 24, y / 24)),
add_transaction: true, add_transaction: true,
}; };
+19 -16
View File
@@ -800,7 +800,7 @@ impl NodeNetwork {
let path = node.original_location.path.clone().unwrap_or_default(); let path = node.original_location.path.clone().unwrap_or_default();
// Replace value inputs with dedicated value nodes // Replace value inputs with dedicated value nodes
if node.implementation != DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into()) { if node.implementation != DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::value::ClonedNode")) {
Self::replace_value_inputs_with_nodes(&mut node.inputs, &mut self.nodes, &path, gen_id, map_ids, id); Self::replace_value_inputs_with_nodes(&mut node.inputs, &mut self.nodes, &path, gen_id, map_ids, id);
} }
@@ -842,7 +842,10 @@ impl NodeNetwork {
for (nested_node_id, mut nested_node) in inner_network.nodes.into_iter() { for (nested_node_id, mut nested_node) in inner_network.nodes.into_iter() {
for (nested_input_index, nested_input) in nested_node.clone().inputs.iter().enumerate() { for (nested_input_index, nested_input) in nested_node.clone().inputs.iter().enumerate() {
if let NodeInput::Import { import_index, .. } = nested_input { if let NodeInput::Import { import_index, .. } = nested_input {
let parent_input = node.inputs.get(*import_index).unwrap_or_else(|| panic!("Import index {import_index} should always exist")); let parent_input = node
.inputs
.get(*import_index)
.unwrap_or_else(|| panic!("Import index {import_index} of network node implementation {:?} should always exist", nested_node.implementation));
match *parent_input { match *parent_input {
// If the input to self is a node, connect the corresponding output of the inner network to it // If the input to self is a node, connect the corresponding output of the inner network to it
NodeInput::Node { node_id, output_index } => { NodeInput::Node { node_id, output_index } => {
@@ -936,7 +939,7 @@ impl NodeNetwork {
merged_node_id, merged_node_id,
DocumentNode { DocumentNode {
inputs: vec![NodeInput::Value { tagged_value, exposed }], inputs: vec![NodeInput::Value { tagged_value, exposed }],
implementation: DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::value::ClonedNode")),
original_location, original_location,
..Default::default() ..Default::default()
}, },
@@ -1035,7 +1038,7 @@ impl NodeNetwork {
assert_eq!(output_index, 0); assert_eq!(output_index, 0);
// TODO: check if we can read lambda checking? // TODO: check if we can read lambda checking?
let mut input_node = self.nodes.remove(&node_id).unwrap(); let mut input_node = self.nodes.remove(&node_id).unwrap();
node.implementation = DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into()); node.implementation = DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::value::ClonedNode"));
if let Some(input) = input_node.inputs.get_mut(0) { if let Some(input) = input_node.inputs.get_mut(0) {
*input = match &input { *input = match &input {
NodeInput::Node { .. } => NodeInput::import(generic!(T), 0), NodeInput::Node { .. } => NodeInput::import(generic!(T), 0),
@@ -1152,7 +1155,7 @@ mod test {
NodeId(0), NodeId(0),
DocumentNode { DocumentNode {
inputs: vec![NodeInput::import(concrete!(u32), 0), NodeInput::import(concrete!(u32), 1)], inputs: vec![NodeInput::import(concrete!(u32), 0), NodeInput::import(concrete!(u32), 1)],
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::structural::ConsNode")),
..Default::default() ..Default::default()
}, },
), ),
@@ -1160,7 +1163,7 @@ mod test {
NodeId(1), NodeId(1),
DocumentNode { DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)], inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode("core_types::ops::AddPairNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::ops::AddPairNode")),
..Default::default() ..Default::default()
}, },
), ),
@@ -1182,7 +1185,7 @@ mod test {
NodeId(1), NodeId(1),
DocumentNode { DocumentNode {
inputs: vec![NodeInput::import(concrete!(u32), 0), NodeInput::import(concrete!(u32), 1)], inputs: vec![NodeInput::import(concrete!(u32), 0), NodeInput::import(concrete!(u32), 1)],
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::structural::ConsNode")),
..Default::default() ..Default::default()
}, },
), ),
@@ -1190,7 +1193,7 @@ mod test {
NodeId(2), NodeId(2),
DocumentNode { DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)], inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode("core_types::ops::AddPairNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::ops::AddPairNode")),
..Default::default() ..Default::default()
}, },
), ),
@@ -1263,13 +1266,13 @@ mod test {
let document_node = DocumentNode { let document_node = DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)], inputs: vec![NodeInput::node(NodeId(0), 0)],
call_argument: concrete!(u32), call_argument: concrete!(u32),
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::structural::ConsNode")),
..Default::default() ..Default::default()
}; };
let proto_node = document_node.resolve_proto_node(); let proto_node = document_node.resolve_proto_node();
let reference = ProtoNode { let reference = ProtoNode {
identifier: "core_types::structural::ConsNode".into(), identifier: ProtoNodeIdentifier::new("core_types::structural::ConsNode"),
call_argument: concrete!(u32), call_argument: concrete!(u32),
construction_args: ConstructionArgs::Nodes(vec![NodeId(0)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(0)]),
..Default::default() ..Default::default()
@@ -1286,7 +1289,7 @@ mod test {
( (
NodeId(10), NodeId(10),
ProtoNode { ProtoNode {
identifier: "core_types::structural::ConsNode".into(), identifier: ProtoNodeIdentifier::new("core_types::structural::ConsNode"),
call_argument: concrete!(u32), call_argument: concrete!(u32),
construction_args: ConstructionArgs::Nodes(vec![NodeId(14)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(14)]),
original_location: OriginalLocation { original_location: OriginalLocation {
@@ -1302,7 +1305,7 @@ mod test {
( (
NodeId(11), NodeId(11),
ProtoNode { ProtoNode {
identifier: "core_types::ops::AddPairNode".into(), identifier: ProtoNodeIdentifier::new("core_types::ops::AddPairNode"),
call_argument: concrete!(Context), call_argument: concrete!(Context),
construction_args: ConstructionArgs::Nodes(vec![NodeId(10)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(10)]),
original_location: OriginalLocation { original_location: OriginalLocation {
@@ -1317,7 +1320,7 @@ mod test {
( (
NodeId(14), NodeId(14),
ProtoNode { ProtoNode {
identifier: "core_types::value::ClonedNode".into(), identifier: ProtoNodeIdentifier::new("core_types::value::ClonedNode"),
call_argument: concrete!(core_types::Context), call_argument: concrete!(core_types::Context),
construction_args: ConstructionArgs::Value(TaggedValue::U32(2).into()), construction_args: ConstructionArgs::Value(TaggedValue::U32(2).into()),
original_location: OriginalLocation { original_location: OriginalLocation {
@@ -1351,7 +1354,7 @@ mod test {
DocumentNode { DocumentNode {
inputs: vec![NodeInput::node(NodeId(14), 0)], inputs: vec![NodeInput::node(NodeId(14), 0)],
call_argument: concrete!(u32), call_argument: concrete!(u32),
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::structural::ConsNode")),
original_location: OriginalLocation { original_location: OriginalLocation {
path: Some(vec![NodeId(1), NodeId(0)]), path: Some(vec![NodeId(1), NodeId(0)]),
inputs_source: [(Source { node: vec![NodeId(1)], index: 1 }, 1)].into(), inputs_source: [(Source { node: vec![NodeId(1)], index: 1 }, 1)].into(),
@@ -1365,7 +1368,7 @@ mod test {
NodeId(14), NodeId(14),
DocumentNode { DocumentNode {
inputs: vec![NodeInput::value(TaggedValue::U32(2), false)], inputs: vec![NodeInput::value(TaggedValue::U32(2), false)],
implementation: DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::value::ClonedNode")),
original_location: OriginalLocation { original_location: OriginalLocation {
path: Some(vec![NodeId(1), NodeId(4)]), path: Some(vec![NodeId(1), NodeId(4)]),
inputs_source: HashMap::new(), inputs_source: HashMap::new(),
@@ -1379,7 +1382,7 @@ mod test {
NodeId(11), NodeId(11),
DocumentNode { DocumentNode {
inputs: vec![NodeInput::node(NodeId(10), 0)], inputs: vec![NodeInput::node(NodeId(10), 0)],
implementation: DocumentNodeImplementation::ProtoNode("core_types::ops::AddPairNode".into()), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::ops::AddPairNode")),
original_location: OriginalLocation { original_location: OriginalLocation {
path: Some(vec![NodeId(1), NodeId(1)]), path: Some(vec![NodeId(1), NodeId(1)]),
inputs_source: HashMap::new(), inputs_source: HashMap::new(),
+13 -13
View File
@@ -30,7 +30,7 @@ impl core::fmt::Display for ProtoNetwork {
return f.write_str("{{Unknown Node}}"); return f.write_str("{{Unknown Node}}");
}; };
f.write_str("Node: ")?; f.write_str("Node: ")?;
f.write_str(&node.identifier.name)?; f.write_str(node.identifier.as_str())?;
f.write_str("\n")?; f.write_str("\n")?;
f.write_str(&"\t".repeat(indent))?; f.write_str(&"\t".repeat(indent))?;
@@ -156,7 +156,7 @@ impl ProtoNode {
use std::hash::Hasher; use std::hash::Hasher;
let mut hasher = rustc_hash::FxHasher::default(); let mut hasher = rustc_hash::FxHasher::default();
self.identifier.name.hash(&mut hasher); self.identifier.as_str().hash(&mut hasher);
self.construction_args.hash(&mut hasher); self.construction_args.hash(&mut hasher);
if self.skip_deduplication { if self.skip_deduplication {
self.original_location.path.hash(&mut hasher); self.original_location.path.hash(&mut hasher);
@@ -612,7 +612,7 @@ impl GraphError {
pub fn new(node: &ProtoNode, text: impl Into<GraphErrorType>) -> Self { pub fn new(node: &ProtoNode, text: impl Into<GraphErrorType>) -> Self {
Self { Self {
node_path: node.original_location.path.clone().unwrap_or_default(), node_path: node.original_location.path.clone().unwrap_or_default(),
identifier: node.identifier.name.clone(), identifier: Cow::Owned(node.identifier.as_str().to_string()),
error: text.into(), error: text.into(),
} }
} }
@@ -916,7 +916,7 @@ mod test {
let ids: Vec<_> = construction_network.nodes.iter().map(|(id, _)| *id).collect(); let ids: Vec<_> = construction_network.nodes.iter().map(|(id, _)| *id).collect();
println!("{ids:#?}"); println!("{ids:#?}");
println!("nodes: {:#?}", construction_network.nodes); println!("nodes: {:#?}", construction_network.nodes);
assert_eq!(construction_network.nodes[0].1.identifier.name.as_ref(), "value"); assert_eq!(construction_network.nodes[0].1.identifier.as_str(), "value");
assert_eq!(ids, vec![NodeId(0), NodeId(1), NodeId(2), NodeId(3)]); assert_eq!(ids, vec![NodeId(0), NodeId(1), NodeId(2), NodeId(3)]);
} }
@@ -929,7 +929,7 @@ mod test {
assert_eq!(sorted, vec![NodeId(0), NodeId(1), NodeId(2), NodeId(3)]); assert_eq!(sorted, vec![NodeId(0), NodeId(1), NodeId(2), NodeId(3)]);
let ids: Vec<_> = construction_network.nodes.iter().map(|(id, _)| *id).collect(); let ids: Vec<_> = construction_network.nodes.iter().map(|(id, _)| *id).collect();
println!("{ids:#?}"); println!("{ids:#?}");
assert_eq!(construction_network.nodes[0].1.identifier.name.as_ref(), "value"); assert_eq!(construction_network.nodes[0].1.identifier.as_str(), "value");
assert_eq!(ids, vec![NodeId(0), NodeId(1), NodeId(2), NodeId(3)]); assert_eq!(ids, vec![NodeId(0), NodeId(1), NodeId(2), NodeId(3)]);
} }
@@ -940,7 +940,7 @@ mod test {
.insert_context_nullification_nodes() .insert_context_nullification_nodes()
.expect("Error when calling 'insert_context_nullification_nodes' on 'construction_network."); .expect("Error when calling 'insert_context_nullification_nodes' on 'construction_network.");
construction_network.generate_stable_node_ids(); construction_network.generate_stable_node_ids();
assert_eq!(construction_network.nodes[0].1.identifier.name.as_ref(), "value"); assert_eq!(construction_network.nodes[0].1.identifier.as_str(), "value");
let ids: Vec<_> = construction_network.nodes.iter().map(|(id, _)| *id).collect(); let ids: Vec<_> = construction_network.nodes.iter().map(|(id, _)| *id).collect();
// If this assert fails: These NodeIds seem to be changing when you modify TaggedValue, just update them. // If this assert fails: These NodeIds seem to be changing when you modify TaggedValue, just update them.
@@ -958,7 +958,7 @@ mod test {
( (
NodeId(7), NodeId(7),
ProtoNode { ProtoNode {
identifier: "id".into(), identifier: ProtoNodeIdentifier::new("id"),
call_argument: concrete!(()), call_argument: concrete!(()),
construction_args: ConstructionArgs::Nodes(vec![NodeId(11)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(11)]),
..Default::default() ..Default::default()
@@ -967,7 +967,7 @@ mod test {
( (
NodeId(1), NodeId(1),
ProtoNode { ProtoNode {
identifier: "id".into(), identifier: ProtoNodeIdentifier::new("id"),
call_argument: concrete!(()), call_argument: concrete!(()),
construction_args: ConstructionArgs::Nodes(vec![NodeId(11)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(11)]),
..Default::default() ..Default::default()
@@ -976,7 +976,7 @@ mod test {
( (
NodeId(10), NodeId(10),
ProtoNode { ProtoNode {
identifier: "cons".into(), identifier: ProtoNodeIdentifier::new("cons"),
call_argument: concrete!(u32), call_argument: concrete!(u32),
construction_args: ConstructionArgs::Nodes(vec![NodeId(14)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(14)]),
..Default::default() ..Default::default()
@@ -985,7 +985,7 @@ mod test {
( (
NodeId(11), NodeId(11),
ProtoNode { ProtoNode {
identifier: "add".into(), identifier: ProtoNodeIdentifier::new("add"),
call_argument: concrete!(()), call_argument: concrete!(()),
construction_args: ConstructionArgs::Nodes(vec![NodeId(10)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(10)]),
..Default::default() ..Default::default()
@@ -994,7 +994,7 @@ mod test {
( (
NodeId(14), NodeId(14),
ProtoNode { ProtoNode {
identifier: "value".into(), identifier: ProtoNodeIdentifier::new("value"),
call_argument: concrete!(()), call_argument: concrete!(()),
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(2).into()), construction_args: ConstructionArgs::Value(value::TaggedValue::U32(2).into()),
..Default::default() ..Default::default()
@@ -1014,7 +1014,7 @@ mod test {
( (
NodeId(1), NodeId(1),
ProtoNode { ProtoNode {
identifier: "id".into(), identifier: ProtoNodeIdentifier::new("id"),
call_argument: concrete!(()), call_argument: concrete!(()),
construction_args: ConstructionArgs::Nodes(vec![NodeId(2)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(2)]),
..Default::default() ..Default::default()
@@ -1023,7 +1023,7 @@ mod test {
( (
NodeId(2), NodeId(2),
ProtoNode { ProtoNode {
identifier: "id".into(), identifier: ProtoNodeIdentifier::new("id"),
call_argument: concrete!(()), call_argument: concrete!(()),
construction_args: ConstructionArgs::Nodes(vec![NodeId(1)]), construction_args: ConstructionArgs::Nodes(vec![NodeId(1)]),
..Default::default() ..Default::default()
+3 -3
View File
@@ -98,9 +98,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
Command::Export { ref document, .. } => document, Command::Export { ref document, .. } => document,
Command::ListNodeIdentifiers => { Command::ListNodeIdentifiers => {
let mut ids: Vec<_> = graphene_std::registry::NODE_METADATA.lock().unwrap().keys().cloned().collect(); let mut ids: Vec<_> = graphene_std::registry::NODE_METADATA.lock().unwrap().keys().cloned().collect();
ids.sort_by_key(|x| x.name.clone()); ids.sort_by_key(|x| x.as_str().to_string());
for id in ids { for id in ids {
println!("{}", id.name) println!("{}", id.as_str());
} }
return Ok(()); return Ok(());
} }
@@ -212,7 +212,7 @@ fn fix_nodes(network: &mut NodeNetwork) {
// https://github.com/GraphiteEditor/Graphite/blob/d68f91ccca69e90e6d2df78d544d36cd1aaf348e/editor/src/messages/portfolio/portfolio_message_handler.rs#L535 // https://github.com/GraphiteEditor/Graphite/blob/d68f91ccca69e90e6d2df78d544d36cd1aaf348e/editor/src/messages/portfolio/portfolio_message_handler.rs#L535
// Since the CLI doesn't have the document node definitions, a less robust method of just patching the inputs is used. // Since the CLI doesn't have the document node definitions, a less robust method of just patching the inputs is used.
DocumentNodeImplementation::ProtoNode(proto_node_identifier) DocumentNodeImplementation::ProtoNode(proto_node_identifier)
if (proto_node_identifier.name.starts_with("graphene_core::ConstructLayerNode") || proto_node_identifier.name.starts_with("graphene_core::AddArtboardNode")) if (proto_node_identifier.as_str().starts_with("graphene_core::ConstructLayerNode") || proto_node_identifier.as_str().starts_with("graphene_core::AddArtboardNode"))
&& node.inputs.len() < 3 => && node.inputs.len() < 3 =>
{ {
node.inputs.push(NodeInput::Reflection(DocumentNodeMetadata::DocumentNodePath)); node.inputs.push(NodeInput::Reflection(DocumentNodeMetadata::DocumentNodePath));
+1 -1
View File
@@ -31,7 +31,7 @@ mod tests {
NodeId(1), NodeId(1),
DocumentNode { DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(0), 0)], inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::ops::AddNode")), implementation: DocumentNodeImplementation::ProtoNode(graphene_std::math_nodes::add::IDENTIFIER),
..Default::default() ..Default::default()
}, },
), ),
@@ -3,14 +3,10 @@ use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::DocumentNode; use graph_craft::document::DocumentNode;
use graph_craft::document::value::RenderOutput; use graph_craft::document::value::RenderOutput;
use graph_craft::proto::{NodeConstructor, TypeErasedBox}; use graph_craft::proto::{NodeConstructor, TypeErasedBox};
use graphene_std::Artboard;
use graphene_std::Context;
use graphene_std::Graphic;
use graphene_std::any::DynAnyNode; use graphene_std::any::DynAnyNode;
use graphene_std::application_io::{ImageTexture, SurfaceFrame}; use graphene_std::application_io::{ImageTexture, SurfaceFrame};
use graphene_std::brush::brush_cache::BrushCache; use graphene_std::brush::brush_cache::BrushCache;
use graphene_std::brush::brush_stroke::BrushStroke; use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::concrete;
use graphene_std::gradient::GradientStops; use graphene_std::gradient::GradientStops;
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
use graphene_std::raster::GPU; use graphene_std::raster::GPU;
@@ -25,9 +21,7 @@ use graphene_std::vector::Vector;
use graphene_std::wasm_application_io::WasmEditorApi; use graphene_std::wasm_application_io::WasmEditorApi;
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
use graphene_std::wasm_application_io::WasmSurfaceHandle; use graphene_std::wasm_application_io::WasmSurfaceHandle;
use graphene_std::{Cow, ProtoNodeIdentifier}; use graphene_std::{Artboard, Context, Graphic, NodeIO, NodeIOTypes, ProtoNodeIdentifier, concrete, fn_type_fut, future};
use graphene_std::{NodeIO, NodeIOTypes};
use graphene_std::{fn_type_fut, future};
use node_registry_macros::{async_node, convert_node, into_node}; use node_registry_macros::{async_node, convert_node, into_node};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::HashMap; use std::collections::HashMap;
@@ -269,11 +263,11 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
} }
} }
for (id, c, types) in node_types.into_iter() { for (id, node_constructor, types) in node_types.into_iter() {
// TODO: this is a hack to remove the newline from the node new_name // TODO: this is a hack to remove the newline from the node new_name
// This occurs for the ChannelMixerNode presumably because of the long name. // This occurs for the ChannelMixerNode presumably because of the long name.
// This might be caused by the stringify! macro // This might be caused by the stringify! macro
let mut new_name = id.name.replace('\n', " "); let mut new_name = id.as_str().replace('\n', " ");
// Remove struct generics for all nodes except for the IntoNode and ConvertNode // Remove struct generics for all nodes except for the IntoNode and ConvertNode
if !(new_name.contains("IntoNode") || new_name.contains("ConvertNode")) if !(new_name.contains("IntoNode") || new_name.contains("ConvertNode"))
@@ -282,8 +276,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
new_name = path.to_string(); new_name = path.to_string();
} }
let nid = ProtoNodeIdentifier { name: Cow::Owned(new_name) }; map.entry(ProtoNodeIdentifier::with_owned_string(new_name)).or_default().insert(types.clone(), node_constructor);
map.entry(nid).or_default().insert(types.clone(), c);
} }
map map
+1 -1
View File
@@ -85,7 +85,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
if cfg!(feature = "gpu") { if cfg!(feature = "gpu") {
nodes.push(DocumentNode { nodes.push(DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::from("graphene_core::ops::IntoNode<&WgpuExecutor>")), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<&WgpuExecutor>")),
inputs: vec![NodeInput::node(NodeId(2), 0)], inputs: vec![NodeInput::node(NodeId(2), 0)],
..Default::default() ..Default::default()
}); });
@@ -1,13 +1,12 @@
use crate::{ContextFeature, Node, NodeIO, NodeIOTypes, ProtoNodeIdentifier, Type, WasmNotSend}; use crate::{ContextFeature, Node, NodeIO, NodeIOTypes, ProtoNodeIdentifier, Type, WasmNotSend};
use dyn_any::{DynAny, StaticType}; use dyn_any::{DynAny, StaticType};
pub use no_std_types::registry::types;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::pin::Pin; use std::pin::Pin;
use std::sync::{LazyLock, Mutex}; use std::sync::{LazyLock, Mutex};
pub use no_std_types::registry::types;
// Translation struct between macro and definition // Translation struct between macro and definition
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct NodeMetadata { pub struct NodeMetadata {
+2 -20
View File
@@ -1,8 +1,6 @@
use std::any::TypeId; use std::any::TypeId;
pub use std::borrow::Cow; pub use std::borrow::Cow;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Deref;
#[macro_export] #[macro_export]
macro_rules! concrete { macro_rules! concrete {
@@ -128,19 +126,7 @@ impl std::fmt::Debug for NodeIOTypes {
#[derive(Clone, Debug, PartialEq, Eq, Hash, specta::Type, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, specta::Type, serde::Serialize, serde::Deserialize)]
pub struct ProtoNodeIdentifier { pub struct ProtoNodeIdentifier {
pub name: Cow<'static, str>, name: Cow<'static, str>,
}
impl From<String> for ProtoNodeIdentifier {
fn from(value: String) -> Self {
Self { name: Cow::Owned(value) }
}
}
impl From<&'static str> for ProtoNodeIdentifier {
fn from(s: &'static str) -> Self {
ProtoNodeIdentifier { name: Cow::Borrowed(s) }
}
} }
impl ProtoNodeIdentifier { impl ProtoNodeIdentifier {
@@ -151,12 +137,8 @@ impl ProtoNodeIdentifier {
pub const fn with_owned_string(name: String) -> Self { pub const fn with_owned_string(name: String) -> Self {
ProtoNodeIdentifier { name: Cow::Owned(name) } ProtoNodeIdentifier { name: Cow::Owned(name) }
} }
}
impl Deref for ProtoNodeIdentifier { pub fn as_str(&self) -> &str {
type Target = str;
fn deref(&self) -> &Self::Target {
self.name.as_ref() self.name.as_ref()
} }
} }
+2 -2
View File
@@ -117,7 +117,7 @@ fn validate_implementations_for_generics(parsed: &ParsedNodeFn) {
quote!(#ty), quote!(#ty),
pat_ident.ident; pat_ident.ident;
help = "Add #[implementations(ConcreteType1, ConcreteType2)] to field '{}'", pat_ident.ident; help = "Add #[implementations(ConcreteType1, ConcreteType2)] to field '{}'", pat_ident.ident;
help = "Or use #[skip_impl] if you want to manually implement the node" help = "Or use #[node_macro::node(skip_impl)] if you want to manually implement the node"
); );
} }
} }
@@ -133,7 +133,7 @@ fn validate_implementations_for_generics(parsed: &ParsedNodeFn) {
"Generic types in Node field `{}` require an #[implementations(...)] attribute", "Generic types in Node field `{}` require an #[implementations(...)] attribute",
pat_ident.ident; pat_ident.ident;
help = "Add #[implementations(InputType1 -> OutputType1, InputType2 -> OutputType2)] to field '{}'", pat_ident.ident; help = "Add #[implementations(InputType1 -> OutputType1, InputType2 -> OutputType2)] to field '{}'", pat_ident.ident;
help = "Or use #[skip_impl] if you want to manually implement the node" help = "Or use #[node_macro::node(skip_impl)] if you want to manually implement the node"
); );
} }
// Additional check for Node implementations // Additional check for Node implementations
+1 -1
View File
@@ -4,7 +4,7 @@ use std::marker::PhantomData;
// Re-export TypeNode from core-types for convenience // Re-export TypeNode from core-types for convenience
pub use core_types::ops::TypeNode; pub use core_types::ops::TypeNode;
// TODO: Rename to "Passthrough" // TODO: Rename to "Passthrough" and make this the node that users use, not the one defined in document_node_definitions.rs
/// Passes-through the input value without changing it. /// Passes-through the input value without changing it.
/// This is useful for rerouting wires for organization purposes. /// This is useful for rerouting wires for organization purposes.
#[node_macro::node(skip_impl)] #[node_macro::node(skip_impl)]
+1 -1
View File
@@ -810,7 +810,7 @@ fn dot_product(
/// An operand of the dot product operation. /// An operand of the dot product operation.
vector_a: DVec2, vector_a: DVec2,
/// The other operand of the dot product operation. /// The other operand of the dot product operation.
#[default((1., 0.))] #[default(1., 0.)]
vector_b: DVec2, vector_b: DVec2,
/// Whether to normalize both input vectors so the calculation ranges in `[-1, 1]` by considering only their degree of directional alignment. /// Whether to normalize both input vectors so the calculation ranges in `[-1, 1]` by considering only their degree of directional alignment.
normalize: bool, normalize: bool,
+5 -9
View File
@@ -69,17 +69,13 @@ pub fn generate_node_substitutions() -> HashMap<ProtoNodeIdentifier, DocumentNod
let input_ty = input.nested_type(); let input_ty = input.nested_type();
let mut inputs = vec![NodeInput::import(input.clone(), i)]; let mut inputs = vec![NodeInput::import(input.clone(), i)];
let into_node_identifier = ProtoNodeIdentifier { let into_node_identifier = ProtoNodeIdentifier::with_owned_string(format!("graphene_core::ops::IntoNode<{}>", input_ty.clone()));
name: format!("graphene_core::ops::IntoNode<{}>", input_ty.clone()).into(), let convert_node_identifier = ProtoNodeIdentifier::with_owned_string(format!("graphene_core::ops::ConvertNode<{}>", input_ty.clone()));
};
let convert_node_identifier = ProtoNodeIdentifier {
name: format!("graphene_core::ops::ConvertNode<{}>", input_ty.clone()).into(),
};
let proto_node = if into_node_registry.keys().any(|ident: &ProtoNodeIdentifier| ident.name.as_ref() == into_node_identifier.name.as_ref()) { let proto_node = if into_node_registry.keys().any(|ident: &ProtoNodeIdentifier| ident.as_str() == into_node_identifier.as_str()) {
generated_nodes += 1; generated_nodes += 1;
into_node_identifier into_node_identifier
} else if into_node_registry.keys().any(|ident| ident.name.as_ref() == convert_node_identifier.name.as_ref()) { } else if into_node_registry.keys().any(|ident| ident.as_str() == convert_node_identifier.as_str()) {
generated_nodes += 1; generated_nodes += 1;
inputs.push(NodeInput::value(TaggedValue::None, false)); inputs.push(NodeInput::value(TaggedValue::None, false));
convert_node_identifier convert_node_identifier
@@ -162,7 +158,7 @@ pub fn node_inputs(fields: &[registry::FieldMetadata], first_node_io: &NodeIOTyp
return NodeInput::value(custom_default, exposed); return NodeInput::value(custom_default, exposed);
} else { } else {
// It is incredibly useful to get a warning when the default type cannot be parsed rather than defaulting to `()`. // It is incredibly useful to get a warning when the default type cannot be parsed rather than defaulting to `()`.
warn!("Failed to parse default value for type {ty:?} with data {data}"); warn!("Failed to parse default value for type `{ty:?}` with data `{data}`");
} }
} }
RegistryValueSource::Scope(data) => return NodeInput::scope(Cow::Borrowed(data)), RegistryValueSource::Scope(data) => return NodeInput::scope(Cow::Borrowed(data)),