mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 23:48:11 +08:00
wires and clip paths
This commit is contained in:
@@ -269,17 +269,7 @@ pub enum FrontendMessage {
|
|||||||
UpdateMouseCursor {
|
UpdateMouseCursor {
|
||||||
cursor: MouseCursorIcon,
|
cursor: MouseCursorIcon,
|
||||||
},
|
},
|
||||||
UpdateNodeGraphSvelteRender {
|
RequestNativeNodeGraphRender,
|
||||||
#[serde(rename = "nodesToRender")]
|
|
||||||
nodes_to_render: Vec<FrontendNodeToRender>,
|
|
||||||
open: bool,
|
|
||||||
opacity: f64,
|
|
||||||
#[serde(rename = "inSelectedNetwork")]
|
|
||||||
in_selected_network: bool,
|
|
||||||
// Displays a dashed border around the node
|
|
||||||
#[serde(rename = "previewedNode")]
|
|
||||||
previewed_node: Option<NodeId>,
|
|
||||||
},
|
|
||||||
UpdateShouldRenderSvelteNodes {
|
UpdateShouldRenderSvelteNodes {
|
||||||
#[serde(rename = "shouldRenderSvelteNodes")]
|
#[serde(rename = "shouldRenderSvelteNodes")]
|
||||||
should_render_svelte_nodes: bool,
|
should_render_svelte_nodes: bool,
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData
|
|||||||
let merge_nodes_and_bg_id = NodeId::new();
|
let merge_nodes_and_bg_id = NodeId::new();
|
||||||
let render_overlay_id = NodeId::new();
|
let render_overlay_id = NodeId::new();
|
||||||
let send_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
|
// 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 {
|
DocumentNode {
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
@@ -32,7 +33,7 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData
|
|||||||
],
|
],
|
||||||
|
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(send_overlay_id, 0)],
|
exports: vec![NodeInput::node(cache_output_id, 0)],
|
||||||
nodes: vec![
|
nodes: vec![
|
||||||
// Create the nodes
|
// Create the nodes
|
||||||
(
|
(
|
||||||
@@ -50,7 +51,7 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData
|
|||||||
DocumentNode {
|
DocumentNode {
|
||||||
call_argument: concrete!(UIContext),
|
call_argument: concrete!(UIContext),
|
||||||
inputs: vec![NodeInput::node(generate_nodes_id, 0)],
|
inputs: vec![NodeInput::node(generate_nodes_id, 0)],
|
||||||
implementation: memo_implementation.clone(),
|
implementation: identity_implementation.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -80,7 +81,7 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData
|
|||||||
DocumentNode {
|
DocumentNode {
|
||||||
call_argument: concrete!(UIContext),
|
call_argument: concrete!(UIContext),
|
||||||
inputs: vec![NodeInput::node(generate_node_graph_bg, 0)],
|
inputs: vec![NodeInput::node(generate_node_graph_bg, 0)],
|
||||||
implementation: memo_implementation.clone(),
|
implementation: identity_implementation.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -114,16 +115,16 @@ pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Cache the full node graph so its not rerendered when the artwork changes
|
// Cache the full node graph so its not rerendered when nothing changes
|
||||||
// (
|
(
|
||||||
// cache_nodes_id,
|
cache_output_id,
|
||||||
// DocumentNode {
|
DocumentNode {
|
||||||
// call_argument: concrete!(UIContext),
|
call_argument: concrete!(UIContext),
|
||||||
// inputs: vec![NodeInput::node(send_overlay_id, 0)],
|
inputs: vec![NodeInput::node(send_overlay_id, 0)],
|
||||||
// implementation: memo_implementation.clone(),
|
implementation: memo_implementation.clone(),
|
||||||
// ..Default::default()
|
..Default::default()
|
||||||
// },
|
},
|
||||||
// ),
|
),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|||||||
@@ -1622,26 +1622,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
responses.add(PropertiesPanelMessage::Refresh);
|
responses.add(PropertiesPanelMessage::Refresh);
|
||||||
responses.add(NodeGraphMessage::UpdateActionButtons);
|
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();
|
if !self.should_render_svelte_nodes {
|
||||||
let previewed_node = network_interface.previewed_node(breadcrumb_network_path);
|
// Generate and render node graph overlay network
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphRender {
|
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, preferences.graph_wire_style, breadcrumb_network_path);
|
||||||
nodes_to_render,
|
self.frontend_nodes = nodes_to_render.iter().map(|node| node.metadata.node_id).collect();
|
||||||
open: graph_view_overlay_open,
|
let previewed_node = network_interface.previewed_node(breadcrumb_network_path);
|
||||||
opacity: graph_fade_artwork_percentage,
|
|
||||||
in_selected_network: selection_network_path == breadcrumb_network_path,
|
|
||||||
previewed_node,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
||||||
|
|
||||||
|
|||||||
+3
-4
@@ -111,7 +111,6 @@ impl NodeNetworkInterface {
|
|||||||
self.upstream_output_connector(&InputConnector::node(node_id, input_index), network_path)
|
self.upstream_output_connector(&InputConnector::node(node_id, input_index), network_path)
|
||||||
.is_some_and(|output| !matches!(output, OutputConnector::Import(_)))
|
.is_some_and(|output| !matches!(output, OutputConnector::Import(_)))
|
||||||
})
|
})
|
||||||
.map(|path| path.to_svg())
|
|
||||||
.map(|wire| {
|
.map(|wire| {
|
||||||
(
|
(
|
||||||
wire,
|
wire,
|
||||||
@@ -401,7 +400,7 @@ impl NodeNetworkInterface {
|
|||||||
if exports.is_empty() {
|
if exports.is_empty() {
|
||||||
exports.push(None);
|
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 }
|
FrontendExports { exports, preview_wire }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +511,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// When previewing, there may be a second path to the root node.
|
/// 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> {
|
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<BezPath> {
|
||||||
let input = InputConnector::Export(0);
|
let input = InputConnector::Export(0);
|
||||||
let current_export = self.upstream_output_connector(&input, network_path)?;
|
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 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);
|
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, false, graph_wire_style);
|
||||||
|
|
||||||
Some(vector_wire.to_svg())
|
Some(vector_wire)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,13 +301,7 @@ pub trait FrontendMessageTestUtils {
|
|||||||
|
|
||||||
impl FrontendMessageTestUtils for FrontendMessage {
|
impl FrontendMessageTestUtils for FrontendMessage {
|
||||||
fn check_node_graph_error(&self) {
|
fn check_node_graph_error(&self) {
|
||||||
let FrontendMessage::UpdateNodeGraphSvelteRender { nodes_to_render, .. } = self else { return };
|
// TODO: Implement
|
||||||
|
|
||||||
for node in nodes_to_render {
|
|
||||||
if let Some(error) = &node.metadata.errors {
|
|
||||||
panic!("error on {}: {}", node.metadata.display_name, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
Graphic,
|
Graphic,
|
||||||
node_graph_overlay::{
|
node_graph_overlay::{
|
||||||
background::generate_background,
|
background::generate_background,
|
||||||
nodes_and_wires::{draw_layers, draw_nodes},
|
nodes_and_wires::{draw_layers, draw_nodes, draw_wires},
|
||||||
types::NodeGraphOverlayData,
|
types::NodeGraphOverlayData,
|
||||||
ui_context::{UIContext, UIRuntimeResponse},
|
ui_context::{UIContext, UIRuntimeResponse},
|
||||||
},
|
},
|
||||||
@@ -19,11 +19,14 @@ pub mod types;
|
|||||||
pub mod ui_context;
|
pub mod ui_context;
|
||||||
|
|
||||||
#[node_macro::node(skip_impl)]
|
#[node_macro::node(skip_impl)]
|
||||||
pub fn generate_nodes(_: impl Ctx, 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 = 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);
|
||||||
|
nodes_and_wires.extend(wires);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ use crate::{
|
|||||||
table::{Table, TableRow},
|
table::{Table, TableRow},
|
||||||
text::{self, TextAlign, TypesettingConfig},
|
text::{self, TextAlign, TypesettingConfig},
|
||||||
transform::ApplyTransform,
|
transform::ApplyTransform,
|
||||||
vector::{Vector, style::Fill},
|
vector::{
|
||||||
|
Vector,
|
||||||
|
style::{Fill, Stroke},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
|
pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
|
||||||
@@ -22,12 +25,13 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
|
|||||||
if let Some(frontend_node) = node_to_render.node_or_layer.node.as_ref() {
|
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 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 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 number_of_exposed_inputs = frontend_node.inputs.iter().skip(1).filter(|x| x.is_some()).count();
|
||||||
let height = 1 + number_of_exposed_inputs;
|
let number_of_exposed_outputs = frontend_node.outputs.iter().skip(1).filter(|x| x.is_some()).count();
|
||||||
let h = height as f64 * GRID_SIZE;
|
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);
|
let node_bez_path = node_rect.to_path(BEZ_PATH_TOLERANCE);
|
||||||
|
|
||||||
// Background table
|
// Background table
|
||||||
@@ -54,7 +58,51 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
|
|||||||
};
|
};
|
||||||
node_table.push(TableRow::new_from_element(Graphic::Vector(bg_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_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.outputs[0]
|
||||||
@@ -63,35 +111,74 @@ pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
|
|||||||
.unwrap_or(FrontendGraphDataType::General.data_color_dim());
|
.unwrap_or(FrontendGraphDataType::General.data_color_dim());
|
||||||
let border_color = Color::from_rgba8_no_srgb(primary_output_color).unwrap();
|
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_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)));
|
node_table.push(TableRow::new_from_element(Graphic::Vector(border_table)));
|
||||||
|
|
||||||
// Border mask table
|
let typesetting = TypesettingConfig {
|
||||||
let mut border_mask_table = Table::new();
|
font_size: 14.,
|
||||||
let mut border_masks = BezPath::new();
|
line_height_ratio: 1.2,
|
||||||
if frontend_node.inputs[0].is_some() {
|
character_spacing: 0.0,
|
||||||
let index = 0;
|
max_width: None,
|
||||||
border_masks.extend(Rect::new(-8., index as f64 * GRID_SIZE + 4., 8., index as f64 * GRID_SIZE + 20.).to_path(BEZ_PATH_TOLERANCE));
|
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() {
|
for (row, input) in frontend_node.inputs.iter().enumerate().skip(1) {
|
||||||
border_masks.extend(Rect::new(-8., index as f64 * GRID_SIZE + 4., 8., index as f64 * GRID_SIZE + 20.).to_path(BEZ_PATH_TOLERANCE));
|
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;
|
// for text_row in node_text.iter_mut() {
|
||||||
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));
|
// text_row.element.style.fill = Fill::Solid(Color::WHITE);
|
||||||
}
|
// }
|
||||||
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 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.)));
|
||||||
let mut border_masks_vector = Vector::from_bezpath(border_masks);
|
// log::debug!("node_text_row {:?}", node_text_row.transform);
|
||||||
border_masks_vector.style.fill = Fill::Solid(node_color);
|
node_table.push(node_text_row);
|
||||||
let mut border_masks_row = TableRow::new_from_element(border_masks_vector);
|
|
||||||
border_masks_row.alpha_blending.clip = true;
|
// Add black clipping path to view text in node
|
||||||
border_masks_row.transform.left_apply_transform(&DAffine2::from_translation(DVec2::new(x, y)));
|
let text_area = Rect::new(x + 8., y, x + node_width - 8., y + node_height);
|
||||||
border_mask_table.push(border_masks_row);
|
let mut text_area_vector = Vector::from_bezpath(text_area.to_path(BEZ_PATH_TOLERANCE));
|
||||||
node_table.push(TableRow::new_from_element(Graphic::Vector(border_mask_table)));
|
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
|
node_table
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,35 +249,55 @@ pub fn draw_layers(nodes: &Vec<FrontendNodeToRender>) -> Table<Graphic> {
|
|||||||
background_table.push(TableRow::new_from_element(bg_vector));
|
background_table.push(TableRow::new_from_element(bg_vector));
|
||||||
layer_table.push(TableRow::new_from_element(Graphic::Vector(background_table)));
|
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 mut border_table = Table::new();
|
||||||
let border_rect = RoundedRect::new(x0, y0, x0 + full_layer_width, y0 + h, 8.);
|
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 bez_path = border_rect.to_path(BEZ_PATH_TOLERANCE);
|
||||||
let mut border_vector = Vector::from_bezpath(bez_path);
|
let mut border_vector = Vector::from_bezpath(bez_path);
|
||||||
let border_color = Color::from_rgba8_no_srgb(COLOR_5_DULLGRAY).unwrap();
|
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_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)));
|
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
|
// The top layer contains the ports,thumbnail,text, etc
|
||||||
for text_row in text_table.iter_mut() {
|
for text_row in text_table.iter_mut() {
|
||||||
text_row.element.style.fill = Fill::Solid(Color::WHITE);
|
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
|
vector
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn node_secondary_row(x0: f64, y: f64, index: usize, rounded_bottom: bool) -> Vector {
|
fn port() -> BezPath {
|
||||||
// let y0 = y + index as f64 * GRID_SIZE;
|
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 x1 = x0 + GRID_SIZE * 5.;
|
panic!("Could not parse port svg from string: {}", e);
|
||||||
// let y1 = y0 + GRID_SIZE;
|
})
|
||||||
// let r = 2.;
|
}
|
||||||
// let bez_path = if rounded_bottom {
|
|
||||||
// let mut path = BezPath::new();
|
|
||||||
// path.move_to((x0, y0));
|
|
||||||
|
|
||||||
// // Top edge
|
pub fn draw_wires(nodes: &mut Vec<FrontendNodeToRender>) -> Table<Graphic> {
|
||||||
// path.line_to((x1, y0));
|
let mut wire_table = Table::new();
|
||||||
|
for node in nodes {
|
||||||
// // Right side down
|
for (wire_string, thick, data_type) in &mut node.wires {
|
||||||
// path.line_to((x1, y1 - r));
|
let mut wire_vector = Vector::from_bezpath(std::mem::take(wire_string));
|
||||||
|
let weight = if *thick { 8. } else { 2. };
|
||||||
// // Bottom-right corner arc
|
wire_vector.style.set_stroke(Stroke::new(Some(Color::from_rgba8_no_srgb(data_type.data_color_dim()).unwrap()), weight));
|
||||||
// path.quad_to((x1, y1), (x1 - r, y1));
|
wire_table.push(TableRow::new_from_element(wire_vector));
|
||||||
|
}
|
||||||
// // Bottom edge
|
}
|
||||||
// path.line_to((x0 + r, y1));
|
Table::new_from_element(Graphic::Vector(wire_table))
|
||||||
|
}
|
||||||
// // 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
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use kurbo::BezPath;
|
||||||
|
|
||||||
use crate::{node_graph_overlay::consts::*, uuid::NodeId};
|
use crate::{node_graph_overlay::consts::*, uuid::NodeId};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@@ -10,21 +11,22 @@ pub struct NodeGraphTransform {
|
|||||||
pub y: f64,
|
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 {
|
impl Hash for NodeGraphTransform {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
// Convert f64 to u64 bit pattern for hashing
|
||||||
self.scale.to_bits().hash(state);
|
self.scale.to_bits().hash(state);
|
||||||
self.x.to_bits().hash(state);
|
self.x.to_bits().hash(state);
|
||||||
self.y.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 struct NodeGraphOverlayData {
|
||||||
pub nodes_to_render: Vec<FrontendNodeToRender>,
|
pub nodes_to_render: Vec<FrontendNodeToRender>,
|
||||||
pub open: bool,
|
pub open: bool,
|
||||||
@@ -33,13 +35,20 @@ pub struct NodeGraphOverlayData {
|
|||||||
pub previewed_node: Option<NodeId>,
|
pub previewed_node: Option<NodeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 struct FrontendNodeToRender {
|
||||||
pub metadata: FrontendNodeMetadata,
|
pub metadata: FrontendNodeMetadata,
|
||||||
#[serde(rename = "nodeOrLayer")]
|
#[serde(rename = "nodeOrLayer")]
|
||||||
pub node_or_layer: FrontendNodeOrLayer,
|
pub node_or_layer: FrontendNodeOrLayer,
|
||||||
//TODO: Remove
|
//TODO: Remove
|
||||||
pub wires: Vec<(String, bool, FrontendGraphDataType)>,
|
pub wires: Vec<(BezPath, bool, FrontendGraphDataType)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for FrontendNodeToRender {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.metadata.hash(state);
|
||||||
|
self.node_or_layer.hash(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata that is common to nodes and layers
|
// Metadata that is common to nodes and layers
|
||||||
|
|||||||
@@ -14,6 +14,20 @@ pub struct UIContextImpl {
|
|||||||
pub response_sender: Sender<UIRuntimeResponse>,
|
pub response_sender: Sender<UIRuntimeResponse>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
impl Hash for UIContextImpl {
|
||||||
|
fn hash<H: Hasher>(&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)]
|
#[derive(Debug, Clone, dyn_any::DynAny)]
|
||||||
pub enum UIRuntimeResponse {
|
pub enum UIRuntimeResponse {
|
||||||
RuntimeReady,
|
RuntimeReady,
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
node_io
|
node_io
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
async_node!(graphene_core::memo::MemoNode<_, _>, 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::GenerateNodesNode<_>, input: UIContext, fn_params: [UIContext => NodeGraphOverlayData]),
|
||||||
async_node!(graphene_core::node_graph_overlay::TransformNodesNode<_>, input: UIContext, fn_params: [UIContext =>Table<Graphic>]),
|
async_node!(graphene_core::node_graph_overlay::TransformNodesNode<_>, input: UIContext, fn_params: [UIContext =>Table<Graphic>]),
|
||||||
async_node!(graphene_core::node_graph_overlay::DotGridBackgroundNode<_>, input: UIContext, fn_params: [UIContext =>f64]),
|
async_node!(graphene_core::node_graph_overlay::DotGridBackgroundNode<_>, input: UIContext, fn_params: [UIContext =>f64]),
|
||||||
|
|||||||
Reference in New Issue
Block a user