Deprecate automatic composition (#3088)

* Make manual_compositon non optional and rename to call_argument

* Fix clippy warnings

* Remove automatic composition compiler infrastructure

* Implement document migration

* Fix tests

* Fix compilation on web

* Fix doble number test

* Remove extra parens

* Cleanup

* Update demo artwork

* Remove last compose node mention

* Remove last mention of manual composition
This commit is contained in:
Dennis Kobert
2025-08-24 08:34:59 +00:00
committed by GitHub
parent bb364c92ad
commit d9cbf975ff
29 changed files with 184 additions and 676 deletions
@@ -1782,7 +1782,8 @@ impl DocumentMessageHandler {
pub fn deserialize_document(serialized_content: &str) -> Result<Self, EditorError> {
let document_message_handler = serde_json::from_str::<DocumentMessageHandler>(serialized_content)
.or_else(|_| {
.or_else(|e| {
log::warn!("failed to directly load document with the following error: {e}. Trying old DocumentMessageHandler");
// TODO: Eventually remove this document upgrade code
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct OldDocumentMessageHandler {
@@ -111,7 +111,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::None, true)],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
@@ -151,19 +151,19 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -233,33 +233,33 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_graphic::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// Secondary (left) input type coercion
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 1)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::wrap_graphic::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// Store the ID of the parent node (which encapsulates this sub-network) in each row we are extending the table with.
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::source_node_id::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// The monitor node is used to display a thumbnail in the UI
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(3), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
..Default::default()
@@ -349,7 +349,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: [
// Ensure this ID is kept in sync with the ID in set_alias so that the name input is kept in sync with the alias
DocumentNode {
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(artboard::create_artboard::IDENTIFIER),
inputs: vec![
NodeInput::network(concrete!(TaggedValue), 1),
@@ -365,7 +365,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::source_node_id::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// The monitor node is used to display a thumbnail in the UI.
@@ -373,12 +373,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![
NodeInput::network(graphene_std::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(Table<Artboard>))), 0),
NodeInput::node(NodeId(2), 0),
@@ -495,13 +495,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: [
DocumentNode {
inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::scope("editor-api"), NodeInput::network(concrete!(String), 1)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::load_resource::IDENTIFIER),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::decode_image::IDENTIFIER),
..Default::default()
},
@@ -568,7 +568,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
..Default::default()
@@ -630,20 +630,20 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::scope("editor-api")],
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0), NodeInput::network(concrete!(Footprint), 1), NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::rasterize::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
]
@@ -716,7 +716,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
category: "Raster: Pattern",
node_template: NodeTemplate {
document_node: DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::std_nodes::noise_pattern::IDENTIFIER),
inputs: vec![
NodeInput::value(TaggedValue::None, false),
@@ -783,7 +783,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Red), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -792,7 +792,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Green), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -801,7 +801,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Blue), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -810,7 +810,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Alpha), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -888,13 +888,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Raster<CPU>>), 0), NodeInput::value(TaggedValue::XY(XY::X), false)],
implementation: DocumentNodeImplementation::ProtoNode(extract_xy::extract_xy::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Raster<CPU>>), 0), NodeInput::value(TaggedValue::XY(XY::Y), false)],
implementation: DocumentNodeImplementation::ProtoNode(extract_xy::extract_xy::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -962,7 +962,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(Vec<brush::brush_stroke::BrushStroke>), 1),
NodeInput::network(concrete!(BrushCache), 2),
],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(brush::brush::brush::IDENTIFIER),
..Default::default()
}]
@@ -1013,7 +1013,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
@@ -1032,7 +1032,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
@@ -1054,13 +1054,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
exports: vec![NodeInput::node(NodeId(1), 0)],
nodes: [
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![NodeInput::scope("editor-api")],
implementation: DocumentNodeImplementation::ProtoNode(wgpu_executor::create_gpu_surface::IDENTIFIER),
..Default::default()
},
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
..Default::default()
@@ -1126,12 +1126,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Raster<CPU>>), 0), NodeInput::node(NodeId(0), 0)],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(wgpu_executor::texture_upload::upload_texture::IDENTIFIER),
..Default::default()
},
DocumentNode {
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
..Default::default()
@@ -1247,7 +1247,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
@@ -1257,7 +1257,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(graphene_std::vector::VectorModification), 1),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(vector::path_modify::IDENTIFIER),
..Default::default()
},
@@ -1317,7 +1317,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(text::text::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![
NodeInput::scope("editor-api"),
NodeInput::value(TaggedValue::String("Lorem ipsum".to_string()), false),
@@ -1427,7 +1427,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
@@ -1439,7 +1439,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(DVec2), 3),
NodeInput::network(concrete!(DVec2), 4),
],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::transform::IDENTIFIER),
..Default::default()
},
@@ -1524,25 +1524,25 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -1622,7 +1622,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(vector::subpath_segment_lengths::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -1637,25 +1637,25 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::node(NodeId(0), 0),
],
implementation: DocumentNodeImplementation::ProtoNode(vector::sample_polyline::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(3), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -1791,26 +1791,26 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(f64), 1),
NodeInput::network(concrete!(u32), 2),
],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(vector::poisson_disk_points::IDENTIFIER),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -59,7 +59,7 @@ pub(super) fn post_process_nodes(mut custom: Vec<DocumentNodeDefinition>) -> Vec
node_template: NodeTemplate {
document_node: DocumentNode {
inputs,
manual_composition: Some(input_type.clone()),
call_argument: (input_type.clone()),
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
visible: true,
skip_deduplication: false,
@@ -542,12 +542,11 @@ impl NodeNetworkInterface {
}
}
DocumentNodeImplementation::ProtoNode(_) => {
// If a node has manual composition, then offset the input index by 1 since the proto node also includes the type of the input passed through manual composition.
let manual_composition_offset = if node.manual_composition.is_some() { 1 } else { 0 };
// Offset the input index by 1 since the proto node also includes the type of the input passed as a call argument.
self.resolved_types
.types
.get(node_id_path.as_slice())
.and_then(|node_types| node_types.inputs.get(input_index + manual_composition_offset).cloned())
.and_then(|node_types| node_types.inputs.get(input_index + 1).cloned())
.map(|node_types| (node_types, TypeSource::Compiled))
}
DocumentNodeImplementation::Extract => None,
@@ -576,7 +575,7 @@ impl NodeNetworkInterface {
return (concrete!(()), TypeSource::Error("could not resolve protonode"));
};
let skip_footprint = if node.manual_composition.is_some() { 1 } else { 0 };
let skip_footprint = 1;
let Some(input_type) = std::iter::once(node_types.call_argument.clone()).chain(node_types.inputs.clone()).nth(input_index + skip_footprint) else {
log::error!("Could not get type");
@@ -1494,7 +1493,7 @@ impl NodeNetworkInterface {
let mut node_metadata = DocumentNodeMetadata::default();
node.inputs = old_node.inputs;
node.manual_composition = old_node.manual_composition;
node.call_argument = old_node.manual_composition.unwrap();
node.visible = old_node.visible;
node.skip_deduplication = old_node.skip_deduplication;
node.original_location = old_node.original_location;
@@ -4150,7 +4149,7 @@ 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_call_argument(&mut self, node_id: &NodeId, network_path: &[NodeId], call_argument: Type) {
let Some(network) = self.network_mut(network_path) else {
log::error!("Could not get nested network in set_implementation");
return;
@@ -4159,7 +4158,7 @@ impl NodeNetworkInterface {
log::error!("Could not get node in set_implementation");
return;
};
node.manual_composition = manual_composition;
node.call_argument = call_argument;
}
pub fn set_input(&mut self, input_connector: &InputConnector, new_input: NodeInput, network_path: &[NodeId]) {