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::input_mapper::utility_types::misc::ActionShortcut;
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::{
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
SendUIMetadata {
#[serde(rename = "nodeDescriptions")]
node_descriptions: Vec<(String, String)>,
node_descriptions: Vec<(DefinitionIdentifier, String)>,
#[serde(rename = "nodeTypes")]
node_types: Vec<FrontendNodeType>,
},
@@ -287,11 +287,7 @@ impl LayoutMessageHandler {
responses.add(callback_message);
}
WidgetValueAction::Update => {
let Some(value) = value.as_str().map(|s| s.to_string()) else {
error!("NodeCatalog update was not of type String");
return;
};
let callback_message = (node_type_input.on_update.callback)(&value);
let callback_message = (node_type_input.on_update.callback)(&value.into());
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
let apply_action_shortcut_to_menu_lists = |entry_sections: &mut MenuListEntrySections| {
struct RecursiveWrapper<'a>(&'a dyn Fn(&mut MenuListEntrySections, &RecursiveWrapper));
let recursive_wrapper = RecursiveWrapper(&|entry_sections: &mut MenuListEntrySections, recursive_wrapper| {
for entries in entry_sections {
for entry in entries {
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
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);
fn apply_action_shortcut_to_menu_lists(entry_sections: &mut MenuListEntrySections, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
for entries in entry_sections {
for entry in entries {
// Convert `ActionShortcut::Action` to `ActionShortcut::Shortcut`
if let Some(tooltip_shortcut) = &mut entry.tooltip_shortcut {
tooltip_shortcut.realize_shortcut(action_input_mapping);
}
// 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
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
let convert_menu_lists = |widget_instance: &mut WidgetInstance| match &mut *widget_instance.widget {
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);
}
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);
}
_ => {}
@@ -1,5 +1,6 @@
use crate::messages::input_mapper::utility_types::misc::ActionShortcut;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::document_node_definitions::DefinitionIdentifier;
use derivative::*;
use graphene_std::Color;
use graphene_std::raster::curve::Curve;
@@ -273,7 +274,7 @@ pub struct NodeCatalog {
// Callbacks
#[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub on_update: WidgetCallback<String>,
pub on_update: WidgetCallback<DefinitionIdentifier>,
#[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
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::graph_operation::utility_types::TransformIn;
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::overlays::grid_overlays::{grid_overlay, overlay_options};
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
let node_id = NodeId::new();
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")
.default_node_template();
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
.reference(id, &[])
.map(|name| name.as_deref().unwrap_or_default() == "Boolean Operation")
.unwrap_or_default()
.is_some_and(|reference| reference == DefinitionIdentifier::Network("Boolean Operation".into()))
})
});
@@ -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());
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()));
}
@@ -1,12 +1,12 @@
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::network_interface::{self, InputConnector, NodeNetworkInterface, OutputConnector};
use crate::messages::prelude::*;
use glam::{DAffine2, IVec2};
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke;
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
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, &[]);
LayerNodeIdentifier::new(new_id, self.network_interface)
}
/// Creates an artboard as the primary export for the document network
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::Graphic(Default::default()), true)),
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) {
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::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) {
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")
.node_template_input_override([Some(NodeInput::value(TaggedValue::Vector(vector), false))]);
let shape_id = NodeId::new();
@@ -164,21 +164,25 @@ impl<'a> ModifyInputsContext<'a> {
self.network_interface.move_node_to_chain_start(&shape_id, layer, &[]);
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();
self.network_interface.insert_node(transform_id, transform, &[]);
self.network_interface.move_node_to_chain_start(&transform_id, layer, &[]);
}
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();
self.network_interface.insert_node(fill_id, fill, &[]);
self.network_interface.move_node_to_chain_start(&fill_id, layer, &[]);
}
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();
self.network_interface.insert_node(stroke_id, stroke, &[]);
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) {
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template();
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template();
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
let text = resolve_document_node_type("Text").expect("Text node does not exist").node_template_input_override([
Some(NodeInput::scope("editor-api")),
Some(NodeInput::value(TaggedValue::String(text), false)),
Some(NodeInput::value(TaggedValue::Font(font), 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 stroke = resolve_proto_node_type(graphene_std::vector_nodes::stroke::IDENTIFIER)
.expect("Stroke 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 transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let text = resolve_proto_node_type(graphene_std::text::text::IDENTIFIER)
.expect("Text node does not exist")
.node_template_input_override([
Some(NodeInput::scope("editor-api")),
Some(NodeInput::value(TaggedValue::String(text), false)),
Some(NodeInput::value(TaggedValue::Font(font), 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();
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) {
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
let image = resolve_document_node_type("Image Value")
let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let image = resolve_proto_node_type(graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER)
.expect("ImageValue node does not exist")
.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.
/// 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.
/// Each dot after that represents an existing path node. If there is an existing upstream node, then it will always be returned first.
pub fn existing_node_id(&mut self, reference_name: &'static str, create_if_nonexistent: bool) -> Option<NodeId> {
/// 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.
pub fn existing_network_node_id(&mut self, reference: &str, create_if_nonexistent: bool) -> Option<NodeId> {
self.existing_node_id(&DefinitionIdentifier::Network(reference.into()), create_if_nonexistent)
}
/// 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
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
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
@@ -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.
/// 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.
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);
// Look at all of the upstream nodes
for upstream_node in upstream {
// Check if this is the node we have been searching for.
if network_interface
.reference(&upstream_node, &[])
.is_some_and(|node_reference| *node_reference == Some(reference_name.to_string()))
{
if network_interface.reference(&upstream_node, &[]).is_some_and(|node_reference| node_reference == *reference) {
if !network_interface.is_visible(&upstream_node, &[]) {
continue;
}
@@ -293,10 +308,10 @@ impl<'a> ModifyInputsContext<'a> {
}
/// 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 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;
};
@@ -305,7 +320,7 @@ impl<'a> ModifyInputsContext<'a> {
if node_definition.identifier == "Path" {
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>)) {
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");
return None;
};
@@ -325,7 +340,9 @@ impl<'a> ModifyInputsContext<'a> {
let backup_color_index = 2;
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 {
Fill::None => {
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) {
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);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::BlendMode(blend_mode), false), false);
}
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);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(opacity * 100.), false), false);
}
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);
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>) {
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);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Bool(clip), false), false);
}
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() };
@@ -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) {
// Get the existing upstream Transform node and its transform, if present, otherwise use the identity transform
let (layer_transform, transform_node_id) = self
.existing_node_id("Transform", false)
.existing_network_node_id("Transform", false)
.and_then(|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))
@@ -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.
pub fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, skip_rerender: bool) {
// 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
let to_transform = match transform_in {
@@ -448,7 +475,7 @@ impl<'a> ModifyInputsContext<'a> {
}
// Create the Transform node
self.existing_node_id("Transform", true)
self.existing_network_node_id("Transform", true)
}) else {
return;
};
@@ -464,19 +491,23 @@ impl<'a> ModifyInputsContext<'a> {
}
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.responses.add(PropertiesPanelMessage::Refresh);
self.responses.add(NodeGraphMessage::RunDocumentGraph);
}
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);
}
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;
};
@@ -26,7 +26,8 @@ use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::transform::Footprint;
use graphene_std::vector::Vector;
use graphene_std::*;
use std::collections::{HashMap, HashSet, VecDeque};
use serde_json::Value;
use std::collections::{HashMap, VecDeque};
pub struct NodePropertiesContext<'a> {
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.
#[derive(Clone)]
#[derive(Debug, Clone)]
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,
/// All data required to construct a [`DocumentNode`] and [`DocumentNodeMetadata`]
@@ -75,14 +129,14 @@ pub struct DocumentNodeDefinition {
pub properties: Option<&'static str>,
}
// We use the once cell for lazy initialization to avoid the overhead of reconstructing the node list every time.
// TODO: make document nodes not require a `'static` lifetime to avoid having to split the construction into const and non-const parts.
static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy<Vec<DocumentNodeDefinition>> = once_cell::sync::Lazy::new(static_nodes);
// We use the once_cell to use the document node definitions throughout the editor without passing a reference
// TODO: If dynamic node library is required, use a Mutex as well
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).
/// 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![
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
@@ -100,7 +154,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..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,
},
// 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."),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Cache",
category: "General",
@@ -224,7 +279,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "To Graphic".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)),
..Default::default()
},
@@ -232,7 +286,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Wrap Graphic".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -1)),
..Default::default()
},
@@ -240,7 +293,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Source Node ID".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -1)),
..Default::default()
},
@@ -248,7 +300,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-7, -1)),
..Default::default()
},
@@ -256,7 +307,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extend".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -3)),
..Default::default()
},
@@ -375,7 +425,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Create Artboard".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)),
..Default::default()
},
@@ -383,7 +432,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Source Node ID".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -3)),
..Default::default()
},
@@ -391,7 +439,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-7, -3)),
..Default::default()
},
@@ -399,7 +446,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extend".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -4)),
..Default::default()
},
@@ -989,7 +1035,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Load Resource".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -997,7 +1042,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Decode Image".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default()
},
@@ -1071,7 +1115,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Create Surface".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)),
..Default::default()
},
@@ -1079,7 +1122,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Cache".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 2)),
..Default::default()
},
@@ -1087,7 +1129,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Rasterize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
..Default::default()
},
@@ -1108,12 +1149,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("TODO"),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Noise Pattern",
category: "Raster: Pattern",
node_template: NodeTemplate {
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![
NodeInput::value(TaggedValue::None, false),
NodeInput::value(TaggedValue::Bool(true), false),
@@ -1228,7 +1270,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -1236,7 +1277,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)),
..Default::default()
},
@@ -1244,7 +1284,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 4)),
..Default::default()
},
@@ -1252,7 +1291,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract Channel".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 6)),
..Default::default()
},
@@ -1312,7 +1350,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract XY".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -1320,7 +1357,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Extract XY".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 2)),
..Default::default()
},
@@ -1382,7 +1418,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
persistent_metadata: NodeNetworkPersistentMetadata {
node_metadata: [DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Brush".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -1402,6 +1437,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("TODO"),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Memoize",
category: "Debug",
@@ -1463,7 +1499,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
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)),
..Default::default()
},
@@ -1471,20 +1513,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Upload Texture".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..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()
.enumerate()
@@ -1592,7 +1625,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -1600,7 +1632,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Path Modify".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default()
},
@@ -1621,6 +1652,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("TODO"),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Text",
category: "Text",
@@ -1788,7 +1820,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Monitor".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -1796,15 +1827,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Transform".to_string(),
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()
@@ -1891,7 +1914,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Boolean Operation".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -1899,7 +1921,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Memoize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default()
},
@@ -1981,7 +2002,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Subpath Segment Lengths".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 7)),
..Default::default()
},
@@ -1989,7 +2009,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Sample Polyline".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default()
},
@@ -1997,7 +2016,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Memoize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(14, 0)),
..Default::default()
},
@@ -2104,7 +2122,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Poisson-Disk Points".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
@@ -2112,7 +2129,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Memoize".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default()
},
@@ -2637,102 +2653,48 @@ fn static_input_properties() -> InputProperties {
map
}
pub fn resolve_document_node_type(identifier: &str) -> Option<&DocumentNodeDefinition> {
DOCUMENT_NODE_TYPES.iter().find(|definition| definition.identifier == identifier)
pub fn resolve_network_node_type(identifier: &str) -> Option<&'static DocumentNodeDefinition> {
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> {
// Create a mapping from registry ID to document node identifier
let id_to_identifier_map: HashMap<ProtoNodeIdentifier, &'static str> = DOCUMENT_NODE_TYPES
DOCUMENT_NODE_TYPES
.iter()
.filter_map(|definition| {
if let DocumentNodeImplementation::ProtoNode(name) = &definition.node_template.document_node.implementation {
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| {
.filter(|(_, definition)| !definition.category.is_empty())
.map(|(identifier, definition)| {
let input_types = definition
.node_template
.document_node
.inputs
.iter()
.filter_map(|node_input| node_input.as_value().map(|node_value| node_value.ty().nested_type().to_cow_string()))
.collect::<Vec<Cow<'static, str>>>();
FrontendNodeType::with_input_types(definition.identifier, definition.category, input_types)
})
.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();
.map(|node_input| node_input.as_value().map(|node_value| node_value.ty().nested_type().to_string()).unwrap_or_default())
.collect::<Vec<String>>();
let mut name = definition.node_template.persistent_node_metadata.display_name.clone();
if name.is_empty() {
name = identifier.implementation_name_from_identifier()
}
}
}
let missing_nodes: Vec<FrontendNodeType> = node_types
.iter()
.filter(|node| !extracted_node_types.iter().any(|extracted| extracted.name == node.name))
.cloned()
.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
FrontendNodeType {
identifier: identifier.clone(),
name,
category: definition.category.to_string(),
input_types,
}
})
.collect()
}
pub fn collect_node_descriptions() -> Vec<(String, String)> {
pub fn collect_node_descriptions() -> Vec<(DefinitionIdentifier, String)> {
DOCUMENT_NODE_TYPES
.iter()
.map(|definition| {
(
definition.identifier.to_string(),
if definition.description != "TODO" { definition.description.to_string() } else { String::new() },
)
})
.map(|(identifier, definition)| (identifier.clone(), if definition.description != "TODO" { definition.description.to_string() } else { String::new() }))
.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
input_override.into_iter().enumerate().for_each(|(index, 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
// assert!(matches!(input_override, NodeInput::Value { .. }), "Only value inputs are supported for input overrides");
// Only value inputs should be overridden, since node inputs change graph structure and must be handled by the network interface.
// 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;
}
});
@@ -2806,12 +2769,6 @@ impl DocumentNodeDefinition {
}
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
}
@@ -1,72 +1,34 @@
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 graph_craft::ProtoNodeIdentifier;
use graph_craft::document::*;
use graphene_std::registry::*;
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
fn traverse_node(node: &DocumentNode, node_metadata: &mut DocumentNodePersistentMetadata) {
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()
.expect("Network node must have network metadata")
.persistent_metadata
.node_metadata
.get_mut(nested_node_id)
.expect("Network metadata must have corresponding node id");
traverse_node(nested_node, &mut nested_metadata.persistent_metadata);
}
}
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())
};
}
pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap<DefinitionIdentifier, DocumentNodeDefinition> {
// Create hashmap for the protonodes added by the macro.
let mut definitions_map = HashMap::new();
// First remove the custom protonodes and add them to the definitions map since they contain different metadata
// from the macro and must be inserted first so that network nodes which reference them use the correct metadata.
let network_nodes = custom
.into_iter()
.filter_map(|definition| {
if let DocumentNodeImplementation::ProtoNode(proto_node_identifier) = &definition.node_template.document_node.implementation {
definitions_map.insert(DefinitionIdentifier::ProtoNode(proto_node_identifier.clone()), definition);
return None;
};
Some(definition)
})
.collect::<Vec<_>>();
// Add the rest of the protonodes from the macro
let node_registry = NODE_REGISTRY.lock().unwrap();
'outer: for (id, metadata) in NODE_METADATA.lock().unwrap().iter() {
for node in custom.iter() {
let DocumentNodeDefinition {
node_template: NodeTemplate {
document_node: DocumentNode { implementation, .. },
..
},
..
} = node;
match implementation {
DocumentNodeImplementation::ProtoNode(name) if name == id => continue 'outer,
_ => (),
}
}
for (id, metadata) in NODE_METADATA.lock().unwrap().iter() {
let identifier = DefinitionIdentifier::ProtoNode(id.clone());
if definitions_map.contains_key(&identifier) {
continue;
};
let NodeMetadata {
display_name,
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 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 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 output_type = &first_node_io.return_value;
let inputs = preprocessor::node_inputs(fields, first_node_io);
let node = DocumentNodeDefinition {
identifier: display_name,
node_template: NodeTemplate {
document_node: DocumentNode {
inputs,
call_argument: input_type.clone(),
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
visible: true,
skip_deduplication: false,
context_features: ContextDependencies::from(context_features.as_slice()),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
// TODO: Store information for input overrides in the node macro
input_metadata: fields
.iter()
.map(|f| match f.widget_override {
RegistryWidgetOverride::None => (f.name, f.description).into(),
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())),
RegistryWidgetOverride::Custom(str) => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::Custom(str.to_string())),
})
.collect(),
output_names: vec![output_type.to_string()],
locked: false,
..Default::default()
definitions_map.insert(
identifier,
DocumentNodeDefinition {
identifier: display_name,
node_template: NodeTemplate {
document_node: DocumentNode {
inputs,
call_argument: input_type.clone(),
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
visible: true,
skip_deduplication: false,
context_features: ContextDependencies::from(context_features.as_slice()),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
// TODO: Store information for input overrides in the node macro
input_metadata: fields
.iter()
.map(|f| match f.widget_override {
RegistryWidgetOverride::None => (f.name, f.description).into(),
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())),
RegistryWidgetOverride::Custom(str) => InputMetadata::with_name_description_override(f.name, f.description, WidgetOverride::Custom(str.to_string())),
})
.collect(),
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 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::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector};
use crate::messages::prelude::*;
@@ -27,16 +28,16 @@ pub enum NodeGraphMessage {
SelectedNodesUpdated,
Copy,
CreateNodeInLayerNoTransaction {
node_type: String,
node_type: DefinitionIdentifier,
layer: LayerNodeIdentifier,
},
CreateNodeInLayerWithTransaction {
node_type: String,
node_type: DefinitionIdentifier,
layer: LayerNodeIdentifier,
},
CreateNodeFromContextMenu {
node_id: Option<NodeId>,
node_type: String,
node_type: DefinitionIdentifier,
xy: Option<(i32, i32)>,
add_transaction: bool,
},
@@ -1,12 +1,14 @@
use super::node_properties;
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode};
use super::{document_node_definitions, node_properties};
use crate::consts::GRID_SIZE;
use crate::messages::clipboard::utility_types::ClipboardContent;
use crate::messages::input_mapper::utility_types::macros::{action_shortcut, action_shortcut_manual};
use crate::messages::layout::utility_types::widget_prelude::*;
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::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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
@@ -131,7 +133,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}
NodeGraphMessage::AddPathNode => {
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);
}
}
@@ -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()
+ network_interface.get_input_center(&input_connector, breadcrumb_network_path).unwrap())
/ 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();
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 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 {
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;
};
@@ -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
let encapsulating_node_id = NodeId::new();
let mut default_node_template = document_node_definitions::resolve_document_node_type("Default Network")
.expect("Default Network node should exist")
.default_node_template();
let mut default_node_template = resolve_network_node_type("Default Network").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 {
log::error!("Could not get center of selected_nodes");
return;
@@ -1701,7 +1704,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
input,
});
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);
}
}
@@ -2596,8 +2599,9 @@ impl NodeGraphMessageHandler {
.node_metadata(&node_id, breadcrumb_network_path)
.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),
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),
implementation_name: network_interface.implementation_name(&node_id, breadcrumb_network_path),
primary_input,
exposed_inputs,
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 data = LayerPanelEntry {
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, &[]),
in_selected_network: selection_network_path.is_empty(),
children_allowed,
@@ -3,6 +3,7 @@
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
use super::utility_types::FrontendGraphDataType;
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::utility_types::{FontCatalogStyle, PersistentData};
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
.network_interface
.reference(&node_id, context.selection_network_path)
.cloned()
.unwrap_or_default()
.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(|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() {
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
.reference(&node_id, context.selection_network_path)
.cloned()
.unwrap_or_default() // If there is an error getting the reference, default to empty string
.or_else(|| {
// If there is no reference, try to get the proto node name
context.network_interface.implementation(&node_id, context.selection_network_path).and_then(|implementation|{
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);
.as_ref()
.and_then(|identifier| resolve_document_node_type(identifier))
.map(|definition| definition.description.to_string())
.filter(|string| string != "TODO")
.unwrap_or_default();
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);
LayoutGroup::Section {
name,
description,
@@ -1,8 +1,8 @@
use super::document_node_definitions::DefinitionIdentifier;
use glam::{DVec2, IVec2};
use graph_craft::document::NodeId;
use graph_craft::document::value::TaggedValue;
use graphene_std::Type;
use std::borrow::Cow;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType {
@@ -79,9 +79,11 @@ pub struct FrontendNode {
pub is_layer: bool,
#[serde(rename = "canBeLayer")]
pub can_be_layer: bool,
pub reference: Option<String>,
pub reference: Option<DefinitionIdentifier>,
#[serde(rename = "displayName")]
pub display_name: String,
#[serde(rename = "implementationName")]
pub implementation_name: String,
#[serde(rename = "primaryInput")]
pub primary_input: Option<FrontendGraphInput>,
#[serde(rename = "exposedInputs")]
@@ -102,29 +104,13 @@ pub struct FrontendNode {
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeType {
pub name: Cow<'static, str>,
pub category: Cow<'static, str>,
pub identifier: DefinitionIdentifier,
pub name: String,
pub category: String,
#[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)]
pub struct DragStart {
pub start_x: f64,
@@ -1,6 +1,7 @@
use super::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::document::graph_operation::transform_utils;
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::tool::common_functionality::graph_modification_utils;
use glam::{DAffine2, DVec2};
@@ -91,7 +92,8 @@ impl DocumentMetadata {
let mut use_local = true;
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())
&& !network_interface
.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 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, &[]));
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 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::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::overlays::utility_functions::text_width;
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::ResolvedDocumentNodeTypes;
@@ -79,7 +79,7 @@ impl NodeNetworkInterface {
fix_network(network);
}
if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation
&& protonode.name.contains("PathModifyNode")
&& protonode.as_str().contains("PathModifyNode")
&& node.inputs.len() < 3
{
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath));
@@ -470,12 +470,6 @@ impl NodeNetworkInterface {
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> {
let Some(network) = self.nested_network(network_path) else {
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>> {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get reference for node: {node_id:?}");
pub fn reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<DefinitionIdentifier> {
let Some(document_node) = self.document_node(node_id, network_path) else {
log::error!("Could not get document_node for node in reference: {node_id:?}");
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> {
@@ -982,11 +994,6 @@ impl NodeNetworkInterface {
pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
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) {
node_metadata.persistent_metadata.display_name.clone()
} else {
@@ -998,18 +1005,19 @@ impl NodeNetworkInterface {
if is_layer {
"Untitled Layer".to_string()
} 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 {
display_name
}
}
/// Returns the description of the node, or an empty string if it is not set.
pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
self.get_node_definition(node_id, network_path)
.map(|node_definition| node_definition.description.to_string())
.unwrap_or_default()
/// The uneditable name in the Properties panel which represents the function name of the node implementation.
pub fn implementation_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
self.reference(node_id, network_path)
.map(|identifier| identifier.implementation_name_from_identifier())
.unwrap_or("Custom Node".to_string())
}
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 {
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> {
@@ -1134,12 +1142,13 @@ impl NodeNetworkInterface {
.node_metadata
.iter()
.filter_map(|(node_id, node_metadata)| {
if node_metadata
.persistent_metadata
.reference
.as_ref()
.is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[]) && self.is_layer(node_id, &[]))
{
if node_metadata.persistent_metadata.network_metadata.as_ref().is_some_and(|network_metadata| {
network_metadata
.persistent_metadata
.reference
.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))
} else {
None
@@ -1345,7 +1354,6 @@ impl NodeNetworkInterface {
node.skip_deduplication = old_node.skip_deduplication;
node.original_location = old_node.original_location;
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.node_type_metadata = if old_node.is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
@@ -3047,7 +3055,7 @@ impl NodeNetworkInterface {
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
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 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]) {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
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());
}
// 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
@@ -3347,7 +3349,9 @@ impl NodeNetworkInterface {
}
// 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
self.unload_node_click_targets(&node_id, &encapsulating_network_path);
@@ -3403,7 +3407,9 @@ impl NodeNetworkInterface {
return;
};
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
self.unload_outward_wires(&encapsulating_network_path);
@@ -3477,7 +3483,9 @@ impl NodeNetworkInterface {
return;
};
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
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);
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
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);
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
self.unload_outward_wires(&encapsulating_network_path);
@@ -3721,28 +3733,17 @@ impl NodeNetworkInterface {
let number_of_inputs = node.inputs.len();
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 {
let reference = metadata.persistent_metadata.reference.as_ref();
let definition = reference.and_then(|reference| resolve_document_node_type(reference));
let input_metadata = definition
let input_metadata = self
.reference(node_id, network_path)
.as_ref()
.and_then(resolve_document_node_type)
.and_then(|definition| definition.node_template.persistent_node_metadata.input_metadata.get(added_input_index))
.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());
}
}
/// 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
pub fn validate_output_names(&mut self, node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId]) {
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
pub fn replace_reference_name(&mut self, node_id: &NodeId, network_path: &[NodeId], reference_name: String) {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node metadata in replace_reference_name");
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts.
/// Only works with network nodes. Proto nodes use their ID as the reference.
pub fn set_reference(&mut self, node_id: &NodeId, network_path: &[NodeId], reference_name: Option<String>) {
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;
};
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
@@ -4397,7 +4402,10 @@ impl NodeNetworkInterface {
node_metadata.persistent_metadata.display_name.clone_from(&display_name);
// 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 {
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 let Some(first_layer) = LayerNodeIdentifier::ROOT_PARENT.children(&self.document_metadata).next()
&& 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)
{
parent = first_layer;
@@ -5974,6 +5984,12 @@ impl NodeNetworkMetadata {
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
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
#[serde(serialize_with = "graphene_std::vector::serialize_hashmap", deserialize_with = "graphene_std::vector::deserialize_hashmap")]
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.
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
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.
// 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.
/// 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.
#[serde(default)]
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.
@@ -6463,7 +6475,9 @@ mod network_interface_tests {
async fn copy_isolated_node() {
let mut editor = EditorTestUtils::create();
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;
let frontend_messages = editor.handle_message(NodeGraphMessage::Copy).await;
let serialized_nodes = frontend_messages
@@ -19,13 +19,14 @@ pub struct DocumentNodePersistentMetadataInputNames {
pub network_metadata: Option<NodeNetworkMetadata>,
}
impl From<DocumentNodePersistentMetadataInputNames> for DocumentNodePersistentMetadata {
impl From<DocumentNodePersistentMetadataInputNames> for DocumentNodePersistentMetadataPropertiesRow {
fn from(old: DocumentNodePersistentMetadataInputNames) -> Self {
DocumentNodePersistentMetadata {
input_metadata: Vec::new(),
DocumentNodePersistentMetadataPropertiesRow {
reference: old.reference,
input_properties: Vec::new(),
display_name: old.display_name,
output_names: old.output_names,
has_primary_output: old.has_primary_output,
locked: old.locked,
pinned: old.pinned,
node_type_metadata: old.node_type_metadata,
@@ -60,7 +61,7 @@ pub struct PropertiesRow {
pub input_description: String,
}
impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersistentMetadata {
impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersistentMetadataHasPrimaryOutput {
fn from(old: DocumentNodePersistentMetadataPropertiesRow) -> Self {
let mut input_metadata = Vec::new();
for properties_row in old.input_properties {
@@ -74,11 +75,12 @@ impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersisten
..Default::default()
})
}
DocumentNodePersistentMetadata {
DocumentNodePersistentMetadataHasPrimaryOutput {
reference: old.reference,
display_name: old.display_name,
input_metadata: Vec::new(),
output_names: old.output_names,
has_primary_output: old.has_primary_output,
locked: old.locked,
pinned: old.pinned,
node_type_metadata: old.node_type_metadata,
@@ -104,9 +106,9 @@ pub struct DocumentNodePersistentMetadataHasPrimaryOutput {
pub network_metadata: Option<NodeNetworkMetadata>,
}
impl From<DocumentNodePersistentMetadataHasPrimaryOutput> for DocumentNodePersistentMetadata {
impl From<DocumentNodePersistentMetadataHasPrimaryOutput> for DocumentNodePersistentMetadataStringReference {
fn from(old: DocumentNodePersistentMetadataHasPrimaryOutput) -> Self {
DocumentNodePersistentMetadata {
DocumentNodePersistentMetadataStringReference {
reference: old.reference,
display_name: old.display_name,
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>
where
D: serde::Deserializer<'de>,
@@ -126,17 +171,31 @@ where
use serde::Deserialize;
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);
};
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)),
}
Ok(current)
}
@@ -1,5 +1,6 @@
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
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 glam::DVec2;
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)]
pub struct LayerPanelEntry {
pub id: NodeId,
pub reference: String,
pub reference: DefinitionIdentifier,
pub alias: String,
#[serde(rename = "inSelectedNetwork")]
pub in_selected_network: bool,
@@ -2,6 +2,7 @@ use super::network_interface::NodeNetworkInterface;
use crate::consts::{ROTATE_INCREMENT, SCALE_INCREMENT};
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::node_graph::document_node_definitions::DefinitionIdentifier;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
@@ -53,7 +54,7 @@ impl OriginalTransforms {
/// Gets the transform from the most downstream transform node
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)?;
Some(transform_utils::get_current_transform(&document_node.inputs))
@@ -1,7 +1,7 @@
// TODO: Eventually remove this document upgrade code
// 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::network_interface::{InputConnector, NodeTemplate, OutputConnector};
use crate::messages::prelude::DocumentMessageHandler;
@@ -734,12 +734,7 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
},
NodeReplacement {
node: graphene_std::vector::auto_tangents::IDENTIFIER,
aliases: &[
"graphene_core::vector::vector_nodes::AutoTangentsNode",
"graphene_core::vector::AutoTangentsNode",
"graphene_core::vector::GenerateHandlesNode",
"graphene_core::vector::RemoveHandlesNode",
],
aliases: &["graphene_core::vector::vector_nodes::AutoTangentsNode", "graphene_core::vector::AutoTangentsNode"],
},
NodeReplacement {
node: graphene_std::vector::bevel::IDENTIFIER,
@@ -919,7 +914,11 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
},
NodeReplacement {
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 {
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() {
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)) {
let mut default_template = NodeTemplate::default();
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<()> {
if reset_node_definitions_on_open && let Some(Some(reference)) = document.network_interface.reference(node_id, network_path) {
let node_definition = resolve_document_node_type(reference)?;
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)?;
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
let reference = document.network_interface.reference(node_id, network_path).cloned().flatten()?;
let reference = &reference;
let reference = document.network_interface.reference(node_id, network_path)?;
let inputs_count = node.inputs.len();
// Upgrade Stroke node to reorder parameters and add "Align" and "Paint Order" (#2644)
if reference == "Stroke" && inputs_count == 8 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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 old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
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);
}
// 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
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
let identifier = document
.network_interface
.implementation(node_id, network_path)
.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;
}
@@ -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
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.");
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
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.");
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
if reference == "Text" && inputs_count != 11 {
let mut template = resolve_document_node_type(reference)?.default_node_template();
if reference == DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER) && inputs_count != 11 {
let mut template: NodeTemplate = resolve_document_node_type(&reference)?.default_node_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)?;
@@ -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
if (reference == "Sine" || reference == "Cosine" || reference == "Tangent") && inputs_count == 1 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
if inputs_count == 1
&& (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);
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
if (reference == "Tangent on Path") && inputs_count == 4 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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();
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)?;
@@ -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
if reference == "Modulo" && inputs_count == 2 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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();
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)?;
@@ -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
if reference == "Mirror" && inputs_count == 3 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::mirror::IDENTIFIER) && inputs_count == 3 {
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)?;
@@ -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`
if reference == "Mirror" && inputs_count == 4 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::mirror::IDENTIFIER) && 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);
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"
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);
document
.network_interface
.set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]);
}
if reference == "Image" && inputs_count == 1 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
// Insert a new empty input for the image
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 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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();
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)?;
@@ -1323,8 +1323,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
}
}
if reference == "Instance on Points" && inputs_count == 2 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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();
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)?;
@@ -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);
}
if reference == "Morph" && 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);
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();
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();
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)?;
@@ -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);
}
if reference == "Flatten Vector Elements" {
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.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();
if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::RemoveHandlesNode")) {
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)?;
@@ -1381,12 +1358,10 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document
.network_interface
.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" {
let mut node_template = resolve_document_node_type("Auto-Tangents")?.default_node_template();
if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::GenerateHandlesNode")) {
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)?;
@@ -1396,12 +1371,10 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document
.network_interface
.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 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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();
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)?;
@@ -1415,8 +1388,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
);
}
if reference == "Spatial Merge by Distance" {
let mut node_template = resolve_document_node_type("Merge by Distance")?.default_node_template();
if reference == DefinitionIdentifier::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::SpatialMergeByDistanceNode")) {
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);
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),
network_path,
);
document.network_interface.replace_reference_name(node_id, network_path, "Merge by Distance".to_string());
}
if reference == "Sample Points" && inputs_count == 5 {
let mut node_template = resolve_document_node_type("Sample Polyline")?.default_node_template();
if reference == DefinitionIdentifier::Network("Sample Points".into()) && inputs_count == 5 {
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);
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, 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.replace_reference_name(node_id, network_path, "Sample Polyline".to_string());
}
// 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
let quantity_value = document
.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")
if reference == "Grid" && inputs_count == 6 {
let node_definition = resolve_document_node_type(reference)?;
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::grid::IDENTIFIER) && inputs_count == 6 {
let node_definition = resolve_document_node_type(&reference)?;
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)?;
@@ -1497,8 +1466,8 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
}
// Add the "Depth" parameter to the "Instance Index" node
if reference == "Instance Index" && inputs_count == 0 {
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
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();
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);
@@ -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
if reference == "Transform" && node.inputs.get(6).is_none() {
let mut node_template = resolve_document_node_type("Transform")?.default_node_template();
if reference == DefinitionIdentifier::Network("Transform".into()) && node.inputs.get(6).is_none() {
let mut node_template = resolve_network_node_type("Transform")?.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)?;
@@ -1547,7 +1516,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
}
NodeInput::Node { .. } => {
// 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");
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 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.
if reference == "Morph" && inputs_count == 3 {
// Old signature:
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::morph::IDENTIFIER) && (inputs_count == 3 || inputs_count == 4) {
// 3 inputs - old signature (#3405):
// 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:
// 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)?;
// 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");
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)
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 context_features = node_definition.node_template.document_node.context_features;
@@ -1678,7 +1650,7 @@ mod tests {
NODE_REPLACEMENTS.iter().for_each(|node| {
*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() {
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::portfolio::document::DocumentMessageContext;
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::resolve_document_node_type;
use crate::messages::portfolio::document::node_graph::document_node_definitions::{self, resolve_network_node_type};
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::nodes::SelectedNodes;
@@ -522,7 +521,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// Ensure each node has the metadata for its inputs
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_display_name_metadata(node_id, &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();
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");
continue;
};
@@ -2,6 +2,7 @@ use crate::consts::GIZMO_HIDE_THRESHOLD;
use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD};
use crate::messages::frontend::utility_types::MouseCursorIcon;
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::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector};
use crate::messages::prelude::FrontendMessage;
@@ -331,7 +332,8 @@ impl PointRadiusHandle {
fn calculate_snap_radii(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, radius_index: usize) -> Vec<f64> {
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;
};
@@ -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) {
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;
};
@@ -1,5 +1,5 @@
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::network_interface::{FlowType, InputConnector, NodeNetworkInterface, NodeTemplate};
use crate::messages::prelude::*;
@@ -24,9 +24,13 @@ pub fn find_spline(document: &DocumentMessageHandler, layer: LayerNodeIdentifier
document
.network_interface
.upstream_flow_back_from_nodes([layer.to_node()].to_vec(), &[], FlowType::HorizontalFlow)
.map(|node_id| (document.network_interface.reference(&node_id, &[]).unwrap(), node_id))
.take_while(|(reference, _)| reference.as_ref().is_some_and(|node_ref| node_ref != "Path"))
.find(|(reference, _)| reference.as_ref().is_some_and(|node_ref| node_ref == "Spline"))
.map(|node_id| (document.network_interface.reference(&node_id, &[]), node_id))
.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 == DefinitionIdentifier::ProtoNode(graphene_std::vector::spline::IDENTIFIER))
})
.map(|node| node.1)
}
@@ -73,7 +77,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
// Merge the inputs of the two layers
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")
.default_node_template();
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
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")
.default_node_template();
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
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")
.default_node_template();
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.
if current_and_other_layer_is_spline {
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")
.default_node_template();
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
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")
.default_node_template();
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> {
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)
} else {
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> {
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 {
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> {
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 {
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.
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 {
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.
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 {
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> {
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 {
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> {
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 {
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> {
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> {
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> {
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> {
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> {
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> {
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> {
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> {
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> {
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> {
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> {
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> {
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
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::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> {
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)
} else {
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.
pub fn is_layer_fed_by_node_of_name(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, node_name: &str) -> bool {
NodeGraphLayer::new(layer, network_interface).find_node_inputs(node_name).is_some()
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(identifier).is_some()
}
/// 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
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()
.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
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
self.horizontal_layer_flow()
.skip(1)
.take_while(|node_id| !self.network_interface.is_layer(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
@@ -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.
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
self.horizontal_layer_flow()
.skip(1)
.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))
}
/// 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)
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
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey;
use super::*;
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::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState};
@@ -133,7 +133,7 @@ pub struct Arc;
impl Arc {
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([
None,
Some(NodeInput::value(TaggedValue::F64(0.5), false)),
@@ -1,6 +1,6 @@
use super::shape_utility::ShapeToolModifierKey;
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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -16,7 +16,7 @@ pub struct Arrow;
impl Arrow {
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);
node_type.node_template_input_override([
None,
@@ -1,5 +1,5 @@
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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -82,7 +82,7 @@ pub struct Circle;
impl Circle {
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))])
}
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey;
use super::*;
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::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils;
@@ -16,7 +16,7 @@ pub struct Ellipse;
impl Ellipse {
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))])
}
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey;
use super::*;
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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -86,7 +86,7 @@ pub struct Grid;
impl Grid {
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([
None,
Some(NodeInput::value(TaggedValue::GridType(grid_type), false)),
@@ -1,6 +1,6 @@
use super::shape_utility::ShapeToolModifierKey;
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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -37,7 +37,8 @@ pub struct Line;
impl Line {
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([
None,
Some(NodeInput::value(TaggedValue::DVec2(document.metadata().document_to_viewport.transform_point2(drag_start)), false)),
@@ -88,7 +89,8 @@ impl Line {
.selected_nodes()
.selected_visible_and_unlocked_layers(&document.network_interface)
.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 {
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 {
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;
};
@@ -216,7 +218,7 @@ mod test_line_tool {
.selected_nodes()
.selected_visible_and_unlocked_layers(network_interface)
.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 {
return None;
};
@@ -1,7 +1,7 @@
use super::shape_utility::{ShapeToolModifierKey, update_radius_sign};
use super::*;
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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -109,7 +109,8 @@ pub struct Polygon;
impl Polygon {
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))])
}
@@ -167,8 +168,8 @@ impl Polygon {
};
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface)
.find_node_inputs("Regular Polygon")
.or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star"))
.find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER))
.or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::star::IDENTIFIER)))
else {
return;
};
@@ -1,7 +1,7 @@
use super::shape_utility::ShapeToolModifierKey;
use super::*;
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::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils;
@@ -16,7 +16,7 @@ pub struct Rectangle;
impl Rectangle {
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))])
}
@@ -2,6 +2,7 @@ use super::ShapeToolData;
use crate::consts::{ARC_SWEEP_GIZMO_RADIUS, ARC_SWEEP_GIZMO_TEXT_HEIGHT};
use crate::messages::frontend::utility_types::MouseCursorIcon;
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::utility_types::document_metadata::LayerNodeIdentifier;
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 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 {
let Some(polygon_node_id) = new_layer.upstream_node_id_from_name("Regular Polygon") else { return };
if new_layer
.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 {
input_connector: InputConnector::node(polygon_node_id, 2),
@@ -167,8 +175,15 @@ pub fn update_radius_sign(end: DVec2, start: DVec2, layer: LayerNodeIdentifier,
return;
}
if new_layer.find_input("Star", 1).unwrap_or(&TaggedValue::U32(0)).to_u32() % 2 == 1 {
let Some(star_node_id) = new_layer.upstream_node_id_from_name("Star") else { return };
if new_layer
.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 {
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.
/// Returns an option of (sides, radius1, radius2).
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))) =
(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.
/// Returns an option of (sides, radius).
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 {
return None;
@@ -263,7 +279,7 @@ pub fn extract_polygon_parameters(layer: Option<LayerNodeIdentifier>, document:
/// Extract the node input values of an arc.
/// 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)> {
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))) = (
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.
/// Returns an option of (radius).
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 {
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)> {
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))) = (
node_inputs.get(GridTypeInput::INDEX)?.as_value(),
@@ -1,6 +1,6 @@
use super::*;
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::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer};
@@ -24,7 +24,8 @@ impl Spiral {
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([
None,
Some(NodeInput::value(TaggedValue::SpiralType(spiral_type), false)),
@@ -62,7 +63,8 @@ impl Spiral {
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;
};
@@ -93,7 +95,8 @@ impl Spiral {
pub fn update_turns(decrease: bool, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
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;
};
@@ -1,7 +1,7 @@
use super::shape_utility::{ShapeToolModifierKey, update_radius_sign};
use super::*;
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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
@@ -109,7 +109,8 @@ pub struct Star;
impl Star {
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([
None,
Some(NodeInput::value(TaggedValue::U32(vertices), false)),
@@ -1,6 +1,7 @@
use super::snapping::{SnapCandidatePoint, SnapData, SnapManager};
use super::transformation_cage::{BoundingBoxManager, SizeSnapData};
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::network_interface::{NodeNetworkInterface, OutputConnector};
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
// (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()
{
return None;
@@ -1,7 +1,7 @@
use super::tool_prelude::*;
use crate::consts::DEFAULT_BRUSH_SIZE;
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::network_interface::FlowType;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
@@ -316,7 +316,7 @@ impl BrushToolData {
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 Some(TaggedValue::BrushStrokes(strokes)) = points_input.as_value() else { continue };
self.strokes.clone_from(strokes);
@@ -324,7 +324,7 @@ impl BrushToolData {
return Some(layer);
}
if *reference == Some("Transform".to_string()) {
if reference == DefinitionIdentifier::Network("Transform".into()) {
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 {
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();
responses.add(GraphOperationMessage::NewCustomLayer {
@@ -1,6 +1,6 @@
use super::tool_prelude::*;
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_types::OverlayContext;
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 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 nodes = vec![(NodeId(0), node)];
@@ -402,7 +402,7 @@ mod test_freehand {
.filter_map(|layer| {
let graph_layer = NodeGraphLayer::new(layer, &document.network_interface);
// 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 transform = document.metadata().transform_to_viewport(layer);
@@ -7,7 +7,7 @@ use crate::consts::{
use crate::messages::clipboard::utility_types::ClipboardContent;
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::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_types::{DrawHandles, OverlayContext};
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) {
layer
} 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");
continue;
};
@@ -1,7 +1,7 @@
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::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_types::{DrawHandles, OverlayContext};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@@ -1282,7 +1282,7 @@ impl PenToolData {
}
// 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 parent = document.new_layer_bounding_artboard(input, viewport);
@@ -4,6 +4,7 @@ use super::tool_prelude::*;
use crate::consts::*;
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::node_graph::document_node_definitions::DefinitionIdentifier;
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::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
@@ -624,7 +625,7 @@ impl Fsm for SelectToolFsmState {
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);
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);
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() {
// 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(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.
/// If the layer is text, the text tool is selected.
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(TextToolMessage::EditSelected);
}
@@ -1,7 +1,7 @@
use super::tool_prelude::*;
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::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_types::OverlayContext;
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 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 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 nodes = vec![(NodeId(1), path_node), (NodeId(0), spline_node)];
@@ -3,6 +3,7 @@
use super::tool_prelude::*;
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::node_graph::document_node_definitions::DefinitionIdentifier;
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::network_interface::InputConnector;
@@ -512,7 +513,7 @@ impl TextToolData {
document
.metadata()
.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| {
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);
@@ -541,7 +542,7 @@ fn can_edit_selected(document: &DocumentMessageHandler) -> Option<LayerNodeIdent
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;
}
@@ -610,7 +611,7 @@ impl Fsm for TextToolFsmState {
// TODO: implement bounding box for multiple layers
let selected = document.network_interface.selected_nodes();
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 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 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() {
responses.add(DocumentMessage::StartTransaction);
@@ -710,7 +711,7 @@ impl Fsm for TextToolFsmState {
// This ensures the cursor only changes if a layer is selected
let selected = document.network_interface.selected_nodes();
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
.bounding_box_manager
@@ -754,6 +754,7 @@ fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &D
mod test_transform_layer {
use crate::messages::portfolio::document::graph_operation::transform_utils;
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::prelude::Message;
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 network_interface = &document.network_interface;
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)?;
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::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
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::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::Key;
@@ -307,11 +308,11 @@ impl EditorTestUtils {
.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();
self.handle_message(NodeGraphMessage::CreateNodeFromContextMenu {
node_id: Some(node_id),
node_type: name.into(),
node_type,
xy: None,
add_transaction: true,
})
@@ -340,6 +341,7 @@ pub mod test_prelude {
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_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::document_metadata::LayerNodeIdentifier;
pub use crate::messages::prelude::*;