Old value node migrations

This commit is contained in:
Adam
2025-07-26 13:58:41 -07:00
parent 54097ebcb5
commit e0f317e67e
9 changed files with 258 additions and 137 deletions
@@ -104,7 +104,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeDefinition {
identifier: "Value",
category: "General",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
@@ -113,12 +113,164 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("", "Value").into()],
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Returns the value stored in its input"),
description: Cow::Borrowed("Construct any value using the dropdown menu."),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "Number Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::F64(0.), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Constructs a number which can be set to any real number"),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "Percentage Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::Percentage(0.), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Constructs a decimal value between 0 and 1."),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "Number Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::U32(0), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Constructs a positive integer value."),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "Bool Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::Bool(true), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Constructs a value which can be true or false"),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "String Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::Percentage(0.), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Constructs a string value which can be set to any plain text."),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "Coordinate Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::DVec2(DVec2::new(0., 0.)), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Constructs a string value which can be set to any plain text."),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "Color Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::OptionalColor(None), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Constructs a color value which may to set to any color, or no color"),
properties: Some("value_properties"),
},
DocumentNodeDefinition {
identifier: "Gradient Value",
category: "Value",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
manual_composition: Some(generic!(T)),
inputs: vec![NodeInput::value(TaggedValue::OptionalColor(None), false)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Value", "").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed(" Constructs a gradient value which may be set to any sequence of color stops to represent the transition between colors."),
properties: Some("value_properties"),
},
// TODO: Auto-generate this from its proto node macro
@@ -936,7 +1088,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
},
description: Cow::Borrowed(
"Decomposes the X and Y components of a 2D coordinate.\n\nThe inverse of this node is \"Coordinate Value\", which can have either or both its X and Y exposed as graph inputs.",
"Decomposes the X and Y components of a 2D coordinate.\n\nThe inverse of this node is \"Coordinate from Numbers\", which can have either or both its X and Y exposed as graph inputs.",
),
properties: None,
},
@@ -256,7 +256,6 @@ pub(crate) fn property_from_type(
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, exposable, context),
Type::Future(out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, exposable, context),
};
extra_widgets.push(widgets);
Ok(extra_widgets)
@@ -840,6 +839,17 @@ pub fn number_widget(parameter_widgets_info: ParameterWidgetsInfo, number_props:
.on_commit(commit_value)
.widget_holder(),
]),
Some(&TaggedValue::Percentage(x)) => widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
number_props
.percentage()
.min(0.)
.max(100.)
.value(Some(x))
.on_update(update_value(move |x: &NumberInput| TaggedValue::Percentage(x.value.unwrap()), node_id, index))
.on_commit(commit_value)
.widget_holder(),
]),
Some(&TaggedValue::U32(x)) => widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
number_props
@@ -4087,20 +4087,32 @@ impl NodeNetworkInterface {
self.unload_stack_dependents(network_path);
}
/// Replaces the implementation and corresponding metadata.
pub fn replace_implementation(&mut self, node_id: &NodeId, network_path: &[NodeId], new_template: &mut NodeTemplate) {
pub fn set_implementation(&mut self, node_id: &NodeId, network_path: &[NodeId], implementation: DocumentNodeImplementation) {
let Some(network) = self.network_mut(network_path) else {
log::error!("Could not get nested network in set_implementation");
log::error!("Could not get nested network in replace_implementation");
return;
};
let Some(node) = network.nodes.get_mut(node_id) else {
log::error!("Could not get node in set_implementation");
log::error!("Could not get node in replace_implementation");
return;
};
node.implementation = implementation;
}
/// Replaces the implementation and corresponding metadata.
pub fn replace_implementation(&mut self, node_id: &NodeId, network_path: &[NodeId], new_template: &mut NodeTemplate) {
let Some(network) = self.network_mut(network_path) else {
log::error!("Could not get nested network in replace_implementation");
return;
};
let Some(node) = network.nodes.get_mut(node_id) else {
log::error!("Could not get node in replace_implementation");
return;
};
let new_implementation = std::mem::take(&mut new_template.document_node.implementation);
let _ = std::mem::replace(&mut node.implementation, new_implementation);
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get metadata in set_implementation");
log::error!("Could not get metadata in replace_implementation");
return;
};
let new_metadata = std::mem::take(&mut new_template.persistent_node_metadata.network_metadata);
@@ -4110,17 +4122,17 @@ impl NodeNetworkInterface {
/// Replaces the inputs and corresponding metadata.
pub fn replace_inputs(&mut self, node_id: &NodeId, network_path: &[NodeId], new_template: &mut NodeTemplate) -> Option<Vec<NodeInput>> {
let Some(network) = self.network_mut(network_path) else {
log::error!("Could not get nested network in set_implementation");
log::error!("Could not get nested network in replace_inputs");
return None;
};
let Some(node) = network.nodes.get_mut(node_id) else {
log::error!("Could not get node in set_implementation");
log::error!("Could not get node in replace_inputs");
return None;
};
let new_inputs = std::mem::take(&mut new_template.document_node.inputs);
let old_inputs = std::mem::replace(&mut node.inputs, new_inputs);
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get metadata in set_implementation");
log::error!("Could not get metadata in replace_inputs");
return None;
};
let new_metadata = std::mem::take(&mut new_template.persistent_node_metadata.input_metadata);
@@ -4177,13 +4189,13 @@ impl NodeNetworkInterface {
}
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts
pub fn set_manual_compostion(&mut self, node_id: &NodeId, network_path: &[NodeId], manual_composition: Option<Type>) {
pub fn set_manual_composition(&mut self, node_id: &NodeId, network_path: &[NodeId], manual_composition: Option<Type>) {
let Some(network) = self.network_mut(network_path) else {
log::error!("Could not get nested network in set_implementation");
log::error!("Could not get nested network in set_manual_composition");
return;
};
let Some(node) = network.nodes.get_mut(node_id) else {
log::error!("Could not get node in set_implementation");
log::error!("Could not get node in set_manual_composition");
return;
};
node.manual_composition = manual_composition;
@@ -173,42 +173,10 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
node: graphene_std::math_nodes::logical_not::IDENTIFIER,
aliases: &["graphene_core::ops::LogicalNotNode", "graphene_core::ops::LogicOrNode", "graphene_core::logic::LogicOrNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::bool_value::IDENTIFIER,
aliases: &["graphene_core::ops::BoolValueNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::number_value::IDENTIFIER,
aliases: &["graphene_core::ops::NumberValueNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::percentage_value::IDENTIFIER,
aliases: &["graphene_core::ops::PercentageValueNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::coordinate_value::IDENTIFIER,
aliases: &[
"graphene_core::ops::CoordinateValueNode",
"graphene_core::ops::ConstructVector2",
"graphene_core::ops::Vector2ValueNode",
],
},
NodeReplacement {
node: graphene_std::math_nodes::color_value::IDENTIFIER,
aliases: &["graphene_core::ops::ColorValueNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::gradient_value::IDENTIFIER,
aliases: &["graphene_core::ops::GradientValueNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::sample_gradient::IDENTIFIER,
aliases: &["graphene_core::ops::SampleGradientNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::string_value::IDENTIFIER,
aliases: &["graphene_core::ops::StringValueNode"],
},
NodeReplacement {
node: graphene_std::math_nodes::dot_product::IDENTIFIER,
aliases: &["graphene_core::ops::DotProductNode"],
@@ -510,7 +478,7 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
let mut default_template = NodeTemplate::default();
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.clone());
document.network_interface.replace_implementation(node_id, &network_path, &mut default_template);
document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
document.network_interface.set_manual_composition(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
}
}
}
@@ -539,7 +507,45 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
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());
.set_manual_composition(node_id, network_path, graph_craft::concrete!(graphene_std::Context).into());
}
// Update old value nodes after https://github.com/GraphiteEditor/Graphite/pull/2822
if let DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier { name }) = &node.implementation {
let value_node_names = [
"graphene_math_nodes::BoolValueNode",
"graphene_math_nodes::ColorValueNode",
"graphene_math_nodes::PercentageValueNode",
"graphene_math_nodes::NumberValueNode",
"graphene_math_nodes::StringValueNode",
"graphene_math_nodes::GradientValueNode",
];
if value_node_names.iter().any(|&s| s == name) {
let mut template = resolve_document_node_type("Value")?.default_node_template();
document.network_interface.replace_implementation(node_id, &network_path, &mut template);
let mut old_inputs = document.network_interface.replace_inputs(node_id, &network_path, &mut template)?;
document.network_interface.set_reference(node_id, network_path, Some("Value".to_string()));
if name == "graphene_math_nodes::PercentageValueNode" {
if let NodeInput::Value { tagged_value, .. } = &old_inputs[1] {
if let TaggedValue::F64(value) = &**tagged_value {
old_inputs[1] = NodeInput::value(TaggedValue::Percentage(*value), false);
}
}
}
// Only migrate value inputs, if its a wire the value is unknown.
if let NodeInput::Value { tagged_value, .. } = old_inputs[1].clone() {
document
.network_interface
.set_input(&InputConnector::node(*node_id, 0), NodeInput::value(tagged_value.into_inner(), false), network_path);
}
} else if name == "graphene_math_nodes::CoordinateValueNode" {
document.network_interface.set_implementation(
node_id,
&network_path,
DocumentNodeImplementation::ProtoNode(graphene_std::math_nodes::coordinate_from_numbers::IDENTIFIER),
);
document.network_interface.set_reference(node_id, network_path, Some("Coordinate From Numbers".to_string()))
}
}
// Only nodes that have not been modified and still refer to a definition can be updated
@@ -1007,7 +1013,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
// Ensure layers are positioned as stacks if they are upstream siblings of another layer
document.network_interface.load_structure();
let all_layers = LayerNodeIdentifier::ROOT_PARENT.descendants(document.network_interface.document_metadata()).collect::<Vec<_>>();
let all_layers: Vec<LayerNodeIdentifier> = LayerNodeIdentifier::ROOT_PARENT.descendants(document.network_interface.document_metadata()).collect::<Vec<_>>();
for layer in all_layers {
let (downstream_node, input_index) = document
.network_interface