diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 7df4e0d2b4..96ebf2f0b3 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -74,7 +74,10 @@ impl MessageHandler> for DialogMessageHandl .all_layers() .filter(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[])) .map(|layer| { - let display_name = document.network_interface.display_name(&layer.to_node(), &[]); + 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) }) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index a60b964525..3d49bc1b6f 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -779,7 +779,8 @@ impl MessageHandler> for DocumentMessag } DocumentMessage::RenderRulers => { let current_ptz = if self.graph_view_overlay_open { - self.network_interface.navigation_metadata(&self.breadcrumb_network_path) + let Some(ptz) = self.network_interface.ptz(&self.breadcrumb_network_path) else { return }; + ptz } else { &self.document_ptz }; @@ -1103,14 +1104,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(), &self.network_interface.navigation_metadata(&self.breadcrumb_network_path).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); @@ -1462,7 +1463,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) } @@ -1978,6 +1980,6 @@ impl DocumentMessageHandler { fn default_document_network_interface() -> NodeNetworkInterface { let mut network_interface = NodeNetworkInterface::default(); network_interface.add_export(TaggedValue::ArtboardGroup(graphene_core::ArtboardGroup::EMPTY), -1, "".to_string(), &[]); - network_interface.insert_network_metadata(&[], NodeNetworkPersistentMetadata::default()); + network_interface.insert_network_metadata(NodeNetworkPersistentMetadata::default(), &[]); network_interface } 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 3b3e4df385..e5298174ec 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -6,10 +6,11 @@ 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::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::value::TaggedValue; use graph_craft::document::{NodeId, PTZ}; use glam::{DAffine2, DVec2}; @@ -41,27 +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 { - Some(network_interface.navigation_metadata(breadcrumb_network_path).node_graph_ptz) - } - } + let ptz = if !graph_view_overlay_open { + Some(*document_ptz) + } else { + network_interface.ptz(breadcrumb_network_path).cloned() + }; - 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 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; }; @@ -76,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); @@ -115,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![ @@ -142,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 } => { @@ -173,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)); @@ -203,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)); @@ -225,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); @@ -243,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(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 { @@ -278,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 @@ -306,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()); @@ -333,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); @@ -340,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, @@ -371,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); @@ -406,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); @@ -466,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/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index ca1e19d998..ad943ebb56 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 @@ -158,10 +158,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 output_connector_position_viewport = network_interface - .navigation_metadata(selection_network_path) - .node_graph_to_viewport - .transform_point2(output_connector_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 CreateNodeFromContextMenu"); + return; + }; + 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; @@ -334,10 +335,13 @@ impl<'a> MessageHandler> for NodeGrap log::error!("Selection network path does not match breadcrumb network path in PointerDown"); return; } - + 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_interface.navigation_metadata(selection_network_path).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) @@ -387,16 +391,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_interface.navigation_metadata(selection_network_path).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_interface.navigation_metadata(selection_network_path).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); @@ -420,10 +427,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_interface - .navigation_metadata(selection_network_path) - .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.) @@ -570,12 +578,13 @@ impl<'a> MessageHandler> for NodeGrap 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_interface - .navigation_metadata(selection_network_path) - .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); @@ -592,7 +601,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; } @@ -612,11 +621,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) { + 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; + }; // 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 = network_interface - .navigation_metadata(selection_network_path) - .node_graph_to_viewport - .transform_point2(wire_in_progress_from_connector); + 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| { @@ -734,22 +744,25 @@ impl<'a> MessageHandler> for NodeGrap 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_interface - .navigation_metadata(selection_network_path) - .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_interface.navigation_metadata(selection_network_path).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); @@ -769,15 +782,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_interface.navigation_metadata(selection_network_path).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), @@ -1259,11 +1270,11 @@ impl<'a> MessageHandler> for NodeGrap log::error!("Could not get network metadata in PointerMove"); return; }; - - let box_selection_start_viewport = network_interface - .navigation_metadata(selection_network_path) - .node_graph_to_viewport - .transform_point2(box_selection_start); + 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 = 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, @@ -1271,11 +1282,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_interface - .navigation_metadata(selection_network_path) - .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 { @@ -1472,7 +1480,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())) @@ -1596,7 +1604,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 { 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/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 5525011559..03edf9d606 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -7,7 +7,7 @@ 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::{InputConnector, NavigationMetadata, OutputConnector, Previewing, RootNode, PTZ}; +use graph_craft::document::{InputConnector, OutputConnector, Previewing, RootNode, PTZ}; use graph_craft::{concrete, Type}; use graphene_std::renderer::{ClickTarget, Quad}; use graphene_std::vector::{PointId, VectorData, VectorModificationType}; @@ -16,6 +16,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)] @@ -38,12 +39,12 @@ pub struct NodeNetworkInterface { transaction_status: TransactionStatus, } +// Enum to represent all metadata in the network, which can be used uniquely identify any stored value #[derive(Hash)] -pub enum Metadata { +pub enum MetadataType { // Network persistent metadata - NodeMetadata, Previewing, - NavigationMetadata, + NavigationMetadata(NavigationMetadataType), SelectionUndoHistory, SelectionRedoHistory, // Network transient metadata @@ -68,6 +69,13 @@ pub enum Metadata { IsLayer, } +#[derive(Hash)] +pub enum NavigationMetadataType { + PTZ, + NodeGraphToViewport, + NodeGraphTopRight, +} + impl Clone for NodeNetworkInterface { fn clone(&self) -> Self { Self { @@ -484,15 +492,15 @@ impl NodeNetworkInterface { } } - fn metadata_node_id(path: &[NodeId], metadata: Metadata) -> NodeId { + fn metadata_node_id(metadata: MetadataType, node_path: &[NodeId]) -> NodeId { let mut hasher = DefaultHasher::new(); - path.hash(&mut hasher); + node_path.hash(&mut hasher); metadata.hash(&mut hasher); NodeId(hasher.finish()) } - fn metadata_value(&self, path: &[NodeId], metadata: Metadata) -> Option<&TaggedValue> { - let node_id = Self::metadata_node_id(path, metadata); + fn metadata_value(&self, metadata: MetadataType, node_path: &[NodeId]) -> Option<&TaggedValue> { + let node_id = Self::metadata_node_id(metadata, node_path); let network = self.network(&[]).unwrap(); let Some(node) = network.nodes.get(&node_id) else { @@ -998,26 +1006,17 @@ impl NodeNetworkInterface { upstream_nodes_below_layer } - pub fn previewing(&self, network_path: &[NodeId]) -> Option<&Previewing> { - let Some(tagged_value) = self.metadata_value(&network_path, Metadata::Previewing) 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) - } - /// 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 previewing = self.previewing(network_path); - match &previewing { + let Some(previewing) = self.previewing(network_path) else { + log::error!("Could not get previewing in root_node"); + return None; + }; + 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 { @@ -1032,16 +1031,52 @@ impl NodeNetworkInterface { } } - pub fn navigation_metadata(&self, network_path: &[NodeId]) -> Option<&NavigationMetadata> { - let Some(tagged_value) = self.metadata_value(&network_path, Metadata::NavigationMetadata) else { - log::error!("Could not get tagged value in navigation_metadata"); + 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::NavigationMetadata(navigation_metadata) = tagged_value else { + let TaggedValue::Previewing(previewing) = tagged_value else { log::error!("Tagged value should be Previewing in previewing"); return None; }; - Some(navigation_metadata) + Some(previewing) + } + + 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 reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option { @@ -1052,7 +1087,7 @@ impl NodeNetworkInterface { pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&String> { let mut node_id_path = network_path.to_vec(); node_id_path.push(*node_id); - let Some(tagged_value) = self.metadata_value(&node_id_path, Metadata::DisplayName) else { + let Some(tagged_value) = self.metadata_value(MetadataType::DisplayName, &node_id_path) else { log::error!("Could not get tagged value in display_name"); return None; }; @@ -1064,6 +1099,10 @@ impl NodeNetworkInterface { } pub fn frontend_display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { + 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 .node_metadata(node_id, network_path) .expect("Could not get persistent node metadata in untitled_layer_label") @@ -1071,14 +1110,14 @@ impl NodeNetworkInterface { .is_layer(); let reference = self.reference(node_id, network_path); 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() } } @@ -1844,8 +1883,8 @@ impl NodeNetworkInterface { log::error!("Could not get rounded_network_edge_distance 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(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 { @@ -1855,11 +1894,7 @@ impl NodeNetworkInterface { let mut import_export_ports = Ports::new(); - let viewport_top_right = self - .navigation_metadata(network_path) - .node_graph_to_viewport - .inverse() - .transform_point2(rounded_network_edge_distance.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() @@ -1876,11 +1911,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 = self - .navigation_metadata(network_path) - .node_graph_to_viewport - .inverse() - .transform_point2(rounded_network_edge_distance.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 @@ -1933,14 +1964,17 @@ impl NodeNetworkInterface { } fn load_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network in set_grid_aligned_edges"); + // 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; }; - // When setting the edges to be grid aligned, update the pixel offset to ensure the next pan starts from the snapped import/export position - let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport; + // TODO: Eventually replace node graph top right with the footprint when trying to get the network edge distance - let node_graph_top_right = network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right; + let 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; + }; 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, @@ -2545,23 +2579,16 @@ impl NodeNetworkInterface { log::error!("Could not get rounded_network_edge_distance in collect_front_end_click_targets"); return FrontendClickTargets::default(); }; - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in collect_front_end_click_targets"); + 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 = 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 = self - .navigation_metadata(network_path) - .node_graph_to_viewport - .inverse() - .transform_point2(import_exports_viewport_top_left); - let node_graph_bottom_right = self - .navigation_metadata(network_path) - .node_graph_to_viewport - .inverse() - .transform_point2(import_exports_viewport_bottom_right); + 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(); @@ -2601,14 +2628,6 @@ impl NodeNetworkInterface { && 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 self.navigation_metadata(network_path).node_graph_ptz) - } - // 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 { @@ -2616,8 +2635,12 @@ impl NodeNetworkInterface { 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 = self.navigation_metadata(network_path).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() @@ -2645,16 +2668,15 @@ impl NodeNetworkInterface { } 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 = self.navigation_metadata(network_path).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 @@ -2679,8 +2701,12 @@ impl NodeNetworkInterface { 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 = self.navigation_metadata(network_path).node_graph_to_viewport.inverse().transform_point2(click); network .nodes .keys() @@ -2708,8 +2734,11 @@ impl NodeNetworkInterface { log::error!("Could not get nested network in output_connector_from_click"); return None; }; - - let point = self.navigation_metadata(network_path).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() @@ -2774,7 +2803,11 @@ impl NodeNetworkInterface { log::error!("Could not get selected nodes in selected_nodes_bounding_box_viewport"); return None; }; - let node_graph_to_viewport = self.navigation_metadata(network_path).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() @@ -2782,7 +2815,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) } @@ -2790,8 +2823,12 @@ 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(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(self.navigation_metadata(network_path).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) { @@ -2939,28 +2976,31 @@ impl NodeNetworkInterface { current_path.push(*node_id); current_path })); - if let Some(other_network_metadata) = other_interface.network_metadata(&path) { - let Some(navigation_metadata) = self.navigation_metadata_mut(&path) else { - log::error!("Could not get nested navigation_metadata in copy_all_navigation_metadata"); - continue; - }; - *navigation_metadata = other_network_metadata.persistent_metadata.navigation_metadata.clone(); + 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]) { - self.navigation_metadata(network_path).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_metadata in set_grid_aligned_edges"); - return; - }; - network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right = node_graph_top_right; + 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); } @@ -3433,7 +3473,7 @@ impl NodeNetworkInterface { network.nodes.insert(node_id, node_template.document_node); // TODO: Remove this clone once the later usage is removed - self.insert_all_node_metadata(node_id, network_path, node_template.persistent_node_metadata.clone()); + self.insert_all_node_metadata(node_id, node_template.persistent_node_metadata.clone(), network_path); self.transaction_modified(); let Some(network_metadata) = self.network_metadata_mut(network_path) else { @@ -3450,33 +3490,45 @@ impl NodeNetworkInterface { self.unload_node_click_targets(&node_id, network_path) } - fn insert_all_node_metadata(&mut self, node_id: NodeId, network_path: &[NodeId], persistent_metadata: DocumentNodePersistentMetadata) { + 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); - let display_name_node_id = Self::metadata_node_id(&node_path, Metadata::DisplayName); - self.insert_node_metadata(display_name_node_id, TaggedValue::String(persistent_metadata.display_name)); + self.insert_node_metadata(MetadataType::DisplayName, TaggedValue::String(persistent_metadata.display_name), &node_path); // TODO: Add the rest of the node metadata nodes if let Some(nested_network) = persistent_metadata.network_metadata { - self.insert_network_metadata(&node_path, nested_network.persistent_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, network_path: &[NodeId], persistent_metadata: NodeNetworkPersistentMetadata) { - let previewing_node_id = Self::metadata_node_id(network_path, Metadata::Previewing); - let navigation_metadata_id = Self::metadata_node_id(network_path, Metadata::NavigationMetadata); - self.insert_node_metadata(previewing_node_id, TaggedValue::Previewing(persistent_metadata.previewing)); - self.insert_node_metadata(navigation_metadata_id, TaggedValue::NavigationMetadata(persistent_metadata.navigation_metadata)); + 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, + ); // TODO: Add the rest of the network metadata nodes for (node_id, node_metadata) in persistent_metadata.node_metadata { - self.insert_all_node_metadata(node_id, network_path, node_metadata.persistent_metadata); + self.insert_all_node_metadata(node_id, node_metadata.persistent_metadata, network_path); } } - /// Adds a node for the metadata. TODO: Consider calling this if a metadata node cannot be found. - fn insert_node_metadata(&mut self, metadata_node_id: NodeId, tagged_value: TaggedValue) { + /// 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); let network = self.network_mut(&[]).unwrap(); log::debug!("Inserting metadata node with id {metadata_node_id}"); network.nodes.insert( @@ -3673,13 +3725,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 - self.set_previewing(network_path, 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 Some(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), @@ -3687,57 +3739,7 @@ impl NodeNetworkInterface { network_path, ); } - self.set_previewing(network_path, Previewing::No); - } - - fn set_previewing(&mut self, network_path: &[NodeId], previewing: Previewing) { - let network = self.network_mut(&[]).unwrap(); - - let previewing_node_id = Self::metadata_node_id(&network_path, Metadata::Previewing); - - let Some(previewing_node) = network.nodes.get_mut(&previewing_node_id) else { - log::error!("Could not get display name node with id {previewing_node_id} in set_previewing"); - return; - }; - - let Some(previewing_input) = previewing_node.inputs.get_mut(0) else { - log::error!("Could not get display name input in set_previewing"); - return; - }; - - let Some(TaggedValue::Previewing(current_previewing)) = previewing_input.as_value() else { - log::error!("Could not get current display name in set_previewing"); - return; - }; - - if *current_previewing == previewing { - return; - } - - *previewing_input = NodeInput::value(TaggedValue::Previewing(previewing), false); - } - - fn navigation_metadata_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NavigationMetadata> { - let network = self.network_mut(&[]).unwrap(); - - let navigation_metadata_id = Self::metadata_node_id(&network_path, Metadata::NavigationMetadata); - - let Some(navigation_metadata_node) = network.nodes.get_mut(&navigation_metadata_id) else { - log::error!("Could not get navigation metadata node with id {navigation_metadata_id} in set_navigation_metadata"); - return None; - }; - - let Some(navigation_metadata_input) = navigation_metadata_node.inputs.get_mut(0) else { - log::error!("Could not get navigation metadata input in set_navigation_metadata"); - return None; - }; - - let Some(TaggedValue::NavigationMetadata(current_navigation_metadata)) = navigation_metadata_input.as_value_mut().as_deref_mut() else { - log::error!("Could not get current navigation metadata in set_navigation_metadata"); - return None; - }; - - Some(current_navigation_metadata) + self.set_metadata(MetadataType::Previewing, TaggedValue::Previewing(Previewing::No), network_path) } /// Sets the root node only if a node is being previewed @@ -3756,33 +3758,40 @@ impl NodeNetworkInterface { // } // } - pub fn set_display_name(&mut self, node_id: &NodeId, display_name: String, network_path: &[NodeId]) { + pub fn set_metadata(&mut self, metadata: MetadataType, tagged_value: TaggedValue, node_path: &[NodeId]) { let network = self.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 node with id {metadata_node_id} in set_metadata"); + return; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in set_metadata"); + return; + }; + let Some(mut value) = metadata_input.as_value_mut() else { + log::error!("Could not get tagged value in set_metadata"); + return; + }; + *value.deref_mut() = tagged_value; + } + + 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); + } + + 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); - let display_name_node_id = Self::metadata_node_id(&node_path, Metadata::DisplayName); - let Some(display_name_node) = network.nodes.get_mut(&display_name_node_id) else { - log::error!("Could not get display name node with id {display_name_node_id} in set_display_name"); - return; - }; - - let Some(display_name_input) = display_name_node.inputs.get_mut(0) else { - log::error!("Could not get display name input in set_display_name"); - return; - }; - - let Some(TaggedValue::String(current_display_name)) = display_name_input.as_value() else { - log::error!("Could not get current display name in set_display_name"); - return; - }; - - if *current_display_name == display_name { - return; - } - - *display_name_input = NodeInput::value(TaggedValue::String(display_name), false); + 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 @@ -4024,7 +4033,7 @@ impl NodeNetworkInterface { self.disconnect_input(&InputConnector::Export(0), network_path); } } - self.set_previewing(network_path, 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 @@ -5393,8 +5402,10 @@ 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, + // TODO: Replace with footprint /// Top right of the node graph in viewport space pub node_graph_top_right: DVec2, } diff --git a/editor/src/messages/portfolio/document/utility_types/network_metadata_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_metadata_interface.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 5fb4b2f345..c9d628e410 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -517,7 +517,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/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/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index 481be53ade..53a84dcdab 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -42,7 +42,7 @@ impl ClickTarget { 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 8a09aa019b..5d45074222 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -6,7 +6,7 @@ use graphene_core::memo::MemoHashGuard; pub use graphene_core::uuid::generate_uuid; use graphene_core::{Cow, MemoHash, ProtoNodeIdentifier, Type}; -use glam::{DAffine2, DVec2, IVec2}; +use glam::{DVec2, IVec2}; use log::Metadata; use rustc_hash::FxHashMap; use std::collections::hash_map::DefaultHasher; @@ -1460,34 +1460,6 @@ impl OutputConnector { } } -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, DynAny)] -pub struct NavigationMetadata { - /// The current pan, and zoom state of the viewport's view of the node graph. - /// Ensure `DocumentMessage::UpdateDocumentTransform` is called when the pan, zoom, or transform changes. - pub node_graph_ptz: PTZ, - // TODO: Remove and replace with calculate_offset_transform from the node_graph_ptz. This will be difficult since it requires both the navigation message handler and the IPP - /// 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, -} - -impl Default for NavigationMetadata { - fn default() -> NavigationMetadata { - //Default PTZ and transform - NavigationMetadata { - node_graph_ptz: PTZ::default(), - node_graph_to_viewport: DAffine2::IDENTITY, - exports_to_edge_distance: DVec2::ZERO, - imports_to_edge_distance: DVec2::ZERO, - } - } -} - #[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, DynAny)] #[serde(default)] pub struct PTZ { diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 17b451a6df..44f86c6eb8 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -179,7 +179,7 @@ tagged_value! { BooleanOperation(graphene_core::vector::misc::BooleanOperation), FontCache(Arc), Previewing(crate::document::Previewing), - NavigationMetadata(crate::document::NavigationMetadata) + PTZ(crate::document::PTZ), } impl TaggedValue { @@ -306,12 +306,4 @@ mod fake_hash { self.zoom.hash(state); } } - impl FakeHash for crate::document::NavigationMetadata { - fn hash(&self, state: &mut H) { - self.node_graph_ptz.hash(state); - self.node_graph_to_viewport.hash(state); - self.exports_to_edge_distance.hash(state); - self.imports_to_edge_distance.hash(state); - } - } }