mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 07:48:13 +08:00
Separate svelte node graph rendering
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
+435
@@ -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)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user