Separate svelte node graph rendering

This commit is contained in:
Adam
2025-11-17 03:22:38 -08:00
parent 86861ab1bb
commit b50062806c
27 changed files with 2302 additions and 1452 deletions
+1
View File
@@ -53,6 +53,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
/// Since we don't need to update the frontend multiple times per frame, /// Since we don't need to update the frontend multiple times per frame,
/// we have a set of messages which we will buffer until the next frame is requested. /// we have a set of messages which we will buffer until the next frame is requested.
const FRONTEND_UPDATE_MESSAGES: &[MessageDiscriminant] = &[ const FRONTEND_UPDATE_MESSAGES: &[MessageDiscriminant] = &[
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel( MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
PropertiesPanelMessageDiscriminant::Refresh, PropertiesPanelMessageDiscriminant::Refresh,
))), ))),
@@ -2,14 +2,14 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform; use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::utility_types::{ use crate::messages::portfolio::document::node_graph::utility_types::{
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphError, Transform, BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendExports, FrontendImport, FrontendNodeOld, FrontendNodeType, NodeGraphError,
}; };
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer}; use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate}; use crate::messages::portfolio::document::utility_types::wires::{WirePathInProgress, WirePathUpdateOld};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::utility_types::HintData; use crate::messages::tool::utility_types::HintData;
use glam::IVec2;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graphene_std::node_graph_overlay::types::{FrontendXY, NodeGraphTransform};
use graphene_std::raster::Image; use graphene_std::raster::Image;
use graphene_std::raster::color::Color; use graphene_std::raster::color::Color;
use graphene_std::text::{Font, TextAlign}; use graphene_std::text::{Font, TextAlign};
@@ -131,24 +131,23 @@ pub enum FrontendMessage {
}, },
UpdateImportsExports { UpdateImportsExports {
/// If the primary import is not visible, then it is None. /// If the primary import is not visible, then it is None.
imports: Vec<Option<FrontendGraphOutput>>, imports: Vec<Option<FrontendImport>>,
/// If the primary export is not visible, then it is None. exports: FrontendExports,
exports: Vec<Option<FrontendGraphInput>>,
/// The primary import location. /// The primary import location.
#[serde(rename = "importPosition")] #[serde(rename = "importPosition")]
import_position: IVec2, import_position: FrontendXY,
/// The primary export location. /// The primary export location.
#[serde(rename = "exportPosition")] #[serde(rename = "exportPosition")]
export_position: IVec2, export_position: FrontendXY,
/// The document network does not have an add import or export button. /// The document network does not have an add import or export button.
#[serde(rename = "addImportExport")] #[serde(rename = "addImportExport")]
add_import_export: bool, add_import_export: bool,
}, },
UpdateInSelectedNetwork { UpdateInSelectedNetworkOld {
#[serde(rename = "inSelectedNetwork")] #[serde(rename = "inSelectedNetwork")]
in_selected_network: bool, in_selected_network: bool,
}, },
UpdateBox { UpdateNodeGraphSelectionBox {
#[serde(rename = "box")] #[serde(rename = "box")]
box_selection: Option<BoxSelection>, box_selection: Option<BoxSelection>,
}, },
@@ -185,7 +184,7 @@ pub enum FrontendMessage {
#[serde(rename = "exportIndex")] #[serde(rename = "exportIndex")]
index: Option<usize>, index: Option<usize>,
}, },
UpdateLayerWidths { UpdateLayerWidthsOld {
#[serde(rename = "layerWidths")] #[serde(rename = "layerWidths")]
layer_widths: HashMap<NodeId, u32>, layer_widths: HashMap<NodeId, u32>,
#[serde(rename = "chainWidths")] #[serde(rename = "chainWidths")]
@@ -256,7 +255,7 @@ pub enum FrontendMessage {
#[serde(rename = "setColorChoice")] #[serde(rename = "setColorChoice")]
set_color_choice: Option<String>, set_color_choice: Option<String>,
}, },
UpdateGraphFadeArtwork { UpdateGraphFadeArtworkOld {
percentage: f64, percentage: f64,
}, },
UpdateInputHints { UpdateInputHints {
@@ -286,29 +285,33 @@ pub enum FrontendMessage {
UpdateMouseCursor { UpdateMouseCursor {
cursor: MouseCursorIcon, cursor: MouseCursorIcon,
}, },
UpdateNodeGraphNodes { UpdateNodeGraphNodesOld {
nodes: Vec<FrontendNode>, nodes: Vec<FrontendNodeOld>,
},
UpdateRenderNativeNodeGraph {
#[serde(rename = "renderNativeNodeGraph")]
render_native_node_graph: bool,
}, },
UpdateNodeGraphError { UpdateNodeGraphError {
error: Option<NodeGraphError>, error: Option<NodeGraphError>,
}, },
UpdateVisibleNodes { UpdateVisibleNodesOld {
nodes: Vec<NodeId>, nodes: Vec<NodeId>,
}, },
UpdateNodeGraphWires { UpdateNodeGraphWiresOld {
wires: Vec<WirePathUpdate>, wires: Vec<WirePathUpdateOld>,
}, },
ClearAllNodeGraphWires, ClearAllNodeGraphWiresOld,
UpdateNodeGraphControlBarLayout { UpdateNodeGraphControlBarLayout {
#[serde(rename = "layoutTarget")] #[serde(rename = "layoutTarget")]
layout_target: LayoutTarget, layout_target: LayoutTarget,
diff: Vec<WidgetDiff>, diff: Vec<WidgetDiff>,
}, },
UpdateNodeGraphSelection { UpdateNodeGraphSelectionOld {
selected: Vec<NodeId>, selected: Vec<NodeId>,
}, },
UpdateNodeGraphTransform { UpdateNodeGraphTransform {
transform: Transform, transform: NodeGraphTransform,
}, },
UpdateNodeThumbnail { UpdateNodeThumbnail {
id: NodeId, id: NodeId,
@@ -334,8 +337,8 @@ pub enum FrontendMessage {
diff: Vec<WidgetDiff>, diff: Vec<WidgetDiff>,
}, },
UpdateWirePathInProgress { UpdateWirePathInProgress {
#[serde(rename = "wirePath")] #[serde(rename = "wirePathInProgress")]
wire_path: Option<WirePath>, wire_path_in_progress: Option<WirePathInProgress>,
}, },
UpdateWorkingColorsLayout { UpdateWorkingColorsLayout {
#[serde(rename = "layoutTarget")] #[serde(rename = "layoutTarget")]
@@ -1,8 +1,8 @@
use crate::messages::input_mapper::utility_types::misc::ActionKeys; use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback; use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback;
use derivative::*; use derivative::*;
use graphene_std::node_graph_overlay::types::FrontendGraphDataType;
use graphene_std::vector::style::FillChoice; use graphene_std::vector::style::FillChoice;
use graphite_proc_macros::WidgetBuilder; use graphite_proc_macros::WidgetBuilder;
@@ -1,5 +1,4 @@
use super::node_graph::document_node_definitions; use super::node_graph::document_node_definitions;
use super::node_graph::utility_types::Transform;
use super::overlays::utility_types::Pivot; use super::overlays::utility_types::Pivot;
use super::utility_types::error::EditorError; use super::utility_types::error::EditorError;
use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState}; use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState};
@@ -31,6 +30,7 @@ use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork}; use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
use graphene_std::math::quad::Quad; use graphene_std::math::quad::Quad;
use graphene_std::node_graph_overlay::types::NodeGraphTransform;
use graphene_std::path_bool::{boolean_intersect, path_bool_lib}; use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
use graphene_std::raster::BlendMode; use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Raster; use graphene_std::raster_types::Raster;
@@ -55,6 +55,7 @@ pub struct DocumentMessageContext<'a> {
pub data_panel_open: bool, pub data_panel_open: bool,
pub layers_panel_open: bool, pub layers_panel_open: bool,
pub properties_panel_open: bool, pub properties_panel_open: bool,
pub render_native_node_graph: bool,
pub viewport: &'a ViewportMessageHandler, pub viewport: &'a ViewportMessageHandler,
} }
@@ -201,6 +202,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
data_panel_open, data_panel_open,
layers_panel_open, layers_panel_open,
properties_panel_open, properties_panel_open,
render_native_node_graph,
} = context; } = context;
match message { match message {
@@ -263,6 +265,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
preferences, preferences,
layers_panel_open, layers_panel_open,
viewport, viewport,
render_native_node_graph,
}, },
); );
} }
@@ -470,7 +473,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
DocumentMessage::EnterNestedNetwork { node_id } => { DocumentMessage::EnterNestedNetwork { node_id } => {
self.breadcrumb_network_path.push(node_id); self.breadcrumb_network_path.push(node_id);
self.selection_network_path.clone_from(&self.breadcrumb_network_path); self.selection_network_path.clone_from(&self.breadcrumb_network_path);
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::UnloadWiresOld);
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::ZoomCanvasToFitAll); responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(NodeGraphMessage::UpdateNodeGraphWidth); responses.add(NodeGraphMessage::UpdateNodeGraphWidth);
@@ -490,7 +493,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None }); responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None });
self.node_graph_handler.wire_in_progress_from_connector = None; self.node_graph_handler.wire_in_progress_from_connector = None;
self.node_graph_handler.wire_in_progress_to_connector = None; self.node_graph_handler.wire_in_progress_to_connector = None;
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
} else if !self.breadcrumb_network_path.is_empty() { } else if !self.breadcrumb_network_path.is_empty() {
// Exit one level up if inside a nested network // Exit one level up if inside a nested network
responses.add(DocumentMessage::ExitNestedNetwork { steps_back: 1 }); responses.add(DocumentMessage::ExitNestedNetwork { steps_back: 1 });
@@ -503,7 +506,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
self.breadcrumb_network_path.pop(); self.breadcrumb_network_path.pop();
self.selection_network_path.clone_from(&self.breadcrumb_network_path); self.selection_network_path.clone_from(&self.breadcrumb_network_path);
} }
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::UnloadWiresOld);
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::PTZUpdate); responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::UpdateNodeGraphWidth); responses.add(NodeGraphMessage::UpdateNodeGraphWidth);
@@ -560,7 +563,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
self.graph_view_overlay_open = open; self.graph_view_overlay_open = open;
responses.add(FrontendMessage::UpdateGraphViewOverlay { open }); responses.add(FrontendMessage::UpdateGraphViewOverlay { open });
responses.add(FrontendMessage::UpdateGraphFadeArtwork { responses.add(FrontendMessage::UpdateGraphFadeArtworkOld {
percentage: self.graph_fade_artwork_percentage, percentage: self.graph_fade_artwork_percentage,
}); });
@@ -570,20 +573,21 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderRulers);
responses.add(DocumentMessage::RenderScrollbars); responses.add(DocumentMessage::RenderScrollbars);
if opened { if opened {
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::UnloadWiresOld);
} }
if open { if open {
responses.add(ToolMessage::DeactivateTools); responses.add(ToolMessage::DeactivateTools);
responses.add(OverlaysMessage::Draw); // Clear the overlays responses.add(OverlaysMessage::Draw); // Clear the overlays
responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. }); responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. });
responses.add(NodeGraphMessage::UpdateGraphBarRight); responses.add(NodeGraphMessage::UpdateGraphBarRight);
responses.add(NodeGraphMessage::SendGraph);
responses.add(NodeGraphMessage::UpdateHints); responses.add(NodeGraphMessage::UpdateHints);
responses.add(NodeGraphMessage::UpdateEdges); responses.add(NodeGraphMessage::UpdateEdges);
} else { } else {
responses.add(ToolMessage::ActivateTool { tool_type: *current_tool }); responses.add(ToolMessage::ActivateTool { tool_type: *current_tool });
responses.add(OverlaysMessage::Draw); // Redraw overlays when graph is closed responses.add(OverlaysMessage::Draw); // Redraw overlays when graph is closed
} }
responses.add(NodeGraphMessage::SendGraph);
} }
DocumentMessage::GraphViewOverlayToggle => { DocumentMessage::GraphViewOverlayToggle => {
responses.add(DocumentMessage::GraphViewOverlay { open: !self.graph_view_overlay_open }); responses.add(DocumentMessage::GraphViewOverlay { open: !self.graph_view_overlay_open });
@@ -1196,7 +1200,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
} }
responses.add(PropertiesPanelMessage::Refresh); responses.add(PropertiesPanelMessage::Refresh);
responses.add(NodeGraphMessage::UpdateLayerPanel); responses.add(NodeGraphMessage::UpdateLayerPanel);
responses.add(NodeGraphMessage::UpdateInSelectedNetwork); responses.add(NodeGraphMessage::SendGraph);
} }
DocumentMessage::SetBlendModeForSelectedLayers { blend_mode } => { DocumentMessage::SetBlendModeForSelectedLayers { blend_mode } => {
for layer in self.network_interface.selected_nodes().selected_layers_except_artboards(&self.network_interface) { for layer in self.network_interface.selected_nodes().selected_layers_except_artboards(&self.network_interface) {
@@ -1205,7 +1209,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
} }
DocumentMessage::SetGraphFadeArtwork { percentage } => { DocumentMessage::SetGraphFadeArtwork { percentage } => {
self.graph_fade_artwork_percentage = percentage; self.graph_fade_artwork_percentage = percentage;
responses.add(FrontendMessage::UpdateGraphFadeArtwork { percentage }); responses.add(FrontendMessage::UpdateGraphFadeArtworkOld { percentage });
if render_native_node_graph {
responses.add(NodeGraphMessage::SendGraph);
}
} }
DocumentMessage::SetNodePinned { node_id, pinned } => { DocumentMessage::SetNodePinned { node_id, pinned } => {
responses.add(DocumentMessage::AddTransaction); responses.add(DocumentMessage::AddTransaction);
@@ -1507,7 +1514,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(FrontendMessage::UpdateNodeGraphTransform { responses.add(FrontendMessage::UpdateNodeGraphTransform {
transform: Transform { transform: NodeGraphTransform {
scale: transform.matrix2.x_axis.x, scale: transform.matrix2.x_axis.x,
x: transform.translation.x, x: transform.translation.x,
y: transform.translation.y, y: transform.translation.y,
@@ -1971,7 +1978,7 @@ impl DocumentMessageHandler {
responses.add(NodeGraphMessage::ForceRunDocumentGraph); responses.add(NodeGraphMessage::ForceRunDocumentGraph);
// TODO: Remove once the footprint is used to load the imports/export distances from the edge // TODO: Remove once the footprint is used to load the imports/export distances from the edge
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::UnloadWiresOld);
Some(previous_network) Some(previous_network)
} }
@@ -2002,8 +2009,8 @@ impl DocumentMessageHandler {
responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::SelectedNodesUpdated);
responses.add(NodeGraphMessage::ForceRunDocumentGraph); responses.add(NodeGraphMessage::ForceRunDocumentGraph);
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::UnloadWiresOld);
responses.add(NodeGraphMessage::SendWires); responses.add(NodeGraphMessage::SendWiresOld);
Some(previous_network) Some(previous_network)
} }
@@ -141,10 +141,12 @@ pub enum NodeGraphMessage {
}, },
SendClickTargets, SendClickTargets,
EndSendClickTargets, EndSendClickTargets,
UnloadWires, UnloadWiresOld,
SendWires, SendWiresOld,
UpdateVisibleNodes, UpdateVisibleNodesOld,
SendGraph, SendGraph,
SendGraphOld,
SendGraphNew,
SetInputValue { SetInputValue {
node_id: NodeId, node_id: NodeId,
input_index: usize, input_index: usize,
@@ -232,7 +234,7 @@ pub enum NodeGraphMessage {
}, },
UpdateActionButtons, UpdateActionButtons,
UpdateGraphBarRight, UpdateGraphBarRight,
UpdateInSelectedNetwork, UpdateInSelectedNetworkOld,
UpdateHints, UpdateHints,
SendSelectedNodes, SendSelectedNodesOld,
} }
@@ -1,4 +1,4 @@
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode}; use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart};
use super::{document_node_definitions, node_properties}; use super::{document_node_definitions, node_properties};
use crate::consts::GRID_SIZE; use crate::consts::GRID_SIZE;
use crate::messages::input_mapper::utility_types::macros::action_keys; use crate::messages::input_mapper::utility_types::macros::action_keys;
@@ -6,14 +6,14 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::document_message_handler::navigation_controls; use crate::messages::portfolio::document::document_message_handler::navigation_controls;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext; use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, NodeGraphError}; use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendNodeOld, NodeGraphError};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
use crate::messages::portfolio::document::utility_types::network_interface::{ use crate::messages::portfolio::document::utility_types::network_interface::{
self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing,
}; };
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry}; use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire}; use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePathInProgress, WirePathUpdateOld, build_vector_wire};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode; use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
@@ -24,6 +24,7 @@ use crate::messages::viewport::Position;
use glam::{DAffine2, DVec2, IVec2}; use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
use graphene_std::math::math_ext::QuadExt; use graphene_std::math::math_ext::QuadExt;
use graphene_std::node_graph_overlay::types::{FrontendGraphDataType, FrontendXY};
use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath; use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath;
use graphene_std::*; use graphene_std::*;
use kurbo::{DEFAULT_ACCURACY, Shape}; use kurbo::{DEFAULT_ACCURACY, Shape};
@@ -44,6 +45,7 @@ pub struct NodeGraphMessageContext<'a> {
pub preferences: &'a PreferencesMessageHandler, pub preferences: &'a PreferencesMessageHandler,
pub layers_panel_open: bool, pub layers_panel_open: bool,
pub viewport: &'a ViewportMessageHandler, pub viewport: &'a ViewportMessageHandler,
pub render_native_node_graph: bool,
} }
#[derive(Debug, Clone, ExtractField)] #[derive(Debug, Clone, ExtractField)]
@@ -92,9 +94,9 @@ pub struct NodeGraphMessageHandler {
/// The end index of the moved connector /// The end index of the moved connector
end_index: Option<usize>, end_index: Option<usize>,
/// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend /// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend
frontend_nodes: Vec<NodeId>, frontend_nodes_old: Vec<NodeId>,
/// Used to keep track of what wires are sent to the front end so the old ones can be removed /// Used to keep track of what wires are sent to the front end so the old ones can be removed
frontend_wires: HashSet<(NodeId, usize)>, frontend_wires_old: HashSet<(NodeId, usize)>,
} }
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network. /// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
@@ -114,6 +116,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
preferences, preferences,
layers_panel_open, layers_panel_open,
viewport, viewport,
render_native_node_graph,
} = context; } = context;
match message { match message {
@@ -193,7 +196,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(MenuBarMessage::SendLayout); responses.add(MenuBarMessage::SendLayout);
responses.add(NodeGraphMessage::UpdateLayerPanel); responses.add(NodeGraphMessage::UpdateLayerPanel);
responses.add(PropertiesPanelMessage::Refresh); responses.add(PropertiesPanelMessage::Refresh);
responses.add(NodeGraphMessage::SendSelectedNodes); responses.add(NodeGraphMessage::SendSelectedNodesOld);
responses.add(ArtboardToolMessage::UpdateSelectedArtboard); responses.add(ArtboardToolMessage::UpdateSelectedArtboard);
responses.add(DocumentMessage::DocumentStructureChanged); responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
@@ -298,7 +301,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.wire_in_progress_type = FrontendGraphDataType::General; self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
} }
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
responses.add(FrontendMessage::UpdateContextMenuInformation { responses.add(FrontendMessage::UpdateContextMenuInformation {
context_menu_information: self.context_menu.clone(), context_menu_information: self.context_menu.clone(),
}); });
@@ -471,7 +474,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
} }
responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(NodeGraphMessage::SendWires); responses.add(NodeGraphMessage::SendWiresOld);
} }
NodeGraphMessage::ExposePrimaryExport { exposed } => { NodeGraphMessage::ExposePrimaryExport { exposed } => {
let export_connector: InputConnector = InputConnector::Export(0); let export_connector: InputConnector = InputConnector::Export(0);
@@ -762,7 +765,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::SelectedNodesSet { responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: self.selection_before_pointer_down.clone(), nodes: self.selection_before_pointer_down.clone(),
}); });
responses.add(FrontendMessage::UpdateBox { box_selection: None }); responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None });
return; return;
} }
// Abort dragging a wire // Abort dragging a wire
@@ -771,7 +774,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.wire_in_progress_type = FrontendGraphDataType::General; self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
responses.add(DocumentMessage::AbortTransaction); responses.add(DocumentMessage::AbortTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
return; return;
} }
@@ -856,7 +859,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(FrontendMessage::UpdateContextMenuInformation { responses.add(FrontendMessage::UpdateContextMenuInformation {
context_menu_information: self.context_menu.clone(), context_menu_information: self.context_menu.clone(),
}); });
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
} }
// Toggle visibility of clicked node and return // Toggle visibility of clicked node and return
@@ -1063,14 +1066,14 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
to_connector_is_layer, to_connector_is_layer,
GraphWireStyle::Direct, GraphWireStyle::Direct,
); );
let path_string = vector_wire.to_svg(); let wire_path = WirePathInProgress {
let wire_path = WirePath { wire: vector_wire.to_svg(),
path_string,
data_type: self.wire_in_progress_type, data_type: self.wire_in_progress_type,
thick: false, thick: false,
dashed: false,
}; };
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) }); responses.add(FrontendMessage::UpdateWirePathInProgress {
wire_path_in_progress: Some(wire_path),
});
} }
} else if let Some((drag_start, dragged)) = &mut self.drag_start { } else if let Some((drag_start, dragged)) = &mut self.drag_start {
if drag_start.start_x != point.x || drag_start.start_y != point.y { if drag_start.start_x != point.x || drag_start.start_y != point.y {
@@ -1321,7 +1324,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
return None; return None;
} }
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?; let wire = network_interface.wire_from_input_new(&input, preferences.graph_wire_style, selection_network_path)?;
let thick = network_interface.wire_is_thick(&input, selection_network_path);
let node_bbox = kurbo::Rect::new(node_bbox[0].x, node_bbox[0].y, node_bbox[1].x, node_bbox[1].y).to_path(DEFAULT_ACCURACY); let node_bbox = kurbo::Rect::new(node_bbox[0].x, node_bbox[0].y, node_bbox[1].x, node_bbox[1].y).to_path(DEFAULT_ACCURACY);
let inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None); let inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None);
@@ -1330,7 +1334,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
.segments() .segments()
.any(|segment| node_bbox.segments().filter_map(|segment| segment.as_line()).any(|line| !segment.intersect_line(line).is_empty())); .any(|segment| node_bbox.segments().filter_map(|segment| segment.as_line()).any(|line| !segment.intersect_line(line).is_empty()));
(intersect || inside).then_some((input, is_stack)) (intersect || inside).then_some((input, thick))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -1397,8 +1401,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.reordering_export = None; self.reordering_export = None;
self.reordering_import = None; self.reordering_import = None;
responses.add(DocumentMessage::EndTransaction); responses.add(DocumentMessage::EndTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
responses.add(FrontendMessage::UpdateBox { box_selection: None }); responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None });
responses.add(FrontendMessage::UpdateImportReorderIndex { index: None }); responses.add(FrontendMessage::UpdateImportReorderIndex { index: None });
responses.add(FrontendMessage::UpdateExportReorderIndex { index: None }); responses.add(FrontendMessage::UpdateExportReorderIndex { index: None });
self.update_node_graph_hints(responses); self.update_node_graph_hints(responses);
@@ -1601,18 +1605,18 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)), click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)),
}), }),
NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }), NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }),
NodeGraphMessage::UnloadWires => { NodeGraphMessage::UnloadWiresOld => {
for input in network_interface.node_graph_input_connectors(breadcrumb_network_path) { for input in network_interface.node_graph_input_connectors(breadcrumb_network_path) {
network_interface.unload_wire(&input, breadcrumb_network_path); network_interface.unload_wire_old(&input, breadcrumb_network_path);
} }
responses.add(FrontendMessage::ClearAllNodeGraphWires); responses.add(FrontendMessage::ClearAllNodeGraphWiresOld);
} }
NodeGraphMessage::SendWires => { NodeGraphMessage::SendWiresOld => {
let wires = self.collect_wires(network_interface, preferences.graph_wire_style, breadcrumb_network_path); let wires = self.collect_wires_old(network_interface, preferences.graph_wire_style, breadcrumb_network_path);
responses.add(FrontendMessage::UpdateNodeGraphWires { wires }); responses.add(FrontendMessage::UpdateNodeGraphWiresOld { wires });
} }
NodeGraphMessage::UpdateVisibleNodes => { NodeGraphMessage::UpdateVisibleNodesOld => {
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
return; return;
}; };
@@ -1621,7 +1625,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let document_bbox: [DVec2; 2] = viewport_bbox.map(|p| network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(p)); let document_bbox: [DVec2; 2] = viewport_bbox.map(|p| network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(p));
let mut nodes = Vec::new(); let mut nodes = Vec::new();
for node_id in &self.frontend_nodes { for node_id in &self.frontend_nodes_old {
let Some(node_bbox) = network_interface.node_bounding_box(node_id, breadcrumb_network_path) else { let Some(node_bbox) = network_interface.node_bounding_box(node_id, breadcrumb_network_path) else {
log::error!("Could not get bbox for node: {node_id:?}"); log::error!("Could not get bbox for node: {node_id:?}");
continue; continue;
@@ -1637,32 +1641,60 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
} }
} }
responses.add(FrontendMessage::UpdateVisibleNodes { nodes }); responses.add(FrontendMessage::UpdateVisibleNodesOld { nodes });
} }
NodeGraphMessage::SendGraph => { NodeGraphMessage::SendGraph => {
if render_native_node_graph {
responses.add(NodeGraphMessage::SendGraphNew);
} else {
responses.add(NodeGraphMessage::SendGraphOld);
}
}
NodeGraphMessage::SendGraphOld => {
responses.add(NodeGraphMessage::UpdateLayerPanel); responses.add(NodeGraphMessage::UpdateLayerPanel);
responses.add(DocumentMessage::DocumentStructureChanged); responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::Refresh); responses.add(PropertiesPanelMessage::Refresh);
if breadcrumb_network_path == selection_network_path && graph_view_overlay_open { if breadcrumb_network_path == selection_network_path && graph_view_overlay_open {
let nodes = self.collect_nodes(network_interface, breadcrumb_network_path); let nodes = self.collect_nodes_old(network_interface, breadcrumb_network_path);
self.frontend_nodes = nodes.iter().map(|node| node.id).collect(); self.frontend_nodes_old = nodes.iter().map(|node| node.id).collect();
responses.add(FrontendMessage::UpdateNodeGraphNodes { nodes }); responses.add(FrontendMessage::UpdateNodeGraphNodesOld { nodes });
responses.add(NodeGraphMessage::UpdateVisibleNodes); responses.add(NodeGraphMessage::UpdateVisibleNodesOld);
let error = self.node_graph_error(network_interface, breadcrumb_network_path); let error = self.node_graph_error(network_interface, breadcrumb_network_path);
responses.add(FrontendMessage::UpdateNodeGraphError { error }); responses.add(FrontendMessage::UpdateNodeGraphError { error });
let (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths(breadcrumb_network_path); let (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths_old(breadcrumb_network_path);
responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(FrontendMessage::UpdateLayerWidths { responses.add(FrontendMessage::UpdateLayerWidthsOld {
layer_widths, layer_widths,
chain_widths, chain_widths,
has_left_input_wire, has_left_input_wire,
}); });
responses.add(NodeGraphMessage::SendSelectedNodes); responses.add(NodeGraphMessage::SendSelectedNodesOld);
responses.add(NodeGraphMessage::UnloadWiresOld);
responses.add(NodeGraphMessage::SendWiresOld);
self.update_node_graph_hints(responses); self.update_node_graph_hints(responses);
} }
} }
NodeGraphMessage::SendGraphNew => {
responses.add(NodeGraphMessage::UpdateLayerPanel);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::Refresh);
responses.add(NodeGraphMessage::UpdateActionButtons);
// let nodes_to_render = network_interface.collect_nodes_new(&self.node_graph_errors, preferences.graph_wire_style, breadcrumb_network_path);
// let previewed_node = network_interface.previewed_node(breadcrumb_network_path);
// responses.add(FrontendMessage::UpdateNodeGraphRenderNew {
// nodes_to_render,
// open: graph_view_overlay_open,
// opacity: graph_fade_artwork_percentage,
// in_selected_network: selection_network_path == breadcrumb_network_path,
// previewed_node,
// });
responses.add(NodeGraphMessage::UpdateImportsExports);
let error = self.node_graph_error(network_interface, breadcrumb_network_path);
responses.add(FrontendMessage::UpdateNodeGraphError { error });
self.update_node_graph_hints(responses);
}
NodeGraphMessage::SetInputValue { node_id, input_index, value } => { NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
let input = NodeInput::value(value, false); let input = NodeInput::value(value, false);
responses.add(NodeGraphMessage::SetInput { responses.add(NodeGraphMessage::SetInput {
@@ -1729,7 +1761,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
} }
} }
responses.add(NodeGraphMessage::SendWires); responses.add(NodeGraphMessage::SendWiresOld);
} }
NodeGraphMessage::ToggleSelectedAsLayersOrNodes => { NodeGraphMessage::ToggleSelectedAsLayersOrNodes => {
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
@@ -1750,7 +1782,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
NodeGraphMessage::ShiftNodePosition { node_id, x, y } => { NodeGraphMessage::ShiftNodePosition { node_id, x, y } => {
network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path); network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path);
responses.add(NodeGraphMessage::SendWires); responses.add(NodeGraphMessage::SendWiresOld);
} }
NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => { NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => {
if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) { if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) {
@@ -1764,7 +1796,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}); });
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(NodeGraphMessage::SendWires); responses.add(NodeGraphMessage::SendWiresOld);
} }
NodeGraphMessage::SetDisplayName { NodeGraphMessage::SetDisplayName {
node_id, node_id,
@@ -1953,23 +1985,31 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
nodes: nodes.into_iter().collect::<Vec<_>>(), nodes: nodes.into_iter().collect::<Vec<_>>(),
}); });
} }
responses.add(FrontendMessage::UpdateBox { box_selection }) responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection })
} }
} }
NodeGraphMessage::UpdateImportsExports => { NodeGraphMessage::UpdateImportsExports => {
let imports = network_interface.frontend_imports(breadcrumb_network_path); let imports = network_interface.frontend_imports(preferences.graph_wire_style, breadcrumb_network_path);
let exports = network_interface.frontend_exports(breadcrumb_network_path); let exports = network_interface.frontend_exports(preferences.graph_wire_style, breadcrumb_network_path);
let Some((import_position, export_position)) = network_interface.import_export_position(breadcrumb_network_path) else { let Some((import_position, export_position)) = network_interface.import_export_position(breadcrumb_network_path) else {
log::error!("Could not get import export positions"); log::error!("Could not get import export positions");
return; return;
}; };
let import_position = FrontendXY {
x: import_position.x,
y: import_position.y,
};
let export_position = FrontendXY {
x: export_position.x,
y: export_position.y,
};
// Do not show the add import or add export button in the document network; // Do not show the add import or add export button in the document network;
let add_import_export = !breadcrumb_network_path.is_empty(); let add_import_export = !breadcrumb_network_path.is_empty();
responses.add(NodeGraphMessage::UpdateVisibleNodes); responses.add(NodeGraphMessage::UpdateVisibleNodesOld);
responses.add(NodeGraphMessage::SendWires);
responses.add(FrontendMessage::UpdateImportsExports { responses.add(FrontendMessage::UpdateImportsExports {
imports, imports,
exports, exports,
@@ -1977,6 +2017,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
export_position, export_position,
add_import_export, add_import_export,
}); });
responses.add(NodeGraphMessage::SendWiresOld)
} }
NodeGraphMessage::UpdateLayerPanel => { NodeGraphMessage::UpdateLayerPanel => {
@@ -2008,19 +2049,19 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.update_graph_bar_right(graph_fade_artwork_percentage, network_interface, breadcrumb_network_path, navigation_handler); self.update_graph_bar_right(graph_fade_artwork_percentage, network_interface, breadcrumb_network_path, navigation_handler);
self.send_node_bar_layout(responses); self.send_node_bar_layout(responses);
} }
NodeGraphMessage::UpdateInSelectedNetwork => responses.add(FrontendMessage::UpdateInSelectedNetwork { NodeGraphMessage::UpdateInSelectedNetworkOld => responses.add(FrontendMessage::UpdateInSelectedNetworkOld {
in_selected_network: selection_network_path == breadcrumb_network_path, in_selected_network: selection_network_path == breadcrumb_network_path,
}), }),
NodeGraphMessage::UpdateHints => { NodeGraphMessage::UpdateHints => {
self.update_node_graph_hints(responses); self.update_node_graph_hints(responses);
} }
NodeGraphMessage::SendSelectedNodes => { NodeGraphMessage::SendSelectedNodesOld => {
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(breadcrumb_network_path) else { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(breadcrumb_network_path) else {
log::error!("Could not get selected nodes in NodeGraphMessage::SendSelectedNodes"); log::error!("Could not get selected nodes in NodeGraphMessage::SendSelectedNodes");
return; return;
}; };
responses.add(NodeGraphMessage::UpdateActionButtons); responses.add(NodeGraphMessage::UpdateActionButtons);
responses.add(FrontendMessage::UpdateNodeGraphSelection { responses.add(FrontendMessage::UpdateNodeGraphSelectionOld {
selected: selected_nodes.selected_nodes().cloned().collect::<Vec<_>>(), selected: selected_nodes.selected_nodes().cloned().collect::<Vec<_>>(),
}); });
} }
@@ -2472,7 +2513,7 @@ impl NodeGraphMessageHandler {
} }
} }
fn collect_wires(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec<WirePathUpdate> { fn collect_wires_old(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec<WirePathUpdateOld> {
let mut added_wires = network_interface let mut added_wires = network_interface
.node_graph_input_connectors(breadcrumb_network_path) .node_graph_input_connectors(breadcrumb_network_path)
.iter() .iter()
@@ -2480,24 +2521,24 @@ impl NodeGraphMessageHandler {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let changed_wire_inputs = added_wires.iter().map(|update| (update.id, update.input_index)).collect::<Vec<_>>(); let changed_wire_inputs = added_wires.iter().map(|update| (update.id, update.input_index)).collect::<Vec<_>>();
self.frontend_wires.extend(changed_wire_inputs); self.frontend_wires_old.extend(changed_wire_inputs);
let mut orphaned_wire_inputs = self.frontend_wires.clone(); let mut orphaned_wire_inputs = self.frontend_wires_old.clone();
self.frontend_wires = network_interface self.frontend_wires_old = network_interface
.node_graph_wire_inputs(breadcrumb_network_path) .node_graph_wire_inputs(breadcrumb_network_path)
.iter() .iter()
.filter_map(|visible_wire_input| orphaned_wire_inputs.take(visible_wire_input)) .filter_map(|visible_wire_input| orphaned_wire_inputs.take(visible_wire_input))
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
added_wires.extend(orphaned_wire_inputs.into_iter().map(|(id, input_index)| WirePathUpdate { added_wires.extend(orphaned_wire_inputs.into_iter().map(|(id, input_index)| WirePathUpdateOld {
id, id,
input_index, input_index,
wire_path_update: None, wire_path_update: None,
})); }));
if let Some(wire_to_root) = network_interface.wire_to_root(graph_wire_style, breadcrumb_network_path) { if let Some(wire_to_root) = network_interface.wire_to_root_old(graph_wire_style, breadcrumb_network_path) {
added_wires.push(wire_to_root); added_wires.push(wire_to_root);
} else { } else {
added_wires.push(WirePathUpdate { added_wires.push(WirePathUpdateOld {
id: NodeId(u64::MAX), id: NodeId(u64::MAX),
input_index: u32::MAX as usize, input_index: u32::MAX as usize,
wire_path_update: None, wire_path_update: None,
@@ -2507,7 +2548,7 @@ impl NodeGraphMessageHandler {
added_wires added_wires
} }
fn collect_nodes(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec<FrontendNode> { fn collect_nodes_old(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec<FrontendNodeOld> {
let Some(network) = network_interface.nested_network(breadcrumb_network_path) else { let Some(network) = network_interface.nested_network(breadcrumb_network_path) else {
log::error!("Could not get nested network when collecting nodes"); log::error!("Could not get nested network when collecting nodes");
return Vec::new(); return Vec::new();
@@ -2520,18 +2561,18 @@ impl NodeGraphMessageHandler {
.input_from_connector(&primary_input_connector, breadcrumb_network_path) .input_from_connector(&primary_input_connector, breadcrumb_network_path)
.is_some_and(|input| input.is_exposed()) .is_some_and(|input| input.is_exposed())
{ {
network_interface.frontend_input_from_connector(&primary_input_connector, breadcrumb_network_path) network_interface.frontend_input_from_connector_old(&primary_input_connector, breadcrumb_network_path)
} else { } else {
None None
}; };
let exposed_inputs = (1..network_interface.number_of_inputs(&node_id, breadcrumb_network_path)) let exposed_inputs = (1..network_interface.number_of_inputs(&node_id, breadcrumb_network_path))
.filter_map(|input_index| network_interface.frontend_input_from_connector(&InputConnector::node(node_id, input_index), breadcrumb_network_path)) .filter_map(|input_index| network_interface.frontend_input_from_connector_old(&InputConnector::node(node_id, input_index), breadcrumb_network_path))
.collect(); .collect();
let primary_output = network_interface.frontend_output_from_connector(&OutputConnector::node(node_id, 0), breadcrumb_network_path); let primary_output = network_interface.frontend_output_from_connector_old(&OutputConnector::node(node_id, 0), breadcrumb_network_path);
let exposed_outputs = (1..network_interface.number_of_outputs(&node_id, breadcrumb_network_path)) let exposed_outputs = (1..network_interface.number_of_outputs(&node_id, breadcrumb_network_path))
.filter_map(|output_index| network_interface.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), breadcrumb_network_path)) .filter_map(|output_index| network_interface.frontend_output_from_connector_old(&OutputConnector::node(node_id, output_index), breadcrumb_network_path))
.collect(); .collect();
let (primary_output_connected_to_layer, primary_input_connected_to_layer) = if network_interface.is_layer(&node_id, breadcrumb_network_path) { let (primary_output_connected_to_layer, primary_input_connected_to_layer) = if network_interface.is_layer(&node_id, breadcrumb_network_path) {
( (
@@ -2555,7 +2596,7 @@ impl NodeGraphMessageHandler {
let locked = network_interface.is_locked(&node_id, breadcrumb_network_path); let locked = network_interface.is_locked(&node_id, breadcrumb_network_path);
nodes.push(FrontendNode { nodes.push(FrontendNodeOld {
id: node_id, id: node_id,
is_layer: network_interface is_layer: network_interface
.node_metadata(&node_id, breadcrumb_network_path) .node_metadata(&node_id, breadcrumb_network_path)
@@ -2774,8 +2815,8 @@ impl Default for NodeGraphMessageHandler {
reordering_export: None, reordering_export: None,
reordering_import: None, reordering_import: None,
end_index: None, end_index: None,
frontend_nodes: Vec::new(), frontend_nodes_old: Vec::new(),
frontend_wires: HashSet::new(), frontend_wires_old: HashSet::new(),
} }
} }
} }
@@ -1,7 +1,6 @@
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments)]
use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext}; use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext};
use super::utility_types::FrontendGraphDataType;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector; use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::*; use crate::messages::prelude::*;
@@ -14,6 +13,7 @@ use graph_craft::{Type, concrete};
use graphene_std::NodeInputDecleration; use graphene_std::NodeInputDecleration;
use graphene_std::animation::RealTimeMode; use graphene_std::animation::RealTimeMode;
use graphene_std::extract_xy::XY; use graphene_std::extract_xy::XY;
use graphene_std::node_graph_overlay::types::FrontendGraphDataType;
use graphene_std::path_bool::BooleanOperation; use graphene_std::path_bool::BooleanOperation;
use graphene_std::raster::curve::Curve; use graphene_std::raster::curve::Curve;
use graphene_std::raster::{ use graphene_std::raster::{
@@ -1,50 +1,10 @@
use glam::{DVec2, IVec2}; use glam::IVec2;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graph_craft::document::value::TaggedValue; use graphene_std::node_graph_overlay::types::{FrontendGraphDataType, FrontendGraphInputNew, FrontendGraphOutputNew, FrontendXY};
use graphene_std::Type;
use std::borrow::Cow; use std::borrow::Cow;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType { pub struct FrontendGraphInputOld {
#[default]
General,
Number,
Artboard,
Graphic,
Raster,
Vector,
Color,
Gradient,
Typography,
Invalid,
}
impl FrontendGraphDataType {
pub fn from_type(input: &Type) -> Self {
match TaggedValue::from_type_or_none(input) {
TaggedValue::U32(_)
| TaggedValue::U64(_)
| TaggedValue::F32(_)
| TaggedValue::F64(_)
| TaggedValue::DVec2(_)
| TaggedValue::F64Array4(_)
| TaggedValue::VecF64(_)
| TaggedValue::VecDVec2(_)
| TaggedValue::DAffine2(_) => Self::Number,
TaggedValue::Artboard(_) => Self::Artboard,
TaggedValue::Graphic(_) => Self::Graphic,
TaggedValue::Raster(_) => Self::Raster,
TaggedValue::Vector(_) => Self::Vector,
TaggedValue::Color(_) => Self::Color,
TaggedValue::Gradient(_) | TaggedValue::GradientStops(_) | TaggedValue::GradientTable(_) => Self::Gradient,
TaggedValue::String(_) | TaggedValue::VecString(_) => Self::Typography,
_ => Self::General,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphInput {
#[serde(rename = "dataType")] #[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType, pub data_type: FrontendGraphDataType,
pub name: String, pub name: String,
@@ -58,8 +18,8 @@ pub struct FrontendGraphInput {
pub connected_to: String, pub connected_to: String,
} }
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphOutput { pub struct FrontendGraphOutputOld {
#[serde(rename = "dataType")] #[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType, pub data_type: FrontendGraphDataType,
pub name: String, pub name: String,
@@ -72,8 +32,8 @@ pub struct FrontendGraphOutput {
pub connected_to: Vec<String>, pub connected_to: Vec<String>,
} }
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNode { pub struct FrontendNodeOld {
pub id: graph_craft::document::NodeId, pub id: graph_craft::document::NodeId,
#[serde(rename = "isLayer")] #[serde(rename = "isLayer")]
pub is_layer: bool, pub is_layer: bool,
@@ -83,13 +43,13 @@ pub struct FrontendNode {
#[serde(rename = "displayName")] #[serde(rename = "displayName")]
pub display_name: String, pub display_name: String,
#[serde(rename = "primaryInput")] #[serde(rename = "primaryInput")]
pub primary_input: Option<FrontendGraphInput>, pub primary_input: Option<FrontendGraphInputOld>,
#[serde(rename = "exposedInputs")] #[serde(rename = "exposedInputs")]
pub exposed_inputs: Vec<FrontendGraphInput>, pub exposed_inputs: Vec<FrontendGraphInputOld>,
#[serde(rename = "primaryOutput")] #[serde(rename = "primaryOutput")]
pub primary_output: Option<FrontendGraphOutput>, pub primary_output: Option<FrontendGraphOutputOld>,
#[serde(rename = "exposedOutputs")] #[serde(rename = "exposedOutputs")]
pub exposed_outputs: Vec<FrontendGraphOutput>, pub exposed_outputs: Vec<FrontendGraphOutputOld>,
#[serde(rename = "primaryOutputConnectedToLayer")] #[serde(rename = "primaryOutputConnectedToLayer")]
pub primary_output_connected_to_layer: bool, pub primary_output_connected_to_layer: bool,
#[serde(rename = "primaryInputConnectedToLayer")] #[serde(rename = "primaryInputConnectedToLayer")]
@@ -100,6 +60,26 @@ pub struct FrontendNode {
pub previewed: bool, pub previewed: bool,
} }
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendExport {
pub port: FrontendGraphInputNew,
pub wire: Option<String>,
}
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendExports {
/// If the primary export is not visible, then it is None.
pub exports: Vec<Option<FrontendExport>>,
#[serde(rename = "previewWire")]
pub preview_wire: Option<String>,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendImport {
pub port: FrontendGraphOutputNew,
pub wires: Vec<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeType { pub struct FrontendNodeType {
pub name: Cow<'static, str>, pub name: Cow<'static, str>,
@@ -207,22 +187,3 @@ pub struct NodeGraphError {
pub position: FrontendXY, pub position: FrontendXY,
pub error: String, pub error: String,
} }
/// Stores node graph coordinates which are then transformed in svelte based on the node graph transform
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendXY {
pub x: i32,
pub y: i32,
}
impl From<DVec2> for FrontendXY {
fn from(v: DVec2) -> Self {
FrontendXY { x: v.x as i32, y: v.y as i32 }
}
}
impl From<IVec2> for FrontendXY {
fn from(v: IVec2) -> Self {
FrontendXY { x: v.x as i32, y: v.y as i32 }
}
}
@@ -1,16 +1,18 @@
mod deserialization; mod deserialization;
mod memo_network; mod memo_network;
pub mod node_graph;
pub mod old_node_graph_render;
mod resolved_types; mod resolved_types;
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier, NodeRelations}; use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier, NodeRelations};
use super::misc::PTZ; use super::misc::PTZ;
use super::nodes::SelectedNodes; use super::nodes::SelectedNodes;
use crate::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}; use crate::consts::GRID_SIZE;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type}; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type};
use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput}; use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets};
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::ResolvedDocumentNodeTypes; use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::ResolvedDocumentNodeTypes;
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire}; use crate::messages::portfolio::document::utility_types::wires::WirePathUpdateOld;
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode; use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode;
use deserialization::deserialize_node_persistent_metadata; use deserialization::deserialize_node_persistent_metadata;
@@ -24,7 +26,6 @@ use graphene_std::subpath::Subpath;
use graphene_std::transform::Footprint; use graphene_std::transform::Footprint;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType}; use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, Vector, VectorModificationType}; use graphene_std::vector::{PointId, Vector, VectorModificationType};
use kurbo::BezPath;
use memo_network::MemoNetwork; use memo_network::MemoNetwork;
use serde_json::{Value, json}; use serde_json::{Value, json};
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
@@ -236,31 +237,6 @@ impl NodeNetworkInterface {
layers layers
} }
pub fn chain_width(&self, node_id: &NodeId, network_path: &[NodeId]) -> u32 {
if self.number_of_displayed_inputs(node_id, network_path) > 1 {
let mut last_chain_node_distance = 0u32;
// Iterate upstream from the layer, and get the number of nodes distance to the last node with Position::Chain
for (index, node_id) in self
.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalPrimaryOutputFlow)
.skip(1)
.enumerate()
.collect::<Vec<_>>()
{
// Check if the node is positioned as a chain
if self.is_chain(&node_id, network_path) {
last_chain_node_distance = (index as u32) + 1;
} else {
return last_chain_node_distance * 7 + 1;
}
}
last_chain_node_distance * 7 + 1
} else {
// Layer with no inputs has no chain
0
}
}
/// Check if the specified node id is connected to the output /// Check if the specified node id is connected to the output
pub fn connected_to_output(&self, target_node_id: &NodeId, network_path: &[NodeId]) -> bool { pub fn connected_to_output(&self, target_node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(network) = self.nested_network(network_path) else { let Some(network) = self.nested_network(network_path) else {
@@ -509,245 +485,6 @@ impl NodeNetworkInterface {
} }
}) })
} }
pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphOutput>> {
match network_path.split_last() {
Some((node_id, encapsulating_network_path)) => {
let Some(node) = self.document_node(node_id, encapsulating_network_path) else {
log::error!("Could not get node {node_id} in network {encapsulating_network_path:?}");
return Vec::new();
};
let mut frontend_imports = (0..node.inputs.len())
.map(|import_index| self.frontend_output_from_connector(&OutputConnector::Import(import_index), network_path))
.collect::<Vec<_>>();
if frontend_imports.is_empty() {
frontend_imports.push(None);
}
frontend_imports
}
// In the document network display no imports
None => Vec::new(),
}
}
pub fn frontend_exports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphInput>> {
let Some(network) = self.nested_network(network_path) else { return Vec::new() };
let mut frontend_exports = ((0..network.exports.len()).map(|export_index| self.frontend_input_from_connector(&InputConnector::Export(export_index), network_path))).collect::<Vec<_>>();
if frontend_exports.is_empty() {
frontend_exports.push(None);
}
frontend_exports
}
pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> {
let Some(all_nodes_bounding_box) = self.all_nodes_bounding_box(network_path).cloned() else {
log::error!("Could not get all nodes bounding box in load_export_ports");
return None;
};
let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get current network in load_export_ports");
return None;
};
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in load_export_ports");
return None;
};
let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport;
let target_viewport_top_left = DVec2::new(IMPORTS_TO_LEFT_EDGE_PIXEL_GAP as f64, IMPORTS_TO_TOP_EDGE_PIXEL_GAP as f64);
let node_graph_pixel_offset_top_left = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_left);
// A 5x5 grid offset from the top left corner
let node_graph_grid_space_offset_top_left = node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO) + DVec2::new(5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64);
// The inner bound of the import is the highest/furthest left of the two offsets
let top_left_inner_bound = DVec2::new(
node_graph_pixel_offset_top_left.x.min(node_graph_grid_space_offset_top_left.x),
node_graph_pixel_offset_top_left.y.min(node_graph_grid_space_offset_top_left.y),
);
let offset_from_top_left = if network
.exports
.first()
.is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path)))
{
DVec2::new(-4. * GRID_SIZE as f64, -2. * GRID_SIZE as f64)
} else {
DVec2::new(-4. * GRID_SIZE as f64, 0.)
};
let bounding_box_top_left = DVec2::new((all_nodes_bounding_box[0].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.) + offset_from_top_left;
let import_top_left = DVec2::new(top_left_inner_bound.x.min(bounding_box_top_left.x), top_left_inner_bound.y.min(bounding_box_top_left.y));
let rounded_import_top_left = DVec2::new((import_top_left.x / 24.).round() * 24., (import_top_left.y / 24.).round() * 24.);
let viewport_width = network_metadata.persistent_metadata.navigation_metadata.node_graph_width;
let target_viewport_top_right = DVec2::new(viewport_width - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64);
// An offset from the right edge in viewport pixels
let node_graph_pixel_offset_top_right = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_right);
// A 5x5 grid offset from the right corner
let node_graph_grid_space_offset_top_right = node_graph_to_viewport.inverse().transform_point2(DVec2::new(viewport_width, 0.)) + DVec2::new(-5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64);
// The inner bound of the export is the highest/furthest right of the two offsets.
// When zoomed out this keeps it a constant grid space away from the edge, but when zoomed in it prevents the exports from getting too far in
let top_right_inner_bound = DVec2::new(
node_graph_pixel_offset_top_right.x.max(node_graph_grid_space_offset_top_right.x),
node_graph_pixel_offset_top_right.y.min(node_graph_grid_space_offset_top_right.y),
);
let offset_from_top_right = if network
.exports
.first()
.is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path)))
{
DVec2::new(2. * GRID_SIZE as f64, -2. * GRID_SIZE as f64)
} else {
DVec2::new(4. * GRID_SIZE as f64, 0.)
};
let mut bounding_box_top_right = DVec2::new((all_nodes_bounding_box[1].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.);
bounding_box_top_right += offset_from_top_right;
let export_top_right = DVec2::new(top_right_inner_bound.x.max(bounding_box_top_right.x), top_right_inner_bound.y.min(bounding_box_top_right.y));
let rounded_export_top_right = DVec2::new((export_top_right.x / 24.).round() * 24., (export_top_right.y / 24.).round() * 24.);
Some((rounded_import_top_left.as_ivec2(), rounded_export_top_right.as_ivec2()))
}
/// Returns None if there is an error, it is a hidden primary export, or a hidden input
pub fn frontend_input_from_connector(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<FrontendGraphInput> {
// Return None if it is a hidden input
if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) {
return None;
}
let input_type = self.input_type(input_connector, network_path);
let data_type = input_type.displayed_type();
let resolved_type = input_type.resolved_type_node_string();
let connected_to = self
.upstream_output_connector(input_connector, network_path)
.map(|output_connector| match output_connector {
OutputConnector::Node { node_id, output_index } => {
let mut name = self.display_name(&node_id, network_path);
if cfg!(debug_assertions) {
name.push_str(&format!(" (id: {node_id})"));
}
format!("{name} output {output_index}")
}
OutputConnector::Import(import_index) => format!("Import index {import_index}"),
})
.unwrap_or("nothing".to_string());
let (name, description) = match input_connector {
InputConnector::Node { node_id, input_index } => self.displayed_input_name_and_description(node_id, *input_index, network_path),
InputConnector::Export(export_index) => {
// Get export name from parent node metadata input, which must match the number of exports.
// Empty string means to use type, or "Export + index" if type is empty determined
let export_name = if network_path.is_empty() {
"Canvas".to_string()
} else {
self.encapsulating_node_metadata(network_path)
.and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.output_names.get(*export_index).cloned())
.unwrap_or_default()
};
let export_name = if !export_name.is_empty() {
export_name
} else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) {
export_type_name
} else {
format!("Export index {}", export_index)
};
(export_name, String::new())
}
};
Some(FrontendGraphInput {
data_type,
resolved_type,
name,
description,
valid_types: self.potential_valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(),
connected_to,
})
}
/// Returns None if there is an error, it is the document network, a hidden primary output or import
pub fn frontend_output_from_connector(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<FrontendGraphOutput> {
let output_type = self.output_type(output_connector, network_path);
let (name, description) = match output_connector {
OutputConnector::Node { node_id, output_index } => {
// Do not display the primary output port for a node if it is a network node with a hidden primary export
if *output_index == 0 && self.hidden_primary_output(node_id, network_path) {
return None;
};
// Get the output name from the interior network export name
let node_metadata = self.node_metadata(node_id, network_path)?;
let output_name = node_metadata.persistent_metadata.output_names.get(*output_index).cloned().unwrap_or_default();
let output_name = if !output_name.is_empty() { output_name } else { output_type.resolved_type_node_string() };
(output_name, String::new())
}
OutputConnector::Import(import_index) => {
// Get the import name from the encapsulating node input metadata
let Some((encapsulating_node_id, encapsulating_path)) = network_path.split_last() else {
// Return None if it is an import in the document network
return None;
};
// Return None if the primary input is hidden and this is the primary import
if *import_index == 0 && self.hidden_primary_import(network_path) {
return None;
};
let (import_name, description) = self.displayed_input_name_and_description(encapsulating_node_id, *import_index, encapsulating_path);
let import_name = if !import_name.is_empty() {
import_name
} else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) {
import_type_name
} else {
format!("Import index {}", import_index)
};
(import_name, description)
}
};
let data_type = output_type.displayed_type();
let resolved_type = output_type.resolved_type_node_string();
let mut connected_to = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(output_connector))
.cloned()
.unwrap_or_default()
.iter()
.map(|input| match input {
InputConnector::Node { node_id, input_index } => {
let mut name = self.display_name(node_id, network_path);
if cfg!(debug_assertions) {
name.push_str(&format!(" (id: {node_id})"));
}
format!("{name} input {input_index}")
}
InputConnector::Export(export_index) => format!("Export index {export_index}"),
})
.collect::<Vec<_>>();
if connected_to.is_empty() {
connected_to.push("nothing".to_string());
}
Some(FrontendGraphOutput {
data_type,
resolved_type,
name,
description,
connected_to,
})
}
pub fn height_from_click_target(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<u32> { pub fn height_from_click_target(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<u32> {
let mut node_height: Option<u32> = self let mut node_height: Option<u32> = self
.node_click_targets(node_id, network_path) .node_click_targets(node_id, network_path)
@@ -950,7 +687,7 @@ impl NodeNetworkInterface {
Some(&metadata.persistent_metadata) Some(&metadata.persistent_metadata)
} }
fn transient_input_metadata(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&InputTransientMetadata> { fn transient_input_metadata_old(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&InputTransientMetadata> {
let metadata = self let metadata = self
.node_metadata(node_id, network_path) .node_metadata(node_id, network_path)
.and_then(|node_metadata| node_metadata.persistent_metadata.input_metadata.get(index))?; .and_then(|node_metadata| node_metadata.persistent_metadata.input_metadata.get(index))?;
@@ -1050,25 +787,6 @@ impl NodeNetworkInterface {
node_metadata.persistent_metadata.is_layer() node_metadata.persistent_metadata.is_layer()
} }
pub fn primary_output_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward_wires in primary_output_connected_to_layer");
return false;
};
let Some(downstream_connectors) = outward_wires.get(&OutputConnector::node(*node_id, 0)) else {
log::error!("Could not get downstream_connectors in primary_output_connected_to_layer");
return false;
};
let downstream_nodes = downstream_connectors.iter().filter_map(|connector| connector.node_id()).collect::<Vec<_>>();
downstream_nodes.iter().any(|node_id| self.is_layer(node_id, network_path))
}
pub fn primary_input_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
self.input_from_connector(&InputConnector::node(*node_id, 0), network_path)
.and_then(|input| input.as_node())
.is_some_and(|node_id| self.is_layer(&node_id, network_path))
}
pub fn hidden_primary_export(&self, network_path: &[NodeId]) -> bool { pub fn hidden_primary_export(&self, network_path: &[NodeId]) -> bool {
let Some((node_id, network_path)) = network_path.split_last() else { let Some((node_id, network_path)) = network_path.split_last() else {
// The document network does not have a hidden primary export // The document network does not have a hidden primary export
@@ -1834,30 +1552,7 @@ impl NodeNetworkInterface {
return; return;
}; };
network_metadata.transient_metadata.import_export_ports.unload(); network_metadata.transient_metadata.import_export_ports.unload();
self.unload_import_export_wires_old(network_path);
// Always unload all wires connected to them as well
let number_of_imports = self.number_of_imports(network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in remove_import");
return;
};
let mut input_connectors = Vec::new();
for import_index in 0..number_of_imports {
let Some(outward_wires_for_import) = outward_wires.get(&OutputConnector::Import(import_index)).cloned() else {
log::error!("Could not get outward wires for import in remove_import");
return;
};
input_connectors.extend(outward_wires_for_import);
}
let Some(network) = self.nested_network(network_path) else {
return;
};
for export_index in 0..network.exports.len() {
input_connectors.push(InputConnector::Export(export_index));
}
for input in &input_connectors {
self.unload_wire(input, network_path);
}
} }
pub fn modify_import_export(&mut self, network_path: &[NodeId]) -> Option<&ModifyImportExportClickTarget> { pub fn modify_import_export(&mut self, network_path: &[NodeId]) -> Option<&ModifyImportExportClickTarget> {
@@ -2214,99 +1909,6 @@ impl NodeNetworkInterface {
.find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None }) .find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None })
} }
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
if !self.wire_is_loaded(input, network_path) {
self.load_wire(input, graph_wire_style, network_path);
} else {
return None;
}
let wire = match input {
InputConnector::Node { node_id, input_index } => {
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
InputConnector::Export(export_index) => {
let network_metadata = self.network_metadata(network_path)?;
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
};
Some(wire)
}
pub fn wire_is_loaded(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(input_metadata) = self.transient_input_metadata(node_id, *input_index, network_path) else {
log::error!("Input metadata should always exist for input");
return false;
};
input_metadata.wire.is_loaded()
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata(network_path) else {
return false;
};
match network_metadata.transient_metadata.wires.get(*export_index) {
Some(wire) => wire.is_loaded(),
None => false,
}
}
}
}
fn load_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) {
let dashed = match self.previewing(network_path) {
Previewing::Yes { .. } => match input {
InputConnector::Node { .. } => false,
InputConnector::Export(export_index) => *export_index == 0,
},
Previewing::No => false,
};
let Some(wire) = self.wire_path_from_input(input, graph_wire_style, dashed, network_path) else {
log::error!("Could not load wire path from input");
return;
};
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return };
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
let wire_update = WirePathUpdate {
id: *node_id,
input_index: *input_index,
wire_path_update: Some(wire),
};
input_metadata.transient_metadata.wire = TransientMetadata::Loaded(wire_update);
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return };
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
let wire_update = WirePathUpdate {
id: NodeId(u64::MAX),
input_index: *export_index,
wire_path_update: Some(wire),
};
*input_metadata = TransientMetadata::Loaded(wire_update);
}
}
}
pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec<InputConnector> { pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec<InputConnector> {
let mut input_connectors = Vec::new(); let mut input_connectors = Vec::new();
let Some(network) = self.nested_network(network_path) else { let Some(network) = self.nested_network(network_path) else {
@@ -2331,144 +1933,6 @@ impl NodeNetworkInterface {
.collect() .collect()
} }
/// Maps to the frontend representation of a wire start. Includes disconnected value wire inputs.
pub fn node_graph_wire_inputs(&self, network_path: &[NodeId]) -> Vec<(NodeId, usize)> {
self.node_graph_input_connectors(network_path)
.iter()
.map(|input| match input {
InputConnector::Node { node_id, input_index } => (*node_id, *input_index),
InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index),
})
.chain(std::iter::once((NodeId(u64::MAX), u32::MAX as usize)))
.collect()
}
fn unload_wires_for_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
let number_of_outputs = self.number_of_outputs(node_id, network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in reorder_export");
return;
};
let mut input_connectors = Vec::new();
for output_index in 0..number_of_outputs {
let Some(inputs) = outward_wires.get(&OutputConnector::node(*node_id, output_index)) else {
continue;
};
input_connectors.extend(inputs.clone())
}
for input_index in 0..self.number_of_inputs(node_id, network_path) {
input_connectors.push(InputConnector::node(*node_id, input_index));
}
for input in input_connectors {
self.unload_wire(&input, network_path);
}
}
pub fn unload_wire(&mut self, input: &InputConnector, network_path: &[NodeId]) {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
input_metadata.transient_metadata.wire = TransientMetadata::Unloaded;
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return;
};
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
*input_metadata = TransientMetadata::Unloaded;
}
}
}
/// When previewing, there may be a second path to the root node.
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
let input = InputConnector::Export(0);
let current_export = self.upstream_output_connector(&input, network_path)?;
let root_node = match self.previewing(network_path) {
Previewing::Yes { root_node_to_restore } => root_node_to_restore,
Previewing::No => None,
}?;
if Some(root_node.node_id) == current_export.node_id() {
return None;
}
let Some(input_position) = self.get_input_center(&input, network_path) else {
log::error!("Could not get input position for wire end in root node: {input:?}");
return None;
};
let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index);
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output position for wire start in root node: {upstream_output:?}");
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start: bool = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
let path_string = vector_wire.to_svg();
let data_type = self.input_type(&input, network_path).displayed_type();
let wire_path_update = Some(WirePath {
path_string,
data_type,
thick,
dashed: false,
});
Some(WirePathUpdate {
id: NodeId(u64::MAX),
input_index: u32::MAX as usize,
wire_path_update,
})
}
/// Returns the vector subpath and a boolean of whether the wire should be thick.
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, bool)> {
let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
};
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return Some((BezPath::new(), false));
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output port for wire start: {:?}", upstream_output);
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
Some((build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style), thick))
}
pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> {
let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
let path_string = vector_wire.to_svg();
let data_type = self
.upstream_output_connector(input, network_path)
.map(|output| self.output_type(&output, network_path).displayed_type())
.unwrap_or(FrontendGraphDataType::General);
Some(WirePath {
path_string,
data_type,
thick,
dashed,
})
}
pub fn node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> { pub fn node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> {
self.try_load_node_click_targets(node_id, network_path); self.try_load_node_click_targets(node_id, network_path);
self.try_get_node_click_targets(node_id, network_path) self.try_get_node_click_targets(node_id, network_path)
@@ -2706,7 +2170,7 @@ impl NodeNetworkInterface {
return; return;
}; };
node_metadata.transient_metadata.click_targets.unload(); node_metadata.transient_metadata.click_targets.unload();
self.unload_wires_for_node(node_id, network_path); self.unload_wires_for_node_old(node_id, network_path);
} }
pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec<NodeId>, network_path: &[NodeId]) { pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec<NodeId>, network_path: &[NodeId]) {
@@ -3062,38 +2526,6 @@ impl NodeNetworkInterface {
bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport) bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport)
} }
pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> (HashMap<NodeId, u32>, HashMap<NodeId, u32>, HashMap<NodeId, bool>) {
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in collect_layer_widths");
return (HashMap::new(), HashMap::new(), HashMap::new());
};
let nodes = network_metadata
.persistent_metadata
.node_metadata
.iter()
.filter_map(|(node_id, _)| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None })
.collect::<Vec<_>>();
let layer_widths = nodes
.iter()
.filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width)))
.collect::<HashMap<NodeId, u32>>();
let chain_widths = nodes.iter().map(|node_id| (*node_id, self.chain_width(node_id, network_path))).collect::<HashMap<NodeId, u32>>();
let has_left_input_wire = nodes
.iter()
.map(|node_id| {
(
*node_id,
!self
.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow)
.skip(1)
.all(|node_id| self.is_chain(&node_id, network_path)),
)
})
.collect::<HashMap<NodeId, bool>>();
(layer_widths, chain_widths, has_left_input_wire)
}
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> { pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self); let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self);
@@ -3278,7 +2710,6 @@ impl NodeNetworkInterface {
self.unload_import_export_ports(network_path); self.unload_import_export_ports(network_path);
self.unload_modify_import_export(network_path); self.unload_modify_import_export(network_path);
} }
pub fn vector_modify(&mut self, node_id: &NodeId, modification_type: VectorModificationType) { pub fn vector_modify(&mut self, node_id: &NodeId, modification_type: VectorModificationType) {
let Some(node) = self.network_mut(&[]).unwrap().nodes.get_mut(node_id) else { let Some(node) = self.network_mut(&[]).unwrap().nodes.get_mut(node_id) else {
log::error!("Could not get node in vector_modification"); log::error!("Could not get node in vector_modification");
@@ -4013,17 +3444,14 @@ impl NodeNetworkInterface {
// If a connection is made to the imports // If a connection is made to the imports
(NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }, NodeInput::Import { .. }) => { (NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }, NodeInput::Import { .. }) => {
self.unload_outward_wires(network_path); self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
} }
// If a connection to the imports is disconnected // If a connection to the imports is disconnected
(NodeInput::Import { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => { (NodeInput::Import { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path); self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
} }
// If a node is disconnected. // If a node is disconnected.
(NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => { (NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path); self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
if let Some((old_upstream_node_id, previous_position)) = previous_metadata { if let Some((old_upstream_node_id, previous_position)) = previous_metadata {
let old_upstream_node_is_layer = self.is_layer(&old_upstream_node_id, network_path); let old_upstream_node_is_layer = self.is_layer(&old_upstream_node_id, network_path);
@@ -6069,7 +5497,7 @@ pub struct NodeNetworkTransientMetadata {
pub modify_import_export: TransientMetadata<ModifyImportExportClickTarget>, pub modify_import_export: TransientMetadata<ModifyImportExportClickTarget>,
// Wires from the exports // Wires from the exports
pub wires: Vec<TransientMetadata<WirePathUpdate>>, pub wires: Vec<TransientMetadata<WirePathUpdateOld>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -6250,7 +5678,7 @@ impl InputPersistentMetadata {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
struct InputTransientMetadata { struct InputTransientMetadata {
wire: TransientMetadata<WirePathUpdate>, wire: TransientMetadata<WirePathUpdateOld>,
// downstream_protonode: populated for all inputs after each compile // downstream_protonode: populated for all inputs after each compile
// types: populated for each protonode after each // types: populated for each protonode after each
} }
@@ -0,0 +1,360 @@
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::node_graph::utility_types::{FrontendExport, FrontendExports, FrontendImport};
use crate::messages::portfolio::document::utility_types::{
network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing},
wires::{GraphWireStyle, build_vector_wire},
};
use glam::{DVec2, IVec2};
use graphene_std::{
node_graph_overlay::types::{FrontendGraphInputNew, FrontendGraphOutputNew},
uuid::NodeId,
};
use kurbo::BezPath;
// Functions used to collect data from the network interface for use in rendering the node graph
impl NodeNetworkInterface {
/// Returns None if there is an error, it is a hidden primary export, or a hidden input
pub fn frontend_input_from_connector(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<FrontendGraphInputNew> {
// Return None if it is a hidden input or doesn't exist
if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) {
return None;
}
let input_type = self.input_type(input_connector, network_path);
let data_type = input_type.displayed_type();
let name = match input_connector {
InputConnector::Node { node_id, input_index } => self.displayed_input_name_and_description(node_id, *input_index, network_path).0,
InputConnector::Export(export_index) => {
// Get export name from parent node metadata input, which must match the number of exports.
// Empty string means to use type, or "Export + index" if type is empty determined
let export_name = if network_path.is_empty() {
"Canvas".to_string()
} else {
self.encapsulating_node_metadata(network_path)
.and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.output_names.get(*export_index).cloned())
.unwrap_or_default()
};
if !export_name.is_empty() {
export_name
} else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) {
export_type_name
} else {
format!("Export index {}", export_index)
}
}
};
let connected_to_node = self.upstream_output_connector(input_connector, network_path).and_then(|output_connector| output_connector.node_id());
Some(FrontendGraphInputNew { data_type, name, connected_to_node })
}
/// Returns None if there is an error, it is the document network, a hidden primary output or import
pub fn frontend_output_from_connector(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<FrontendGraphOutputNew> {
let output_type = self.output_type(output_connector, network_path);
let name = match output_connector {
OutputConnector::Node { node_id, output_index } => {
// Do not display the primary output port for a node if it is a network node with a hidden primary export
if *output_index == 0 && self.hidden_primary_output(node_id, network_path) {
return None;
};
// Get the output name from the interior network export name
let node_metadata = self.node_metadata(node_id, network_path)?;
let output_name = node_metadata.persistent_metadata.output_names.get(*output_index).cloned().unwrap_or_default();
if !output_name.is_empty() { output_name } else { output_type.resolved_type_node_string() }
}
OutputConnector::Import(import_index) => {
// Get the import name from the encapsulating node input metadata
let Some((encapsulating_node_id, encapsulating_path)) = network_path.split_last() else {
// Return None if it is an import in the document network
return None;
};
// Return None if the primary input is hidden and this is the primary import
if *import_index == 0 && self.hidden_primary_import(network_path) {
return None;
};
let import_name = self.displayed_input_name_and_description(encapsulating_node_id, *import_index, encapsulating_path).0;
if !import_name.is_empty() {
import_name
} else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) {
import_type_name
} else {
format!("Import index {}", *import_index)
}
}
};
let connected = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(output_connector))
.is_some_and(|downstream| downstream.len() > 0);
let data_type = output_type.displayed_type();
Some(FrontendGraphOutputNew { data_type, name, connected })
}
pub fn chain_width(&self, node_id: &NodeId, network_path: &[NodeId]) -> u32 {
if self.number_of_displayed_inputs(node_id, network_path) > 1 {
let mut last_chain_node_distance = 0u32;
// Iterate upstream from the layer, and get the number of nodes distance to the last node with Position::Chain
for (index, node_id) in self
.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalPrimaryOutputFlow)
.skip(1)
.enumerate()
.collect::<Vec<_>>()
{
// Check if the node is positioned as a chain
if self.is_chain(&node_id, network_path) {
last_chain_node_distance = (index as u32) + 1;
} else {
return last_chain_node_distance * 7 + 1;
}
}
last_chain_node_distance * 7 + 1
} else {
// Layer with no inputs has no chain
0
}
}
/// Checks if a layer should display a gap in its left border
pub fn layer_has_left_border_gap(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).skip(1).any(|node_id| {
!self.is_chain(&node_id, network_path)
|| self
.upstream_output_connector(&InputConnector::node(node_id, 0), network_path)
.is_some_and(|output_connector| matches!(output_connector, OutputConnector::Import(_)))
})
}
/// Returns the node which should have a dashed border drawn around it
pub fn previewed_node(&self, network_path: &[NodeId]) -> Option<NodeId> {
self.upstream_output_connector(&InputConnector::Export(0), network_path)
.and_then(|output_connector| output_connector.node_id())
.filter(|output_node| self.root_node(network_path).is_some_and(|root_node| root_node.node_id != *output_node))
}
/// If any downstream input are bottom layer inputs, then the thick cap should be displayed above the output port
pub fn primary_output_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward_wires in primary_output_connected_to_layer");
return false;
};
let Some(downstream_connectors) = outward_wires.get(&OutputConnector::node(*node_id, 0)) else {
log::error!("Could not get downstream_connectors in primary_output_connected_to_layer");
return false;
};
let downstream_nodes = downstream_connectors
.iter()
.filter_map(|connector| if connector.input_index() == 0 { connector.node_id() } else { None })
.collect::<Vec<_>>();
downstream_nodes.iter().any(|node_id| self.is_layer(node_id, network_path))
}
/// If any upstream nodes are layers, then the thick cap should be displayed below the primary input port
pub fn primary_input_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
self.input_from_connector(&InputConnector::node(*node_id, 0), network_path)
.and_then(|input| input.as_node())
.is_some_and(|node_id| self.is_layer(&node_id, network_path))
}
/// The imports contain both the output port and the outward wires
pub fn frontend_imports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Vec<Option<FrontendImport>> {
match network_path.split_last() {
Some((node_id, encapsulating_network_path)) => {
let Some(node) = self.document_node(node_id, encapsulating_network_path) else {
log::error!("Could not get node {node_id} in network {encapsulating_network_path:?}");
return Vec::new();
};
let mut frontend_imports = (0..node.inputs.len())
.map(|import_index| {
let port = self.frontend_output_from_connector(&OutputConnector::Import(import_index), network_path);
port.and_then(|port| {
let outward_wires = self.outward_wires(network_path)?;
let downstream_inputs = outward_wires.get(&OutputConnector::Import(import_index)).cloned()?;
let wires = downstream_inputs
.iter()
.filter_map(|input_connector| {
let Some(wire) = self.wire_from_input_new(&input_connector, graph_wire_style, network_path) else {
log::error!("Could not get wire path for import input: {input_connector:?}");
return None;
};
Some(wire.to_svg())
})
.collect::<Vec<_>>();
Some(FrontendImport { port, wires })
})
})
.collect::<Vec<_>>();
if frontend_imports.is_empty() {
frontend_imports.push(None);
}
frontend_imports
}
// In the document network display no imports
None => Vec::new(),
}
}
/// The imports contain the export port, the outward wires, and the preview wire if it exists
pub fn frontend_exports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> FrontendExports {
let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get nested network in frontend exports");
return FrontendExports::default();
};
let mut exports = (0..network.exports.len())
.map(|export_index| {
let export_connector = InputConnector::Export(export_index);
let frontend_export = self.frontend_input_from_connector(&export_connector, network_path);
frontend_export.and_then(|export| {
let wire = self.wire_from_input_new(&export_connector, graph_wire_style, network_path).map(|path| path.to_svg());
Some(FrontendExport { port: export, wire })
})
})
.collect::<Vec<_>>();
if exports.is_empty() {
exports.push(None);
}
let preview_wire = self.wire_to_root_new(graph_wire_style, network_path).map(|wire| wire.to_svg());
FrontendExports { exports, preview_wire }
}
pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> {
let Some(all_nodes_bounding_box) = self.all_nodes_bounding_box(network_path).cloned() else {
log::error!("Could not get all nodes bounding box in load_export_ports");
return None;
};
let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get current network in load_export_ports");
return None;
};
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in load_export_ports");
return None;
};
let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport;
let target_viewport_top_left = DVec2::new(IMPORTS_TO_LEFT_EDGE_PIXEL_GAP as f64, IMPORTS_TO_TOP_EDGE_PIXEL_GAP as f64);
let node_graph_pixel_offset_top_left = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_left);
// A 5x5 grid offset from the top left corner
let node_graph_grid_space_offset_top_left = node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO) + DVec2::new(5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64);
// The inner bound of the import is the highest/furthest left of the two offsets
let top_left_inner_bound = DVec2::new(
node_graph_pixel_offset_top_left.x.min(node_graph_grid_space_offset_top_left.x),
node_graph_pixel_offset_top_left.y.min(node_graph_grid_space_offset_top_left.y),
);
let offset_from_top_left = if network
.exports
.first()
.is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path)))
{
DVec2::new(-4. * GRID_SIZE as f64, -2. * GRID_SIZE as f64)
} else {
DVec2::new(-4. * GRID_SIZE as f64, 0.)
};
let bounding_box_top_left = DVec2::new((all_nodes_bounding_box[0].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.) + offset_from_top_left;
let import_top_left = DVec2::new(top_left_inner_bound.x.min(bounding_box_top_left.x), top_left_inner_bound.y.min(bounding_box_top_left.y));
let rounded_import_top_left = DVec2::new((import_top_left.x / 24.).round() * 24., (import_top_left.y / 24.).round() * 24.);
let viewport_width = network_metadata.persistent_metadata.navigation_metadata.node_graph_width;
let target_viewport_top_right = DVec2::new(viewport_width - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64);
// An offset from the right edge in viewport pixels
let node_graph_pixel_offset_top_right = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_right);
// A 5x5 grid offset from the right corner
let node_graph_grid_space_offset_top_right = node_graph_to_viewport.inverse().transform_point2(DVec2::new(viewport_width, 0.)) + DVec2::new(-5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64);
// The inner bound of the export is the highest/furthest right of the two offsets.
// When zoomed out this keeps it a constant grid space away from the edge, but when zoomed in it prevents the exports from getting too far in
let top_right_inner_bound = DVec2::new(
node_graph_pixel_offset_top_right.x.max(node_graph_grid_space_offset_top_right.x),
node_graph_pixel_offset_top_right.y.min(node_graph_grid_space_offset_top_right.y),
);
let offset_from_top_right = if network
.exports
.first()
.is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path)))
{
DVec2::new(2. * GRID_SIZE as f64, -2. * GRID_SIZE as f64)
} else {
DVec2::new(4. * GRID_SIZE as f64, 0.)
};
let mut bounding_box_top_right = DVec2::new((all_nodes_bounding_box[1].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.);
bounding_box_top_right += offset_from_top_right;
let export_top_right = DVec2::new(top_right_inner_bound.x.max(bounding_box_top_right.x), top_right_inner_bound.y.min(bounding_box_top_right.y));
let rounded_export_top_right = DVec2::new((export_top_right.x / 24.).round() * 24., (export_top_right.y / 24.).round() * 24.);
Some((rounded_import_top_left.as_ivec2(), rounded_export_top_right.as_ivec2()))
}
pub fn wire_is_thick(&self, input: &InputConnector, network_path: &[NodeId]) -> bool {
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return false;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
vertical_end && vertical_start
}
/// Returns the vector subpath and a boolean of whether the wire should be thick.
pub fn wire_from_input_new(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<BezPath> {
let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
};
// An upstream output could not be found
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return None;
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output port for wire start: {:?}", upstream_output);
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
Some(build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style))
}
/// When previewing, there may be a second path to the root node.
pub fn wire_to_root_new(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<BezPath> {
let input = InputConnector::Export(0);
let current_export = self.upstream_output_connector(&input, network_path)?;
let root_node = match self.previewing(network_path) {
Previewing::Yes { root_node_to_restore } => root_node_to_restore,
Previewing::No => None,
}?;
if Some(root_node.node_id) == current_export.node_id() {
return None;
}
let Some(input_position) = self.get_input_center(&input, network_path) else {
log::error!("Could not get input position for wire end in root node: {input:?}");
return None;
};
let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index);
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output position for wire start in root node: {upstream_output:?}");
return None;
};
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, false, graph_wire_style);
Some(vector_wire)
}
}
@@ -0,0 +1,435 @@
use crate::messages::portfolio::document::node_graph::utility_types::{FrontendGraphInputOld, FrontendGraphOutputOld};
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::document::utility_types::network_interface::Previewing;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, OutputConnector, TransientMetadata};
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePathOld, WirePathUpdateOld, build_vector_wire};
use graphene_std::node_graph_overlay::types::FrontendGraphDataType;
use graphene_std::uuid::NodeId;
use kurbo::BezPath;
use std::collections::HashMap;
// All these functions will be deleted once the svelte node graph rendering is removed
impl NodeNetworkInterface {
/// Returns None if there is an error, it is a hidden primary export, or a hidden input
pub fn frontend_input_from_connector_old(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<FrontendGraphInputOld> {
// Return None if it is a hidden input
if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) {
return None;
}
let input_type = self.input_type(input_connector, network_path);
let data_type = input_type.displayed_type();
let resolved_type = input_type.resolved_type_node_string();
let connected_to = self
.upstream_output_connector(input_connector, network_path)
.map(|output_connector| match output_connector {
OutputConnector::Node { node_id, output_index } => {
let mut name = self.display_name(&node_id, network_path);
if cfg!(debug_assertions) {
name.push_str(&format!(" (id: {node_id})"));
}
format!("{name} output {output_index}")
}
OutputConnector::Import(import_index) => format!("Import index {import_index}"),
})
.unwrap_or("nothing".to_string());
let (name, description) = match input_connector {
InputConnector::Node { node_id, input_index } => self.displayed_input_name_and_description(node_id, *input_index, network_path),
InputConnector::Export(export_index) => {
// Get export name from parent node metadata input, which must match the number of exports.
// Empty string means to use type, or "Export + index" if type is empty determined
let export_name = if network_path.is_empty() {
"Canvas".to_string()
} else {
self.encapsulating_node_metadata(network_path)
.and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.output_names.get(*export_index).cloned())
.unwrap_or_default()
};
let export_name = if !export_name.is_empty() {
export_name
} else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) {
export_type_name
} else {
format!("Export index {}", export_index)
};
(export_name, String::new())
}
};
Some(FrontendGraphInputOld {
data_type,
resolved_type,
name,
description,
valid_types: self.potential_valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(),
connected_to,
})
}
/// Returns None if there is an error, it is the document network, a hidden primary output or import
pub fn frontend_output_from_connector_old(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<FrontendGraphOutputOld> {
let output_type = self.output_type(output_connector, network_path);
let (name, description) = match output_connector {
OutputConnector::Node { node_id, output_index } => {
// Do not display the primary output port for a node if it is a network node with a hidden primary export
if *output_index == 0 && self.hidden_primary_output(node_id, network_path) {
return None;
};
// Get the output name from the interior network export name
let node_metadata = self.node_metadata(node_id, network_path)?;
let output_name = node_metadata.persistent_metadata.output_names.get(*output_index).cloned().unwrap_or_default();
let output_name = if !output_name.is_empty() { output_name } else { output_type.resolved_type_node_string() };
(output_name, String::new())
}
OutputConnector::Import(import_index) => {
// Get the import name from the encapsulating node input metadata
let Some((encapsulating_node_id, encapsulating_path)) = network_path.split_last() else {
// Return None if it is an import in the document network
return None;
};
// Return None if the primary input is hidden and this is the primary import
if *import_index == 0 && self.hidden_primary_import(network_path) {
return None;
};
let (import_name, description) = self.displayed_input_name_and_description(encapsulating_node_id, *import_index, encapsulating_path);
let import_name = if !import_name.is_empty() {
import_name
} else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) {
import_type_name
} else {
format!("Import index {}", import_index)
};
(import_name, description)
}
};
let data_type = output_type.displayed_type();
let resolved_type = output_type.resolved_type_node_string();
let mut connected_to = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(output_connector))
.cloned()
.unwrap_or_default()
.iter()
.map(|input| match input {
InputConnector::Node { node_id, input_index } => {
let mut name = self.display_name(node_id, network_path);
if cfg!(debug_assertions) {
name.push_str(&format!(" (id: {node_id})"));
}
format!("{name} input {input_index}")
}
InputConnector::Export(export_index) => format!("Export index {export_index}"),
})
.collect::<Vec<_>>();
if connected_to.is_empty() {
connected_to.push("nothing".to_string());
}
Some(FrontendGraphOutputOld {
data_type,
resolved_type,
name,
description,
connected_to,
})
}
pub fn unload_import_export_wires_old(&mut self, network_path: &[NodeId]) {
// Always unload all wires connected to them as well
let number_of_imports = self.number_of_imports(network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in remove_import");
return;
};
let mut input_connectors = Vec::new();
for import_index in 0..number_of_imports {
let Some(outward_wires_for_import) = outward_wires.get(&OutputConnector::Import(import_index)).cloned() else {
log::error!("Could not get outward wires for import in remove_import");
return;
};
input_connectors.extend(outward_wires_for_import);
}
let Some(network) = self.nested_network(network_path) else {
return;
};
for export_index in 0..network.exports.len() {
input_connectors.push(InputConnector::Export(export_index));
}
for input in &input_connectors {
self.unload_wire_old(input, network_path);
}
}
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdateOld> {
if !self.wire_is_loaded_old(input, network_path) {
self.load_wire_old(input, graph_wire_style, network_path);
} else {
return None;
}
let wire = match input {
InputConnector::Node { node_id, input_index } => {
let input_metadata = self.transient_input_metadata_old(node_id, *input_index, network_path)?;
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
InputConnector::Export(export_index) => {
let network_metadata = self.network_metadata(network_path)?;
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
};
Some(wire)
}
pub fn wire_is_loaded_old(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(input_metadata) = self.transient_input_metadata_old(node_id, *input_index, network_path) else {
log::error!("Input metadata should always exist for input");
return false;
};
input_metadata.wire.is_loaded()
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata(network_path) else {
return false;
};
match network_metadata.transient_metadata.wires.get(*export_index) {
Some(wire) => wire.is_loaded(),
None => false,
}
}
}
}
fn load_wire_old(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) {
let dashed = match self.previewing(network_path) {
Previewing::Yes { .. } => match input {
InputConnector::Node { .. } => false,
InputConnector::Export(export_index) => *export_index == 0,
},
Previewing::No => false,
};
let Some(wire) = self.wire_path_from_input_old(input, graph_wire_style, dashed, network_path) else {
log::error!("Could not load wire path from input");
return;
};
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return };
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
let wire_update = WirePathUpdateOld {
id: *node_id,
input_index: *input_index,
wire_path_update: Some(wire),
};
input_metadata.transient_metadata.wire = TransientMetadata::Loaded(wire_update);
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return };
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
let wire_update = WirePathUpdateOld {
id: NodeId(u64::MAX),
input_index: *export_index,
wire_path_update: Some(wire),
};
*input_metadata = TransientMetadata::Loaded(wire_update);
}
}
}
/// Maps to the frontend representation of a wire start. Includes disconnected value wire inputs.
pub fn node_graph_wire_inputs(&self, network_path: &[NodeId]) -> Vec<(NodeId, usize)> {
self.node_graph_input_connectors(network_path)
.iter()
.map(|input| match input {
InputConnector::Node { node_id, input_index } => (*node_id, *input_index),
InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index),
})
.chain(std::iter::once((NodeId(u64::MAX), u32::MAX as usize)))
.collect()
}
pub fn unload_wires_for_node_old(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
let number_of_outputs = self.number_of_outputs(node_id, network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in reorder_export");
return;
};
let mut input_connectors = Vec::new();
for output_index in 0..number_of_outputs {
let Some(inputs) = outward_wires.get(&OutputConnector::node(*node_id, output_index)) else {
continue;
};
input_connectors.extend(inputs.clone())
}
for input_index in 0..self.number_of_inputs(node_id, network_path) {
input_connectors.push(InputConnector::node(*node_id, input_index));
}
for input in input_connectors {
self.unload_wire_old(&input, network_path);
}
}
pub fn unload_wire_old(&mut self, input: &InputConnector, network_path: &[NodeId]) {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
input_metadata.transient_metadata.wire = TransientMetadata::Unloaded;
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return;
};
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
*input_metadata = TransientMetadata::Unloaded;
}
}
}
/// When previewing, there may be a second path to the root node.
pub fn wire_to_root_old(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdateOld> {
let input = InputConnector::Export(0);
let current_export = self.upstream_output_connector(&input, network_path)?;
let root_node = match self.previewing(network_path) {
Previewing::Yes { root_node_to_restore } => root_node_to_restore,
Previewing::No => None,
}?;
if Some(root_node.node_id) == current_export.node_id() {
return None;
}
let Some(input_position) = self.get_input_center(&input, network_path) else {
log::error!("Could not get input position for wire end in root node: {input:?}");
return None;
};
let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index);
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output position for wire start in root node: {upstream_output:?}");
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start: bool = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
let path_string = vector_wire.to_svg();
let data_type = self.input_type(&input, network_path).displayed_type();
let wire_path_update = Some(WirePathOld {
path_string,
data_type,
thick,
dashed: false,
});
Some(WirePathUpdateOld {
id: NodeId(u64::MAX),
input_index: u32::MAX as usize,
wire_path_update,
})
}
/// Returns the vector subpath and a boolean of whether the wire should be thick.
pub fn vector_wire_from_input_old(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, bool)> {
let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
};
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return Some((BezPath::new(), false));
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output port for wire start: {:?}", upstream_output);
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
Some((build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style), thick))
}
pub fn wire_path_from_input_old(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePathOld> {
let (vector_wire, thick) = self.vector_wire_from_input_old(input, graph_wire_style, network_path)?;
let path_string = vector_wire.to_svg();
let data_type = self
.upstream_output_connector(input, network_path)
.as_ref()
.map(|output| self.output_type(output, network_path).displayed_type())
.unwrap_or(FrontendGraphDataType::General);
Some(WirePathOld {
path_string,
data_type,
thick,
dashed,
})
}
pub fn collect_layer_widths_old(&mut self, network_path: &[NodeId]) -> (HashMap<NodeId, u32>, HashMap<NodeId, u32>, HashMap<NodeId, bool>) {
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in collect_layer_widths");
return (HashMap::new(), HashMap::new(), HashMap::new());
};
let nodes = network_metadata
.persistent_metadata
.node_metadata
.iter()
.filter_map(|(node_id, _)| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None })
.collect::<Vec<_>>();
let layer_widths = nodes
.iter()
.filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width)))
.collect::<HashMap<NodeId, u32>>();
let chain_widths = nodes.iter().map(|node_id| (*node_id, self.chain_width(node_id, network_path))).collect::<HashMap<NodeId, u32>>();
let has_left_input_wire = nodes
.iter()
.map(|node_id| {
(
*node_id,
!self
.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow)
.skip(1)
.all(|node_id| self.is_chain(&node_id, network_path)),
)
})
.collect::<HashMap<NodeId, bool>>();
(layer_widths, chain_widths, has_left_input_wire)
}
}
@@ -4,11 +4,11 @@ use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNodeImplementation, InlineRust, NodeInput}; use graph_craft::document::{DocumentNodeImplementation, InlineRust, NodeInput};
use graph_craft::proto::GraphErrors; use graph_craft::proto::GraphErrors;
use graph_craft::{Type, concrete}; use graph_craft::{Type, concrete};
use graphene_std::node_graph_overlay::types::FrontendGraphDataType;
use graphene_std::uuid::NodeId; use graphene_std::uuid::NodeId;
use interpreted_executor::dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta}; use interpreted_executor::dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta};
use interpreted_executor::node_registry::NODE_REGISTRY; use interpreted_executor::node_registry::NODE_REGISTRY;
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector};
// This file contains utility methods for interfacing with the resolved types returned from the compiler // This file contains utility methods for interfacing with the resolved types returned from the compiler
@@ -1,10 +1,10 @@
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use glam::{DVec2, IVec2}; use glam::{DVec2, IVec2};
use graphene_std::node_graph_overlay::types::FrontendGraphDataType;
use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point}; use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point};
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape}; use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WirePath { pub struct WirePathOld {
#[serde(rename = "pathString")] #[serde(rename = "pathString")]
pub path_string: String, pub path_string: String,
#[serde(rename = "dataType")] #[serde(rename = "dataType")]
@@ -14,13 +14,21 @@ pub struct WirePath {
} }
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WirePathUpdate { pub struct WirePathUpdateOld {
pub id: NodeId, pub id: NodeId,
#[serde(rename = "inputIndex")] #[serde(rename = "inputIndex")]
pub input_index: usize, pub input_index: usize,
// If none, then remove the wire from the map // If none, then remove the wire from the map
#[serde(rename = "wirePathUpdate")] #[serde(rename = "wirePathUpdate")]
pub wire_path_update: Option<WirePath>, pub wire_path_update: Option<WirePathOld>,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WirePathInProgress {
pub wire: String,
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub thick: bool,
} }
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
@@ -18,6 +18,7 @@ pub struct MenuBarMessageHandler {
pub has_selection_history: (bool, bool), pub has_selection_history: (bool, bool),
pub message_logging_verbosity: MessageLoggingVerbosity, pub message_logging_verbosity: MessageLoggingVerbosity,
pub reset_node_definitions_on_open: bool, pub reset_node_definitions_on_open: bool,
pub render_native_node_graph: bool,
pub make_path_editable_is_allowed: bool, pub make_path_editable_is_allowed: bool,
pub data_panel_open: bool, pub data_panel_open: bool,
pub layers_panel_open: bool, pub layers_panel_open: bool,
@@ -48,6 +49,7 @@ impl LayoutHolder for MenuBarMessageHandler {
let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names; let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names;
let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents; let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents;
let reset_node_definitions_on_open = self.reset_node_definitions_on_open; let reset_node_definitions_on_open = self.reset_node_definitions_on_open;
let render_native_node_graph = self.render_native_node_graph;
let make_path_editable_is_allowed = self.make_path_editable_is_allowed; let make_path_editable_is_allowed = self.make_path_editable_is_allowed;
let menu_bar_entries = vec![ let menu_bar_entries = vec![
@@ -696,6 +698,12 @@ impl LayoutHolder for MenuBarMessageHandler {
action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen.into()), action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen.into()),
..MenuBarEntry::default() ..MenuBarEntry::default()
}], }],
vec![MenuBarEntry {
label: " Render Native Node Graph UI".into(),
icon: Some(if render_native_node_graph { "CheckboxChecked" } else { "CheckboxUnchecked" }.into()),
action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleRenderNativeNodeGraph.into()),
..MenuBarEntry::default()
}],
vec![ vec![
MenuBarEntry { MenuBarEntry {
label: "Print Trace Logs".into(), label: "Print Trace Logs".into(),
@@ -81,6 +81,7 @@ pub enum PortfolioMessage {
to_front: bool, to_front: bool,
select_after_open: bool, select_after_open: bool,
}, },
ToggleRenderNativeNodeGraph,
ToggleResetNodesToDefinitionsOnOpen, ToggleResetNodesToDefinitionsOnOpen,
PasteIntoFolder { PasteIntoFolder {
clipboard: Clipboard, clipboard: Clipboard,
@@ -63,6 +63,7 @@ pub struct PortfolioMessageHandler {
pub layers_panel_open: bool, pub layers_panel_open: bool,
#[derivative(Default(value = "true"))] #[derivative(Default(value = "true"))]
pub properties_panel_open: bool, pub properties_panel_open: bool,
pub render_native_node_graph: bool,
} }
#[message_handler_data] #[message_handler_data]
@@ -111,6 +112,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
(!metadata.selection_undo_history.is_empty(), !metadata.selection_redo_history.is_empty()) (!metadata.selection_undo_history.is_empty(), !metadata.selection_redo_history.is_empty())
}; };
self.menu_bar_message_handler.make_path_editable_is_allowed = make_path_editable_is_allowed(&mut document.network_interface).is_some(); self.menu_bar_message_handler.make_path_editable_is_allowed = make_path_editable_is_allowed(&mut document.network_interface).is_some();
self.menu_bar_message_handler.render_native_node_graph = self.render_native_node_graph;
} }
self.menu_bar_message_handler.process_message(message, responses, ()); self.menu_bar_message_handler.process_message(message, responses, ());
@@ -129,6 +131,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
data_panel_open: self.data_panel_open, data_panel_open: self.data_panel_open,
layers_panel_open: self.layers_panel_open, layers_panel_open: self.layers_panel_open,
properties_panel_open: self.properties_panel_open, properties_panel_open: self.properties_panel_open,
render_native_node_graph: self.render_native_node_graph,
}; };
document.process_message(message, responses, document_inputs) document.process_message(message, responses, document_inputs)
} }
@@ -170,6 +173,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
data_panel_open: self.data_panel_open, data_panel_open: self.data_panel_open,
layers_panel_open: self.layers_panel_open, layers_panel_open: self.layers_panel_open,
properties_panel_open: self.properties_panel_open, properties_panel_open: self.properties_panel_open,
render_native_node_graph: self.render_native_node_graph,
}; };
document.process_message(message, responses, document_inputs) document.process_message(message, responses, document_inputs)
} }
@@ -442,6 +446,13 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
select_after_open: true, select_after_open: true,
}); });
} }
PortfolioMessage::ToggleRenderNativeNodeGraph => {
self.render_native_node_graph = !self.render_native_node_graph;
responses.add(FrontendMessage::UpdateRenderNativeNodeGraph {
render_native_node_graph: self.render_native_node_graph,
});
responses.add(NodeGraphMessage::SendGraph);
}
PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen => { PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen => {
self.reset_node_definitions_on_open = !self.reset_node_definitions_on_open; self.reset_node_definitions_on_open = !self.reset_node_definitions_on_open;
responses.add(MenuBarMessage::SendLayout); responses.add(MenuBarMessage::SendLayout);
@@ -914,8 +925,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open }); responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
if node_graph_open { if node_graph_open {
responses.add(NodeGraphMessage::UpdateGraphBarRight); responses.add(NodeGraphMessage::UpdateGraphBarRight);
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::UnloadWiresOld);
responses.add(NodeGraphMessage::SendWires) responses.add(NodeGraphMessage::SendWiresOld)
} else { } else {
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
@@ -87,8 +87,7 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
} }
PreferencesMessage::GraphWireStyle { style } => { PreferencesMessage::GraphWireStyle { style } => {
self.graph_wire_style = style; self.graph_wire_style = style;
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::SendGraph);
responses.add(NodeGraphMessage::SendWires);
} }
PreferencesMessage::ViewportZoomWheelRate { rate } => { PreferencesMessage::ViewportZoomWheelRate { rate } => {
self.viewport_zoom_wheel_rate = rate; self.viewport_zoom_wheel_rate = rate;
+4 -4
View File
@@ -326,11 +326,11 @@ pub trait FrontendMessageTestUtils {
impl FrontendMessageTestUtils for FrontendMessage { impl FrontendMessageTestUtils for FrontendMessage {
fn check_node_graph_error(&self) { fn check_node_graph_error(&self) {
let FrontendMessage::UpdateNodeGraphNodes { nodes, .. } = self else { return }; let FrontendMessage::UpdateNodeGraphRenderNew { nodes_to_render, .. } = self else { return };
for node in nodes { for node in nodes_to_render {
if let Some(error) = &node.errors { if let Some(error) = &node.metadata.errors {
panic!("error on {}: {}", node.display_name, error); panic!("error on {}: {}", node.metadata.display_name, error);
} }
} }
} }
@@ -569,7 +569,7 @@
{/if} {/if}
</div> </div>
<div class="graph-view" class:open={$document.graphViewOverlayOpen} style:--fade-artwork={`${$document.fadeArtwork}%`} data-graph> <div class="graph-view" class:open={$document.graphViewOverlayOpen} data-graph>
<Graph /> <Graph />
</div> </div>
</LayoutCol> </LayoutCol>
@@ -768,7 +768,7 @@
.viewport-container-inner-2 { .viewport-container-inner-2 {
flex: 1 1 100%; flex: 1 1 100%;
position: relative; position: relative;
overflow: hidden;
.viewport { .viewport {
background: var(--color-2-mildblack); background: var(--color-2-mildblack);
} }
@@ -852,7 +852,6 @@
} }
} }
.fade-artwork,
.graph { .graph {
position: absolute; position: absolute;
top: 0; top: 0;
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
These components will continue to be rendered in svelte after the first stage of native node graph rendering.
- Import and export ports
- Wires to import and export
- Wire in progress
- Add Node context menu
- Toggle Layer context menu
- Error dialog
- Node/Input/Output Tooltips (disabled until tooltip system)
- Solo drag grip tooltip (disabled until tooltip system)
These elements will be not be rendered in svelte when rendering the native node graph. They are rendered below all other components
- Dot grid background
- Nodes/Layers
- Wires between nodes/layers
+96 -53
View File
@@ -25,33 +25,46 @@ export type XY = { x: number; y: number };
// for details about how to transform the JSON from wasm-bindgen into classes. // for details about how to transform the JSON from wasm-bindgen into classes.
// ============================================================================ // ============================================================================
export class UpdateBox extends JsMessage {
readonly box!: Box | undefined;
}
export class UpdateClickTargets extends JsMessage { export class UpdateClickTargets extends JsMessage {
readonly clickTargets!: FrontendClickTargets | undefined; readonly clickTargets!: FrontendClickTargets | undefined;
} }
export class FrontendSelectionBox {
readonly startX!: number;
readonly startY!: number;
readonly endX!: number;
readonly endY!: number;
}
export class UpdateNodeGraphSelectionBox extends JsMessage {
readonly box!: FrontendSelectionBox | undefined;
}
export class UpdateContextMenuInformation extends JsMessage { export class UpdateContextMenuInformation extends JsMessage {
readonly contextMenuInformation!: ContextMenuInformation | undefined; readonly contextMenuInformation!: ContextMenuInformation | undefined;
} }
export type ContextMenuInformation = {
contextMenuCoordinates: XY;
contextMenuData: { type: "CreateNode"; data: { compatibleType: string | undefined } } | { type: "ModifyNode"; data: { canBeLayer: boolean; currentlyIsNode: boolean; nodeId: bigint } };
};
export class UpdateImportsExports extends JsMessage { export class UpdateImportsExports extends JsMessage {
readonly imports!: (FrontendGraphOutput | undefined)[]; readonly imports!: (FrontendImport | undefined)[];
readonly exports!: (FrontendGraphInput | undefined)[]; readonly exports!: FrontendExports;
@TupleToVec2
readonly importPosition!: XY; readonly importPosition!: XY;
@TupleToVec2
readonly exportPosition!: XY; readonly exportPosition!: XY;
readonly addImportExport!: boolean; readonly addImportExport!: boolean;
} }
export class UpdateInSelectedNetwork extends JsMessage { export class UpdateInSelectedNetworkOld extends JsMessage {
readonly inSelectedNetwork!: boolean; readonly inSelectedNetwork!: boolean;
} }
@@ -67,7 +80,7 @@ const LayerWidths = Transform(({ obj }) => obj.layerWidths);
const ChainWidths = Transform(({ obj }) => obj.chainWidths); const ChainWidths = Transform(({ obj }) => obj.chainWidths);
const HasLeftInputWire = Transform(({ obj }) => obj.hasLeftInputWire); const HasLeftInputWire = Transform(({ obj }) => obj.hasLeftInputWire);
export class UpdateLayerWidths extends JsMessage { export class UpdateLayerWidthsOld extends JsMessage {
@LayerWidths @LayerWidths
readonly layerWidths!: Map<bigint, number>; readonly layerWidths!: Map<bigint, number>;
@ChainWidths @ChainWidths
@@ -76,9 +89,9 @@ export class UpdateLayerWidths extends JsMessage {
readonly hasLeftInputWire!: Map<bigint, boolean>; readonly hasLeftInputWire!: Map<bigint, boolean>;
} }
export class UpdateNodeGraphNodes extends JsMessage { export class UpdateNodeGraphNodesOld extends JsMessage {
@Type(() => FrontendNode) @Type(() => FrontendNodeOld)
readonly nodes!: FrontendNode[]; readonly nodes!: FrontendNodeOld[];
} }
export class UpdateNodeGraphError extends JsMessage { export class UpdateNodeGraphError extends JsMessage {
@@ -90,15 +103,15 @@ export class NodeGraphError {
readonly error!: string; readonly error!: string;
} }
export class UpdateVisibleNodes extends JsMessage { export class UpdateVisibleNodesOld extends JsMessage {
readonly nodes!: bigint[]; readonly nodes!: bigint[];
} }
export class UpdateNodeGraphWires extends JsMessage { export class UpdateNodeGraphWiresOld extends JsMessage {
readonly wires!: WireUpdate[]; readonly wires!: WireUpdateOld[];
} }
export class ClearAllNodeGraphWires extends JsMessage {} export class ClearAllNodeGraphWiresOld extends JsMessage {}
export class UpdateNodeGraphTransform extends JsMessage { export class UpdateNodeGraphTransform extends JsMessage {
readonly transform!: NodeGraphTransform; readonly transform!: NodeGraphTransform;
@@ -109,7 +122,7 @@ const NodeDescriptions = Transform(({ obj }) => new Map(obj.nodeDescriptions));
export class SendUIMetadata extends JsMessage { export class SendUIMetadata extends JsMessage {
@NodeDescriptions @NodeDescriptions
readonly nodeDescriptions!: Map<string, string>; readonly nodeDescriptions!: Map<string, string>;
@Type(() => FrontendNode) @Type(() => FrontendNodeOld)
readonly nodeTypes!: FrontendNodeType[]; readonly nodeTypes!: FrontendNodeType[];
} }
@@ -119,7 +132,7 @@ export class UpdateNodeThumbnail extends JsMessage {
readonly value!: string; readonly value!: string;
} }
export class UpdateNodeGraphSelection extends JsMessage { export class UpdateNodeGraphSelectionOld extends JsMessage {
@Type(() => BigInt) @Type(() => BigInt)
readonly selected!: bigint[]; readonly selected!: bigint[];
} }
@@ -129,8 +142,18 @@ export class UpdateOpenDocumentsList extends JsMessage {
readonly openDocuments!: OpenDocument[]; readonly openDocuments!: OpenDocument[];
} }
export class WirePathInProgress {
readonly wire!: string;
readonly thick!: boolean;
readonly dataType!: FrontendGraphDataType;
}
export class UpdateWirePathInProgress extends JsMessage { export class UpdateWirePathInProgress extends JsMessage {
readonly wirePath!: WirePath | undefined; readonly wirePathInProgress!: WirePathInProgress | undefined;
}
export class UpdateRenderNativeNodeGraph extends JsMessage {
readonly renderNativeNodeGraph!: boolean;
} }
export class OpenDocument { export class OpenDocument {
@@ -148,14 +171,8 @@ export class DocumentDetails {
readonly isSaved!: boolean; readonly isSaved!: boolean;
} }
export class Box { export class FrontendDocumentDetails extends DocumentDetails {
readonly startX!: number; readonly id!: bigint;
readonly startY!: number;
readonly endX!: number;
readonly endY!: number;
} }
export type FrontendClickTargets = { export type FrontendClickTargets = {
@@ -167,14 +184,9 @@ export type FrontendClickTargets = {
readonly modifyImportExport: string[]; readonly modifyImportExport: string[];
}; };
export type ContextMenuInformation = {
contextMenuCoordinates: XY;
contextMenuData: { type: "CreateNode"; data: { compatibleType: string | undefined } } | { type: "ModifyNode"; data: { canBeLayer: boolean; currentlyIsNode: boolean; nodeId: bigint } };
};
export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color" | "Invalid"; export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color" | "Invalid";
export class FrontendGraphInput { export class FrontendGraphInputOld {
readonly dataType!: FrontendGraphDataType; readonly dataType!: FrontendGraphDataType;
readonly name!: string; readonly name!: string;
@@ -188,7 +200,15 @@ export class FrontendGraphInput {
readonly connectedTo!: string; readonly connectedTo!: string;
} }
export class FrontendGraphOutput { export class FrontendGraphInputNew {
readonly dataType!: FrontendGraphDataType;
readonly name!: string;
readonly connectedToNode!: bigint | undefined;
}
export class FrontendGraphOutputOld {
readonly dataType!: FrontendGraphDataType; readonly dataType!: FrontendGraphDataType;
readonly name!: string; readonly name!: string;
@@ -199,8 +219,30 @@ export class FrontendGraphOutput {
readonly connectedTo!: string[]; readonly connectedTo!: string[];
} }
export class FrontendGraphOutputNew {
readonly dataType!: FrontendGraphDataType;
export class FrontendNode { readonly name!: string;
readonly connected!: boolean;
}
export class FrontendExport {
readonly port!: FrontendGraphInputNew;
readonly wire!: string | undefined;
}
export class FrontendExports {
readonly exports!: (FrontendExport | undefined)[];
readonly previewWire!: string | undefined;
}
export class FrontendImport {
readonly port!: FrontendGraphOutputNew;
readonly wires!: string[];
}
export class FrontendNodeOld {
readonly isLayer!: boolean; readonly isLayer!: boolean;
readonly canBeLayer!: boolean; readonly canBeLayer!: boolean;
@@ -211,13 +253,13 @@ export class FrontendNode {
readonly displayName!: string; readonly displayName!: string;
readonly primaryInput!: FrontendGraphInput | undefined; readonly primaryInput!: FrontendGraphInputOld | undefined;
readonly exposedInputs!: FrontendGraphInput[]; readonly exposedInputs!: FrontendGraphInputOld[];
readonly primaryOutput!: FrontendGraphOutput | undefined; readonly primaryOutput!: FrontendGraphOutputOld | undefined;
readonly exposedOutputs!: FrontendGraphOutput[]; readonly exposedOutputs!: FrontendGraphOutputOld[];
readonly primaryInputConnectedToLayer!: boolean; readonly primaryInputConnectedToLayer!: boolean;
@@ -251,17 +293,17 @@ export class NodeGraphTransform {
readonly y!: number; readonly y!: number;
} }
export class WirePath { export class WirePathOld {
readonly pathString!: string; readonly pathString!: string;
readonly dataType!: FrontendGraphDataType; readonly dataType!: FrontendGraphDataType;
readonly thick!: boolean; readonly thick!: boolean;
readonly dashed!: boolean; readonly dashed!: boolean;
} }
export class WireUpdate { export class WireUpdateOld {
readonly id!: bigint; readonly id!: bigint;
readonly inputIndex!: number; readonly inputIndex!: number;
readonly wirePathUpdate!: WirePath | undefined; readonly wirePathUpdate!: WirePathOld | undefined;
} }
export class TriggerPersistenceWriteDocument extends JsMessage { export class TriggerPersistenceWriteDocument extends JsMessage {
@@ -695,7 +737,7 @@ export class UpdateGraphViewOverlay extends JsMessage {
open!: boolean; open!: boolean;
} }
export class UpdateGraphFadeArtwork extends JsMessage { export class UpdateGraphFadeArtworkOld extends JsMessage {
readonly percentage!: number; readonly percentage!: number;
} }
@@ -1627,7 +1669,7 @@ type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHand
type MessageMaker = typeof JsMessage | JSMessageFactory; type MessageMaker = typeof JsMessage | JSMessageFactory;
export const messageMakers: Record<string, MessageMaker> = { export const messageMakers: Record<string, MessageMaker> = {
ClearAllNodeGraphWires, ClearAllNodeGraphWiresOld,
DisplayDialog, DisplayDialog,
DisplayDialogDismiss, DisplayDialogDismiss,
DisplayDialogPanic, DisplayDialogPanic,
@@ -1656,7 +1698,6 @@ export const messageMakers: Record<string, MessageMaker> = {
TriggerTextCopy, TriggerTextCopy,
TriggerVisitLink, TriggerVisitLink,
UpdateActiveDocument, UpdateActiveDocument,
UpdateBox,
UpdateClickTargets, UpdateClickTargets,
UpdateContextMenuInformation, UpdateContextMenuInformation,
UpdateDialogButtons, UpdateDialogButtons,
@@ -1671,25 +1712,26 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateDocumentScrollbars, UpdateDocumentScrollbars,
UpdateExportReorderIndex, UpdateExportReorderIndex,
UpdateEyedropperSamplingState, UpdateEyedropperSamplingState,
UpdateGraphFadeArtwork, UpdateGraphFadeArtworkOld,
UpdateGraphViewOverlay, UpdateGraphViewOverlay,
UpdateImportReorderIndex, UpdateImportReorderIndex,
UpdateImportsExports, UpdateImportsExports,
UpdateInputHints, UpdateInputHints,
UpdateInSelectedNetwork, UpdateInSelectedNetworkOld,
UpdateLayersPanelBottomBarLayout, UpdateLayersPanelBottomBarLayout,
UpdateLayersPanelControlBarLeftLayout, UpdateLayersPanelControlBarLeftLayout,
UpdateLayersPanelControlBarRightLayout, UpdateLayersPanelControlBarRightLayout,
UpdateLayerWidths, UpdateLayerWidthsOld,
UpdateMaximized, UpdateMaximized,
UpdateMenuBarLayout, UpdateMenuBarLayout,
UpdateMouseCursor, UpdateMouseCursor,
UpdateNodeGraphControlBarLayout, UpdateNodeGraphControlBarLayout,
UpdateNodeGraphNodes, UpdateNodeGraphNodesOld,
UpdateNodeGraphError, UpdateNodeGraphError,
UpdateNodeGraphSelection, UpdateNodeGraphSelectionOld,
UpdateNodeGraphSelectionBox,
UpdateNodeGraphTransform, UpdateNodeGraphTransform,
UpdateNodeGraphWires, UpdateNodeGraphWiresOld,
UpdateNodeThumbnail, UpdateNodeThumbnail,
UpdateOpenDocumentsList, UpdateOpenDocumentsList,
UpdatePlatform, UpdatePlatform,
@@ -1701,8 +1743,9 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateToolOptionsLayout, UpdateToolOptionsLayout,
UpdateToolShelfLayout, UpdateToolShelfLayout,
UpdateViewportHolePunch, UpdateViewportHolePunch,
UpdateVisibleNodes, UpdateVisibleNodesOld,
UpdateWirePathInProgress, UpdateWirePathInProgress,
UpdateRenderNativeNodeGraph,
UpdateWorkingColorsLayout, UpdateWorkingColorsLayout,
} as const; } as const;
export type JsMessageType = keyof typeof messageMakers; export type JsMessageType = keyof typeof messageMakers;
-9
View File
@@ -13,7 +13,6 @@ import {
UpdateWorkingColorsLayout, UpdateWorkingColorsLayout,
UpdateNodeGraphControlBarLayout, UpdateNodeGraphControlBarLayout,
UpdateGraphViewOverlay, UpdateGraphViewOverlay,
UpdateGraphFadeArtwork,
} from "@graphite/messages"; } from "@graphite/messages";
export function createDocumentState(editor: Editor) { export function createDocumentState(editor: Editor) {
@@ -25,19 +24,11 @@ export function createDocumentState(editor: Editor) {
toolShelfLayout: defaultWidgetLayout(), toolShelfLayout: defaultWidgetLayout(),
workingColorsLayout: defaultWidgetLayout(), workingColorsLayout: defaultWidgetLayout(),
nodeGraphControlBarLayout: defaultWidgetLayout(), nodeGraphControlBarLayout: defaultWidgetLayout(),
// Graph view overlay
graphViewOverlayOpen: false, graphViewOverlayOpen: false,
fadeArtwork: 100,
}); });
const { subscribe, update } = state; const { subscribe, update } = state;
// Update layouts // Update layouts
editor.subscriptions.subscribeJsMessage(UpdateGraphFadeArtwork, (updateGraphFadeArtwork) => {
update((state) => {
state.fadeArtwork = updateGraphFadeArtwork.percentage;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateDocumentModeLayout, async (updateDocumentModeLayout) => { editor.subscriptions.subscribeJsMessage(UpdateDocumentModeLayout, async (updateDocumentModeLayout) => {
await tick(); await tick();
+79 -54
View File
@@ -3,55 +3,63 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor"; import { type Editor } from "@graphite/editor";
import type { NodeGraphError } from "@graphite/messages"; import type { NodeGraphError } from "@graphite/messages";
import { import {
type Box, type FrontendSelectionBox,
type FrontendClickTargets, type FrontendClickTargets,
type ContextMenuInformation, type ContextMenuInformation,
type FrontendNode, type FrontendNodeOld,
type FrontendNodeType, type FrontendNodeType,
type WirePath, type WirePathInProgress,
ClearAllNodeGraphWires, type WirePathOld,
ClearAllNodeGraphWiresOld,
SendUIMetadata, SendUIMetadata,
UpdateBox, UpdateNodeGraphSelectionBox,
UpdateClickTargets, UpdateClickTargets,
UpdateContextMenuInformation, UpdateContextMenuInformation,
UpdateInSelectedNetwork, UpdateInSelectedNetworkOld,
UpdateImportReorderIndex, UpdateImportReorderIndex,
UpdateExportReorderIndex, UpdateExportReorderIndex,
UpdateImportsExports, UpdateImportsExports,
UpdateLayerWidths, UpdateLayerWidthsOld,
UpdateNodeGraphNodes, UpdateNodeGraphNodesOld,
UpdateVisibleNodes, UpdateVisibleNodesOld,
UpdateNodeGraphWires, UpdateNodeGraphWiresOld,
UpdateNodeGraphSelection, UpdateNodeGraphSelectionOld,
UpdateNodeGraphTransform, UpdateNodeGraphTransform,
UpdateNodeThumbnail, UpdateNodeThumbnail,
UpdateWirePathInProgress, UpdateWirePathInProgress,
UpdateNodeGraphError, UpdateNodeGraphError,
UpdateRenderNativeNodeGraph,
UpdateGraphFadeArtworkOld,
} from "@graphite/messages"; } from "@graphite/messages";
export function createNodeGraphState(editor: Editor) { export function createNodeGraphState(editor: Editor) {
const { subscribe, update } = writable({ const { subscribe, update } = writable({
box: undefined as Box | undefined, // Data that will continue to be rendered in Svelte
selectionBox: undefined as FrontendSelectionBox | undefined,
clickTargets: undefined as FrontendClickTargets | undefined, clickTargets: undefined as FrontendClickTargets | undefined,
contextMenuInformation: undefined as ContextMenuInformation | undefined,
error: undefined as NodeGraphError | undefined, error: undefined as NodeGraphError | undefined,
layerWidths: new Map<bigint, number>(), wirePathInProgress: undefined as WirePathInProgress | undefined,
chainWidths: new Map<bigint, number>(), importsExports: undefined as UpdateImportsExports | undefined,
hasLeftInputWire: new Map<bigint, boolean>(),
updateImportsExports: undefined as UpdateImportsExports | undefined,
nodes: new Map<bigint, FrontendNode>(),
visibleNodes: new Set<bigint>(),
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
wires: new Map<bigint, Map<number, WirePath>>(),
wirePathInProgress: undefined as WirePath | undefined,
nodeDescriptions: new Map<string, string>(),
nodeTypes: [] as FrontendNodeType[],
thumbnails: new Map<bigint, string>(),
selected: [] as bigint[],
transform: { scale: 1, x: 0, y: 0 },
inSelectedNetwork: true,
reorderImportIndex: undefined as number | undefined, reorderImportIndex: undefined as number | undefined,
reorderExportIndex: undefined as number | undefined, reorderExportIndex: undefined as number | undefined,
contextMenuInformation: undefined as ContextMenuInformation | undefined,
nodeTypes: [] as FrontendNodeType[],
nodeDescriptions: new Map<string, string>(),
thumbnails: new Map<bigint, string>(),
transform: { scale: 1, x: 0, y: 0 },
// Data that will be removed when the node graph is rendered natively
nodesOld: new Map<bigint, FrontendNodeOld>(),
selectedOld: [] as bigint[],
wiresOld: new Map<bigint, Map<number, WirePathOld>>(),
opacityOld: 80,
inSelectedNetworkOld: true,
previewedNodeOld: undefined as bigint | undefined,
visibleNodesOld: new Set<bigint>(),
layerWidthsOld: new Map<bigint, number>(),
chainWidthsOld: new Map<bigint, number>(),
hasLeftInputWireOld: new Map<bigint, boolean>(),
renderNativeNodeGraphOld: false,
}); });
// Set up message subscriptions on creation // Set up message subscriptions on creation
@@ -62,15 +70,21 @@ export function createNodeGraphState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateBox, (updateBox) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSelectionBox, (updateBox) => {
update((state) => { update((state) => {
state.box = updateBox.box; state.selectionBox = updateBox.box;
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (UpdateClickTargets) => { editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (updateClickTargets) => {
update((state) => { update((state) => {
state.clickTargets = UpdateClickTargets.clickTargets; state.clickTargets = updateClickTargets.clickTargets;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => {
update((state) => {
state.wirePathInProgress = updateWirePathInProgress.wirePathInProgress;
return state; return state;
}); });
}); });
@@ -94,29 +108,29 @@ export function createNodeGraphState(editor: Editor) {
}); });
editor.subscriptions.subscribeJsMessage(UpdateImportsExports, (updateImportsExports) => { editor.subscriptions.subscribeJsMessage(UpdateImportsExports, (updateImportsExports) => {
update((state) => { update((state) => {
state.updateImportsExports = updateImportsExports; state.importsExports = updateImportsExports;
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateInSelectedNetwork, (updateInSelectedNetwork) => { editor.subscriptions.subscribeJsMessage(UpdateInSelectedNetworkOld, (updateInSelectedNetwork) => {
update((state) => { update((state) => {
state.inSelectedNetwork = updateInSelectedNetwork.inSelectedNetwork; state.inSelectedNetworkOld = updateInSelectedNetwork.inSelectedNetwork;
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateLayerWidths, (updateLayerWidths) => { editor.subscriptions.subscribeJsMessage(UpdateLayerWidthsOld, (updateLayerWidths) => {
update((state) => { update((state) => {
state.layerWidths = updateLayerWidths.layerWidths; state.layerWidthsOld = updateLayerWidths.layerWidths;
state.chainWidths = updateLayerWidths.chainWidths; state.chainWidthsOld = updateLayerWidths.chainWidths;
state.hasLeftInputWire = updateLayerWidths.hasLeftInputWire; state.hasLeftInputWireOld = updateLayerWidths.hasLeftInputWire;
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodesOld, (updateNodeGraphNodesOld) => {
update((state) => { update((state) => {
state.nodes.clear(); state.nodesOld.clear();
updateNodeGraphNodes.nodes.forEach((node) => { updateNodeGraphNodesOld.nodes.forEach((node) => {
state.nodes.set(node.id, node); state.nodesOld.set(node.id, node);
}); });
return state; return state;
}); });
@@ -127,20 +141,20 @@ export function createNodeGraphState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateVisibleNodes, (updateVisibleNodes) => { editor.subscriptions.subscribeJsMessage(UpdateVisibleNodesOld, (updateVisibleNodesOld) => {
update((state) => { update((state) => {
state.visibleNodes = new Set<bigint>(updateVisibleNodes.nodes); state.visibleNodesOld = new Set<bigint>(updateVisibleNodesOld.nodes);
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphWires, (updateNodeWires) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphWiresOld, (updateNodeWires) => {
update((state) => { update((state) => {
updateNodeWires.wires.forEach((wireUpdate) => { updateNodeWires.wires.forEach((wireUpdate) => {
let inputMap = state.wires.get(wireUpdate.id); let inputMap = state.wiresOld.get(wireUpdate.id);
// If it doesn't exist, create it and set it in the outer map // If it doesn't exist, create it and set it in the outer map
if (!inputMap) { if (!inputMap) {
inputMap = new Map(); inputMap = new Map();
state.wires.set(wireUpdate.id, inputMap); state.wiresOld.set(wireUpdate.id, inputMap);
} }
if (wireUpdate.wirePathUpdate !== undefined) { if (wireUpdate.wirePathUpdate !== undefined) {
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate); inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
@@ -151,15 +165,15 @@ export function createNodeGraphState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWires, (_) => { editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWiresOld, (_) => {
update((state) => { update((state) => {
state.wires.clear(); state.wiresOld.clear();
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSelection, (updateNodeGraphSelection) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSelectionOld, (updateNodeGraphSelection) => {
update((state) => { update((state) => {
state.selected = updateNodeGraphSelection.selected; state.selectedOld = updateNodeGraphSelection.selected;
return state; return state;
}); });
}); });
@@ -177,11 +191,22 @@ export function createNodeGraphState(editor: Editor) {
}); });
editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => { editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => {
update((state) => { update((state) => {
state.wirePathInProgress = updateWirePathInProgress.wirePath; state.wirePathInProgress = updateWirePathInProgress.wirePathInProgress;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateRenderNativeNodeGraph, (updateRenderNativeNodeGraph) => {
update((state) => {
state.renderNativeNodeGraphOld = updateRenderNativeNodeGraph.renderNativeNodeGraph;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateGraphFadeArtworkOld, (updateGraphFadeArtwork) => {
update((state) => {
state.opacityOld = updateGraphFadeArtwork.percentage;
return state; return state;
}); });
}); });
return { return {
subscribe, subscribe,
}; };
+1
View File
@@ -17,6 +17,7 @@ pub mod logic;
pub mod math; pub mod math;
pub mod memo; pub mod memo;
pub mod misc; pub mod misc;
pub mod node_graph_overlay;
pub mod ops; pub mod ops;
pub mod raster; pub mod raster;
pub mod raster_types; pub mod raster_types;
@@ -0,0 +1 @@
pub mod types;
@@ -0,0 +1,163 @@
use glam::{DAffine2, DVec2, IVec2};
use kurbo::BezPath;
use crate::{Graphic, uuid::NodeId};
use std::{
collections::HashMap,
hash::{Hash, Hasher},
};
#[derive(Clone, Debug, Default, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct NodeGraphTransform {
pub scale: f64,
pub x: f64,
pub y: f64,
}
/// Stores node graph coordinates which are then transformed in svelte based on the node graph transform
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendXY {
pub x: i32,
pub y: i32,
}
impl From<DVec2> for FrontendXY {
fn from(v: DVec2) -> Self {
FrontendXY { x: v.x as i32, y: v.y as i32 }
}
}
impl From<IVec2> for FrontendXY {
fn from(v: IVec2) -> Self {
FrontendXY { x: v.x as i32, y: v.y as i32 }
}
}
impl Hash for NodeGraphTransform {
fn hash<H: Hasher>(&self, state: &mut H) {
// Convert f64 to u64 bit pattern for hashing
self.scale.to_bits().hash(state);
self.x.to_bits().hash(state);
self.y.to_bits().hash(state);
}
}
impl NodeGraphTransform {
pub fn to_daffine2(&self) -> DAffine2 {
DAffine2::from_scale_angle_translation(DVec2::splat(self.scale), 0.0, DVec2::new(self.x, self.y))
}
}
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphInputNew {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
/// Used to render the upstream node once this node is rendered
#[serde(rename = "connectedToNode")]
pub connected_to_node: Option<NodeId>,
}
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendGraphOutputNew {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
pub connected: bool,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType {
#[default]
General,
Number,
Artboard,
Graphic,
Raster,
Vector,
Color,
Gradient,
Typography,
Invalid,
}
#[derive(Clone, Debug, Default, PartialEq, dyn_any::DynAny)]
pub struct NodeGraphOverlayData {
pub nodes_to_render: Vec<FrontendNodeToRenderNew>,
pub in_selected_network: bool,
// Displays a dashed border around the node
pub previewed_node: Option<NodeId>,
pub thumbnails: HashMap<NodeId, Graphic>,
}
impl Hash for NodeGraphOverlayData {
fn hash<H: Hasher>(&self, state: &mut H) {
self.nodes_to_render.hash(state);
self.in_selected_network.hash(state);
self.previewed_node.hash(state);
let mut entries: Vec<_> = self.thumbnails.iter().collect();
entries.sort_by(|a, b| a.0.cmp(b.0));
let mut hasher = std::collections::hash_map::DefaultHasher::new();
entries.hash(&mut hasher);
}
}
#[derive(Clone, Debug, PartialEq, dyn_any::DynAny)]
pub struct FrontendNodeToRenderNew {
pub metadata: FrontendNodeMetadataNew,
pub node_or_layer: FrontendNodeOrLayer,
//TODO: Remove and replace with method of generating wires when generating nodes
pub wires: Vec<(BezPath, bool, FrontendGraphDataType)>,
}
impl Hash for FrontendNodeToRenderNew {
fn hash<H: Hasher>(&self, state: &mut H) {
self.metadata.hash(state);
self.node_or_layer.hash(state);
}
}
// Metadata that is common to nodes and layers
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny)]
pub struct FrontendNodeMetadataNew {
pub node_id: NodeId,
pub display_name: String,
pub selected: bool,
// Used to get the description, which is stored in a global hashmap
pub reference: Option<String>,
// Reduces opacity of node/hidden eye icon
pub visible: bool,
// The svg string for each input
// pub wires: Vec<Option<String>>,
pub errors: Option<String>,
}
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny)]
pub struct FrontendNodeNew {
// pub position: FrontendNodePosition,
pub position: FrontendXY,
pub primary_output: Option<FrontendGraphOutputNew>,
pub primary_input: Option<FrontendGraphInputNew>,
pub secondary_inputs: Vec<FrontendGraphInputNew>,
pub secondary_outputs: Vec<FrontendGraphOutputNew>,
}
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny)]
pub struct FrontendLayerNew {
pub bottom_input: FrontendGraphInputNew,
pub side_input: Option<FrontendGraphInputNew>,
pub output: FrontendGraphOutputNew,
// pub position: FrontendLayerPosition,
pub position: FrontendXY,
pub locked: bool,
pub chain_width: u32,
pub layer_has_left_border_gap: bool,
pub primary_input_connected_to_layer: bool,
pub primary_output_connected_to_layer: bool,
}
#[derive(Clone, Debug, PartialEq, Hash, dyn_any::DynAny)]
pub enum FrontendNodeOrLayer {
Node(FrontendNodeNew),
Layer(FrontendLayerNew),
}