Complete separating node rendering from imports/exports

This commit is contained in:
Adam
2025-09-08 19:19:27 -07:00
parent e2c45195ef
commit 1ab681da8c
14 changed files with 1001 additions and 1193 deletions
@@ -2,10 +2,10 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform; use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::utility_types::{ use crate::messages::portfolio::document::node_graph::utility_types::{
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, 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::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::prelude::*;
use crate::messages::tool::utility_types::HintData; use crate::messages::tool::utility_types::HintData;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
@@ -127,9 +127,8 @@ pub enum FrontendMessage {
}, },
UpdateImportsExports { UpdateImportsExports {
/// If the primary import is not visible, then it is None. /// If the primary import is not visible, then it is None.
imports: Vec<Option<FrontendGraphOutput>>, imports: Vec<Option<FrontendImport>>,
/// If the primary export is not visible, then it is None. exports: FrontendExports,
exports: Vec<Option<FrontendGraphInput>>,
/// The primary import location. /// The primary import location.
#[serde(rename = "importPosition")] #[serde(rename = "importPosition")]
import_position: FrontendXY, import_position: FrontendXY,
@@ -140,7 +139,7 @@ pub enum FrontendMessage {
#[serde(rename = "addImportExport")] #[serde(rename = "addImportExport")]
add_import_export: bool, add_import_export: bool,
}, },
UpdateBox { UpdateNodeGraphSelectionBox {
#[serde(rename = "box")] #[serde(rename = "box")]
box_selection: Option<BoxSelection>, box_selection: Option<BoxSelection>,
}, },
@@ -285,10 +284,6 @@ pub enum FrontendMessage {
UpdateVisibleNodes { UpdateVisibleNodes {
nodes: Vec<NodeId>, nodes: Vec<NodeId>,
}, },
UpdateNodeGraphWires {
wires: Vec<WirePathUpdate>,
},
ClearAllNodeGraphWires,
UpdateNodeGraphControlBarLayout { UpdateNodeGraphControlBarLayout {
#[serde(rename = "layoutTarget")] #[serde(rename = "layoutTarget")]
layout_target: LayoutTarget, layout_target: LayoutTarget,
@@ -321,8 +316,8 @@ pub enum FrontendMessage {
diff: Vec<WidgetDiff>, diff: Vec<WidgetDiff>,
}, },
UpdateWirePathInProgress { UpdateWirePathInProgress {
#[serde(rename = "wirePath")] #[serde(rename = "wirePathInProgress")]
wire_path: Option<WirePath>, wire_path_in_progress: Option<WirePathInProgress>,
}, },
UpdateWorkingColorsLayout { UpdateWorkingColorsLayout {
#[serde(rename = "layoutTarget")] #[serde(rename = "layoutTarget")]
@@ -474,7 +474,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
DocumentMessage::EnterNestedNetwork { node_id } => { DocumentMessage::EnterNestedNetwork { node_id } => {
self.breadcrumb_network_path.push(node_id); self.breadcrumb_network_path.push(node_id);
self.selection_network_path.clone_from(&self.breadcrumb_network_path); self.selection_network_path.clone_from(&self.breadcrumb_network_path);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::ZoomCanvasToFitAll); responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(NodeGraphMessage::SetGridAlignedEdges); responses.add(NodeGraphMessage::SetGridAlignedEdges);
@@ -494,7 +493,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None }); responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None });
self.node_graph_handler.wire_in_progress_from_connector = None; self.node_graph_handler.wire_in_progress_from_connector = None;
self.node_graph_handler.wire_in_progress_to_connector = None; self.node_graph_handler.wire_in_progress_to_connector = None;
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
} else { } else {
responses.add(DocumentMessage::GraphViewOverlay { open: false }); responses.add(DocumentMessage::GraphViewOverlay { open: false });
} }
@@ -504,7 +503,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
self.breadcrumb_network_path.pop(); self.breadcrumb_network_path.pop();
self.selection_network_path.clone_from(&self.breadcrumb_network_path); self.selection_network_path.clone_from(&self.breadcrumb_network_path);
} }
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::PTZUpdate); responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::SetGridAlignedEdges); responses.add(NodeGraphMessage::SetGridAlignedEdges);
@@ -557,7 +555,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
} }
} }
DocumentMessage::GraphViewOverlay { open } => { DocumentMessage::GraphViewOverlay { open } => {
let opened = !self.graph_view_overlay_open && open;
self.graph_view_overlay_open = open; self.graph_view_overlay_open = open;
responses.add(FrontendMessage::UpdateGraphViewOverlay { open }); responses.add(FrontendMessage::UpdateGraphViewOverlay { open });
@@ -567,9 +564,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderRulers);
responses.add(DocumentMessage::RenderScrollbars); responses.add(DocumentMessage::RenderScrollbars);
if opened {
responses.add(NodeGraphMessage::UnloadWires);
}
if open { if open {
responses.add(ToolMessage::DeactivateTools); responses.add(ToolMessage::DeactivateTools);
responses.add(OverlaysMessage::Draw); // Clear the overlays responses.add(OverlaysMessage::Draw); // Clear the overlays
@@ -1949,7 +1943,6 @@ impl DocumentMessageHandler {
responses.add(NodeGraphMessage::ForceRunDocumentGraph); responses.add(NodeGraphMessage::ForceRunDocumentGraph);
// TODO: Remove once the footprint is used to load the imports/export distances from the edge // TODO: Remove once the footprint is used to load the imports/export distances from the edge
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SetGridAlignedEdges); responses.add(NodeGraphMessage::SetGridAlignedEdges);
Some(previous_network) Some(previous_network)
@@ -1981,8 +1974,6 @@ impl DocumentMessageHandler {
responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::SelectedNodesUpdated);
responses.add(NodeGraphMessage::ForceRunDocumentGraph); responses.add(NodeGraphMessage::ForceRunDocumentGraph);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendWires);
Some(previous_network) Some(previous_network)
} }
@@ -139,8 +139,6 @@ pub enum NodeGraphMessage {
}, },
SendClickTargets, SendClickTargets,
EndSendClickTargets, EndSendClickTargets,
UnloadWires,
SendWires,
UpdateVisibleNodes, UpdateVisibleNodes,
SendGraph, SendGraph,
SetGridAlignedEdges, SetGridAlignedEdges,
@@ -13,7 +13,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::{
self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing,
}; };
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry}; use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire}; use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePathInProgress, build_vector_wire};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode; use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
@@ -93,8 +93,6 @@ pub struct NodeGraphMessageHandler {
end_index: Option<usize>, end_index: Option<usize>,
/// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend /// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend
frontend_nodes: Vec<NodeId>, frontend_nodes: Vec<NodeId>,
/// 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)>,
} }
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network. /// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
@@ -297,7 +295,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.wire_in_progress_type = FrontendGraphDataType::General; self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
} }
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
responses.add(FrontendMessage::UpdateContextMenuInformation { responses.add(FrontendMessage::UpdateContextMenuInformation {
context_menu_information: self.context_menu.clone(), context_menu_information: self.context_menu.clone(),
}); });
@@ -470,7 +468,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
} }
responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(NodeGraphMessage::SendWires);
} }
NodeGraphMessage::ExposePrimaryExport { exposed } => { NodeGraphMessage::ExposePrimaryExport { exposed } => {
let export_connector: InputConnector = InputConnector::Export(0); let export_connector: InputConnector = InputConnector::Export(0);
@@ -761,7 +758,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::SelectedNodesSet { responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: self.selection_before_pointer_down.clone(), nodes: self.selection_before_pointer_down.clone(),
}); });
responses.add(FrontendMessage::UpdateBox { box_selection: None }); responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None });
return; return;
} }
// Abort dragging a wire // Abort dragging a wire
@@ -770,7 +767,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.wire_in_progress_type = FrontendGraphDataType::General; self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
responses.add(DocumentMessage::AbortTransaction); responses.add(DocumentMessage::AbortTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
return; return;
} }
@@ -851,7 +848,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(FrontendMessage::UpdateContextMenuInformation { responses.add(FrontendMessage::UpdateContextMenuInformation {
context_menu_information: self.context_menu.clone(), context_menu_information: self.context_menu.clone(),
}); });
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
} }
// Toggle visibility of clicked node and return // Toggle visibility of clicked node and return
@@ -1058,14 +1055,14 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
to_connector_is_layer, to_connector_is_layer,
GraphWireStyle::Direct, GraphWireStyle::Direct,
); );
let path_string = vector_wire.to_svg(); let wire_path = WirePathInProgress {
let wire_path = WirePath { wire: vector_wire.to_svg(),
path_string,
data_type: self.wire_in_progress_type, data_type: self.wire_in_progress_type,
thick: false, thick: false,
dashed: false,
}; };
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) }); responses.add(FrontendMessage::UpdateWirePathInProgress {
wire_path_in_progress: Some(wire_path),
});
} }
} else if let Some((drag_start, dragged)) = &mut self.drag_start { } else if let Some((drag_start, dragged)) = &mut self.drag_start {
if drag_start.start_x != point.x || drag_start.start_y != point.y { if drag_start.start_x != point.x || drag_start.start_y != point.y {
@@ -1315,7 +1312,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
return None; return None;
} }
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?; let wire = network_interface.wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
let thick = network_interface.wire_is_thick(&input, selection_network_path);
let node_bbox = kurbo::Rect::new(node_bbox[0].x, node_bbox[0].y, node_bbox[1].x, node_bbox[1].y).to_path(DEFAULT_ACCURACY); let node_bbox = kurbo::Rect::new(node_bbox[0].x, node_bbox[0].y, node_bbox[1].x, node_bbox[1].y).to_path(DEFAULT_ACCURACY);
let inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None); let inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None);
@@ -1324,7 +1322,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
.segments() .segments()
.any(|segment| node_bbox.segments().filter_map(|segment| segment.as_line()).any(|line| !segment.intersect_line(line).is_empty())); .any(|segment| node_bbox.segments().filter_map(|segment| segment.as_line()).any(|line| !segment.intersect_line(line).is_empty()));
(intersect || inside).then_some((input, is_stack)) (intersect || inside).then_some((input, thick))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -1391,8 +1389,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.reordering_export = None; self.reordering_export = None;
self.reordering_import = None; self.reordering_import = None;
responses.add(DocumentMessage::EndTransaction); responses.add(DocumentMessage::EndTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
responses.add(FrontendMessage::UpdateBox { box_selection: None }); responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None });
responses.add(FrontendMessage::UpdateImportReorderIndex { index: None }); responses.add(FrontendMessage::UpdateImportReorderIndex { index: None });
responses.add(FrontendMessage::UpdateExportReorderIndex { index: None }); responses.add(FrontendMessage::UpdateExportReorderIndex { index: None });
self.update_node_graph_hints(responses); self.update_node_graph_hints(responses);
@@ -1591,17 +1589,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)), click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)),
}), }),
NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }), NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }),
NodeGraphMessage::UnloadWires => {
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 => { NodeGraphMessage::UpdateVisibleNodes => {
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
return; return;
@@ -1634,7 +1621,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(DocumentMessage::DocumentStructureChanged); responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::Refresh); responses.add(PropertiesPanelMessage::Refresh);
responses.add(NodeGraphMessage::UpdateActionButtons); responses.add(NodeGraphMessage::UpdateActionButtons);
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, breadcrumb_network_path); 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(); self.frontend_nodes = nodes_to_render.iter().map(|node| node.metadata.node_id).collect();
let previewed_node = network_interface.previewed_node(breadcrumb_network_path); let previewed_node = network_interface.previewed_node(breadcrumb_network_path);
responses.add(FrontendMessage::UpdateNodeGraphRender { responses.add(FrontendMessage::UpdateNodeGraphRender {
@@ -1650,7 +1637,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(FrontendMessage::UpdateLayerWidths { layer_widths }); responses.add(FrontendMessage::UpdateLayerWidths { layer_widths });
responses.add(NodeGraphMessage::SendWires);
self.update_node_graph_hints(responses); self.update_node_graph_hints(responses);
} }
NodeGraphMessage::SetGridAlignedEdges => { NodeGraphMessage::SetGridAlignedEdges => {
@@ -1725,8 +1711,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
Ordering::Equal => {} Ordering::Equal => {}
} }
} }
responses.add(NodeGraphMessage::SendWires);
} }
NodeGraphMessage::ToggleSelectedAsLayersOrNodes => { NodeGraphMessage::ToggleSelectedAsLayersOrNodes => {
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
@@ -1746,8 +1730,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
} }
NodeGraphMessage::ShiftNodePosition { node_id, x, y } => { NodeGraphMessage::ShiftNodePosition { node_id, x, y } => {
network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path); network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path);
responses.add(NodeGraphMessage::SendWires);
} }
NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => { NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => {
if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) { if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) {
@@ -1761,7 +1743,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}); });
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(NodeGraphMessage::SendWires);
} }
NodeGraphMessage::SetDisplayName { NodeGraphMessage::SetDisplayName {
node_id, node_id,
@@ -1945,12 +1926,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
nodes: nodes.into_iter().collect::<Vec<_>>(), nodes: nodes.into_iter().collect::<Vec<_>>(),
}); });
} }
responses.add(FrontendMessage::UpdateBox { box_selection }) responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection })
} }
} }
NodeGraphMessage::UpdateImportsExports => { NodeGraphMessage::UpdateImportsExports => {
let imports = network_interface.frontend_imports(breadcrumb_network_path); let imports = network_interface.frontend_imports(preferences.graph_wire_style, breadcrumb_network_path);
let exports = network_interface.frontend_exports(breadcrumb_network_path); let exports = network_interface.frontend_exports(preferences.graph_wire_style, breadcrumb_network_path);
let Some((import_position, export_position)) = network_interface.import_export_position(breadcrumb_network_path) else { let Some((import_position, export_position)) = network_interface.import_export_position(breadcrumb_network_path) else {
log::error!("Could not get import export positions"); log::error!("Could not get import export positions");
@@ -1970,7 +1951,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let add_import_export = !breadcrumb_network_path.is_empty(); let add_import_export = !breadcrumb_network_path.is_empty();
responses.add(NodeGraphMessage::UpdateVisibleNodes); responses.add(NodeGraphMessage::UpdateVisibleNodes);
responses.add(NodeGraphMessage::SendWires);
responses.add(FrontendMessage::UpdateImportsExports { responses.add(FrontendMessage::UpdateImportsExports {
imports, imports,
exports, exports,
@@ -2461,41 +2441,6 @@ impl NodeGraphMessageHandler {
} }
} }
fn collect_wires(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec<WirePathUpdate> {
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::<Vec<_>>();
let changed_wire_inputs = added_wires.iter().map(|update| (update.id, update.input_index)).collect::<Vec<_>>();
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::<HashSet<_>>();
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<Vec<String>> { fn collect_subgraph_names(network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Option<Vec<String>> {
let mut current_network_path = vec![]; let mut current_network_path = vec![];
let mut current_network = network_interface.nested_network(&current_network_path).unwrap(); let mut current_network = network_interface.nested_network(&current_network_path).unwrap();
@@ -2667,7 +2612,6 @@ impl Default for NodeGraphMessageHandler {
reordering_import: None, reordering_import: None,
end_index: None, end_index: None,
frontend_nodes: Vec::new(), frontend_nodes: Vec::new(),
frontend_wires: HashSet::new(),
} }
} }
} }
@@ -60,10 +60,10 @@ pub struct FrontendXY {
pub struct FrontendGraphInput { pub struct FrontendGraphInput {
#[serde(rename = "dataType")] #[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType, pub data_type: FrontendGraphDataType,
pub name: String,
pub description: String,
#[serde(rename = "resolvedType")] #[serde(rename = "resolvedType")]
pub resolved_type: String, pub resolved_type: String,
pub name: String,
pub description: String,
/// Either "nothing", "import index {index}", or "{node name} output {output_index}". /// Either "nothing", "import index {index}", or "{node name} output {output_index}".
#[serde(rename = "connectedToString")] #[serde(rename = "connectedToString")]
pub connected_to: String, pub connected_to: String,
@@ -86,6 +86,26 @@ pub struct FrontendGraphOutput {
pub connected_to: Vec<String>, pub connected_to: Vec<String>,
} }
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendExport {
pub port: FrontendGraphInput,
pub wire: Option<String>,
}
#[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<Option<FrontendExport>>,
#[serde(rename = "previewWire")]
pub preview_wire: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendImport {
pub port: FrontendGraphOutput,
pub wires: Vec<String>,
}
// Metadata that is common to nodes and layers // Metadata that is common to nodes and layers
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeMetadata { pub struct FrontendNodeMetadata {
@@ -159,6 +179,8 @@ pub struct FrontendNodeToRender {
pub metadata: FrontendNodeMetadata, pub metadata: FrontendNodeMetadata,
#[serde(rename = "nodeOrLayer")] #[serde(rename = "nodeOrLayer")]
pub node_or_layer: FrontendNodeOrLayer, pub node_or_layer: FrontendNodeOrLayer,
//TODO: Remove
pub wires: Vec<(String, bool, FrontendGraphDataType)>,
} }
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
@@ -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::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP};
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type}; use crate::messages::portfolio::document::node_graph::document_node_definitions::{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::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::common_functionality::graph_modification_utils;
use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode; use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode;
use deserialization::deserialize_node_persistent_metadata; use deserialization::deserialize_node_persistent_metadata;
@@ -20,7 +19,6 @@ use graphene_std::subpath::Subpath;
use graphene_std::transform::Footprint; use graphene_std::transform::Footprint;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType}; use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, Vector, VectorModificationType}; use graphene_std::vector::{PointId, Vector, VectorModificationType};
use kurbo::BezPath;
use serde_json::{Value, json}; use serde_json::{Value, json};
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::Hash; use std::hash::Hash;
@@ -684,13 +682,6 @@ impl NodeNetworkInterface {
Some(&metadata.persistent_metadata) Some(&metadata.persistent_metadata)
} }
fn transient_input_metadata(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&InputTransientMetadata> {
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. /// 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) { 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 { let Some(input_metadata) = self.persistent_input_metadata(node_id, input_index, network_path) else {
@@ -1538,30 +1529,6 @@ impl NodeNetworkInterface {
return; return;
}; };
network_metadata.transient_metadata.import_export_ports.unload(); 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> { pub fn modify_import_export(&mut self, network_path: &[NodeId]) -> Option<&ModifyImportExportClickTarget> {
@@ -1980,99 +1947,6 @@ impl NodeNetworkInterface {
.find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None }) .find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None })
} }
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
if !self.wire_is_loaded(input, network_path) {
self.load_wire(input, graph_wire_style, network_path);
} else {
return None;
}
let wire = match input {
InputConnector::Node { node_id, input_index } => {
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
InputConnector::Export(export_index) => {
let network_metadata = self.network_metadata(network_path)?;
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
};
Some(wire)
}
pub fn wire_is_loaded(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(input_metadata) = self.transient_input_metadata(node_id, *input_index, network_path) else {
log::error!("Input metadata should always exist for input");
return false;
};
input_metadata.wire.is_loaded()
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata(network_path) else {
return false;
};
match network_metadata.transient_metadata.wires.get(*export_index) {
Some(wire) => wire.is_loaded(),
None => false,
}
}
}
}
fn load_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) {
let dashed = match self.previewing(network_path) {
Previewing::Yes { .. } => match input {
InputConnector::Node { .. } => false,
InputConnector::Export(export_index) => *export_index == 0,
},
Previewing::No => false,
};
let Some(wire) = self.wire_path_from_input(input, graph_wire_style, dashed, network_path) else {
log::error!("Could not load wire path from input");
return;
};
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return };
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
let wire_update = WirePathUpdate {
id: *node_id,
input_index: *input_index,
wire_path_update: Some(wire),
};
input_metadata.transient_metadata.wire = TransientMetadata::Loaded(wire_update);
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return };
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
let wire_update = WirePathUpdate {
id: NodeId(u64::MAX),
input_index: *export_index,
wire_path_update: Some(wire),
};
*input_metadata = TransientMetadata::Loaded(wire_update);
}
}
}
pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec<InputConnector> { pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec<InputConnector> {
let mut input_connectors = Vec::new(); let mut input_connectors = Vec::new();
let Some(network) = self.nested_network(network_path) else { let Some(network) = self.nested_network(network_path) else {
@@ -2097,141 +1971,6 @@ impl NodeNetworkInterface {
.collect() .collect()
} }
/// Maps to the frontend representation of a wire start. Includes disconnected value wire inputs.
pub fn node_graph_wire_inputs(&self, network_path: &[NodeId]) -> Vec<(NodeId, usize)> {
self.node_graph_input_connectors(network_path)
.iter()
.map(|input| match input {
InputConnector::Node { node_id, input_index } => (*node_id, *input_index),
InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index),
})
.chain(std::iter::once((NodeId(u64::MAX), u32::MAX as usize)))
.collect()
}
fn unload_wires_for_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
let number_of_outputs = self.number_of_outputs(node_id, network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in reorder_export");
return;
};
let mut input_connectors = Vec::new();
for output_index in 0..number_of_outputs {
let Some(inputs) = outward_wires.get(&OutputConnector::node(*node_id, output_index)) else {
continue;
};
input_connectors.extend(inputs.clone())
}
for input_index in 0..self.number_of_inputs(node_id, network_path) {
input_connectors.push(InputConnector::node(*node_id, input_index));
}
for input in input_connectors {
self.unload_wire(&input, network_path);
}
}
pub fn unload_wire(&mut self, input: &InputConnector, network_path: &[NodeId]) {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
input_metadata.transient_metadata.wire = TransientMetadata::Unloaded;
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return;
};
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
*input_metadata = TransientMetadata::Unloaded;
}
}
}
/// When previewing, there may be a second path to the root node.
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
let input = InputConnector::Export(0);
let current_export = self.upstream_output_connector(&input, network_path)?;
let root_node = match self.previewing(network_path) {
Previewing::Yes { root_node_to_restore } => root_node_to_restore,
Previewing::No => None,
}?;
if Some(root_node.node_id) == current_export.node_id() {
return None;
}
let Some(input_position) = self.get_input_center(&input, network_path) else {
log::error!("Could not get input position for wire end in root node: {input:?}");
return None;
};
let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index);
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output position for wire start in root node: {upstream_output:?}");
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start: bool = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
let path_string = vector_wire.to_svg();
let data_type = 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<WirePath> {
let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
let path_string = vector_wire.to_svg();
let data_type = 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> { pub fn node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> {
self.try_load_node_click_targets(node_id, network_path); self.try_load_node_click_targets(node_id, network_path);
self.try_get_node_click_targets(node_id, network_path) self.try_get_node_click_targets(node_id, network_path)
@@ -2469,7 +2208,6 @@ impl NodeNetworkInterface {
return; return;
}; };
node_metadata.transient_metadata.click_targets.unload(); node_metadata.transient_metadata.click_targets.unload();
self.unload_wires_for_node(node_id, network_path);
} }
pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec<NodeId>, network_path: &[NodeId]) { pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec<NodeId>, network_path: &[NodeId]) {
@@ -3792,17 +3530,14 @@ impl NodeNetworkInterface {
// If a connection is made to the imports // If a connection is made to the imports
(NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }, NodeInput::Network { .. }) => { (NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }, NodeInput::Network { .. }) => {
self.unload_outward_wires(network_path); self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
} }
// If a connection to the imports is disconnected // If a connection to the imports is disconnected
(NodeInput::Network { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => { (NodeInput::Network { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path); self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
} }
// If a node is disconnected. // If a node is disconnected.
(NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => { (NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path); self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
if let Some((old_upstream_node_id, previous_position)) = previous_metadata { if let Some((old_upstream_node_id, previous_position)) = previous_metadata {
let old_upstream_node_is_layer = self.is_layer(&old_upstream_node_id, network_path); let old_upstream_node_is_layer = self.is_layer(&old_upstream_node_id, network_path);
@@ -5848,9 +5583,6 @@ pub struct NodeNetworkTransientMetadata {
pub modify_import_export: TransientMetadata<ModifyImportExportClickTarget>, pub modify_import_export: TransientMetadata<ModifyImportExportClickTarget>,
// Distance to the edges of the network, where the import/export ports are displayed. Rounded to nearest grid space when the panning ends. // 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<NetworkEdgeDistance>, pub rounded_network_edge_distance: TransientMetadata<NetworkEdgeDistance>,
// Wires from the exports
pub wires: Vec<TransientMetadata<WirePathUpdate>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -6029,12 +5761,11 @@ impl InputPersistentMetadata {
} }
} }
#[derive(Debug, Clone, Default)] // #[derive(Debug, Clone, Default)]
struct InputTransientMetadata { // struct InputTransientMetadata {
wire: TransientMetadata<WirePathUpdate>, // // downstream_protonode: populated for all inputs after each compile
// downstream_protonode: populated for all inputs after each compile // // types: populated for each protonode after each
// types: populated for each protonode after each // }
}
/// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node. /// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node.
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
@@ -6073,15 +5804,15 @@ impl DocumentNodePersistentMetadata {
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)] #[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct InputMetadata { pub struct InputMetadata {
pub persistent_metadata: InputPersistentMetadata, pub persistent_metadata: InputPersistentMetadata,
#[serde(skip)] // #[serde(skip)]
transient_metadata: InputTransientMetadata, // transient_metadata: InputTransientMetadata,
} }
impl Clone for InputMetadata { impl Clone for InputMetadata {
fn clone(&self) -> Self { fn clone(&self) -> Self {
InputMetadata { InputMetadata {
persistent_metadata: self.persistent_metadata.clone(), persistent_metadata: self.persistent_metadata.clone(),
transient_metadata: Default::default(), // transient_metadata: Default::default(),
} }
} }
} }
@@ -1,20 +1,25 @@
use glam::{DVec2, IVec2}; use glam::{DVec2, IVec2};
use graph_craft::proto::GraphErrors; use graph_craft::proto::GraphErrors;
use graphene_std::uuid::NodeId; use graphene_std::uuid::NodeId;
use kurbo::BezPath;
use crate::{ 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}, 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::{ messages::portfolio::document::{
node_graph::utility_types::{ 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 // Functions used to collect data from the network interface for use in rendering the node graph
impl NodeNetworkInterface { impl NodeNetworkInterface {
pub fn collect_nodes(&mut self, node_graph_errors: &GraphErrors, network_path: &[NodeId]) -> Vec<FrontendNodeToRender> { pub fn collect_nodes(&mut self, node_graph_errors: &GraphErrors, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Vec<FrontendNodeToRender> {
let Some(network) = self.nested_network(network_path) else { let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get nested network when collecting nodes"); log::error!("Could not get nested network when collecting nodes");
return Vec::new(); return Vec::new();
@@ -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); nodes.push(frontend_node_to_render);
} }
@@ -318,16 +341,35 @@ impl NodeNetworkInterface {
.is_some_and(|node_id| self.is_layer(&node_id, network_path)) .is_some_and(|node_id| self.is_layer(&node_id, network_path))
} }
pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphOutput>> { /// The imports contain both the output port and the outward wires
pub fn frontend_imports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Vec<Option<FrontendImport>> {
match network_path.split_last() { match network_path.split_last() {
Some((node_id, encapsulatingnetwork_path)) => { Some((node_id, encapsulating_network_path)) => {
let Some(node) = self.document_node(node_id, encapsulatingnetwork_path) else { let Some(node) = self.document_node(node_id, encapsulating_network_path) else {
log::error!("Could not get node {node_id} in network {encapsulatingnetwork_path:?}"); log::error!("Could not get node {node_id} in network {encapsulating_network_path:?}");
return Vec::new(); return Vec::new();
}; };
let mut frontend_imports = (0..node.inputs.len()) 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::<Vec<_>>();
Some(FrontendImport { port, wires })
})
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if frontend_imports.is_empty() { if frontend_imports.is_empty() {
frontend_imports.push(None); frontend_imports.push(None);
} }
@@ -338,13 +380,29 @@ impl NodeNetworkInterface {
} }
} }
pub fn frontend_exports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphInput>> { /// The imports contain the export port, the outward wires, and the preview wire if it exists
let Some(network) = self.nested_network(network_path) else { return Vec::new() }; pub fn frontend_exports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> FrontendExports {
let mut frontend_exports = ((0..network.exports.len()).map(|export_index| self.frontend_input_from_connector(&InputConnector::Export(export_index), network_path))).collect::<Vec<_>>(); let Some(network) = self.nested_network(network_path) else {
if frontend_exports.is_empty() { log::error!("Could not get nested network in frontend exports");
frontend_exports.push(None); 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::<Vec<_>>();
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)> { 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())) 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<BezPath> {
let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
};
// An upstream output could not be found
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return None;
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output port for wire start: {:?}", upstream_output);
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
Some(build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style))
}
/// When previewing, there may be a second path to the root node.
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<String> {
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())
}
} }
@@ -1,26 +1,14 @@
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType; use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use glam::{DVec2, IVec2}; 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}; use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WirePath { pub struct WirePathInProgress {
#[serde(rename = "pathString")] pub wire: String,
pub path_string: String,
#[serde(rename = "dataType")] #[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType, pub data_type: FrontendGraphDataType,
pub thick: bool, 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<WirePath>,
} }
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
@@ -909,8 +909,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open }); responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
if node_graph_open { if node_graph_open {
responses.add(NodeGraphMessage::UpdateGraphBarRight); responses.add(NodeGraphMessage::UpdateGraphBarRight);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendWires)
} else { } else {
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
@@ -87,8 +87,7 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
} }
PreferencesMessage::GraphWireStyle { style } => { PreferencesMessage::GraphWireStyle { style } => {
self.graph_wire_style = style; self.graph_wire_style = style;
responses.add(NodeGraphMessage::UnloadWires); responses.add(NodeGraphMessage::SendGraph);
responses.add(NodeGraphMessage::SendWires);
} }
PreferencesMessage::ViewportZoomWheelRate { rate } => { PreferencesMessage::ViewportZoomWheelRate { rate } => {
self.viewport_zoom_wheel_rate = rate; self.viewport_zoom_wheel_rate = rate;
+15 -2
View File
@@ -564,8 +564,9 @@
</canvas> </canvas>
{/if} {/if}
</div> </div>
<div class="graph-view" class:open={$document.graphViewOverlayOpen} data-graph>
<Graph /> <Graph />
</div>
</LayoutCol> </LayoutCol>
<LayoutCol class="ruler-or-scrollbar right-scrollbar"> <LayoutCol class="ruler-or-scrollbar right-scrollbar">
<ScrollbarInput <ScrollbarInput
@@ -823,6 +824,18 @@
} }
} }
.graph-view {
pointer-events: none;
transition: opacity 0.2s;
opacity: 0;
&.open {
cursor: auto;
pointer-events: auto;
opacity: 1;
}
}
.fade-artwork, .fade-artwork,
.graph { .graph {
position: absolute; position: absolute;
File diff suppressed because it is too large Load Diff
+64 -13
View File
@@ -26,13 +26,37 @@ export type XY = { x: number; y: number };
// ============================================================================ // ============================================================================
export class UpdateBox extends JsMessage { export class UpdateBox extends JsMessage {
readonly box!: Box | undefined; readonly box!: FrontendSelectionBox | undefined;
}
export class FrontendClickTargets {
readonly nodeClickTargets!: string[];
readonly layerClickTargets!: string[];
readonly connectorClickTargets!: string[];
readonly iconClickTargets!: string[];
readonly allNodesBoundingBox!: string;
readonly importExportsBoundingBox!: string;
readonly modifyImportExport!: string[];
} }
export class UpdateClickTargets extends JsMessage { export class UpdateClickTargets extends JsMessage {
readonly clickTargets!: FrontendClickTargets | undefined; readonly clickTargets!: FrontendClickTargets | undefined;
} }
export class FrontendSelectionBox {
readonly startX!: number;
readonly startY!: number;
readonly endX!: number;
readonly endY!: number;
}
export class UpdateNodeGraphSelectionBox extends JsMessage {
readonly box!: FrontendSelectionBox | undefined;
}
const ContextTupleToVec2 = Transform((data) => { const ContextTupleToVec2 = Transform((data) => {
if (data.obj.contextMenuInformation === undefined) return undefined; if (data.obj.contextMenuInformation === undefined) return undefined;
const contextMenuCoordinates = { x: data.obj.contextMenuInformation.contextMenuCoordinates[0], y: data.obj.contextMenuInformation.contextMenuCoordinates[1] }; const contextMenuCoordinates = { x: data.obj.contextMenuInformation.contextMenuCoordinates[0], y: data.obj.contextMenuInformation.contextMenuCoordinates[1] };
@@ -45,15 +69,20 @@ const ContextTupleToVec2 = Transform((data) => {
return { contextMenuCoordinates, contextMenuData }; return { contextMenuCoordinates, contextMenuData };
}); });
export class ContextMenuInformation {
readonly contextMenuCoordinates!: XY;
readonly contextMenuData!: "CreateNode" | { type: "CreateNode"; compatibleType: string } | { nodeId: bigint; currentlyIsNode: boolean };
}
export class UpdateContextMenuInformation extends JsMessage { export class UpdateContextMenuInformation extends JsMessage {
@ContextTupleToVec2 @ContextTupleToVec2
readonly contextMenuInformation!: ContextMenuInformation | undefined; readonly contextMenuInformation!: ContextMenuInformation | undefined;
} }
export class UpdateImportsExports extends JsMessage { export class UpdateImportsExports extends JsMessage {
readonly imports!: (FrontendGraphOutput | undefined)[]; readonly imports!: (FrontendImport | undefined)[];
readonly exports!: (FrontendGraphInput | undefined)[]; readonly exports!: FrontendExports;
readonly importPosition!: XY; readonly importPosition!: XY;
@@ -93,12 +122,6 @@ export class UpdateVisibleNodes extends JsMessage {
readonly nodes!: bigint[]; readonly nodes!: bigint[];
} }
export class UpdateNodeGraphWires extends JsMessage {
readonly wires!: WireUpdate[];
}
export class ClearAllNodeGraphWires extends JsMessage {}
export class UpdateNodeGraphTransform extends JsMessage { export class UpdateNodeGraphTransform extends JsMessage {
readonly transform!: NodeGraphTransform; readonly transform!: NodeGraphTransform;
} }
@@ -123,8 +146,14 @@ export class UpdateOpenDocumentsList extends JsMessage {
readonly openDocuments!: OpenDocument[]; readonly openDocuments!: OpenDocument[];
} }
export class WirePathInProgress {
readonly wire!: string;
readonly thick!: boolean;
readonly dataType!: FrontendGraphDataType;
}
export class UpdateWirePathInProgress extends JsMessage { export class UpdateWirePathInProgress extends JsMessage {
readonly wirePath!: WirePath | undefined; readonly wirePathInProgress!: WirePathInProgress | undefined;
} }
export class OpenDocument { export class OpenDocument {
@@ -149,6 +178,7 @@ export class DocumentDetails {
} }
} }
<<<<<<< HEAD
export class Box { export class Box {
readonly startX!: number; readonly startX!: number;
@@ -174,6 +204,12 @@ export type ContextMenuInformation = {
contextMenuData: "CreateNode" | { type: "CreateNode"; compatibleType: string } | { nodeId: bigint; currentlyIsNode: boolean }; contextMenuData: "CreateNode" | { type: "CreateNode"; compatibleType: string } | { nodeId: bigint; currentlyIsNode: boolean };
}; };
=======
export class FrontendDocumentDetails extends DocumentDetails {
readonly id!: bigint;
}
>>>>>>> 17a1a3d5 (Complete separating node rendering from imports/exports)
export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color"; export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color";
export class FrontendGraphInput { export class FrontendGraphInput {
@@ -202,6 +238,21 @@ export class FrontendGraphOutput {
readonly connectedTo!: string[]; readonly connectedTo!: string[];
} }
export class FrontendExport {
readonly port!: FrontendGraphInput;
readonly wire!: string | undefined;
}
export class FrontendExports {
readonly exports!: (FrontendExport | undefined)[];
readonly previewWire!: string | undefined;
}
export class FrontendImport {
readonly port!: FrontendGraphOutput;
readonly wires!: string[];
}
export class FrontendNodeMetadata { export class FrontendNodeMetadata {
readonly nodeId!: bigint; readonly nodeId!: bigint;
@@ -268,6 +319,8 @@ export class FrontendNodeOrLayer {
export class FrontendNodeToRender { export class FrontendNodeToRender {
readonly metadata!: FrontendNodeMetadata; readonly metadata!: FrontendNodeMetadata;
readonly nodeOrLayer!: FrontendNodeOrLayer; readonly nodeOrLayer!: FrontendNodeOrLayer;
// TODO: Remove
readonly wires!: [string, boolean, FrontendGraphDataType][];
} }
export class UpdateCentralNodeGraph extends JsMessage { export class UpdateCentralNodeGraph extends JsMessage {
@@ -1647,7 +1700,6 @@ type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHand
type MessageMaker = typeof JsMessage | JSMessageFactory; type MessageMaker = typeof JsMessage | JSMessageFactory;
export const messageMakers: Record<string, MessageMaker> = { export const messageMakers: Record<string, MessageMaker> = {
ClearAllNodeGraphWires,
DisplayDialog, DisplayDialog,
DisplayDialogDismiss, DisplayDialogDismiss,
DisplayDialogPanic, DisplayDialogPanic,
@@ -1676,7 +1728,6 @@ export const messageMakers: Record<string, MessageMaker> = {
TriggerTextCopy, TriggerTextCopy,
TriggerVisitLink, TriggerVisitLink,
UpdateActiveDocument, UpdateActiveDocument,
UpdateBox,
UpdateClickTargets, UpdateClickTargets,
UpdateContextMenuInformation, UpdateContextMenuInformation,
UpdateDialogButtons, UpdateDialogButtons,
@@ -1704,8 +1755,8 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateMouseCursor, UpdateMouseCursor,
UpdateNodeGraphControlBarLayout, UpdateNodeGraphControlBarLayout,
UpdateNodeGraphRender, UpdateNodeGraphRender,
UpdateNodeGraphSelectionBox,
UpdateNodeGraphTransform, UpdateNodeGraphTransform,
UpdateNodeGraphWires,
UpdateNodeThumbnail, UpdateNodeThumbnail,
UpdateOpenDocumentsList, UpdateOpenDocumentsList,
UpdatePlatform, UpdatePlatform,
+35 -52
View File
@@ -2,15 +2,13 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor"; import { type Editor } from "@graphite/editor";
import { import {
type Box, type FrontendSelectionBox,
type FrontendClickTargets, type FrontendClickTargets,
type ContextMenuInformation, type ContextMenuInformation,
type FrontendNodeToRender, type FrontendNodeToRender,
type FrontendNodeType, type FrontendNodeType,
type WirePath, type WirePathInProgress,
ClearAllNodeGraphWires,
SendUIMetadata, SendUIMetadata,
UpdateBox,
UpdateClickTargets, UpdateClickTargets,
UpdateContextMenuInformation, UpdateContextMenuInformation,
UpdateImportReorderIndex, UpdateImportReorderIndex,
@@ -19,35 +17,39 @@ import {
UpdateLayerWidths, UpdateLayerWidths,
UpdateNodeGraphRender, UpdateNodeGraphRender,
UpdateVisibleNodes, UpdateVisibleNodes,
UpdateNodeGraphWires,
UpdateNodeGraphTransform, UpdateNodeGraphTransform,
UpdateNodeThumbnail, UpdateNodeThumbnail,
UpdateWirePathInProgress, UpdateWirePathInProgress,
UpdateNodeGraphSelectionBox,
} from "@graphite/messages"; } from "@graphite/messages";
export function createNodeGraphState(editor: Editor) { export function createNodeGraphState(editor: Editor) {
const { subscribe, update } = writable({ const { subscribe, update } = writable({
box: undefined as Box | undefined, // Data that will continue to be rendered in Svelte for now
selectionBox: undefined as FrontendSelectionBox | undefined,
clickTargets: undefined as FrontendClickTargets | undefined, clickTargets: undefined as FrontendClickTargets | undefined,
contextMenuInformation: undefined as ContextMenuInformation | undefined, wirePathInProgress: undefined as WirePathInProgress | undefined,
layerWidths: new Map<bigint, number>(),
updateImportsExports: undefined as UpdateImportsExports | undefined, updateImportsExports: undefined as UpdateImportsExports | undefined,
nodesToRender: new Map<bigint, FrontendNodeToRender>(),
open: false,
opacity: 0.8,
visibleNodes: new Set<bigint>(),
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
wires: new Map<bigint, Map<number, WirePath>>(),
wirePathInProgress: undefined as WirePath | undefined,
nodeDescriptions: new Map<string, string>(),
nodeTypes: [] as FrontendNodeType[],
thumbnails: new Map<bigint, string>(),
transform: { scale: 1, x: 0, y: 0 },
inSelectedNetwork: true,
previewedNode: undefined as bigint | undefined,
reorderImportIndex: undefined as number | undefined, reorderImportIndex: undefined as number | undefined,
reorderExportIndex: undefined as number | undefined, reorderExportIndex: undefined as number | undefined,
contextMenuInformation: undefined as ContextMenuInformation | undefined,
nodeTypes: [] as FrontendNodeType[],
nodeDescriptions: new Map<string, string>(),
// Data that will be moved into the node graph to be rendered natively
nodesToRender: new Map<bigint, FrontendNodeToRender>(),
opacity: 0.8,
inSelectedNetwork: true,
previewedNode: undefined as bigint | undefined,
// TODO: Remove these fields
visibleNodes: new Set<bigint>(),
layerWidths: new Map<bigint, number>(),
// Data that will be passed in the context
thumbnails: new Map<bigint, string>(),
transform: { scale: 1, x: 0, y: 0 },
}); });
// Set up message subscriptions on creation // Set up message subscriptions on creation
@@ -58,15 +60,21 @@ export function createNodeGraphState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateBox, (updateBox) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSelectionBox, (updateBox) => {
update((state) => { update((state) => {
state.box = updateBox.box; state.selectionBox = updateBox.box;
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (UpdateClickTargets) => { editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (updateClickTargets) => {
update((state) => { update((state) => {
state.clickTargets = UpdateClickTargets.clickTargets; state.clickTargets = updateClickTargets.clickTargets;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => {
update((state) => {
state.wirePathInProgress = updateWirePathInProgress.wirePathInProgress;
return state; return state;
}); });
}); });
@@ -107,7 +115,6 @@ export function createNodeGraphState(editor: Editor) {
updateNodeGraphRender.nodesToRender.forEach((node) => { updateNodeGraphRender.nodesToRender.forEach((node) => {
state.nodesToRender.set(node.metadata.nodeId, node); state.nodesToRender.set(node.metadata.nodeId, node);
}); });
state.open = updateNodeGraphRender.open;
state.opacity = updateNodeGraphRender.opacity; state.opacity = updateNodeGraphRender.opacity;
state.inSelectedNetwork = updateNodeGraphRender.inSelectedNetwork; state.inSelectedNetwork = updateNodeGraphRender.inSelectedNetwork;
state.previewedNode = updateNodeGraphRender.previewedNode; state.previewedNode = updateNodeGraphRender.previewedNode;
@@ -120,30 +127,6 @@ export function createNodeGraphState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphWires, (updateNodeWires) => {
update((state) => {
updateNodeWires.wires.forEach((wireUpdate) => {
let inputMap = state.wires.get(wireUpdate.id);
// If it doesn't exist, create it and set it in the outer map
if (!inputMap) {
inputMap = new Map();
state.wires.set(wireUpdate.id, inputMap);
}
if (wireUpdate.wirePathUpdate !== undefined) {
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
} else {
inputMap.delete(wireUpdate.inputIndex);
}
});
return state;
});
});
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWires, (_) => {
update((state) => {
state.wires.clear();
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphTransform, (updateNodeGraphTransform) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphTransform, (updateNodeGraphTransform) => {
update((state) => { update((state) => {
state.transform = updateNodeGraphTransform.transform; state.transform = updateNodeGraphTransform.transform;
@@ -158,7 +141,7 @@ export function createNodeGraphState(editor: Editor) {
}); });
editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => { editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => {
update((state) => { update((state) => {
state.wirePathInProgress = updateWirePathInProgress.wirePath; state.wirePathInProgress = updateWirePathInProgress.wirePathInProgress;
return state; return state;
}); });
}); });