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
This commit is contained in:
Dennis Kobert 2025-03-06 16:42:58 +01:00 committed by GitHub
parent 19a140682e
commit 1190e82322
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 85 additions and 68 deletions

View File

@ -497,24 +497,25 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
("graphene_std::executor::BlendGpuImageNode", "graphene_std::gpu_nodes::BlendGpuImageNode"), ("graphene_std::executor::BlendGpuImageNode", "graphene_std::gpu_nodes::BlendGpuImageNode"),
("graphene_std::raster::SampleNode", "graphene_std::raster::SampleImageNode"), ("graphene_std::raster::SampleNode", "graphene_std::raster::SampleImageNode"),
]; ];
for node_id in &document let mut network = document.network_interface.network(&[]).unwrap().clone();
.network_interface network.generate_node_paths(&[]);
.network_metadata(&[])
.unwrap() let node_ids: Vec<_> = network.recursive_nodes().map(|(&id, node)| (id, node.original_location.path.clone().unwrap())).collect();
.persistent_metadata
.node_metadata // Apply upgrades to each node
.keys() for (node_id, path) in &node_ids {
.cloned() let network_path: Vec<_> = path.iter().copied().take(path.len() - 1).collect();
.collect::<Vec<NodeId>>()
{ if let Some(DocumentNodeImplementation::ProtoNode(protonode_id)) =
if let Some(DocumentNodeImplementation::ProtoNode(protonode_id)) = document.network_interface.network(&[]).unwrap().nodes.get(node_id).map(|node| node.implementation.clone()) { document.network_interface.network(&network_path).unwrap().nodes.get(node_id).map(|node| node.implementation.clone())
{
for (old, new) in REPLACEMENTS { for (old, new) in REPLACEMENTS {
let node_path_without_type_args = protonode_id.name.split('<').next(); let node_path_without_type_args = protonode_id.name.split('<').next();
if node_path_without_type_args == Some(old) { if node_path_without_type_args == Some(old) {
document document
.network_interface .network_interface
.replace_implementation(node_id, &[], DocumentNodeImplementation::ProtoNode(new.to_string().into())); .replace_implementation(node_id, &network_path, DocumentNodeImplementation::ProtoNode(new.to_string().into()));
document.network_interface.set_manual_compostion(node_id, &[], Some(graph_craft::Type::Generic("T".into()))); document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
} }
} }
} }
@ -566,9 +567,29 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
document.network_interface.delete_nodes(vec![NodeId(0)], true, &[]); document.network_interface.delete_nodes(vec![NodeId(0)], true, &[]);
} }
let node_ids = document.network_interface.network(&[]).unwrap().nodes.keys().cloned().collect::<Vec<_>>(); let mut network = document.network_interface.network(&[]).unwrap().clone();
for node_id in &node_ids { network.generate_node_paths(&[]);
let Some(node_metadata) = document.network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata.get(node_id) else {
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"); log::error!("could not get node metadata for node {node_id} in deserialize_document");
continue; continue;
}; };
@ -578,26 +599,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
continue; 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(); 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 // Upgrade Fill nodes to the format change in #1778
if reference == "Fill" && inputs_count == 8 { if reference == "Fill" && inputs_count == 8 {
let node_definition = resolve_document_node_type(reference).unwrap(); let node_definition = resolve_document_node_type(reference).unwrap();
let document_node = node_definition.default_node_template().document_node; 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 Some(fill_type) = old_inputs[1].as_value().cloned() else { continue };
let TaggedValue::FillType(fill_type) = fill_type else { continue }; let TaggedValue::FillType(fill_type) = fill_type else { continue };
@ -627,41 +639,44 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
}; };
document document
.network_interface .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 { match fill {
Fill::None => { Fill::None => {
document document
.network_interface .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) => { Fill::Solid(color) => {
document document
.network_interface .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) => { Fill::Gradient(gradient) => {
document document
.network_interface .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 // Rename the old "Splines from Points" node to "Spline" and upgrade it to the new "Spline" node
if reference == "Splines from Points" { 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 // Upgrade the old "Spline" node to the new "Spline" node
if reference == "Spline" { 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 // 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()) { if identifier.map(|identifier| &identifier.name) != Some(&"graphene_core::vector::generator_nodes::SplineNode".into()) {
continue; continue;
} }
// Obtain the document node for the given node ID, extract the vector points, and create vector data from the list of points // 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 { 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"); log::error!("The old Spline node's input at index 1 is not a TaggedValue::VecDVec2");
continue; continue;
@ -671,14 +686,14 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
// Retrieve the output connectors linked to the "Spline" node's output port // Retrieve the output connectors linked to the "Spline" node's output port
let spline_outputs = document let spline_outputs = document
.network_interface .network_interface
.outward_wires(&[]) .outward_wires(network_path)
.unwrap() .unwrap()
.get(&OutputConnector::node(*node_id, 0)) .get(&OutputConnector::node(*node_id, 0))
.expect("Vec of InputConnector Spline node is connected to its output port 0.") .expect("Vec of InputConnector Spline node is connected to its output port 0.")
.clone(); .clone();
// Get the node's current position in the graph // 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."); log::error!("Could not get position of spline node.");
continue; continue;
}; };
@ -701,20 +716,20 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
let new_path_id = *new_ids.get(&NodeId(1)).unwrap(); let new_path_id = *new_ids.get(&NodeId(1)).unwrap();
// Remove the old "Spline" node from the document // 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 // 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 // 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 // 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 // Redirect each output connection from the old node to the new "Spline" node's output port
for input_connector in spline_outputs { 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<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
if reference == "Text" && inputs_count != 8 { if reference == "Text" && inputs_count != 8 {
let node_definition = resolve_document_node_type(reference).unwrap(); let node_definition = resolve_document_node_type(reference).unwrap();
let document_node = node_definition.default_node_template().document_node; 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), old_inputs[1].clone(), &[]); 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(), &[]); 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(), &[]); document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), network_path);
document.network_interface.set_input( document.network_interface.set_input(
&InputConnector::node(*node_id, 4), &InputConnector::node(*node_id, 4),
if inputs_count == 6 { if inputs_count == 6 {
@ -737,7 +752,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
} else { } else {
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().line_height_ratio), false) NodeInput::value(TaggedValue::F64(TypesettingConfig::default().line_height_ratio), false)
}, },
&[], network_path,
); );
document.network_interface.set_input( document.network_interface.set_input(
&InputConnector::node(*node_id, 5), &InputConnector::node(*node_id, 5),
@ -746,17 +761,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
} else { } else {
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false) NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false)
}, },
&[], network_path,
); );
document.network_interface.set_input( document.network_interface.set_input(
&InputConnector::node(*node_id, 6), &InputConnector::node(*node_id, 6),
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false), NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false),
&[], network_path,
); );
document.network_interface.set_input( document.network_interface.set_input(
&InputConnector::node(*node_id, 7), &InputConnector::node(*node_id, 7),
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false), NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false),
&[], network_path,
); );
} }
@ -764,34 +779,34 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
if (reference == "Sine" || reference == "Cosine" || reference == "Tangent") && inputs_count == 1 { if (reference == "Sine" || reference == "Cosine" || reference == "Tangent") && inputs_count == 1 {
let node_definition = resolve_document_node_type(reference).unwrap(); let node_definition = resolve_document_node_type(reference).unwrap();
let document_node = node_definition.default_node_template().document_node; 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 document
.network_interface .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 // 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 { if reference == "Modulo" && inputs_count == 2 {
let node_definition = resolve_document_node_type(reference).unwrap(); let node_definition = resolve_document_node_type(reference).unwrap();
let document_node = node_definition.default_node_template().document_node; 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), old_inputs[1].clone(), &[]); document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
document document
.network_interface .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" // Upgrade artboard name being passed as hidden value input to "To Artboard"
if reference == "Artboard" && upgrade_from_before_returning_nested_click_targets { 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 document
.network_interface .network_interface
.set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]); .set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]);
@ -800,25 +815,27 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
if reference == "Image" && inputs_count == 1 { 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 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(); 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 // Insert a new empty input for the image
document.network_interface.add_import(TaggedValue::None, false, 0, "Empty", &[*node_id]); 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 { 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 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(); 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 document
.network_interface .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() { 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);
} }
} }
} }