From 1190e823226b46a9a1b71d6e7170011d223e37c0 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Thu, 6 Mar 2025 16:42:58 +0100 Subject: [PATCH] Fix document upgrades to work recursively within subgraph nodes (#2369) * Apply updates to the call argument recursively to subnetworks * Fetch recursive metadata * Apply replacements recursively --- .../portfolio/portfolio_message_handler.rs | 153 ++++++++++-------- 1 file changed, 85 insertions(+), 68 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index d7aff75c..4c1e1cc0 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -497,24 +497,25 @@ impl MessageHandler> for PortfolioMes ("graphene_std::executor::BlendGpuImageNode", "graphene_std::gpu_nodes::BlendGpuImageNode"), ("graphene_std::raster::SampleNode", "graphene_std::raster::SampleImageNode"), ]; - for node_id in &document - .network_interface - .network_metadata(&[]) - .unwrap() - .persistent_metadata - .node_metadata - .keys() - .cloned() - .collect::>() - { - if let Some(DocumentNodeImplementation::ProtoNode(protonode_id)) = document.network_interface.network(&[]).unwrap().nodes.get(node_id).map(|node| node.implementation.clone()) { + let mut network = document.network_interface.network(&[]).unwrap().clone(); + network.generate_node_paths(&[]); + + let node_ids: Vec<_> = network.recursive_nodes().map(|(&id, node)| (id, node.original_location.path.clone().unwrap())).collect(); + + // Apply upgrades to each node + for (node_id, path) in &node_ids { + let network_path: Vec<_> = path.iter().copied().take(path.len() - 1).collect(); + + if let Some(DocumentNodeImplementation::ProtoNode(protonode_id)) = + document.network_interface.network(&network_path).unwrap().nodes.get(node_id).map(|node| node.implementation.clone()) + { for (old, new) in REPLACEMENTS { let node_path_without_type_args = protonode_id.name.split('<').next(); if node_path_without_type_args == Some(old) { document .network_interface - .replace_implementation(node_id, &[], DocumentNodeImplementation::ProtoNode(new.to_string().into())); - document.network_interface.set_manual_compostion(node_id, &[], Some(graph_craft::Type::Generic("T".into()))); + .replace_implementation(node_id, &network_path, DocumentNodeImplementation::ProtoNode(new.to_string().into())); + document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into()))); } } } @@ -566,9 +567,29 @@ impl MessageHandler> for PortfolioMes document.network_interface.delete_nodes(vec![NodeId(0)], true, &[]); } - let node_ids = document.network_interface.network(&[]).unwrap().nodes.keys().cloned().collect::>(); - for node_id in &node_ids { - let Some(node_metadata) = document.network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata.get(node_id) else { + let mut network = document.network_interface.network(&[]).unwrap().clone(); + network.generate_node_paths(&[]); + + let node_ids: Vec<_> = network.recursive_nodes().map(|(&id, node)| (id, node.original_location.path.clone().unwrap())).collect(); + + // Apply upgrades to each node + for (node_id, path) in &node_ids { + let network_path: Vec<_> = path.iter().copied().take(path.len() - 1).collect(); + let network_path = &network_path; + + let Some(node) = document.network_interface.network(network_path).unwrap().nodes.get(node_id).cloned() else { + log::error!("could not get node in deserialize_document"); + continue; + }; + + // Upgrade old nodes to use `Context` instead of `()` or `Footprint` for manual composition + if node.manual_composition == Some(graph_craft::concrete!(())) || node.manual_composition == Some(graph_craft::concrete!(graphene_std::transform::Footprint)) { + document + .network_interface + .set_manual_compostion(node_id, network_path, graph_craft::concrete!(graphene_std::Context).into()); + } + + let Some(node_metadata) = document.network_interface.network_metadata(network_path).unwrap().persistent_metadata.node_metadata.get(node_id) else { log::error!("could not get node metadata for node {node_id} in deserialize_document"); continue; }; @@ -578,26 +599,17 @@ impl MessageHandler> for PortfolioMes continue; }; - let Some(node) = document.network_interface.network(&[]).unwrap().nodes.get(node_id) else { - log::error!("could not get node in deserialize_document"); - continue; - }; let inputs_count = node.inputs.len(); - // Upgrade old nodes to use `Context` instead of `()` or `Footprint` for manual composition - if node.manual_composition == Some(graph_craft::concrete!(())) || node.manual_composition == Some(graph_craft::concrete!(graphene_std::transform::Footprint)) { - document.network_interface.set_manual_compostion(node_id, &[], graph_craft::concrete!(graphene_std::Context).into()); - } - // Upgrade Fill nodes to the format change in #1778 if reference == "Fill" && inputs_count == 8 { let node_definition = resolve_document_node_type(reference).unwrap(); let document_node = node_definition.default_node_template().document_node; - document.network_interface.replace_implementation(node_id, &[], document_node.implementation.clone()); + document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone()); - let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), &[]); + let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path); - document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), &[]); + document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path); let Some(fill_type) = old_inputs[1].as_value().cloned() else { continue }; let TaggedValue::FillType(fill_type) = fill_type else { continue }; @@ -627,41 +639,44 @@ impl MessageHandler> for PortfolioMes }; document .network_interface - .set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::Fill(fill.clone()), false), &[]); + .set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::Fill(fill.clone()), false), network_path); match fill { Fill::None => { document .network_interface - .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::OptionalColor(None), false), &[]); + .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::OptionalColor(None), false), network_path); } Fill::Solid(color) => { document .network_interface - .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::OptionalColor(Some(color)), false), &[]); + .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::OptionalColor(Some(color)), false), network_path); } Fill::Gradient(gradient) => { document .network_interface - .set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Gradient(gradient), false), &[]); + .set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Gradient(gradient), false), network_path); } } } // Rename the old "Splines from Points" node to "Spline" and upgrade it to the new "Spline" node if reference == "Splines from Points" { - document.network_interface.set_reference(node_id, &[], Some("Spline".to_string())); + document.network_interface.set_reference(node_id, network_path, Some("Spline".to_string())); } // Upgrade the old "Spline" node to the new "Spline" node if reference == "Spline" { // Retrieve the proto node identifier and verify it is the old "Spline" node, otherwise skip it if this is the new "Spline" node - let identifier = document.network_interface.implementation(node_id, &[]).and_then(|implementation| implementation.get_proto_node()); + let identifier = document + .network_interface + .implementation(node_id, network_path) + .and_then(|implementation| implementation.get_proto_node()); if identifier.map(|identifier| &identifier.name) != Some(&"graphene_core::vector::generator_nodes::SplineNode".into()) { continue; } // Obtain the document node for the given node ID, extract the vector points, and create vector data from the list of points - let node = document.network_interface.document_node(node_id, &[]).unwrap(); + let node = document.network_interface.document_node(node_id, network_path).unwrap(); let Some(TaggedValue::VecDVec2(points)) = node.inputs.get(1).and_then(|tagged_value| tagged_value.as_value()) else { log::error!("The old Spline node's input at index 1 is not a TaggedValue::VecDVec2"); continue; @@ -671,14 +686,14 @@ impl MessageHandler> for PortfolioMes // Retrieve the output connectors linked to the "Spline" node's output port let spline_outputs = document .network_interface - .outward_wires(&[]) + .outward_wires(network_path) .unwrap() .get(&OutputConnector::node(*node_id, 0)) .expect("Vec of InputConnector Spline node is connected to its output port 0.") .clone(); // Get the node's current position in the graph - let Some(node_position) = document.network_interface.position(node_id, &[]) else { + let Some(node_position) = document.network_interface.position(node_id, network_path) else { log::error!("Could not get position of spline node."); continue; }; @@ -701,20 +716,20 @@ impl MessageHandler> for PortfolioMes let new_path_id = *new_ids.get(&NodeId(1)).unwrap(); // Remove the old "Spline" node from the document - document.network_interface.delete_nodes(vec![*node_id], false, &[]); + document.network_interface.delete_nodes(vec![*node_id], false, network_path); // Insert the new "Path" and "Spline" nodes into the network interface with generated IDs - document.network_interface.insert_node_group(nodes.clone(), new_ids, &[]); + document.network_interface.insert_node_group(nodes.clone(), new_ids, network_path); // Reposition the new "Spline" node to match the original "Spline" node's position - document.network_interface.shift_node(&new_spline_id, node_position, &[]); + document.network_interface.shift_node(&new_spline_id, node_position, network_path); // Reposition the new "Path" node with an offset relative to the original "Spline" node's position - document.network_interface.shift_node(&new_path_id, node_position + IVec2::new(-7, 0), &[]); + document.network_interface.shift_node(&new_path_id, node_position + IVec2::new(-7, 0), network_path); // Redirect each output connection from the old node to the new "Spline" node's output port for input_connector in spline_outputs { - document.network_interface.set_input(&input_connector, NodeInput::node(new_spline_id, 0), &[]); + document.network_interface.set_input(&input_connector, NodeInput::node(new_spline_id, 0), network_path); } } @@ -722,14 +737,14 @@ impl MessageHandler> for PortfolioMes if reference == "Text" && inputs_count != 8 { let node_definition = resolve_document_node_type(reference).unwrap(); let document_node = node_definition.default_node_template().document_node; - document.network_interface.replace_implementation(node_id, &[], document_node.implementation.clone()); + document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone()); - let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), &[]); + let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path); - document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), &[]); - document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), &[]); - document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), &[]); - document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), &[]); + document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path); + document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path); + document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path); + document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), network_path); document.network_interface.set_input( &InputConnector::node(*node_id, 4), if inputs_count == 6 { @@ -737,7 +752,7 @@ impl MessageHandler> for PortfolioMes } else { NodeInput::value(TaggedValue::F64(TypesettingConfig::default().line_height_ratio), false) }, - &[], + network_path, ); document.network_interface.set_input( &InputConnector::node(*node_id, 5), @@ -746,17 +761,17 @@ impl MessageHandler> for PortfolioMes } else { NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false) }, - &[], + network_path, ); document.network_interface.set_input( &InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false), - &[], + network_path, ); document.network_interface.set_input( &InputConnector::node(*node_id, 7), NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false), - &[], + network_path, ); } @@ -764,34 +779,34 @@ impl MessageHandler> for PortfolioMes if (reference == "Sine" || reference == "Cosine" || reference == "Tangent") && inputs_count == 1 { let node_definition = resolve_document_node_type(reference).unwrap(); let document_node = node_definition.default_node_template().document_node; - document.network_interface.replace_implementation(node_id, &[], document_node.implementation.clone()); + document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone()); - let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), &[]); + let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path); - document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), &[]); + document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path); document .network_interface - .set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::Bool(true), false), &[]); + .set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::Bool(true), false), network_path); } // Upgrade the Modulo node to include a boolean input for whether the output should be always positive, which was previously not an option if reference == "Modulo" && inputs_count == 2 { let node_definition = resolve_document_node_type(reference).unwrap(); let document_node = node_definition.default_node_template().document_node; - document.network_interface.replace_implementation(node_id, &[], document_node.implementation.clone()); + document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone()); - let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), &[]); + let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path); - document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), &[]); - document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), &[]); + document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path); + document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path); document .network_interface - .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), &[]); + .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), network_path); } // Upgrade artboard name being passed as hidden value input to "To Artboard" if reference == "Artboard" && upgrade_from_before_returning_nested_click_targets { - let label = document.network_interface.frontend_display_name(node_id, &[]); + let label = document.network_interface.frontend_display_name(node_id, network_path); document .network_interface .set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]); @@ -800,25 +815,27 @@ impl MessageHandler> for PortfolioMes if reference == "Image" && inputs_count == 1 { let node_definition = crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type(reference).unwrap(); let new_image_node = node_definition.default_node_template(); - document.network_interface.replace_implementation(node_id, &[], new_image_node.document_node.implementation); + document.network_interface.replace_implementation(node_id, network_path, new_image_node.document_node.implementation); // Insert a new empty input for the image document.network_interface.add_import(TaggedValue::None, false, 0, "Empty", &[*node_id]); - document.network_interface.set_reference(node_id, &[], Some("Image".to_string())); + document.network_interface.set_reference(node_id, network_path, Some("Image".to_string())); } if reference == "Noise Pattern" && inputs_count == 15 { let node_definition = crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type(reference).unwrap(); let new_noise_pattern_node = node_definition.default_node_template(); - document.network_interface.replace_implementation(node_id, &[], new_noise_pattern_node.document_node.implementation); + document + .network_interface + .replace_implementation(node_id, network_path, new_noise_pattern_node.document_node.implementation); - let old_inputs = document.network_interface.replace_inputs(node_id, new_noise_pattern_node.document_node.inputs.clone(), &[]); + let old_inputs = document.network_interface.replace_inputs(node_id, new_noise_pattern_node.document_node.inputs.clone(), network_path); document .network_interface - .set_input(&InputConnector::node(*node_id, 0), NodeInput::value(TaggedValue::None, false), &[]); + .set_input(&InputConnector::node(*node_id, 0), NodeInput::value(TaggedValue::None, false), network_path); for (i, input) in old_inputs.iter().enumerate() { - document.network_interface.set_input(&InputConnector::node(*node_id, i + 1), input.clone(), &[]); + document.network_interface.set_input(&InputConnector::node(*node_id, i + 1), input.clone(), network_path); } } }