Allow multiple top output wires to come from layers (#2049)

* Allow multiple outputs from layer

* Stable Ids for Flatten Vector Elements

* Clippy
This commit is contained in:
adamgerhant 2024-10-25 15:32:31 -07:00 committed by GitHub
parent 9d13a8f73b
commit 6b1356fe13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 121 additions and 109 deletions

View File

@ -499,14 +499,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
if let Some(clicked_output) = clicked_output {
responses.add(DocumentMessage::StartTransaction);
self.initial_disconnecting = false;
// Disconnect vertical output wire from an already-connected layer
if let OutputConnector::Node { node_id, .. } = clicked_output {
if network_interface.is_layer(&node_id, selection_network_path) {
if let Some(input_connectors) = network_interface.outward_wires(selection_network_path).and_then(|outward_wires| outward_wires.get(&clicked_output)) {
self.disconnecting = input_connectors.first().cloned();
}
}
}
self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
return;

View File

@ -2563,19 +2563,10 @@ impl NodeNetworkInterface {
let input_count = self.number_of_inputs(node_id, network_path);
let output_count = self.number_of_outputs(node_id, network_path);
let outward_wires = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)).cloned())
.unwrap_or_default();
let has_single_output_wire = outward_wires.len() <= 1;
// TODO: Eventually allow nodes at the bottom of a stack to be layers, where `input_count` is 0
self.node_metadata(node_id, network_path)
.is_some_and(|node_metadata| node_metadata.persistent_metadata.has_primary_output)
&& output_count == 1
&& (input_count == 1 || input_count == 2)
&& has_single_output_wire
&& (input_count <= 2)
}
pub fn node_graph_ptz_mut(&mut self, network_path: &[NodeId]) -> Option<&mut PTZ> {
@ -3284,15 +3275,19 @@ impl NodeNetworkInterface {
node_id: downstream_node_id,
input_index,
} => {
// If a layer is connected to another node, it should be set to stack positioning
// If a layer has a single connection to the bottom of another layer, it should be set to stack positioning
let Some(downstream_node_metadata) = self.node_metadata(downstream_node_id, network_path) else {
log::error!("Could not get downstream node_metadata in set_input");
return;
};
match &downstream_node_metadata.persistent_metadata.node_type_metadata {
NodeTypePersistentMetadata::Layer(_) => {
// If the layer feeds into the bottom input of layer, set its position to stack at its previous y position
if *input_index == 0 {
// 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)))
.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);
} else {
self.set_absolute_position(upstream_node_id, current_node_position, network_path);
@ -3324,25 +3319,31 @@ impl NodeNetworkInterface {
// If a node is disconnected.
(NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path);
// If a node was previously connected, and it is no longer connected to any nodes, then set its position to absolute at its previous position
if let Some((old_upstream_node_id, previous_position)) = previous_metadata {
let mut set_to_absolute = true;
// Do not set to absolute if the node is being disconnected, but still has another connection to a layer node
if matches!(new_input, NodeInput::Value { .. }) {
if let Some(outward_wires) = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(old_upstream_node_id, 0)))
{
if outward_wires.len() == 1
&& outward_wires[0].input_index() == 0
&& outward_wires[0].node_id().is_some_and(|downstream_node| self.is_layer(&downstream_node, network_path))
{
set_to_absolute = false;
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)))
else {
log::error!("Could not get outward wires in set_input");
return;
};
// If it is a layer and is connected to a single layer, set its position to stack at its previous y position
if old_upstream_node_is_layer && outward_wires.len() == 1 && outward_wires[0].input_index() == 0 {
if let Some(downstream_node_id) = outward_wires[0].node_id() {
if self.is_layer(&downstream_node_id, network_path) {
self.set_stack_position_calculated_offset(&old_upstream_node_id, &downstream_node_id, network_path);
self.unload_upstream_node_click_targets(vec![old_upstream_node_id], network_path);
}
}
}
if set_to_absolute {
// If it is a node and is eligible to be in a chain, then set it to chain positioning
else if !old_upstream_node_is_layer {
self.try_set_node_to_chain(&old_upstream_node_id, network_path);
}
// If a node was previously connected, and it is no longer connected to any nodes, then set its position to absolute at its previous position
else {
self.set_absolute_position(&old_upstream_node_id, previous_position, network_path);
}
}
@ -3625,12 +3626,6 @@ impl NodeNetworkInterface {
let mut reconnect_node = None;
// Don't reconnect if the upstream node is a layer and there are multiple downstream inputs to reconnect
let multiple_disconnect_and_upstream_is_layer = downstream_inputs_to_disconnect.len() > 1
&& reconnect_to_input
.as_ref()
.is_some_and(|upstream_input| upstream_input.as_node().map_or(false, |node_id| self.is_layer(&node_id, network_path)));
for downstream_input in &downstream_inputs_to_disconnect {
self.disconnect_input(downstream_input, network_path);
// Prevent reconnecting export to import until https://github.com/GraphiteEditor/Graphite/issues/1762 is solved
@ -3638,9 +3633,7 @@ impl NodeNetworkInterface {
if 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);
if !multiple_disconnect_and_upstream_is_layer {
self.set_input(downstream_input, reconnect_input.clone(), network_path);
}
self.set_input(downstream_input, reconnect_input.clone(), network_path);
}
}
}
@ -3815,7 +3808,13 @@ impl NodeNetworkInterface {
(false, true) => {
// If a node is set to a layer
if let Some(upstream_sibling_id) = upstream_sibling_id {
if self.is_layer(&upstream_sibling_id, network_path) {
// If the upstream sibling layer has a single output, then set it to stack position
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)))
.is_some_and(|outward_wires| outward_wires.len() == 1)
{
self.set_stack_position_calculated_offset(&upstream_sibling_id, node_id, network_path);
} else {
self.set_upstream_chain_to_absolute(&upstream_sibling_id, network_path);
@ -3830,72 +3829,61 @@ impl NodeNetworkInterface {
return;
};
let downstream_is_layer = self
let single_downstream_layer_position = self
.outward_wires(network_path)
.and_then(|outward_wires| {
outward_wires
.get(&OutputConnector::node(*node_id, 0))
.and_then(|outward_wires| outward_wires.first())
.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 })
})
.is_some_and(|downstream_node_id| self.is_layer(&downstream_node_id, network_path));
.filter(|downstream_node_id| self.is_layer(downstream_node_id, network_path))
.and_then(|downstream_layer| self.position(&downstream_layer, network_path));
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node_metadata for node {node_id}");
return;
};
// First set the position to absolute
node_metadata.persistent_metadata.node_type_metadata = if is_layer {
if downstream_is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Stack(0),
owned_nodes: TransientMetadata::Unloaded,
})
} else {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position),
owned_nodes: TransientMetadata::Unloaded,
})
}
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position),
owned_nodes: TransientMetadata::Unloaded,
})
} else {
NodeTypePersistentMetadata::Node(NodePersistentMetadata {
position: NodePosition::Absolute(position),
})
};
// Try build the chain
if is_layer {
self.try_set_upstream_to_chain(&InputConnector::node(*node_id, 1), network_path);
} else {
self.try_set_node_to_chain(node_id, network_path);
}
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node_metadata for node {node_id}");
return;
};
// Set the position to stack if necessary
if let Some(downstream_position) = is_layer.then_some(single_downstream_layer_position).flatten() {
node_metadata.persistent_metadata.node_type_metadata = NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Stack((position.y - downstream_position.y - 3).max(0) as u32),
owned_nodes: TransientMetadata::Unloaded,
})
}
if is_layer {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata::default());
} else {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Node;
}
if is_layer {
self.try_set_upstream_to_chain(&InputConnector::node(*node_id, 1), network_path);
// Reload click target of the layer which used to encapsulate the node
let mut downstream_layer = Some(*node_id);
while let Some(downstream_layer_id) = downstream_layer {
if downstream_layer_id == *node_id || !self.is_layer(&downstream_layer_id, network_path) {
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in set_to_node_or_layer");
downstream_layer = None;
break;
};
downstream_layer = outward_wires
.get(&OutputConnector::node(downstream_layer_id, 0))
.and_then(|outward_wires| if outward_wires.len() == 1 { outward_wires[0].node_id() } else { None });
} else {
break;
}
}
if let Some(downstream_layer) = downstream_layer {
self.unload_node_click_targets(&downstream_layer, network_path);
}
} else {
self.try_set_upstream_to_chain(&InputConnector::node(*node_id, 0), network_path);
}
self.transaction_modified();
self.unload_stack_dependents(network_path);
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
self.unload_all_nodes_bounding_box(network_path);
self.unload_import_export_ports(network_path);
@ -4114,9 +4102,39 @@ impl NodeNetworkInterface {
}
downstream_id = upstream_node;
}
if set_position_to_chain {
self.unload_upstream_node_click_targets(vec![*input_connector_node_id], network_path);
}
// Reload click target of the layer which used to encapsulate the node
if set_position_to_chain {
let mut downstream_layer = Some(*input_connector_node_id);
while let Some(downstream_layer_id) = downstream_layer {
if downstream_layer_id == *input_connector_node_id || !self.is_layer(&downstream_layer_id, network_path) {
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in try_set_upstream_to_chain");
downstream_layer = None;
break;
};
downstream_layer = outward_wires
.get(&OutputConnector::node(downstream_layer_id, 0))
.and_then(|outward_wires| if outward_wires.len() == 1 { outward_wires[0].node_id() } else { None });
} else {
break;
}
}
if let Some(downstream_layer) = downstream_layer {
self.unload_node_click_targets(&downstream_layer, network_path);
}
}
}
}
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)))
.cloned()
{
if outward_wires.len() == 1 {
self.try_set_upstream_to_chain(&outward_wires[0], network_path)
}
}
}
@ -4146,7 +4164,7 @@ impl NodeNetworkInterface {
};
for upstream_id in self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).collect::<Vec<_>>().iter() {
let Some(previous_position) = self.position(upstream_id, network_path) else {
log::error!("Could not get position in set_to_node_or_layer");
log::error!("Could not get position in set_upstream_chain_to_absolute");
return;
};
// Set any chain nodes to absolute positioning
@ -4594,16 +4612,7 @@ impl NodeNetworkInterface {
self.transaction_modified();
// Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
if let Some(outward_wires) = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)))
.cloned()
{
if outward_wires.len() == 1 {
self.try_set_upstream_to_chain(&outward_wires[0], network_path)
}
}
self.try_set_node_to_chain(node_id, network_path);
} else if let NodePosition::Chain = node_metadata.position {
self.set_upstream_chain_to_absolute(node_id, network_path);
self.shift_node(node_id, shift, network_path);

View File

@ -758,17 +758,27 @@ impl bezier_rs::Identifier for PointId {
}
impl crate::vector::ConcatElement for super::VectorData {
fn concat(&mut self, other: &Self, transform: glam::DAffine2) {
let new_ids = other.point_domain.id.iter().filter(|id| self.point_domain.id.contains(id)).map(|&old| (old, PointId::generate()));
fn concat(&mut self, other: &Self, transform: glam::DAffine2, node_id: u64) {
let new_ids = other
.point_domain
.id
.iter()
.filter(|id| self.point_domain.id.contains(id))
.map(|&old| (old, old.generate_from_hash(node_id)));
let point_map = new_ids.collect::<HashMap<_, _>>();
let new_ids = other
.segment_domain
.ids
.iter()
.filter(|id| self.segment_domain.ids.contains(id))
.map(|&old| (old, SegmentId::generate()));
.map(|&old| (old, old.generate_from_hash(node_id)));
let segment_map = new_ids.collect::<HashMap<_, _>>();
let new_ids = other.region_domain.ids.iter().filter(|id| self.region_domain.ids.contains(id)).map(|&old| (old, RegionId::generate()));
let new_ids = other
.region_domain
.ids
.iter()
.filter(|id| self.region_domain.ids.contains(id))
.map(|&old| (old, old.generate_from_hash(node_id)));
let region_map = new_ids.collect::<HashMap<_, _>>();
let id_map = IdMap {
point_offset: self.point_domain.ids().len(),

View File

@ -178,11 +178,11 @@ impl SegmentModification {
let Some(&stroke) = self.stroke.get(&add_id) else { continue };
let Some(start_index) = point_domain.resolve_id(start) else {
warn!("invalid start id");
warn!("invalid start id: {:#?}", start);
continue;
};
let Some(end_index) = point_domain.resolve_id(end) else {
warn!("invalid end id");
warn!("invalid end id: {:#?}", end);
continue;
};

View File

@ -548,10 +548,10 @@ async fn flatten_vector_elements<F: 'n + Send>(
let graphic_group = graphic_group_input.eval(footprint).await;
fn concat_group(graphic_group: &GraphicGroup, current_transform: DAffine2, result: &mut VectorData) {
for (element, _) in graphic_group.iter() {
for (element, reference) in graphic_group.iter() {
match element {
GraphicElement::VectorData(vector_data) => {
result.concat(vector_data, current_transform);
result.concat(vector_data, current_transform, reference.map(|node_id| node_id.0).unwrap_or_default());
}
GraphicElement::GraphicGroup(graphic_group) => {
concat_group(graphic_group, current_transform * graphic_group.transform, result);
@ -565,15 +565,16 @@ async fn flatten_vector_elements<F: 'n + Send>(
concat_group(&graphic_group, DAffine2::IDENTITY, &mut result);
// TODO: This leads to incorrect stroke widths when flattening groups with different transforms.
result.style.set_stroke_transform(DAffine2::IDENTITY);
result
}
pub trait ConcatElement {
fn concat(&mut self, other: &Self, transform: DAffine2);
fn concat(&mut self, other: &Self, transform: DAffine2, node_id: u64);
}
impl ConcatElement for GraphicGroup {
fn concat(&mut self, other: &Self, transform: DAffine2) {
fn concat(&mut self, other: &Self, transform: DAffine2, _node_id: u64) {
// TODO: Decide if we want to keep this behavior whereby the layers are flattened
for (mut element, footprint_mapping) in other.iter().cloned() {
*element.transform_mut() = transform * element.transform() * other.transform();