mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 09:18:12 +08:00
Deduplicate the network interface query helpers and collapse TransientMetadata into TransientCache (#4382)
* Route the remaining view queries through the logging query helper * Name the cache readers by their loading contract and stop cloning the owned nodes per collision check * Deduplicate the port center, output count, and upstream connector helpers and drop dead accessors * Collapse the TransientMetadata enum into the Option inside TransientCache * Extract the shared history snapshot install from undo and redo
This commit is contained in:
@@ -2414,22 +2414,26 @@ impl DocumentMessageHandler {
|
|||||||
self.drive_storage_undo_redo(document_id, resource_storage, legacy_applied, true, responses);
|
self.drive_storage_undo_redo(document_id, resource_storage, legacy_applied, true, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
/// Installs a history snapshot as the active network interface, carrying over the current view state and structure load, and returns the replaced interface.
|
||||||
// If there is no history return and don't broadcast SelectionChanged
|
fn install_history_snapshot(&mut self, mut network_interface: NodeNetworkInterface, viewport: &ViewportMessageHandler) -> NodeNetworkInterface {
|
||||||
let mut network_interface = self.history.pop_undo()?;
|
|
||||||
|
|
||||||
// Set the previous network navigation metadata to the current navigation metadata
|
// Set the previous network navigation metadata to the current navigation metadata
|
||||||
network_interface.copy_all_navigation_metadata(&self.network_interface);
|
network_interface.copy_all_navigation_metadata(&self.network_interface);
|
||||||
std::mem::swap(&mut network_interface.resolved_types, &mut self.network_interface.resolved_types);
|
std::mem::swap(&mut network_interface.resolved_types, &mut self.network_interface.resolved_types);
|
||||||
|
|
||||||
//Update the metadata transform based on document PTZ
|
// Update the metadata transform based on document PTZ
|
||||||
let transform = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
let transform = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
||||||
network_interface.set_document_to_viewport_transform(transform);
|
network_interface.set_document_to_viewport_transform(transform);
|
||||||
|
|
||||||
// Ensure document structure is loaded so that updating the selected nodes has the correct metadata
|
// Ensure document structure is loaded so that updating the selected nodes has the correct metadata
|
||||||
network_interface.load_structure();
|
network_interface.load_structure();
|
||||||
|
|
||||||
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
|
std::mem::replace(&mut self.network_interface, network_interface)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn undo(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
||||||
|
// If there is no history return and don't broadcast SelectionChanged
|
||||||
|
let network_interface = self.history.pop_undo()?;
|
||||||
|
let previous_network = self.install_history_snapshot(network_interface, viewport);
|
||||||
|
|
||||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
@@ -2454,20 +2458,9 @@ impl DocumentMessageHandler {
|
|||||||
|
|
||||||
pub fn redo(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
pub fn redo(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) -> Option<NodeNetworkInterface> {
|
||||||
// If there is no history return and don't broadcast SelectionChanged
|
// If there is no history return and don't broadcast SelectionChanged
|
||||||
let mut network_interface = self.history.pop_redo()?;
|
let network_interface = self.history.pop_redo()?;
|
||||||
|
let previous_network = self.install_history_snapshot(network_interface, viewport);
|
||||||
|
|
||||||
// Set the previous network navigation metadata to the current navigation metadata
|
|
||||||
network_interface.copy_all_navigation_metadata(&self.network_interface);
|
|
||||||
std::mem::swap(&mut network_interface.resolved_types, &mut self.network_interface.resolved_types);
|
|
||||||
|
|
||||||
//Update the metadata transform based on document PTZ
|
|
||||||
let transform = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
|
|
||||||
network_interface.set_document_to_viewport_transform(transform);
|
|
||||||
|
|
||||||
// Ensure document structure is loaded so that updating the selected nodes has the correct metadata
|
|
||||||
network_interface.load_structure();
|
|
||||||
|
|
||||||
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
|
|
||||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ pub struct NodeNetworkInterface {
|
|||||||
network: MemoNetwork,
|
network: MemoNetwork,
|
||||||
/// Stores all editor information for a NodeNetwork. Should automatically kept in sync by the setter methods when changes to the document network are made.
|
/// Stores all editor information for a NodeNetwork. Should automatically kept in sync by the setter methods when changes to the document network are made.
|
||||||
network_metadata: NodeNetworkMetadata,
|
network_metadata: NodeNetworkMetadata,
|
||||||
// TODO: Wrap in TransientMetadata Option
|
// TODO: Wrap in a TransientCache
|
||||||
/// Stores the document network's structural topology. Should automatically kept in sync by the setter methods when changes to the document network are made.
|
/// Stores the document network's structural topology. Should automatically kept in sync by the setter methods when changes to the document network are made.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
document_metadata: DocumentMetadata,
|
document_metadata: DocumentMetadata,
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the stack dependents through &self if they are already loaded.
|
/// Reads the stack dependents through &self if they are already loaded.
|
||||||
pub(crate) fn with_stack_dependents<R>(&self, network_path: &[NodeId], read: impl FnOnce(&HashMap<NodeId, LayerOwner>) -> R) -> Option<R> {
|
pub(crate) fn with_stack_dependents_if_loaded<R>(&self, network_path: &[NodeId], read: impl FnOnce(&HashMap<NodeId, LayerOwner>) -> R) -> Option<R> {
|
||||||
self.network_metadata(network_path)?.transient_metadata.stack_dependents.with_loaded(read)
|
self.network_metadata(network_path)?.transient_metadata.stack_dependents.with_loaded(read)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +454,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the owned nodes of a layer through &self if they are loaded.
|
/// Reads the owned nodes of a layer through &self if they are loaded.
|
||||||
pub(crate) fn with_owned_nodes<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&HashSet<NodeId>) -> R) -> Option<R> {
|
pub(crate) fn with_owned_nodes_if_loaded<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&HashSet<NodeId>) -> R) -> Option<R> {
|
||||||
let layer_node = self.node_metadata(node_id, network_path)?;
|
let layer_node = self.node_metadata(node_id, network_path)?;
|
||||||
if !layer_node.persistent_metadata.is_layer() {
|
if !layer_node.persistent_metadata.is_layer() {
|
||||||
return None;
|
return None;
|
||||||
@@ -679,38 +679,20 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_input_center(&self, input: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
pub fn get_input_center(&self, input: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
||||||
fn port_center(ports: &Ports, index: usize) -> Option<DVec2> {
|
|
||||||
ports
|
|
||||||
.input_ports
|
|
||||||
.iter()
|
|
||||||
.find_map(|(input_index, click_target)| if index == *input_index { click_target.bounding_box_center() } else { None })
|
|
||||||
}
|
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
InputConnector::Node { node_id, input_index } => {
|
InputConnector::Node { node_id, input_index } => self
|
||||||
self.try_load_node_click_targets(node_id, network_path);
|
.with_node_click_targets(node_id, network_path, |click_targets| click_targets.port_click_targets.input_port_position(*input_index))
|
||||||
self.with_node_click_targets(node_id, network_path, |click_targets| port_center(&click_targets.port_click_targets, *input_index))
|
.flatten(),
|
||||||
.flatten()
|
InputConnector::Export(export_index) => self.with_import_export_ports(network_path, |ports| ports.input_port_position(*export_index)).flatten(),
|
||||||
}
|
|
||||||
InputConnector::Export(export_index) => self.with_import_export_ports(network_path, |ports| port_center(ports, *export_index)).flatten(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_output_center(&self, output: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
pub fn get_output_center(&self, output: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
||||||
fn port_center(ports: &Ports, index: usize) -> Option<DVec2> {
|
|
||||||
ports
|
|
||||||
.output_ports
|
|
||||||
.iter()
|
|
||||||
.find_map(|(output_index, click_target)| if index == *output_index { click_target.bounding_box_center() } else { None })
|
|
||||||
}
|
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
OutputConnector::Node { node_id, output_index } => {
|
OutputConnector::Node { node_id, output_index } => self
|
||||||
self.try_load_node_click_targets(node_id, network_path);
|
.with_node_click_targets(node_id, network_path, |click_targets| click_targets.port_click_targets.output_port_position(*output_index))
|
||||||
self.with_node_click_targets(node_id, network_path, |click_targets| port_center(&click_targets.port_click_targets, *output_index))
|
.flatten(),
|
||||||
.flatten()
|
OutputConnector::Import(import_index) => self.with_import_export_ports(network_path, |ports| ports.output_port_position(*import_index)).flatten(),
|
||||||
}
|
|
||||||
OutputConnector::Import(import_index) => self.with_import_export_ports(network_path, |ports| port_center(ports, *import_index)).flatten(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,25 +920,29 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the node click targets if needed, then reads them through &self.
|
/// Loads the node click targets if needed, then reads them through &self.
|
||||||
pub(crate) fn with_loaded_node_click_targets<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&DocumentNodeClickTargets) -> R) -> Option<R> {
|
pub(crate) fn with_node_click_targets<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&DocumentNodeClickTargets) -> R) -> Option<R> {
|
||||||
self.try_load_node_click_targets(node_id, network_path);
|
self.try_load_node_click_targets(node_id, network_path);
|
||||||
self.with_node_click_targets(node_id, network_path, read)
|
self.with_node_click_targets_if_loaded(node_id, network_path, read)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the modify import/export click targets through &self, loading them first if needed.
|
/// Reads the modify import/export click targets through &self, loading them first if needed.
|
||||||
pub(crate) fn with_modify_import_export<R>(&self, network_path: &[NodeId], read: impl FnOnce(&ModifyImportExportClickTarget) -> R) -> Option<R> {
|
pub(crate) fn with_modify_import_export<R>(&self, network_path: &[NodeId], read: impl FnOnce(&ModifyImportExportClickTarget) -> R) -> Option<R> {
|
||||||
|
self.try_load_modify_import_export(network_path);
|
||||||
|
self.network_metadata(network_path)?.transient_metadata.modify_import_export.with_loaded(read)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_load_modify_import_export(&self, network_path: &[NodeId]) {
|
||||||
let Some(network_metadata) = self.network_metadata(network_path) else {
|
let Some(network_metadata) = self.network_metadata(network_path) else {
|
||||||
log::error!("Could not get nested network_metadata in modify_import_export");
|
log::error!("Could not get nested network_metadata in modify_import_export");
|
||||||
return None;
|
return;
|
||||||
};
|
};
|
||||||
if !network_metadata.transient_metadata.modify_import_export.is_loaded() {
|
if !network_metadata.transient_metadata.modify_import_export.is_loaded() {
|
||||||
self.load_modify_import_export(network_path);
|
self.load_modify_import_export(network_path);
|
||||||
}
|
}
|
||||||
self.network_metadata(network_path)?.transient_metadata.modify_import_export.with_loaded(read)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the node click targets through &self if they are already loaded.
|
/// Reads the node click targets through &self if they are already loaded.
|
||||||
pub(crate) fn with_node_click_targets<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&DocumentNodeClickTargets) -> R) -> Option<R> {
|
pub(crate) fn with_node_click_targets_if_loaded<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&DocumentNodeClickTargets) -> R) -> Option<R> {
|
||||||
let node_metadata = self.node_metadata(node_id, network_path)?;
|
let node_metadata = self.node_metadata(node_id, network_path)?;
|
||||||
let result = node_metadata.transient_metadata.click_targets.with_loaded(read);
|
let result = node_metadata.transient_metadata.click_targets.with_loaded(read);
|
||||||
if result.is_none() {
|
if result.is_none() {
|
||||||
@@ -1095,8 +1081,8 @@ impl NodeNetworkInterface {
|
|||||||
let name_left = node_top_left.x + NAME_LEFT_OFFSET;
|
let name_left = node_top_left.x + NAME_LEFT_OFFSET;
|
||||||
let icons_reserve = VISIBILITY_INSET_FROM_LAYER_RIGHT + icons_width + GRIP_WIDTH;
|
let icons_reserve = VISIBILITY_INSET_FROM_LAYER_RIGHT + icons_width + GRIP_WIDTH;
|
||||||
let name_right_max = node_top_left.x + width as f64 - icons_reserve;
|
let name_right_max = node_top_left.x + width as f64 - icons_reserve;
|
||||||
let text_w = text_width(&display_name, FONT_SIZE);
|
let name_width = text_width(&display_name, FONT_SIZE);
|
||||||
let name_right = (name_left + text_w).min(name_right_max);
|
let name_right = (name_left + name_width).min(name_right_max);
|
||||||
if name_right > name_left {
|
if name_right > name_left {
|
||||||
// The 1-grid-tall name strip is centered vertically in the 2-grid-tall layer.
|
// The 1-grid-tall name strip is centered vertically in the 2-grid-tall layer.
|
||||||
let name_top = node_top_left.y + HALF_GRID_SIZE as f64;
|
let name_top = node_top_left.y + HALF_GRID_SIZE as f64;
|
||||||
@@ -1142,7 +1128,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_get_node_bounding_box(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
|
pub fn try_get_node_bounding_box(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
|
||||||
self.with_node_click_targets(node_id, network_path, |click_targets| click_targets.node_click_target.bounding_box())
|
self.with_node_click_targets_if_loaded(node_id, network_path, |click_targets| click_targets.node_click_target.bounding_box())
|
||||||
.flatten()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
nodes.into_iter().for_each(|node_id| {
|
nodes.into_iter().for_each(|node_id| {
|
||||||
self.with_loaded_node_click_targets(&node_id, network_path, |node_click_targets| {
|
self.with_node_click_targets(&node_id, network_path, |node_click_targets| {
|
||||||
let mut node_path = String::new();
|
let mut node_path = String::new();
|
||||||
|
|
||||||
if let ClickTargetType::Subpath(subpath) = node_click_targets.node_click_target.target_type() {
|
if let ClickTargetType::Subpath(subpath) = node_click_targets.node_click_target.target_type() {
|
||||||
@@ -146,7 +146,7 @@ impl NodeNetworkInterface {
|
|||||||
let clicked_nodes = nodes
|
let clicked_nodes = nodes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|node_id| {
|
.filter(|node_id| {
|
||||||
self.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
self.with_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
||||||
transient_node_metadata.node_click_target.intersect_point_no_stroke(point)
|
transient_node_metadata.node_click_target.intersect_point_no_stroke(point)
|
||||||
}) == Some(true)
|
}) == Some(true)
|
||||||
})
|
})
|
||||||
@@ -181,7 +181,7 @@ impl NodeNetworkInterface {
|
|||||||
node_ids
|
node_ids
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|node_id| {
|
.filter_map(|node_id| {
|
||||||
self.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
self.with_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
||||||
if let NodeTypeClickTargets::Layer(layer) = &transient_node_metadata.node_type_metadata {
|
if let NodeTypeClickTargets::Layer(layer) = &transient_node_metadata.node_type_metadata {
|
||||||
match click_target_type {
|
match click_target_type {
|
||||||
LayerClickTargetTypes::Visibility => layer.visibility_click_target.intersect_point_no_stroke(point).then_some(*node_id),
|
LayerClickTargetTypes::Visibility => layer.visibility_click_target.intersect_point_no_stroke(point).then_some(*node_id),
|
||||||
@@ -216,7 +216,7 @@ impl NodeNetworkInterface {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|node_id| {
|
.filter_map(|node_id| {
|
||||||
self.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
self.with_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
||||||
transient_node_metadata
|
transient_node_metadata
|
||||||
.port_click_targets
|
.port_click_targets
|
||||||
.clicked_input_port_from_point(point)
|
.clicked_input_port_from_point(point)
|
||||||
@@ -246,7 +246,7 @@ impl NodeNetworkInterface {
|
|||||||
nodes
|
nodes
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|node_id| {
|
.filter_map(|node_id| {
|
||||||
self.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
self.with_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
||||||
transient_node_metadata
|
transient_node_metadata
|
||||||
.port_click_targets
|
.port_click_targets
|
||||||
.clicked_output_port_from_point(point)
|
.clicked_output_port_from_point(point)
|
||||||
@@ -266,7 +266,7 @@ impl NodeNetworkInterface {
|
|||||||
pub fn input_position(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
pub fn input_position(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
||||||
match input_connector {
|
match input_connector {
|
||||||
InputConnector::Node { node_id, input_index } => self
|
InputConnector::Node { node_id, input_index } => self
|
||||||
.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
.with_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
||||||
transient_node_metadata.port_click_targets.input_port_position(*input_index)
|
transient_node_metadata.port_click_targets.input_port_position(*input_index)
|
||||||
})
|
})
|
||||||
.flatten(),
|
.flatten(),
|
||||||
@@ -279,7 +279,7 @@ impl NodeNetworkInterface {
|
|||||||
pub fn output_position(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
pub fn output_position(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> {
|
||||||
match output_connector {
|
match output_connector {
|
||||||
OutputConnector::Node { node_id, output_index } => self
|
OutputConnector::Node { node_id, output_index } => self
|
||||||
.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
.with_node_click_targets(node_id, network_path, |transient_node_metadata| {
|
||||||
transient_node_metadata.port_click_targets.output_port_position(*output_index)
|
transient_node_metadata.port_click_targets.output_port_position(*output_index)
|
||||||
})
|
})
|
||||||
.flatten(),
|
.flatten(),
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ impl NodeNetworkInterface {
|
|||||||
else {
|
else {
|
||||||
log::error!("Could not set chain position for layer node {node_id}");
|
log::error!("Could not set chain position for layer node {node_id}");
|
||||||
}
|
}
|
||||||
// let previous_upstream_node = self.upstream_output_connector(&InputConnector::node(*node_id, 0), network_path).and_then(|output| output.node_id());
|
|
||||||
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
|
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
|
||||||
// Reload click target of the layer which encapsulate the chain
|
// Reload click target of the layer which encapsulate the chain
|
||||||
if let Some(downstream_layer) = self.downstream_layer_for_chain_node(node_id, network_path) {
|
if let Some(downstream_layer) = self.downstream_layer_for_chain_node(node_id, network_path) {
|
||||||
@@ -282,7 +281,7 @@ impl NodeNetworkInterface {
|
|||||||
self.try_load_stack_dependents(network_path);
|
self.try_load_stack_dependents(network_path);
|
||||||
for node_id in node_ids.clone() {
|
for node_id in node_ids.clone() {
|
||||||
if self.is_layer(&node_id, network_path) {
|
if self.is_layer(&node_id, network_path) {
|
||||||
self.with_owned_nodes(&node_id, network_path, |owned_nodes| {
|
self.with_owned_nodes_if_loaded(&node_id, network_path, |owned_nodes| {
|
||||||
for owned_node in owned_nodes {
|
for owned_node in owned_nodes {
|
||||||
node_ids.remove(owned_node);
|
node_ids.remove(owned_node);
|
||||||
}
|
}
|
||||||
@@ -405,7 +404,7 @@ impl NodeNetworkInterface {
|
|||||||
shifted_nodes.insert(*node_id);
|
shifted_nodes.insert(*node_id);
|
||||||
self.shift_node(node_id, IVec2::new(0, shift_sign), network_path);
|
self.shift_node(node_id, IVec2::new(0, shift_sign), network_path);
|
||||||
|
|
||||||
if self.with_stack_dependents(network_path, |stack_dependents| matches!(stack_dependents.get(node_id), Some(LayerOwner::None))) == Some(true) {
|
if self.with_stack_dependents_if_loaded(network_path, |stack_dependents| matches!(stack_dependents.get(node_id), Some(LayerOwner::None))) == Some(true) {
|
||||||
self.add_drag_offset(node_id, shift_sign, network_path);
|
self.add_drag_offset(node_id, shift_sign, network_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +444,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
if seed_nodes
|
if seed_nodes
|
||||||
.iter()
|
.iter()
|
||||||
.any(|seed_node| seed_node == node_id || self.with_owned_nodes(node_id, network_path, |owned_nodes| owned_nodes.contains(seed_node)) == Some(true))
|
.any(|seed_node| seed_node == node_id || self.with_owned_nodes_if_loaded(node_id, network_path, |owned_nodes| owned_nodes.contains(seed_node)) == Some(true))
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
@@ -499,7 +498,7 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
self.shift_node(node_id, IVec2::new(0, shift_sign), network_path);
|
self.shift_node(node_id, IVec2::new(0, shift_sign), network_path);
|
||||||
|
|
||||||
match self.with_stack_dependents(network_path, |stack_dependents| stack_dependents.get(node_id).cloned()) {
|
match self.with_stack_dependents_if_loaded(network_path, |stack_dependents| stack_dependents.get(node_id).cloned()) {
|
||||||
Some(Some(LayerOwner::None)) => self.add_drag_offset(node_id, shift_sign, network_path),
|
Some(Some(LayerOwner::None)) => self.add_drag_offset(node_id, shift_sign, network_path),
|
||||||
Some(Some(LayerOwner::Layer(_))) => log::error!("Node being shifted with a push should not be owned"),
|
Some(Some(LayerOwner::Layer(_))) => log::error!("Node being shifted with a push should not be owned"),
|
||||||
Some(None) => log::error!("Could not get layer owner in vertical_shift_with_push for node {node_id}"),
|
Some(None) => log::error!("Could not get layer owner in vertical_shift_with_push for node {node_id}"),
|
||||||
@@ -519,7 +518,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shift the nodes that are owned by the layer (if any)
|
// Shift the nodes that are owned by the layer (if any)
|
||||||
if let Some(owned_nodes) = self.with_owned_nodes(node_id, network_path, |owned_nodes| owned_nodes.clone()) {
|
if let Some(owned_nodes) = self.with_owned_nodes_if_loaded(node_id, network_path, |owned_nodes| owned_nodes.clone()) {
|
||||||
for owned_node in owned_nodes {
|
for owned_node in owned_nodes {
|
||||||
if self.is_absolute(&owned_node, network_path) {
|
if self.is_absolute(&owned_node, network_path) {
|
||||||
self.try_shift_node(&owned_node, IVec2::new(0, shift_sign), shifted_nodes, network_path);
|
self.try_shift_node(&owned_node, IVec2::new(0, shift_sign), shifted_nodes, network_path);
|
||||||
@@ -533,50 +532,54 @@ impl NodeNetworkInterface {
|
|||||||
self.try_load_stack_dependents(network_path);
|
self.try_load_stack_dependents(network_path);
|
||||||
|
|
||||||
// Check collisions and for all owned nodes and recursively shift them
|
// Check collisions and for all owned nodes and recursively shift them
|
||||||
let nodes_to_shift = self.with_stack_dependents(network_path, |stack_dependents| {
|
let nodes_to_shift = self.with_stack_dependents_if_loaded(network_path, |stack_dependents| {
|
||||||
let mut nodes_to_shift = Vec::new();
|
// Hot path during drags: borrow the owned nodes for the sweep rather than cloning them per call
|
||||||
|
let collect_collisions = |owned_nodes: &HashSet<NodeId>| {
|
||||||
|
let mut nodes_to_shift = Vec::new();
|
||||||
|
|
||||||
let owned_nodes = self.with_owned_nodes(node_id, network_path, |owned_nodes| owned_nodes.clone()).unwrap_or_default();
|
for current_node in owned_nodes.iter().chain(std::iter::once(node_id)) {
|
||||||
|
for node_to_check_collision in stack_dependents {
|
||||||
|
// Do not check collision between any of the owned nodes or the shifted node
|
||||||
|
if owned_nodes.contains(node_to_check_collision.0) || node_to_check_collision.0 == node_id {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for current_node in owned_nodes.iter().chain(std::iter::once(node_id)) {
|
if node_to_check_collision.0 == current_node {
|
||||||
for node_to_check_collision in stack_dependents {
|
continue;
|
||||||
// Do not check collision between any of the owned nodes or the shifted node
|
}
|
||||||
if owned_nodes.contains(node_to_check_collision.0) || node_to_check_collision.0 == node_id {
|
let Some(mut current_node_bounding_box) = self.try_get_node_bounding_box(current_node, network_path) else {
|
||||||
continue;
|
log::error!("Could not get bounding box for node {node_id} in shift_selected_nodes");
|
||||||
}
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
if node_to_check_collision.0 == current_node {
|
let Some(node_bounding_box) = self.try_get_node_bounding_box(node_to_check_collision.0, network_path) else {
|
||||||
continue;
|
log::error!("Could not get bounding box for node {node_to_check_collision:?} in shift_selected_nodes");
|
||||||
}
|
continue;
|
||||||
let Some(mut current_node_bounding_box) = self.try_get_node_bounding_box(current_node, network_path) else {
|
};
|
||||||
log::error!("Could not get bounding box for node {node_id} in shift_selected_nodes");
|
// If the nodes do not intersect horizontally, then there is no collision
|
||||||
continue;
|
if current_node_bounding_box[1].x < node_bounding_box[0].x || current_node_bounding_box[0].x > node_bounding_box[1].x {
|
||||||
};
|
continue;
|
||||||
|
}
|
||||||
|
// Do not check collision if the nodes are currently intersecting
|
||||||
|
if current_node_bounding_box[1].y >= node_bounding_box[0].y - 0.1 && current_node_bounding_box[0].y <= node_bounding_box[1].y + 0.1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let Some(node_bounding_box) = self.try_get_node_bounding_box(node_to_check_collision.0, network_path) else {
|
current_node_bounding_box[1].y += GRID_SIZE as f64 * shift_sign as f64;
|
||||||
log::error!("Could not get bounding box for node {node_to_check_collision:?} in shift_selected_nodes");
|
current_node_bounding_box[0].y += GRID_SIZE as f64 * shift_sign as f64;
|
||||||
continue;
|
|
||||||
};
|
|
||||||
// If the nodes do not intersect horizontally, then there is no collision
|
|
||||||
if current_node_bounding_box[1].x < node_bounding_box[0].x || current_node_bounding_box[0].x > node_bounding_box[1].x {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Do not check collision if the nodes are currently intersecting
|
|
||||||
if current_node_bounding_box[1].y >= node_bounding_box[0].y - 0.1 && current_node_bounding_box[0].y <= node_bounding_box[1].y + 0.1 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_node_bounding_box[1].y += GRID_SIZE as f64 * shift_sign as f64;
|
let collision = current_node_bounding_box[1].y >= node_bounding_box[0].y - 0.1 && current_node_bounding_box[0].y <= node_bounding_box[1].y + 0.1;
|
||||||
current_node_bounding_box[0].y += GRID_SIZE as f64 * shift_sign as f64;
|
if collision {
|
||||||
|
nodes_to_shift.push((*node_to_check_collision.0, node_to_check_collision.1.clone()));
|
||||||
let collision = current_node_bounding_box[1].y >= node_bounding_box[0].y - 0.1 && current_node_bounding_box[0].y <= node_bounding_box[1].y + 0.1;
|
}
|
||||||
if collision {
|
|
||||||
nodes_to_shift.push((*node_to_check_collision.0, node_to_check_collision.1.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
nodes_to_shift
|
nodes_to_shift
|
||||||
|
};
|
||||||
|
|
||||||
|
self.with_owned_nodes_if_loaded(node_id, network_path, collect_collisions)
|
||||||
|
.unwrap_or_else(|| collect_collisions(&HashSet::new()))
|
||||||
});
|
});
|
||||||
|
|
||||||
let Some(nodes_to_shift) = nodes_to_shift else {
|
let Some(nodes_to_shift) = nodes_to_shift else {
|
||||||
|
|||||||
@@ -80,14 +80,11 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
/// Runs an encapsulating-node query, staying silent for the document network which has no encapsulating node.
|
/// Runs an encapsulating-node query, staying silent for the document network which has no encapsulating node.
|
||||||
fn query_encapsulating<'a, 'p, T>(&'a self, network_path: &'p [NodeId], caller: &str, query: impl FnOnce(NetworkView<'a, 'p>) -> Result<T, NetworkError>) -> Option<T> {
|
fn query_encapsulating<'a, 'p, T>(&'a self, network_path: &'p [NodeId], caller: &str, query: impl FnOnce(NetworkView<'a, 'p>) -> Result<T, NetworkError>) -> Option<T> {
|
||||||
match self.view(network_path).and_then(query) {
|
self.query(network_path, caller, |view| match query(view) {
|
||||||
Ok(value) => Some(value),
|
Err(NetworkError::NoEncapsulatingNode) => Ok(None),
|
||||||
Err(NetworkError::NoEncapsulatingNode) => None,
|
result => result.map(Some),
|
||||||
Err(error) => {
|
})
|
||||||
log::error!("{error} in {caller}");
|
.flatten()
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the network which the encapsulating node of the currently viewed network is part of. Will always be None in the document network.
|
/// Get the network which the encapsulating node of the currently viewed network is part of. Will always be None in the document network.
|
||||||
@@ -153,11 +150,11 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn number_of_imports(&self, network_path: &[NodeId]) -> usize {
|
pub fn number_of_imports(&self, network_path: &[NodeId]) -> usize {
|
||||||
self.view(network_path).map(|view| view.number_of_imports()).unwrap_or_default()
|
self.query(network_path, "number_of_imports", |view| Ok(view.number_of_imports())).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn number_of_exports(&self, network_path: &[NodeId]) -> usize {
|
pub fn number_of_exports(&self, network_path: &[NodeId]) -> usize {
|
||||||
self.view(network_path).map(|view| view.number_of_exports()).unwrap_or_default()
|
self.query(network_path, "number_of_exports", |view| Ok(view.number_of_exports())).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn number_of_displayed_inputs(&self, node_id: &NodeId, network_path: &[NodeId]) -> usize {
|
pub(crate) fn number_of_displayed_inputs(&self, node_id: &NodeId, network_path: &[NodeId]) -> usize {
|
||||||
@@ -172,7 +169,7 @@ impl NodeNetworkInterface {
|
|||||||
/// Whether the node has an exposed input at index 0 to accept the horizontal flow from upstream.
|
/// Whether the node has an exposed input at index 0 to accept the horizontal flow from upstream.
|
||||||
/// A node without one (e.g. a generator) can only be the most-upstream node in a chain.
|
/// A node without one (e.g. a generator) can only be the most-upstream node in a chain.
|
||||||
pub fn has_primary_input(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
pub fn has_primary_input(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||||
self.view(network_path).and_then(|view| view.has_primary_input(node_id)).unwrap_or_default()
|
self.query(network_path, "has_primary_input", |view| view.has_primary_input(node_id)).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn number_of_outputs(&self, node_id: &NodeId, network_path: &[NodeId]) -> usize {
|
pub fn number_of_outputs(&self, node_id: &NodeId, network_path: &[NodeId]) -> usize {
|
||||||
@@ -837,7 +834,7 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
/// The given network's pinned nodes in display order: pinning appends, dragging rearranges, and any not yet recorded go last.
|
/// The given network's pinned nodes in display order: pinning appends, dragging rearranges, and any not yet recorded go last.
|
||||||
pub fn ordered_pinned_nodes(&self, network_path: &[NodeId]) -> Vec<NodeId> {
|
pub fn ordered_pinned_nodes(&self, network_path: &[NodeId]) -> Vec<NodeId> {
|
||||||
self.view(network_path).map(|view| view.ordered_pinned_nodes()).unwrap_or_default()
|
self.query(network_path, "ordered_pinned_nodes", |view| Ok(view.ordered_pinned_nodes())).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_visible(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
pub fn is_visible(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||||
@@ -871,15 +868,15 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn hidden_primary_export(&self, network_path: &[NodeId]) -> bool {
|
pub fn hidden_primary_export(&self, network_path: &[NodeId]) -> bool {
|
||||||
self.view(network_path).map(|view| view.hidden_primary_export()).unwrap_or_default()
|
self.query(network_path, "hidden_primary_export", |view| Ok(view.hidden_primary_export())).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hidden_primary_output(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
pub fn hidden_primary_output(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||||
self.view(network_path).and_then(|view| view.hidden_primary_output(node_id)).unwrap_or_default()
|
self.query(network_path, "hidden_primary_output", |view| view.hidden_primary_output(node_id)).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hidden_primary_import(&self, network_path: &[NodeId]) -> bool {
|
pub fn hidden_primary_import(&self, network_path: &[NodeId]) -> bool {
|
||||||
self.view(network_path).map(|view| view.hidden_primary_import()).unwrap_or_default()
|
self.query(network_path, "hidden_primary_import", |view| Ok(view.hidden_primary_import())).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_absolute(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
pub fn is_absolute(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||||
@@ -897,7 +894,7 @@ impl NodeNetworkInterface {
|
|||||||
/// Whether the node is an Artboard node by identity, regardless of whether it currently participates in the scene.
|
/// Whether the node is an Artboard node by identity, regardless of whether it currently participates in the scene.
|
||||||
/// Callers that care about scene membership should source their layers from the document structure or check connectivity separately.
|
/// Callers that care about scene membership should source their layers from the document structure or check connectivity separately.
|
||||||
pub fn is_artboard(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
pub fn is_artboard(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||||
self.view(network_path).map(|view| view.is_artboard(node_id)).unwrap_or_default()
|
self.query(network_path, "is_artboard", |view| Ok(view.is_artboard(node_id))).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All artboard layers that participate in the scene, excluding disconnected Artboard nodes.
|
/// All artboard layers that participate in the scene, excluding disconnected Artboard nodes.
|
||||||
|
|||||||
@@ -196,11 +196,6 @@ impl NodeTemplate {
|
|||||||
(document_node, persistent_node_metadata)
|
(document_node, persistent_node_metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`DocumentNode`] half alone, for callers performing raw network surgery.
|
|
||||||
pub fn into_document_node(self) -> DocumentNode {
|
|
||||||
self.into_parts().0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resizes `input_metadata` to match `inputs` at every nesting level, filling gaps with defaults.
|
/// Resizes `input_metadata` to match `inputs` at every nesting level, filling gaps with defaults.
|
||||||
pub fn normalize_input_metadata(&mut self) {
|
pub fn normalize_input_metadata(&mut self) {
|
||||||
self.input_metadata.resize_with(self.inputs.len(), InputMetadata::default);
|
self.input_metadata.resize_with(self.inputs.len(), InputMetadata::default);
|
||||||
|
|||||||
@@ -239,23 +239,15 @@ impl Ports {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_port_position(&self, index: usize) -> Option<DVec2> {
|
pub fn input_port_position(&self, index: usize) -> Option<DVec2> {
|
||||||
self.input_ports.iter().find_map(|(port_index, click_target)| {
|
self.input_ports
|
||||||
if *port_index == index {
|
.iter()
|
||||||
click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.))
|
.find_map(|(port_index, click_target)| if *port_index == index { click_target.bounding_box_center() } else { None })
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_port_position(&self, index: usize) -> Option<DVec2> {
|
pub fn output_port_position(&self, index: usize) -> Option<DVec2> {
|
||||||
self.output_ports.iter().find_map(|(port_index, click_target)| {
|
self.output_ports
|
||||||
if *port_index == index {
|
.iter()
|
||||||
click_target.bounding_box().map(|bounds| bounds[0] + DVec2::new(8., 8.))
|
.find_map(|(port_index, click_target)| if *port_index == index { click_target.bounding_box_center() } else { None })
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,62 +350,37 @@ pub struct NodeNetworkPersistentMetadata {
|
|||||||
pub selection_redo_history: VecDeque<SelectedNodes>,
|
pub selection_redo_history: VecDeque<SelectedNodes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the same as Option, but more clear in the context of having cached metadata either being loaded or unloaded
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub enum TransientMetadata<T> {
|
|
||||||
Loaded(T),
|
|
||||||
#[default]
|
|
||||||
Unloaded,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> TransientMetadata<T> {
|
|
||||||
/// Set the current transient metadata to unloaded
|
|
||||||
pub fn unload(&mut self) {
|
|
||||||
*self = TransientMetadata::Unloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_loaded(&self) -> bool {
|
|
||||||
matches!(self, TransientMetadata::Loaded(_))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A lazily computed cache slot whose load and read paths work through &self, with interior mutability guarding the stored value.
|
/// A lazily computed cache slot whose load and read paths work through &self, with interior mutability guarding the stored value.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct TransientCache<T>(std::cell::RefCell<TransientMetadata<T>>);
|
pub(crate) struct TransientCache<T>(std::cell::RefCell<Option<T>>);
|
||||||
|
|
||||||
impl<T> Default for TransientCache<T> {
|
impl<T> Default for TransientCache<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TransientCache(std::cell::RefCell::new(TransientMetadata::Unloaded))
|
TransientCache(std::cell::RefCell::new(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TransientCache<T> {
|
impl<T> TransientCache<T> {
|
||||||
pub(crate) fn is_loaded(&self) -> bool {
|
pub(crate) fn is_loaded(&self) -> bool {
|
||||||
self.0.borrow().is_loaded()
|
self.0.borrow().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn store(&self, value: T) {
|
pub(crate) fn store(&self, value: T) {
|
||||||
*self.0.borrow_mut() = TransientMetadata::Loaded(value);
|
*self.0.borrow_mut() = Some(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn unload(&self) {
|
pub(crate) fn unload(&self) {
|
||||||
*self.0.borrow_mut() = TransientMetadata::Unloaded;
|
*self.0.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs `read` on the cached value if it is loaded.
|
/// Runs `read` on the cached value if it is loaded.
|
||||||
pub(crate) fn with_loaded<R>(&self, read: impl FnOnce(&T) -> R) -> Option<R> {
|
pub(crate) fn with_loaded<R>(&self, read: impl FnOnce(&T) -> R) -> Option<R> {
|
||||||
match &*self.0.borrow() {
|
self.0.borrow().as_ref().map(read)
|
||||||
TransientMetadata::Loaded(value) => Some(read(value)),
|
|
||||||
TransientMetadata::Unloaded => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Direct access without runtime borrow tracking, for callers already holding exclusive access.
|
/// Direct access without runtime borrow tracking, for callers already holding exclusive access.
|
||||||
pub(crate) fn get_loaded_mut(&mut self) -> Option<&mut T> {
|
pub(crate) fn get_loaded_mut(&mut self) -> Option<&mut T> {
|
||||||
match self.0.get_mut() {
|
self.0.get_mut().as_mut()
|
||||||
TransientMetadata::Loaded(value) => Some(value),
|
|
||||||
TransientMetadata::Unloaded => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,6 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'p> NetworkView<'a, 'p> {
|
impl<'a, 'p> NetworkView<'a, 'p> {
|
||||||
pub fn network(&self) -> &'a NodeNetwork {
|
|
||||||
self.network
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn network_metadata(&self) -> &'a NodeNetworkMetadata {
|
pub fn network_metadata(&self) -> &'a NodeNetworkMetadata {
|
||||||
self.metadata
|
self.metadata
|
||||||
}
|
}
|
||||||
@@ -291,11 +287,7 @@ impl<'a, 'p> NetworkView<'a, 'p> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn upstream_output_connector(&self, input_connector: &InputConnector) -> Result<Option<OutputConnector>, NetworkError> {
|
pub fn upstream_output_connector(&self, input_connector: &InputConnector) -> Result<Option<OutputConnector>, NetworkError> {
|
||||||
Ok(match self.input(input_connector)? {
|
Ok(OutputConnector::from_input(self.input(input_connector)?))
|
||||||
NodeInput::Node { node_id, output_index, .. } => Some(OutputConnector::node(*node_id, *output_index)),
|
|
||||||
NodeInput::Import { import_index, .. } => Some(OutputConnector::Import(*import_index)),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the node reaches the exports by following wires downstream.
|
/// Whether the node reaches the exports by following wires downstream.
|
||||||
@@ -326,16 +318,16 @@ impl<'a, 'p> NetworkView<'a, 'p> {
|
|||||||
|
|
||||||
while let Some(node) = stack.pop() {
|
while let Some(node) = stack.pop() {
|
||||||
for input in &node.inputs {
|
for input in &node.inputs {
|
||||||
if let &NodeInput::Node { node_id: ref_id, .. } = input {
|
if let &NodeInput::Node { node_id: upstream_id, .. } = input {
|
||||||
if already_visited.contains(&ref_id) {
|
if already_visited.contains(&upstream_id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ref_id == *target_node_id {
|
if upstream_id == *target_node_id {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let Some(ref_node) = self.network.nodes.get(&ref_id) else { continue };
|
let Some(upstream_node) = self.network.nodes.get(&upstream_id) else { continue };
|
||||||
already_visited.insert(ref_id);
|
already_visited.insert(upstream_id);
|
||||||
stack.push(ref_node);
|
stack.push(upstream_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user