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-07-26 00:09:03 -07:00
committed by GitHub
parent eabaf82a26
commit 20f9780f55
11 changed files with 519 additions and 550 deletions

View File

@@ -1712,7 +1712,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
}
DocumentMessage::ZoomCanvasToFitAll => {
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 {
self.network_interface.document_bounds_document_space(true)
};

View File

@@ -79,7 +79,7 @@ impl NodeNetworkInterface {
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 {
log::error!("Could not get nested network_metadata in stack_dependents");
return;
@@ -90,12 +90,17 @@ impl NodeNetworkInterface {
}
}
pub(crate) fn try_get_stack_dependents(&self, network_path: &[NodeId]) -> Option<&HashMap<NodeId, LayerOwner>> {
let Some(network_metadata) = self.network_metadata(network_path) else {
/// Reads the stack dependents through &self if they are already loaded.
pub(crate) fn with_stack_dependents<R>(&self, network_path: &[NodeId], read: impl FnOnce(&HashMap<NodeId, LayerOwner>) -> R) -> Option<R> {
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");
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");
return None;
};
@@ -103,7 +108,7 @@ impl NodeNetworkInterface {
}
// 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 {
log::error!("Could not get selected nodes in load_stack_dependents");
return;
@@ -134,15 +139,19 @@ impl NodeNetworkInterface {
stack_tops.insert(current_node);
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");
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}");
break;
};
match layer_outward_wires.first() {
match first_downstream_input {
Some(downstream_input) => {
let Some(downstream_node) = downstream_input.node_id() else {
log::error!("Node connected to export should be absolute");
@@ -174,15 +183,11 @@ impl NodeNetworkInterface {
owned_sole_dependents.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");
continue;
};
let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &mut layer_node.persistent_metadata.node_type_metadata else {
log::error!("upstream layer should be a layer");
return;
};
*owned_nodes = TransientMetadata::Loaded(new_owned_nodes);
layer_node.transient_metadata.owned_nodes.store(new_owned_nodes);
}
}
}
@@ -222,12 +227,12 @@ impl NodeNetworkInterface {
}
}
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get current network in load_export_ports");
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get current network in load_stack_dependents");
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]) {
@@ -245,7 +250,7 @@ impl NodeNetworkInterface {
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() {
if let LayerOwner::None(offset) = layer_owner {
*offset = 0;
@@ -255,26 +260,36 @@ impl NodeNetworkInterface {
}
pub fn import_export_ports(&mut self, network_path: &[NodeId]) -> Option<&Ports> {
let Some(network_metadata) = self.network_metadata(network_path) else {
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);
}
self.try_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");
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");
return None;
};
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 {
log::error!("Could not get import_export_position");
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.));
}
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");
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]) {
@@ -342,74 +357,75 @@ impl NodeNetworkInterface {
if !network_metadata.transient_metadata.modify_import_export.is_loaded() {
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");
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");
return None;
};
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 remove_imports_exports = Ports::new();
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");
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");
return;
};
network_metadata.transient_metadata.modify_import_export = TransientMetadata::Loaded(ModifyImportExportClickTarget {
network_metadata.transient_metadata.modify_import_export.store(ModifyImportExportClickTarget {
remove_imports_exports,
reorder_imports_exports,
});
@@ -423,18 +439,16 @@ impl NodeNetworkInterface {
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 NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &layer_node.persistent_metadata.node_type_metadata else {
if !layer_node.persistent_metadata.is_layer() {
return None;
};
let TransientMetadata::Loaded(owned_nodes) = owned_nodes else {
return None;
};
Some(owned_nodes)
}
layer_node.transient_metadata.owned_nodes.with_loaded(read)
}
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 {
log::error!("Could not get nested network_metadata in all_nodes_bounding_box");
return None;
@@ -444,17 +458,14 @@ impl NodeNetworkInterface {
self.load_all_nodes_bounding_box(network_path);
}
let network_metadata = self.network_metadata(network_path)?;
let TransientMetadata::Loaded(bounding_box) = &network_metadata.transient_metadata.all_nodes_bounding_box else {
let bounding_box = self.network_metadata(network_path)?.transient_metadata.all_nodes_bounding_box.with_loaded(|bounds| *bounds);
if bounding_box.is_none() {
log::error!("could not load all nodes bounding box");
return None;
};
Some(bounding_box)
}
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 {
log::error!("Could not get nested network_metadata in load_all_nodes_bounding_box");
return;
@@ -463,15 +474,12 @@ impl NodeNetworkInterface {
let all_nodes_bounding_box = nodes
.iter()
.filter_map(|node_id| {
self.node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box())
})
.filter_map(|node_id| self.node_bounding_box(node_id, network_path))
.reduce(Quad::combine_bounds)
.unwrap_or([DVec2::new(0., 0.), DVec2::new(0., 0.)]);
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return };
network_metadata.transient_metadata.all_nodes_bounding_box = TransientMetadata::Loaded(all_nodes_bounding_box);
let Some(network_metadata) = self.network_metadata(network_path) else { return };
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]) {
@@ -484,18 +492,13 @@ impl NodeNetworkInterface {
}
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");
return None;
};
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 {
let Some(outward_wires) = network_metadata.transient_metadata.outward_wires.get_loaded_mut() else {
log::error!("could not load outward wires");
return None;
};
@@ -503,7 +506,23 @@ impl NodeNetworkInterface {
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 Some(network) = self.nested_network(network_path) else {
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]) {
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");
return;
};
@@ -566,7 +585,7 @@ impl NodeNetworkInterface {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
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;
};
@@ -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 {
log::error!("Could not get nested node_metadata in layer_width");
return None;
@@ -593,29 +612,15 @@ impl NodeNetworkInterface {
return None;
}
let layer_width_loaded = if let NodeTypeTransientMetadata::Layer(layer_metadata) = &node_metadata.transient_metadata.node_type_metadata {
layer_metadata.layer_width.is_loaded()
} else {
false
};
if !layer_width_loaded {
if !node_metadata.transient_metadata.layer_width.is_loaded() {
self.load_layer_width(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 {
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)
node_metadata.transient_metadata.layer_width.with_loaded(|layer_width| *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 FONT_SIZE: f64 = 14.;
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 = ((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");
return;
};
// Ensure layer width is not loaded for a non layer node
if node_metadata.persistent_metadata.is_layer() {
if let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata {
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),
});
}
node_metadata.transient_metadata.layer_width.store(layer_width);
} else {
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 is_layer && let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata {
layer_metadata.layer_width.unload();
if is_layer {
node_metadata.transient_metadata.layer_width.unload();
}
}
pub fn get_input_center(&mut self, input: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> {
let (ports, index) = match input {
pub fn get_input_center(&self, input: &InputConnector, network_path: &[NodeId]) -> Option<DVec2> {
fn port_center(ports: &Ports, index: usize) -> Option<DVec2> {
ports
.input_ports
.iter()
.find_map(|(input_index, click_target)| if index == *input_index { click_target.bounding_box_center() } else { None })
}
match input {
InputConnector::Node { node_id, input_index } => {
let node_click_target = self.node_click_targets(node_id, network_path)?;
(&node_click_target.port_click_targets, input_index)
self.try_load_node_click_targets(node_id, network_path);
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) => {
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 })
InputConnector::Export(export_index) => self.with_import_export_ports(network_path, |ports| port_center(ports, *export_index)).flatten(),
}
}
pub fn get_output_center(&mut self, output: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> {
let (ports, index) = match output {
pub fn get_output_center(&self, output: &OutputConnector, network_path: &[NodeId]) -> Option<DVec2> {
fn port_center(ports: &Ports, index: usize) -> Option<DVec2> {
ports
.output_ports
.iter()
.find_map(|(output_index, click_target)| if index == *output_index { click_target.bounding_box_center() } else { None })
}
match output {
OutputConnector::Node { node_id, output_index } => {
let node_click_target = self.node_click_targets(node_id, network_path)?;
(&node_click_target.port_click_targets, output_index)
self.try_load_node_click_targets(node_id, network_path);
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) => {
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 })
OutputConnector::Import(import_index) => self.with_import_export_ports(network_path, |ports| port_center(ports, *import_index)).flatten(),
}
}
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) {
self.load_wire(input, graph_wire_style, network_path);
} else {
return None;
}
let wire = match input {
InputConnector::Node { node_id, input_index } => {
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
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()
}
let network_metadata = self.network_metadata(network_path)?;
let Some(wire) = network_metadata.transient_metadata.wires.borrow().get(input).cloned() else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
Some(wire)
}
pub fn wire_is_loaded(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool {
match input {
InputConnector::Node { node_id, input_index } => {
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,
}
}
}
pub fn wire_is_loaded(&self, input: &InputConnector, network_path: &[NodeId]) -> bool {
self.network_metadata(network_path)
.is_some_and(|network_metadata| network_metadata.transient_metadata.wires.borrow().contains_key(input))
}
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) {
Previewing::Yes { .. } => match input {
InputConnector::Node { .. } => false,
@@ -761,36 +732,18 @@ impl NodeNetworkInterface {
log::error!("Could not load wire path from input");
return;
};
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return };
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;
};
let wire_update = WirePathUpdate {
id: *node_id,
input_index: *input_index,
wire_path_update: Some(wire),
};
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);
}
}
let (id, input_index) = match input {
InputConnector::Node { node_id, input_index } => (*node_id, *input_index),
InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index),
};
let wire_update = WirePathUpdate {
id,
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);
}
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]) {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
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;
}
}
let Some(network_metadata) = self.network_metadata(network_path) else {
return;
};
network_metadata.transient_metadata.wires.borrow_mut().remove(input);
}
/// 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 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.
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 {
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
@@ -948,7 +881,7 @@ impl NodeNetworkInterface {
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 path_string = vector_wire.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> {
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 {
log::error!("Could not get nested node_metadata in node_click_targets");
return;
@@ -984,16 +923,35 @@ impl NodeNetworkInterface {
};
}
fn try_get_node_click_targets(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> {
let node_metadata = self.node_metadata(node_id, network_path)?;
let TransientMetadata::Loaded(click_target) = &node_metadata.transient_metadata.click_targets else {
log::error!("Could not load node type metadata when getting click targets");
return None;
};
Some(click_target)
/// Loads the node click targets if needed, then reads them through &self.
pub(crate) fn with_loaded_node_click_targets<R>(&self, node_id: &NodeId, network_path: &[NodeId], read: impl FnOnce(&DocumentNodeClickTargets) -> R) -> Option<R> {
self.try_load_node_click_targets(node_id, network_path);
self.with_node_click_targets(node_id, network_path, read)
}
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 {
log::error!("Could not get node position in load_node_click_targets for node {node_id}");
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");
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]> {
self.node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box())
pub fn node_bounding_box(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
self.try_load_node_click_targets(node_id, network_path);
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]> {
self.try_get_node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box())
self.with_node_click_targets(node_id, network_path, |click_targets| click_targets.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 {
log::error!("Could not get network in load_all_node_click_targets");
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.
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 {
log::error!("Could not get nested node_metadata in position_from_downstream_node");
return None;
@@ -1196,9 +1154,8 @@ impl NodeNetworkInterface {
LayerPosition::Absolute(position) => Some(position),
LayerPosition::Stack(y_offset) => {
let Some(downstream_node_connectors) = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)))
.cloned()
.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)).cloned())
.flatten()
else {
log::error!("Could not get downstream node in position_from_downstream_node");
return None;
@@ -1231,9 +1188,8 @@ impl NodeNetworkInterface {
loop {
// TODO: Use root node to restore if previewing
let Some(downstream_node_connectors) = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(current_node_id, 0)))
.cloned()
.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(current_node_id, 0)).cloned())
.flatten()
else {
log::error!("Could not get downstream node for node {node_id} with Position::Chain");
return None;

View File

@@ -72,7 +72,6 @@ impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersisten
input_name: properties_row.input_name,
input_description: properties_row.input_description,
},
..Default::default()
})
}
DocumentNodePersistentMetadataHasPrimaryOutput {

View File

@@ -22,7 +22,7 @@ impl NodeNetworkInterface {
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 connector_click_targets = Vec::new();
let mut icon_click_targets = Vec::new();
@@ -31,15 +31,15 @@ impl NodeNetworkInterface {
return FrontendClickTargets::default();
};
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() {
if let ClickTargetType::Subpath(subpath) = port.target_type() {
connector_click_targets.push(subpath.to_bezpath().to_svg());
}
}
}
});
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();
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());
}
}
}
});
});
let mut layer_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 all_nodes_bounding_box = rect.to_bezpath().to_svg();
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
.remove_imports_exports
.click_targets()
@@ -94,7 +94,7 @@ impl NodeNetworkInterface {
modify_import_export.push(subpath.to_bezpath().to_svg());
}
}
}
});
FrontendClickTargets {
node_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
/// 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 {
log::error!("Could not get nested network_metadata in node_from_click");
return None;
@@ -146,8 +146,9 @@ impl NodeNetworkInterface {
let clicked_nodes = nodes
.iter()
.filter(|node_id| {
self.node_click_targets(node_id, network_path)
.is_some_and(|transient_node_metadata| transient_node_metadata.node_click_target.intersect_point_no_stroke(point))
self.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
transient_node_metadata.node_click_target.intersect_point_no_stroke(point)
}) == Some(true)
})
.cloned()
.collect::<Vec<_>>();
@@ -164,7 +165,7 @@ impl NodeNetworkInterface {
.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 {
log::error!("Could not get nested network_metadata in visibility_from_click");
return None;
@@ -180,7 +181,7 @@ impl NodeNetworkInterface {
node_ids
.iter()
.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 {
match click_target_type {
LayerClickTargetTypes::Visibility => layer.visibility_click_target.intersect_point_no_stroke(point).then_some(*node_id),
@@ -192,11 +193,12 @@ impl NodeNetworkInterface {
None
}
})
.flatten()
})
.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 {
log::error!("Could not get nested network_metadata in input_connector_from_click");
return None;
@@ -214,21 +216,22 @@ impl NodeNetworkInterface {
.collect::<Vec<_>>()
.iter()
.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
.port_click_targets
.clicked_input_port_from_point(point)
.map(|port| InputConnector::node(*node_id, port))
})
.flatten()
})
.next()
.or_else(|| {
self.import_export_ports(network_path)
.and_then(|import_export_ports| import_export_ports.clicked_input_port_from_point(point).map(InputConnector::Export))
self.with_import_export_ports(network_path, |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 {
log::error!("Could not get nested network_metadata in output_connector_from_click");
return None;
@@ -243,44 +246,51 @@ impl NodeNetworkInterface {
nodes
.iter()
.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
.port_click_targets
.clicked_output_port_from_point(point)
.map(|output_index| OutputConnector::node(*node_id, output_index))
})
.flatten()
})
.next()
.or_else(|| {
self.import_export_ports(network_path)
.and_then(|import_export_ports| import_export_ports.clicked_output_port_from_point(point).map(OutputConnector::Import))
self.with_import_export_ports(network_path, |import_export_ports| {
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 {
InputConnector::Node { node_id, input_index } => self
.node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.port_click_targets.input_port_position(*input_index)),
.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
transient_node_metadata.port_click_targets.input_port_position(*input_index)
})
.flatten(),
InputConnector::Export(export_index) => self
.import_export_ports(network_path)
.and_then(|import_export_ports| import_export_ports.input_port_position(*export_index)),
.with_import_export_ports(network_path, |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 {
OutputConnector::Node { node_id, output_index } => self
.node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.port_click_targets.output_port_position(*output_index)),
.with_loaded_node_click_targets(node_id, network_path, |transient_node_metadata| {
transient_node_metadata.port_click_targets.output_port_position(*output_index)
})
.flatten(),
OutputConnector::Import(import_index) => self
.import_export_ports(network_path)
.and_then(|import_export_ports| import_export_ports.output_port_position(*import_index)),
.with_import_export_ports(network_path, |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
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
let Some(network_metadata) = self.network_metadata(network_path) else {
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
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 {
log::error!("Could not get selected nodes in selected_nodes_bounding_box_viewport");
return None;
@@ -320,16 +330,13 @@ impl NodeNetworkInterface {
.cloned()
.collect::<Vec<_>>()
.iter()
.filter_map(|node_id| {
self.node_click_targets(node_id, network_path)
.and_then(|transient_node_metadata| transient_node_metadata.node_click_target.bounding_box())
})
.filter_map(|node_id| self.node_bounding_box(node_id, network_path))
.reduce(graphene_std::renderer::Quad::combine_bounds)
}
/// 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]> {
let bounds = *self.all_nodes_bounding_box(network_path)?;
pub fn graph_bounds_viewport_space(&self, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
let bounds = self.all_nodes_bounding_box(network_path)?;
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in graph_bounds_viewport_space");
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)
}
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 {
log::error!("Could not get nested network_metadata in collect_layer_widths");
return (HashMap::new(), HashMap::new(), HashMap::new());

View File

@@ -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 {
node_id: input_connector_node_id,
input_index,
@@ -136,11 +136,16 @@ impl NodeNetworkInterface {
if self.is_layer(&upstream_node, network_path) || self.hidden_primary_output(&upstream_node, network_path) {
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");
break;
};
if outward_wires.len() != 1 {
if downstream_connection_count != 1 {
break;
}
let downstream_position = self.position(&downstream_id, network_path);
@@ -300,13 +305,15 @@ impl NodeNetworkInterface {
return;
};
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() {
if self.is_layer(&node_id, network_path)
&& let Some(owned_nodes) = self.owned_nodes(&node_id, network_path)
{
for owned_node in owned_nodes {
node_ids.remove(owned_node);
}
if self.is_layer(&node_id, network_path) {
self.with_owned_nodes(&node_id, network_path, |owned_nodes| {
for owned_node in owned_nodes {
node_ids.remove(owned_node);
}
});
};
}
}
@@ -429,7 +436,7 @@ impl NodeNetworkInterface {
log::error!("Could not get nested network_metadata in export_ports");
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)
{
*offset += shift_sign;
@@ -472,7 +479,7 @@ impl NodeNetworkInterface {
if self.selected_nodes_in_nested_network(network_path).is_some_and(|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;
};
@@ -530,7 +537,7 @@ impl NodeNetworkInterface {
log::error!("Could not get nested network_metadata in export_ports");
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");
return;
};
@@ -564,7 +571,7 @@ impl NodeNetworkInterface {
}
// 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 {
if self.is_absolute(&owned_node, 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)> {
self.try_load_all_node_click_targets(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");
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
}

View File

@@ -1498,7 +1498,6 @@ impl NodeNetworkInterface {
node_metadata.persistent_metadata.node_type_metadata = if is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position),
owned_nodes: TransientMetadata::Unloaded,
})
} else {
NodeTypePersistentMetadata::Node(NodePersistentMetadata {
@@ -1521,15 +1520,11 @@ impl NodeNetworkInterface {
if let Some(downstream_position) = is_layer.then_some(single_downstream_layer_position).flatten() {
node_metadata.persistent_metadata.node_type_metadata = NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Stack((position.y - downstream_position.y - STACK_VERTICAL_GAP).max(0) as u32),
owned_nodes: TransientMetadata::Unloaded,
})
}
if is_layer {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata::default());
} else {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Node;
}
node_metadata.transient_metadata.layer_width.unload();
node_metadata.transient_metadata.owned_nodes.unload();
self.transaction_modified();
self.unload_stack_dependents(network_path);

View File

@@ -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.
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;
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)
}
/// 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 layers = Vec::new();
while let Some(current_node) = stack.pop() {
if self.is_layer(&current_node, network_path) {
layers.push(current_node);
} 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");
return Vec::new();
};
stack.extend(outward_wires.iter().filter_map(|input_connector| input_connector.node_id()));
}
}
}
layers
@@ -167,18 +178,22 @@ impl NodeNetworkInterface {
/// 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
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
.iter()
.filter_map(|(node_id, &new)| {
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");
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 !has_selected_node_downstream {
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.
///
/// 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() {
if let &mut NodeInput::Node { node_id: id, output_index } = input {
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> {
let top_left_position = self
.node_click_targets(node_id, network_path)
.and_then(|click_targets| click_targets.node_click_target.bounding_box())
.map(|mut bounding_box| {
if !self.is_layer(node_id, network_path) {
bounding_box[0] -= DVec2::new(0., 12.);
}
(bounding_box[0] / 24.).as_ivec2()
});
pub fn position(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<IVec2> {
self.try_load_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| {
if !self.is_layer(node_id, network_path) {
bounding_box[0] -= DVec2::new(0., 12.);
}
(bounding_box[0] / 24.).as_ivec2()
});
top_left_position.map(|position| {
if self.is_layer(node_id, network_path) {
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);
}
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() {
Some((node_id, encapsulating_network_path)) => {
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 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() {
@@ -330,8 +343,8 @@ impl NodeNetworkInterface {
frontend_exports
}
pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> {
let Some(all_nodes_bounding_box) = self.all_nodes_bounding_box(network_path).cloned() else {
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) else {
log::error!("Could not get all nodes bounding box in load_export_ports");
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
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
if self.input_from_connector(input_connector, network_path).is_some_and(|input| !input.is_exposed()) {
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
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 (name, description) = match output_connector {
OutputConnector::Node { node_id, output_index } => {
@@ -512,9 +525,8 @@ impl NodeNetworkInterface {
let data_type = output_type.displayed_type();
let resolved_type = output_type.resolved_type_node_string();
let mut connected_to = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(output_connector))
.cloned()
.with_outward_wires(network_path, |outward_wires| outward_wires.get(output_connector).cloned())
.flatten()
.unwrap_or_default()
.iter()
.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
.node_click_targets(node_id, network_path)
.and_then(|click_targets: &DocumentNodeClickTargets| click_targets.node_click_target.bounding_box())
.try_get_node_bounding_box(node_id, network_path)
.map(|bounding_box| ((bounding_box[1].y - bounding_box[0].y) / 24.) as u32);
if !self.is_layer(node_id, network_path) {
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.
/// 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 stack = vec![node_id];
@@ -561,47 +573,54 @@ impl NodeNetworkInterface {
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
let mut has_downstream_connections = false;
let mut nodes_to_walk_through = Vec::new();
for output_index in 0..number_of_outputs {
let Some(downstream_connections) = outward_wires.get(&OutputConnector::node(current_node, output_index)) else {
continue;
};
for downstream_connection in downstream_connections {
has_downstream_connections = true;
let InputConnector::Node {
node_id: downstream_node,
input_index,
} = downstream_connection
else {
return false;
let number_of_outputs = self.number_of_outputs(&current_node, network_path);
let keeps_within_set = self.with_outward_wires(network_path, |outward_wires| {
let mut has_downstream_connections = false;
let mut nodes_to_walk_through = Vec::new();
for output_index in 0..number_of_outputs {
let Some(downstream_connections) = outward_wires.get(&OutputConnector::node(current_node, output_index)) else {
continue;
};
match classify(*downstream_node, *input_index) {
SoleDependentStep::Terminate => {}
SoleDependentStep::Continue => nodes_to_walk_through.push(*downstream_node),
SoleDependentStep::Escape => return false,
for downstream_connection in downstream_connections {
has_downstream_connections = true;
let InputConnector::Node {
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 {
return false;
if !has_downstream_connections {
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
}
// 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
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())
}
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]) {
let Some(metadata) = self
.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.
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 {
log::warn!("input metadata not found in displayed_input_name_and_description");
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()
}
pub fn primary_output_connected_to_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(outward_wires) = self.outward_wires(network_path) else {
pub fn primary_output_connected_to_layer(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(downstream_connectors) = self.with_outward_wires(network_path, |outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)).cloned()) else {
log::error!("Could not get outward_wires in primary_output_connected_to_layer");
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");
return false;
};
@@ -1086,7 +1101,6 @@ impl NodeNetworkInterface {
node_metadata.persistent_metadata.node_type_metadata = if old_node.is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(old_node.metadata.position),
owned_nodes: TransientMetadata::Unloaded,
})
} else {
NodeTypePersistentMetadata::Node(NodePersistentMetadata {

View File

@@ -111,7 +111,7 @@ impl TypeSource {
}
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 {
InputConnector::Node { node_id, input_index } => {
let Some(implementation) = self.implementation(node_id, network_path) else {
@@ -121,9 +121,9 @@ impl NodeNetworkInterface {
let node_path = [network_path, &[*node_id]].concat();
match implementation {
DocumentNodeImplementation::Network(_) => {
let Some(map) = self.outward_wires(&node_path) else { return false };
let Some(outward_wires) = map.get(&OutputConnector::Import(*input_index)) else { return false };
outward_wires.clone().iter().any(|connector| match connector {
let outward_wires = self.with_outward_wires(&node_path, |map| map.get(&OutputConnector::Import(*input_index)).cloned()).flatten();
let Some(outward_wires) = outward_wires else { return false };
outward_wires.iter().any(|connector| match connector {
InputConnector::Node { node_id, input_index } => self.input_has_error(&InputConnector::node(*node_id, *input_index), &node_path),
InputConnector::Export(_) => false,
})
@@ -143,7 +143,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 {
return TypeSource::Error("Could not get input from connector");
};
@@ -171,7 +171,7 @@ impl NodeNetworkInterface {
/// Get the [`TypeSource`] for any InputConnector.
/// 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
if self.input_has_error(input_connector, network_path) {
return TypeSource::Invalid;
@@ -180,7 +180,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.
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) {
TypeSource::Compiled(compiled) => compiled,
TypeSource::TaggedValue(value) => value,
@@ -219,7 +219,7 @@ impl NodeNetworkInterface {
}
/// 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 {
// An export can have any type connected to it
return vec![graph_craft::generic!(T)];
@@ -231,17 +231,15 @@ impl NodeNetworkInterface {
match implementation {
DocumentNodeImplementation::Network(_) => {
let nested_path = [network_path, &[*node_id]].concat();
let Some(outward_wires) = self.outward_wires(&nested_path) else {
log::error!("Could not get outward wires in potential_valid_input_types");
return Vec::new();
};
let Some(inputs_from_import) = outward_wires.get(&OutputConnector::Import(*input_index)) else {
let inputs_from_import = self
.with_outward_wires(&nested_path, |outward_wires| outward_wires.get(&OutputConnector::Import(*input_index)).cloned())
.flatten();
let Some(inputs_from_import) = inputs_from_import else {
log::error!("Could not get inputs from import in potential_valid_input_types");
return Vec::new();
};
let intersection: HashSet<Type> = inputs_from_import
.clone()
.iter()
.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 {
@@ -285,7 +283,7 @@ impl NodeNetworkInterface {
}
/// 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 {
InputConnector::Node { node_id, input_index } => {
let Some(implementation) = self.implementation(node_id, network_path) else {
@@ -339,7 +337,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 {
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
@@ -376,18 +374,14 @@ impl NodeNetworkInterface {
}
/// 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> {
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in valid_output_types");
return Vec::new();
};
let Some(inputs_from_import) = outward_wires.get(output_connector) else {
fn valid_output_types(&self, output_connector: &OutputConnector, network_path: &[NodeId]) -> Vec<Type> {
let inputs_from_import = self.with_outward_wires(network_path, |outward_wires| outward_wires.get(output_connector).cloned()).flatten();
let Some(inputs_from_import) = inputs_from_import else {
log::error!("Could not get inputs from import in valid_output_types");
return Vec::new();
};
let intersection = inputs_from_import
.clone()
.iter()
.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 {

View File

@@ -387,6 +387,5 @@ fn input_metadata_entry_to_runtime(entry: InputMetadataEntry) -> InputMetadata {
widget_override: entry.widget_override,
input_data: entry.input_data,
},
..Default::default()
}
}

View File

@@ -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
#[derive(Debug, Default, Clone)]
pub struct NodeNetworkTransientMetadata {
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.
/// 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.
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
// node_group_bounding_box: Vec<(Subpath<ManipulatorGroupId>, Vec<Nodes>)>,
/// 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
pub import_export_ports: TransientMetadata<Ports>,
pub(crate) import_export_ports: TransientCache<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
pub wires: Vec<TransientMetadata<WirePathUpdate>>,
/// Cached wire SVG paths per input connector, where an entry's presence means that wire is loaded.
pub(crate) wires: std::cell::RefCell<HashMap<InputConnector, WirePathUpdate>>,
}
#[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.
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[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 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 {
@@ -641,7 +663,6 @@ impl From<(&str, &str)> for InputMetadata {
persistent_metadata: InputPersistentMetadata::default()
.with_name(input_name_and_description.0)
.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 {
InputMetadata {
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 {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position),
owned_nodes: TransientMetadata::default(),
})
}
}
@@ -688,9 +707,6 @@ pub struct LayerPersistentMetadata {
// preview_click_target: Option<ClickTarget>,
/// Stores the position of a layer node, which can either be Absolute or Stack
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 {
@@ -736,9 +752,11 @@ pub enum NodePosition {
#[derive(Debug, Default, Clone)]
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.
pub click_targets: TransientMetadata<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.
pub node_type_metadata: NodeTypeTransientMetadata,
pub(crate) click_targets: TransientCache<DocumentNodeClickTargets>,
/// All nodes that should be moved when this layer is moved, kept here since only layers own nodes.
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)]
@@ -752,23 +770,6 @@ pub struct DocumentNodeClickTargets {
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)]
pub enum NodeTypeClickTargets {
Layer(LayerClickTargets),

View File

@@ -290,14 +290,6 @@ impl<'a, 'p> NetworkView<'a, 'p> {
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> {
Ok(match self.input(input_connector)? {
NodeInput::Node { node_id, output_index, .. } => Some(OutputConnector::node(*node_id, *output_index)),