From 6b7904f14167efb513758537ee42a4fdb0629ae9 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 3 Sep 2024 14:14:38 -0700 Subject: [PATCH 01/10] Fix path tool select --- editor/src/messages/tool/tool_messages/path_tool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c424bbfa73..c27114307b 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -349,7 +349,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 +358,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 From 0a03cb71af49be92b9b7a291a3f5bd7c31310142 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 3 Sep 2024 15:32:18 -0700 Subject: [PATCH 02/10] Refactor rounded edge distance --- .../document/document_message_handler.rs | 2 +- .../utility_types/network_interface.rs | 127 +++++++++++++----- 2 files changed, 94 insertions(+), 35 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 3ecc06c8cd..11d07c90ec 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1435,7 +1435,7 @@ impl DocumentMessageHandler { responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::ForceRunDocumentGraph); - + responses.add(NodeGraphMessage::SetGridAlignedEdges); Some(previous_network) } pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { 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..237864f4d7 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -1772,6 +1772,10 @@ impl NodeNetworkInterface { log::error!("Could not get all nodes bounding box in load_export_ports"); return; }; + 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(network_metadata) = self.network_metadata(network_path) else { log::error!("Could not get nested network_metadata in load_export_ports"); return; @@ -1780,6 +1784,7 @@ impl NodeNetworkInterface { 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 @@ -1787,7 +1792,7 @@ impl NodeNetworkInterface { .navigation_metadata .node_graph_to_viewport .inverse() - .transform_point2(network_metadata.persistent_metadata.navigation_metadata.exports_to_edge_distance); + .transform_point2(rounded_network_edge_distance.exports_to_edge_distance); let offset_from_top_right = if network .exports .first() @@ -1809,7 +1814,7 @@ impl NodeNetworkInterface { .navigation_metadata .node_graph_to_viewport .inverse() - .transform_point2(network_metadata.persistent_metadata.navigation_metadata.imports_to_edge_distance); + .transform_point2(rounded_network_edge_distance.imports_to_edge_distance); let offset_from_top_left = if network .exports @@ -1842,6 +1847,69 @@ impl NodeNetworkInterface { network_metadata.transient_metadata.import_export_ports.unload(); } + pub fn rounded_network_edge_distance(&mut self, network_path: &[NodeId]) -> Option<&NetworkEdgeDistance> { + let Some(network_metadata) = self.network_metadata(network_path) else { + log::error!("Could not get nested network_metadata in rounded_network_edge_distance"); + return None; + }; + if !network_metadata.transient_metadata.rounded_network_edge_distance.is_loaded() { + self.load_rounded_network_edge_distance(network_path); + } + let Some(network_metadata) = self.network_metadata(network_path) else { + log::error!("Could not get nested network_metadata in rounded_network_edge_distance"); + return None; + }; + let TransientMetadata::Loaded(rounded_network_edge_distance) = &network_metadata.transient_metadata.rounded_network_edge_distance else { + log::error!("could not load import rounded_network_edge_distance"); + return None; + }; + Some(rounded_network_edge_distance) + } + + fn load_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) { + let Some(network_metadata) = self.network_metadata_mut(network_path) else { + log::error!("Could not get nested network in set_grid_aligned_edges"); + return; + }; + // When setting the edges to be grid aligned, update the pixel offset to ensure the next pan starts from the snapped import/export position + let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport; + // TODO: Eventually replace node graph top right with the footprint when trying to get the network edge distance + let node_graph_top_right = network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right; + + let target_exports_distance = node_graph_to_viewport.inverse().transform_point2(DVec2::new( + node_graph_top_right.x - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, + node_graph_top_right.y + EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64, + )); + + let target_imports_distance = node_graph_to_viewport + .inverse() + .transform_point2(DVec2::new(IMPORTS_TO_LEFT_EDGE_PIXEL_GAP as f64, IMPORTS_TO_TOP_EDGE_PIXEL_GAP as f64)); + + let rounded_exports_distance = DVec2::new((target_exports_distance.x / 24. + 0.5).floor() * 24., (target_exports_distance.y / 24. + 0.5).floor() * 24.); + let rounded_imports_distance = DVec2::new((target_imports_distance.x / 24. + 0.5).floor() * 24., (target_imports_distance.y / 24. + 0.5).floor() * 24.); + + let rounded_viewport_exports_distance = node_graph_to_viewport.transform_point2(rounded_exports_distance); + let rounded_viewport_imports_distance = node_graph_to_viewport.transform_point2(rounded_imports_distance); + + let network_edge_distance = NetworkEdgeDistance { + exports_to_edge_distance: rounded_viewport_exports_distance, + imports_to_edge_distance: rounded_viewport_imports_distance, + }; + let Some(network_metadata) = self.network_metadata_mut(network_path) else { + log::error!("Could not get current network in load_export_ports"); + return; + }; + network_metadata.transient_metadata.rounded_network_edge_distance = TransientMetadata::Loaded(network_edge_distance); + } + + fn unload_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) { + let Some(network_metadata) = self.network_metadata_mut(network_path) else { + log::error!("Could not get nested network_metadata in unload_export_ports"); + return; + }; + network_metadata.transient_metadata.rounded_network_edge_distance.unload(); + } + fn owned_nodes(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&HashSet> { let layer_node = self.node_metadata(node_id, network_path)?; let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &layer_node.persistent_metadata.node_type_metadata else { @@ -2407,12 +2475,16 @@ impl NodeNetworkInterface { let mut all_nodes_bounding_box = String::new(); let _ = rect.subpath_to_svg(&mut all_nodes_bounding_box, DAffine2::IDENTITY); + 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(network_metadata) = self.network_metadata(network_path) else { log::error!("Could not get nested network_metadata 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 import_exports_viewport_top_left = rounded_network_edge_distance.imports_to_edge_distance; + let import_exports_viewport_bottom_right = rounded_network_edge_distance.exports_to_edge_distance; let node_graph_top_left = network_metadata .persistent_metadata @@ -2843,30 +2915,11 @@ impl NodeNetworkInterface { // This should be run whenever the pan ends, a zoom occurs, or the network is opened pub fn set_grid_aligned_edges(&mut self, node_graph_top_right: DVec2, network_path: &[NodeId]) { let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network in set_grid_aligned_edges"); + log::error!("Could not get nested network_metadata 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; - + network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right = node_graph_top_right; + self.unload_rounded_network_edge_distance(network_path); self.unload_import_export_ports(network_path); } @@ -5076,6 +5129,16 @@ pub struct NodeNetworkTransientMetadata { // pub wire_paths: Vec /// All export connector click targets pub import_export_ports: TransientMetadata, + // Distance to the edges of the network, where the import/export ports are displayed. Rounded to nearest grid space when the panning ends. + pub rounded_network_edge_distance: TransientMetadata, +} + +#[derive(Debug, Clone)] +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, } #[derive(Debug, Clone)] @@ -5297,12 +5360,8 @@ pub struct NavigationMetadata { // 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, + /// Top right of the node graph in viewport space + pub node_graph_top_right: DVec2, } impl Default for NavigationMetadata { @@ -5311,8 +5370,8 @@ 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, } } } From 3738a46e48f31f131d48e6844593b9014d0d769b Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 3 Sep 2024 15:35:52 -0700 Subject: [PATCH 03/10] comment --- .../src/messages/portfolio/document/document_message_handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 11d07c90ec..36ac6bb5d5 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1435,6 +1435,7 @@ 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) } From 91ee7b9ff0718bc97ba3c55f72f1be61e9e677c5 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 2 Sep 2024 19:12:19 -0700 Subject: [PATCH 04/10] Storing display name as a node input --- .../messages/dialog/dialog_message_handler.rs | 8 +- .../utility_types/network_interface.rs | 160 ++++++++++++++---- 2 files changed, 129 insertions(+), 39 deletions(-) diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 9e16a055e2..7df4e0d2b4 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -74,12 +74,8 @@ 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(), &[]); + let name = if display_name.is_empty() { "Artboard".to_string() } else { display_name }; (layer, name) }) .collect(); 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 237864f4d7..79407649d3 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -23,6 +23,7 @@ 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, + // TODO: Remove and store in node network with IDs based on Self::metadata_node_id /// 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, // TODO: Wrap in TransientMetadata Option @@ -37,6 +38,36 @@ pub struct NodeNetworkInterface { transaction_status: TransactionStatus, } +#[derive(Hash)] +pub enum Metadata { + // Network persistent metadata + NodeMetadata, + Previewing, + NavigationMetadata, + SelectionUndoHistory, + SelectionRedoHistory, + // Network transient metadata + SelectedNodes, + StackDependents, + AllNodesBoundingBox, + OutwardWires, + ImportExportPorts, + // Node persistent metadata + Reference, + DisplayName, + InputNames, + OutputNames, + HasPrimaryOutput, + Locked, + NodeTypeMetadata, + // Node transient metadata + ClickTargets, + NodeTypeClickTargets, + // Derived metadata + Position, + IsLayer, +} + impl Clone for NodeNetworkInterface { fn clone(&self) -> Self { Self { @@ -453,6 +484,24 @@ impl NodeNetworkInterface { } } + fn metadata_node_id(path: &[NodeId], metadata: Metadata) -> NodeId { + let mut hasher = DefaultHasher::new(); + 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); + + let network = self.network(&[]).unwrap(); + let Some(node) = 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() + } + /// 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 @@ -988,11 +1037,17 @@ impl NodeNetworkInterface { } 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"); + 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 { + log::error!("Could not get tagged value in display_name"); + return "".to_string(); + }; + let TaggedValue::String(display_name) = tagged_value else { + log::error!("Tagged value should be String in display_name"); return "".to_string(); }; - node_metadata.persistent_metadata.display_name.clone() + display_name.clone() } pub fn frontend_display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { @@ -2212,7 +2267,7 @@ impl NodeNetworkInterface { DocumentNodeClickTargets { node_click_target, port_click_targets, - node_type_metadata: NodeTypeClickTargets::Node, + node_type_click_targets: NodeTypeClickTargets::Node, } } else { // Layer inputs @@ -2251,7 +2306,7 @@ 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, }), @@ -2450,7 +2505,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); @@ -2601,7 +2656,7 @@ impl NodeNetworkInterface { .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), @@ -3390,6 +3445,8 @@ 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.transaction_modified(); let Some(network_metadata) = self.network_metadata_mut(network_path) else { @@ -3406,6 +3463,26 @@ 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) { + 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)); + } + + fn insert_node_metadata(&mut self, metadata_node_id: NodeId, tagged_value: TaggedValue) { + let network = self.network_mut(&[]).unwrap(); + log::debug!("Inserting metadata node with id {metadata_node_id}"); + 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 { @@ -3632,39 +3709,56 @@ 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"); + let network = self.network_mut(network_path).unwrap(); + + 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; }; - if node_metadata.persistent_metadata.display_name == display_name { + 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; - } - - 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; - }; + 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; + }; - 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; + if *current_display_name == display_name { + return; } + *display_name_input = NodeInput::value(TaggedValue::String(display_name), false); + + // 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); self.unload_node_click_targets(node_id, network_path); @@ -5309,7 +5403,7 @@ pub struct DocumentNodeClickTargets { /// 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, + pub node_type_click_targets: NodeTypeClickTargets, } #[derive(Debug, Default, Clone)] From bc62fd2dea759fbb34723a044a610e7d2b1a2038 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 3 Sep 2024 13:39:21 -0700 Subject: [PATCH 05/10] Begin transferring network metadata --- editor/src/consts.rs | 2 - .../document/document_message_handler.rs | 14 +- .../graph_operation_message_handler.rs | 4 +- .../graph_operation/transform_utils.rs | 4 +- .../document/graph_operation/utility_types.rs | 4 +- .../navigation/navigation_message_handler.rs | 12 +- .../document/node_graph/node_graph_message.rs | 4 +- .../node_graph/node_graph_message_handler.rs | 69 ++-- .../document/node_graph/utility_types.rs | 4 +- .../portfolio/document/utility_types/misc.rs | 25 +- .../utility_types/network_interface.rs | 315 +++++++----------- .../portfolio/portfolio_message_handler.rs | 4 +- .../messages/tool/tool_messages/text_tool.rs | 3 +- node-graph/gcore/src/consts.rs | 4 + node-graph/graph-craft/src/document.rs | 158 ++++++++- node-graph/graph-craft/src/document/value.rs | 17 + 16 files changed, 354 insertions(+), 289 deletions(-) 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/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 36ac6bb5d5..a60b964525 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -2,7 +2,7 @@ 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::network_interface::{NodeNetworkInterface, NodeNetworkPersistentMetadata, TransactionStatus}; use super::utility_types::nodes::{CollapsedLayers, 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}; @@ -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; @@ -779,10 +779,7 @@ 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 + self.network_interface.navigation_metadata(&self.breadcrumb_network_path) } else { &self.document_ptz }; @@ -1112,7 +1109,7 @@ impl MessageHandler> for DocumentMessag let transform = self .navigation_handler - .calculate_offset_transform(ipp.viewport_bounds.center(), &network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz); + .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 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(); @@ -1981,5 +1978,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 } 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..0d9f80943a 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; 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..3b3e4df385 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,16 @@ 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::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; -use graph_craft::document::NodeId; +use graph_craft::document::{NodeId, PTZ}; use glam::{DAffine2, DVec2}; @@ -46,8 +45,7 @@ impl MessageHandler> for Navigation 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) + Some(network_interface.navigation_metadata(breadcrumb_network_path).node_graph_ptz) } } @@ -249,7 +247,7 @@ impl MessageHandler> for Navigation 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); responses.add(PortfolioMessage::UpdateDocumentWidgets); 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..ca1e19d998 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,12 @@ 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::{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, NodeId, NodeInput, OutputConnector, Previewing}; use graph_craft::proto::GraphErrors; use graphene_core::*; use renderer::{ClickTarget, Quad}; @@ -158,13 +158,8 @@ 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"); - return; - }; - let output_connector_position_viewport = network_metadata - .persistent_metadata - .navigation_metadata + let output_connector_position_viewport = network_interface + .navigation_metadata(selection_network_path) .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 { @@ -339,14 +334,10 @@ 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"); - 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 = network_interface.navigation_metadata(selection_network_path).node_graph_to_viewport.inverse().transform_point2(click); if network_interface .layer_click_target_from_click(click, network_interface::LayerClickTargetTypes::Grip, selection_network_path) @@ -358,7 +349,6 @@ impl<'a> MessageHandler> for NodeGrap 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 { @@ -402,11 +392,11 @@ impl<'a> MessageHandler> for NodeGrap 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) / network_interface.navigation_metadata(selection_network_path).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) / network_interface.navigation_metadata(selection_network_path).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,9 +420,8 @@ 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 + 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 (width, height) = if matches!(context_menu.context_menu_data, ContextMenuData::ToggleLayer { .. }) { @@ -576,18 +565,14 @@ 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 viewport_location = ipp.mouse.position; - let point = network_metadata - .persistent_metadata - .navigation_metadata + let point = network_interface + .navigation_metadata(selection_network_path) .node_graph_to_viewport .inverse() .transform_point2(viewport_location); @@ -628,12 +613,8 @@ 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 { - return; - }; - let from_connector_viewport = network_metadata - .persistent_metadata - .navigation_metadata + 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_is_layer = network_interface @@ -747,10 +728,6 @@ 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); @@ -764,16 +741,15 @@ impl<'a> MessageHandler> for NodeGrap responses.add(NodeGraphMessage::SelectedNodesSet { nodes: new_selected_nodes }); self.deselect_on_pointer_up = None; } - let point = network_metadata - .persistent_metadata - .navigation_metadata + let point = network_interface + .navigation_metadata(selection_network_path) .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 node_graph_to_viewport = network_interface.navigation_metadata(selection_network_path).node_graph_to_viewport; 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); @@ -800,7 +776,8 @@ impl<'a> MessageHandler> for NodeGrap 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) / network_interface.navigation_metadata(selection_network_path).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), @@ -1283,7 +1260,10 @@ impl<'a> MessageHandler> for NodeGrap 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 = network_interface + .navigation_metadata(selection_network_path) + .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,9 +1271,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 + let box_selection_end_graph = network_interface + .navigation_metadata(selection_network_path) .node_graph_to_viewport .inverse() .transform_point2(ipp.mouse.position); 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/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 79407649d3..5525011559 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,6 +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::{concrete, Type}; use graphene_std::renderer::{ClickTarget, Quad}; use graphene_std::vector::{PointId, VectorData, VectorModificationType}; @@ -998,12 +998,16 @@ 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; + 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; }; - network_metadata.persistent_metadata.previewing + 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 @@ -1012,11 +1016,8 @@ impl NodeNetworkInterface { 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"); - return None; - }; - match &network_metadata.persistent_metadata.previewing { + let previewing = self.previewing(network_path); + 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 { @@ -1031,23 +1032,35 @@ 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"); + return None; + }; + let TaggedValue::NavigationMetadata(navigation_metadata) = tagged_value else { + log::error!("Tagged value should be Previewing in previewing"); + return None; + }; + Some(navigation_metadata) + } + 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 display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { + 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(&node_id_path, Metadata::DisplayName) else { log::error!("Could not get tagged value in display_name"); - return "".to_string(); + return None; }; let TaggedValue::String(display_name) = tagged_value else { log::error!("Tagged value should be String in display_name"); - return "".to_string(); + return None; }; - display_name.clone() + Some(display_name) } pub fn frontend_display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { @@ -1842,9 +1855,8 @@ impl NodeNetworkInterface { let mut import_export_ports = Ports::new(); - let viewport_top_right = network_metadata - .persistent_metadata - .navigation_metadata + let viewport_top_right = self + .navigation_metadata(network_path) .node_graph_to_viewport .inverse() .transform_point2(rounded_network_edge_distance.exports_to_edge_distance); @@ -1864,9 +1876,8 @@ 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 + let viewport_top_left = self + .navigation_metadata(network_path) .node_graph_to_viewport .inverse() .transform_point2(rounded_network_edge_distance.imports_to_edge_distance); @@ -2541,15 +2552,13 @@ impl NodeNetworkInterface { let import_exports_viewport_top_left = rounded_network_edge_distance.imports_to_edge_distance; let import_exports_viewport_bottom_right = rounded_network_edge_distance.exports_to_edge_distance; - let node_graph_top_left = network_metadata - .persistent_metadata - .navigation_metadata + 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 = network_metadata - .persistent_metadata - .navigation_metadata + let node_graph_bottom_right = self + .navigation_metadata(network_path) .node_graph_to_viewport .inverse() .transform_point2(import_exports_viewport_bottom_right); @@ -2597,22 +2606,18 @@ impl NodeNetworkInterface { 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) + 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 { - 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 point = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(click); + let point = self.navigation_metadata(network_path).node_graph_to_viewport.inverse().transform_point2(click); let nodes = network.nodes.keys().copied().collect::>(); let clicked_nodes = nodes .iter() @@ -2649,7 +2654,7 @@ impl NodeNetworkInterface { return None; }; - let point = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(click); + let point = self.navigation_metadata(network_path).node_graph_to_viewport.inverse().transform_point2(click); let node_ids: Vec<_> = network.nodes.keys().copied().collect(); node_ids @@ -2670,16 +2675,12 @@ 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 point = network_metadata.persistent_metadata.navigation_metadata.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() @@ -2703,16 +2704,12 @@ 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 point = self.navigation_metadata(network_path).node_graph_to_viewport.inverse().transform_point2(click); let nodes = network.nodes.keys().copied().collect::>(); nodes .iter() @@ -2773,15 +2770,11 @@ 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 node_graph_to_viewport = self.navigation_metadata(network_path).node_graph_to_viewport; selected_nodes .selected_nodes() .cloned() @@ -2797,13 +2790,8 @@ 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"); - 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(self.navigation_metadata(network_path).node_graph_to_viewport) } pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> (HashMap, HashMap) { @@ -2943,27 +2931,26 @@ 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_metadata(&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| { let mut current_path = path.clone(); 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(); + }; } } 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.navigation_metadata(network_path).node_graph_to_viewport = transform; self.unload_import_export_ports(network_path); } @@ -3468,8 +3455,27 @@ impl NodeNetworkInterface { 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)); + // 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); + } + } + + /// 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)); + // 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); + } } + /// 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) { let network = self.network_mut(&[]).unwrap(); log::debug!("Inserting metadata node with id {metadata_node_id}"); @@ -3667,15 +3673,11 @@ 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_previewing(network_path, Previewing::Yes { root_node_to_restore: None }); } 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) { @@ -3685,11 +3687,57 @@ 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"); + 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; }; - network_metadata.persistent_metadata.previewing = Previewing::No; + + 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) } /// Sets the root node only if a node is being previewed @@ -3709,7 +3757,7 @@ impl NodeNetworkInterface { // } pub fn set_display_name(&mut self, node_id: &NodeId, display_name: String, network_path: &[NodeId]) { - let network = self.network_mut(network_path).unwrap(); + let network = self.network_mut(&[]).unwrap(); let mut node_path = network_path.to_vec(); node_path.push(*node_id); @@ -3911,7 +3959,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; } @@ -3930,7 +3978,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 @@ -3940,7 +3988,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), }; } } @@ -3976,10 +4024,7 @@ 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_previewing(network_path, new_previewing_state); } /// Sets the position of a node to an absolute position @@ -4932,86 +4977,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)>, @@ -5097,30 +5062,6 @@ impl Ports { } } -#[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 { diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index ad8327c46b..5fb4b2f345 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; @@ -13,7 +13,7 @@ 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; diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index a68afe7469..41a56c3aec 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -5,12 +5,11 @@ 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::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/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/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 82b790d084..8a09aa019b 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::IVec2; +use glam::{DAffine2, DVec2, IVec2}; use log::Metadata; use rustc_hash::FxHashMap; use std::collections::hash_map::DefaultHasher; @@ -1356,6 +1356,162 @@ 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)] +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, 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 { + 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) + } +} + #[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..17b451a6df 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -178,6 +178,8 @@ tagged_value! { CentroidType(graphene_core::vector::misc::CentroidType), BooleanOperation(graphene_core::vector::misc::BooleanOperation), FontCache(Arc), + Previewing(crate::document::Previewing), + NavigationMetadata(crate::document::NavigationMetadata) } impl TaggedValue { @@ -297,4 +299,19 @@ 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 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); + } + } } From d2d451dd6f02bf7e5239fb7cad407be7813541a7 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 3 Sep 2024 21:13:52 -0700 Subject: [PATCH 06/10] Store navigation metadata in network --- .../messages/dialog/dialog_message_handler.rs | 5 +- .../document/document_message_handler.rs | 18 +- .../navigation/navigation_message_handler.rs | 119 ++---- .../node_graph/node_graph_message_handler.rs | 104 ++--- .../utility_types/document_metadata.rs | 2 +- .../utility_types/network_interface.rs | 371 +++++++++--------- .../network_metadata_interface.rs | 0 .../portfolio/portfolio_message_handler.rs | 5 +- libraries/bezier-rs/src/subpath/solvers.rs | 2 +- .../gcore/src/graphic_element/renderer.rs | 2 +- node-graph/graph-craft/src/document.rs | 30 +- node-graph/graph-craft/src/document/value.rs | 10 +- 12 files changed, 303 insertions(+), 365 deletions(-) create mode 100644 editor/src/messages/portfolio/document/utility_types/network_metadata_interface.rs 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) - } - } - - 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; }; @@ -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 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 metadata_node_id = Self::metadata_node_id(metadata, node_path); - 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"); + 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(display_name_input) = display_name_node.inputs.get_mut(0) else { - log::error!("Could not get display name input in set_display_name"); + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in set_metadata"); 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"); + 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; + } - if *current_display_name == display_name { - 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); + } + + 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); - *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); - } - } } From 73a30368c0003d7a299999ae34ece77b4d28cc3b Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 4 Sep 2024 00:23:06 -0700 Subject: [PATCH 07/10] Implement SelectedNodes as trait --- .../messages/dialog/dialog_message_handler.rs | 1 + .../document/document_message_handler.rs | 6 +- .../node_graph/node_graph_message_handler.rs | 26 +- .../document/overlays/utility_functions.rs | 1 + .../utility_types/network_interface.rs | 227 +++++++++++++----- .../portfolio/document/utility_types/nodes.rs | 99 +++++--- .../portfolio/portfolio_message_handler.rs | 1 + .../tool/common_functionality/pivot.rs | 1 + .../common_functionality/utility_functions.rs | 1 + .../tool/tool_messages/artboard_tool.rs | 1 + .../messages/tool/tool_messages/brush_tool.rs | 1 + .../tool/tool_messages/gradient_tool.rs | 1 + .../messages/tool/tool_messages/path_tool.rs | 1 + .../tool/tool_messages/select_tool.rs | 1 + .../messages/tool/tool_messages/text_tool.rs | 1 + .../transform_layer_message_handler.rs | 1 + node-graph/graph-craft/src/document/value.rs | 2 + 17 files changed, 259 insertions(+), 113 deletions(-) diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 96ebf2f0b3..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> { diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 3d49bc1b6f..48828ee878 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -3,7 +3,7 @@ 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, NodeNetworkPersistentMetadata, TransactionStatus}; -use super::utility_types::nodes::{CollapsedLayers, SelectedNodes}; +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; @@ -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, @@ -1507,7 +1507,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() 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 ad943ebb56..3cc7e94c98 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 @@ -7,6 +7,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions: 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, 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; @@ -1037,27 +1038,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); } @@ -1324,11 +1313,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); @@ -1343,6 +1328,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 => { 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/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 03edf9d606..93139bab30 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -127,20 +127,22 @@ 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()), ) } @@ -1079,6 +1081,18 @@ impl NodeNetworkInterface { 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 { self.node_metadata(node_id, network_path) .and_then(|node_metadata| node_metadata.persistent_metadata.reference.as_ref().map(|reference| reference.to_string())) @@ -1459,7 +1473,6 @@ 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(); @@ -1613,44 +1626,137 @@ 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 network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(MetadataType::SelectionUndoHistory, network_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; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionUndoHistory in set_metadata"); + 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 network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(MetadataType::SelectionUndoHistory, network_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; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionUndoHistory in set_metadata"); 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 network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(adding_to, network_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 selection_step"); + return; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in selection_step"); + return; + }; + let Some(mut value) = metadata_input.as_value_mut() 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 network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(removing_from, network_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 selection_step"); + return; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in selection_step"); + return; + }; + let Some(mut value) = metadata_input.as_value_mut() 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); } } @@ -3519,6 +3625,16 @@ impl NodeNetworkInterface { 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, + ); // TODO: Add the rest of the network metadata nodes for (node_id, node_metadata) in persistent_metadata.node_metadata { @@ -3635,11 +3751,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. @@ -3703,12 +3815,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; @@ -3717,7 +3824,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 @@ -4774,11 +4881,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 { @@ -4797,7 +4900,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 @@ -4836,17 +4939,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); } } @@ -5131,10 +5230,10 @@ 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, + 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 @@ -5159,7 +5258,6 @@ impl TransientMetadata { /// 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>, @@ -5422,6 +5520,17 @@ impl Default for NavigationMetadata { } } +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 c9d628e410..a1f5ecd95e 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -7,6 +7,7 @@ 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}; 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..1ce9fd2f3f 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -4,6 +4,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions: use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::FlowType; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graph_craft::document::value::TaggedValue; use graph_craft::document::NodeId; 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 c27114307b..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; 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 41a56c3aec..0b5158a487 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -5,6 +5,7 @@ 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::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}; 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/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 44f86c6eb8..d392f7235a 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -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::VecDeque; use std::fmt::Display; use std::hash::Hash; use std::marker::PhantomData; @@ -180,6 +181,7 @@ tagged_value! { FontCache(Arc), Previewing(crate::document::Previewing), PTZ(crate::document::PTZ), + SelectionHistory(VecDeque>), } impl TaggedValue { From de31dbfb19a672e97534cc2e732d75c0a2e10426 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 6 Sep 2024 16:27:22 -0700 Subject: [PATCH 08/10] Move all network metadata to node network --- .../document/document_message_handler.rs | 7 +- .../utility_types/network_interface.rs | 473 ++++++------------ .../gcore/src/graphic_element/renderer.rs | 6 +- node-graph/graph-craft/src/document.rs | 152 +++++- node-graph/graph-craft/src/document/value.rs | 108 +++- 5 files changed, 424 insertions(+), 322 deletions(-) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 48828ee878..0002268c69 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -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 }); @@ -1326,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() }; @@ -1979,7 +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.add_export(TaggedValue::ArtboardGroup(graphene_core::ArtboardGroup::EMPTY), -1, "".to_string(), &[]); 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/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 93139bab30..271ed7cbc4 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -7,8 +7,9 @@ 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, OutputConnector, Previewing, RootNode, PTZ}; +use graph_craft::document::{InputConnector, LayerOwner, NetworkEdgeDistance, 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}; @@ -24,6 +25,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 the document network as inputs to nodes. The node graph overlay nodes are added to this network. + metadata_network: NodeNetwork, // TODO: Remove and store in node network with IDs based on Self::metadata_node_id /// 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, @@ -39,8 +42,9 @@ 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(Hash)] +#[derive(Copy, Clone, Hash, Debug)] pub enum MetadataType { // Network persistent metadata Previewing, @@ -48,11 +52,11 @@ pub enum MetadataType { SelectionUndoHistory, SelectionRedoHistory, // Network transient metadata - SelectedNodes, StackDependents, AllNodesBoundingBox, OutwardWires, ImportExportPorts, + RoundedNetworkEdgeDistance, // Node persistent metadata Reference, DisplayName, @@ -69,7 +73,7 @@ pub enum MetadataType { IsLayer, } -#[derive(Hash)] +#[derive(Copy, Clone, Hash, Debug)] pub enum NavigationMetadataType { PTZ, NodeGraphToViewport, @@ -80,6 +84,7 @@ impl Clone for NodeNetworkInterface { fn clone(&self) -> Self { Self { network: self.network.clone(), + metadata_network: self.metadata_network.clone(), network_metadata: self.network_metadata.clone(), document_metadata: Default::default(), resolved_types: Default::default(), @@ -90,7 +95,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 } } @@ -101,6 +106,10 @@ impl NodeNetworkInterface { self.network.nested_network(network_path) } + pub fn metadata_network(&self, network_path: &[NodeId]) -> Option<&NodeNetwork> { + self.metadata_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) @@ -504,7 +513,7 @@ impl NodeNetworkInterface { 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 network = self.metadata_network(&[]).unwrap(); let Some(node) = network.nodes.get(&node_id) else { log::error!("Could not get node {node_id} in value_from_node_id"); return None; @@ -512,6 +521,21 @@ impl NodeNetworkInterface { 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 @@ -735,14 +759,13 @@ impl NodeNetworkInterface { pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Option> { self.import_export_ports(network_path).cloned().map(|import_export_ports| { 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()) + .and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.input_names.get(import_index).cloned()) .unwrap_or_default(); let mut import_metadata = None; @@ -751,14 +774,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"); @@ -785,15 +808,14 @@ impl NodeNetworkInterface { pub fn frontend_exports(&mut self, network_path: &[NodeId]) -> Option> { 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; }; @@ -817,7 +839,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)) @@ -833,7 +855,7 @@ impl NodeNetworkInterface { "Canvas".to_string() } else { self.encapsulating_node_metadata(network_path) - .and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.output_names.get(*export_index).cloned()) + .and_then(|encapsulating_metadata| encapsulating_metadata.persistent_metadata.output_names.get(export_index).cloned()) .unwrap_or_default() }; @@ -1517,8 +1539,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, + metadata_network, network_metadata, document_metadata: DocumentMetadata::default(), resolved_types: ResolvedDocumentNodeTypes::default(), @@ -1551,6 +1576,11 @@ impl NodeNetworkInterface { self.network.nested_network_mut(network_path) } + fn metadata_network_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetwork> { + self.metadata_network.nested_network_mut(network_path) + } + + // TODO: Remove fn network_metadata_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetworkMetadata> { self.network_metadata.nested_metadata_mut(network_path) } @@ -1568,15 +1598,15 @@ impl NodeNetworkInterface { } /// 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) - } + // 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> { @@ -1646,22 +1676,12 @@ impl NodeNetworkInterface { } } - let network = self.network_mut(&[]).unwrap(); - let metadata_node_id = Self::metadata_node_id(MetadataType::SelectionUndoHistory, network_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"); + 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 set_metadata"); + log::error!("Tagged value should be SelectionUndoHistory in mutate_selected_nodes"); return; }; selection_undo_history.push_back(last_selection_state); @@ -1683,22 +1703,12 @@ impl NodeNetworkInterface { self.mutate_selected_nodes(nodes, SelectionOperation::Set, network_path); } pub fn remove_selection_history_step(&mut self, network_path: &[NodeId]) { - let network = self.network_mut(&[]).unwrap(); - let metadata_node_id = Self::metadata_node_id(MetadataType::SelectionUndoHistory, network_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"); + 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 set_metadata"); + log::error!("Tagged value should be SelectionUndoHistory in remove_selection_history_step"); return; }; selection_undo_history.pop_back(); @@ -1718,17 +1728,7 @@ impl NodeNetworkInterface { SelectionDirection::Forward => (MetadataType::SelectionRedoHistory, MetadataType::SelectionUndoHistory), }; - let network = self.network_mut(&[]).unwrap(); - let metadata_node_id = Self::metadata_node_id(adding_to, network_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 selection_step"); - return; - }; - let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { - log::error!("Could not get metadata input in selection_step"); - return; - }; - let Some(mut value) = metadata_input.as_value_mut() else { + let Some(mut value) = self.metadata_value_mut(adding_to, network_path) else { log::error!("Could not get tagged value in selection_step"); return; }; @@ -1738,17 +1738,7 @@ impl NodeNetworkInterface { }; if let Some(selection_state) = selection_history.pop_back() { drop(value); - let network = self.network_mut(&[]).unwrap(); - let metadata_node_id = Self::metadata_node_id(removing_from, network_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 selection_step"); - return; - }; - let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { - log::error!("Could not get metadata input in selection_step"); - return; - }; - let Some(mut value) = metadata_input.as_value_mut() else { + let Some(mut value) = self.metadata_value_mut(removing_from, network_path) else { log::error!("Could not get tagged value in selection_step"); return; }; @@ -1766,23 +1756,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) @@ -1928,30 +1918,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; @@ -1961,22 +1941,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]) { @@ -2034,35 +2010,27 @@ 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"); - return; - }; - network_metadata.transient_metadata.import_export_ports.unload(); + 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(network_metadata) = self.network_metadata(network_path) else { + 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 !network_metadata.transient_metadata.rounded_network_edge_distance.is_loaded() { + if !rounded_network_edge_distance.is_loaded() { self.load_rounded_network_edge_distance(network_path); } - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in rounded_network_edge_distance"); - return None; - }; - let TransientMetadata::Loaded(rounded_network_edge_distance) = &network_metadata.transient_metadata.rounded_network_edge_distance else { + 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; }; @@ -2101,19 +2069,19 @@ impl NodeNetworkInterface { exports_to_edge_distance: rounded_viewport_exports_distance, imports_to_edge_distance: rounded_viewport_imports_distance, }; - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get current network in load_export_ports"); - return; - }; - network_metadata.transient_metadata.rounded_network_edge_distance = TransientMetadata::Loaded(network_edge_distance); + 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]) { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in unload_export_ports"); - return; - }; - network_metadata.transient_metadata.rounded_network_edge_distance.unload(); + self.set_metadata( + MetadataType::RoundedNetworkEdgeDistance, + TaggedValue::RoundedNetworkEdgeDistance(TransientMetadata::Unloaded), + network_path, + ); } fn owned_nodes(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&HashSet> { @@ -2128,31 +2096,28 @@ impl NodeNetworkInterface { } 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() @@ -2163,33 +2128,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; }; @@ -2240,17 +2201,11 @@ 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 { @@ -3164,9 +3119,11 @@ impl NodeNetworkInterface { 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); } // Update the click targets for the encapsulating node, if it exists. There is no encapsulating node if the network is the document network @@ -3216,9 +3173,11 @@ impl NodeNetworkInterface { } // 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 node_metadata.persistent_metadata.network_metadata.is_some() { + let mut internal_network_path = network_path.to_vec(); + internal_network_path.push(*node_id); + self.unload_import_export_ports(&internal_network_path); + self.unload_outward_wires(&internal_network_path); } // Update the click targets for the node @@ -3545,6 +3504,8 @@ 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, node_template.persistent_node_metadata.clone(), network_path); self.transaction_modified(); let Some(network_metadata) = self.network_metadata_mut(network_path) else { @@ -3635,7 +3596,15 @@ impl NodeNetworkInterface { TaggedValue::SelectionHistory(persistent_metadata.selection_redo_history), network_path, ); - // TODO: Add the rest of the network metadata nodes + 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); @@ -3645,8 +3614,8 @@ impl NodeNetworkInterface { /// 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}"); + 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 { @@ -3866,20 +3835,8 @@ impl NodeNetworkInterface { // } 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"); + 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; @@ -4487,16 +4444,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) { @@ -4592,11 +4555,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; }; @@ -4607,16 +4570,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 = { @@ -5085,91 +5052,6 @@ impl<'a> Iterator for FlowIter<'a> { } } -#[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.))) - } -} - /// 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 { @@ -5236,25 +5118,6 @@ pub struct NodeNetworkPersistentMetadata { 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(_)) - } -} - /// If some network calculation is too slow to compute for every usage, cache the data here #[derive(Debug, Default, Clone)] pub struct NodeNetworkTransientMetadata { @@ -5275,22 +5138,6 @@ pub struct NodeNetworkTransientMetadata { pub rounded_network_edge_distance: TransientMetadata, } -#[derive(Debug, Clone)] -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, -} - -#[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), -} - /// Utility function for providing a default boolean value to serde. #[inline(always)] fn return_true() -> bool { diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index 53a84dcdab..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,6 +38,10 @@ impl ClickTarget { &self.subpath } + pub fn stroke_width(&self) -> f64 { + self.stroke_width + } + pub fn bounding_box(&self) -> Option<[DVec2; 2]> { self.bounding_box } diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 5d45074222..4fecd60c4a 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -1,8 +1,10 @@ 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}; @@ -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>, @@ -1381,7 +1383,7 @@ impl RootNode { } /// 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)] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type, Hash, DynAny)] pub enum InputConnector { #[serde(rename = "node")] Node { @@ -1421,7 +1423,7 @@ impl InputConnector { } /// Represents an output connector -#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type, DynAny)] pub enum OutputConnector { #[serde(rename = "node")] Node { @@ -1484,6 +1486,150 @@ impl PTZ { } } +#[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, serde::Deserialize, 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 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, +} #[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 d392f7235a..7350108206 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,7 +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::VecDeque; +use std::collections::{HashMap, VecDeque}; use std::fmt::Display; use std::hash::Hash; use std::marker::PhantomData; @@ -179,9 +179,16 @@ 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), } impl TaggedValue { @@ -258,6 +265,10 @@ trait FakeHash { fn hash(&self, state: &mut H); } mod fake_hash { + use std::collections::HashMap; + + use crate::document::{InputConnector, OutputConnector, Ports, TransientMetadata}; + use super::*; impl FakeHash for f64 { fn hash(&self, state: &mut H) { @@ -274,6 +285,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 { @@ -284,6 +311,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); @@ -308,4 +345,71 @@ mod fake_hash { 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) + }); + } + } } From 0f18438243ca7436b53a66ff7fa5df6fa9e1ab3d Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 6 Sep 2024 23:18:04 -0700 Subject: [PATCH 09/10] Move all persistent node metadata into the network --- .../document/graph_operation/utility_types.rs | 7 +- .../node_graph/document_node_definitions.rs | 2 +- .../node_graph/node_graph_message_handler.rs | 55 +- .../document/node_graph/node_properties.rs | 10 +- .../utility_types/network_interface.rs | 664 +++++++++--------- .../portfolio/portfolio_message_handler.rs | 32 +- .../graph_modification_utils.rs | 16 +- .../messages/tool/tool_messages/brush_tool.rs | 6 +- frontend/wasm/src/editor_api.rs | 29 +- node-graph/graph-craft/src/document.rs | 102 ++- node-graph/graph-craft/src/document/value.rs | 4 + 11 files changed, 539 insertions(+), 388 deletions(-) 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 0d9f80943a..416d17b3b8 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -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/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_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 3cc7e94c98..b1ba0b5a28 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 @@ -1087,9 +1087,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); } } @@ -1182,12 +1184,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 }); @@ -1637,17 +1634,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, _)| { @@ -1657,9 +1656,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()); @@ -1727,16 +1724,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 { @@ -1776,9 +1765,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), @@ -1836,8 +1823,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/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 271ed7cbc4..aa73477dc0 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,10 @@ 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, LayerOwner, NetworkEdgeDistance, OutputConnector, Ports, Previewing, RootNode, TransientMetadata, PTZ}; +use graph_craft::document::{ + InputConnector, LayerMetadata, LayerOwner, LayerPersistentMetadata, LayerPosition, LayerTransientMetadata, NetworkEdgeDistance, NodePersistentMetadata, NodePosition, NodeTypePersistentMetadata, + OutputConnector, Ports, Previewing, RootNode, TransientMetadata, PTZ, +}; use graph_craft::{concrete, Type}; use graphene_std::memo::MemoHashGuard; use graphene_std::renderer::{ClickTarget, Quad}; @@ -80,6 +83,20 @@ pub enum NavigationMetadataType { 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 { @@ -230,20 +247,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 { @@ -385,7 +393,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), }; } @@ -410,7 +418,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"), }; } } @@ -420,7 +428,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) } } @@ -758,14 +766,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() .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; @@ -806,6 +823,15 @@ 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() @@ -854,8 +880,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() }; @@ -1115,15 +1142,20 @@ impl NodeNetworkInterface { Some(selection_undo_history) } - 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 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 mut node_id_path = network_path.to_vec(); - node_id_path.push(*node_id); - let Some(tagged_value) = self.metadata_value(MetadataType::DisplayName, &node_id_path) else { + 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; }; @@ -1139,12 +1171,12 @@ impl NodeNetworkInterface { 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") - .persistent_metadata - .is_layer(); - let reference = self.reference(node_id, network_path); + 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 display_name.is_empty() { if is_layer && is_merge_node { @@ -1157,12 +1189,40 @@ impl NodeNetworkInterface { } } + 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; }; - node_metadata.persistent_metadata.locked + let TaggedValue::Bool(locked) = tagged_value else { + log::error!("Tagged value should be Bool in is_locked for node {node_id}"); + return false; + }; + *locked } pub fn is_visible(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { @@ -1178,73 +1238,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 @@ -1384,12 +1456,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, }; }; @@ -1408,8 +1480,8 @@ impl NodeNetworkInterface { }; FlowIter { stack: node_ids, - network, - network_metadata, + network_interface: self, + network_path, flow_type, } } @@ -1499,6 +1571,7 @@ impl NodeNetworkInterface { 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; @@ -1509,9 +1582,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 { @@ -1850,15 +1925,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); } } } @@ -2085,11 +2160,11 @@ impl NodeNetworkInterface { } 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) @@ -2209,30 +2284,27 @@ impl NodeNetworkInterface { } 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; }; @@ -2256,39 +2328,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(); } } @@ -2321,10 +2383,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; @@ -2336,7 +2394,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() { @@ -2355,7 +2413,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; @@ -2448,13 +2506,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 @@ -2515,11 +2573,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)); @@ -2682,11 +2736,7 @@ 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 + 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 @@ -2714,17 +2764,7 @@ 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()) } @@ -3105,15 +3145,6 @@ 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); @@ -3124,6 +3155,19 @@ impl NodeNetworkInterface { 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 @@ -3161,23 +3205,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 node_metadata.persistent_metadata.network_metadata.is_some() { - let mut internal_network_path = network_path.to_vec(); - internal_network_path.push(*node_id); - self.unload_import_export_ports(&internal_network_path); - self.unload_outward_wires(&internal_network_path); + 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 @@ -3331,11 +3386,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(_) => { @@ -3347,11 +3402,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 { @@ -3560,7 +3615,36 @@ impl NodeNetworkInterface { 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); + // match persistent_metadata.node_type_metadata { + // NodeTypePersistentMetadata::Layer(layer_metadata) => { + // self.insert_node_metadata( + // MetadataType::NodeTypeMetadata(NodeTypeMetadata::Layer(LayerMetadataType::Position)), + // TaggedValue::LayerPosition(layer_metadata.persistent_metadata.position), + // &node_path, + // ); + // self.insert_node_metadata( + // MetadataType::NodeTypeMetadata(NodeTypeMetadata::Layer(LayerMetadataType::OwnedNodes)), + // TaggedValue::OwnedNodes(TransientMetadata::Unloaded), + // &node_path, + // ); + // } + // NodeTypePersistentMetadata::Node(node_metadata) => { + // self.insert_node_metadata( + // MetadataType::NodeTypeMetadata(NodeTypeMetadata::Node(NodeMetadataType::Position)), + // TaggedValue::NodePosition(node_metadata.position), + // &node_path, + // ); + // } + // } + // TODO: Add the rest of the node metadata nodes if let Some(nested_network) = persistent_metadata.network_metadata { @@ -3900,12 +3984,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(); } @@ -3954,34 +4043,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); @@ -4102,37 +4189,48 @@ impl NodeNetworkInterface { /// 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}"); @@ -4155,20 +4253,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); @@ -4283,13 +4387,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; + }; + 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 &mut node_to_shift_metadata.persistent_metadata.node_type_metadata { + 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; } } @@ -4335,12 +4443,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"); @@ -4680,15 +4788,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 { @@ -4705,13 +4818,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); @@ -4726,10 +4843,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); @@ -4747,10 +4870,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; } @@ -5023,18 +5147,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 @@ -5076,6 +5204,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); @@ -5088,6 +5217,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); @@ -5183,6 +5313,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. @@ -5207,87 +5338,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)] @@ -5301,23 +5356,6 @@ pub struct DocumentNodeClickTargets { pub node_type_click_targets: 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), diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index a1f5ecd95e..5c97049c64 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -415,15 +415,11 @@ impl MessageHandler> for PortfolioMes .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()) - { + 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); @@ -433,14 +429,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::>(); @@ -449,14 +444,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 { 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/tool_messages/brush_tool.rs b/editor/src/messages/tool/tool_messages/brush_tool.rs index 1ce9fd2f3f..a81fe71829 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -3,8 +3,8 @@ 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::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; 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; use graph_craft::document::NodeId; @@ -272,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/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index bcca50ea91..c1016fccfc 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, &[]); diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 4fecd60c4a..eac0cbabfc 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -12,7 +12,7 @@ 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; @@ -1630,6 +1630,106 @@ pub struct NetworkEdgeDistance { /// 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, +} + #[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 7350108206..02bd8a3554 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -189,6 +189,10 @@ tagged_value! { OutwardWires(TransientMetadata>>), ImportExportPorts(TransientMetadata), RoundedNetworkEdgeDistance(TransientMetadata), + // Persistent Node Metadata + OptionalString(Option), + VecString(Vec), + NodeTypeMetadata(crate::document::NodeTypePersistentMetadata), } impl TaggedValue { From 3428e1cedad3e8bda9b5a015781b4103b5b06cec Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 7 Sep 2024 18:33:47 -0700 Subject: [PATCH 10/10] Move all network metadata to the node network --- .../node_graph/node_graph_message_handler.rs | 21 +- .../utility_types/network_interface.rs | 452 ++++++++---------- .../network_metadata_interface.rs | 0 .../portfolio/portfolio_message_handler.rs | 13 +- frontend/wasm/src/editor_api.rs | 2 +- node-graph/graph-craft/src/document.rs | 46 +- node-graph/graph-craft/src/document/value.rs | 24 +- 7 files changed, 268 insertions(+), 290 deletions(-) delete mode 100644 editor/src/messages/portfolio/document/utility_types/network_metadata_interface.rs 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 b1ba0b5a28..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 @@ -12,7 +12,7 @@ use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers use crate::messages::prelude::*; use crate::messages::tool::common_functionality::auto_panning::AutoPanning; -use graph_craft::document::{DocumentNodeImplementation, InputConnector, NodeId, NodeInput, OutputConnector, Previewing}; +use graph_craft::document::{DocumentNodeImplementation, InputConnector, LayerClickTargetTypes, NodeId, NodeInput, OutputConnector, Previewing}; use graph_craft::proto::GraphErrors; use graphene_core::*; use renderer::{ClickTarget, Quad}; @@ -247,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; @@ -344,10 +344,7 @@ impl<'a> MessageHandler> for NodeGrap 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; } @@ -463,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; } @@ -1252,10 +1249,6 @@ 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"); - 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 PointerUp"); return; @@ -1282,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}"); 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 aa73477dc0..51cd2e4795 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -8,8 +8,8 @@ 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, LayerMetadata, LayerOwner, LayerPersistentMetadata, LayerPosition, LayerTransientMetadata, NetworkEdgeDistance, NodePersistentMetadata, NodePosition, NodeTypePersistentMetadata, - OutputConnector, Ports, Previewing, RootNode, TransientMetadata, PTZ, + 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; @@ -30,9 +30,6 @@ pub struct NodeNetworkInterface { network: NodeNetwork, /// 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: Remove and store in node network with IDs based on Self::metadata_node_id - /// 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, // 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)] @@ -70,8 +67,7 @@ pub enum MetadataType { NodeTypeMetadata, // Node transient metadata ClickTargets, - NodeTypeClickTargets, - // Derived metadata + // Derived metadata (might not be necessary) Position, IsLayer, } @@ -102,7 +98,6 @@ impl Clone for NodeNetworkInterface { Self { network: self.network.clone(), metadata_network: self.metadata_network.clone(), - network_metadata: self.network_metadata.clone(), document_metadata: Default::default(), resolved_types: Default::default(), transaction_status: TransactionStatus::Finished, @@ -123,27 +118,6 @@ impl NodeNetworkInterface { self.network.nested_network(network_path) } - pub fn metadata_network(&self, network_path: &[NodeId]) -> Option<&NodeNetwork> { - self.metadata_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 } @@ -172,17 +146,6 @@ impl NodeNetworkInterface { ) } - /// 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(); @@ -195,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; @@ -457,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 @@ -521,8 +578,7 @@ impl NodeNetworkInterface { fn metadata_value(&self, metadata: MetadataType, node_path: &[NodeId]) -> Option<&TaggedValue> { let node_id = Self::metadata_node_id(metadata, node_path); - let network = self.metadata_network(&[]).unwrap(); - let Some(node) = network.nodes.get(&node_id) else { + 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; }; @@ -1619,7 +1675,6 @@ impl NodeNetworkInterface { Self { network: node_network, metadata_network, - network_metadata, document_metadata: DocumentMetadata::default(), resolved_types: ResolvedDocumentNodeTypes::default(), transaction_status: TransactionStatus::Finished, @@ -1654,64 +1709,6 @@ impl NodeNetworkInterface { fn metadata_network_mut(&mut self, network_path: &[NodeId]) -> Option<&mut NodeNetwork> { self.metadata_network.nested_network_mut(network_path) } - - // TODO: Remove - 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) - } } // Public mutable getters for data that involves transient network metadata @@ -2360,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]) { @@ -2477,11 +2485,47 @@ impl NodeNetworkInterface { } }; - 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]> { @@ -2586,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 @@ -2650,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(); @@ -2933,15 +2941,14 @@ impl NodeNetworkInterface { } 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 @@ -3069,10 +3076,10 @@ 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(&path) else { + let Some(self_network_metadata) = self.network(&path) else { continue; }; - 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 @@ -3171,9 +3178,10 @@ impl NodeNetworkInterface { } // 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) { @@ -3559,19 +3567,8 @@ 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, node_template.persistent_node_metadata.clone(), network_path); + 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); @@ -3594,20 +3591,9 @@ 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, node_template.persistent_node_metadata.clone(), network_path); + 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) } @@ -3623,30 +3609,7 @@ impl NodeNetworkInterface { 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); - // match persistent_metadata.node_type_metadata { - // NodeTypePersistentMetadata::Layer(layer_metadata) => { - // self.insert_node_metadata( - // MetadataType::NodeTypeMetadata(NodeTypeMetadata::Layer(LayerMetadataType::Position)), - // TaggedValue::LayerPosition(layer_metadata.persistent_metadata.position), - // &node_path, - // ); - // self.insert_node_metadata( - // MetadataType::NodeTypeMetadata(NodeTypeMetadata::Layer(LayerMetadataType::OwnedNodes)), - // TaggedValue::OwnedNodes(TransientMetadata::Unloaded), - // &node_path, - // ); - // } - // NodeTypePersistentMetadata::Node(node_metadata) => { - // self.insert_node_metadata( - // MetadataType::NodeTypeMetadata(NodeTypeMetadata::Node(NodeMetadataType::Position)), - // TaggedValue::NodePosition(node_metadata.position), - // &node_path, - // ); - // } - // } - - // TODO: Add the rest of the node metadata nodes - + 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); } @@ -3792,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); } @@ -5345,40 +5303,6 @@ pub struct DocumentNodeTransientMetadata { pub click_targets: TransientMetadata, } -#[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_click_targets: NodeTypeClickTargets, -} - -#[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)] pub struct NavigationMetadata { /// The current pan, and zoom state of the viewport's view of the node graph. 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 deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 5c97049c64..918d75ca88 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -405,16 +405,7 @@ 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::>() - { + 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; @@ -433,7 +424,7 @@ impl MessageHandler> for PortfolioMes .unwrap() .nodes .keys() - .any(|node_id|*node_id == NodeId(0) && document.network_interface.reference(node_id, &[]).cloned().flatten().is_some_and(|reference| reference == "Output")) + .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)], false, &[]); } diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index c1016fccfc..0ed907df61 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -848,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/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index eac0cbabfc..98960ea99a 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -1590,7 +1590,7 @@ impl Ports { } /// 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, serde::Deserialize, Hash)] +#[derive(Debug, Default, Clone, PartialEq, Hash)] pub enum TransientMetadata { Loaded(T), #[default] @@ -1612,6 +1612,16 @@ impl Serialize for TransientMetadata { } } +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) { @@ -1730,6 +1740,40 @@ pub struct NodePersistentMetadata { 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 02bd8a3554..c9ae5cac58 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -193,6 +193,8 @@ tagged_value! { OptionalString(Option), VecString(Vec), NodeTypeMetadata(crate::document::NodeTypePersistentMetadata), + // Transient Node Metadata + ClickTargets(TransientMetadata), } impl TaggedValue { @@ -271,7 +273,7 @@ trait FakeHash { mod fake_hash { use std::collections::HashMap; - use crate::document::{InputConnector, OutputConnector, Ports, TransientMetadata}; + use crate::document::{DocumentNodeClickTargets, InputConnector, OutputConnector, Ports, TransientMetadata}; use super::*; impl FakeHash for f64 { @@ -416,4 +418,24 @@ mod fake_hash { }); } } + 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), + } + } + } }