diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index d77a115587..e6f84f5aa1 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -2,7 +2,7 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument}; use crate::messages::app_window::app_window_message_handler::AppWindowPlatform; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::node_graph::utility_types::{ - BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeOrLayer, FrontendNodeType, FrontendXY, FrontendXY, Transform, + BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNodeToRender, FrontendNodeType, FrontendXY, Transform, }; use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer}; use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate}; diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 8864d63f0d..1aa6cdf6d6 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -6,9 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::document_message_handler::navigation_controls; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext; -use crate::messages::portfolio::document::node_graph::utility_types::{ - ContextMenuData, Direction, FrontendGraphDataType, FrontendLayer, FrontendNodeMetadata, FrontendNodeOrLayer, FrontendXY, FrontendXY, -}; +use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, FrontendXY}; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::misc::GroupFolderType; use crate::messages::portfolio::document::utility_types::network_interface::{ @@ -24,7 +22,7 @@ use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion}; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; use glam::{DAffine2, DVec2, IVec2}; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; -use graph_craft::proto::{GraphErrors, NodeMetadata}; +use graph_craft::proto::GraphErrors; use graphene_std::math::math_ext::QuadExt; use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath; use graphene_std::*; @@ -1638,7 +1636,7 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::UpdateActionButtons); if graph_view_overlay_open { let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, breadcrumb_network_path); - self.frontend_nodes = nodes_to_render.iter().map(|node| node.id).collect(); + self.frontend_nodes = nodes_to_render.iter().map(|node| node.metadata.node_id).collect(); let previewed_node = network_interface.previewed_node(breadcrumb_network_path); responses.add(FrontendMessage::UpdateNodeGraphNodes { nodes_to_render, diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs index 1adf67902e..14a9ea5107 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -89,6 +89,8 @@ pub struct FrontendGraphOutput { // Metadata that is common to nodes and layers #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] pub struct FrontendNodeMetadata { + #[serde(rename = "node_id")] + pub node_id: NodeId, // TODO: Remove and replace with popup manager system #[serde(rename = "canBeLayer")] pub can_be_layer: bool, @@ -125,7 +127,7 @@ pub struct FrontendLayer { #[serde(rename = "chainWidth")] pub chain_width: u32, #[serde(rename = "layerHasLeftBorderGap")] - layer_has_left_border_gap: bool, + pub layer_has_left_border_gap: bool, #[serde(rename = "primaryInputConnectedToLayer")] pub primary_input_connected_to_layer: bool, #[serde(rename = "primaryOutputConnectedToLayer")] @@ -148,11 +150,17 @@ pub struct FrontendLayer { #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] pub struct FrontendNodeOrLayer { - pub metadata: FrontendNodeMetadata, pub node: Option, pub layer: Option, } +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendNodeToRender { + pub metadata: FrontendNodeMetadata, + #[serde(rename = "nodeOrLayer")] + pub node_or_layer: FrontendNodeOrLayer, +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] pub struct FrontendNodeType { pub name: Cow<'static, str>, diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs index 055003117e..aae024f91c 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs @@ -5,14 +5,16 @@ use graphene_std::uuid::NodeId; use crate::{ consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}, messages::portfolio::document::{ - node_graph::utility_types::{FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendXY}, + node_graph::utility_types::{ + FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer, FrontendNodeToRender, FrontendXY, + }, utility_types::network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector}, }, }; // Functions used to collect data from the network interface for use in rendering the node graph impl NodeNetworkInterface { - pub fn collect_nodes(&mut self, node_graph_errors: &GraphErrors, network_path: &[NodeId]) -> Vec { + pub fn collect_nodes(&mut self, node_graph_errors: &GraphErrors, network_path: &[NodeId]) -> Vec { let Some(network) = self.nested_network(network_path) else { log::error!("Could not get nested network when collecting nodes"); return Vec::new(); @@ -22,32 +24,6 @@ impl NodeNetworkInterface { for (node_id, visible) in network.nodes.iter().map(|(node_id, node)| (*node_id, node.visible)).collect::>() { let node_id_path = [network_path, &[node_id]].concat(); - let primary_input_connector = InputConnector::node(node_id, 0); - - let primary_input = if self.input_from_connector(&primary_input_connector, network_path).is_some_and(|input| input.is_exposed()) { - self.frontend_input_from_connector(&primary_input_connector, network_path) - } else { - None - }; - let exposed_inputs = (1..self.number_of_inputs(&node_id, network_path)) - .filter_map(|input_index| self.frontend_input_from_connector(&InputConnector::node(node_id, input_index), network_path)) - .collect(); - - let primary_output = self.frontend_output_from_connector(&OutputConnector::node(node_id, 0), network_path); - - let exposed_outputs = (1..self.number_of_outputs(&node_id, network_path)) - .filter_map(|output_index| self.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), network_path)) - .collect(); - - let Some(position) = self.position(&node_id, network_path) else { - log::error!("Could not get position for node: {node_id}"); - continue; - }; - let position = FrontendXY { x: position.x, y: position.y }; - let previewed = self.previewed_node(network_path) == Some(node_id); - - let locked = self.is_locked(&node_id, network_path); - let errors = node_graph_errors .iter() .find(|error| error.node_path == node_id_path) @@ -60,31 +36,73 @@ impl NodeNetworkInterface { } }); - nodes.push(FrontendNode { - id: node_id, - is_layer: self.node_metadata(&node_id, network_path).is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()), + let metadata = FrontendNodeMetadata { + node_id, can_be_layer: self.is_eligible_to_be_layer(&node_id, network_path), + display_name: self.display_name(&node_id, network_path), selected: selected_nodes.0.contains(&node_id), reference: self.reference(&node_id, network_path).cloned().unwrap_or_default(), - display_name: self.display_name(&node_id, network_path), - previewed, visible, errors, + }; - primary_input, - exposed_inputs, - primary_output, - exposed_outputs, - position, + let node_or_layer = match self.is_layer(&node_id, network_path) { + true => { + let Some(position) = self.position(&node_id, network_path) else { + log::error!("Could not get position for node: {node_id}"); + continue; + }; + let position = FrontendXY { x: position.x, y: position.y }; - locked, - chain_width: self.chain_width(&node_id, network_path), - layer_has_left_border_gap: self.layer_has_left_border_gap(&node_id, network_path), - primary_input_connected_to_layer: self.primary_output_connected_to_layer(&node_id, network_path), - primary_output_connected_to_layer: self.primary_input_connected_to_layer(&node_id, network_path), - }); + let Some(bottom_input) = self.frontend_input_from_connector(&InputConnector::node(node_id, 0), network_path) else { + log::error!("Layer must have a visible primary input"); + continue; + }; + let side_input = self.frontend_input_from_connector(&InputConnector::node(node_id, 1), network_path); + let Some(output) = self.frontend_output_from_connector(&OutputConnector::node(node_id, 0), network_path) else { + log::error!("Layer must have a visible primary output"); + continue; + }; + + let layer = Some(FrontendLayer { + bottom_input, + side_input, + output, + position, + locked: self.is_locked(&node_id, network_path), + chain_width: self.chain_width(&node_id, network_path), + layer_has_left_border_gap: self.layer_has_left_border_gap(&node_id, network_path), + primary_input_connected_to_layer: self.primary_output_connected_to_layer(&node_id, network_path), + primary_output_connected_to_layer: self.primary_input_connected_to_layer(&node_id, network_path), + }); + FrontendNodeOrLayer { node: None, layer } + } + false => { + let Some(position) = self.position(&node_id, network_path) else { + log::error!("Could not get position for node: {node_id}"); + continue; + }; + + let position = FrontendXY { x: position.x, y: position.y }; + + let inputs = (0..self.number_of_inputs(&node_id, network_path)) + .map(|input_index| self.frontend_input_from_connector(&InputConnector::node(node_id, input_index), network_path)) + .collect(); + + let outputs = (0..self.number_of_outputs(&node_id, network_path)) + .map(|output_index| self.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), network_path)) + .collect(); + + let node = Some(FrontendNode { position, inputs, outputs }); + + FrontendNodeOrLayer { node, layer: None } + } + }; + + let frontend_node_to_render = FrontendNodeToRender { metadata, node_or_layer }; + + nodes.push(frontend_node_to_render); } - nodes } @@ -146,12 +164,15 @@ impl NodeNetworkInterface { // } // }; + let connected_to_node = self.upstream_output_connector(input_connector, network_path).and_then(|output_connector| output_connector.node_id()); + Some(FrontendGraphInput { data_type, resolved_type, name, description, connected_to, + connected_to_node, }) } diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts index 7a76acb655..66092a2d3f 100644 --- a/frontend/src/messages.ts +++ b/frontend/src/messages.ts @@ -78,7 +78,7 @@ export class UpdateLayerWidths extends JsMessage { } export class UpdateNodeGraphNodes extends JsMessage { - readonly nodesToRender!: FrontendNode[]; + readonly nodesToRender!: FrontendNodeToRender[]; readonly inSelectedNetwork!: boolean; @@ -199,6 +199,8 @@ export class FrontendGraphOutput { } export class FrontendNodeMetadata { + readonly nodeId!: bigint; + readonly canBeLayer!: boolean; readonly displayName!: string; @@ -255,11 +257,15 @@ export class FrontendLayerPosition { } export class FrontendNodeOrLayer { - readonly metadata!: FrontendNodeMetadata; readonly node!: FrontendNode | undefined; readonly layer!: FrontendLayer | undefined; } +export class FrontendNodeToRender { + readonly metadata!: FrontendNodeMetadata; + readonly nodeOrLayer!: FrontendNodeOrLayer; +} + export class UpdateCentralNodeGraph extends JsMessage { readonly nodeOrLayer!: FrontendNodeOrLayer[]; readonly inSelectedNetwork!: boolean; diff --git a/frontend/src/state-providers/node-graph.ts b/frontend/src/state-providers/node-graph.ts index d2f4c693f2..9e12d4820f 100644 --- a/frontend/src/state-providers/node-graph.ts +++ b/frontend/src/state-providers/node-graph.ts @@ -1,11 +1,11 @@ import { writable } from "svelte/store"; import { type Editor } from "@graphite/editor"; -import type { FrontendNodeOrLayer } from "@graphite/messages"; import { type Box, type FrontendClickTargets, type ContextMenuInformation, + type FrontendNodeToRender, type FrontendNodeType, type WirePath, ClearAllNodeGraphWires, @@ -102,7 +102,7 @@ export function createNodeGraphState(editor: Editor) { update((state) => { state.nodesToRender.clear(); updateNodeGraphNodes.nodesToRender.forEach((node) => { - state.nodesToRender.set(node.id, node); + state.nodesToRender.set(node.metadata.nodeId, node); }); state.inSelectedNetwork = updateNodeGraphNodes.inSelectedNetwork; state.previewedNode = updateNodeGraphNodes.previewedNode;