mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
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:
committed by
Dennis Kobert
parent
0b05821ec6
commit
9d76683a3d
@@ -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)
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -72,7 +72,6 @@ impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersisten
|
||||
input_name: properties_row.input_name,
|
||||
input_description: properties_row.input_description,
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
DocumentNodePersistentMetadataHasPrimaryOutput {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -1486,7 +1486,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 {
|
||||
@@ -1509,15 +1508,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);
|
||||
|
||||
@@ -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(¤t_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(¤t_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(¤t_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 {
|
||||
|
||||
@@ -116,7 +116,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 {
|
||||
@@ -126,9 +126,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,
|
||||
})
|
||||
@@ -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 {
|
||||
return TypeSource::Error("Could not get input from connector");
|
||||
};
|
||||
@@ -176,7 +176,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;
|
||||
@@ -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.
|
||||
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,
|
||||
@@ -213,7 +213,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)];
|
||||
@@ -225,17 +225,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 {
|
||||
@@ -280,7 +278,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 {
|
||||
@@ -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 {
|
||||
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
|
||||
@@ -372,18 +370,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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(Box<LayerClickTargets>),
|
||||
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user