Make the network interface's queries take &self instead of &mut self (#4378)

* Back the outward wires cache with an interior-mutability cell readable through &self

* Convert the remaining network-level caches to interior-mutability TransientCache cells

* Move the owned nodes cache out of persistent layer metadata into a transient cell

* Flatten the transient node type enum into a layer width cell with &self loading

* Back the per-node click targets cache with a TransientCache cell

* Unify the per-input and per-export wire caches into one connector-keyed map

* Flip the click target, position, and stack dependent load chains to &self

* Flip the bounding box, import export position, and port load chains to &self

* Flip the wire geometry and resolved type query families to &self

* Flip the hit testing and frontend click target surface to &self

* Flip the frontend assembly, clipboard copy, and chain validation queries to &self

* Load stack dependents before filtering layer-owned nodes out of a selected-nodes shift
This commit is contained in:
Keavon Chambers
2026-09-15 14:37:28 +02:00
committed by Dennis Kobert
parent 7ec885e7c7
commit f045b87daf
11 changed files with 519 additions and 550 deletions
@@ -1712,7 +1712,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
} }
DocumentMessage::ZoomCanvasToFitAll => { DocumentMessage::ZoomCanvasToFitAll => {
let bounds = if self.graph_view_overlay_open { let bounds = if self.graph_view_overlay_open {
self.network_interface.all_nodes_bounding_box(&self.breadcrumb_network_path).cloned() self.network_interface.all_nodes_bounding_box(&self.breadcrumb_network_path)
} else { } else {
self.network_interface.document_bounds_document_space(true) self.network_interface.document_bounds_document_space(true)
}; };
@@ -79,7 +79,7 @@ impl NodeNetworkInterface {
self.try_get_stack_dependents(network_path) self.try_get_stack_dependents(network_path)
} }
pub(crate) fn try_load_stack_dependents(&mut self, network_path: &[NodeId]) { pub(crate) fn try_load_stack_dependents(&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 stack_dependents"); log::error!("Could not get nested network_metadata in stack_dependents");
return; return;
@@ -90,12 +90,17 @@ impl NodeNetworkInterface {
} }
} }
pub(crate) fn try_get_stack_dependents(&self, network_path: &[NodeId]) -> Option<&HashMap<NodeId, LayerOwner>> { /// Reads the stack dependents through &self if they are already loaded.
let Some(network_metadata) = self.network_metadata(network_path) else { pub(crate) fn with_stack_dependents<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)
}
pub(crate) fn try_get_stack_dependents(&mut self, network_path: &[NodeId]) -> Option<&HashMap<NodeId, LayerOwner>> {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network_metadata in try_get_stack_dependents"); log::error!("Could not get nested network_metadata in try_get_stack_dependents");
return None; return None;
}; };
let TransientMetadata::Loaded(stack_dependents) = &network_metadata.transient_metadata.stack_dependents else { let Some(stack_dependents) = network_metadata.transient_metadata.stack_dependents.get_loaded_mut() else {
log::error!("could not load stack_dependents"); log::error!("could not load stack_dependents");
return None; return None;
}; };
@@ -103,7 +108,7 @@ impl NodeNetworkInterface {
} }
// This function always has to be in sync with the selected nodes. // This function always has to be in sync with the selected nodes.
fn load_stack_dependents(&mut self, network_path: &[NodeId]) { fn load_stack_dependents(&self, network_path: &[NodeId]) {
let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else { let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else {
log::error!("Could not get selected nodes in load_stack_dependents"); log::error!("Could not get selected nodes in load_stack_dependents");
return; return;
@@ -134,15 +139,19 @@ impl NodeNetworkInterface {
stack_tops.insert(current_node); stack_tops.insert(current_node);
break; break;
}; };
let Some(outward_wires) = self.outward_wires(network_path) else { let Some(first_downstream_input) = self.with_outward_wires(network_path, |outward_wires| {
outward_wires
.get(&OutputConnector::node(current_node, 0))
.map(|layer_outward_wires| layer_outward_wires.first().copied())
}) else {
log::error!("Cannot load outward wires in load_stack_dependents"); log::error!("Cannot load outward wires in load_stack_dependents");
return; return;
}; };
let Some(layer_outward_wires) = outward_wires.get(&OutputConnector::node(current_node, 0)) else { let Some(first_downstream_input) = first_downstream_input else {
log::error!("Could not get outward_wires for layer {current_node}"); log::error!("Could not get outward_wires for layer {current_node}");
break; break;
}; };
match layer_outward_wires.first() { match first_downstream_input {
Some(downstream_input) => { Some(downstream_input) => {
let Some(downstream_node) = downstream_input.node_id() else { let Some(downstream_node) = downstream_input.node_id() else {
log::error!("Node connected to export should be absolute"); log::error!("Node connected to export should be absolute");
@@ -174,15 +183,11 @@ impl NodeNetworkInterface {
owned_sole_dependents.insert(*layer_sole_dependent); owned_sole_dependents.insert(*layer_sole_dependent);
new_owned_nodes.insert(*layer_sole_dependent); new_owned_nodes.insert(*layer_sole_dependent);
} }
let Some(layer_node) = self.node_metadata_mut(&upstream_layer, network_path) else { let Some(layer_node) = self.node_metadata(&upstream_layer, network_path) else {
log::error!("Could not get layer node in load_stack_dependents"); log::error!("Could not get layer node in load_stack_dependents");
continue; continue;
}; };
let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &mut layer_node.persistent_metadata.node_type_metadata else { layer_node.transient_metadata.owned_nodes.store(new_owned_nodes);
log::error!("upstream layer should be a layer");
return;
};
*owned_nodes = TransientMetadata::Loaded(new_owned_nodes);
} }
} }
} }
@@ -222,12 +227,12 @@ impl NodeNetworkInterface {
} }
} }
let Some(network_metadata) = self.network_metadata_mut(network_path) else { let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get current network in load_export_ports"); log::error!("Could not get current network in load_stack_dependents");
return; return;
}; };
network_metadata.transient_metadata.stack_dependents = TransientMetadata::Loaded(stack_dependents); network_metadata.transient_metadata.stack_dependents.store(stack_dependents);
} }
pub fn unload_stack_dependents(&mut self, network_path: &[NodeId]) { pub fn unload_stack_dependents(&mut self, network_path: &[NodeId]) {
@@ -245,7 +250,7 @@ impl NodeNetworkInterface {
return; return;
}; };
if let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents { if let Some(stack_dependents) = network_metadata.transient_metadata.stack_dependents.get_loaded_mut() {
for layer_owner in stack_dependents.values_mut() { for layer_owner in stack_dependents.values_mut() {
if let LayerOwner::None(offset) = layer_owner { if let LayerOwner::None(offset) = layer_owner {
*offset = 0; *offset = 0;
@@ -255,26 +260,36 @@ impl NodeNetworkInterface {
} }
pub fn import_export_ports(&mut self, network_path: &[NodeId]) -> Option<&Ports> { pub fn import_export_ports(&mut self, network_path: &[NodeId]) -> Option<&Ports> {
let Some(network_metadata) = self.network_metadata(network_path) else { self.try_load_import_export_ports(network_path);
log::error!("Could not get nested network_metadata in export_ports");
return None;
};
if !network_metadata.transient_metadata.import_export_ports.is_loaded() {
self.load_import_export_ports(network_path);
}
let Some(network_metadata) = self.network_metadata(network_path) else { let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network_metadata in export_ports"); log::error!("Could not get nested network_metadata in export_ports");
return None; return None;
}; };
let TransientMetadata::Loaded(ports) = &network_metadata.transient_metadata.import_export_ports else { let Some(ports) = network_metadata.transient_metadata.import_export_ports.get_loaded_mut() else {
log::error!("could not load import ports"); log::error!("could not load import ports");
return None; return None;
}; };
Some(ports) Some(ports)
} }
pub fn load_import_export_ports(&mut self, network_path: &[NodeId]) { /// Reads the import/export ports through &self, loading them first if needed.
pub(crate) fn with_import_export_ports<R>(&self, network_path: &[NodeId], read: impl FnOnce(&Ports) -> R) -> Option<R> {
self.try_load_import_export_ports(network_path);
self.network_metadata(network_path)?.transient_metadata.import_export_ports.with_loaded(read)
}
fn try_load_import_export_ports(&self, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in export_ports");
return;
};
if !network_metadata.transient_metadata.import_export_ports.is_loaded() {
self.load_import_export_ports(network_path);
}
}
pub fn load_import_export_ports(&self, network_path: &[NodeId]) {
let Some(import_export_position) = self.import_export_position(network_path) else { let Some(import_export_position) = self.import_export_position(network_path) else {
log::error!("Could not get import_export_position"); log::error!("Could not get import_export_position");
return; return;
@@ -294,12 +309,12 @@ impl NodeNetworkInterface {
import_export_ports.insert_input_port_at_center(export_index, import_export_position.1.as_dvec2() + DVec2::new(0., export_index as f64 * 24.)); import_export_ports.insert_input_port_at_center(export_index, import_export_position.1.as_dvec2() + DVec2::new(0., export_index as f64 * 24.));
} }
let Some(network_metadata) = self.network_metadata_mut(network_path) else { let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get current network in load_export_ports"); log::error!("Could not get current network in load_export_ports");
return; return;
}; };
network_metadata.transient_metadata.import_export_ports = TransientMetadata::Loaded(import_export_ports); network_metadata.transient_metadata.import_export_ports.store(import_export_ports);
} }
pub(crate) fn unload_import_export_ports(&mut self, network_path: &[NodeId]) { pub(crate) fn unload_import_export_ports(&mut self, network_path: &[NodeId]) {
@@ -342,74 +357,75 @@ impl NodeNetworkInterface {
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);
} }
let Some(network_metadata) = self.network_metadata(network_path) else { let Some(network_metadata) = self.network_metadata_mut(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 None;
}; };
let TransientMetadata::Loaded(click_targets) = &network_metadata.transient_metadata.modify_import_export else { let Some(click_targets) = network_metadata.transient_metadata.modify_import_export.get_loaded_mut() else {
log::error!("could not load modify import export ports"); log::error!("could not load modify import export ports");
return None; return None;
}; };
Some(click_targets) Some(click_targets)
} }
pub fn load_modify_import_export(&mut self, network_path: &[NodeId]) { pub fn load_modify_import_export(&self, network_path: &[NodeId]) {
let mut reorder_imports_exports = Ports::new(); let mut reorder_imports_exports = Ports::new();
let mut remove_imports_exports = Ports::new(); let mut remove_imports_exports = Ports::new();
if !network_path.is_empty() { if !network_path.is_empty() {
let Some(import_exports) = self.import_export_ports(network_path) else { let ports_built = self.with_import_export_ports(network_path, |import_exports| {
for (import_index, import_click_target) in import_exports.output_ports() {
let Some(import_bounding_box) = import_click_target.bounding_box() else {
log::error!("Could not get export bounding box in load_modify_import_export");
continue;
};
let reorder_import_center = (import_bounding_box[0] + import_bounding_box[1]) / 2. + DVec2::new(-12., 0.);
if *import_index == 0 {
let remove_import_center = reorder_import_center + DVec2::new(-4., 0.);
let remove_import = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_import_center - DVec2::new(8., 8.), remove_import_center + DVec2::new(8., 8.)), 0.);
remove_imports_exports.insert_custom_output_port(*import_index, remove_import);
} else {
let remove_import_center = reorder_import_center + DVec2::new(-12., 0.);
let reorder_import = ClickTarget::new_with_subpath(Subpath::new_rectangle(reorder_import_center - DVec2::new(3., 4.), reorder_import_center + DVec2::new(3., 4.)), 0.);
let remove_import = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_import_center - DVec2::new(8., 8.), remove_import_center + DVec2::new(8., 8.)), 0.);
reorder_imports_exports.insert_custom_output_port(*import_index, reorder_import);
remove_imports_exports.insert_custom_output_port(*import_index, remove_import);
}
}
for (export_index, export_click_target) in import_exports.input_ports() {
let Some(export_bounding_box) = export_click_target.bounding_box() else {
log::error!("Could not get export bounding box in load_modify_import_export");
continue;
};
let reorder_export_center = (export_bounding_box[0] + export_bounding_box[1]) / 2. + DVec2::new(12., 0.);
if *export_index == 0 {
let remove_export_center = reorder_export_center + DVec2::new(4., 0.);
let remove_export = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_export_center - DVec2::new(8., 8.), remove_export_center + DVec2::new(8., 8.)), 0.);
remove_imports_exports.insert_custom_input_port(*export_index, remove_export);
} else {
let remove_export_center = reorder_export_center + DVec2::new(12., 0.);
let reorder_export = ClickTarget::new_with_subpath(Subpath::new_rectangle(reorder_export_center - DVec2::new(3., 4.), reorder_export_center + DVec2::new(3., 4.)), 0.);
let remove_export = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_export_center - DVec2::new(8., 8.), remove_export_center + DVec2::new(8., 8.)), 0.);
reorder_imports_exports.insert_custom_input_port(*export_index, reorder_export);
remove_imports_exports.insert_custom_input_port(*export_index, remove_export);
}
}
});
if ports_built.is_none() {
log::error!("Could not get import_export_ports in load_modify_import_export"); log::error!("Could not get import_export_ports in load_modify_import_export");
return; return;
};
for (import_index, import_click_target) in import_exports.output_ports() {
let Some(import_bounding_box) = import_click_target.bounding_box() else {
log::error!("Could not get export bounding box in load_modify_import_export");
continue;
};
let reorder_import_center = (import_bounding_box[0] + import_bounding_box[1]) / 2. + DVec2::new(-12., 0.);
if *import_index == 0 {
let remove_import_center = reorder_import_center + DVec2::new(-4., 0.);
let remove_import = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_import_center - DVec2::new(8., 8.), remove_import_center + DVec2::new(8., 8.)), 0.);
remove_imports_exports.insert_custom_output_port(*import_index, remove_import);
} else {
let remove_import_center = reorder_import_center + DVec2::new(-12., 0.);
let reorder_import = ClickTarget::new_with_subpath(Subpath::new_rectangle(reorder_import_center - DVec2::new(3., 4.), reorder_import_center + DVec2::new(3., 4.)), 0.);
let remove_import = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_import_center - DVec2::new(8., 8.), remove_import_center + DVec2::new(8., 8.)), 0.);
reorder_imports_exports.insert_custom_output_port(*import_index, reorder_import);
remove_imports_exports.insert_custom_output_port(*import_index, remove_import);
}
}
for (export_index, export_click_target) in import_exports.input_ports() {
let Some(export_bounding_box) = export_click_target.bounding_box() else {
log::error!("Could not get export bounding box in load_modify_import_export");
continue;
};
let reorder_export_center = (export_bounding_box[0] + export_bounding_box[1]) / 2. + DVec2::new(12., 0.);
if *export_index == 0 {
let remove_export_center = reorder_export_center + DVec2::new(4., 0.);
let remove_export = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_export_center - DVec2::new(8., 8.), remove_export_center + DVec2::new(8., 8.)), 0.);
remove_imports_exports.insert_custom_input_port(*export_index, remove_export);
} else {
let remove_export_center = reorder_export_center + DVec2::new(12., 0.);
let reorder_export = ClickTarget::new_with_subpath(Subpath::new_rectangle(reorder_export_center - DVec2::new(3., 4.), reorder_export_center + DVec2::new(3., 4.)), 0.);
let remove_export = ClickTarget::new_with_subpath(Subpath::new_rectangle(remove_export_center - DVec2::new(8., 8.), remove_export_center + DVec2::new(8., 8.)), 0.);
reorder_imports_exports.insert_custom_input_port(*export_index, reorder_export);
remove_imports_exports.insert_custom_input_port(*export_index, remove_export);
}
} }
} }
let Some(network_metadata) = self.network_metadata_mut(network_path) else { let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get current network in load_modify_import_export"); log::error!("Could not get current network in load_modify_import_export");
return; return;
}; };
network_metadata.transient_metadata.modify_import_export = TransientMetadata::Loaded(ModifyImportExportClickTarget { network_metadata.transient_metadata.modify_import_export.store(ModifyImportExportClickTarget {
remove_imports_exports, remove_imports_exports,
reorder_imports_exports, reorder_imports_exports,
}); });
@@ -423,18 +439,16 @@ impl NodeNetworkInterface {
network_metadata.transient_metadata.modify_import_export.unload(); network_metadata.transient_metadata.modify_import_export.unload();
} }
pub(crate) fn owned_nodes(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&HashSet<NodeId>> { /// 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> {
let layer_node = self.node_metadata(node_id, network_path)?; let layer_node = self.node_metadata(node_id, network_path)?;
let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &layer_node.persistent_metadata.node_type_metadata else { if !layer_node.persistent_metadata.is_layer() {
return None; return None;
}; }
let TransientMetadata::Loaded(owned_nodes) = owned_nodes else { layer_node.transient_metadata.owned_nodes.with_loaded(read)
return None;
};
Some(owned_nodes)
} }
pub fn all_nodes_bounding_box(&mut self, network_path: &[NodeId]) -> Option<&[DVec2; 2]> { pub fn all_nodes_bounding_box(&self, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
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 all_nodes_bounding_box"); log::error!("Could not get nested network_metadata in all_nodes_bounding_box");
return None; return None;
@@ -444,17 +458,14 @@ impl NodeNetworkInterface {
self.load_all_nodes_bounding_box(network_path); self.load_all_nodes_bounding_box(network_path);
} }
let network_metadata = self.network_metadata(network_path)?; let bounding_box = self.network_metadata(network_path)?.transient_metadata.all_nodes_bounding_box.with_loaded(|bounds| *bounds);
if bounding_box.is_none() {
let TransientMetadata::Loaded(bounding_box) = &network_metadata.transient_metadata.all_nodes_bounding_box else {
log::error!("could not load all nodes bounding box"); log::error!("could not load all nodes bounding box");
return None; }
}; bounding_box
Some(bounding_box)
} }
pub fn load_all_nodes_bounding_box(&mut self, network_path: &[NodeId]) { pub fn load_all_nodes_bounding_box(&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 load_all_nodes_bounding_box"); log::error!("Could not get nested network_metadata in load_all_nodes_bounding_box");
return; return;
@@ -463,15 +474,12 @@ impl NodeNetworkInterface {
let all_nodes_bounding_box = nodes let all_nodes_bounding_box = nodes
.iter() .iter()
.filter_map(|node_id| { .filter_map(|node_id| self.node_bounding_box(node_id, network_path))
self.node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box())
})
.reduce(Quad::combine_bounds) .reduce(Quad::combine_bounds)
.unwrap_or([DVec2::new(0., 0.), DVec2::new(0., 0.)]); .unwrap_or([DVec2::new(0., 0.), DVec2::new(0., 0.)]);
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return }; let Some(network_metadata) = self.network_metadata(network_path) else { return };
network_metadata.transient_metadata.all_nodes_bounding_box = TransientMetadata::Loaded(all_nodes_bounding_box); network_metadata.transient_metadata.all_nodes_bounding_box.store(all_nodes_bounding_box);
} }
pub fn unload_all_nodes_bounding_box(&mut self, network_path: &[NodeId]) { pub fn unload_all_nodes_bounding_box(&mut self, network_path: &[NodeId]) {
@@ -484,18 +492,13 @@ impl NodeNetworkInterface {
} }
pub fn outward_wires(&mut self, network_path: &[NodeId]) -> Option<&HashMap<OutputConnector, Vec<InputConnector>>> { pub fn outward_wires(&mut self, network_path: &[NodeId]) -> Option<&HashMap<OutputConnector, Vec<InputConnector>>> {
let Some(network_metadata) = self.network_metadata(network_path) else { self.try_load_outward_wires(network_path);
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network_metadata in outward_wires"); log::error!("Could not get nested network_metadata in outward_wires");
return None; return None;
}; };
let Some(outward_wires) = network_metadata.transient_metadata.outward_wires.get_loaded_mut() else {
if !network_metadata.transient_metadata.outward_wires.is_loaded() {
self.load_outward_wires(network_path);
}
let network_metadata = self.network_metadata(network_path)?;
let TransientMetadata::Loaded(outward_wires) = &network_metadata.transient_metadata.outward_wires else {
log::error!("could not load outward wires"); log::error!("could not load outward wires");
return None; return None;
}; };
@@ -503,7 +506,23 @@ impl NodeNetworkInterface {
Some(outward_wires) Some(outward_wires)
} }
fn load_outward_wires(&mut self, network_path: &[NodeId]) { /// Reads the outward wires through &self, loading them first if needed.
pub(crate) fn with_outward_wires<R>(&self, network_path: &[NodeId], read: impl FnOnce(&HashMap<OutputConnector, Vec<InputConnector>>) -> R) -> Option<R> {
self.try_load_outward_wires(network_path);
self.network_metadata(network_path)?.transient_metadata.outward_wires.with_loaded(read)
}
fn try_load_outward_wires(&self, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in outward_wires");
return;
};
if !network_metadata.transient_metadata.outward_wires.is_loaded() {
self.load_outward_wires(network_path);
}
}
fn load_outward_wires(&self, network_path: &[NodeId]) {
let mut outward_wires = HashMap::new(); let mut outward_wires = HashMap::new();
let Some(network) = self.nested_network(network_path) else { let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get nested network in load_outward_wires"); log::error!("Could not get nested network in load_outward_wires");
@@ -546,13 +565,13 @@ impl NodeNetworkInterface {
} }
} }
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return }; let Some(network_metadata) = self.network_metadata(network_path) else { return };
network_metadata.transient_metadata.outward_wires = TransientMetadata::Loaded(outward_wires); network_metadata.transient_metadata.outward_wires.store(outward_wires);
} }
pub(crate) fn unload_outward_wires(&mut self, network_path: &[NodeId]) { pub(crate) fn unload_outward_wires(&mut self, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in unload_outward_wires"); log::error!("Could not get nested network_metadata in unload_outward_wires");
return; return;
}; };
@@ -566,7 +585,7 @@ impl NodeNetworkInterface {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return; return;
}; };
let TransientMetadata::Loaded(outward_wires) = &mut network_metadata.transient_metadata.outward_wires else { let Some(outward_wires) = network_metadata.transient_metadata.outward_wires.get_loaded_mut() else {
return; return;
}; };
@@ -583,7 +602,7 @@ impl NodeNetworkInterface {
} }
} }
pub fn layer_width(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<u32> { pub fn layer_width(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<u32> {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else { let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get nested node_metadata in layer_width"); log::error!("Could not get nested node_metadata in layer_width");
return None; return None;
@@ -593,29 +612,15 @@ impl NodeNetworkInterface {
return None; return None;
} }
let layer_width_loaded = if let NodeTypeTransientMetadata::Layer(layer_metadata) = &node_metadata.transient_metadata.node_type_metadata { if !node_metadata.transient_metadata.layer_width.is_loaded() {
layer_metadata.layer_width.is_loaded()
} else {
false
};
if !layer_width_loaded {
self.load_layer_width(node_id, network_path); self.load_layer_width(node_id, network_path);
} }
let node_metadata = self.node_metadata(node_id, network_path)?; let node_metadata = self.node_metadata(node_id, network_path)?;
let NodeTypeTransientMetadata::Layer(layer_metadata) = &node_metadata.transient_metadata.node_type_metadata else { node_metadata.transient_metadata.layer_width.with_loaded(|layer_width| *layer_width)
log::error!("Transient metadata should be layer metadata when getting layer width");
return None;
};
let TransientMetadata::Loaded(layer_width) = layer_metadata.layer_width else {
log::error!("Transient metadata was not loaded when getting layer width");
return None;
};
Some(layer_width)
} }
pub fn load_layer_width(&mut self, node_id: &NodeId, network_path: &[NodeId]) { pub fn load_layer_width(&self, node_id: &NodeId, network_path: &[NodeId]) {
const GAP_WIDTH: f64 = 8.; const GAP_WIDTH: f64 = 8.;
const FONT_SIZE: f64 = 14.; const FONT_SIZE: f64 = 14.;
let left_thumbnail_padding = GRID_SIZE as f64 / 2.; let left_thumbnail_padding = GRID_SIZE as f64 / 2.;
@@ -632,21 +637,14 @@ impl NodeNetworkInterface {
let layer_width_pixels = left_thumbnail_padding + thumbnail_width + GAP_WIDTH + text_width + grip_padding + grip_width + lock_icon_width + icon_overhang_width; let layer_width_pixels = left_thumbnail_padding + thumbnail_width + GAP_WIDTH + text_width + grip_padding + grip_width + lock_icon_width + icon_overhang_width;
let layer_width = ((layer_width_pixels / 24.).ceil() as u32).max(8); let layer_width = ((layer_width_pixels / 24.).ceil() as u32).max(8);
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get nested node_metadata in load_layer_width"); log::error!("Could not get nested node_metadata in load_layer_width");
return; return;
}; };
// Ensure layer width is not loaded for a non layer node // Ensure layer width is not loaded for a non layer node
if node_metadata.persistent_metadata.is_layer() { if node_metadata.persistent_metadata.is_layer() {
if let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata { node_metadata.transient_metadata.layer_width.store(layer_width);
layer_metadata.layer_width = TransientMetadata::Loaded(layer_width);
} else {
// Set the entire transient node type metadata to be a layer, in case it was previously a node
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata {
layer_width: TransientMetadata::Loaded(layer_width),
});
}
} else { } else {
log::warn!("Tried loading layer width for non layer node"); log::warn!("Tried loading layer width for non layer node");
} }
@@ -661,95 +659,68 @@ impl NodeNetworkInterface {
}; };
// If the node is a layer, then the width and click targets need to be recalculated // If the node is a layer, then the width and click targets need to be recalculated
if is_layer && let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata { if is_layer {
layer_metadata.layer_width.unload(); node_metadata.transient_metadata.layer_width.unload();
} }
} }
pub fn get_input_center(&mut self, input: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> { pub fn get_input_center(&self, input: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> {
let (ports, index) = match input { 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 {
InputConnector::Node { node_id, input_index } => { InputConnector::Node { node_id, input_index } => {
let node_click_target = self.node_click_targets(node_id, network_path)?; self.try_load_node_click_targets(node_id, network_path);
(&node_click_target.port_click_targets, input_index) self.with_node_click_targets(node_id, network_path, |click_targets| port_center(&click_targets.port_click_targets, *input_index))
.flatten()
} }
InputConnector::Export(export_index) => { InputConnector::Export(export_index) => self.with_import_export_ports(network_path, |ports| port_center(ports, *export_index)).flatten(),
let ports = self.import_export_ports(network_path)?; }
(ports, export_index)
}
};
ports
.input_ports
.iter()
.find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None })
} }
pub fn get_output_center(&mut self, output: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> { pub fn get_output_center(&self, output: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> {
let (ports, index) = match output { 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 {
OutputConnector::Node { node_id, output_index } => { OutputConnector::Node { node_id, output_index } => {
let node_click_target = self.node_click_targets(node_id, network_path)?; self.try_load_node_click_targets(node_id, network_path);
(&node_click_target.port_click_targets, output_index) self.with_node_click_targets(node_id, network_path, |click_targets| port_center(&click_targets.port_click_targets, *output_index))
.flatten()
} }
OutputConnector::Import(import_index) => { OutputConnector::Import(import_index) => self.with_import_export_ports(network_path, |ports| port_center(ports, *import_index)).flatten(),
let ports = self.import_export_ports(network_path)?; }
(ports, import_index)
}
};
ports
.output_ports
.iter()
.find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None })
} }
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> { pub fn newly_loaded_input_wire(&self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
if !self.wire_is_loaded(input, network_path) { if !self.wire_is_loaded(input, network_path) {
self.load_wire(input, graph_wire_style, network_path); self.load_wire(input, graph_wire_style, network_path);
} else { } else {
return None; return None;
} }
let wire = match input { let network_metadata = self.network_metadata(network_path)?;
InputConnector::Node { node_id, input_index } => { let Some(wire) = network_metadata.transient_metadata.wires.borrow().get(input).cloned() else {
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?; log::error!("Could not load wire for input: {input:?}");
let TransientMetadata::Loaded(wire) = &input_metadata.wire else { return None;
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
InputConnector::Export(export_index) => {
let network_metadata = self.network_metadata(network_path)?;
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
}; };
Some(wire) Some(wire)
} }
pub fn wire_is_loaded(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool { pub fn wire_is_loaded(&self, input: &InputConnector, network_path: &[NodeId]) -> bool {
match input { self.network_metadata(network_path)
InputConnector::Node { node_id, input_index } => { .is_some_and(|network_metadata| network_metadata.transient_metadata.wires.borrow().contains_key(input))
let Some(input_metadata) = self.transient_input_metadata(node_id, *input_index, network_path) else {
log::error!("Input metadata should always exist for input");
return false;
};
input_metadata.wire.is_loaded()
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata(network_path) else {
return false;
};
match network_metadata.transient_metadata.wires.get(*export_index) {
Some(wire) => wire.is_loaded(),
None => false,
}
}
}
} }
fn load_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) { fn load_wire(&self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) {
let dashed = match self.previewing(network_path) { let dashed = match self.previewing(network_path) {
Previewing::Yes { .. } => match input { Previewing::Yes { .. } => match input {
InputConnector::Node { .. } => false, InputConnector::Node { .. } => false,
@@ -761,36 +732,18 @@ impl NodeNetworkInterface {
log::error!("Could not load wire path from input"); log::error!("Could not load wire path from input");
return; return;
}; };
match input { let (id, input_index) = match input {
InputConnector::Node { node_id, input_index } => { InputConnector::Node { node_id, input_index } => (*node_id, *input_index),
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return }; InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index),
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else { };
// log::warn!("Node metadata must exist on node: {input:?}"); let wire_update = WirePathUpdate {
return; id,
}; input_index,
let wire_update = WirePathUpdate { wire_path_update: Some(wire),
id: *node_id, };
input_index: *input_index,
wire_path_update: Some(wire), let Some(network_metadata) = self.network_metadata(network_path) else { return };
}; network_metadata.transient_metadata.wires.borrow_mut().insert(*input, wire_update);
input_metadata.transient_metadata.wire = TransientMetadata::Loaded(wire_update);
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return };
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
let wire_update = WirePathUpdate {
id: NodeId(u64::MAX),
input_index: *export_index,
wire_path_update: Some(wire),
};
*input_metadata = TransientMetadata::Loaded(wire_update);
}
}
} }
pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec<InputConnector> { pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec<InputConnector> {
@@ -851,34 +804,14 @@ impl NodeNetworkInterface {
} }
pub fn unload_wire(&mut self, input: &InputConnector, network_path: &[NodeId]) { pub fn unload_wire(&mut self, input: &InputConnector, network_path: &[NodeId]) {
match input { let Some(network_metadata) = self.network_metadata(network_path) else {
InputConnector::Node { node_id, input_index } => { return;
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { };
return; network_metadata.transient_metadata.wires.borrow_mut().remove(input);
};
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
input_metadata.transient_metadata.wire = TransientMetadata::Unloaded;
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return;
};
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
*input_metadata = TransientMetadata::Unloaded;
}
}
} }
/// When previewing, there may be a second path to the root node. /// When previewing, there may be a second path to the root node.
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> { pub fn wire_to_root(&self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
let input = InputConnector::Export(0); let input = InputConnector::Export(0);
let current_export = self.upstream_output_connector(&input, network_path)?; let current_export = self.upstream_output_connector(&input, network_path)?;
@@ -927,7 +860,7 @@ impl NodeNetworkInterface {
} }
/// Returns the wire subpath, its thick center-line subpath, and whether the wire should be thick. /// Returns the wire subpath, its thick center-line subpath, and whether the wire should be thick.
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, BezPath, bool)> { pub fn vector_wire_from_input(&self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, BezPath, bool)> {
let Some(input_position) = self.get_input_center(input, network_path) else { let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {input:?}"); log::error!("Could not get dom rect for wire end: {input:?}");
return None; return None;
@@ -948,7 +881,7 @@ impl NodeNetworkInterface {
Some((vector_wire, center_line, thick)) Some((vector_wire, center_line, thick))
} }
pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> { pub fn wire_path_from_input(&self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> {
let (vector_wire, center_line, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?; let (vector_wire, center_line, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
let path_string = vector_wire.to_svg(); let path_string = vector_wire.to_svg();
let center_path_string = center_line.to_svg(); let center_path_string = center_line.to_svg();
@@ -971,10 +904,16 @@ impl NodeNetworkInterface {
pub fn node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> { pub fn node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> {
self.try_load_node_click_targets(node_id, network_path); self.try_load_node_click_targets(node_id, network_path);
self.try_get_node_click_targets(node_id, network_path)
let node_metadata = self.node_metadata_mut(node_id, network_path)?;
let Some(click_targets) = node_metadata.transient_metadata.click_targets.get_loaded_mut() else {
log::error!("Could not load node type metadata when getting click targets");
return None;
};
Some(click_targets)
} }
fn try_load_node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) { pub(crate) fn try_load_node_click_targets(&self, node_id: &NodeId, network_path: &[NodeId]) {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else { let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get nested node_metadata in node_click_targets"); log::error!("Could not get nested node_metadata in node_click_targets");
return; return;
@@ -984,16 +923,35 @@ impl NodeNetworkInterface {
}; };
} }
fn try_get_node_click_targets(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> { /// Loads the node click targets if needed, then reads them through &self.
let node_metadata = self.node_metadata(node_id, network_path)?; pub(crate) fn with_loaded_node_click_targets<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&DocumentNodeClickTargets) -> R) -> Option<R> {
let TransientMetadata::Loaded(click_target) = &node_metadata.transient_metadata.click_targets else { self.try_load_node_click_targets(node_id, network_path);
log::error!("Could not load node type metadata when getting click targets"); self.with_node_click_targets(node_id, network_path, read)
return None;
};
Some(click_target)
} }
pub fn load_node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) { /// 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> {
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in modify_import_export");
return None;
};
if !network_metadata.transient_metadata.modify_import_export.is_loaded() {
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.
pub(crate) fn with_node_click_targets<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 result = node_metadata.transient_metadata.click_targets.with_loaded(read);
if result.is_none() {
log::error!("Could not load node type metadata when getting click targets");
}
result
}
pub fn load_node_click_targets(&self, node_id: &NodeId, network_path: &[NodeId]) {
let Some(node_position) = self.position_from_downstream_node(node_id, network_path) else { let Some(node_position) = self.position_from_downstream_node(node_id, network_path) else {
log::error!("Could not get node position in load_node_click_targets for node {node_id}"); log::error!("Could not get node position in load_node_click_targets for node {node_id}");
return; return;
@@ -1157,24 +1115,24 @@ impl NodeNetworkInterface {
} }
}; };
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get nested node_metadata in load_node_click_targets"); log::error!("Could not get nested node_metadata in load_node_click_targets");
return; return;
}; };
node_metadata.transient_metadata.click_targets = TransientMetadata::Loaded(document_node_click_targets); node_metadata.transient_metadata.click_targets.store(document_node_click_targets);
} }
pub fn node_bounding_box(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<[DVec2; 2]> { pub fn node_bounding_box(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
self.node_click_targets(node_id, network_path) self.try_load_node_click_targets(node_id, network_path);
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box()) self.try_get_node_bounding_box(node_id, network_path)
} }
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.try_get_node_click_targets(node_id, network_path) self.with_node_click_targets(node_id, network_path, |click_targets| click_targets.node_click_target.bounding_box())
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box()) .flatten()
} }
pub fn try_load_all_node_click_targets(&mut self, network_path: &[NodeId]) { pub fn try_load_all_node_click_targets(&self, network_path: &[NodeId]) {
let Some(network) = self.nested_network(network_path) else { let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get network in load_all_node_click_targets"); log::error!("Could not get network in load_all_node_click_targets");
return; return;
@@ -1185,7 +1143,7 @@ impl NodeNetworkInterface {
} }
/// Get the top left position in node graph coordinates for a node by recursively iterating downstream through cached positions, which means the iteration can be broken once a known position is reached. /// Get the top left position in node graph coordinates for a node by recursively iterating downstream through cached positions, which means the iteration can be broken once a known position is reached.
pub fn position_from_downstream_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<IVec2> { pub fn position_from_downstream_node(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<IVec2> {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else { let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get nested node_metadata in position_from_downstream_node"); log::error!("Could not get nested node_metadata in position_from_downstream_node");
return None; return None;
@@ -1196,9 +1154,8 @@ impl NodeNetworkInterface {
LayerPosition::Absolute(position) => Some(position), LayerPosition::Absolute(position) => Some(position),
LayerPosition::Stack(y_offset) => { LayerPosition::Stack(y_offset) => {
let Some(downstream_node_connectors) = self let Some(downstream_node_connectors) = self
.outward_wires(network_path) .with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)).cloned())
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0))) .flatten()
.cloned()
else { else {
log::error!("Could not get downstream node in position_from_downstream_node"); log::error!("Could not get downstream node in position_from_downstream_node");
return None; return None;
@@ -1231,9 +1188,8 @@ impl NodeNetworkInterface {
loop { loop {
// TODO: Use root node to restore if previewing // TODO: Use root node to restore if previewing
let Some(downstream_node_connectors) = self let Some(downstream_node_connectors) = self
.outward_wires(network_path) .with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(current_node_id, 0)).cloned())
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(current_node_id, 0))) .flatten()
.cloned()
else { else {
log::error!("Could not get downstream node for node {node_id} with Position::Chain"); log::error!("Could not get downstream node for node {node_id} with Position::Chain");
return None; return None;
@@ -72,7 +72,6 @@ impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersisten
input_name: properties_row.input_name, input_name: properties_row.input_name,
input_description: properties_row.input_description, input_description: properties_row.input_description,
}, },
..Default::default()
}) })
} }
DocumentNodePersistentMetadataHasPrimaryOutput { DocumentNodePersistentMetadataHasPrimaryOutput {
@@ -22,7 +22,7 @@ impl NodeNetworkInterface {
all_selected_nodes all_selected_nodes
} }
pub fn collect_frontend_click_targets(&mut self, network_path: &[NodeId]) -> FrontendClickTargets { pub fn collect_frontend_click_targets(&self, network_path: &[NodeId]) -> FrontendClickTargets {
let mut all_node_click_targets = Vec::new(); let mut all_node_click_targets = Vec::new();
let mut connector_click_targets = Vec::new(); let mut connector_click_targets = Vec::new();
let mut icon_click_targets = Vec::new(); let mut icon_click_targets = Vec::new();
@@ -31,15 +31,15 @@ impl NodeNetworkInterface {
return FrontendClickTargets::default(); return FrontendClickTargets::default();
}; };
let nodes = network_metadata.persistent_metadata.node_metadata.keys().copied().collect::<Vec<_>>(); let nodes = network_metadata.persistent_metadata.node_metadata.keys().copied().collect::<Vec<_>>();
if let Some(import_export_click_targets) = self.import_export_ports(network_path).cloned() { self.with_import_export_ports(network_path, |import_export_click_targets| {
for port in import_export_click_targets.click_targets() { for port in import_export_click_targets.click_targets() {
if let ClickTargetType::Subpath(subpath) = port.target_type() { if let ClickTargetType::Subpath(subpath) = port.target_type() {
connector_click_targets.push(subpath.to_bezpath().to_svg()); connector_click_targets.push(subpath.to_bezpath().to_svg());
} }
} }
} });
nodes.into_iter().for_each(|node_id| { nodes.into_iter().for_each(|node_id| {
if let Some(node_click_targets) = self.node_click_targets(&node_id, network_path) { self.with_loaded_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() {
@@ -67,7 +67,7 @@ impl NodeNetworkInterface {
icon_click_targets.push(subpath.to_bezpath().to_svg()); icon_click_targets.push(subpath.to_bezpath().to_svg());
} }
} }
} });
}); });
let mut layer_click_targets = Vec::new(); let mut layer_click_targets = Vec::new();
let mut node_click_targets = Vec::new(); let mut node_click_targets = Vec::new();
@@ -79,12 +79,12 @@ impl NodeNetworkInterface {
} }
}); });
let bounds = self.all_nodes_bounding_box(network_path).cloned().unwrap_or([DVec2::ZERO, DVec2::ZERO]); let bounds = self.all_nodes_bounding_box(network_path).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
let rect = Subpath::<PointId>::new_rectangle(bounds[0], bounds[1]); let rect = Subpath::<PointId>::new_rectangle(bounds[0], bounds[1]);
let all_nodes_bounding_box = rect.to_bezpath().to_svg(); let all_nodes_bounding_box = rect.to_bezpath().to_svg();
let mut modify_import_export = Vec::new(); let mut modify_import_export = Vec::new();
if let Some(modify_import_export_click_targets) = self.modify_import_export(network_path) { self.with_modify_import_export(network_path, |modify_import_export_click_targets| {
for click_target in modify_import_export_click_targets for click_target in modify_import_export_click_targets
.remove_imports_exports .remove_imports_exports
.click_targets() .click_targets()
@@ -94,7 +94,7 @@ impl NodeNetworkInterface {
modify_import_export.push(subpath.to_bezpath().to_svg()); modify_import_export.push(subpath.to_bezpath().to_svg());
} }
} }
} });
FrontendClickTargets { FrontendClickTargets {
node_click_targets, node_click_targets,
layer_click_targets, layer_click_targets,
@@ -131,7 +131,7 @@ impl NodeNetworkInterface {
// TODO: Optimize getting click target intersections from click by using a spacial data structure like a quadtree instead of linear search // TODO: Optimize getting click target intersections from click by using a spacial data structure like a quadtree instead of linear search
/// Click target getter methods /// Click target getter methods
pub fn node_from_click(&mut self, click: DVec2, network_path: &[NodeId]) -> Option<NodeId> { pub fn node_from_click(&self, click: DVec2, network_path: &[NodeId]) -> Option<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 node_from_click"); log::error!("Could not get nested network_metadata in node_from_click");
return None; return None;
@@ -146,8 +146,9 @@ impl NodeNetworkInterface {
let clicked_nodes = nodes let clicked_nodes = nodes
.iter() .iter()
.filter(|node_id| { .filter(|node_id| {
self.node_click_targets(node_id, network_path) self.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
.is_some_and(|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)
}) })
.cloned() .cloned()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -164,7 +165,7 @@ impl NodeNetworkInterface {
.or_else(|| clicked_nodes.into_iter().next()) .or_else(|| clicked_nodes.into_iter().next())
} }
pub fn layer_click_target_from_click(&mut self, click: DVec2, click_target_type: LayerClickTargetTypes, network_path: &[NodeId]) -> Option<NodeId> { pub fn layer_click_target_from_click(&self, click: DVec2, click_target_type: LayerClickTargetTypes, network_path: &[NodeId]) -> Option<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 visibility_from_click"); log::error!("Could not get nested network_metadata in visibility_from_click");
return None; return None;
@@ -180,7 +181,7 @@ impl NodeNetworkInterface {
node_ids node_ids
.iter() .iter()
.filter_map(|node_id| { .filter_map(|node_id| {
self.node_click_targets(node_id, network_path).and_then(|transient_node_metadata| { self.with_loaded_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),
@@ -192,11 +193,12 @@ impl NodeNetworkInterface {
None None
} }
}) })
.flatten()
}) })
.next() .next()
} }
pub fn input_connector_from_click(&mut self, click: DVec2, network_path: &[NodeId]) -> Option<InputConnector> { pub fn input_connector_from_click(&self, click: DVec2, network_path: &[NodeId]) -> Option<InputConnector> {
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 input_connector_from_click"); log::error!("Could not get nested network_metadata in input_connector_from_click");
return None; return None;
@@ -214,21 +216,22 @@ impl NodeNetworkInterface {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.iter() .iter()
.filter_map(|node_id| { .filter_map(|node_id| {
self.node_click_targets(node_id, network_path).and_then(|transient_node_metadata| { self.with_loaded_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)
.map(|port| InputConnector::node(*node_id, port)) .map(|port| InputConnector::node(*node_id, port))
}) })
.flatten()
}) })
.next() .next()
.or_else(|| { .or_else(|| {
self.import_export_ports(network_path) self.with_import_export_ports(network_path, |import_export_ports| import_export_ports.clicked_input_port_from_point(point).map(InputConnector::Export))
.and_then(|import_export_ports| import_export_ports.clicked_input_port_from_point(point).map(InputConnector::Export)) .flatten()
}) })
} }
pub fn output_connector_from_click(&mut self, click: DVec2, network_path: &[NodeId]) -> Option<OutputConnector> { pub fn output_connector_from_click(&self, click: DVec2, network_path: &[NodeId]) -> Option<OutputConnector> {
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 output_connector_from_click"); log::error!("Could not get nested network_metadata in output_connector_from_click");
return None; return None;
@@ -243,44 +246,51 @@ impl NodeNetworkInterface {
nodes nodes
.iter() .iter()
.filter_map(|node_id| { .filter_map(|node_id| {
self.node_click_targets(node_id, network_path).and_then(|transient_node_metadata| { self.with_loaded_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)
.map(|output_index| OutputConnector::node(*node_id, output_index)) .map(|output_index| OutputConnector::node(*node_id, output_index))
}) })
.flatten()
}) })
.next() .next()
.or_else(|| { .or_else(|| {
self.import_export_ports(network_path) self.with_import_export_ports(network_path, |import_export_ports| {
.and_then(|import_export_ports| import_export_ports.clicked_output_port_from_point(point).map(OutputConnector::Import)) import_export_ports.clicked_output_port_from_point(point).map(OutputConnector::Import)
})
.flatten()
}) })
} }
pub fn input_position(&mut 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
.node_click_targets(node_id, network_path) .with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
.and_then(|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(),
InputConnector::Export(export_index) => self InputConnector::Export(export_index) => self
.import_export_ports(network_path) .with_import_export_ports(network_path, |import_export_ports| import_export_ports.input_port_position(*export_index))
.and_then(|import_export_ports| import_export_ports.input_port_position(*export_index)), .flatten(),
} }
} }
pub fn output_position(&mut 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
.node_click_targets(node_id, network_path) .with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
.and_then(|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(),
OutputConnector::Import(import_index) => self OutputConnector::Import(import_index) => self
.import_export_ports(network_path) .with_import_export_ports(network_path, |import_export_ports| import_export_ports.output_port_position(*import_index))
.and_then(|import_export_ports| import_export_ports.output_port_position(*import_index)), .flatten(),
} }
} }
/// Get the combined bounding box of the click targets of the selected nodes in the node graph in viewport space /// Get the combined bounding box of the click targets of the selected nodes in the node graph in viewport space
pub fn selected_nodes_bounding_box_viewport(&mut self, network_path: &[NodeId]) -> Option<[DVec2; 2]> { pub fn selected_nodes_bounding_box_viewport(&self, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
// Always get the bounding box for nodes in the currently viewed network // Always get the bounding box for nodes in the currently viewed network
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 selected_nodes_bounding_box_viewport"); log::error!("Could not get nested network_metadata in selected_nodes_bounding_box_viewport");
@@ -310,7 +320,7 @@ impl NodeNetworkInterface {
} }
/// Get the combined bounding box of the click targets of the selected nodes in the node graph in layer space /// Get the combined bounding box of the click targets of the selected nodes in the node graph in layer space
pub fn selected_nodes_bounding_box(&mut self, network_path: &[NodeId]) -> Option<[DVec2; 2]> { pub fn selected_nodes_bounding_box(&self, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else { let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else {
log::error!("Could not get selected nodes in selected_nodes_bounding_box_viewport"); log::error!("Could not get selected nodes in selected_nodes_bounding_box_viewport");
return None; return None;
@@ -320,16 +330,13 @@ impl NodeNetworkInterface {
.cloned() .cloned()
.collect::<Vec<_>>() .collect::<Vec<_>>()
.iter() .iter()
.filter_map(|node_id| { .filter_map(|node_id| self.node_bounding_box(node_id, network_path))
self.node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box())
})
.reduce(graphene_std::renderer::Quad::combine_bounds) .reduce(graphene_std::renderer::Quad::combine_bounds)
} }
/// Gets the bounding box in viewport coordinates for each node in the node graph /// Gets the bounding box in viewport coordinates for each node in the node graph
pub fn graph_bounds_viewport_space(&mut self, network_path: &[NodeId]) -> Option<[DVec2; 2]> { pub fn graph_bounds_viewport_space(&self, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
let bounds = *self.all_nodes_bounding_box(network_path)?; let bounds = self.all_nodes_bounding_box(network_path)?;
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 graph_bounds_viewport_space"); log::error!("Could not get nested network_metadata in graph_bounds_viewport_space");
return None; return None;
@@ -339,7 +346,7 @@ impl NodeNetworkInterface {
bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport) bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport)
} }
pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> (HashMap<NodeId, u32>, HashMap<NodeId, u32>, HashMap<NodeId, bool>) { pub fn collect_layer_widths(&self, network_path: &[NodeId]) -> (HashMap<NodeId, u32>, HashMap<NodeId, u32>, HashMap<NodeId, bool>) {
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 collect_layer_widths"); log::error!("Could not get nested network_metadata in collect_layer_widths");
return (HashMap::new(), HashMap::new(), HashMap::new()); return (HashMap::new(), HashMap::new(), HashMap::new());
@@ -117,7 +117,7 @@ impl NodeNetworkInterface {
// } // }
} }
pub(crate) fn valid_upstream_chain_nodes(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<NodeId> { pub(crate) fn valid_upstream_chain_nodes(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<NodeId> {
let InputConnector::Node { let InputConnector::Node {
node_id: input_connector_node_id, node_id: input_connector_node_id,
input_index, input_index,
@@ -136,11 +136,16 @@ impl NodeNetworkInterface {
if self.is_layer(&upstream_node, network_path) || self.hidden_primary_output(&upstream_node, network_path) { if self.is_layer(&upstream_node, network_path) || self.hidden_primary_output(&upstream_node, network_path) {
break; break;
} }
let Some(outward_wires) = self.outward_wires(network_path).and_then(|outward_wires| outward_wires.get(&OutputConnector::node(upstream_node, 0))) else { let downstream_connection_count = self
.with_outward_wires(network_path, |outward_wires| {
outward_wires.get(&OutputConnector::node(upstream_node, 0)).map(|connections| connections.len())
})
.flatten();
let Some(downstream_connection_count) = downstream_connection_count else {
log::error!("Could not get outward wires in try_set_upstream_to_chain"); log::error!("Could not get outward wires in try_set_upstream_to_chain");
break; break;
}; };
if outward_wires.len() != 1 { if downstream_connection_count != 1 {
break; break;
} }
let downstream_position = self.position(&downstream_id, network_path); let downstream_position = self.position(&downstream_id, network_path);
@@ -300,13 +305,15 @@ impl NodeNetworkInterface {
return; return;
}; };
if !shift_without_push { if !shift_without_push {
// The owned nodes of each layer are populated by the stack dependents load, which otherwise may not run until after this filter
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) {
&& let Some(owned_nodes) = self.owned_nodes(&node_id, network_path) self.with_owned_nodes(&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); }
} });
}; };
} }
} }
@@ -429,7 +436,7 @@ impl NodeNetworkInterface {
log::error!("Could not get nested network_metadata in export_ports"); log::error!("Could not get nested network_metadata in export_ports");
continue; continue;
}; };
if let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents if let Some(stack_dependents) = network_metadata.transient_metadata.stack_dependents.get_loaded_mut()
&& let Some(LayerOwner::None(offset)) = stack_dependents.get_mut(node_id) && let Some(LayerOwner::None(offset)) = stack_dependents.get_mut(node_id)
{ {
*offset += shift_sign; *offset += shift_sign;
@@ -472,7 +479,7 @@ impl NodeNetworkInterface {
if self.selected_nodes_in_nested_network(network_path).is_some_and(|selected_nodes| { if self.selected_nodes_in_nested_network(network_path).is_some_and(|selected_nodes| {
selected_nodes selected_nodes
.selected_nodes() .selected_nodes()
.any(|selected_node| selected_node == node_id || self.owned_nodes(node_id, network_path).is_some_and(|owned_nodes| owned_nodes.contains(selected_node))) .any(|selected_node| selected_node == node_id || self.with_owned_nodes(node_id, network_path, |owned_nodes| owned_nodes.contains(selected_node)) == Some(true))
}) { }) {
return None; return None;
}; };
@@ -530,7 +537,7 @@ impl NodeNetworkInterface {
log::error!("Could not get nested network_metadata in export_ports"); log::error!("Could not get nested network_metadata in export_ports");
return; return;
}; };
let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents else { let Some(stack_dependents) = network_metadata.transient_metadata.stack_dependents.get_loaded_mut() else {
log::error!("Stack dependents should be loaded in vertical_shift_with_push"); log::error!("Stack dependents should be loaded in vertical_shift_with_push");
return; return;
}; };
@@ -564,7 +571,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.owned_nodes(node_id, network_path).cloned() { if let Some(owned_nodes) = self.with_owned_nodes(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);
@@ -576,53 +583,58 @@ impl NodeNetworkInterface {
pub(crate) fn check_collision_with_stack_dependents(&mut self, node_id: &NodeId, shift_sign: i32, network_path: &[NodeId]) -> Vec<(NodeId, LayerOwner)> { pub(crate) fn check_collision_with_stack_dependents(&mut self, node_id: &NodeId, shift_sign: i32, network_path: &[NodeId]) -> Vec<(NodeId, LayerOwner)> {
self.try_load_all_node_click_targets(network_path); self.try_load_all_node_click_targets(network_path);
self.try_load_stack_dependents(network_path); self.try_load_stack_dependents(network_path);
let Some(stack_dependents) = self.try_get_stack_dependents(network_path) else {
// Check collisions and for all owned nodes and recursively shift them
let nodes_to_shift = self.with_stack_dependents(network_path, |stack_dependents| {
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;
}
if node_to_check_collision.0 == current_node {
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");
continue;
};
let Some(node_bounding_box) = self.try_get_node_bounding_box(node_to_check_collision.0, network_path) else {
log::error!("Could not get bounding box for node {node_to_check_collision:?} in shift_selected_nodes");
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;
current_node_bounding_box[0].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;
if collision {
nodes_to_shift.push((*node_to_check_collision.0, node_to_check_collision.1.clone()));
}
}
}
nodes_to_shift
});
let Some(nodes_to_shift) = nodes_to_shift else {
log::error!("Could not load stack dependents in shift_selected_nodes"); log::error!("Could not load stack dependents in shift_selected_nodes");
return Vec::new(); return Vec::new();
}; };
// Check collisions and for all owned nodes and recursively shift them
let mut nodes_to_shift = Vec::new();
let default_hashset = HashSet::new();
let owned_nodes = self.owned_nodes(node_id, network_path).unwrap_or(&default_hashset);
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;
}
if node_to_check_collision.0 == current_node {
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");
continue;
};
let Some(node_bounding_box) = self.try_get_node_bounding_box(node_to_check_collision.0, network_path) else {
log::error!("Could not get bounding box for node {node_to_check_collision:?} in shift_selected_nodes");
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;
current_node_bounding_box[0].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;
if collision {
nodes_to_shift.push((*node_to_check_collision.0, node_to_check_collision.1.clone()));
}
}
}
nodes_to_shift nodes_to_shift
} }
@@ -1486,7 +1486,6 @@ impl NodeNetworkInterface {
node_metadata.persistent_metadata.node_type_metadata = if is_layer { node_metadata.persistent_metadata.node_type_metadata = if is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position), position: LayerPosition::Absolute(position),
owned_nodes: TransientMetadata::Unloaded,
}) })
} else { } else {
NodeTypePersistentMetadata::Node(NodePersistentMetadata { NodeTypePersistentMetadata::Node(NodePersistentMetadata {
@@ -1509,15 +1508,11 @@ impl NodeNetworkInterface {
if let Some(downstream_position) = is_layer.then_some(single_downstream_layer_position).flatten() { if let Some(downstream_position) = is_layer.then_some(single_downstream_layer_position).flatten() {
node_metadata.persistent_metadata.node_type_metadata = NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { node_metadata.persistent_metadata.node_type_metadata = NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Stack((position.y - downstream_position.y - STACK_VERTICAL_GAP).max(0) as u32), position: LayerPosition::Stack((position.y - downstream_position.y - STACK_VERTICAL_GAP).max(0) as u32),
owned_nodes: TransientMetadata::Unloaded,
}) })
} }
if is_layer { node_metadata.transient_metadata.layer_width.unload();
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata::default()); node_metadata.transient_metadata.owned_nodes.unload();
} else {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Node;
}
self.transaction_modified(); self.transaction_modified();
self.unload_stack_dependents(network_path); self.unload_stack_dependents(network_path);
@@ -103,27 +103,38 @@ impl NodeNetworkInterface {
} }
/// Returns the first downstream layer(inclusive) from a node. If the node is a layer, it will return itself. /// Returns the first downstream layer(inclusive) from a node. If the node is a layer, it will return itself.
pub fn downstream_layer_for_chain_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<NodeId> { pub fn downstream_layer_for_chain_node(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<NodeId> {
let mut id = *node_id; let mut id = *node_id;
while !self.is_layer(&id, network_path) { while !self.is_layer(&id, network_path) {
id = self.outward_wires(network_path)?.get(&OutputConnector::node(id, 0))?.first()?.node_id()?; id = self.with_outward_wires(network_path, |outward_wires| {
outward_wires
.get(&OutputConnector::node(id, 0))
.and_then(|connections| connections.first())
.and_then(|connector| connector.node_id())
})??;
} }
Some(id) Some(id)
} }
/// Returns all downstream layers (inclusive) from a node. If the node is a layer, it will return itself. /// Returns all downstream layers (inclusive) from a node. If the node is a layer, it will return itself.
pub fn downstream_layers(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Vec<NodeId> { pub fn downstream_layers(&self, node_id: &NodeId, network_path: &[NodeId]) -> Vec<NodeId> {
let mut stack = vec![*node_id]; let mut stack = vec![*node_id];
let mut layers = Vec::new(); let mut layers = Vec::new();
while let Some(current_node) = stack.pop() { while let Some(current_node) = stack.pop() {
if self.is_layer(&current_node, network_path) { if self.is_layer(&current_node, network_path) {
layers.push(current_node); layers.push(current_node);
} else { } else {
let Some(outward_wires) = self.outward_wires(network_path).and_then(|outward_wires| outward_wires.get(&OutputConnector::node(current_node, 0))) else { let downstream_found = self.with_outward_wires(network_path, |outward_wires| {
let Some(connections) = outward_wires.get(&OutputConnector::node(current_node, 0)) else {
return false;
};
stack.extend(connections.iter().filter_map(|input_connector| input_connector.node_id()));
true
});
if downstream_found != Some(true) {
log::error!("Could not get outward wires in downstream_layer"); log::error!("Could not get outward wires in downstream_layer");
return Vec::new(); return Vec::new();
}; }
stack.extend(outward_wires.iter().filter_map(|input_connector| input_connector.node_id()));
} }
} }
layers layers
@@ -167,18 +178,22 @@ impl NodeNetworkInterface {
/// Creates a copy for each node by disconnecting nodes which are not connected to other copied nodes. /// Creates a copy for each node by disconnecting nodes which are not connected to other copied nodes.
/// Returns an iterator of all persistent metadata for a node and their ids /// Returns an iterator of all persistent metadata for a node and their ids
pub fn copy_nodes<'a>(&'a mut self, new_ids: &'a HashMap<NodeId, NodeId>, network_path: &'a [NodeId]) -> impl Iterator<Item = (NodeId, NodeTemplate)> + 'a { pub fn copy_nodes<'a>(&'a self, new_ids: &'a HashMap<NodeId, NodeId>, network_path: &'a [NodeId]) -> impl Iterator<Item = (NodeId, NodeTemplate)> + 'a {
let mut new_nodes = new_ids let mut new_nodes = new_ids
.iter() .iter()
.filter_map(|(node_id, &new)| { .filter_map(|(node_id, &new)| {
self.create_node_template(node_id, network_path).and_then(|mut node_template| { self.create_node_template(node_id, network_path).and_then(|mut node_template| {
let Some(outward_wires) = self.outward_wires(network_path) else { // 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| {
outputs
.iter()
.any(|input_connector| input_connector.node_id().is_some_and(|upstream_id| new_ids.keys().any(|key| *key == upstream_id)))
})
}) else {
log::error!("Could not get outward wires in copy_nodes"); log::error!("Could not get outward wires in copy_nodes");
return None; return None;
}; };
// TODO: Get downstream connections from all outputs
let mut downstream_connections = outward_wires.get(&OutputConnector::node(*node_id, 0)).map_or([].iter(), |outputs| outputs.iter());
let has_selected_node_downstream = downstream_connections.any(|input_connector| input_connector.node_id().is_some_and(|upstream_id| new_ids.keys().any(|key| *key == upstream_id)));
// If the copied node does not have a downstream connection to another copied node, then set the position to absolute // If the copied node does not have a downstream connection to another copied node, then set the position to absolute
if !has_selected_node_downstream { if !has_selected_node_downstream {
let Some(position) = self.position(node_id, network_path) else { let Some(position) = self.position(node_id, network_path) else {
@@ -247,7 +262,7 @@ impl NodeNetworkInterface {
/// Converts all node id inputs to a new id based on a HashMap. /// Converts all node id inputs to a new id based on a HashMap.
/// ///
/// If the node is not in the hashmap then a default input is found based on the compiled network, using the node_id passed as a parameter /// If the node is not in the hashmap then a default input is found based on the compiled network, using the node_id passed as a parameter
pub fn map_ids(&mut self, mut node_template: NodeTemplate, node_id: &NodeId, new_ids: &HashMap<NodeId, NodeId>, network_path: &[NodeId]) -> NodeTemplate { pub fn map_ids(&self, mut node_template: NodeTemplate, node_id: &NodeId, new_ids: &HashMap<NodeId, NodeId>, network_path: &[NodeId]) -> NodeTemplate {
for (input_index, input) in node_template.inputs.iter_mut().enumerate() { for (input_index, input) in node_template.inputs.iter_mut().enumerate() {
if let &mut NodeInput::Node { node_id: id, output_index } = input { if let &mut NodeInput::Node { node_id: id, output_index } = input {
if let Some(&new_id) = new_ids.get(&id) { if let Some(&new_id) = new_ids.get(&id) {
@@ -278,16 +293,14 @@ impl NodeNetworkInterface {
} }
} }
pub fn position(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<IVec2> { pub fn position(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<IVec2> {
let top_left_position = self self.try_load_node_click_targets(node_id, network_path);
.node_click_targets(node_id, network_path) let top_left_position = self.try_get_node_bounding_box(node_id, network_path).map(|mut bounding_box| {
.and_then(|click_targets| click_targets.node_click_target.bounding_box()) if !self.is_layer(node_id, network_path) {
.map(|mut bounding_box| { bounding_box[0] -= DVec2::new(0., 12.);
if !self.is_layer(node_id, network_path) { }
bounding_box[0] -= DVec2::new(0., 12.); (bounding_box[0] / 24.).as_ivec2()
} });
(bounding_box[0] / 24.).as_ivec2()
});
top_left_position.map(|position| { top_left_position.map(|position| {
if self.is_layer(node_id, network_path) { if self.is_layer(node_id, network_path) {
position + IVec2::new(self.chain_width(node_id, network_path) as i32, 0) position + IVec2::new(self.chain_width(node_id, network_path) as i32, 0)
@@ -301,7 +314,7 @@ impl NodeNetworkInterface {
collect_network_resources(self.document_network(), target); collect_network_resources(self.document_network(), target);
} }
pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphOutput>> { pub fn frontend_imports(&self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphOutput>> {
match network_path.split_last() { match network_path.split_last() {
Some((node_id, encapsulating_network_path)) => { Some((node_id, encapsulating_network_path)) => {
let Some(node) = self.document_node(node_id, encapsulating_network_path) else { let Some(node) = self.document_node(node_id, encapsulating_network_path) else {
@@ -321,7 +334,7 @@ impl NodeNetworkInterface {
} }
} }
pub fn frontend_exports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphInput>> { pub fn frontend_exports(&self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphInput>> {
let Some(network) = self.nested_network(network_path) else { return Vec::new() }; let Some(network) = self.nested_network(network_path) else { return Vec::new() };
let mut frontend_exports = ((0..network.exports.len()).map(|export_index| self.frontend_input_from_connector(&InputConnector::Export(export_index), network_path))).collect::<Vec<_>>(); let mut frontend_exports = ((0..network.exports.len()).map(|export_index| self.frontend_input_from_connector(&InputConnector::Export(export_index), network_path))).collect::<Vec<_>>();
if frontend_exports.is_empty() { if frontend_exports.is_empty() {
@@ -330,8 +343,8 @@ impl NodeNetworkInterface {
frontend_exports frontend_exports
} }
pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> { pub fn import_export_position(&self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> {
let Some(all_nodes_bounding_box) = self.all_nodes_bounding_box(network_path).cloned() else { let Some(all_nodes_bounding_box) = self.all_nodes_bounding_box(network_path) else {
log::error!("Could not get all nodes bounding box in load_export_ports"); log::error!("Could not get all nodes bounding box in load_export_ports");
return None; return None;
}; };
@@ -408,7 +421,7 @@ impl NodeNetworkInterface {
} }
/// Returns None if there is an error, it is a hidden primary export, or a hidden input /// Returns None if there is an error, it is a hidden primary export, or a hidden input
pub fn frontend_input_from_connector(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<FrontendGraphInput> { pub fn frontend_input_from_connector(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Option<FrontendGraphInput> {
// Return None if it is a hidden input // Return None if it is a hidden input
if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) { if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) {
return None; return None;
@@ -471,7 +484,7 @@ impl NodeNetworkInterface {
} }
/// Returns None if there is an error, it is the document network, a hidden primary output or import /// Returns None if there is an error, it is the document network, a hidden primary output or import
pub fn frontend_output_from_connector(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<FrontendGraphOutput> { pub fn frontend_output_from_connector(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Option<FrontendGraphOutput> {
let output_type = self.output_type(output_connector, network_path); let output_type = self.output_type(output_connector, network_path);
let (name, description) = match output_connector { let (name, description) = match output_connector {
OutputConnector::Node { node_id, output_index } => { OutputConnector::Node { node_id, output_index } => {
@@ -512,9 +525,8 @@ impl NodeNetworkInterface {
let data_type = output_type.displayed_type(); let data_type = output_type.displayed_type();
let resolved_type = output_type.resolved_type_node_string(); let resolved_type = output_type.resolved_type_node_string();
let mut connected_to = self let mut connected_to = self
.outward_wires(network_path) .with_outward_wires(network_path, |outward_wires| outward_wires.get(output_connector).cloned())
.and_then(|outward_wires| outward_wires.get(output_connector)) .flatten()
.cloned()
.unwrap_or_default() .unwrap_or_default()
.iter() .iter()
.map(|input| match input { .map(|input| match input {
@@ -539,10 +551,10 @@ impl NodeNetworkInterface {
}) })
} }
pub fn height_from_click_target(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<u32> { pub fn height_from_click_target(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<u32> {
self.try_load_node_click_targets(node_id, network_path);
let mut node_height: Option<u32> = self let mut node_height: Option<u32> = self
.node_click_targets(node_id, network_path) .try_get_node_bounding_box(node_id, network_path)
.and_then(|click_targets: &DocumentNodeClickTargets| click_targets.node_click_target.bounding_box())
.map(|bounding_box| ((bounding_box[1].y - bounding_box[0].y) / 24.) as u32); .map(|bounding_box| ((bounding_box[1].y - bounding_box[0].y) / 24.) as u32);
if !self.is_layer(node_id, network_path) { if !self.is_layer(node_id, network_path) {
node_height = node_height.map(|height| height + 1); node_height = node_height.map(|height| height + 1);
@@ -552,7 +564,7 @@ impl NodeNetworkInterface {
/// Returns whether every downstream path from the node's outputs stays within the dependent set defined by `classify`, meaning nothing else in the graph depends on this node. /// Returns whether every downstream path from the node's outputs stays within the dependent set defined by `classify`, meaning nothing else in the graph depends on this node.
/// Reaching an export or a dead end (a walked node with no outward wires) always escapes. O(nodes + wires) per call. /// Reaching an export or a dead end (a walked node with no outward wires) always escapes. O(nodes + wires) per call.
pub(crate) fn is_sole_dependent(&mut self, node_id: NodeId, network_path: &[NodeId], classify: impl Fn(NodeId, usize) -> SoleDependentStep) -> bool { pub(crate) fn is_sole_dependent(&self, node_id: NodeId, network_path: &[NodeId], classify: impl Fn(NodeId, usize) -> SoleDependentStep) -> bool {
let mut visited = HashSet::new(); let mut visited = HashSet::new();
let mut stack = vec![node_id]; let mut stack = vec![node_id];
@@ -561,47 +573,54 @@ impl NodeNetworkInterface {
continue; continue;
} }
let number_of_outputs = self.number_of_outputs(&current_node, network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in is_sole_dependent");
return false;
};
// Classify every downstream connection of this node, collecting the ones to keep walking through // Classify every downstream connection of this node, collecting the ones to keep walking through
let mut has_downstream_connections = false; let number_of_outputs = self.number_of_outputs(&current_node, network_path);
let mut nodes_to_walk_through = Vec::new(); let keeps_within_set = self.with_outward_wires(network_path, |outward_wires| {
for output_index in 0..number_of_outputs { let mut has_downstream_connections = false;
let Some(downstream_connections) = outward_wires.get(&OutputConnector::node(current_node, output_index)) else { let mut nodes_to_walk_through = Vec::new();
continue; for output_index in 0..number_of_outputs {
}; let Some(downstream_connections) = outward_wires.get(&OutputConnector::node(current_node, output_index)) else {
for downstream_connection in downstream_connections { continue;
has_downstream_connections = true;
let InputConnector::Node {
node_id: downstream_node,
input_index,
} = downstream_connection
else {
return false;
}; };
match classify(*downstream_node, *input_index) { for downstream_connection in downstream_connections {
SoleDependentStep::Terminate => {} has_downstream_connections = true;
SoleDependentStep::Continue => nodes_to_walk_through.push(*downstream_node), let InputConnector::Node {
SoleDependentStep::Escape => return false, node_id: downstream_node,
input_index,
} = downstream_connection
else {
return false;
};
match classify(*downstream_node, *input_index) {
SoleDependentStep::Terminate => {}
SoleDependentStep::Continue => nodes_to_walk_through.push(*downstream_node),
SoleDependentStep::Escape => return false,
}
} }
} }
}
if !has_downstream_connections { if !has_downstream_connections {
return false; return false;
}
stack.extend(nodes_to_walk_through);
true
});
match keeps_within_set {
Some(true) => {}
Some(false) => return false,
None => {
log::error!("Could not get outward wires in is_sole_dependent");
return false;
}
} }
stack.extend(nodes_to_walk_through);
} }
true true
} }
// All chain nodes and branches from the chain which are sole dependents of the layer // All chain nodes and branches from the chain which are sole dependents of the layer
pub fn upstream_nodes_below_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> HashSet<NodeId> { pub fn upstream_nodes_below_layer(&self, node_id: &NodeId, network_path: &[NodeId]) -> HashSet<NodeId> {
// Every upstream node below layer must be a sole dependent // Every upstream node below layer must be a sole dependent
let mut upstream_nodes_below_layer = HashSet::new(); let mut upstream_nodes_below_layer = HashSet::new();
@@ -712,10 +731,6 @@ impl NodeNetworkInterface {
self.view(network_path).ok().and_then(|view| view.persistent_input_metadata(node_id, index).ok()) self.view(network_path).ok().and_then(|view| view.persistent_input_metadata(node_id, index).ok())
} }
pub(crate) fn transient_input_metadata(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&InputTransientMetadata> {
self.view(network_path).ok().and_then(|view| view.transient_input_metadata(node_id, index).ok())
}
pub fn set_input_override(&mut self, node_id: &NodeId, index: usize, widget_override: Option<String>, network_path: &[NodeId]) { pub fn set_input_override(&mut self, node_id: &NodeId, index: usize, widget_override: Option<String>, network_path: &[NodeId]) {
let Some(metadata) = self let Some(metadata) = self
.node_metadata_mut(node_id, network_path) .node_metadata_mut(node_id, network_path)
@@ -728,7 +743,7 @@ impl NodeNetworkInterface {
} }
/// Returns the input name to display in the properties panel. If the name is empty then the type is used. /// Returns the input name to display in the properties panel. If the name is empty then the type is used.
pub fn displayed_input_name_and_description(&mut self, node_id: &NodeId, input_index: usize, network_path: &[NodeId]) -> (String, String) { pub fn displayed_input_name_and_description(&self, node_id: &NodeId, input_index: usize, network_path: &[NodeId]) -> (String, String) {
let Some(input_metadata) = self.persistent_input_metadata(node_id, input_index, network_path) else { let Some(input_metadata) = self.persistent_input_metadata(node_id, input_index, network_path) else {
log::warn!("input metadata not found in displayed_input_name_and_description"); log::warn!("input metadata not found in displayed_input_name_and_description");
return (String::new(), String::new()); return (String::new(), String::new());
@@ -775,12 +790,12 @@ impl NodeNetworkInterface {
self.query(network_path, "is_layer", |view| view.is_layer(node_id)).unwrap_or_default() self.query(network_path, "is_layer", |view| view.is_layer(node_id)).unwrap_or_default()
} }
pub fn primary_output_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool { pub fn primary_output_connected_to_layer(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(outward_wires) = self.outward_wires(network_path) else { let Some(downstream_connectors) = self.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)).cloned()) else {
log::error!("Could not get outward_wires in primary_output_connected_to_layer"); log::error!("Could not get outward_wires in primary_output_connected_to_layer");
return false; return false;
}; };
let Some(downstream_connectors) = outward_wires.get(&OutputConnector::node(*node_id, 0)) else { let Some(downstream_connectors) = downstream_connectors else {
log::error!("Could not get downstream_connectors in primary_output_connected_to_layer"); log::error!("Could not get downstream_connectors in primary_output_connected_to_layer");
return false; return false;
}; };
@@ -1086,7 +1101,6 @@ impl NodeNetworkInterface {
node_metadata.persistent_metadata.node_type_metadata = if old_node.is_layer { node_metadata.persistent_metadata.node_type_metadata = if old_node.is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(old_node.metadata.position), position: LayerPosition::Absolute(old_node.metadata.position),
owned_nodes: TransientMetadata::Unloaded,
}) })
} else { } else {
NodeTypePersistentMetadata::Node(NodePersistentMetadata { NodeTypePersistentMetadata::Node(NodePersistentMetadata {
@@ -116,7 +116,7 @@ impl TypeSource {
} }
impl NodeNetworkInterface { impl NodeNetworkInterface {
fn input_has_error(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> bool { fn input_has_error(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> bool {
match input_connector { match input_connector {
InputConnector::Node { node_id, input_index } => { InputConnector::Node { node_id, input_index } => {
let Some(implementation) = self.implementation(node_id, network_path) else { let Some(implementation) = self.implementation(node_id, network_path) else {
@@ -126,9 +126,9 @@ impl NodeNetworkInterface {
let node_path = [network_path, &[*node_id]].concat(); let node_path = [network_path, &[*node_id]].concat();
match implementation { match implementation {
DocumentNodeImplementation::Network(_) => { DocumentNodeImplementation::Network(_) => {
let Some(map) = self.outward_wires(&node_path) else { return false }; let outward_wires = self.with_outward_wires(&node_path, |map| map.get(&OutputConnector::Import(*input_index)).cloned()).flatten();
let Some(outward_wires) = map.get(&OutputConnector::Import(*input_index)) else { return false }; let Some(outward_wires) = outward_wires else { return false };
outward_wires.clone().iter().any(|connector| match connector { 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(*node_id, *input_index), &node_path),
InputConnector::Export(_) => false, InputConnector::Export(_) => false,
}) })
@@ -148,7 +148,7 @@ impl NodeNetworkInterface {
} }
} }
pub fn input_type_not_invalid(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TypeSource { pub fn input_type_not_invalid(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> TypeSource {
let Some(input) = self.input_from_connector(input_connector, network_path) else { let Some(input) = self.input_from_connector(input_connector, network_path) else {
return TypeSource::Error("Could not get input from connector"); return TypeSource::Error("Could not get input from connector");
}; };
@@ -176,7 +176,7 @@ impl NodeNetworkInterface {
/// Get the [`TypeSource`] for any InputConnector. /// Get the [`TypeSource`] for any InputConnector.
/// If the input is not compiled, then an Unknown or default from the definition is returned. /// If the input is not compiled, then an Unknown or default from the definition is returned.
pub fn input_type(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TypeSource { pub fn input_type(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> TypeSource {
// First check if there is an error with this node or any protonodes it is connected to // First check if there is an error with this node or any protonodes it is connected to
if self.input_has_error(input_connector, network_path) { if self.input_has_error(input_connector, network_path) {
return TypeSource::Invalid; return TypeSource::Invalid;
@@ -185,7 +185,7 @@ impl NodeNetworkInterface {
} }
/// Gets the default tagged value for an input. If its not compiled, then it tries to get a valid type. If there are no valid types, then it picks a random implementation. /// Gets the default tagged value for an input. If its not compiled, then it tries to get a valid type. If there are no valid types, then it picks a random implementation.
pub fn tagged_value_from_input(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TaggedValue { pub fn tagged_value_from_input(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> TaggedValue {
let guaranteed_type = match self.input_type(input_connector, network_path) { let guaranteed_type = match self.input_type(input_connector, network_path) {
TypeSource::Compiled(compiled) => compiled, TypeSource::Compiled(compiled) => compiled,
TypeSource::TaggedValue(value) => value, TypeSource::TaggedValue(value) => value,
@@ -213,7 +213,7 @@ impl NodeNetworkInterface {
} }
/// A list of all valid input types for this specific node. /// A list of all valid input types for this specific node.
pub fn potential_valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<Type> { pub fn potential_valid_input_types(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<Type> {
let InputConnector::Node { node_id, input_index } = input_connector else { let InputConnector::Node { node_id, input_index } = input_connector else {
// An export can have any type connected to it // An export can have any type connected to it
return vec![graph_craft::generic!(T)]; return vec![graph_craft::generic!(T)];
@@ -225,17 +225,15 @@ impl NodeNetworkInterface {
match implementation { match implementation {
DocumentNodeImplementation::Network(_) => { DocumentNodeImplementation::Network(_) => {
let nested_path = [network_path, &[*node_id]].concat(); let nested_path = [network_path, &[*node_id]].concat();
let Some(outward_wires) = self.outward_wires(&nested_path) else { let inputs_from_import = self
log::error!("Could not get outward wires in potential_valid_input_types"); .with_outward_wires(&nested_path, |outward_wires| outward_wires.get(&OutputConnector::Import(*input_index)).cloned())
return Vec::new(); .flatten();
}; let Some(inputs_from_import) = inputs_from_import else {
let Some(inputs_from_import) = outward_wires.get(&OutputConnector::Import(*input_index)) else {
log::error!("Could not get inputs from import in potential_valid_input_types"); log::error!("Could not get inputs from import in potential_valid_input_types");
return Vec::new(); return Vec::new();
}; };
let intersection: HashSet<Type> = inputs_from_import let intersection: HashSet<Type> = inputs_from_import
.clone()
.iter() .iter()
.map(|input_connector| self.potential_valid_input_types(input_connector, &nested_path).into_iter().collect::<HashSet<_>>()) .map(|input_connector| self.potential_valid_input_types(input_connector, &nested_path).into_iter().collect::<HashSet<_>>())
.fold(None, |acc: Option<HashSet<Type>>, set| match acc { .fold(None, |acc: Option<HashSet<Type>>, set| match acc {
@@ -280,7 +278,7 @@ impl NodeNetworkInterface {
} }
/// Performs a downstream traversal to ensure input type will work in the full context of the graph. /// Performs a downstream traversal to ensure input type will work in the full context of the graph.
pub fn complete_valid_input_types(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<Type> { pub fn complete_valid_input_types(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<Type> {
match input_connector { match input_connector {
InputConnector::Node { node_id, input_index } => { InputConnector::Node { node_id, input_index } => {
let Some(implementation) = self.implementation(node_id, network_path) else { let Some(implementation) = self.implementation(node_id, network_path) else {
@@ -335,7 +333,7 @@ impl NodeNetworkInterface {
} }
} }
pub fn output_type(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> TypeSource { pub fn output_type(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> TypeSource {
match output_connector { match output_connector {
OutputConnector::Node { node_id, output_index } => { 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 // A hidden node is replaced by a passthrough during flattening, so its output carries its primary input's type
@@ -372,18 +370,14 @@ impl NodeNetworkInterface {
} }
/// The valid output types are all types that are valid for each downstream connection. /// The valid output types are all types that are valid for each downstream connection.
fn valid_output_types(&mut self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Vec<Type> { fn valid_output_types(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Vec<Type> {
let Some(outward_wires) = self.outward_wires(network_path) else { let inputs_from_import = self.with_outward_wires(network_path, |outward_wires| outward_wires.get(output_connector).cloned()).flatten();
log::error!("Could not get outward wires in valid_output_types"); let Some(inputs_from_import) = inputs_from_import else {
return Vec::new();
};
let Some(inputs_from_import) = outward_wires.get(output_connector) else {
log::error!("Could not get inputs from import in valid_output_types"); log::error!("Could not get inputs from import in valid_output_types");
return Vec::new(); return Vec::new();
}; };
let intersection = inputs_from_import let intersection = inputs_from_import
.clone()
.iter() .iter()
.map(|input_connector| self.potential_valid_input_types(input_connector, network_path).into_iter().collect::<HashSet<_>>()) .map(|input_connector| self.potential_valid_input_types(input_connector, network_path).into_iter().collect::<HashSet<_>>())
.fold(None, |acc: Option<HashSet<Type>>, set| match acc { .fold(None, |acc: Option<HashSet<Type>>, set| match acc {
@@ -387,6 +387,5 @@ fn input_metadata_entry_to_runtime(entry: InputMetadataEntry) -> InputMetadata {
widget_override: entry.widget_override, widget_override: entry.widget_override,
input_data: entry.input_data, input_data: entry.input_data,
}, },
..Default::default()
} }
} }
@@ -377,26 +377,66 @@ impl<T> TransientMetadata<T> {
} }
} }
/// A lazily computed cache slot whose load and read paths work through &self, with interior mutability guarding the stored value.
#[derive(Debug, Clone)]
pub(crate) struct TransientCache<T>(std::cell::RefCell<TransientMetadata<T>>);
impl<T> Default for TransientCache<T> {
fn default() -> Self {
TransientCache(std::cell::RefCell::new(TransientMetadata::Unloaded))
}
}
impl<T> TransientCache<T> {
pub(crate) fn is_loaded(&self) -> bool {
self.0.borrow().is_loaded()
}
pub(crate) fn store(&self, value: T) {
*self.0.borrow_mut() = TransientMetadata::Loaded(value);
}
pub(crate) fn unload(&self) {
*self.0.borrow_mut() = TransientMetadata::Unloaded;
}
/// Runs `read` on the cached value if it is loaded.
pub(crate) fn with_loaded<R>(&self, read: impl FnOnce(&T) -> R) -> Option<R> {
match &*self.0.borrow() {
TransientMetadata::Loaded(value) => Some(read(value)),
TransientMetadata::Unloaded => None,
}
}
/// Direct access without runtime borrow tracking, for callers already holding exclusive access.
pub(crate) fn get_loaded_mut(&mut self) -> Option<&mut T> {
match self.0.get_mut() {
TransientMetadata::Loaded(value) => Some(value),
TransientMetadata::Unloaded => None,
}
}
}
/// If some network calculation is too slow to compute for every usage, cache the data here /// If some network calculation is too slow to compute for every usage, cache the data here
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct NodeNetworkTransientMetadata { pub struct NodeNetworkTransientMetadata {
pub selected_nodes: SelectedNodes, pub selected_nodes: SelectedNodes,
/// Sole dependents of the top of the stacks of all selected nodes. Used to determine which nodes are checked for collision when shifting. /// Sole dependents of the top of the stacks of all selected nodes. Used to determine which nodes are checked for collision when shifting.
/// The LayerOwner is used to determine whether the collided node should be shifted, or the layer that owns it. /// The LayerOwner is used to determine whether the collided node should be shifted, or the layer that owns it.
pub stack_dependents: TransientMetadata<HashMap<NodeId, LayerOwner>>, pub(crate) stack_dependents: TransientCache<HashMap<NodeId, LayerOwner>>,
/// Cache for the bounding box around all nodes in node graph space. /// Cache for the bounding box around all nodes in node graph space.
pub all_nodes_bounding_box: TransientMetadata<[DVec2; 2]>, pub(crate) all_nodes_bounding_box: TransientCache<[DVec2; 2]>,
// /// Cache bounding box for all "groups of nodes", which will be used to prevent overlapping nodes // /// Cache bounding box for all "groups of nodes", which will be used to prevent overlapping nodes
// node_group_bounding_box: Vec<(Subpath<ManipulatorGroupId>, Vec<Nodes>)>, // node_group_bounding_box: Vec<(Subpath<ManipulatorGroupId>, Vec<Nodes>)>,
/// Cache for all outward wire connections /// Cache for all outward wire connections
pub outward_wires: TransientMetadata<HashMap<OutputConnector, Vec<InputConnector>>>, pub(crate) outward_wires: TransientCache<HashMap<OutputConnector, Vec<InputConnector>>>,
/// All export connector click targets /// All export connector click targets
pub import_export_ports: TransientMetadata<Ports>, pub(crate) import_export_ports: TransientCache<Ports>,
/// Click targets for adding, removing, and moving import/export ports /// Click targets for adding, removing, and moving import/export ports
pub modify_import_export: TransientMetadata<ModifyImportExportClickTarget>, pub(crate) modify_import_export: TransientCache<ModifyImportExportClickTarget>,
// Wires from the exports /// Cached wire SVG paths per input connector, where an entry's presence means that wire is loaded.
pub wires: Vec<TransientMetadata<WirePathUpdate>>, pub(crate) wires: std::cell::RefCell<HashMap<InputConnector, WirePathUpdate>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -576,13 +616,6 @@ impl InputPersistentMetadata {
} }
} }
#[derive(Debug, Clone, Default)]
pub(crate) struct InputTransientMetadata {
pub(crate) wire: TransientMetadata<WirePathUpdate>,
// downstream_protonode: populated for all inputs after each compile
// types: populated for each protonode after each
}
/// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node. /// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node.
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
@@ -613,20 +646,9 @@ impl DocumentNodePersistentMetadata {
} }
} }
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct InputMetadata { pub struct InputMetadata {
pub persistent_metadata: InputPersistentMetadata, pub persistent_metadata: InputPersistentMetadata,
#[serde(skip)]
pub(crate) transient_metadata: InputTransientMetadata,
}
impl Clone for InputMetadata {
fn clone(&self) -> Self {
InputMetadata {
persistent_metadata: self.persistent_metadata.clone(),
transient_metadata: Default::default(),
}
}
} }
impl PartialEq for InputMetadata { impl PartialEq for InputMetadata {
@@ -641,7 +663,6 @@ impl From<(&str, &str)> for InputMetadata {
persistent_metadata: InputPersistentMetadata::default() persistent_metadata: InputPersistentMetadata::default()
.with_name(input_name_and_description.0) .with_name(input_name_and_description.0)
.with_description(input_name_and_description.1), .with_description(input_name_and_description.1),
..Default::default()
} }
} }
} }
@@ -650,7 +671,6 @@ impl InputMetadata {
pub fn with_name_description_override(input_name: &str, description: &str, widget_override: WidgetOverride) -> Self { pub fn with_name_description_override(input_name: &str, description: &str, widget_override: WidgetOverride) -> Self {
InputMetadata { InputMetadata {
persistent_metadata: InputPersistentMetadata::default().with_name(input_name).with_description(description).with_override(widget_override), persistent_metadata: InputPersistentMetadata::default().with_name(input_name).with_description(description).with_override(widget_override),
..Default::default()
} }
} }
} }
@@ -676,7 +696,6 @@ impl NodeTypePersistentMetadata {
pub fn layer(position: IVec2) -> NodeTypePersistentMetadata { pub fn layer(position: IVec2) -> NodeTypePersistentMetadata {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position), position: LayerPosition::Absolute(position),
owned_nodes: TransientMetadata::default(),
}) })
} }
} }
@@ -688,9 +707,6 @@ pub struct LayerPersistentMetadata {
// preview_click_target: Option<ClickTarget>, // preview_click_target: Option<ClickTarget>,
/// Stores the position of a layer node, which can either be Absolute or Stack /// Stores the position of a layer node, which can either be Absolute or Stack
pub position: LayerPosition, pub position: LayerPosition,
/// All nodes that should be moved when the layer is moved.
#[serde(skip)]
pub owned_nodes: TransientMetadata<HashSet<NodeId>>,
} }
impl PartialEq for LayerPersistentMetadata { impl PartialEq for LayerPersistentMetadata {
@@ -736,9 +752,11 @@ pub enum NodePosition {
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct DocumentNodeTransientMetadata { pub struct DocumentNodeTransientMetadata {
// The click targets are stored as a single struct since it is very rare for only one to be updated, and recomputing all click targets in one function is more efficient than storing them separately. // The click targets are stored as a single struct since it is very rare for only one to be updated, and recomputing all click targets in one function is more efficient than storing them separately.
pub click_targets: TransientMetadata<DocumentNodeClickTargets>, pub(crate) click_targets: TransientCache<DocumentNodeClickTargets>,
// Metadata that is specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer. /// All nodes that should be moved when this layer is moved, kept here since only layers own nodes.
pub node_type_metadata: NodeTypeTransientMetadata, pub(crate) owned_nodes: TransientCache<HashSet<NodeId>>,
/// Width in grid units from the left edge of the layer's thumbnail to its left end, cached since text measurement is slow. Only loaded for layers.
pub(crate) layer_width: TransientCache<u32>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -752,23 +770,6 @@ pub struct DocumentNodeClickTargets {
pub node_type_metadata: NodeTypeClickTargets, pub node_type_metadata: NodeTypeClickTargets,
} }
#[derive(Debug, Default, Clone)]
pub enum NodeTypeTransientMetadata {
Layer(LayerTransientMetadata),
#[default]
Node, // No transient data is stored exclusively for nodes
}
#[derive(Debug, Default, Clone)]
pub struct LayerTransientMetadata {
// Stores the width in grid units for layer nodes from the left edge of the thumbnail (+12px padding since thumbnail ends between grid spaces) to the left end of the node
/// This is necessary since calculating the layer width through web_sys is very slow
pub layer_width: TransientMetadata<u32>,
// Should not be a performance concern to calculate when needed with chain_width.
// Stores the width in grid units for layer nodes from the left edge of the thumbnail to the end of the chain
// chain_width: u32,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum NodeTypeClickTargets { pub enum NodeTypeClickTargets {
Layer(Box<LayerClickTargets>), Layer(Box<LayerClickTargets>),
@@ -290,14 +290,6 @@ impl<'a, 'p> NetworkView<'a, 'p> {
Ok(&input_metadata.persistent_metadata) Ok(&input_metadata.persistent_metadata)
} }
pub(crate) fn transient_input_metadata(&self, node_id: &NodeId, index: usize) -> Result<&'a InputTransientMetadata, 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),
})?;
Ok(&input_metadata.transient_metadata)
}
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(match self.input(input_connector)? {
NodeInput::Node { node_id, output_index, .. } => Some(OutputConnector::node(*node_id, *output_index)), NodeInput::Node { node_id, output_index, .. } => Some(OutputConnector::node(*node_id, *output_index)),