Clean up document message wrappers around proto nodes so they're now used directly (#4101)

* Rename the 'Identity' node to 'Passthrough' internally

* Rename the 'Memoize'  node to 'Cache' internally

* Let skip_impl proto nodes auto-generate as document node definitions

* Remove the wrapper 'Passthrough' node from document_node_definitions.rs

* Remove the wrapper 'Cache' node from document_node_definitions.rs

* Remove the wrapper 'Monitor' node from document_node_definitions.rs

* Remove the wrapper 'Noise Pattern' node from document_node_definitions.rs

* Remove the wrapper 'Brush' node from document_node_definitions.rs

* Remove the wrapper 'Transform' node from document_node_definitions.rs

* Code review improvements

* Rename Cache node back to Memoize

* More code review
This commit is contained in:
Keavon Chambers
2026-05-03 19:26:36 -07:00
committed by GitHub
parent b27b4c6be7
commit 21e5e06b0b
29 changed files with 408 additions and 662 deletions
@@ -692,7 +692,7 @@ fn import_usvg_path(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
modify_inputs.insert_vector(subpaths, layer, has_transform, path.fill().is_some(), path.stroke().is_some());
if has_transform && let Some(transform_node_id) = modify_inputs.existing_network_node_id("Transform", false) {
if has_transform && let Some(transform_node_id) = modify_inputs.existing_proto_node_id(graphene_std::transform_nodes::transform::IDENTIFIER, false) {
transform_utils::update_transform(modify_inputs.network_interface, &transform_node_id, node_transform);
}
@@ -224,7 +224,9 @@ impl<'a> ModifyInputsContext<'a> {
self.network_interface.move_node_to_chain_start(&shape_id, layer, &[], self.import);
if include_transform {
let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let transform = resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER)
.expect("Transform node does not exist")
.default_node_template();
let transform_id = NodeId::new();
self.network_interface.insert_node(transform_id, transform, &[]);
self.network_interface.move_node_to_chain_start(&transform_id, layer, &[], self.import);
@@ -266,7 +268,9 @@ impl<'a> ModifyInputsContext<'a> {
Some(NodeInput::value(TaggedValue::F64(typesetting.tilt), false)),
Some(NodeInput::value(TaggedValue::TextAlign(typesetting.align), false)),
]);
let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let transform = resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER)
.expect("Transform node does not exist")
.default_node_template();
let stroke = resolve_proto_node_type(graphene_std::vector_nodes::stroke::IDENTIFIER)
.expect("Stroke node does not exist")
.default_node_template();
@@ -305,7 +309,9 @@ impl<'a> ModifyInputsContext<'a> {
}
pub fn insert_image_data(&mut self, image: Image<Color>, layer: LayerNodeIdentifier) {
let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let transform = resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER)
.expect("Transform node does not exist")
.default_node_template();
let image_node = resolve_proto_node_type(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER)
.expect("Image node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::ImageData(image), false))]);
@@ -506,7 +512,7 @@ impl<'a> ModifyInputsContext<'a> {
pub fn gradient_line_set(&mut self, new_start: DVec2, new_end: DVec2) {
let Some(output_layer) = self.get_output_layer() else { return };
let transform_reference = DefinitionIdentifier::Network("Transform".into());
let transform_reference = DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER);
let upstream_transforms: Vec<NodeId> = self
.network_interface
.upstream_flow_back_from_nodes(vec![output_layer.to_node()], &[], network_interface::FlowType::HorizontalFlow)
@@ -553,7 +559,9 @@ impl<'a> ModifyInputsContext<'a> {
if last_transform_value.abs_diff_eq(DAffine2::IDENTITY, 1e-6) {
return;
}
let Some(id) = self.existing_network_node_id("Transform", true) else { return };
let Some(id) = self.existing_proto_node_id(graphene_std::transform_nodes::transform::IDENTIFIER, true) else {
return;
};
id
};
@@ -626,7 +634,7 @@ impl<'a> ModifyInputsContext<'a> {
pub fn transform_change_with_parent(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, skip_rerender: bool) {
// Get the existing upstream Transform node and its transform, if present, otherwise use the identity transform
let (layer_transform, transform_node_id) = self
.existing_network_node_id("Transform", false)
.existing_proto_node_id(graphene_std::transform_nodes::transform::IDENTIFIER, false)
.and_then(|transform_node_id| {
let document_node = self.network_interface.document_network().nodes.get(&transform_node_id)?;
Some((transform_utils::get_current_transform(&document_node.inputs), transform_node_id))
@@ -650,7 +658,7 @@ impl<'a> ModifyInputsContext<'a> {
/// A new Transform node is created if one does not exist, unless it would be given the identity transform.
pub fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, skip_rerender: bool) {
// Get the existing upstream Transform node, if present
let transform_node_id = self.existing_network_node_id("Transform", false);
let transform_node_id = self.existing_proto_node_id(graphene_std::transform_nodes::transform::IDENTIFIER, false);
// Compute the Transform node value so `transform_to_viewport` matches the target after re-render
let final_transform = match transform_in {
@@ -690,7 +698,7 @@ impl<'a> ModifyInputsContext<'a> {
}
// Create the Transform node
self.existing_network_node_id("Transform", true)
self.existing_proto_node_id(graphene_std::transform_nodes::transform::IDENTIFIER, true)
}) else {
return;
};
@@ -715,7 +723,7 @@ impl<'a> ModifyInputsContext<'a> {
}
pub fn brush_modify(&mut self, strokes: Vec<BrushStroke>) {
let Some(brush_node_id) = self.existing_network_node_id("Brush", true) else {
let Some(brush_node_id) = self.existing_proto_node_id(graphene_std::brush::brush::brush::IDENTIFIER, true) else {
return;
};
let strokes_table = strokes.into_iter().map(graphene_std::table::TableRow::new_from_element).collect();
@@ -16,7 +16,6 @@ use graph_craft::ProtoNodeIdentifier;
use graph_craft::concrete;
use graph_craft::document::value::*;
use graph_craft::document::*;
use graphene_std::brush::brush_cache::BrushCache;
use graphene_std::extract_xy::XY;
use graphene_std::raster::{CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, NoiseType, RedGreenBlueAlpha};
use graphene_std::raster_types::{CPU, Raster};
@@ -133,46 +132,6 @@ static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy<HashMap<DefinitionIdentifier,
/// Only the position can be set for protonodes within a definition. The rest of the metadata comes from the node macro in NODE_METADATA
fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefinition> {
let custom = vec![
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Passthrough",
category: "General",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::None, true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Content", "TODO").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Passes-through the input value without changing it. This is useful for rerouting wires for organization purposes."),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Monitor",
category: "",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::None, true)],
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("In", "TODO").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("The Monitor node is used by the editor to access the data flowing through it."),
properties: Some("monitor_properties"),
},
DocumentNodeDefinition {
identifier: "Custom Node",
category: "General",
@@ -189,30 +148,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
description: Cow::Borrowed("An empty node network you can use to create your own custom nodes."),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Cache",
category: "General",
node_template: NodeTemplate {
document_node: DocumentNode {
inputs: vec![NodeInput::value(TaggedValue::None, true)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
call_argument: generic!(T),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Data", "TODO").into()],
output_names: vec!["Data".to_string()],
..Default::default()
},
},
description: Cow::Borrowed(
"Improves rendering performance if used in rare circumstances where automatic caching is not yet advanced enough to handle the situation.\n\
\n\
Stores the last evaluated data that flowed through this node, and immediately returns that data on subsequent renders if the context has not changed.",
),
properties: None,
},
DocumentNodeDefinition {
identifier: "Merge",
category: "General",
@@ -384,7 +319,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
..Default::default()
},
// The monitor node is used to display a thumbnail in the UI.
// TODO: Check if thumbnail is reversed
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
@@ -1062,7 +996,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(memo::memoize::IDENTIFIER),
..Default::default()
},
DocumentNode {
@@ -1132,59 +1066,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
description: Cow::Borrowed("TODO"),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Noise Pattern",
category: "Raster: Pattern",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::raster_nodes::std_nodes::noise_pattern::IDENTIFIER),
inputs: vec![
NodeInput::value(TaggedValue::None, false),
NodeInput::value(TaggedValue::Bool(true), false),
NodeInput::value(TaggedValue::U32(0), false),
NodeInput::value(TaggedValue::F64(10.), false),
NodeInput::value(TaggedValue::NoiseType(NoiseType::default()), false),
NodeInput::value(TaggedValue::DomainWarpType(DomainWarpType::default()), false),
NodeInput::value(TaggedValue::F64(100.), false),
NodeInput::value(TaggedValue::FractalType(FractalType::default()), false),
NodeInput::value(TaggedValue::U32(3), false),
NodeInput::value(TaggedValue::F64(2.), false),
NodeInput::value(TaggedValue::F64(0.5), false),
NodeInput::value(TaggedValue::F64(0.), false), // 0-1 range
NodeInput::value(TaggedValue::F64(2.), false),
NodeInput::value(TaggedValue::CellularDistanceFunction(CellularDistanceFunction::default()), false),
NodeInput::value(TaggedValue::CellularReturnType(CellularReturnType::default()), false),
NodeInput::value(TaggedValue::F64(1.), false),
],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![
("Spacer", "TODO").into(),
("Clip", "TODO").into(),
("Seed", "TODO").into(),
InputMetadata::with_name_description_override("Scale", "TODO", WidgetOverride::Custom("noise_properties_scale".to_string())),
InputMetadata::with_name_description_override("Noise Type", "TODO", WidgetOverride::Custom("noise_properties_noise_type".to_string())),
InputMetadata::with_name_description_override("Domain Warp Type", "TODO", WidgetOverride::Custom("noise_properties_domain_warp_type".to_string())),
InputMetadata::with_name_description_override("Domain Warp Amplitude", "TODO", WidgetOverride::Custom("noise_properties_domain_warp_amplitude".to_string())),
InputMetadata::with_name_description_override("Fractal Type", "TODO", WidgetOverride::Custom("noise_properties_fractal_type".to_string())),
InputMetadata::with_name_description_override("Fractal Octaves", "TODO", WidgetOverride::Custom("noise_properties_fractal_octaves".to_string())),
InputMetadata::with_name_description_override("Fractal Lacunarity", "TODO", WidgetOverride::Custom("noise_properties_fractal_lacunarity".to_string())),
InputMetadata::with_name_description_override("Fractal Gain", "TODO", WidgetOverride::Custom("noise_properties_fractal_gain".to_string())),
InputMetadata::with_name_description_override("Fractal Weighted Strength", "TODO", WidgetOverride::Custom("noise_properties_fractal_weighted_strength".to_string())),
InputMetadata::with_name_description_override("Fractal Ping Pong Strength", "TODO", WidgetOverride::Custom("noise_properties_ping_pong_strength".to_string())),
InputMetadata::with_name_description_override("Cellular Distance Function", "TODO", WidgetOverride::Custom("noise_properties_cellular_distance_function".to_string())),
InputMetadata::with_name_description_override("Cellular Return Type", "TODO", WidgetOverride::Custom("noise_properties_cellular_return_type".to_string())),
InputMetadata::with_name_description_override("Cellular Jitter", "TODO", WidgetOverride::Custom("noise_properties_cellular_jitter".to_string())),
],
output_names: vec!["Image".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("Generates customizable procedural noise patterns."),
properties: None,
},
DocumentNodeDefinition {
identifier: "Split Channels",
category: "Raster: Channels",
@@ -1364,81 +1245,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
),
properties: None,
},
// TODO: Remove this and just use the proto node definition directly
DocumentNodeDefinition {
identifier: "Brush",
category: "Raster",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::Network(NodeNetwork {
exports: vec![NodeInput::node(NodeId(0), 0)],
nodes: vec![DocumentNode {
inputs: vec![
NodeInput::import(concrete!(Table<Raster<CPU>>), 0),
NodeInput::import(concrete!(Vec<brush::brush_stroke::BrushStroke>), 1),
NodeInput::import(concrete!(BrushCache), 2),
],
implementation: DocumentNodeImplementation::ProtoNode(brush::brush::brush::IDENTIFIER),
..Default::default()
}]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::Raster(Default::default()), true),
NodeInput::value(TaggedValue::BrushStrokeTable(Default::default()), false),
NodeInput::value(TaggedValue::BrushCache(BrushCache::default()), false),
],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Background", "TODO").into(), ("Trace", "TODO").into(), ("Cache", "TODO").into()],
output_names: vec!["Image".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
node_metadata: [DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
..Default::default()
}]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
},
..Default::default()
}),
..Default::default()
},
},
description: Cow::Borrowed("TODO"),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Memoize",
category: "Debug",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Image", "TODO").into()],
output_names: vec!["Image".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("TODO"),
properties: None,
},
#[cfg(feature = "gpu")]
DocumentNodeDefinition {
identifier: "Upload Texture",
@@ -1462,7 +1268,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
DocumentNode {
call_argument: generic!(T),
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(memo::memoize::IDENTIFIER),
..Default::default()
},
]
@@ -1641,34 +1447,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
),
properties: None,
},
// Aims for interoperable compatibility with:
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=levl%27%20%3D%20Levels-,%27curv%27%20%3D%20Curves,-%27expA%27%20%3D%20Exposure
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Max%20input%20range-,Curves,-Curves%20settings%20files
//
// Some further analysis available at:
// https://geraldbakker.nl/psnumbers/curves.html
// TODO: Fix this, it's currently broken
// DocumentNodeDefinition {
// identifier: "Curves",
// category: "Raster: Adjustment",
// node_template: NodeTemplate {
// document_node: DocumentNode {
// implementation: DocumentNodeImplementation::proto("core_types::raster::CurvesNode"),
// inputs: vec![
// NodeInput::value(TaggedValue::Raster(Default::default()), true),
// NodeInput::value(TaggedValue::Curve(Default::default()), false),
// ],
// ..Default::default()
// },
// persistent_node_metadata: DocumentNodePersistentMetadata {
// input_properties: vec![("Image", "TODO").into(), ("Curve", "TODO").into()],
// output_names: vec!["Image".to_string()],
// ..Default::default()
// },
// },
// description: Cow::Borrowed("TODO"),
// properties: None,
// },
DocumentNodeDefinition {
identifier: "Path",
category: "Vector",
@@ -1742,132 +1520,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
description: Cow::Borrowed("TODO"),
properties: None,
},
// TODO: Auto-generate this from its proto node macro
DocumentNodeDefinition {
identifier: "Transform",
category: "Math: Transform",
node_template: NodeTemplate {
document_node: DocumentNode {
inputs: vec![
// Value
NodeInput::value(TaggedValue::DAffine2(DAffine2::default()), true),
// Translation
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
// Rotation
NodeInput::value(TaggedValue::F64(0.), false),
// Scale
NodeInput::value(TaggedValue::DVec2(DVec2::ONE), false),
// Skew
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
// Origin Offset
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
// Scale Appearance
NodeInput::value(TaggedValue::Bool(true), false),
],
implementation: DocumentNodeImplementation::Network(NodeNetwork {
exports: vec![
// From the Transform node
NodeInput::node(NodeId(1), 0),
],
nodes: [
// Monitor node
DocumentNode {
inputs: vec![
// From the Value import
NodeInput::import(generic!(T), 0),
],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
// Transform node
DocumentNode {
inputs: vec![
// From the Monitor node
NodeInput::node(NodeId(0), 0),
// From the Translation import
NodeInput::import(concrete!(DVec2), 1),
// From the Rotation import
NodeInput::import(concrete!(f64), 2),
// From the Scale import
NodeInput::import(concrete!(DVec2), 3),
// From the Skew import
NodeInput::import(concrete!(DVec2), 4),
],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::transform::IDENTIFIER),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)),
..Default::default()
},
..Default::default()
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)),
..Default::default()
},
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
},
..Default::default()
}),
input_metadata: vec![
("Value", "TODO").into(),
InputMetadata::with_name_description_override(
"Translation",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "X".to_string(),
y: "Y".to_string(),
unit: " px".to_string(),
..Default::default()
}),
),
InputMetadata::with_name_description_override("Rotation", "TODO", WidgetOverride::Custom("transform_rotation".to_string())),
InputMetadata::with_name_description_override(
"Scale",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "W".to_string(),
y: "H".to_string(),
unit: "x".to_string(),
..Default::default()
}),
),
InputMetadata::with_name_description_override("Skew", "TODO", WidgetOverride::Custom("transform_skew".to_string())),
InputMetadata::with_name_description_override("Origin Offset", "TODO", WidgetOverride::Custom("hidden".to_string())),
InputMetadata::with_name_description_override("Scale Appearance", "TODO", WidgetOverride::Custom("hidden".to_string())),
],
output_names: vec!["Data".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("TODO"),
properties: None,
},
];
document_node_derive::post_process_nodes(custom)
@@ -2323,6 +1975,25 @@ fn static_input_properties() -> InputProperties {
Ok(vec![LayoutGroup::row(widgets)])
}),
);
// Translation uses a Vec2 widget with X/Y labels and a "px" unit suffix
map.insert(
"transform_translation".to_string(),
Box::new(|node_id, index, context| {
Ok(vec![node_properties::vec2_widget(
ParameterWidgetsInfo::new(node_id, index, true, context),
"X",
"Y",
" px",
None,
false,
)])
}),
);
// Scale uses a Vec2 widget with W/H labels and an "x" unit suffix
map.insert(
"transform_scale".to_string(),
Box::new(|node_id, index, context| Ok(vec![node_properties::vec2_widget(ParameterWidgetsInfo::new(node_id, index, true, context), "W", "H", "x", None, false)])),
);
// Skew has a custom override that maps to degrees
map.insert(
"transform_skew".to_string(),
@@ -22,8 +22,15 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
})
.collect::<Vec<_>>();
// Add the rest of the protonodes from the macro
// Add the rest of the protonodes from the macro.
// Typed nodes are registered in `core_types::NODE_REGISTRY` via the macro's auto-generated `register_node` codegen.
// `skip_impl` nodes (e.g. Cache, Monitor) bypass that registration but are wired up manually in
// `interpreted_executor::node_registry::NODE_REGISTRY` via `async_node!`. We consult that extended registry as a
// fallback when deriving `call_argument` so it reflects the impls actually registered, which will usually be `Context`.
let extended_node_registry = &*interpreted_executor::node_registry::NODE_REGISTRY;
let node_registry = NODE_REGISTRY.lock().unwrap();
let empty_implementations: Vec<(NodeConstructor, NodeIOTypes)> = Vec::new();
let context_type = concrete!(Context);
for (id, metadata) in NODE_METADATA.lock().unwrap().iter() {
let identifier = DefinitionIdentifier::ProtoNode(id.clone());
if definitions_map.contains_key(&identifier) {
@@ -39,12 +46,25 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
memoize: _,
} = metadata;
let Some(implementations) = &node_registry.get(id) else { continue };
let implementations = node_registry.get(id).unwrap_or(&empty_implementations);
let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() });
let valid_inputs: HashSet<_> = implementations.iter().map(|(_, node_io)| node_io.call_argument.clone()).collect();
let input_type = if valid_inputs.len() > 1 { &const { generic!(D) } } else { &first_node_io.call_argument };
let call_arguments: Vec<&Type> = if !implementations.is_empty() {
implementations.iter().map(|(_, io)| &io.call_argument).collect()
} else if let Some(impls) = extended_node_registry.get(id) {
impls.keys().map(|io| &io.call_argument).collect()
} else {
Vec::new()
};
let valid_inputs: HashSet<&Type> = call_arguments.iter().copied().collect();
let input_type = if valid_inputs.is_empty() {
&context_type
} else if valid_inputs.len() > 1 {
&const { generic!(D) }
} else {
call_arguments[0]
};
let inputs = preprocessor::node_inputs(fields, first_node_io);
definitions_map.insert(
@@ -83,16 +103,6 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
);
}
// If any protonode does not have metadata then set its display name to its identifier string
for definition in definitions_map.values_mut() {
let metadata = NODE_METADATA.lock().unwrap();
if let DocumentNodeImplementation::ProtoNode(id) = &definition.node_template.document_node.implementation
&& !metadata.contains_key(id)
{
definition.node_template.persistent_node_metadata.display_name = definition.identifier.to_string();
}
}
// Add the rest of the network nodes to the map and add the metadata for their internal protonodes
for mut network_node in network_nodes {
traverse_node(&network_node.node_template.document_node, &mut network_node.node_template.persistent_node_metadata, &definitions_map);
@@ -214,7 +214,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let mid_point = (network_interface.get_output_center(&output_connector, breadcrumb_network_path).unwrap()
+ network_interface.get_input_center(&input_connector, breadcrumb_network_path).unwrap())
/ 2.;
let node_template = Box::new(resolve_proto_node_type(graphene_core::ops::identity::IDENTIFIER).unwrap().default_node_template());
let Some(passthrough_definition) = resolve_proto_node_type(graphene_core::ops::passthrough::IDENTIFIER) else {
log::error!("Could not resolve passthrough node when wiring an export to an import");
return;
};
let node_template = Box::new(passthrough_definition.default_node_template());
let node_id = NodeId::new();
responses.add(NodeGraphMessage::InsertNode { node_id, node_template });
@@ -126,7 +126,7 @@ impl DocumentMetadata {
let local_transform = self.local_transforms.get(&layer.to_node()).copied();
let transform = local_transform.unwrap_or_else(|| {
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain(&DefinitionIdentifier::Network("Transform".into()), layer, network_interface);
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain(&DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER), layer, network_interface);
let transform_node = transform_node_id.and_then(|id| network_interface.document_node(&id, &[]));
transform_node.map(|node| transform_utils::get_current_transform(node.inputs.as_slice())).unwrap_or_default()
});
@@ -236,8 +236,8 @@ impl NodeNetworkInterface {
}
DocumentNodeImplementation::ProtoNode(proto_node_identifier) => {
let Some(implementations) = NODE_REGISTRY.get(proto_node_identifier) else {
// The compiler removes the identity node, so it's expected to be absent from the registry
if proto_node_identifier != &graphene_std::ops::identity::IDENTIFIER {
// The compiler removes the passthrough node, so it's expected to be absent from the registry
if proto_node_identifier != &graphene_std::ops::passthrough::IDENTIFIER {
log::error!("Proto node `{proto_node_identifier:?}` not found in the node registry, in potential_valid_input_types");
}
return Vec::new();
@@ -54,7 +54,7 @@ impl OriginalTransforms {
/// Gets the transform from the most downstream transform node
fn get_layer_transform(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DAffine2> {
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain(&DefinitionIdentifier::Network("Transform".into()), layer, network_interface)?;
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain(&DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER), layer, network_interface)?;
let document_node = network_interface.document_network().nodes.get(&transform_node_id)?;
Some(transform_utils::get_current_transform(&document_node.inputs))