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 { if let Some(clicked_output) = clicked_output {
responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::StartTransaction);
self.initial_disconnecting = false; 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); self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
return; return;

View File

@ -2563,19 +2563,10 @@ impl NodeNetworkInterface {
let input_count = self.number_of_inputs(node_id, network_path); let input_count = self.number_of_inputs(node_id, network_path);
let output_count = self.number_of_outputs(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) self.node_metadata(node_id, network_path)
.is_some_and(|node_metadata| node_metadata.persistent_metadata.has_primary_output) .is_some_and(|node_metadata| node_metadata.persistent_metadata.has_primary_output)
&& output_count == 1 && output_count == 1
&& (input_count == 1 || input_count == 2) && (input_count <= 2)
&& has_single_output_wire
} }
pub fn node_graph_ptz_mut(&mut self, network_path: &[NodeId]) -> Option<&mut PTZ> { 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, node_id: downstream_node_id,
input_index, 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 { 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"); log::error!("Could not get downstream node_metadata in set_input");
return; return;
}; };
match &downstream_node_metadata.persistent_metadata.node_type_metadata { match &downstream_node_metadata.persistent_metadata.node_type_metadata {
NodeTypePersistentMetadata::Layer(_) => { NodeTypePersistentMetadata::Layer(_) => {
// If the layer feeds into the bottom input of layer, set its position to stack at its previous y position // 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
if *input_index == 0 { 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); self.set_stack_position_calculated_offset(upstream_node_id, downstream_node_id, network_path);
} else { } else {
self.set_absolute_position(upstream_node_id, current_node_position, network_path); self.set_absolute_position(upstream_node_id, current_node_position, network_path);
@ -3324,25 +3319,31 @@ impl NodeNetworkInterface {
// If a node is disconnected. // If a node is disconnected.
(NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => { (NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path); 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 { if let Some((old_upstream_node_id, previous_position)) = previous_metadata {
let mut set_to_absolute = true; let old_upstream_node_is_layer = self.is_layer(&old_upstream_node_id, network_path);
// Do not set to absolute if the node is being disconnected, but still has another connection to a layer node let Some(outward_wires) = self
if matches!(new_input, NodeInput::Value { .. }) { .outward_wires(network_path)
if let Some(outward_wires) = self .and_then(|outward_wires| outward_wires.get(&OutputConnector::node(old_upstream_node_id, 0)))
.outward_wires(network_path) else {
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(old_upstream_node_id, 0))) log::error!("Could not get outward wires in set_input");
{ return;
if outward_wires.len() == 1 };
&& outward_wires[0].input_index() == 0 // If it is a layer and is connected to a single layer, set its position to stack at its previous y position
&& outward_wires[0].node_id().is_some_and(|downstream_node| self.is_layer(&downstream_node, network_path)) 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() {
set_to_absolute = false; 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 it is a node and is eligible to be in a chain, then set it to chain positioning
if set_to_absolute { 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); self.set_absolute_position(&old_upstream_node_id, previous_position, network_path);
} }
} }
@ -3625,12 +3626,6 @@ impl NodeNetworkInterface {
let mut reconnect_node = None; 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 { for downstream_input in &downstream_inputs_to_disconnect {
self.disconnect_input(downstream_input, network_path); self.disconnect_input(downstream_input, network_path);
// Prevent reconnecting export to import until https://github.com/GraphiteEditor/Graphite/issues/1762 is solved // 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 { 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 }); 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::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) => { (false, true) => {
// If a node is set to a layer // If a node is set to a layer
if let Some(upstream_sibling_id) = upstream_sibling_id { 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); self.set_stack_position_calculated_offset(&upstream_sibling_id, node_id, network_path);
} else { } else {
self.set_upstream_chain_to_absolute(&upstream_sibling_id, network_path); self.set_upstream_chain_to_absolute(&upstream_sibling_id, network_path);
@ -3830,72 +3829,61 @@ impl NodeNetworkInterface {
return; return;
}; };
let downstream_is_layer = self let single_downstream_layer_position = self
.outward_wires(network_path) .outward_wires(network_path)
.and_then(|outward_wires| { .and_then(|outward_wires| {
outward_wires outward_wires
.get(&OutputConnector::node(*node_id, 0)) .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 }) .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 { let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node_metadata for node {node_id}"); log::error!("Could not get node_metadata for node {node_id}");
return; return;
}; };
// First set the position to absolute
node_metadata.persistent_metadata.node_type_metadata = if is_layer { node_metadata.persistent_metadata.node_type_metadata = if is_layer {
if downstream_is_layer { NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { position: LayerPosition::Absolute(position),
position: LayerPosition::Stack(0), owned_nodes: TransientMetadata::Unloaded,
owned_nodes: TransientMetadata::Unloaded, })
})
} else {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position),
owned_nodes: TransientMetadata::Unloaded,
})
}
} else { } else {
NodeTypePersistentMetadata::Node(NodePersistentMetadata { NodeTypePersistentMetadata::Node(NodePersistentMetadata {
position: NodePosition::Absolute(position), 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 { if is_layer {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata::default()); node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata::default());
} else { } else {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Node; 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.transaction_modified();
self.unload_stack_dependents(network_path);
self.unload_upstream_node_click_targets(vec![*node_id], network_path); self.unload_upstream_node_click_targets(vec![*node_id], network_path);
self.unload_all_nodes_bounding_box(network_path); self.unload_all_nodes_bounding_box(network_path);
self.unload_import_export_ports(network_path); self.unload_import_export_ports(network_path);
@ -4114,9 +4102,39 @@ impl NodeNetworkInterface {
} }
downstream_id = upstream_node; 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() { 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 { 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; return;
}; };
// Set any chain nodes to absolute positioning // Set any chain nodes to absolute positioning
@ -4594,16 +4612,7 @@ impl NodeNetworkInterface {
self.transaction_modified(); self.transaction_modified();
// Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted // 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); self.unload_upstream_node_click_targets(vec![*node_id], network_path);
self.try_set_node_to_chain(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)
}
}
} else if let NodePosition::Chain = node_metadata.position { } else if let NodePosition::Chain = node_metadata.position {
self.set_upstream_chain_to_absolute(node_id, network_path); self.set_upstream_chain_to_absolute(node_id, network_path);
self.shift_node(node_id, shift, 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 { impl crate::vector::ConcatElement for super::VectorData {
fn concat(&mut self, other: &Self, transform: glam::DAffine2) { 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, PointId::generate())); 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 point_map = new_ids.collect::<HashMap<_, _>>();
let new_ids = other let new_ids = other
.segment_domain .segment_domain
.ids .ids
.iter() .iter()
.filter(|id| self.segment_domain.ids.contains(id)) .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 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 region_map = new_ids.collect::<HashMap<_, _>>();
let id_map = IdMap { let id_map = IdMap {
point_offset: self.point_domain.ids().len(), 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(&stroke) = self.stroke.get(&add_id) else { continue };
let Some(start_index) = point_domain.resolve_id(start) else { let Some(start_index) = point_domain.resolve_id(start) else {
warn!("invalid start id"); warn!("invalid start id: {:#?}", start);
continue; continue;
}; };
let Some(end_index) = point_domain.resolve_id(end) else { let Some(end_index) = point_domain.resolve_id(end) else {
warn!("invalid end id"); warn!("invalid end id: {:#?}", end);
continue; continue;
}; };

View File

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