diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 5921186733..af91f3ebe4 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::{FrontendDocumentDetails, MouseCursorIcon}; 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, FrontendNodeType, 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}; @@ -274,7 +274,7 @@ pub enum FrontendMessage { }, UpdateNodeGraphNodes { #[serde(rename = "nodesToRender")] - nodes_to_render: Vec, + nodes_to_render: Vec, #[serde(rename = "inSelectedNetwork")] in_selected_network: bool, // Displays a dashed border around the node 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 7af7cd2ee5..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 @@ -1636,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 45dd0532f2..f004921ef8 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -64,9 +64,12 @@ pub struct FrontendGraphInput { pub description: String, #[serde(rename = "resolvedType")] pub resolved_type: String, - #[serde(rename = "connectedTo")] /// Either "nothing", "import index {index}", or "{node name} output {output_index}". + #[serde(rename = "connectedToString")] pub connected_to: String, + /// Used to render the upstream node once this node is rendered + #[serde(rename = "connectedToNode")] + pub connected_to_node: Option, } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] @@ -83,38 +86,79 @@ pub struct FrontendGraphOutput { pub connected_to: Vec, } +// Metadata that is common to nodes and layers #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendNode { - pub id: graph_craft::document::NodeId, - #[serde(rename = "isLayer")] - pub is_layer: bool, +pub struct FrontendNodeMetadata { + #[serde(rename = "nodeId")] + pub node_id: NodeId, + // TODO: Remove and replace with popup manager system #[serde(rename = "canBeLayer")] pub can_be_layer: bool, - pub selected: bool, - pub reference: Option, #[serde(rename = "displayName")] pub display_name: String, - #[serde(rename = "primaryInput")] - pub primary_input: Option, - #[serde(rename = "exposedInputs")] - pub exposed_inputs: Vec, - #[serde(rename = "primaryOutput")] - pub primary_output: Option, - #[serde(rename = "exposedOutputs")] - pub exposed_outputs: Vec, + pub selected: bool, + // Used to get the description, which is stored in a global hashmap + pub reference: Option, + // Reduces opacity of node/hidden eye icon + pub visible: bool, + // The svg string for each input + // pub wires: Vec>, + pub errors: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendNode { + // pub position: FrontendNodePosition, + pub position: FrontendXY, + pub inputs: Vec>, + pub outputs: Vec>, +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendLayer { + #[serde(rename = "bottomInput")] + pub bottom_input: FrontendGraphInput, + #[serde(rename = "sideInput")] + pub side_input: Option, + pub output: FrontendGraphOutput, + // pub position: FrontendLayerPosition, + pub position: FrontendXY, + pub locked: bool, #[serde(rename = "chainWidth")] pub chain_width: u32, #[serde(rename = "layerHasLeftBorderGap")] pub layer_has_left_border_gap: bool, - #[serde(rename = "primaryOutputConnectedToLayer")] - pub primary_output_connected_to_layer: bool, #[serde(rename = "primaryInputConnectedToLayer")] pub primary_input_connected_to_layer: bool, - pub position: FrontendXY, - pub visible: bool, - pub locked: bool, - pub previewed: bool, - pub errors: Option, + #[serde(rename = "primaryOutputConnectedToLayer")] + pub primary_output_connected_to_layer: bool, +} + +// // Should be an enum but those are hard to serialize/deserialize to TS +// #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +// pub struct FrontendNodePosition { +// pub absolute: Option, +// pub chain: Option, +// } + +// // Should be an enum but those are hard to serialize/deserialize to TS +// #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +// pub struct FrontendLayerPosition { +// pub absolute: Option, +// pub stack: Option, +// } + +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] +pub struct FrontendNodeOrLayer { + 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)] 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..60a3357725 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, - - 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 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 }; + + 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_input_connected_to_layer(&node_id, network_path), + primary_output_connected_to_layer: self.primary_output_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/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index 84f860f465..e16943df83 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -117,10 +117,11 @@ function toggleLayerDisplay(displayAsLayer: boolean, toggleId: bigint) { editor.handle.setToNodeOrLayer(toggleId, displayAsLayer); + editor.handle.setToNodeOrLayer(toggleId, displayAsLayer); } function canBeToggledBetweenNodeAndLayer(toggleDisplayAsLayerNodeId: bigint) { - return $nodeGraph.nodesToRender.get(toggleDisplayAsLayerNodeId)?.canBeLayer || false; + return $nodeGraph.nodesToRender.get(toggleDisplayAsLayerNodeId)?.metadata.canBeLayer || false; } function createNode(nodeType: string) { @@ -129,20 +130,24 @@ editor.handle.createNode(nodeType, $nodeGraph.contextMenuInformation.contextMenuCoordinates.x, $nodeGraph.contextMenuInformation.contextMenuCoordinates.y); } - function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, exposedSecondaryInputs: number, primaryOutputExists: boolean, exposedSecondaryOutputs: number): string { - const nodeHeight = Math.max(1 + exposedSecondaryInputs, 1 + exposedSecondaryOutputs) * 24; + function nodeBorderMask(nodeInputs: (FrontendGraphInput | undefined)[], nodeOutputs: (FrontendGraphOutput | undefined)[]): string { + const nodeWidth = 120; + const secondaryInputs = nodeInputs.slice(1).filter((x): x is FrontendGraphInput => x !== undefined); + const secondaryOutputs = nodeOutputs.slice(1); + + const nodeHeight = Math.max(1 + secondaryInputs.length, 1 + secondaryOutputs.length) * 24; const boxes: { x: number; y: number; width: number; height: number }[] = []; // Primary input - if (primaryInputExists) boxes.push({ x: -8, y: 4, width: 16, height: 16 }); + if (nodeInputs[0]) boxes.push({ x: -8, y: 4, width: 16, height: 16 }); // Secondary inputs - for (let i = 0; i < exposedSecondaryInputs; i++) boxes.push({ x: -8, y: 4 + (i + 1) * 24, width: 16, height: 16 }); + for (let i = 0; i < secondaryInputs.length; i++) boxes.push({ x: -8, y: 4 + (i + 1) * 24, width: 16, height: 16 }); // Primary output - if (primaryOutputExists) boxes.push({ x: nodeWidth - 8, y: 4, width: 16, height: 16 }); + if (nodeOutputs[0]) boxes.push({ x: nodeWidth - 8, y: 4, width: 16, height: 16 }); // Exposed outputs - for (let i = 0; i < exposedSecondaryOutputs; i++) boxes.push({ x: nodeWidth - 8, y: 4 + (i + 1) * 24, width: 16, height: 16 }); + for (let i = 0; i < secondaryOutputs.length; i++) boxes.push({ x: nodeWidth - 8, y: 4 + (i + 1) * 24, width: 16, height: 16 }); return borderMask(boxes, nodeWidth, nodeHeight); } @@ -199,14 +204,20 @@ } function inputConnectedToText(input: FrontendGraphInput): string { - return `Connected to:\n${input.connectedTo}`; + return `Connected to:\n${input.connectedToString}`; } - function zipWithUndefined(arr1: FrontendGraphInput[], arr2: FrontendGraphOutput[]) { - const maxLength = Math.max(arr1.length, arr2.length); - const result = []; + function collectExposedInputsOutputs( + inputs: (FrontendGraphInput | undefined)[], + outputs: (FrontendGraphOutput | undefined)[], + ): [FrontendGraphInput | undefined, FrontendGraphOutput | undefined][] { + const secondaryInputs = inputs.slice(1).filter((x): x is FrontendGraphInput => x !== undefined); + const secondaryOutputs = outputs.slice(1); + const maxLength = Math.max(secondaryInputs.length, secondaryOutputs.length); + const result: [FrontendGraphInput | undefined, FrontendGraphOutput | undefined][] = []; + for (let i = 0; i < maxLength; i++) { - result.push([arr1[i], arr2[i]]); + result.push([secondaryInputs[i] || undefined, secondaryOutputs[i] || undefined]); } return result; } @@ -484,123 +495,118 @@ {/if} -
- - {#each Array.from($nodeGraph.nodesToRender).filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) as [nodeId, layer]} - {@const clipPathId = String(Math.random()).substring(2)} - {@const layerAreaWidth = $nodeGraph.layerWidths.get(layer.id) || 8} - {@const layerChainWidth = layer.chainWidth !== 0 ? layer.chainWidth + 0.5 : 0} - {@const description = (layer.reference && $nodeGraph.nodeDescriptions.get(layer.reference)) || undefined} -
- {#if layer.errors} - {layer.errors} - {layer.errors} - {/if} -
- {#if $nodeGraph.thumbnails.has(nodeId)} - {@html $nodeGraph.thumbnails.get(nodeId)} + {#each Array.from($nodeGraph.nodesToRender) as [nodeId, nodeToRender]} + {#if nodeToRender.nodeOrLayer.layer !== undefined} + {@const nodeMetadata = nodeToRender.metadata} + {@const layer = nodeToRender.nodeOrLayer.layer} + {@const clipPathId = String(Math.random()).substring(2)} + {@const layerAreaWidth = $nodeGraph.layerWidths.get(nodeToRender.metadata.nodeId) || 8} + {@const layerChainWidth = layer.chainWidth !== 0 ? layer.chainWidth + 0.5 : 0} + {@const description = (nodeMetadata.reference && $nodeGraph.nodeDescriptions.get(nodeMetadata.reference)) || undefined} +
+ {#if nodeMetadata.errors} + {layer.errors} + {layer.errors} {/if} - - {#if layer.primaryOutput} +
+ {#if $nodeGraph.thumbnails.has(nodeId)} + {@html $nodeGraph.thumbnails.get(nodeId)} + {/if} + - {outputTooltip(layer.primaryOutput)} - {#if layer.primaryOutput.connectedTo.length > 0} - - {#if layer.primaryOutputConnectedToLayer} - - {/if} - {:else} - + {outputTooltip(layer.output)} + 0 ? "var(--data-color)" : "var(--data-color-dim)"} /> + + {#if layer.output.connectedTo.length > 0 && layer.primaryOutputConnectedToLayer} + {/if} - {/if} - - - {#if layer.primaryInput} - {inputTooltip(layer.primaryInput)} - {/if} - {#if layer.primaryInput?.connectedTo !== "nothing"} - - {#if layer.primaryInputConnectedToLayer} - - {/if} - {:else} - - {/if} - -
- - {#if layer.exposedInputs.length > 0} -
+ - {inputTooltip(layer.exposedInputs[0])} - {#if layer.exposedInputs[0].connectedTo !== undefined} - + {#if layer.bottomInput} + {inputTooltip(layer.bottomInput)} + {/if} + {#if layer.bottomInput?.connectedToNode !== undefined} + + {#if layer.primaryInputConnectedToLayer} + + {/if} {:else} - + {/if}
- {/if} -
- - {layer.displayName} + + {#if layer.sideInput} +
+ + {inputTooltip(layer.sideInput)} + + +
+ {/if} +
+ + {nodeMetadata.displayName} +
+
+ { + /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ + }} + tooltip={nodeMetadata.visible ? "Visible" : "Hidden"} + /> + + + + + + + + +
-
- { - /* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */ - }} - tooltip={layer.visible ? "Visible" : "Hidden"} - /> - - - - - - - - - -
+ {/if} {/each} -
@@ -628,129 +634,96 @@ {/if}
- - - {#each Array.from($nodeGraph.nodesToRender).filter(([nodeId, node]) => !node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) as [nodeId, node]} - {@const exposedInputsOutputs = zipWithUndefined(node.exposedInputs, node.exposedOutputs)} - {@const clipPathId = String(Math.random()).substring(2)} - {@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined} -
- {#if node.errors} - {node.errors} - {node.errors} - {/if} - -
- - - {node.displayName} -
- - {#if exposedInputsOutputs.length > 0} -
- {#each exposedInputsOutputs as [input, output]} -
- - {input !== undefined ? input.name : output.name} - -
- {/each} + {#each Array.from($nodeGraph.nodesToRender) as [nodeId, nodeToRender]} + {#if nodeToRender.nodeOrLayer.node !== undefined && $nodeGraph.visibleNodes.has(nodeId)} + {@const nodeMetadata = nodeToRender.metadata} + {@const node = nodeToRender.nodeOrLayer.node} + {@const exposedInputsOutputs = collectExposedInputsOutputs(node.inputs, node.outputs)} + {@const clipPathId = String(Math.random()).substring(2)} + {@const description = (nodeMetadata.reference && $nodeGraph.nodeDescriptions.get(nodeMetadata.reference)) || undefined} +
+ {#if nodeMetadata.errors} + {node.errors} + {node.errors} + {/if} + +
+ + + {nodeMetadata.displayName}
- {/if} - -
- {#if node.primaryInput?.dataType} - - {inputTooltip(node.primaryInput)} - {#if node.primaryInput.connectedTo !== undefined} - - {:else} - - {/if} - + + {#if exposedInputsOutputs.length > 0} +
+ {#each exposedInputsOutputs as [input, output]} +
+ + {input?.name ?? output?.name ?? ""} + +
+ {/each} +
{/if} - {#each node.exposedInputs as secondary, index} - {#if index < node.exposedInputs.length} - - {inputTooltip(secondary)} - {#if secondary.connectedTo !== undefined} - - {:else} - - {/if} - - {/if} - {/each} -
- -
- {#if node.primaryOutput} - - {outputTooltip(node.primaryOutput)} - {#if node.primaryOutput.connectedTo !== undefined} - - {:else} - + +
+ {#each node.inputs as input} + {#if input !== undefined} + + {inputTooltip(input)} + + {/if} - - {/if} - {#each node.exposedOutputs as secondary} - - {outputTooltip(secondary)} - {#if secondary.connectedTo !== undefined} - - {:else} - + {/each} +
+ +
+ {#each node.outputs as output} + {#if output !== undefined} + + {outputTooltip(output)} + + {/if} - - {/each} + {/each} +
+ + + + + + +
- - - - - - - -
+ {/if} {/each}
@@ -1093,6 +1066,8 @@ // Keeps the connectors above the wires z-index: 1; + margin-top: -24px; + &.input { left: -3px; } diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts index 008922c329..5254514dff 100644 --- a/frontend/src/messages.ts +++ b/frontend/src/messages.ts @@ -79,7 +79,7 @@ export class UpdateLayerWidths extends JsMessage { } export class UpdateNodeGraphNodes extends JsMessage { - readonly nodesToRender!: FrontendNode[]; + readonly nodesToRender!: FrontendNodeToRender[]; readonly inSelectedNetwork!: boolean; @@ -177,13 +177,15 @@ export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic export class FrontendGraphInput { readonly dataType!: FrontendGraphDataType; + readonly resolvedType!: string; + readonly name!: string; readonly description!: string; - readonly resolvedType!: string; + readonly connectedToString!: string; - readonly connectedTo!: string; + readonly connectedToNode!: bigint | undefined; } export class FrontendGraphOutput { @@ -198,26 +200,44 @@ export class FrontendGraphOutput { readonly connectedTo!: string[]; } -export class FrontendNode { - readonly id!: bigint; - - readonly isLayer!: boolean; +export class FrontendNodeMetadata { + readonly nodeId!: bigint; readonly canBeLayer!: boolean; + readonly displayName!: string; + readonly selected!: boolean; readonly reference!: string | undefined; - readonly displayName!: string; + readonly visible!: boolean; + + // readonly wires!: (string | undefined)[]; - readonly primaryInput!: FrontendGraphInput | undefined; + readonly errors!: string | undefined; +} - readonly exposedInputs!: FrontendGraphInput[]; +export class FrontendNode { + // readonly position!: FrontendNodePosition; + readonly position!: XY; - readonly primaryOutput!: FrontendGraphOutput | undefined; + readonly inputs!: (FrontendGraphInput | undefined)[]; - readonly exposedOutputs!: FrontendGraphOutput[]; + readonly outputs!: (FrontendGraphOutput | undefined)[]; +} + +export class FrontendLayer { + // readonly position!: FrontendLayerPosition; + readonly position!: XY; + + readonly bottomInput!: FrontendGraphInput; + + readonly sideInput!: FrontendGraphInput | undefined; + + readonly output!: FrontendGraphOutput; + + readonly locked!: boolean; readonly chainWidth!: number; @@ -226,19 +246,31 @@ export class FrontendNode { readonly primaryInputConnectedToLayer!: boolean; readonly primaryOutputConnectedToLayer!: boolean; +} - @TupleToVec2 - readonly position!: XY; - - // TODO: Store field for the width of the left node chain +export class FrontendNodePosition { + readonly absolute!: XY | undefined; + readonly chain!: boolean | undefined; +} - readonly previewed!: boolean; +export class FrontendLayerPosition { + readonly absolute!: XY | undefined; + readonly stack!: number | undefined; +} - readonly visible!: boolean; +export class FrontendNodeOrLayer { + readonly node!: FrontendNode | undefined; + readonly layer!: FrontendLayer | undefined; +} - readonly unlocked!: boolean; +export class FrontendNodeToRender { + readonly metadata!: FrontendNodeMetadata; + readonly nodeOrLayer!: FrontendNodeOrLayer; +} - readonly errors!: string | undefined; +export class UpdateCentralNodeGraph extends JsMessage { + readonly nodeOrLayer!: FrontendNodeOrLayer[]; + readonly inSelectedNetwork!: boolean; } export class FrontendNodeType { diff --git a/frontend/src/state-providers/node-graph.ts b/frontend/src/state-providers/node-graph.ts index 4b27781fd4..b0c1b9e832 100644 --- a/frontend/src/state-providers/node-graph.ts +++ b/frontend/src/state-providers/node-graph.ts @@ -5,7 +5,7 @@ import { type Box, type FrontendClickTargets, type ContextMenuInformation, - type FrontendNode, + type FrontendNodeToRender, type FrontendNodeType, type WirePath, ClearAllNodeGraphWires, @@ -33,8 +33,7 @@ export function createNodeGraphState(editor: Editor) { contextMenuInformation: undefined as ContextMenuInformation | undefined, layerWidths: new Map(), updateImportsExports: undefined as UpdateImportsExports | undefined, - nodesToRender: new Map(), - + nodesToRender: new Map(), visibleNodes: new Set(), /// The index is the exposed input index. The exports have a first key value of u32::MAX. wires: new Map>(), @@ -93,6 +92,7 @@ export function createNodeGraphState(editor: Editor) { return state; }); }); + editor.subscriptions.subscribeJsMessage(UpdateLayerWidths, (updateLayerWidths) => { update((state) => { state.layerWidths = updateLayerWidths.layerWidths; @@ -103,7 +103,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;