Move node visibility flag from NodeNetwork to DocumentNode (#1708)

* protonode -> proto node

* Move node visibility flag from NodeNetwork to DocumentNode

* Add serde default for new field

* Logic improvements
This commit is contained in:
Keavon Chambers
2024-03-27 05:17:08 -07:00
committed by GitHub
parent d3e3e19822
commit 27f9e3f00e
27 changed files with 161 additions and 140 deletions
@@ -57,7 +57,7 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut), entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut),
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy), entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes), entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes),
entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedHidden), entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedVisibility),
// //
// TransformLayerMessage // TransformLayerMessage
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation), entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
@@ -822,7 +822,7 @@ impl DocumentMessageHandler {
self.metadata self.metadata
.root() .root()
.descendants(&self.metadata) .descendants(&self.metadata)
.filter(|&layer| self.selected_nodes.layer_visible(layer, self.network(), self.metadata())) .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata()))
.filter(|&layer| !is_artboard(layer, network)) .filter(|&layer| !is_artboard(layer, network))
.filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets)))
.filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.metadata.transform_to_document(*layer)))) .filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.metadata.transform_to_document(*layer))))
@@ -835,7 +835,7 @@ impl DocumentMessageHandler {
self.metadata self.metadata
.root() .root()
.descendants(&self.metadata) .descendants(&self.metadata)
.filter(|&layer| self.selected_nodes.layer_visible(layer, self.network(), self.metadata())) .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata()))
.filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets)))
.filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.metadata.transform_to_document(*layer)))) .filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.metadata.transform_to_document(*layer))))
.map(|(layer, _)| layer) .map(|(layer, _)| layer)
@@ -849,7 +849,7 @@ impl DocumentMessageHandler {
/// Get the combined bounding box of the click targets of the selected visible layers in viewport space /// Get the combined bounding box of the click targets of the selected visible layers in viewport space
pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> { pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
self.selected_nodes self.selected_nodes
.selected_visible_layers(self.network(), self.metadata()) .selected_visible_layers(self.metadata())
.filter_map(|layer| self.metadata.bounding_box_viewport(layer)) .filter_map(|layer| self.metadata.bounding_box_viewport(layer))
.reduce(graphene_core::renderer::Quad::combine_bounds) .reduce(graphene_core::renderer::Quad::combine_bounds)
} }
@@ -887,13 +887,6 @@ impl DocumentMessageHandler {
Ok(document) Ok(document)
} }
/// Returns the bounding boxes for all visible layers.
pub fn bounding_boxes(&self) -> impl Iterator<Item = [DVec2; 2]> + '_ {
// TODO: Remove this function entirely?
// self.visible_layers().filter_map(|path| self.document_legacy.viewport_bounding_box(path, font_cache).ok()?)
std::iter::empty()
}
/// Called recursively by the entry function [`serialize_root`]. /// Called recursively by the entry function [`serialize_root`].
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure_section: &mut Vec<u64>, data_section: &mut Vec<u64>, path: &mut Vec<LayerNodeIdentifier>) { fn serialize_structure(&self, folder: LayerNodeIdentifier, structure_section: &mut Vec<u64>, data_section: &mut Vec<u64>, path: &mut Vec<LayerNodeIdentifier>) {
let mut space = 0; let mut space = 0;
@@ -89,13 +89,13 @@ pub enum NodeGraphMessage {
ShiftNode { ShiftNode {
node_id: NodeId, node_id: NodeId,
}, },
ToggleSelectedHidden, ToggleSelectedVisibility,
ToggleHidden { ToggleVisibility {
node_id: NodeId, node_id: NodeId,
}, },
SetHidden { SetVisibility {
node_id: NodeId, node_id: NodeId,
hidden: bool, visible: bool,
}, },
SetName { SetName {
node_id: NodeId, node_id: NodeId,
@@ -59,8 +59,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
load_network_structure(document_network, document_metadata, selected_nodes, collapsed); load_network_structure(document_network, document_metadata, selected_nodes, collapsed);
} }
NodeGraphMessage::SelectedNodesUpdated => { NodeGraphMessage::SelectedNodesUpdated => {
self.update_selection_action_buttons(document_network, selected_nodes, responses); self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses);
self.update_selected(document_network, selected_nodes, responses); self.update_selected(document_network, document_metadata, selected_nodes, responses);
if selected_nodes.selected_layers(document_metadata).count() <= 1 { if selected_nodes.selected_layers(document_metadata).count() <= 1 {
responses.add(DocumentMessage::SetRangeSelectionLayer { responses.add(DocumentMessage::SetRangeSelectionLayer {
new_layer: selected_nodes.selected_layers(document_metadata).next(), new_layer: selected_nodes.selected_layers(document_metadata).next(),
@@ -195,7 +195,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
if let Some(network) = document_network.nested_network(&self.network) { if let Some(network) = document_network.nested_network(&self.network) {
self.send_graph(network, graph_view_overlay_open, document_metadata, selected_nodes, collapsed, responses); self.send_graph(network, graph_view_overlay_open, document_metadata, selected_nodes, collapsed, responses);
} }
self.update_selected(document_network, selected_nodes, responses); self.update_selected(document_network, document_metadata, selected_nodes, responses);
} }
NodeGraphMessage::DuplicateSelectedNodes => { NodeGraphMessage::DuplicateSelectedNodes => {
if let Some(network) = document_network.nested_network(&self.network) { if let Some(network) = document_network.nested_network(&self.network) {
@@ -221,7 +221,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::InsertNode { node_id, document_node }); responses.add(NodeGraphMessage::InsertNode { node_id, document_node });
} }
self.update_selected(document_network, selected_nodes, responses); self.update_selected(document_network, document_metadata, selected_nodes, responses);
} }
} }
NodeGraphMessage::ExitNestedNetwork { depth_of_nesting } => { NodeGraphMessage::ExitNestedNetwork { depth_of_nesting } => {
@@ -234,7 +234,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
if let Some(network) = document_network.nested_network(&self.network) { if let Some(network) = document_network.nested_network(&self.network) {
self.send_graph(network, graph_view_overlay_open, document_metadata, selected_nodes, collapsed, responses); self.send_graph(network, graph_view_overlay_open, document_metadata, selected_nodes, collapsed, responses);
} }
self.update_selected(document_network, selected_nodes, responses); self.update_selected(document_network, document_metadata, selected_nodes, responses);
} }
NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => { NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => {
let Some(network) = document_network.nested_network(&self.network) else { let Some(network) = document_network.nested_network(&self.network) else {
@@ -447,36 +447,46 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
self.send_graph(network, graph_view_overlay_open, document_metadata, selected_nodes, collapsed, responses); self.send_graph(network, graph_view_overlay_open, document_metadata, selected_nodes, collapsed, responses);
} }
NodeGraphMessage::ToggleSelectedHidden => { NodeGraphMessage::ToggleSelectedVisibility => {
if let Some(network) = document_network.nested_network(&self.network) {
responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::StartTransaction);
let new_hidden = !selected_nodes.selected_nodes().any(|id| network.disabled.contains(id)); // If any of the selected nodes are hidden, show them all. Otherwise, hide them all.
let visible = selected_nodes.selected_nodes().all(|&node_id| document_metadata.node_is_visible(node_id));
let visible = !visible;
for &node_id in selected_nodes.selected_nodes() { for &node_id in selected_nodes.selected_nodes() {
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden }); responses.add(NodeGraphMessage::SetVisibility { node_id, visible });
} }
} }
NodeGraphMessage::ToggleVisibility { node_id } => {
let visible = document_metadata.node_is_visible(node_id);
let visible = !visible;
responses.add(NodeGraphMessage::SetVisibility { node_id, visible });
} }
NodeGraphMessage::ToggleHidden { node_id } => { NodeGraphMessage::SetVisibility { node_id, visible } => {
if let Some(network) = document_network.nested_network(&self.network) { (|| {
let new_hidden = !network.disabled.contains(&node_id); let Some(network) = document_network.nested_network_mut(&self.network) else { return };
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden });
} let input_or_output = network.imports.contains(&node_id) || network.original_outputs().iter().any(|output| output.node_id == node_id);
} let visibility = if visible {
NodeGraphMessage::SetHidden { node_id, hidden } => { true
if let Some(network) = document_network.nested_network_mut(&self.network) { } else if !input_or_output {
if !hidden { false
network.disabled.retain(|&id| node_id != id); } else {
} else if !network.imports.contains(&node_id) && !network.original_outputs().iter().any(|output| output.node_id == node_id) { return;
network.disabled.push(node_id); };
}
// Set what we determined shall be the visibility of the node
let Some(node) = network.nodes.get_mut(&node_id) else { return };
node.visible = visibility;
// Only generate node graph if one of the selected nodes is connected to the output // Only generate node graph if one of the selected nodes is connected to the output
if network.connected_to_output(node_id) { if network.connected_to_output(node_id) {
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
} })();
self.update_selection_action_buttons(document_network, selected_nodes, responses); self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses);
} }
NodeGraphMessage::SetName { node_id, name } => { NodeGraphMessage::SetName { node_id, name } => {
responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::StartTransaction);
@@ -508,7 +518,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
} }
} }
self.update_selection_action_buttons(document_network, selected_nodes, responses); self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses);
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
@@ -522,7 +532,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
let node_types = document_node_types::collect_node_types(); let node_types = document_node_types::collect_node_types();
responses.add(FrontendMessage::UpdateNodeTypes { node_types }); responses.add(FrontendMessage::UpdateNodeTypes { node_types });
} }
self.update_selected(document_network, selected_nodes, responses); self.update_selected(document_network, document_metadata, selected_nodes, responses);
} }
NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => { NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => {
self.resolved_types = resolved_types; self.resolved_types = resolved_types;
@@ -540,7 +550,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
impl NodeGraphMessageHandler { impl NodeGraphMessageHandler {
pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList { pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList {
if self.has_selection && graph_open { if self.has_selection && graph_open {
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleSelectedHidden) actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleSelectedVisibility)
} else { } else {
actions!(NodeGraphMessageDiscriminant;) actions!(NodeGraphMessageDiscriminant;)
} }
@@ -554,8 +564,8 @@ impl NodeGraphMessageHandler {
}); });
} }
/// Updates the buttons for disable and preview /// Updates the buttons for visibility and preview
fn update_selection_action_buttons(&mut self, document_network: &NodeNetwork, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) { fn update_selection_action_buttons(&mut self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) {
if let Some(network) = document_network.nested_network(&self.network) { if let Some(network) = document_network.nested_network(&self.network) {
let mut widgets = Vec::new(); let mut widgets = Vec::new();
@@ -565,18 +575,18 @@ impl NodeGraphMessageHandler {
// If there is at least one other selected node then show the hide or show button // If there is at least one other selected node then show the hide or show button
if selection.next().is_some() { if selection.next().is_some() {
// Check if any of the selected nodes are disabled // Check if any of the selected nodes are disabled
let is_hidden = selected_nodes.selected_nodes().any(|id| network.disabled.contains(id)); let all_visible = selected_nodes.selected_nodes().all(|&id| document_metadata.node_is_visible(id));
// Check if multiple nodes are selected // Check if multiple nodes are selected
let multiple_nodes = selection.next().is_some(); let multiple_nodes = selection.next().is_some();
// Generate the enable or disable button accordingly // Generate the visible/hidden button accordingly
let (hide_show_label, hide_show_icon) = if is_hidden { ("Make Visible", "EyeHidden") } else { ("Make Hidden", "EyeVisible") }; let (hide_show_label, hide_show_icon) = if all_visible { ("Make Hidden", "EyeVisible") } else { ("Make Visible", "EyeHidden") };
let hide_button = TextButton::new(hide_show_label) let hide_button = TextButton::new(hide_show_label)
.icon(Some(hide_show_icon.to_string())) .icon(Some(hide_show_icon.to_string()))
.tooltip(if is_hidden { "Show selected nodes/layers" } else { "Hide selected nodes/layers" }.to_string() + if multiple_nodes { "s" } else { "" }) .tooltip(if all_visible { "Hide selected nodes/layers" } else { "Show selected nodes/layers" }.to_string() + if multiple_nodes { "s" } else { "" })
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedHidden)) .tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
.on_update(move |_| NodeGraphMessage::ToggleSelectedHidden.into()) .on_update(move |_| NodeGraphMessage::ToggleSelectedVisibility.into())
.widget_holder(); .widget_holder();
widgets.push(hide_button); widgets.push(hide_button);
@@ -745,7 +755,7 @@ impl NodeGraphMessageHandler {
exposed_outputs, exposed_outputs,
position: node.metadata.position.into(), position: node.metadata.position.into(),
previewed: network.outputs_contain(node_id), previewed: network.outputs_contain(node_id),
disabled: network.disabled.contains(&node_id), visible: node.visible,
errors: errors.map(|e| format!("{e:?}")), errors: errors.map(|e| format!("{e:?}")),
}); });
} }
@@ -774,7 +784,7 @@ impl NodeGraphMessageHandler {
parent_id: layer.parent(metadata).map(|parent| parent.to_node()), parent_id: layer.parent(metadata).map(|parent| parent.to_node()),
name: network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(), name: network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(),
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() }, tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
disabled: network.disabled.contains(&node_id), visible: node.visible,
}; };
responses.add(FrontendMessage::UpdateDocumentLayerDetails { data }); responses.add(FrontendMessage::UpdateDocumentLayerDetails { data });
} }
@@ -795,8 +805,8 @@ impl NodeGraphMessageHandler {
} }
/// Updates the frontend's selection state in line with the backend /// Updates the frontend's selection state in line with the backend
fn update_selected(&mut self, document_network: &NodeNetwork, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) { fn update_selected(&mut self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) {
self.update_selection_action_buttons(document_network, selected_nodes, responses); self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses);
responses.add(FrontendMessage::UpdateNodeGraphSelection { responses.add(FrontendMessage::UpdateNodeGraphSelection {
selected: selected_nodes.selected_nodes_ref().clone(), selected: selected_nodes.selected_nodes_ref().clone(),
}); });
@@ -84,7 +84,7 @@ pub struct FrontendNode {
#[serde(rename = "exposedOutputs")] #[serde(rename = "exposedOutputs")]
pub exposed_outputs: Vec<FrontendGraphOutput>, pub exposed_outputs: Vec<FrontendGraphOutput>,
pub position: (i32, i32), pub position: (i32, i32),
pub disabled: bool, pub visible: bool,
pub previewed: bool, pub previewed: bool,
pub errors: Option<String>, pub errors: Option<String>,
} }
@@ -8,7 +8,7 @@ use std::collections::HashMap;
pub enum Clipboard { pub enum Clipboard {
Internal, Internal,
_InternalClipboardCount, // Keep this as the last entry in internal clipboards since it is used for counting the number of enum variants _InternalClipboardCount, // Keep this as the last entry of **internal** clipboards since it is used for counting the number of enum variants
Device, Device,
} }
@@ -19,7 +19,7 @@ pub const INTERNAL_CLIPBOARD_COUNT: u8 = Clipboard::_InternalClipboardCount as u
pub struct CopyBufferEntry { pub struct CopyBufferEntry {
pub nodes: HashMap<NodeId, DocumentNode>, pub nodes: HashMap<NodeId, DocumentNode>,
pub selected: bool, pub selected: bool,
pub disabled: bool, pub visible: bool,
pub collapsed: bool, pub collapsed: bool,
pub alias: String, pub alias: String,
} }
@@ -14,12 +14,15 @@ use std::num::NonZeroU64;
// DocumentMetadata // DocumentMetadata
// ================ // ================
// TODO: To avoid storing a stateful snapshot of some other system's state (which is easily to accidentally get out of sync),
// TODO: it might be better to have a system that can query the state of the node network on demand.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DocumentMetadata { pub struct DocumentMetadata {
upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>, upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
structure: HashMap<LayerNodeIdentifier, NodeRelations>, structure: HashMap<LayerNodeIdentifier, NodeRelations>,
artboards: HashSet<LayerNodeIdentifier>, artboards: HashSet<LayerNodeIdentifier>,
folders: HashSet<LayerNodeIdentifier>, folders: HashSet<LayerNodeIdentifier>,
hidden: HashSet<NodeId>,
click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>, click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
/// Transform from document space to viewport space. /// Transform from document space to viewport space.
pub document_to_viewport: DAffine2, pub document_to_viewport: DAffine2,
@@ -29,10 +32,11 @@ impl Default for DocumentMetadata {
fn default() -> Self { fn default() -> Self {
Self { Self {
upstream_transforms: HashMap::new(), upstream_transforms: HashMap::new(),
click_targets: HashMap::new(),
structure: HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]), structure: HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]),
artboards: HashSet::new(), artboards: HashSet::new(),
folders: HashSet::new(), folders: HashSet::new(),
hidden: HashSet::new(),
click_targets: HashMap::new(),
document_to_viewport: DAffine2::IDENTITY, document_to_viewport: DAffine2::IDENTITY,
} }
} }
@@ -118,6 +122,10 @@ impl DocumentMetadata {
self.artboards.contains(&layer) self.artboards.contains(&layer)
} }
pub fn node_is_visible(&self, layer: NodeId) -> bool {
!self.hidden.contains(&layer)
}
/// Folders sorted from most nested to least nested /// Folders sorted from most nested to least nested
pub fn folders_sorted_by_most_nested(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<LayerNodeIdentifier> { pub fn folders_sorted_by_most_nested(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<LayerNodeIdentifier> {
let mut folders: Vec<_> = layers.filter(|layer| self.folders.contains(layer)).collect(); let mut folders: Vec<_> = layers.filter(|layer| self.folders.contains(layer)).collect();
@@ -138,8 +146,9 @@ impl DocumentMetadata {
} }
self.structure = HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]); self.structure = HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]);
self.folders = HashSet::new();
self.artboards = HashSet::new(); self.artboards = HashSet::new();
self.folders = HashSet::new();
self.hidden = HashSet::new();
let id = graph.exports[0].node_id; let id = graph.exports[0].node_id;
let Some(output_node) = graph.nodes.get(&id) else { let Some(output_node) = graph.nodes.get(&id) else {
@@ -150,22 +159,26 @@ impl DocumentMetadata {
}; };
let parent = LayerNodeIdentifier::ROOT; let parent = LayerNodeIdentifier::ROOT;
let mut stack = vec![(layer_node, node_id, parent)]; let mut stack = vec![(layer_node, node_id, parent)];
while let Some((node, id, parent)) = stack.pop() { while let Some((node, node_id, parent)) = stack.pop() {
let mut current = Some((node, id)); let mut current = Some((node, node_id));
while let Some(&(current_node, current_id)) = current.as_ref() { while let Some(&(current_node, current_node_id)) = current.as_ref() {
let current_identifier = LayerNodeIdentifier::new_unchecked(current_id); let current_layer_id = LayerNodeIdentifier::new_unchecked(current_node_id);
if !self.structure.contains_key(&current_identifier) { if !self.structure.contains_key(&current_layer_id) {
parent.push_child(self, current_identifier); parent.push_child(self, current_layer_id);
if let Some((child_node, child_id)) = first_child_layer(graph, current_node) { if let Some((child_node, child_id)) = first_child_layer(graph, current_node) {
stack.push((child_node, child_id, current_identifier)); stack.push((child_node, child_id, current_layer_id));
} }
if is_artboard(current_identifier, graph) { if is_artboard(current_layer_id, graph) {
self.artboards.insert(current_identifier); self.artboards.insert(current_layer_id);
} }
if is_folder(current_identifier, graph) { if is_folder(current_layer_id, graph) {
self.folders.insert(current_identifier); self.folders.insert(current_layer_id);
}
if !current_node.visible {
self.hidden.insert(current_node_id);
} }
} }
@@ -532,9 +545,9 @@ impl<'a> Iterator for AxisIter<'a> {
} }
} }
// ============== // ===============
// DescendantsIter // DescendantsIter
// ============== // ===============
#[derive(Clone)] #[derive(Clone)]
pub struct DescendantsIter<'a> { pub struct DescendantsIter<'a> {
@@ -1,9 +1,9 @@
use graph_craft::document::{NodeId, NodeNetwork}; use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use graph_craft::document::NodeId;
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct RawBuffer(Vec<u8>); pub struct RawBuffer(Vec<u8>);
@@ -46,7 +46,7 @@ pub struct LayerPanelEntry {
#[serde(rename = "layerClassification")] #[serde(rename = "layerClassification")]
pub layer_classification: LayerClassification, pub layer_classification: LayerClassification,
pub expanded: bool, pub expanded: bool,
pub disabled: bool, pub visible: bool,
#[serde(rename = "parentId")] #[serde(rename = "parentId")]
pub parent_id: Option<NodeId>, pub parent_id: Option<NodeId>,
pub depth: usize, pub depth: usize,
@@ -56,12 +56,12 @@ pub struct LayerPanelEntry {
pub struct SelectedNodes(pub Vec<NodeId>); pub struct SelectedNodes(pub Vec<NodeId>);
impl SelectedNodes { impl SelectedNodes {
pub fn layer_visible(&self, layer: LayerNodeIdentifier, network: &NodeNetwork, metadata: &DocumentMetadata) -> bool { pub fn layer_visible(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {
!layer.ancestors(metadata).any(|layer| network.disabled.contains(&layer.to_node())) layer.ancestors(metadata).all(|layer| metadata.node_is_visible(layer.to_node()))
} }
pub fn selected_visible_layers<'a>(&'a self, network: &'a NodeNetwork, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ { pub fn selected_visible_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
self.selected_layers(metadata).filter(move |&layer| self.layer_visible(layer, network, metadata)) self.selected_layers(metadata).filter(move |&layer| self.layer_visible(layer, metadata))
} }
pub fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ { pub fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
@@ -202,7 +202,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
) )
.collect(), .collect(),
selected: active_document.selected_nodes.selected_layers_contains(layer, active_document.metadata()), selected: active_document.selected_nodes.selected_layers_contains(layer, active_document.metadata()),
disabled: !active_document.selected_nodes.layer_visible(layer, active_document.network(), active_document.metadata()), visible: active_document.selected_nodes.layer_visible(layer, active_document.metadata()),
collapsed: false, collapsed: false,
alias: previous_alias, alias: previous_alias,
}); });
@@ -388,8 +388,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
if entry.selected { if entry.selected {
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] }); responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
} }
if entry.disabled { if !entry.visible {
responses.add(NodeGraphMessage::SetHidden { node_id: id, hidden: entry.disabled }); responses.add(NodeGraphMessage::SetVisibility { node_id: id, visible: false });
} }
} }
}; };
@@ -419,8 +419,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
if entry.selected { if entry.selected {
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] }); responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
} }
if entry.disabled { if !entry.visible {
responses.add(NodeGraphMessage::SetHidden { node_id: id, hidden: entry.disabled }); responses.add(NodeGraphMessage::SetVisibility { node_id: id, visible: false });
} }
} }
@@ -45,7 +45,7 @@ impl Pivot {
/// Recomputes the pivot position and transform. /// Recomputes the pivot position and transform.
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) { fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) {
let mut layers = document.selected_nodes.selected_visible_layers(document.network(), document.metadata()); let mut layers = document.selected_nodes.selected_visible_layers(document.metadata());
let Some(first) = layers.next() else { let Some(first) = layers.next() else {
// If no layers are selected then we revert things back to default // If no layers are selected then we revert things back to default
self.normalized_pivot = DVec2::splat(0.5); self.normalized_pivot = DVec2::splat(0.5);
@@ -66,7 +66,7 @@ impl Pivot {
// If more than one layer is selected we use the AABB with the mean of the pivots // If more than one layer is selected we use the AABB with the mean of the pivots
let xy_summation = document let xy_summation = document
.selected_nodes .selected_nodes
.selected_visible_layers(document.network(), document.metadata()) .selected_visible_layers(document.metadata())
.map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network, &document.metadata)) .map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network, &document.metadata))
.reduce(|a, b| a + b) .reduce(|a, b| a + b)
.unwrap_or_default(); .unwrap_or_default();
@@ -101,7 +101,7 @@ impl Pivot {
/// Sets the viewport position of the pivot for all selected layers. /// Sets the viewport position of the pivot for all selected layers.
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
for layer in document.selected_nodes.selected_visible_layers(document.network(), document.metadata()) { for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
let transform = Self::get_layer_pivot_transform(layer, document); let transform = Self::get_layer_pivot_transform(layer, document);
let pivot = transform.inverse().transform_point2(position); let pivot = transform.inverse().transform_point2(position);
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0. // Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
@@ -255,7 +255,7 @@ impl SnapManager {
if candidates.len() > 10 { if candidates.len() > 10 {
return; return;
} }
if !document.selected_nodes.layer_visible(layer, &document.network, &document.metadata) { if !document.selected_nodes.layer_visible(layer, &document.metadata) {
return; return;
} }
if snap_data.ignore.contains(&layer) { if snap_data.ignore.contains(&layer) {
@@ -247,7 +247,7 @@ impl Fsm for GradientToolFsmState {
(_, GradientToolMessage::Overlays(mut overlay_context)) => { (_, GradientToolMessage::Overlays(mut overlay_context)) => {
let selected = tool_data.selected_gradient.as_ref(); let selected = tool_data.selected_gradient.as_ref();
for layer in document.selected_nodes.selected_visible_layers(document.network(), document.metadata()) { for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
let Some(gradient) = get_gradient(layer, &document.network) else { continue }; let Some(gradient) = get_gradient(layer, &document.network) else { continue };
let transform = gradient_space_transform(layer, document); let transform = gradient_space_transform(layer, document);
let dragging = selected.filter(|selected| selected.layer == layer).map(|selected| selected.dragging); let dragging = selected.filter(|selected| selected.layer == layer).map(|selected| selected.dragging);
@@ -318,7 +318,7 @@ impl Fsm for GradientToolFsmState {
self self
} }
(_, GradientToolMessage::InsertStop) => { (_, GradientToolMessage::InsertStop) => {
for layer in document.selected_nodes.selected_visible_layers(document.network(), document.metadata()) { for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
let Some(mut gradient) = get_gradient(layer, &document.network) else { continue }; let Some(mut gradient) = get_gradient(layer, &document.network) else { continue };
let transform = gradient_space_transform(layer, document); let transform = gradient_space_transform(layer, document);
@@ -357,7 +357,7 @@ impl Fsm for GradientToolFsmState {
let tolerance = (MANIPULATOR_GROUP_MARKER_SIZE * 2.).powi(2); let tolerance = (MANIPULATOR_GROUP_MARKER_SIZE * 2.).powi(2);
let mut dragging = false; let mut dragging = false;
for layer in document.selected_nodes.selected_visible_layers(document.network(), document.metadata()) { for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
let Some(gradient) = get_gradient(layer, &document.network) else { continue }; let Some(gradient) = get_gradient(layer, &document.network) else { continue };
let transform = gradient_space_transform(layer, document); let transform = gradient_space_transform(layer, document);
@@ -391,7 +391,7 @@ impl Fsm for SelectToolFsmState {
tool_data.selected_layers_count = selected_layers_count; tool_data.selected_layers_count = selected_layers_count;
// Outline selected layers // Outline selected layers
for layer in document.selected_nodes.selected_visible_layers(document.network(), document.metadata()) { for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
} }
@@ -405,13 +405,13 @@ impl Fsm for SelectToolFsmState {
// Update bounds // Update bounds
let transform = document let transform = document
.selected_nodes .selected_nodes
.selected_visible_layers(document.network(), document.metadata()) .selected_visible_layers(document.metadata())
.next() .next()
.map(|layer| document.metadata().transform_to_viewport(layer)); .map(|layer| document.metadata().transform_to_viewport(layer));
let transform = transform.unwrap_or(DAffine2::IDENTITY); let transform = transform.unwrap_or(DAffine2::IDENTITY);
let bounds = document let bounds = document
.selected_nodes .selected_nodes
.selected_visible_layers(document.network(), document.metadata()) .selected_visible_layers(document.metadata())
.filter_map(|layer| { .filter_map(|layer| {
document document
.metadata() .metadata()
@@ -472,7 +472,7 @@ impl Fsm for SelectToolFsmState {
.map(|bounding_box| bounding_box.check_rotate(input.mouse.position)) .map(|bounding_box| bounding_box.check_rotate(input.mouse.position))
.unwrap_or_default(); .unwrap_or_default();
let mut selected: Vec<_> = document.selected_nodes.selected_visible_layers(document.network(), document.metadata()).collect(); let mut selected: Vec<_> = document.selected_nodes.selected_visible_layers(document.metadata()).collect();
let intersection = document.click(input.mouse.position, &document.network); let intersection = document.click(input.mouse.position, &document.network);
// If the user is dragging the bounding box bounds, go into ResizingBounds mode. // If the user is dragging the bounding box bounds, go into ResizingBounds mode.
@@ -215,7 +215,7 @@ impl TextToolData {
/// Set the editing state of the currently modifying layer /// Set the editing state of the currently modifying layer
fn set_editing(&self, editable: bool, font_cache: &FontCache, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { fn set_editing(&self, editable: bool, font_cache: &FontCache, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.network) { if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.network) {
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: editable }); responses.add(NodeGraphMessage::SetVisibility { node_id, visible: !editable });
} }
if let Some(editing_text) = self.editing_text.as_ref().filter(|_| editable) { if let Some(editing_text) = self.editing_text.as_ref().filter(|_| editable) {
+3 -1
View File
@@ -50,7 +50,7 @@ module.exports = {
camelcase: ["error", { properties: "always" }], camelcase: ["error", { properties: "always" }],
"linebreak-style": ["error", "unix"], "linebreak-style": ["error", "unix"],
"eol-last": ["error", "always"], "eol-last": ["error", "always"],
"max-len": ["error", { code: 200, tabWidth: 4 }], "max-len": ["error", { code: 200, tabWidth: 4, ignorePattern: `d="([\\s\\S]*?)"` }],
"prefer-destructuring": "off", "prefer-destructuring": "off",
"no-console": "warn", "no-console": "warn",
"no-debugger": "warn", "no-debugger": "warn",
@@ -81,6 +81,8 @@ module.exports = {
// Import plugin config (for intelligently validating module import statements) // Import plugin config (for intelligently validating module import statements)
"import/no-unresolved": "error", "import/no-unresolved": "error",
// `no-duplicates` disabled due to <https://github.com/import-js/eslint-plugin-import/issues/1479#issuecomment-1789527447>. Reenable if that issue gets fixed.
"import/no-duplicates": "off",
"import/prefer-default-export": "off", "import/prefer-default-export": "off",
"import/no-relative-packages": "error", "import/no-relative-packages": "error",
"import/order": [ "import/order": [
+2 -2
View File
@@ -417,8 +417,8 @@
class={"visibility"} class={"visibility"}
action={(e) => (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())} action={(e) => (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())}
size={24} size={24}
icon={listing.entry.disabled ? "EyeHidden" : "EyeVisible"} icon={listing.entry.visible ? "EyeVisible" : "EyeHidden"}
tooltip={listing.entry.disabled ? "Disabled" : "Enabled"} tooltip={listing.entry.visible ? "Visible" : "Hidden"}
/> />
</LayoutRow> </LayoutRow>
{/each} {/each}
+4 -4
View File
@@ -795,7 +795,7 @@
class="layer" class="layer"
class:selected={showSelected($nodeGraph.selected, boxSelection, node.id, nodeIndex)} class:selected={showSelected($nodeGraph.selected, boxSelection, node.id, nodeIndex)}
class:previewed={node.previewed} class:previewed={node.previewed}
class:disabled={node.disabled} class:disabled={!node.visible}
style:--offset-left={(node.position?.x || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundX || 0 : 0)} style:--offset-left={(node.position?.x || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundX || 0 : 0)}
style:--offset-top={(node.position?.y || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundY || 0 : 0)} style:--offset-top={(node.position?.y || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundY || 0 : 0)}
style:--clip-path-id={`url(#${clipPathId})`} style:--clip-path-id={`url(#${clipPathId})`}
@@ -891,8 +891,8 @@
class={"visibility"} class={"visibility"}
action={(e) => (toggleLayerVisibility(node.id), e?.stopPropagation())} action={(e) => (toggleLayerVisibility(node.id), e?.stopPropagation())}
size={24} size={24}
icon={node.disabled ? "EyeHidden" : "EyeVisible"} icon={node.visible ? "EyeVisible" : "EyeHidden"}
tooltip={node.disabled ? "Disabled" : "Enabled"} tooltip={node.visible ? "Visible" : "Hidden"}
/> />
<svg class="border-mask" width="0" height="0"> <svg class="border-mask" width="0" height="0">
@@ -913,7 +913,7 @@
class="node" class="node"
class:selected={showSelected($nodeGraph.selected, boxSelection, node.id, nodeIndex)} class:selected={showSelected($nodeGraph.selected, boxSelection, node.id, nodeIndex)}
class:previewed={node.previewed} class:previewed={node.previewed}
class:disabled={node.disabled} class:disabled={!node.visible}
style:--offset-left={(node.position?.x || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundX || 0 : 0)} style:--offset-left={(node.position?.x || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundX || 0 : 0)}
style:--offset-top={(node.position?.y || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundY || 0 : 0)} style:--offset-top={(node.position?.y || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundY || 0 : 0)}
style:--clip-path-id={`url(#${clipPathId})`} style:--clip-path-id={`url(#${clipPathId})`}
+2 -2
View File
@@ -124,7 +124,7 @@ export class FrontendNode {
readonly previewed!: boolean; readonly previewed!: boolean;
readonly disabled!: boolean; readonly visible!: boolean;
readonly errors!: string | undefined; readonly errors!: string | undefined;
} }
@@ -613,7 +613,7 @@ export class LayerPanelEntry {
expanded!: boolean; expanded!: boolean;
disabled!: boolean; visible!: boolean;
parentId!: bigint | undefined; parentId!: bigint | undefined;
+2 -2
View File
@@ -757,8 +757,8 @@ impl JsEditorHandle {
/// Toggle visibility of a layer from the layer list /// Toggle visibility of a layer from the layer list
#[wasm_bindgen(js_name = toggleLayerVisibility)] #[wasm_bindgen(js_name = toggleLayerVisibility)]
pub fn toggle_layer_visibility(&self, id: u64) { pub fn toggle_layer_visibility(&self, id: u64) {
let id = NodeId(id); let node_id = NodeId(id);
let message = NodeGraphMessage::ToggleHidden { node_id: id }; let message = NodeGraphMessage::ToggleVisibility { node_id };
self.dispatch(message); self.dispatch(message);
} }
@@ -39,7 +39,6 @@ fn add_network() -> NodeNetwork {
NodeNetwork { NodeNetwork {
imports: vec![], imports: vec![],
exports: vec![NodeOutput::new(NodeId(0), 0)], exports: vec![NodeOutput::new(NodeId(0), 0)],
disabled: vec![],
previous_outputs: None, previous_outputs: None,
nodes: [DocumentNode { nodes: [DocumentNode {
name: "Blend Image".into(), name: "Blend Image".into(),
+13 -9
View File
@@ -152,10 +152,16 @@ pub struct DocumentNode {
/// Now, the call from `F` directly reaches the `CacheNode` and the `CacheNode` can decide whether to call `G.eval(input_from_f)` /// Now, the call from `F` directly reaches the `CacheNode` and the `CacheNode` can decide whether to call `G.eval(input_from_f)`
/// in the event of a cache miss or just return the cached data in the event of a cache hit. /// in the event of a cache miss or just return the cached data in the event of a cache hit.
pub manual_composition: Option<Type>, pub manual_composition: Option<Type>,
// TODO: Remove once this references its definition instead (see above TODO).
/// Indicates to the UI if a primary output should be drawn for this node.
/// True for most nodes, but the Split Channels node is an example of a node that has multiple secondary outputs but no primary output.
#[serde(default = "return_true")] #[serde(default = "return_true")]
pub has_primary_output: bool, pub has_primary_output: bool,
// A nested document network or a proto-node identifier. // A nested document network or a proto-node identifier.
pub implementation: DocumentNodeImplementation, pub implementation: DocumentNodeImplementation,
/// Represents the eye icon for hiding/showing the node in the graph UI. When hidden, a node gets replaced with an identity node during the graph flattening step.
#[serde(default = "return_true")]
pub visible: bool,
/// Metadata about the node including its position in the graph UI. /// Metadata about the node including its position in the graph UI.
pub metadata: DocumentNodeMetadata, pub metadata: DocumentNodeMetadata,
/// When two different proto nodes hash to the same value (e.g. two value nodes each containing `2_u32` or two multiply nodes that have the same node IDs as input), the duplicates are removed. /// When two different proto nodes hash to the same value (e.g. two value nodes each containing `2_u32` or two multiply nodes that have the same node IDs as input), the duplicates are removed.
@@ -203,6 +209,7 @@ impl Default for DocumentNode {
manual_composition: Default::default(), manual_composition: Default::default(),
has_primary_output: true, has_primary_output: true,
implementation: Default::default(), implementation: Default::default(),
visible: true,
metadata: Default::default(), metadata: Default::default(),
skip_deduplication: Default::default(), skip_deduplication: Default::default(),
world_state_hash: Default::default(), world_state_hash: Default::default(),
@@ -523,9 +530,6 @@ pub struct NodeNetwork {
pub exports: Vec<NodeOutput>, pub exports: Vec<NodeOutput>,
/// The list of all nodes in this network. /// The list of all nodes in this network.
pub nodes: HashMap<NodeId, DocumentNode>, pub nodes: HashMap<NodeId, DocumentNode>,
/// Nodes that the user has disabled/hidden with the visibility eye icon.
/// These nodes get replaced with Identity nodes during the graph flattening step.
pub disabled: Vec<NodeId>,
/// In the case when another node is previewed (chosen by the user as a temporary output), this stores what it previously was so it can be restored later. /// In the case when another node is previewed (chosen by the user as a temporary output), this stores what it previously was so it can be restored later.
pub previous_outputs: Option<Vec<NodeOutput>>, pub previous_outputs: Option<Vec<NodeOutput>>,
} }
@@ -540,7 +544,6 @@ impl std::hash::Hash for NodeNetwork {
id.hash(state); id.hash(state);
node.hash(state); node.hash(state);
} }
self.disabled.hash(state);
self.previous_outputs.hash(state); self.previous_outputs.hash(state);
} }
} }
@@ -567,7 +570,6 @@ impl NodeNetwork {
imports: node.inputs.iter().filter(|input| matches!(input, NodeInput::Network(_))).map(|_| NodeId(0)).collect(), imports: node.inputs.iter().filter(|input| matches!(input, NodeInput::Network(_))).map(|_| NodeId(0)).collect(),
exports: vec![NodeOutput::new(NodeId(0), 0)], exports: vec![NodeOutput::new(NodeId(0), 0)],
nodes: [(NodeId(0), node)].into_iter().collect(), nodes: [(NodeId(0), node)].into_iter().collect(),
disabled: vec![],
previous_outputs: None, previous_outputs: None,
} }
} }
@@ -815,7 +817,6 @@ impl NodeNetwork {
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) { pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) {
self.imports.iter_mut().for_each(|id| *id = f(*id)); self.imports.iter_mut().for_each(|id| *id = f(*id));
self.exports.iter_mut().for_each(|output| output.node_id = f(output.node_id)); self.exports.iter_mut().for_each(|output| output.node_id = f(output.node_id));
self.disabled.iter_mut().for_each(|id| *id = f(*id));
self.previous_outputs self.previous_outputs
.iter_mut() .iter_mut()
.for_each(|nodes| nodes.iter_mut().for_each(|output| output.node_id = f(output.node_id))); .for_each(|nodes| nodes.iter_mut().for_each(|output| output.node_id = f(output.node_id)));
@@ -930,8 +931,11 @@ impl NodeNetwork {
return; return;
}; };
if node.implementation != DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into()) && self.disabled.contains(&id) { // If the node is hidden, replace it with an identity node
node.implementation = DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into()); let identity_node = DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into());
if !node.visible && node.implementation != identity_node {
node.implementation = identity_node;
if node.is_layer() { if node.is_layer() {
// Connect layer node to the graphic group below // Connect layer node to the graphic group below
node.inputs.drain(..1); node.inputs.drain(..1);
@@ -939,6 +943,7 @@ impl NodeNetwork {
node.inputs.drain(1..); node.inputs.drain(1..);
} }
self.nodes.insert(id, node); self.nodes.insert(id, node);
return; return;
} }
@@ -983,7 +988,6 @@ impl NodeNetwork {
let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>(); let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>();
// Copy nodes from the inner network into the parent network // Copy nodes from the inner network into the parent network
self.nodes.extend(inner_network.nodes); self.nodes.extend(inner_network.nodes);
self.disabled.extend(inner_network.disabled);
let mut network_offsets = HashMap::new(); let mut network_offsets = HashMap::new();
assert_eq!( assert_eq!(
@@ -193,7 +193,7 @@ impl BorrowTree {
.extend((0..params).flat_map(|i| proto_node.original_location.inputs(i).map(move |source| (source, (id, i))))); .extend((0..params).flat_map(|i| proto_node.original_location.inputs(i).map(move |source| (source, (id, i)))));
self.outputs_source_map.extend(proto_node.original_location.outputs(0).map(|source| (source, id))); self.outputs_source_map.extend(proto_node.original_location.outputs(0).map(|source| (source, id)));
for x in proto_node.original_location.outputs_source.values() { for x in proto_node.original_location.outputs_source.values() {
assert_eq!(*x, 0, "protonodes should refer to output index 0"); assert_eq!(*x, 0, "Proto nodes should refer to output index 0");
} }
match &proto_node.construction_args { match &proto_node.construction_args {
+1 -1
View File
@@ -38,7 +38,7 @@ module.exports = {
camelcase: ["error", { properties: "always" }], camelcase: ["error", { properties: "always" }],
"linebreak-style": ["error", "unix"], "linebreak-style": ["error", "unix"],
"eol-last": ["error", "always"], "eol-last": ["error", "always"],
"max-len": ["error", { code: 200, tabWidth: 4 }], "max-len": ["error", { code: 200, tabWidth: 4, ignorePattern: `d="([\\s\\S]*?)"` }],
"prefer-destructuring": "off", "prefer-destructuring": "off",
"no-console": "warn", "no-console": "warn",
"no-debugger": "warn", "no-debugger": "warn",
+1 -1
View File
@@ -35,7 +35,7 @@ module.exports = {
camelcase: ["error", { properties: "always" }], camelcase: ["error", { properties: "always" }],
"linebreak-style": ["error", "unix"], "linebreak-style": ["error", "unix"],
"eol-last": ["error", "always"], "eol-last": ["error", "always"],
"max-len": ["error", { code: 200, tabWidth: 4 }], "max-len": ["error", { code: 200, tabWidth: 4, ignorePattern: `d="([\\s\\S]*?)"` }],
"prefer-destructuring": "off", "prefer-destructuring": "off",
"no-console": "warn", "no-console": "warn",
"no-debugger": "warn", "no-debugger": "warn",