diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 3dcbd72736..0b8c27796d 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -8,8 +8,6 @@ pub const IMPORTS_TO_LEFT_EDGE_PIXEL_GAP: u32 = 120; // Viewport pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = (1. / 600.) * 3.; pub const VIEWPORT_ZOOM_MOUSE_RATE: f64 = 1. / 400.; -pub const VIEWPORT_ZOOM_SCALE_MIN: f64 = 0.000_000_1; -pub const VIEWPORT_ZOOM_SCALE_MAX: f64 = 10_000.; pub const VIEWPORT_ZOOM_MIN_FRACTION_COVER: f64 = 0.01; pub const VIEWPORT_ZOOM_LEVELS: [f64; 74] = [ 0.0001, 0.000125, 0.00016, 0.0002, 0.00025, 0.00032, 0.0004, 0.0005, 0.00064, 0.0008, 0.001, 0.0016, 0.002, 0.0025, 0.0032, 0.004, 0.005, 0.0064, 0.008, 0.01, 0.01125, 0.015, 0.02, 0.025, 0.03, diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 9e16a055e2..7a3563b769 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -1,5 +1,6 @@ use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog}; use crate::messages::layout::utility_types::widget_prelude::*; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::prelude::*; pub struct DialogMessageData<'a> { @@ -74,12 +75,11 @@ impl MessageHandler> for DialogMessageHandl .all_layers() .filter(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[])) .map(|layer| { - let name = document - .network_interface - .node_metadata(&layer.to_node(), &[]) - .map(|node| node.persistent_metadata.display_name.clone()) - .and_then(|name| if name.is_empty() { None } else { Some(name) }) - .unwrap_or_else(|| "Artboard".to_string()); + let display_name = document.network_interface.display_name(&layer.to_node(), &[]).cloned().unwrap_or_else(|| { + log::error!("Artboard has no display name: {:?}", layer); + "".to_string() + }); + let name = if display_name.is_empty() { "Artboard".to_string() } else { display_name }; (layer, name) }) .collect(); diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 3ecc06c8cd..0002268c69 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -2,8 +2,8 @@ use super::node_graph::utility_types::Transform; use super::utility_types::clipboards::Clipboard; use super::utility_types::error::EditorError; use super::utility_types::misc::{SnappingOptions, SnappingState, GET_SNAP_BOX_FUNCTIONS, GET_SNAP_GEOMETRY_FUNCTIONS}; -use super::utility_types::network_interface::{NodeNetworkInterface, TransactionStatus}; -use super::utility_types::nodes::{CollapsedLayers, SelectedNodes}; +use super::utility_types::network_interface::{NodeNetworkInterface, NodeNetworkPersistentMetadata, TransactionStatus}; +use super::utility_types::nodes::{CollapsedLayers, OldSelectedNodes, SelectedNodes}; use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH}; use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL}; use crate::messages::input_mapper::utility_types::macros::action_keys; @@ -13,7 +13,7 @@ use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData; use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options}; use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData; 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::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis}; use crate::messages::portfolio::document::utility_types::nodes::RawBuffer; use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::prelude::*; @@ -24,7 +24,7 @@ use crate::messages::tool::utility_types::ToolType; use crate::node_graph_executor::NodeGraphExecutor; use graph_craft::document::value::TaggedValue; -use graph_craft::document::{NodeId, NodeNetwork, OldNodeNetwork}; +use graph_craft::document::{NodeId, NodeNetwork, OldNodeNetwork, PTZ}; use graphene_core::raster::BlendMode; use graphene_core::raster::ImageFrame; use graphene_core::vector::style::ViewMode; @@ -48,7 +48,7 @@ pub struct OldDocumentMessageHandler { /// It recursively stores its sub-graphs, so this root graph is the whole snapshot of the document content. pub network: OldNodeNetwork, /// List of the [`NodeId`]s that are currently selected by the user. - pub selected_nodes: SelectedNodes, + pub selected_nodes: OldSelectedNodes, /// List of the [`LayerNodeIdentifier`]s that are currently collapsed by the user in the Layers panel. /// Collapsed means that the expansion arrow isn't set to show the children of these layers. pub collapsed: CollapsedLayers, @@ -70,7 +70,7 @@ pub struct OldDocumentMessageHandler { /// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area. pub rulers_visible: bool, /// Sets whether or not the node graph is drawn (as an overlay) on top of the viewport area, or otherwise if it's hidden. - pub graph_view_overlay_open: bool, + pub graph_ui_open: bool, /// The current user choices for snapping behavior, including whether snapping is enabled at all. pub snapping_state: SnappingState, } @@ -299,6 +299,7 @@ impl MessageHandler> for DocumentMessag } DocumentMessage::ClearLayersPanel => { // Send an empty layer list + // TODO: Dont create a new document message handler just to get the default data buffer for an empty layer structure let data_buffer: RawBuffer = Self::default().serialize_root(); responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer }); @@ -779,10 +780,8 @@ impl MessageHandler> for DocumentMessag } DocumentMessage::RenderRulers => { let current_ptz = if self.graph_view_overlay_open { - let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else { - return; - }; - &network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz + let Some(ptz) = self.network_interface.ptz(&self.breadcrumb_network_path) else { return }; + ptz } else { &self.document_ptz }; @@ -1106,14 +1105,14 @@ impl MessageHandler> for DocumentMessag })); responses.add(NodeGraphMessage::RunDocumentGraph); } else { - let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else { + let Some(ptz) = self.network_interface.ptz(&self.breadcrumb_network_path) else { return; }; - let transform = self - .navigation_handler - .calculate_offset_transform(ipp.viewport_bounds.center(), &network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz); - self.network_interface.set_transform(transform, &self.breadcrumb_network_path); + let transform = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), ptz); + + self.network_interface.set_node_graph_to_viewport(transform, &self.breadcrumb_network_path); + let imports = self.network_interface.frontend_imports(&self.breadcrumb_network_path).unwrap_or_default(); let exports = self.network_interface.frontend_exports(&self.breadcrumb_network_path).unwrap_or_default(); responses.add(DocumentMessage::RenderRulers); @@ -1328,7 +1327,7 @@ impl DocumentMessageHandler { view_mode: old_message_handler.view_mode, overlays_visible: old_message_handler.overlays_visible, rulers_visible: old_message_handler.rulers_visible, - graph_view_overlay_open: old_message_handler.graph_view_overlay_open, + graph_view_overlay_open: old_message_handler.graph_ui_open, snapping_state: old_message_handler.snapping_state, ..Default::default() }; @@ -1435,7 +1434,8 @@ impl DocumentMessageHandler { responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::ForceRunDocumentGraph); - + // TODO: Remove once the footprint is used to load the imports/export distances from the edge + responses.add(NodeGraphMessage::SetGridAlignedEdges); Some(previous_network) } pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { @@ -1464,7 +1464,8 @@ impl DocumentMessageHandler { responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::ForceRunDocumentGraph); - + // TODO: Remove once the footprint is used to load the imports/export distances from the edge + responses.add(NodeGraphMessage::SetGridAlignedEdges); Some(previous_network) } @@ -1507,7 +1508,7 @@ impl DocumentMessageHandler { .unwrap_or_else(|| self.network_interface.all_artboards().iter().next().copied().unwrap_or(LayerNodeIdentifier::ROOT_PARENT)) } - pub fn get_calculated_insert_index(metadata: &DocumentMetadata, selected_nodes: SelectedNodes, parent: LayerNodeIdentifier) -> usize { + pub fn get_calculated_insert_index(metadata: &DocumentMetadata, selected_nodes: impl SelectedNodes, parent: LayerNodeIdentifier) -> usize { parent .children(metadata) .enumerate() @@ -1979,6 +1980,7 @@ impl DocumentMessageHandler { /// Create a network interface with a single export fn default_document_network_interface() -> NodeNetworkInterface { let mut network_interface = NodeNetworkInterface::default(); + network_interface.insert_network_metadata(NodeNetworkPersistentMetadata::default(), &[]); network_interface.add_export(TaggedValue::ArtboardGroup(graphene_core::ArtboardGroup::EMPTY), -1, "".to_string(), &[]); network_interface } 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 9523c5701c..23f266108e 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 @@ -1,11 +1,11 @@ use super::transform_utils; use super::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface}; +use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers; use crate::messages::prelude::*; -use graph_craft::document::{generate_uuid, NodeId, NodeInput}; +use graph_craft::document::{generate_uuid, NodeId, NodeInput, InputConnector}; use graphene_core::renderer::Quad; use graphene_core::text::Font; use graphene_core::vector::style::{Fill, Gradient, GradientStops, GradientType, LineCap, LineJoin, Stroke}; diff --git a/editor/src/messages/portfolio/document/graph_operation/transform_utils.rs b/editor/src/messages/portfolio/document/graph_operation/transform_utils.rs index 64ae4577fb..9106fc5c1c 100644 --- a/editor/src/messages/portfolio/document/graph_operation/transform_utils.rs +++ b/editor/src/messages/portfolio/document/graph_operation/transform_utils.rs @@ -1,7 +1,7 @@ -use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface}; +use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; use bezier_rs::Subpath; -use graph_craft::document::{value::TaggedValue, NodeId, NodeInput}; +use graph_craft::document::{value::TaggedValue, InputConnector, NodeId, NodeInput}; use graphene_core::vector::PointId; use glam::{DAffine2, DVec2}; 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 a49238b1c7..416d17b3b8 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -1,12 +1,12 @@ use super::transform_utils; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::portfolio::document::utility_types::network_interface::{self, InputConnector, NodeNetworkInterface, OutputConnector}; +use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface}; use crate::messages::prelude::*; use bezier_rs::Subpath; use graph_craft::document::value::TaggedValue; -use graph_craft::document::{generate_uuid, NodeId, NodeInput}; +use graph_craft::document::{generate_uuid, InputConnector, NodeId, NodeInput, OutputConnector}; use graphene_core::raster::{BlendMode, ImageFrame}; use graphene_core::text::Font; use graphene_core::vector::brush_stroke::BrushStroke; @@ -242,7 +242,12 @@ impl<'a> ModifyInputsContext<'a> { // Take until another layer node is found (but not the first layer node) let existing_node_id = upstream .take_while(|node_id| is_traversal_start(*node_id) || !self.network_interface.is_layer(node_id, &[])) - .find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|node_reference| node_reference == reference)); + .find(|node_id| { + let Some(node_reference) = self.network_interface.reference(node_id, &[]) else { + log::error!("Node reference does not exist in ModifyInputsContext::existing_node_id"); + return false; + }; + node_reference.as_ref().is_some_and(|node_reference| node_reference == reference)}); // Create a new node if the node does not exist and update its inputs existing_node_id.or_else(|| { 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 a1929f95c2..e5298174ec 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -1,17 +1,17 @@ use crate::consts::{ - VIEWPORT_ROTATE_SNAP_INTERVAL, VIEWPORT_SCROLL_RATE, VIEWPORT_ZOOM_LEVELS, VIEWPORT_ZOOM_MIN_FRACTION_COVER, VIEWPORT_ZOOM_MOUSE_RATE, VIEWPORT_ZOOM_SCALE_MAX, VIEWPORT_ZOOM_SCALE_MIN, - VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR, VIEWPORT_ZOOM_WHEEL_RATE, + VIEWPORT_ROTATE_SNAP_INTERVAL, VIEWPORT_SCROLL_RATE, VIEWPORT_ZOOM_LEVELS, VIEWPORT_ZOOM_MIN_FRACTION_COVER, VIEWPORT_ZOOM_MOUSE_RATE, VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR, + VIEWPORT_ZOOM_WHEEL_RATE, }; use crate::messages::frontend::utility_types::MouseCursorIcon; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion}; use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition; use crate::messages::portfolio::document::navigation::utility_types::NavigationOperation; -use crate::messages::portfolio::document::utility_types::misc::PTZ; -use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; +use crate::messages::portfolio::document::utility_types::network_interface::{MetadataType, NavigationMetadataType, NodeNetworkInterface}; use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; -use graph_craft::document::NodeId; +use graph_craft::document::value::TaggedValue; +use graph_craft::document::{NodeId, PTZ}; use glam::{DAffine2, DVec2}; @@ -42,28 +42,13 @@ impl MessageHandler> for Navigation graph_view_overlay_open, } = data; - fn get_ptz<'a>(document_ptz: &'a PTZ, network_interface: &'a NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a PTZ> { - if !graph_view_overlay_open { - Some(document_ptz) - } else { - let network_metadata = network_interface.network_metadata(breadcrumb_network_path)?; - Some(&network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz) - } - } - - fn get_ptz_mut<'a>(document_ptz: &'a mut PTZ, network_interface: &'a mut NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a mut PTZ> { - if !graph_view_overlay_open { - Some(document_ptz) - } else { - let Some(node_graph_ptz) = network_interface.node_graph_ptz_mut(breadcrumb_network_path) else { - log::error!("Could not get node graph PTZ in NavigationMessageHandler process_message"); - return None; - }; - Some(node_graph_ptz) - } - } + let ptz = if !graph_view_overlay_open { + Some(*document_ptz) + } else { + network_interface.ptz(breadcrumb_network_path).cloned() + }; - let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { + let Some(mut ptz) = ptz else { log::error!("Could not get PTZ in NavigationMessageHandler process_message"); return; }; @@ -78,15 +63,10 @@ impl MessageHandler> for Navigation }); self.mouse_position = ipp.mouse.position; - let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - return; - }; + self.navigation_operation = NavigationOperation::Pan { pan_original_for_abort: ptz.pan }; } NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu } => { - let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - return; - }; // If the node graph is open, prevent tilt and instead start panning if graph_view_overlay_open { responses.add(NavigationMessage::BeginCanvasPan); @@ -117,10 +97,6 @@ impl MessageHandler> for Navigation } } NavigationMessage::BeginCanvasZoom => { - let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - return; - }; - responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn }); responses.add(FrontendMessage::UpdateInputHints { hint_data: HintData(vec![ @@ -144,26 +120,20 @@ impl MessageHandler> for Navigation self.mouse_position = ipp.mouse.position; } NavigationMessage::CanvasPan { delta } => { - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get PTZ in CanvasPan"); - return; - }; - let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz); + let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), &ptz); let transformed_delta = document_to_viewport.inverse().transform_vector2(delta); ptz.pan += transformed_delta; + Self::set_ptz(ptz, document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path); responses.add(BroadcastEvent::CanvasTransformed); responses.add(DocumentMessage::PTZUpdate); } NavigationMessage::CanvasPanByViewportFraction { delta } => { - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction"); - return; - }; - let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz); + let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), &ptz); let transformed_delta = document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size()); ptz.pan += transformed_delta; + Self::set_ptz(ptz, document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path); responses.add(DocumentMessage::PTZUpdate); } NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => { @@ -175,29 +145,19 @@ impl MessageHandler> for Navigation responses.add(NodeGraphMessage::SetGridAlignedEdges); } NavigationMessage::CanvasTiltResetAndZoomTo100Percent => { - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get mutable PTZ in CanvasTiltResetAndZoomTo100Percent"); - return; - }; ptz.tilt = 0.; ptz.set_zoom(1.); + Self::set_ptz(ptz, document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path); 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 { - log::error!("Could not get mutable PTZ in CanvasTiltSet"); - return; - }; ptz.tilt = angle_radians; + Self::set_ptz(ptz, document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path); responses.add(DocumentMessage::PTZUpdate); } NavigationMessage::CanvasZoomDecrease { center_on_mouse } => { - let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - return; - }; - let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom()).unwrap_or(&ptz.zoom()); if center_on_mouse { responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position)); @@ -205,10 +165,6 @@ impl MessageHandler> for Navigation responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale }); } NavigationMessage::CanvasZoomIncrease { center_on_mouse } => { - let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - return; - }; - let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom()).unwrap_or(&ptz.zoom()); if center_on_mouse { responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position)); @@ -227,9 +183,6 @@ impl MessageHandler> for Navigation } else { network_interface.graph_bounds_viewport_space(breadcrumb_network_path) }; - let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - return; - }; zoom_factor *= Self::clamp_zoom(ptz.zoom() * zoom_factor, document_bounds, old_zoom, ipp); @@ -245,22 +198,17 @@ impl MessageHandler> for Navigation } else { network_interface.graph_bounds_viewport_space(breadcrumb_network_path) }; - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get mutable PTZ in CanvasZoomSet"); - return; - }; - let zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); + + let zoom = zoom_factor.clamp(graphene_std::consts::VIEWPORT_ZOOM_SCALE_MIN, graphene_std::consts::VIEWPORT_ZOOM_SCALE_MAX); let zoom = zoom * Self::clamp_zoom(zoom, document_bounds, old_zoom, ipp); ptz.set_zoom(zoom); + Self::set_ptz(ptz, document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path); + responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(DocumentMessage::PTZUpdate); responses.add(NodeGraphMessage::SetGridAlignedEdges); } NavigationMessage::EndCanvasPTZ { abort_transform } => { - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get mutable PTZ in EndCanvasPTZ"); - return; - }; // If an abort was requested, reset the active PTZ value to its original state if abort_transform && self.navigation_operation != NavigationOperation::None { match self.navigation_operation { @@ -280,6 +228,8 @@ impl MessageHandler> for Navigation // Final chance to apply snapping if the key was pressed during this final frame ptz.tilt = self.snapped_tilt(ptz.tilt); ptz.set_zoom(self.snapped_zoom(ptz.zoom())); + Self::set_ptz(ptz, document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path); + responses.add(DocumentMessage::PTZUpdate); responses.add(NodeGraphMessage::SetGridAlignedEdges); // Reset the navigation operation now that it's done @@ -308,11 +258,7 @@ impl MessageHandler> for Navigation return; } - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction"); - return; - }; - let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz); + let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), &ptz); let v1 = document_to_viewport.inverse().transform_point2(DVec2::ZERO); let v2 = document_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size()); @@ -335,6 +281,7 @@ impl MessageHandler> for Navigation if prevent_zoom_past_100 && ptz.zoom() > VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR { ptz.set_zoom(1.); } + Self::set_ptz(ptz, document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path); responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(DocumentMessage::PTZUpdate); @@ -342,11 +289,7 @@ impl MessageHandler> for Navigation } NavigationMessage::FitViewportToSelection => { if let Some(bounds) = selection_bounds { - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get node graph PTZ in FitViewportToSelection"); - return; - }; - let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz); + let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), &ptz); responses.add(NavigationMessage::FitViewportToBounds { bounds: [document_to_viewport.inverse().transform_point2(bounds[0]), document_to_viewport.inverse().transform_point2(bounds[1])], prevent_zoom_past_100: false, @@ -373,10 +316,7 @@ impl MessageHandler> for Navigation tilt_raw_not_snapped + angle }; - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get mutable PTZ in Tilt"); - return; - }; + ptz.tilt = self.snapped_tilt(tilt_raw_not_snapped); let snap = ipp.keyboard.get(snap as usize); @@ -408,10 +348,7 @@ impl MessageHandler> for Navigation updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp) }; - let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { - log::error!("Could not get mutable PTZ in Zoom"); - return; - }; + ptz.set_zoom(self.snapped_zoom(zoom_raw_not_snapped)); let snap = ipp.keyboard.get(snap as usize); @@ -468,6 +405,14 @@ impl MessageHandler> for Navigation } impl NavigationMessageHandler { + fn set_ptz(new_ptz: PTZ, document_ptz: &mut PTZ, network_interface: &mut NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) { + if !graph_view_overlay_open { + *document_ptz = new_ptz; + } else { + network_interface.set_metadata(MetadataType::NavigationMetadata(NavigationMetadataType::PTZ), TaggedValue::PTZ(new_ptz), breadcrumb_network_path); + } + } + pub fn snapped_tilt(&self, tilt: f64) -> f64 { let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians(); if matches!(self.navigation_operation, NavigationOperation::Tilt { snap: true, .. }) { diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs index 1c56254196..2646eb372f 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs @@ -2,7 +2,7 @@ use super::node_properties; use super::utility_types::FrontendNodeType; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::utility_types::network_interface::{ - DocumentNodeMetadata, DocumentNodePersistentMetadata, NodeNetworkInterface, NodeNetworkMetadata, NodeNetworkPersistentMetadata, NodeTemplate, NodeTypePersistentMetadata, + DocumentNodeMetadata, DocumentNodePersistentMetadata, NodeNetworkInterface, NodeNetworkMetadata, NodeNetworkPersistentMetadata, NodeTemplate, }; use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::prelude::Message; 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 8f7f016d13..dc4f5ca84e 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 @@ -1,11 +1,11 @@ use super::utility_types::Direction; use crate::messages::input_mapper::utility_types::input_keyboard::Key; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate, OutputConnector}; +use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate; use crate::messages::prelude::*; use graph_craft::document::value::TaggedValue; -use graph_craft::document::{NodeId, NodeInput}; +use graph_craft::document::{InputConnector, NodeId, NodeInput, OutputConnector}; use graph_craft::proto::GraphErrors; use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta; 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 0722b302f0..8db018eb14 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 @@ -6,12 +6,13 @@ use crate::messages::layout::utility_types::widget_prelude::*; 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::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::portfolio::document::utility_types::network_interface::{self, InputConnector, NodeNetworkInterface, NodeTemplate, OutputConnector, Previewing}; +use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface, NodeTemplate}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry}; use crate::messages::prelude::*; use crate::messages::tool::common_functionality::auto_panning::AutoPanning; -use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; +use graph_craft::document::{DocumentNodeImplementation, InputConnector, LayerClickTargetTypes, NodeId, NodeInput, OutputConnector, Previewing}; use graph_craft::proto::GraphErrors; use graphene_core::*; use renderer::{ClickTarget, Quad}; @@ -158,15 +159,11 @@ impl<'a> MessageHandler> for NodeGrap responses.add(NodeGraphMessage::ShiftNodePosition { node_id, x, y }); // Only auto connect to the dragged wire if the node is being added to the currently opened network if let Some(output_connector_position) = self.wire_in_progress_from_connector { - let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { - log::error!("Could not get network metadata in CreateNodeFromContextMenu"); + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in CreateNodeFromContextMenu"); return; }; - let output_connector_position_viewport = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .transform_point2(output_connector_position); + let output_connector_position_viewport = node_graph_to_viewport.transform_point2(output_connector_position); let Some(output_connector) = &network_interface.output_connector_from_click(output_connector_position_viewport, breadcrumb_network_path) else { log::error!("Could not get output from connector start"); return; @@ -250,7 +247,7 @@ impl<'a> MessageHandler> for NodeGrap return; }; if network_interface - .layer_click_target_from_click(ipp.mouse.position, network_interface::LayerClickTargetTypes::Visibility, selection_network_path) + .layer_click_target_from_click(ipp.mouse.position, LayerClickTargetTypes::Visibility, selection_network_path) .is_some() { return; @@ -339,26 +336,21 @@ impl<'a> MessageHandler> for NodeGrap log::error!("Selection network path does not match breadcrumb network path in PointerDown"); return; } - let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { - log::error!("Could not get network metadata in PointerDown"); + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerDown"); return; }; - let click = ipp.mouse.position; - let node_graph_point = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(click); + let node_graph_point = node_graph_to_viewport.inverse().transform_point2(click); - if network_interface - .layer_click_target_from_click(click, network_interface::LayerClickTargetTypes::Grip, selection_network_path) - .is_some() - { + if network_interface.layer_click_target_from_click(click, LayerClickTargetTypes::Grip, selection_network_path).is_some() { self.shift_without_push = true; } let clicked_id = network_interface.node_from_click(click, selection_network_path); let clicked_input = network_interface.input_connector_from_click(click, selection_network_path); let clicked_output = network_interface.output_connector_from_click(click, selection_network_path); - let network_metadata = network_interface.network_metadata(selection_network_path).unwrap(); // Create the add node popup on right click, then exit if right_click { @@ -397,16 +389,19 @@ impl<'a> MessageHandler> for NodeGrap } else { ContextMenuData::CreateNode }; - + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerDown"); + return; + }; // TODO: Create function let node_graph_shift = if matches!(context_menu_data, ContextMenuData::CreateNode) { let appear_right_of_mouse = if click.x > ipp.viewport_bounds.size().x - 180. { -180. } else { 0. }; let appear_above_mouse = if click.y > ipp.viewport_bounds.size().y - 200. { -200. } else { 0. }; - DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x + DVec2::new(appear_right_of_mouse, appear_above_mouse) / node_graph_to_viewport.matrix2.x_axis.x } else { let appear_right_of_mouse = if click.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. }; let appear_above_mouse = if click.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. }; - DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x + DVec2::new(appear_right_of_mouse, appear_above_mouse) / 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); @@ -430,11 +425,11 @@ impl<'a> MessageHandler> for NodeGrap // If the user is clicking on the create nodes list or context menu, break here if let Some(context_menu) = &self.context_menu { - let context_menu_viewport = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .transform_point2(DVec2::new(context_menu.context_menu_coordinates.0 as f64, context_menu.context_menu_coordinates.1 as f64)); + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerDown"); + return; + }; + let context_menu_viewport = node_graph_to_viewport.transform_point2(DVec2::new(context_menu.context_menu_coordinates.0 as f64, context_menu.context_menu_coordinates.1 as f64)); let (width, height) = if matches!(context_menu.context_menu_data, ContextMenuData::ToggleLayer { .. }) { // Height and width for toggle layer menu (173., 34.) @@ -465,7 +460,7 @@ impl<'a> MessageHandler> for NodeGrap } // Toggle visibility of clicked node and return - if let Some(clicked_visibility) = network_interface.layer_click_target_from_click(click, network_interface::LayerClickTargetTypes::Visibility, selection_network_path) { + if let Some(clicked_visibility) = network_interface.layer_click_target_from_click(click, LayerClickTargetTypes::Visibility, selection_network_path) { responses.add(NodeGraphMessage::ToggleVisibility { node_id: clicked_visibility }); return; } @@ -576,21 +571,18 @@ impl<'a> MessageHandler> for NodeGrap log::error!("Selection network path does not match breadcrumb network path in PointerMove"); return; } - let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { - return; - }; // Auto-panning let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()]; self.auto_panning.setup_by_mouse_position(ipp, &messages, responses); + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerDown"); + return; + }; + let viewport_location = ipp.mouse.position; - let point = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .inverse() - .transform_point2(viewport_location); + let point = node_graph_to_viewport.inverse().transform_point2(viewport_location); if self.wire_in_progress_from_connector.is_some() && self.context_menu.is_none() { let to_connector = network_interface.input_connector_from_click(ipp.mouse.position, selection_network_path); @@ -607,7 +599,7 @@ impl<'a> MessageHandler> for NodeGrap // Disconnect if the wire was previously connected to an input if let Some(disconnecting) = &self.disconnecting { let mut disconnect_root_node = false; - if let Previewing::Yes { root_node_to_restore } = network_interface.previewing(selection_network_path) { + if let Some(Previewing::Yes { root_node_to_restore }) = network_interface.previewing(selection_network_path) { if root_node_to_restore.is_some() && *disconnecting == InputConnector::Export(0) { disconnect_root_node = true; } @@ -627,15 +619,12 @@ impl<'a> MessageHandler> for NodeGrap } if let (Some(wire_in_progress_from_connector), Some(wire_in_progress_to_connector)) = (self.wire_in_progress_from_connector, self.wire_in_progress_to_connector) { - // If performance is a concern this can be stored as a field in the wire_in_progress_from/to_connector struct, and updated when snapping to an output - let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerDown"); return; }; - let from_connector_viewport = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .transform_point2(wire_in_progress_from_connector); + // If performance is a concern this can be stored as a field in the wire_in_progress_from/to_connector struct, and updated when snapping to an output + let from_connector_viewport = node_graph_to_viewport.transform_point2(wire_in_progress_from_connector); let from_connector_is_layer = network_interface .output_connector_from_click(from_connector_viewport, selection_network_path) .is_some_and(|output_connector| { @@ -747,33 +736,31 @@ impl<'a> MessageHandler> for NodeGrap log::error!("Could not get selected nodes in PointerUp"); return; }; - let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { - warn!("No network_metadata"); - return; - }; responses.add(DocumentMessage::EndTransaction); if let Some(preview_node) = self.preview_on_mouse_up { responses.add(NodeGraphMessage::TogglePreview { node_id: preview_node }); self.preview_on_mouse_up = None; - } + }; if let Some(node_to_deselect) = self.deselect_on_pointer_up { let mut new_selected_nodes = selected_nodes.selected_nodes_ref().clone(); new_selected_nodes.remove(node_to_deselect); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: new_selected_nodes }); self.deselect_on_pointer_up = None; - } - let point = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .inverse() - .transform_point2(ipp.mouse.position); + }; + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerUp"); + return; + }; + let point = node_graph_to_viewport.inverse().transform_point2(ipp.mouse.position); // Disconnect if the wire was previously connected to an input if let (Some(wire_in_progress_from_connector), Some(wire_in_progress_to_connector)) = (self.wire_in_progress_from_connector, self.wire_in_progress_to_connector) { // Check if dragged connector is reconnected to another input - let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport; + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerUp"); + return; + }; let from_connector_viewport = node_graph_to_viewport.transform_point2(wire_in_progress_from_connector); let to_connector_viewport = node_graph_to_viewport.transform_point2(wire_in_progress_to_connector); let output_connector = network_interface.output_connector_from_click(from_connector_viewport, selection_network_path); @@ -793,14 +780,13 @@ impl<'a> MessageHandler> for NodeGrap if self.context_menu.is_some() { return; } - let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { - warn!("No network_metadata"); + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerUp"); return; }; - let appear_right_of_mouse = if ipp.mouse.position.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. }; let appear_above_mouse = if ipp.mouse.position.y > ipp.viewport_bounds.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 node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / node_graph_to_viewport.matrix2.x_axis.x; self.context_menu = Some(ContextMenuInformation { context_menu_coordinates: ((point.x + node_graph_shift.x) as i32, (point.y + node_graph_shift.y) as i32), @@ -1049,27 +1035,15 @@ impl<'a> MessageHandler> for NodeGrap responses.add(PortfolioMessage::SubmitGraphRender { document_id, ignore_hash: true }); } NodeGraphMessage::SelectedNodesAdd { nodes } => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesAdd"); - return; - }; - selected_nodes.add_selected_nodes(nodes); + network_interface.add_selected_nodes(nodes, selection_network_path); responses.add(BroadcastEvent::SelectionChanged); } NodeGraphMessage::SelectedNodesRemove { nodes } => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesRemove"); - return; - }; - selected_nodes.retain_selected_nodes(|node| !nodes.contains(node)); + network_interface.remove_selected_nodes(nodes, selection_network_path); responses.add(BroadcastEvent::SelectionChanged); } NodeGraphMessage::SelectedNodesSet { nodes } => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesSet"); - return; - }; - selected_nodes.set_selected_nodes(nodes); + network_interface.set_selected_nodes(nodes, selection_network_path); responses.add(BroadcastEvent::SelectionChanged); responses.add(PropertiesPanelMessage::Refresh); } @@ -1110,9 +1084,11 @@ impl<'a> MessageHandler> for NodeGrap input, }); responses.add(PropertiesPanelMessage::Refresh); - if (!network_interface.reference(&node_id, selection_network_path).is_some_and(|reference| reference == "Imaginate") || input_index == 0) - && network_interface.connected_to_output(&node_id, selection_network_path) - { + let Some(reference) = network_interface.reference(&node_id, selection_network_path) else { + log::error!("Could not get reference for node: {node_id:?} in NodeGraphMessage::SetInputValue"); + return; + }; + if (!reference.as_ref().is_some_and(|reference| reference == "Imaginate") || input_index == 0) && network_interface.connected_to_output(&node_id, selection_network_path) { responses.add(NodeGraphMessage::RunDocumentGraph); } } @@ -1205,12 +1181,7 @@ impl<'a> MessageHandler> for NodeGrap responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids }) } NodeGraphMessage::ToggleLocked { node_id } => { - let Some(node_metadata) = network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata.get(&node_id) else { - log::error!("Cannot get node {:?} in NodeGraphMessage::ToggleLocked", node_id); - return; - }; - - let locked = !node_metadata.persistent_metadata.locked; + let locked = !network_interface.is_locked(&node_id, &[]); responses.add(DocumentMessage::AddTransaction); responses.add(NodeGraphMessage::SetLocked { node_id, locked }); @@ -1278,12 +1249,11 @@ impl<'a> MessageHandler> for NodeGrap // boxSelection = undefined; // } - let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else { - log::error!("Could not get network metadata in PointerMove"); + let Some(node_graph_to_viewport) = network_interface.node_graph_to_viewport(selection_network_path) else { + log::error!("Could not get node_graph_to_viewport in PointerUp"); return; }; - - let box_selection_start_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.transform_point2(box_selection_start); + let box_selection_start_viewport = node_graph_to_viewport.transform_point2(box_selection_start); let box_selection = Some(BoxSelection { start_x: box_selection_start_viewport.x.max(0.) as u32, @@ -1291,12 +1261,8 @@ impl<'a> MessageHandler> for NodeGrap end_x: ipp.mouse.position.x.max(0.) as u32, end_y: ipp.mouse.position.y.max(0.) as u32, }); - let box_selection_end_graph = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .inverse() - .transform_point2(ipp.mouse.position); + + let box_selection_end_graph = node_graph_to_viewport.inverse().transform_point2(ipp.mouse.position); let shift = ipp.keyboard.get(crate::messages::tool::tool_messages::tool_prelude::Key::Shift as usize); let Some(selected_nodes) = network_interface.selected_nodes(selection_network_path) else { @@ -1309,7 +1275,11 @@ impl<'a> MessageHandler> for NodeGrap } else { HashSet::new() }; - let all_nodes = network_metadata.persistent_metadata.node_metadata.keys().cloned().collect::>(); + let Some(network) = network_interface.network(selection_network_path) else { + log::error!("Could not get network in PointerMove"); + return; + }; + let all_nodes = network.nodes.keys().cloned().collect::>(); for node_id in all_nodes { let Some(click_targets) = network_interface.node_click_targets(&node_id, selection_network_path) else { log::error!("Could not get transient metadata for node {node_id}"); @@ -1337,11 +1307,7 @@ impl<'a> MessageHandler> for NodeGrap // Update the import/export UI edges whenever the PTZ changes or the bounding box of all nodes changes } NodeGraphMessage::UpdateNewNodeGraph => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::UpdateNewNodeGraph"); - return; - }; - selected_nodes.clear_selected_nodes(); + network_interface.set_selected_nodes(Vec::new(), selection_network_path); responses.add(BroadcastEvent::SelectionChanged); responses.add(NodeGraphMessage::SendGraph); @@ -1356,6 +1322,7 @@ impl<'a> MessageHandler> for NodeGrap for path in resolved_types.remove { network_interface.resolved_types.types.remove(&path.to_vec()); } + self.node_graph_errors = node_graph_errors; } NodeGraphMessage::UpdateActionButtons => { @@ -1493,7 +1460,7 @@ impl NodeGraphMessageHandler { if let (Some(&node_id), None) = (selection.next(), selection.next()) { // Is this node the current output let is_output = network.outputs_contain(node_id); - let is_previewing = matches!(network_interface.previewing(breadcrumb_network_path), Previewing::Yes { .. }); + let is_previewing = matches!(network_interface.previewing(breadcrumb_network_path), Some(Previewing::Yes { .. })); let output_button = TextButton::new(if is_output && is_previewing { "End Preview" } else { "Preview" }) .icon(Some("Rescale".to_string())) @@ -1617,7 +1584,7 @@ impl NodeGraphMessageHandler { // Connect rest of exports to their actual export field since they are not affected by previewing. Only connect the primary export if it is dashed for (i, export) in network.exports.iter().enumerate() { - let dashed = matches!(network_interface.previewing(breadcrumb_network_path), Previewing::Yes { .. }) && i == 0; + let dashed = matches!(network_interface.previewing(breadcrumb_network_path), Some(Previewing::Yes { .. })) && i == 0; if dashed || i != 0 { if let NodeInput::Node { node_id, output_index, .. } = export { wires.push(FrontendNodeWire { @@ -1664,17 +1631,19 @@ impl NodeGraphMessageHandler { log::error!("Could not get nested network when collecting nodes"); return Vec::new(); }; - let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { - log::error!("Could not get network_metadata when collecting nodes"); - return Vec::new(); - }; let mut nodes = Vec::new(); for (&node_id, node) in &network.nodes { let node_id_path = &[breadcrumb_network_path, (&[node_id])].concat(); - let Some(node_metadata) = network_metadata.persistent_metadata.node_metadata.get(&node_id) else { - log::error!("Could not get node_metadata for {node_id_path:?}"); - continue; + + let Some(input_names) = network_interface.input_names(&node_id, breadcrumb_network_path) else { + log::error!("Could not get input names for node: {node_id}"); + return Vec::new(); + }; + + let Some(output_names) = network_interface.output_names(&node_id, breadcrumb_network_path) else { + log::error!("Could not get output names for node: {node_id}"); + return Vec::new(); }; let frontend_graph_inputs = node.inputs.iter().enumerate().map(|(index, _)| { @@ -1684,9 +1653,7 @@ impl NodeGraphMessageHandler { // TODO: Should display the color of the "most commonly relevant" (we'd need some sort of precedence) data type it allows given the current generic form that's constrained by the other present connections. let data_type = FrontendGraphDataType::with_type(&node_type); - let input_name = node_metadata - .persistent_metadata - .input_names + let input_name = input_names .get(index) .cloned() .unwrap_or(network_interface.input_type(&InputConnector::node(node_id, index), breadcrumb_network_path).nested_type().to_string()); @@ -1754,16 +1721,8 @@ impl NodeGraphMessageHandler { } else { FrontendGraphDataType::General }; - let Some(node_metadata) = network_metadata.persistent_metadata.node_metadata.get(&node_id) else { - log::error!("Could not get node_metadata when getting output for {node_id}"); - continue; - }; - let output_name = node_metadata - .persistent_metadata - .output_names - .get(index) - .map(|output_name| output_name.to_string()) - .unwrap_or(format!("Output {}", index + 1)); + + let output_name = output_names.get(index).map(|output_name| output_name.to_string()).unwrap_or(format!("Output {}", index + 1)); let connected_to = outward_wires.get(&OutputConnector::node(node_id, index)).cloned().unwrap_or_default(); exposed_outputs.push(FrontendGraphOutput { @@ -1803,9 +1762,7 @@ impl NodeGraphMessageHandler { nodes.push(FrontendNode { id: node_id, - is_layer: network_interface - .node_metadata(&node_id, breadcrumb_network_path) - .is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()), + is_layer: network_interface.is_layer(&node_id, breadcrumb_network_path), can_be_layer: can_be_layer_lookup.contains(&node_id), reference: None, display_name: network_interface.frontend_display_name(&node_id, breadcrumb_network_path), @@ -1863,8 +1820,8 @@ impl NodeGraphMessageHandler { } } - for (&node_id, node_metadata) in &network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata { - if node_metadata.persistent_metadata.is_layer() { + for &node_id in network_interface.network(&[]).unwrap().nodes.keys() { + if network_interface.is_layer(&node_id, &[]) { let layer = LayerNodeIdentifier::new(node_id, network_interface, &[]); let children_allowed = 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 5779ec1841..4d3c43dcbc 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -2343,7 +2343,15 @@ pub fn index_properties(document_node: &DocumentNode, node_id: NodeId, _context: } pub fn generate_node_properties(document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext) -> LayoutGroup { - let reference = context.network_interface.reference(&node_id, context.selection_network_path).clone(); + let Some(reference) = context.network_interface.reference(&node_id, context.selection_network_path).cloned() else { + log::error!("Node {node_id} has no reference in generate_node_properties"); + return LayoutGroup::Section { + name: "Unknown".to_string(), + visible: true, + id: node_id.0, + layout: unknown_node_properties(&"Unknown".to_string()), + }; + }; let layout = if let Some(ref reference) = reference { match super::document_node_definitions::resolve_document_node_type(reference) { Some(document_node_type) => (document_node_type.properties)(document_node, node_id, context), 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 ac04885825..6abacab59e 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -1,9 +1,7 @@ use graph_craft::document::value::TaggedValue; -use graph_craft::document::NodeId; +use graph_craft::document::{NodeId, InputConnector, OutputConnector}; use graphene_core::Type; -use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, OutputConnector}; - #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)] pub enum FrontendGraphDataType { #[default] diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index c99d83e3b4..c8554111c9 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -2,6 +2,7 @@ use super::utility_types::OverlayContext; use crate::consts::HIDE_HANDLE_DISTANCE; use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerState, ShapeState}; use crate::messages::tool::tool_messages::tool_prelude::DocumentMessageHandler; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::vector::ManipulatorPointId; diff --git a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs index 1363442f09..eaa7f428c5 100644 --- a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs +++ b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs @@ -130,7 +130,7 @@ impl DocumentMetadata { self.click_targets .get(&layer)? .iter() - .filter_map(|click_target| click_target.subpath().bounding_box_with_transform(transform)) + .filter_map(|click_target| click_target.subpath().bounding_box_with_transform(&transform)) .reduce(Quad::combine_bounds) } diff --git a/editor/src/messages/portfolio/document/utility_types/misc.rs b/editor/src/messages/portfolio/document/utility_types/misc.rs index 34610ea5fc..7e925e6df1 100644 --- a/editor/src/messages/portfolio/document/utility_types/misc.rs +++ b/editor/src/messages/portfolio/document/utility_types/misc.rs @@ -1,5 +1,6 @@ use crate::consts::COLOR_OVERLAY_GRAY; +use graph_craft::document::PTZ; use graphene_core::raster::Color; use glam::DVec2; @@ -440,27 +441,3 @@ impl fmt::Display for SnappingOptions { } } } - -#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)] -#[serde(default)] -pub struct PTZ { - pub pan: DVec2, - pub tilt: f64, - zoom: f64, -} - -impl Default for PTZ { - fn default() -> Self { - Self { pan: DVec2::ZERO, tilt: 0., zoom: 1. } - } -} - -impl PTZ { - pub fn zoom(&self) -> f64 { - self.zoom - } - - pub fn set_zoom(&mut self, zoom: f64) { - self.zoom = zoom.clamp(crate::consts::VIEWPORT_ZOOM_SCALE_MIN, crate::consts::VIEWPORT_ZOOM_SCALE_MAX) - } -} 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 d512fadca7..51cd2e4795 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -1,5 +1,4 @@ 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::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; @@ -8,7 +7,12 @@ use crate::messages::tool::common_functionality::graph_modification_utils; use bezier_rs::Subpath; use graph_craft::document::{value::TaggedValue, DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork}; +use graph_craft::document::{ + DocumentNodeClickTargets, InputConnector, LayerClickTargetTypes, LayerClickTargets, LayerMetadata, LayerOwner, LayerPersistentMetadata, LayerPosition, LayerTransientMetadata, NetworkEdgeDistance, + NodePersistentMetadata, NodePosition, NodeTypeClickTargets, NodeTypePersistentMetadata, OutputConnector, Ports, Previewing, RootNode, TransientMetadata, PTZ, +}; use graph_craft::{concrete, Type}; +use graphene_std::memo::MemoHashGuard; use graphene_std::renderer::{ClickTarget, Quad}; use graphene_std::vector::{PointId, VectorData, VectorModificationType}; use interpreted_executor::{dynamic_executor::ResolvedDocumentNodeTypes, node_registry::NODE_REGISTRY}; @@ -16,6 +20,7 @@ use interpreted_executor::{dynamic_executor::ResolvedDocumentNodeTypes, node_reg use glam::{DAffine2, DVec2, IVec2}; use std::collections::{HashMap, HashSet, VecDeque}; use std::hash::{DefaultHasher, Hash, Hasher}; +use std::ops::DerefMut; /// 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. #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] @@ -23,8 +28,8 @@ pub struct NodeNetworkInterface { /// The node graph that generates this document's artwork. It recursively stores its sub-graphs, so this root graph is the whole snapshot of the document content. /// A public mutable reference should never be created. It should only be mutated through custom setters which perform the necessary side effects to keep network_metadata in sync network: NodeNetwork, - /// Stores all editor information for a NodeNetwork. Should automatically kept in sync by the setter methods when changes to the document network are made. - network_metadata: NodeNetworkMetadata, + /// Stores all editor information for the document network as inputs to nodes. The node graph overlay nodes are added to this network. + metadata_network: NodeNetwork, // TODO: Wrap in TransientMetadata Option /// Stores the document network's structural topology. Should automatically kept in sync by the setter methods when changes to the document network are made. #[serde(skip)] @@ -37,11 +42,62 @@ pub struct NodeNetworkInterface { transaction_status: TransactionStatus, } +//TODO: This could probably be merged into tagged values +// Enum to represent all metadata in the network, which can be used uniquely identify any stored value +#[derive(Copy, Clone, Hash, Debug)] +pub enum MetadataType { + // Network persistent metadata + Previewing, + NavigationMetadata(NavigationMetadataType), + SelectionUndoHistory, + SelectionRedoHistory, + // Network transient metadata + StackDependents, + AllNodesBoundingBox, + OutwardWires, + ImportExportPorts, + RoundedNetworkEdgeDistance, + // Node persistent metadata + Reference, + DisplayName, + InputNames, + OutputNames, + HasPrimaryOutput, + Locked, + NodeTypeMetadata, + // Node transient metadata + ClickTargets, + // Derived metadata (might not be necessary) + Position, + IsLayer, +} + +#[derive(Copy, Clone, Hash, Debug)] +pub enum NavigationMetadataType { + PTZ, + NodeGraphToViewport, + NodeGraphTopRight, +} + +pub enum NodeTypeMetadata { + Layer(LayerMetadataType), + Node(NodeMetadataType), +} + +pub enum LayerMetadataType { + Position, + OwnedNodes, +} + +pub enum NodeMetadataType { + Position, +} + impl Clone for NodeNetworkInterface { fn clone(&self) -> Self { Self { network: self.network.clone(), - network_metadata: self.network_metadata.clone(), + metadata_network: self.metadata_network.clone(), document_metadata: Default::default(), resolved_types: Default::default(), transaction_status: TransactionStatus::Finished, @@ -51,7 +107,7 @@ impl Clone for NodeNetworkInterface { impl PartialEq for NodeNetworkInterface { fn eq(&self, other: &Self) -> bool { - self.network == other.network && self.network_metadata == other.network_metadata + self.network == other.network && self.metadata_network == other.metadata_network } } @@ -62,23 +118,6 @@ impl NodeNetworkInterface { self.network.nested_network(network_path) } - /// The network metadata should always exist for the current network - pub fn network_metadata(&self, network_path: &[NodeId]) -> Option<&NodeNetworkMetadata> { - self.network_metadata.nested_metadata(network_path) - } - - pub fn node_metadata(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeMetadata> { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata"); - return None; - }; - let Some(node_metadata) = network_metadata.persistent_metadata.node_metadata.get(node_id) else { - log::error!("Could not get nested node_metadata for node {node_id} in network {network_path:?}"); - return None; - }; - Some(node_metadata) - } - pub fn document_metadata(&self) -> &DocumentMetadata { &self.document_metadata } @@ -88,34 +127,25 @@ impl NodeNetworkInterface { } /// Get the selected nodes for the network at the network_path - pub fn selected_nodes(&self, network_path: &[NodeId]) -> Option { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in selected_nodes"); + pub fn selected_nodes(&self, network_path: &[NodeId]) -> Option { + let Some(network) = self.network(network_path) else { + log::error!("Could not get network in selected_nodes"); + return None; + }; + let Some(selection_undo_history) = self.selection_undo_history(network_path) else { + log::error!("Could not get nested selection_undo_history for path {network_path:?}"); return None; }; Some( - network_metadata - .persistent_metadata - .selection_undo_history + selection_undo_history .back() .cloned() .unwrap_or_default() - .filtered_selected_nodes(network_metadata.persistent_metadata.node_metadata.keys().cloned().collect()), + .filtered_selected_nodes(network.nodes.keys().cloned().collect()), ) } - /// Get the network which the encapsulating node of the currently viewed network is part of. Will always be None in the document network. - pub fn encapsulating_network_metadata(&self, network_path: &[NodeId]) -> Option<&NodeNetworkMetadata> { - let mut encapsulating_path = network_path.to_vec(); - encapsulating_path.pop()?; - let Some(parent_metadata) = self.network_metadata(&encapsulating_path) else { - log::error!("Could not get parent network in encapsulating_node_metadata"); - return None; - }; - Some(parent_metadata) - } - /// Get the node which encapsulates the currently viewed network. Will always be None in the document network. pub fn encapsulating_node(&self, network_path: &[NodeId]) -> Option<&DocumentNode> { let mut encapsulating_path = network_path.to_vec(); @@ -128,21 +158,6 @@ impl NodeNetworkInterface { Some(encapsulating_node) } - /// Get the node metadata for the node which encapsulates the currently viewed network. Will always be None in the document network. - pub fn encapsulating_node_metadata(&self, network_path: &[NodeId]) -> Option<&DocumentNodeMetadata> { - let mut encapsulating_path = network_path.to_vec(); - let encapsulating_node_id = encapsulating_path.pop()?; - let Some(parent_metadata) = self.network_metadata(&encapsulating_path) else { - log::error!("Could not get parent network in encapsulating_node_metadata"); - return None; - }; - let Some(encapsulating_node_metadata) = parent_metadata.persistent_metadata.node_metadata.get(&encapsulating_node_id) else { - log::error!("Could not get encapsulating node metadata in encapsulating_node_metadata"); - return None; - }; - Some(encapsulating_node_metadata) - } - /// Returns the first downstream layer(inclusive) from a node. If the node is a layer, it will return itself. pub fn downstream_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option { let mut id = *node_id; @@ -180,20 +195,11 @@ impl NodeNetworkInterface { .enumerate() .collect::>() { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in chain_width"); - return 0; - }; // Check if the node is positioned as a chain - let is_chain = network_metadata - .persistent_metadata - .node_metadata - .get(&node_id) - .map(|node_metadata| &node_metadata.persistent_metadata.node_type_metadata) - .is_some_and(|node_type_metadata| match node_type_metadata { - NodeTypePersistentMetadata::Node(node_persistent_metadata) => matches!(node_persistent_metadata.position, NodePosition::Chain), - _ => false, - }); + let is_chain = self.node_type_metadata(&node_id, network_path).is_some_and(|node_type_metadata| match node_type_metadata { + NodeTypePersistentMetadata::Node(node_persistent_metadata) => matches!(node_persistent_metadata.position, NodePosition::Chain), + _ => false, + }); if is_chain { last_chain_node_distance = (index as u32) + 1; } else { @@ -335,7 +341,7 @@ impl NodeNetworkInterface { return None; }; match &mut node_template.persistent_node_metadata.node_type_metadata { - NodeTypePersistentMetadata::Layer(layer_metadata) => layer_metadata.position = LayerPosition::Absolute(position), + NodeTypePersistentMetadata::Layer(layer_metadata) => layer_metadata.persistent_metadata.position = LayerPosition::Absolute(position), NodeTypePersistentMetadata::Node(node_metadata) => node_metadata.position = NodePosition::Absolute(position), }; } @@ -360,7 +366,7 @@ impl NodeNetworkInterface { { match &mut node_template.persistent_node_metadata.node_type_metadata { NodeTypePersistentMetadata::Node(node_metadata) => node_metadata.position = NodePosition::Chain, - NodeTypePersistentMetadata::Layer(_) => log::error!("Node is not be a layer"), + NodeTypePersistentMetadata::Layer(_) => log::error!("Node cannot be a layer"), }; } } @@ -370,7 +376,7 @@ impl NodeNetworkInterface { // TODO: Remove 2x2 offset and replace with layout system to find space for new node match &mut node_template.persistent_node_metadata.node_type_metadata { NodeTypePersistentMetadata::Layer(layer_metadata) => { - if let LayerPosition::Absolute(position) = &mut layer_metadata.position { + if let LayerPosition::Absolute(position) = &mut layer_metadata.persistent_metadata.position { *position += IVec2::new(2, 2) } } @@ -399,17 +405,126 @@ impl NodeNetworkInterface { log::error!("Could not get node {node_id} in create_node_template"); return None; }; - let Some(node_metadata) = self.node_metadata(node_id, network_path).cloned() else { - log::error!("Could not get node_metadata in create_node_template"); - return None; - }; - + let node_metadata = self.get_all_node_metadata(node_id, network_path)?; Some(NodeTemplate { persistent_node_metadata: node_metadata.persistent_metadata, document_node: node.clone(), }) } + pub fn get_all_node_metadata(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option { + let mut node_path = network_path.to_vec(); + node_path.push(*node_id); + + let TaggedValue::OptionalString(reference) = self.metadata_value(MetadataType::Reference, &node_path).cloned()? else { + log::error!("Reference should be OptionalString in get_all_node_metadata"); + return None; + }; + let TaggedValue::String(display_name) = self.metadata_value(MetadataType::DisplayName, &node_path).cloned()? else { + log::error!("display_name should be String in get_all_node_metadata"); + return None; + }; + let TaggedValue::VecString(input_names) = self.metadata_value(MetadataType::InputNames, &node_path).cloned()? else { + log::error!("input_names should be VecString in get_all_node_metadata"); + return None; + }; + let TaggedValue::VecString(output_names) = self.metadata_value(MetadataType::OutputNames, &node_path).cloned()? else { + log::error!("output_names should be VecString in get_all_node_metadata"); + return None; + }; + let TaggedValue::Bool(has_primary_output) = self.metadata_value(MetadataType::HasPrimaryOutput, &node_path).cloned()? else { + log::error!("has_primary_output should be Bool in get_all_node_metadata"); + return None; + }; + let TaggedValue::Bool(locked) = self.metadata_value(MetadataType::Locked, &node_path).cloned()? else { + log::error!("locked should be Bool in get_all_node_metadata"); + return None; + }; + let TaggedValue::NodeTypeMetadata(node_type_metadata) = self.metadata_value(MetadataType::NodeTypeMetadata, &node_path).cloned()? else { + log::error!("node_type_metadata should be NodeTypeMetadata in get_all_node_metadata"); + return None; + }; + + let Some(network) = self.network(network_path) else { + log::error!("Could not get network in get_all_node_metadata"); + return None; + }; + let Some(node) = network.nodes.get(&node_id) else { + log::error!("Could not get node {node_id} in get_all_node_metadata"); + return None; + }; + let mut network_metadata = None; + if matches!(node.implementation, DocumentNodeImplementation::Network(_)) { + network_metadata = Some(self.get_network_metadata(&node_path)?); + } + Some(DocumentNodeMetadata { + persistent_metadata: DocumentNodePersistentMetadata { + reference, + display_name, + input_names, + output_names, + has_primary_output, + locked, + node_type_metadata, + network_metadata, + }, + transient_metadata: DocumentNodeTransientMetadata::default(), + }) + } + + /// Adds nodes for the network metadata. Should always be called when creating a new NodeNetwork + pub fn get_network_metadata(&self, network_path: &[NodeId]) -> Option { + let TaggedValue::Previewing(previewing) = self.metadata_value(MetadataType::Previewing, network_path).cloned()? else { + log::error!("Previewing should be TaggedValue::Previewing in get_network_metadata"); + return None; + }; + let TaggedValue::PTZ(node_graph_ptz) = self.metadata_value(MetadataType::NavigationMetadata(NavigationMetadataType::PTZ), network_path).cloned()? else { + log::error!("PTZ should be TaggedValue::PTZ in get_network_metadata"); + return None; + }; + let TaggedValue::DAffine2(node_graph_to_viewport) = self + .metadata_value(MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphToViewport), network_path) + .cloned()? + else { + log::error!("node_graph_to_viewport should be TaggedValue::DAffine2 in get_network_metadata"); + return None; + }; + let TaggedValue::DVec2(node_graph_top_right) = self + .metadata_value(MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphTopRight), network_path) + .cloned()? + else { + log::error!("node_graph_top_right should be TaggedValue::DVec2 in get_network_metadata"); + return None; + }; + + let mut all_node_metadata = HashMap::new(); + let Some(network) = self.network(network_path) else { + log::error!("Could not get network in get_network_metadata"); + return None; + }; + for node_id in network.nodes.keys() { + let Some(node_metadata) = self.get_all_node_metadata(node_id, network_path) else { + log::error!("Could not get node metadata for node {node_id} in get_network_metadata"); + return None; + }; + all_node_metadata.insert(*node_id, node_metadata); + } + + Some(NodeNetworkMetadata { + persistent_metadata: NodeNetworkPersistentMetadata { + previewing, + navigation_metadata: NavigationMetadata { + node_graph_ptz, + node_graph_to_viewport, + node_graph_top_right, + }, + node_metadata: all_node_metadata, + ..Default::default() + }, + transient_metadata: NodeNetworkTransientMetadata::default(), + }) + } + /// Converts all node id inputs to a new id based on a HashMap. /// /// If the node is not in the hashmap then a default input is found based on the compiled network, using the node_id passed as a parameter @@ -453,6 +568,38 @@ impl NodeNetworkInterface { } } + fn metadata_node_id(metadata: MetadataType, node_path: &[NodeId]) -> NodeId { + let mut hasher = DefaultHasher::new(); + node_path.hash(&mut hasher); + metadata.hash(&mut hasher); + NodeId(hasher.finish()) + } + + fn metadata_value(&self, metadata: MetadataType, node_path: &[NodeId]) -> Option<&TaggedValue> { + let node_id = Self::metadata_node_id(metadata, node_path); + + let Some(node) = self.metadata_network.nodes.get(&node_id) else { + log::error!("Could not get node {node_id} in value_from_node_id"); + return None; + }; + node.inputs.first().expect("Metadata node should always have primary input to store data").as_value() + } + + /// Returns the MemoHashGuard, which much be dereferenced with .deref_mut() to access the mutable value + fn metadata_value_mut(&mut self, metadata: MetadataType, node_path: &[NodeId]) -> Option> { + let network = self.metadata_network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(metadata, node_path); + let Some(metadata_node) = network.nodes.get_mut(&metadata_node_id) else { + log::error!("Could not get metadata {metadata:?} for node path {node_path:?} with id {metadata_node_id} in metadata_value_mut"); + return None; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in metadata_value_mut"); + return None; + }; + metadata_input.as_value_mut() + } + /// Get the [`Type`] for any InputConnector pub fn input_type(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Type { // TODO: If the input_connector is a NodeInput::Value, return the type of the tagged value @@ -675,15 +822,23 @@ impl NodeNetworkInterface { pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Option> { self.import_export_ports(network_path).cloned().map(|import_export_ports| { + let mut encapsulating_path = network_path.to_vec(); + let mut encapsulating_input_names = None; + if let Some(current_node) = encapsulating_path.pop() { + let Some(input_names) = self.input_names(¤t_node, &encapsulating_path).cloned() else { + log::error!("Could not get input names in frontend_imports for node {current_node:?}"); + return Vec::new(); + }; + encapsulating_input_names = Some(input_names); + } import_export_ports - .output_ports - .iter() + .output_ports() .filter_map(|(import_index, click_target)| { // Get import name from parent node metadata input, which must match the number of imports. // Empty string means to use type, or "Import + index" if type can't be determined - let import_name = self - .encapsulating_node_metadata(network_path) - .and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.input_names.get(*import_index).cloned()) + let import_name = encapsulating_input_names + .as_ref() + .and_then(|encapsulating_input_names| encapsulating_input_names.get(import_index).cloned()) .unwrap_or_default(); let mut import_metadata = None; @@ -692,14 +847,14 @@ impl NodeNetworkInterface { let mut encapsulating_path = network_path.to_vec(); let encapsulating_node_id = encapsulating_path.pop().unwrap(); - let input_type = self.input_type(&InputConnector::node(encapsulating_node_id, *import_index), &encapsulating_path); + let input_type = self.input_type(&InputConnector::node(encapsulating_node_id, import_index), &encapsulating_path); let data_type = FrontendGraphDataType::with_type(&input_type); let import_name = if import_name.is_empty() { input_type.clone().nested_type().to_string() } else { import_name }; let connected_to = self .outward_wires(network_path) - .and_then(|outward_wires| outward_wires.get(&OutputConnector::Import(*import_index))) + .and_then(|outward_wires| outward_wires.get(&OutputConnector::Import(import_index))) .cloned() .unwrap_or_else(|| { log::error!("Could not get OutputConnector::Import({import_index}) in outward wires"); @@ -724,17 +879,25 @@ impl NodeNetworkInterface { } pub fn frontend_exports(&mut self, network_path: &[NodeId]) -> Option> { + let mut encapsulating_path = network_path.to_vec(); + let mut encapsulating_output_names = None; + if let Some(current_node) = encapsulating_path.pop() { + let Some(output_names) = self.input_names(¤t_node, &encapsulating_path).cloned() else { + log::error!("Could not get output names in frontend_exports for node {current_node:?}"); + return None; + }; + encapsulating_output_names = Some(output_names); + } self.import_export_ports(network_path).cloned().map(|import_export_ports| { import_export_ports - .input_ports - .iter() + .input_ports() .filter_map(|(export_index, click_target)| { let Some(network) = self.network(network_path) else { log::error!("Could not get network in frontend_exports"); return None; }; - let Some(export) = network.exports.get(*export_index) else { + let Some(export) = network.exports.get(export_index) else { log::error!("Could not get export {export_index} in frontend_exports"); return None; }; @@ -758,7 +921,7 @@ impl NodeNetworkInterface { }; // First import index is visually connected to the root node instead of its actual export input so previewing does not change the connection - let connected_to = if *export_index == 0 { + let connected_to = if export_index == 0 { self.root_node(network_path).map(|root_node| OutputConnector::node(root_node.node_id, root_node.output_index)) } else if let NodeInput::Node { node_id, output_index, .. } = export { Some(OutputConnector::node(*node_id, *output_index)) @@ -773,8 +936,9 @@ impl NodeNetworkInterface { 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()) + encapsulating_output_names + .as_ref() + .and_then(|encapsulating_output_names| encapsulating_output_names.get(export_index).cloned()) .unwrap_or_default() }; @@ -949,25 +1113,17 @@ impl NodeNetworkInterface { upstream_nodes_below_layer } - pub fn previewing(&self, network_path: &[NodeId]) -> Previewing { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in previewing"); - return Previewing::No; - }; - network_metadata.persistent_metadata.previewing - } - /// Returns the root node (the node that the solid line is connect to), or None if no nodes are connected to the output pub fn root_node(&self, network_path: &[NodeId]) -> Option { let Some(network) = self.network(network_path) else { log::error!("Could not get network in root_node"); return None; }; - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in root_node"); + let Some(previewing) = self.previewing(network_path) else { + log::error!("Could not get previewing in root_node"); return None; }; - match &network_metadata.persistent_metadata.previewing { + match previewing { Previewing::Yes { root_node_to_restore } => *root_node_to_restore, Previewing::No => network.exports.first().and_then(|export| { if let NodeInput::Node { node_id, output_index, .. } = export { @@ -982,44 +1138,147 @@ impl NodeNetworkInterface { } } - pub fn reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option { - self.node_metadata(node_id, network_path) - .and_then(|node_metadata| node_metadata.persistent_metadata.reference.as_ref().map(|reference| reference.to_string())) + pub fn previewing(&self, network_path: &[NodeId]) -> Option<&Previewing> { + let Some(tagged_value) = self.metadata_value(MetadataType::Previewing, &network_path) else { + log::error!("Could not get tagged value in previewing"); + return None; + }; + let TaggedValue::Previewing(previewing) = tagged_value else { + log::error!("Tagged value should be Previewing in previewing"); + return None; + }; + Some(previewing) } - pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { - log::error!("Could not get node_metadata in display_name"); - return "".to_string(); + pub fn ptz(&self, network_path: &[NodeId]) -> Option<&PTZ> { + let Some(tagged_value) = self.metadata_value(MetadataType::NavigationMetadata(NavigationMetadataType::PTZ), &network_path) else { + log::error!("Could not get tagged value in ptz"); + return None; + }; + let TaggedValue::PTZ(ptz) = tagged_value else { + log::error!("Tagged value should be PTZ in ptz"); + return None; + }; + Some(ptz) + } + + pub fn node_graph_to_viewport(&self, network_path: &[NodeId]) -> Option<&DAffine2> { + let Some(tagged_value) = self.metadata_value(MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphToViewport), network_path) else { + log::error!("Could not get tagged value in node_graph_to_viewport"); + return None; + }; + let TaggedValue::DAffine2(daffine) = tagged_value else { + log::error!("Tagged value should be DAffine2 in node_graph_to_viewport"); + return None; + }; + Some(daffine) + } + + pub fn node_graph_top_right(&self, network_path: &[NodeId]) -> Option<&DVec2> { + let Some(tagged_value) = self.metadata_value(MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphTopRight), network_path) else { + log::error!("Could not get tagged value in viewport_top_right"); + return None; + }; + let TaggedValue::DVec2(top_right) = tagged_value else { + log::error!("Tagged value should be DVec2 in viewport_top_right"); + return None; + }; + Some(top_right) + } + + pub fn selection_undo_history(&self, network_path: &[NodeId]) -> Option<&VecDeque>> { + let Some(tagged_value) = self.metadata_value(MetadataType::SelectionUndoHistory, network_path) else { + log::error!("Could not get tagged value in selection_undo_history"); + return None; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = tagged_value else { + log::error!("Tagged value should be SelectionUndoHistory in selection_undo_history"); + return None; + }; + Some(selection_undo_history) + } + + pub fn reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&Option> { + let Some(tagged_value) = self.metadata_value(MetadataType::Reference, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get tagged value in reference"); + return None; + }; + let TaggedValue::OptionalString(reference) = tagged_value else { + log::error!("Tagged value should be String in reference"); + return None; + }; + Some(reference) + } + + pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&String> { + let Some(tagged_value) = self.metadata_value(MetadataType::DisplayName, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get tagged value in display_name"); + return None; + }; + let TaggedValue::String(display_name) = tagged_value else { + log::error!("Tagged value should be String in display_name"); + return None; }; - node_metadata.persistent_metadata.display_name.clone() + Some(display_name) } pub fn frontend_display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { - let is_layer = self - .node_metadata(node_id, network_path) - .expect("Could not get persistent node metadata in untitled_layer_label") - .persistent_metadata - .is_layer(); - let reference = self.reference(node_id, network_path); + let Some(display_name) = self.display_name(node_id, network_path) else { + log::error!("Could not get display name in frontend_display_name"); + return "".to_string(); + }; + let is_layer = self.is_layer(node_id, network_path); + + let Some(reference) = self.reference(node_id, network_path).cloned() else { + log::error!("Could not get reference in untitled_layer_label"); + return "".to_string(); + }; let is_merge_node = reference.as_ref().is_some_and(|reference| reference == "Merge"); - if self.display_name(node_id, network_path).is_empty() { + if display_name.is_empty() { if is_layer && is_merge_node { "Untitled Layer".to_string() } else { reference.unwrap_or("Untitled node".to_string()) } } else { - self.display_name(node_id, network_path) + display_name.to_string() } } + pub fn input_names(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&Vec> { + let Some(tagged_value) = self.metadata_value(MetadataType::InputNames, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get tagged value in input_names"); + return None; + }; + let TaggedValue::VecString(input_names) = tagged_value else { + log::error!("Tagged value should be StringArray in input_names"); + return None; + }; + Some(input_names) + } + + pub fn output_names(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&Vec> { + let Some(tagged_value) = self.metadata_value(MetadataType::OutputNames, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get tagged value in output_names"); + return None; + }; + let TaggedValue::VecString(output_names) = tagged_value else { + log::error!("Tagged value should be StringArray in output_names"); + return None; + }; + Some(output_names) + } + pub fn is_locked(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { - log::error!("Could not get persistent node metadata in is_locked for node {node_id}"); + let Some(tagged_value) = self.metadata_value(MetadataType::Locked, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get tagged value in is_locked for node {node_id}"); + return false; + }; + let TaggedValue::Bool(locked) = tagged_value else { + log::error!("Tagged value should be Bool in is_locked for node {node_id}"); return false; }; - node_metadata.persistent_metadata.locked + *locked } pub fn is_visible(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { @@ -1035,73 +1294,85 @@ impl NodeNetworkInterface { } pub fn is_layer(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(node_type_metadata) = self.node_type_metadata(node_id, network_path) else { log::error!("Could not get nested node_metadata in is_layer"); return false; }; - node_metadata.persistent_metadata.is_layer() + matches!(node_type_metadata, NodeTypePersistentMetadata::Layer(_)) + } + + pub fn node_type_metadata(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&NodeTypePersistentMetadata> { + let Some(tagged_value) = self.metadata_value(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get tagged value in is_locked for node {node_id}"); + return None; + }; + let TaggedValue::NodeTypeMetadata(node_type_metadata) = tagged_value else { + log::error!("Tagged value should be NodeTypeMetadata in node_type_metadata for node {node_id}"); + return None; + }; + Some(node_type_metadata) } pub fn has_primary_output(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(node_metadata) = self.metadata_value(MetadataType::HasPrimaryOutput, &[network_path, &[*node_id]].concat()) else { log::error!("Could not get node_metadata in has_primary_output"); return false; }; - node_metadata.persistent_metadata.has_primary_output + let TaggedValue::Bool(has_primary_output) = node_metadata else { + log::error!("Tagged value should be Bool in has_primary_output"); + return false; + }; + *has_primary_output } pub fn is_absolute(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(node_type_metadata) = self.node_type_metadata(node_id, network_path) else { log::error!("Could not get node_metadata in is_absolute"); return false; }; - match &node_metadata.persistent_metadata.node_type_metadata { - NodeTypePersistentMetadata::Layer(layer_metadata) => matches!(layer_metadata.position, LayerPosition::Absolute(_)), + match node_type_metadata { + NodeTypePersistentMetadata::Layer(layer_metadata) => matches!(layer_metadata.persistent_metadata.position, LayerPosition::Absolute(_)), NodeTypePersistentMetadata::Node(node_metadata) => matches!(node_metadata.position, NodePosition::Absolute(_)), } } pub fn is_chain(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(node_type_metadata) = self.node_type_metadata(node_id, network_path) else { log::error!("Could not get node_metadata in is_chain"); return false; }; - match &node_metadata.persistent_metadata.node_type_metadata { + match node_type_metadata { NodeTypePersistentMetadata::Node(node_metadata) => matches!(node_metadata.position, NodePosition::Chain), _ => false, } } pub fn is_stack(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(node_type_metadata) = self.node_type_metadata(node_id, network_path) else { log::error!("Could not get node_metadata in is_stack"); return false; }; - match &node_metadata.persistent_metadata.node_type_metadata { - NodeTypePersistentMetadata::Layer(layer_metadata) => matches!(layer_metadata.position, LayerPosition::Stack(_)), + match node_type_metadata { + NodeTypePersistentMetadata::Layer(layer_metadata) => matches!(layer_metadata.persistent_metadata.position, LayerPosition::Stack(_)), _ => false, } } pub fn is_artboard(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { - self.reference(node_id, network_path) - .as_ref() - .is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[])) + let Some(reference) = self.reference(node_id, network_path) else { + log::error!("Could not get reference for node {node_id} in is_artboard"); + return false; + }; + reference.as_ref().is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[])) } pub fn all_artboards(&self) -> HashSet { - self.network_metadata(&[]) + self.network(&[]) .unwrap() - .persistent_metadata - .node_metadata - .iter() - .filter_map(|(node_id, node_metadata)| { - if node_metadata - .persistent_metadata - .reference - .as_ref() - .is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[]) && self.is_layer(node_id, &[])) - { + .nodes + .keys() + .filter_map(|node_id| { + if self.is_artboard(node_id, &[]) { Some(LayerNodeIdentifier::new(*node_id, self, &[])) } else { None @@ -1241,12 +1512,12 @@ impl NodeNetworkInterface { /// Gives an iterator to all nodes connected to the given nodes by all inputs (primary or primary + secondary depending on `only_follow_primary` choice), traversing backwards upstream starting from the given node's inputs. pub fn upstream_flow_back_from_nodes<'a>(&'a self, mut node_ids: Vec, network_path: &'a [NodeId], mut flow_type: FlowType) -> impl Iterator + 'a { - let (Some(network), Some(network_metadata)) = (self.network(network_path), self.network_metadata(network_path)) else { + let Some(network) = self.network(network_path) else { log::error!("Could not get network or network_metadata in upstream_flow_back_from_nodes"); return FlowIter { stack: Vec::new(), - network: &self.network, - network_metadata: &self.network_metadata, + network_interface: self, + network_path, flow_type: FlowType::UpstreamFlow, }; }; @@ -1265,8 +1536,8 @@ impl NodeNetworkInterface { }; FlowIter { stack: node_ids, - network, - network_metadata, + network_interface: self, + network_path, flow_type, } } @@ -1352,11 +1623,11 @@ impl NodeNetworkInterface { log::error!("Could not get nested network in from_old_network"); continue; }; - nested_network_metadata.persistent_metadata.previewing = Previewing::No; for (node_id, old_node) in old_network.nodes { let mut node = DocumentNode::default(); let mut node_metadata = DocumentNodeMetadata::default(); + // TODO: Add upgrade to metadata stored in network node.inputs = old_node.inputs; node.manual_composition = old_node.manual_composition; node.visible = old_node.visible; @@ -1367,9 +1638,11 @@ impl NodeNetworkInterface { node_metadata.persistent_metadata.has_primary_output = old_node.has_primary_output; node_metadata.persistent_metadata.locked = old_node.locked; node_metadata.persistent_metadata.node_type_metadata = if old_node.is_layer { - NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { - position: LayerPosition::Absolute(old_node.metadata.position), - owned_nodes: TransientMetadata::Unloaded, + NodeTypePersistentMetadata::Layer(LayerMetadata { + persistent_metadata: LayerPersistentMetadata { + position: LayerPosition::Absolute(old_node.metadata.position), + }, + transient_metadata: LayerTransientMetadata::default(), }) } else { NodeTypePersistentMetadata::Node(NodePersistentMetadata { @@ -1397,9 +1670,11 @@ impl NodeNetworkInterface { nested_network_metadata.persistent_metadata.node_metadata.insert(node_id, node_metadata); } } + //TODO: Add metadata to this + let metadata_network = NodeNetwork::default(); Self { network: node_network, - network_metadata, + metadata_network, document_metadata: DocumentMetadata::default(), resolved_types: ResolvedDocumentNodeTypes::default(), transaction_status: TransactionStatus::Finished, @@ -1431,61 +1706,8 @@ impl NodeNetworkInterface { self.network.nested_network_mut(network_path) } - fn network_metadata_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetworkMetadata> { - self.network_metadata.nested_metadata_mut(network_path) - } - - fn node_metadata_mut(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&mut DocumentNodeMetadata> { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata"); - return None; - }; - let Some(node_metadata) = network_metadata.persistent_metadata.node_metadata.get_mut(node_id) else { - log::error!("Could not get nested node_metadata for node {node_id} in network {network_path:?}"); - return None; - }; - Some(node_metadata) - } - - /// Mutably get the network which the encapsulating node of the currently viewed network is part of. Will always be None in the document network. - fn encapsulating_network_metadata_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetworkMetadata> { - let mut encapsulating_path = network_path.to_vec(); - encapsulating_path.pop()?; - let Some(parent_metadata) = self.network_metadata_mut(&encapsulating_path) else { - log::error!("Could not get parent network in encapsulating_node_metadata"); - return None; - }; - Some(parent_metadata) - } - - /// Mutably get the node which encapsulates the currently viewed network. Will always be None in the document network. - // fn encapsulating_node_mut(&mut self, network_path: &[NodeId]) -> Option<&mut DocumentNode> { - // let mut encapsulating_path = network_path.to_vec(); - // let encapsulating_node_id = encapsulating_path.pop()?; - // let Some(parent_network) = self.network_mut(&encapsulating_path) else { - // log::error!("Could not get parent network in encapsulating_node_mut"); - // return None; - // }; - // let Some(encapsulating_node) = parent_network.nodes.mut(&encapsulating_node_id) else { - // log::error!("Could not get encapsulating node in encapsulating_node_mut"); - // return None; - // }; - // Some(encapsulating_node) - // } - - /// Get the node metadata for the node which encapsulates the currently viewed network. Will always be None in the document network. - fn encapsulating_node_metadata_mut(&mut self, network_path: &[NodeId]) -> Option<&mut DocumentNodeMetadata> { - let mut encapsulating_path = network_path.to_vec(); - let encapsulating_node_id = encapsulating_path.pop()?; - let Some(parent_metadata) = self.network_metadata_mut(&encapsulating_path) else { - log::error!("Could not get parent network in encapsulating_node_metadata"); - return None; - }; - let Some(encapsulating_node_metadata) = parent_metadata.persistent_metadata.node_metadata.get_mut(&encapsulating_node_id) else { - log::error!("Could not get encapsulating node metadata in encapsulating_node_metadata"); - return None; - }; - Some(encapsulating_node_metadata) + fn metadata_network_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetwork> { + self.metadata_network.nested_network_mut(network_path) } } @@ -1506,44 +1728,97 @@ impl NodeNetworkInterface { self.transaction_status = TransactionStatus::Finished; } - /// Mutably get the selected nodes for the network at the network_path. Every time they are mutated, the transient metadata for the top of the stack gets unloaded. - pub fn selected_nodes_mut(&mut self, network_path: &[NodeId]) -> Option<&mut SelectedNodes> { - self.unload_stack_dependents(network_path); - let Some(network_metadata) = self.network_metadata_mut(network_path) else { + /// Change the selected nodes for the network at the network_path. Every time they are mutated, the transient metadata for the top of the stack gets unloaded. + fn mutate_selected_nodes(&mut self, nodes: Vec, selection_operation: SelectionOperation, network_path: &[NodeId]) { + let Some(selection_undo_history) = self.selection_undo_history(network_path) else { log::error!("Could not get nested network_metadata in selected_nodes"); - return None; + return; }; - let last_selection_state = network_metadata.persistent_metadata.selection_undo_history.back().cloned().unwrap_or_default(); - - network_metadata.persistent_metadata.selection_undo_history.push_back(last_selection_state); - network_metadata.persistent_metadata.selection_redo_history.clear(); + let mut last_selection_state = selection_undo_history.back().cloned().unwrap_or_default(); + match selection_operation { + SelectionOperation::Add => { + last_selection_state.extend(nodes); + } + SelectionOperation::Remove => { + last_selection_state.retain(|node| !nodes.contains(node)); + } + SelectionOperation::Set => { + last_selection_state = nodes; + } + } - if network_metadata.persistent_metadata.selection_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { - network_metadata.persistent_metadata.selection_undo_history.pop_front(); + let Some(mut value) = self.metadata_value_mut(MetadataType::SelectionUndoHistory, network_path) else { + log::error!("Could not get tagged value in mutate_selected_nodes"); + return; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionUndoHistory in mutate_selected_nodes"); + return; + }; + selection_undo_history.push_back(last_selection_state); + if selection_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { + selection_undo_history.pop_front(); } - network_metadata.persistent_metadata.selection_undo_history.back_mut() + drop(value); + self.unload_stack_dependents(network_path); + self.set_metadata(MetadataType::SelectionRedoHistory, TaggedValue::SelectionHistory(VecDeque::new()), network_path); } - pub fn selection_step_back(&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 selection_step_back"); + pub fn add_selected_nodes(&mut self, nodes: Vec, network_path: &[NodeId]) { + self.mutate_selected_nodes(nodes, SelectionOperation::Add, network_path); + } + pub fn remove_selected_nodes(&mut self, nodes: Vec, network_path: &[NodeId]) { + self.mutate_selected_nodes(nodes, SelectionOperation::Remove, network_path); + } + pub fn set_selected_nodes(&mut self, nodes: Vec, network_path: &[NodeId]) { + self.mutate_selected_nodes(nodes, SelectionOperation::Set, network_path); + } + pub fn remove_selection_history_step(&mut self, network_path: &[NodeId]) { + let Some(mut value) = self.metadata_value_mut(MetadataType::SelectionUndoHistory, network_path) else { + log::error!("Could not get tagged value in remove_selection_history_step"); + return; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionUndoHistory in remove_selection_history_step"); return; }; + selection_undo_history.pop_back(); + } - if let Some(selection_state) = network_metadata.persistent_metadata.selection_undo_history.pop_back() { - network_metadata.persistent_metadata.selection_redo_history.push_front(selection_state); - } + pub fn selection_step_back(&mut self, network_path: &[NodeId]) { + self.selection_step(SelectionDirection::Back, network_path); } pub fn selection_step_forward(&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 selection_step_forward"); - return; + self.selection_step(SelectionDirection::Forward, network_path); + } + + fn selection_step(&mut self, direction: SelectionDirection, network_path: &[NodeId]) { + let (adding_to, removing_from) = match direction { + SelectionDirection::Back => (MetadataType::SelectionUndoHistory, MetadataType::SelectionRedoHistory), + SelectionDirection::Forward => (MetadataType::SelectionRedoHistory, MetadataType::SelectionUndoHistory), }; - if let Some(selection_state) = network_metadata.persistent_metadata.selection_redo_history.pop_front() { - network_metadata.persistent_metadata.selection_undo_history.push_back(selection_state); + let Some(mut value) = self.metadata_value_mut(adding_to, network_path) else { + log::error!("Could not get tagged value in selection_step"); + return; + }; + let TaggedValue::SelectionHistory(selection_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionHistory in selection_step"); + return; + }; + if let Some(selection_state) = selection_history.pop_back() { + drop(value); + let Some(mut value) = self.metadata_value_mut(removing_from, network_path) else { + log::error!("Could not get tagged value in selection_step"); + return; + }; + let TaggedValue::SelectionHistory(selection_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionHistory in selection_step"); + return; + }; + selection_history.push_back(selection_state); } } @@ -1553,23 +1828,23 @@ impl NodeNetworkInterface { } fn try_load_stack_dependents(&mut self, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata(network_path) else { + let Some(TaggedValue::StackDependents(stack_dependents)) = self.metadata_value(MetadataType::StackDependents, network_path) else { log::error!("Could not get nested network_metadata in stack_dependents"); return; }; - if !network_metadata.transient_metadata.stack_dependents.is_loaded() { + if !stack_dependents.is_loaded() { self.load_stack_dependents(network_path); } } fn try_get_stack_dependents(&self, network_path: &[NodeId]) -> Option<&HashMap> { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in try_get_stack_dependents"); + let Some(stack_dependents) = self.metadata_value(MetadataType::StackDependents, network_path) else { + log::error!("Could not get stack_dependents in try_get_stack_dependents"); return None; }; - let TransientMetadata::Loaded(stack_dependents) = &network_metadata.transient_metadata.stack_dependents else { - log::error!("could not load stack_dependents"); + let TaggedValue::StackDependents(TransientMetadata::Loaded(stack_dependents)) = stack_dependents else { + log::error!("Could not get stack_dependents in try_get_stack_dependents"); return None; }; Some(stack_dependents) @@ -1647,15 +1922,15 @@ impl NodeNetworkInterface { owned_sole_dependents.insert(*layer_sole_dependent); new_owned_nodes.insert(*layer_sole_dependent); } - let Some(layer_node) = self.node_metadata_mut(&upstream_layer, network_path) else { + let Some(mut node_type_metadata) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[upstream_layer]].concat()) else { log::error!("Could not get layer node in load_stack_dependents"); continue; }; - let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &mut layer_node.persistent_metadata.node_type_metadata else { + let TaggedValue::NodeTypeMetadata(NodeTypePersistentMetadata::Layer(layer_metadata)) = node_type_metadata.deref_mut() else { log::error!("upstream layer should be a layer"); return; }; - *owned_nodes = TransientMetadata::Loaded(new_owned_nodes); + layer_metadata.transient_metadata.owned_nodes = TransientMetadata::Loaded(new_owned_nodes); } } } @@ -1715,30 +1990,20 @@ impl NodeNetworkInterface { } } - 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.stack_dependents = TransientMetadata::Loaded(stack_dependents); + self.set_metadata(MetadataType::StackDependents, TaggedValue::StackDependents(TransientMetadata::Loaded(stack_dependents)), network_path); } pub fn unload_stack_dependents(&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_stack_dependents"); - return; - }; - network_metadata.transient_metadata.stack_dependents.unload(); + self.set_metadata(MetadataType::StackDependents, TaggedValue::StackDependents(TransientMetadata::Unloaded), network_path) } /// Resets all the offsets for nodes with no LayerOwner when the drag ends pub fn unload_stack_dependents_y_offset(&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_stack_dependents_y_offset"); + let Some(mut value) = self.metadata_value_mut(MetadataType::StackDependents, network_path) else { + log::error!("Could not get stack_dependents in unload_stack_dependents_y_offset"); return; }; - - if let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents { + if let TaggedValue::StackDependents(TransientMetadata::Loaded(stack_dependents)) = value.deref_mut() { for layer_owner in stack_dependents.values_mut() { if let LayerOwner::None(offset) = layer_owner { *offset = 0; @@ -1748,22 +2013,18 @@ impl NodeNetworkInterface { } pub fn import_export_ports(&mut self, network_path: &[NodeId]) -> Option<&Ports> { - let Some(network_metadata) = self.network_metadata(network_path) else { + let Some(TaggedValue::ImportExportPorts(import_export_ports)) = self.metadata_value(MetadataType::ImportExportPorts, network_path) else { log::error!("Could not get nested network_metadata in export_ports"); return None; }; - if !network_metadata.transient_metadata.import_export_ports.is_loaded() { + if !import_export_ports.is_loaded() { self.load_import_export_ports(network_path); } - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in export_ports"); - return None; - }; - let TransientMetadata::Loaded(ports) = &network_metadata.transient_metadata.import_export_ports else { + let Some(TaggedValue::ImportExportPorts(TransientMetadata::Loaded(import_export_ports))) = self.metadata_value(MetadataType::ImportExportPorts, network_path) else { log::error!("could not load import ports"); return None; }; - Some(ports) + Some(import_export_ports) } pub fn load_import_export_ports(&mut self, network_path: &[NodeId]) { @@ -1772,22 +2033,22 @@ impl NodeNetworkInterface { log::error!("Could not get all nodes bounding box in load_export_ports"); return; }; - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in load_export_ports"); + 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 load_export_ports"); + return; + }; + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path).cloned() else { + log::error!("Could not get node_graph_to_viewport in load_export_ports"); return; }; let Some(network) = self.network(network_path) else { log::error!("Could not get current network in load_export_ports"); return; }; + let mut import_export_ports = Ports::new(); - let viewport_top_right = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .inverse() - .transform_point2(network_metadata.persistent_metadata.navigation_metadata.exports_to_edge_distance); + let viewport_top_right = node_graph_to_viewport.inverse().transform_point2(rounded_network_edge_distance.exports_to_edge_distance); let offset_from_top_right = if network .exports .first() @@ -1804,12 +2065,7 @@ impl NodeNetworkInterface { import_export_ports.insert_input_port_at_center(input_index, export_top_right + DVec2::new(0., input_index as f64 * 24.)); } - let viewport_top_left = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .inverse() - .transform_point2(network_metadata.persistent_metadata.navigation_metadata.imports_to_edge_distance); + let viewport_top_left = node_graph_to_viewport.inverse().transform_point2(rounded_network_edge_distance.imports_to_edge_distance); let offset_from_top_left = if network .exports @@ -1826,59 +2082,114 @@ impl NodeNetworkInterface { for output_index in 0..self.number_of_displayed_imports(network_path) { import_export_ports.insert_output_port_at_center(output_index, import_top_left + DVec2::new(0., output_index as f64 * 24.)); } - 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.import_export_ports = TransientMetadata::Loaded(import_export_ports); + self.set_metadata( + MetadataType::ImportExportPorts, + TaggedValue::ImportExportPorts(TransientMetadata::Loaded(import_export_ports)), + network_path, + ); } fn unload_import_export_ports(&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"); + self.set_metadata(MetadataType::ImportExportPorts, TaggedValue::ImportExportPorts(TransientMetadata::Unloaded), network_path); + } + + pub fn rounded_network_edge_distance(&mut self, network_path: &[NodeId]) -> Option<&NetworkEdgeDistance> { + let Some(TaggedValue::RoundedNetworkEdgeDistance(rounded_network_edge_distance)) = self.metadata_value(MetadataType::RoundedNetworkEdgeDistance, network_path) else { + log::error!("Could not get nested network_metadata in rounded_network_edge_distance"); + return None; + }; + if !rounded_network_edge_distance.is_loaded() { + self.load_rounded_network_edge_distance(network_path); + } + let Some(TaggedValue::RoundedNetworkEdgeDistance(TransientMetadata::Loaded(rounded_network_edge_distance))) = self.metadata_value(MetadataType::RoundedNetworkEdgeDistance, network_path) + 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]) { + // 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 Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path) else { + log::error!("Could not get node_graph_to_viewport in load_export_ports"); + return; + }; + + // TODO: Eventually replace node graph top right with the footprint when trying to get the network edge distance + let Some(node_graph_top_right) = self.node_graph_top_right(network_path) else { + log::error!("Could not get node_graph_top_right in load_export_ports"); return; }; - network_metadata.transient_metadata.import_export_ports.unload(); + + 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, + }; + self.set_metadata( + MetadataType::RoundedNetworkEdgeDistance, + TaggedValue::RoundedNetworkEdgeDistance(TransientMetadata::Loaded(network_edge_distance)), + network_path, + ); + } + + fn unload_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) { + self.set_metadata( + MetadataType::RoundedNetworkEdgeDistance, + TaggedValue::RoundedNetworkEdgeDistance(TransientMetadata::Unloaded), + network_path, + ); } 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 { + let node_type_metadata = self.node_type_metadata(node_id, network_path)?; + let NodeTypePersistentMetadata::Layer(layer_metadata) = node_type_metadata else { return None; }; - let TransientMetadata::Loaded(owned_nodes) = owned_nodes else { + let TransientMetadata::Loaded(owned_nodes) = &layer_metadata.transient_metadata.owned_nodes else { return None; }; Some(owned_nodes) } pub fn all_nodes_bounding_box(&mut self, network_path: &[NodeId]) -> Option<&[DVec2; 2]> { - let Some(network_metadata) = self.network_metadata(network_path) else { + let Some(TaggedValue::AllNodesBoundingBox(all_nodes_bounding_box)) = self.metadata_value(MetadataType::AllNodesBoundingBox, network_path) else { log::error!("Could not get nested network_metadata in all_nodes_bounding_box"); return None; }; - - if !network_metadata.transient_metadata.all_nodes_bounding_box.is_loaded() { + if !all_nodes_bounding_box.is_loaded() { self.load_all_nodes_bounding_box(network_path); } - let network_metadata = self.network_metadata(network_path)?; - - let TransientMetadata::Loaded(bounding_box) = &network_metadata.transient_metadata.all_nodes_bounding_box else { + let Some(TaggedValue::AllNodesBoundingBox(TransientMetadata::Loaded(all_nodes_bounding_box))) = self.metadata_value(MetadataType::AllNodesBoundingBox, network_path) else { log::error!("could not load all nodes bounding box"); return None; }; - Some(bounding_box) + Some(all_nodes_bounding_box) } pub fn load_all_nodes_bounding_box(&mut self, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata(network_path) else { + let Some(network) = self.network(network_path) else { log::error!("Could not get nested network_metadata in load_all_nodes_bounding_box"); return; }; - let nodes = network_metadata.persistent_metadata.node_metadata.keys().copied().collect::>(); + let nodes = network.nodes.keys().copied().collect::>(); let all_nodes_bounding_box = nodes .iter() @@ -1889,33 +2200,29 @@ impl NodeNetworkInterface { .reduce(Quad::combine_bounds) .unwrap_or([DVec2::new(0., 0.), DVec2::new(0., 0.)]); - let Some(network_metadata) = self.network_metadata_mut(network_path) else { return }; - - network_metadata.transient_metadata.all_nodes_bounding_box = TransientMetadata::Loaded(all_nodes_bounding_box); + self.set_metadata( + MetadataType::AllNodesBoundingBox, + TaggedValue::AllNodesBoundingBox(TransientMetadata::Loaded(all_nodes_bounding_box)), + network_path, + ); } pub fn unload_all_nodes_bounding_box(&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_all_nodes_bounding_box"); - return; - }; - network_metadata.transient_metadata.all_nodes_bounding_box.unload(); - network_metadata.transient_metadata.import_export_ports.unload(); + self.set_metadata(MetadataType::AllNodesBoundingBox, TaggedValue::AllNodesBoundingBox(TransientMetadata::Unloaded), network_path); + self.set_metadata(MetadataType::ImportExportPorts, TaggedValue::ImportExportPorts(TransientMetadata::Unloaded), network_path); } pub fn outward_wires(&mut self, network_path: &[NodeId]) -> Option<&HashMap>> { - let Some(network_metadata) = self.network_metadata(network_path) else { + let Some(TaggedValue::OutwardWires(outward_wires)) = self.metadata_value(MetadataType::OutwardWires, network_path) else { log::error!("Could not get nested network_metadata in outward_wires"); return None; }; - if !network_metadata.transient_metadata.outward_wires.is_loaded() { + if !outward_wires.is_loaded() { self.load_outward_wires(network_path); } - let network_metadata = self.network_metadata(network_path)?; - - let TransientMetadata::Loaded(outward_wires) = &network_metadata.transient_metadata.outward_wires else { + let Some(TaggedValue::OutwardWires(TransientMetadata::Loaded(outward_wires))) = self.metadata_value(MetadataType::OutwardWires, network_path) else { log::error!("could not load outward wires"); return None; }; @@ -1966,44 +2273,35 @@ impl NodeNetworkInterface { } } - let Some(network_metadata) = self.network_metadata_mut(network_path) else { return }; - - network_metadata.transient_metadata.outward_wires = TransientMetadata::Loaded(outward_wires); + self.set_metadata(MetadataType::OutwardWires, TaggedValue::OutwardWires(TransientMetadata::Loaded(outward_wires)), network_path); } fn unload_outward_wires(&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_outward_wires"); - return; - }; - network_metadata.transient_metadata.outward_wires.unload(); + self.set_metadata(MetadataType::OutwardWires, TaggedValue::OutwardWires(TransientMetadata::Unloaded), network_path); } pub fn layer_width(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(node_type_metadata) = self.node_type_metadata(node_id, network_path) else { log::error!("Could not get nested node_metadata in layer_width"); return None; }; - if !node_metadata.persistent_metadata.is_layer() { - log::error!("Cannot get layer width for non layer node {node_id} in network {network_path:?}"); - return None; - } - let layer_width_loaded = if let NodeTypeTransientMetadata::Layer(layer_metadata) = &node_metadata.transient_metadata.node_type_metadata { - layer_metadata.layer_width.is_loaded() + let layer_width_loaded = if let NodeTypePersistentMetadata::Layer(layer_metadata) = node_type_metadata { + layer_metadata.transient_metadata.layer_width.is_loaded() } else { - false + log::error!("Could not get layer width for non layer node"); + return None; }; if !layer_width_loaded { self.load_layer_width(node_id, network_path); } - let node_metadata = self.node_metadata(node_id, network_path)?; - let NodeTypeTransientMetadata::Layer(layer_metadata) = &node_metadata.transient_metadata.node_type_metadata else { - log::error!("Transient metadata should be layer metadata when getting layer width"); + let node_type_metadata = self.node_type_metadata(node_id, network_path)?; + let NodeTypePersistentMetadata::Layer(layer_metadata) = node_type_metadata else { + log::error!("Metadata should be layer metadata when getting layer width"); return None; }; - let TransientMetadata::Loaded(layer_width) = layer_metadata.layer_width else { + let TransientMetadata::Loaded(layer_width) = layer_metadata.transient_metadata.layer_width else { log::error!("Transient metadata was not loaded when getting layer width"); return None; }; @@ -2027,39 +2325,29 @@ impl NodeNetworkInterface { let layer_width_pixels = left_thumbnail_padding + thumbnail_width + gap_width + text_width + grip_padding + grip_width + icon_overhang_width; let layer_width = ((layer_width_pixels / 24.).ceil() as u32).max(8); - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { - log::error!("Could not get nested node_metadata in load_layer_width"); + let Some(mut node_metadata_value) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get node_metadata in load_layer_width"); return; }; // Ensure layer width is not loaded for a non layer node - if node_metadata.persistent_metadata.is_layer() { - if let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata { - layer_metadata.layer_width = TransientMetadata::Loaded(layer_width); - } else { - // Set the entire transient node type metadata to be a layer, in case it was previously a node - node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata { - layer_width: TransientMetadata::Loaded(layer_width), - }); - } + if let TaggedValue::NodeTypeMetadata(NodeTypePersistentMetadata::Layer(layer_metadata)) = node_metadata_value.deref_mut() { + layer_metadata.transient_metadata.layer_width = TransientMetadata::Loaded(layer_width); } else { - log::warn!("Tried loading layer width for non layer node"); + log::warn!("Loaded layer width for non layer node"); } } /// Unloads layer width if the node is a layer pub fn try_unload_layer_width(&mut self, node_id: &NodeId, network_path: &[NodeId]) { - let is_layer = self.is_layer(node_id, network_path); - - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + let Some(mut node_metadata_value) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get node_metadata in load_layer_width"); return; }; // If the node is a layer, then the width and click targets need to be recalculated - if is_layer { - if let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata { - layer_metadata.layer_width.unload(); - } + if let TaggedValue::NodeTypeMetadata(NodeTypePersistentMetadata::Layer(layer_metadata)) = node_metadata_value.deref_mut() { + layer_metadata.transient_metadata.layer_width.unload(); } } @@ -2069,22 +2357,33 @@ impl NodeNetworkInterface { } fn try_load_node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(click_targets) = self.metadata_value(MetadataType::ClickTargets, &[network_path, &[*node_id]].concat()) else { log::error!("Could not get nested node_metadata in node_click_targets"); return; }; - if !node_metadata.transient_metadata.click_targets.is_loaded() { + let TaggedValue::ClickTargets(click_targets) = click_targets else { + log::error!("Could not get click targets in node_click_targets"); + return; + }; + if !click_targets.is_loaded() { self.load_node_click_targets(node_id, network_path) }; } fn try_get_node_click_targets(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> { - let node_metadata = self.node_metadata(node_id, network_path)?; - let TransientMetadata::Loaded(click_target) = &node_metadata.transient_metadata.click_targets else { + let Some(click_targets) = self.metadata_value(MetadataType::ClickTargets, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get nested node_metadata in node_click_targets"); + return None; + }; + let TaggedValue::ClickTargets(click_targets) = click_targets else { + log::error!("Could not get click targets in node_click_targets"); + return None; + }; + let TransientMetadata::Loaded(click_targets) = click_targets else { log::error!("Could not load node type metadata when getting click targets"); return None; }; - Some(click_target) + Some(click_targets) } pub fn load_node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) { @@ -2092,10 +2391,6 @@ impl NodeNetworkInterface { log::error!("Could not get node position in load_node_click_targets for node {node_id}"); return; }; - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { - log::error!("Could not get nested node_metadata in load_node_click_targets"); - return; - }; let Some(network) = self.network(network_path) else { log::error!("Could not get network in load_node_click_targets"); return; @@ -2107,7 +2402,7 @@ impl NodeNetworkInterface { let node_top_left = node_position.as_dvec2() * 24.; let mut port_click_targets = Ports::new(); - let document_node_click_targets = if !node_metadata.persistent_metadata.is_layer() { + let document_node_click_targets = if !self.is_layer(node_id, network_path) { // Create input/output click targets let mut input_row_count = 0; for (input_index, input) in document_node.inputs.iter().enumerate() { @@ -2126,7 +2421,7 @@ impl NodeNetworkInterface { 1 }; // If the node does not have a primary output, shift all ports down a row - let mut output_row_count = if !node_metadata.persistent_metadata.has_primary_output { 1 } else { 0 }; + let mut output_row_count = if !self.has_primary_output(node_id, network_path) { 1 } else { 0 }; for output_index in 0..number_of_outputs { port_click_targets.insert_node_output(output_index, output_row_count, node_top_left); output_row_count += 1; @@ -2144,7 +2439,7 @@ impl NodeNetworkInterface { DocumentNodeClickTargets { node_click_target, port_click_targets, - node_type_metadata: NodeTypeClickTargets::Node, + node_type_click_targets: NodeTypeClickTargets::Node, } } else { // Layer inputs @@ -2183,18 +2478,54 @@ impl NodeNetworkInterface { DocumentNodeClickTargets { node_click_target, port_click_targets, - node_type_metadata: NodeTypeClickTargets::Layer(LayerClickTargets { + node_type_click_targets: NodeTypeClickTargets::Layer(LayerClickTargets { visibility_click_target, grip_click_target, }), } }; - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + let Some(mut click_targets_metadata) = self.metadata_value_mut(MetadataType::ClickTargets, &[network_path, &[*node_id]].concat()) else { log::error!("Could not get nested node_metadata in load_node_click_targets"); return; }; - node_metadata.transient_metadata.click_targets = TransientMetadata::Loaded(document_node_click_targets); + let TaggedValue::ClickTargets(click_targets) = click_targets_metadata.deref_mut() else { + log::error!("Could not get click targets in load_node_click_targets"); + return; + }; + *click_targets = TransientMetadata::Loaded(document_node_click_targets); + } + + pub fn unload_node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) { + let Some(mut click_targets_metadata) = self.metadata_value_mut(MetadataType::ClickTargets, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get nested node_metadata in load_node_click_targets"); + return; + }; + let TaggedValue::ClickTargets(click_targets) = click_targets_metadata.deref_mut() else { + log::error!("Could not get click targets in load_node_click_targets"); + return; + }; + *click_targets = TransientMetadata::Unloaded; + } + + pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec, network_path: &[NodeId]) { + let upstream_nodes = self.upstream_flow_back_from_nodes(node_ids, network_path, FlowType::UpstreamFlow).collect::>(); + + for upstream_id in &upstream_nodes { + self.unload_node_click_targets(upstream_id, network_path) + } + } + + pub fn unload_all_nodes_click_targets(&mut self, network_path: &[NodeId]) { + let Some(network) = self.network(network_path) else { + log::error!("Could not get nested network in unload_all_nodes_click_targets"); + return; + }; + let upstream_nodes = network.nodes.keys().cloned().collect::>(); + + for upstream_id in &upstream_nodes { + self.unload_node_click_targets(upstream_id, network_path) + } } pub fn node_bounding_box(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<[DVec2; 2]> { @@ -2219,13 +2550,13 @@ impl NodeNetworkInterface { /// Get the top left position in node graph coordinates for a node by recursively iterating downstream through cached positions, which means the iteration can be broken once a known position is reached. pub fn position_from_downstream_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + let Some(node_type_metadata) = self.node_type_metadata(node_id, network_path) else { log::error!("Could not get nested node_metadata in position_from_downstream_node"); return None; }; - match &node_metadata.persistent_metadata.node_type_metadata.clone() { + match node_type_metadata { NodeTypePersistentMetadata::Layer(layer_metadata) => { - match layer_metadata.position { + match layer_metadata.persistent_metadata.position { LayerPosition::Absolute(position) => Some(position), LayerPosition::Stack(y_offset) => { let Some(downstream_node_connectors) = self @@ -2286,11 +2617,7 @@ impl NodeNetworkInterface { log::error!("Could not get downstream node input connector with input index 1 for node with Position::Chain"); return None; }; - let Some(downstream_node_metadata) = self.network_metadata(network_path)?.persistent_metadata.node_metadata.get(downstream_node_id) else { - log::error!("Downstream node metadata not found in node_metadata for node with Position::Chain"); - return None; - }; - if downstream_node_metadata.persistent_metadata.is_layer() { + if self.is_layer(downstream_node_id, network_path) { // Get the position of the layer let layer_position = self.position(downstream_node_id, network_path)?; return Some(layer_position + IVec2::new(-node_distance_from_layer * 7, 0)); @@ -2303,42 +2630,6 @@ impl NodeNetworkInterface { } } } - - pub fn unload_node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { - log::error!("Could not get nested node_metadata in unload_node_click_target"); - return; - }; - node_metadata.transient_metadata.click_targets.unload(); - } - - pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec, network_path: &[NodeId]) { - let upstream_nodes = self.upstream_flow_back_from_nodes(node_ids, network_path, FlowType::UpstreamFlow).collect::>(); - - for upstream_id in &upstream_nodes { - let Some(node_metadata) = self.node_metadata_mut(upstream_id, network_path) else { - log::error!("Could not get node_metadata for node {upstream_id}"); - return; - }; - node_metadata.transient_metadata.click_targets.unload(); - } - } - - pub fn unload_all_nodes_click_targets(&mut self, network_path: &[NodeId]) { - let Some(network) = self.network(network_path) else { - log::error!("Could not get nested network in unload_all_nodes_click_targets"); - return; - }; - let upstream_nodes = network.nodes.keys().cloned().collect::>(); - - for upstream_id in &upstream_nodes { - let Some(node_metadata) = self.node_metadata_mut(upstream_id, network_path) else { - log::error!("Could not get node_metadata for node {upstream_id}"); - return; - }; - node_metadata.transient_metadata.click_targets.unload(); - } - } } // Helper functions for mutable getters @@ -2367,11 +2658,11 @@ impl NodeNetworkInterface { let mut all_node_click_targets = Vec::new(); let mut port_click_targets = Vec::new(); let mut icon_click_targets = Vec::new(); - let Some(network_metadata) = self.network_metadata(network_path) else { + let Some(network) = self.network(network_path) else { log::error!("Could not get nested network_metadata in collect_frontend_click_targets"); return FrontendClickTargets::default(); }; - network_metadata.persistent_metadata.node_metadata.keys().copied().collect::>().into_iter().for_each(|node_id| { + network.nodes.keys().copied().collect::>().into_iter().for_each(|node_id| { if let (Some(import_export_click_targets), Some(node_click_targets)) = (self.import_export_ports(network_path).cloned(), self.node_click_targets(&node_id, network_path)) { let mut node_path = String::new(); @@ -2382,7 +2673,7 @@ impl NodeNetworkInterface { let _ = port.subpath().subpath_to_svg(&mut port_path, DAffine2::IDENTITY); port_click_targets.push(port_path); } - if let NodeTypeClickTargets::Layer(layer_metadata) = &node_click_targets.node_type_metadata { + if let NodeTypeClickTargets::Layer(layer_metadata) = &node_click_targets.node_type_click_targets { let mut port_path = String::new(); let _ = layer_metadata.visibility_click_target.subpath().subpath_to_svg(&mut port_path, DAffine2::IDENTITY); icon_click_targets.push(port_path); @@ -2407,25 +2698,20 @@ impl NodeNetworkInterface { let mut all_nodes_bounding_box = String::new(); let _ = rect.subpath_to_svg(&mut all_nodes_bounding_box, DAffine2::IDENTITY); - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in collect_front_end_click_targets"); + 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_front_end_click_targets"); + return FrontendClickTargets::default(); + }; + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path) else { + log::error!("Could not get node_graph_to_viewport in collect_front_end_click_targets"); return FrontendClickTargets::default(); }; - let import_exports_viewport_top_left = network_metadata.persistent_metadata.navigation_metadata.imports_to_edge_distance; - let import_exports_viewport_bottom_right = network_metadata.persistent_metadata.navigation_metadata.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_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 = node_graph_to_viewport.inverse().transform_point2(import_exports_viewport_top_left); + let node_graph_bottom_right = node_graph_to_viewport.inverse().transform_point2(import_exports_viewport_bottom_right); let import_exports_target = bezier_rs::Subpath::::new_rect(node_graph_top_left, node_graph_bottom_right); let mut import_exports_bounding_box = String::new(); @@ -2458,34 +2744,22 @@ impl NodeNetworkInterface { let has_single_output_wire = outward_wires.len() <= 1; // TODO: Eventually allow nodes at the bottom of a stack to be layers, where `input_count` is 0 - self.node_metadata(node_id, network_path) - .is_some_and(|node_metadata| node_metadata.persistent_metadata.has_primary_output) - && output_count == 1 - && (input_count == 1 || input_count == 2) - && has_single_output_wire - } - - pub fn node_graph_ptz_mut(&mut self, network_path: &[NodeId]) -> Option<&mut PTZ> { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in node_graph_ptz_mut"); - return None; - }; - Some(&mut network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz) + self.has_primary_output(node_id, network_path) && output_count == 1 && (input_count == 1 || input_count == 2) && has_single_output_wire } // TODO: Optimize getting click target intersections from click by using a spacial data structure like a quadtree instead of linear search /// Click target getter methods pub fn node_from_click(&mut self, click: DVec2, network_path: &[NodeId]) -> Option { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in node_from_click"); - return None; - }; let Some(network) = self.network(network_path) else { log::error!("Could not get nested network in node_from_click"); return None; }; + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path) else { + log::error!("Could not get node_graph_to_viewport in collect_front_end_click_targets"); + return None; + }; - let point = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(click); + let point = node_graph_to_viewport.inverse().transform_point2(click); let nodes = network.nodes.keys().copied().collect::>(); let clicked_nodes = nodes .iter() @@ -2498,38 +2772,27 @@ impl NodeNetworkInterface { // Since nodes are placed on top of layer chains, find the first non layer node that was clicked, and if there way no non layer nodes clicked, then find the first layer node that was clicked clicked_nodes .iter() - .find_map(|node_id| { - let Some(node_metadata) = self.network_metadata(network_path)?.persistent_metadata.node_metadata.get(node_id) else { - log::error!("Could not get node_metadata for node {node_id}"); - return None; - }; - if !node_metadata.persistent_metadata.is_layer() { - Some(*node_id) - } else { - None - } - }) + .find_map(|node_id| if !self.is_layer(node_id, network_path) { Some(*node_id) } else { None }) .or_else(|| clicked_nodes.into_iter().next()) } pub fn layer_click_target_from_click(&mut self, click: DVec2, click_target_type: LayerClickTargetTypes, network_path: &[NodeId]) -> Option { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in visibility_from_click"); - return None; - }; let Some(network) = self.network(network_path) else { log::error!("Could not get nested network in visibility_from_click"); return None; }; - - let point = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(click); + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path) else { + log::error!("Could not get node_graph_to_viewport in collect_front_end_click_targets"); + return None; + }; + let point = node_graph_to_viewport.inverse().transform_point2(click); let node_ids: Vec<_> = network.nodes.keys().copied().collect(); node_ids .iter() .filter_map(|node_id| { self.node_click_targets(node_id, network_path).and_then(|transient_node_metadata| { - if let NodeTypeClickTargets::Layer(layer) = &transient_node_metadata.node_type_metadata { + if let NodeTypeClickTargets::Layer(layer) = &transient_node_metadata.node_type_click_targets { match click_target_type { LayerClickTargetTypes::Visibility => layer.visibility_click_target.intersect_point_no_stroke(point).then_some(*node_id), LayerClickTargetTypes::Grip => layer.grip_click_target.intersect_point_no_stroke(point).then_some(*node_id), @@ -2543,16 +2806,16 @@ impl NodeNetworkInterface { } pub fn input_connector_from_click(&mut self, click: DVec2, network_path: &[NodeId]) -> Option { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in input_connector_from_click"); - return None; - }; let Some(network) = self.network(network_path) else { log::error!("Could not get nested network in input_connector_from_click"); return None; }; + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path) else { + log::error!("Could not get node_graph_to_viewport in collect_front_end_click_targets"); + return None; + }; + let point = node_graph_to_viewport.inverse().transform_point2(click); - let point = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(click); network .nodes .keys() @@ -2576,16 +2839,15 @@ impl NodeNetworkInterface { } pub fn output_connector_from_click(&mut self, click: DVec2, network_path: &[NodeId]) -> Option { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in output_connector_from_click"); - return None; - }; let Some(network) = self.network(network_path) else { log::error!("Could not get nested network in output_connector_from_click"); return None; }; - - let point = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(click); + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path) else { + log::error!("Could not get node_graph_to_viewport in collect_front_end_click_targets"); + return None; + }; + let point = node_graph_to_viewport.inverse().transform_point2(click); let nodes = network.nodes.keys().copied().collect::>(); nodes .iter() @@ -2646,15 +2908,15 @@ impl NodeNetworkInterface { /// Get the combined bounding box of the click targets of the selected nodes in the node graph in viewport space pub fn selected_nodes_bounding_box_viewport(&mut self, network_path: &[NodeId]) -> Option<[DVec2; 2]> { // Always get the bounding box for nodes in the currently viewed network - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in selected_nodes_bounding_box_viewport"); - return None; - }; let Some(selected_nodes) = self.selected_nodes(network_path) else { log::error!("Could not get selected nodes in selected_nodes_bounding_box_viewport"); return None; }; - let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport; + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path).cloned() else { + log::error!("Could not get node_graph_to_viewport in collect_front_end_click_targets"); + return None; + }; + selected_nodes .selected_nodes() .cloned() @@ -2662,7 +2924,7 @@ impl NodeNetworkInterface { .iter() .filter_map(|node_id| { self.node_click_targets(node_id, network_path) - .and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box_with_transform(node_graph_to_viewport)) + .and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box_with_transform(&node_graph_to_viewport)) }) .reduce(graphene_core::renderer::Quad::combine_bounds) } @@ -2670,25 +2932,23 @@ impl NodeNetworkInterface { /// Gets the bounding box in viewport coordinates for each node in the node graph pub fn graph_bounds_viewport_space(&mut self, network_path: &[NodeId]) -> Option<[DVec2; 2]> { let bounds = *self.all_nodes_bounding_box(network_path)?; - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in graph_bounds_viewport_space"); + let Some(node_graph_to_viewport) = self.node_graph_to_viewport(network_path) else { + log::error!("Could not get node_graph_to_viewport in graph_bounds_viewport_space"); return None; }; - let bounding_box_subpath = bezier_rs::Subpath::::new_rect(bounds[0], bounds[1]); - bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport) + bounding_box_subpath.bounding_box_with_transform(node_graph_to_viewport) } pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> (HashMap, HashMap) { - let Some(network_metadata) = self.network_metadata(network_path) else { + let Some(network) = self.network(network_path) else { log::error!("Could not get nested network_metadata in collect_layer_widths"); return (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 }) + let nodes = network + .nodes + .keys() + .filter_map(|node_id| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None }) .collect::>(); ( nodes @@ -2816,57 +3076,40 @@ impl NodeNetworkInterface { pub fn copy_all_navigation_metadata(&mut self, other_interface: &NodeNetworkInterface) { let mut stack = vec![vec![]]; while let Some(path) = stack.pop() { - let Some(self_network_metadata) = self.network_metadata_mut(&path) else { + let Some(self_network_metadata) = self.network(&path) else { continue; }; - if let Some(other_network_metadata) = other_interface.network_metadata(&path) { - self_network_metadata.persistent_metadata.navigation_metadata = other_network_metadata.persistent_metadata.navigation_metadata.clone(); - } - - stack.extend(self_network_metadata.persistent_metadata.node_metadata.keys().map(|node_id| { + stack.extend(self_network_metadata.nodes.keys().map(|node_id| { let mut current_path = path.clone(); current_path.push(*node_id); current_path })); + if let (Some(ptz), Some(node_graph_to_viewport), Some(node_graph_top_right)) = + (other_interface.ptz(&path), other_interface.node_graph_to_viewport(&path), other_interface.node_graph_top_right(&path)) + { + self.set_metadata(MetadataType::NavigationMetadata(NavigationMetadataType::PTZ), TaggedValue::PTZ(*ptz), &path); + self.set_metadata( + MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphToViewport), + TaggedValue::DAffine2(*node_graph_to_viewport), + &path, + ); + self.set_metadata( + MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphTopRight), + TaggedValue::DVec2(*node_graph_top_right), + &path, + ); + }; } } - pub fn set_transform(&mut self, transform: DAffine2, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network in set_transform"); - return; - }; - network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport = transform; - self.unload_import_export_ports(network_path); - } - // 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]) { - 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; - - 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); - - network_metadata.persistent_metadata.navigation_metadata.exports_to_edge_distance = rounded_viewport_exports_distance; - network_metadata.persistent_metadata.navigation_metadata.imports_to_edge_distance = rounded_viewport_imports_distance; - + self.set_metadata( + MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphTopRight), + TaggedValue::DVec2(node_graph_top_right), + network_path, + ); + self.unload_rounded_network_edge_distance(network_path); self.unload_import_export_ports(network_path); } @@ -2909,29 +3152,36 @@ impl NodeNetworkInterface { self.transaction_modified(); - // There will not be an encapsulating node if the network is the document network - if let Some(encapsulating_node_metadata) = self.encapsulating_node_metadata_mut(network_path) { - if insert_index == -1 { - encapsulating_node_metadata.persistent_metadata.output_names.push(output_name); - } else { - encapsulating_node_metadata.persistent_metadata.output_names.insert(insert_index as usize, output_name); - } - }; - // Update the export ports and outward wires for the current network self.unload_import_export_ports(network_path); self.unload_outward_wires(network_path); // Update the outward wires and bounding box for all nodes in the encapsulating network - if let Some(encapsulating_network_metadata) = self.encapsulating_network_metadata_mut(network_path) { - encapsulating_network_metadata.transient_metadata.outward_wires.unload(); - encapsulating_network_metadata.transient_metadata.all_nodes_bounding_box.unload(); + if network_path.len() > 1 { + let mut encapsulating_network_path = network_path.to_vec(); + encapsulating_network_path.pop(); + self.unload_outward_wires(&encapsulating_network_path); + self.unload_all_nodes_bounding_box(&encapsulating_network_path); + let Some(mut output_names_value) = self.metadata_value_mut(MetadataType::OutputNames, &encapsulating_network_path) else { + log::error!("Could not get output_names in add_export for network {:?}", encapsulating_network_path); + return; + }; + let TaggedValue::VecString(output_names) = output_names_value.deref_mut() else { + log::error!("Could not get output_names in add_export for network {:?}", encapsulating_network_path); + return; + }; + if insert_index == -1 { + output_names.push(output_name); + } else { + output_names.insert(insert_index as usize, output_name); + } } // Update the click targets for the encapsulating node, if it exists. There is no encapsulating node if the network is the document network - if let Some(encapsulating_node_metadata_mut) = self.encapsulating_node_metadata_mut(network_path) { - encapsulating_node_metadata_mut.transient_metadata.click_targets.unload(); - }; + let mut encapsulating_network_path = network_path.to_vec(); + if let Some(encapsulating_node_id) = encapsulating_network_path.pop() { + self.unload_node_click_targets(&encapsulating_node_id, &encapsulating_network_path); + } // If the export is inserted as the first input or second input, and the parent network is the document_network, then it may have affected the document metadata structure if network_path.len() == 1 && (insert_index == 0 || insert_index == 1) { @@ -2963,21 +3213,34 @@ impl NodeNetworkInterface { } self.transaction_modified(); - - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { - log::error!("Could not get node_metadata in insert_input"); + let node_path = [network_path, &[*node_id]].concat(); + let Some(mut input_names_value) = self.metadata_value_mut(MetadataType::InputNames, &node_path) else { + log::error!("Could not get input_names in insert_input for node {:?}", node_path); + return; + }; + let TaggedValue::VecString(input_names) = input_names_value.deref_mut() else { + log::error!("Could not get input_names in insert_input for node {:?}", node_path); return; }; if insert_index == -1 { - node_metadata.persistent_metadata.input_names.push(input_name); + input_names.push(input_name); } else { - node_metadata.persistent_metadata.input_names.insert(insert_index as usize, input_name); + input_names.insert(insert_index as usize, input_name); } + drop(input_names_value); + let Some(network) = self.network(network_path) else { + log::error!("Could not get network in insert_input"); + return; + }; + let Some(node) = network.nodes.get(node_id) else { + log::error!("Could not get node in insert_input"); + return; + }; // Update the internal network import ports and outwards connections (if has a network implementation) - if let Some(internal_network) = &mut node_metadata.persistent_metadata.network_metadata { - internal_network.transient_metadata.import_export_ports.unload(); - internal_network.transient_metadata.outward_wires.unload(); + if matches!(node.implementation, DocumentNodeImplementation::Network { .. }) { + self.unload_import_export_ports(&node_path); + self.unload_outward_wires(&node_path); } // Update the click targets for the node @@ -3131,11 +3394,11 @@ impl NodeNetworkInterface { log::error!("Could not get current node position in set_input for node {upstream_node_id}"); return; }; - let Some(node_metadata) = self.node_metadata(upstream_node_id, network_path) else { + let Some(node_type_metadata) = self.node_type_metadata(upstream_node_id, network_path) else { log::error!("Could not get node_metadata in set_input"); return; }; - match &node_metadata.persistent_metadata.node_type_metadata { + match node_type_metadata { NodeTypePersistentMetadata::Layer(_) => { match &input_connector { InputConnector::Export(_) => { @@ -3147,11 +3410,11 @@ impl NodeNetworkInterface { input_index, } => { // If a layer is connected to another node, it should be set to stack positioning - let Some(downstream_node_metadata) = self.node_metadata(downstream_node_id, network_path) else { + let Some(downstream_node_type_metadata) = self.node_type_metadata(downstream_node_id, network_path) else { log::error!("Could not get downstream node_metadata in set_input"); return; }; - match &downstream_node_metadata.persistent_metadata.node_type_metadata { + match downstream_node_type_metadata { NodeTypePersistentMetadata::Layer(_) => { // If the layer feeds into the bottom input of layer, set its position to stack at its previous y position if *input_index == 0 { @@ -3304,17 +3567,8 @@ impl NodeNetworkInterface { }; network.nodes.insert(node_id, node_template.document_node); + self.insert_all_node_metadata(node_id, node_template.persistent_node_metadata, network_path); self.transaction_modified(); - - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Network not found in insert_node"); - return; - }; - let node_metadata = DocumentNodeMetadata { - persistent_metadata: node_template.persistent_node_metadata, - transient_metadata: DocumentNodeTransientMetadata::default(), - }; - network_metadata.persistent_metadata.node_metadata.insert(node_id, node_metadata); } for new_node_id in new_ids.values() { self.unload_node_click_targets(new_node_id, network_path); @@ -3337,22 +3591,88 @@ impl NodeNetworkInterface { }; network.nodes.insert(node_id, node_template.document_node); + self.insert_all_node_metadata(node_id, node_template.persistent_node_metadata, network_path); self.transaction_modified(); - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Network not found in insert_node"); - return; - }; - let node_metadata = DocumentNodeMetadata { - persistent_metadata: node_template.persistent_node_metadata, - transient_metadata: DocumentNodeTransientMetadata::default(), - }; - network_metadata.persistent_metadata.node_metadata.insert(node_id, node_metadata); - self.unload_all_nodes_bounding_box(network_path); self.unload_node_click_targets(&node_id, network_path) } + fn insert_all_node_metadata(&mut self, node_id: NodeId, persistent_metadata: DocumentNodePersistentMetadata, network_path: &[NodeId]) { + let mut node_path = network_path.to_vec(); + node_path.push(node_id); + + self.insert_node_metadata(MetadataType::Reference, TaggedValue::OptionalString(persistent_metadata.reference), &node_path); + self.insert_node_metadata(MetadataType::DisplayName, TaggedValue::String(persistent_metadata.display_name), &node_path); + self.insert_node_metadata(MetadataType::InputNames, TaggedValue::VecString(persistent_metadata.input_names), &node_path); + self.insert_node_metadata(MetadataType::OutputNames, TaggedValue::VecString(persistent_metadata.output_names), &node_path); + self.insert_node_metadata(MetadataType::HasPrimaryOutput, TaggedValue::Bool(persistent_metadata.has_primary_output), &node_path); + self.insert_node_metadata(MetadataType::Locked, TaggedValue::Bool(persistent_metadata.locked), &node_path); + self.insert_node_metadata(MetadataType::NodeTypeMetadata, TaggedValue::NodeTypeMetadata(persistent_metadata.node_type_metadata), &node_path); + self.insert_node_metadata(MetadataType::ClickTargets, TaggedValue::ClickTargets(TransientMetadata::Unloaded), &node_path); + if let Some(nested_network) = persistent_metadata.network_metadata { + self.insert_network_metadata(nested_network.persistent_metadata, &node_path); + } + } + + /// Adds nodes for the network metadata. Should always be called when creating a new NodeNetwork + pub fn insert_network_metadata(&mut self, persistent_metadata: NodeNetworkPersistentMetadata, network_path: &[NodeId]) { + self.insert_node_metadata(MetadataType::Previewing, TaggedValue::Previewing(persistent_metadata.previewing), network_path); + self.insert_node_metadata( + MetadataType::NavigationMetadata(NavigationMetadataType::PTZ), + TaggedValue::PTZ(persistent_metadata.navigation_metadata.node_graph_ptz), + network_path, + ); + self.insert_node_metadata( + MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphToViewport), + TaggedValue::DAffine2(persistent_metadata.navigation_metadata.node_graph_to_viewport), + network_path, + ); + self.insert_node_metadata( + MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphTopRight), + TaggedValue::DVec2(persistent_metadata.navigation_metadata.node_graph_top_right), + network_path, + ); + self.insert_node_metadata( + MetadataType::SelectionUndoHistory, + TaggedValue::SelectionHistory(persistent_metadata.selection_undo_history), + network_path, + ); + self.insert_node_metadata( + MetadataType::SelectionRedoHistory, + TaggedValue::SelectionHistory(persistent_metadata.selection_redo_history), + network_path, + ); + self.insert_node_metadata(MetadataType::StackDependents, TaggedValue::StackDependents(TransientMetadata::Unloaded), network_path); + self.insert_node_metadata(MetadataType::AllNodesBoundingBox, TaggedValue::AllNodesBoundingBox(TransientMetadata::Unloaded), network_path); + self.insert_node_metadata(MetadataType::OutwardWires, TaggedValue::OutwardWires(TransientMetadata::Unloaded), network_path); + self.insert_node_metadata(MetadataType::ImportExportPorts, TaggedValue::ImportExportPorts(TransientMetadata::Unloaded), network_path); + self.insert_node_metadata( + MetadataType::RoundedNetworkEdgeDistance, + TaggedValue::RoundedNetworkEdgeDistance(TransientMetadata::Unloaded), + network_path, + ); + + for (node_id, node_metadata) in persistent_metadata.node_metadata { + self.insert_all_node_metadata(node_id, node_metadata.persistent_metadata, network_path); + } + } + + /// Adds a node for the metadata. TODO: Consider calling this in set_metadata if a metadata node cannot be found. + fn insert_node_metadata(&mut self, metadata: MetadataType, tagged_value: TaggedValue, node_path: &[NodeId]) { + let metadata_node_id = Self::metadata_node_id(metadata, node_path); + log::debug!("Inserting metadata {metadata:?} for node {node_path:?} with id {metadata_node_id}"); + let network = self.metadata_network_mut(&[]).unwrap(); + network.nodes.insert( + metadata_node_id, + DocumentNode { + implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::ClonedNode".into()), + inputs: vec![NodeInput::value(tagged_value, true)], + ..Default::default() + }, + ); + } + /// Deletes all nodes in `node_ids` and any sole dependents in the horizontal chain if the node to delete is a layer node. pub fn delete_nodes(&mut self, nodes_to_delete: Vec, delete_children: bool, network_path: &[NodeId]) { let Some(outward_wires) = self.outward_wires(network_path).cloned() else { @@ -3435,11 +3755,6 @@ impl NodeNetworkInterface { network.nodes.remove(delete_node_id); self.transaction_modified(); - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in delete_nodes"); - continue; - }; - network_metadata.persistent_metadata.node_metadata.remove(delete_node_id); for previous_chain_node in upstream_chain_nodes { self.set_chain_position(&previous_chain_node, network_path); } @@ -3447,11 +3762,7 @@ impl NodeNetworkInterface { self.unload_all_nodes_bounding_box(network_path); // Instead of unloaded all node click targets, just unload the nodes upstream from the deleted nodes. unload_upstream_node_click_targets will not work since the nodes have been deleted. self.unload_all_nodes_click_targets(network_path); - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::DeleteNodes"); - return; - }; - selected_nodes.retain_selected_nodes(|node_id| !nodes_to_delete.contains(node_id)); + self.remove_selected_nodes(nodes_to_delete, network_path); } /// Removes all references to the node with the given id from the network, and reconnects the input to the node below. @@ -3515,12 +3826,7 @@ impl NodeNetworkInterface { let upstream_nodes = self.upstream_flow_back_from_nodes(vec![*reconnect_node], network_path, FlowType::PrimaryFlow).collect::>(); // Select the reconnect node to move to ensure the shifting works correctly - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in remove_references_from_network"); - return false; - }; - - let old_selected_nodes = selected_nodes.replace_with(upstream_nodes); + self.set_selected_nodes(upstream_nodes, network_path); // Shift up until there is either a collision or the disconnected node position is reached let mut current_shift_distance = 0; @@ -3529,7 +3835,7 @@ impl NodeNetworkInterface { current_shift_distance += 1; } - let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes); + self.remove_selection_history_step(network_path); } true @@ -3537,17 +3843,13 @@ impl NodeNetworkInterface { pub fn start_previewing_without_restore(&mut self, network_path: &[NodeId]) { // Some logic will have to be performed to prevent the graph positions from being completely changed when the export changes to some previewed node - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in start_previewing_without_restore"); - return; - }; - network_metadata.persistent_metadata.previewing = Previewing::Yes { root_node_to_restore: None }; + self.set_metadata(MetadataType::Previewing, TaggedValue::Previewing(Previewing::Yes { root_node_to_restore: None }), network_path); } fn stop_previewing(&mut self, network_path: &[NodeId]) { - if let Previewing::Yes { + if let Some(Previewing::Yes { root_node_to_restore: Some(root_node_to_restore), - } = self.previewing(network_path) + }) = self.previewing(network_path) { self.set_input( &InputConnector::Export(0), @@ -3555,11 +3857,7 @@ impl NodeNetworkInterface { network_path, ); } - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in stop_previewing"); - return; - }; - network_metadata.persistent_metadata.previewing = Previewing::No; + self.set_metadata(MetadataType::Previewing, TaggedValue::Previewing(Previewing::No), network_path) } /// Sets the root node only if a node is being previewed @@ -3578,39 +3876,51 @@ impl NodeNetworkInterface { // } // } - pub fn set_display_name(&mut self, node_id: &NodeId, display_name: String, network_path: &[NodeId]) { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { - log::error!("Could not get node {node_id} in set_visibility"); + pub fn set_metadata(&mut self, metadata: MetadataType, tagged_value: TaggedValue, node_path: &[NodeId]) { + let Some(mut value) = self.metadata_value_mut(metadata, node_path) else { + log::error!("Could not set tagged value {tagged_value:?} for metadata {metadata:?} and node {node_path:?} in set_metadata"); return; }; + *value.deref_mut() = tagged_value; + } - if node_metadata.persistent_metadata.display_name == display_name { - return; - } - - node_metadata.persistent_metadata.display_name.clone_from(&display_name); - - // Keep the alias in sync with the `ToArtboard` name input - if node_metadata.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Artboard") { - let Some(nested_network) = self.network_mut(network_path) else { - return; - }; - let Some(artboard_node) = nested_network.nodes.get_mut(node_id) else { - return; - }; - let DocumentNodeImplementation::Network(network) = &mut artboard_node.implementation else { - return; - }; - // Keep this in sync with the definition - let Some(to_artboard) = network.nodes.get_mut(&NodeId(0)) else { - return; - }; + pub fn set_node_graph_to_viewport(&mut self, transform: DAffine2, network_path: &[NodeId]) { + self.set_metadata( + MetadataType::NavigationMetadata(NavigationMetadataType::NodeGraphToViewport), + TaggedValue::DAffine2(transform), + network_path, + ); + self.unload_import_export_ports(network_path); + } - let label_index = 1; - let label = if !display_name.is_empty() { display_name } else { "Artboard".to_string() }; - let label_input = NodeInput::value(TaggedValue::String(label), false); - to_artboard.inputs[label_index] = label_input; - } + pub fn set_display_name(&mut self, node_id: &NodeId, display_name: String, network_path: &[NodeId]) { + let mut node_path = network_path.to_vec(); + node_path.push(*node_id); + + self.set_metadata(MetadataType::DisplayName, TaggedValue::String(display_name), &node_path); + + // TODO: Connect to the display name node. Commenting this out breaks https://github.com/GraphiteEditor/Graphite/issues/1706 + // Keep the display in sync with the `ToArtboard` name input + // if node_metadata.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Artboard") { + // let Some(nested_network) = self.network_mut(network_path) else { + // return; + // }; + // let Some(artboard_node) = nested_network.nodes.get_mut(node_id) else { + // return; + // }; + // let DocumentNodeImplementation::Network(network) = &mut artboard_node.implementation else { + // return; + // }; + // // Keep this in sync with the definition + // let Some(to_artboard) = network.nodes.get_mut(&NodeId(0)) else { + // return; + // }; + + // let label_index = 1; + // let label = if !display_name.is_empty() { display_name } else { "Artboard".to_string() }; + // let label_input = NodeInput::value(TaggedValue::String(label), false); + // to_artboard.inputs[label_index] = label_input; + // } self.transaction_modified(); self.try_unload_layer_width(node_id, network_path); @@ -3632,12 +3942,17 @@ impl NodeNetworkInterface { } pub fn set_locked(&mut self, node_id: &NodeId, network_path: &[NodeId], locked: bool) { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { - log::error!("Could not get node {node_id} in set_visibility"); + let Some(mut locked_value_metadata) = self.metadata_value_mut(MetadataType::Locked, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get locked value in set_locked"); + return; + }; + let TaggedValue::Bool(locked_value) = locked_value_metadata.deref_mut() else { + log::error!("Could not get locked value in set_locked"); return; }; - node_metadata.persistent_metadata.locked = locked; + *locked_value = locked; + drop(locked_value_metadata); self.transaction_modified(); } @@ -3686,34 +4001,32 @@ impl NodeNetworkInterface { }) .is_some_and(|downstream_node_id| self.is_layer(&downstream_node_id, network_path)); - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + let Some(mut node_metadata_value) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { log::error!("Could not get node_metadata for node {node_id}"); return; }; - - node_metadata.persistent_metadata.node_type_metadata = if is_layer { - if downstream_is_layer { - NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { - position: LayerPosition::Stack(0), - owned_nodes: TransientMetadata::Unloaded, - }) - } else { - NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { - position: LayerPosition::Absolute(position), - owned_nodes: TransientMetadata::Unloaded, - }) - } - } else { - NodeTypePersistentMetadata::Node(NodePersistentMetadata { - position: NodePosition::Absolute(position), - }) + let TaggedValue::NodeTypeMetadata(node_metadata_mut) = node_metadata_value.deref_mut() else { + log::error!("Could not get node_metadata for node {node_id}"); + return; }; - - if is_layer { - node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata::default()); - } else { - node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Node; + match node_metadata_mut { + NodeTypePersistentMetadata::Layer(layer_metadata) => { + if downstream_is_layer { + layer_metadata.persistent_metadata = LayerPersistentMetadata { position: LayerPosition::Stack(0) }; + } else { + layer_metadata.persistent_metadata = LayerPersistentMetadata { + position: LayerPosition::Absolute(position), + }; + } + layer_metadata.transient_metadata = LayerTransientMetadata::default(); + } + NodeTypePersistentMetadata::Node(node_metadata) => { + *node_metadata = NodePersistentMetadata { + position: NodePosition::Absolute(position), + }; + } } + drop(node_metadata_value); if is_layer { self.try_set_upstream_to_chain(&InputConnector::node(*node_id, 1), network_path); @@ -3764,7 +4077,7 @@ impl NodeNetworkInterface { // The export is clicked if *node_id == toggle_id { // If the current export is clicked and is being previewed end the preview and set either export back to root node or disconnect - if let Previewing::Yes { root_node_to_restore } = self.previewing(network_path) { + if let Some(Previewing::Yes { root_node_to_restore }) = self.previewing(network_path) { new_export = root_node_to_restore.map(|root_node| root_node.to_connector()); new_previewing_state = Previewing::No; } @@ -3783,7 +4096,7 @@ impl NodeNetworkInterface { new_export = Some(OutputConnector::node(toggle_id, 0)); // There is currently a dashed line being drawn - if let Previewing::Yes { root_node_to_restore } = self.previewing(network_path) { + if let Some(Previewing::Yes { root_node_to_restore }) = self.previewing(network_path) { // There is also a solid line being drawn if let Some(root_node_to_restore) = root_node_to_restore { // If the node with the solid line is clicked, then start previewing that node without restore @@ -3793,7 +4106,7 @@ impl NodeNetworkInterface { } else { // Root node to restore does not change new_previewing_state = Previewing::Yes { - root_node_to_restore: Some(root_node_to_restore), + root_node_to_restore: Some(*root_node_to_restore), }; } } @@ -3829,45 +4142,53 @@ impl NodeNetworkInterface { self.disconnect_input(&InputConnector::Export(0), network_path); } } - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - return; - }; - network_metadata.persistent_metadata.previewing = new_previewing_state; + self.set_metadata(MetadataType::Previewing, TaggedValue::Previewing(new_previewing_state), network_path) } /// Sets the position of a node to an absolute position fn set_absolute_position(&mut self, node_id: &NodeId, position: IVec2, network_path: &[NodeId]) { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + let Some(mut metadata_value_mut) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get node_metadata for node {node_id}"); + return; + }; + let TaggedValue::NodeTypeMetadata(node_type_metadata) = metadata_value_mut.deref_mut() else { log::error!("Could not get node_metadata for node {node_id}"); return; }; - if let NodeTypePersistentMetadata::Node(node_metadata) = &mut node_metadata.persistent_metadata.node_type_metadata { + if let NodeTypePersistentMetadata::Node(node_metadata) = node_type_metadata { if node_metadata.position == NodePosition::Absolute(position) { return; } node_metadata.position = NodePosition::Absolute(position); + drop(metadata_value_mut); self.transaction_modified(); - } else if let NodeTypePersistentMetadata::Layer(layer_metadata) = &mut node_metadata.persistent_metadata.node_type_metadata { - if layer_metadata.position == LayerPosition::Absolute(position) { + } else if let NodeTypePersistentMetadata::Layer(layer_metadata) = node_type_metadata { + if layer_metadata.persistent_metadata.position == LayerPosition::Absolute(position) { return; } - layer_metadata.position = LayerPosition::Absolute(position); + layer_metadata.persistent_metadata.position = LayerPosition::Absolute(position); + drop(metadata_value_mut); self.transaction_modified(); } } /// Sets the position of a layer to a stack position pub fn set_stack_position(&mut self, node_id: &NodeId, y_offset: u32, network_path: &[NodeId]) { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + let Some(mut metadata_value_mut) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { log::error!("Could not get node_metadata for node {node_id}"); return; }; - if let NodeTypePersistentMetadata::Layer(layer_metadata) = &mut node_metadata.persistent_metadata.node_type_metadata { - if layer_metadata.position == LayerPosition::Stack(y_offset) { + let TaggedValue::NodeTypeMetadata(node_type_metadata) = metadata_value_mut.deref_mut() else { + log::error!("Could not get node_metadata for node {node_id}"); + return; + }; + if let NodeTypePersistentMetadata::Layer(layer_metadata) = node_type_metadata { + if layer_metadata.persistent_metadata.position == LayerPosition::Stack(y_offset) { return; } - layer_metadata.position = LayerPosition::Stack(y_offset); + layer_metadata.persistent_metadata.position = LayerPosition::Stack(y_offset); + drop(metadata_value_mut); self.transaction_modified(); } else { log::error!("Could not set stack position for non layer node {node_id}"); @@ -3890,20 +4211,26 @@ impl NodeNetworkInterface { /// Sets the position of a node to a chain position pub fn set_chain_position(&mut self, node_id: &NodeId, network_path: &[NodeId]) { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + let Some(mut metadata_value_mut) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { + log::error!("Could not get node_metadata for node {node_id}"); + return; + }; + let TaggedValue::NodeTypeMetadata(node_type_metadata) = metadata_value_mut.deref_mut() else { log::error!("Could not get node_metadata for node {node_id}"); return; }; // Set any absolute nodes to chain positioning - if let NodeTypePersistentMetadata::Node(NodePersistentMetadata { position }) = &mut node_metadata.persistent_metadata.node_type_metadata { + if let NodeTypePersistentMetadata::Node(NodePersistentMetadata { position }) = node_type_metadata { if *position == NodePosition::Chain { return; } *position = NodePosition::Chain; + drop(metadata_value_mut); self.transaction_modified(); } // If there is an upstream layer then stop breaking the chain else { + drop(metadata_value_mut); log::error!("Could not set chain position for layer node {node_id}"); } self.unload_upstream_node_click_targets(vec![*node_id], network_path); @@ -4018,13 +4345,17 @@ impl NodeNetworkInterface { nodes_to_shift.insert(*layer); for node_id in nodes_to_shift { - let Some(node_to_shift_metadata) = self.node_metadata_mut(&node_id, network_path) else { - log::error!("Could not get node metadata for node {node_id} in set_layer_position"); - continue; + let Some(mut metadata_value_mut) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[node_id]].concat()) else { + log::error!("Could not get node_metadata for node {node_id}"); + return; }; - match &mut node_to_shift_metadata.persistent_metadata.node_type_metadata { + let TaggedValue::NodeTypeMetadata(node_type_metadata) = metadata_value_mut.deref_mut() else { + log::error!("Could not get node_metadata for node {node_id}"); + return; + }; + match node_type_metadata { NodeTypePersistentMetadata::Layer(layer_metadata) => { - if let LayerPosition::Absolute(layer_position) = &mut layer_metadata.position { + if let LayerPosition::Absolute(layer_position) = &mut layer_metadata.persistent_metadata.position { *layer_position += shift; } } @@ -4070,12 +4401,12 @@ impl NodeNetworkInterface { // If shifting up without a push, cancel the shift if there is a stack node that cannot move up if direction == Direction::Up && shift_without_push { for node_id in &node_ids { - let Some(node_metadata) = self.node_metadata(node_id, network_path) else { - log::error!("Could not get node metadata for node {node_id} in shift_selected_nodes"); + let Some(node_type_metadata) = self.node_type_metadata(node_id, network_path) else { + log::error!("Could not get node_metadata for node {node_id}"); return; }; - if let NodeTypePersistentMetadata::Layer(layer_metadata) = &node_metadata.persistent_metadata.node_type_metadata { - if let LayerPosition::Stack(offset) = layer_metadata.position { + if let NodeTypePersistentMetadata::Layer(layer_metadata) = node_type_metadata { + if let LayerPosition::Stack(offset) = layer_metadata.persistent_metadata.position { // If the upstream layer is selected, then skip let Some(outward_wires) = self.outward_wires(network_path).and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0))) else { log::error!("Could not get outward wires in shift_selected_nodes"); @@ -4179,16 +4510,22 @@ impl NodeNetworkInterface { shifted_nodes.insert(*node_id); self.shift_node(node_id, IVec2::new(0, shift_sign), network_path); - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in export_ports"); - continue; + let Some(mut value) = self.metadata_value_mut(MetadataType::StackDependents, network_path) else { + log::error!("Could not get stack dependents in shift_selected_nodes"); + return; }; - if let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents { + let mut transaction_modified = false; + if let TaggedValue::StackDependents(TransientMetadata::Loaded(stack_dependents)) = value.deref_mut() { if let Some(LayerOwner::None(offset)) = stack_dependents.get_mut(node_id) { *offset += shift_sign; - self.transaction_modified(); + transaction_modified = true; }; }; + drop(value); + + if transaction_modified { + self.transaction_modified(); + } // Shift the upstream layer so that it stays in the same place if self.is_layer(node_id, network_path) { @@ -4284,11 +4621,11 @@ impl NodeNetworkInterface { self.shift_node(node_id, IVec2::new(0, shift_sign), network_path); - let Some(network_metadata) = self.network_metadata_mut(network_path) else { + let Some(mut stack_dependents_value) = self.metadata_value_mut(MetadataType::StackDependents, network_path) else { log::error!("Could not get nested network_metadata in export_ports"); return; }; - let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents else { + let TaggedValue::StackDependents(TransientMetadata::Loaded(stack_dependents)) = stack_dependents_value.deref_mut() else { log::error!("Stack dependents should be loaded in vertical_shift_with_push"); return; }; @@ -4299,16 +4636,20 @@ impl NodeNetworkInterface { &mut default_layer_owner }); + let mut transaction_modified = false; match layer_owner { LayerOwner::None(offset) => { *offset += shift_sign; - self.transaction_modified(); + transaction_modified = true; } LayerOwner::Layer(_) => { log::error!("Node being shifted with a push should not be owned"); } } - + drop(stack_dependents_value); + if transaction_modified { + self.transaction_modified(); + } // Shift the upstream layer so that it stays in the same place if self.is_layer(node_id, network_path) { let upstream_layer = { @@ -4405,15 +4746,20 @@ impl NodeNetworkInterface { /// Shifts a node by a certain offset without the auto layout system. If the node is a layer in a stack, the y_offset is shifted. If the node is a node in a chain, its position gets set to absolute. // TODO: Check for unnecessary unloading of click targets pub fn shift_node(&mut self, node_id: &NodeId, shift: IVec2, network_path: &[NodeId]) { - let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + let Some(mut metadata_value_mut) = self.metadata_value_mut(MetadataType::NodeTypeMetadata, &[network_path, &[*node_id]].concat()) else { log::error!("Could not get node_metadata for node {node_id}"); return; }; - if let NodeTypePersistentMetadata::Layer(layer_metadata) = &mut node_metadata.persistent_metadata.node_type_metadata { - if let LayerPosition::Absolute(layer_position) = &mut layer_metadata.position { + let TaggedValue::NodeTypeMetadata(node_type_metadata) = metadata_value_mut.deref_mut() else { + log::error!("Could not get node_metadata for node {node_id}"); + return; + }; + if let NodeTypePersistentMetadata::Layer(layer_metadata) = node_type_metadata { + if let LayerPosition::Absolute(layer_position) = &mut layer_metadata.persistent_metadata.position { *layer_position += shift; + drop(metadata_value_mut); self.transaction_modified(); - } else if let LayerPosition::Stack(y_offset) = &mut layer_metadata.position { + } else if let LayerPosition::Stack(y_offset) = &mut layer_metadata.persistent_metadata.position { let shifted_y_offset = *y_offset as i32 + shift.y; // A layer can only be shifted to a positive y_offset if shifted_y_offset < 0 { @@ -4430,13 +4776,17 @@ impl NodeNetworkInterface { return; } *y_offset = new_y_offset; + drop(metadata_value_mut); self.transaction_modified(); + } else { + drop(metadata_value_mut); } // Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted self.unload_upstream_node_click_targets(vec![*node_id], network_path); - } else if let NodeTypePersistentMetadata::Node(node_metadata) = &mut node_metadata.persistent_metadata.node_type_metadata { + } else if let NodeTypePersistentMetadata::Node(node_metadata) = node_type_metadata { if let NodePosition::Absolute(node_metadata) = &mut node_metadata.position { *node_metadata += shift; + drop(metadata_value_mut); self.transaction_modified(); // Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted self.unload_upstream_node_click_targets(vec![*node_id], network_path); @@ -4451,10 +4801,16 @@ impl NodeNetworkInterface { } } } else if let NodePosition::Chain = node_metadata.position { + drop(metadata_value_mut); self.set_upstream_chain_to_absolute(node_id, network_path); self.shift_node(node_id, shift, network_path); + } else { + drop(metadata_value_mut); } + } else { + drop(metadata_value_mut); } + // Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted self.unload_upstream_node_click_targets(vec![*node_id], network_path); self.unload_all_nodes_bounding_box(network_path); @@ -4472,10 +4828,11 @@ impl NodeNetworkInterface { // A layer is considered to be the height of that layer plus the height to the upstream layer sibling // If a non artboard layer is attempted to be connected to the exports, and there is already an artboard connected, then connect the layer to the artboard. if let Some(first_layer) = LayerNodeIdentifier::ROOT_PARENT.children(&self.document_metadata).next() { - if parent == LayerNodeIdentifier::ROOT_PARENT - && !self.reference(&layer.to_node(), network_path).is_some_and(|reference| reference == "Artboard") - && self.is_artboard(&first_layer.to_node(), network_path) - { + let Some(layer_reference) = self.reference(&layer.to_node(), network_path) else { + log::error!("Could not get layer reference for layer {layer:?} in move_layer_to_stack"); + return; + }; + if parent == LayerNodeIdentifier::ROOT_PARENT && !layer_reference.as_ref().is_some_and(|reference| reference == "Artboard") && self.is_artboard(&first_layer.to_node(), network_path) { parent = first_layer; insert_index = 0; } @@ -4573,11 +4930,7 @@ impl NodeNetworkInterface { // If there is an upstream node in the new location for the layer, create space for the moved layer by shifting the upstream node down if let Some(upstream_node_id) = post_node_input.as_node() { // Select the layer to move to ensure the shifting works correctly - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in move_layer_to_stack"); - return; - }; - let old_selected_nodes = selected_nodes.replace_with(vec![upstream_node_id]); + self.set_selected_nodes(vec![upstream_node_id], network_path); // Create the minimum amount space for the moved layer for _ in 0..3 { @@ -4596,7 +4949,7 @@ impl NodeNetworkInterface { self.vertical_shift_with_push(&upstream_node_id, 1, &mut HashSet::new(), network_path); } - let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes); + self.remove_selection_history_step(network_path); } // If inserting into a stack with a parent, ensure the parent stack has enough space for the child stack @@ -4635,17 +4988,13 @@ impl NodeNetworkInterface { let upstream_nodes = self .upstream_flow_back_from_nodes(vec![upstream_sibling.to_node()], network_path, FlowType::UpstreamFlow) .collect::>(); - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in move_layer_to_stack"); - return; - }; - let old_selected_nodes = selected_nodes.replace_with(upstream_nodes); + + self.set_selected_nodes(upstream_nodes, network_path); for _ in 0..(target_gap - current_gap).max(0) { self.shift_selected_nodes(Direction::Down, true, network_path); } - - let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes); + self.remove_selection_history_step(network_path); } } @@ -4756,18 +5105,22 @@ pub enum FlowType { struct FlowIter<'a> { stack: Vec, - network: &'a NodeNetwork, - network_metadata: &'a NodeNetworkMetadata, + network_interface: &'a NodeNetworkInterface, + network_path: &'a [NodeId], flow_type: FlowType, } impl<'a> Iterator for FlowIter<'a> { type Item = NodeId; fn next(&mut self) -> Option { loop { + let Some(network) = self.network_interface.network(self.network_path) else { + log::error!("Could not get network in FlowIter"); + return None; + }; let node_id = self.stack.pop()?; - if let (Some(document_node), Some(node_metadata)) = (self.network.nodes.get(&node_id), self.network_metadata.persistent_metadata.node_metadata.get(&node_id)) { - let skip = if self.flow_type == FlowType::HorizontalFlow && node_metadata.persistent_metadata.is_layer() { + if let Some(document_node) = network.nodes.get(&node_id) { + let skip = if self.flow_type == FlowType::HorizontalFlow && self.network_interface.is_layer(&node_id, self.network_path) { 1 } else { 0 @@ -4785,195 +5138,6 @@ impl<'a> Iterator for FlowIter<'a> { } } -/// Represents an input connector with index based on the [`DocumentNode::inputs`] index, not the visible input index -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum InputConnector { - #[serde(rename = "node")] - Node { - #[serde(rename = "nodeId")] - node_id: NodeId, - #[serde(rename = "inputIndex")] - input_index: usize, - }, - #[serde(rename = "export")] - Export(usize), -} - -impl Default for InputConnector { - fn default() -> Self { - InputConnector::Export(0) - } -} - -impl InputConnector { - pub fn node(node_id: NodeId, input_index: usize) -> Self { - InputConnector::Node { node_id, input_index } - } - - pub fn input_index(&self) -> usize { - match self { - InputConnector::Node { input_index, .. } => *input_index, - InputConnector::Export(input_index) => *input_index, - } - } - - pub fn node_id(&self) -> Option { - match self { - InputConnector::Node { node_id, .. } => Some(*node_id), - _ => None, - } - } -} - -/// Represents an output connector -#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum OutputConnector { - #[serde(rename = "node")] - Node { - #[serde(rename = "nodeId")] - node_id: NodeId, - #[serde(rename = "outputIndex")] - output_index: usize, - }, - #[serde(rename = "import")] - Import(usize), -} - -impl Default for OutputConnector { - fn default() -> Self { - OutputConnector::Import(0) - } -} - -impl OutputConnector { - pub fn node(node_id: NodeId, output_index: usize) -> Self { - OutputConnector::Node { node_id, output_index } - } - - pub fn index(&self) -> usize { - match self { - OutputConnector::Node { output_index, .. } => *output_index, - OutputConnector::Import(output_index) => *output_index, - } - } - - pub fn node_id(&self) -> Option { - match self { - OutputConnector::Node { node_id, .. } => Some(*node_id), - _ => None, - } - } -} - -#[derive(Debug, Clone)] -pub struct Ports { - input_ports: Vec<(usize, ClickTarget)>, - output_ports: Vec<(usize, ClickTarget)>, -} - -impl Default for Ports { - fn default() -> Self { - Self::new() - } -} - -impl Ports { - pub fn new() -> Ports { - Ports { - input_ports: Vec::new(), - output_ports: Vec::new(), - } - } - - pub fn click_targets(&self) -> impl Iterator { - self.input_ports - .iter() - .map(|(_, click_target)| click_target) - .chain(self.output_ports.iter().map(|(_, click_target)| click_target)) - } - - pub fn insert_input_port_at_center(&mut self, input_index: usize, center: DVec2) { - let subpath = Subpath::new_ellipse(center - DVec2::new(8., 8.), center + DVec2::new(8., 8.)); - self.input_ports.push((input_index, ClickTarget::new(subpath, 0.))); - } - - pub fn insert_output_port_at_center(&mut self, output_index: usize, center: DVec2) { - let subpath = Subpath::new_ellipse(center - DVec2::new(8., 8.), center + DVec2::new(8., 8.)); - self.output_ports.push((output_index, ClickTarget::new(subpath, 0.))); - } - - fn insert_node_input(&mut self, input_index: usize, row_index: usize, node_top_left: DVec2) { - // The center of the click target is always 24 px down from the top left corner of the node - let center = node_top_left + DVec2::new(0., 24. + 24. * row_index as f64); - self.insert_input_port_at_center(input_index, center); - } - - fn insert_node_output(&mut self, output_index: usize, row_index: usize, node_top_left: DVec2) { - // The center of the click target is always 24 px down from the top left corner of the node - let center = node_top_left + DVec2::new(5. * 24., 24. + 24. * row_index as f64); - self.insert_output_port_at_center(output_index, center); - } - - fn insert_layer_input(&mut self, input_index: usize, node_top_left: DVec2) { - let center = if input_index == 0 { - node_top_left + DVec2::new(2. * 24., 24. * 2. + 8.) - } else { - node_top_left + DVec2::new(0., 24. * 1.) - }; - self.insert_input_port_at_center(input_index, center); - } - - fn insert_layer_output(&mut self, node_top_left: DVec2) { - // The center of the click target is always 24 px down from the top left corner of the node - let center = node_top_left + DVec2::new(2. * 24., -8.0); - self.insert_output_port_at_center(0, center); - } - - pub fn clicked_input_port_from_point(&self, point: DVec2) -> Option { - self.input_ports.iter().find_map(|(port, click_target)| click_target.intersect_point_no_stroke(point).then_some(*port)) - } - - pub fn clicked_output_port_from_point(&self, point: DVec2) -> Option { - self.output_ports.iter().find_map(|(port, click_target)| click_target.intersect_point_no_stroke(point).then_some(*port)) - } - - pub fn input_port_position(&self, index: usize) -> Option { - self.input_ports - .get(index) - .and_then(|(_, click_target)| click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.))) - } - - pub fn output_port_position(&self, index: usize) -> Option { - self.output_ports - .get(index) - .and_then(|(_, click_target)| click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.))) - } -} - -#[derive(PartialEq, Debug, Clone, Copy, Hash, Default, serde::Serialize, serde::Deserialize)] -pub struct RootNode { - pub node_id: NodeId, - pub output_index: usize, -} - -impl RootNode { - pub fn to_connector(&self) -> OutputConnector { - OutputConnector::Node { - node_id: self.node_id, - output_index: self.output_index, - } - } -} - -#[derive(PartialEq, Debug, Clone, Copy, Hash, Default, serde::Serialize, serde::Deserialize)] -pub enum Previewing { - /// If there is a node to restore the connection to the export for, then it is stored in the option. - /// Otherwise, nothing gets restored and the primary export is disconnected. - Yes { root_node_to_restore: Option }, - #[default] - No, -} - /// All fields in NetworkMetadata should automatically be updated by using the network interface API. If a field is none then it should be calculated based on the network state. #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] pub struct NodeNetworkMetadata { @@ -4998,6 +5162,7 @@ impl PartialEq for NodeNetworkMetadata { } impl NodeNetworkMetadata { + // TODO: Remove pub fn nested_metadata(&self, nested_path: &[NodeId]) -> Option<&Self> { let mut network_metadata = Some(self); @@ -5010,6 +5175,7 @@ impl NodeNetworkMetadata { } /// Get the mutable nested network given by the path of node ids + // TODO: Remove pub fn nested_metadata_mut(&mut self, nested_path: &[NodeId]) -> Option<&mut Self> { let mut network_metadata = Some(self); @@ -5034,35 +5200,15 @@ pub struct NodeNetworkPersistentMetadata { pub navigation_metadata: NavigationMetadata, /// Stack of selection snapshots for previous history states. #[serde(default)] - pub selection_undo_history: VecDeque, + pub selection_undo_history: VecDeque>, /// Stack of selection snapshots for future history states. #[serde(default)] - pub selection_redo_history: VecDeque, -} - -/// This is the same as Option, but more clear in the context of having cached metadata either being loaded or unloaded -#[derive(Debug, Default, Clone)] -pub enum TransientMetadata { - Loaded(T), - #[default] - Unloaded, -} - -impl TransientMetadata { - /// Set the current transient metadata to unloaded - pub fn unload(&mut self) { - *self = TransientMetadata::Unloaded; - } - - pub fn is_loaded(&self) -> bool { - matches!(self, TransientMetadata::Loaded(_)) - } + pub selection_redo_history: VecDeque>, } /// If some network calculation is too slow to compute for every usage, cache the data here #[derive(Debug, Default, Clone)] pub struct NodeNetworkTransientMetadata { - pub selected_nodes: SelectedNodes, /// Sole dependents of the top of the stacks of all selected nodes. Used to determine which nodes are checked for collision when shifting. /// The LayerOwner is used to determine whether the collided node should be shifted, or the layer that owns it. pub stack_dependents: TransientMetadata>, @@ -5076,14 +5222,8 @@ pub struct NodeNetworkTransientMetadata { // pub wire_paths: Vec /// All export connector click targets pub import_export_ports: TransientMetadata, -} - -#[derive(Debug, Clone)] -pub enum LayerOwner { - // Used to get the layer that should be shifted when there is a collision. - Layer(NodeId), - // The vertical offset of a node from the start of its shift. Should be reset when the drag ends. - None(i32), + // 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, } /// Utility function for providing a default boolean value to serde. @@ -5131,6 +5271,7 @@ pub struct DocumentNodePersistentMetadata { #[serde(default = "return_true")] pub has_primary_output: bool, /// Represents the lock icon for locking/unlocking the node in the graph UI. When locked, a node cannot be moved in the graph UI. + /// Only nodes in the document network can be locked/unlocked #[serde(default)] pub locked: bool, /// Metadata that is specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer. @@ -5155,138 +5296,11 @@ impl Default for DocumentNodePersistentMetadata { } } -impl DocumentNodePersistentMetadata { - pub fn is_layer(&self) -> bool { - matches!(self.node_type_metadata, NodeTypePersistentMetadata::Layer(_)) - } -} - -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum NodeTypePersistentMetadata { - Layer(LayerPersistentMetadata), - Node(NodePersistentMetadata), -} - -impl Default for NodeTypePersistentMetadata { - fn default() -> Self { - NodeTypePersistentMetadata::node(IVec2::ZERO) - } -} - -impl NodeTypePersistentMetadata { - pub fn node(position: IVec2) -> NodeTypePersistentMetadata { - NodeTypePersistentMetadata::Node(NodePersistentMetadata { - position: NodePosition::Absolute(position), - }) - } - pub fn layer(position: IVec2) -> NodeTypePersistentMetadata { - NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { - position: LayerPosition::Absolute(position), - owned_nodes: TransientMetadata::default(), - }) - } -} - -/// All fields in LayerMetadata should automatically be updated by using the network interface API -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct LayerPersistentMetadata { - // TODO: Store click target for the preview button, which will appear when the node is a selected/(hovered?) layer node - // preview_click_target: Option, - /// Stores the position of a layer node, which can either be Absolute or Stack - pub position: LayerPosition, - /// All nodes that should be moved when the layer is moved. - #[serde(skip)] - pub owned_nodes: TransientMetadata>, -} - -impl PartialEq for LayerPersistentMetadata { - fn eq(&self, other: &Self) -> bool { - self.position == other.position - } -} - -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct NodePersistentMetadata { - /// Stores the position of a non layer node, which can either be Absolute or Chain - position: NodePosition, -} - -/// A layer can either be position as Absolute or in a Stack -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum LayerPosition { - // Position of the node in grid spaces - Absolute(IVec2), - // A layer is in a Stack when it feeds into the bottom input of a layer. The Y position stores the vertical distance between the layer and its upstream sibling/parent. - Stack(u32), -} - -/// A node can either be position as Absolute or in a Chain -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -pub enum NodePosition { - // Position of the node in grid spaces - Absolute(IVec2), - // In a chain the position is based on the number of nodes to the first layer node - Chain, -} - /// Cached metadata that should be calculated when creating a node, and should be recalculated when modifying a node property that affects one of the cached fields. #[derive(Debug, Default, Clone)] pub struct DocumentNodeTransientMetadata { // The click targets are stored as a single struct since it is very rare for only one to be updated, and recomputing all click targets in one function is more efficient than storing them separately. pub click_targets: TransientMetadata, - // Metadata that is specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer. - pub node_type_metadata: NodeTypeTransientMetadata, -} - -#[derive(Debug, Clone)] -pub struct DocumentNodeClickTargets { - /// In order to keep the displayed position of the node in sync with the click target, the displayed position of a node is derived from the top left of the click target - /// Ensure node_click_target is kept in sync when modifying a node property that changes its size. Currently this is alias, inputs, is_layer, and metadata - pub node_click_target: ClickTarget, - /// Stores all port click targets in node graph space. - pub port_click_targets: Ports, - // Click targets that are specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer. - pub node_type_metadata: NodeTypeClickTargets, -} - -#[derive(Debug, Default, Clone)] -pub enum NodeTypeTransientMetadata { - Layer(LayerTransientMetadata), - #[default] - Node, // No transient data is stored exclusively for nodes -} - -#[derive(Debug, Default, Clone)] -pub struct LayerTransientMetadata { - // Stores the width in grid cell units for layer nodes from the left edge of the thumbnail (+12px padding since thumbnail ends between grid spaces) to the left end of the node - /// This is necessary since calculating the layer width through web_sys is very slow - pub layer_width: TransientMetadata, - // Should not be a performance concern to calculate when needed with chain_width. - // Stores the width in grid cell units for layer nodes from the left edge of the thumbnail to the end of the chain - // chain_width: u32, -} - -#[derive(Debug, Clone)] -pub enum NodeTypeClickTargets { - Layer(LayerClickTargets), - Node, // No transient click targets are stored exclusively for nodes -} - -/// All fields in TransientLayerMetadata should automatically be updated by using the network interface API -#[derive(Debug, Clone)] -pub struct LayerClickTargets { - /// Cache for all visibility buttons. Should be automatically updated when update_click_target is called - pub visibility_click_target: ClickTarget, - /// Cache for the grip icon, which is next to the visibility button. - pub grip_click_target: ClickTarget, - // TODO: Store click target for the preview button, which will appear when the node is a selected/(hovered?) layer node - // preview_click_target: ClickTarget, -} - -pub enum LayerClickTargetTypes { - Visibility, - Grip, - // Preview, } #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] @@ -5295,14 +5309,12 @@ pub struct NavigationMetadata { /// 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 + // It might be possible to calculate this with the footprint when the graph is getting rendered /// Transform from node graph space to viewport space. pub node_graph_to_viewport: DAffine2, - /// The viewport pixel distance distance between the left edge of the node graph and the exports. Rounded to nearest grid space when the panning ends. - #[serde(skip)] - pub exports_to_edge_distance: DVec2, - /// The viewport pixel distance between the left edge of the node graph and the imports. Rounded to nearest grid space when the panning ends. - #[serde(skip)] - pub imports_to_edge_distance: DVec2, + // TODO: Replace with footprint + /// Top right of the node graph in viewport space + pub node_graph_top_right: DVec2, } impl Default for NavigationMetadata { @@ -5311,12 +5323,23 @@ impl Default for NavigationMetadata { NavigationMetadata { node_graph_ptz: PTZ::default(), node_graph_to_viewport: DAffine2::IDENTITY, - exports_to_edge_distance: DVec2::ZERO, - imports_to_edge_distance: DVec2::ZERO, + // TODO: Eventually replace with footprint + node_graph_top_right: DVec2::ZERO, } } } +enum SelectionDirection { + Back, + Forward, +} + +enum SelectionOperation { + Add, + Remove, + Set, +} + // PartialEq required by message handlers /// All persistent editor and Graphene data for a node. Used to serialize and deserialize a node, pass it through the editor, and create definitions. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] diff --git a/editor/src/messages/portfolio/document/utility_types/nodes.rs b/editor/src/messages/portfolio/document/utility_types/nodes.rs index de101fb56b..523418556e 100644 --- a/editor/src/messages/portfolio/document/utility_types/nodes.rs +++ b/editor/src/messages/portfolio/document/utility_types/nodes.rs @@ -58,10 +58,47 @@ pub struct LayerPanelEntry { } #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)] -pub struct SelectedNodes(pub Vec); +pub struct OldSelectedNodes(pub Vec); -impl SelectedNodes { - pub fn layer_visible(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { +pub trait SelectedNodes { + fn layer_visible(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool; + + fn selected_visible_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn layer_locked(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool; + + fn selected_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn selected_visible_and_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_; + + fn selected_layers_except_artboards<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn selected_layers_contains(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool; + + fn selected_nodes(&self) -> impl Iterator + '_; + + fn selected_nodes_ref(&self) -> &Vec; + + fn network_has_selected_nodes(&self, network: &NodeNetwork) -> bool; + + fn has_selected_nodes(&self) -> bool; + + fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool); + + fn set_selected_nodes(&mut self, new: Vec); + + fn add_selected_nodes(&mut self, new: Vec); + + fn clear_selected_nodes(&mut self); + + fn replace_with(&mut self, new: Vec) -> Vec; + + fn filtered_selected_nodes(&self, node_ids: std::collections::HashSet) -> Vec; +} +impl SelectedNodes for Vec { + fn layer_visible(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { layer.ancestors(network_interface.document_metadata()).all(|layer| { if layer != LayerNodeIdentifier::ROOT_PARENT { network_interface.is_visible(&layer.to_node(), &[]) @@ -71,12 +108,12 @@ impl SelectedNodes { }) } - pub fn selected_visible_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_visible_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| self.layer_visible(layer, network_interface)) } - pub fn layer_locked(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { + fn layer_locked(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { layer.ancestors(network_interface.document_metadata()).any(|layer| { if layer != LayerNodeIdentifier::ROOT_PARENT { network_interface.is_locked(&layer.to_node(), &[]) @@ -86,67 +123,67 @@ impl SelectedNodes { }) } - pub fn selected_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| !self.layer_locked(layer, network_interface)) } - pub fn selected_visible_and_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_visible_and_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| self.layer_visible(layer, network_interface) && !self.layer_locked(layer, network_interface)) } - pub fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_ { - metadata.all_layers().filter(|layer| self.0.contains(&layer.to_node())) + fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_ { + metadata.all_layers().filter(|layer| self.contains(&layer.to_node())) } - pub fn selected_layers_except_artboards<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_layers_except_artboards<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| !network_interface.is_artboard(&layer.to_node(), &[])) } - pub fn selected_layers_contains(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool { + fn selected_layers_contains(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool { self.selected_layers(metadata).any(|selected| selected == layer) } - pub fn selected_nodes(&self) -> impl Iterator + '_ { - self.0.iter() + fn selected_nodes(&self) -> impl Iterator + '_ { + self.iter() } - pub fn selected_nodes_ref(&self) -> &Vec { - &self.0 + fn selected_nodes_ref(&self) -> &Vec { + &self } - pub fn network_has_selected_nodes(&self, network: &NodeNetwork) -> bool { - self.0.iter().any(|node_id| network.nodes.contains_key(node_id)) + fn network_has_selected_nodes(&self, network: &NodeNetwork) -> bool { + self.iter().any(|node_id| network.nodes.contains_key(node_id)) } - pub fn has_selected_nodes(&self) -> bool { - !self.0.is_empty() + fn has_selected_nodes(&self) -> bool { + !self.is_empty() } - pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) { - self.0.retain(f); + fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) { + self.retain(f); } - pub fn set_selected_nodes(&mut self, new: Vec) { - self.0 = new; + fn set_selected_nodes(&mut self, new: Vec) { + *self = new; } - pub fn add_selected_nodes(&mut self, new: Vec) { - self.0.extend(new); + fn add_selected_nodes(&mut self, new: Vec) { + self.extend(new); } - pub fn clear_selected_nodes(&mut self) { - self.0 = Vec::new(); + fn clear_selected_nodes(&mut self) { + *self = Vec::new(); } - pub fn replace_with(&mut self, new: Vec) -> Vec { - std::mem::replace(&mut self.0, new) + fn replace_with(&mut self, new: Vec) -> Vec { + std::mem::replace(self, new) } - pub fn filtered_selected_nodes(&self, node_ids: std::collections::HashSet) -> SelectedNodes { - SelectedNodes(self.0.iter().filter(|node_id| node_ids.contains(node_id)).cloned().collect()) + fn filtered_selected_nodes(&self, node_ids: std::collections::HashSet) -> Vec { + self.iter().filter(|node_id| node_ids.contains(node_id)).cloned().collect() } } diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index ad8327c46b..918d75ca88 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1,5 +1,5 @@ use super::document::utility_types::document_metadata::LayerNodeIdentifier; -use super::document::utility_types::network_interface::{self, InputConnector, OutputConnector}; +use super::document::utility_types::network_interface; use super::utility_types::{PanelType, PersistentData}; use crate::application::generate_uuid; use crate::consts::DEFAULT_DOCUMENT_NAME; @@ -7,13 +7,14 @@ use crate::messages::dialog::simple_dialogs; use crate::messages::frontend::utility_types::FrontendDocumentDetails; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::DocumentMessageData; use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup}; use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; use graph_craft::document::value::TaggedValue; -use graph_craft::document::{NodeId, NodeInput}; +use graph_craft::document::{InputConnector, NodeId, NodeInput, OutputConnector}; use graphene_core::text::Font; use graphene_std::vector::style::{Fill, FillType, Gradient}; use interpreted_executor::dynamic_executor::IntrospectError; @@ -404,25 +405,12 @@ impl MessageHandler> for PortfolioMes if upgrade_from_before_editable_subgraphs { // This can be used, if uncommented, to upgrade demo artwork with outdated document node internals from their definitions. Delete when it's no longer needed. // Used for upgrading old internal networks for demo artwork nodes. Will reset all node internals for any opened file - for node_id in &document - .network_interface - .network_metadata(&[]) - .unwrap() - .persistent_metadata - .node_metadata - .keys() - .cloned() - .collect::>() - { - if let Some(reference) = document - .network_interface - .network_metadata(&[]) - .unwrap() - .persistent_metadata - .node_metadata - .get(node_id) - .and_then(|node| node.persistent_metadata.reference.as_ref()) - { + for node_id in &document.network_interface.network(&[]).unwrap().nodes.keys().cloned().collect::>() { + let Some(reference) = document.network_interface.reference(&node_id, &[]) else { + log::error!("could not get reference in deserialize_document"); + continue; + }; + if let Some(reference) = reference { let node_definition = crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type(reference).unwrap(); let default_definition_node = node_definition.default_node_template(); document.network_interface.set_implementation(node_id, &[], default_definition_node.document_node.implementation); @@ -432,14 +420,13 @@ impl MessageHandler> for PortfolioMes if document .network_interface - .network_metadata(&[]) + .network(&[]) .unwrap() - .persistent_metadata - .node_metadata - .iter() - .any(|(node_id, node)| node.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Output") && *node_id == NodeId(0)) + .nodes + .keys() + .any(|node_id| *node_id == NodeId(0) && document.network_interface.reference(node_id, &[]).cloned().flatten().is_some_and(|reference| reference == "Output")) { - document.network_interface.delete_nodes(vec![NodeId(0)], true, &[]); + document.network_interface.delete_nodes(vec![NodeId(0)], false, &[]); } let node_ids = document.network_interface.network(&[]).unwrap().nodes.keys().cloned().collect::>(); @@ -448,14 +435,9 @@ impl MessageHandler> for PortfolioMes log::error!("could not get node in deserialize_document"); continue; }; - let Some(node_metadata) = document.network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata.get(node_id) else { - log::error!("could not get node metadata for node {node_id} in deserialize_document"); - continue; - }; - // Upgrade Fill nodes to the format change in #1778 // TODO: Eventually remove this (probably starting late 2024) - let Some(ref reference) = node_metadata.persistent_metadata.reference.clone() else { + let Some(ref reference) = document.network_interface.reference(node_id, &[]).cloned().flatten() else { continue; }; if reference == "Fill" && node.inputs.len() == 8 { @@ -517,7 +499,10 @@ impl MessageHandler> for PortfolioMes // Upgrade artboard name being passed as hidden value input to "To Artboard" if reference == "Artboard" { - let label = document.network_interface.display_name(node_id, &[]); + let label = document.network_interface.display_name(node_id, &[]).cloned().unwrap_or_else(|| { + log::error!("Could not get display name for node {node_id} when opening file"); + "".to_string() + }); document .network_interface .set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]); 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 9121f31677..cb1ba2df69 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -174,8 +174,14 @@ impl<'a> NodeGraphLayer<'a> { /// Node id of a node if it exists in the layer's primary flow pub fn upstream_node_id_from_name(&self, node_name: &str) -> Option { + self.horizontal_layer_flow() - .find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| reference == node_name)) + .find(|node_id| { + let Some(reference) = self.network_interface.reference(node_id, &[]) else { + log::error!("Reference could not be found for node {node_id} in upstream_node_id_from_name"); + return false }; + reference.as_ref().is_some_and(|reference| reference == node_name) + }) } /// Find all of the inputs of a specific node within the layer's primary flow, up until the next layer is reached. @@ -183,7 +189,13 @@ impl<'a> NodeGraphLayer<'a> { self.horizontal_layer_flow() .skip(1)// Skip self .take_while(|node_id| !self.network_interface.is_layer(node_id,&[])) - .find(|node_id| self.network_interface.reference(node_id,&[]).is_some_and(|reference| reference == node_name)) + .find(|node_id| + { + let Some(reference) = self.network_interface.reference(node_id, &[]) else { + log::error!("Reference could not be found for node {node_id} in upstream_node_id_from_name"); + return false }; + reference.as_ref().is_some_and(|reference| reference == node_name) + }) .and_then(|node_id| self.network_interface.network(&[]).unwrap().nodes.get(&node_id).map(|node| &node.inputs)) } diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index c56f12127e..d408788a24 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -6,6 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::prelude::*; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use glam::{DAffine2, DVec2}; use std::collections::VecDeque; diff --git a/editor/src/messages/tool/common_functionality/utility_functions.rs b/editor/src/messages/tool/common_functionality/utility_functions.rs index 21aed42521..1aad2ce938 100644 --- a/editor/src/messages/tool/common_functionality/utility_functions.rs +++ b/editor/src/messages/tool/common_functionality/utility_functions.rs @@ -1,4 +1,5 @@ use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::prelude::*; use graphene_std::vector::PointId; diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 263a1bd416..25139f65a8 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -8,6 +8,7 @@ use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint; use crate::messages::tool::common_functionality::snapping::SnapData; use crate::messages::tool::common_functionality::snapping::SnapManager; use crate::messages::tool::common_functionality::transformation_cage::*; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graph_craft::document::NodeId; use graphene_core::renderer::Quad; diff --git a/editor/src/messages/tool/tool_messages/brush_tool.rs b/editor/src/messages/tool/tool_messages/brush_tool.rs index 5b8d733f46..a81fe71829 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -3,6 +3,7 @@ use crate::messages::portfolio::document::graph_operation::transform_utils::{get use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::FlowType; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use graph_craft::document::value::TaggedValue; @@ -271,6 +272,10 @@ impl BrushToolData { continue; }; let Some(reference) = document.network_interface.reference(&node_id, &[]) else { + log::error!("Could not get reference for node {node_id} in load_existing_strokes"); + continue; + }; + let Some(reference) = reference else { continue; }; if reference == "Brush" && node_id != layer.to_node() { diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index e90f60511e..eed6b10b13 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -5,6 +5,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::graph_modification_utils::get_gradient; use crate::messages::tool::common_functionality::snapping::SnapManager; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::vector::style::{Fill, Gradient, GradientType}; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c424bbfa73..e2b127bdf2 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -7,6 +7,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::shape_editor::{ClosestSegment, ManipulatorAngle, OpposingHandleLengths, SelectedPointsInfo, ShapeState}; use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::renderer::Quad; use graphene_core::vector::ManipulatorPointId; @@ -349,7 +350,6 @@ impl PathToolData { } // We didn't find a segment path, so consider selecting the nearest shape instead else if let Some(layer) = document.click(input) { - responses.add(DocumentMessage::StartTransaction); if add_to_selection { responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![layer.to_node()] }); } else { @@ -359,6 +359,7 @@ impl PathToolData { self.previous_mouse_position = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position); shape_editor.select_connected_anchors(document, layer, input.mouse.position); + responses.add(DocumentMessage::StartTransaction); PathToolFsmState::Dragging } // Start drawing a box diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 2a836421e0..3e9fc4fcb5 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -9,6 +9,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis}; use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeNetworkInterface, NodeTemplate}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::utility_types::transformation::Selected; use crate::messages::tool::common_functionality::graph_modification_utils::is_layer_fed_by_node_of_name; use crate::messages::tool::common_functionality::pivot::Pivot; diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index a68afe7469..0b5158a487 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -5,12 +5,12 @@ use crate::application::generate_uuid; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::portfolio::document::utility_types::network_interface::InputConnector; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_layer_fed_by_node_of_name}; use graph_craft::document::value::TaggedValue; -use graph_craft::document::{NodeId, NodeInput}; +use graph_craft::document::{InputConnector, NodeId, NodeInput}; use graphene_core::renderer::Quad; use graphene_core::text::{load_face, Font, FontCache}; use graphene_core::vector::style::Fill; diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index a7a38c81ef..11d66397b7 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -4,6 +4,7 @@ use crate::messages::portfolio::document::utility_types::transformation::{Axis, use crate::messages::prelude::*; use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::utility_types::{ToolData, ToolType}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::vector::ManipulatorPointId; diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index bcca50ea91..0ed907df61 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -702,13 +702,12 @@ impl EditorHandle { let document = editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap(); for node in document .network_interface - .network_metadata(&[]) + .network(&[]) .unwrap() - .persistent_metadata - .node_metadata - .iter() - .filter(|(_, d)| d.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Artboard")) - .map(|(id, _)| *id) + .nodes + .keys() + .filter(|node_id| document.network_interface.reference(node_id, &[]).cloned().flatten().is_some_and(|reference| reference == "Artboard")) + .cloned() .collect::>() { let Some(document_node) = document.network_interface.network(&[]).unwrap().nodes.get(&node) else { @@ -718,7 +717,12 @@ impl EditorHandle { if let Some(network) = document_node.implementation.get_network() { let mut nodes_to_upgrade = Vec::new(); for (node_id, _) in network.nodes.iter().collect::>() { - if document.network_interface.reference(node_id, &[]).is_some_and(|reference| reference == "To Artboard") + if document + .network_interface + .reference(node_id, &[]) + .cloned() + .flatten() + .is_some_and(|reference| reference == "To Artboard") && document .network_interface .network(&[]) @@ -773,13 +777,12 @@ impl EditorHandle { document.network_interface.load_structure(); for node in document .network_interface - .network_metadata(&[]) + .network(&[]) .unwrap() - .persistent_metadata - .node_metadata - .iter() - .filter(|(_, d)| d.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Merge")) - .map(|(id, _)| *id) + .nodes + .keys() + .filter(|node_id| document.network_interface.reference(node_id, &[]).cloned().flatten().is_some_and(|reference| reference == "Merge")) + .cloned() .collect::>() { let layer = LayerNodeIdentifier::new(node, &document.network_interface, &[]); @@ -845,7 +848,7 @@ impl EditorHandle { .node_template_input_override([None, Some(NodeInput::value(TaggedValue::VectorModification(modification), false))]) .document_node; - let node_metadata = document.network_interface.node_metadata(&node_id, &[]).cloned().unwrap_or_default(); + let node_metadata = document.network_interface.get_all_node_metadata(&node_id, &[]).unwrap_or_default(); document.network_interface.insert_node( node_id, diff --git a/libraries/bezier-rs/src/subpath/solvers.rs b/libraries/bezier-rs/src/subpath/solvers.rs index d9bcd99be0..65389aebb2 100644 --- a/libraries/bezier-rs/src/subpath/solvers.rs +++ b/libraries/bezier-rs/src/subpath/solvers.rs @@ -274,7 +274,7 @@ impl Subpath { } /// Return the min and max corners that represent the bounding box of the subpath, after a given affine transform. - pub fn bounding_box_with_transform(&self, transform: glam::DAffine2) -> Option<[DVec2; 2]> { + pub fn bounding_box_with_transform(&self, transform: &glam::DAffine2) -> Option<[DVec2; 2]> { self.iter() .map(|bezier| bezier.apply_transformation(|v| transform.transform_point2(v)).bounding_box()) .reduce(|bbox1, bbox2| [bbox1[0].min(bbox2[0]), bbox1[1].max(bbox2[1])]) diff --git a/node-graph/gcore/src/consts.rs b/node-graph/gcore/src/consts.rs index 3ca96f7ce4..b30fcddc2b 100644 --- a/node-graph/gcore/src/consts.rs +++ b/node-graph/gcore/src/consts.rs @@ -7,3 +7,7 @@ pub const LAYER_OUTLINE_STROKE_WEIGHT: f64 = 1.; // Fonts pub const DEFAULT_FONT_FAMILY: &str = "Cabin"; pub const DEFAULT_FONT_STYLE: &str = "Normal (400)"; + +// Zoom +pub const VIEWPORT_ZOOM_SCALE_MIN: f64 = 0.000_000_1; +pub const VIEWPORT_ZOOM_SCALE_MAX: f64 = 10_000.; diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index 481be53ade..4f2037f714 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -21,7 +21,7 @@ use std::fmt::Write; use vello::*; /// Represents a clickable target for the layer -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ClickTarget { subpath: bezier_rs::Subpath, stroke_width: f64, @@ -38,11 +38,15 @@ impl ClickTarget { &self.subpath } + pub fn stroke_width(&self) -> f64 { + self.stroke_width + } + pub fn bounding_box(&self) -> Option<[DVec2; 2]> { self.bounding_box } - pub fn bounding_box_with_transform(&self, transform: DAffine2) -> Option<[DVec2; 2]> { + pub fn bounding_box_with_transform(&self, transform: &DAffine2) -> Option<[DVec2; 2]> { self.bounding_box.map(|[a, b]| [transform.transform_point2(a), transform.transform_point2(b)]) } diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 82b790d084..98960ea99a 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -1,16 +1,18 @@ use crate::document::value::TaggedValue; use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput}; +use bezier_rs::Subpath; use dyn_any::{DynAny, StaticType}; use graphene_core::memo::MemoHashGuard; +use graphene_core::renderer::ClickTarget; pub use graphene_core::uuid::generate_uuid; use graphene_core::{Cow, MemoHash, ProtoNodeIdentifier, Type}; -use glam::IVec2; +use glam::{DVec2, IVec2}; use log::Metadata; use rustc_hash::FxHashMap; use std::collections::hash_map::DefaultHasher; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; pub mod value; @@ -73,7 +75,7 @@ pub enum OldNodeInput { } // TODO: Eventually remove this (probably starting late 2024) -use serde::Deserialize; +use serde::{Deserialize, Serialize}; fn deserialize_inputs<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, @@ -1356,6 +1358,422 @@ impl<'a> Iterator for RecursiveNodeIter<'a> { } } +#[derive(PartialEq, Debug, Clone, Copy, Hash, Default, serde::Serialize, serde::Deserialize, DynAny)] +pub enum Previewing { + /// If there is a node to restore the connection to the export for, then it is stored in the option. + /// Otherwise, nothing gets restored and the primary export is disconnected. + Yes { root_node_to_restore: Option }, + #[default] + No, +} + +#[derive(PartialEq, Debug, Clone, Copy, Hash, Default, serde::Serialize, serde::Deserialize, DynAny)] +pub struct RootNode { + pub node_id: NodeId, + pub output_index: usize, +} + +impl RootNode { + pub fn to_connector(&self) -> OutputConnector { + OutputConnector::Node { + node_id: self.node_id, + output_index: self.output_index, + } + } +} + +/// Represents an input connector with index based on the [`DocumentNode::inputs`] index, not the visible input index +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type, Hash, DynAny)] +pub enum InputConnector { + #[serde(rename = "node")] + Node { + #[serde(rename = "nodeId")] + node_id: NodeId, + #[serde(rename = "inputIndex")] + input_index: usize, + }, + #[serde(rename = "export")] + Export(usize), +} + +impl Default for InputConnector { + fn default() -> Self { + InputConnector::Export(0) + } +} + +impl InputConnector { + pub fn node(node_id: NodeId, input_index: usize) -> Self { + InputConnector::Node { node_id, input_index } + } + + pub fn input_index(&self) -> usize { + match self { + InputConnector::Node { input_index, .. } => *input_index, + InputConnector::Export(input_index) => *input_index, + } + } + + pub fn node_id(&self) -> Option { + match self { + InputConnector::Node { node_id, .. } => Some(*node_id), + _ => None, + } + } +} + +/// Represents an output connector +#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type, DynAny)] +pub enum OutputConnector { + #[serde(rename = "node")] + Node { + #[serde(rename = "nodeId")] + node_id: NodeId, + #[serde(rename = "outputIndex")] + output_index: usize, + }, + #[serde(rename = "import")] + Import(usize), +} + +impl Default for OutputConnector { + fn default() -> Self { + OutputConnector::Import(0) + } +} + +impl OutputConnector { + pub fn node(node_id: NodeId, output_index: usize) -> Self { + OutputConnector::Node { node_id, output_index } + } + + pub fn index(&self) -> usize { + match self { + OutputConnector::Node { output_index, .. } => *output_index, + OutputConnector::Import(output_index) => *output_index, + } + } + + pub fn node_id(&self) -> Option { + match self { + OutputConnector::Node { node_id, .. } => Some(*node_id), + _ => None, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, DynAny)] +#[serde(default)] +pub struct PTZ { + pub pan: DVec2, + pub tilt: f64, + zoom: f64, +} + +impl Default for PTZ { + fn default() -> Self { + Self { pan: DVec2::ZERO, tilt: 0., zoom: 1. } + } +} + +impl PTZ { + pub fn zoom(&self) -> f64 { + self.zoom + } + + pub fn set_zoom(&mut self, zoom: f64) { + self.zoom = zoom.clamp(graphene_core::consts::VIEWPORT_ZOOM_SCALE_MIN, graphene_core::consts::VIEWPORT_ZOOM_SCALE_MAX) + } +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Hash, DynAny)] +pub enum LayerOwner { + // Used to get the layer that should be shifted when there is a collision. + Layer(NodeId), + // The vertical offset of a node from the start of its shift. Should be reset when the drag ends. + None(i32), +} + +#[derive(Debug, Clone, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] +pub struct Ports { + #[serde(skip)] + input_ports: Vec<(usize, ClickTarget)>, + #[serde(skip)] + output_ports: Vec<(usize, ClickTarget)>, +} + +impl Default for Ports { + fn default() -> Self { + Self::new() + } +} + +impl Ports { + pub fn new() -> Ports { + Ports { + input_ports: Vec::new(), + output_ports: Vec::new(), + } + } + + pub fn input_ports(&self) -> impl Iterator { + self.input_ports.iter().map(|(index, click_target)| (*index, click_target)) + } + + pub fn output_ports(&self) -> impl Iterator { + self.output_ports.iter().map(|(index, click_target)| (*index, click_target)) + } + + pub fn click_targets(&self) -> impl Iterator { + self.input_ports + .iter() + .map(|(_, click_target)| click_target) + .chain(self.output_ports.iter().map(|(_, click_target)| click_target)) + } + + pub fn insert_input_port_at_center(&mut self, input_index: usize, center: DVec2) { + let subpath = Subpath::new_ellipse(center - DVec2::new(8., 8.), center + DVec2::new(8., 8.)); + self.input_ports.push((input_index, ClickTarget::new(subpath, 0.))); + } + + pub fn insert_output_port_at_center(&mut self, output_index: usize, center: DVec2) { + let subpath = Subpath::new_ellipse(center - DVec2::new(8., 8.), center + DVec2::new(8., 8.)); + self.output_ports.push((output_index, ClickTarget::new(subpath, 0.))); + } + + pub fn insert_node_input(&mut self, input_index: usize, row_index: usize, node_top_left: DVec2) { + // The center of the click target is always 24 px down from the top left corner of the node + let center = node_top_left + DVec2::new(0., 24. + 24. * row_index as f64); + self.insert_input_port_at_center(input_index, center); + } + + pub fn insert_node_output(&mut self, output_index: usize, row_index: usize, node_top_left: DVec2) { + // The center of the click target is always 24 px down from the top left corner of the node + let center = node_top_left + DVec2::new(5. * 24., 24. + 24. * row_index as f64); + self.insert_output_port_at_center(output_index, center); + } + + pub fn insert_layer_input(&mut self, input_index: usize, node_top_left: DVec2) { + let center = if input_index == 0 { + node_top_left + DVec2::new(2. * 24., 24. * 2. + 8.) + } else { + node_top_left + DVec2::new(0., 24. * 1.) + }; + self.insert_input_port_at_center(input_index, center); + } + + pub fn insert_layer_output(&mut self, node_top_left: DVec2) { + // The center of the click target is always 24 px down from the top left corner of the node + let center = node_top_left + DVec2::new(2. * 24., -8.0); + self.insert_output_port_at_center(0, center); + } + + pub fn clicked_input_port_from_point(&self, point: DVec2) -> Option { + self.input_ports.iter().find_map(|(port, click_target)| click_target.intersect_point_no_stroke(point).then_some(*port)) + } + + pub fn clicked_output_port_from_point(&self, point: DVec2) -> Option { + self.output_ports.iter().find_map(|(port, click_target)| click_target.intersect_point_no_stroke(point).then_some(*port)) + } + + pub fn input_port_position(&self, index: usize) -> Option { + self.input_ports + .get(index) + .and_then(|(_, click_target)| click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.))) + } + + pub fn output_port_position(&self, index: usize) -> Option { + self.output_ports + .get(index) + .and_then(|(_, click_target)| click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.))) + } +} + +/// This is the same as Option, but more clear in the context of having cached metadata either being loaded or unloaded +#[derive(Debug, Default, Clone, PartialEq, Hash)] +pub enum TransientMetadata { + Loaded(T), + #[default] + Unloaded, +} + +unsafe impl StaticType for TransientMetadata { + type Static = TransientMetadata; +} + +// Ensure transient metadata is always serialized as Unloaded +impl Serialize for TransientMetadata { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + // Always serialize as Unloaded + serializer.serialize_unit_variant("TransientMetadata", 1, "Unloaded") + } +} + +impl<'de, T> Deserialize<'de> for TransientMetadata { + fn deserialize(_deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + // Always deserialize to Unloaded + Ok(TransientMetadata::Unloaded) + } +} + +impl TransientMetadata { + /// Set the current transient metadata to unloaded + pub fn unload(&mut self) { + *self = TransientMetadata::Unloaded; + } + + pub fn is_loaded(&self) -> bool { + matches!(self, TransientMetadata::Loaded(_)) + } +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, DynAny)] +pub struct NetworkEdgeDistance { + /// The viewport pixel distance distance between the left edge of the node graph and the exports. + pub exports_to_edge_distance: DVec2, + /// The viewport pixel distance between the left edge of the node graph and the imports. + pub imports_to_edge_distance: DVec2, +} + +/// A layer can either be position as Absolute or in a Stack +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Hash, DynAny)] +pub enum LayerPosition { + // Position of the layer in grid spaces. Measured from the top left corner of the layer, not including the left chain. This means it is always half a grid space to the left of the thumbnail. + Absolute(IVec2), + // A layer is in a Stack when it feeds into the bottom input of a layer. The Y position stores the vertical distance between the layer and its upstream sibling/parent. + Stack(u32), +} + +/// A node can either be position as Absolute or in a Chain +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Hash, DynAny)] +pub enum NodePosition { + // Position of the node in grid spaces + Absolute(IVec2), + // In a chain the position is based on the number of nodes to the first layer node + Chain, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Hash, DynAny)] +pub enum NodeTypePersistentMetadata { + Layer(LayerMetadata), + Node(NodePersistentMetadata), +} + +impl Default for NodeTypePersistentMetadata { + fn default() -> Self { + NodeTypePersistentMetadata::node(IVec2::ZERO) + } +} + +impl NodeTypePersistentMetadata { + pub fn node(position: IVec2) -> NodeTypePersistentMetadata { + NodeTypePersistentMetadata::Node(NodePersistentMetadata { + position: NodePosition::Absolute(position), + }) + } + pub fn layer(position: IVec2) -> NodeTypePersistentMetadata { + NodeTypePersistentMetadata::Layer(LayerMetadata { + persistent_metadata: LayerPersistentMetadata { + position: LayerPosition::Absolute(position), + }, + transient_metadata: Default::default(), + }) + } +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, DynAny)] +pub struct LayerMetadata { + pub persistent_metadata: LayerPersistentMetadata, + #[serde(skip)] + pub transient_metadata: LayerTransientMetadata, +} + +impl Hash for LayerMetadata { + fn hash(&self, state: &mut H) { + self.persistent_metadata.hash(state); + } +} + +impl Clone for LayerMetadata { + fn clone(&self) -> Self { + LayerMetadata { + persistent_metadata: self.persistent_metadata.clone(), + transient_metadata: Default::default(), + } + } +} + +impl PartialEq for LayerMetadata { + fn eq(&self, other: &Self) -> bool { + self.persistent_metadata == other.persistent_metadata + } +} + +/// All fields in LayerMetadata should automatically be updated by using the network interface API +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Hash, DynAny)] +pub struct LayerPersistentMetadata { + /// Stores the position of a layer node, which can either be Absolute or Stack + pub position: LayerPosition, +} + +#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize, DynAny)] +pub struct LayerTransientMetadata { + /// All nodes that should be moved when the layer is moved. + pub owned_nodes: TransientMetadata>, + // Stores the width in grid cell units for layer nodes from the left edge of the thumbnail (+12px padding since thumbnail ends between grid spaces) to the left end of the node + /// This is necessary since calculating the layer width through web_sys is very slow + pub layer_width: TransientMetadata, + // Should not be a performance concern to calculate when needed with chain_width. + // Stores the width in grid cell units for layer nodes from the left edge of the thumbnail to the end of the chain + // chain_width: u32, +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Hash, DynAny)] +pub struct NodePersistentMetadata { + /// Stores the position of a non layer node, which can either be Absolute or Chain + pub position: NodePosition, +} + +#[derive(Debug, Clone, PartialEq, DynAny)] +pub struct DocumentNodeClickTargets { + /// In order to keep the displayed position of the node in sync with the click target, the displayed position of a node is derived from the top left of the click target + /// Ensure node_click_target is kept in sync when modifying a node property that changes its size. Currently this is alias, inputs, is_layer, and metadata + pub node_click_target: ClickTarget, + /// Stores all port click targets in node graph space. + pub port_click_targets: Ports, + // Click targets that are specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer. + pub node_type_click_targets: NodeTypeClickTargets, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum NodeTypeClickTargets { + Layer(LayerClickTargets), + Node, // No transient click targets are stored exclusively for nodes +} + +/// All fields in TransientLayerMetadata should automatically be updated by using the network interface API +#[derive(Debug, Clone, PartialEq)] +pub struct LayerClickTargets { + /// Cache for all visibility buttons. Should be automatically updated when update_click_target is called + pub visibility_click_target: ClickTarget, + /// Cache for the grip icon, which is next to the visibility button. + pub grip_click_target: ClickTarget, + // TODO: Store click target for the preview button, which will appear when the node is a selected/(hovered?) layer node + // preview_click_target: ClickTarget, +} + +pub enum LayerClickTargetTypes { + Visibility, + Grip, + // Preview, +} + #[cfg(test)] mod test { use super::*; diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 32b62b4b81..c9ae5cac58 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -1,4 +1,4 @@ -use super::DocumentNode; +use super::{DocumentNode, TransientMetadata}; use crate::document::NodeId; pub use crate::imaginate_input::{ImaginateCache, ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod}; use crate::proto::{Any as DAny, FutureAny}; @@ -11,6 +11,7 @@ use graphene_core::{Color, MemoHash, Node, Type}; use dyn_any::DynAny; pub use dyn_any::StaticType; pub use glam::{DAffine2, DVec2, IVec2, UVec2}; +use std::collections::{HashMap, VecDeque}; use std::fmt::Display; use std::hash::Hash; use std::marker::PhantomData; @@ -178,6 +179,22 @@ tagged_value! { CentroidType(graphene_core::vector::misc::CentroidType), BooleanOperation(graphene_core::vector::misc::BooleanOperation), FontCache(Arc), + // Persistent Network Metadata + Previewing(crate::document::Previewing), + PTZ(crate::document::PTZ), + SelectionHistory(VecDeque>), + // Transient Network Metadata + StackDependents(TransientMetadata>), + AllNodesBoundingBox(TransientMetadata<[DVec2; 2]>), + OutwardWires(TransientMetadata>>), + ImportExportPorts(TransientMetadata), + RoundedNetworkEdgeDistance(TransientMetadata), + // Persistent Node Metadata + OptionalString(Option), + VecString(Vec), + NodeTypeMetadata(crate::document::NodeTypePersistentMetadata), + // Transient Node Metadata + ClickTargets(TransientMetadata), } impl TaggedValue { @@ -254,6 +271,10 @@ trait FakeHash { fn hash(&self, state: &mut H); } mod fake_hash { + use std::collections::HashMap; + + use crate::document::{DocumentNodeClickTargets, InputConnector, OutputConnector, Ports, TransientMetadata}; + use super::*; impl FakeHash for f64 { fn hash(&self, state: &mut H) { @@ -270,6 +291,22 @@ mod fake_hash { self.to_cols_array().iter().for_each(|x| x.to_bits().hash(state)) } } + // impl FakeHash for HashMap { + // fn hash(&self, state: &mut H) { + // self.iter().for_each(|(k, v)| { + // k.hash(state); + // v.hash(state); + // }) + // } + // } + impl FakeHash for HashMap { + fn hash(&self, state: &mut H) { + self.iter().for_each(|(k, v)| { + k.hash(state); + v.hash(state); + }) + } + } impl FakeHash for Option { fn hash(&self, state: &mut H) { if let Some(x) = self { @@ -280,6 +317,16 @@ mod fake_hash { } } } + impl FakeHash for TransientMetadata { + fn hash(&self, state: &mut H) { + if let TransientMetadata::Loaded(x) = self { + 1.hash(state); + x.hash(state); + } else { + 0.hash(state); + } + } + } impl FakeHash for Vec { fn hash(&self, state: &mut H) { self.len().hash(state); @@ -297,4 +344,98 @@ mod fake_hash { self.1.hash(state) } } + impl FakeHash for crate::document::PTZ { + fn hash(&self, state: &mut H) { + self.pan.hash(state); + self.tilt.hash(state); + self.zoom.hash(state); + } + } + // impl FakeHash for Option> { + // fn hash(&self, state: &mut H) { + // if let Some(x) = self { + // 1.hash(state); + // x.iter().for_each(|(k, v)| { + // k.hash(state); + // v.hash(state); + // }); + // } else { + // 0.hash(state); + // } + // } + // } + // impl FakeHash for HashMap> { + // fn hash(&self, state: &mut H) { + // self.iter().for_each(|(k, v)| { + // k.hash(state); + // v.hash(state); + // }); + // } + // } + impl FakeHash for OutputConnector { + fn hash(&self, state: &mut H) { + match self { + OutputConnector::Node { node_id, output_index } => { + 0.hash(state); + node_id.hash(state); + output_index.hash(state); + } + OutputConnector::Import(import_index) => { + 1.hash(state); + import_index.hash(state); + } + } + } + } + impl FakeHash for InputConnector { + fn hash(&self, state: &mut H) { + match self { + InputConnector::Node { node_id, input_index } => { + 0.hash(state); + node_id.hash(state); + input_index.hash(state); + } + InputConnector::Export(export_index) => { + 1.hash(state); + export_index.hash(state); + } + } + } + } + impl FakeHash for crate::document::NetworkEdgeDistance { + fn hash(&self, state: &mut H) { + self.exports_to_edge_distance.hash(state); + self.imports_to_edge_distance.hash(state); + } + } + impl FakeHash for Ports { + fn hash(&self, state: &mut H) { + self.input_ports.iter().chain(self.output_ports.iter()).for_each(|(index, click_target)| { + index.hash(state); + click_target.subpath().hash(state); + click_target.stroke_width().hash(state); + click_target.bounding_box().hash(state) + }); + } + } + impl FakeHash for DocumentNodeClickTargets { + fn hash(&self, state: &mut H) { + self.node_click_target.subpath().hash(state); + self.node_click_target.stroke_width().hash(state); + self.node_click_target.bounding_box().hash(state); + self.port_click_targets.hash(state); + match &self.node_type_click_targets { + crate::document::NodeTypeClickTargets::Layer(layer_click_target) => { + 1.hash(state); + layer_click_target.visibility_click_target.subpath().hash(state); + layer_click_target.visibility_click_target.stroke_width().hash(state); + layer_click_target.visibility_click_target.bounding_box().hash(state); + layer_click_target.grip_click_target.subpath().hash(state); + layer_click_target.grip_click_target.stroke_width().hash(state); + layer_click_target.grip_click_target.bounding_box().hash(state); + } + crate::document::NodeTypeClickTargets::Node => 0.hash(state), + } + } + } }