mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-20 03:18:06 +08:00
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:
@@ -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))
|
||||
|
||||
@@ -91,8 +91,9 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||
aliases: &["graphene_core::ops::ExtractXyNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::ops::identity::IDENTIFIER,
|
||||
node: graphene_std::ops::passthrough::IDENTIFIER,
|
||||
aliases: &[
|
||||
"graphene_core::ops::IdentityNode",
|
||||
"graphene_core::transform::CullNode",
|
||||
"graphene_core::transform::BoundlessFootprintNode",
|
||||
"graphene_core::transform::FreezeRealTimeNode",
|
||||
@@ -107,7 +108,7 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||
aliases: &["graphene_core::memo::MonitorNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::memo::memo::IDENTIFIER,
|
||||
node: graphene_std::memo::memoize::IDENTIFIER,
|
||||
aliases: &["graphene_core::memo::MemoNode", "graphene_core::memo::ImpureMemoNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
@@ -1064,6 +1065,95 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
|
||||
}
|
||||
}
|
||||
|
||||
// The "Brush" wrapper network was replaced with the `brush` proto node directly. Convert old `Network("Brush")` instances to the proto node, forwarding all 3 inputs (Background, Trace, Cache) one-to-one.
|
||||
// This must run as a pre-pass before the recursive iteration below: replacing the outer Brush's network impl orphans its child paths, and the recursive iteration would log errors for those stale paths.
|
||||
let brush_layers: Vec<(NodeId, Vec<NodeId>)> = document
|
||||
.network_interface
|
||||
.document_network()
|
||||
.recursive_nodes()
|
||||
.filter_map(|(node_id, _, path)| (document.network_interface.reference(node_id, &path) == Some(DefinitionIdentifier::Network("Brush".into()))).then_some((*node_id, path)))
|
||||
.collect();
|
||||
for (node_id, network_path) in &brush_layers {
|
||||
// Pre-load `outward_wires` so the chain-break check inside `set_input` resolves the original upstream→node wire from cache
|
||||
// rather than triggering a fresh rebuild from the (already-mutated) post-`replace_inputs` state, which would orphan wires.
|
||||
let _ = document.network_interface.outward_wires(network_path);
|
||||
let new_reference = DefinitionIdentifier::ProtoNode(graphene_std::brush::brush::brush::IDENTIFIER);
|
||||
let Some(definition) = resolve_document_node_type(&new_reference) else { continue };
|
||||
let mut node_template = definition.default_node_template();
|
||||
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
||||
let Some(old_inputs) = document.network_interface.replace_inputs(node_id, network_path, &mut node_template) else {
|
||||
continue;
|
||||
};
|
||||
for (index, input) in old_inputs.iter().take(3).enumerate() {
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path);
|
||||
}
|
||||
}
|
||||
|
||||
// The "Transform" wrapper network was replaced with the `transform` proto node directly. Convert old `Network("Transform")` instances to the proto node, forwarding the 5 user-facing inputs (Value, Translation, Rotation, Scale, Skew) and dropping the legacy migration sentinels (Origin Offset, Scale Appearance) at indices 5 and 6 if present.
|
||||
// Pre-pass for the same reason as the Brush migration above: replacing the outer Transform's network impl orphans its child paths.
|
||||
let transform_layers: Vec<(NodeId, Vec<NodeId>, usize)> = document
|
||||
.network_interface
|
||||
.document_network()
|
||||
.recursive_nodes()
|
||||
.filter_map(|(node_id, node, path)| {
|
||||
(document.network_interface.reference(node_id, &path) == Some(DefinitionIdentifier::Network("Transform".into()))).then_some((*node_id, path, node.inputs.len()))
|
||||
})
|
||||
.collect();
|
||||
for (node_id, network_path, old_inputs_count) in &transform_layers {
|
||||
// Pre-load `outward_wires` so the chain-break check inside `set_input` resolves the original upstream→node wire from cache
|
||||
// rather than triggering a fresh rebuild from the (already-mutated) post-`replace_inputs` state, which would orphan wires.
|
||||
let _ = document.network_interface.outward_wires(network_path);
|
||||
let new_reference = DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER);
|
||||
let Some(definition) = resolve_document_node_type(&new_reference) else { continue };
|
||||
let mut node_template = definition.default_node_template();
|
||||
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
||||
let Some(old_inputs) = document.network_interface.replace_inputs(node_id, network_path, &mut node_template) else {
|
||||
continue;
|
||||
};
|
||||
// Forward the first 5 inputs (Value, Translation, Rotation, Scale, Skew); drop indices 5 and 6 if present.
|
||||
for (index, input) in old_inputs.iter().take(5).enumerate() {
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path);
|
||||
}
|
||||
|
||||
// Pre-2024 documents stored Transform with 6 inputs and used radians for Rotation and `tan(radians)` for Skew. Detect that legacy
|
||||
// shape (no input at index 6) and convert the units to degrees so the values match what the new Properties panel widgets expect.
|
||||
if *old_inputs_count == 6 {
|
||||
match old_inputs.get(2) {
|
||||
Some(NodeInput::Value { tagged_value, exposed }) => {
|
||||
if let TaggedValue::F64(radians) = *tagged_value.clone().into_inner() {
|
||||
let degrees = NodeInput::value(TaggedValue::F64(radians.to_degrees()), *exposed);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 2), degrees, network_path);
|
||||
}
|
||||
}
|
||||
Some(NodeInput::Node { .. }) => {
|
||||
// Wired upstream: splice in a Multiply node by 180/π that converts radians to degrees so the upstream value
|
||||
// (which represented radians in the legacy format) reaches the now-degrees Rotation input correctly.
|
||||
if let Some(multiply_node) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::multiply::IDENTIFIER)) {
|
||||
let mut multiply_template = multiply_node.default_node_template();
|
||||
multiply_template.document_node.inputs[1] = NodeInput::value(TaggedValue::F64(180. / PI), false);
|
||||
let multiply_node_id = NodeId::new();
|
||||
if let Some(transform_position) = document.network_interface.position_from_downstream_node(node_id, network_path) {
|
||||
let multiply_position = transform_position + IVec2::new(-7, 1);
|
||||
document.network_interface.insert_node(multiply_node_id, multiply_template, network_path);
|
||||
document.network_interface.shift_absolute_node_position(&multiply_node_id, multiply_position, network_path);
|
||||
document.network_interface.insert_node_between(&multiply_node_id, &InputConnector::node(*node_id, 2), 0, network_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(NodeInput::Value { tagged_value, exposed }) = old_inputs.get(4)
|
||||
&& let TaggedValue::DVec2(old_value) = *tagged_value.clone().into_inner()
|
||||
{
|
||||
// The previous skew value stored `tan(radians)`, now it stores degrees directly.
|
||||
let new_value = DVec2::new(old_value.x.atan().to_degrees(), old_value.y.atan().to_degrees());
|
||||
let new_input = NodeInput::value(TaggedValue::DVec2(new_value), *exposed);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 4), new_input, network_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply upgrades to each unmodified node.
|
||||
let nodes = document
|
||||
.network_interface
|
||||
@@ -1677,79 +1767,6 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
document.network_interface.add_import(TaggedValue::U32(0), false, 1, "Loop Level", "TODO", &node_path);
|
||||
}
|
||||
|
||||
// Migrate the Transform node to use degrees instead of radians
|
||||
if reference == DefinitionIdentifier::Network("Transform".into()) && node.inputs.get(6).is_none() {
|
||||
let mut node_template = resolve_network_node_type("Transform")?.default_node_template();
|
||||
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
||||
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
|
||||
|
||||
// Value
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
||||
// Translation
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
||||
// Rotation
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
||||
// Scale
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), network_path);
|
||||
// Skew
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[4].clone(), network_path);
|
||||
// Origin Offset
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 5), NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false), network_path);
|
||||
// Scale Appearance
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 6), NodeInput::value(TaggedValue::Bool(true), false), network_path);
|
||||
|
||||
// Migrate rotation from radians to degrees
|
||||
match node.inputs.get(2)? {
|
||||
NodeInput::Value { tagged_value, exposed } => {
|
||||
// Read the existing Properties panel number value, which used to be in radians
|
||||
let TaggedValue::F64(radians) = *tagged_value.clone().into_inner() else { return None };
|
||||
|
||||
// Convert the radians to degrees and set it back as the new input value
|
||||
let degrees = NodeInput::value(TaggedValue::F64(radians.to_degrees()), *exposed);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 2), degrees, network_path);
|
||||
}
|
||||
NodeInput::Node { .. } => {
|
||||
// Construct a new Multiply node for converting from degrees to radians
|
||||
let Some(multiply_node) = resolve_document_node_type(&DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::multiply::IDENTIFIER)) else {
|
||||
log::error!("Could not get multiply node from definition when upgrading transform");
|
||||
return None;
|
||||
};
|
||||
let mut multiply_template = multiply_node.default_node_template();
|
||||
multiply_template.document_node.inputs[1] = NodeInput::value(TaggedValue::F64(180. / PI), false);
|
||||
|
||||
// Decide on the placement position of the new Multiply node
|
||||
let multiply_node_id = NodeId::new();
|
||||
let Some(transform_position) = document.network_interface.position_from_downstream_node(node_id, network_path) else {
|
||||
log::error!("Could not get positon for transform node {node_id}");
|
||||
return None;
|
||||
};
|
||||
let multiply_position = transform_position + IVec2::new(-7, 1);
|
||||
|
||||
// Insert the new Multiply node into the network directly before it's used
|
||||
document.network_interface.insert_node(multiply_node_id, multiply_template, network_path);
|
||||
document.network_interface.shift_absolute_node_position(&multiply_node_id, multiply_position, network_path);
|
||||
document.network_interface.insert_node_between(&multiply_node_id, &InputConnector::node(*node_id, 2), 0, network_path);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
// Migrate skew from radians to degrees
|
||||
if let NodeInput::Value { tagged_value, exposed } = node.inputs.get(4)? {
|
||||
// Read the existing Properties panel number value, which used to be in radians
|
||||
let TaggedValue::DVec2(old_value) = *tagged_value.clone().into_inner() else { return None };
|
||||
|
||||
// The previous value stored the tangent of the displayed degrees. Now it stores the degrees, so take the arctan of it and convert to degrees.
|
||||
let new_value = DVec2::new(old_value.x.atan().to_degrees(), old_value.y.atan().to_degrees());
|
||||
let new_input = NodeInput::value(TaggedValue::DVec2(new_value), *exposed);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 4), new_input, network_path);
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade the "Animation" node to add the "Rate" input
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::animation::animation_time::IDENTIFIER) && inputs_count < 2 {
|
||||
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
||||
@@ -1954,7 +1971,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
|
||||
if let Some(downstream_input) = downstream {
|
||||
// Create a Transform node with translation = start
|
||||
let Some(transform_node_type) = resolve_network_node_type("Transform") else {
|
||||
let Some(transform_node_type) = resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER) else {
|
||||
log::error!("Transform node definition not found during Arrow migration");
|
||||
return None;
|
||||
};
|
||||
@@ -2015,7 +2032,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
|
||||
if let Some(downstream_input) = downstream {
|
||||
// Create a Transform node with translation = start
|
||||
let Some(transform_node_type) = resolve_network_node_type("Transform") else {
|
||||
let Some(transform_node_type) = resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER) else {
|
||||
log::error!("Transform node definition not found during Line migration");
|
||||
return None;
|
||||
};
|
||||
@@ -2140,7 +2157,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
fn migrate_removed_catalog_definitions(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler) -> Option<()> {
|
||||
// Collapse the legacy "Sample Polyline" wrapper network into the standalone `sample_polyline` proto node.
|
||||
// The proto node now computes per-bezpath segment lengths inline, so the wrapper's separate `subpath_segment_lengths`
|
||||
// and `Memo` nodes are no longer needed. The 7 user-facing inputs are positionally identical between the
|
||||
// and `Memoize` nodes are no longer needed. The 7 user-facing inputs are positionally identical between the
|
||||
// old wrapper and the new proto node.
|
||||
if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path)
|
||||
&& name == "Sample Polyline"
|
||||
@@ -2155,7 +2172,7 @@ fn migrate_removed_catalog_definitions(node_id: &NodeId, node: &DocumentNode, ne
|
||||
}
|
||||
|
||||
// Collapse the legacy "Scatter Points" wrapper network into the standalone `scatter_points` proto node.
|
||||
// The wrapper's trailing `Memo` node is now produced automatically by the `memoize` attribute on the
|
||||
// The wrapper's trailing `Memoize` node is now produced automatically by the `memoize` attribute on the
|
||||
// proto node, so the wrapper itself is redundant. The 3 user-facing inputs are positionally identical
|
||||
// between the old wrapper and the new proto node.
|
||||
if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path)
|
||||
@@ -2171,7 +2188,7 @@ fn migrate_removed_catalog_definitions(node_id: &NodeId, node: &DocumentNode, ne
|
||||
}
|
||||
|
||||
// Collapse the legacy "Boolean Operation" wrapper network into the standalone `boolean_operation` proto node.
|
||||
// The wrapper's trailing `Memo` node is now produced automatically by the `memoize` attribute on the
|
||||
// The wrapper's trailing `Memoize` node is now produced automatically by the `memoize` attribute on the
|
||||
// proto node, so the wrapper itself is redundant. The 2 user-facing inputs are positionally identical
|
||||
// between the old wrapper and the new proto node.
|
||||
if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path)
|
||||
|
||||
@@ -147,7 +147,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
|
||||
|
||||
// Add a transform node to ensure correct tooling modifications
|
||||
let transform_node_id = NodeId::new();
|
||||
let transform_node = document_node_definitions::resolve_network_node_type("Transform")
|
||||
let transform_node = document_node_definitions::resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER)
|
||||
.expect("Failed to create transform node")
|
||||
.default_node_template();
|
||||
responses.add(NodeGraphMessage::InsertNode {
|
||||
@@ -251,7 +251,9 @@ pub fn new_custom(id: NodeId, nodes: Vec<(NodeId, NodeTemplate)>, parent: LayerN
|
||||
pub fn get_origin(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DVec2> {
|
||||
use graphene_std::transform_nodes::transform::*;
|
||||
|
||||
if let TaggedValue::DVec2(origin) = NodeGraphLayer::new(layer, network_interface).find_input(&DefinitionIdentifier::Network("Transform".into()), TranslationInput::INDEX)? {
|
||||
if let TaggedValue::DVec2(origin) =
|
||||
NodeGraphLayer::new(layer, network_interface).find_input(&DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER), TranslationInput::INDEX)?
|
||||
{
|
||||
Some(*origin)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::tool_prelude::*;
|
||||
use crate::consts::DEFAULT_BRUSH_SIZE;
|
||||
use crate::messages::portfolio::document::graph_operation::transform_utils::get_current_transform;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_network_node_type};
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_proto_node_type};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
@@ -319,7 +319,7 @@ impl BrushToolData {
|
||||
continue;
|
||||
};
|
||||
|
||||
if reference == DefinitionIdentifier::Network("Brush".into()) && node_id != layer.to_node() {
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::brush::brush::brush::IDENTIFIER) && node_id != layer.to_node() {
|
||||
let points_input = node.inputs.get(1)?;
|
||||
let Some(TaggedValue::BrushStrokeTable(strokes)) = points_input.as_value() else { continue };
|
||||
self.strokes = strokes.iter_element_values().cloned().collect();
|
||||
@@ -327,7 +327,7 @@ impl BrushToolData {
|
||||
return Some(layer);
|
||||
}
|
||||
|
||||
if reference == DefinitionIdentifier::Network("Transform".into()) {
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER) {
|
||||
self.transform = get_current_transform(&node.inputs) * self.transform;
|
||||
}
|
||||
}
|
||||
@@ -478,7 +478,9 @@ impl Fsm for BrushToolFsmState {
|
||||
fn new_brush_layer(document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
|
||||
let brush_node = resolve_network_node_type("Brush").expect("Brush node does not exist").default_node_template();
|
||||
let brush_node = resolve_proto_node_type(graphene_std::brush::brush::brush::IDENTIFIER)
|
||||
.expect("Brush node does not exist")
|
||||
.default_node_template();
|
||||
|
||||
let id = NodeId::new();
|
||||
responses.add(GraphOperationMessage::NewCustomLayer {
|
||||
|
||||
@@ -344,7 +344,7 @@ struct GradientChainState {
|
||||
/// Resolve the gradient transform, type, and spread method by walking the chain feeding the layer. Transform composes all
|
||||
/// 'Transform' nodes. Type and spread method come from the closest-to-layer node of each kind, or the type default.
|
||||
fn read_gradient_chain_state(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> GradientChainState {
|
||||
let transform_reference = DefinitionIdentifier::Network("Transform".into());
|
||||
let transform_reference = DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER);
|
||||
let gradient_type_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::gradient_type::IDENTIFIER);
|
||||
let spread_method_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::spread_method::IDENTIFIER);
|
||||
|
||||
|
||||
@@ -767,7 +767,7 @@ mod test_transform_layer {
|
||||
let document = editor.active_document();
|
||||
let network_interface = &document.network_interface;
|
||||
let _responses: VecDeque<Message> = VecDeque::new();
|
||||
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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user