mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
wires and clip paths
This commit is contained in:
@@ -269,17 +269,7 @@ pub enum FrontendMessage {
|
||||
UpdateMouseCursor {
|
||||
cursor: MouseCursorIcon,
|
||||
},
|
||||
UpdateNodeGraphSvelteRender {
|
||||
#[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>,
|
||||
},
|
||||
RequestNativeNodeGraphRender,
|
||||
UpdateShouldRenderSvelteNodes {
|
||||
#[serde(rename = "shouldRenderSvelteNodes")]
|
||||
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 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(),
|
||||
|
||||
@@ -1622,26 +1622,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
responses.add(NodeGraphMessage::UpdateActionButtons);
|
||||
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, 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);
|
||||
|
||||
|
||||
@@ -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<String> {
|
||||
pub fn wire_to_root(&mut self, graph_wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<BezPath> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Graphic> {
|
||||
pub fn generate_nodes(_: impl Ctx, mut node_graph_overlay_data: NodeGraphOverlayData) -> Table<Graphic> {
|
||||
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);
|
||||
|
||||
|
||||
@@ -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<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() {
|
||||
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<FrontendNodeToRender>) -> Table<Graphic> {
|
||||
};
|
||||
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<FrontendNodeToRender>) -> Table<Graphic> {
|
||||
.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<FrontendNodeToRender>) -> Table<Graphic> {
|
||||
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<FrontendNodeToRender>) -> Table<Graphic> {
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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<H: Hasher>(&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<FrontendNodeToRender>,
|
||||
pub open: bool,
|
||||
@@ -33,13 +35,20 @@ pub struct NodeGraphOverlayData {
|
||||
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 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<H: Hasher>(&self, state: &mut H) {
|
||||
self.metadata.hash(state);
|
||||
self.node_or_layer.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata that is common to nodes and layers
|
||||
|
||||
@@ -14,6 +14,20 @@ pub struct UIContextImpl {
|
||||
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)]
|
||||
pub enum UIRuntimeResponse {
|
||||
RuntimeReady,
|
||||
|
||||
@@ -253,6 +253,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
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::TransformNodesNode<_>, input: UIContext, fn_params: [UIContext =>Table<Graphic>]),
|
||||
async_node!(graphene_core::node_graph_overlay::DotGridBackgroundNode<_>, input: UIContext, fn_params: [UIContext =>f64]),
|
||||
|
||||
Reference in New Issue
Block a user