Complete separating node rendering from imports/exports

This commit is contained in:
Adam
2025-09-01 17:39:54 -07:00
parent e2c45195ef
commit 1ab681da8c
14 changed files with 1001 additions and 1193 deletions

View File

@@ -2,10 +2,10 @@ 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, FrontendNodeToRender, FrontendNodeType, FrontendXY, Transform,
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendExports, FrontendImport, 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};
use crate::messages::portfolio::document::utility_types::wires::WirePathInProgress;
use crate::messages::prelude::*;
use crate::messages::tool::utility_types::HintData;
use graph_craft::document::NodeId;
@@ -127,9 +127,8 @@ pub enum FrontendMessage {
},
UpdateImportsExports {
/// If the primary import is not visible, then it is None.
imports: Vec<Option<FrontendGraphOutput>>,
/// If the primary export is not visible, then it is None.
exports: Vec<Option<FrontendGraphInput>>,
imports: Vec<Option<FrontendImport>>,
exports: FrontendExports,
/// The primary import location.
#[serde(rename = "importPosition")]
import_position: FrontendXY,
@@ -140,7 +139,7 @@ pub enum FrontendMessage {
#[serde(rename = "addImportExport")]
add_import_export: bool,
},
UpdateBox {
UpdateNodeGraphSelectionBox {
#[serde(rename = "box")]
box_selection: Option<BoxSelection>,
},
@@ -285,10 +284,6 @@ pub enum FrontendMessage {
UpdateVisibleNodes {
nodes: Vec<NodeId>,
},
UpdateNodeGraphWires {
wires: Vec<WirePathUpdate>,
},
ClearAllNodeGraphWires,
UpdateNodeGraphControlBarLayout {
#[serde(rename = "layoutTarget")]
layout_target: LayoutTarget,
@@ -321,8 +316,8 @@ pub enum FrontendMessage {
diff: Vec<WidgetDiff>,
},
UpdateWirePathInProgress {
#[serde(rename = "wirePath")]
wire_path: Option<WirePath>,
#[serde(rename = "wirePathInProgress")]
wire_path_in_progress: Option<WirePathInProgress>,
},
UpdateWorkingColorsLayout {
#[serde(rename = "layoutTarget")]

View File

@@ -474,7 +474,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
DocumentMessage::EnterNestedNetwork { node_id } => {
self.breadcrumb_network_path.push(node_id);
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
@@ -494,7 +493,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None });
self.node_graph_handler.wire_in_progress_from_connector = None;
self.node_graph_handler.wire_in_progress_to_connector = None;
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
} else {
responses.add(DocumentMessage::GraphViewOverlay { open: false });
}
@@ -504,7 +503,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
self.breadcrumb_network_path.pop();
self.selection_network_path.clone_from(&self.breadcrumb_network_path);
}
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
@@ -557,7 +555,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
}
}
DocumentMessage::GraphViewOverlay { open } => {
let opened = !self.graph_view_overlay_open && open;
self.graph_view_overlay_open = open;
responses.add(FrontendMessage::UpdateGraphViewOverlay { open });
@@ -567,9 +564,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(DocumentMessage::RenderRulers);
responses.add(DocumentMessage::RenderScrollbars);
if opened {
responses.add(NodeGraphMessage::UnloadWires);
}
if open {
responses.add(ToolMessage::DeactivateTools);
responses.add(OverlaysMessage::Draw); // Clear the overlays
@@ -1949,7 +1943,6 @@ impl DocumentMessageHandler {
responses.add(NodeGraphMessage::ForceRunDocumentGraph);
// TODO: Remove once the footprint is used to load the imports/export distances from the edge
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
Some(previous_network)
@@ -1981,8 +1974,6 @@ impl DocumentMessageHandler {
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(NodeGraphMessage::SelectedNodesUpdated);
responses.add(NodeGraphMessage::ForceRunDocumentGraph);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendWires);
Some(previous_network)
}

View File

@@ -139,8 +139,6 @@ pub enum NodeGraphMessage {
},
SendClickTargets,
EndSendClickTargets,
UnloadWires,
SendWires,
UpdateVisibleNodes,
SendGraph,
SetGridAlignedEdges,

View File

@@ -13,7 +13,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::{
self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing,
};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePathInProgress, build_vector_wire};
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
@@ -93,8 +93,6 @@ pub struct NodeGraphMessageHandler {
end_index: Option<usize>,
/// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend
frontend_nodes: Vec<NodeId>,
/// Used to keep track of what wires are sent to the front end so the old ones can be removed
frontend_wires: HashSet<(NodeId, usize)>,
}
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
@@ -297,7 +295,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None;
}
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
responses.add(FrontendMessage::UpdateContextMenuInformation {
context_menu_information: self.context_menu.clone(),
});
@@ -470,7 +468,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}
responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(NodeGraphMessage::SendWires);
}
NodeGraphMessage::ExposePrimaryExport { exposed } => {
let export_connector: InputConnector = InputConnector::Export(0);
@@ -761,7 +758,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: self.selection_before_pointer_down.clone(),
});
responses.add(FrontendMessage::UpdateBox { box_selection: None });
responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None });
return;
}
// Abort dragging a wire
@@ -770,7 +767,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None;
responses.add(DocumentMessage::AbortTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
return;
}
@@ -851,7 +848,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(FrontendMessage::UpdateContextMenuInformation {
context_menu_information: self.context_menu.clone(),
});
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
}
// Toggle visibility of clicked node and return
@@ -1058,14 +1055,14 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
to_connector_is_layer,
GraphWireStyle::Direct,
);
let path_string = vector_wire.to_svg();
let wire_path = WirePath {
path_string,
let wire_path = WirePathInProgress {
wire: vector_wire.to_svg(),
data_type: self.wire_in_progress_type,
thick: false,
dashed: false,
};
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: Some(wire_path) });
responses.add(FrontendMessage::UpdateWirePathInProgress {
wire_path_in_progress: Some(wire_path),
});
}
} else if let Some((drag_start, dragged)) = &mut self.drag_start {
if drag_start.start_x != point.x || drag_start.start_y != point.y {
@@ -1315,7 +1312,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
return None;
}
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
let wire = network_interface.wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
let thick = network_interface.wire_is_thick(&input, selection_network_path);
let node_bbox = kurbo::Rect::new(node_bbox[0].x, node_bbox[0].y, node_bbox[1].x, node_bbox[1].y).to_path(DEFAULT_ACCURACY);
let inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None);
@@ -1324,7 +1322,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
.segments()
.any(|segment| node_bbox.segments().filter_map(|segment| segment.as_line()).any(|line| !segment.intersect_line(line).is_empty()));
(intersect || inside).then_some((input, is_stack))
(intersect || inside).then_some((input, thick))
})
.collect::<Vec<_>>();
@@ -1391,8 +1389,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
self.reordering_export = None;
self.reordering_import = None;
responses.add(DocumentMessage::EndTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(FrontendMessage::UpdateBox { box_selection: None });
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path_in_progress: None });
responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection: None });
responses.add(FrontendMessage::UpdateImportReorderIndex { index: None });
responses.add(FrontendMessage::UpdateExportReorderIndex { index: None });
self.update_node_graph_hints(responses);
@@ -1591,17 +1589,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)),
}),
NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }),
NodeGraphMessage::UnloadWires => {
for input in network_interface.node_graph_input_connectors(breadcrumb_network_path) {
network_interface.unload_wire(&input, breadcrumb_network_path);
}
responses.add(FrontendMessage::ClearAllNodeGraphWires);
}
NodeGraphMessage::SendWires => {
let wires = self.collect_wires(network_interface, preferences.graph_wire_style, breadcrumb_network_path);
responses.add(FrontendMessage::UpdateNodeGraphWires { wires });
}
NodeGraphMessage::UpdateVisibleNodes => {
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
return;
@@ -1634,7 +1621,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::Refresh);
responses.add(NodeGraphMessage::UpdateActionButtons);
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, breadcrumb_network_path);
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, preferences.graph_wire_style, breadcrumb_network_path);
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::UpdateNodeGraphRender {
@@ -1650,7 +1637,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(FrontendMessage::UpdateLayerWidths { layer_widths });
responses.add(NodeGraphMessage::SendWires);
self.update_node_graph_hints(responses);
}
NodeGraphMessage::SetGridAlignedEdges => {
@@ -1725,8 +1711,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
Ordering::Equal => {}
}
}
responses.add(NodeGraphMessage::SendWires);
}
NodeGraphMessage::ToggleSelectedAsLayersOrNodes => {
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
@@ -1746,8 +1730,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}
NodeGraphMessage::ShiftNodePosition { node_id, x, y } => {
network_interface.shift_absolute_node_position(&node_id, IVec2::new(x, y), selection_network_path);
responses.add(NodeGraphMessage::SendWires);
}
NodeGraphMessage::SetToNodeOrLayer { node_id, is_layer } => {
if is_layer && !network_interface.is_eligible_to_be_layer(&node_id, selection_network_path) {
@@ -1761,7 +1743,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
});
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::SendGraph);
responses.add(NodeGraphMessage::SendWires);
}
NodeGraphMessage::SetDisplayName {
node_id,
@@ -1945,12 +1926,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
nodes: nodes.into_iter().collect::<Vec<_>>(),
});
}
responses.add(FrontendMessage::UpdateBox { box_selection })
responses.add(FrontendMessage::UpdateNodeGraphSelectionBox { box_selection })
}
}
NodeGraphMessage::UpdateImportsExports => {
let imports = network_interface.frontend_imports(breadcrumb_network_path);
let exports = network_interface.frontend_exports(breadcrumb_network_path);
let imports = network_interface.frontend_imports(preferences.graph_wire_style, breadcrumb_network_path);
let exports = network_interface.frontend_exports(preferences.graph_wire_style, breadcrumb_network_path);
let Some((import_position, export_position)) = network_interface.import_export_position(breadcrumb_network_path) else {
log::error!("Could not get import export positions");
@@ -1970,7 +1951,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let add_import_export = !breadcrumb_network_path.is_empty();
responses.add(NodeGraphMessage::UpdateVisibleNodes);
responses.add(NodeGraphMessage::SendWires);
responses.add(FrontendMessage::UpdateImportsExports {
imports,
exports,
@@ -2461,41 +2441,6 @@ impl NodeGraphMessageHandler {
}
}
fn collect_wires(&mut self, network_interface: &mut NodeNetworkInterface, graph_wire_style: GraphWireStyle, breadcrumb_network_path: &[NodeId]) -> Vec<WirePathUpdate> {
let mut added_wires = network_interface
.node_graph_input_connectors(breadcrumb_network_path)
.iter()
.filter_map(|connector| network_interface.newly_loaded_input_wire(connector, graph_wire_style, breadcrumb_network_path))
.collect::<Vec<_>>();
let changed_wire_inputs = added_wires.iter().map(|update| (update.id, update.input_index)).collect::<Vec<_>>();
self.frontend_wires.extend(changed_wire_inputs);
let mut orphaned_wire_inputs = self.frontend_wires.clone();
self.frontend_wires = network_interface
.node_graph_wire_inputs(breadcrumb_network_path)
.iter()
.filter_map(|visible_wire_input| orphaned_wire_inputs.take(visible_wire_input))
.collect::<HashSet<_>>();
added_wires.extend(orphaned_wire_inputs.into_iter().map(|(id, input_index)| WirePathUpdate {
id,
input_index,
wire_path_update: None,
}));
if let Some(wire_to_root) = network_interface.wire_to_root(graph_wire_style, breadcrumb_network_path) {
added_wires.push(wire_to_root);
} else {
added_wires.push(WirePathUpdate {
id: NodeId(u64::MAX),
input_index: u32::MAX as usize,
wire_path_update: None,
})
}
added_wires
}
fn collect_subgraph_names(network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Option<Vec<String>> {
let mut current_network_path = vec![];
let mut current_network = network_interface.nested_network(&current_network_path).unwrap();
@@ -2667,7 +2612,6 @@ impl Default for NodeGraphMessageHandler {
reordering_import: None,
end_index: None,
frontend_nodes: Vec::new(),
frontend_wires: HashSet::new(),
}
}
}

View File

@@ -60,10 +60,10 @@ pub struct FrontendXY {
pub struct FrontendGraphInput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
pub description: String,
#[serde(rename = "resolvedType")]
pub resolved_type: String,
pub name: String,
pub description: String,
/// Either "nothing", "import index {index}", or "{node name} output {output_index}".
#[serde(rename = "connectedToString")]
pub connected_to: String,
@@ -86,6 +86,26 @@ pub struct FrontendGraphOutput {
pub connected_to: Vec<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendExport {
pub port: FrontendGraphInput,
pub wire: Option<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendExports {
/// If the primary export is not visible, then it is None.
pub exports: Vec<Option<FrontendExport>>,
#[serde(rename = "previewWire")]
pub preview_wire: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendImport {
pub port: FrontendGraphOutput,
pub wires: Vec<String>,
}
// Metadata that is common to nodes and layers
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeMetadata {
@@ -159,6 +179,8 @@ pub struct FrontendNodeToRender {
pub metadata: FrontendNodeMetadata,
#[serde(rename = "nodeOrLayer")]
pub node_or_layer: FrontendNodeOrLayer,
//TODO: Remove
pub wires: Vec<(String, bool, FrontendGraphDataType)>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]

View File

@@ -4,9 +4,8 @@ use super::nodes::SelectedNodes;
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};
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type};
use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets, FrontendGraphDataType};
use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets};
use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::ResolvedDocumentNodeTypes;
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::tool_messages::tool_prelude::NumberInputMode;
use deserialization::deserialize_node_persistent_metadata;
@@ -20,7 +19,6 @@ use graphene_std::subpath::Subpath;
use graphene_std::transform::Footprint;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, Vector, VectorModificationType};
use kurbo::BezPath;
use serde_json::{Value, json};
use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::Hash;
@@ -684,13 +682,6 @@ impl NodeNetworkInterface {
Some(&metadata.persistent_metadata)
}
fn transient_input_metadata(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&InputTransientMetadata> {
let metadata = self
.node_metadata(node_id, network_path)
.and_then(|node_metadata| node_metadata.persistent_metadata.input_metadata.get(index))?;
Some(&metadata.transient_metadata)
}
/// Returns the input name to display in the properties panel. If the name is empty then the type is used.
pub fn displayed_input_name_and_description(&mut self, node_id: &NodeId, input_index: usize, network_path: &[NodeId]) -> (String, String) {
let Some(input_metadata) = self.persistent_input_metadata(node_id, input_index, network_path) else {
@@ -1538,30 +1529,6 @@ impl NodeNetworkInterface {
return;
};
network_metadata.transient_metadata.import_export_ports.unload();
// Always unload all wires connected to them as well
let number_of_imports = self.number_of_imports(network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in remove_import");
return;
};
let mut input_connectors = Vec::new();
for import_index in 0..number_of_imports {
let Some(outward_wires_for_import) = outward_wires.get(&OutputConnector::Import(import_index)).cloned() else {
log::error!("Could not get outward wires for import in remove_import");
return;
};
input_connectors.extend(outward_wires_for_import);
}
let Some(network) = self.nested_network(network_path) else {
return;
};
for export_index in 0..network.exports.len() {
input_connectors.push(InputConnector::Export(export_index));
}
for input in &input_connectors {
self.unload_wire(input, network_path);
}
}
pub fn modify_import_export(&mut self, network_path: &[NodeId]) -> Option<&ModifyImportExportClickTarget> {
@@ -1980,99 +1947,6 @@ impl NodeNetworkInterface {
.find_map(|(input_index, click_target)| if index == input_index { click_target.bounding_box_center() } else { None })
}
pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
if !self.wire_is_loaded(input, network_path) {
self.load_wire(input, graph_wire_style, network_path);
} else {
return None;
}
let wire = match input {
InputConnector::Node { node_id, input_index } => {
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
InputConnector::Export(export_index) => {
let network_metadata = self.network_metadata(network_path)?;
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
}
};
Some(wire)
}
pub fn wire_is_loaded(&mut self, input: &InputConnector, network_path: &[NodeId]) -> bool {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(input_metadata) = self.transient_input_metadata(node_id, *input_index, network_path) else {
log::error!("Input metadata should always exist for input");
return false;
};
input_metadata.wire.is_loaded()
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata(network_path) else {
return false;
};
match network_metadata.transient_metadata.wires.get(*export_index) {
Some(wire) => wire.is_loaded(),
None => false,
}
}
}
}
fn load_wire(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) {
let dashed = match self.previewing(network_path) {
Previewing::Yes { .. } => match input {
InputConnector::Node { .. } => false,
InputConnector::Export(export_index) => *export_index == 0,
},
Previewing::No => false,
};
let Some(wire) = self.wire_path_from_input(input, graph_wire_style, dashed, network_path) else {
log::error!("Could not load wire path from input");
return;
};
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return };
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
let wire_update = WirePathUpdate {
id: *node_id,
input_index: *input_index,
wire_path_update: Some(wire),
};
input_metadata.transient_metadata.wire = TransientMetadata::Loaded(wire_update);
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else { return };
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
let wire_update = WirePathUpdate {
id: NodeId(u64::MAX),
input_index: *export_index,
wire_path_update: Some(wire),
};
*input_metadata = TransientMetadata::Loaded(wire_update);
}
}
}
pub fn all_input_connectors(&self, network_path: &[NodeId]) -> Vec<InputConnector> {
let mut input_connectors = Vec::new();
let Some(network) = self.nested_network(network_path) else {
@@ -2097,141 +1971,6 @@ impl NodeNetworkInterface {
.collect()
}
/// Maps to the frontend representation of a wire start. Includes disconnected value wire inputs.
pub fn node_graph_wire_inputs(&self, network_path: &[NodeId]) -> Vec<(NodeId, usize)> {
self.node_graph_input_connectors(network_path)
.iter()
.map(|input| match input {
InputConnector::Node { node_id, input_index } => (*node_id, *input_index),
InputConnector::Export(export_index) => (NodeId(u64::MAX), *export_index),
})
.chain(std::iter::once((NodeId(u64::MAX), u32::MAX as usize)))
.collect()
}
fn unload_wires_for_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
let number_of_outputs = self.number_of_outputs(node_id, network_path);
let Some(outward_wires) = self.outward_wires(network_path) else {
log::error!("Could not get outward wires in reorder_export");
return;
};
let mut input_connectors = Vec::new();
for output_index in 0..number_of_outputs {
let Some(inputs) = outward_wires.get(&OutputConnector::node(*node_id, output_index)) else {
continue;
};
input_connectors.extend(inputs.clone())
}
for input_index in 0..self.number_of_inputs(node_id, network_path) {
input_connectors.push(InputConnector::node(*node_id, input_index));
}
for input in input_connectors {
self.unload_wire(&input, network_path);
}
}
pub fn unload_wire(&mut self, input: &InputConnector, network_path: &[NodeId]) {
match input {
InputConnector::Node { node_id, input_index } => {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
return;
};
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
// log::warn!("Node metadata must exist on node: {input:?}");
return;
};
input_metadata.transient_metadata.wire = TransientMetadata::Unloaded;
}
InputConnector::Export(export_index) => {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
return;
};
if *export_index >= network_metadata.transient_metadata.wires.len() {
network_metadata.transient_metadata.wires.resize(export_index + 1, TransientMetadata::Unloaded);
}
let Some(input_metadata) = network_metadata.transient_metadata.wires.get_mut(*export_index) else {
return;
};
*input_metadata = TransientMetadata::Unloaded;
}
}
}
/// When previewing, there may be a second path to the root node.
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<WirePathUpdate> {
let input = InputConnector::Export(0);
let current_export = self.upstream_output_connector(&input, network_path)?;
let root_node = match self.previewing(network_path) {
Previewing::Yes { root_node_to_restore } => root_node_to_restore,
Previewing::No => None,
}?;
if Some(root_node.node_id) == current_export.node_id() {
return None;
}
let Some(input_position) = self.get_input_center(&input, network_path) else {
log::error!("Could not get input position for wire end in root node: {input:?}");
return None;
};
let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index);
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output position for wire start in root node: {upstream_output:?}");
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start: bool = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
let path_string = vector_wire.to_svg();
let data_type = FrontendGraphDataType::displayed_type(&self.input_type(&input, network_path));
let wire_path_update = Some(WirePath {
path_string,
data_type,
thick,
dashed: false,
});
Some(WirePathUpdate {
id: NodeId(u64::MAX),
input_index: u32::MAX as usize,
wire_path_update,
})
}
/// Returns the vector subpath and a boolean of whether the wire should be thick.
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, bool)> {
let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
};
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return Some((BezPath::new(), false));
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output port for wire start: {:?}", upstream_output);
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let thick = vertical_end && vertical_start;
Some((build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style), thick))
}
pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> {
let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
let path_string = vector_wire.to_svg();
let data_type = FrontendGraphDataType::displayed_type(&self.input_type(input, network_path));
Some(WirePath {
path_string,
data_type,
thick,
dashed,
})
}
pub fn node_click_targets(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&DocumentNodeClickTargets> {
self.try_load_node_click_targets(node_id, network_path);
self.try_get_node_click_targets(node_id, network_path)
@@ -2469,7 +2208,6 @@ impl NodeNetworkInterface {
return;
};
node_metadata.transient_metadata.click_targets.unload();
self.unload_wires_for_node(node_id, network_path);
}
pub fn unload_upstream_node_click_targets(&mut self, node_ids: Vec<NodeId>, network_path: &[NodeId]) {
@@ -3792,17 +3530,14 @@ impl NodeNetworkInterface {
// If a connection is made to the imports
(NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }, NodeInput::Network { .. }) => {
self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
}
// If a connection to the imports is disconnected
(NodeInput::Network { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
}
// If a node is disconnected.
(NodeInput::Node { .. }, NodeInput::Value { .. } | NodeInput::Scope { .. } | NodeInput::Inline { .. }) => {
self.unload_outward_wires(network_path);
self.unload_wire(input_connector, network_path);
if let Some((old_upstream_node_id, previous_position)) = previous_metadata {
let old_upstream_node_is_layer = self.is_layer(&old_upstream_node_id, network_path);
@@ -5848,9 +5583,6 @@ pub struct NodeNetworkTransientMetadata {
pub modify_import_export: TransientMetadata<ModifyImportExportClickTarget>,
// Distance to the edges of the network, where the import/export ports are displayed. Rounded to nearest grid space when the panning ends.
pub rounded_network_edge_distance: TransientMetadata<NetworkEdgeDistance>,
// Wires from the exports
pub wires: Vec<TransientMetadata<WirePathUpdate>>,
}
#[derive(Debug, Clone)]
@@ -6029,12 +5761,11 @@ impl InputPersistentMetadata {
}
}
#[derive(Debug, Clone, Default)]
struct InputTransientMetadata {
wire: TransientMetadata<WirePathUpdate>,
// downstream_protonode: populated for all inputs after each compile
// types: populated for each protonode after each
}
// #[derive(Debug, Clone, Default)]
// struct InputTransientMetadata {
// // downstream_protonode: populated for all inputs after each compile
// // types: populated for each protonode after each
// }
/// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node.
#[derive(Default, Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
@@ -6073,15 +5804,15 @@ impl DocumentNodePersistentMetadata {
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct InputMetadata {
pub persistent_metadata: InputPersistentMetadata,
#[serde(skip)]
transient_metadata: InputTransientMetadata,
// #[serde(skip)]
// transient_metadata: InputTransientMetadata,
}
impl Clone for InputMetadata {
fn clone(&self) -> Self {
InputMetadata {
persistent_metadata: self.persistent_metadata.clone(),
transient_metadata: Default::default(),
// transient_metadata: Default::default(),
}
}
}

View File

@@ -1,20 +1,25 @@
use glam::{DVec2, IVec2};
use graph_craft::proto::GraphErrors;
use graphene_std::uuid::NodeId;
use kurbo::BezPath;
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, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer, FrontendNodeToRender, FrontendXY,
FrontendExport, FrontendExports, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendImport, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer,
FrontendNodeToRender, FrontendXY,
},
utility_types::{
network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing},
wires::{GraphWireStyle, build_vector_wire},
},
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<FrontendNodeToRender> {
pub fn collect_nodes(&mut self, node_graph_errors: &GraphErrors, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Vec<FrontendNodeToRender> {
let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get nested network when collecting nodes");
return Vec::new();
@@ -99,7 +104,25 @@ impl NodeNetworkInterface {
}
};
let frontend_node_to_render = FrontendNodeToRender { metadata, node_or_layer };
let wires = (0..self.number_of_displayed_inputs(&node_id, network_path))
.filter_map(|input_index| {
self.wire_from_input(&InputConnector::node(node_id, input_index), wire_style, network_path)
.filter(|_| {
self.upstream_output_connector(&InputConnector::node(node_id, input_index), network_path)
.is_some_and(|output| !matches!(output, OutputConnector::Import(_)))
})
.map(|path| path.to_svg())
.map(|wire| {
(
wire,
self.wire_is_thick(&InputConnector::node(node_id, input_index), network_path),
FrontendGraphDataType::displayed_type(&self.input_type(&InputConnector::node(node_id, input_index), network_path)),
)
})
})
.collect();
let frontend_node_to_render = FrontendNodeToRender { metadata, node_or_layer, wires };
nodes.push(frontend_node_to_render);
}
@@ -318,16 +341,35 @@ impl NodeNetworkInterface {
.is_some_and(|node_id| self.is_layer(&node_id, network_path))
}
pub fn frontend_imports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphOutput>> {
/// The imports contain both the output port and the outward wires
pub fn frontend_imports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Vec<Option<FrontendImport>> {
match network_path.split_last() {
Some((node_id, encapsulatingnetwork_path)) => {
let Some(node) = self.document_node(node_id, encapsulatingnetwork_path) else {
log::error!("Could not get node {node_id} in network {encapsulatingnetwork_path:?}");
Some((node_id, encapsulating_network_path)) => {
let Some(node) = self.document_node(node_id, encapsulating_network_path) else {
log::error!("Could not get node {node_id} in network {encapsulating_network_path:?}");
return Vec::new();
};
let mut frontend_imports = (0..node.inputs.len())
.map(|import_index| self.frontend_output_from_connector(&OutputConnector::Import(import_index), network_path))
.map(|import_index| {
let port = self.frontend_output_from_connector(&OutputConnector::Import(import_index), network_path);
port.and_then(|port| {
let outward_wires = self.outward_wires(network_path)?;
let downstream_inputs = outward_wires.get(&OutputConnector::Import(import_index)).cloned()?;
let wires = downstream_inputs
.iter()
.filter_map(|input_connector| {
let Some(wire) = self.wire_from_input(&input_connector, graph_wire_style, network_path) else {
log::error!("Could not get wire path for import input: {input_connector:?}");
return None;
};
Some(wire.to_svg())
})
.collect::<Vec<_>>();
Some(FrontendImport { port, wires })
})
})
.collect::<Vec<_>>();
if frontend_imports.is_empty() {
frontend_imports.push(None);
}
@@ -338,13 +380,29 @@ impl NodeNetworkInterface {
}
}
pub fn frontend_exports(&mut self, network_path: &[NodeId]) -> Vec<Option<FrontendGraphInput>> {
let Some(network) = self.nested_network(network_path) else { return Vec::new() };
let mut frontend_exports = ((0..network.exports.len()).map(|export_index| self.frontend_input_from_connector(&InputConnector::Export(export_index), network_path))).collect::<Vec<_>>();
if frontend_exports.is_empty() {
frontend_exports.push(None);
/// The imports contain the export port, the outward wires, and the preview wire if it exists
pub fn frontend_exports(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> FrontendExports {
let Some(network) = self.nested_network(network_path) else {
log::error!("Could not get nested network in frontend exports");
return FrontendExports::default();
};
let mut exports = (0..network.exports.len())
.map(|export_index| {
let export_connector = InputConnector::Export(export_index);
let frontend_export = self.frontend_input_from_connector(&export_connector, network_path);
frontend_export.and_then(|export| {
let wire = self.wire_from_input(&export_connector, graph_wire_style, network_path).map(|path| path.to_svg());
Some(FrontendExport { port: export, wire })
})
})
.collect::<Vec<_>>();
if exports.is_empty() {
exports.push(None);
}
frontend_exports
let preview_wire = self.wire_to_root(graph_wire_style, network_path);
FrontendExports { exports, preview_wire }
}
pub fn import_export_position(&mut self, network_path: &[NodeId]) -> Option<(IVec2, IVec2)> {
@@ -424,4 +482,60 @@ impl NodeNetworkInterface {
Some((rounded_import_top_left.as_ivec2(), rounded_export_top_right.as_ivec2()))
}
pub fn wire_is_thick(&self, input: &InputConnector, network_path: &[NodeId]) -> bool {
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return false;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
vertical_end && vertical_start
}
/// Returns the vector subpath and a boolean of whether the wire should be thick.
pub fn wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<BezPath> {
let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
};
// An upstream output could not be found
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
return None;
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output port for wire start: {:?}", upstream_output);
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
Some(build_vector_wire(output_position, input_position, vertical_start, vertical_end, wire_style))
}
/// When previewing, there may be a second path to the root node.
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<String> {
let input = InputConnector::Export(0);
let current_export = self.upstream_output_connector(&input, network_path)?;
let root_node = match self.previewing(network_path) {
Previewing::Yes { root_node_to_restore } => root_node_to_restore,
Previewing::No => None,
}?;
if Some(root_node.node_id) == current_export.node_id() {
return None;
}
let Some(input_position) = self.get_input_center(&input, network_path) else {
log::error!("Could not get input position for wire end in root node: {input:?}");
return None;
};
let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index);
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get output position for wire start in root node: {upstream_output:?}");
return None;
};
let vertical_start = upstream_output.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path));
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, false, graph_wire_style);
Some(vector_wire.to_svg())
}
}

View File

@@ -1,26 +1,14 @@
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use glam::{DVec2, IVec2};
use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point};
use graphene_std::vector::misc::dvec2_to_point;
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WirePath {
#[serde(rename = "pathString")]
pub path_string: String,
pub struct WirePathInProgress {
pub wire: String,
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub thick: bool,
pub dashed: bool,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WirePathUpdate {
pub id: NodeId,
#[serde(rename = "inputIndex")]
pub input_index: usize,
// If none, then remove the wire from the map
#[serde(rename = "wirePathUpdate")]
pub wire_path_update: Option<WirePath>,
}
#[derive(Copy, Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]

View File

@@ -909,8 +909,6 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
if node_graph_open {
responses.add(NodeGraphMessage::UpdateGraphBarRight);
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendWires)
} else {
responses.add(PortfolioMessage::UpdateDocumentWidgets);
}

View File

@@ -87,8 +87,7 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
}
PreferencesMessage::GraphWireStyle { style } => {
self.graph_wire_style = style;
responses.add(NodeGraphMessage::UnloadWires);
responses.add(NodeGraphMessage::SendWires);
responses.add(NodeGraphMessage::SendGraph);
}
PreferencesMessage::ViewportZoomWheelRate { rate } => {
self.viewport_zoom_wheel_rate = rate;

View File

@@ -564,8 +564,9 @@
</canvas>
{/if}
</div>
<Graph />
<div class="graph-view" class:open={$document.graphViewOverlayOpen} data-graph>
<Graph />
</div>
</LayoutCol>
<LayoutCol class="ruler-or-scrollbar right-scrollbar">
<ScrollbarInput
@@ -823,6 +824,18 @@
}
}
.graph-view {
pointer-events: none;
transition: opacity 0.2s;
opacity: 0;
&.open {
cursor: auto;
pointer-events: auto;
opacity: 1;
}
}
.fade-artwork,
.graph {
position: absolute;

File diff suppressed because it is too large Load Diff

View File

@@ -26,13 +26,37 @@ export type XY = { x: number; y: number };
// ============================================================================
export class UpdateBox extends JsMessage {
readonly box!: Box | undefined;
readonly box!: FrontendSelectionBox | undefined;
}
export class FrontendClickTargets {
readonly nodeClickTargets!: string[];
readonly layerClickTargets!: string[];
readonly connectorClickTargets!: string[];
readonly iconClickTargets!: string[];
readonly allNodesBoundingBox!: string;
readonly importExportsBoundingBox!: string;
readonly modifyImportExport!: string[];
}
export class UpdateClickTargets extends JsMessage {
readonly clickTargets!: FrontendClickTargets | undefined;
}
export class FrontendSelectionBox {
readonly startX!: number;
readonly startY!: number;
readonly endX!: number;
readonly endY!: number;
}
export class UpdateNodeGraphSelectionBox extends JsMessage {
readonly box!: FrontendSelectionBox | undefined;
}
const ContextTupleToVec2 = Transform((data) => {
if (data.obj.contextMenuInformation === undefined) return undefined;
const contextMenuCoordinates = { x: data.obj.contextMenuInformation.contextMenuCoordinates[0], y: data.obj.contextMenuInformation.contextMenuCoordinates[1] };
@@ -45,15 +69,20 @@ const ContextTupleToVec2 = Transform((data) => {
return { contextMenuCoordinates, contextMenuData };
});
export class ContextMenuInformation {
readonly contextMenuCoordinates!: XY;
readonly contextMenuData!: "CreateNode" | { type: "CreateNode"; compatibleType: string } | { nodeId: bigint; currentlyIsNode: boolean };
}
export class UpdateContextMenuInformation extends JsMessage {
@ContextTupleToVec2
readonly contextMenuInformation!: ContextMenuInformation | undefined;
}
export class UpdateImportsExports extends JsMessage {
readonly imports!: (FrontendGraphOutput | undefined)[];
readonly imports!: (FrontendImport | undefined)[];
readonly exports!: (FrontendGraphInput | undefined)[];
readonly exports!: FrontendExports;
readonly importPosition!: XY;
@@ -93,12 +122,6 @@ export class UpdateVisibleNodes extends JsMessage {
readonly nodes!: bigint[];
}
export class UpdateNodeGraphWires extends JsMessage {
readonly wires!: WireUpdate[];
}
export class ClearAllNodeGraphWires extends JsMessage {}
export class UpdateNodeGraphTransform extends JsMessage {
readonly transform!: NodeGraphTransform;
}
@@ -123,8 +146,14 @@ export class UpdateOpenDocumentsList extends JsMessage {
readonly openDocuments!: OpenDocument[];
}
export class WirePathInProgress {
readonly wire!: string;
readonly thick!: boolean;
readonly dataType!: FrontendGraphDataType;
}
export class UpdateWirePathInProgress extends JsMessage {
readonly wirePath!: WirePath | undefined;
readonly wirePathInProgress!: WirePathInProgress | undefined;
}
export class OpenDocument {
@@ -149,6 +178,7 @@ export class DocumentDetails {
}
}
<<<<<<< HEAD
export class Box {
readonly startX!: number;
@@ -174,6 +204,12 @@ export type ContextMenuInformation = {
contextMenuData: "CreateNode" | { type: "CreateNode"; compatibleType: string } | { nodeId: bigint; currentlyIsNode: boolean };
};
=======
export class FrontendDocumentDetails extends DocumentDetails {
readonly id!: bigint;
}
>>>>>>> 17a1a3d5 (Complete separating node rendering from imports/exports)
export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color";
export class FrontendGraphInput {
@@ -202,6 +238,21 @@ export class FrontendGraphOutput {
readonly connectedTo!: string[];
}
export class FrontendExport {
readonly port!: FrontendGraphInput;
readonly wire!: string | undefined;
}
export class FrontendExports {
readonly exports!: (FrontendExport | undefined)[];
readonly previewWire!: string | undefined;
}
export class FrontendImport {
readonly port!: FrontendGraphOutput;
readonly wires!: string[];
}
export class FrontendNodeMetadata {
readonly nodeId!: bigint;
@@ -268,6 +319,8 @@ export class FrontendNodeOrLayer {
export class FrontendNodeToRender {
readonly metadata!: FrontendNodeMetadata;
readonly nodeOrLayer!: FrontendNodeOrLayer;
// TODO: Remove
readonly wires!: [string, boolean, FrontendGraphDataType][];
}
export class UpdateCentralNodeGraph extends JsMessage {
@@ -1647,7 +1700,6 @@ type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHand
type MessageMaker = typeof JsMessage | JSMessageFactory;
export const messageMakers: Record<string, MessageMaker> = {
ClearAllNodeGraphWires,
DisplayDialog,
DisplayDialogDismiss,
DisplayDialogPanic,
@@ -1676,7 +1728,6 @@ export const messageMakers: Record<string, MessageMaker> = {
TriggerTextCopy,
TriggerVisitLink,
UpdateActiveDocument,
UpdateBox,
UpdateClickTargets,
UpdateContextMenuInformation,
UpdateDialogButtons,
@@ -1704,8 +1755,8 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateMouseCursor,
UpdateNodeGraphControlBarLayout,
UpdateNodeGraphRender,
UpdateNodeGraphSelectionBox,
UpdateNodeGraphTransform,
UpdateNodeGraphWires,
UpdateNodeThumbnail,
UpdateOpenDocumentsList,
UpdatePlatform,

View File

@@ -2,15 +2,13 @@ import { writable } from "svelte/store";
import { type Editor } from "@graphite/editor";
import {
type Box,
type FrontendSelectionBox,
type FrontendClickTargets,
type ContextMenuInformation,
type FrontendNodeToRender,
type FrontendNodeType,
type WirePath,
ClearAllNodeGraphWires,
type WirePathInProgress,
SendUIMetadata,
UpdateBox,
UpdateClickTargets,
UpdateContextMenuInformation,
UpdateImportReorderIndex,
@@ -19,35 +17,39 @@ import {
UpdateLayerWidths,
UpdateNodeGraphRender,
UpdateVisibleNodes,
UpdateNodeGraphWires,
UpdateNodeGraphTransform,
UpdateNodeThumbnail,
UpdateWirePathInProgress,
UpdateNodeGraphSelectionBox,
} from "@graphite/messages";
export function createNodeGraphState(editor: Editor) {
const { subscribe, update } = writable({
box: undefined as Box | undefined,
// Data that will continue to be rendered in Svelte for now
selectionBox: undefined as FrontendSelectionBox | undefined,
clickTargets: undefined as FrontendClickTargets | undefined,
contextMenuInformation: undefined as ContextMenuInformation | undefined,
layerWidths: new Map<bigint, number>(),
wirePathInProgress: undefined as WirePathInProgress | undefined,
updateImportsExports: undefined as UpdateImportsExports | undefined,
nodesToRender: new Map<bigint, FrontendNodeToRender>(),
open: false,
opacity: 0.8,
visibleNodes: new Set<bigint>(),
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
wires: new Map<bigint, Map<number, WirePath>>(),
wirePathInProgress: undefined as WirePath | undefined,
nodeDescriptions: new Map<string, string>(),
nodeTypes: [] as FrontendNodeType[],
thumbnails: new Map<bigint, string>(),
transform: { scale: 1, x: 0, y: 0 },
inSelectedNetwork: true,
previewedNode: undefined as bigint | undefined,
reorderImportIndex: undefined as number | undefined,
reorderExportIndex: undefined as number | undefined,
contextMenuInformation: undefined as ContextMenuInformation | undefined,
nodeTypes: [] as FrontendNodeType[],
nodeDescriptions: new Map<string, string>(),
// Data that will be moved into the node graph to be rendered natively
nodesToRender: new Map<bigint, FrontendNodeToRender>(),
opacity: 0.8,
inSelectedNetwork: true,
previewedNode: undefined as bigint | undefined,
// TODO: Remove these fields
visibleNodes: new Set<bigint>(),
layerWidths: new Map<bigint, number>(),
// Data that will be passed in the context
thumbnails: new Map<bigint, string>(),
transform: { scale: 1, x: 0, y: 0 },
});
// Set up message subscriptions on creation
@@ -58,15 +60,21 @@ export function createNodeGraphState(editor: Editor) {
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateBox, (updateBox) => {
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSelectionBox, (updateBox) => {
update((state) => {
state.box = updateBox.box;
state.selectionBox = updateBox.box;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (UpdateClickTargets) => {
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (updateClickTargets) => {
update((state) => {
state.clickTargets = UpdateClickTargets.clickTargets;
state.clickTargets = updateClickTargets.clickTargets;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => {
update((state) => {
state.wirePathInProgress = updateWirePathInProgress.wirePathInProgress;
return state;
});
});
@@ -107,7 +115,6 @@ export function createNodeGraphState(editor: Editor) {
updateNodeGraphRender.nodesToRender.forEach((node) => {
state.nodesToRender.set(node.metadata.nodeId, node);
});
state.open = updateNodeGraphRender.open;
state.opacity = updateNodeGraphRender.opacity;
state.inSelectedNetwork = updateNodeGraphRender.inSelectedNetwork;
state.previewedNode = updateNodeGraphRender.previewedNode;
@@ -120,30 +127,6 @@ export function createNodeGraphState(editor: Editor) {
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphWires, (updateNodeWires) => {
update((state) => {
updateNodeWires.wires.forEach((wireUpdate) => {
let inputMap = state.wires.get(wireUpdate.id);
// If it doesn't exist, create it and set it in the outer map
if (!inputMap) {
inputMap = new Map();
state.wires.set(wireUpdate.id, inputMap);
}
if (wireUpdate.wirePathUpdate !== undefined) {
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
} else {
inputMap.delete(wireUpdate.inputIndex);
}
});
return state;
});
});
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWires, (_) => {
update((state) => {
state.wires.clear();
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphTransform, (updateNodeGraphTransform) => {
update((state) => {
state.transform = updateNodeGraphTransform.transform;
@@ -158,7 +141,7 @@ export function createNodeGraphState(editor: Editor) {
});
editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => {
update((state) => {
state.wirePathInProgress = updateWirePathInProgress.wirePath;
state.wirePathInProgress = updateWirePathInProgress.wirePathInProgress;
return state;
});
});