mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-04 10:28:11 +08:00
Replace raw node input indices with compile-time parameter symbols (#4387)
* Remove the dead InputAccessor traits and the uncallable test helper built on them * Replace raw node input indices with macro-generated parameter symbols across the editor * Audit dynamic input index usage, converting to parameter symbols and named input position constants * Abstract the remaining input index plumbing behind ParameterRef APIs and accessors * Build SetInputValue messages as struct literals to keep message enums impl-free * Delete the typed parameter markers in favor of explicit-output test introspection * Wire the interpolation control path input per chain node type * Return no input when a parameter symbol is read against the wrong node's parameter view
This commit is contained in:
@@ -2613,7 +2613,11 @@ impl DocumentMessageHandler {
|
||||
|
||||
// If there's already a boolean operation on the selected layer, update it with the new operation
|
||||
if let (Some(upstream_boolean_op), Some(only_selected_layer)) = (upstream_boolean_op, only_selected_layer) {
|
||||
network_interface.set_input(&InputConnector::node(upstream_boolean_op, 1), NodeInput::value(TaggedValue::BooleanOperation(operation), false), &[]);
|
||||
network_interface.set_input(
|
||||
&InputConnector::node(upstream_boolean_op, graphene_std::path_bool_nodes::boolean_operation::OperationInput),
|
||||
NodeInput::value(TaggedValue::BooleanOperation(operation), false),
|
||||
&[],
|
||||
);
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
|
||||
@@ -2814,7 +2818,8 @@ impl DocumentMessageHandler {
|
||||
self.network_interface.insert_node(new_index_id, new_index_template, &[]);
|
||||
self.network_interface.move_node_to_chain_start(&new_index_id, new_layer, &[], false);
|
||||
|
||||
self.network_interface.create_wire(&OutputConnector::node(solidify_id, 0), &InputConnector::node(new_index_id, 0), &[]);
|
||||
self.network_interface
|
||||
.create_wire(&OutputConnector::primary_output(solidify_id), &InputConnector::primary_input(new_index_id), &[]);
|
||||
|
||||
resulting_layers.push(layer.to_node());
|
||||
resulting_layers.push(new_layer.to_node());
|
||||
@@ -3579,7 +3584,7 @@ impl DocumentMessageHandler {
|
||||
// Showing only compatible types for the layer based on the output type of the node upstream from its horizontal input
|
||||
let compatible_type = selected_layer.and_then(|layer| {
|
||||
self.network_interface
|
||||
.upstream_output_connector(&InputConnector::node(layer.to_node(), 1), &[])
|
||||
.upstream_output_connector(&InputConnector::layer_secondary_input(layer.to_node()), &[])
|
||||
.and_then(|upstream_output| self.network_interface.output_type(&upstream_output, &[]).add_node_string())
|
||||
});
|
||||
|
||||
@@ -4335,13 +4340,13 @@ mod document_message_handler_tests {
|
||||
// A base that wrongly carried a phantom element would therefore show up as a recorded row, which this catches.
|
||||
// The `news` guard below is what keeps both assertions honest, since a wrong `Output` type empties every record.
|
||||
let base_lengths: Vec<usize> = instrumented
|
||||
.grab_all_input_as::<graphene_std::list::extend::BaseInput<graphene_std::Graphic>, graphene_std::list::List<graphene_std::Graphic>>(&editor.runtime)
|
||||
.grab_all_input::<graphene_std::list::extend::BaseInput, graphene_std::list::List<graphene_std::Graphic>>(&editor.runtime)
|
||||
.map(|base| base.len())
|
||||
.collect();
|
||||
assert!(base_lengths.iter().all(|&len| len == 0), "Every stack base should be empty, found lengths {base_lengths:?}");
|
||||
|
||||
let news: Vec<graphene_std::list::List<graphene_std::Graphic>> = instrumented
|
||||
.grab_all_input_as::<graphene_std::list::extend::NewInput<graphene_std::Graphic>, graphene_std::list::List<graphene_std::Graphic>>(&editor.runtime)
|
||||
.grab_all_input::<graphene_std::list::extend::NewInput, graphene_std::list::List<graphene_std::Graphic>>(&editor.runtime)
|
||||
.collect();
|
||||
assert!(!news.is_empty(), "Instrumentation should have recorded at least one stacked element list");
|
||||
let phantom_count = news
|
||||
|
||||
+25
-21
@@ -2,6 +2,7 @@ use super::transform_utils;
|
||||
use super::utility_types::ModifyInputsContext;
|
||||
use crate::consts::{LAYER_INDENT_OFFSET, STACK_VERTICAL_GAP};
|
||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::BLEND_PATH_INPUT_INDEX;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers;
|
||||
@@ -132,7 +133,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::SetUpstreamToChain { layer } => {
|
||||
let Some(OutputConnector::Node { node_id: first_chain_node, .. }) = network_interface.upstream_output_connector(&InputConnector::node(layer.to_node(), 1), &[]) else {
|
||||
let Some(OutputConnector::Node { node_id: first_chain_node, .. }) = network_interface.upstream_output_connector(&InputConnector::layer_secondary_input(layer.to_node()), &[]) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -181,15 +182,15 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
|
||||
// Set the bottom input of the artboard back to artboard
|
||||
let bottom_input = NodeInput::type_default(descriptor!(List<Artboard>), true);
|
||||
network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 0), bottom_input, &[]);
|
||||
network_interface.set_input(&InputConnector::primary_input(artboard_layer.to_node()), bottom_input, &[]);
|
||||
} else {
|
||||
// We have some non layers (e.g. just a rectangle node). We disconnect the bottom input and connect it to the left input.
|
||||
network_interface.disconnect_input(&InputConnector::node(artboard_layer.to_node(), 0), &[]);
|
||||
network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 1), primary_input, &[]);
|
||||
network_interface.disconnect_input(&InputConnector::primary_input(artboard_layer.to_node()), &[]);
|
||||
network_interface.set_input(&InputConnector::layer_secondary_input(artboard_layer.to_node()), primary_input, &[]);
|
||||
|
||||
// Set the bottom input of the artboard back to artboard
|
||||
let bottom_input = NodeInput::type_default(descriptor!(List<Artboard>), true);
|
||||
network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 0), bottom_input, &[]);
|
||||
network_interface.set_input(&InputConnector::primary_input(artboard_layer.to_node()), bottom_input, &[]);
|
||||
}
|
||||
}
|
||||
responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
|
||||
@@ -212,11 +213,14 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
|
||||
// Insert the main chain node (Blend or Morph) depending on whether a blend count is provided
|
||||
let (chain_node_id, layer_alias, path_alias) = if let Some(count) = blend_count {
|
||||
(modify_inputs.insert_blend_data(layer, count as f64), "Blend", "Blend Path")
|
||||
// Insert the main chain node (Blend or Morph) depending on whether a blend count is provided, referencing
|
||||
// its control path input by the Blend template's named position or the Morph proto node's parameter symbol
|
||||
let (path_input_connector, layer_alias, path_alias) = if let Some(count) = blend_count {
|
||||
let blend_node_id = modify_inputs.insert_blend_data(layer, count as f64);
|
||||
(InputConnector::node_at_index(blend_node_id, BLEND_PATH_INPUT_INDEX), "Blend", "Blend Path")
|
||||
} else {
|
||||
(modify_inputs.insert_morph_data(layer), "Morph", "Morph Path")
|
||||
let morph_node_id = modify_inputs.insert_morph_data(layer);
|
||||
(InputConnector::node(morph_node_id, graphene_std::vector::morph::PathInput), "Morph", "Morph Path")
|
||||
};
|
||||
|
||||
// Create the control path layer (Path → Auto-Tangents → Origins to Polyline)
|
||||
@@ -226,9 +230,9 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
network_interface.move_layer_to_stack(control_path_layer, parent, insert_index, &[]);
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index + 1, &[]);
|
||||
|
||||
// Connect the Path node's output to the chain node's path parameter input (input 4 for both Morph and Blend).
|
||||
// Connect the Path node's output to the chain node's control path input.
|
||||
// Done after move_layer_to_stack so chain nodes have correct positions when converted to absolute.
|
||||
network_interface.set_input(&InputConnector::node(chain_node_id, 4), NodeInput::node(path_node_id, 0), &[]);
|
||||
network_interface.set_input(&path_input_connector, NodeInput::node(path_node_id, 0), &[]);
|
||||
|
||||
responses.add(NodeGraphMessage::SetDisplayNameImpl {
|
||||
node_id: id,
|
||||
@@ -246,29 +250,29 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
control_path_id,
|
||||
} => {
|
||||
// Find the chain node (Blend or Morph, first in chain of the layer)
|
||||
let Some(OutputConnector::Node { node_id: chain_node, .. }) = network_interface.upstream_output_connector(&InputConnector::node(interpolation_layer_id, 1), &[]) else {
|
||||
let Some(OutputConnector::Node { node_id: chain_node, .. }) = network_interface.upstream_output_connector(&InputConnector::layer_secondary_input(interpolation_layer_id), &[]) else {
|
||||
log::error!("Could not find chain node for layer {interpolation_layer_id}");
|
||||
return;
|
||||
};
|
||||
|
||||
// Get what feeds into the chain node's primary input (the children stack)
|
||||
let Some(OutputConnector::Node { node_id: children_id, output_index }) = network_interface.upstream_output_connector(&InputConnector::node(chain_node, 0), &[]) else {
|
||||
let Some(OutputConnector::Node { node_id: children_id, output_index }) = network_interface.upstream_output_connector(&InputConnector::primary_input(chain_node), &[]) else {
|
||||
log::error!("Could not find children stack feeding chain node {chain_node}");
|
||||
return;
|
||||
};
|
||||
|
||||
// Find the deepest node in the control path layer's chain (Origins to Polyline)
|
||||
let mut deepest_chain_node = None;
|
||||
let mut current_connector = InputConnector::node(control_path_id, 1);
|
||||
let mut current_connector = InputConnector::layer_secondary_input(control_path_id);
|
||||
while let Some(OutputConnector::Node { node_id, .. }) = network_interface.upstream_output_connector(¤t_connector, &[]) {
|
||||
deepest_chain_node = Some(node_id);
|
||||
current_connector = InputConnector::node(node_id, 0);
|
||||
current_connector = InputConnector::primary_input(node_id);
|
||||
}
|
||||
|
||||
// Connect children to the deepest chain node's input 0 (or the layer's input 1 if no chain)
|
||||
let target_connector = match deepest_chain_node {
|
||||
Some(node_id) => InputConnector::node(node_id, 0),
|
||||
None => InputConnector::node(control_path_id, 1),
|
||||
Some(node_id) => InputConnector::primary_input(node_id),
|
||||
None => InputConnector::layer_secondary_input(control_path_id),
|
||||
};
|
||||
network_interface.set_input(&target_connector, NodeInput::node(children_id, output_index), &[]);
|
||||
|
||||
@@ -299,7 +303,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
responses.add(NodeGraphMessage::AddNodes { nodes, new_ids });
|
||||
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(layer.to_node(), 1),
|
||||
input_connector: InputConnector::layer_secondary_input(layer.to_node()),
|
||||
input: NodeInput::node(first_new_node_id, 0),
|
||||
});
|
||||
}
|
||||
@@ -368,7 +372,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
input_node: NodeInput::node(document_node.inputs[1].as_node().unwrap_or_default(), 0),
|
||||
output_nodes: network_interface
|
||||
.outward_wires(&[])
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(artboard.to_node(), 0)))
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::primary_output(artboard.to_node())))
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
merge_node: node_id,
|
||||
@@ -394,7 +398,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
for artboard in &artboard_data {
|
||||
// Modify downstream connections
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(artboard.1.merge_node, 1),
|
||||
input_connector: InputConnector::layer_secondary_input(artboard.1.merge_node),
|
||||
input: NodeInput::node(artboard.1.input_node.as_node().unwrap_or_default(), 0),
|
||||
});
|
||||
|
||||
@@ -402,7 +406,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
for outward_wire in &artboard.1.output_nodes {
|
||||
let input = NodeInput::node(artboard_data[artboard.0].merge_node, 0);
|
||||
let input_connector = match artboard_data.get(&outward_wire.node_id().unwrap_or_default()) {
|
||||
Some(artboard_info) => InputConnector::node(artboard_info.merge_node, outward_wire.input_index()),
|
||||
Some(artboard_info) => InputConnector::node_at_index(artboard_info.merge_node, outward_wire.input_index()),
|
||||
_ => *outward_wire,
|
||||
};
|
||||
responses.add(NodeGraphMessage::SetInput { input_connector, input });
|
||||
|
||||
@@ -14,10 +14,26 @@ pub fn update_transform(network_interface: &mut NodeNetworkInterface, node_id: &
|
||||
let rotation = rotation.to_degrees();
|
||||
let skew = DVec2::new(skew.atan().to_degrees(), 0.);
|
||||
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::DVec2(translation), false), &[]);
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::F64(rotation), false), &[]);
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::DVec2(scale), false), &[]);
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 4), NodeInput::value(TaggedValue::DVec2(skew), false), &[]);
|
||||
network_interface.set_input(
|
||||
&InputConnector::node(*node_id, graphene_std::transform_nodes::transform::TranslationInput),
|
||||
NodeInput::value(TaggedValue::DVec2(translation), false),
|
||||
&[],
|
||||
);
|
||||
network_interface.set_input(
|
||||
&InputConnector::node(*node_id, graphene_std::transform_nodes::transform::RotationInput),
|
||||
NodeInput::value(TaggedValue::F64(rotation), false),
|
||||
&[],
|
||||
);
|
||||
network_interface.set_input(
|
||||
&InputConnector::node(*node_id, graphene_std::transform_nodes::transform::ScaleInput),
|
||||
NodeInput::value(TaggedValue::DVec2(scale), false),
|
||||
&[],
|
||||
);
|
||||
network_interface.set_input(
|
||||
&InputConnector::node(*node_id, graphene_std::transform_nodes::transform::SkewInput),
|
||||
NodeInput::value(TaggedValue::DVec2(skew), false),
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: This should be extracted from the graph at the location of the transform node.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use super::transform_utils;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type, resolve_network_node_type, resolve_proto_node_type};
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::{
|
||||
ARTBOARD_DIMENSIONS_INPUT_INDEX, ARTBOARD_LOCATION_INPUT_INDEX, DefinitionIdentifier, resolve_document_node_type, resolve_network_node_type, resolve_proto_node_type,
|
||||
};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{self, FlowType, InputConnector, NodeNetworkInterface};
|
||||
use crate::messages::prelude::*;
|
||||
@@ -17,7 +19,7 @@ use graphene_std::subpath::Subpath;
|
||||
use graphene_std::text::{Font, TypesettingConfig};
|
||||
use graphene_std::vector::style::{GradientSpreadMethod, GradientType, Stroke};
|
||||
use graphene_std::vector::{Gradient, PointId, Vector, VectorModification, VectorModificationType};
|
||||
use graphene_std::{Artboard, Color, Graphic, NodeInputDecleration};
|
||||
use graphene_std::{Artboard, Color, Graphic};
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum TransformIn {
|
||||
@@ -370,7 +372,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
// If inserting a 'Path' node, insert a 'Combine Paths' node if the type is `Graphic`.
|
||||
// TODO: Allow the 'Path' node to operate on `List` data by utilizing the reference (index or ID?) for each item.
|
||||
if node_definition.identifier == "Path" {
|
||||
let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]);
|
||||
let layer_input_type = self.network_interface.input_type(&InputConnector::layer_secondary_input(output_layer.to_node()), &[]);
|
||||
if layer_input_type.compiled_nested_type() == Some(&concrete!(List<Graphic>)) {
|
||||
let Some(combine_paths_definition) = resolve_proto_node_type(graphene_std::vector_nodes::combine_paths::IDENTIFIER) else {
|
||||
log::error!("Combine Paths does not exist in ModifyInputsContext::existing_node_id");
|
||||
@@ -391,8 +393,8 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
let Some(fill_node_id) = self.existing_proto_node_id(graphene_std::vector_nodes::fill::IDENTIFIER, true) else {
|
||||
return;
|
||||
};
|
||||
let input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::FillInput::INDEX);
|
||||
let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupColorInput::INDEX);
|
||||
let input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::FillInput);
|
||||
let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupColorInput);
|
||||
|
||||
// The backup remembers the last solid color, so the red-slash "none" choice leaves it untouched
|
||||
if let Some(color) = color {
|
||||
@@ -406,13 +408,13 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
let Some(fill_node_id) = self.existing_proto_node_id(graphene_std::vector_nodes::fill::IDENTIFIER, true) else {
|
||||
return;
|
||||
};
|
||||
let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupGradientInput::INDEX);
|
||||
let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupGradientInput);
|
||||
|
||||
self.set_input_with_refresh(backup_input_connector, NodeInput::value(TaggedValue::Gradient(gradient.clone()), false), true);
|
||||
|
||||
// Skip the rerender on all but the last input so the whole update triggers a single graph run
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::FillInput::INDEX),
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::FillInput),
|
||||
NodeInput::value(TaggedValue::Gradient(gradient), false),
|
||||
true,
|
||||
);
|
||||
@@ -423,29 +425,29 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
.document_network()
|
||||
.nodes
|
||||
.get(&fill_node_id)
|
||||
.and_then(|node| node.inputs.get(graphene_std::vector::fill::TransformInput::INDEX))
|
||||
.and_then(|node| node.input(graphene_std::vector::fill::TransformInput))
|
||||
.is_some_and(|input| input.as_value().is_some());
|
||||
if transform_is_value {
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::HasTransformInput::INDEX),
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::HasTransformInput),
|
||||
NodeInput::value(TaggedValue::Bool(true), false),
|
||||
true,
|
||||
);
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::TransformInput::INDEX),
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::TransformInput),
|
||||
NodeInput::value(TaggedValue::DAffine2(transform), false),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::GradientTypeInput::INDEX),
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::GradientTypeInput),
|
||||
NodeInput::value(TaggedValue::GradientType(gradient_type), false),
|
||||
true,
|
||||
);
|
||||
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::SpreadMethodInput::INDEX),
|
||||
InputConnector::node(fill_node_id, graphene_std::vector::fill::SpreadMethodInput),
|
||||
NodeInput::value(TaggedValue::GradientSpreadMethod(spread_method), false),
|
||||
false,
|
||||
);
|
||||
@@ -455,7 +457,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
let Some(blend_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::blend_mode::IDENTIFIER, true) else {
|
||||
return;
|
||||
};
|
||||
let input_connector = InputConnector::node(blend_node_id, graphene_std::blending_nodes::blend_mode::BlendModeInput::INDEX);
|
||||
let input_connector = InputConnector::node(blend_node_id, graphene_std::blending_nodes::blend_mode::BlendModeInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::BlendMode(blend_mode), false), false);
|
||||
}
|
||||
|
||||
@@ -465,12 +467,12 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
};
|
||||
// Enable the `has_opacity` checkbox so the value is applied
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasOpacityInput::INDEX),
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasOpacityInput),
|
||||
NodeInput::value(TaggedValue::Bool(true), false),
|
||||
false,
|
||||
);
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::OpacityInput::INDEX),
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::OpacityInput),
|
||||
NodeInput::value(TaggedValue::F64(opacity * 100.), false),
|
||||
false,
|
||||
);
|
||||
@@ -487,19 +489,19 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
// Freshly-created node defaults to opacity enabled; disable it so the fill slider works independently
|
||||
if !existed {
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasOpacityInput::INDEX),
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasOpacityInput),
|
||||
NodeInput::value(TaggedValue::Bool(false), false),
|
||||
false,
|
||||
);
|
||||
}
|
||||
// Enable the `has_fill` checkbox so the value is applied
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasFillInput::INDEX),
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::HasFillInput),
|
||||
NodeInput::value(TaggedValue::Bool(true), false),
|
||||
false,
|
||||
);
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::FillInput::INDEX),
|
||||
InputConnector::node(opacity_node_id, graphene_std::blending_nodes::opacity::FillInput),
|
||||
NodeInput::value(TaggedValue::F64(fill * 100.), false),
|
||||
false,
|
||||
);
|
||||
@@ -513,7 +515,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
let target = gradient_chain_target_input(output_layer, self.network_interface);
|
||||
let starts_layer_chain = target == InputConnector::node(output_layer.to_node(), 1);
|
||||
let starts_layer_chain = target == InputConnector::layer_secondary_input(output_layer.to_node());
|
||||
|
||||
// The Gradient Value node discards its primary input, so starting a chain ahead of existing layer content would drop that content; refuse instead
|
||||
if starts_layer_chain && self.network_interface.upstream_output_connector(&target, &[]).is_some() {
|
||||
@@ -546,7 +548,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
let input_connector = InputConnector::node(gradient_value_id, graphene_std::math_nodes::gradient_value::GradientInput::INDEX);
|
||||
let input_connector = InputConnector::node(gradient_value_id, graphene_std::math_nodes::gradient_value::GradientInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Gradient(stops), false), false);
|
||||
}
|
||||
|
||||
@@ -624,7 +626,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
return;
|
||||
};
|
||||
|
||||
let input_connector = InputConnector::node(node_id, graphene_std::math_nodes::gradient_type::GradientTypeInput::INDEX);
|
||||
let input_connector = InputConnector::node(node_id, graphene_std::math_nodes::gradient_type::GradientTypeInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::GradientType(gradient_type), false), false);
|
||||
}
|
||||
|
||||
@@ -640,7 +642,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
return;
|
||||
};
|
||||
|
||||
let input_connector = InputConnector::node(node_id, graphene_std::math_nodes::spread_method::SpreadMethodInput::INDEX);
|
||||
let input_connector = InputConnector::node(node_id, graphene_std::math_nodes::spread_method::SpreadMethodInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::GradientSpreadMethod(spread_method), false), false);
|
||||
}
|
||||
|
||||
@@ -649,7 +651,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
let Some(clip_node_id) = self.existing_proto_node_id(graphene_std::blending_nodes::clipping_mask::IDENTIFIER, true) else {
|
||||
return;
|
||||
};
|
||||
let input_connector = InputConnector::node(clip_node_id, graphene_std::blending_nodes::clipping_mask::ClipInput::INDEX);
|
||||
let input_connector = InputConnector::node(clip_node_id, graphene_std::blending_nodes::clipping_mask::ClipInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Bool(clip), false), false);
|
||||
}
|
||||
|
||||
@@ -658,23 +660,23 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
return;
|
||||
};
|
||||
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(color.map_or_else(TaggedValue::no_paint, TaggedValue::Color), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::WeightInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::WeightInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.weight), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::AlignInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::AlignInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::StrokeAlign(stroke.align), false), false);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::CapInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::CapInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::StrokeCap(stroke.cap), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::JoinInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::JoinInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::StrokeJoin(stroke.join), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::MiterLimitInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::MiterLimitInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.join_miter_limit), false), false);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintOrderInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintOrderInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::PaintOrder(stroke.paint_order), false), false);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashPatternInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashPatternInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::DashPattern(stroke.dash_lengths.into()), false), true);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashOffsetInput::INDEX);
|
||||
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashOffsetInput);
|
||||
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.dash_offset), false), true);
|
||||
}
|
||||
|
||||
@@ -775,7 +777,11 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
let Some(brush_node_id) = self.existing_proto_node_id(graphene_std::brush::brush::brush::IDENTIFIER, true) else {
|
||||
return;
|
||||
};
|
||||
self.set_input_with_refresh(InputConnector::node(brush_node_id, 1), NodeInput::value(TaggedValue::BrushStrokes(strokes), false), false);
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node(brush_node_id, graphene_std::brush::brush::brush::TraceInput),
|
||||
NodeInput::value(TaggedValue::BrushStrokes(strokes), false),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn resize_artboard(&mut self, location: DVec2, dimensions: DVec2) {
|
||||
@@ -794,8 +800,16 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
dimensions.y = -dimensions.y;
|
||||
location.y -= dimensions.y;
|
||||
}
|
||||
self.set_input_with_refresh(InputConnector::node(artboard_node_id, 2), NodeInput::value(TaggedValue::DVec2(location), false), false);
|
||||
self.set_input_with_refresh(InputConnector::node(artboard_node_id, 3), NodeInput::value(TaggedValue::DVec2(dimensions), false), false);
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node_at_index(artboard_node_id, ARTBOARD_LOCATION_INPUT_INDEX),
|
||||
NodeInput::value(TaggedValue::DVec2(location), false),
|
||||
false,
|
||||
);
|
||||
self.set_input_with_refresh(
|
||||
InputConnector::node_at_index(artboard_node_id, ARTBOARD_DIMENSIONS_INPUT_INDEX),
|
||||
NodeInput::value(TaggedValue::DVec2(dimensions), false),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Set the input, refresh the Properties panel, and run the document graph if skip_rerender is false
|
||||
|
||||
@@ -53,6 +53,13 @@ impl NodePropertiesContext<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Input position of the location parameter on the "Artboard" layer template, which follows the two layer-shaped inputs (stack and content) and maps through to the inner Create Artboard proto node.
|
||||
pub const ARTBOARD_LOCATION_INPUT_INDEX: usize = 2;
|
||||
/// Input position of the dimensions parameter on the "Artboard" layer template, which follows the two layer-shaped inputs (stack and content) and maps through to the inner Create Artboard proto node.
|
||||
pub const ARTBOARD_DIMENSIONS_INPUT_INDEX: usize = 3;
|
||||
/// Input position of the control path parameter on the "Blend" network node template.
|
||||
pub const BLEND_PATH_INPUT_INDEX: usize = 4;
|
||||
|
||||
/// The key used to access definitions for a network node or proto node.
|
||||
/// For proto nodes, this is their [`ProtoNodeIdentifier`].
|
||||
/// For network nodes, it doesn't necessarily have to be the same as the network's display name, but it often is.
|
||||
@@ -1050,7 +1057,7 @@ fn static_input_properties() -> InputProperties {
|
||||
});
|
||||
|
||||
Ok(vec![LayoutGroup::row(node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, blank_assist, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, blank_assist, context),
|
||||
number_input,
|
||||
))])
|
||||
}),
|
||||
@@ -1091,7 +1098,7 @@ fn static_input_properties() -> InputProperties {
|
||||
};
|
||||
// NOTE: The bool input MUST be at the input index directly before the f64 input!
|
||||
Ok(vec![LayoutGroup::row(node_properties::optional_f64_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, false, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, false, context),
|
||||
index - 1,
|
||||
number_input,
|
||||
))])
|
||||
@@ -1104,7 +1111,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let number_input = NumberInput::default().percentage().min(0.).max(100.);
|
||||
Ok(vec![LayoutGroup::row(node_properties::optional_f64_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, false, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, false, context),
|
||||
index - 1,
|
||||
number_input,
|
||||
))])
|
||||
@@ -1151,7 +1158,7 @@ fn static_input_properties() -> InputProperties {
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(vec![node_properties::vec2_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
&x,
|
||||
&y,
|
||||
&unit,
|
||||
@@ -1165,7 +1172,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let scale = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default().min(0.).disabled(!coherent_noise_active),
|
||||
);
|
||||
Ok(vec![scale.into()])
|
||||
@@ -1174,7 +1181,7 @@ fn static_input_properties() -> InputProperties {
|
||||
map.insert(
|
||||
"noise_properties_noise_type".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let noise_type_row = enum_choice::<NoiseType>().for_socket(ParameterWidgetsInfo::new(node_id, index, true, context)).property_row();
|
||||
let noise_type_row = enum_choice::<NoiseType>().for_socket(ParameterWidgetsInfo::at_index(node_id, index, true, context)).property_row();
|
||||
Ok(vec![noise_type_row, LayoutGroup::row(Vec::new())])
|
||||
}),
|
||||
);
|
||||
@@ -1183,7 +1190,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let domain_warp_type = enum_choice::<DomainWarpType>()
|
||||
.for_socket(ParameterWidgetsInfo::new(node_id, index, true, context))
|
||||
.for_socket(ParameterWidgetsInfo::at_index(node_id, index, true, context))
|
||||
.disabled(!coherent_noise_active)
|
||||
.property_row();
|
||||
Ok(vec![domain_warp_type])
|
||||
@@ -1194,7 +1201,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (_, coherent_noise_active, _, _, domain_warp_active, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let domain_warp_amplitude = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default().min(0.).disabled(!coherent_noise_active || !domain_warp_active),
|
||||
);
|
||||
Ok(vec![domain_warp_amplitude.into(), LayoutGroup::row(Vec::new())])
|
||||
@@ -1205,7 +1212,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_type_row = enum_choice::<FractalType>()
|
||||
.for_socket(ParameterWidgetsInfo::new(node_id, index, true, context))
|
||||
.for_socket(ParameterWidgetsInfo::at_index(node_id, index, true, context))
|
||||
.disabled(!coherent_noise_active)
|
||||
.property_row();
|
||||
Ok(vec![fractal_type_row])
|
||||
@@ -1216,7 +1223,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_octaves = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(1.)
|
||||
@@ -1233,7 +1240,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_lacunarity = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(0.)
|
||||
@@ -1248,7 +1255,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_gain = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(0.)
|
||||
@@ -1263,7 +1270,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_weighted_strength = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(0.)
|
||||
@@ -1278,7 +1285,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (fractal_active, coherent_noise_active, _, ping_pong_active, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let fractal_ping_pong_strength = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.min(0.)
|
||||
@@ -1293,7 +1300,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let cellular_distance_function_row = enum_choice::<CellularDistanceFunction>()
|
||||
.for_socket(ParameterWidgetsInfo::new(node_id, index, true, context))
|
||||
.for_socket(ParameterWidgetsInfo::at_index(node_id, index, true, context))
|
||||
.disabled(!coherent_noise_active || !cellular_noise_active)
|
||||
.property_row();
|
||||
Ok(vec![cellular_distance_function_row])
|
||||
@@ -1304,7 +1311,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let cellular_return_type = enum_choice::<CellularReturnType>()
|
||||
.for_socket(ParameterWidgetsInfo::new(node_id, index, true, context))
|
||||
.for_socket(ParameterWidgetsInfo::at_index(node_id, index, true, context))
|
||||
.disabled(!coherent_noise_active || !cellular_noise_active)
|
||||
.property_row();
|
||||
Ok(vec![cellular_return_type])
|
||||
@@ -1315,7 +1322,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
|
||||
let cellular_jitter = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default()
|
||||
.mode_range()
|
||||
.range_min(Some(0.))
|
||||
@@ -1328,7 +1335,7 @@ fn static_input_properties() -> InputProperties {
|
||||
map.insert(
|
||||
"assign_colors_gradient".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let gradient_row = node_properties::color_widget(ParameterWidgetsInfo::new(node_id, index, true, context), ColorInput::default().allow_none(false));
|
||||
let gradient_row = node_properties::color_widget(ParameterWidgetsInfo::at_index(node_id, index, true, context), ColorInput::default().allow_none(false));
|
||||
Ok(vec![gradient_row])
|
||||
}),
|
||||
);
|
||||
@@ -1337,7 +1344,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let randomize_enabled = node_properties::query_assign_colors_randomize(node_id, context)?;
|
||||
let seed_row = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default().min(0.).int().disabled(!randomize_enabled),
|
||||
);
|
||||
Ok(vec![seed_row.into()])
|
||||
@@ -1348,7 +1355,7 @@ fn static_input_properties() -> InputProperties {
|
||||
Box::new(|node_id, index, context| {
|
||||
let randomize_enabled = node_properties::query_assign_colors_randomize(node_id, context)?;
|
||||
let repeat_every_row = node_properties::number_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
NumberInput::default().min(0.).int().disabled(randomize_enabled),
|
||||
);
|
||||
Ok(vec![repeat_every_row.into()])
|
||||
@@ -1357,7 +1364,7 @@ fn static_input_properties() -> InputProperties {
|
||||
map.insert(
|
||||
"transform_rotation".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let mut widgets = node_properties::start_widgets(ParameterWidgetsInfo::new(node_id, index, true, context));
|
||||
let mut widgets = node_properties::start_widgets(&ParameterWidgetsInfo::at_index(node_id, index, true, context));
|
||||
|
||||
let document_node = node_properties::get_document_node(node_id, context)?;
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
@@ -1371,7 +1378,7 @@ fn static_input_properties() -> InputProperties {
|
||||
.mode(NumberInputMode::Range)
|
||||
.range_min(Some(-180.))
|
||||
.range_max(Some(180.))
|
||||
.on_update(node_properties::update_value(
|
||||
.on_update(node_properties::update_value_at_index(
|
||||
|number_input: &NumberInput| TaggedValue::F64(number_input.value.unwrap()),
|
||||
node_id,
|
||||
index,
|
||||
@@ -1389,7 +1396,7 @@ fn static_input_properties() -> InputProperties {
|
||||
"transform_translation".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
Ok(vec![node_properties::vec2_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
"X",
|
||||
"Y",
|
||||
" px",
|
||||
@@ -1401,13 +1408,22 @@ fn static_input_properties() -> InputProperties {
|
||||
// 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)])),
|
||||
Box::new(|node_id, index, context| {
|
||||
Ok(vec![node_properties::vec2_widget(
|
||||
ParameterWidgetsInfo::at_index(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(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let mut widgets = node_properties::start_widgets(ParameterWidgetsInfo::new(node_id, index, true, context));
|
||||
let mut widgets = node_properties::start_widgets(&ParameterWidgetsInfo::at_index(node_id, index, true, context));
|
||||
|
||||
let document_node = node_properties::get_document_node(node_id, context)?;
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
@@ -1421,7 +1437,7 @@ fn static_input_properties() -> InputProperties {
|
||||
.unit("°")
|
||||
.min(-89.9)
|
||||
.max(89.9)
|
||||
.on_update(node_properties::update_value(
|
||||
.on_update(node_properties::update_value_at_index(
|
||||
move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), val.y)),
|
||||
node_id,
|
||||
index,
|
||||
@@ -1434,7 +1450,7 @@ fn static_input_properties() -> InputProperties {
|
||||
.unit("°")
|
||||
.min(-89.9)
|
||||
.max(89.9)
|
||||
.on_update(node_properties::update_value(
|
||||
.on_update(node_properties::update_value_at_index(
|
||||
move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(val.x, input.value.unwrap())),
|
||||
node_id,
|
||||
index,
|
||||
@@ -1449,7 +1465,7 @@ fn static_input_properties() -> InputProperties {
|
||||
);
|
||||
map.insert(
|
||||
"text_area".to_string(),
|
||||
Box::new(|node_id, index, context| Ok(vec![LayoutGroup::row(node_properties::text_area_widget(ParameterWidgetsInfo::new(node_id, index, true, context)))])),
|
||||
Box::new(|node_id, index, context| Ok(vec![LayoutGroup::row(node_properties::text_area_widget(ParameterWidgetsInfo::at_index(node_id, index, true, context)))])),
|
||||
);
|
||||
map.insert(
|
||||
"text_font".to_string(),
|
||||
@@ -1458,7 +1474,7 @@ fn static_input_properties() -> InputProperties {
|
||||
if context.fonts.font_catalog.is_empty() {
|
||||
context.responses.add(FontsMessage::LoadCatalog);
|
||||
}
|
||||
let (font, style) = node_properties::font_inputs(ParameterWidgetsInfo::new(node_id, index, true, context));
|
||||
let (font, style) = node_properties::font_inputs(ParameterWidgetsInfo::at_index(node_id, index, true, context));
|
||||
let mut result = vec![LayoutGroup::row(font)];
|
||||
if let Some(style) = style {
|
||||
result.push(LayoutGroup::row(style));
|
||||
@@ -1470,7 +1486,7 @@ fn static_input_properties() -> InputProperties {
|
||||
"artboard_background".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
Ok(vec![node_properties::color_widget(
|
||||
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||
ParameterWidgetsInfo::at_index(node_id, index, true, context),
|
||||
ColorInput::default().allow_none(false),
|
||||
)])
|
||||
}),
|
||||
@@ -1478,7 +1494,9 @@ fn static_input_properties() -> InputProperties {
|
||||
map.insert(
|
||||
"text_align".to_string(),
|
||||
Box::new(|node_id, index, context| {
|
||||
let choices = enum_choice::<text::TextAlign>().for_socket(ParameterWidgetsInfo::new(node_id, index, true, context)).property_row();
|
||||
let choices = enum_choice::<text::TextAlign>()
|
||||
.for_socket(ParameterWidgetsInfo::at_index(node_id, index, true, context))
|
||||
.property_row();
|
||||
Ok(vec![choices])
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -234,8 +234,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
x: (mid_point.x / 24.) as i32,
|
||||
y: (mid_point.y / 24.) as i32,
|
||||
});
|
||||
let node_input_connector = InputConnector::node(node_id, 0);
|
||||
let node_output_connector = OutputConnector::node(node_id, 0);
|
||||
let node_input_connector = InputConnector::primary_input(node_id);
|
||||
let node_output_connector = OutputConnector::primary_output(node_id);
|
||||
responses.add(NodeGraphMessage::CreateWire {
|
||||
output_connector,
|
||||
input_connector: node_input_connector,
|
||||
@@ -301,7 +301,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
// A freshly added Text node carries no font, so give it the default font (registered like the Text tool does)
|
||||
if node_type == DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER) {
|
||||
let font_resource_id = graph_craft::application_io::resource::ResourceId::new();
|
||||
if let Some(font_input) = node_template.inputs.get_mut(graphene_std::text::text::FontInput::INDEX) {
|
||||
if let Some(font_input) = node_template.input_mut(graphene_std::text::text::FontInput) {
|
||||
*font_input = NodeInput::value(TaggedValue::Resource(font_resource_id), false);
|
||||
}
|
||||
responses.add(DocumentMessage::Resource(ResourceMessage::AddFont {
|
||||
@@ -339,7 +339,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
if let Some((input_index, _)) = node_template.inputs.iter().enumerate().find(|(_, input)| input.is_exposed()) {
|
||||
responses.add(NodeGraphMessage::CreateWire {
|
||||
output_connector: *output_connector,
|
||||
input_connector: InputConnector::node(node_id, input_index),
|
||||
input_connector: InputConnector::node_at_index(node_id, input_index),
|
||||
});
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
@@ -493,7 +493,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
*exposed = set_to_exposed;
|
||||
}
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, input_index),
|
||||
input_connector: InputConnector::node_at_index(node_id, input_index),
|
||||
input: node_input,
|
||||
});
|
||||
|
||||
@@ -515,7 +515,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
return;
|
||||
};
|
||||
|
||||
let encapsulating_connector = InputConnector::node(*node_id, 0);
|
||||
let encapsulating_connector = InputConnector::primary_input(*node_id);
|
||||
if !exposed {
|
||||
network_interface.disconnect_input(&encapsulating_connector, network_path);
|
||||
}
|
||||
@@ -554,7 +554,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
|
||||
// Disconnect all connections in the encapsulating network
|
||||
if let Some((encapsulating_node, encapsulating_path)) = breadcrumb_network_path.split_last() {
|
||||
network_interface.disconnect_output_wires(&OutputConnector::node(*encapsulating_node, 0), encapsulating_path);
|
||||
network_interface.disconnect_output_wires(&OutputConnector::primary_output(*encapsulating_node), encapsulating_path);
|
||||
}
|
||||
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
@@ -596,7 +596,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
// Ensure that nodes can be grouped by checking if there is an unselected node between selected nodes
|
||||
for selected_node_id in &selected_node_ids {
|
||||
for input_index in 0..network_interface.number_of_inputs(selected_node_id, breadcrumb_network_path) {
|
||||
let input_connector = InputConnector::node(*selected_node_id, input_index);
|
||||
let input_connector = InputConnector::node_at_index(*selected_node_id, input_index);
|
||||
if let Some(upstream_deselected_node_id) = network_interface
|
||||
.upstream_output_connector(&input_connector, breadcrumb_network_path)
|
||||
.and_then(|output_connector| output_connector.node_id())
|
||||
@@ -618,7 +618,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
for node_id in nodes_sorted_top_to_bottom {
|
||||
for input_index in 0..network_interface.number_of_inputs(&node_id, breadcrumb_network_path) {
|
||||
let current_input_connector = InputConnector::node(node_id, input_index);
|
||||
let current_input_connector = InputConnector::node_at_index(node_id, input_index);
|
||||
let Some(upstream_connector) = network_interface.upstream_output_connector(¤t_input_connector, breadcrumb_network_path) else {
|
||||
continue;
|
||||
};
|
||||
@@ -715,7 +715,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
for (input_index, output_connector) in input_connections.into_iter().enumerate() {
|
||||
responses.add(NodeGraphMessage::CreateWire {
|
||||
output_connector,
|
||||
input_connector: InputConnector::node(encapsulating_node_id, input_index),
|
||||
input_connector: InputConnector::node_at_index(encapsulating_node_id, input_index),
|
||||
});
|
||||
}
|
||||
for (output_index, input_connectors) in output_connections.into_iter().enumerate() {
|
||||
@@ -1359,7 +1359,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
{
|
||||
network_interface.try_set_upstream_to_chain(&InputConnector::node(layer, 1), selection_network_path);
|
||||
network_interface.try_set_upstream_to_chain(&InputConnector::layer_secondary_input(layer), selection_network_path);
|
||||
}
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
|
||||
@@ -1370,9 +1370,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
// Check if a single node was dragged onto a wire and that the node was dragged onto the wire
|
||||
if selected_nodes.selected_nodes_ref().len() == 1 && !self.begin_dragging {
|
||||
let selected_node_id = selected_nodes.selected_nodes_ref()[0];
|
||||
let has_primary_output_connection = network_interface
|
||||
.outward_wires(selection_network_path)
|
||||
.is_some_and(|outward_wires| outward_wires.get(&OutputConnector::node(selected_node_id, 0)).is_some_and(|outward_wires| !outward_wires.is_empty()));
|
||||
let has_primary_output_connection = network_interface.outward_wires(selection_network_path).is_some_and(|outward_wires| {
|
||||
outward_wires
|
||||
.get(&OutputConnector::primary_output(selected_node_id))
|
||||
.is_some_and(|outward_wires| !outward_wires.is_empty())
|
||||
});
|
||||
if !has_primary_output_connection {
|
||||
let Some(network) = network_interface.nested_network(selection_network_path) else {
|
||||
return;
|
||||
@@ -1397,7 +1399,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
// Prevent inserting on a link that is connected upstream to the selected node
|
||||
for upstream_node in network_interface.upstream_flow_back_from_nodes(vec![selected_node_id], selection_network_path, network_interface::FlowType::UpstreamFlow) {
|
||||
for input_index in 0..network_interface.number_of_inputs(&upstream_node, selection_network_path) {
|
||||
wires_to_check.remove(&InputConnector::node(upstream_node, input_index));
|
||||
wires_to_check.remove(&InputConnector::node_at_index(upstream_node, input_index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1554,7 +1556,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
for selected_node in &all_selected_nodes {
|
||||
// Handle inputs of selected node
|
||||
for input_index in 0..network_interface.number_of_inputs(selected_node, selection_network_path) {
|
||||
let input_connector = InputConnector::node(*selected_node, input_index);
|
||||
let input_connector = InputConnector::node_at_index(*selected_node, input_index);
|
||||
// Only disconnect inputs to non selected nodes
|
||||
if network_interface
|
||||
.upstream_output_connector(&input_connector, selection_network_path)
|
||||
@@ -1565,13 +1567,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
|
||||
let number_of_outputs = network_interface.number_of_outputs(selected_node, selection_network_path);
|
||||
let mut first_deselected_upstream_output = network_interface.upstream_output_connector(&InputConnector::node(*selected_node, 0), selection_network_path);
|
||||
let mut first_deselected_upstream_output = network_interface.upstream_output_connector(&InputConnector::primary_input(*selected_node), selection_network_path);
|
||||
while let Some(OutputConnector::Node { node_id, .. }) = &first_deselected_upstream_output {
|
||||
if !all_selected_nodes.contains(node_id) {
|
||||
break;
|
||||
}
|
||||
|
||||
first_deselected_upstream_output = network_interface.upstream_output_connector(&InputConnector::node(*node_id, 0), selection_network_path);
|
||||
first_deselected_upstream_output = network_interface.upstream_output_connector(&InputConnector::primary_input(*node_id), selection_network_path);
|
||||
}
|
||||
|
||||
let Some(outward_wires) = network_interface.outward_wires(selection_network_path) else {
|
||||
@@ -1594,7 +1596,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
// Handle reconnection
|
||||
// Find first non selected upstream node by primary flow
|
||||
if let Some(first_deselected_upstream_output) = first_deselected_upstream_output {
|
||||
let Some(downstream_connections_to_first_output) = outward_wires.get(&OutputConnector::node(*selected_node, 0)).cloned() else {
|
||||
let Some(downstream_connections_to_first_output) = outward_wires.get(&OutputConnector::primary_output(*selected_node)).cloned() else {
|
||||
log::error!("Could not get downstream_connections_to_first_output in shake node");
|
||||
return;
|
||||
};
|
||||
@@ -1765,7 +1767,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
|
||||
let input = NodeInput::value(value, false);
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, input_index),
|
||||
input_connector: InputConnector::node_at_index(node_id, input_index),
|
||||
input,
|
||||
});
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
@@ -2608,7 +2610,7 @@ impl NodeGraphMessageHandler {
|
||||
.popover_layout({
|
||||
let compatible_type = context
|
||||
.network_interface
|
||||
.upstream_output_connector(&InputConnector::node(layer, 1), &[])
|
||||
.upstream_output_connector(&InputConnector::layer_secondary_input(layer), &[])
|
||||
.and_then(|upstream_output| context.network_interface.output_type(&upstream_output, &[]).add_node_string());
|
||||
|
||||
let mut node_chooser = NodeCatalog::new();
|
||||
@@ -2705,7 +2707,7 @@ impl NodeGraphMessageHandler {
|
||||
};
|
||||
let mut nodes = Vec::new();
|
||||
for (node_id, visible) in network.nodes.iter().map(|(node_id, node)| (*node_id, node.visible)).collect::<Vec<_>>() {
|
||||
let primary_input_connector = InputConnector::node(node_id, 0);
|
||||
let primary_input_connector = InputConnector::primary_input(node_id);
|
||||
|
||||
let primary_input = if network_interface
|
||||
.input_from_connector(&primary_input_connector, breadcrumb_network_path)
|
||||
@@ -2716,10 +2718,10 @@ impl NodeGraphMessageHandler {
|
||||
None
|
||||
};
|
||||
let exposed_inputs = (1..network_interface.number_of_inputs(&node_id, breadcrumb_network_path))
|
||||
.filter_map(|input_index| network_interface.frontend_input_from_connector(&InputConnector::node(node_id, input_index), breadcrumb_network_path))
|
||||
.filter_map(|input_index| network_interface.frontend_input_from_connector(&InputConnector::node_at_index(node_id, input_index), breadcrumb_network_path))
|
||||
.collect();
|
||||
|
||||
let primary_output = network_interface.frontend_output_from_connector(&OutputConnector::node(node_id, 0), breadcrumb_network_path);
|
||||
let primary_output = network_interface.frontend_output_from_connector(&OutputConnector::primary_output(node_id), breadcrumb_network_path);
|
||||
|
||||
let exposed_outputs = (1..network_interface.number_of_outputs(&node_id, breadcrumb_network_path))
|
||||
.filter_map(|output_index| network_interface.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), breadcrumb_network_path))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::storage_metadata::{StorageMetadataView, build_interface_from_storage};
|
||||
use crate::test_utils::test_prelude::*;
|
||||
use graphene_std::NodeParameter;
|
||||
use graphene_std::vector::style::RenderMode;
|
||||
|
||||
/// Every node addressable in `original` resolves identically through the round-tripped interface:
|
||||
@@ -722,21 +723,14 @@ fn find_fill_node(document: &DocumentMessageHandler) -> (Vec<graph_craft::docume
|
||||
|
||||
/// The stored paint value of the document's single Fill node.
|
||||
fn fill_paint_value(document: &DocumentMessageHandler) -> graph_craft::document::value::TaggedValue {
|
||||
use graphene_std::NodeInputDecleration as _;
|
||||
|
||||
let (network_path, node_id) = find_fill_node(document);
|
||||
let network = document.network_interface.nested_network(&network_path).expect("the found network path should resolve");
|
||||
let input = network.nodes[&node_id]
|
||||
.inputs
|
||||
.get(graphene_std::vector::fill::FillInput::INDEX)
|
||||
.expect("Fill should have a paint input");
|
||||
let input = network.nodes[&node_id].input(graphene_std::vector::fill::FillInput).expect("Fill should have a paint input");
|
||||
input.as_value().expect("the paint input should hold a value").clone()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn none_fill_survives_document_reopen() {
|
||||
use graphene_std::NodeInputDecleration as _;
|
||||
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.drag_tool(ToolType::Rectangle, 0., 0., 100., 100., ModifierKeys::empty()).await;
|
||||
@@ -769,7 +763,6 @@ async fn none_fill_survives_document_reopen() {
|
||||
#[tokio::test]
|
||||
async fn legacy_four_input_fill_migrates_to_the_split_transform_shape() {
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::NodeInputDecleration as _;
|
||||
|
||||
// A minimal master-era document: a 4-input Fill (content, fill: wired, backup color, backup gradient) fed by another node
|
||||
const LEGACY_DOCUMENT: &str = r#"{"network_interface":{"network":{"exports":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"nodes":[[1,{"inputs":[{"Value":{"tagged_value":{"GraphicGroup":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Node":{"node_id":2,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"Gradient":{"stops":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]],"gradient_type":"Linear","start":[0.0,0.5],"end":[1.0,0.5],"transform":[1.0,0.0,0.0,1.0,0.0,0.0]}},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::vector::FillNode"}},"visible":true,"skip_deduplication":false}],[2,{"inputs":[{"Value":{"tagged_value":"None","exposed":false}},{"Value":{"tagged_value":{"GradientStops":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}},{"Value":{"tagged_value":{"F64":0.5},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::ops::SampleGradientNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]},"network_metadata":{"persistent_metadata":{"node_metadata":[[1,{"persistent_metadata":{"reference":"Fill","display_name":"","input_properties":[{"input_data":{"input_name":"Vector Data"},"widget_override":null},{"input_data":{"input_name":"Fill"},"widget_override":null},{"input_data":{"input_name":"Backup Color"},"widget_override":null},{"input_data":{"input_name":"Backup Gradient"},"widget_override":null}],"output_names":["Future<Instances<VectorData>>"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}],[2,{"persistent_metadata":{"reference":"Sample Gradient","display_name":"","input_properties":[{"input_data":{"input_name":"Primary"},"widget_override":null},{"input_data":{"input_name":"Gradient"},"widget_override":null},{"input_data":{"input_name":"Position"},"widget_override":null}],"output_names":["Future<Color>"],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[-20,0]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}},"collapsed":[],"name":"legacy_fill.graphite","commit_hash":"0000000000000000000000000000000000000000","document_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"document_mode":"DesignMode","view_mode":"Normal","overlays_visibility_settings":{"all":true,"artboard_name":true,"compass_rose":true,"quick_measurement":true,"transform_measurement":true,"transform_cage":true,"hover_outline":true,"selection_outline":true,"pivot":true,"path":true,"anchors":true,"handles":true},"rulers_visible":true,"snapping_state":{"snapping_enabled":true,"grid_snapping":false,"artboards":true,"tolerance":8.0,"bounding_box":{"center_point":true,"corner_point":true,"edge_midpoint":true,"align_with_edges":true,"distribute_evenly":true},"path":{"anchor_point":true,"line_midpoint":true,"along_path":true,"normal_to_path":true,"tangent_to_path":true,"path_intersection_point":true,"align_with_anchor_point":true,"perpendicular_from_endpoint":true},"grid":{"origin":[0.0,0.0],"grid_type":{"Rectangular":{"spacing":[1.0,1.0]}},"grid_color":{"red":0.6,"green":0.6,"blue":0.6,"alpha":1.0},"dot_display":false}},"graph_view_overlay_open":false,"graph_fade_artwork_percentage":80.0}"#;
|
||||
@@ -789,20 +782,20 @@ async fn legacy_four_input_fill_migrates_to_the_split_transform_shape() {
|
||||
let document = editor.active_document();
|
||||
let (network_path, node_id) = find_fill_node(document);
|
||||
let network = document.network_interface.nested_network(&network_path).expect("the found network path should resolve");
|
||||
let inputs = &network.nodes[&node_id].inputs;
|
||||
let fill_node = &network.nodes[&node_id];
|
||||
|
||||
assert_eq!(inputs.len(), 8, "the legacy Fill should upgrade to the 8-input shape");
|
||||
let paint = &inputs[graphene_std::vector::fill::FillInput::INDEX];
|
||||
assert_eq!(fill_node.inputs.len(), 8, "the legacy Fill should upgrade to the 8-input shape");
|
||||
let paint = fill_node.input(graphene_std::vector::fill::FillInput);
|
||||
assert!(
|
||||
matches!(paint, graph_craft::document::NodeInput::Node { .. }),
|
||||
matches!(paint, Some(graph_craft::document::NodeInput::Node { .. })),
|
||||
"the wired legacy fill should keep its connection, but became {paint:?}"
|
||||
);
|
||||
let has_transform = inputs[graphene_std::vector::fill::HasTransformInput::INDEX].as_value();
|
||||
let has_transform = fill_node.input_value(graphene_std::vector::fill::HasTransformInput);
|
||||
assert!(
|
||||
matches!(has_transform, Some(TaggedValue::Bool(_))),
|
||||
"the has-transform input should hold a bool, but became {has_transform:?}"
|
||||
);
|
||||
let transform = inputs[graphene_std::vector::fill::TransformInput::INDEX].as_value();
|
||||
let transform = fill_node.input_value(graphene_std::vector::fill::TransformInput);
|
||||
assert!(
|
||||
matches!(transform, Some(TaggedValue::DAffine2(_))),
|
||||
"the transform input should hold a matrix, but became {transform:?}"
|
||||
|
||||
@@ -145,7 +145,7 @@ impl NodeNetworkInterface {
|
||||
};
|
||||
let Some(first_downstream_input) = self.with_outward_wires(network_path, |outward_wires| {
|
||||
outward_wires
|
||||
.get(&OutputConnector::node(current_node, 0))
|
||||
.get(&OutputConnector::primary_output(current_node))
|
||||
.map(|layer_outward_wires| layer_outward_wires.first().copied())
|
||||
}) else {
|
||||
log::error!("Cannot load outward wires in load_stack_dependents");
|
||||
@@ -565,9 +565,13 @@ impl NodeNetworkInterface {
|
||||
for (current_node_id, node) in network.nodes.iter() {
|
||||
for (input_index, input) in node.inputs.iter().enumerate() {
|
||||
if let NodeInput::Node { node_id, output_index, .. } = input {
|
||||
push_outward_wire(&mut outward_wires, OutputConnector::node(*node_id, *output_index), InputConnector::node(*current_node_id, input_index));
|
||||
push_outward_wire(
|
||||
&mut outward_wires,
|
||||
OutputConnector::node(*node_id, *output_index),
|
||||
InputConnector::node_at_index(*current_node_id, input_index),
|
||||
);
|
||||
} else if let NodeInput::Import { import_index, .. } = input {
|
||||
push_outward_wire(&mut outward_wires, OutputConnector::Import(*import_index), InputConnector::node(*current_node_id, input_index));
|
||||
push_outward_wire(&mut outward_wires, OutputConnector::Import(*import_index), InputConnector::node_at_index(*current_node_id, input_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,7 +757,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
for (node_id, node) in &network.nodes {
|
||||
for input_index in 0..node.inputs.len() {
|
||||
input_connectors.push(InputConnector::node(*node_id, input_index));
|
||||
input_connectors.push(InputConnector::node_at_index(*node_id, input_index));
|
||||
}
|
||||
}
|
||||
input_connectors
|
||||
@@ -792,7 +796,7 @@ impl NodeNetworkInterface {
|
||||
input_connectors.extend(inputs.clone())
|
||||
}
|
||||
for input_index in 0..self.number_of_inputs(node_id, network_path) {
|
||||
input_connectors.push(InputConnector::node(*node_id, input_index));
|
||||
input_connectors.push(InputConnector::node_at_index(*node_id, input_index));
|
||||
}
|
||||
for input in input_connectors {
|
||||
self.unload_wire(&input, network_path);
|
||||
@@ -1154,7 +1158,7 @@ impl NodeNetworkInterface {
|
||||
LayerPosition::Absolute(position) => Some(position),
|
||||
LayerPosition::Stack(y_offset) => {
|
||||
let Some(downstream_node_connectors) = self
|
||||
.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)).cloned())
|
||||
.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::primary_output(*node_id)).cloned())
|
||||
.flatten()
|
||||
else {
|
||||
log::error!("Could not get downstream node in position_from_downstream_node");
|
||||
@@ -1188,7 +1192,7 @@ impl NodeNetworkInterface {
|
||||
loop {
|
||||
// TODO: Use root node to restore if previewing
|
||||
let Some(downstream_node_connectors) = self
|
||||
.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(current_node_id, 0)).cloned())
|
||||
.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::primary_output(current_node_id)).cloned())
|
||||
.flatten()
|
||||
else {
|
||||
log::error!("Could not get downstream node for node {node_id} with Position::Chain");
|
||||
|
||||
+11
-11
@@ -63,7 +63,7 @@ async fn deleting_a_node_with_children_prunes_them_from_the_selection() {
|
||||
assert!(network_interface.number_of_inputs(&parent, &[]) >= 2, "Test needs a secondary input to wire the child into");
|
||||
|
||||
// Wire the child into the parent's secondary input so it is a sole dependent, then select both and delete only the parent
|
||||
network_interface.set_input(&InputConnector::node(parent, 1), NodeInput::node(child, 0), &[]);
|
||||
network_interface.set_input(&InputConnector::node_at_index(parent, 1), NodeInput::node(child, 0), &[]);
|
||||
network_interface.selected_nodes_mut(&[]).unwrap().set_selected_nodes(vec![parent, child]);
|
||||
network_interface.delete_nodes(vec![parent], true, &[]);
|
||||
|
||||
@@ -86,8 +86,8 @@ async fn deleting_a_node_keeps_children_shared_with_other_nodes() {
|
||||
let network_interface = &mut editor.active_document_mut().network_interface;
|
||||
|
||||
// Wire the same child into the secondary inputs of both nodes, then delete only the parent along with its children
|
||||
network_interface.set_input(&InputConnector::node(parent, 1), NodeInput::node(shared_child, 0), &[]);
|
||||
network_interface.set_input(&InputConnector::node(sibling, 1), NodeInput::node(shared_child, 0), &[]);
|
||||
network_interface.set_input(&InputConnector::node_at_index(parent, 1), NodeInput::node(shared_child, 0), &[]);
|
||||
network_interface.set_input(&InputConnector::node_at_index(sibling, 1), NodeInput::node(shared_child, 0), &[]);
|
||||
network_interface.delete_nodes(vec![parent], true, &[]);
|
||||
|
||||
let nodes = &network_interface.document_network().nodes;
|
||||
@@ -107,14 +107,14 @@ async fn cyclic_connection_is_rejected_without_side_effects() {
|
||||
let b = editor.create_node_by_name(rectangle_definition()).await;
|
||||
|
||||
let network_interface = &mut editor.active_document_mut().network_interface;
|
||||
network_interface.set_input(&InputConnector::node(a, 1), NodeInput::node(b, 0), &[]);
|
||||
network_interface.set_input(&InputConnector::node_at_index(a, 1), NodeInput::node(b, 0), &[]);
|
||||
|
||||
// Attempt to complete a cycle inside a transaction: the edit must be rejected without marking the transaction as modified
|
||||
network_interface.start_transaction();
|
||||
let input_before = network_interface.input_from_connector(&InputConnector::node(b, 1), &[]).cloned();
|
||||
network_interface.set_input(&InputConnector::node(b, 1), NodeInput::node(a, 0), &[]);
|
||||
let input_before = network_interface.input_from_connector(&InputConnector::node_at_index(b, 1), &[]).cloned();
|
||||
network_interface.set_input(&InputConnector::node_at_index(b, 1), NodeInput::node(a, 0), &[]);
|
||||
|
||||
let input_after = network_interface.input_from_connector(&InputConnector::node(b, 1), &[]).cloned();
|
||||
let input_after = network_interface.input_from_connector(&InputConnector::node_at_index(b, 1), &[]).cloned();
|
||||
assert_eq!(input_before, input_after, "A rejected cyclic connection should leave the input unchanged");
|
||||
assert_eq!(
|
||||
network_interface.transaction_status(),
|
||||
@@ -204,13 +204,13 @@ async fn layer_stacking_follows_wiring() {
|
||||
|
||||
// Wiring a layer into the bottom input of another layer converts it to stack positioning at its current visual spot
|
||||
let lower_position_before = network_interface.position(&lower, &[]).expect("Lower layer should have a position");
|
||||
network_interface.create_wire(&OutputConnector::node(lower, 0), &InputConnector::node(upper, 0), &[]);
|
||||
network_interface.create_wire(&OutputConnector::primary_output(lower), &InputConnector::primary_input(upper), &[]);
|
||||
assert!(network_interface.is_stack(&lower, &[]), "A layer feeding the bottom of a layer should be stack positioned");
|
||||
let stacked_position = network_interface.position(&lower, &[]).expect("Stacked layer should have a position");
|
||||
assert_eq!(stacked_position.y, lower_position_before.y, "Stacking should preserve the layer's vertical position");
|
||||
|
||||
// Disconnecting converts the layer back to absolute positioning without moving it
|
||||
network_interface.disconnect_input(&InputConnector::node(upper, 0), &[]);
|
||||
network_interface.disconnect_input(&InputConnector::primary_input(upper), &[]);
|
||||
assert!(network_interface.is_absolute(&lower, &[]), "A disconnected stack layer should return to absolute positioning");
|
||||
assert_eq!(network_interface.position(&lower, &[]), Some(stacked_position), "Unstacking should not move the layer");
|
||||
|
||||
@@ -229,7 +229,7 @@ async fn chain_membership_follows_wiring() {
|
||||
network_interface.set_to_node_or_layer(&layer, &[], true);
|
||||
|
||||
// A node wired into a layer's secondary input from the same row, within chain distance, joins the chain
|
||||
network_interface.create_wire(&OutputConnector::node(node, 0), &InputConnector::node(layer, 1), &[]);
|
||||
network_interface.create_wire(&OutputConnector::primary_output(node), &InputConnector::layer_secondary_input(layer), &[]);
|
||||
assert!(
|
||||
network_interface.is_chain(&node, &[]),
|
||||
"A node feeding a layer's secondary input from chain range should become a chain node"
|
||||
@@ -237,7 +237,7 @@ async fn chain_membership_follows_wiring() {
|
||||
|
||||
// Disconnecting breaks the chain and the node becomes absolute at its chain spot
|
||||
let chained_y = network_interface.position(&node, &[]).expect("Chained node should have a position").y;
|
||||
network_interface.disconnect_input(&InputConnector::node(layer, 1), &[]);
|
||||
network_interface.disconnect_input(&InputConnector::layer_secondary_input(layer), &[]);
|
||||
assert!(!network_interface.is_chain(&node, &[]), "Disconnecting should break the chain");
|
||||
assert!(network_interface.is_absolute(&node, &[]));
|
||||
assert_eq!(network_interface.position(&node, &[]).map(|position| position.y), Some(chained_y));
|
||||
|
||||
@@ -220,7 +220,7 @@ impl NodeNetworkInterface {
|
||||
transient_node_metadata
|
||||
.port_click_targets
|
||||
.clicked_input_port_from_point(point)
|
||||
.map(|port| InputConnector::node(*node_id, port))
|
||||
.map(|port| InputConnector::node_at_index(*node_id, port))
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
|
||||
@@ -105,7 +105,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
let downstream_connection_count = self
|
||||
.with_outward_wires(network_path, |outward_wires| {
|
||||
outward_wires.get(&OutputConnector::node(upstream_node, 0)).map(|connections| connections.len())
|
||||
outward_wires.get(&OutputConnector::primary_output(upstream_node)).map(|connections| connections.len())
|
||||
})
|
||||
.flatten();
|
||||
let Some(downstream_connection_count) = downstream_connection_count else {
|
||||
@@ -155,7 +155,7 @@ impl NodeNetworkInterface {
|
||||
break;
|
||||
};
|
||||
downstream_layer = outward_wires
|
||||
.get(&OutputConnector::node(downstream_layer_id, 0))
|
||||
.get(&OutputConnector::primary_output(downstream_layer_id))
|
||||
.and_then(|outward_wires| if outward_wires.len() == 1 { outward_wires[0].node_id() } else { None });
|
||||
} else {
|
||||
break;
|
||||
@@ -170,7 +170,7 @@ impl NodeNetworkInterface {
|
||||
pub(crate) fn try_set_node_to_chain(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
|
||||
if let Some(outward_wires) = self
|
||||
.outward_wires(network_path)
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)))
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::primary_output(*node_id)))
|
||||
.cloned() && outward_wires.len() == 1
|
||||
{
|
||||
self.try_set_upstream_to_chain(&outward_wires[0], network_path)
|
||||
@@ -182,7 +182,7 @@ impl NodeNetworkInterface {
|
||||
if !self.is_layer(upstream_id, network_path)
|
||||
&& self
|
||||
.outward_wires(network_path)
|
||||
.is_some_and(|outward_wires| outward_wires.get(&OutputConnector::node(*upstream_id, 0)).is_some_and(|outward_wires| outward_wires.len() == 1))
|
||||
.is_some_and(|outward_wires| outward_wires.get(&OutputConnector::primary_output(*upstream_id)).is_some_and(|outward_wires| outward_wires.len() == 1))
|
||||
{
|
||||
self.set_chain_position(upstream_id, network_path);
|
||||
}
|
||||
@@ -312,7 +312,7 @@ impl NodeNetworkInterface {
|
||||
&& let LayerPosition::Stack(offset) = layer_metadata.position
|
||||
{
|
||||
// If the upstream layer is selected, then skip
|
||||
let Some(outward_wires) = self.outward_wires(network_path).and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0))) else {
|
||||
let Some(outward_wires) = self.outward_wires(network_path).and_then(|outward_wires| outward_wires.get(&OutputConnector::primary_output(*node_id))) else {
|
||||
log::error!("Could not get outward wires in shift_selected_nodes");
|
||||
return;
|
||||
};
|
||||
@@ -365,7 +365,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
let Some(downstream_node) = self
|
||||
.outward_wires(network_path)
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(downstream_absolute_layer, 0)))
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::primary_output(downstream_absolute_layer)))
|
||||
.and_then(|downstream_nodes| downstream_nodes.first())
|
||||
.and_then(|downstream_node| downstream_node.node_id())
|
||||
else {
|
||||
@@ -692,7 +692,7 @@ impl NodeNetworkInterface {
|
||||
// 1. Disconnect old upstream from post_node, wire layer output to post_node
|
||||
self.set_input_for_import(&post_node, layer_output, network_path);
|
||||
// 2. Wire old upstream into layer's primary (stack) input
|
||||
self.set_input_for_import(&InputConnector::node(layer.to_node(), 0), post_node_input, network_path);
|
||||
self.set_input_for_import(&InputConnector::primary_input(layer.to_node()), post_node_input, network_path);
|
||||
}
|
||||
NodeInput::Import { .. } => {
|
||||
log::error!("Cannot insert import layer into a parent that connects to the imports");
|
||||
@@ -856,7 +856,7 @@ impl NodeNetworkInterface {
|
||||
match post_node_input {
|
||||
// Create a new stack
|
||||
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Inline(_) | NodeInput::Reflection(_) => {
|
||||
self.create_wire(&OutputConnector::node(layer.to_node(), 0), &post_node, network_path);
|
||||
self.create_wire(&OutputConnector::primary_output(layer.to_node()), &post_node, network_path);
|
||||
|
||||
let final_layer_position = after_move_post_layer_position + IVec2::new(-LAYER_INDENT_OFFSET, STACK_VERTICAL_GAP);
|
||||
let shift = final_layer_position - previous_layer_position;
|
||||
@@ -884,7 +884,7 @@ impl NodeNetworkInterface {
|
||||
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Inline(_) | NodeInput::Reflection(_) => {
|
||||
let offset = after_move_post_layer_position - previous_layer_position + IVec2::new(0, STACK_VERTICAL_GAP + height_above_layer);
|
||||
self.shift_absolute_node_position(&layer.to_node(), offset, network_path);
|
||||
self.create_wire(&OutputConnector::node(layer.to_node(), 0), &post_node, network_path);
|
||||
self.create_wire(&OutputConnector::primary_output(layer.to_node()), &post_node, network_path);
|
||||
}
|
||||
// Insert into the stack
|
||||
NodeInput::Node { .. } => {
|
||||
@@ -903,7 +903,7 @@ impl NodeNetworkInterface {
|
||||
self.insert_node_between(&layer.to_node(), &post_node, 0, network_path);
|
||||
|
||||
// Get the other wires which need to be moved to the output of the moved layer
|
||||
let layer_input_connector = InputConnector::node(layer.to_node(), 0);
|
||||
let layer_input_connector = InputConnector::primary_input(layer.to_node());
|
||||
let other_outward_wires = self
|
||||
.upstream_output_connector(&layer_input_connector, network_path)
|
||||
.and_then(|pre_node_output| self.outward_wires(network_path).and_then(|wires| wires.get(&pre_node_output)))
|
||||
@@ -919,7 +919,7 @@ impl NodeNetworkInterface {
|
||||
// Disconnect and reconnect
|
||||
for other_outward_wire in &other_outward_wires {
|
||||
self.disconnect_input(other_outward_wire, network_path);
|
||||
self.create_wire(&OutputConnector::node(layer.to_node(), 0), other_outward_wire, network_path);
|
||||
self.create_wire(&OutputConnector::primary_output(layer.to_node()), other_outward_wire, network_path);
|
||||
}
|
||||
}
|
||||
self.unload_upstream_node_click_targets(vec![layer.to_node()], network_path);
|
||||
@@ -941,10 +941,10 @@ impl NodeNetworkInterface {
|
||||
self.disconnect_input(input_connector, network_path);
|
||||
|
||||
// Connect the input connector to the new node
|
||||
self.create_wire(&OutputConnector::node(*node_id, 0), input_connector, network_path);
|
||||
self.create_wire(&OutputConnector::primary_output(*node_id), input_connector, network_path);
|
||||
|
||||
// Connect the new node to the previous node
|
||||
self.create_wire(&upstream_output, &InputConnector::node(*node_id, insert_node_input_index), network_path);
|
||||
self.create_wire(&upstream_output, &InputConnector::node_at_index(*node_id, insert_node_input_index), network_path);
|
||||
}
|
||||
|
||||
/// Inserts the freshly-created `node_id` onto the wire feeding `input_connector`: the previous upstream becomes the
|
||||
@@ -962,11 +962,11 @@ impl NodeNetworkInterface {
|
||||
return;
|
||||
};
|
||||
|
||||
if self.input_from_connector(&InputConnector::node(*node_id, 0), network_path).is_none() {
|
||||
if self.input_from_connector(&InputConnector::primary_input(*node_id), network_path).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.set_input(&InputConnector::node(*node_id, 0), current_input, network_path);
|
||||
self.set_input(&InputConnector::primary_input(*node_id), current_input, network_path);
|
||||
self.set_input(input_connector, NodeInput::node(*node_id, 0), network_path);
|
||||
|
||||
// If `set_input` chain-positioned the node (it joined a layer chain), there's nothing more to do.
|
||||
@@ -990,7 +990,7 @@ impl NodeNetworkInterface {
|
||||
/// Moves a node to the start of a layer chain (feeding into the secondary input of the layer).
|
||||
/// When `import` is true, uses lightweight wiring that skips `is_acyclic` checks and per-node cache invalidation.
|
||||
pub fn move_node_to_chain_start(&mut self, node_id: &NodeId, parent: LayerNodeIdentifier, network_path: &[NodeId], import: bool) {
|
||||
let parent_input = InputConnector::node(parent.to_node(), 1);
|
||||
let parent_input = InputConnector::layer_secondary_input(parent.to_node());
|
||||
let Some(current_input) = self.input_from_connector(&parent_input, network_path).cloned() else {
|
||||
log::error!("Could not get input for node {node_id}");
|
||||
return;
|
||||
@@ -999,7 +999,7 @@ impl NodeNetworkInterface {
|
||||
// Chain is empty: wire the node as the first (and only) entry in the chain
|
||||
if matches!(current_input, NodeInput::Value { .. }) {
|
||||
// A node whose exposed primary defaults to no value inherits the layer's content value, so the chain keeps producing the layer's content type
|
||||
let node_primary = InputConnector::node(*node_id, 0);
|
||||
let node_primary = InputConnector::primary_input(*node_id);
|
||||
let default_is_valueless = self
|
||||
.input_from_connector(&node_primary, network_path)
|
||||
.is_some_and(|input| matches!(input, NodeInput::Value { tagged_value, exposed: true } if matches!(**tagged_value, TaggedValue::None)));
|
||||
@@ -1015,7 +1015,7 @@ impl NodeNetworkInterface {
|
||||
if import {
|
||||
self.set_input_for_import(&parent_input, NodeInput::node(*node_id, 0), network_path);
|
||||
} else {
|
||||
self.create_wire(&OutputConnector::node(*node_id, 0), &parent_input, network_path);
|
||||
self.create_wire(&OutputConnector::primary_output(*node_id), &parent_input, network_path);
|
||||
}
|
||||
|
||||
// Mark this lone node as chain-positioned
|
||||
@@ -1026,7 +1026,7 @@ impl NodeNetworkInterface {
|
||||
// Wire: [parent] -> [new node] -> [existing node]
|
||||
if import {
|
||||
self.set_input_for_import(&parent_input, NodeInput::node(*node_id, 0), network_path);
|
||||
self.set_input_for_import(&InputConnector::node(*node_id, 0), current_input, network_path);
|
||||
self.set_input_for_import(&InputConnector::primary_input(*node_id), current_input, network_path);
|
||||
} else {
|
||||
self.insert_node_between(node_id, &parent_input, 0, network_path);
|
||||
}
|
||||
@@ -1075,7 +1075,7 @@ impl NodeNetworkInterface {
|
||||
let tail_input = if let Some(source) = pinned_source {
|
||||
NodeInput::node(source, 0)
|
||||
} else {
|
||||
let Some(input) = self.input_from_connector(&InputConnector::node(*chain.last().unwrap(), 0), network_path).cloned() else {
|
||||
let Some(input) = self.input_from_connector(&InputConnector::primary_input(*chain.last().unwrap()), network_path).cloned() else {
|
||||
log::error!("Could not get the upstream input of the chain in reorder_chain_node");
|
||||
return;
|
||||
};
|
||||
@@ -1084,15 +1084,15 @@ impl NodeNetworkInterface {
|
||||
|
||||
// Disconnect first so the rewiring can't transiently form a cycle (the pinned source keeps its wiring)
|
||||
for &chain_node in reorderable {
|
||||
self.disconnect_input(&InputConnector::node(chain_node, 0), network_path);
|
||||
self.disconnect_input(&InputConnector::primary_input(chain_node), network_path);
|
||||
}
|
||||
|
||||
// Rewire in the new order: layer's secondary input -> new_order[0] -> ... -> new_order[last] -> tail input
|
||||
self.set_input(&InputConnector::node(layer, 1), NodeInput::node(new_order[0], 0), network_path);
|
||||
self.set_input(&InputConnector::layer_secondary_input(layer), NodeInput::node(new_order[0], 0), network_path);
|
||||
for pair in new_order.windows(2) {
|
||||
self.set_input(&InputConnector::node(pair[0], 0), NodeInput::node(pair[1], 0), network_path);
|
||||
self.set_input(&InputConnector::primary_input(pair[0]), NodeInput::node(pair[1], 0), network_path);
|
||||
}
|
||||
self.set_input(&InputConnector::node(*new_order.last().unwrap(), 0), tail_input, network_path);
|
||||
self.set_input(&InputConnector::primary_input(*new_order.last().unwrap()), tail_input, network_path);
|
||||
|
||||
// Re-establish chain positioning for the reordered nodes
|
||||
self.force_set_upstream_to_chain(&new_order[0], network_path);
|
||||
|
||||
+16
-13
@@ -875,7 +875,7 @@ impl NodeNetworkInterface {
|
||||
// If the layer feeds into the bottom input of layer, and has no other outputs, set its position to stack at its previous y position
|
||||
let multiple_outward_wires = self
|
||||
.outward_wires(network_path)
|
||||
.and_then(|all_outward_wires| all_outward_wires.get(&OutputConnector::node(*upstream_node_id, 0)))
|
||||
.and_then(|all_outward_wires| all_outward_wires.get(&OutputConnector::primary_output(*upstream_node_id)))
|
||||
.is_some_and(|outward_wires| outward_wires.len() > 1);
|
||||
if *input_index == 0 && !multiple_outward_wires {
|
||||
self.set_stack_position_calculated_offset(upstream_node_id, downstream_node_id, network_path);
|
||||
@@ -920,7 +920,7 @@ impl NodeNetworkInterface {
|
||||
let old_upstream_node_is_layer = self.is_layer(&old_upstream_node_id, network_path);
|
||||
let Some(outward_wires) = self
|
||||
.outward_wires(network_path)
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(old_upstream_node_id, 0)))
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::primary_output(old_upstream_node_id)))
|
||||
else {
|
||||
log::error!("Could not get outward wires in set_input");
|
||||
return;
|
||||
@@ -1120,11 +1120,14 @@ impl NodeNetworkInterface {
|
||||
|
||||
// Perform an upstream traversal to try delete children for secondary inputs
|
||||
let mut upstream_nodes = (1..self.number_of_inputs(node_id, network_path))
|
||||
.filter_map(|input_index| self.upstream_output_connector(&InputConnector::node(*node_id, input_index), network_path).and_then(|oc| oc.node_id()))
|
||||
.filter_map(|input_index| {
|
||||
self.upstream_output_connector(&InputConnector::node_at_index(*node_id, input_index), network_path)
|
||||
.and_then(|oc| oc.node_id())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
while let Some(upstream_node) = upstream_nodes.pop() {
|
||||
// Add the upstream nodes to the traversal
|
||||
for input_connector in (0..self.number_of_inputs(&upstream_node, network_path)).map(|input_index| InputConnector::node(upstream_node, input_index)) {
|
||||
for input_connector in (0..self.number_of_inputs(&upstream_node, network_path)).map(|input_index| InputConnector::node_at_index(upstream_node, input_index)) {
|
||||
if let Some(upstream_node) = self.upstream_output_connector(&input_connector, network_path).and_then(|oc| oc.node_id()) {
|
||||
upstream_nodes.push(upstream_node);
|
||||
}
|
||||
@@ -1162,7 +1165,7 @@ impl NodeNetworkInterface {
|
||||
|
||||
// Disconnect every input by position, since hidden inputs make the displayed count undershoot the index of a later exposed wire
|
||||
for input_index in 0..self.number_of_inputs(delete_node_id, network_path) {
|
||||
self.disconnect_input(&InputConnector::node(*delete_node_id, input_index), network_path);
|
||||
self.disconnect_input(&InputConnector::node_at_index(*delete_node_id, input_index), network_path);
|
||||
}
|
||||
|
||||
let Some(network) = self.network_mut(network_path) else {
|
||||
@@ -1245,7 +1248,7 @@ impl NodeNetworkInterface {
|
||||
&& let Some(reconnect_input) = &reconnect_to_input
|
||||
{
|
||||
reconnect_node = reconnect_input.as_node().and_then(|node_id| if self.is_stack(&node_id, network_path) { Some(node_id) } else { None });
|
||||
self.disconnect_input(&InputConnector::node(*node_id, 0), network_path);
|
||||
self.disconnect_input(&InputConnector::primary_input(*node_id), network_path);
|
||||
self.set_input(downstream_input, reconnect_input.clone(), network_path);
|
||||
}
|
||||
}
|
||||
@@ -1451,7 +1454,7 @@ impl NodeNetworkInterface {
|
||||
if self.is_layer(&upstream_sibling_id, network_path)
|
||||
&& self
|
||||
.outward_wires(network_path)
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(upstream_sibling_id, 0)))
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::primary_output(upstream_sibling_id)))
|
||||
.is_some_and(|outward_wires| outward_wires.len() == 1)
|
||||
{
|
||||
self.set_stack_position_calculated_offset(&upstream_sibling_id, node_id, network_path);
|
||||
@@ -1472,7 +1475,7 @@ impl NodeNetworkInterface {
|
||||
.outward_wires(network_path)
|
||||
.and_then(|outward_wires| {
|
||||
outward_wires
|
||||
.get(&OutputConnector::node(*node_id, 0))
|
||||
.get(&OutputConnector::primary_output(*node_id))
|
||||
.and_then(|outward_wires| (outward_wires.len() == 1).then(|| outward_wires[0]))
|
||||
.and_then(|downstream_connector| if downstream_connector.input_index() == 0 { downstream_connector.node_id() } else { None })
|
||||
})
|
||||
@@ -1497,7 +1500,7 @@ impl NodeNetworkInterface {
|
||||
|
||||
// Try build the chain
|
||||
if is_layer {
|
||||
self.try_set_upstream_to_chain(&InputConnector::node(*node_id, 1), network_path);
|
||||
self.try_set_upstream_to_chain(&InputConnector::layer_secondary_input(*node_id), network_path);
|
||||
} else {
|
||||
self.try_set_node_to_chain(node_id, network_path);
|
||||
}
|
||||
@@ -1557,7 +1560,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
// The export is not clicked
|
||||
else {
|
||||
new_export = Some(OutputConnector::node(toggle_id, 0));
|
||||
new_export = Some(OutputConnector::primary_output(toggle_id));
|
||||
|
||||
// There is currently a dashed line being drawn
|
||||
if let Previewing::Yes { root_node_to_restore } = self.previewing(network_path) {
|
||||
@@ -1565,7 +1568,7 @@ impl NodeNetworkInterface {
|
||||
if let Some(root_node_to_restore) = root_node_to_restore {
|
||||
// If the node with the solid line is clicked, then start previewing that node without restore
|
||||
if root_node_to_restore.node_id == toggle_id {
|
||||
new_export = Some(OutputConnector::node(toggle_id, 0));
|
||||
new_export = Some(OutputConnector::primary_output(toggle_id));
|
||||
new_previewing_state = Previewing::Yes { root_node_to_restore: None };
|
||||
} else {
|
||||
// Root node to restore does not change
|
||||
@@ -1581,7 +1584,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
// Not previewing, there is no dashed line being drawn
|
||||
else {
|
||||
new_export = Some(OutputConnector::node(toggle_id, 0));
|
||||
new_export = Some(OutputConnector::primary_output(toggle_id));
|
||||
new_previewing_state = Previewing::Yes {
|
||||
root_node_to_restore: Some(RootNode {
|
||||
node_id: previous_export_id,
|
||||
@@ -1593,7 +1596,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
// The primary export is disconnected, so preview the node with nothing to restore, which disconnects the export again when the preview ends
|
||||
else {
|
||||
new_export = Some(OutputConnector::node(toggle_id, 0));
|
||||
new_export = Some(OutputConnector::primary_output(toggle_id));
|
||||
new_previewing_state = Previewing::Yes { root_node_to_restore: None };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ impl NodeNetworkInterface {
|
||||
while !self.is_layer(&id, network_path) {
|
||||
id = self.with_outward_wires(network_path, |outward_wires| {
|
||||
outward_wires
|
||||
.get(&OutputConnector::node(id, 0))
|
||||
.get(&OutputConnector::primary_output(id))
|
||||
.and_then(|connections| connections.first())
|
||||
.and_then(|connector| connector.node_id())
|
||||
})??;
|
||||
@@ -125,7 +125,7 @@ impl NodeNetworkInterface {
|
||||
layers.push(current_node);
|
||||
} else {
|
||||
let downstream_found = self.with_outward_wires(network_path, |outward_wires| {
|
||||
let Some(connections) = outward_wires.get(&OutputConnector::node(current_node, 0)) else {
|
||||
let Some(connections) = outward_wires.get(&OutputConnector::primary_output(current_node)) else {
|
||||
return false;
|
||||
};
|
||||
stack.extend(connections.iter().filter_map(|input_connector| input_connector.node_id()));
|
||||
@@ -185,7 +185,7 @@ impl NodeNetworkInterface {
|
||||
self.create_node_template(node_id, network_path).and_then(|mut node_template| {
|
||||
// TODO: Get downstream connections from all outputs
|
||||
let Some(has_selected_node_downstream) = self.with_outward_wires(network_path, |outward_wires| {
|
||||
outward_wires.get(&OutputConnector::node(*node_id, 0)).is_some_and(|outputs| {
|
||||
outward_wires.get(&OutputConnector::primary_output(*node_id)).is_some_and(|outputs| {
|
||||
outputs
|
||||
.iter()
|
||||
.any(|input_connector| input_connector.node_id().is_some_and(|upstream_id| new_ids.keys().any(|key| *key == upstream_id)))
|
||||
@@ -241,7 +241,7 @@ impl NodeNetworkInterface {
|
||||
for old_id in new_nodes.iter().map(|(_, old_id, _)| *old_id).collect::<Vec<_>>() {
|
||||
// Try set all selected nodes upstream of a layer to be chain nodes
|
||||
if self.is_layer(&old_id, network_path) {
|
||||
for valid_upstream_chain_node in self.valid_upstream_chain_nodes(&InputConnector::node(old_id, 1), network_path) {
|
||||
for valid_upstream_chain_node in self.valid_upstream_chain_nodes(&InputConnector::layer_secondary_input(old_id), network_path) {
|
||||
if let Some(node_template) = new_nodes.iter_mut().find_map(|(_, old_id, template)| (*old_id == valid_upstream_chain_node).then_some(template)) {
|
||||
match &mut node_template.node_type_metadata {
|
||||
NodeTypePersistentMetadata::Node(node_metadata) => node_metadata.position = NodePosition::Chain,
|
||||
@@ -269,12 +269,12 @@ impl NodeNetworkInterface {
|
||||
*input = NodeInput::Node { node_id: new_id, output_index };
|
||||
} else {
|
||||
// Disconnect node input if it is not connected to another node in new_ids
|
||||
let tagged_value = self.tagged_value_from_input(&InputConnector::node(*node_id, input_index), network_path);
|
||||
let tagged_value = self.tagged_value_from_input(&InputConnector::node_at_index(*node_id, input_index), network_path);
|
||||
*input = NodeInput::value(tagged_value, true);
|
||||
}
|
||||
} else if let &mut NodeInput::Import { .. } = input {
|
||||
// Always disconnect network node input
|
||||
let tagged_value = self.tagged_value_from_input(&InputConnector::node(*node_id, input_index), network_path);
|
||||
let tagged_value = self.tagged_value_from_input(&InputConnector::node_at_index(*node_id, input_index), network_path);
|
||||
*input = NodeInput::value(tagged_value, true);
|
||||
}
|
||||
}
|
||||
@@ -624,7 +624,7 @@ impl NodeNetworkInterface {
|
||||
let mut post_node_input_connector = if parent == LayerNodeIdentifier::ROOT_PARENT {
|
||||
InputConnector::Export(0)
|
||||
} else {
|
||||
InputConnector::node(parent.to_node(), 1)
|
||||
InputConnector::layer_secondary_input(parent.to_node())
|
||||
};
|
||||
// Skip layers based on skip_layer_nodes, which inserts the new layer at a certain index of the layer stack.
|
||||
let mut current_index = 0;
|
||||
@@ -644,7 +644,7 @@ impl NodeNetworkInterface {
|
||||
current_index += 1;
|
||||
}
|
||||
// Input as a sibling to the Layer node above
|
||||
post_node_input_connector = InputConnector::node(*next_node_in_stack_id, 0);
|
||||
post_node_input_connector = InputConnector::primary_input(*next_node_in_stack_id);
|
||||
} else {
|
||||
log::error!("Error getting post node: insert_index out of bounds");
|
||||
break;
|
||||
@@ -660,7 +660,7 @@ impl NodeNetworkInterface {
|
||||
match pre_node_output_connector {
|
||||
Some(OutputConnector::Node { node_id: pre_node_id, .. }) if !self.is_layer(&pre_node_id, network_path) => {
|
||||
// Update post_node_input_connector for the next iteration
|
||||
post_node_input_connector = InputConnector::node(pre_node_id, 0);
|
||||
post_node_input_connector = InputConnector::primary_input(pre_node_id);
|
||||
// Insert directly under layer if moving to the end of a layer stack that ends with a non layer node that does not have an exposed primary input
|
||||
let primary_is_exposed = self.input_from_connector(&post_node_input_connector, network_path).is_some_and(|input| input.is_exposed());
|
||||
if !primary_is_exposed {
|
||||
@@ -805,7 +805,7 @@ impl NodeNetworkInterface {
|
||||
};
|
||||
let description = input_metadata.input_description.to_string();
|
||||
let name = if input_metadata.input_name.is_empty() {
|
||||
self.input_type(&InputConnector::node(*node_id, input_index), network_path).resolved_type_node_string()
|
||||
self.input_type(&InputConnector::node_at_index(*node_id, input_index), network_path).resolved_type_node_string()
|
||||
} else {
|
||||
input_metadata.input_name.to_string()
|
||||
};
|
||||
@@ -846,7 +846,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
|
||||
pub fn primary_output_connected_to_layer(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||
let Some(downstream_connectors) = self.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)).cloned()) else {
|
||||
let Some(downstream_connectors) = self.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::primary_output(*node_id)).cloned()) else {
|
||||
log::error!("Could not get outward_wires in primary_output_connected_to_layer");
|
||||
return false;
|
||||
};
|
||||
|
||||
+10
-8
@@ -129,7 +129,7 @@ impl NodeNetworkInterface {
|
||||
let outward_wires = self.with_outward_wires(&node_path, |map| map.get(&OutputConnector::Import(*input_index)).cloned()).flatten();
|
||||
let Some(outward_wires) = outward_wires else { return false };
|
||||
outward_wires.iter().any(|connector| match connector {
|
||||
InputConnector::Node { node_id, input_index } => self.input_has_error(&InputConnector::node(*node_id, *input_index), &node_path),
|
||||
InputConnector::Node { node_id, input_index } => self.input_has_error(&InputConnector::node_at_index(*node_id, *input_index), &node_path),
|
||||
InputConnector::Export(_) => false,
|
||||
})
|
||||
}
|
||||
@@ -166,7 +166,7 @@ impl NodeNetworkInterface {
|
||||
let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else {
|
||||
return TypeSource::Error("Could not get type of import in document network since it has no imports");
|
||||
};
|
||||
self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path)
|
||||
self.input_type(&InputConnector::node_at_index(*encapsulating_node, *import_index), encapsulating_path)
|
||||
}
|
||||
NodeInput::Scope(_) => TypeSource::Compiled(concrete!(())),
|
||||
NodeInput::Reflection(document_node_metadata) => TypeSource::Compiled(document_node_metadata.ty()),
|
||||
@@ -259,7 +259,7 @@ impl NodeNetworkInterface {
|
||||
let node_io = &entry.io;
|
||||
// Check if this NodeIOTypes implementation is valid for the other inputs
|
||||
let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
|
||||
let input_type = self.input_type_not_invalid(&InputConnector::node(*node_id, iterator_index), network_path);
|
||||
let input_type = self.input_type_not_invalid(&InputConnector::node_at_index(*node_id, iterator_index), network_path);
|
||||
// TODO: Fix type checking for different call arguments
|
||||
// For example a node input of (Footprint) -> Vector would not be compatible with a node that is called with () and returns Vector
|
||||
node_io.inputs.get(iterator_index).map(|ty| ty.nested_type()) == input_type.compiled_nested_type()
|
||||
@@ -292,7 +292,7 @@ impl NodeNetworkInterface {
|
||||
log::error!("Protonode {proto_node_identifier:?} not found in registry in complete_valid_input_types");
|
||||
return Vec::new();
|
||||
};
|
||||
let valid_output_types = self.valid_output_types(&OutputConnector::node(*node_id, 0), network_path);
|
||||
let valid_output_types = self.valid_output_types(&OutputConnector::primary_output(*node_id), network_path);
|
||||
|
||||
implementations
|
||||
.iter()
|
||||
@@ -303,7 +303,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
|
||||
let valid_inputs = (0..node_io.inputs.len()).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
|
||||
let input_type = self.input_type_not_invalid(&InputConnector::node(*node_id, iterator_index), network_path);
|
||||
let input_type = self.input_type_not_invalid(&InputConnector::node_at_index(*node_id, iterator_index), network_path);
|
||||
match input_type.compiled_nested_type() {
|
||||
Some(input_type) => node_io.inputs.get(iterator_index).is_some_and(|node_io_input_type| node_io_input_type.nested_type() == input_type),
|
||||
None => true,
|
||||
@@ -338,7 +338,7 @@ impl NodeNetworkInterface {
|
||||
OutputConnector::Node { node_id, output_index } => {
|
||||
// A hidden node is replaced by a passthrough during flattening, so its output carries its primary input's type
|
||||
if *output_index == 0 && !self.is_visible(node_id, network_path) {
|
||||
return self.input_type(&InputConnector::node(*node_id, 0), network_path);
|
||||
return self.input_type(&InputConnector::primary_input(*node_id), network_path);
|
||||
}
|
||||
|
||||
// First try iterating upstream to the first protonode and try get its compiled type
|
||||
@@ -348,7 +348,9 @@ impl NodeNetworkInterface {
|
||||
match implementation {
|
||||
DocumentNodeImplementation::Network(_) => self.input_type(&InputConnector::Export(*output_index), &[network_path, &[*node_id]].concat()),
|
||||
// The compiler removes passthrough nodes so they resolve no type of their own, but their output carries their primary input's type
|
||||
DocumentNodeImplementation::ProtoNode(identifier) if *identifier == graphene_std::ops::passthrough::IDENTIFIER => self.input_type(&InputConnector::node(*node_id, 0), network_path),
|
||||
DocumentNodeImplementation::ProtoNode(identifier) if *identifier == graphene_std::ops::passthrough::IDENTIFIER => {
|
||||
self.input_type(&InputConnector::primary_input(*node_id), network_path)
|
||||
}
|
||||
DocumentNodeImplementation::ProtoNode(_) => match self.resolved_types.types.get(&[network_path, &[*node_id]].concat()) {
|
||||
Some(resolved_type) => TypeSource::Compiled(resolved_type.output.clone()),
|
||||
None => TypeSource::Unknown,
|
||||
@@ -360,7 +362,7 @@ impl NodeNetworkInterface {
|
||||
let Some((encapsulating_node, encapsulating_path)) = network_path.split_last() else {
|
||||
return TypeSource::Error("Cannot get import type in document network since it has no imports");
|
||||
};
|
||||
let mut input_type = self.input_type(&InputConnector::node(*encapsulating_node, *import_index), encapsulating_path);
|
||||
let mut input_type = self.input_type(&InputConnector::node_at_index(*encapsulating_node, *import_index), encapsulating_path);
|
||||
if matches!(input_type, TypeSource::Invalid) {
|
||||
input_type = TypeSource::Unknown
|
||||
}
|
||||
|
||||
@@ -39,6 +39,18 @@ pub struct NodeTemplate {
|
||||
pub node_type_metadata: NodeTypePersistentMetadata,
|
||||
}
|
||||
|
||||
impl NodeTemplate {
|
||||
/// The input slot named by the given parameter symbol, mirroring [`DocumentNode::input`].
|
||||
pub fn input<P: graphene_std::NodeParameter>(&self, _parameter: P) -> Option<&NodeInput> {
|
||||
self.inputs.get(P::INDEX)
|
||||
}
|
||||
|
||||
/// Mutable access to the input slot named by the given parameter symbol, mirroring [`DocumentNode::input_mut`].
|
||||
pub fn input_mut<P: graphene_std::NodeParameter>(&mut self, _parameter: P) -> Option<&mut NodeInput> {
|
||||
self.inputs.get_mut(P::INDEX)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NodeTemplate {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use graphene_std::ParameterRef;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum FlowType {
|
||||
@@ -60,6 +61,13 @@ pub enum ImportOrExport {
|
||||
Export(usize),
|
||||
}
|
||||
|
||||
/// The primary input (index 0) of any node: a chain node's horizontal wire continuing from the left, or a layer's vertical stack wire from the sibling rendered below it.
|
||||
pub const PRIMARY_INPUT_INDEX: usize = 0;
|
||||
/// The secondary input (index 1) of a layer-shaped node: the horizontal wire from the left, carrying the node chain or child stack that the layer renders.
|
||||
pub const LAYER_SECONDARY_INPUT_INDEX: usize = 1;
|
||||
/// The primary output (index 0) of a node, which most nodes expose as their only output.
|
||||
pub const PRIMARY_OUTPUT_INDEX: usize = 0;
|
||||
|
||||
/// Represents an input connector with index based on the [`DocumentNode::inputs`] index, not the visible input index
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
@@ -82,7 +90,32 @@ impl Default for InputConnector {
|
||||
}
|
||||
|
||||
impl InputConnector {
|
||||
pub fn node(node_id: NodeId, input_index: usize) -> Self {
|
||||
/// Reference a node's input by its parameter symbol, e.g. `InputConnector::node(node_id, stroke::WeightInput)`, or by an erased [`ParameterRef`] chosen at runtime.
|
||||
pub fn node(node_id: NodeId, parameter: impl Into<ParameterRef>) -> Self {
|
||||
InputConnector::Node {
|
||||
node_id,
|
||||
input_index: parameter.into().input_index,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference a node's primary input: a chain node's continuation from the left, or a layer's stack wire from the bottom.
|
||||
pub fn primary_input(node_id: NodeId) -> Self {
|
||||
InputConnector::Node {
|
||||
node_id,
|
||||
input_index: PRIMARY_INPUT_INDEX,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference a layer-shaped node's secondary input, the wire from the left carrying the content that the layer renders.
|
||||
pub fn layer_secondary_input(node_id: NodeId) -> Self {
|
||||
InputConnector::Node {
|
||||
node_id,
|
||||
input_index: LAYER_SECONDARY_INPUT_INDEX,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference a node's input by a runtime index, for genuinely dynamic cases like clicked ports, input enumeration, and document upgrades.
|
||||
pub fn node_at_index(node_id: NodeId, input_index: usize) -> Self {
|
||||
InputConnector::Node { node_id, input_index }
|
||||
}
|
||||
|
||||
@@ -127,6 +160,14 @@ impl OutputConnector {
|
||||
OutputConnector::Node { node_id, output_index }
|
||||
}
|
||||
|
||||
/// Reference a node's primary (first) output.
|
||||
pub fn primary_output(node_id: NodeId) -> Self {
|
||||
OutputConnector::Node {
|
||||
node_id,
|
||||
output_index: PRIMARY_OUTPUT_INDEX,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
match self {
|
||||
OutputConnector::Node { output_index, .. } => *output_index,
|
||||
|
||||
@@ -160,7 +160,7 @@ impl<'a, 'p> NetworkView<'a, 'p> {
|
||||
}
|
||||
|
||||
pub fn primary_input_connected_to_layer(&self, node_id: &NodeId) -> bool {
|
||||
self.input(&InputConnector::node(*node_id, 0))
|
||||
self.input(&InputConnector::primary_input(*node_id))
|
||||
.ok()
|
||||
.and_then(|input| input.as_node())
|
||||
.is_some_and(|upstream_id| self.is_layer(&upstream_id).unwrap_or_default())
|
||||
@@ -234,7 +234,7 @@ impl<'a, 'p> NetworkView<'a, 'p> {
|
||||
}
|
||||
|
||||
pub fn has_primary_input(&self, node_id: &NodeId) -> Result<bool, NetworkError> {
|
||||
Ok(self.input(&InputConnector::node(*node_id, 0)).is_ok_and(|input| input.is_exposed()))
|
||||
Ok(self.input(&InputConnector::primary_input(*node_id)).is_ok_and(|input| input.is_exposed()))
|
||||
}
|
||||
|
||||
pub fn hidden_primary_output(&self, node_id: &NodeId) -> Result<bool, NetworkError> {
|
||||
@@ -281,7 +281,7 @@ impl<'a, 'p> NetworkView<'a, 'p> {
|
||||
pub fn persistent_input_metadata(&self, node_id: &NodeId, index: usize) -> Result<&'a InputPersistentMetadata, NetworkError> {
|
||||
let metadata = self.node_metadata(node_id)?;
|
||||
let input_metadata = metadata.persistent_metadata.input_metadata.get(index).ok_or(NetworkError::InputNotFound {
|
||||
connector: InputConnector::node(*node_id, index),
|
||||
connector: InputConnector::node_at_index(*node_id, index),
|
||||
})?;
|
||||
Ok(&input_metadata.persistent_metadata)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -912,7 +912,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
let Some((downstream_node, input_index)) = document
|
||||
.network_interface
|
||||
.outward_wires(&[])
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(layer.to_node(), 0)))
|
||||
.and_then(|outward_wires| outward_wires.get(&OutputConnector::primary_output(layer.to_node())))
|
||||
.and_then(|outward_wires| outward_wires.first())
|
||||
.and_then(|input_connector| input_connector.node_id().map(|node_id| (node_id, input_connector.input_index())))
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user