Separate svelte node graph rendering

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