mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
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:
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(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
|
||||
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
|
||||
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
|
||||
|
|
|
@ -822,7 +822,7 @@ impl DocumentMessageHandler {
|
|||
self.metadata
|
||||
.root()
|
||||
.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_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))))
|
||||
|
@ -835,7 +835,7 @@ impl DocumentMessageHandler {
|
|||
self.metadata
|
||||
.root()
|
||||
.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(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.metadata.transform_to_document(*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
|
||||
pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
|
||||
self.selected_nodes
|
||||
.selected_visible_layers(self.network(), self.metadata())
|
||||
.selected_visible_layers(self.metadata())
|
||||
.filter_map(|layer| self.metadata.bounding_box_viewport(layer))
|
||||
.reduce(graphene_core::renderer::Quad::combine_bounds)
|
||||
}
|
||||
|
@ -887,13 +887,6 @@ impl DocumentMessageHandler {
|
|||
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`].
|
||||
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure_section: &mut Vec<u64>, data_section: &mut Vec<u64>, path: &mut Vec<LayerNodeIdentifier>) {
|
||||
let mut space = 0;
|
||||
|
|
|
@ -89,13 +89,13 @@ pub enum NodeGraphMessage {
|
|||
ShiftNode {
|
||||
node_id: NodeId,
|
||||
},
|
||||
ToggleSelectedHidden,
|
||||
ToggleHidden {
|
||||
ToggleSelectedVisibility,
|
||||
ToggleVisibility {
|
||||
node_id: NodeId,
|
||||
},
|
||||
SetHidden {
|
||||
SetVisibility {
|
||||
node_id: NodeId,
|
||||
hidden: bool,
|
||||
visible: bool,
|
||||
},
|
||||
SetName {
|
||||
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);
|
||||
}
|
||||
NodeGraphMessage::SelectedNodesUpdated => {
|
||||
self.update_selection_action_buttons(document_network, selected_nodes, responses);
|
||||
self.update_selected(document_network, selected_nodes, responses);
|
||||
self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses);
|
||||
self.update_selected(document_network, document_metadata, selected_nodes, responses);
|
||||
if selected_nodes.selected_layers(document_metadata).count() <= 1 {
|
||||
responses.add(DocumentMessage::SetRangeSelectionLayer {
|
||||
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) {
|
||||
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 => {
|
||||
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 });
|
||||
}
|
||||
|
||||
self.update_selected(document_network, selected_nodes, responses);
|
||||
self.update_selected(document_network, document_metadata, selected_nodes, responses);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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 } => {
|
||||
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);
|
||||
}
|
||||
NodeGraphMessage::ToggleSelectedHidden => {
|
||||
if let Some(network) = document_network.nested_network(&self.network) {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
NodeGraphMessage::ToggleSelectedVisibility => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
let new_hidden = !selected_nodes.selected_nodes().any(|id| network.disabled.contains(id));
|
||||
for &node_id in selected_nodes.selected_nodes() {
|
||||
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden });
|
||||
}
|
||||
// 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() {
|
||||
responses.add(NodeGraphMessage::SetVisibility { node_id, visible });
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::ToggleHidden { node_id } => {
|
||||
if let Some(network) = document_network.nested_network(&self.network) {
|
||||
let new_hidden = !network.disabled.contains(&node_id);
|
||||
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden });
|
||||
}
|
||||
NodeGraphMessage::ToggleVisibility { node_id } => {
|
||||
let visible = document_metadata.node_is_visible(node_id);
|
||||
let visible = !visible;
|
||||
|
||||
responses.add(NodeGraphMessage::SetVisibility { node_id, visible });
|
||||
}
|
||||
NodeGraphMessage::SetHidden { node_id, hidden } => {
|
||||
if let Some(network) = document_network.nested_network_mut(&self.network) {
|
||||
if !hidden {
|
||||
network.disabled.retain(|&id| node_id != id);
|
||||
} else if !network.imports.contains(&node_id) && !network.original_outputs().iter().any(|output| output.node_id == node_id) {
|
||||
network.disabled.push(node_id);
|
||||
}
|
||||
NodeGraphMessage::SetVisibility { node_id, visible } => {
|
||||
(|| {
|
||||
let Some(network) = document_network.nested_network_mut(&self.network) else { return };
|
||||
|
||||
let input_or_output = network.imports.contains(&node_id) || network.original_outputs().iter().any(|output| output.node_id == node_id);
|
||||
let visibility = if visible {
|
||||
true
|
||||
} else if !input_or_output {
|
||||
false
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// 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
|
||||
if network.connected_to_output(node_id) {
|
||||
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 } => {
|
||||
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);
|
||||
}
|
||||
|
@ -522,7 +532,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
let node_types = document_node_types::collect_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 } => {
|
||||
self.resolved_types = resolved_types;
|
||||
|
@ -540,7 +550,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
impl NodeGraphMessageHandler {
|
||||
pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList {
|
||||
if self.has_selection && graph_open {
|
||||
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleSelectedHidden)
|
||||
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleSelectedVisibility)
|
||||
} else {
|
||||
actions!(NodeGraphMessageDiscriminant;)
|
||||
}
|
||||
|
@ -554,8 +564,8 @@ impl NodeGraphMessageHandler {
|
|||
});
|
||||
}
|
||||
|
||||
/// Updates the buttons for disable and preview
|
||||
fn update_selection_action_buttons(&mut self, document_network: &NodeNetwork, selected_nodes: &SelectedNodes, responses: &mut VecDeque<Message>) {
|
||||
/// Updates the buttons for visibility and preview
|
||||
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) {
|
||||
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 selection.next().is_some() {
|
||||
// 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
|
||||
let multiple_nodes = selection.next().is_some();
|
||||
|
||||
// Generate the enable or disable button accordingly
|
||||
let (hide_show_label, hide_show_icon) = if is_hidden { ("Make Visible", "EyeHidden") } else { ("Make Hidden", "EyeVisible") };
|
||||
// Generate the visible/hidden button accordingly
|
||||
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)
|
||||
.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_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedHidden))
|
||||
.on_update(move |_| NodeGraphMessage::ToggleSelectedHidden.into())
|
||||
.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::ToggleSelectedVisibility))
|
||||
.on_update(move |_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
||||
.widget_holder();
|
||||
widgets.push(hide_button);
|
||||
|
||||
|
@ -745,7 +755,7 @@ impl NodeGraphMessageHandler {
|
|||
exposed_outputs,
|
||||
position: node.metadata.position.into(),
|
||||
previewed: network.outputs_contain(node_id),
|
||||
disabled: network.disabled.contains(&node_id),
|
||||
visible: node.visible,
|
||||
errors: errors.map(|e| format!("{e:?}")),
|
||||
});
|
||||
}
|
||||
|
@ -774,7 +784,7 @@ impl NodeGraphMessageHandler {
|
|||
parent_id: layer.parent(metadata).map(|parent| parent.to_node()),
|
||||
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() },
|
||||
disabled: network.disabled.contains(&node_id),
|
||||
visible: node.visible,
|
||||
};
|
||||
responses.add(FrontendMessage::UpdateDocumentLayerDetails { data });
|
||||
}
|
||||
|
@ -795,8 +805,8 @@ impl NodeGraphMessageHandler {
|
|||
}
|
||||
|
||||
/// 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>) {
|
||||
self.update_selection_action_buttons(document_network, selected_nodes, responses);
|
||||
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, document_metadata, selected_nodes, responses);
|
||||
responses.add(FrontendMessage::UpdateNodeGraphSelection {
|
||||
selected: selected_nodes.selected_nodes_ref().clone(),
|
||||
});
|
||||
|
|
|
@ -84,7 +84,7 @@ pub struct FrontendNode {
|
|||
#[serde(rename = "exposedOutputs")]
|
||||
pub exposed_outputs: Vec<FrontendGraphOutput>,
|
||||
pub position: (i32, i32),
|
||||
pub disabled: bool,
|
||||
pub visible: bool,
|
||||
pub previewed: bool,
|
||||
pub errors: Option<String>,
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::collections::HashMap;
|
|||
pub enum Clipboard {
|
||||
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,
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ pub const INTERNAL_CLIPBOARD_COUNT: u8 = Clipboard::_InternalClipboardCount as u
|
|||
pub struct CopyBufferEntry {
|
||||
pub nodes: HashMap<NodeId, DocumentNode>,
|
||||
pub selected: bool,
|
||||
pub disabled: bool,
|
||||
pub visible: bool,
|
||||
pub collapsed: bool,
|
||||
pub alias: String,
|
||||
}
|
||||
|
|
|
@ -14,12 +14,15 @@ use std::num::NonZeroU64;
|
|||
// 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)]
|
||||
pub struct DocumentMetadata {
|
||||
upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
|
||||
structure: HashMap<LayerNodeIdentifier, NodeRelations>,
|
||||
artboards: HashSet<LayerNodeIdentifier>,
|
||||
folders: HashSet<LayerNodeIdentifier>,
|
||||
hidden: HashSet<NodeId>,
|
||||
click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
||||
/// Transform from document space to viewport space.
|
||||
pub document_to_viewport: DAffine2,
|
||||
|
@ -29,10 +32,11 @@ impl Default for DocumentMetadata {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
upstream_transforms: HashMap::new(),
|
||||
click_targets: HashMap::new(),
|
||||
structure: HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]),
|
||||
artboards: HashSet::new(),
|
||||
folders: HashSet::new(),
|
||||
hidden: HashSet::new(),
|
||||
click_targets: HashMap::new(),
|
||||
document_to_viewport: DAffine2::IDENTITY,
|
||||
}
|
||||
}
|
||||
|
@ -118,6 +122,10 @@ impl DocumentMetadata {
|
|||
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
|
||||
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();
|
||||
|
@ -138,8 +146,9 @@ impl DocumentMetadata {
|
|||
}
|
||||
|
||||
self.structure = HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]);
|
||||
self.folders = HashSet::new();
|
||||
self.artboards = HashSet::new();
|
||||
self.folders = HashSet::new();
|
||||
self.hidden = HashSet::new();
|
||||
|
||||
let id = graph.exports[0].node_id;
|
||||
let Some(output_node) = graph.nodes.get(&id) else {
|
||||
|
@ -150,22 +159,26 @@ impl DocumentMetadata {
|
|||
};
|
||||
let parent = LayerNodeIdentifier::ROOT;
|
||||
let mut stack = vec![(layer_node, node_id, parent)];
|
||||
while let Some((node, id, parent)) = stack.pop() {
|
||||
let mut current = Some((node, id));
|
||||
while let Some(&(current_node, current_id)) = current.as_ref() {
|
||||
let current_identifier = LayerNodeIdentifier::new_unchecked(current_id);
|
||||
if !self.structure.contains_key(¤t_identifier) {
|
||||
parent.push_child(self, current_identifier);
|
||||
while let Some((node, node_id, parent)) = stack.pop() {
|
||||
let mut current = Some((node, node_id));
|
||||
while let Some(&(current_node, current_node_id)) = current.as_ref() {
|
||||
let current_layer_id = LayerNodeIdentifier::new_unchecked(current_node_id);
|
||||
if !self.structure.contains_key(¤t_layer_id) {
|
||||
parent.push_child(self, current_layer_id);
|
||||
|
||||
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) {
|
||||
self.artboards.insert(current_identifier);
|
||||
if is_artboard(current_layer_id, graph) {
|
||||
self.artboards.insert(current_layer_id);
|
||||
}
|
||||
if is_folder(current_identifier, graph) {
|
||||
self.folders.insert(current_identifier);
|
||||
if is_folder(current_layer_id, graph) {
|
||||
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
|
||||
// ==============
|
||||
// ===============
|
||||
|
||||
#[derive(Clone)]
|
||||
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 super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
pub struct RawBuffer(Vec<u8>);
|
||||
|
||||
|
@ -46,7 +46,7 @@ pub struct LayerPanelEntry {
|
|||
#[serde(rename = "layerClassification")]
|
||||
pub layer_classification: LayerClassification,
|
||||
pub expanded: bool,
|
||||
pub disabled: bool,
|
||||
pub visible: bool,
|
||||
#[serde(rename = "parentId")]
|
||||
pub parent_id: Option<NodeId>,
|
||||
pub depth: usize,
|
||||
|
@ -56,12 +56,12 @@ pub struct LayerPanelEntry {
|
|||
pub struct SelectedNodes(pub Vec<NodeId>);
|
||||
|
||||
impl SelectedNodes {
|
||||
pub fn layer_visible(&self, layer: LayerNodeIdentifier, network: &NodeNetwork, metadata: &DocumentMetadata) -> bool {
|
||||
!layer.ancestors(metadata).any(|layer| network.disabled.contains(&layer.to_node()))
|
||||
pub fn layer_visible(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {
|
||||
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> + '_ {
|
||||
self.selected_layers(metadata).filter(move |&layer| self.layer_visible(layer, network, metadata))
|
||||
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, metadata))
|
||||
}
|
||||
|
||||
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(),
|
||||
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,
|
||||
alias: previous_alias,
|
||||
});
|
||||
|
@ -388,8 +388,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
if entry.selected {
|
||||
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
|
||||
}
|
||||
if entry.disabled {
|
||||
responses.add(NodeGraphMessage::SetHidden { node_id: id, hidden: entry.disabled });
|
||||
if !entry.visible {
|
||||
responses.add(NodeGraphMessage::SetVisibility { node_id: id, visible: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -419,8 +419,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
if entry.selected {
|
||||
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
|
||||
}
|
||||
if entry.disabled {
|
||||
responses.add(NodeGraphMessage::SetHidden { node_id: id, hidden: entry.disabled });
|
||||
if !entry.visible {
|
||||
responses.add(NodeGraphMessage::SetVisibility { node_id: id, visible: false });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ impl Pivot {
|
|||
|
||||
/// Recomputes the pivot position and transform.
|
||||
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 {
|
||||
// If no layers are selected then we revert things back to default
|
||||
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
|
||||
let xy_summation = document
|
||||
.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))
|
||||
.reduce(|a, b| a + b)
|
||||
.unwrap_or_default();
|
||||
|
@ -101,7 +101,7 @@ impl Pivot {
|
|||
|
||||
/// 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>) {
|
||||
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 pivot = transform.inverse().transform_point2(position);
|
||||
// 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 {
|
||||
return;
|
||||
}
|
||||
if !document.selected_nodes.layer_visible(layer, &document.network, &document.metadata) {
|
||||
if !document.selected_nodes.layer_visible(layer, &document.metadata) {
|
||||
return;
|
||||
}
|
||||
if snap_data.ignore.contains(&layer) {
|
||||
|
|
|
@ -247,7 +247,7 @@ impl Fsm for GradientToolFsmState {
|
|||
(_, GradientToolMessage::Overlays(mut overlay_context)) => {
|
||||
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 transform = gradient_space_transform(layer, document);
|
||||
let dragging = selected.filter(|selected| selected.layer == layer).map(|selected| selected.dragging);
|
||||
|
@ -318,7 +318,7 @@ impl Fsm for GradientToolFsmState {
|
|||
self
|
||||
}
|
||||
(_, 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 transform = gradient_space_transform(layer, document);
|
||||
|
||||
|
@ -357,7 +357,7 @@ impl Fsm for GradientToolFsmState {
|
|||
let tolerance = (MANIPULATOR_GROUP_MARKER_SIZE * 2.).powi(2);
|
||||
|
||||
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 transform = gradient_space_transform(layer, document);
|
||||
|
||||
|
|
|
@ -391,7 +391,7 @@ impl Fsm for SelectToolFsmState {
|
|||
tool_data.selected_layers_count = selected_layers_count;
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
@ -405,13 +405,13 @@ impl Fsm for SelectToolFsmState {
|
|||
// Update bounds
|
||||
let transform = document
|
||||
.selected_nodes
|
||||
.selected_visible_layers(document.network(), document.metadata())
|
||||
.selected_visible_layers(document.metadata())
|
||||
.next()
|
||||
.map(|layer| document.metadata().transform_to_viewport(layer));
|
||||
let transform = transform.unwrap_or(DAffine2::IDENTITY);
|
||||
let bounds = document
|
||||
.selected_nodes
|
||||
.selected_visible_layers(document.network(), document.metadata())
|
||||
.selected_visible_layers(document.metadata())
|
||||
.filter_map(|layer| {
|
||||
document
|
||||
.metadata()
|
||||
|
@ -472,7 +472,7 @@ impl Fsm for SelectToolFsmState {
|
|||
.map(|bounding_box| bounding_box.check_rotate(input.mouse.position))
|
||||
.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);
|
||||
|
||||
// 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
|
||||
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) {
|
||||
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) {
|
||||
|
|
|
@ -190,7 +190,7 @@ impl NodeRuntime {
|
|||
image_frame: None,
|
||||
};
|
||||
|
||||
// Required to ensure that the appropriate protonodes are reinserted when the Editor API changes.
|
||||
// Required to ensure that the appropriate proto nodes are reinserted when the Editor API changes.
|
||||
let mut graph_input_hash = DefaultHasher::new();
|
||||
editor_api.font_cache.hash(&mut graph_input_hash);
|
||||
let font_hash_code = graph_input_hash.finish();
|
||||
|
@ -218,7 +218,7 @@ impl NodeRuntime {
|
|||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
|
||||
assert_ne!(proto_network.nodes.len(), 0, "No proto nodes exist?");
|
||||
if let Err(e) = self.executor.update(proto_network).await {
|
||||
self.node_graph_errors = e;
|
||||
} else {
|
||||
|
|
|
@ -50,7 +50,7 @@ module.exports = {
|
|||
camelcase: ["error", { properties: "always" }],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"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",
|
||||
"no-console": "warn",
|
||||
"no-debugger": "warn",
|
||||
|
@ -81,6 +81,8 @@ module.exports = {
|
|||
|
||||
// Import plugin config (for intelligently validating module import statements)
|
||||
"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/no-relative-packages": "error",
|
||||
"import/order": [
|
||||
|
|
|
@ -417,8 +417,8 @@
|
|||
class={"visibility"}
|
||||
action={(e) => (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())}
|
||||
size={24}
|
||||
icon={listing.entry.disabled ? "EyeHidden" : "EyeVisible"}
|
||||
tooltip={listing.entry.disabled ? "Disabled" : "Enabled"}
|
||||
icon={listing.entry.visible ? "EyeVisible" : "EyeHidden"}
|
||||
tooltip={listing.entry.visible ? "Visible" : "Hidden"}
|
||||
/>
|
||||
</LayoutRow>
|
||||
{/each}
|
||||
|
|
|
@ -795,7 +795,7 @@
|
|||
class="layer"
|
||||
class:selected={showSelected($nodeGraph.selected, boxSelection, node.id, nodeIndex)}
|
||||
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-top={(node.position?.y || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundY || 0 : 0)}
|
||||
style:--clip-path-id={`url(#${clipPathId})`}
|
||||
|
@ -891,8 +891,8 @@
|
|||
class={"visibility"}
|
||||
action={(e) => (toggleLayerVisibility(node.id), e?.stopPropagation())}
|
||||
size={24}
|
||||
icon={node.disabled ? "EyeHidden" : "EyeVisible"}
|
||||
tooltip={node.disabled ? "Disabled" : "Enabled"}
|
||||
icon={node.visible ? "EyeVisible" : "EyeHidden"}
|
||||
tooltip={node.visible ? "Visible" : "Hidden"}
|
||||
/>
|
||||
|
||||
<svg class="border-mask" width="0" height="0">
|
||||
|
@ -913,7 +913,7 @@
|
|||
class="node"
|
||||
class:selected={showSelected($nodeGraph.selected, boxSelection, node.id, nodeIndex)}
|
||||
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-top={(node.position?.y || 0) + ($nodeGraph.selected.includes(node.id) ? draggingNodes?.roundY || 0 : 0)}
|
||||
style:--clip-path-id={`url(#${clipPathId})`}
|
||||
|
|
|
@ -124,7 +124,7 @@ export class FrontendNode {
|
|||
|
||||
readonly previewed!: boolean;
|
||||
|
||||
readonly disabled!: boolean;
|
||||
readonly visible!: boolean;
|
||||
|
||||
readonly errors!: string | undefined;
|
||||
}
|
||||
|
@ -613,7 +613,7 @@ export class LayerPanelEntry {
|
|||
|
||||
expanded!: boolean;
|
||||
|
||||
disabled!: boolean;
|
||||
visible!: boolean;
|
||||
|
||||
parentId!: bigint | undefined;
|
||||
|
||||
|
|
|
@ -757,8 +757,8 @@ impl JsEditorHandle {
|
|||
/// Toggle visibility of a layer from the layer list
|
||||
#[wasm_bindgen(js_name = toggleLayerVisibility)]
|
||||
pub fn toggle_layer_visibility(&self, id: u64) {
|
||||
let id = NodeId(id);
|
||||
let message = NodeGraphMessage::ToggleHidden { node_id: id };
|
||||
let node_id = NodeId(id);
|
||||
let message = NodeGraphMessage::ToggleVisibility { node_id };
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ pub fn multiply_opacity(document_node: &DocumentNode, node_id: NodeId, _context:
|
|||
}
|
||||
```
|
||||
|
||||
## Graphene (protonode executor)
|
||||
## Graphene (proto node executor)
|
||||
|
||||
The graphene crate (found in `gcore/`) and the graphene standard library (found in `gstd/`) is where actual implementation for nodes are located.
|
||||
|
||||
|
@ -100,7 +100,7 @@ fn test_opacity_node() {
|
|||
|
||||
The `graphene_core::value::CopiedNode` is a node that, when evaluated, copies `10_f32` and returns it.
|
||||
|
||||
## Creating a new protonode
|
||||
## Creating a new proto node
|
||||
|
||||
Instead of manually implementing the `Node` trait with complex generics, one can use the `node_fn` macro, which can be applied to a function like `opacity_node` with an attribute of the name of the node:
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ fn add_network() -> NodeNetwork {
|
|||
NodeNetwork {
|
||||
imports: vec![],
|
||||
exports: vec![NodeOutput::new(NodeId(0), 0)],
|
||||
disabled: vec![],
|
||||
previous_outputs: None,
|
||||
nodes: [DocumentNode {
|
||||
name: "Blend Image".into(),
|
||||
|
|
|
@ -152,18 +152,24 @@ 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)`
|
||||
/// 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>,
|
||||
// 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")]
|
||||
pub has_primary_output: bool,
|
||||
// A nested document network or a proto-node identifier.
|
||||
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.
|
||||
pub metadata: DocumentNodeMetadata,
|
||||
/// When two different protonodes 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.
|
||||
/// See [`crate::proto::ProtoNetwork::generate_stable_node_ids`] for details.
|
||||
/// However sometimes this is not desirable, for example in the case of a [`graphene_core::memo::MonitorNode`] that needs to be accessed outside of the graph.
|
||||
#[serde(default)]
|
||||
pub skip_deduplication: bool,
|
||||
/// Used as a hash of the graph input where applicable. This ensures that protonodes that depend on the graph's input are always regenerated.
|
||||
/// Used as a hash of the graph input where applicable. This ensures that proto nodes that depend on the graph's input are always regenerated.
|
||||
#[serde(default)]
|
||||
pub world_state_hash: u64,
|
||||
/// The path to this node and its inputs and outputs as of when [`NodeNetwork::generate_node_paths`] was called.
|
||||
|
@ -185,7 +191,7 @@ pub struct Source {
|
|||
pub struct OriginalLocation {
|
||||
/// The original location to the document node - e.g. [grandparent_id, parent_id, node_id].
|
||||
pub path: Option<Vec<NodeId>>,
|
||||
/// Each document input source maps to one protonode input (however one protonode input may come from several sources)
|
||||
/// Each document input source maps to one proto node input (however one proto node input may come from several sources)
|
||||
pub inputs_source: HashMap<Source, usize>,
|
||||
/// A list of document sources for the node's output
|
||||
pub outputs_source: HashMap<Source, usize>,
|
||||
|
@ -203,6 +209,7 @@ impl Default for DocumentNode {
|
|||
manual_composition: Default::default(),
|
||||
has_primary_output: true,
|
||||
implementation: Default::default(),
|
||||
visible: true,
|
||||
metadata: Default::default(),
|
||||
skip_deduplication: Default::default(),
|
||||
world_state_hash: Default::default(),
|
||||
|
@ -272,7 +279,7 @@ impl DocumentNode {
|
|||
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value))
|
||||
}
|
||||
NodeInput::Node { node_id, output_index, lambda } => {
|
||||
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode. {:#?}", self.name);
|
||||
assert_eq!(output_index, 0, "Outputs should be flattened before converting to proto node. {:#?}", self.name);
|
||||
let node = if lambda { ProtoNodeInput::NodeLambda(node_id) } else { ProtoNodeInput::Node(node_id) };
|
||||
(node, ConstructionArgs::Nodes(vec![]))
|
||||
}
|
||||
|
@ -445,9 +452,9 @@ pub enum DocumentNodeImplementation {
|
|||
///
|
||||
/// A nested [`NodeNetwork`] that is flattened by the [`NodeNetwork::flatten`] function.
|
||||
Network(NodeNetwork),
|
||||
/// This describes a (document) node implemented as a protonode.
|
||||
/// This describes a (document) node implemented as a proto node.
|
||||
///
|
||||
/// A protonode identifier which can be found in `node_registry.rs`.
|
||||
/// A proto node identifier which can be found in `node_registry.rs`.
|
||||
ProtoNode(ProtoNodeIdentifier),
|
||||
/// The Extract variant is a tag which tells the compilation process to do something special. It invokes language-level functionality built for use by the ExtractNode to enable metaprogramming.
|
||||
/// When the ExtractNode is compiled, it gets replaced by a value node containing a representation of the source code for the function/lambda of the document node that's fed into the ExtractNode
|
||||
|
@ -523,9 +530,6 @@ pub struct NodeNetwork {
|
|||
pub exports: Vec<NodeOutput>,
|
||||
/// The list of all nodes in this network.
|
||||
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.
|
||||
pub previous_outputs: Option<Vec<NodeOutput>>,
|
||||
}
|
||||
|
@ -540,7 +544,6 @@ impl std::hash::Hash for NodeNetwork {
|
|||
id.hash(state);
|
||||
node.hash(state);
|
||||
}
|
||||
self.disabled.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(),
|
||||
exports: vec![NodeOutput::new(NodeId(0), 0)],
|
||||
nodes: [(NodeId(0), node)].into_iter().collect(),
|
||||
disabled: vec![],
|
||||
previous_outputs: None,
|
||||
}
|
||||
}
|
||||
|
@ -815,7 +817,6 @@ impl NodeNetwork {
|
|||
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) {
|
||||
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.disabled.iter_mut().for_each(|id| *id = f(*id));
|
||||
self.previous_outputs
|
||||
.iter_mut()
|
||||
.for_each(|nodes| nodes.iter_mut().for_each(|output| output.node_id = f(output.node_id)));
|
||||
|
@ -843,7 +844,7 @@ impl NodeNetwork {
|
|||
outwards_links
|
||||
}
|
||||
|
||||
/// Populate the [`DocumentNode::path`], which stores the location of the document node to allow for matching the resulting protonodes to the document node for the purposes of typing and finding monitor nodes.
|
||||
/// Populate the [`DocumentNode::path`], which stores the location of the document node to allow for matching the resulting proto nodes to the document node for the purposes of typing and finding monitor nodes.
|
||||
pub fn generate_node_paths(&mut self, prefix: &[NodeId]) {
|
||||
for (node_id, node) in &mut self.nodes {
|
||||
let mut new_path = prefix.to_vec();
|
||||
|
@ -930,8 +931,11 @@ impl NodeNetwork {
|
|||
return;
|
||||
};
|
||||
|
||||
if node.implementation != DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into()) && self.disabled.contains(&id) {
|
||||
node.implementation = DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into());
|
||||
// If the node is hidden, replace it with an identity node
|
||||
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() {
|
||||
// Connect layer node to the graphic group below
|
||||
node.inputs.drain(..1);
|
||||
|
@ -939,6 +943,7 @@ impl NodeNetwork {
|
|||
node.inputs.drain(1..);
|
||||
}
|
||||
self.nodes.insert(id, node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -983,7 +988,6 @@ impl NodeNetwork {
|
|||
let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>();
|
||||
// Copy nodes from the inner network into the parent network
|
||||
self.nodes.extend(inner_network.nodes);
|
||||
self.disabled.extend(inner_network.disabled);
|
||||
|
||||
let mut network_offsets = HashMap::new();
|
||||
assert_eq!(
|
||||
|
|
|
@ -200,7 +200,7 @@ impl ConstructionArgs {
|
|||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
||||
/// A protonode is an intermediate step between the `DocumentNode` and the boxed struct that actually runs the node (found in the [`BorrowTree`]). It has one primary input and several secondary inputs in [`ConstructionArgs`].
|
||||
/// A proto node is an intermediate step between the `DocumentNode` and the boxed struct that actually runs the node (found in the [`BorrowTree`]). It has one primary input and several secondary inputs in [`ConstructionArgs`].
|
||||
pub struct ProtoNode {
|
||||
pub construction_args: ConstructionArgs,
|
||||
pub input: ProtoNodeInput,
|
||||
|
@ -718,7 +718,7 @@ impl TypingContext {
|
|||
ConstructionArgs::Inline(ref inline) => vec![inline.ty.clone()],
|
||||
};
|
||||
|
||||
// Get the node input type from the protonode declaration
|
||||
// Get the node input type from the proto node declaration
|
||||
let input = match node.input {
|
||||
ProtoNodeInput::None => concrete!(()),
|
||||
ProtoNodeInput::ManualComposition(ref ty) => ty.clone(),
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct DynamicExecutor {
|
|||
output: NodeId,
|
||||
/// Stores all of the dynamic node structs.
|
||||
tree: BorrowTree,
|
||||
/// Stores the types of the protonodes.
|
||||
/// Stores the types of the proto nodes.
|
||||
typing_context: TypingContext,
|
||||
// This allows us to keep the nodes around for one more frame which is used for introspection
|
||||
orphaned_nodes: Vec<NodeId>,
|
||||
|
@ -111,11 +111,11 @@ impl<'a, I: StaticType + 'a> Executor<I, TaggedValue> for &'a DynamicExecutor {
|
|||
pub struct BorrowTree {
|
||||
/// A hashmap of node IDs and dynamically typed nodes.
|
||||
nodes: HashMap<NodeId, SharedNodeContainer>,
|
||||
/// A hashmap from the document path to the protonode ID.
|
||||
/// A hashmap from the document path to the proto node ID.
|
||||
source_map: HashMap<Vec<NodeId>, NodeId>,
|
||||
/// Each document input source maps to one protonode input (however one protonode input may come from several sources)
|
||||
/// Each document input source maps to one proto node input (however one proto node input may come from several sources)
|
||||
inputs_source_map: HashMap<Source, (NodeId, usize)>,
|
||||
/// A mapping of document input sources to the (single) protonode output
|
||||
/// A mapping of document input sources to the (single) proto node output
|
||||
outputs_source_map: HashMap<Source, NodeId>,
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ impl BorrowTree {
|
|||
.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)));
|
||||
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 {
|
||||
|
|
|
@ -38,7 +38,7 @@ module.exports = {
|
|||
camelcase: ["error", { properties: "always" }],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"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",
|
||||
"no-console": "warn",
|
||||
"no-debugger": "warn",
|
||||
|
|
|
@ -35,7 +35,7 @@ module.exports = {
|
|||
camelcase: ["error", { properties: "always" }],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"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",
|
||||
"no-console": "warn",
|
||||
"no-debugger": "warn",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue