Add icons

This commit is contained in:
Adam
2025-09-08 19:37:26 -07:00
parent 3566fb8c10
commit cb1fb756b1
19 changed files with 166 additions and 332 deletions
@@ -7,7 +7,7 @@ use crate::messages::portfolio::document::utility_types::wires::WirePathInProgre
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::utility_types::HintData; use crate::messages::tool::utility_types::HintData;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graphene_std::node_graph_overlay::types::{FrontendExports, FrontendImport, FrontendNodeToRender, FrontendXY, NodeGraphTransform}; use graphene_std::node_graph_overlay::types::{FrontendExports, FrontendImport, FrontendXY, NodeGraphTransform};
use graphene_std::raster::Image; use graphene_std::raster::Image;
use graphene_std::raster::color::Color; use graphene_std::raster::color::Color;
use graphene_std::text::{Font, TextAlign}; use graphene_std::text::{Font, TextAlign};
@@ -270,17 +270,10 @@ pub enum FrontendMessage {
cursor: MouseCursorIcon, cursor: MouseCursorIcon,
}, },
RequestNativeNodeGraphRender, RequestNativeNodeGraphRender,
UpdateShouldRenderSvelteNodes {
#[serde(rename = "shouldRenderSvelteNodes")]
should_render_svelte_nodes: bool,
},
UpdateNativeNodeGraphSVG { UpdateNativeNodeGraphSVG {
#[serde(rename = "svgString")] #[serde(rename = "svgString")]
svg_string: String, svg_string: String,
}, },
UpdateVisibleNodes {
nodes: Vec<NodeId>,
},
UpdateNodeGraphControlBarLayout { UpdateNodeGraphControlBarLayout {
#[serde(rename = "layoutTarget")] #[serde(rename = "layoutTarget")]
layout_target: LayoutTarget, layout_target: LayoutTarget,
@@ -34,7 +34,6 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> f
self.viewport_bounds = bounds; self.viewport_bounds = bounds;
responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO }); responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO });
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
responses.add(DeferMessage::AfterGraphRun { responses.add(DeferMessage::AfterGraphRun {
messages: vec![ messages: vec![
@@ -476,7 +476,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
self.selection_network_path.clone_from(&self.breadcrumb_network_path); self.selection_network_path.clone_from(&self.breadcrumb_network_path);
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::ZoomCanvasToFitAll); responses.add(DocumentMessage::ZoomCanvasToFitAll);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
DocumentMessage::Escape => { DocumentMessage::Escape => {
if self.node_graph_handler.drag_start.is_some() { if self.node_graph_handler.drag_start.is_some() {
@@ -505,7 +504,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
} }
responses.add(NodeGraphMessage::SendGraph); responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::PTZUpdate); responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
DocumentMessage::FlipSelectedLayers { flip_axis } => { DocumentMessage::FlipSelectedLayers { flip_axis } => {
let scale = match flip_axis { let scale = match flip_axis {
@@ -568,7 +566,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(ToolMessage::DeactivateTools); responses.add(ToolMessage::DeactivateTools);
responses.add(OverlaysMessage::Draw); // Clear the overlays responses.add(OverlaysMessage::Draw); // Clear the overlays
responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. }); responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. });
responses.add(NodeGraphMessage::SetGridAlignedEdges);
responses.add(NodeGraphMessage::UpdateGraphBarRight); responses.add(NodeGraphMessage::UpdateGraphBarRight);
responses.add(NodeGraphMessage::UpdateHints); responses.add(NodeGraphMessage::UpdateHints);
} else { } else {
@@ -1489,10 +1486,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderRulers);
responses.add(DocumentMessage::RenderScrollbars); responses.add(DocumentMessage::RenderScrollbars);
responses.add(NodeGraphMessage::UpdateEdges);
responses.add(NodeGraphMessage::UpdateBoxSelection); responses.add(NodeGraphMessage::UpdateBoxSelection);
responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(FrontendMessage::UpdateNodeGraphTransform { responses.add(FrontendMessage::UpdateNodeGraphTransform {
transform: NodeGraphTransform { transform: NodeGraphTransform {
scale: transform.matrix2.x_axis.x, scale: transform.matrix2.x_axis.x,
@@ -1942,9 +1937,6 @@ impl DocumentMessageHandler {
responses.add(NodeGraphMessage::SelectedNodesUpdated); responses.add(NodeGraphMessage::SelectedNodesUpdated);
responses.add(NodeGraphMessage::ForceRunDocumentGraph); responses.add(NodeGraphMessage::ForceRunDocumentGraph);
// TODO: Remove once the footprint is used to load the imports/export distances from the edge
responses.add(NodeGraphMessage::SetGridAlignedEdges);
Some(previous_network) Some(previous_network)
} }
pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) { pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
@@ -178,7 +178,6 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => { NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => {
let delta = if use_y_as_x { (-ipp.mouse.scroll_delta.y, 0.).into() } else { -ipp.mouse.scroll_delta.as_dvec2() } * VIEWPORT_SCROLL_RATE; let delta = if use_y_as_x { (-ipp.mouse.scroll_delta.y, 0.).into() } else { -ipp.mouse.scroll_delta.as_dvec2() } * VIEWPORT_SCROLL_RATE;
responses.add(NavigationMessage::CanvasPan { delta }); responses.add(NavigationMessage::CanvasPan { delta });
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
NavigationMessage::CanvasTiltResetAndZoomTo100Percent => { NavigationMessage::CanvasTiltResetAndZoomTo100Percent => {
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
@@ -193,7 +192,6 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
responses.add(DocumentMessage::PTZUpdate); responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
NavigationMessage::CanvasTiltSet { angle_radians } => { NavigationMessage::CanvasTiltSet { angle_radians } => {
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
@@ -272,7 +270,6 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
responses.add(DocumentMessage::PTZUpdate); responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
NavigationMessage::CanvasFlip => { NavigationMessage::CanvasFlip => {
if graph_view_overlay_open { if graph_view_overlay_open {
@@ -320,7 +317,6 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
} else { } else {
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
responses.add(NodeGraphMessage::SetGridAlignedEdges);
// Reset the navigation operation now that it's done // Reset the navigation operation now that it's done
self.navigation_operation = NavigationOperation::None; self.navigation_operation = NavigationOperation::None;
@@ -382,7 +378,6 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
responses.add(PortfolioMessage::UpdateDocumentWidgets); responses.add(PortfolioMessage::UpdateDocumentWidgets);
} }
responses.add(DocumentMessage::PTZUpdate); responses.add(DocumentMessage::PTZUpdate);
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
// Fully zooms in on the selected // Fully zooms in on the selected
NavigationMessage::FitViewportToSelection => { NavigationMessage::FitViewportToSelection => {
@@ -476,7 +471,6 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
}; };
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom() }); responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom() });
responses.add(NodeGraphMessage::SetGridAlignedEdges);
} }
} }
@@ -139,9 +139,7 @@ pub enum NodeGraphMessage {
}, },
SendClickTargets, SendClickTargets,
EndSendClickTargets, EndSendClickTargets,
UpdateVisibleNodes,
SendGraph, SendGraph,
SetGridAlignedEdges,
SetInputValue { SetInputValue {
node_id: NodeId, node_id: NodeId,
input_index: usize, input_index: usize,
@@ -183,7 +181,6 @@ pub enum NodeGraphMessage {
TogglePreviewImpl { TogglePreviewImpl {
node_id: NodeId, node_id: NodeId,
}, },
ToggleNativeNodeGraphRender,
SetImportExportName { SetImportExportName {
name: String, name: String,
index: ImportOrExport, index: ImportOrExport,
@@ -217,7 +214,6 @@ pub enum NodeGraphMessage {
SetLockedOrVisibilitySideEffects { SetLockedOrVisibilitySideEffects {
node_ids: Vec<NodeId>, node_ids: Vec<NodeId>,
}, },
UpdateEdges,
UpdateBoxSelection, UpdateBoxSelection,
UpdateImportsExports, UpdateImportsExports,
UpdateLayerPanel, UpdateLayerPanel,
@@ -92,8 +92,6 @@ pub struct NodeGraphMessageHandler {
reordering_export: Option<usize>, reordering_export: Option<usize>,
/// The end index of the moved connector /// The end index of the moved connector
end_index: Option<usize>, 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>,
} }
/// 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. /// 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.
@@ -1590,61 +1588,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)), click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)),
}), }),
NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }), NodeGraphMessage::EndSendClickTargets => responses.add(FrontendMessage::UpdateClickTargets { click_targets: None }),
NodeGraphMessage::UpdateVisibleNodes => {
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
return;
};
let viewport_bbox = ipp.document_bounds();
let document_bbox: [DVec2; 2] = viewport_bbox.map(|p| network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_point2(p));
let mut nodes = Vec::new();
for node_id in &self.frontend_nodes {
let Some(node_bbox) = network_interface.node_bounding_box(node_id, breadcrumb_network_path) else {
log::error!("Could not get bbox for node: {node_id:?}");
continue;
};
if node_bbox[1].x >= document_bbox[0].x && node_bbox[0].x <= document_bbox[1].x && node_bbox[1].y >= document_bbox[0].y && node_bbox[0].y <= document_bbox[1].y {
nodes.push(*node_id);
}
for error in &self.node_graph_errors {
if error.node_path.contains(node_id) {
nodes.push(*node_id);
}
}
}
responses.add(FrontendMessage::UpdateVisibleNodes { nodes });
}
NodeGraphMessage::SendGraph => { NodeGraphMessage::SendGraph => {
responses.add(NodeGraphMessage::UpdateLayerPanel); responses.add(NodeGraphMessage::UpdateLayerPanel);
responses.add(DocumentMessage::DocumentStructureChanged); responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::Refresh); responses.add(PropertiesPanelMessage::Refresh);
responses.add(NodeGraphMessage::UpdateActionButtons); responses.add(NodeGraphMessage::UpdateActionButtons);
if !self.should_render_svelte_nodes { responses.add(FrontendMessage::RequestNativeNodeGraphRender);
// Generate and render node graph overlay network
} else {
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(NodeGraphMessage::UpdateVisibleNodes);
let layer_widths = network_interface.collect_layer_widths(breadcrumb_network_path);
responses.add(NodeGraphMessage::UpdateImportsExports); responses.add(NodeGraphMessage::UpdateImportsExports);
responses.add(FrontendMessage::UpdateLayerWidths { layer_widths });
self.update_node_graph_hints(responses); self.update_node_graph_hints(responses);
} }
NodeGraphMessage::SetGridAlignedEdges => {
if graph_view_overlay_open {
network_interface.set_grid_aligned_edges(DVec2::new(ipp.viewport_bounds.bottom_right.x - ipp.viewport_bounds.top_left.x, 0.), breadcrumb_network_path);
// Send the new edges to the frontend
responses.add(NodeGraphMessage::UpdateImportsExports);
}
}
NodeGraphMessage::SetInputValue { node_id, input_index, value } => { NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
let input = NodeInput::value(value, false); let input = NodeInput::value(value, false);
responses.add(NodeGraphMessage::SetInput { responses.add(NodeGraphMessage::SetInput {
@@ -1780,14 +1733,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
NodeGraphMessage::TogglePreviewImpl { node_id } => { NodeGraphMessage::TogglePreviewImpl { node_id } => {
network_interface.toggle_preview(node_id, selection_network_path); network_interface.toggle_preview(node_id, selection_network_path);
} }
NodeGraphMessage::ToggleNativeNodeGraphRender => {
self.should_render_svelte_nodes = !self.should_render_svelte_nodes;
responses.add(FrontendMessage::UpdateShouldRenderSvelteNodes {
should_render_svelte_nodes: self.should_render_svelte_nodes,
});
responses.add(NodeGraphMessage::SendGraph);
responses.add(MenuBarMessage::SendLayout);
}
NodeGraphMessage::ToggleSelectedLocked => { NodeGraphMessage::ToggleSelectedLocked => {
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else { let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
log::error!("Could not get selected nodes in NodeGraphMessage::ToggleSelectedLocked"); log::error!("Could not get selected nodes in NodeGraphMessage::ToggleSelectedLocked");
@@ -1957,7 +1902,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
// Do not show the add import or add export button in the document network; // Do not show the add import or add export button in the document network;
let add_import_export = !breadcrumb_network_path.is_empty(); let add_import_export = !breadcrumb_network_path.is_empty();
responses.add(NodeGraphMessage::UpdateVisibleNodes);
responses.add(FrontendMessage::UpdateImportsExports { responses.add(FrontendMessage::UpdateImportsExports {
imports, imports,
exports, exports,
@@ -1970,9 +1914,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
NodeGraphMessage::UpdateLayerPanel => { NodeGraphMessage::UpdateLayerPanel => {
Self::update_layer_panel(network_interface, selection_network_path, collapsed, layers_panel_open, responses); Self::update_layer_panel(network_interface, selection_network_path, collapsed, layers_panel_open, responses);
} }
NodeGraphMessage::UpdateEdges => {
// Update the import/export UI edges whenever the PTZ changes or the bounding box of all nodes changes
}
NodeGraphMessage::UpdateNewNodeGraph => { NodeGraphMessage::UpdateNewNodeGraph => {
let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else {
log::error!("Could not get selected nodes in NodeGraphMessage::UpdateNewNodeGraph"); log::error!("Could not get selected nodes in NodeGraphMessage::UpdateNewNodeGraph");
@@ -2618,7 +2559,6 @@ impl Default for NodeGraphMessageHandler {
reordering_export: None, reordering_export: None,
reordering_import: None, reordering_import: None,
end_index: None, end_index: None,
frontend_nodes: Vec::new(),
} }
} }
} }
@@ -262,8 +262,6 @@ pub struct FrontendClickTargets {
pub icon_click_targets: Vec<String>, pub icon_click_targets: Vec<String>,
#[serde(rename = "allNodesBoundingBox")] #[serde(rename = "allNodesBoundingBox")]
pub all_nodes_bounding_box: String, pub all_nodes_bounding_box: String,
#[serde(rename = "importExportsBoundingBox")]
pub import_exports_bounding_box: String,
#[serde(rename = "modifyImportExport")] #[serde(rename = "modifyImportExport")]
pub modify_import_export: Vec<String>, pub modify_import_export: Vec<String>,
} }
@@ -1,7 +1,7 @@
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier, NodeRelations}; use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier, NodeRelations};
use super::misc::PTZ; use super::misc::PTZ;
use super::nodes::SelectedNodes; 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::consts::GRID_SIZE;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; 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::document_node_definitions::{DocumentNodeDefinition, resolve_document_node_type};
use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets}; use crate::messages::portfolio::document::node_graph::utility_types::{Direction, FrontendClickTargets};
@@ -1620,68 +1620,6 @@ impl NodeNetworkInterface {
network_metadata.transient_metadata.modify_import_export.unload(); network_metadata.transient_metadata.modify_import_export.unload();
} }
pub fn rounded_network_edge_distance(&mut self, network_path: &[NodeId]) -> Option<&NetworkEdgeDistance> {
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in rounded_network_edge_distance");
return None;
};
if !network_metadata.transient_metadata.rounded_network_edge_distance.is_loaded() {
self.load_rounded_network_edge_distance(network_path);
}
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in rounded_network_edge_distance");
return None;
};
let TransientMetadata::Loaded(rounded_network_edge_distance) = &network_metadata.transient_metadata.rounded_network_edge_distance else {
log::error!("could not load import rounded_network_edge_distance");
return None;
};
Some(rounded_network_edge_distance)
}
fn load_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network in set_grid_aligned_edges");
return;
};
// When setting the edges to be grid aligned, update the pixel offset to ensure the next pan starts from the snapped import/export position
let node_graph_to_viewport = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport;
// TODO: Eventually replace node graph top right with the footprint when trying to get the network edge distance
let node_graph_top_right = network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right;
let target_exports_distance = node_graph_to_viewport.inverse().transform_point2(DVec2::new(
node_graph_top_right.x - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64,
node_graph_top_right.y + EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64,
));
let target_imports_distance = node_graph_to_viewport
.inverse()
.transform_point2(DVec2::new(IMPORTS_TO_LEFT_EDGE_PIXEL_GAP as f64, IMPORTS_TO_TOP_EDGE_PIXEL_GAP as f64));
let rounded_exports_distance = DVec2::new((target_exports_distance.x / 24. + 0.5).floor() * 24., (target_exports_distance.y / 24. + 0.5).floor() * 24.);
let rounded_imports_distance = DVec2::new((target_imports_distance.x / 24. + 0.5).floor() * 24., (target_imports_distance.y / 24. + 0.5).floor() * 24.);
let rounded_viewport_exports_distance = node_graph_to_viewport.transform_point2(rounded_exports_distance);
let rounded_viewport_imports_distance = node_graph_to_viewport.transform_point2(rounded_imports_distance);
let network_edge_distance = NetworkEdgeDistance {
exports_to_edge_distance: rounded_viewport_exports_distance,
imports_to_edge_distance: rounded_viewport_imports_distance,
};
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get current network in load_export_ports");
return;
};
network_metadata.transient_metadata.rounded_network_edge_distance = TransientMetadata::Loaded(network_edge_distance);
}
fn unload_rounded_network_edge_distance(&mut self, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network_metadata in unload_export_ports");
return;
};
network_metadata.transient_metadata.rounded_network_edge_distance.unload();
}
fn owned_nodes(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&HashSet<NodeId>> { fn owned_nodes(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&HashSet<NodeId>> {
let layer_node = self.node_metadata(node_id, network_path)?; let layer_node = self.node_metadata(node_id, network_path)?;
let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &layer_node.persistent_metadata.node_type_metadata else { let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { owned_nodes, .. }) = &layer_node.persistent_metadata.node_type_metadata else {
@@ -2299,33 +2237,6 @@ impl NodeNetworkInterface {
let rect = Subpath::<PointId>::new_rect(bounds[0], bounds[1]); let rect = Subpath::<PointId>::new_rect(bounds[0], bounds[1]);
let all_nodes_bounding_box = rect.to_bezpath().to_svg(); let all_nodes_bounding_box = rect.to_bezpath().to_svg();
let Some(rounded_network_edge_distance) = self.rounded_network_edge_distance(network_path).cloned() else {
log::error!("Could not get rounded_network_edge_distance in collect_frontend_click_targets");
return FrontendClickTargets::default();
};
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in collect_frontend_click_targets");
return FrontendClickTargets::default();
};
let import_exports_viewport_top_left = rounded_network_edge_distance.imports_to_edge_distance;
let import_exports_viewport_bottom_right = rounded_network_edge_distance.exports_to_edge_distance;
let node_graph_top_left = network_metadata
.persistent_metadata
.navigation_metadata
.node_graph_to_viewport
.inverse()
.transform_point2(import_exports_viewport_top_left);
let node_graph_bottom_right = network_metadata
.persistent_metadata
.navigation_metadata
.node_graph_to_viewport
.inverse()
.transform_point2(import_exports_viewport_bottom_right);
let import_exports_target = Subpath::<PointId>::new_rect(node_graph_top_left, node_graph_bottom_right);
let import_exports_bounding_box = import_exports_target.to_bezpath().to_svg();
let mut modify_import_export = Vec::new(); let mut modify_import_export = Vec::new();
if let Some(modify_import_export_click_targets) = self.modify_import_export(network_path) { if let Some(modify_import_export_click_targets) = self.modify_import_export(network_path) {
for click_target in modify_import_export_click_targets for click_target in modify_import_export_click_targets
@@ -2344,7 +2255,6 @@ impl NodeNetworkInterface {
connector_click_targets, connector_click_targets,
icon_click_targets, icon_click_targets,
all_nodes_bounding_box, all_nodes_bounding_box,
import_exports_bounding_box,
modify_import_export, modify_import_export,
} }
} }
@@ -2591,26 +2501,6 @@ impl NodeNetworkInterface {
bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport) bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport)
} }
// TODO: Remove and get layer click targets from render output
pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> HashMap<NodeId, u32> {
let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in collect_layer_widths");
return HashMap::new();
};
let nodes = network_metadata
.persistent_metadata
.node_metadata
.iter()
.filter_map(|(node_id, _)| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None })
.collect::<Vec<_>>();
let layer_widths = nodes
.iter()
.filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width)))
.collect::<HashMap<NodeId, u32>>();
layer_widths
}
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> { pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self); let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self);
@@ -2785,18 +2675,6 @@ impl NodeNetworkInterface {
self.unload_modify_import_export(network_path); self.unload_modify_import_export(network_path);
} }
// This should be run whenever the pan ends, a zoom occurs, or the network is opened
pub fn set_grid_aligned_edges(&mut self, node_graph_top_right: DVec2, network_path: &[NodeId]) {
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
log::error!("Could not get nested network_metadata in set_grid_aligned_edges");
return;
};
network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right = node_graph_top_right;
self.unload_rounded_network_edge_distance(network_path);
self.unload_import_export_ports(network_path);
self.unload_modify_import_export(network_path);
}
pub fn vector_modify(&mut self, node_id: &NodeId, modification_type: VectorModificationType) { pub fn vector_modify(&mut self, node_id: &NodeId, modification_type: VectorModificationType) {
let Some(node) = self.network_mut(&[]).unwrap().nodes.get_mut(node_id) else { let Some(node) = self.network_mut(&[]).unwrap().nodes.get_mut(node_id) else {
log::error!("Could not get node in vector_modification"); log::error!("Could not get node in vector_modification");
@@ -5581,8 +5459,6 @@ pub struct NodeNetworkTransientMetadata {
pub import_export_ports: TransientMetadata<Ports>, pub import_export_ports: TransientMetadata<Ports>,
/// Click targets for adding, removing, and moving import/export ports /// Click targets for adding, removing, and moving import/export ports
pub modify_import_export: TransientMetadata<ModifyImportExportClickTarget>, 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>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -90,15 +90,23 @@ impl NodeNetworkInterface {
let position = FrontendXY { x: position.x, y: position.y }; let position = FrontendXY { x: position.x, y: position.y };
let inputs = (0..self.number_of_inputs(&node_id, network_path)) let primary_input = self.frontend_input_from_connector(&InputConnector::node(node_id, 0), network_path);
.map(|input_index| self.frontend_input_from_connector(&InputConnector::node(node_id, input_index), network_path)) let secondary_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(); .collect();
let outputs = (0..self.number_of_outputs(&node_id, network_path)) let primary_output = self.frontend_output_from_connector(&OutputConnector::node(node_id, 0), network_path);
.map(|output_index| self.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), network_path)) let secondary_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(); .collect();
let node = Some(FrontendNode { position, inputs, outputs }); let node = Some(FrontendNode {
position,
primary_input,
primary_output,
secondary_inputs,
secondary_outputs,
});
FrontendNodeOrLayer { node, layer: None } FrontendNodeOrLayer { node, layer: None }
} }
@@ -54,8 +54,19 @@ pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical
let horizontal_curve = horizontal_curve_amount * curve_length; let horizontal_curve = horizontal_curve_amount * curve_length;
let vertical_curve = vertical_curve_amount * curve_length; let vertical_curve = vertical_curve_amount * curve_length;
let wire_start = if vertical_in && vertical_out { output_position + DVec2::new(0., -4.) } else { output_position };
let wire_end = if vertical_in && vertical_in {
DVec2::new(input_position.x, input_position.y) + DVec2::new(0., 4.)
} else {
DVec2::new(input_position.x, input_position.y)
};
if vertical_in && vertical_in && wire_end.y + 5. > wire_start.y {
return BezPath::new();
}
let locations = [ let locations = [
output_position, wire_start,
DVec2::new( DVec2::new(
if vertical_out { output_position.x } else { output_position.x + horizontal_curve }, if vertical_out { output_position.x } else { output_position.x + horizontal_curve },
if vertical_out { output_position.y - vertical_curve } else { output_position.y }, if vertical_out { output_position.y - vertical_curve } else { output_position.y },
@@ -64,7 +75,7 @@ pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical
if vertical_in { input_position.x } else { input_position.x - horizontal_curve }, if vertical_in { input_position.x } else { input_position.x - horizontal_curve },
if vertical_in { input_position.y + vertical_curve } else { input_position.y }, if vertical_in { input_position.y + vertical_curve } else { input_position.y },
), ),
DVec2::new(input_position.x, input_position.y), wire_end,
]; ];
let smoothing = 0.5; let smoothing = 0.5;
@@ -18,7 +18,6 @@ pub struct MenuBarMessageHandler {
pub has_selection_history: (bool, bool), pub has_selection_history: (bool, bool),
pub message_logging_verbosity: MessageLoggingVerbosity, pub message_logging_verbosity: MessageLoggingVerbosity,
pub reset_node_definitions_on_open: bool, pub reset_node_definitions_on_open: bool,
pub should_render_svelte_nodes: bool,
pub make_path_editable_is_allowed: bool, pub make_path_editable_is_allowed: bool,
pub data_panel_open: bool, pub data_panel_open: bool,
pub layers_panel_open: bool, pub layers_panel_open: bool,
@@ -49,7 +48,6 @@ impl LayoutHolder for MenuBarMessageHandler {
let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names; let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names;
let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents; let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents;
let reset_node_definitions_on_open = self.reset_node_definitions_on_open; let reset_node_definitions_on_open = self.reset_node_definitions_on_open;
let should_render_svelte_nodes = self.should_render_svelte_nodes;
let make_path_editable_is_allowed = self.make_path_editable_is_allowed; let make_path_editable_is_allowed = self.make_path_editable_is_allowed;
let menu_bar_entries = vec![ let menu_bar_entries = vec![
@@ -698,12 +696,6 @@ impl LayoutHolder for MenuBarMessageHandler {
action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen.into()), action: MenuBarEntry::create_action(|_| PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen.into()),
..MenuBarEntry::default() ..MenuBarEntry::default()
}], }],
vec![MenuBarEntry {
label: "HTML Node Graph Render".into(),
icon: Some(if should_render_svelte_nodes { "CheckboxChecked" } else { "CheckboxUnchecked" }.into()),
action: MenuBarEntry::create_action(|_| NodeGraphMessage::ToggleNativeNodeGraphRender.into()),
..MenuBarEntry::default()
}],
vec![ vec![
MenuBarEntry { MenuBarEntry {
label: "Print Trace Logs".into(), label: "Print Trace Logs".into(),
@@ -587,7 +587,6 @@
thumbPosition={scrollbarPos.x} thumbPosition={scrollbarPos.x}
on:trackShift={({ detail }) => editor.handle.panCanvasByFraction(detail, 0)} on:trackShift={({ detail }) => editor.handle.panCanvasByFraction(detail, 0)}
on:thumbPosition={({ detail }) => panCanvasX(detail)} on:thumbPosition={({ detail }) => panCanvasX(detail)}
on:thumbDragEnd={() => editor.handle.setGridAlignedEdges()}
on:thumbDragStart={() => editor.handle.panCanvasAbortPrepare(true)} on:thumbDragStart={() => editor.handle.panCanvasAbortPrepare(true)}
on:thumbDragAbort={() => editor.handle.panCanvasAbort(true)} on:thumbDragAbort={() => editor.handle.panCanvasAbort(true)}
/> />
@@ -617,7 +617,6 @@
<path class="visibility" d={pathString} /> <path class="visibility" d={pathString} />
{/each} {/each}
<path class="all-nodes-bounding-box" d={$nodeGraph.clickTargets.allNodesBoundingBox} /> <path class="all-nodes-bounding-box" d={$nodeGraph.clickTargets.allNodesBoundingBox} />
<path class="all-nodes-bounding-box" d={$nodeGraph.clickTargets.importExportsBoundingBox} />
{#each $nodeGraph.clickTargets.modifyImportExport as pathString} {#each $nodeGraph.clickTargets.modifyImportExport as pathString}
<path class="modify-import-export" d={pathString} /> <path class="modify-import-export" d={pathString} />
{/each} {/each}
+2 -24
View File
@@ -35,7 +35,6 @@ export class FrontendClickTargets {
readonly connectorClickTargets!: string[]; readonly connectorClickTargets!: string[];
readonly iconClickTargets!: string[]; readonly iconClickTargets!: string[];
readonly allNodesBoundingBox!: string; readonly allNodesBoundingBox!: string;
readonly importExportsBoundingBox!: string;
readonly modifyImportExport!: string[]; readonly modifyImportExport!: string[];
} }
@@ -106,30 +105,10 @@ export class UpdateLayerWidths extends JsMessage {
readonly layerWidths!: Map<bigint, number>; readonly layerWidths!: Map<bigint, number>;
} }
export class UpdateNodeGraphSvelteRender extends JsMessage {
readonly nodesToRender!: FrontendNodeToRender[];
readonly open!: boolean;
readonly opacity!: number;
readonly inSelectedNetwork!: boolean;
readonly previewedNode!: bigint | undefined;
}
export class UpdateShouldRenderSvelteNodes extends JsMessage {
readonly shouldRenderSvelteNodes!: boolean;
}
export class UpdateNativeNodeGraphSVG extends JsMessage { export class UpdateNativeNodeGraphSVG extends JsMessage {
readonly svgString!: string; readonly svgString!: string;
} }
export class UpdateVisibleNodes extends JsMessage {
readonly nodes!: bigint[];
}
export class UpdateNodeGraphTransform extends JsMessage { export class UpdateNodeGraphTransform extends JsMessage {
readonly transform!: NodeGraphTransform; readonly transform!: NodeGraphTransform;
} }
@@ -1762,9 +1741,9 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateMenuBarLayout, UpdateMenuBarLayout,
UpdateMouseCursor, UpdateMouseCursor,
UpdateNodeGraphControlBarLayout, UpdateNodeGraphControlBarLayout,
UpdateNodeGraphRender, UpdateNativeNodeGraphSVG,
UpdateNodeGraphSelectionBox,
UpdateNodeGraphTransform, UpdateNodeGraphTransform,
UpdateNodeGraphSelectionBox,
UpdateNodeThumbnail, UpdateNodeThumbnail,
UpdateOpenDocumentsList, UpdateOpenDocumentsList,
UpdatePlatform, UpdatePlatform,
@@ -1776,7 +1755,6 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateToolOptionsLayout, UpdateToolOptionsLayout,
UpdateToolShelfLayout, UpdateToolShelfLayout,
UpdateViewportHolePunch, UpdateViewportHolePunch,
UpdateVisibleNodes,
UpdateWirePathInProgress, UpdateWirePathInProgress,
UpdateWorkingColorsLayout, UpdateWorkingColorsLayout,
} as const; } as const;
+1 -45
View File
@@ -15,14 +15,11 @@ import {
UpdateExportReorderIndex, UpdateExportReorderIndex,
UpdateImportsExports, UpdateImportsExports,
UpdateLayerWidths, UpdateLayerWidths,
UpdateNodeGraphSvelteRender,
UpdateShouldRenderSvelteNodes,
UpdateNativeNodeGraphSVG, UpdateNativeNodeGraphSVG,
UpdateVisibleNodes,
UpdateNodeGraphTransform,
UpdateNodeThumbnail, UpdateNodeThumbnail,
UpdateWirePathInProgress, UpdateWirePathInProgress,
UpdateNodeGraphSelectionBox, UpdateNodeGraphSelectionBox,
UpdateNodeGraphTransform,
} from "@graphite/messages"; } from "@graphite/messages";
export function createNodeGraphState(editor: Editor) { export function createNodeGraphState(editor: Editor) {
@@ -46,11 +43,6 @@ export function createNodeGraphState(editor: Editor) {
inSelectedNetwork: true, inSelectedNetwork: true,
previewedNode: undefined as bigint | undefined, previewedNode: undefined as bigint | undefined,
// TODO: Remove these fields
visibleNodes: new Set<bigint>(),
layerWidths: new Map<bigint, number>(),
shouldRenderSvelteNodes: false,
// Data that will be passed in the context // Data that will be passed in the context
thumbnails: new Map<bigint, string>(), thumbnails: new Map<bigint, string>(),
transform: { scale: 1, x: 0, y: 0 }, transform: { scale: 1, x: 0, y: 0 },
@@ -107,42 +99,12 @@ export function createNodeGraphState(editor: Editor) {
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateLayerWidths, (updateLayerWidths) => {
update((state) => {
state.layerWidths = updateLayerWidths.layerWidths;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSvelteRender, (updateNodeGraphSvelteRender) => {
update((state) => {
state.nodesToRender.clear();
updateNodeGraphSvelteRender.nodesToRender.forEach((node) => {
state.nodesToRender.set(node.metadata.nodeId, node);
});
state.opacity = updateNodeGraphSvelteRender.opacity;
state.inSelectedNetwork = updateNodeGraphSvelteRender.inSelectedNetwork;
state.previewedNode = updateNodeGraphSvelteRender.previewedNode;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateShouldRenderSvelteNodes, (UpdateShouldRenderSvelteNodes) => {
update((state) => {
state.shouldRenderSvelteNodes = UpdateShouldRenderSvelteNodes.shouldRenderSvelteNodes;
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateNativeNodeGraphSVG, (updateNativeNodeGraphRender) => { editor.subscriptions.subscribeJsMessage(UpdateNativeNodeGraphSVG, (updateNativeNodeGraphRender) => {
update((state) => { update((state) => {
state.nativeNodeGraphSVGString = updateNativeNodeGraphRender.svgString; state.nativeNodeGraphSVGString = updateNativeNodeGraphRender.svgString;
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateVisibleNodes, (updateVisibleNodes) => {
update((state) => {
state.visibleNodes = new Set<bigint>(updateVisibleNodes.nodes);
return state;
});
});
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphTransform, (updateNodeGraphTransform) => { editor.subscriptions.subscribeJsMessage(UpdateNodeGraphTransform, (updateNodeGraphTransform) => {
update((state) => { update((state) => {
state.transform = updateNodeGraphTransform.transform; state.transform = updateNodeGraphTransform.transform;
@@ -155,12 +117,6 @@ export function createNodeGraphState(editor: Editor) {
return state; return state;
}); });
}); });
editor.subscriptions.subscribeJsMessage(UpdateWirePathInProgress, (updateWirePathInProgress) => {
update((state) => {
state.wirePathInProgress = updateWirePathInProgress.wirePathInProgress;
return state;
});
});
return { return {
subscribe, subscribe,
-7
View File
@@ -785,13 +785,6 @@ impl EditorHandle {
self.dispatch(message); self.dispatch(message);
} }
/// Snaps the import/export edges to a grid space when the scroll bar is released
#[wasm_bindgen(js_name = setGridAlignedEdges)]
pub fn set_grid_aligned_edges(&self) {
let message = NodeGraphMessage::SetGridAlignedEdges;
self.dispatch(message);
}
/// Merge the selected nodes into a subnetwork /// Merge the selected nodes into a subnetwork
#[wasm_bindgen(js_name = mergeSelectedNodes)] #[wasm_bindgen(js_name = mergeSelectedNodes)]
pub fn merge_nodes(&self) { pub fn merge_nodes(&self) {
+3 -1
View File
@@ -21,12 +21,14 @@ pub mod ui_context;
#[node_macro::node(skip_impl)] #[node_macro::node(skip_impl)]
pub fn generate_nodes(_: impl Ctx, mut node_graph_overlay_data: NodeGraphOverlayData) -> Table<Graphic> { pub fn generate_nodes(_: impl Ctx, mut node_graph_overlay_data: NodeGraphOverlayData) -> Table<Graphic> {
let mut nodes_and_wires = Table::new(); let mut nodes_and_wires = Table::new();
let layers = draw_layers(&node_graph_overlay_data.nodes_to_render); let (layers, side_ports) = draw_layers(&node_graph_overlay_data.nodes_to_render);
nodes_and_wires.extend(layers); nodes_and_wires.extend(layers);
let wires = draw_wires(&mut node_graph_overlay_data.nodes_to_render); let wires = draw_wires(&mut node_graph_overlay_data.nodes_to_render);
nodes_and_wires.extend(wires); nodes_and_wires.extend(wires);
nodes_and_wires.extend(side_ports);
let nodes = draw_nodes(&node_graph_overlay_data.nodes_to_render); let nodes = draw_nodes(&node_graph_overlay_data.nodes_to_render);
nodes_and_wires.extend(nodes); nodes_and_wires.extend(nodes);
@@ -1,6 +1,6 @@
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use graphene_core_shaders::color::{AlphaMut, Color}; use graphene_core_shaders::color::{AlphaMut, Color};
use kurbo::{BezPath, Rect, RoundedRect, Shape}; use kurbo::{BezPath, Circle, Rect, RoundedRect, Shape};
use crate::{ use crate::{
Graphic, Graphic,
@@ -26,9 +26,9 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
let x = frontend_node.position.x as f64 * GRID_SIZE; let x = frontend_node.position.x as f64 * GRID_SIZE;
let y = frontend_node.position.y as f64 * GRID_SIZE + GRID_SIZE / 2.; let y = frontend_node.position.y as f64 * GRID_SIZE + GRID_SIZE / 2.;
let node_width = GRID_SIZE * 5.0; let node_width = GRID_SIZE * 5.0;
let number_of_exposed_inputs = frontend_node.inputs.iter().skip(1).filter(|x| x.is_some()).count(); let number_of_secondary_inputs = frontend_node.secondary_inputs.len();
let number_of_exposed_outputs = frontend_node.outputs.iter().skip(1).filter(|x| x.is_some()).count(); let number_of_secondary_outputs = frontend_node.secondary_outputs.len();
let number_of_rows = 1 + number_of_exposed_inputs.max(number_of_exposed_outputs); let number_of_rows = 1 + number_of_secondary_inputs.max(number_of_secondary_outputs);
let node_height = number_of_rows as f64 * GRID_SIZE; let node_height = number_of_rows as f64 * GRID_SIZE;
let node_rect = RoundedRect::new(x, y, x + node_width, y + node_height, 2.); let node_rect = RoundedRect::new(x, y, x + node_width, y + node_height, 2.);
@@ -49,7 +49,7 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
bg_vector.style.fill = crate::vector::style::Fill::Solid(node_color.clone()); bg_vector.style.fill = crate::vector::style::Fill::Solid(node_color.clone());
bg_table.push(TableRow::new_from_element(bg_vector)); bg_table.push(TableRow::new_from_element(bg_vector));
// Make primary input brighter // Make primary input brighter
if number_of_exposed_inputs == 0 { if number_of_secondary_inputs == 0 {
// Draw the first row with rounded bottom corners // Draw the first row with rounded bottom corners
bg_table.push(TableRow::new_from_element(node_first_row(x, y, false))); bg_table.push(TableRow::new_from_element(node_first_row(x, y, false)));
} else { } else {
@@ -62,7 +62,7 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
let mut border_mask_path = BezPath::new(); let mut border_mask_path = BezPath::new();
border_mask_path.move_to((-2., -2.)); border_mask_path.move_to((-2., -2.));
border_mask_path.line_to((node_width + 2., -2.)); border_mask_path.line_to((node_width + 2., -2.));
if frontend_node.outputs[0].is_some() { if frontend_node.primary_output.is_some() {
border_mask_path.line_to((node_width + 2., 4.)); border_mask_path.line_to((node_width + 2., 4.));
border_mask_path.line_to((node_width - 2., 4.)); border_mask_path.line_to((node_width - 2., 4.));
border_mask_path.line_to((node_width - 2., 20.)); border_mask_path.line_to((node_width - 2., 20.));
@@ -70,7 +70,7 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
border_mask_path.line_to((node_width + 2., 20.)); border_mask_path.line_to((node_width + 2., 20.));
for row in 1..number_of_rows { for row in 1..number_of_rows {
border_mask_path.line_to((node_width + 2., row as f64 * GRID_SIZE + 4.)); border_mask_path.line_to((node_width + 2., row as f64 * GRID_SIZE + 4.));
if frontend_node.outputs.get(row).is_some_and(|output| output.is_some()) { if row <= number_of_secondary_outputs {
border_mask_path.line_to((node_width - 2., row as f64 * GRID_SIZE + 4.)); border_mask_path.line_to((node_width - 2., row as f64 * GRID_SIZE + 4.));
border_mask_path.line_to((node_width - 2., row as f64 * GRID_SIZE + 20.)); border_mask_path.line_to((node_width - 2., row as f64 * GRID_SIZE + 20.));
border_mask_path.line_to((node_width + 2., row as f64 * GRID_SIZE + 20.)); border_mask_path.line_to((node_width + 2., row as f64 * GRID_SIZE + 20.));
@@ -80,13 +80,13 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
border_mask_path.line_to((-2., number_of_rows as f64 * GRID_SIZE + 2.)); border_mask_path.line_to((-2., number_of_rows as f64 * GRID_SIZE + 2.));
for row in (1..number_of_rows).rev() { for row in (1..number_of_rows).rev() {
border_mask_path.line_to((-2., row as f64 * GRID_SIZE + 20.)); border_mask_path.line_to((-2., row as f64 * GRID_SIZE + 20.));
if frontend_node.inputs.iter().skip(1).filter(|input| input.is_some()).nth(row - 1).is_some_and(|input| input.is_some()) { if row <= number_of_secondary_inputs {
border_mask_path.line_to((2., row as f64 * GRID_SIZE + 20.)); border_mask_path.line_to((2., row as f64 * GRID_SIZE + 20.));
border_mask_path.line_to((2., row as f64 * GRID_SIZE + 4.)); border_mask_path.line_to((2., row as f64 * GRID_SIZE + 4.));
border_mask_path.line_to((2., row as f64 * GRID_SIZE + 4.)); border_mask_path.line_to((2., row as f64 * GRID_SIZE + 4.));
} }
} }
if frontend_node.inputs[0].is_some() { if frontend_node.primary_input.is_some() {
border_mask_path.line_to((-2., 20.)); border_mask_path.line_to((-2., 20.));
border_mask_path.line_to((2., 20.)); border_mask_path.line_to((2., 20.));
border_mask_path.line_to((2., 4.)); border_mask_path.line_to((2., 4.));
@@ -105,7 +105,8 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
// Border table is implemented as a clip mask // Border table is implemented as a clip mask
let mut border_table = Table::new(); let mut border_table = Table::new();
let mut border_vector = Vector::from_bezpath(node_bez_path); let mut border_vector = Vector::from_bezpath(node_bez_path);
let primary_output_color = frontend_node.outputs[0] let primary_output_color = frontend_node
.primary_output
.as_ref() .as_ref()
.map(|primary_output| primary_output.data_type.data_color_dim()) .map(|primary_output| primary_output.data_type.data_color_dim())
.unwrap_or(FrontendGraphDataType::General.data_color_dim()); .unwrap_or(FrontendGraphDataType::General.data_color_dim());
@@ -132,15 +133,16 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
for text_row in node_text.iter_mut() { for text_row in node_text.iter_mut() {
*text_row.transform = DAffine2::from_translation(DVec2::new(x + 8., y + 3.)); *text_row.transform = DAffine2::from_translation(DVec2::new(x + 8., y + 3.));
} }
for (row, input) in frontend_node.inputs.iter().enumerate().skip(1) {
if let Some(input) = input { for row in 1..=number_of_rows {
if let Some(input) = frontend_node.secondary_inputs.get(row - 1) {
let font_blob = Some(text::load_font(SOURCE_SANS_FONT_DATA)); let font_blob = Some(text::load_font(SOURCE_SANS_FONT_DATA));
let mut input_row_text = crate::text::to_path(&input.name, font_blob, typesetting, false); let mut input_row_text = crate::text::to_path(&input.name, font_blob, typesetting, false);
for text_row in input_row_text.iter_mut() { for text_row in input_row_text.iter_mut() {
*text_row.transform = DAffine2::from_translation(DVec2::new(x + 8., y + 24. * row as f64 + 3.)); *text_row.transform = DAffine2::from_translation(DVec2::new(x + 8., y + 24. * row as f64 + 3.));
} }
node_text.extend(input_row_text); node_text.extend(input_row_text);
} else if let Some(Some(output)) = frontend_node.outputs.get(row) { } else if let Some(output) = frontend_node.secondary_outputs.get(row - 1) {
let font_blob = Some(text::load_font(SOURCE_SANS_FONT_DATA)); let font_blob = Some(text::load_font(SOURCE_SANS_FONT_DATA));
let mut output_row_text = crate::text::to_path(&output.name, font_blob, typesetting, false); let mut output_row_text = crate::text::to_path(&output.name, font_blob, typesetting, false);
// Find width to right align text // Find width to right align text
@@ -176,14 +178,41 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
text_area_row.alpha_blending.clip = true; text_area_row.alpha_blending.clip = true;
let text_area_table = Table::new_from_row(text_area_row); let text_area_table = Table::new_from_row(text_area_row);
node_table.push(TableRow::new_from_element(Graphic::Vector(text_area_table))); node_table.push(TableRow::new_from_element(Graphic::Vector(text_area_table)));
// Input and output ports
let mut ports_table = Table::new();
if let Some(primary_input) = &frontend_node.primary_input {
let mut row = port_row(&primary_input.data_type);
row.transform = DAffine2::from_translation(DVec2::new(0., 12.));
ports_table.push(row);
}
for (index, secondary_input) in frontend_node.secondary_inputs.iter().enumerate() {
let mut row = port_row(&secondary_input.data_type);
row.transform = DAffine2::from_translation(DVec2::new(0., 12. + GRID_SIZE * (index + 1) as f64));
ports_table.push(row);
}
if let Some(primary_output) = &frontend_node.primary_output {
let mut row = port_row(&primary_output.data_type);
row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12.));
ports_table.push(row);
}
for (index, secondary_output) in frontend_node.secondary_outputs.iter().enumerate() {
let mut row = port_row(&secondary_output.data_type);
row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12. + GRID_SIZE * (index + 1) as f64));
ports_table.push(row);
}
let mut graphic_ports_row = TableRow::new_from_element(Graphic::Vector(ports_table));
graphic_ports_row.transform = DAffine2::from_translation(DVec2::new(x - 3., y - 4.));
node_table.push(graphic_ports_row);
} }
} }
node_table node_table
} }
pub fn draw_layers(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> { pub fn draw_layers(nodes: &Vec<FrontendNodeToRender>) -> (Table<Graphic>, Table<Graphic>) {
let mut layer_table = Table::new(); let mut layer_table = Table::new();
let mut side_ports = Table::new();
for node_to_render in nodes { for node_to_render in nodes {
if let Some(frontend_layer) = node_to_render.node_or_layer.layer.as_ref() { if let Some(frontend_layer) = node_to_render.node_or_layer.layer.as_ref() {
// The layer position is the top left of the thumbnail // The layer position is the top left of the thumbnail
@@ -219,12 +248,12 @@ pub fn draw_layers(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
// Text starts at thumbnail + left padding // Text starts at thumbnail + left padding
let text_start = 12. + 8.; let text_start = 12. + 8.;
let right_text_edge = text_start + text_width; let right_text_edge = 8. + text_width;
let rounded_text_edge = (right_text_edge as f64 / 24.).ceil() * 24.; let rounded_text_edge = (right_text_edge as f64 / 24.).ceil() * 24.;
let rounded_layer_width_pixels = rounded_text_edge + 24.; let rounded_layer_width_pixels = rounded_text_edge + 24.;
// Subtract the left thumbnail // add the left thumbnail gap
let layer_right_edge_width = rounded_layer_width_pixels - 12.; let layer_right_edge_width = rounded_layer_width_pixels + 12.;
let right_layer_width = layer_right_edge_width.max(4.5 * GRID_SIZE); let right_layer_width = layer_right_edge_width.max(4.5 * GRID_SIZE);
let thumbnail_width = 3. * GRID_SIZE; let thumbnail_width = 3. * GRID_SIZE;
@@ -305,10 +334,84 @@ pub fn draw_layers(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
} }
let top_layer = text_table; let top_layer = text_table;
layer_table.push(TableRow::new_from_element(Graphic::Vector(top_layer))); layer_table.push(TableRow::new_from_element(Graphic::Vector(top_layer)));
// Ports
let mut ports_table = Table::new();
if let Some(side_input) = &frontend_layer.side_input {
let mut port = port_row(&side_input.data_type);
port.transform = DAffine2::from_translation(DVec2::new(layer_position.x - 15., layer_position.y + GRID_SIZE - 4.));
ports_table.push(port);
}
let top_port = BezPath::from_svg("M0,6.953l2.521,-1.694a2.649,2.649,0,0,1,2.959,0l2.52,1.694v5.047h-8z").unwrap();
let mut vector = Vector::from_bezpath(top_port);
vector.style.fill = Fill::Solid(Color::from_rgba8_no_srgb(frontend_layer.output.data_type.data_color()).unwrap());
let mut side_port = TableRow::new_from_element(vector);
side_port.transform = DAffine2::from_translation(DVec2::new(frontend_layer.position.x as f64 * 24. + GRID_SIZE * 2. - 4., layer_position.y - 12.));
side_ports.push(side_port);
if frontend_layer.primary_output_connected_to_layer {
let top_wire_cap = BezPath::from_svg("M0,-3.5h8v8l-2.521,-1.681a2.666,2.666,0,0,0,-2.959,0l-2.52,1.681z").unwrap();
let mut vector = Vector::from_bezpath(top_wire_cap);
vector.style.fill = Fill::Solid(Color::from_rgba8_no_srgb(frontend_layer.output.data_type.data_color_dim()).unwrap());
let mut vector_row = TableRow::new_from_element(vector);
vector_row.transform = DAffine2::from_translation(DVec2::new(frontend_layer.position.x as f64 * 24. + GRID_SIZE * 2. - 4., layer_position.y - 12.));
ports_table.push(vector_row);
}
let bottom_port = BezPath::from_svg("M0,0H8V8L5.479,6.319a2.666,2.666,0,0,0-2.959,0L0,8Z").unwrap();
let mut vector = Vector::from_bezpath(bottom_port);
vector.style.fill = Fill::Solid(Color::from_rgba8_no_srgb(frontend_layer.bottom_input.data_type.data_color()).unwrap());
let mut vector_row = TableRow::new_from_element(vector);
vector_row.transform = DAffine2::from_translation(DVec2::new(frontend_layer.position.x as f64 * 24. + GRID_SIZE * 2. - 4., layer_position.y + 2. * GRID_SIZE));
ports_table.push(vector_row);
if frontend_layer.primary_input_connected_to_layer {
let bottom_port_cap = BezPath::from_svg("M0,10.95l2.52,-1.69c0.89,-0.6,2.06,-0.6,2.96,0l2.52,1.69v5.05h-8v-5.05z").unwrap();
let mut vector = Vector::from_bezpath(bottom_port_cap);
vector.style.fill = Fill::Solid(Color::from_rgba8_no_srgb(frontend_layer.bottom_input.data_type.data_color_dim()).unwrap());
let mut vector_row = TableRow::new_from_element(vector);
vector_row.transform = DAffine2::from_translation(DVec2::new(frontend_layer.position.x as f64 * 24. + GRID_SIZE * 2. - 4., layer_position.y + 2. * GRID_SIZE));
ports_table.push(vector_row);
}
layer_table.push(TableRow::new_from_element(Graphic::Vector(ports_table)));
// Eye and grip icon
let mut icons_table = Table::new();
let icon_svg = if node_to_render.metadata.visible {
BezPath::from_svg("M8,3C3,3,0,8,0,8s3,5,8,5s8-5,8-5S13,3,8,3z M8,12c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S10.2,12,8,12z").unwrap()
} else {
BezPath::from_svg("M8,4c3.5,0,5.9,2.8,6.8,4c-0.9,1.2-3.3,4-6.8,4S2.1,9.2,1.2,8C2.1,6.8,4.5,4,8,4 M8,3C3,3,0,8,0,8s3,5,8,5s8-5,8-5S13,3,8,3L8,3z").unwrap()
};
let mut icon_vector = Vector::from_bezpath(icon_svg);
icon_vector.style.fill = Fill::Solid(Color::WHITE);
let mut icon_row = TableRow::new_from_element(icon_vector);
icon_row.transform = DAffine2::from_translation(layer_position + DVec2::new(thumbnail_width + right_layer_width - 8., 16.));
icons_table.push(icon_row);
if node_to_render.metadata.selected {
let mut grip_path = BezPath::new();
let circle = Circle::new((0.5, 1.5), 0.5);
grip_path.extend(circle.to_path(BEZ_PATH_TOLERANCE));
let circle = Circle::new((0.5, 4.5), 0.5);
grip_path.extend(circle.to_path(BEZ_PATH_TOLERANCE));
let circle = Circle::new((0.5, 7.5), 0.5);
grip_path.extend(circle.to_path(BEZ_PATH_TOLERANCE));
let circle = Circle::new((3.5, 1.5), 0.5);
grip_path.extend(circle.to_path(BEZ_PATH_TOLERANCE));
let circle = Circle::new((3.5, 4.5), 0.5);
grip_path.extend(circle.to_path(BEZ_PATH_TOLERANCE));
let circle = Circle::new((3.5, 7.5), 0.5);
grip_path.extend(circle.to_path(BEZ_PATH_TOLERANCE));
let mut grip_vector = Vector::from_bezpath(grip_path);
grip_vector.style.fill = Fill::Solid(Color::from_rgba8_no_srgb(COLOR_E_NEARWHITE).unwrap());
let mut grip_row = TableRow::new_from_element(grip_vector);
grip_row.transform = DAffine2::from_translation(layer_position + DVec2::new(thumbnail_width + right_layer_width + 6. - GRID_SIZE, 19.5));
icons_table.push(grip_row);
}
layer_table.push(TableRow::new_from_element(Graphic::Vector(icons_table)));
} }
} }
layer_table let mut ports_table = Table::new();
ports_table.push(TableRow::new_from_element(Graphic::Vector(side_ports)));
(layer_table, ports_table)
} }
fn node_first_row(x0: f64, y0: f64, rounded_bottom: bool) -> Vector { fn node_first_row(x0: f64, y0: f64, rounded_bottom: bool) -> Vector {
@@ -352,10 +455,13 @@ fn node_first_row(x0: f64, y0: f64, rounded_bottom: bool) -> Vector {
vector vector
} }
fn port() -> BezPath { fn port_row(data_type: &FrontendGraphDataType) -> TableRow<Vector> {
BezPath::from_svg("M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z").unwrap_or_else(|e| { let path = BezPath::from_svg("M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z").unwrap_or_else(|e| {
panic!("Could not parse port svg from string: {}", e); panic!("Could not parse port svg from string: {}", e);
}) });
let mut vector = Vector::from_bezpath(path);
vector.style.fill = Fill::Solid(Color::from_rgba8_no_srgb(data_type.data_color()).unwrap());
TableRow::new_from_element(vector)
} }
pub fn draw_wires(nodes: &mut Vec<FrontendNodeToRender>) -> Table<Graphic> { pub fn draw_wires(nodes: &mut Vec<FrontendNodeToRender>) -> Table<Graphic> {
@@ -75,8 +75,10 @@ pub struct FrontendNodeMetadata {
pub struct FrontendNode { pub struct FrontendNode {
// pub position: FrontendNodePosition, // pub position: FrontendNodePosition,
pub position: FrontendXY, pub position: FrontendXY,
pub inputs: Vec<Option<FrontendGraphInput>>, pub primary_output: Option<FrontendGraphOutput>,
pub outputs: Vec<Option<FrontendGraphOutput>>, pub primary_input: Option<FrontendGraphInput>,
pub secondary_inputs: Vec<FrontendGraphInput>,
pub secondary_outputs: Vec<FrontendGraphOutput>,
} }
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]