diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index af91f3ebe4..4661baf394 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -2,10 +2,10 @@ use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon}; 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, FrontendNodeToRender, FrontendNodeType, FrontendXY, Transform, + BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendExports, FrontendImport, FrontendNodeToRender, FrontendNodeType, FrontendXY, Transform, }; 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; use crate::messages::prelude::*; use crate::messages::tool::utility_types::HintData; use graph_craft::document::NodeId; @@ -125,9 +125,8 @@ 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: FrontendXY, @@ -138,7 +137,7 @@ pub enum FrontendMessage { #[serde(rename = "addImportExport")] add_import_export: bool, }, - UpdateBox { + UpdateNodeGraphSelectionBox { #[serde(rename = "box")] box_selection: Option, }, @@ -242,9 +241,6 @@ pub enum FrontendMessage { #[serde(rename = "setColorChoice")] set_color_choice: Option, }, - UpdateGraphFadeArtwork { - percentage: f64, - }, UpdateInputHints { #[serde(rename = "hintData")] hint_data: HintData, @@ -272,22 +268,22 @@ pub enum FrontendMessage { UpdateMouseCursor { cursor: MouseCursorIcon, }, - UpdateNodeGraphNodes { + UpdateNodeGraphRender { #[serde(rename = "nodesToRender")] nodes_to_render: Vec, + open: bool, + opacity: f64, #[serde(rename = "inSelectedNetwork")] in_selected_network: bool, // Displays a dashed border around the node #[serde(rename = "previewedNode")] previewed_node: Option, + #[serde(rename = "nativeNodeGraphRender")] + native_node_graph_render: bool, }, UpdateVisibleNodes { nodes: Vec, }, - UpdateNodeGraphWires { - wires: Vec, - }, - ClearAllNodeGraphWires, UpdateNodeGraphControlBarLayout { #[serde(rename = "layoutTarget")] layout_target: LayoutTarget, @@ -320,8 +316,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/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 6a9c395ab8..32a73a4496 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -474,7 +474,6 @@ 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::SendGraph); responses.add(DocumentMessage::ZoomCanvasToFitAll); responses.add(NodeGraphMessage::SetGridAlignedEdges); @@ -494,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 { responses.add(DocumentMessage::GraphViewOverlay { open: false }); } @@ -504,7 +503,6 @@ 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::SendGraph); responses.add(DocumentMessage::PTZUpdate); responses.add(NodeGraphMessage::SetGridAlignedEdges); @@ -557,34 +555,28 @@ impl MessageHandler> for DocumentMes } } DocumentMessage::GraphViewOverlay { open } => { - let opened = !self.graph_view_overlay_open && open; self.graph_view_overlay_open = open; responses.add(FrontendMessage::UpdateGraphViewOverlay { open }); - responses.add(FrontendMessage::UpdateGraphFadeArtwork { - percentage: self.graph_fade_artwork_percentage, - }); // Update the tilt menu bar buttons to be disabled when the graph is open responses.add(MenuBarMessage::SendLayout); responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderScrollbars); - if opened { - responses.add(NodeGraphMessage::UnloadWires); - } if open { responses.add(ToolMessage::DeactivateTools); responses.add(OverlaysMessage::Draw); // Clear the overlays responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. }); responses.add(NodeGraphMessage::SetGridAlignedEdges); responses.add(NodeGraphMessage::UpdateGraphBarRight); - responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::UpdateHints); } 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 }); @@ -1199,7 +1191,7 @@ impl MessageHandler> for DocumentMes } DocumentMessage::SetGraphFadeArtwork { percentage } => { self.graph_fade_artwork_percentage = percentage; - responses.add(FrontendMessage::UpdateGraphFadeArtwork { percentage }); + responses.add(NodeGraphMessage::SendGraph); } DocumentMessage::SetNodePinned { node_id, pinned } => { responses.add(DocumentMessage::AddTransaction); @@ -1938,7 +1930,6 @@ 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::SetGridAlignedEdges); Some(previous_network) @@ -1970,8 +1961,6 @@ impl DocumentMessageHandler { responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::ForceRunDocumentGraph); - responses.add(NodeGraphMessage::UnloadWires); - responses.add(NodeGraphMessage::SendWires); 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 37ac29df44..15ef3ab246 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 @@ -139,8 +139,6 @@ pub enum NodeGraphMessage { }, SendClickTargets, EndSendClickTargets, - UnloadWires, - SendWires, UpdateVisibleNodes, SendGraph, SetGridAlignedEdges, @@ -185,6 +183,7 @@ pub enum NodeGraphMessage { TogglePreviewImpl { node_id: NodeId, }, + ToggleNativeNodeGraphRender, SetImportExportName { name: String, index: ImportOrExport, 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 1aa6cdf6d6..f337f5f1e8 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 @@ -13,7 +13,7 @@ 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, 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; @@ -93,8 +93,8 @@ pub struct NodeGraphMessageHandler { 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, - /// 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)>, + /// Disables rendering nodes in Svelte + native_node_graph_render: bool, } /// 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. @@ -297,7 +297,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(), }); @@ -470,7 +470,6 @@ impl<'a> MessageHandler> for NodeG } responses.add(NodeGraphMessage::UpdateImportsExports); - responses.add(NodeGraphMessage::SendWires); } NodeGraphMessage::ExposePrimaryExport { exposed } => { let export_connector: InputConnector = InputConnector::Export(0); @@ -761,7 +760,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 @@ -770,7 +769,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; } @@ -851,7 +850,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 @@ -1058,14 +1057,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 { @@ -1315,7 +1314,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(&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); @@ -1324,7 +1324,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::>(); @@ -1391,8 +1391,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); @@ -1591,17 +1591,6 @@ 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 => { - for input in network_interface.node_graph_input_connectors(breadcrumb_network_path) { - network_interface.unload_wire(&input, breadcrumb_network_path); - } - - responses.add(FrontendMessage::ClearAllNodeGraphWires); - } - NodeGraphMessage::SendWires => { - let wires = self.collect_wires(network_interface, preferences.graph_wire_style, breadcrumb_network_path); - responses.add(FrontendMessage::UpdateNodeGraphWires { wires }); - } NodeGraphMessage::UpdateVisibleNodes => { let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { return; @@ -1634,24 +1623,24 @@ impl<'a> MessageHandler> for NodeG responses.add(DocumentMessage::DocumentStructureChanged); responses.add(PropertiesPanelMessage::Refresh); responses.add(NodeGraphMessage::UpdateActionButtons); - if graph_view_overlay_open { - let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, breadcrumb_network_path); - self.frontend_nodes = nodes_to_render.iter().map(|node| node.metadata.node_id).collect(); - let previewed_node = network_interface.previewed_node(breadcrumb_network_path); - responses.add(FrontendMessage::UpdateNodeGraphNodes { - nodes_to_render, - in_selected_network: selection_network_path == breadcrumb_network_path, - previewed_node, - }); - responses.add(NodeGraphMessage::UpdateVisibleNodes); + let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, preferences.graph_wire_style, breadcrumb_network_path); + self.frontend_nodes = nodes_to_render.iter().map(|node| node.metadata.node_id).collect(); + let previewed_node = network_interface.previewed_node(breadcrumb_network_path); + responses.add(FrontendMessage::UpdateNodeGraphRender { + nodes_to_render, + open: graph_view_overlay_open, + opacity: graph_fade_artwork_percentage, + in_selected_network: selection_network_path == breadcrumb_network_path, + previewed_node, + native_node_graph_render: self.native_node_graph_render, + }); + responses.add(NodeGraphMessage::UpdateVisibleNodes); - let layer_widths = network_interface.collect_layer_widths(breadcrumb_network_path); + let layer_widths = network_interface.collect_layer_widths(breadcrumb_network_path); - responses.add(NodeGraphMessage::UpdateImportsExports); - responses.add(FrontendMessage::UpdateLayerWidths { layer_widths }); - responses.add(NodeGraphMessage::SendWires); - self.update_node_graph_hints(responses); - } + responses.add(NodeGraphMessage::UpdateImportsExports); + responses.add(FrontendMessage::UpdateLayerWidths { layer_widths }); + self.update_node_graph_hints(responses); } NodeGraphMessage::SetGridAlignedEdges => { if graph_view_overlay_open { @@ -1725,8 +1714,6 @@ impl<'a> MessageHandler> for NodeG Ordering::Equal => {} } } - - responses.add(NodeGraphMessage::SendWires); } NodeGraphMessage::ToggleSelectedAsLayersOrNodes => { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else { @@ -1746,8 +1733,6 @@ 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); } NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => { if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) { @@ -1761,7 +1746,6 @@ impl<'a> MessageHandler> for NodeG }); responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::SendGraph); - responses.add(NodeGraphMessage::SendWires); } NodeGraphMessage::SetDisplayName { node_id, @@ -1800,6 +1784,10 @@ impl<'a> MessageHandler> for NodeG NodeGraphMessage::TogglePreviewImpl { node_id } => { network_interface.toggle_preview(node_id, selection_network_path); } + NodeGraphMessage::ToggleNativeNodeGraphRender => { + self.native_node_graph_render = !self.native_node_graph_render; + responses.add(NodeGraphMessage::SendGraph); + } NodeGraphMessage::ToggleSelectedLocked => { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else { log::error!("Could not get selected nodes in NodeGraphMessage::ToggleSelectedLocked"); @@ -1945,12 +1933,12 @@ 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"); @@ -1970,7 +1958,6 @@ impl<'a> MessageHandler> for NodeG let add_import_export = !breadcrumb_network_path.is_empty(); responses.add(NodeGraphMessage::UpdateVisibleNodes); - responses.add(NodeGraphMessage::SendWires); responses.add(FrontendMessage::UpdateImportsExports { imports, exports, @@ -2461,41 +2448,6 @@ impl NodeGraphMessageHandler { } } - fn collect_wires(&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() - .filter_map(|connector| network_interface.newly_loaded_input_wire(connector, graph_wire_style, breadcrumb_network_path)) - .collect::>(); - - let changed_wire_inputs = added_wires.iter().map(|update| (update.id, update.input_index)).collect::>(); - self.frontend_wires.extend(changed_wire_inputs); - - let mut orphaned_wire_inputs = self.frontend_wires.clone(); - self.frontend_wires = 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 { - id, - input_index, - wire_path_update: None, - })); - - if let Some(wire_to_root) = network_interface.wire_to_root(graph_wire_style, breadcrumb_network_path) { - added_wires.push(wire_to_root); - } else { - added_wires.push(WirePathUpdate { - id: NodeId(u64::MAX), - input_index: u32::MAX as usize, - wire_path_update: None, - }) - } - - added_wires - } - fn collect_subgraph_names(network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Option> { let mut current_network_path = vec![]; let mut current_network = network_interface.nested_network(¤t_network_path).unwrap(); @@ -2667,7 +2619,7 @@ impl Default for NodeGraphMessageHandler { reordering_import: None, end_index: None, frontend_nodes: Vec::new(), - frontend_wires: HashSet::new(), + native_node_graph_render: false, } } } 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 f004921ef8..7551ad0734 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -60,10 +60,10 @@ pub struct FrontendXY { pub struct FrontendGraphInput { #[serde(rename = "dataType")] pub data_type: FrontendGraphDataType, - pub name: String, - pub description: String, #[serde(rename = "resolvedType")] pub resolved_type: String, + pub name: String, + pub description: String, /// Either "nothing", "import index {index}", or "{node name} output {output_index}". #[serde(rename = "connectedToString")] pub connected_to: String, @@ -86,6 +86,26 @@ pub struct FrontendGraphOutput { pub connected_to: Vec, } +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendExport { + pub port: FrontendGraphInput, + pub wire: Option, +} + +#[derive(Clone, Debug, Default, Eq, 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, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendImport { + pub port: FrontendGraphOutput, + pub wires: Vec, +} + // Metadata that is common to nodes and layers #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] pub struct FrontendNodeMetadata { @@ -159,6 +179,8 @@ pub struct FrontendNodeToRender { pub metadata: FrontendNodeMetadata, #[serde(rename = "nodeOrLayer")] pub node_or_layer: FrontendNodeOrLayer, + //TODO: Remove + pub wires: Vec<(String, bool, FrontendGraphDataType)>, } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] 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 7a9b617824..af8c15dff5 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -4,9 +4,8 @@ use super::nodes::SelectedNodes; use crate::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::node_graph::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type}; -use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphDataType}; +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::tool::common_functionality::graph_modification_utils; use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode; use deserialization::deserialize_node_persistent_metadata; @@ -19,7 +18,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 serde_json::{Value, json}; use std::collections::{HashMap, HashSet, VecDeque}; use std::hash::Hash; @@ -683,13 +681,6 @@ impl NodeNetworkInterface { Some(&metadata.persistent_metadata) } - fn transient_input_metadata(&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))?; - Some(&metadata.transient_metadata) - } - /// Returns the input name to display in the properties panel. If the name is empty then the type is used. pub fn displayed_input_name_and_description(&mut self, node_id: &NodeId, input_index: usize, network_path: &[NodeId]) -> (String, String) { let Some(input_metadata) = self.persistent_input_metadata(node_id, input_index, network_path) else { @@ -1541,30 +1532,6 @@ 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); - } } pub fn modify_import_export(&mut self, network_path: &[NodeId]) -> Option<&ModifyImportExportClickTarget> { @@ -1983,99 +1950,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::error!("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 { @@ -2100,141 +1974,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::error!("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 = FrontendGraphDataType::displayed_type(&self.input_type(&input, network_path)); - 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 = FrontendGraphDataType::displayed_type(&self.input_type(input, network_path)); - 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) @@ -2472,7 +2211,6 @@ impl NodeNetworkInterface { return; }; node_metadata.transient_metadata.click_targets.unload(); - self.unload_wires_for_node(node_id, network_path); } pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec, network_path: &[NodeId]) { @@ -3783,17 +3521,14 @@ impl NodeNetworkInterface { // If a connection is made to the imports (NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }, NodeInput::Network { .. }) => { self.unload_outward_wires(network_path); - self.unload_wire(input_connector, network_path); } // If a connection to the imports is disconnected (NodeInput::Network { .. }, 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); @@ -5839,9 +5574,6 @@ pub struct NodeNetworkTransientMetadata { pub modify_import_export: TransientMetadata, // Distance to the edges of the network, where the import/export ports are displayed. Rounded to nearest grid space when the panning ends. pub rounded_network_edge_distance: TransientMetadata, - - // Wires from the exports - pub wires: Vec>, } #[derive(Debug, Clone)] @@ -6020,12 +5752,11 @@ impl InputPersistentMetadata { } } -#[derive(Debug, Clone, Default)] -struct InputTransientMetadata { - wire: TransientMetadata, - // downstream_protonode: populated for all inputs after each compile - // types: populated for each protonode after each -} +// #[derive(Debug, Clone, Default)] +// struct InputTransientMetadata { +// // downstream_protonode: populated for all inputs after each compile +// // types: populated for each protonode after each +// } /// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -6079,15 +5810,15 @@ impl DocumentNodePersistentMetadata { #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] pub struct InputMetadata { pub persistent_metadata: InputPersistentMetadata, - #[serde(skip)] - transient_metadata: InputTransientMetadata, + // #[serde(skip)] + // transient_metadata: InputTransientMetadata, } impl Clone for InputMetadata { fn clone(&self) -> Self { InputMetadata { persistent_metadata: self.persistent_metadata.clone(), - transient_metadata: Default::default(), + // transient_metadata: Default::default(), } } } 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 index 60a3357725..c06e65d40a 100644 --- 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 @@ -1,20 +1,25 @@ use glam::{DVec2, IVec2}; use graph_craft::proto::GraphErrors; use graphene_std::uuid::NodeId; +use kurbo::BezPath; 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}, messages::portfolio::document::{ node_graph::utility_types::{ - FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer, FrontendNodeToRender, FrontendXY, + FrontendExport, FrontendExports, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendImport, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer, + FrontendNodeToRender, FrontendXY, + }, + utility_types::{ + network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing}, + wires::{GraphWireStyle, build_vector_wire}, }, - utility_types::network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector}, }, }; // Functions used to collect data from the network interface for use in rendering the node graph impl NodeNetworkInterface { - pub fn collect_nodes(&mut self, node_graph_errors: &GraphErrors, network_path: &[NodeId]) -> Vec { + pub fn collect_nodes(&mut self, node_graph_errors: &GraphErrors, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Vec { let Some(network) = self.nested_network(network_path) else { log::error!("Could not get nested network when collecting nodes"); return Vec::new(); @@ -99,7 +104,25 @@ impl NodeNetworkInterface { } }; - let frontend_node_to_render = FrontendNodeToRender { metadata, node_or_layer }; + let wires = (0..self.number_of_displayed_inputs(&node_id, network_path)) + .filter_map(|input_index| { + self.wire_from_input(&InputConnector::node(node_id, input_index), wire_style, network_path) + .filter(|_| { + self.upstream_output_connector(&InputConnector::node(node_id, input_index), network_path) + .is_some_and(|output| !matches!(output, OutputConnector::Import(_))) + }) + .map(|path| path.to_svg()) + .map(|wire| { + ( + wire, + self.wire_is_thick(&InputConnector::node(node_id, input_index), network_path), + FrontendGraphDataType::displayed_type(&self.input_type(&InputConnector::node(node_id, input_index), network_path)), + ) + }) + }) + .collect(); + + let frontend_node_to_render = FrontendNodeToRender { metadata, node_or_layer, wires }; nodes.push(frontend_node_to_render); } @@ -318,16 +341,35 @@ impl NodeNetworkInterface { .is_some_and(|node_id| self.is_layer(&node_id, network_path)) } - pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Vec> { + /// 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, encapsulatingnetwork_path)) => { - let Some(node) = self.document_node(node_id, encapsulatingnetwork_path) else { - log::error!("Could not get node {node_id} in network {encapsulatingnetwork_path:?}"); + 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)) + .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(&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); } @@ -338,13 +380,29 @@ impl NodeNetworkInterface { } } - 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); + /// 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(&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); } - frontend_exports + let preview_wire = self.wire_to_root(graph_wire_style, network_path); + FrontendExports { exports, preview_wire } } pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> { @@ -424,4 +482,60 @@ impl NodeNetworkInterface { 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(&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(&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.to_svg()) + } } diff --git a/editor/src/messages/portfolio/document/utility_types/wires.rs b/editor/src/messages/portfolio/document/utility_types/wires.rs index ad6ab32843..869bcb8890 100644 --- a/editor/src/messages/portfolio/document/utility_types/wires.rs +++ b/editor/src/messages/portfolio/document/utility_types/wires.rs @@ -1,26 +1,14 @@ use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType; use glam::{DVec2, IVec2}; -use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point}; +use graphene_std::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 { - #[serde(rename = "pathString")] - pub path_string: String, +pub struct WirePathInProgress { + pub wire: String, #[serde(rename = "dataType")] pub data_type: FrontendGraphDataType, pub thick: bool, - pub dashed: bool, -} - -#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct WirePathUpdate { - 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, } #[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..f66fdd4783 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 native_node_graph_render: 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 native_node_graph_render = self.native_node_graph_render; 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: "Native Node Graph UI Render".into(), + icon: Some(if native_node_graph_render { "CheckboxChecked" } else { "CheckboxUnchecked" }.into()), + action: MenuBarEntry::create_action(|_| NodeGraphMessage::ToggleNativeNodeGraphRender.into()), + ..MenuBarEntry::default() + }], vec![ MenuBarEntry { label: "Print Trace Logs".into(), diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index f11cc39427..3f28944765 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -905,8 +905,6 @@ 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) } 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 214903b9b9..9081376669 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 addadae0c2..f917a8b003 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -301,11 +301,11 @@ pub trait FrontendMessageTestUtils { impl FrontendMessageTestUtils for FrontendMessage { fn check_node_graph_error(&self) { - let FrontendMessage::UpdateNodeGraphNodes { nodes, .. } = self else { return }; + let FrontendMessage::UpdateNodeGraphRender { 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 22c7862438..bb26b5e7b6 100644 --- a/frontend/src/components/panels/Document.svelte +++ b/frontend/src/components/panels/Document.svelte @@ -564,8 +564,7 @@ {/if} - -
+
@@ -835,18 +834,6 @@ pointer-events: auto; opacity: 1; } - - &::before { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: var(--color-2-mildblack); - opacity: var(--fade-artwork); - pointer-events: none; - } } .fade-artwork, diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index e16943df83..747d4f6d71 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 FrontendGraphInput, type FrontendGraphOutput } from "@graphite/messages"; import type { NodeGraphState } from "@graphite/state-providers/node-graph"; import type { IconName } from "@graphite/utility-functions/icons"; @@ -223,15 +223,231 @@ } -
+{#if !$nodeGraph.nativeNodeGraphRender} +
+ +
+ {#each Array.from($nodeGraph.nodesToRender) as [nodeId, nodeToRender]} + {#if nodeToRender.nodeOrLayer.layer !== undefined} + {@const nodeMetadata = nodeToRender.metadata} + {@const layer = nodeToRender.nodeOrLayer.layer} + {@const clipPathId = String(Math.random()).substring(2)} + {@const layerAreaWidth = $nodeGraph.layerWidths.get(nodeToRender.metadata.nodeId) || 8} + {@const layerChainWidth = layer.chainWidth !== 0 ? layer.chainWidth + 0.5 : 0} + {@const description = (nodeMetadata.reference && $nodeGraph.nodeDescriptions.get(nodeMetadata.reference)) || undefined} +
+ {#if nodeMetadata.errors} + {layer.errors} + {layer.errors} + {/if} +
+ {#if $nodeGraph.thumbnails.has(nodeId)} + {@html $nodeGraph.thumbnails.get(nodeId)} + {/if} + + + {outputTooltip(layer.output)} + 0 ? "var(--data-color)" : "var(--data-color-dim)"} /> + + {#if layer.output.connectedTo.length > 0 && layer.primaryOutputConnectedToLayer} + + {/if} + + + + {#if layer.bottomInput} + {inputTooltip(layer.bottomInput)} + {/if} + {#if layer.bottomInput?.connectedToNode !== undefined} + + {#if layer.primaryInputConnectedToLayer} + + {/if} + {:else} + + {/if} + +
+ + {#if layer.sideInput} +
+ + {inputTooltip(layer.sideInput)} + + +
+ {/if} +
+ + {nodeMetadata.displayName} +
+
+ { + /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ + }} + tooltip={nodeMetadata.visible ? "Visible" : "Hidden"} + /> + + + + + + + + + +
+ {/if} + {/each} + + {#each Array.from($nodeGraph.nodesToRender) as [_, nodeToRender]} + {#each nodeToRender.wires as [wire, thick, dataType]} + + + + {/each} + {/each} + {#each Array.from($nodeGraph.nodesToRender) as [nodeId, nodeToRender]} + {#if nodeToRender.nodeOrLayer.node !== undefined && $nodeGraph.visibleNodes.has(nodeId)} + {@const nodeMetadata = nodeToRender.metadata} + {@const node = nodeToRender.nodeOrLayer.node} + {@const exposedInputsOutputs = collectExposedInputsOutputs(node.inputs, node.outputs)} + {@const clipPathId = String(Math.random()).substring(2)} + {@const description = (nodeMetadata.reference && $nodeGraph.nodeDescriptions.get(nodeMetadata.reference)) || undefined} +
+ {#if nodeMetadata.errors} + {node.errors} + {node.errors} + {/if} + +
+ + + {nodeMetadata.displayName} +
+ + {#if exposedInputsOutputs.length > 0} +
+ {#each exposedInputsOutputs as [input, output]} +
+ + {input?.name ?? output?.name ?? ""} + +
+ {/each} +
+ {/if} + +
+ {#each node.inputs as input} + {#if input !== undefined} + + {inputTooltip(input)} + + + {/if} + {/each} +
+ +
+ {#each node.outputs as output} + {#if output !== undefined} + + {outputTooltip(output)} + + + {/if} + {/each} +
+ + + + + + + +
+ {/if} + {/each} +
+{/if} + +
{#if $nodeGraph.contextMenuInformation} {/if} - -
- - {#each $nodeGraph.wires.values() as map} - {#each map.values() as { pathString, dataType, thick, dashed }} - {#if thick} - - {/if} - {/each} - {/each} - -
+ + + {#if $nodeGraph.wirePathInProgress} + + {/if} +
{#if $nodeGraph.updateImportsExports} - {#each $nodeGraph.updateImportsExports.imports as frontendOutput, index} - {#if frontendOutput} + {#each $nodeGraph.updateImportsExports.imports as frontendImport, index} + {#if frontendImport} + {@const frontendOutput = frontendImport.port} + + {#each frontendImport.wires as wire} + + + + {/each} + + + {/if} - 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} -
- -
- {#each Array.from($nodeGraph.nodesToRender) as [nodeId, nodeToRender]} - {#if nodeToRender.nodeOrLayer.layer !== undefined} - {@const nodeMetadata = nodeToRender.metadata} - {@const layer = nodeToRender.nodeOrLayer.layer} - {@const clipPathId = String(Math.random()).substring(2)} - {@const layerAreaWidth = $nodeGraph.layerWidths.get(nodeToRender.metadata.nodeId) || 8} - {@const layerChainWidth = layer.chainWidth !== 0 ? layer.chainWidth + 0.5 : 0} - {@const description = (nodeMetadata.reference && $nodeGraph.nodeDescriptions.get(nodeMetadata.reference)) || undefined} -
- {#if nodeMetadata.errors} - {layer.errors} - {layer.errors} - {/if} -
- {#if $nodeGraph.thumbnails.has(nodeId)} - {@html $nodeGraph.thumbnails.get(nodeId)} - {/if} - - - {outputTooltip(layer.output)} - 0 ? "var(--data-color)" : "var(--data-color-dim)"} /> - - {#if layer.output.connectedTo.length > 0 && layer.primaryOutputConnectedToLayer} - - {/if} - - - - {#if layer.bottomInput} - {inputTooltip(layer.bottomInput)} - {/if} - {#if layer.bottomInput?.connectedToNode !== undefined} - - {#if layer.primaryInputConnectedToLayer} - - {/if} - {:else} - - {/if} - -
- - {#if layer.sideInput} -
- - {inputTooltip(layer.sideInput)} - - -
- {/if} -
- - {nodeMetadata.displayName} -
-
- { - /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ - }} - tooltip={nodeMetadata.visible ? "Visible" : "Hidden"} - /> - - - - - - - - - -
- {/if} - {/each} - -
- - {#each $nodeGraph.wires.values() as map} - {#each map.values() as { pathString, dataType, thick, dashed }} - {#if !thick} - - {/if} - {/each} - {/each} - {#if $nodeGraph.wirePathInProgress} - - {/if} - -
- {#each Array.from($nodeGraph.nodesToRender) as [nodeId, nodeToRender]} - {#if nodeToRender.nodeOrLayer.node !== undefined && $nodeGraph.visibleNodes.has(nodeId)} - {@const nodeMetadata = nodeToRender.metadata} - {@const node = nodeToRender.nodeOrLayer.node} - {@const exposedInputsOutputs = collectExposedInputsOutputs(node.inputs, node.outputs)} - {@const clipPathId = String(Math.random()).substring(2)} - {@const description = (nodeMetadata.reference && $nodeGraph.nodeDescriptions.get(nodeMetadata.reference)) || undefined} -
- {#if nodeMetadata.errors} - {node.errors} - {node.errors} - {/if} - -
- - - {nodeMetadata.displayName} -
- - {#if exposedInputsOutputs.length > 0} -
- {#each exposedInputsOutputs as [input, output]} -
- - {input?.name ?? output?.name ?? ""} - -
- {/each} -
- {/if} - -
- {#each node.inputs as input} - {#if input !== undefined} - - {inputTooltip(input)} - - - {/if} - {/each} -
- -
- {#each node.outputs as output} - {#if output !== undefined} - - {outputTooltip(output)} - - - {/if} - {/each} -
- - - - - - - -
- {/if} - {/each} -
-
- - -{#if $nodeGraph.box} -
-{/if} - -