From b50062806c2e69a9967d261c0238d9184d6e0d19 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 16 Nov 2025 00:54:18 -0800 Subject: [PATCH] Separate svelte node graph rendering --- editor/src/dispatcher.rs | 1 + .../src/messages/frontend/frontend_message.rs | 47 +- .../utility_types/widgets/button_widgets.rs | 2 +- .../document/document_message_handler.rs | 33 +- .../document/node_graph/node_graph_message.rs | 12 +- .../node_graph/node_graph_message_handler.rs | 165 +- .../document/node_graph/node_properties.rs | 2 +- .../document/node_graph/utility_types.rs | 103 +- .../utility_types/network_interface.rs | 592 +------ .../network_interface/node_graph.rs | 360 ++++ .../old_node_graph_render.rs | 435 +++++ .../network_interface/resolved_types.rs | 2 +- .../portfolio/document/utility_types/wires.rs | 16 +- .../menu_bar/menu_bar_message_handler.rs | 8 + .../messages/portfolio/portfolio_message.rs | 1 + .../portfolio/portfolio_message_handler.rs | 15 +- .../preferences_message_handler.rs | 3 +- editor/src/test_utils.rs | 8 +- .../src/components/panels/Document.svelte | 5 +- frontend/src/components/views/Graph.svelte | 1474 ++++++++++------- frontend/src/components/views/readme.md | 14 + frontend/src/messages.ts | 149 +- frontend/src/state-providers/document.ts | 9 - frontend/src/state-providers/node-graph.ts | 133 +- node-graph/gcore/src/lib.rs | 1 + node-graph/gcore/src/node_graph_overlay.rs | 1 + .../gcore/src/node_graph_overlay/types.rs | 163 ++ 27 files changed, 2302 insertions(+), 1452 deletions(-) create mode 100644 editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs create mode 100644 editor/src/messages/portfolio/document/utility_types/network_interface/old_node_graph_render.rs create mode 100644 frontend/src/components/views/readme.md create mode 100644 node-graph/gcore/src/node_graph_overlay.rs create mode 100644 node-graph/gcore/src/node_graph_overlay/types.rs diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 4119eb0aa9..52db3c1742 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -53,6 +53,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[ /// 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. const FRONTEND_UPDATE_MESSAGES: &[MessageDiscriminant] = &[ + MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel( PropertiesPanelMessageDiscriminant::Refresh, ))), diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 22348c8cc9..c26d82cab2 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -2,14 +2,14 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument}; use crate::messages::app_window::app_window_message_handler::AppWindowPlatform; use crate::messages::layout::utility_types::widget_prelude::*; 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::wires::{WirePath, WirePathUpdate}; +use crate::messages::portfolio::document::utility_types::wires::{WirePathInProgress, WirePathUpdateOld}; use crate::messages::prelude::*; use crate::messages::tool::utility_types::HintData; -use glam::IVec2; use graph_craft::document::NodeId; +use graphene_std::node_graph_overlay::types::{FrontendXY, NodeGraphTransform}; use graphene_std::raster::Image; use graphene_std::raster::color::Color; use graphene_std::text::{Font, TextAlign}; @@ -131,24 +131,23 @@ pub enum FrontendMessage { }, UpdateImportsExports { /// If the primary import is not visible, then it is None. - imports: Vec>, - /// If the primary export is not visible, then it is None. - exports: Vec>, + imports: Vec>, + exports: FrontendExports, /// The primary import location. #[serde(rename = "importPosition")] - import_position: IVec2, + import_position: FrontendXY, /// The primary export location. #[serde(rename = "exportPosition")] - export_position: IVec2, + export_position: FrontendXY, /// The document network does not have an add import or export button. #[serde(rename = "addImportExport")] add_import_export: bool, }, - UpdateInSelectedNetwork { + UpdateInSelectedNetworkOld { #[serde(rename = "inSelectedNetwork")] in_selected_network: bool, }, - UpdateBox { + UpdateNodeGraphSelectionBox { #[serde(rename = "box")] box_selection: Option, }, @@ -185,7 +184,7 @@ pub enum FrontendMessage { #[serde(rename = "exportIndex")] index: Option, }, - UpdateLayerWidths { + UpdateLayerWidthsOld { #[serde(rename = "layerWidths")] layer_widths: HashMap, #[serde(rename = "chainWidths")] @@ -256,7 +255,7 @@ pub enum FrontendMessage { #[serde(rename = "setColorChoice")] set_color_choice: Option, }, - UpdateGraphFadeArtwork { + UpdateGraphFadeArtworkOld { percentage: f64, }, UpdateInputHints { @@ -286,29 +285,33 @@ pub enum FrontendMessage { UpdateMouseCursor { cursor: MouseCursorIcon, }, - UpdateNodeGraphNodes { - nodes: Vec, + UpdateNodeGraphNodesOld { + nodes: Vec, + }, + UpdateRenderNativeNodeGraph { + #[serde(rename = "renderNativeNodeGraph")] + render_native_node_graph: bool, }, UpdateNodeGraphError { error: Option, }, - UpdateVisibleNodes { + UpdateVisibleNodesOld { nodes: Vec, }, - UpdateNodeGraphWires { - wires: Vec, + UpdateNodeGraphWiresOld { + wires: Vec, }, - ClearAllNodeGraphWires, + ClearAllNodeGraphWiresOld, UpdateNodeGraphControlBarLayout { #[serde(rename = "layoutTarget")] layout_target: LayoutTarget, diff: Vec, }, - UpdateNodeGraphSelection { + UpdateNodeGraphSelectionOld { selected: Vec, }, UpdateNodeGraphTransform { - transform: Transform, + transform: NodeGraphTransform, }, UpdateNodeThumbnail { id: NodeId, @@ -334,8 +337,8 @@ pub enum FrontendMessage { diff: Vec, }, UpdateWirePathInProgress { - #[serde(rename = "wirePath")] - wire_path: Option, + #[serde(rename = "wirePathInProgress")] + wire_path_in_progress: Option, }, UpdateWorkingColorsLayout { #[serde(rename = "layoutTarget")] diff --git a/editor/src/messages/layout/utility_types/widgets/button_widgets.rs b/editor/src/messages/layout/utility_types/widgets/button_widgets.rs index 7a7e54856b..d5b7705079 100644 --- a/editor/src/messages/layout/utility_types/widgets/button_widgets.rs +++ b/editor/src/messages/layout/utility_types/widgets/button_widgets.rs @@ -1,8 +1,8 @@ use crate::messages::input_mapper::utility_types::misc::ActionKeys; 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 derivative::*; +use graphene_std::node_graph_overlay::types::FrontendGraphDataType; use graphene_std::vector::style::FillChoice; use graphite_proc_macros::WidgetBuilder; diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 85fb01c76a..cc8c6498d6 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1,5 +1,4 @@ use super::node_graph::document_node_definitions; -use super::node_graph::utility_types::Transform; use super::overlays::utility_types::Pivot; use super::utility_types::error::EditorError; 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::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork}; 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::raster::BlendMode; use graphene_std::raster_types::Raster; @@ -55,6 +55,7 @@ pub struct DocumentMessageContext<'a> { pub data_panel_open: bool, pub layers_panel_open: bool, pub properties_panel_open: bool, + pub render_native_node_graph: bool, pub viewport: &'a ViewportMessageHandler, } @@ -201,6 +202,7 @@ impl MessageHandler> for DocumentMes data_panel_open, layers_panel_open, properties_panel_open, + render_native_node_graph, } = context; match message { @@ -263,6 +265,7 @@ impl MessageHandler> for DocumentMes preferences, layers_panel_open, viewport, + render_native_node_graph, }, ); } @@ -470,7 +473,7 @@ impl MessageHandler> for DocumentMes DocumentMessage::EnterNestedNetwork { node_id } => { self.breadcrumb_network_path.push(node_id); self.selection_network_path.clone_from(&self.breadcrumb_network_path); - responses.add(NodeGraphMessage::UnloadWires); + responses.add(NodeGraphMessage::UnloadWiresOld); responses.add(NodeGraphMessage::SendGraph); responses.add(DocumentMessage::ZoomCanvasToFitAll); responses.add(NodeGraphMessage::UpdateNodeGraphWidth); @@ -490,7 +493,7 @@ impl MessageHandler> for DocumentMes 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_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() { // Exit one level up if inside a nested network responses.add(DocumentMessage::ExitNestedNetwork { steps_back: 1 }); @@ -503,7 +506,7 @@ impl MessageHandler> for DocumentMes self.breadcrumb_network_path.pop(); self.selection_network_path.clone_from(&self.breadcrumb_network_path); } - responses.add(NodeGraphMessage::UnloadWires); + responses.add(NodeGraphMessage::UnloadWiresOld); responses.add(NodeGraphMessage::SendGraph); responses.add(DocumentMessage::PTZUpdate); responses.add(NodeGraphMessage::UpdateNodeGraphWidth); @@ -560,7 +563,7 @@ impl MessageHandler> for DocumentMes self.graph_view_overlay_open = open; responses.add(FrontendMessage::UpdateGraphViewOverlay { open }); - responses.add(FrontendMessage::UpdateGraphFadeArtwork { + responses.add(FrontendMessage::UpdateGraphFadeArtworkOld { percentage: self.graph_fade_artwork_percentage, }); @@ -570,20 +573,21 @@ impl MessageHandler> for DocumentMes responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderScrollbars); if opened { - responses.add(NodeGraphMessage::UnloadWires); + responses.add(NodeGraphMessage::UnloadWiresOld); } if open { responses.add(ToolMessage::DeactivateTools); responses.add(OverlaysMessage::Draw); // Clear the overlays responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. }); responses.add(NodeGraphMessage::UpdateGraphBarRight); - responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::UpdateHints); responses.add(NodeGraphMessage::UpdateEdges); } else { responses.add(ToolMessage::ActivateTool { tool_type: *current_tool }); responses.add(OverlaysMessage::Draw); // Redraw overlays when graph is closed } + + responses.add(NodeGraphMessage::SendGraph); } DocumentMessage::GraphViewOverlayToggle => { responses.add(DocumentMessage::GraphViewOverlay { open: !self.graph_view_overlay_open }); @@ -1196,7 +1200,7 @@ impl MessageHandler> for DocumentMes } responses.add(PropertiesPanelMessage::Refresh); responses.add(NodeGraphMessage::UpdateLayerPanel); - responses.add(NodeGraphMessage::UpdateInSelectedNetwork); + responses.add(NodeGraphMessage::SendGraph); } DocumentMessage::SetBlendModeForSelectedLayers { blend_mode } => { for layer in self.network_interface.selected_nodes().selected_layers_except_artboards(&self.network_interface) { @@ -1205,7 +1209,10 @@ impl MessageHandler> for DocumentMes } DocumentMessage::SetGraphFadeArtwork { 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 } => { responses.add(DocumentMessage::AddTransaction); @@ -1507,7 +1514,7 @@ impl MessageHandler> for DocumentMes responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(FrontendMessage::UpdateNodeGraphTransform { - transform: Transform { + transform: NodeGraphTransform { scale: transform.matrix2.x_axis.x, x: transform.translation.x, y: transform.translation.y, @@ -1971,7 +1978,7 @@ impl DocumentMessageHandler { responses.add(NodeGraphMessage::ForceRunDocumentGraph); // 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) } @@ -2002,8 +2009,8 @@ impl DocumentMessageHandler { responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::ForceRunDocumentGraph); - responses.add(NodeGraphMessage::UnloadWires); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::UnloadWiresOld); + responses.add(NodeGraphMessage::SendWiresOld); Some(previous_network) } diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs index 330d123efe..6ab38ac1f9 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs @@ -141,10 +141,12 @@ pub enum NodeGraphMessage { }, SendClickTargets, EndSendClickTargets, - UnloadWires, - SendWires, - UpdateVisibleNodes, + UnloadWiresOld, + SendWiresOld, + UpdateVisibleNodesOld, SendGraph, + SendGraphOld, + SendGraphNew, SetInputValue { node_id: NodeId, input_index: usize, @@ -232,7 +234,7 @@ pub enum NodeGraphMessage { }, UpdateActionButtons, UpdateGraphBarRight, - UpdateInSelectedNetwork, + UpdateInSelectedNetworkOld, UpdateHints, - SendSelectedNodes, + SendSelectedNodesOld, } diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index f1b2cc0c4b..bd9e7fce3b 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -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 crate::consts::GRID_SIZE; 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::graph_operation::utility_types::ModifyInputsContext; 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::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::network_interface::{ 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::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::tool::common_functionality::auto_panning::AutoPanning; 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 graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; 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::*; use kurbo::{DEFAULT_ACCURACY, Shape}; @@ -44,6 +45,7 @@ pub struct NodeGraphMessageContext<'a> { pub preferences: &'a PreferencesMessageHandler, pub layers_panel_open: bool, pub viewport: &'a ViewportMessageHandler, + pub render_native_node_graph: bool, } #[derive(Debug, Clone, ExtractField)] @@ -92,9 +94,9 @@ pub struct NodeGraphMessageHandler { /// The end index of the moved connector end_index: Option, /// 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, + frontend_nodes_old: Vec, /// 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. @@ -114,6 +116,7 @@ impl<'a> MessageHandler> for NodeG preferences, layers_panel_open, viewport, + render_native_node_graph, } = context; match message { @@ -193,7 +196,7 @@ impl<'a> MessageHandler> for NodeG responses.add(MenuBarMessage::SendLayout); responses.add(NodeGraphMessage::UpdateLayerPanel); responses.add(PropertiesPanelMessage::Refresh); - responses.add(NodeGraphMessage::SendSelectedNodes); + responses.add(NodeGraphMessage::SendSelectedNodesOld); responses.add(ArtboardToolMessage::UpdateSelectedArtboard); responses.add(DocumentMessage::DocumentStructureChanged); responses.add(OverlaysMessage::Draw); @@ -298,7 +301,7 @@ impl<'a> MessageHandler> for NodeG self.wire_in_progress_type = FrontendGraphDataType::General; 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 { context_menu_information: self.context_menu.clone(), }); @@ -471,7 +474,7 @@ impl<'a> MessageHandler> for NodeG } responses.add(NodeGraphMessage::UpdateImportsExports); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendWiresOld); } NodeGraphMessage::ExposePrimaryExport { exposed } => { let export_connector: InputConnector = InputConnector::Export(0); @@ -762,7 +765,7 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::SelectedNodesSet { nodes: self.selection_before_pointer_down.clone(), }); - responses.add(FrontendMessage::UpdateBox { box_selection: None }); + responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None }); return; } // Abort dragging a wire @@ -771,7 +774,7 @@ impl<'a> MessageHandler> for NodeG self.wire_in_progress_type = FrontendGraphDataType::General; self.wire_in_progress_to_connector = None; responses.add(DocumentMessage::AbortTransaction); - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); + responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None }); return; } @@ -856,7 +859,7 @@ impl<'a> MessageHandler> for NodeG responses.add(FrontendMessage::UpdateContextMenuInformation { 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 @@ -1063,14 +1066,14 @@ impl<'a> MessageHandler> for NodeG to_connector_is_layer, GraphWireStyle::Direct, ); - let path_string = vector_wire.to_svg(); - let wire_path = WirePath { - path_string, + let wire_path = WirePathInProgress { + wire: vector_wire.to_svg(), data_type: self.wire_in_progress_type, 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 { if drag_start.start_x != point.x || drag_start.start_y != point.y { @@ -1321,7 +1324,8 @@ impl<'a> MessageHandler> for NodeG 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 inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None); @@ -1330,7 +1334,7 @@ impl<'a> MessageHandler> for NodeG .segments() .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::>(); @@ -1397,8 +1401,8 @@ impl<'a> MessageHandler> for NodeG self.reordering_export = None; self.reordering_import = None; responses.add(DocumentMessage::EndTransaction); - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); - responses.add(FrontendMessage::UpdateBox { box_selection: None }); + responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None }); + responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None }); responses.add(FrontendMessage::UpdateImportReorderIndex { index: None }); responses.add(FrontendMessage::UpdateExportReorderIndex { index: None }); self.update_node_graph_hints(responses); @@ -1601,18 +1605,18 @@ impl<'a> MessageHandler> for NodeG click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)), }), 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) { - 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 => { - let wires = self.collect_wires(network_interface, preferences.graph_wire_style, breadcrumb_network_path); - responses.add(FrontendMessage::UpdateNodeGraphWires { wires }); + NodeGraphMessage::SendWiresOld => { + let wires = self.collect_wires_old(network_interface, preferences.graph_wire_style, breadcrumb_network_path); + responses.add(FrontendMessage::UpdateNodeGraphWiresOld { wires }); } - NodeGraphMessage::UpdateVisibleNodes => { + NodeGraphMessage::UpdateVisibleNodesOld => { let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { return; }; @@ -1621,7 +1625,7 @@ impl<'a> MessageHandler> 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 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 { log::error!("Could not get bbox for node: {node_id:?}"); continue; @@ -1637,32 +1641,60 @@ impl<'a> MessageHandler> for NodeG } } - responses.add(FrontendMessage::UpdateVisibleNodes { nodes }); + responses.add(FrontendMessage::UpdateVisibleNodesOld { nodes }); } NodeGraphMessage::SendGraph => { + if render_native_node_graph { + responses.add(NodeGraphMessage::SendGraphNew); + } else { + responses.add(NodeGraphMessage::SendGraphOld); + } + } + NodeGraphMessage::SendGraphOld => { responses.add(NodeGraphMessage::UpdateLayerPanel); responses.add(DocumentMessage::DocumentStructureChanged); responses.add(PropertiesPanelMessage::Refresh); if breadcrumb_network_path == selection_network_path && graph_view_overlay_open { - let nodes = self.collect_nodes(network_interface, breadcrumb_network_path); - self.frontend_nodes = nodes.iter().map(|node| node.id).collect(); - responses.add(FrontendMessage::UpdateNodeGraphNodes { nodes }); - responses.add(NodeGraphMessage::UpdateVisibleNodes); + let nodes = self.collect_nodes_old(network_interface, breadcrumb_network_path); + self.frontend_nodes_old = nodes.iter().map(|node| node.id).collect(); + responses.add(FrontendMessage::UpdateNodeGraphNodesOld { nodes }); + responses.add(NodeGraphMessage::UpdateVisibleNodesOld); let error = self.node_graph_error(network_interface, breadcrumb_network_path); 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(FrontendMessage::UpdateLayerWidths { + responses.add(FrontendMessage::UpdateLayerWidthsOld { layer_widths, chain_widths, 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); } } + 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 } => { let input = NodeInput::value(value, false); responses.add(NodeGraphMessage::SetInput { @@ -1729,7 +1761,7 @@ impl<'a> MessageHandler> for NodeG } } - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendWiresOld); } NodeGraphMessage::ToggleSelectedAsLayersOrNodes => { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else { @@ -1750,7 +1782,7 @@ impl<'a> MessageHandler> for NodeG NodeGraphMessage::ShiftNodePosition { node_id, x, y } => { 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 } => { if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) { @@ -1764,7 +1796,7 @@ impl<'a> MessageHandler> for NodeG }); responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::SendGraph); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendWiresOld); } NodeGraphMessage::SetDisplayName { node_id, @@ -1953,23 +1985,31 @@ impl<'a> MessageHandler> for NodeG nodes: nodes.into_iter().collect::>(), }); } - responses.add(FrontendMessage::UpdateBox { box_selection }) + responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection }) } } NodeGraphMessage::UpdateImportsExports => { - let imports = network_interface.frontend_imports(breadcrumb_network_path); - let exports = network_interface.frontend_exports(breadcrumb_network_path); + let imports = network_interface.frontend_imports(preferences.graph_wire_style, 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 { log::error!("Could not get import export positions"); 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; let add_import_export = !breadcrumb_network_path.is_empty(); - responses.add(NodeGraphMessage::UpdateVisibleNodes); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::UpdateVisibleNodesOld); responses.add(FrontendMessage::UpdateImportsExports { imports, exports, @@ -1977,6 +2017,7 @@ impl<'a> MessageHandler> for NodeG export_position, add_import_export, }); + responses.add(NodeGraphMessage::SendWiresOld) } NodeGraphMessage::UpdateLayerPanel => { @@ -2008,19 +2049,19 @@ impl<'a> MessageHandler> for NodeG self.update_graph_bar_right(graph_fade_artwork_percentage, network_interface, breadcrumb_network_path, navigation_handler); 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, }), NodeGraphMessage::UpdateHints => { 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 { log::error!("Could not get selected nodes in NodeGraphMessage::SendSelectedNodes"); return; }; responses.add(NodeGraphMessage::UpdateActionButtons); - responses.add(FrontendMessage::UpdateNodeGraphSelection { + responses.add(FrontendMessage::UpdateNodeGraphSelectionOld { selected: selected_nodes.selected_nodes().cloned().collect::>(), }); } @@ -2472,7 +2513,7 @@ impl NodeGraphMessageHandler { } } - fn collect_wires(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec { + fn collect_wires_old(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec { let mut added_wires = network_interface .node_graph_input_connectors(breadcrumb_network_path) .iter() @@ -2480,24 +2521,24 @@ impl NodeGraphMessageHandler { .collect::>(); let changed_wire_inputs = added_wires.iter().map(|update| (update.id, update.input_index)).collect::>(); - self.frontend_wires.extend(changed_wire_inputs); + self.frontend_wires_old.extend(changed_wire_inputs); - let mut orphaned_wire_inputs = self.frontend_wires.clone(); - self.frontend_wires = network_interface + let mut orphaned_wire_inputs = self.frontend_wires_old.clone(); + self.frontend_wires_old = network_interface .node_graph_wire_inputs(breadcrumb_network_path) .iter() .filter_map(|visible_wire_input| orphaned_wire_inputs.take(visible_wire_input)) .collect::>(); - 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, input_index, 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); } else { - added_wires.push(WirePathUpdate { + added_wires.push(WirePathUpdateOld { id: NodeId(u64::MAX), input_index: u32::MAX as usize, wire_path_update: None, @@ -2507,7 +2548,7 @@ impl NodeGraphMessageHandler { added_wires } - fn collect_nodes(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec { + fn collect_nodes_old(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec { let Some(network) = network_interface.nested_network(breadcrumb_network_path) else { log::error!("Could not get nested network when collecting nodes"); return Vec::new(); @@ -2520,18 +2561,18 @@ impl NodeGraphMessageHandler { .input_from_connector(&primary_input_connector, breadcrumb_network_path) .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 { None }; 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(); - 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)) - .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(); 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); - nodes.push(FrontendNode { + nodes.push(FrontendNodeOld { id: node_id, is_layer: network_interface .node_metadata(&node_id, breadcrumb_network_path) @@ -2774,8 +2815,8 @@ impl Default for NodeGraphMessageHandler { reordering_export: None, reordering_import: None, end_index: None, - frontend_nodes: Vec::new(), - frontend_wires: HashSet::new(), + frontend_nodes_old: Vec::new(), + frontend_wires_old: HashSet::new(), } } } diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index 7c3706a4b7..7365bb88c5 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -1,7 +1,6 @@ #![allow(clippy::too_many_arguments)] use super::document_node_definitions::{NODE_OVERRIDES, NodePropertiesContext}; -use super::utility_types::FrontendGraphDataType; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::utility_types::network_interface::InputConnector; use crate::messages::prelude::*; @@ -14,6 +13,7 @@ use graph_craft::{Type, concrete}; use graphene_std::NodeInputDecleration; use graphene_std::animation::RealTimeMode; use graphene_std::extract_xy::XY; +use graphene_std::node_graph_overlay::types::FrontendGraphDataType; use graphene_std::path_bool::BooleanOperation; use graphene_std::raster::curve::Curve; use graphene_std::raster::{ diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs index edc6a9b754..5458247248 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -1,50 +1,10 @@ -use glam::{DVec2, IVec2}; +use glam::IVec2; use graph_craft::document::NodeId; -use graph_craft::document::value::TaggedValue; -use graphene_std::Type; +use graphene_std::node_graph_overlay::types::{FrontendGraphDataType, FrontendGraphInputNew, FrontendGraphOutputNew, FrontendXY}; use std::borrow::Cow; -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum FrontendGraphDataType { - #[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 { +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendGraphInputOld { #[serde(rename = "dataType")] pub data_type: FrontendGraphDataType, pub name: String, @@ -58,8 +18,8 @@ pub struct FrontendGraphInput { pub connected_to: String, } -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendGraphOutput { +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendGraphOutputOld { #[serde(rename = "dataType")] pub data_type: FrontendGraphDataType, pub name: String, @@ -72,8 +32,8 @@ pub struct FrontendGraphOutput { pub connected_to: Vec, } -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendNode { +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendNodeOld { pub id: graph_craft::document::NodeId, #[serde(rename = "isLayer")] pub is_layer: bool, @@ -83,13 +43,13 @@ pub struct FrontendNode { #[serde(rename = "displayName")] pub display_name: String, #[serde(rename = "primaryInput")] - pub primary_input: Option, + pub primary_input: Option, #[serde(rename = "exposedInputs")] - pub exposed_inputs: Vec, + pub exposed_inputs: Vec, #[serde(rename = "primaryOutput")] - pub primary_output: Option, + pub primary_output: Option, #[serde(rename = "exposedOutputs")] - pub exposed_outputs: Vec, + pub exposed_outputs: Vec, #[serde(rename = "primaryOutputConnectedToLayer")] pub primary_output_connected_to_layer: bool, #[serde(rename = "primaryInputConnectedToLayer")] @@ -100,6 +60,26 @@ pub struct FrontendNode { pub previewed: bool, } +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendExport { + pub port: FrontendGraphInputNew, + pub wire: Option, +} + +#[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>, + #[serde(rename = "previewWire")] + pub preview_wire: Option, +} + +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendImport { + pub port: FrontendGraphOutputNew, + pub wires: Vec, +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] pub struct FrontendNodeType { pub name: Cow<'static, str>, @@ -207,22 +187,3 @@ pub struct NodeGraphError { pub position: FrontendXY, 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 for FrontendXY { - fn from(v: DVec2) -> Self { - FrontendXY { x: v.x as i32, y: v.y as i32 } - } -} - -impl From for FrontendXY { - fn from(v: IVec2) -> Self { - FrontendXY { x: v.x as i32, y: v.y as i32 } - } -} diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index b3420e34b8..d2dd75b31f 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -1,16 +1,18 @@ mod deserialization; mod memo_network; +pub mod node_graph; +pub mod old_node_graph_render; mod resolved_types; use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier, NodeRelations}; use super::misc::PTZ; use super::nodes::SelectedNodes; -use crate::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}; +use crate::consts::GRID_SIZE; 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::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::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::tool_messages::tool_prelude::NumberInputMode; use deserialization::deserialize_node_persistent_metadata; @@ -24,7 +26,6 @@ use graphene_std::subpath::Subpath; use graphene_std::transform::Footprint; use graphene_std::vector::click_target::{ClickTarget, ClickTargetType}; use graphene_std::vector::{PointId, Vector, VectorModificationType}; -use kurbo::BezPath; use memo_network::MemoNetwork; use serde_json::{Value, json}; use std::collections::{HashMap, HashSet, VecDeque}; @@ -236,31 +237,6 @@ impl NodeNetworkInterface { 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::>() - { - // 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 pub fn connected_to_output(&self, target_node_id: &NodeId, network_path: &[NodeId]) -> bool { 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> { - 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::>(); - 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> { - 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::>(); - 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 { - // 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 { - 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::>(); - - 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 { let mut node_height: Option = self .node_click_targets(node_id, network_path) @@ -950,7 +687,7 @@ impl NodeNetworkInterface { 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 .node_metadata(node_id, network_path) .and_then(|node_metadata| node_metadata.persistent_metadata.input_metadata.get(index))?; @@ -1050,25 +787,6 @@ impl NodeNetworkInterface { 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::>(); - 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 { let Some((node_id, network_path)) = network_path.split_last() else { // The document network does not have a hidden primary export @@ -1834,30 +1552,7 @@ impl NodeNetworkInterface { return; }; network_metadata.transient_metadata.import_export_ports.unload(); - - // 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); - } + self.unload_import_export_wires_old(network_path); } 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 }) } - pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { - 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 { let mut input_connectors = Vec::new(); let Some(network) = self.nested_network(network_path) else { @@ -2331,144 +1933,6 @@ impl NodeNetworkInterface { .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 { - 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 { - 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> { self.try_load_node_click_targets(node_id, network_path); self.try_get_node_click_targets(node_id, network_path) @@ -2706,7 +2170,7 @@ impl NodeNetworkInterface { return; }; 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, 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) } - pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> (HashMap, HashMap, HashMap) { - 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::>(); - let layer_widths = nodes - .iter() - .filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width))) - .collect::>(); - let chain_widths = nodes.iter().map(|node_id| (*node_id, self.chain_width(node_id, network_path))).collect::>(); - 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::>(); - - (layer_widths, chain_widths, has_left_input_wire) - } - pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option { 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_modify_import_export(network_path); } - 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 { log::error!("Could not get node in vector_modification"); @@ -4013,17 +3444,14 @@ impl NodeNetworkInterface { // If a connection is made to the imports (NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }, NodeInput::Import { .. }) => { self.unload_outward_wires(network_path); - self.unload_wire(input_connector, network_path); } // If a connection to the imports is disconnected (NodeInput::Import { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => { self.unload_outward_wires(network_path); - self.unload_wire(input_connector, network_path); } // If a node is disconnected. (NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => { self.unload_outward_wires(network_path); - self.unload_wire(input_connector, network_path); 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); @@ -6069,7 +5497,7 @@ pub struct NodeNetworkTransientMetadata { pub modify_import_export: TransientMetadata, // Wires from the exports - pub wires: Vec>, + pub wires: Vec>, } #[derive(Debug, Clone)] @@ -6250,7 +5678,7 @@ impl InputPersistentMetadata { #[derive(Debug, Clone, Default)] struct InputTransientMetadata { - wire: TransientMetadata, + wire: TransientMetadata, // downstream_protonode: populated for all inputs after each compile // types: populated for each protonode after each } diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs new file mode 100644 index 0000000000..f672b1ffda --- /dev/null +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs @@ -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 { + // 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 { + 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::>() + { + // 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 { + 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::>(); + 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> { + 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::>(); + Some(FrontendImport { port, wires }) + }) + }) + .collect::>(); + + 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::>(); + + 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 { + 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 { + 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) + } +} diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/old_node_graph_render.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/old_node_graph_render.rs new file mode 100644 index 0000000000..c3449fbf61 --- /dev/null +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/old_node_graph_render.rs @@ -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 { + // 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 { + 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::>(); + + 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 { + 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 { + 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 { + 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, HashMap, HashMap) { + 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::>(); + let layer_widths = nodes + .iter() + .filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width))) + .collect::>(); + let chain_widths = nodes.iter().map(|node_id| (*node_id, self.chain_width(node_id, network_path))).collect::>(); + 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::>(); + + (layer_widths, chain_widths, has_left_input_wire) + } +} diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs index c54c767896..225eaad8b9 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs @@ -4,11 +4,11 @@ use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNodeImplementation, InlineRust, NodeInput}; use graph_craft::proto::GraphErrors; use graph_craft::{Type, concrete}; +use graphene_std::node_graph_overlay::types::FrontendGraphDataType; use graphene_std::uuid::NodeId; use interpreted_executor::dynamic_executor::{NodeTypes, ResolvedDocumentNodeTypesDelta}; 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}; // This file contains utility methods for interfacing with the resolved types returned from the compiler diff --git a/editor/src/messages/portfolio/document/utility_types/wires.rs b/editor/src/messages/portfolio/document/utility_types/wires.rs index ad6ab32843..37f135f03b 100644 --- a/editor/src/messages/portfolio/document/utility_types/wires.rs +++ b/editor/src/messages/portfolio/document/utility_types/wires.rs @@ -1,10 +1,10 @@ -use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType; use glam::{DVec2, IVec2}; +use graphene_std::node_graph_overlay::types::FrontendGraphDataType; use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point}; use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape}; #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct WirePath { +pub struct WirePathOld { #[serde(rename = "pathString")] pub path_string: String, #[serde(rename = "dataType")] @@ -14,13 +14,21 @@ pub struct WirePath { } #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct WirePathUpdate { +pub struct WirePathUpdateOld { pub id: NodeId, #[serde(rename = "inputIndex")] pub input_index: usize, // If none, then remove the wire from the map #[serde(rename = "wirePathUpdate")] - pub wire_path_update: Option, + pub wire_path_update: Option, +} + +#[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)] diff --git a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs index 9b86761d87..aa13ea5c44 100644 --- a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs +++ b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs @@ -18,6 +18,7 @@ pub struct MenuBarMessageHandler { pub has_selection_history: (bool, bool), pub message_logging_verbosity: MessageLoggingVerbosity, pub reset_node_definitions_on_open: bool, + pub render_native_node_graph: bool, pub make_path_editable_is_allowed: bool, pub data_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_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents; 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 menu_bar_entries = vec![ @@ -696,6 +698,12 @@ impl LayoutHolder for MenuBarMessageHandler { action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen.into()), ..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![ MenuBarEntry { label: "Print Trace Logs".into(), diff --git a/editor/src/messages/portfolio/portfolio_message.rs b/editor/src/messages/portfolio/portfolio_message.rs index 58209fe649..aa0b062c87 100644 --- a/editor/src/messages/portfolio/portfolio_message.rs +++ b/editor/src/messages/portfolio/portfolio_message.rs @@ -81,6 +81,7 @@ pub enum PortfolioMessage { to_front: bool, select_after_open: bool, }, + ToggleRenderNativeNodeGraph, ToggleResetNodesToDefinitionsOnOpen, PasteIntoFolder { clipboard: Clipboard, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index f034d85a65..72900c5826 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -63,6 +63,7 @@ pub struct PortfolioMessageHandler { pub layers_panel_open: bool, #[derivative(Default(value = "true"))] pub properties_panel_open: bool, + pub render_native_node_graph: bool, } #[message_handler_data] @@ -111,6 +112,7 @@ impl MessageHandler> for Portfolio (!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.render_native_node_graph = self.render_native_node_graph; } self.menu_bar_message_handler.process_message(message, responses, ()); @@ -129,6 +131,7 @@ impl MessageHandler> for Portfolio data_panel_open: self.data_panel_open, layers_panel_open: self.layers_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) } @@ -170,6 +173,7 @@ impl MessageHandler> for Portfolio data_panel_open: self.data_panel_open, layers_panel_open: self.layers_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) } @@ -442,6 +446,13 @@ impl MessageHandler> for Portfolio 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 => { self.reset_node_definitions_on_open = !self.reset_node_definitions_on_open; responses.add(MenuBarMessage::SendLayout); @@ -914,8 +925,8 @@ impl MessageHandler> for Portfolio responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open }); if node_graph_open { responses.add(NodeGraphMessage::UpdateGraphBarRight); - responses.add(NodeGraphMessage::UnloadWires); - responses.add(NodeGraphMessage::SendWires) + responses.add(NodeGraphMessage::UnloadWiresOld); + responses.add(NodeGraphMessage::SendWiresOld) } else { responses.add(PortfolioMessage::UpdateDocumentWidgets); } diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index 8c61fa78af..0c0c0164fe 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -87,8 +87,7 @@ impl MessageHandler for PreferencesMessageHandler { } PreferencesMessage::GraphWireStyle { style } => { self.graph_wire_style = style; - responses.add(NodeGraphMessage::UnloadWires); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendGraph); } PreferencesMessage::ViewportZoomWheelRate { rate } => { self.viewport_zoom_wheel_rate = rate; diff --git a/editor/src/test_utils.rs b/editor/src/test_utils.rs index 3b8a568d04..d1fca3a05c 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -326,11 +326,11 @@ pub trait FrontendMessageTestUtils { impl FrontendMessageTestUtils for FrontendMessage { 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 { - if let Some(error) = &node.errors { - panic!("error on {}: {}", node.display_name, error); + for node in nodes_to_render { + if let Some(error) = &node.metadata.errors { + panic!("error on {}: {}", node.metadata.display_name, error); } } } diff --git a/frontend/src/components/panels/Document.svelte b/frontend/src/components/panels/Document.svelte index 3f2a96ff65..454fe5ae32 100644 --- a/frontend/src/components/panels/Document.svelte +++ b/frontend/src/components/panels/Document.svelte @@ -569,7 +569,7 @@ {/if} -
+
@@ -768,7 +768,7 @@ .viewport-container-inner-2 { flex: 1 1 100%; position: relative; - + overflow: hidden; .viewport { background: var(--color-2-mildblack); } @@ -852,7 +852,6 @@ } } - .fade-artwork, .graph { position: absolute; top: 0; diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index fe0769872c..2bc0be74b3 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -4,7 +4,7 @@ import { fade } from "svelte/transition"; import type { Editor } from "@graphite/editor"; - import type { FrontendGraphInput, FrontendGraphOutput } from "@graphite/messages"; + import { type FrontendGraphInputOld, type FrontendGraphOutputOld, type FrontendGraphInputNew, type FrontendGraphOutputNew, UpdateRenderNativeNodeGraph } from "@graphite/messages"; import type { NodeGraphState } from "@graphite/state-providers/node-graph"; import type { IconName } from "@graphite/utility-functions/icons"; @@ -183,7 +183,7 @@ // return `Connected to:\n${input.connectedTo}`; // } - function zipWithUndefined(arr1: FrontendGraphInput[], arr2: FrontendGraphOutput[]) { + function zipWithUndefined(arr1: FrontendGraphInputOld[], arr2: FrontendGraphOutputOld[]) { const maxLength = Math.max(arr1.length, arr2.length); const result = []; for (let i = 0; i < maxLength; i++) { @@ -193,310 +193,31 @@ } -
- - {#if $nodeGraph.contextMenuInformation} - - {#if $nodeGraph.contextMenuInformation.contextMenuData.type == "CreateNode"} - createNode(e.detail)} /> - {:else if $nodeGraph.contextMenuInformation.contextMenuData.type == "ModifyNode"} - - Display as - editor.handle.setToNodeOrLayer($nodeGraph.contextMenuInformation.contextMenuData.data.nodeId, false), - }, - { - value: "layer", - label: "Layer", - action: () => editor.handle.setToNodeOrLayer($nodeGraph.contextMenuInformation.contextMenuData.data.nodeId, true), - }, - ]} - disabled={!$nodeGraph.contextMenuInformation.contextMenuData.data.canBeLayer} - /> - - - - editor.handle.mergeSelectedNodes()} /> - - {/if} - - {/if} - - {#if $nodeGraph.error} -
- {$nodeGraph.error.error} - {$nodeGraph.error.error} -
- {/if} - - - {#if $nodeGraph.clickTargets} -
- - {#each $nodeGraph.clickTargets.nodeClickTargets as pathString} - - {/each} - {#each $nodeGraph.clickTargets.layerClickTargets as pathString} - - {/each} - {#each $nodeGraph.clickTargets.connectorClickTargets as pathString} - - {/each} - {#each $nodeGraph.clickTargets.iconClickTargets as pathString} - - {/each} - - {#each $nodeGraph.clickTargets.modifyImportExport as pathString} - - {/each} - -
- {/if} - - -
- - {#each $nodeGraph.wires.values() as map} - {#each map.values() as { pathString, dataType, thick, dashed }} - {#if thick} - - {/if} - {/each} - {/each} - -
- - -
- {#if $nodeGraph.updateImportsExports} - {#each $nodeGraph.updateImportsExports.imports as frontendOutput, index} - {#if frontendOutput} - - {#if frontendOutput.connectedTo.length > 0} - - {:else} - - {/if} - - -
(hoveringImportIndex = index)} - on:pointerleave={() => (hoveringImportIndex = undefined)} - class="edit-import-export import" - class:separator-bottom={index === 0 && $nodeGraph.updateImportsExports.addImportExport} - class:separator-top={index === 1 && $nodeGraph.updateImportsExports.addImportExport} - style:--offset-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24} - style:--offset-top={($nodeGraph.updateImportsExports.importPosition.y - 8) / 24 + index} - > - {#if editingNameImportIndex == index} - e.key === "Enter" && setEditingImportName(e)} - /> - {:else} -

setEditingImportNameIndex(index, frontendOutput.name)}> - {frontendOutput.name} -

- {/if} - {#if (hoveringImportIndex === index || editingNameImportIndex === index) && $nodeGraph.updateImportsExports.addImportExport} - { - /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ - }} - /> - {#if index > 0} -
- {/if} - {/if} -
- {:else} -
- editor.handle.addPrimaryImport()} /> -
- {/if} - {/each} - - {#each $nodeGraph.updateImportsExports.exports as frontendInput, index} - {#if frontendInput} - - {#if frontendInput.connectedTo !== "nothing"} - - {:else} - - {/if} - -
(hoveringExportIndex = index)} - on:pointerleave={() => (hoveringExportIndex = undefined)} - class="edit-import-export export" - class:separator-bottom={index === 0 && $nodeGraph.updateImportsExports.addImportExport} - class:separator-top={index === 1 && $nodeGraph.updateImportsExports.addImportExport} - style:--offset-left={($nodeGraph.updateImportsExports.exportPosition.x - 8) / 24} - style:--offset-top={($nodeGraph.updateImportsExports.exportPosition.y - 8) / 24 + index} - > - {#if (hoveringExportIndex === index || editingNameExportIndex === index) && $nodeGraph.updateImportsExports.addImportExport} - {#if index > 0} -
- {/if} - { - /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ - }} - /> - {/if} - {#if editingNameExportIndex === index} - e.key === "Enter" && setEditingExportName(e)} - /> - {:else} -

setEditingExportNameIndex(index, frontendInput.name)}> - {frontendInput.name} -

- {/if} -
- {:else} -
- editor.handle.addPrimaryExport()} /> -
- {/if} - {/each} - - {#if $nodeGraph.updateImportsExports.addImportExport == true} -
- editor.handle.addSecondaryImport()} /> -
-
- editor.handle.addSecondaryExport()} /> -
- {/if} - - {#if $nodeGraph.reorderImportIndex !== undefined} - {@const position = { - x: Number($nodeGraph.updateImportsExports.importPosition.x), - y: Number($nodeGraph.updateImportsExports.importPosition.y) + Number($nodeGraph.reorderImportIndex) * 24, - }} -
- {/if} - - {#if $nodeGraph.reorderExportIndex !== undefined} - {@const position = { - x: Number($nodeGraph.updateImportsExports.exportPosition.x), - y: Number($nodeGraph.updateImportsExports.exportPosition.y) + Number($nodeGraph.reorderExportIndex) * 24, - }} -
- {/if} - {/if} -
+{#if !$nodeGraph.renderNativeNodeGraphOld} +
- {#each Array.from($nodeGraph.nodes) - .filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) + {#each Array.from($nodeGraph.nodesOld) + .filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodesOld.has(nodeId)) .map(([_, node], nodeIndex) => ({ node, nodeIndex })) as { node, nodeIndex } (nodeIndex)} {@const clipPathId = String(Math.random()).substring(2)} {@const stackDataInput = node.exposedInputs[0]} - {@const layerAreaWidth = $nodeGraph.layerWidths.get(node.id) || 8} - {@const layerChainWidth = $nodeGraph.chainWidths.get(node.id) || 0} - {@const hasLeftInputWire = $nodeGraph.hasLeftInputWire.get(node.id) || false} + {@const layerAreaWidth = $nodeGraph.layerWidthsOld.get(node.id) || 8} + {@const layerChainWidth = $nodeGraph.chainWidthsOld.get(node.id) || 0} + {@const hasLeftInputWire = $nodeGraph.hasLeftInputWireOld.get(node.id) || false}
{/each} - +
- {#each $nodeGraph.wires.values() as map} + {#each $nodeGraph.wiresOld.values() as map} {#each map.values() as { pathString, dataType, thick, dashed }} - {#if !thick} - - {/if} + {/each} {/each} - {#if $nodeGraph.wirePathInProgress} - - {/if}
- - {#each Array.from($nodeGraph.nodes) - .filter(([nodeId, node]) => !node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) + {#each Array.from($nodeGraph.nodesOld) + .filter(([nodeId, node]) => !node.isLayer && $nodeGraph.visibleNodesOld.has(nodeId)) .map(([_, node], nodeIndex) => ({ node, nodeIndex })) as { node, nodeIndex } (nodeIndex)} {@const exposedInputsOutputs = zipWithUndefined(node.exposedInputs, node.exposedOutputs)} {@const clipPathId = String(Math.random()).substring(2)}
-
+
{node.displayName}
{#if exposedInputsOutputs.length > 0} -
+
{#each exposedInputsOutputs as [input, output]}
@@ -753,26 +462,330 @@
{/each}
+{/if} + +
+ + {#if $nodeGraph.contextMenuInformation} + + {#if $nodeGraph.contextMenuInformation.contextMenuData.type == "CreateNode"} + createNode(e.detail)} /> + {:else if $nodeGraph.contextMenuInformation.contextMenuData.type == "ModifyNode"} + + Display as + editor.handle.setToNodeOrLayer($nodeGraph.contextMenuInformation.contextMenuData.data.nodeId, false), + }, + { + value: "layer", + label: "Layer", + action: () => editor.handle.setToNodeOrLayer($nodeGraph.contextMenuInformation.contextMenuData.data.nodeId, true), + }, + ]} + disabled={!$nodeGraph.contextMenuInformation.contextMenuData.data.canBeLayer} + /> + + + + editor.handle.mergeSelectedNodes()} /> + + {/if} + + {/if} + + {#if $nodeGraph.error} +
+ {$nodeGraph.error.error} + {$nodeGraph.error.error} +
+ {/if} + + + {#if $nodeGraph.clickTargets} +
+ + {#each $nodeGraph.clickTargets.nodeClickTargets as pathString} + + {/each} + {#each $nodeGraph.clickTargets.layerClickTargets as pathString} + + {/each} + {#each $nodeGraph.clickTargets.connectorClickTargets as pathString} + + {/each} + {#each $nodeGraph.clickTargets.iconClickTargets as pathString} + + {/each} + + {#each $nodeGraph.clickTargets.modifyImportExport as pathString} + + {/each} + +
+ {/if} + + + + {#if $nodeGraph.wirePathInProgress} + + {/if} + + + +
+ {#if $nodeGraph.importsExports} + {#each $nodeGraph.importsExports.imports as frontendImport, index} + {#if frontendImport} + {@const frontendOutput = frontendImport.port} + + {#each frontendImport.wires as wire} + + + + {/each} + + {#if frontendOutput.connected} + + {:else} + + {/if} + + +
(hoveringImportIndex = index)} + on:pointerleave={() => (hoveringImportIndex = undefined)} + class="edit-import-export import" + class:separator-bottom={index === 0 && $nodeGraph.importsExports.addImportExport} + class:separator-top={index === 1 && $nodeGraph.importsExports.addImportExport} + style:--offset-left={($nodeGraph.importsExports.importPosition.x - 8) / 24} + style:--offset-top={($nodeGraph.importsExports.importPosition.y - 8) / 24 + index} + > + {#if editingNameImportIndex == index} + e.key === "Enter" && setEditingImportName(e)} + /> + {:else} +

setEditingImportNameIndex(index, frontendOutput.name)}> + {frontendOutput.name} +

+ {/if} + {#if (hoveringImportIndex === index || editingNameImportIndex === index) && $nodeGraph.importsExports.addImportExport} + { + /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ + }} + /> + {#if index > 0} +
+ {/if} + {/if} +
+ {:else} +
+ editor.handle.addPrimaryImport()} /> +
+ {/if} + {/each} + + {#each $nodeGraph.importsExports.exports.exports as frontendExport, index} + {#if frontendExport} + {@const frontendInput = frontendExport.port} + {#if frontendExport.wire} + + + + {/if} + + {#if frontendInput.connectedToNode !== undefined} + + {:else} + + {/if} + +
(hoveringExportIndex = index)} + on:pointerleave={() => (hoveringExportIndex = undefined)} + class="edit-import-export export" + class:separator-bottom={index === 0 && $nodeGraph.importsExports.addImportExport} + class:separator-top={index === 1 && $nodeGraph.importsExports.addImportExport} + style:--offset-left={($nodeGraph.importsExports.exportPosition.x - 8) / 24} + style:--offset-top={($nodeGraph.importsExports.exportPosition.y - 8) / 24 + index} + > + {#if (hoveringExportIndex === index || editingNameExportIndex === index) && $nodeGraph.importsExports.addImportExport} + {#if index > 0} +
+ {/if} + { + /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ + }} + /> + {/if} + {#if editingNameExportIndex === index} + e.key === "Enter" && setEditingExportName(e)} + /> + {:else} +

setEditingExportNameIndex(index, frontendInput.name)}> + {frontendInput.name} +

+ {/if} +
+ {:else} +
+ editor.handle.addPrimaryExport()} /> +
+ {/if} + {/each} + + {#if $nodeGraph.importsExports.exports.previewWire && $nodeGraph.importsExports.exports.exports[0] !== undefined} + + + + {/if} + + {#if $nodeGraph.importsExports.addImportExport == true} +
+ editor.handle.addSecondaryImport()} /> +
+
+ editor.handle.addSecondaryExport()} /> +
+ {/if} + + {#if $nodeGraph.reorderImportIndex !== undefined} + {@const position = { + x: Number($nodeGraph.importsExports.importPosition.x), + y: Number($nodeGraph.importsExports.importPosition.y) + Number($nodeGraph.reorderImportIndex) * 24, + }} +
+ {/if} + + {#if $nodeGraph.reorderExportIndex !== undefined} + {@const position = { + x: Number($nodeGraph.importsExports.exportPosition.x), + y: Number($nodeGraph.importsExports.exportPosition.y) + Number($nodeGraph.reorderExportIndex) * 24, + }} +
+ {/if} + {/if} +
-{#if $nodeGraph.box} +{#if $nodeGraph.selectionBox}
{/if}