diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 4119eb0aa9..52db3c1742 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -53,6 +53,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[ /// Since we don't need to update the frontend multiple times per frame, /// we have a set of messages which we will buffer until the next frame is requested. const FRONTEND_UPDATE_MESSAGES: &[MessageDiscriminant] = &[ + MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel( PropertiesPanelMessageDiscriminant::Refresh, ))), diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index c3324a16ff..c26d82cab2 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -2,14 +2,14 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument}; use crate::messages::app_window::app_window_message_handler::AppWindowPlatform; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::node_graph::utility_types::{ - BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, Transform, + BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendExports, FrontendImport, FrontendNodeOld, FrontendNodeType, NodeGraphError, }; use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer}; -use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate}; +use crate::messages::portfolio::document::utility_types::wires::{WirePathInProgress, WirePathUpdateOld}; use crate::messages::prelude::*; use crate::messages::tool::utility_types::HintData; -use glam::IVec2; use graph_craft::document::NodeId; +use graphene_std::node_graph_overlay::types::{FrontendXY, NodeGraphTransform}; use graphene_std::raster::Image; use graphene_std::raster::color::Color; use graphene_std::text::{Font, TextAlign}; @@ -131,24 +131,23 @@ pub enum FrontendMessage { }, UpdateImportsExports { /// If the primary import is not visible, then it is None. - imports: Vec>, - /// If the primary export is not visible, then it is None. - exports: Vec>, + imports: Vec>, + exports: FrontendExports, /// The primary import location. #[serde(rename = "importPosition")] - import_position: IVec2, + import_position: FrontendXY, /// The primary export location. #[serde(rename = "exportPosition")] - export_position: IVec2, + export_position: FrontendXY, /// The document network does not have an add import or export button. #[serde(rename = "addImportExport")] add_import_export: bool, }, - UpdateInSelectedNetwork { + UpdateInSelectedNetworkOld { #[serde(rename = "inSelectedNetwork")] in_selected_network: bool, }, - UpdateBox { + UpdateNodeGraphSelectionBox { #[serde(rename = "box")] box_selection: Option, }, @@ -185,7 +184,7 @@ pub enum FrontendMessage { #[serde(rename = "exportIndex")] index: Option, }, - UpdateLayerWidths { + UpdateLayerWidthsOld { #[serde(rename = "layerWidths")] layer_widths: HashMap, #[serde(rename = "chainWidths")] @@ -256,7 +255,7 @@ pub enum FrontendMessage { #[serde(rename = "setColorChoice")] set_color_choice: Option, }, - UpdateGraphFadeArtwork { + UpdateGraphFadeArtworkOld { percentage: f64, }, UpdateInputHints { @@ -286,26 +285,33 @@ pub enum FrontendMessage { UpdateMouseCursor { cursor: MouseCursorIcon, }, - UpdateNodeGraphNodes { - nodes: Vec, + UpdateNodeGraphNodesOld { + nodes: Vec, }, - UpdateVisibleNodes { + UpdateRenderNativeNodeGraph { + #[serde(rename = "renderNativeNodeGraph")] + render_native_node_graph: bool, + }, + UpdateNodeGraphError { + error: Option, + }, + UpdateVisibleNodesOld { nodes: Vec, }, - UpdateNodeGraphWires { - wires: Vec, + UpdateNodeGraphWiresOld { + wires: Vec, }, - ClearAllNodeGraphWires, + ClearAllNodeGraphWiresOld, UpdateNodeGraphControlBarLayout { #[serde(rename = "layoutTarget")] layout_target: LayoutTarget, diff: Vec, }, - UpdateNodeGraphSelection { + UpdateNodeGraphSelectionOld { selected: Vec, }, UpdateNodeGraphTransform { - transform: Transform, + transform: NodeGraphTransform, }, UpdateNodeThumbnail { id: NodeId, @@ -331,8 +337,8 @@ pub enum FrontendMessage { diff: Vec, }, UpdateWirePathInProgress { - #[serde(rename = "wirePath")] - wire_path: Option, + #[serde(rename = "wirePathInProgress")] + wire_path_in_progress: Option, }, UpdateWorkingColorsLayout { #[serde(rename = "layoutTarget")] diff --git a/editor/src/messages/layout/utility_types/widgets/button_widgets.rs b/editor/src/messages/layout/utility_types/widgets/button_widgets.rs index 7a7e54856b..d5b7705079 100644 --- a/editor/src/messages/layout/utility_types/widgets/button_widgets.rs +++ b/editor/src/messages/layout/utility_types/widgets/button_widgets.rs @@ -1,8 +1,8 @@ use crate::messages::input_mapper::utility_types::misc::ActionKeys; use crate::messages::layout::utility_types::widget_prelude::*; -use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType; use crate::messages::tool::tool_messages::tool_prelude::WidgetCallback; use derivative::*; +use graphene_std::node_graph_overlay::types::FrontendGraphDataType; use graphene_std::vector::style::FillChoice; use graphite_proc_macros::WidgetBuilder; diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index d9a6905997..cc8c6498d6 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1,5 +1,4 @@ use super::node_graph::document_node_definitions; -use super::node_graph::utility_types::Transform; use super::overlays::utility_types::Pivot; use super::utility_types::error::EditorError; use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState}; @@ -17,7 +16,7 @@ use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ}; -use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate, OutputConnector}; +use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::nodes::RawBuffer; use crate::messages::portfolio::utility_types::PanelType; use crate::messages::portfolio::utility_types::PersistentData; @@ -31,6 +30,7 @@ use glam::{DAffine2, DVec2, IVec2}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork}; use graphene_std::math::quad::Quad; +use graphene_std::node_graph_overlay::types::NodeGraphTransform; use graphene_std::path_bool::{boolean_intersect, path_bool_lib}; use graphene_std::raster::BlendMode; use graphene_std::raster_types::Raster; @@ -55,6 +55,7 @@ pub struct DocumentMessageContext<'a> { pub data_panel_open: bool, pub layers_panel_open: bool, pub properties_panel_open: bool, + pub render_native_node_graph: bool, pub viewport: &'a ViewportMessageHandler, } @@ -201,6 +202,7 @@ impl MessageHandler> for DocumentMes data_panel_open, layers_panel_open, properties_panel_open, + render_native_node_graph, } = context; match message { @@ -263,6 +265,7 @@ impl MessageHandler> for DocumentMes preferences, layers_panel_open, viewport, + render_native_node_graph, }, ); } @@ -470,10 +473,10 @@ impl MessageHandler> for DocumentMes DocumentMessage::EnterNestedNetwork { node_id } => { self.breadcrumb_network_path.push(node_id); self.selection_network_path.clone_from(&self.breadcrumb_network_path); - responses.add(NodeGraphMessage::UnloadWires); + responses.add(NodeGraphMessage::UnloadWiresOld); responses.add(NodeGraphMessage::SendGraph); responses.add(DocumentMessage::ZoomCanvasToFitAll); - responses.add(NodeGraphMessage::SetGridAlignedEdges); + responses.add(NodeGraphMessage::UpdateNodeGraphWidth); } DocumentMessage::Escape => { if self.node_graph_handler.drag_start.is_some() { @@ -490,7 +493,7 @@ impl MessageHandler> for DocumentMes responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None }); self.node_graph_handler.wire_in_progress_from_connector = None; self.node_graph_handler.wire_in_progress_to_connector = None; - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); + responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None }); } else if !self.breadcrumb_network_path.is_empty() { // Exit one level up if inside a nested network responses.add(DocumentMessage::ExitNestedNetwork { steps_back: 1 }); @@ -503,10 +506,10 @@ impl MessageHandler> for DocumentMes self.breadcrumb_network_path.pop(); self.selection_network_path.clone_from(&self.breadcrumb_network_path); } - responses.add(NodeGraphMessage::UnloadWires); + responses.add(NodeGraphMessage::UnloadWiresOld); responses.add(NodeGraphMessage::SendGraph); responses.add(DocumentMessage::PTZUpdate); - responses.add(NodeGraphMessage::SetGridAlignedEdges); + responses.add(NodeGraphMessage::UpdateNodeGraphWidth); } DocumentMessage::FlipSelectedLayers { flip_axis } => { let scale = match flip_axis { @@ -560,7 +563,7 @@ impl MessageHandler> for DocumentMes self.graph_view_overlay_open = open; responses.add(FrontendMessage::UpdateGraphViewOverlay { open }); - responses.add(FrontendMessage::UpdateGraphFadeArtwork { + responses.add(FrontendMessage::UpdateGraphFadeArtworkOld { percentage: self.graph_fade_artwork_percentage, }); @@ -570,20 +573,21 @@ impl MessageHandler> for DocumentMes responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderScrollbars); if opened { - responses.add(NodeGraphMessage::UnloadWires); + responses.add(NodeGraphMessage::UnloadWiresOld); } if open { responses.add(ToolMessage::DeactivateTools); responses.add(OverlaysMessage::Draw); // Clear the overlays responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. }); - responses.add(NodeGraphMessage::SetGridAlignedEdges); responses.add(NodeGraphMessage::UpdateGraphBarRight); - responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::UpdateHints); + responses.add(NodeGraphMessage::UpdateEdges); } else { responses.add(ToolMessage::ActivateTool { tool_type: *current_tool }); responses.add(OverlaysMessage::Draw); // Redraw overlays when graph is closed } + + responses.add(NodeGraphMessage::SendGraph); } DocumentMessage::GraphViewOverlayToggle => { responses.add(DocumentMessage::GraphViewOverlay { open: !self.graph_view_overlay_open }); @@ -1196,7 +1200,7 @@ impl MessageHandler> for DocumentMes } responses.add(PropertiesPanelMessage::Refresh); responses.add(NodeGraphMessage::UpdateLayerPanel); - responses.add(NodeGraphMessage::UpdateInSelectedNetwork); + responses.add(NodeGraphMessage::SendGraph); } DocumentMessage::SetBlendModeForSelectedLayers { blend_mode } => { for layer in self.network_interface.selected_nodes().selected_layers_except_artboards(&self.network_interface) { @@ -1205,7 +1209,10 @@ impl MessageHandler> for DocumentMes } DocumentMessage::SetGraphFadeArtwork { percentage } => { self.graph_fade_artwork_percentage = percentage; - responses.add(FrontendMessage::UpdateGraphFadeArtwork { percentage }); + responses.add(FrontendMessage::UpdateGraphFadeArtworkOld { percentage }); + if render_native_node_graph { + responses.add(NodeGraphMessage::SendGraph); + } } DocumentMessage::SetNodePinned { node_id, pinned } => { responses.add(DocumentMessage::AddTransaction); @@ -1507,7 +1514,7 @@ impl MessageHandler> for DocumentMes responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(FrontendMessage::UpdateNodeGraphTransform { - transform: Transform { + transform: NodeGraphTransform { scale: transform.matrix2.x_axis.x, x: transform.translation.x, y: transform.translation.y, @@ -1971,8 +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::SetGridAlignedEdges); + responses.add(NodeGraphMessage::UnloadWiresOld); Some(previous_network) } @@ -2003,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) } @@ -2820,16 +2826,11 @@ impl DocumentMessageHandler { .tooltip("Add an operation to the end of this layer's chain of nodes") .disabled(!has_selection || has_multiple_selection) .popover_layout({ - // Showing only compatible types + // Showing only compatible types for the layer based on the output type of the node upstream from its horizontal input let compatible_type = selected_layer.and_then(|layer| { - let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &self.network_interface); - let node_type = graph_layer.horizontal_layer_flow().nth(1); - if let Some(node_id) = node_type { - let (output_type, _) = self.network_interface.output_type(&OutputConnector::node(node_id, 0), &self.selection_network_path); - Some(format!("type:{}", output_type.nested_type())) - } else { - None - } + self.network_interface + .upstream_output_connector(&InputConnector::node(layer.to_node(), 1), &[]) + .and_then(|upstream_output| self.network_interface.output_type(&upstream_output, &[]).add_node_string()) }); let mut node_chooser = NodeCatalog::new(); diff --git a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs index f40b07d1c9..d22eeda8b1 100644 --- a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs @@ -7,10 +7,12 @@ use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers; use crate::messages::prelude::*; use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode; use glam::{DAffine2, DVec2, IVec2}; +use graph_craft::document::value::TaggedValue; use graph_craft::document::{NodeId, NodeInput}; use graphene_std::Color; use graphene_std::renderer::Quad; use graphene_std::renderer::convert_usvg_path::convert_usvg_path; +use graphene_std::table::Table; use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin}; @@ -140,10 +142,16 @@ impl MessageHandler> for skip_rerender: true, }); } + // Set the bottom input of the artboard back to artboard + let bottom_input = NodeInput::value(TaggedValue::Artboard(Table::new()), true); + network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 0), bottom_input, &[]); } else { // We have some non layers (e.g. just a rectangle node). We disconnect the bottom input and connect it to the left input. network_interface.disconnect_input(&InputConnector::node(artboard_layer.to_node(), 0), &[]); network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 1), primary_input, &[]); + // Set the bottom input of the artboard back to artboard + let bottom_input = NodeInput::value(TaggedValue::Artboard(Table::new()), true); + network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 0), bottom_input, &[]); } } responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] }); diff --git a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs index 80e005448f..8ecc086249 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -303,8 +303,8 @@ impl<'a> ModifyInputsContext<'a> { // If inserting a 'Path' node, insert a 'Flatten Path' node if the type is `Graphic`. // TODO: Allow the 'Path' node to operate on table data by utilizing the reference (index or ID?) for each row. if node_definition.identifier == "Path" { - let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]).0.nested_type().clone(); - if layer_input_type == concrete!(Table) { + let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]); + if layer_input_type.compiled_nested_type() == Some(&concrete!(Table)) { let Some(flatten_path_definition) = resolve_document_node_type("Flatten Path") else { log::error!("Flatten Path does not exist in ModifyInputsContext::existing_node_id"); return None; diff --git a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs index ec1ce9aab5..4161d6f7d5 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -180,7 +180,6 @@ impl MessageHandler> for Navigat NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => { let delta = if use_y_as_x { (-ipp.mouse.scroll_delta.y, 0.).into() } else { -ipp.mouse.scroll_delta.as_dvec2() } * VIEWPORT_SCROLL_RATE; responses.add(NavigationMessage::CanvasPan { delta }); - responses.add(NodeGraphMessage::SetGridAlignedEdges); } NavigationMessage::CanvasTiltResetAndZoomTo100Percent => { let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { @@ -195,7 +194,6 @@ impl MessageHandler> for Navigat responses.add(PortfolioMessage::UpdateDocumentWidgets); } responses.add(DocumentMessage::PTZUpdate); - responses.add(NodeGraphMessage::SetGridAlignedEdges); } NavigationMessage::CanvasTiltSet { angle_radians } => { let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { @@ -274,7 +272,6 @@ impl MessageHandler> for Navigat responses.add(PortfolioMessage::UpdateDocumentWidgets); } responses.add(DocumentMessage::PTZUpdate); - responses.add(NodeGraphMessage::SetGridAlignedEdges); } NavigationMessage::CanvasFlip => { if graph_view_overlay_open { @@ -322,7 +319,6 @@ impl MessageHandler> for Navigat } else { responses.add(PortfolioMessage::UpdateDocumentWidgets); } - responses.add(NodeGraphMessage::SetGridAlignedEdges); // Reset the navigation operation now that it's done self.navigation_operation = NavigationOperation::None; @@ -384,7 +380,6 @@ impl MessageHandler> for Navigat responses.add(PortfolioMessage::UpdateDocumentWidgets); } responses.add(DocumentMessage::PTZUpdate); - responses.add(NodeGraphMessage::SetGridAlignedEdges); } // Fully zooms in on the selected NavigationMessage::FitViewportToSelection => { @@ -479,7 +474,6 @@ impl MessageHandler> for Navigat }; responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom() }); - responses.add(NodeGraphMessage::SetGridAlignedEdges); } } diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs index f9db802ac5..4be5c13378 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions/document_node_derive.rs @@ -6,7 +6,39 @@ use graphene_std::registry::*; use graphene_std::*; use std::collections::HashSet; +/// Traverses a document node template and metadata in parallel to link the protonodes to their reference +fn traverse_node(node: &DocumentNode, node_metadata: &mut DocumentNodePersistentMetadata) { + match &node.implementation { + DocumentNodeImplementation::Network(node_network) => { + for (nested_node_id, nested_node) in node_network.nodes.iter() { + let nested_metadata = node_metadata + .network_metadata + .as_mut() + .expect("Network node must have network metadata") + .persistent_metadata + .node_metadata + .get_mut(nested_node_id) + .expect("Network metadata must have corresponding node id"); + traverse_node(nested_node, &mut nested_metadata.persistent_metadata); + } + } + DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { + if let Some(metadata) = NODE_METADATA.lock().unwrap().get(&proto_node_identifier) { + node_metadata.reference = Some(metadata.display_name.to_string()); + } + } + DocumentNodeImplementation::Extract => {} + } +} + pub(super) fn post_process_nodes(mut custom: Vec) -> Vec { + NODE_METADATA.lock().unwrap().keys().for_each(|key| log::debug!("{key:?}")); + + // Link the protonodes with custom networks to their reference + for node in custom.iter_mut() { + traverse_node(&node.node_template.document_node, &mut node.node_template.persistent_node_metadata); + } + // Remove struct generics for DocumentNodeDefinition { node_template, .. } in custom.iter_mut() { let NodeTemplate { @@ -20,7 +52,6 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec } }; } - let node_registry = NODE_REGISTRY.lock().unwrap(); 'outer: for (id, metadata) in NODE_METADATA.lock().unwrap().iter() { for node in custom.iter() { @@ -32,7 +63,10 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec .. } = node; match implementation { - DocumentNodeImplementation::ProtoNode(name) if name == id => continue 'outer, + DocumentNodeImplementation::ProtoNode(name) if name == id => { + log::debug!("skipping defintion {name}"); + continue 'outer; + } _ => (), } } @@ -55,6 +89,8 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec let output_type = &first_node_io.return_value; let inputs = preprocessor::node_inputs(fields, first_node_io); + log::debug!("generating definition for {display_name:?}"); + let node = DocumentNodeDefinition { identifier: display_name, node_template: NodeTemplate { @@ -91,5 +127,6 @@ pub(super) fn post_process_nodes(mut custom: Vec) -> Vec custom.push(node); } + custom.iter().for_each(|def| log::debug!("{:?}", def.identifier)); custom } diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs index 196b28ecff..6ab38ac1f9 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs @@ -113,6 +113,7 @@ pub enum NodeGraphMessage { shift: Key, }, ShakeNode, + UpdateNodeGraphWidth, RemoveImport { import_index: usize, }, @@ -140,11 +141,12 @@ pub enum NodeGraphMessage { }, SendClickTargets, EndSendClickTargets, - UnloadWires, - SendWires, - UpdateVisibleNodes, + UnloadWiresOld, + SendWiresOld, + UpdateVisibleNodesOld, SendGraph, - SetGridAlignedEdges, + SendGraphOld, + SendGraphNew, SetInputValue { node_id: NodeId, input_index: usize, @@ -232,7 +234,7 @@ pub enum NodeGraphMessage { }, UpdateActionButtons, UpdateGraphBarRight, - UpdateInSelectedNetwork, + UpdateInSelectedNetworkOld, UpdateHints, - SendSelectedNodes, + SendSelectedNodesOld, } diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 379da12328..bd9e7fce3b 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -1,4 +1,4 @@ -use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode}; +use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart}; use super::{document_node_definitions, node_properties}; use crate::consts::GRID_SIZE; use crate::messages::input_mapper::utility_types::macros::action_keys; @@ -6,25 +6,25 @@ 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}; +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, TypeSource, + 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::{self, get_clip_mode}; +use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode; use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed; use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion}; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; -use crate::messages::viewport::{Position, Rect}; +use crate::messages::viewport::Position; use glam::{DAffine2, DVec2, IVec2}; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; -use graph_craft::proto::GraphErrors; 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}; @@ -45,13 +45,13 @@ 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)] pub struct NodeGraphMessageHandler { // TODO: Remove network and move to NodeNetworkInterface pub network: Vec, - pub node_graph_errors: GraphErrors, has_selection: bool, widgets: [LayoutGroup; 2], /// Used to add a transaction for the first node move when dragging. @@ -94,9 +94,9 @@ pub struct NodeGraphMessageHandler { /// The end index of the moved connector end_index: Option, /// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend - frontend_nodes: Vec, + frontend_nodes_old: Vec, /// Used to keep track of what wires are sent to the front end so the old ones can be removed - frontend_wires: HashSet<(NodeId, usize)>, + frontend_wires_old: HashSet<(NodeId, usize)>, } /// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network. @@ -116,6 +116,7 @@ impl<'a> MessageHandler> for NodeG preferences, layers_panel_open, viewport, + render_native_node_graph, } = context; match message { @@ -195,7 +196,7 @@ impl<'a> MessageHandler> for NodeG responses.add(MenuBarMessage::SendLayout); responses.add(NodeGraphMessage::UpdateLayerPanel); responses.add(PropertiesPanelMessage::Refresh); - responses.add(NodeGraphMessage::SendSelectedNodes); + responses.add(NodeGraphMessage::SendSelectedNodesOld); responses.add(ArtboardToolMessage::UpdateSelectedArtboard); responses.add(DocumentMessage::DocumentStructureChanged); responses.add(OverlaysMessage::Draw); @@ -300,7 +301,7 @@ impl<'a> MessageHandler> for NodeG self.wire_in_progress_type = FrontendGraphDataType::General; self.wire_in_progress_to_connector = None; } - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); + responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None }); responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: self.context_menu.clone(), }); @@ -473,7 +474,7 @@ impl<'a> MessageHandler> for NodeG } responses.add(NodeGraphMessage::UpdateImportsExports); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendWiresOld); } NodeGraphMessage::ExposePrimaryExport { exposed } => { let export_connector: InputConnector = InputConnector::Export(0); @@ -764,7 +765,7 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::SelectedNodesSet { nodes: self.selection_before_pointer_down.clone(), }); - responses.add(FrontendMessage::UpdateBox { box_selection: None }); + responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None }); return; } // Abort dragging a wire @@ -773,13 +774,18 @@ impl<'a> MessageHandler> for NodeG self.wire_in_progress_type = FrontendGraphDataType::General; self.wire_in_progress_to_connector = None; responses.add(DocumentMessage::AbortTransaction); - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); + responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None }); return; } let context_menu_data = if let Some(node_id) = clicked_id { - let currently_is_node = !network_interface.is_layer(&node_id, selection_network_path); - ContextMenuData::ToggleLayer { node_id, currently_is_node } + let currently_is_node = !network_interface.is_layer(&node_id, breadcrumb_network_path); + let can_be_layer = network_interface.is_eligible_to_be_layer(&node_id, breadcrumb_network_path); + ContextMenuData::ModifyNode { + can_be_layer, + currently_is_node, + node_id, + } } else { ContextMenuData::CreateNode { compatible_type: None } }; @@ -795,10 +801,9 @@ impl<'a> MessageHandler> for NodeG DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x }; - let context_menu_coordinates = ((node_graph_point.x + node_graph_shift.x) as i32, (node_graph_point.y + node_graph_shift.y) as i32); - + let context_menu_coordinates = node_graph_point + node_graph_shift; self.context_menu = Some(ContextMenuInformation { - context_menu_coordinates, + context_menu_coordinates: context_menu_coordinates.into(), context_menu_data, }); @@ -854,7 +859,7 @@ impl<'a> MessageHandler> for NodeG responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: self.context_menu.clone(), }); - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); + responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None }); } // Toggle visibility of clicked node and return @@ -881,7 +886,7 @@ impl<'a> MessageHandler> for NodeG }; let Some(output_connector) = output_connector else { return }; self.wire_in_progress_from_connector = network_interface.output_position(&output_connector, selection_network_path); - self.wire_in_progress_type = FrontendGraphDataType::from_type(&network_interface.input_type(clicked_input, breadcrumb_network_path).0); + self.wire_in_progress_type = network_interface.input_type(clicked_input, breadcrumb_network_path).displayed_type(); return; } @@ -891,8 +896,8 @@ impl<'a> MessageHandler> for NodeG self.initial_disconnecting = false; self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path); - let (output_type, source) = &network_interface.output_type(&clicked_output, breadcrumb_network_path); - self.wire_in_progress_type = FrontendGraphDataType::displayed_type(output_type, source); + let output_type = network_interface.output_type(&clicked_output, breadcrumb_network_path); + self.wire_in_progress_type = output_type.displayed_type(); self.update_node_graph_hints(responses); return; @@ -1061,14 +1066,14 @@ impl<'a> MessageHandler> for NodeG to_connector_is_layer, GraphWireStyle::Direct, ); - let path_string = vector_wire.to_svg(); - let wire_path = WirePath { - path_string, + let wire_path = WirePathInProgress { + wire: vector_wire.to_svg(), data_type: self.wire_in_progress_type, thick: false, - dashed: false, }; - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) }); + responses.add(FrontendMessage::UpdateWirePathInProgress { + wire_path_in_progress: Some(wire_path), + }); } } else if let Some((drag_start, dragged)) = &mut self.drag_start { if drag_start.start_x != point.x || drag_start.start_y != point.y { @@ -1210,23 +1215,20 @@ impl<'a> MessageHandler> for NodeG } // Get the output types from the network interface - let (output_type, type_source) = network_interface.output_type(&output_connector, selection_network_path); let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { warn!("No network_metadata"); return; }; - let compatible_type = match type_source { - TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None, - _ => Some(format!("type:{}", output_type.nested_type())), - }; - let appear_right_of_mouse = if ipp.mouse.position.x > viewport.size().y() - 173. { -173. } else { 0. }; let appear_above_mouse = if ipp.mouse.position.y > viewport.size().y() - 34. { -34. } else { 0. }; let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x; + let compatible_type = network_interface.output_type(&output_connector, selection_network_path).add_node_string(); + let context_menu_coordinates = point + node_graph_shift; + self.context_menu = Some(ContextMenuInformation { - context_menu_coordinates: ((point.x + node_graph_shift.x) as i32, (point.y + node_graph_shift.y) as i32), + context_menu_coordinates: context_menu_coordinates.into(), context_menu_data: ContextMenuData::CreateNode { compatible_type }, }); @@ -1322,7 +1324,8 @@ impl<'a> MessageHandler> for NodeG return None; } - let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?; + let wire = network_interface.wire_from_input_new(&input, preferences.graph_wire_style, selection_network_path)?; + let thick = network_interface.wire_is_thick(&input, selection_network_path); let node_bbox = kurbo::Rect::new(node_bbox[0].x, node_bbox[0].y, node_bbox[1].x, node_bbox[1].y).to_path(DEFAULT_ACCURACY); let inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None); @@ -1331,7 +1334,7 @@ impl<'a> MessageHandler> for NodeG .segments() .any(|segment| node_bbox.segments().filter_map(|segment| segment.as_line()).any(|line| !segment.intersect_line(line).is_empty())); - (intersect || inside).then_some((input, is_stack)) + (intersect || inside).then_some((input, thick)) }) .collect::>(); @@ -1398,8 +1401,8 @@ impl<'a> MessageHandler> for NodeG self.reordering_export = None; self.reordering_import = None; responses.add(DocumentMessage::EndTransaction); - responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); - responses.add(FrontendMessage::UpdateBox { box_selection: None }); + responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None }); + responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None }); responses.add(FrontendMessage::UpdateImportReorderIndex { index: None }); responses.add(FrontendMessage::UpdateExportReorderIndex { index: None }); self.update_node_graph_hints(responses); @@ -1542,6 +1545,10 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::SendGraph); } + NodeGraphMessage::UpdateNodeGraphWidth => { + network_interface.set_node_graph_width(viewport.size().x(), breadcrumb_network_path); + responses.add(NodeGraphMessage::UpdateImportsExports); + } NodeGraphMessage::RemoveImport { import_index: usize } => { network_interface.remove_import(usize, selection_network_path); responses.add(NodeGraphMessage::UpdateImportsExports); @@ -1598,18 +1605,18 @@ impl<'a> MessageHandler> for NodeG click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)), }), NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }), - NodeGraphMessage::UnloadWires => { + NodeGraphMessage::UnloadWiresOld => { for input in network_interface.node_graph_input_connectors(breadcrumb_network_path) { - network_interface.unload_wire(&input, breadcrumb_network_path); + network_interface.unload_wire_old(&input, breadcrumb_network_path); } - responses.add(FrontendMessage::ClearAllNodeGraphWires); + responses.add(FrontendMessage::ClearAllNodeGraphWiresOld); } - NodeGraphMessage::SendWires => { - let wires = self.collect_wires(network_interface, preferences.graph_wire_style, breadcrumb_network_path); - responses.add(FrontendMessage::UpdateNodeGraphWires { wires }); + NodeGraphMessage::SendWiresOld => { + let wires = self.collect_wires_old(network_interface, preferences.graph_wire_style, breadcrumb_network_path); + responses.add(FrontendMessage::UpdateNodeGraphWiresOld { wires }); } - NodeGraphMessage::UpdateVisibleNodes => { + NodeGraphMessage::UpdateVisibleNodesOld => { let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { return; }; @@ -1618,7 +1625,7 @@ impl<'a> MessageHandler> for NodeG let document_bbox: [DVec2; 2] = viewport_bbox.map(|p| network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(p)); let mut nodes = Vec::new(); - for node_id in &self.frontend_nodes { + for node_id in &self.frontend_nodes_old { let Some(node_bbox) = network_interface.node_bounding_box(node_id, breadcrumb_network_path) else { log::error!("Could not get bbox for node: {node_id:?}"); continue; @@ -1627,44 +1634,66 @@ impl<'a> MessageHandler> for NodeG if node_bbox[1].x >= document_bbox[0].x && node_bbox[0].x <= document_bbox[1].x && node_bbox[1].y >= document_bbox[0].y && node_bbox[0].y <= document_bbox[1].y { nodes.push(*node_id); } - for error in &self.node_graph_errors { + for error in &network_interface.resolved_types.node_graph_errors { if error.node_path.contains(node_id) { nodes.push(*node_id); } } } - 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 (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths(breadcrumb_network_path); + 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_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::SetGridAlignedEdges => { - if graph_view_overlay_open { - let viewport_bounds = viewport.bounds(); - network_interface.set_grid_aligned_edges(DVec2::new(viewport_bounds.x() - viewport_bounds.width(), 0.), breadcrumb_network_path); - // Send the new edges to the frontend - responses.add(NodeGraphMessage::UpdateImportsExports); - } + 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); @@ -1732,7 +1761,7 @@ impl<'a> MessageHandler> for NodeG } } - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendWiresOld); } NodeGraphMessage::ToggleSelectedAsLayersOrNodes => { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else { @@ -1753,7 +1782,7 @@ impl<'a> MessageHandler> for NodeG NodeGraphMessage::ShiftNodePosition { node_id, x, y } => { network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendWiresOld); } NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => { if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) { @@ -1767,7 +1796,7 @@ impl<'a> MessageHandler> for NodeG }); responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::SendGraph); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendWiresOld); } NodeGraphMessage::SetDisplayName { node_id, @@ -1956,23 +1985,31 @@ impl<'a> MessageHandler> for NodeG nodes: nodes.into_iter().collect::>(), }); } - responses.add(FrontendMessage::UpdateBox { box_selection }) + responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection }) } } NodeGraphMessage::UpdateImportsExports => { - let imports = network_interface.frontend_imports(breadcrumb_network_path); - let exports = network_interface.frontend_exports(breadcrumb_network_path); + let imports = network_interface.frontend_imports(preferences.graph_wire_style, breadcrumb_network_path); + let exports = network_interface.frontend_exports(preferences.graph_wire_style, breadcrumb_network_path); let Some((import_position, export_position)) = network_interface.import_export_position(breadcrumb_network_path) else { log::error!("Could not get import export positions"); return; }; + let import_position = FrontendXY { + x: import_position.x, + y: import_position.y, + }; + let export_position = FrontendXY { + x: export_position.x, + y: export_position.y, + }; + // Do not show the add import or add export button in the document network; let add_import_export = !breadcrumb_network_path.is_empty(); - responses.add(NodeGraphMessage::UpdateVisibleNodes); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::UpdateVisibleNodesOld); responses.add(FrontendMessage::UpdateImportsExports { imports, exports, @@ -1980,6 +2017,7 @@ impl<'a> MessageHandler> for NodeG export_position, add_import_export, }); + responses.add(NodeGraphMessage::SendWiresOld) } NodeGraphMessage::UpdateLayerPanel => { @@ -1999,13 +2037,7 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::SendGraph); } NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => { - for (path, node_type) in resolved_types.add { - network_interface.resolved_types.types.insert(path.to_vec(), node_type); - } - for path in resolved_types.remove { - network_interface.resolved_types.types.remove(&path.to_vec()); - } - self.node_graph_errors = node_graph_errors; + network_interface.resolved_types.update(resolved_types, node_graph_errors); } NodeGraphMessage::UpdateActionButtons => { if selection_network_path == breadcrumb_network_path { @@ -2017,19 +2049,19 @@ impl<'a> MessageHandler> for NodeG self.update_graph_bar_right(graph_fade_artwork_percentage, network_interface, breadcrumb_network_path, navigation_handler); self.send_node_bar_layout(responses); } - NodeGraphMessage::UpdateInSelectedNetwork => responses.add(FrontendMessage::UpdateInSelectedNetwork { + NodeGraphMessage::UpdateInSelectedNetworkOld => responses.add(FrontendMessage::UpdateInSelectedNetworkOld { in_selected_network: selection_network_path == breadcrumb_network_path, }), NodeGraphMessage::UpdateHints => { self.update_node_graph_hints(responses); } - NodeGraphMessage::SendSelectedNodes => { + NodeGraphMessage::SendSelectedNodesOld => { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(breadcrumb_network_path) else { log::error!("Could not get selected nodes in NodeGraphMessage::SendSelectedNodes"); return; }; responses.add(NodeGraphMessage::UpdateActionButtons); - responses.add(FrontendMessage::UpdateNodeGraphSelection { + responses.add(FrontendMessage::UpdateNodeGraphSelectionOld { selected: selected_nodes.selected_nodes().cloned().collect::>(), }); } @@ -2118,16 +2150,7 @@ impl NodeGraphMessageHandler { .popover_layout({ // Showing only compatible types let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) { - (true, false, Some(layer)) => { - let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface); - let node_type = graph_layer.horizontal_layer_flow().nth(1); - if let Some(node_id) = node_type { - let (output_type, _) = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); - Some(format!("type:{}", output_type.nested_type())) - } else { - None - } - } + (true, false, Some(layer)) => network_interface.output_type(&OutputConnector::node(layer.to_node(), 1), &[]).add_node_string(), _ => None, }; @@ -2440,17 +2463,10 @@ impl NodeGraphMessageHandler { .icon(Some("Node".to_string())) .tooltip("Add an operation to the end of this layer's chain of nodes") .popover_layout({ - let layer_identifier = LayerNodeIdentifier::new(layer, context.network_interface); - let compatible_type = { - let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer_identifier, context.network_interface); - let node_type = graph_layer.horizontal_layer_flow().nth(1); - if let Some(node_id) = node_type { - let (output_type, _) = context.network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); - Some(format!("type:{}", output_type.nested_type())) - } else { - None - } - }; + let compatible_type = context + .network_interface + .upstream_output_connector(&InputConnector::node(layer, 1), &[]) + .and_then(|upstream_output| context.network_interface.output_type(&upstream_output, &[]).add_node_string()); let mut node_chooser = NodeCatalog::new(); node_chooser.intial_search = compatible_type.unwrap_or("".to_string()); @@ -2497,7 +2513,7 @@ impl NodeGraphMessageHandler { } } - fn collect_wires(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec { + fn collect_wires_old(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec { let mut added_wires = network_interface .node_graph_input_connectors(breadcrumb_network_path) .iter() @@ -2505,24 +2521,24 @@ impl NodeGraphMessageHandler { .collect::>(); let changed_wire_inputs = added_wires.iter().map(|update| (update.id, update.input_index)).collect::>(); - self.frontend_wires.extend(changed_wire_inputs); + self.frontend_wires_old.extend(changed_wire_inputs); - let mut orphaned_wire_inputs = self.frontend_wires.clone(); - self.frontend_wires = network_interface + let mut orphaned_wire_inputs = self.frontend_wires_old.clone(); + self.frontend_wires_old = network_interface .node_graph_wire_inputs(breadcrumb_network_path) .iter() .filter_map(|visible_wire_input| orphaned_wire_inputs.take(visible_wire_input)) .collect::>(); - added_wires.extend(orphaned_wire_inputs.into_iter().map(|(id, input_index)| WirePathUpdate { + added_wires.extend(orphaned_wire_inputs.into_iter().map(|(id, input_index)| WirePathUpdateOld { id, input_index, wire_path_update: None, })); - if let Some(wire_to_root) = network_interface.wire_to_root(graph_wire_style, breadcrumb_network_path) { + if let Some(wire_to_root) = network_interface.wire_to_root_old(graph_wire_style, breadcrumb_network_path) { added_wires.push(wire_to_root); } else { - added_wires.push(WirePathUpdate { + added_wires.push(WirePathUpdateOld { id: NodeId(u64::MAX), input_index: u32::MAX as usize, wire_path_update: None, @@ -2532,33 +2548,31 @@ impl NodeGraphMessageHandler { added_wires } - fn collect_nodes(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec { + fn collect_nodes_old(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec { let Some(network) = network_interface.nested_network(breadcrumb_network_path) else { log::error!("Could not get nested network when collecting nodes"); return Vec::new(); }; let mut nodes = Vec::new(); for (node_id, visible) in network.nodes.iter().map(|(node_id, node)| (*node_id, node.visible)).collect::>() { - let node_id_path = [breadcrumb_network_path, &[node_id]].concat(); - let primary_input_connector = InputConnector::node(node_id, 0); let primary_input = if network_interface .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) { ( @@ -2582,20 +2596,7 @@ impl NodeGraphMessageHandler { let locked = network_interface.is_locked(&node_id, breadcrumb_network_path); - let errors = self - .node_graph_errors - .iter() - .find(|error| error.node_path == node_id_path) - .map(|error| format!("{:?}", error.error.clone())) - .or_else(|| { - if self.node_graph_errors.iter().any(|error| error.node_path.starts_with(&node_id_path)) { - Some("Node graph type error within this node".to_string()) - } else { - None - } - }); - - nodes.push(FrontendNode { + nodes.push(FrontendNodeOld { id: node_id, is_layer: network_interface .node_metadata(&node_id, breadcrumb_network_path) @@ -2613,7 +2614,6 @@ impl NodeGraphMessageHandler { previewed, visible, locked, - errors, }); } @@ -2635,6 +2635,28 @@ impl NodeGraphMessageHandler { Some(subgraph_names) } + fn node_graph_error(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Option { + let graph_error = network_interface + .resolved_types + .node_graph_errors + .iter() + .filter(|error| error.node_path.starts_with(breadcrumb_network_path) && error.node_path.len() > breadcrumb_network_path.len()) + .next()?; + let error = if graph_error.node_path.len() == breadcrumb_network_path.len() + 1 { + format!("{:?}", graph_error.error) + } else { + "Node graph type error within this node".to_string() + }; + let error_node = graph_error.node_path[breadcrumb_network_path.len()]; + let mut position = network_interface.position(&error_node, breadcrumb_network_path)?; + // Convert to graph space + position *= 24; + if network_interface.is_layer(&error_node, breadcrumb_network_path) { + position += IVec2::new(12, -12) + } + Some(NodeGraphError { position: position.into(), error }) + } + fn update_layer_panel(network_interface: &NodeNetworkInterface, selection_network_path: &[NodeId], collapsed: &CollapsedLayers, layers_panel_open: bool, responses: &mut VecDeque) { if !layers_panel_open { return; @@ -2771,7 +2793,6 @@ impl Default for NodeGraphMessageHandler { fn default() -> Self { Self { network: Vec::new(), - node_graph_errors: Vec::new(), has_selection: false, widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: Vec::new() }], drag_start: None, @@ -2794,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(), } } } @@ -2803,7 +2824,6 @@ impl Default for NodeGraphMessageHandler { impl PartialEq for NodeGraphMessageHandler { fn eq(&self, other: &Self) -> bool { self.network == other.network - && self.node_graph_errors == other.node_graph_errors && self.has_selection == other.has_selection && self.widgets == other.widgets && self.drag_start == other.drag_start diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index 184df8bf34..7365bb88c5 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -1,19 +1,19 @@ #![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::*; use choice::enum_choice; use dyn_any::DynAny; use glam::{DAffine2, DVec2}; -use graph_craft::Type; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput}; +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::{ @@ -85,7 +85,7 @@ pub fn start_widgets(parameter_widgets_info: ParameterWidgetsInfo) -> Vec Vec().for_socket(parameter_info).property_row(); let document_node = match get_document_node(node_id, context) { @@ -1185,7 +1185,7 @@ pub(crate) fn selective_color_properties(node_id: NodeId, context: &mut NodeProp use graphene_std::raster::selective_color::*; let mut default_info = ParameterWidgetsInfo::new(node_id, ColorsInput::INDEX, true, context); - default_info.exposeable = false; + default_info.exposable = false; let colors = enum_choice::().for_socket(default_info).property_row(); let document_node = match get_document_node(node_id, context) { @@ -1574,6 +1574,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper let mut display_decimal_places = None; let mut step = None; let mut unit_suffix = None; + let input_type = match implementation { DocumentNodeImplementation::ProtoNode(proto_node_identifier) => 'early_return: { if let Some(field) = graphene_std::registry::NODE_METADATA @@ -1610,7 +1611,12 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper input_type.clone() } - _ => context.network_interface.input_type(&InputConnector::node(node_id, input_index), context.selection_network_path).0, + _ => context + .network_interface + .input_type(&InputConnector::node(node_id, input_index), context.selection_network_path) + .compiled_nested_type() + .cloned() + .unwrap_or(concrete!(())), }; property_from_type(node_id, input_index, &input_type, number_options, unit_suffix, display_decimal_places, step, context).unwrap_or_else(|value| value) @@ -1989,13 +1995,16 @@ pub struct ParameterWidgetsInfo<'a> { description: String, input_type: FrontendGraphDataType, blank_assist: bool, - exposeable: bool, + exposable: bool, } impl<'a> ParameterWidgetsInfo<'a> { pub fn new(node_id: NodeId, index: usize, blank_assist: bool, context: &'a mut NodePropertiesContext) -> ParameterWidgetsInfo<'a> { let (name, description) = context.network_interface.displayed_input_name_and_description(&node_id, index, context.selection_network_path); - let input_type = FrontendGraphDataType::from_type(&context.network_interface.input_type(&InputConnector::node(node_id, index), context.selection_network_path).0); + let input_type = context + .network_interface + .input_type(&InputConnector::node(node_id, index), context.selection_network_path) + .displayed_type(); let document_node = context.network_interface.document_node(&node_id, context.selection_network_path); ParameterWidgetsInfo { @@ -2006,7 +2015,7 @@ impl<'a> ParameterWidgetsInfo<'a> { description, input_type, blank_assist, - exposeable: true, + exposable: true, } } } diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs index c22df85c39..5458247248 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -1,57 +1,10 @@ -use crate::messages::portfolio::document::utility_types::network_interface::TypeSource; 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, -} - -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, - } - } - - pub fn displayed_type(input: &Type, type_source: &TypeSource) -> Self { - match type_source { - TypeSource::Error(_) | TypeSource::RandomProtonodeImplementation => Self::General, - _ => Self::from_type(input), - } - } -} - -#[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, @@ -65,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, @@ -79,8 +32,8 @@ pub struct FrontendGraphOutput { pub connected_to: Vec, } -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendNode { +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendNodeOld { pub id: graph_craft::document::NodeId, #[serde(rename = "isLayer")] pub is_layer: bool, @@ -90,13 +43,13 @@ pub struct FrontendNode { #[serde(rename = "displayName")] pub display_name: String, #[serde(rename = "primaryInput")] - pub primary_input: Option, + pub primary_input: Option, #[serde(rename = "exposedInputs")] - pub exposed_inputs: Vec, + pub exposed_inputs: Vec, #[serde(rename = "primaryOutput")] - pub primary_output: Option, + pub primary_output: Option, #[serde(rename = "exposedOutputs")] - pub exposed_outputs: Vec, + pub exposed_outputs: Vec, #[serde(rename = "primaryOutputConnectedToLayer")] pub primary_output_connected_to_layer: bool, #[serde(rename = "primaryInputConnectedToLayer")] @@ -105,7 +58,26 @@ pub struct FrontendNode { pub visible: bool, pub locked: bool, pub previewed: bool, - pub errors: Option, +} + +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendExport { + pub port: FrontendGraphInputNew, + pub wire: Option, +} + +#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendExports { + /// If the primary export is not visible, then it is None. + pub exports: Vec>, + #[serde(rename = "previewWire")] + pub preview_wire: Option, +} + +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendImport { + pub port: FrontendGraphOutputNew, + pub wires: Vec, } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] @@ -161,16 +133,18 @@ pub struct BoxSelection { } #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +#[serde(tag = "type", content = "data")] pub enum ContextMenuData { - ToggleLayer { - #[serde(rename = "nodeId")] - node_id: NodeId, + ModifyNode { + #[serde(rename = "canBeLayer")] + can_be_layer: bool, #[serde(rename = "currentlyIsNode")] currently_is_node: bool, + #[serde(rename = "nodeId")] + node_id: NodeId, }, CreateNode { #[serde(rename = "compatibleType")] - #[serde(default)] compatible_type: Option, }, } @@ -179,7 +153,7 @@ pub enum ContextMenuData { pub struct ContextMenuInformation { // Stores whether the context menu is open and its position in graph coordinates #[serde(rename = "contextMenuCoordinates")] - pub context_menu_coordinates: (i32, i32), + pub context_menu_coordinates: FrontendXY, #[serde(rename = "contextMenuData")] pub context_menu_data: ContextMenuData, } @@ -196,8 +170,6 @@ pub struct FrontendClickTargets { pub icon_click_targets: Vec, #[serde(rename = "allNodesBoundingBox")] pub all_nodes_bounding_box: String, - #[serde(rename = "importExportsBoundingBox")] - pub import_exports_bounding_box: String, #[serde(rename = "modifyImportExport")] pub modify_import_export: Vec, } @@ -209,3 +181,9 @@ pub enum Direction { Left, Right, } + +#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct NodeGraphError { + pub position: FrontendXY, + pub error: String, +} diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 2920cc360e..d2dd75b31f 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -1,36 +1,35 @@ 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::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire}; +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::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; use glam::{DAffine2, DVec2, IVec2}; +use graph_craft::Type; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork}; -use graph_craft::{Type, concrete}; -use graphene_std::Artboard; use graphene_std::ContextDependencies; use graphene_std::math::quad::Quad; use graphene_std::subpath::Subpath; -use graphene_std::table::Table; use graphene_std::transform::Footprint; use graphene_std::vector::click_target::{ClickTarget, ClickTargetType}; use graphene_std::vector::{PointId, Vector, VectorModificationType}; -use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes; -use interpreted_executor::node_registry::NODE_REGISTRY; -use kurbo::BezPath; use memo_network::MemoNetwork; use serde_json::{Value, json}; use std::collections::{HashMap, HashSet, VecDeque}; -use std::hash::{DefaultHasher, Hash, Hasher}; +use std::hash::Hash; use std::ops::Deref; /// All network modifications should be done through this API, so the fields cannot be public. However, all fields within this struct can be public since it it not possible to have a public mutable reference. @@ -238,31 +237,6 @@ impl NodeNetworkInterface { layers } - pub fn chain_width(&self, node_id: &NodeId, network_path: &[NodeId]) -> u32 { - if self.number_of_displayed_inputs(node_id, network_path) > 1 { - let mut last_chain_node_distance = 0u32; - // Iterate upstream from the layer, and get the number of nodes distance to the last node with Position::Chain - for (index, node_id) in self - .upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalPrimaryOutputFlow) - .skip(1) - .enumerate() - .collect::>() - { - // Check if the node is positioned as a chain - if self.is_chain(&node_id, network_path) { - last_chain_node_distance = (index as u32) + 1; - } else { - return last_chain_node_distance * 7 + 1; - } - } - - last_chain_node_distance * 7 + 1 - } else { - // Layer with no inputs has no chain - 0 - } - } - /// Check if the specified node id is connected to the output pub fn connected_to_output(&self, target_node_id: &NodeId, network_path: &[NodeId]) -> bool { let Some(network) = self.nested_network(network_path) else { @@ -458,12 +432,12 @@ impl NodeNetworkInterface { *input = NodeInput::Node { node_id: new_id, output_index }; } else { // Disconnect node input if it is not connected to another node in new_ids - let tagged_value = TaggedValue::from_type_or_none(&self.input_type(&InputConnector::node(*node_id, input_index), network_path).0); + let tagged_value = self.tagged_value_from_input(&InputConnector::node(*node_id, input_index), network_path); *input = NodeInput::value(tagged_value, true); } } else if let &mut NodeInput::Import { .. } = input { // Always disconnect network node input - let tagged_value = TaggedValue::from_type_or_none(&self.input_type(&InputConnector::node(*node_id, input_index), network_path).0); + let tagged_value = self.tagged_value_from_input(&InputConnector::node(*node_id, input_index), network_path); *input = NodeInput::value(tagged_value, true); } } @@ -471,8 +445,8 @@ impl NodeNetworkInterface { } /// Try and get the [`DocumentNodeDefinition`] for a node - pub fn get_node_definition(&self, network_path: &[NodeId], node_id: NodeId) -> Option<&DocumentNodeDefinition> { - let metadata = self.node_metadata(&node_id, network_path)?; + pub fn get_node_definition(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeDefinition> { + let metadata = self.node_metadata(node_id, network_path)?; resolve_document_node_type(metadata.persistent_metadata.reference.as_ref()?) } @@ -493,278 +467,6 @@ impl NodeNetworkInterface { } } - /// Try and get the [`Type`] for any [`InputConnector`] based on the `self.resolved_types`. - fn node_type_from_compiled(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<(Type, TypeSource)> { - let (node_id, input_index) = match *input_connector { - InputConnector::Node { node_id, input_index } => (node_id, input_index), - InputConnector::Export(export_index) => { - let Some((encapsulating_node_id, encapsulating_node_id_path)) = network_path.split_last() else { - // The outermost network export defaults to a Table. - return Some((concrete!(Table), TypeSource::OuterMostExportDefault)); - }; - - let output_type = self.output_type(&OutputConnector::node(*encapsulating_node_id, export_index), encapsulating_node_id_path); - return Some(output_type); - } - }; - let Some(node) = self.document_node(&node_id, network_path) else { - log::error!("Could not get node {node_id} in input_type"); - return None; - }; - // If the input_connector is a NodeInput::Value, return the type of the tagged value. - if let Some(value) = node.inputs.get(input_index).and_then(|input| input.as_value()) { - return Some((value.ty(), TypeSource::TaggedValue)); - } - let node_id_path = [network_path, &[node_id]].concat(); - match &node.implementation { - DocumentNodeImplementation::Network(_nested_network) => { - // Attempt to resolve where this import is within the nested network (it may be connected to the node or directly to an export) - let outwards_wires = self.outward_wires(&node_id_path); - let inputs_using_import = outwards_wires.and_then(|outwards_wires| outwards_wires.get(&OutputConnector::Import(input_index))); - let first_input = inputs_using_import.and_then(|input| input.first()).copied(); - - if inputs_using_import.is_some_and(|inputs| inputs.len() > 1) { - warn!("Found multiple inputs using an import. Using the type of the first one."); - } - - if let Some(input_connector) = first_input { - self.node_type_from_compiled(&input_connector, &node_id_path) - } - // Nothing is connected to the import - else { - None - } - } - DocumentNodeImplementation::ProtoNode(_) => { - // Offset the input index by 1 since the proto node also includes the type of the input passed as a call argument. - self.resolved_types - .types - .get(node_id_path.as_slice()) - .and_then(|node_types| node_types.inputs.get(input_index + 1).cloned()) - .map(|node_types| (node_types, TypeSource::Compiled)) - } - DocumentNodeImplementation::Extract => None, - } - } - - /// Guess the type from the node based on a document node default or a random protonode definition. - fn guess_type_from_node(&mut self, network_path: &mut Vec, node_id: NodeId, input_index: usize) -> (Type, TypeSource) { - // Try and get the default value from the document node definition - if let Some(value) = self - .get_node_definition(network_path, node_id) - .and_then(|definition| definition.node_template.document_node.inputs.get(input_index)) - .and_then(|input| input.as_value()) - { - return (value.ty(), TypeSource::DocumentNodeDefault); - } - - let Some(node) = self.document_node(&node_id, network_path) else { - return (concrete!(()), TypeSource::Error("node id {node_id:?} not in network {network_path:?}")); - }; - - let node_id_path = [network_path.as_slice(), &[node_id]].concat(); - match &node.implementation { - DocumentNodeImplementation::ProtoNode(protonode) => { - let Some(node_types) = random_protonode_implementation(protonode) else { - return (concrete!(()), TypeSource::Error("could not resolve protonode")); - }; - - let skip_footprint = 1; - - let Some(input_type) = std::iter::once(node_types.call_argument.clone()).chain(node_types.inputs.clone()).nth(input_index + skip_footprint) else { - // log::warn!("Could not get type for {node_id_path:?}, input: {input_index}"); - return (concrete!(()), TypeSource::Error("could not get the protonode's input")); - }; - - (input_type, TypeSource::RandomProtonodeImplementation) - } - DocumentNodeImplementation::Network(_network) => { - // Attempt to resolve where this import is within the nested network - let outwards_wires = self.outward_wires(&node_id_path); - let inputs_using_import = outwards_wires.and_then(|outwards_wires| outwards_wires.get(&OutputConnector::Import(input_index))); - let first_input = inputs_using_import.and_then(|input| input.first()).copied(); - - if let Some(InputConnector::Node { - node_id: child_id, - input_index: child_input_index, - }) = first_input - { - network_path.push(node_id); - let result = self.guess_type_from_node(network_path, child_id, child_input_index); - network_path.pop(); - return result; - } - - // Input is disconnected - (concrete!(()), TypeSource::Error("disconnected network input")) - } - _ => (concrete!(()), TypeSource::Error("implementation is not network or protonode")), - } - } - - /// Get the [`Type`] for any InputConnector - pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> (Type, TypeSource) { - if let Some(result) = self.node_type_from_compiled(input_connector, network_path) { - return result; - } - - // Resolve types from proto nodes in node_registry - let Some(node_id) = input_connector.node_id() else { - return (concrete!(()), TypeSource::Error("input connector is not a node")); - }; - - self.guess_type_from_node(&mut network_path.to_vec(), node_id, input_connector.input_index()) - } - - pub fn valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec { - let InputConnector::Node { node_id, input_index } = input_connector else { - // An export can have any type connected to it - return vec![graph_craft::generic!(T)]; - }; - let Some(implementation) = self.implementation(node_id, network_path) else { - log::error!("Could not get node implementation in valid_input_types"); - return Vec::new(); - }; - match implementation { - DocumentNodeImplementation::Network(_) => { - let nested_path = [network_path, &[*node_id]].concat(); - let Some(outward_wires) = self.outward_wires(&nested_path) else { - log::error!("Could not get outward wires in valid_input_types"); - return Vec::new(); - }; - let Some(inputs_from_import) = outward_wires.get(&OutputConnector::Import(*input_index)) else { - log::error!("Could not get inputs from import in valid_input_types"); - return Vec::new(); - }; - - let intersection: HashSet = inputs_from_import - .clone() - .iter() - .map(|input_connector| self.valid_input_types(input_connector, &nested_path)) - .map(|vec| vec.into_iter().collect::>()) - .fold(None, |acc: Option>, set| match acc { - Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()), - None => Some(set), - }) - .unwrap_or_default(); - - intersection.into_iter().collect::>() - } - DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { - let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { - log::error!("Protonode {proto_node_identifier:?} not found in registry"); - return Vec::new(); - }; - let number_of_inputs = self.number_of_inputs(node_id, network_path); - implementations - .iter() - .filter_map(|(node_io, _)| { - let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| { - let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path).0; - // Value inputs are stored as concrete, so they are compared to the nested type. Node inputs are stored as fn, so they are compared to the entire type. - // For example a node input of (Footprint) -> Vector would not be compatible with () -> Vector - node_io.inputs.get(iterator_index).map(|ty| ty.nested_type().clone()).as_ref() == Some(&input_type) || node_io.inputs.get(iterator_index) == Some(&input_type) - }); - if valid_implementation { node_io.inputs.get(*input_index).cloned() } else { None } - }) - .collect::>() - } - DocumentNodeImplementation::Extract => { - log::error!("Input types for extract node not supported"); - Vec::new() - } - } - } - - /// Retrieves the output types for a given document node and its exports. - /// - /// This function traverses the node and its nested network structure (if applicable) to determine - /// the types of all outputs, including the primary output and any additional exports. - /// - /// # Arguments - /// - /// * `node` - A reference to the `DocumentNode` for which to determine output types. - /// * `resolved_types` - A reference to `ResolvedDocumentNodeTypes` containing pre-resolved type information. - /// * `node_id_path` - A slice of `NodeId`s representing the path to the current node in the document graph. - /// - /// # Returns - /// - /// A `Vec>` where: - /// - The first element is the primary output type of the node. - /// - Subsequent elements are types of additional exports (if the node is a network). - /// - `None` values indicate that a type couldn't be resolved for a particular output. - /// - /// # Behavior - /// - /// 1. Retrieves the primary output type from `resolved_types`. - /// 2. If the node is a network: - /// - Iterates through its exports (skipping the first/primary export). - /// - For each export, traverses the network until reaching a protonode or terminal condition. - /// - Determines the output type based on the final node/value encountered. - /// 3. Collects and returns all resolved types. - /// - /// # Note - /// - /// This function assumes that export indices and node IDs always exist within their respective - /// collections. It will panic if these assumptions are violated. - /// - pub fn output_type(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> (Type, TypeSource) { - match output_connector { - OutputConnector::Node { node_id, output_index } => { - let Some(implementation) = self.implementation(node_id, network_path) else { - log::error!("Could not get output type for node {node_id} output index {output_index}. This node is no longer supported, and needs to be upgraded."); - return (concrete!(()), TypeSource::Error("Could not get implementation")); - }; - - // If the node is not a protonode, get types by traversing across exports until a proto node is reached. - match &implementation { - graph_craft::document::DocumentNodeImplementation::Network(internal_network) => { - let Some(export) = internal_network.exports.get(*output_index) else { - return (concrete!(()), TypeSource::Error("Could not get export index")); - }; - match export { - NodeInput::Node { - node_id: nested_node_id, - output_index, - .. - } => self.output_type(&OutputConnector::node(*nested_node_id, *output_index), &[network_path, &[*node_id]].concat()), - NodeInput::Value { tagged_value, .. } => (tagged_value.ty(), TypeSource::TaggedValue), - NodeInput::Import { .. } => { - // let mut encapsulating_path = network_path.to_vec(); - // let encapsulating_node = encapsulating_path.pop().expect("No imports exist in document network"); - // self.input_type(&InputConnector::node(encapsulating_node, *import_index), network_path) - (concrete!(()), TypeSource::Error("Could not type from network")) - } - NodeInput::Scope(_) => todo!(), - NodeInput::Inline(_) => todo!(), - NodeInput::Reflection(_) => todo!(), - } - } - graph_craft::document::DocumentNodeImplementation::ProtoNode(protonode) => { - let node_id_path = &[network_path, &[*node_id]].concat(); - self.resolved_types - .types - .get(node_id_path) - .map(|ty| (ty.output.clone(), TypeSource::Compiled)) - .or_else(|| { - let node_types = random_protonode_implementation(protonode)?; - Some((node_types.return_value.clone(), TypeSource::RandomProtonodeImplementation)) - }) - .unwrap_or((concrete!(()), TypeSource::Error("Could not get protonode implementation"))) - } - graph_craft::document::DocumentNodeImplementation::Extract => (concrete!(()), TypeSource::Error("extract node")), - } - } - OutputConnector::Import(import_index) => { - let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else { - log::error!("Cannot get type of import in document network"); - return (concrete!(()), TypeSource::Error("Cannot get import type in document network")); - }; - self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path) - } - } - } - pub fn position(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option { let top_left_position = self .node_click_targets(node_id, network_path) @@ -783,247 +485,6 @@ impl NodeNetworkInterface { } }) } - - pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Vec> { - match network_path.split_last() { - Some((node_id, encapsulating_network_path)) => { - let Some(node) = self.document_node(node_id, encapsulating_network_path) else { - log::error!("Could not get node {node_id} in network {encapsulating_network_path:?}"); - return Vec::new(); - }; - let mut frontend_imports = (0..node.inputs.len()) - .map(|import_index| self.frontend_output_from_connector(&OutputConnector::Import(import_index), network_path)) - .collect::>(); - if frontend_imports.is_empty() { - frontend_imports.push(None); - } - frontend_imports - } - // In the document network display no imports - None => Vec::new(), - } - } - - pub fn frontend_exports(&mut self, network_path: &[NodeId]) -> Vec> { - let Some(network) = self.nested_network(network_path) else { return Vec::new() }; - let mut frontend_exports = ((0..network.exports.len()).map(|export_index| self.frontend_input_from_connector(&InputConnector::Export(export_index), network_path))).collect::>(); - if frontend_exports.is_empty() { - frontend_exports.push(None); - } - frontend_exports - } - - pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> { - let Some(all_nodes_bounding_box) = self.all_nodes_bounding_box(network_path).cloned() else { - log::error!("Could not get all nodes bounding box in load_export_ports"); - return None; - }; - let Some(network) = self.nested_network(network_path) else { - log::error!("Could not get current network in load_export_ports"); - return None; - }; - - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in load_export_ports"); - return None; - }; - let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport; - let target_viewport_top_left = DVec2::new(IMPORTS_TO_LEFT_EDGE_PIXEL_GAP as f64, IMPORTS_TO_TOP_EDGE_PIXEL_GAP as f64); - - let node_graph_pixel_offset_top_left = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_left); - - // A 5x5 grid offset from the top left corner - let node_graph_grid_space_offset_top_left = node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO) + DVec2::new(5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64); - - // The inner bound of the import is the highest/furthest left of the two offsets - let top_left_inner_bound = DVec2::new( - node_graph_pixel_offset_top_left.x.min(node_graph_grid_space_offset_top_left.x), - node_graph_pixel_offset_top_left.y.min(node_graph_grid_space_offset_top_left.y), - ); - - let offset_from_top_left = if network - .exports - .first() - .is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path))) - { - DVec2::new(-4. * GRID_SIZE as f64, -2. * GRID_SIZE as f64) - } else { - DVec2::new(-4. * GRID_SIZE as f64, 0.) - }; - - let bounding_box_top_left = DVec2::new((all_nodes_bounding_box[0].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.) + offset_from_top_left; - let import_top_left = DVec2::new(top_left_inner_bound.x.min(bounding_box_top_left.x), top_left_inner_bound.y.min(bounding_box_top_left.y)); - let rounded_import_top_left = DVec2::new((import_top_left.x / 24.).round() * 24., (import_top_left.y / 24.).round() * 24.); - - let viewport_top_right = network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right; - let target_viewport_top_right = DVec2::new( - viewport_top_right.x - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, - viewport_top_right.y + 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(viewport_top_right) + 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 - let top_right_inner_bound = DVec2::new( - node_graph_pixel_offset_top_right.x.max(node_graph_grid_space_offset_top_right.x), - node_graph_pixel_offset_top_right.y.min(node_graph_grid_space_offset_top_right.y), - ); - - let offset_from_top_right = if network - .exports - .first() - .is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path))) - { - DVec2::new(2. * GRID_SIZE as f64, -2. * GRID_SIZE as f64) - } else { - DVec2::new(4. * GRID_SIZE as f64, 0.) - }; - - let mut bounding_box_top_right = DVec2::new((all_nodes_bounding_box[1].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.); - bounding_box_top_right += offset_from_top_right; - let export_top_right = DVec2::new(top_right_inner_bound.x.max(bounding_box_top_right.x), top_right_inner_bound.y.min(bounding_box_top_right.y)); - let rounded_export_top_right = DVec2::new((export_top_right.x / 24.).round() * 24., (export_top_right.y / 24.).round() * 24.); - - Some((rounded_import_top_left.as_ivec2(), rounded_export_top_right.as_ivec2())) - } - - /// Returns None if there is an error, it is a hidden primary export, or a hidden input - pub fn frontend_input_from_connector(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option { - // Return None if it is a hidden input - if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) { - return None; - } - let (export_type, source) = self.input_type(input_connector, network_path); - let data_type = FrontendGraphDataType::displayed_type(&export_type, &source); - 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 *export_type.nested_type() != concrete!(()) { - export_type.nested_type().to_string() - } else { - format!("Export index {}", export_index) - }; - - (export_name, String::new()) - } - }; - Some(FrontendGraphInput { - data_type, - name, - description, - resolved_type: format!("{export_type:?}"), - valid_types: self.valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(), - connected_to, - }) - } - - /// Returns None if there is an error, it is the document network, a hidden primary output or import - pub fn frontend_output_from_connector(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option { - let (output_type, type_source) = 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 if *output_type.nested_type() != concrete!(()) { - output_type.nested_type().to_string() - } else { - format!("Output {}", *output_index + 1) - }; - - (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 *output_type.nested_type() != concrete!(()) { - import_name - } else { - format!("Import index {}", *import_index) - }; - (import_name, description) - } - }; - - let data_type = FrontendGraphDataType::displayed_type(&output_type, &type_source); - - let mut connected_to = self - .outward_wires(network_path) - .and_then(|outward_wires| outward_wires.get(output_connector)) - .cloned() - .unwrap_or_default() - .iter() - .map(|input| match input { - InputConnector::Node { node_id, input_index } => { - let mut name = self.display_name(node_id, network_path); - if cfg!(debug_assertions) { - name.push_str(&format!(" (id: {node_id})")); - } - format!("{name} input {input_index}") - } - InputConnector::Export(export_index) => format!("Export index {export_index}"), - }) - .collect::>(); - - if connected_to.is_empty() { - connected_to.push("nothing".to_string()); - } - - Some(FrontendGraphOutput { - data_type, - name, - resolved_type: format!("{:?}", output_type), - description, - connected_to, - }) - } - pub fn height_from_click_target(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option { let mut node_height: Option = self .node_click_targets(node_id, network_path) @@ -1226,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))?; @@ -1252,7 +713,7 @@ impl NodeNetworkInterface { }; let description = input_metadata.input_description.to_string(); let name = if input_metadata.input_name.is_empty() { - self.input_type(&InputConnector::node(*node_id, input_index), network_path).0.nested_type().to_string() + self.input_type(&InputConnector::node(*node_id, input_index), network_path).resolved_type_node_string() } else { input_metadata.input_name.to_string() }; @@ -1288,9 +749,9 @@ impl NodeNetworkInterface { /// Returns the description of the node, or an empty string if it is not set. pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { - self.get_node_definition(network_path, *node_id) + self.get_node_definition(node_id, network_path) .map(|node_definition| node_definition.description.to_string()) - .filter(|description| description != "TODO") + .filter(|description: &String| description != "TODO") .unwrap_or_default() } @@ -1326,25 +787,6 @@ impl NodeNetworkInterface { node_metadata.persistent_metadata.is_layer() } - pub fn primary_output_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - let Some(outward_wires) = self.outward_wires(network_path) else { - log::error!("Could not get outward_wires in primary_output_connected_to_layer"); - return false; - }; - let Some(downstream_connectors) = outward_wires.get(&OutputConnector::node(*node_id, 0)) else { - log::error!("Could not get downstream_connectors in primary_output_connected_to_layer"); - return false; - }; - let downstream_nodes = downstream_connectors.iter().filter_map(|connector| connector.node_id()).collect::>(); - downstream_nodes.iter().any(|node_id| self.is_layer(node_id, network_path)) - } - - pub fn primary_input_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - self.input_from_connector(&InputConnector::node(*node_id, 0), network_path) - .and_then(|input| input.as_node()) - .is_some_and(|node_id| self.is_layer(&node_id, network_path)) - } - pub fn hidden_primary_export(&self, network_path: &[NodeId]) -> bool { let Some((node_id, network_path)) = network_path.split_last() else { // The document network does not have a hidden primary export @@ -1718,30 +1160,6 @@ impl NodeNetworkInterface { } } -/// Gets the type for a random protonode implementation (used if there is no type from the compiled network) -fn random_protonode_implementation(protonode: &graph_craft::ProtoNodeIdentifier) -> Option<&graphene_std::NodeIOTypes> { - let mut protonode = protonode.clone(); - // TODO: Remove - if let Some((path, _generics)) = protonode.name.split_once('<') { - protonode = path.to_string().to_string().into(); - } - let Some(node_io_hashmap) = NODE_REGISTRY.get(&protonode) else { - log::error!("Could not get hashmap for proto node: {protonode:?}"); - return None; - }; - - let node_types = node_io_hashmap.keys().min_by_key(|node_io_types| { - let mut hasher = DefaultHasher::new(); - node_io_types.hash(&mut hasher); - hasher.finish() - }); - - if node_types.is_none() { - log::error!("Could not get node_types from hashmap"); - }; - node_types -} - // Private mutable getters for use within the network interface impl NodeNetworkInterface { fn network_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetwork> { @@ -2134,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> { @@ -2249,68 +1644,6 @@ impl NodeNetworkInterface { network_metadata.transient_metadata.modify_import_export.unload(); } - pub fn rounded_network_edge_distance(&mut self, network_path: &[NodeId]) -> Option<&NetworkEdgeDistance> { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in rounded_network_edge_distance"); - return None; - }; - if !network_metadata.transient_metadata.rounded_network_edge_distance.is_loaded() { - self.load_rounded_network_edge_distance(network_path); - } - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in rounded_network_edge_distance"); - return None; - }; - let TransientMetadata::Loaded(rounded_network_edge_distance) = &network_metadata.transient_metadata.rounded_network_edge_distance else { - log::error!("could not load import rounded_network_edge_distance"); - return None; - }; - Some(rounded_network_edge_distance) - } - - fn load_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network in set_grid_aligned_edges"); - return; - }; - // When setting the edges to be grid aligned, update the pixel offset to ensure the next pan starts from the snapped import/export position - let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport; - // TODO: Eventually replace node graph top right with the footprint when trying to get the network edge distance - let node_graph_top_right = network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right; - let target_exports_distance = node_graph_to_viewport.inverse().transform_point2(DVec2::new( - node_graph_top_right.x - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, - node_graph_top_right.y + EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64, - )); - - let target_imports_distance = node_graph_to_viewport - .inverse() - .transform_point2(DVec2::new(IMPORTS_TO_LEFT_EDGE_PIXEL_GAP as f64, IMPORTS_TO_TOP_EDGE_PIXEL_GAP as f64)); - - let rounded_exports_distance = DVec2::new((target_exports_distance.x / 24. + 0.5).floor() * 24., (target_exports_distance.y / 24. + 0.5).floor() * 24.); - let rounded_imports_distance = DVec2::new((target_imports_distance.x / 24. + 0.5).floor() * 24., (target_imports_distance.y / 24. + 0.5).floor() * 24.); - - let rounded_viewport_exports_distance = node_graph_to_viewport.transform_point2(rounded_exports_distance); - let rounded_viewport_imports_distance = node_graph_to_viewport.transform_point2(rounded_imports_distance); - - let network_edge_distance = NetworkEdgeDistance { - exports_to_edge_distance: rounded_viewport_exports_distance, - imports_to_edge_distance: rounded_viewport_imports_distance, - }; - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get current network in load_export_ports"); - return; - }; - network_metadata.transient_metadata.rounded_network_edge_distance = TransientMetadata::Loaded(network_edge_distance); - } - - fn unload_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in unload_export_ports"); - return; - }; - network_metadata.transient_metadata.rounded_network_edge_distance.unload(); - } - fn owned_nodes(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&HashSet> { let layer_node = self.node_metadata(node_id, network_path)?; let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &layer_node.persistent_metadata.node_type_metadata else { @@ -2576,99 +1909,6 @@ impl NodeNetworkInterface { .find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None }) } - pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { - if !self.wire_is_loaded(input, network_path) { - self.load_wire(input, graph_wire_style, network_path); - } else { - return None; - } - - let wire = match input { - InputConnector::Node { node_id, input_index } => { - let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?; - let TransientMetadata::Loaded(wire) = &input_metadata.wire else { - log::error!("Could not load wire for input: {input:?}"); - return None; - }; - wire.clone() - } - InputConnector::Export(export_index) => { - let network_metadata = self.network_metadata(network_path)?; - let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else { - log::error!("Could not load wire for input: {input:?}"); - return None; - }; - wire.clone() - } - }; - Some(wire) - } - - pub fn wire_is_loaded(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool { - match input { - InputConnector::Node { node_id, input_index } => { - let Some(input_metadata) = self.transient_input_metadata(node_id, *input_index, network_path) else { - log::error!("Input metadata should always exist for input"); - return false; - }; - input_metadata.wire.is_loaded() - } - InputConnector::Export(export_index) => { - let Some(network_metadata) = self.network_metadata(network_path) else { - return false; - }; - match network_metadata.transient_metadata.wires.get(*export_index) { - Some(wire) => wire.is_loaded(), - None => false, - } - } - } - } - - fn load_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) { - let dashed = match self.previewing(network_path) { - Previewing::Yes { .. } => match input { - InputConnector::Node { .. } => false, - InputConnector::Export(export_index) => *export_index == 0, - }, - Previewing::No => false, - }; - let Some(wire) = self.wire_path_from_input(input, graph_wire_style, dashed, network_path) else { - log::error!("Could not load wire path from input"); - return; - }; - match input { - InputConnector::Node { node_id, input_index } => { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return }; - let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else { - // log::warn!("Node metadata must exist on node: {input:?}"); - return; - }; - let wire_update = WirePathUpdate { - id: *node_id, - input_index: *input_index, - wire_path_update: Some(wire), - }; - input_metadata.transient_metadata.wire = TransientMetadata::Loaded(wire_update); - } - InputConnector::Export(export_index) => { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { return }; - if *export_index >= network_metadata.transient_metadata.wires.len() { - network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded); - } - let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else { - return; - }; - let wire_update = WirePathUpdate { - id: NodeId(u64::MAX), - input_index: *export_index, - wire_path_update: Some(wire), - }; - *input_metadata = TransientMetadata::Loaded(wire_update); - } - } - } - pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec { let mut input_connectors = Vec::new(); let Some(network) = self.nested_network(network_path) else { @@ -2693,141 +1933,6 @@ impl NodeNetworkInterface { .collect() } - /// Maps to the frontend representation of a wire start. Includes disconnected value wire inputs. - pub fn node_graph_wire_inputs(&self, network_path: &[NodeId]) -> Vec<(NodeId, usize)> { - self.node_graph_input_connectors(network_path) - .iter() - .map(|input| match input { - InputConnector::Node { node_id, input_index } => (*node_id, *input_index), - InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index), - }) - .chain(std::iter::once((NodeId(u64::MAX), u32::MAX as usize))) - .collect() - } - - fn unload_wires_for_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) { - let number_of_outputs = self.number_of_outputs(node_id, network_path); - let Some(outward_wires) = self.outward_wires(network_path) else { - log::error!("Could not get outward wires in reorder_export"); - return; - }; - let mut input_connectors = Vec::new(); - for output_index in 0..number_of_outputs { - let Some(inputs) = outward_wires.get(&OutputConnector::node(*node_id, output_index)) else { - continue; - }; - input_connectors.extend(inputs.clone()) - } - for input_index in 0..self.number_of_inputs(node_id, network_path) { - input_connectors.push(InputConnector::node(*node_id, input_index)); - } - for input in input_connectors { - self.unload_wire(&input, network_path); - } - } - - pub fn unload_wire(&mut self, input: &InputConnector, network_path: &[NodeId]) { - match input { - InputConnector::Node { node_id, input_index } => { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { - return; - }; - let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else { - // log::warn!("Node metadata must exist on node: {input:?}"); - return; - }; - input_metadata.transient_metadata.wire = TransientMetadata::Unloaded; - } - InputConnector::Export(export_index) => { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - return; - }; - if *export_index >= network_metadata.transient_metadata.wires.len() { - network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded); - } - let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else { - return; - }; - *input_metadata = TransientMetadata::Unloaded; - } - } - } - - /// When previewing, there may be a second path to the root node. - pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { - let input = InputConnector::Export(0); - let current_export = self.upstream_output_connector(&input, network_path)?; - - let root_node = match self.previewing(network_path) { - Previewing::Yes { root_node_to_restore } => root_node_to_restore, - Previewing::No => None, - }?; - - if Some(root_node.node_id) == current_export.node_id() { - return None; - } - let Some(input_position) = self.get_input_center(&input, network_path) else { - log::error!("Could not get input position for wire end in root node: {input:?}"); - return None; - }; - let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index); - let Some(output_position) = self.get_output_center(&upstream_output, network_path) else { - log::error!("Could not get output position for wire start in root node: {upstream_output:?}"); - return None; - }; - let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0); - let vertical_start: bool = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path)); - let thick = vertical_end && vertical_start; - let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style); - - let path_string = vector_wire.to_svg(); - let data_type = FrontendGraphDataType::from_type(&self.input_type(&input, network_path).0); - let wire_path_update = Some(WirePath { - path_string, - data_type, - thick, - dashed: false, - }); - - Some(WirePathUpdate { - id: NodeId(u64::MAX), - input_index: u32::MAX as usize, - wire_path_update, - }) - } - - /// Returns the vector subpath and a boolean of whether the wire should be thick. - pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, bool)> { - let Some(input_position) = self.get_input_center(input, network_path) else { - log::error!("Could not get dom rect for wire end: {input:?}"); - return None; - }; - // An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector - let Some(upstream_output) = self.upstream_output_connector(input, network_path) else { - return Some((BezPath::new(), false)); - }; - let Some(output_position) = self.get_output_center(&upstream_output, network_path) else { - log::error!("Could not get output port for wire start: {:?}", upstream_output); - return None; - }; - let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0); - let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path)); - let thick = vertical_end && vertical_start; - Some((build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style), thick)) - } - - pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option { - let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?; - let path_string = vector_wire.to_svg(); - let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0); - 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) @@ -3065,7 +2170,7 @@ impl NodeNetworkInterface { return; }; node_metadata.transient_metadata.click_targets.unload(); - self.unload_wires_for_node(node_id, network_path); + self.unload_wires_for_node_old(node_id, network_path); } pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec, network_path: &[NodeId]) { @@ -3157,33 +2262,6 @@ impl NodeNetworkInterface { let rect = Subpath::::new_rect(bounds[0], bounds[1]); let all_nodes_bounding_box = rect.to_bezpath().to_svg(); - let Some(rounded_network_edge_distance) = self.rounded_network_edge_distance(network_path).cloned() else { - log::error!("Could not get rounded_network_edge_distance in collect_frontend_click_targets"); - return FrontendClickTargets::default(); - }; - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in collect_frontend_click_targets"); - return FrontendClickTargets::default(); - }; - let import_exports_viewport_top_left = rounded_network_edge_distance.imports_to_edge_distance; - let import_exports_viewport_bottom_right = rounded_network_edge_distance.exports_to_edge_distance; - - let node_graph_top_left = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .inverse() - .transform_point2(import_exports_viewport_top_left); - let node_graph_bottom_right = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .inverse() - .transform_point2(import_exports_viewport_bottom_right); - - let import_exports_target = Subpath::::new_rect(node_graph_top_left, node_graph_bottom_right); - let import_exports_bounding_box = import_exports_target.to_bezpath().to_svg(); - let mut modify_import_export = Vec::new(); if let Some(modify_import_export_click_targets) = self.modify_import_export(network_path) { for click_target in modify_import_export_click_targets @@ -3202,7 +2280,6 @@ impl NodeNetworkInterface { connector_click_targets, icon_click_targets, all_nodes_bounding_box, - import_exports_bounding_box, modify_import_export, } } @@ -3449,38 +2526,6 @@ impl NodeNetworkInterface { bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport) } - pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> (HashMap, HashMap, HashMap) { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in collect_layer_widths"); - return (HashMap::new(), HashMap::new(), HashMap::new()); - }; - let nodes = network_metadata - .persistent_metadata - .node_metadata - .iter() - .filter_map(|(node_id, _)| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None }) - .collect::>(); - let layer_widths = nodes - .iter() - .filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width))) - .collect::>(); - let chain_widths = nodes.iter().map(|node_id| (*node_id, self.chain_width(node_id, network_path))).collect::>(); - let has_left_input_wire = nodes - .iter() - .map(|node_id| { - ( - *node_id, - !self - .upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow) - .skip(1) - .all(|node_id| self.is_chain(&node_id, network_path)), - ) - }) - .collect::>(); - - (layer_widths, chain_widths, has_left_input_wire) - } - pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option { let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self); @@ -3656,17 +2701,15 @@ impl NodeNetworkInterface { } // This should be run whenever the pan ends, a zoom occurs, or the network is opened - pub fn set_grid_aligned_edges(&mut self, node_graph_top_right: DVec2, network_path: &[NodeId]) { + pub fn set_node_graph_width(&mut self, node_graph_width: f64, network_path: &[NodeId]) { let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in set_grid_aligned_edges"); + log::error!("Could not get nested network in set_transform"); return; }; - network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right = node_graph_top_right; - self.unload_rounded_network_edge_distance(network_path); + network_metadata.persistent_metadata.navigation_metadata.node_graph_width = node_graph_width; 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"); @@ -4401,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); @@ -4504,7 +3544,7 @@ impl NodeNetworkInterface { } } - let tagged_value = TaggedValue::from_type_or_none(&self.input_type(input_connector, network_path).0); + let tagged_value = self.tagged_value_from_input(input_connector, network_path); let value_input = NodeInput::value(tagged_value, true); @@ -6124,22 +5164,6 @@ impl Iterator for FlowIter<'_> { } } -// TODO: Refactor to be Unknown, Compiled(Type) for NodeInput::Node, or Value(Type) for NodeInput::Value -/// Represents the source of a resolved type (for debugging). -/// There will be two valid types list. One for the current valid types that will not cause a node graph error, -/// based on the other inputs to that node and returned during compilation. THe other list will be all potential -/// Valid types, based on the protonode implementation/downstream users. -#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum TypeSource { - Compiled, - RandomProtonodeImplementation, - DocumentNodeDefault, - TaggedValue, - OuterMostExportDefault, - - Error(&'static str), -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)] pub enum ImportOrExport { Import(usize), @@ -6471,11 +5495,9 @@ pub struct NodeNetworkTransientMetadata { pub import_export_ports: TransientMetadata, /// Click targets for adding, removing, and moving import/export ports pub modify_import_export: TransientMetadata, - // Distance to the edges of the network, where the import/export ports are displayed. Rounded to nearest grid space when the panning ends. - pub rounded_network_edge_distance: TransientMetadata, // Wires from the exports - pub wires: Vec>, + pub wires: Vec>, } #[derive(Debug, Clone)] @@ -6656,7 +5678,7 @@ impl InputPersistentMetadata { #[derive(Debug, Clone, Default)] struct InputTransientMetadata { - wire: TransientMetadata, + wire: TransientMetadata, // downstream_protonode: populated for all inputs after each compile // types: populated for each protonode after each } @@ -6871,29 +5893,18 @@ pub enum LayerClickTargetTypes { // Preview, } -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)] pub struct NavigationMetadata { /// The current pan, and zoom state of the viewport's view of the node graph. /// Ensure `DocumentMessage::UpdateDocumentTransform` is called when the pan, zoom, or transform changes. pub node_graph_ptz: PTZ, - // TODO: Remove and replace with calculate_offset_transform from the node_graph_ptz. This will be difficult since it requires both the navigation message handler and the IPP + // TODO: Eventually remove once te click targets are extracted from the native render /// Transform from node graph space to viewport space. pub node_graph_to_viewport: DAffine2, - /// Top right of the node graph in viewport space + // TODO: Eventually remove once the import/export positions are extracted from the native render + /// The width of the node graph in viewport space #[serde(default)] - pub node_graph_top_right: DVec2, -} - -impl Default for NavigationMetadata { - fn default() -> NavigationMetadata { - // Default PTZ and transform - NavigationMetadata { - node_graph_ptz: PTZ::default(), - node_graph_to_viewport: DAffine2::IDENTITY, - // TODO: Eventually replace with footprint - node_graph_top_right: DVec2::ZERO, - } - } + pub node_graph_width: f64, } // PartialEq required by message handlers diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs new file mode 100644 index 0000000000..f672b1ffda --- /dev/null +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs @@ -0,0 +1,360 @@ +use crate::consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}; +use crate::messages::portfolio::document::node_graph::utility_types::{FrontendExport, FrontendExports, FrontendImport}; +use crate::messages::portfolio::document::utility_types::{ + network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing}, + wires::{GraphWireStyle, build_vector_wire}, +}; +use glam::{DVec2, IVec2}; +use graphene_std::{ + node_graph_overlay::types::{FrontendGraphInputNew, FrontendGraphOutputNew}, + uuid::NodeId, +}; +use kurbo::BezPath; + +// Functions used to collect data from the network interface for use in rendering the node graph +impl NodeNetworkInterface { + /// Returns None if there is an error, it is a hidden primary export, or a hidden input + pub fn frontend_input_from_connector(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option { + // Return None if it is a hidden input or doesn't exist + if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) { + return None; + } + let input_type = self.input_type(input_connector, network_path); + let data_type = input_type.displayed_type(); + + let name = match input_connector { + InputConnector::Node { node_id, input_index } => self.displayed_input_name_and_description(node_id, *input_index, network_path).0, + InputConnector::Export(export_index) => { + // Get export name from parent node metadata input, which must match the number of exports. + // Empty string means to use type, or "Export + index" if type is empty determined + let export_name = if network_path.is_empty() { + "Canvas".to_string() + } else { + self.encapsulating_node_metadata(network_path) + .and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.output_names.get(*export_index).cloned()) + .unwrap_or_default() + }; + + if !export_name.is_empty() { + export_name + } else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) { + export_type_name + } else { + format!("Export index {}", export_index) + } + } + }; + + let connected_to_node = self.upstream_output_connector(input_connector, network_path).and_then(|output_connector| output_connector.node_id()); + + Some(FrontendGraphInputNew { data_type, name, connected_to_node }) + } + + /// Returns None if there is an error, it is the document network, a hidden primary output or import + pub fn frontend_output_from_connector(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option { + let output_type = self.output_type(output_connector, network_path); + + let name = match output_connector { + OutputConnector::Node { node_id, output_index } => { + // Do not display the primary output port for a node if it is a network node with a hidden primary export + if *output_index == 0 && self.hidden_primary_output(node_id, network_path) { + return None; + }; + // Get the output name from the interior network export name + let node_metadata = self.node_metadata(node_id, network_path)?; + let output_name = node_metadata.persistent_metadata.output_names.get(*output_index).cloned().unwrap_or_default(); + + if !output_name.is_empty() { output_name } else { output_type.resolved_type_node_string() } + } + OutputConnector::Import(import_index) => { + // Get the import name from the encapsulating node input metadata + let Some((encapsulating_node_id, encapsulating_path)) = network_path.split_last() else { + // Return None if it is an import in the document network + return None; + }; + // Return None if the primary input is hidden and this is the primary import + if *import_index == 0 && self.hidden_primary_import(network_path) { + return None; + }; + let import_name = self.displayed_input_name_and_description(encapsulating_node_id, *import_index, encapsulating_path).0; + + if !import_name.is_empty() { + import_name + } else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) { + import_type_name + } else { + format!("Import index {}", *import_index) + } + } + }; + let connected = self + .outward_wires(network_path) + .and_then(|outward_wires| outward_wires.get(output_connector)) + .is_some_and(|downstream| downstream.len() > 0); + let data_type = output_type.displayed_type(); + Some(FrontendGraphOutputNew { data_type, name, connected }) + } + + pub fn chain_width(&self, node_id: &NodeId, network_path: &[NodeId]) -> u32 { + if self.number_of_displayed_inputs(node_id, network_path) > 1 { + let mut last_chain_node_distance = 0u32; + // Iterate upstream from the layer, and get the number of nodes distance to the last node with Position::Chain + for (index, node_id) in self + .upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalPrimaryOutputFlow) + .skip(1) + .enumerate() + .collect::>() + { + // Check if the node is positioned as a chain + if self.is_chain(&node_id, network_path) { + last_chain_node_distance = (index as u32) + 1; + } else { + return last_chain_node_distance * 7 + 1; + } + } + + last_chain_node_distance * 7 + 1 + } else { + // Layer with no inputs has no chain + 0 + } + } + + /// Checks if a layer should display a gap in its left border + pub fn layer_has_left_border_gap(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { + self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).skip(1).any(|node_id| { + !self.is_chain(&node_id, network_path) + || self + .upstream_output_connector(&InputConnector::node(node_id, 0), network_path) + .is_some_and(|output_connector| matches!(output_connector, OutputConnector::Import(_))) + }) + } + + /// Returns the node which should have a dashed border drawn around it + pub fn previewed_node(&self, network_path: &[NodeId]) -> Option { + self.upstream_output_connector(&InputConnector::Export(0), network_path) + .and_then(|output_connector| output_connector.node_id()) + .filter(|output_node| self.root_node(network_path).is_some_and(|root_node| root_node.node_id != *output_node)) + } + + /// If any downstream input are bottom layer inputs, then the thick cap should be displayed above the output port + pub fn primary_output_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool { + let Some(outward_wires) = self.outward_wires(network_path) else { + log::error!("Could not get outward_wires in primary_output_connected_to_layer"); + return false; + }; + let Some(downstream_connectors) = outward_wires.get(&OutputConnector::node(*node_id, 0)) else { + log::error!("Could not get downstream_connectors in primary_output_connected_to_layer"); + return false; + }; + let downstream_nodes = downstream_connectors + .iter() + .filter_map(|connector| if connector.input_index() == 0 { connector.node_id() } else { None }) + .collect::>(); + downstream_nodes.iter().any(|node_id| self.is_layer(node_id, network_path)) + } + + /// If any upstream nodes are layers, then the thick cap should be displayed below the primary input port + pub fn primary_input_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool { + self.input_from_connector(&InputConnector::node(*node_id, 0), network_path) + .and_then(|input| input.as_node()) + .is_some_and(|node_id| self.is_layer(&node_id, network_path)) + } + + /// The imports contain both the output port and the outward wires + pub fn frontend_imports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Vec> { + match network_path.split_last() { + Some((node_id, encapsulating_network_path)) => { + let Some(node) = self.document_node(node_id, encapsulating_network_path) else { + log::error!("Could not get node {node_id} in network {encapsulating_network_path:?}"); + return Vec::new(); + }; + let mut frontend_imports = (0..node.inputs.len()) + .map(|import_index| { + let port = self.frontend_output_from_connector(&OutputConnector::Import(import_index), network_path); + port.and_then(|port| { + let outward_wires = self.outward_wires(network_path)?; + let downstream_inputs = outward_wires.get(&OutputConnector::Import(import_index)).cloned()?; + let wires = downstream_inputs + .iter() + .filter_map(|input_connector| { + let Some(wire) = self.wire_from_input_new(&input_connector, graph_wire_style, network_path) else { + log::error!("Could not get wire path for import input: {input_connector:?}"); + return None; + }; + Some(wire.to_svg()) + }) + .collect::>(); + Some(FrontendImport { port, wires }) + }) + }) + .collect::>(); + + if frontend_imports.is_empty() { + frontend_imports.push(None); + } + frontend_imports + } + // In the document network display no imports + None => Vec::new(), + } + } + + /// The imports contain the export port, the outward wires, and the preview wire if it exists + pub fn frontend_exports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> FrontendExports { + let Some(network) = self.nested_network(network_path) else { + log::error!("Could not get nested network in frontend exports"); + return FrontendExports::default(); + }; + let mut exports = (0..network.exports.len()) + .map(|export_index| { + let export_connector = InputConnector::Export(export_index); + let frontend_export = self.frontend_input_from_connector(&export_connector, network_path); + + frontend_export.and_then(|export| { + let wire = self.wire_from_input_new(&export_connector, graph_wire_style, network_path).map(|path| path.to_svg()); + Some(FrontendExport { port: export, wire }) + }) + }) + .collect::>(); + + if exports.is_empty() { + exports.push(None); + } + let preview_wire = self.wire_to_root_new(graph_wire_style, network_path).map(|wire| wire.to_svg()); + FrontendExports { exports, preview_wire } + } + + pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> { + let Some(all_nodes_bounding_box) = self.all_nodes_bounding_box(network_path).cloned() else { + log::error!("Could not get all nodes bounding box in load_export_ports"); + return None; + }; + let Some(network) = self.nested_network(network_path) else { + log::error!("Could not get current network in load_export_ports"); + return None; + }; + + let Some(network_metadata) = self.network_metadata(network_path) else { + log::error!("Could not get nested network_metadata in load_export_ports"); + return None; + }; + let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport; + let target_viewport_top_left = DVec2::new(IMPORTS_TO_LEFT_EDGE_PIXEL_GAP as f64, IMPORTS_TO_TOP_EDGE_PIXEL_GAP as f64); + + let node_graph_pixel_offset_top_left = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_left); + + // A 5x5 grid offset from the top left corner + let node_graph_grid_space_offset_top_left = node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO) + DVec2::new(5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64); + + // The inner bound of the import is the highest/furthest left of the two offsets + let top_left_inner_bound = DVec2::new( + node_graph_pixel_offset_top_left.x.min(node_graph_grid_space_offset_top_left.x), + node_graph_pixel_offset_top_left.y.min(node_graph_grid_space_offset_top_left.y), + ); + + let offset_from_top_left = if network + .exports + .first() + .is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path))) + { + DVec2::new(-4. * GRID_SIZE as f64, -2. * GRID_SIZE as f64) + } else { + DVec2::new(-4. * GRID_SIZE as f64, 0.) + }; + + let bounding_box_top_left = DVec2::new((all_nodes_bounding_box[0].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.) + offset_from_top_left; + let import_top_left = DVec2::new(top_left_inner_bound.x.min(bounding_box_top_left.x), top_left_inner_bound.y.min(bounding_box_top_left.y)); + let rounded_import_top_left = DVec2::new((import_top_left.x / 24.).round() * 24., (import_top_left.y / 24.).round() * 24.); + + let viewport_width = network_metadata.persistent_metadata.navigation_metadata.node_graph_width; + + let target_viewport_top_right = DVec2::new(viewport_width - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64); + + // An offset from the right edge in viewport pixels + let node_graph_pixel_offset_top_right = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_right); + + // A 5x5 grid offset from the right corner + let node_graph_grid_space_offset_top_right = node_graph_to_viewport.inverse().transform_point2(DVec2::new(viewport_width, 0.)) + DVec2::new(-5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64); + + // The inner bound of the export is the highest/furthest right of the two offsets. + // When zoomed out this keeps it a constant grid space away from the edge, but when zoomed in it prevents the exports from getting too far in + let top_right_inner_bound = DVec2::new( + node_graph_pixel_offset_top_right.x.max(node_graph_grid_space_offset_top_right.x), + node_graph_pixel_offset_top_right.y.min(node_graph_grid_space_offset_top_right.y), + ); + + let offset_from_top_right = if network + .exports + .first() + .is_some_and(|export| export.as_node().is_some_and(|export_node| self.is_layer(&export_node, network_path))) + { + DVec2::new(2. * GRID_SIZE as f64, -2. * GRID_SIZE as f64) + } else { + DVec2::new(4. * GRID_SIZE as f64, 0.) + }; + + let mut bounding_box_top_right = DVec2::new((all_nodes_bounding_box[1].x / 24. + 0.5).floor() * 24., (all_nodes_bounding_box[0].y / 24. + 0.5).floor() * 24.); + bounding_box_top_right += offset_from_top_right; + let export_top_right = DVec2::new(top_right_inner_bound.x.max(bounding_box_top_right.x), top_right_inner_bound.y.min(bounding_box_top_right.y)); + let rounded_export_top_right = DVec2::new((export_top_right.x / 24.).round() * 24., (export_top_right.y / 24.).round() * 24.); + + Some((rounded_import_top_left.as_ivec2(), rounded_export_top_right.as_ivec2())) + } + + pub fn wire_is_thick(&self, input: &InputConnector, network_path: &[NodeId]) -> bool { + let Some(upstream_output) = self.upstream_output_connector(input, network_path) else { + return false; + }; + let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0); + let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path)); + vertical_end && vertical_start + } + + /// Returns the vector subpath and a boolean of whether the wire should be thick. + pub fn wire_from_input_new(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { + let Some(input_position) = self.get_input_center(input, network_path) else { + log::error!("Could not get dom rect for wire end: {input:?}"); + return None; + }; + // An upstream output could not be found + let Some(upstream_output) = self.upstream_output_connector(input, network_path) else { + return None; + }; + let Some(output_position) = self.get_output_center(&upstream_output, network_path) else { + log::error!("Could not get output port for wire start: {:?}", upstream_output); + return None; + }; + let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0); + let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path)); + Some(build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style)) + } + + /// When previewing, there may be a second path to the root node. + pub fn wire_to_root_new(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { + let input = InputConnector::Export(0); + let current_export = self.upstream_output_connector(&input, network_path)?; + + let root_node = match self.previewing(network_path) { + Previewing::Yes { root_node_to_restore } => root_node_to_restore, + Previewing::No => None, + }?; + + if Some(root_node.node_id) == current_export.node_id() { + return None; + } + let Some(input_position) = self.get_input_center(&input, network_path) else { + log::error!("Could not get input position for wire end in root node: {input:?}"); + return None; + }; + let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index); + let Some(output_position) = self.get_output_center(&upstream_output, network_path) else { + log::error!("Could not get output position for wire start in root node: {upstream_output:?}"); + return None; + }; + let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path)); + let vector_wire = build_vector_wire(output_position, input_position, vertical_start, false, graph_wire_style); + + Some(vector_wire) + } +} diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/old_node_graph_render.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/old_node_graph_render.rs new file mode 100644 index 0000000000..c3449fbf61 --- /dev/null +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/old_node_graph_render.rs @@ -0,0 +1,435 @@ +use crate::messages::portfolio::document::node_graph::utility_types::{FrontendGraphInputOld, FrontendGraphOutputOld}; +use crate::messages::portfolio::document::utility_types::network_interface::FlowType; +use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; +use crate::messages::portfolio::document::utility_types::network_interface::Previewing; +use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, OutputConnector, TransientMetadata}; +use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePathOld, WirePathUpdateOld, build_vector_wire}; +use graphene_std::node_graph_overlay::types::FrontendGraphDataType; +use graphene_std::uuid::NodeId; +use kurbo::BezPath; +use std::collections::HashMap; + +// All these functions will be deleted once the svelte node graph rendering is removed +impl NodeNetworkInterface { + /// Returns None if there is an error, it is a hidden primary export, or a hidden input + pub fn frontend_input_from_connector_old(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option { + // Return None if it is a hidden input + if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) { + return None; + } + let input_type = self.input_type(input_connector, network_path); + let data_type = input_type.displayed_type(); + let resolved_type = input_type.resolved_type_node_string(); + + let connected_to = self + .upstream_output_connector(input_connector, network_path) + .map(|output_connector| match output_connector { + OutputConnector::Node { node_id, output_index } => { + let mut name = self.display_name(&node_id, network_path); + if cfg!(debug_assertions) { + name.push_str(&format!(" (id: {node_id})")); + } + format!("{name} output {output_index}") + } + OutputConnector::Import(import_index) => format!("Import index {import_index}"), + }) + .unwrap_or("nothing".to_string()); + + let (name, description) = match input_connector { + InputConnector::Node { node_id, input_index } => self.displayed_input_name_and_description(node_id, *input_index, network_path), + InputConnector::Export(export_index) => { + // Get export name from parent node metadata input, which must match the number of exports. + // Empty string means to use type, or "Export + index" if type is empty determined + let export_name = if network_path.is_empty() { + "Canvas".to_string() + } else { + self.encapsulating_node_metadata(network_path) + .and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.output_names.get(*export_index).cloned()) + .unwrap_or_default() + }; + + let export_name = if !export_name.is_empty() { + export_name + } else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) { + export_type_name + } else { + format!("Export index {}", export_index) + }; + + (export_name, String::new()) + } + }; + + Some(FrontendGraphInputOld { + data_type, + resolved_type, + name, + description, + valid_types: self.potential_valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(), + connected_to, + }) + } + + /// Returns None if there is an error, it is the document network, a hidden primary output or import + pub fn frontend_output_from_connector_old(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option { + let output_type = self.output_type(output_connector, network_path); + + let (name, description) = match output_connector { + OutputConnector::Node { node_id, output_index } => { + // Do not display the primary output port for a node if it is a network node with a hidden primary export + if *output_index == 0 && self.hidden_primary_output(node_id, network_path) { + return None; + }; + // Get the output name from the interior network export name + let node_metadata = self.node_metadata(node_id, network_path)?; + let output_name = node_metadata.persistent_metadata.output_names.get(*output_index).cloned().unwrap_or_default(); + + let output_name = if !output_name.is_empty() { output_name } else { output_type.resolved_type_node_string() }; + (output_name, String::new()) + } + OutputConnector::Import(import_index) => { + // Get the import name from the encapsulating node input metadata + let Some((encapsulating_node_id, encapsulating_path)) = network_path.split_last() else { + // Return None if it is an import in the document network + return None; + }; + // Return None if the primary input is hidden and this is the primary import + if *import_index == 0 && self.hidden_primary_import(network_path) { + return None; + }; + let (import_name, description) = self.displayed_input_name_and_description(encapsulating_node_id, *import_index, encapsulating_path); + + let import_name = if !import_name.is_empty() { + import_name + } else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) { + import_type_name + } else { + format!("Import index {}", import_index) + }; + + (import_name, description) + } + }; + let data_type = output_type.displayed_type(); + let resolved_type = output_type.resolved_type_node_string(); + let mut connected_to = self + .outward_wires(network_path) + .and_then(|outward_wires| outward_wires.get(output_connector)) + .cloned() + .unwrap_or_default() + .iter() + .map(|input| match input { + InputConnector::Node { node_id, input_index } => { + let mut name = self.display_name(node_id, network_path); + if cfg!(debug_assertions) { + name.push_str(&format!(" (id: {node_id})")); + } + format!("{name} input {input_index}") + } + InputConnector::Export(export_index) => format!("Export index {export_index}"), + }) + .collect::>(); + + if connected_to.is_empty() { + connected_to.push("nothing".to_string()); + } + + Some(FrontendGraphOutputOld { + data_type, + resolved_type, + name, + description, + connected_to, + }) + } + + pub fn unload_import_export_wires_old(&mut self, network_path: &[NodeId]) { + // Always unload all wires connected to them as well + let number_of_imports = self.number_of_imports(network_path); + let Some(outward_wires) = self.outward_wires(network_path) else { + log::error!("Could not get outward wires in remove_import"); + return; + }; + let mut input_connectors = Vec::new(); + for import_index in 0..number_of_imports { + let Some(outward_wires_for_import) = outward_wires.get(&OutputConnector::Import(import_index)).cloned() else { + log::error!("Could not get outward wires for import in remove_import"); + return; + }; + input_connectors.extend(outward_wires_for_import); + } + let Some(network) = self.nested_network(network_path) else { + return; + }; + for export_index in 0..network.exports.len() { + input_connectors.push(InputConnector::Export(export_index)); + } + for input in &input_connectors { + self.unload_wire_old(input, network_path); + } + } + + pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { + if !self.wire_is_loaded_old(input, network_path) { + self.load_wire_old(input, graph_wire_style, network_path); + } else { + return None; + } + + let wire = match input { + InputConnector::Node { node_id, input_index } => { + let input_metadata = self.transient_input_metadata_old(node_id, *input_index, network_path)?; + let TransientMetadata::Loaded(wire) = &input_metadata.wire else { + log::error!("Could not load wire for input: {input:?}"); + return None; + }; + wire.clone() + } + InputConnector::Export(export_index) => { + let network_metadata = self.network_metadata(network_path)?; + let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else { + log::error!("Could not load wire for input: {input:?}"); + return None; + }; + wire.clone() + } + }; + Some(wire) + } + + pub fn wire_is_loaded_old(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool { + match input { + InputConnector::Node { node_id, input_index } => { + let Some(input_metadata) = self.transient_input_metadata_old(node_id, *input_index, network_path) else { + log::error!("Input metadata should always exist for input"); + return false; + }; + input_metadata.wire.is_loaded() + } + InputConnector::Export(export_index) => { + let Some(network_metadata) = self.network_metadata(network_path) else { + return false; + }; + match network_metadata.transient_metadata.wires.get(*export_index) { + Some(wire) => wire.is_loaded(), + None => false, + } + } + } + } + + fn load_wire_old(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) { + let dashed = match self.previewing(network_path) { + Previewing::Yes { .. } => match input { + InputConnector::Node { .. } => false, + InputConnector::Export(export_index) => *export_index == 0, + }, + Previewing::No => false, + }; + let Some(wire) = self.wire_path_from_input_old(input, graph_wire_style, dashed, network_path) else { + log::error!("Could not load wire path from input"); + return; + }; + match input { + InputConnector::Node { node_id, input_index } => { + let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return }; + let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else { + // log::warn!("Node metadata must exist on node: {input:?}"); + return; + }; + let wire_update = WirePathUpdateOld { + id: *node_id, + input_index: *input_index, + wire_path_update: Some(wire), + }; + input_metadata.transient_metadata.wire = TransientMetadata::Loaded(wire_update); + } + InputConnector::Export(export_index) => { + let Some(network_metadata) = self.network_metadata_mut(network_path) else { return }; + if *export_index >= network_metadata.transient_metadata.wires.len() { + network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded); + } + let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else { + return; + }; + let wire_update = WirePathUpdateOld { + id: NodeId(u64::MAX), + input_index: *export_index, + wire_path_update: Some(wire), + }; + *input_metadata = TransientMetadata::Loaded(wire_update); + } + } + } + + /// Maps to the frontend representation of a wire start. Includes disconnected value wire inputs. + pub fn node_graph_wire_inputs(&self, network_path: &[NodeId]) -> Vec<(NodeId, usize)> { + self.node_graph_input_connectors(network_path) + .iter() + .map(|input| match input { + InputConnector::Node { node_id, input_index } => (*node_id, *input_index), + InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index), + }) + .chain(std::iter::once((NodeId(u64::MAX), u32::MAX as usize))) + .collect() + } + + pub fn unload_wires_for_node_old(&mut self, node_id: &NodeId, network_path: &[NodeId]) { + let number_of_outputs = self.number_of_outputs(node_id, network_path); + let Some(outward_wires) = self.outward_wires(network_path) else { + log::error!("Could not get outward wires in reorder_export"); + return; + }; + let mut input_connectors = Vec::new(); + for output_index in 0..number_of_outputs { + let Some(inputs) = outward_wires.get(&OutputConnector::node(*node_id, output_index)) else { + continue; + }; + input_connectors.extend(inputs.clone()) + } + for input_index in 0..self.number_of_inputs(node_id, network_path) { + input_connectors.push(InputConnector::node(*node_id, input_index)); + } + for input in input_connectors { + self.unload_wire_old(&input, network_path); + } + } + + pub fn unload_wire_old(&mut self, input: &InputConnector, network_path: &[NodeId]) { + match input { + InputConnector::Node { node_id, input_index } => { + let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + return; + }; + let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else { + // log::warn!("Node metadata must exist on node: {input:?}"); + return; + }; + input_metadata.transient_metadata.wire = TransientMetadata::Unloaded; + } + InputConnector::Export(export_index) => { + let Some(network_metadata) = self.network_metadata_mut(network_path) else { + return; + }; + if *export_index >= network_metadata.transient_metadata.wires.len() { + network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded); + } + let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else { + return; + }; + *input_metadata = TransientMetadata::Unloaded; + } + } + } + + /// When previewing, there may be a second path to the root node. + pub fn wire_to_root_old(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { + let input = InputConnector::Export(0); + let current_export = self.upstream_output_connector(&input, network_path)?; + + let root_node = match self.previewing(network_path) { + Previewing::Yes { root_node_to_restore } => root_node_to_restore, + Previewing::No => None, + }?; + + if Some(root_node.node_id) == current_export.node_id() { + return None; + } + let Some(input_position) = self.get_input_center(&input, network_path) else { + log::error!("Could not get input position for wire end in root node: {input:?}"); + return None; + }; + let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index); + let Some(output_position) = self.get_output_center(&upstream_output, network_path) else { + log::error!("Could not get output position for wire start in root node: {upstream_output:?}"); + return None; + }; + let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0); + let vertical_start: bool = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path)); + let thick = vertical_end && vertical_start; + let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style); + + let path_string = vector_wire.to_svg(); + let data_type = self.input_type(&input, network_path).displayed_type(); + let wire_path_update = Some(WirePathOld { + path_string, + data_type, + thick, + dashed: false, + }); + + Some(WirePathUpdateOld { + id: NodeId(u64::MAX), + input_index: u32::MAX as usize, + wire_path_update, + }) + } + + /// Returns the vector subpath and a boolean of whether the wire should be thick. + pub fn vector_wire_from_input_old(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, bool)> { + let Some(input_position) = self.get_input_center(input, network_path) else { + log::error!("Could not get dom rect for wire end: {input:?}"); + return None; + }; + // An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector + let Some(upstream_output) = self.upstream_output_connector(input, network_path) else { + return Some((BezPath::new(), false)); + }; + let Some(output_position) = self.get_output_center(&upstream_output, network_path) else { + log::error!("Could not get output port for wire start: {:?}", upstream_output); + return None; + }; + let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0); + let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path)); + let thick = vertical_end && vertical_start; + Some((build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style), thick)) + } + + pub fn wire_path_from_input_old(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option { + let (vector_wire, thick) = self.vector_wire_from_input_old(input, graph_wire_style, network_path)?; + let path_string = vector_wire.to_svg(); + let data_type = self + .upstream_output_connector(input, network_path) + .as_ref() + .map(|output| self.output_type(output, network_path).displayed_type()) + .unwrap_or(FrontendGraphDataType::General); + Some(WirePathOld { + path_string, + data_type, + thick, + dashed, + }) + } + + pub fn collect_layer_widths_old(&mut self, network_path: &[NodeId]) -> (HashMap, HashMap, HashMap) { + let Some(network_metadata) = self.network_metadata(network_path) else { + log::error!("Could not get nested network_metadata in collect_layer_widths"); + return (HashMap::new(), HashMap::new(), HashMap::new()); + }; + let nodes = network_metadata + .persistent_metadata + .node_metadata + .iter() + .filter_map(|(node_id, _)| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None }) + .collect::>(); + let layer_widths = nodes + .iter() + .filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width))) + .collect::>(); + let chain_widths = nodes.iter().map(|node_id| (*node_id, self.chain_width(node_id, network_path))).collect::>(); + let has_left_input_wire = nodes + .iter() + .map(|node_id| { + ( + *node_id, + !self + .upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow) + .skip(1) + .all(|node_id| self.is_chain(&node_id, network_path)), + ) + }) + .collect::>(); + + (layer_widths, chain_widths, has_left_input_wire) + } +} diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs new file mode 100644 index 0000000000..225eaad8b9 --- /dev/null +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs @@ -0,0 +1,364 @@ +use std::collections::{HashMap, HashSet}; + +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::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector}; + +// This file contains utility methods for interfacing with the resolved types returned from the compiler +#[derive(Debug, Default)] +pub struct ResolvedDocumentNodeTypes { + pub types: HashMap, NodeTypes>, + pub node_graph_errors: GraphErrors, +} + +impl ResolvedDocumentNodeTypes { + pub fn update(&mut self, delta: ResolvedDocumentNodeTypesDelta, errors: GraphErrors) { + for (path, node_type) in delta.add { + self.types.insert(path.to_vec(), node_type); + } + for path in delta.remove { + self.types.remove(&path.to_vec()); + } + self.node_graph_errors = errors; + } +} + +/// Represents the result of a type query for an input or output connector. +#[derive(Debug, Clone, PartialEq)] +pub enum TypeSource { + /// A type that has been compiled based on all upstream types. + Compiled(Type), + /// The type of value inputs. + TaggedValue(Type), + /// When the input/output is not compiled. The Type is from the document node definition, or () if it doesn't exist. + Unknown, + /// When there is a node graph error for the inputs to a node. The Type is from the document node definition, or () if it doesn't exist. + Invalid, + /// When there is an error in the algorithm for determining the input/output type (indicates a bug in the editor). + Error(&'static str), +} + +impl TypeSource { + /// The reduced set of frontend types for displaying color. + pub fn displayed_type(&self) -> FrontendGraphDataType { + if matches!(self, TypeSource::Invalid) { + return FrontendGraphDataType::Invalid; + }; + match self.compiled_nested_type() { + Some(nested_type) => match TaggedValue::from_type_or_none(nested_type) { + TaggedValue::U32(_) + | TaggedValue::U64(_) + | TaggedValue::F32(_) + | TaggedValue::F64(_) + | TaggedValue::DVec2(_) + | TaggedValue::F64Array4(_) + | TaggedValue::VecF64(_) + | TaggedValue::VecDVec2(_) + | TaggedValue::DAffine2(_) => FrontendGraphDataType::Number, + TaggedValue::Artboard(_) => FrontendGraphDataType::Artboard, + TaggedValue::Graphic(_) => FrontendGraphDataType::Graphic, + TaggedValue::Raster(_) => FrontendGraphDataType::Raster, + TaggedValue::Vector(_) => FrontendGraphDataType::Vector, + TaggedValue::Color(_) => FrontendGraphDataType::Color, + TaggedValue::Gradient(_) | TaggedValue::GradientStops(_) | TaggedValue::GradientTable(_) => FrontendGraphDataType::Gradient, + TaggedValue::String(_) => FrontendGraphDataType::Typography, + _ => FrontendGraphDataType::General, + }, + None => FrontendGraphDataType::General, + } + } + + pub fn compiled_nested_type(&self) -> Option<&Type> { + match self { + TypeSource::Compiled(compiled_type) => Some(compiled_type.nested_type()), + TypeSource::TaggedValue(value_type) => Some(value_type.nested_type()), + _ => None, + } + } + + /// Used when searching for nodes in the add Node popup. + pub fn add_node_string(self) -> Option { + self.compiled_nested_type().map(|ty| format!("type:{}", ty.to_string())) + } + + /// The type to display in the tooltip. + pub fn resolved_type_tooltip_string(&self) -> String { + match self { + TypeSource::Compiled(compiled_type) => format!("Data Type: {:?}", compiled_type.nested_type_name().to_string()), + TypeSource::TaggedValue(value_type) => format!("Data Type: {:?}", value_type.nested_type_name().to_string()), + TypeSource::Unknown => "Unknown Data Type".to_string(), + TypeSource::Invalid => "Invalid Type Combination".to_string(), + TypeSource::Error(_) => "Error Getting Data Type".to_string(), + } + } + + /// The type to display in the node row. + pub fn resolved_type_node_string(&self) -> String { + match self { + TypeSource::Compiled(compiled_type) => compiled_type.nested_type_name().to_string(), + TypeSource::TaggedValue(value_type) => value_type.nested_type_name().to_string(), + TypeSource::Unknown => "Unknown".to_string(), + TypeSource::Invalid => "Invalid".to_string(), + TypeSource::Error(_) => "Error".to_string(), + } + } +} + +impl NodeNetworkInterface { + fn input_has_error(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> bool { + match input_connector { + InputConnector::Node { node_id, input_index } => { + let Some(implementation) = self.implementation(node_id, network_path) else { + log::error!("Could not get implementation in input_has_error"); + return false; + }; + let node_path = [network_path, &[*node_id]].concat(); + match implementation { + DocumentNodeImplementation::Network(_) => { + let Some(map) = self.outward_wires(&node_path) else { return false }; + let Some(outward_wires) = map.get(&OutputConnector::Import(*input_index)) else { return false }; + outward_wires.clone().iter().any(|connector| match connector { + InputConnector::Node { node_id, input_index } => self.input_has_error(&InputConnector::node(*node_id, *input_index), &node_path), + InputConnector::Export(_) => false, + }) + } + DocumentNodeImplementation::ProtoNode(_) => self.resolved_types.node_graph_errors.iter().any(|error| error.node_path == node_path), + DocumentNodeImplementation::Extract => false, + } + } + InputConnector::Export(_) => false, + } + } + + /// Get the [`TypeSource`] for any InputConnector. + /// If the input is not compiled, then an Unknown or default from the definition is returned. + pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TypeSource { + let Some(input) = self.input_from_connector(input_connector, network_path) else { + return TypeSource::Error("Could not get input from connector"); + }; + + match input { + NodeInput::Node { node_id, output_index } => { + let output_connector = OutputConnector::node(*node_id, *output_index); + // First check if there is an error with this node or any protonodes it is connected to + if self.input_has_error(input_connector, network_path) { + return TypeSource::Invalid; + } + self.output_type(&output_connector, network_path) + } + + NodeInput::Value { tagged_value, .. } => TypeSource::TaggedValue(tagged_value.ty()), + NodeInput::Import { import_index, .. } => { + // Get the input type of the encapsulating node input + let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else { + return TypeSource::Error("Could not get type of import in document network since it has no imports"); + }; + self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path) + } + NodeInput::Scope(_) => TypeSource::Compiled(concrete!(())), + NodeInput::Reflection(document_node_metadata) => TypeSource::Compiled(document_node_metadata.ty()), + NodeInput::Inline(_) => TypeSource::Compiled(concrete!(InlineRust)), + } + } + + // Gets the default tagged value for an input. If its not compiled, then it tries to get a valid type. If there are no valid types, then it picks a random implementation + pub fn tagged_value_from_input(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TaggedValue { + let guaranteed_type = match self.input_type(input_connector, network_path) { + TypeSource::Compiled(compiled) => compiled, + TypeSource::TaggedValue(value) => value, + TypeSource::Unknown | TypeSource::Invalid => { + let mut ret = concrete!(()); + if let InputConnector::Node { node_id, input_index } = input_connector { + if let Some(definition) = self.get_node_definition(node_id, network_path) { + if let Some(value) = definition.node_template.document_node.inputs.get(*input_index).and_then(|input| input.as_value()) { + ret = value.ty(); + } + } + } + ret + } + TypeSource::Error(e) => { + log::error!("Error getting tagged_value_from_input for {input_connector:?} {e}"); + concrete!(()) + } + }; + TaggedValue::from_type_or_none(&guaranteed_type) + } + + /// A list of all valid input types for this specific node. + pub fn potential_valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec { + let InputConnector::Node { node_id, input_index } = input_connector else { + // An export can have any type connected to it + return vec![graph_craft::generic!(T)]; + }; + let Some(implementation) = self.implementation(node_id, network_path) else { + log::error!("Could not get node implementation in potential_valid_input_types"); + return Vec::new(); + }; + match implementation { + DocumentNodeImplementation::Network(_) => { + let nested_path = [network_path, &[*node_id]].concat(); + let Some(outward_wires) = self.outward_wires(&nested_path) else { + log::error!("Could not get outward wires in potential_valid_input_types"); + return Vec::new(); + }; + let Some(inputs_from_import) = outward_wires.get(&OutputConnector::Import(*input_index)) else { + log::error!("Could not get inputs from import in potential_valid_input_types"); + return Vec::new(); + }; + + let intersection: HashSet = inputs_from_import + .clone() + .iter() + .map(|input_connector| self.potential_valid_input_types(input_connector, &nested_path).into_iter().collect::>()) + .fold(None, |acc: Option>, set| match acc { + Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()), + None => Some(set), + }) + .unwrap_or_default(); + + intersection.into_iter().collect::>() + } + DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { + let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { + log::error!("Protonode {proto_node_identifier:?} not found in registry in potential_valid_input_types"); + return Vec::new(); + }; + let number_of_inputs = self.number_of_inputs(node_id, network_path); + implementations + .iter() + .filter_map(|(node_io, _)| { + let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| { + let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path); + // TODO: Fix type checking for different call arguments + // For example a node input of (Footprint) -> Vector would not be compatible with a node that is called with () and returns Vector + node_io.inputs.get(iterator_index).map(|ty| ty.nested_type()) == input_type.compiled_nested_type() + }); + if valid_implementation { node_io.inputs.get(*input_index).cloned() } else { None } + }) + .collect::>() + } + DocumentNodeImplementation::Extract => { + log::error!("Input types for extract node not supported"); + Vec::new() + } + } + } + + /// Performs a downstream traversal to ensure input type will work in the full context of the graph. + pub fn complete_valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Result, String> { + match input_connector { + InputConnector::Node { node_id, input_index } => { + let Some(implementation) = self.implementation(node_id, network_path) else { + return Err(format!("Could not get node implementation for {:?} {} in complete_valid_input_types", network_path, *node_id)); + }; + match implementation { + DocumentNodeImplementation::Network(_) => self.valid_output_types(&OutputConnector::Import(input_connector.input_index()), &[network_path, &[*node_id]].concat()), + DocumentNodeImplementation::ProtoNode(proto_node_identifier) => { + let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else { + return Err(format!("Protonode {proto_node_identifier:?} not found in registry in complete_valid_input_types")); + }; + let valid_output_types = match self.valid_output_types(&OutputConnector::node(*node_id, 0), network_path) { + Ok(valid_types) => valid_types, + Err(e) => return Err(e), + }; + + let valid_types = implementations + .iter() + .filter_map(|(node_io, _)| { + if !valid_output_types.iter().any(|output_type| output_type.nested_type() == node_io.return_value.nested_type()) { + return None; + } + + let valid_inputs = (0..node_io.inputs.len()).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| { + let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path); + match input_type.compiled_nested_type() { + Some(input_type) => node_io.inputs.get(iterator_index).is_some_and(|node_io_input_type| node_io_input_type.nested_type() == input_type), + None => true, + } + }); + if valid_inputs { node_io.inputs.get(*input_index).cloned() } else { None } + }) + .collect::>(); + Ok(valid_types) + } + DocumentNodeImplementation::Extract => { + log::error!("Input types for extract node not supported"); + Ok(Vec::new()) + } + } + } + InputConnector::Export(export_index) => { + match network_path.split_last() { + Some((encapsulating_node, encapsulating_path)) => self.valid_output_types(&OutputConnector::node(*encapsulating_node, *export_index), encapsulating_path), + None => { + // Valid types for the export are all types that can be fed into the render node + let render_node = graphene_std::render_node::render::IDENTIFIER; + let Some(implementations) = NODE_REGISTRY.get(&render_node) else { + return Err(format!("Protonode {render_node:?} not found in registry")); + }; + Ok(implementations.iter().map(|(types, _)| types.inputs[1].clone()).collect()) + } + } + } + } + } + + pub fn output_type(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> TypeSource { + match output_connector { + OutputConnector::Node { node_id, output_index } => { + // First try iterating upstream to the first protonode and try get its compiled type + let Some(implementation) = self.implementation(node_id, network_path) else { + return TypeSource::Error("Could not get implementation"); + }; + match implementation { + DocumentNodeImplementation::Network(_) => self.input_type(&InputConnector::Export(*output_index), &[network_path, &[*node_id]].concat()), + DocumentNodeImplementation::ProtoNode(_) => match self.resolved_types.types.get(&[network_path, &[*node_id]].concat()) { + Some(resolved_type) => TypeSource::Compiled(resolved_type.output.clone()), + None => TypeSource::Unknown, + }, + DocumentNodeImplementation::Extract => TypeSource::Compiled(concrete!(())), + } + } + OutputConnector::Import(import_index) => { + let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else { + return TypeSource::Error("Cannot get import type in document network since it has no imports"); + }; + let mut input_type = self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path); + if matches!(input_type, TypeSource::Invalid) { + input_type = TypeSource::Unknown + } + return input_type; + } + } + } + + /// The valid output types are all types that are valid for each downstream connection. + fn valid_output_types(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Result, String> { + let Some(outward_wires) = self.outward_wires(&network_path) else { + return Err("Could not get outward wires in valid_output_types".to_string()); + }; + let Some(inputs_from_import) = outward_wires.get(output_connector) else { + return Err("Could not get inputs from import in valid_output_types".to_string()); + }; + + let intersection = inputs_from_import + .clone() + .iter() + .map(|input_connector| self.potential_valid_input_types(input_connector, &network_path).into_iter().collect::>()) + .fold(None, |acc: Option>, set| match acc { + Some(acc_set) => Some(acc_set.intersection(&set).cloned().collect()), + None => Some(set), + }) + .unwrap_or_default(); + + Ok(intersection.into_iter().collect::>()) + } +} diff --git a/editor/src/messages/portfolio/document/utility_types/wires.rs b/editor/src/messages/portfolio/document/utility_types/wires.rs index ad6ab32843..37f135f03b 100644 --- a/editor/src/messages/portfolio/document/utility_types/wires.rs +++ b/editor/src/messages/portfolio/document/utility_types/wires.rs @@ -1,10 +1,10 @@ -use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType; use glam::{DVec2, IVec2}; +use graphene_std::node_graph_overlay::types::FrontendGraphDataType; use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point}; use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape}; #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct WirePath { +pub struct WirePathOld { #[serde(rename = "pathString")] pub path_string: String, #[serde(rename = "dataType")] @@ -14,13 +14,21 @@ pub struct WirePath { } #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct WirePathUpdate { +pub struct WirePathUpdateOld { pub id: NodeId, #[serde(rename = "inputIndex")] pub input_index: usize, // If none, then remove the wire from the map #[serde(rename = "wirePathUpdate")] - pub wire_path_update: Option, + pub wire_path_update: Option, +} + +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct WirePathInProgress { + pub wire: String, + #[serde(rename = "dataType")] + pub data_type: FrontendGraphDataType, + pub thick: bool, } #[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] diff --git a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs index 9b86761d87..aa13ea5c44 100644 --- a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs +++ b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs @@ -18,6 +18,7 @@ pub struct MenuBarMessageHandler { pub has_selection_history: (bool, bool), pub message_logging_verbosity: MessageLoggingVerbosity, pub reset_node_definitions_on_open: bool, + pub render_native_node_graph: bool, pub make_path_editable_is_allowed: bool, pub data_panel_open: bool, pub layers_panel_open: bool, @@ -48,6 +49,7 @@ impl LayoutHolder for MenuBarMessageHandler { let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names; let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents; let reset_node_definitions_on_open = self.reset_node_definitions_on_open; + let render_native_node_graph = self.render_native_node_graph; let make_path_editable_is_allowed = self.make_path_editable_is_allowed; let menu_bar_entries = vec![ @@ -696,6 +698,12 @@ impl LayoutHolder for MenuBarMessageHandler { action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen.into()), ..MenuBarEntry::default() }], + vec![MenuBarEntry { + label: " Render Native Node Graph UI".into(), + icon: Some(if render_native_node_graph { "CheckboxChecked" } else { "CheckboxUnchecked" }.into()), + action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleRenderNativeNodeGraph.into()), + ..MenuBarEntry::default() + }], vec![ MenuBarEntry { label: "Print Trace Logs".into(), diff --git a/editor/src/messages/portfolio/portfolio_message.rs b/editor/src/messages/portfolio/portfolio_message.rs index 58209fe649..aa0b062c87 100644 --- a/editor/src/messages/portfolio/portfolio_message.rs +++ b/editor/src/messages/portfolio/portfolio_message.rs @@ -81,6 +81,7 @@ pub enum PortfolioMessage { to_front: bool, select_after_open: bool, }, + ToggleRenderNativeNodeGraph, ToggleResetNodesToDefinitionsOnOpen, PasteIntoFolder { clipboard: Clipboard, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index f034d85a65..72900c5826 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -63,6 +63,7 @@ pub struct PortfolioMessageHandler { pub layers_panel_open: bool, #[derivative(Default(value = "true"))] pub properties_panel_open: bool, + pub render_native_node_graph: bool, } #[message_handler_data] @@ -111,6 +112,7 @@ impl MessageHandler> for Portfolio (!metadata.selection_undo_history.is_empty(), !metadata.selection_redo_history.is_empty()) }; self.menu_bar_message_handler.make_path_editable_is_allowed = make_path_editable_is_allowed(&mut document.network_interface).is_some(); + self.menu_bar_message_handler.render_native_node_graph = self.render_native_node_graph; } self.menu_bar_message_handler.process_message(message, responses, ()); @@ -129,6 +131,7 @@ impl MessageHandler> for Portfolio data_panel_open: self.data_panel_open, layers_panel_open: self.layers_panel_open, properties_panel_open: self.properties_panel_open, + render_native_node_graph: self.render_native_node_graph, }; document.process_message(message, responses, document_inputs) } @@ -170,6 +173,7 @@ impl MessageHandler> for Portfolio data_panel_open: self.data_panel_open, layers_panel_open: self.layers_panel_open, properties_panel_open: self.properties_panel_open, + render_native_node_graph: self.render_native_node_graph, }; document.process_message(message, responses, document_inputs) } @@ -442,6 +446,13 @@ impl MessageHandler> for Portfolio select_after_open: true, }); } + PortfolioMessage::ToggleRenderNativeNodeGraph => { + self.render_native_node_graph = !self.render_native_node_graph; + responses.add(FrontendMessage::UpdateRenderNativeNodeGraph { + render_native_node_graph: self.render_native_node_graph, + }); + responses.add(NodeGraphMessage::SendGraph); + } PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen => { self.reset_node_definitions_on_open = !self.reset_node_definitions_on_open; responses.add(MenuBarMessage::SendLayout); @@ -914,8 +925,8 @@ impl MessageHandler> for Portfolio responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open }); if node_graph_open { responses.add(NodeGraphMessage::UpdateGraphBarRight); - responses.add(NodeGraphMessage::UnloadWires); - responses.add(NodeGraphMessage::SendWires) + responses.add(NodeGraphMessage::UnloadWiresOld); + responses.add(NodeGraphMessage::SendWiresOld) } else { responses.add(PortfolioMessage::UpdateDocumentWidgets); } diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index 8c61fa78af..0c0c0164fe 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -87,8 +87,7 @@ impl MessageHandler for PreferencesMessageHandler { } PreferencesMessage::GraphWireStyle { style } => { self.graph_wire_style = style; - responses.add(NodeGraphMessage::UnloadWires); - responses.add(NodeGraphMessage::SendWires); + responses.add(NodeGraphMessage::SendGraph); } PreferencesMessage::ViewportZoomWheelRate { rate } => { self.viewport_zoom_wheel_rate = rate; diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 0c2d87a1f0..c0d1081501 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -484,8 +484,8 @@ impl<'a> NodeGraphLayer<'a> { /// Check if a layer is a raster layer pub fn is_raster_layer(layer: LayerNodeIdentifier, network_interface: &mut NodeNetworkInterface) -> bool { - let layer_input_type = network_interface.input_type(&InputConnector::node(layer.to_node(), 1), &[]).0.nested_type().clone(); + let layer_input_type = network_interface.input_type(&InputConnector::node(layer.to_node(), 1), &[]); - layer_input_type == concrete!(Table>) || layer_input_type == concrete!(Table>) + layer_input_type.compiled_nested_type() == Some(&concrete!(Table>)) || layer_input_type.compiled_nested_type() == Some(&concrete!(Table>)) } } diff --git a/editor/src/messages/tool/common_functionality/utility_functions.rs b/editor/src/messages/tool/common_functionality/utility_functions.rs index ada904527e..25c96db9fb 100644 --- a/editor/src/messages/tool/common_functionality/utility_functions.rs +++ b/editor/src/messages/tool/common_functionality/utility_functions.rs @@ -581,8 +581,8 @@ pub fn make_path_editable_is_allowed(network_interface: &mut NodeNetworkInterfac // Must be a layer of type Table let node_id = NodeGraphLayer::new(first_layer, network_interface).horizontal_layer_flow().nth(1)?; - let (output_type, _) = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); - if output_type.nested_type() != concrete!(Table).nested_type() { + let output_type = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]); + if output_type.compiled_nested_type().as_deref() != Some(&concrete!(Table)) { return None; } diff --git a/editor/src/messages/viewport/viewport_message_handler.rs b/editor/src/messages/viewport/viewport_message_handler.rs index d562917715..c291839575 100644 --- a/editor/src/messages/viewport/viewport_message_handler.rs +++ b/editor/src/messages/viewport/viewport_message_handler.rs @@ -33,6 +33,7 @@ impl MessageHandler for ViewportMessageHandler { offset: Point { x, y }, size: Point { x: width, y: height }, }; + responses.add(NodeGraphMessage::UpdateNodeGraphWidth); } ViewportMessage::RepropagateUpdate => {} } @@ -46,7 +47,6 @@ impl MessageHandler for ViewportMessageHandler { }); responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO }); - responses.add(NodeGraphMessage::SetGridAlignedEdges); responses.add(DeferMessage::AfterGraphRun { messages: vec![ diff --git a/editor/src/test_utils.rs b/editor/src/test_utils.rs index 3b8a568d04..d1fca3a05c 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -326,11 +326,11 @@ pub trait FrontendMessageTestUtils { impl FrontendMessageTestUtils for FrontendMessage { fn check_node_graph_error(&self) { - let FrontendMessage::UpdateNodeGraphNodes { nodes, .. } = self else { return }; + let FrontendMessage::UpdateNodeGraphRenderNew { nodes_to_render, .. } = self else { return }; - for node in nodes { - if let Some(error) = &node.errors { - panic!("error on {}: {}", node.display_name, error); + for node in nodes_to_render { + if let Some(error) = &node.metadata.errors { + panic!("error on {}: {}", node.metadata.display_name, error); } } } diff --git a/frontend/src/components/Editor.svelte b/frontend/src/components/Editor.svelte index 9a965338a2..2761fdd8f5 100644 --- a/frontend/src/components/Editor.svelte +++ b/frontend/src/components/Editor.svelte @@ -129,6 +129,8 @@ --color-data-gradient-dim: #6c489b; --color-data-typography: #eea7a7; --color-data-typography-dim: #955252; + --color-data-invalid: var(--color-error-red); + --color-data-invalid-dim: color-mix(in srgb, var(--color-error-red) 75%, black); --color-none: white; --color-none-repeat: no-repeat; diff --git a/frontend/src/components/panels/Document.svelte b/frontend/src/components/panels/Document.svelte index e6e5282d53..454fe5ae32 100644 --- a/frontend/src/components/panels/Document.svelte +++ b/frontend/src/components/panels/Document.svelte @@ -569,7 +569,7 @@ {/if} -
+
@@ -592,7 +592,6 @@ thumbPosition={scrollbarPos.x} on:trackShift={({ detail }) => editor.handle.panCanvasByFraction(detail, 0)} on:thumbPosition={({ detail }) => panCanvasX(detail)} - on:thumbDragEnd={() => editor.handle.setGridAlignedEdges()} on:thumbDragStart={() => editor.handle.panCanvasAbortPrepare(true)} on:thumbDragAbort={() => editor.handle.panCanvasAbort(true)} /> @@ -769,7 +768,7 @@ .viewport-container-inner-2 { flex: 1 1 100%; position: relative; - + overflow: hidden; .viewport { background: var(--color-2-mildblack); } @@ -853,7 +852,6 @@ } } - .fade-artwork, .graph { position: absolute; top: 0; diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index 7ac2d4f162..2bc0be74b3 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -4,7 +4,7 @@ import { fade } from "svelte/transition"; import type { Editor } from "@graphite/editor"; - import type { FrontendGraphInput, FrontendGraphOutput } from "@graphite/messages"; + import { type FrontendGraphInputOld, type FrontendGraphOutputOld, type FrontendGraphInputNew, type FrontendGraphOutputNew, UpdateRenderNativeNodeGraph } from "@graphite/messages"; import type { NodeGraphState } from "@graphite/state-providers/node-graph"; import type { IconName } from "@graphite/utility-functions/icons"; @@ -27,9 +27,6 @@ let graph: HTMLDivElement | undefined; - // Key value is node id + input/output index - // Imports/Export are stored at a key value of 0 - $: gridSpacing = calculateGridSpacing($nodeGraph.transform.scale); $: gridDotRadius = 1 + Math.floor($nodeGraph.transform.scale - 0.5 + 0.001) / 2; @@ -115,15 +112,6 @@ return iconMap[icon] || "NodeNodes"; } - function toggleLayerDisplay(displayAsLayer: boolean, toggleId: bigint) { - let node = $nodeGraph.nodes.get(toggleId); - if (node) editor.handle.setToNodeOrLayer(node.id, displayAsLayer); - } - - function canBeToggledBetweenNodeAndLayer(toggleDisplayAsLayerNodeId: bigint) { - return $nodeGraph.nodes.get(toggleDisplayAsLayerNodeId)?.canBeLayer || false; - } - function createNode(nodeType: string) { if ($nodeGraph.contextMenuInformation === undefined) return; @@ -176,26 +164,26 @@ return `M-2,-2 L${nodeWidth + 2},-2 L${nodeWidth + 2},${nodeHeight + 2} L-2,${nodeHeight + 2}z ${rectangles.join(" ")}`; } - function dataTypeTooltip(value: FrontendGraphInput | FrontendGraphOutput): string { - return `Data Type: ${value.resolvedType}`; - } + // function dataTypeTooltip(value: FrontendGraphInput | FrontendGraphOutput): string { + // return `Data Type: ${value.resolvedType}`; + // } - function validTypesText(value: FrontendGraphInput): string { - const validTypes = value.validTypes.length > 0 ? value.validTypes.map((x) => `• ${x}`).join("\n") : "None"; - return `Valid Types:\n${validTypes}`; - } + // function validTypesText(value: FrontendGraphInput): string { + // const validTypes = value.validTypes.length > 0 ? value.validTypes.map((x) => `• ${x}`).join("\n") : "None"; + // return `Valid Types:\n${validTypes}`; + // } - function outputConnectedToText(output: FrontendGraphOutput): string { - if (output.connectedTo.length === 0) return "Connected to nothing"; + // function outputConnectedToText(output: FrontendGraphOutput): string { + // if (output.connectedTo.length === 0) return "Connected to nothing"; - return `Connected to:\n${output.connectedTo.join("\n")}`; - } + // return `Connected to:\n${output.connectedTo.join("\n")}`; + // } - function inputConnectedToText(input: FrontendGraphInput): string { - return `Connected to:\n${input.connectedTo}`; - } + // function inputConnectedToText(input: FrontendGraphInput): string { + // return `Connected to:\n${input.connectedTo}`; + // } - function zipWithUndefined(arr1: FrontendGraphInput[], arr2: FrontendGraphOutput[]) { + function zipWithUndefined(arr1: FrontendGraphInputOld[], arr2: FrontendGraphOutputOld[]) { const maxLength = Math.max(arr1.length, arr2.length); const result = []; for (let i = 0; i < maxLength; i++) { @@ -205,298 +193,31 @@ } -
- - {#if $nodeGraph.contextMenuInformation} - - {#if typeof $nodeGraph.contextMenuInformation.contextMenuData === "string" && $nodeGraph.contextMenuInformation.contextMenuData === "CreateNode"} - createNode(e.detail)} /> - {:else if $nodeGraph.contextMenuInformation.contextMenuData && "compatibleType" in $nodeGraph.contextMenuInformation.contextMenuData} - createNode(e.detail)} /> - {:else} - {@const contextMenuData = $nodeGraph.contextMenuInformation.contextMenuData} - - Display as - toggleLayerDisplay(false, contextMenuData.nodeId), - }, - { - value: "layer", - label: "Layer", - action: () => toggleLayerDisplay(true, contextMenuData.nodeId), - }, - ]} - disabled={!canBeToggledBetweenNodeAndLayer(contextMenuData.nodeId)} - /> - - - - editor.handle.mergeSelectedNodes()} /> - - {/if} - - {/if} - - - {#if $nodeGraph.clickTargets} -
- - {#each $nodeGraph.clickTargets.nodeClickTargets as pathString} - - {/each} - {#each $nodeGraph.clickTargets.layerClickTargets as pathString} - - {/each} - {#each $nodeGraph.clickTargets.connectorClickTargets as pathString} - - {/each} - {#each $nodeGraph.clickTargets.iconClickTargets as pathString} - - {/each} - - - {#each $nodeGraph.clickTargets.modifyImportExport as pathString} - - {/each} - -
- {/if} - - -
- - {#each $nodeGraph.wires.values() as map} - {#each map.values() as { pathString, dataType, thick, dashed }} - {#if thick} - - {/if} - {/each} - {/each} - -
- - -
- {#if $nodeGraph.updateImportsExports} - {#each $nodeGraph.updateImportsExports.imports as frontendOutput, index} - {#if frontendOutput} - - {`${dataTypeTooltip(frontendOutput)}\n\n${outputConnectedToText(frontendOutput)}`} - {#if frontendOutput.connectedTo.length > 0} - - {:else} - - {/if} - - -
(hoveringImportIndex = index)} - on:pointerleave={() => (hoveringImportIndex = undefined)} - class="edit-import-export import" - class:separator-bottom={index === 0 && $nodeGraph.updateImportsExports.addImportExport} - class:separator-top={index === 1 && $nodeGraph.updateImportsExports.addImportExport} - style:--offset-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24} - style:--offset-top={($nodeGraph.updateImportsExports.importPosition.y - 8) / 24 + index} - > - {#if editingNameImportIndex == index} - e.key === "Enter" && setEditingImportName(e)} - /> - {:else} -

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

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

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

- {/if} -
- {:else} -
- editor.handle.addPrimaryExport()} /> -
- {/if} - {/each} - - {#if $nodeGraph.updateImportsExports.addImportExport == true} -
- editor.handle.addSecondaryImport()} /> -
-
- editor.handle.addSecondaryExport()} /> -
- {/if} - - {#if $nodeGraph.reorderImportIndex !== undefined} - {@const position = { - x: Number($nodeGraph.updateImportsExports.importPosition.x), - y: Number($nodeGraph.updateImportsExports.importPosition.y) + Number($nodeGraph.reorderImportIndex) * 24, - }} -
- {/if} - - {#if $nodeGraph.reorderExportIndex !== undefined} - {@const position = { - x: Number($nodeGraph.updateImportsExports.exportPosition.x), - y: Number($nodeGraph.updateImportsExports.exportPosition.y) + Number($nodeGraph.reorderExportIndex) * 24, - }} -
- {/if} - {/if} -
+{#if !$nodeGraph.renderNativeNodeGraphOld} +
- {#each Array.from($nodeGraph.nodes) - .filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) + {#each Array.from($nodeGraph.nodesOld) + .filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodesOld.has(nodeId)) .map(([_, node], nodeIndex) => ({ node, nodeIndex })) as { node, nodeIndex } (nodeIndex)} {@const clipPathId = String(Math.random()).substring(2)} {@const stackDataInput = node.exposedInputs[0]} - {@const layerAreaWidth = $nodeGraph.layerWidths.get(node.id) || 8} - {@const layerChainWidth = $nodeGraph.chainWidths.get(node.id) || 0} - {@const hasLeftInputWire = $nodeGraph.hasLeftInputWire.get(node.id) || false} - {@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined} + {@const layerAreaWidth = $nodeGraph.layerWidthsOld.get(node.id) || 8} + {@const layerChainWidth = $nodeGraph.chainWidthsOld.get(node.id) || 0} + {@const hasLeftInputWire = $nodeGraph.hasLeftInputWireOld.get(node.id) || false}
- {#if node.errors} - {node.errors} - {node.errors} - {/if}
{#if $nodeGraph.thumbnails.has(node.id)} {@html $nodeGraph.thumbnails.get(node.id)} @@ -528,7 +244,6 @@ style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(node.primaryOutput)}\n\n${outputConnectedToText(node.primaryOutput)}`} {#if node.primaryOutput.connectedTo.length > 0} {#if node.primaryOutputConnectedToLayer} @@ -549,9 +264,6 @@ style:--data-color={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()})`} style:--data-color-dim={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()}-dim)`} > - {#if node.primaryInput} - {`${dataTypeTooltip(node.primaryInput)}\n\n${validTypesText(node.primaryInput)}\n\n${inputConnectedToText(node.primaryInput)}`} - {/if} {#if node.primaryInput?.connectedTo !== "nothing"} {#if node.primaryInputConnectedToLayer} @@ -574,7 +286,6 @@ style:--data-color={`var(--color-data-${stackDataInput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${stackDataInput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(stackDataInput)}\n\n${validTypesText(stackDataInput)}\n\n${inputConnectedToText(stackDataInput)}`} {#if stackDataInput.connectedTo !== undefined} {:else} @@ -587,7 +298,7 @@ {node.displayName}
-
+
{/each} - +
- {#each $nodeGraph.wires.values() as map} + {#each $nodeGraph.wiresOld.values() as map} {#each map.values() as { pathString, dataType, thick, dashed }} - {#if !thick} - - {/if} + {/each} {/each} - {#if $nodeGraph.wirePathInProgress} - - {/if}
- - {#each Array.from($nodeGraph.nodes) - .filter(([nodeId, node]) => !node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) + {#each Array.from($nodeGraph.nodesOld) + .filter(([nodeId, node]) => !node.isLayer && $nodeGraph.visibleNodesOld.has(nodeId)) .map(([_, node], nodeIndex) => ({ node, nodeIndex })) as { node, nodeIndex } (nodeIndex)} {@const exposedInputsOutputs = zipWithUndefined(node.exposedInputs, node.exposedOutputs)} {@const clipPathId = String(Math.random()).substring(2)} - {@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
- {#if node.errors} - {node.errors} - {node.errors} - {/if} -
+
{node.displayName}
{#if exposedInputsOutputs.length > 0} -
+
{#each exposedInputsOutputs as [input, output]}
- + {input !== undefined ? input.name : output.name}
@@ -692,7 +385,6 @@ style:--data-color={`var(--color-data-${node.primaryInput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${node.primaryInput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(node.primaryInput)}\n\n${validTypesText(node.primaryInput)}\n\n${inputConnectedToText(node.primaryInput)}`} {#if node.primaryInput.connectedTo !== undefined} {:else} @@ -711,7 +403,6 @@ style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(secondary)}\n\n${validTypesText(secondary)}\n\n${inputConnectedToText(secondary)}`} {#if secondary.connectedTo !== undefined} {:else} @@ -733,7 +424,6 @@ style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(node.primaryOutput)}\n\n${outputConnectedToText(node.primaryOutput)}`} {#if node.primaryOutput.connectedTo !== undefined} {:else} @@ -751,7 +441,6 @@ style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`} style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`} > - {`${dataTypeTooltip(secondary)}\n\n${outputConnectedToText(secondary)}`} {#if secondary.connectedTo !== undefined} {:else} @@ -773,34 +462,339 @@
{/each}
-
- - - -{#if $nodeGraph.box} -
{/if} -