diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 8538930f8c..6042c9bfca 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -269,17 +269,7 @@ pub enum FrontendMessage { UpdateMouseCursor { cursor: MouseCursorIcon, }, - UpdateNodeGraphSvelteRender { - #[serde(rename = "nodesToRender")] - nodes_to_render: Vec, - open: bool, - opacity: f64, - #[serde(rename = "inSelectedNetwork")] - in_selected_network: bool, - // Displays a dashed border around the node - #[serde(rename = "previewedNode")] - previewed_node: Option, - }, + RequestNativeNodeGraphRender, UpdateShouldRenderSvelteNodes { #[serde(rename = "shouldRenderSvelteNodes")] should_render_svelte_nodes: bool, diff --git a/editor/src/messages/portfolio/document/node_graph/generate_node_graph_overlay.rs b/editor/src/messages/portfolio/document/node_graph/generate_node_graph_overlay.rs index d3d7a9f237..2d69ae1dba 100644 --- a/editor/src/messages/portfolio/document/node_graph/generate_node_graph_overlay.rs +++ b/editor/src/messages/portfolio/document/node_graph/generate_node_graph_overlay.rs @@ -20,9 +20,10 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData let merge_nodes_and_bg_id = NodeId::new(); let render_overlay_id = NodeId::new(); let send_overlay_id = NodeId::new(); - let _cache_output_id = NodeId::new(); + let cache_output_id = NodeId::new(); // TODO: Replace with new cache node - let memo_implementation = DocumentNodeImplementation::ProtoNode(graphene_std::ops::identity::IDENTIFIER); + let identity_implementation = DocumentNodeImplementation::ProtoNode(graphene_std::ops::identity::IDENTIFIER); + let memo_implementation = DocumentNodeImplementation::ProtoNode(graphene_std::memo::memo::IDENTIFIER); DocumentNode { inputs: vec![ @@ -32,7 +33,7 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData ], implementation: DocumentNodeImplementation::Network(NodeNetwork { - exports: vec![NodeInput::node(send_overlay_id, 0)], + exports: vec![NodeInput::node(cache_output_id, 0)], nodes: vec![ // Create the nodes ( @@ -50,7 +51,7 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData DocumentNode { call_argument: concrete!(UIContext), inputs: vec![NodeInput::node(generate_nodes_id, 0)], - implementation: memo_implementation.clone(), + implementation: identity_implementation.clone(), ..Default::default() }, ), @@ -80,7 +81,7 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData DocumentNode { call_argument: concrete!(UIContext), inputs: vec![NodeInput::node(generate_node_graph_bg, 0)], - implementation: memo_implementation.clone(), + implementation: identity_implementation.clone(), ..Default::default() }, ), @@ -114,16 +115,16 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData ..Default::default() }, ), - // Cache the full node graph so its not rerendered when the artwork changes - // ( - // cache_nodes_id, - // DocumentNode { - // call_argument: concrete!(UIContext), - // inputs: vec![NodeInput::node(send_overlay_id, 0)], - // implementation: memo_implementation.clone(), - // ..Default::default() - // }, - // ), + // Cache the full node graph so its not rerendered when nothing changes + ( + cache_output_id, + DocumentNode { + call_argument: concrete!(UIContext), + inputs: vec![NodeInput::node(send_overlay_id, 0)], + implementation: memo_implementation.clone(), + ..Default::default() + }, + ), ] .into_iter() .collect(), diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 924707861c..9e75fdcbd8 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -1622,26 +1622,13 @@ impl<'a> MessageHandler> 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, 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); - if self.native_node_graph_render { - let node_graph_render_data = node_graph_overlay::types::NodeGraphOverlayData { - nodes_to_render, - open: graph_view_overlay_open, - in_selected_network: selection_network_path == breadcrumb_network_path, - previewed_node, - }; - self.node_graph_overlay = Some(super::generate_node_graph_overlay::generate_node_graph_overlay(node_graph_render_data, graph_fade_artwork_percentage)); - responses.add(PortfolioMessage::SubmitActiveGraphRender); + + if !self.should_render_svelte_nodes { + // Generate and render node graph overlay network } else { - responses.add(FrontendMessage::UpdateNodeGraphRender { - nodes_to_render, - open: graph_view_overlay_open, - opacity: graph_fade_artwork_percentage, - in_selected_network: selection_network_path == breadcrumb_network_path, - previewed_node, - }); + 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); diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs index 9571658679..537cda8e80 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs @@ -111,7 +111,6 @@ impl NodeNetworkInterface { 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, @@ -401,7 +400,7 @@ impl NodeNetworkInterface { if exports.is_empty() { exports.push(None); } - let preview_wire = self.wire_to_root(graph_wire_style, network_path); + let preview_wire = self.wire_to_root(graph_wire_style, network_path).map(|wire| wire.to_svg()); FrontendExports { exports, preview_wire } } @@ -512,7 +511,7 @@ impl NodeNetworkInterface { } /// 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 { + pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option { let input = InputConnector::Export(0); let current_export = self.upstream_output_connector(&input, network_path)?; @@ -536,6 +535,6 @@ impl NodeNetworkInterface { 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()) + Some(vector_wire) } } diff --git a/editor/src/test_utils.rs b/editor/src/test_utils.rs index 9bbb022da4..32ce75e835 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -301,13 +301,7 @@ pub trait FrontendMessageTestUtils { impl FrontendMessageTestUtils for FrontendMessage { fn check_node_graph_error(&self) { - let FrontendMessage::UpdateNodeGraphSvelteRender { nodes_to_render, .. } = self else { return }; - - for node in nodes_to_render { - if let Some(error) = &node.metadata.errors { - panic!("error on {}: {}", node.metadata.display_name, error); - } - } + // TODO: Implement } } diff --git a/node-graph/gcore/src/node_graph_overlay.rs b/node-graph/gcore/src/node_graph_overlay.rs index fb6b18f223..bebeb0a102 100644 --- a/node-graph/gcore/src/node_graph_overlay.rs +++ b/node-graph/gcore/src/node_graph_overlay.rs @@ -4,7 +4,7 @@ use crate::{ Graphic, node_graph_overlay::{ background::generate_background, - nodes_and_wires::{draw_layers, draw_nodes}, + nodes_and_wires::{draw_layers, draw_nodes, draw_wires}, types::NodeGraphOverlayData, ui_context::{UIContext, UIRuntimeResponse}, }, @@ -19,11 +19,14 @@ pub mod types; pub mod ui_context; #[node_macro::node(skip_impl)] -pub fn generate_nodes(_: impl Ctx, node_graph_overlay_data: NodeGraphOverlayData) -> Table { +pub fn generate_nodes(_: impl Ctx, mut node_graph_overlay_data: NodeGraphOverlayData) -> Table { let mut nodes_and_wires = Table::new(); let layers = draw_layers(&node_graph_overlay_data.nodes_to_render); nodes_and_wires.extend(layers); + let wires = draw_wires(&mut node_graph_overlay_data.nodes_to_render); + nodes_and_wires.extend(wires); + let nodes = draw_nodes(&node_graph_overlay_data.nodes_to_render); nodes_and_wires.extend(nodes); diff --git a/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs b/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs index 33e39c1cdd..48ba8cf9ed 100644 --- a/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs +++ b/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs @@ -13,7 +13,10 @@ use crate::{ table::{Table, TableRow}, text::{self, TextAlign, TypesettingConfig}, transform::ApplyTransform, - vector::{Vector, style::Fill}, + vector::{ + Vector, + style::{Fill, Stroke}, + }, }; pub fn draw_nodes(nodes: &Vec) -> Table { @@ -22,12 +25,13 @@ pub fn draw_nodes(nodes: &Vec) -> Table { if let Some(frontend_node) = node_to_render.node_or_layer.node.as_ref() { let x = frontend_node.position.x as f64 * GRID_SIZE; let y = frontend_node.position.y as f64 * GRID_SIZE + GRID_SIZE / 2.; - let w = 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 height = 1 + number_of_exposed_inputs; - let h = height as f64 * GRID_SIZE; + let number_of_exposed_outputs = frontend_node.outputs.iter().skip(1).filter(|x| x.is_some()).count(); + let number_of_rows = 1 + number_of_exposed_inputs.max(number_of_exposed_outputs); + let node_height = number_of_rows as f64 * GRID_SIZE; - let node_rect = RoundedRect::new(x, y, x + w, y + h, 2.); + let node_rect = RoundedRect::new(x, y, x + node_width, y + node_height, 2.); let node_bez_path = node_rect.to_path(BEZ_PATH_TOLERANCE); // Background table @@ -54,7 +58,51 @@ pub fn draw_nodes(nodes: &Vec) -> Table { }; node_table.push(TableRow::new_from_element(Graphic::Vector(bg_table))); - // Border table + // Border mask table is the region where to display the border + let mut border_mask_path = BezPath::new(); + border_mask_path.move_to((-2., -2.)); + border_mask_path.line_to((node_width + 2., -2.)); + if frontend_node.outputs[0].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., 20.)); + } + border_mask_path.line_to((node_width + 2., 20.)); + for row in 1..number_of_rows { + 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()) { + 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., 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() { + 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()) { + 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.)); + } + } + if frontend_node.inputs[0].is_some() { + 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.)); + } + border_mask_path.line_to((-2., -2.)); + border_mask_path.close_path(); + let mut border_mask_vector = Vector::from_bezpath(border_mask_path); + border_mask_vector.style.fill = Fill::Solid(Color::WHITE); + let mut border_mask_row = TableRow::new_from_element(border_mask_vector); + border_mask_row.alpha_blending.fill = 0.; + border_mask_row.transform = DAffine2::from_translation(DVec2::new(x, y)); + let border_mask_table = Table::new_from_row(border_mask_row); + node_table.push(TableRow::new_from_element(Graphic::Vector(border_mask_table))); + + // Border table is implemented as a clip mask let mut border_table = Table::new(); let mut border_vector = Vector::from_bezpath(node_bez_path); let primary_output_color = frontend_node.outputs[0] @@ -63,35 +111,74 @@ pub fn draw_nodes(nodes: &Vec) -> Table { .unwrap_or(FrontendGraphDataType::General.data_color_dim()); let border_color = Color::from_rgba8_no_srgb(primary_output_color).unwrap(); border_vector.style.stroke = Some(crate::vector::style::Stroke::new(Some(border_color), 1.)); - border_table.push(TableRow::new_from_element(border_vector)); + let mut border_vector_row = TableRow::new_from_element(border_vector); + border_vector_row.alpha_blending.clip = true; + border_table.push(border_vector_row); node_table.push(TableRow::new_from_element(Graphic::Vector(border_table))); - // Border mask table - let mut border_mask_table = Table::new(); - let mut border_masks = BezPath::new(); - if frontend_node.inputs[0].is_some() { - let index = 0; - border_masks.extend(Rect::new(-8., index as f64 * GRID_SIZE + 4., 8., index as f64 * GRID_SIZE + 20.).to_path(BEZ_PATH_TOLERANCE)); + let typesetting = TypesettingConfig { + font_size: 14., + line_height_ratio: 1.2, + character_spacing: 0.0, + max_width: None, + max_height: None, + tilt: 0.0, + align: TextAlign::Left, + }; + + // Names for each row + let font_blob = Some(text::load_font(SOURCE_SANS_FONT_DATA)); + let mut node_text = crate::text::to_path(&node_to_render.metadata.display_name, font_blob, typesetting, false); + for text_row in node_text.iter_mut() { + *text_row.transform = DAffine2::from_translation(DVec2::new(x + 8., y + 3.)); } - for index in 1..=frontend_node.inputs.iter().skip(1).filter(|input| input.is_some()).count() { - border_masks.extend(Rect::new(-8., index as f64 * GRID_SIZE + 4., 8., index as f64 * GRID_SIZE + 20.).to_path(BEZ_PATH_TOLERANCE)); + for (row, input) in frontend_node.inputs.iter().enumerate().skip(1) { + if let Some(input) = input { + 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); + 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.)); + } + node_text.extend(input_row_text); + } else if let Some(Some(output)) = frontend_node.outputs.get(row) { + 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); + // Find width to right align text + let full_text_width = if let RenderBoundingBox::Rectangle(bbox) = output_row_text.bounding_box(DAffine2::default(), true) { + bbox[1].x - bbox[0].x + } else { + 0. + }; + // Account for clipping + let text_width = full_text_width.min(5. * GRID_SIZE - 16.); + let left_offset = 5. * GRID_SIZE - 8. - text_width; + for text_row in output_row_text.iter_mut() { + *text_row.transform = DAffine2::from_translation(DVec2::new(x + 8. + left_offset, y + 24. * row as f64 + 3.)); + } + node_text.extend(output_row_text); + } } - if frontend_node.outputs[0].is_some() { - let index = 0; - border_masks.extend(Rect::new(-8. + 5. * GRID_SIZE, index as f64 * GRID_SIZE + 4., 8. + 5. * GRID_SIZE, index as f64 * GRID_SIZE + 20.).to_path(BEZ_PATH_TOLERANCE)); - } - for index in 1..=frontend_node.outputs.iter().skip(1).filter(|output| output.is_some()).count() { - border_masks.extend(Rect::new(-8. + 5. * GRID_SIZE, index as f64 * GRID_SIZE + 4., 8. + 5. * GRID_SIZE, index as f64 * GRID_SIZE + 20.).to_path(BEZ_PATH_TOLERANCE)); - } - let mut border_masks_vector = Vector::from_bezpath(border_masks); - border_masks_vector.style.fill = Fill::Solid(node_color); - let mut border_masks_row = TableRow::new_from_element(border_masks_vector); - border_masks_row.alpha_blending.clip = true; - border_masks_row.transform.left_apply_transform(&DAffine2::from_translation(DVec2::new(x, y))); - border_mask_table.push(border_masks_row); - node_table.push(TableRow::new_from_element(Graphic::Vector(border_mask_table))); + + // for text_row in node_text.iter_mut() { + // text_row.element.style.fill = Fill::Solid(Color::WHITE); + // } + + let node_text_row = TableRow::new_from_element(Graphic::Vector(node_text)); + // node_text_row.transform.left_apply_transform(&DAffine2::from_translation(DVec2::new(x + 8., y + 8.))); + // log::debug!("node_text_row {:?}", node_text_row.transform); + node_table.push(node_text_row); + + // Add black clipping path to view text in node + let text_area = Rect::new(x + 8., y, x + node_width - 8., y + node_height); + let mut text_area_vector = Vector::from_bezpath(text_area.to_path(BEZ_PATH_TOLERANCE)); + text_area_vector.style.fill = Fill::Solid(Color::WHITE); + let mut text_area_row = TableRow::new_from_element(text_area_vector); + text_area_row.alpha_blending.clip = true; + 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 } @@ -162,35 +249,55 @@ pub fn draw_layers(nodes: &Vec) -> Table { background_table.push(TableRow::new_from_element(bg_vector)); layer_table.push(TableRow::new_from_element(Graphic::Vector(background_table))); - // Border + // Border mask is a transparent region for where to draw the border + let mut border_mask_table = Table::new(); + let mut border_mask = BezPath::new(); + border_mask.move_to((-2., -2.)); + border_mask.line_to((chain_width - 8., -2.)); + border_mask.line_to((chain_width - 8., 2.)); + border_mask.line_to((chain_width + GRID_SIZE * 3. + 8., 2.)); + border_mask.line_to((chain_width + GRID_SIZE * 3. + 8., -2.)); + border_mask.line_to((full_layer_width + 2., -2.)); + border_mask.line_to((full_layer_width + 2., 12.)); + border_mask.line_to((full_layer_width - 2., 12.)); + border_mask.line_to((full_layer_width - 2., 36.)); + border_mask.line_to((full_layer_width + 2., 36.)); + border_mask.line_to((full_layer_width + 2., 50.)); + border_mask.line_to((full_layer_width + 2., 50.)); + border_mask.line_to((chain_width + GRID_SIZE * 3. + 8., 50.)); + border_mask.line_to((chain_width + GRID_SIZE * 3. + 8., 46.)); + border_mask.line_to((chain_width - 8., 46.)); + border_mask.line_to((chain_width - 8., 50.)); + border_mask.line_to((-2., 50.)); + border_mask.line_to((-2., 32.)); + if frontend_layer.layer_has_left_border_gap && chain_width > 0.1 { + border_mask.line_to((2., 32.)); + border_mask.line_to((2., 16.)); + } + border_mask.line_to((-2., 16.)); + border_mask.line_to((-2., -2.)); + border_mask.close_path(); + + let mut border_mask_vector = Vector::from_bezpath(border_mask); + border_mask_vector.style.fill = Fill::Solid(Color::WHITE); + let mut border_mask_row = TableRow::new_from_element(border_mask_vector); + border_mask_row.alpha_blending.fill = 0.; + border_mask_row.transform.left_apply_transform(&DAffine2::from_translation(DVec2::new(x0, y0))); + border_mask_table.push(border_mask_row); + layer_table.push(TableRow::new_from_element(Graphic::Vector(border_mask_table))); + + // Border is implemented as a mask let mut border_table = Table::new(); let border_rect = RoundedRect::new(x0, y0, x0 + full_layer_width, y0 + h, 8.); let bez_path = border_rect.to_path(BEZ_PATH_TOLERANCE); let mut border_vector = Vector::from_bezpath(bez_path); let border_color = Color::from_rgba8_no_srgb(COLOR_5_DULLGRAY).unwrap(); border_vector.style.stroke = Some(crate::vector::style::Stroke::new(Some(border_color), 1.)); - border_table.push(TableRow::new_from_element(border_vector)); + let mut layer_border_clip = TableRow::new_from_element(border_vector); + layer_border_clip.alpha_blending.clip = true; + border_table.push(layer_border_clip); layer_table.push(TableRow::new_from_element(Graphic::Vector(border_table))); - // Border mask - let mut border_mask_table = Table::new(); - let mut border_mask = BezPath::new(); - if frontend_layer.layer_has_left_border_gap && chain_width > 0.1 { - let left_input_mask = Rect::new(-8., 16., 8., 32.); - border_mask.extend(left_input_mask.to_path(BEZ_PATH_TOLERANCE)); - } - let thumbnail_mask = Rect::new(chain_width - 8., -2., chain_width + 72. + 8. * 2., 2. * GRID_SIZE + 2.); - border_mask.extend(thumbnail_mask.to_path(BEZ_PATH_TOLERANCE)); - let right_visibility_mask = Rect::new(full_layer_width - 12., 12., full_layer_width + 12., 36.); - border_mask.extend(right_visibility_mask.to_path(BEZ_PATH_TOLERANCE)); - let mut border_mask_vector = Vector::from_bezpath(border_mask); - border_mask_vector.style.fill = Fill::Solid(background); - let mut border_mask_row = TableRow::new_from_element(border_mask_vector); - border_mask_row.alpha_blending.clip = true; - border_mask_row.transform.left_apply_transform(&DAffine2::from_translation(DVec2::new(x0, y0))); - border_mask_table.push(border_mask_row); - layer_table.push(TableRow::new_from_element(Graphic::Vector(border_mask_table))); - // The top layer contains the ports,thumbnail,text, etc for text_row in text_table.iter_mut() { text_row.element.style.fill = Fill::Solid(Color::WHITE); @@ -245,41 +352,21 @@ fn node_first_row(x0: f64, y0: f64, rounded_bottom: bool) -> Vector { vector } -// fn node_secondary_row(x0: f64, y: f64, index: usize, rounded_bottom: bool) -> Vector { -// let y0 = y + index as f64 * GRID_SIZE; -// let x1 = x0 + GRID_SIZE * 5.; -// let y1 = y0 + GRID_SIZE; -// let r = 2.; -// let bez_path = if rounded_bottom { -// let mut path = BezPath::new(); -// path.move_to((x0, y0)); +fn port() -> BezPath { + 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); + }) +} -// // Top edge -// path.line_to((x1, y0)); - -// // Right side down -// path.line_to((x1, y1 - r)); - -// // Bottom-right corner arc -// path.quad_to((x1, y1), (x1 - r, y1)); - -// // Bottom edge -// path.line_to((x0 + r, y1)); - -// // Bottom-left corner arc -// path.quad_to((x0, y1), (x0, y1 - r)); - -// // Left side up -// path.line_to((x0, y0)); - -// path.close_path(); -// path -// } else { -// Rect::new(x0, y0, x1, y1).to_path(BEZ_PATH_TOLERANCE) -// }; -// let mut vector = Vector::from_bezpath(bez_path); -// let mut color = Color::from_rgba8_no_srgb(COLOR_0_BLACK).unwrap(); -// color.set_alpha(0.33); -// vector.style.fill = Fill::Solid(color); -// vector -// } +pub fn draw_wires(nodes: &mut Vec) -> Table { + let mut wire_table = Table::new(); + for node in nodes { + for (wire_string, thick, data_type) in &mut node.wires { + let mut wire_vector = Vector::from_bezpath(std::mem::take(wire_string)); + let weight = if *thick { 8. } else { 2. }; + wire_vector.style.set_stroke(Stroke::new(Some(Color::from_rgba8_no_srgb(data_type.data_color_dim()).unwrap()), weight)); + wire_table.push(TableRow::new_from_element(wire_vector)); + } + } + Table::new_from_element(Graphic::Vector(wire_table)) +} diff --git a/node-graph/gcore/src/node_graph_overlay/types.rs b/node-graph/gcore/src/node_graph_overlay/types.rs index 8c52d02f62..ff85dee33b 100644 --- a/node-graph/gcore/src/node_graph_overlay/types.rs +++ b/node-graph/gcore/src/node_graph_overlay/types.rs @@ -1,4 +1,5 @@ use glam::{DAffine2, DVec2}; +use kurbo::BezPath; use crate::{node_graph_overlay::consts::*, uuid::NodeId}; use std::hash::{Hash, Hasher}; @@ -10,21 +11,22 @@ pub struct NodeGraphTransform { pub y: f64, } -impl NodeGraphTransform { - pub fn to_daffine2(&self) -> DAffine2 { - DAffine2::from_scale_angle_translation(DVec2::splat(self.scale), 0.0, DVec2::new(self.x, self.y)) - } -} - impl Hash for NodeGraphTransform { fn hash(&self, state: &mut H) { + // Convert f64 to u64 bit pattern for hashing self.scale.to_bits().hash(state); self.x.to_bits().hash(state); self.y.to_bits().hash(state); } } -#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)] +impl NodeGraphTransform { + pub fn to_daffine2(&self) -> DAffine2 { + DAffine2::from_scale_angle_translation(DVec2::splat(self.scale), 0.0, DVec2::new(self.x, self.y)) + } +} + +#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize)] pub struct NodeGraphOverlayData { pub nodes_to_render: Vec, pub open: bool, @@ -33,13 +35,20 @@ pub struct NodeGraphOverlayData { pub previewed_node: Option, } -#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)] +#[derive(Clone, Debug, Default, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)] pub struct FrontendNodeToRender { pub metadata: FrontendNodeMetadata, #[serde(rename = "nodeOrLayer")] pub node_or_layer: FrontendNodeOrLayer, //TODO: Remove - pub wires: Vec<(String, bool, FrontendGraphDataType)>, + pub wires: Vec<(BezPath, bool, FrontendGraphDataType)>, +} + +impl Hash for FrontendNodeToRender { + fn hash(&self, state: &mut H) { + self.metadata.hash(state); + self.node_or_layer.hash(state); + } } // Metadata that is common to nodes and layers diff --git a/node-graph/gcore/src/node_graph_overlay/ui_context.rs b/node-graph/gcore/src/node_graph_overlay/ui_context.rs index 62ca8aa858..a5a22cc958 100644 --- a/node-graph/gcore/src/node_graph_overlay/ui_context.rs +++ b/node-graph/gcore/src/node_graph_overlay/ui_context.rs @@ -14,6 +14,20 @@ pub struct UIContextImpl { pub response_sender: Sender, } +use std::hash::{Hash, Hasher}; +impl Hash for UIContextImpl { + fn hash(&self, state: &mut H) { + self.transform.hash(state); + self.resolution.hash(state); + } +} + +impl PartialEq for UIContextImpl { + fn eq(&self, other: &Self) -> bool { + self.transform == other.transform && self.resolution == other.resolution + } +} + #[derive(Debug, Clone, dyn_any::DynAny)] pub enum UIRuntimeResponse { RuntimeReady, diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 96fca973c0..58af7eb576 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -253,6 +253,7 @@ fn node_registry() -> HashMap, input: UIContext, fn_params: [UIContext => ()]), async_node!(graphene_core::node_graph_overlay::GenerateNodesNode<_>, input: UIContext, fn_params: [UIContext => NodeGraphOverlayData]), async_node!(graphene_core::node_graph_overlay::TransformNodesNode<_>, input: UIContext, fn_params: [UIContext =>Table]), async_node!(graphene_core::node_graph_overlay::DotGridBackgroundNode<_>, input: UIContext, fn_params: [UIContext =>f64]),