mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 00:48:12 +08:00
Improve native UI network
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
||||||
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendNodeType, Transform};
|
use crate::messages::portfolio::document::node_graph::utility_types::{BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendNodeType};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::WirePathInProgress;
|
use crate::messages::portfolio::document::utility_types::wires::WirePathInProgress;
|
||||||
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};
|
use graphene_std::node_graph_overlay::types::{FrontendExports, FrontendImport, FrontendNodeToRender, 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};
|
||||||
@@ -269,7 +269,7 @@ pub enum FrontendMessage {
|
|||||||
UpdateMouseCursor {
|
UpdateMouseCursor {
|
||||||
cursor: MouseCursorIcon,
|
cursor: MouseCursorIcon,
|
||||||
},
|
},
|
||||||
UpdateNodeGraphRender {
|
UpdateNodeGraphSvelteRender {
|
||||||
#[serde(rename = "nodesToRender")]
|
#[serde(rename = "nodesToRender")]
|
||||||
nodes_to_render: Vec<FrontendNodeToRender>,
|
nodes_to_render: Vec<FrontendNodeToRender>,
|
||||||
open: bool,
|
open: bool,
|
||||||
@@ -280,9 +280,13 @@ pub enum FrontendMessage {
|
|||||||
#[serde(rename = "previewedNode")]
|
#[serde(rename = "previewedNode")]
|
||||||
previewed_node: Option<NodeId>,
|
previewed_node: Option<NodeId>,
|
||||||
},
|
},
|
||||||
UpdateNativeNodeGraphRender {
|
UpdateShouldRenderSvelteNodes {
|
||||||
#[serde(rename = "nativeNodeGraphRender")]
|
#[serde(rename = "shouldRenderSvelteNodes")]
|
||||||
native_node_graph_render: bool,
|
should_render_svelte_nodes: bool,
|
||||||
|
},
|
||||||
|
UpdateNativeNodeGraphSVG {
|
||||||
|
#[serde(rename = "svgString")]
|
||||||
|
svg_string: String,
|
||||||
},
|
},
|
||||||
UpdateVisibleNodes {
|
UpdateVisibleNodes {
|
||||||
nodes: Vec<NodeId>,
|
nodes: Vec<NodeId>,
|
||||||
@@ -293,7 +297,7 @@ pub enum FrontendMessage {
|
|||||||
diff: Vec<WidgetDiff>,
|
diff: Vec<WidgetDiff>,
|
||||||
},
|
},
|
||||||
UpdateNodeGraphTransform {
|
UpdateNodeGraphTransform {
|
||||||
transform: Transform,
|
transform: NodeGraphTransform,
|
||||||
},
|
},
|
||||||
UpdateNodeThumbnail {
|
UpdateNodeThumbnail {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::node_graph::document_node_definitions;
|
use super::node_graph::document_node_definitions;
|
||||||
use super::node_graph::utility_types::Transform;
|
|
||||||
use super::overlays::utility_types::Pivot;
|
use super::overlays::utility_types::Pivot;
|
||||||
use super::utility_types::error::EditorError;
|
use super::utility_types::error::EditorError;
|
||||||
use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState};
|
use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState};
|
||||||
@@ -31,6 +30,7 @@ use glam::{DAffine2, DVec2, IVec2};
|
|||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
|
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
|
||||||
use graphene_std::math::quad::Quad;
|
use graphene_std::math::quad::Quad;
|
||||||
|
use graphene_std::node_graph_overlay::types::NodeGraphTransform;
|
||||||
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
|
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
|
||||||
use graphene_std::raster::BlendMode;
|
use graphene_std::raster::BlendMode;
|
||||||
use graphene_std::raster_types::Raster;
|
use graphene_std::raster_types::Raster;
|
||||||
@@ -1494,7 +1494,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
||||||
transform: Transform {
|
transform: NodeGraphTransform {
|
||||||
scale: transform.matrix2.x_axis.x,
|
scale: transform.matrix2.x_axis.x,
|
||||||
x: transform.translation.x,
|
x: transform.translation.x,
|
||||||
y: transform.translation.y,
|
y: transform.translation.y,
|
||||||
|
|||||||
@@ -3,108 +3,133 @@ use graph_craft::{
|
|||||||
document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork, value::TaggedValue},
|
document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork, value::TaggedValue},
|
||||||
};
|
};
|
||||||
use graphene_std::{
|
use graphene_std::{
|
||||||
Context, graphic, memo,
|
node_graph_overlay::{types::NodeGraphOverlayData, ui_context::UIContext},
|
||||||
node_graph_overlay::{self, types::NodeGraphOverlayData},
|
table::Table,
|
||||||
uuid::NodeId,
|
uuid::NodeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// https://excalidraw.com/#json=LgKS6I4lQvGPmke06ZJyp,D9aON9vVZJAjNnZWfwy_SQ
|
||||||
pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData, opacity: f64) -> DocumentNode {
|
pub fn generate_node_graph_overlay(node_graph_overlay_data: NodeGraphOverlayData, opacity: f64) -> DocumentNode {
|
||||||
// TODO: Implement as Network and implement finer grained caching for the background, nodes, and exports
|
let generate_nodes_id = NodeId::new();
|
||||||
|
let cache_nodes_id = NodeId::new();
|
||||||
|
let transform_nodes_id = NodeId::new();
|
||||||
|
|
||||||
|
let generate_node_graph_bg = NodeId::new();
|
||||||
|
let cache_node_graph_bg = NodeId::new();
|
||||||
|
|
||||||
|
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();
|
||||||
|
// TODO: Replace with new cache node
|
||||||
|
let memo_implementation = DocumentNodeImplementation::ProtoNode(graphene_std::ops::identity::IDENTIFIER);
|
||||||
|
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::value(TaggedValue::None, true),
|
NodeInput::value(TaggedValue::Vector(Table::new()), true),
|
||||||
NodeInput::value(TaggedValue::NodeGraphOverlayData(node_graph_overlay_data), true),
|
NodeInput::value(TaggedValue::NodeGraphOverlayData(node_graph_overlay_data), true),
|
||||||
NodeInput::value(TaggedValue::F64(opacity), true),
|
NodeInput::value(TaggedValue::F64(opacity), true),
|
||||||
],
|
],
|
||||||
|
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||||
exports: vec![NodeInput::node(NodeId(0), 0)],
|
exports: vec![NodeInput::node(send_overlay_id, 0)],
|
||||||
nodes: vec![
|
nodes: vec![
|
||||||
// Merge the overlay on top of the artwork
|
// Create the nodes
|
||||||
(
|
(
|
||||||
NodeId(0),
|
generate_nodes_id,
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
call_argument: concrete!(Context),
|
call_argument: concrete!(UIContext),
|
||||||
inputs: vec![NodeInput::node(NodeId(2), 0), NodeInput::node(NodeId(1), 0)],
|
inputs: vec![NodeInput::network(concrete!(UIContext), 1)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::node_graph_overlay::GenerateNodesNode".into()),
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
//Wrap Artwork in a table
|
|
||||||
(
|
|
||||||
NodeId(1),
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::network(concrete!(Context), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::wrap_graphic::IDENTIFIER),
|
|
||||||
call_argument: concrete!(Context),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Cache the full node graph so its not rerendered when the artwork changes
|
|
||||||
(
|
|
||||||
NodeId(2),
|
|
||||||
DocumentNode {
|
|
||||||
call_argument: concrete!(Context),
|
|
||||||
inputs: vec![NodeInput::node(NodeId(3), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Merge the nodes on top of the dot grid background
|
|
||||||
(
|
|
||||||
NodeId(3),
|
|
||||||
DocumentNode {
|
|
||||||
call_argument: concrete!(Context),
|
|
||||||
inputs: vec![NodeInput::node(NodeId(5), 0), NodeInput::node(NodeId(4), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Generate the dot grid background
|
|
||||||
(
|
|
||||||
NodeId(4),
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::network(concrete!(Context), 2)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(node_graph_overlay::dot_grid_background::IDENTIFIER),
|
|
||||||
call_argument: concrete!(Context),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Transform the nodes based on the Context
|
|
||||||
(
|
|
||||||
NodeId(5),
|
|
||||||
DocumentNode {
|
|
||||||
call_argument: concrete!(Context),
|
|
||||||
inputs: vec![NodeInput::node(NodeId(6), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(node_graph_overlay::transform_nodes::IDENTIFIER),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Cache the nodes
|
// Cache the nodes
|
||||||
(
|
(
|
||||||
NodeId(6),
|
cache_nodes_id,
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
call_argument: concrete!(Context),
|
call_argument: concrete!(UIContext),
|
||||||
inputs: vec![NodeInput::node(NodeId(7), 0)],
|
inputs: vec![NodeInput::node(generate_nodes_id, 0)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
|
implementation: memo_implementation.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// Create the nodes
|
// Transform the nodes based on the Context
|
||||||
(
|
(
|
||||||
NodeId(7),
|
transform_nodes_id,
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
call_argument: concrete!(Context),
|
call_argument: concrete!(UIContext),
|
||||||
inputs: vec![NodeInput::network(concrete!(Context), 1)],
|
inputs: vec![NodeInput::node(cache_nodes_id, 0)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(node_graph_overlay::generate_nodes::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::node_graph_overlay::TransformNodesNode".into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// Generate the dot grid background
|
||||||
|
(
|
||||||
|
generate_node_graph_bg,
|
||||||
|
DocumentNode {
|
||||||
|
inputs: vec![NodeInput::network(concrete!(UIContext), 2)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::node_graph_overlay::DotGridBackgroundNode".into()),
|
||||||
|
call_argument: concrete!(UIContext),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Cache the dot grid background
|
||||||
|
(
|
||||||
|
cache_node_graph_bg,
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(UIContext),
|
||||||
|
inputs: vec![NodeInput::node(generate_node_graph_bg, 0)],
|
||||||
|
implementation: memo_implementation.clone(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Merge the nodes on top of the dot grid background
|
||||||
|
(
|
||||||
|
merge_nodes_and_bg_id,
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(UIContext),
|
||||||
|
inputs: vec![NodeInput::node(transform_nodes_id, 0), NodeInput::node(cache_node_graph_bg, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::node_graph_overlay::NodeGraphUiExtendNode".into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Render the node graph UI graphic to an SVG
|
||||||
|
(
|
||||||
|
render_overlay_id,
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(UIContext),
|
||||||
|
inputs: vec![NodeInput::node(merge_nodes_and_bg_id, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode("graphene_std::wasm_application_io::RenderNodeGraphUiNode".into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Send the overlay to the frontend
|
||||||
|
(
|
||||||
|
send_overlay_id,
|
||||||
|
DocumentNode {
|
||||||
|
call_argument: concrete!(UIContext),
|
||||||
|
inputs: vec![NodeInput::node(render_overlay_id, 0)],
|
||||||
|
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::node_graph_overlay::SendRenderNode".into()),
|
||||||
|
..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()
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
call_argument: concrete!(Context),
|
call_argument: concrete!(UIContext),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use crate::messages::tool::common_functionality::utility_functions::make_path_ed
|
|||||||
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
use graphene_std::math::math_ext::QuadExt;
|
use graphene_std::math::math_ext::QuadExt;
|
||||||
use graphene_std::node_graph_overlay::types::{FrontendGraphDataType, FrontendXY};
|
use graphene_std::node_graph_overlay::types::{FrontendGraphDataType, FrontendXY};
|
||||||
@@ -1795,8 +1795,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleNativeNodeGraphRender => {
|
NodeGraphMessage::ToggleNativeNodeGraphRender => {
|
||||||
self.native_node_graph_render = !self.native_node_graph_render;
|
self.native_node_graph_render = !self.native_node_graph_render;
|
||||||
self.node_graph_overlay = None;
|
responses.add(FrontendMessage::UpdateShouldRenderSvelteNodes {
|
||||||
|
should_render_svelte_nodes: self.native_node_graph_render,
|
||||||
|
});
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
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 {
|
||||||
|
|||||||
@@ -214,13 +214,6 @@ pub struct DragStart {
|
|||||||
pub round_y: i32,
|
pub round_y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
|
||||||
pub struct Transform {
|
|
||||||
pub scale: f64,
|
|
||||||
pub x: f64,
|
|
||||||
pub y: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct BoxSelection {
|
pub struct BoxSelection {
|
||||||
#[serde(rename = "startX")]
|
#[serde(rename = "startX")]
|
||||||
|
|||||||
@@ -301,7 +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::UpdateNodeGraphRender { nodes_to_render, .. } = self else { return };
|
let FrontendMessage::UpdateNodeGraphSvelteRender { nodes_to_render, .. } = self else { return };
|
||||||
|
|
||||||
for node in nodes_to_render {
|
for node in nodes_to_render {
|
||||||
if let Some(error) = &node.metadata.errors {
|
if let Some(error) = &node.metadata.errors {
|
||||||
|
|||||||
@@ -74,6 +74,7 @@
|
|||||||
// Replace usage of `-rgb` variants with CSS color() function to calculate alpha when browsers support it
|
// Replace usage of `-rgb` variants with CSS color() function to calculate alpha when browsers support it
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color() and https://caniuse.com/css-color-function
|
// See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color() and https://caniuse.com/css-color-function
|
||||||
// Specifically, support for the relative syntax is needed: `color(from var(--color-0-black) srgb r g b / 0.5)` to convert black to 50% alpha
|
// Specifically, support for the relative syntax is needed: `color(from var(--color-0-black) srgb r g b / 0.5)` to convert black to 50% alpha
|
||||||
|
// Keep in sync with node_graph_overlay/consts.rs
|
||||||
--color-0-black: #000;
|
--color-0-black: #000;
|
||||||
--color-0-black-rgb: 0, 0, 0;
|
--color-0-black-rgb: 0, 0, 0;
|
||||||
--color-1-nearblack: #111;
|
--color-1-nearblack: #111;
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export class UpdateLayerWidths extends JsMessage {
|
|||||||
readonly layerWidths!: Map<bigint, number>;
|
readonly layerWidths!: Map<bigint, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateNodeGraphRender extends JsMessage {
|
export class UpdateNodeGraphSvelteRender extends JsMessage {
|
||||||
readonly nodesToRender!: FrontendNodeToRender[];
|
readonly nodesToRender!: FrontendNodeToRender[];
|
||||||
|
|
||||||
readonly open!: boolean;
|
readonly open!: boolean;
|
||||||
@@ -118,8 +118,12 @@ export class UpdateNodeGraphRender extends JsMessage {
|
|||||||
readonly previewedNode!: bigint | undefined;
|
readonly previewedNode!: bigint | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateNativeNodeGraphRender extends JsMessage {
|
export class UpdateShouldRenderSvelteNodes extends JsMessage {
|
||||||
readonly nativeNodeGraphRender!: boolean;
|
readonly shouldRenderSvelteNodes!: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateNativeNodeGraphSVG extends JsMessage {
|
||||||
|
readonly svgString!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateVisibleNodes extends JsMessage {
|
export class UpdateVisibleNodes extends JsMessage {
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ import {
|
|||||||
UpdateExportReorderIndex,
|
UpdateExportReorderIndex,
|
||||||
UpdateImportsExports,
|
UpdateImportsExports,
|
||||||
UpdateLayerWidths,
|
UpdateLayerWidths,
|
||||||
UpdateNodeGraphRender,
|
UpdateNodeGraphSvelteRender,
|
||||||
UpdateNativeNodeGraphRender,
|
UpdateShouldRenderSvelteNodes,
|
||||||
|
UpdateNativeNodeGraphSVG,
|
||||||
UpdateVisibleNodes,
|
UpdateVisibleNodes,
|
||||||
UpdateNodeGraphTransform,
|
UpdateNodeGraphTransform,
|
||||||
UpdateNodeThumbnail,
|
UpdateNodeThumbnail,
|
||||||
@@ -33,6 +34,7 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
updateImportsExports: undefined as UpdateImportsExports | undefined,
|
updateImportsExports: undefined as UpdateImportsExports | undefined,
|
||||||
reorderImportIndex: undefined as number | undefined,
|
reorderImportIndex: undefined as number | undefined,
|
||||||
reorderExportIndex: undefined as number | undefined,
|
reorderExportIndex: undefined as number | undefined,
|
||||||
|
nativeNodeGraphSVGString: "",
|
||||||
|
|
||||||
contextMenuInformation: undefined as ContextMenuInformation | undefined,
|
contextMenuInformation: undefined as ContextMenuInformation | undefined,
|
||||||
nodeTypes: [] as FrontendNodeType[],
|
nodeTypes: [] as FrontendNodeType[],
|
||||||
@@ -47,6 +49,7 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
// TODO: Remove these fields
|
// TODO: Remove these fields
|
||||||
visibleNodes: new Set<bigint>(),
|
visibleNodes: new Set<bigint>(),
|
||||||
layerWidths: new Map<bigint, number>(),
|
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>(),
|
||||||
@@ -110,21 +113,27 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphRender, (updateNodeGraphRender) => {
|
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSvelteRender, (updateNodeGraphSvelteRender) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.nodesToRender.clear();
|
state.nodesToRender.clear();
|
||||||
updateNodeGraphRender.nodesToRender.forEach((node) => {
|
updateNodeGraphSvelteRender.nodesToRender.forEach((node) => {
|
||||||
state.nodesToRender.set(node.metadata.nodeId, node);
|
state.nodesToRender.set(node.metadata.nodeId, node);
|
||||||
});
|
});
|
||||||
state.opacity = updateNodeGraphRender.opacity;
|
state.opacity = updateNodeGraphSvelteRender.opacity;
|
||||||
state.inSelectedNetwork = updateNodeGraphRender.inSelectedNetwork;
|
state.inSelectedNetwork = updateNodeGraphSvelteRender.inSelectedNetwork;
|
||||||
state.previewedNode = updateNodeGraphRender.previewedNode;
|
state.previewedNode = updateNodeGraphSvelteRender.previewedNode;
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateNativeNodeGraphRender, (updateNativeNodeGraphRender) => {
|
editor.subscriptions.subscribeJsMessage(UpdateShouldRenderSvelteNodes, (UpdateShouldRenderSvelteNodes) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.nativeNodeGraphRender = updateNativeNodeGraphRender.nativeNodeGraphRender;
|
state.shouldRenderSvelteNodes = UpdateShouldRenderSvelteNodes.shouldRenderSvelteNodes;
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
editor.subscriptions.subscribeJsMessage(UpdateNativeNodeGraphSVG, (updateNativeNodeGraphRender) => {
|
||||||
|
update((state) => {
|
||||||
|
state.nativeNodeGraphSVGString = updateNativeNodeGraphRender.svgString;
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -427,6 +427,16 @@ impl Color {
|
|||||||
Color { red, green, blue, alpha }.to_linear_srgb().map_rgb(|channel| channel * alpha)
|
Color { red, green, blue, alpha }.to_linear_srgb().map_rgb(|channel| channel * alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_rgba8(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||||
|
let map_range = |int_color| int_color as f32 / 255.;
|
||||||
|
|
||||||
|
let red = map_range(red);
|
||||||
|
let green = map_range(green);
|
||||||
|
let blue = map_range(blue);
|
||||||
|
let alpha = map_range(alpha);
|
||||||
|
Color { red, green, blue, alpha }
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a [Color] from a hue, saturation, lightness and alpha (all between 0 and 1)
|
/// Create a [Color] from a hue, saturation, lightness and alpha (all between 0 and 1)
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@@ -940,6 +950,17 @@ impl Color {
|
|||||||
Some(Color::from_rgb8_srgb(r, g, b))
|
Some(Color::from_rgb8_srgb(r, g, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_rgba8_no_srgb(color_str: &str) -> Option<Color> {
|
||||||
|
if color_str.len() != 6 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let r = u8::from_str_radix(&color_str[0..2], 16).ok()?;
|
||||||
|
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
|
||||||
|
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
|
||||||
|
let a = 255;
|
||||||
|
Some(Color::from_rgba8(r, g, b, a))
|
||||||
|
}
|
||||||
|
|
||||||
/// Linearly interpolates between two colors based on t.
|
/// Linearly interpolates between two colors based on t.
|
||||||
///
|
///
|
||||||
/// T must be between 0 and 1.
|
/// T must be between 0 and 1.
|
||||||
|
|||||||
@@ -1,38 +1,55 @@
|
|||||||
use graphene_core_shaders::{Ctx, color::Color};
|
use graphene_core_shaders::{Ctx, color::Color};
|
||||||
use kurbo::{BezPath, Point};
|
use kurbo::{BezPath, Point};
|
||||||
|
|
||||||
use crate::{ExtractFootprint, table::Table, vector::Vector};
|
use crate::{
|
||||||
|
node_graph_overlay::{
|
||||||
|
nodes_and_wires::{draw_layers, draw_nodes},
|
||||||
|
types::NodeGraphOverlayData,
|
||||||
|
ui_context::{UIContext, UIRuntimeResponse},
|
||||||
|
},
|
||||||
|
table::Table,
|
||||||
|
transform::ApplyTransform,
|
||||||
|
vector::Vector,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod consts;
|
||||||
|
pub mod nodes_and_wires;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod ui_context;
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(skip_impl)]
|
||||||
pub fn generate_nodes(_: impl Ctx, _node_graph_overlay_data: types::NodeGraphOverlayData) -> Table<Vector> {
|
pub fn generate_nodes(_: impl Ctx, node_graph_overlay_data: NodeGraphOverlayData) -> Table<Vector> {
|
||||||
Table::new()
|
let mut nodes_and_wires = Table::new();
|
||||||
|
let layers = draw_layers(&node_graph_overlay_data.nodes_to_render);
|
||||||
|
nodes_and_wires.extend(layers);
|
||||||
|
|
||||||
|
let nodes = draw_nodes(&node_graph_overlay_data.nodes_to_render);
|
||||||
|
nodes_and_wires.extend(nodes);
|
||||||
|
|
||||||
|
nodes_and_wires
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(skip_impl)]
|
||||||
pub fn transform_nodes(_ctx: impl Ctx + ExtractFootprint, nodes: Table<Vector>) -> Table<Vector> {
|
pub fn transform_nodes(ui_context: UIContext, mut nodes: Table<Vector>) -> Table<Vector> {
|
||||||
|
let matrix = ui_context.transform.to_daffine2();
|
||||||
|
nodes.apply_transform(&matrix);
|
||||||
nodes
|
nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(skip_impl)]
|
||||||
pub fn dot_grid_background(ctx: impl Ctx + ExtractFootprint, opacity: f64) -> Table<Vector> {
|
pub fn dot_grid_background(ui_context: UIContext, opacity: f64) -> Table<Vector> {
|
||||||
let Some(footprint) = ctx.try_footprint() else {
|
|
||||||
log::error!("Could not get footprint from context in dot_grid_background");
|
|
||||||
return Table::new();
|
|
||||||
};
|
|
||||||
// From --color-2-mildblack: --color-2-mildblack-rgb: 34, 34, 34;
|
// From --color-2-mildblack: --color-2-mildblack-rgb: 34, 34, 34;
|
||||||
let gray = (34. / 255.) as f32;
|
let gray = (34. / 255.) as f32;
|
||||||
let Some(bg_color) = Color::from_rgbaf32(gray, gray, gray, opacity as f32) else {
|
let Some(bg_color) = Color::from_rgbaf32(gray, gray, gray, (opacity / 100.) as f32) else {
|
||||||
log::error!("Could not create color in dot grid background");
|
log::error!("Could not create color in dot grid background");
|
||||||
return Table::new();
|
return Table::new();
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut bez_path = BezPath::new();
|
let mut bez_path = BezPath::new();
|
||||||
let p0 = Point::new(0., 0.); // bottom-left
|
let p0 = Point::new(0., 0.); // bottom-left
|
||||||
let p1 = Point::new(footprint.resolution.x as f64, 0.); // bottom-right
|
let p1 = Point::new(ui_context.resolution.x as f64, 0.); // bottom-right
|
||||||
let p2 = Point::new(footprint.resolution.x as f64, footprint.resolution.y as f64); // top-right
|
let p2 = Point::new(ui_context.resolution.x as f64, ui_context.resolution.y as f64); // top-right
|
||||||
let p3 = Point::new(0., footprint.resolution.y as f64); // top-left
|
let p3 = Point::new(0., ui_context.resolution.y as f64); // top-left
|
||||||
|
|
||||||
bez_path.move_to(p0);
|
bez_path.move_to(p0);
|
||||||
bez_path.line_to(p1);
|
bez_path.line_to(p1);
|
||||||
@@ -45,3 +62,14 @@ pub fn dot_grid_background(ctx: impl Ctx + ExtractFootprint, opacity: f64) -> Ta
|
|||||||
|
|
||||||
Table::new_from_element(vector)
|
Table::new_from_element(vector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(skip_impl)]
|
||||||
|
pub fn node_graph_ui_extend(_: impl Ctx, new: Table<Vector>, mut base: Table<Vector>) -> Table<Vector> {
|
||||||
|
base.extend(new);
|
||||||
|
base
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(skip_impl)]
|
||||||
|
pub fn send_render(ui_context: UIContext, render: String) -> () {
|
||||||
|
let _ = ui_context.response_sender.send(UIRuntimeResponse::OverlaySVG(render));
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
pub const GRID_SIZE: f64 = 24.;
|
||||||
|
pub const BEZ_PATH_TOLERANCE: f64 = 0.1;
|
||||||
|
|
||||||
|
// Keep in sync with colors in Editor.svelte
|
||||||
|
pub const COLOR_0_BLACK: &str = "000000";
|
||||||
|
pub const COLOR_1_NEARBLACK: &str = "111111";
|
||||||
|
pub const COLOR_2_MILDBLACK: &str = "222222";
|
||||||
|
pub const COLOR_3_DARKGRAY: &str = "333333";
|
||||||
|
pub const COLOR_4_DIMGRAY: &str = "444444";
|
||||||
|
pub const COLOR_5_DULLGRAY: &str = "555555";
|
||||||
|
pub const COLOR_6_LOWERGRAY: &str = "666666";
|
||||||
|
pub const COLOR_7_MIDDLEGRAY: &str = "777777";
|
||||||
|
pub const COLOR_8_UPPERGRAY: &str = "888888";
|
||||||
|
pub const COLOR_9_PALEGRAY: &str = "999999";
|
||||||
|
pub const COLOR_A_SOFTGRAY: &str = "AAAAAA";
|
||||||
|
pub const COLOR_B_LIGHTGRAY: &str = "BBBBBB";
|
||||||
|
pub const COLOR_C_BRIGHTGRAY: &str = "CCCCCC";
|
||||||
|
pub const COLOR_D_MILDWHITE: &str = "DDDDDD";
|
||||||
|
pub const COLOR_E_NEARWHITE: &str = "EEEEEE";
|
||||||
|
pub const COLOR_F_WHITE: &str = "FFFFFF";
|
||||||
|
|
||||||
|
pub const COLOR_ERROR_RED: &str = "D6536E";
|
||||||
|
pub const COLOR_WARNING_YELLOW: &str = "D5AA43";
|
||||||
|
|
||||||
|
pub const COLOR_DATA_GENERAL: &str = "CFCFCF";
|
||||||
|
pub const COLOR_DATA_GENERAL_DIM: &str = "8A8A8A";
|
||||||
|
pub const COLOR_DATA_NUMBER: &str = "C9A699";
|
||||||
|
pub const COLOR_DATA_NUMBER_DIM: &str = "886B60";
|
||||||
|
pub const COLOR_DATA_ARTBOARD: &str = "FBF9EB";
|
||||||
|
pub const COLOR_DATA_ARTBOARD_DIM: &str = "B9B9A9";
|
||||||
|
pub const COLOR_DATA_GRAPHIC: &str = "68C587";
|
||||||
|
pub const COLOR_DATA_GRAPHIC_DIM: &str = "37754C";
|
||||||
|
pub const COLOR_DATA_RASTER: &str = "E4BB72";
|
||||||
|
pub const COLOR_DATA_RASTER_DIM: &str = "9A7B43";
|
||||||
|
pub const COLOR_DATA_VECTOR: &str = "65BBE5";
|
||||||
|
pub const COLOR_DATA_VECTOR_DIM: &str = "417892";
|
||||||
|
pub const COLOR_DATA_COLOR: &str = "CE6EA7";
|
||||||
|
pub const COLOR_DATA_COLOR_DIM: &str = "924071";
|
||||||
|
pub const COLOR_DATA_GRADIENT: &str = "AF81EB";
|
||||||
|
pub const COLOR_DATA_GRADIENT_DIM: &str = "6C489B";
|
||||||
|
pub const COLOR_DATA_TYPOGRAPHY: &str = "EEA7A7";
|
||||||
|
pub const COLOR_DATA_TYPOGRAPHY_DIM: &str = "955252";
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
use graphene_core_shaders::color::{AlphaMut, Color};
|
||||||
|
use kurbo::{BezPath, RoundedRect, Shape};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
node_graph_overlay::{
|
||||||
|
consts::*,
|
||||||
|
types::{FrontendGraphDataType, FrontendNodeToRender},
|
||||||
|
},
|
||||||
|
table::{Table, TableRow},
|
||||||
|
vector::{Vector, style::Fill},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn draw_nodes(nodes: &Vec<FrontendNodeToRender>) -> Table<Vector> {
|
||||||
|
let mut node_table = Table::new();
|
||||||
|
for node_to_render in nodes {
|
||||||
|
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 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 border_rect = RoundedRect::new(x, y, x + w, y + h, 2.);
|
||||||
|
let bez_path = border_rect.to_path(BEZ_PATH_TOLERANCE);
|
||||||
|
let mut border_vector = Vector::from_bezpath(bez_path);
|
||||||
|
let primary_output_color = frontend_node.outputs[0]
|
||||||
|
.as_ref()
|
||||||
|
.map(|primary_output| primary_output.data_type.data_color_dim())
|
||||||
|
.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.));
|
||||||
|
let node_color = if node_to_render.metadata.selected {
|
||||||
|
let mut selection_color = Color::from_rgba8_no_srgb(COLOR_F_WHITE).unwrap();
|
||||||
|
selection_color.set_alpha(0.15);
|
||||||
|
selection_color
|
||||||
|
} else {
|
||||||
|
let mut bg_color = Color::from_rgba8_no_srgb(COLOR_0_BLACK).unwrap();
|
||||||
|
bg_color.set_alpha(0.33);
|
||||||
|
bg_color
|
||||||
|
};
|
||||||
|
border_vector.style.fill = crate::vector::style::Fill::Solid(node_color);
|
||||||
|
|
||||||
|
// Make primary input brighter
|
||||||
|
if number_of_exposed_inputs == 0 {
|
||||||
|
// Draw the first row with rounded bottom corners
|
||||||
|
node_table.push(TableRow::new_from_element(node_first_row(x, y, true)));
|
||||||
|
} else {
|
||||||
|
// Draw the first row without rounded bottom corners
|
||||||
|
node_table.push(TableRow::new_from_element(node_first_row(x, y, false)));
|
||||||
|
// for node_index in 0..(number_of_exposed_inputs - 1) {
|
||||||
|
// node_table.push(TableRow::new_from_element(node_secondary_row(x, y, node_index + 1, false)));
|
||||||
|
// }
|
||||||
|
// // Draw the last row with bottom corners
|
||||||
|
// node_table.push(TableRow::new_from_element(node_secondary_row(x, y, number_of_exposed_inputs, true)));
|
||||||
|
};
|
||||||
|
|
||||||
|
node_table.push(TableRow::new_from_element(border_vector));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node_table
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_layers(nodes: &Vec<FrontendNodeToRender>) -> Table<Vector> {
|
||||||
|
let mut layer_table = Table::new();
|
||||||
|
for node_to_render in nodes {
|
||||||
|
if let Some(frontend_layer) = node_to_render.node_or_layer.layer.as_ref() {
|
||||||
|
let chain_width = if frontend_layer.chain_width > 0 {
|
||||||
|
frontend_layer.chain_width as f64 * GRID_SIZE + 0.5 * GRID_SIZE
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
};
|
||||||
|
|
||||||
|
let x0 = frontend_layer.position.x as f64 * GRID_SIZE - chain_width + 0.5 * GRID_SIZE;
|
||||||
|
let y0 = frontend_layer.position.y as f64 * GRID_SIZE;
|
||||||
|
let h = 2. * GRID_SIZE;
|
||||||
|
let w = chain_width + 8. * GRID_SIZE - 0.5 * GRID_SIZE;
|
||||||
|
|
||||||
|
let rect = RoundedRect::new(x0, y0, x0 + w, y0 + h, 8.);
|
||||||
|
let bez_path = rect.to_path(BEZ_PATH_TOLERANCE);
|
||||||
|
let mut vector = Vector::from_bezpath(bez_path);
|
||||||
|
let border_color = Color::from_rgba8_no_srgb(COLOR_5_DULLGRAY).unwrap();
|
||||||
|
vector.style.stroke = Some(crate::vector::style::Stroke::new(Some(border_color), 1.));
|
||||||
|
let mut background = if node_to_render.metadata.selected {
|
||||||
|
Color::from_rgba8_no_srgb(COLOR_6_LOWERGRAY).unwrap()
|
||||||
|
} else {
|
||||||
|
Color::from_rgba8_no_srgb(COLOR_0_BLACK).unwrap()
|
||||||
|
};
|
||||||
|
background.set_alpha(0.33);
|
||||||
|
vector.style.fill = crate::vector::style::Fill::Solid(background);
|
||||||
|
layer_table.push(TableRow::new_from_element(vector));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layer_table
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_first_row(x0: f64, y0: f64, rounded_bottom: bool) -> Vector {
|
||||||
|
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();
|
||||||
|
// Start at bottom-left
|
||||||
|
path.move_to((x0, y1));
|
||||||
|
|
||||||
|
// Left side up
|
||||||
|
path.line_to((x0, y0 + r));
|
||||||
|
|
||||||
|
// Top-left corner arc
|
||||||
|
path.quad_to((x0, y0), (x0 + r, y0));
|
||||||
|
|
||||||
|
// Top edge
|
||||||
|
path.line_to((x1 - r, y0));
|
||||||
|
|
||||||
|
// Top-right corner arc
|
||||||
|
path.quad_to((x1, y0), (x1, y0 + r));
|
||||||
|
|
||||||
|
// Right side down
|
||||||
|
path.line_to((x1, y1));
|
||||||
|
|
||||||
|
// Bottom edge
|
||||||
|
path.line_to((x0, y1));
|
||||||
|
|
||||||
|
path.close_path();
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
RoundedRect::new(x0, y0, x1, y1, r).to_path(BEZ_PATH_TOLERANCE)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut vector = Vector::from_bezpath(bez_path);
|
||||||
|
let mut color = Color::from_rgba8_no_srgb(COLOR_F_WHITE).unwrap();
|
||||||
|
color.set_alpha(0.05);
|
||||||
|
vector.style.fill = Fill::Solid(color);
|
||||||
|
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));
|
||||||
|
|
||||||
|
// // 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
|
||||||
|
// }
|
||||||
@@ -1,7 +1,30 @@
|
|||||||
use crate::uuid::NodeId;
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
|
use crate::{node_graph_overlay::consts::*, uuid::NodeId};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct NodeGraphTransform {
|
||||||
|
pub scale: f64,
|
||||||
|
pub x: 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 {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
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)]
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
||||||
pub struct NodeGraphOverlayData {
|
pub struct NodeGraphOverlayData {
|
||||||
pub nodes_to_render: Vec<FrontendNodeToRender>,
|
pub nodes_to_render: Vec<FrontendNodeToRender>,
|
||||||
pub open: bool,
|
pub open: bool,
|
||||||
@@ -21,7 +44,6 @@ pub struct FrontendNodeToRender {
|
|||||||
|
|
||||||
// Metadata that is common to nodes and layers
|
// Metadata that is common to nodes and layers
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendNodeMetadata {
|
pub struct FrontendNodeMetadata {
|
||||||
#[serde(rename = "nodeId")]
|
#[serde(rename = "nodeId")]
|
||||||
pub node_id: NodeId,
|
pub node_id: NodeId,
|
||||||
@@ -41,7 +63,6 @@ pub struct FrontendNodeMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendNode {
|
pub struct FrontendNode {
|
||||||
// pub position: FrontendNodePosition,
|
// pub position: FrontendNodePosition,
|
||||||
pub position: FrontendXY,
|
pub position: FrontendXY,
|
||||||
@@ -50,7 +71,6 @@ pub struct FrontendNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendLayer {
|
pub struct FrontendLayer {
|
||||||
#[serde(rename = "bottomInput")]
|
#[serde(rename = "bottomInput")]
|
||||||
pub bottom_input: FrontendGraphInput,
|
pub bottom_input: FrontendGraphInput,
|
||||||
@@ -71,7 +91,6 @@ pub struct FrontendLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendXY {
|
pub struct FrontendXY {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
@@ -93,15 +112,32 @@ pub struct FrontendXY {
|
|||||||
// pub stack: Option<u32>,
|
// pub stack: Option<u32>,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// Should be an enum but those are hard to serialize/deserialize to TS
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendNodeOrLayer {
|
pub struct FrontendNodeOrLayer {
|
||||||
pub node: Option<FrontendNode>,
|
pub node: Option<FrontendNode>,
|
||||||
pub layer: Option<FrontendLayer>,
|
pub layer: Option<FrontendLayer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
impl FrontendNodeOrLayer {
|
||||||
|
pub fn to_enum(self) -> NodeOrLayer {
|
||||||
|
let node_or_layer = if let Some(node) = self.node {
|
||||||
|
Some(NodeOrLayer::Node(node))
|
||||||
|
} else if let Some(layer) = self.layer {
|
||||||
|
Some(NodeOrLayer::Layer(layer))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
node_or_layer.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum NodeOrLayer {
|
||||||
|
Node(FrontendNode),
|
||||||
|
Layer(FrontendLayer),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Hash, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct FrontendGraphInput {
|
pub struct FrontendGraphInput {
|
||||||
#[serde(rename = "dataType")]
|
#[serde(rename = "dataType")]
|
||||||
pub data_type: FrontendGraphDataType,
|
pub data_type: FrontendGraphDataType,
|
||||||
@@ -118,7 +154,6 @@ pub struct FrontendGraphInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendGraphOutput {
|
pub struct FrontendGraphOutput {
|
||||||
#[serde(rename = "dataType")]
|
#[serde(rename = "dataType")]
|
||||||
pub data_type: FrontendGraphDataType,
|
pub data_type: FrontendGraphDataType,
|
||||||
@@ -140,7 +175,6 @@ pub struct FrontendExport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendExports {
|
pub struct FrontendExports {
|
||||||
/// If the primary export is not visible, then it is None.
|
/// If the primary export is not visible, then it is None.
|
||||||
pub exports: Vec<Option<FrontendExport>>,
|
pub exports: Vec<Option<FrontendExport>>,
|
||||||
@@ -149,7 +183,6 @@ pub struct FrontendExports {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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)]
|
||||||
|
|
||||||
pub struct FrontendImport {
|
pub struct FrontendImport {
|
||||||
pub port: FrontendGraphOutput,
|
pub port: FrontendGraphOutput,
|
||||||
pub wires: Vec<String>,
|
pub wires: Vec<String>,
|
||||||
@@ -168,3 +201,32 @@ pub enum FrontendGraphDataType {
|
|||||||
Gradient,
|
Gradient,
|
||||||
Typography,
|
Typography,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FrontendGraphDataType {
|
||||||
|
pub fn data_color(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FrontendGraphDataType::General => COLOR_DATA_GENERAL,
|
||||||
|
FrontendGraphDataType::Number => COLOR_DATA_NUMBER,
|
||||||
|
FrontendGraphDataType::Artboard => COLOR_DATA_ARTBOARD,
|
||||||
|
FrontendGraphDataType::Graphic => COLOR_DATA_GRAPHIC,
|
||||||
|
FrontendGraphDataType::Raster => COLOR_DATA_RASTER,
|
||||||
|
FrontendGraphDataType::Vector => COLOR_DATA_VECTOR,
|
||||||
|
FrontendGraphDataType::Color => COLOR_DATA_COLOR,
|
||||||
|
FrontendGraphDataType::Gradient => COLOR_DATA_GRADIENT,
|
||||||
|
FrontendGraphDataType::Typography => COLOR_DATA_TYPOGRAPHY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn data_color_dim(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FrontendGraphDataType::General => COLOR_DATA_GENERAL_DIM,
|
||||||
|
FrontendGraphDataType::Number => COLOR_DATA_NUMBER_DIM,
|
||||||
|
FrontendGraphDataType::Artboard => COLOR_DATA_ARTBOARD_DIM,
|
||||||
|
FrontendGraphDataType::Graphic => COLOR_DATA_GRAPHIC_DIM,
|
||||||
|
FrontendGraphDataType::Raster => COLOR_DATA_RASTER_DIM,
|
||||||
|
FrontendGraphDataType::Vector => COLOR_DATA_VECTOR_DIM,
|
||||||
|
FrontendGraphDataType::Color => COLOR_DATA_COLOR_DIM,
|
||||||
|
FrontendGraphDataType::Gradient => COLOR_DATA_GRADIENT_DIM,
|
||||||
|
FrontendGraphDataType::Typography => COLOR_DATA_TYPOGRAPHY_DIM,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
use std::sync::{Arc, mpsc::Sender};
|
||||||
|
|
||||||
|
use glam::UVec2;
|
||||||
|
use graphene_core_shaders::{Ctx, context::ArcCtx};
|
||||||
|
|
||||||
|
use crate::node_graph_overlay::types::NodeGraphTransform;
|
||||||
|
|
||||||
|
pub type UIContext = Arc<UIContextImpl>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, dyn_any::DynAny)]
|
||||||
|
pub struct UIContextImpl {
|
||||||
|
pub transform: NodeGraphTransform,
|
||||||
|
pub resolution: UVec2,
|
||||||
|
pub response_sender: Sender<UIRuntimeResponse>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, dyn_any::DynAny)]
|
||||||
|
pub enum UIRuntimeResponse {
|
||||||
|
RuntimeReady,
|
||||||
|
OverlaySVG(String),
|
||||||
|
OverlayTexture(wgpu::Texture),
|
||||||
|
// OverlayClickTargets(NodeId, ClickTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ctx for UIContextImpl {}
|
||||||
|
impl ArcCtx for UIContextImpl {}
|
||||||
@@ -672,7 +672,7 @@ impl TypingContext {
|
|||||||
// If the node has a value input we can infer the return type from it
|
// If the node has a value input we can infer the return type from it
|
||||||
ConstructionArgs::Value(ref v) => {
|
ConstructionArgs::Value(ref v) => {
|
||||||
// TODO: This should return a reference to the value
|
// TODO: This should return a reference to the value
|
||||||
let types = NodeIOTypes::new(concrete!(Context), Type::Future(Box::new(v.ty())), vec![]);
|
let types = NodeIOTypes::new(generic!(T), Type::Future(Box::new(v.ty())), vec![]);
|
||||||
self.inferred.insert(node_id, types.clone());
|
self.inferred.insert(node_id, types.clone());
|
||||||
return Ok(types);
|
return Ok(types);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use glam::DVec2;
|
||||||
use graph_craft::document::value::RenderOutput;
|
use graph_craft::document::value::RenderOutput;
|
||||||
pub use graph_craft::document::value::RenderOutputType;
|
pub use graph_craft::document::value::RenderOutputType;
|
||||||
pub use graph_craft::wasm_application_io::*;
|
pub use graph_craft::wasm_application_io::*;
|
||||||
@@ -6,6 +7,7 @@ use graphene_core::Artboard;
|
|||||||
use graphene_core::gradient::GradientStops;
|
use graphene_core::gradient::GradientStops;
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
use graphene_core::math::bbox::Bbox;
|
use graphene_core::math::bbox::Bbox;
|
||||||
|
use graphene_core::node_graph_overlay::ui_context::UIContext;
|
||||||
use graphene_core::raster::image::Image;
|
use graphene_core::raster::image::Image;
|
||||||
use graphene_core::raster_types::{CPU, Raster};
|
use graphene_core::raster_types::{CPU, Raster};
|
||||||
use graphene_core::table::Table;
|
use graphene_core::table::Table;
|
||||||
@@ -349,3 +351,24 @@ async fn render<'a: 'n, T: 'n + Render + WasmNotSend>(
|
|||||||
};
|
};
|
||||||
RenderOutput { data, metadata }
|
RenderOutput { data, metadata }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(skip_impl)]
|
||||||
|
async fn render_node_graph_ui<T: Render + WasmNotSend>(
|
||||||
|
ui_context: UIContext,
|
||||||
|
#[implementations(
|
||||||
|
UIContext -> Table<Artboard>,
|
||||||
|
UIContext -> Table<Graphic>,
|
||||||
|
UIContext -> Table<Vector>,
|
||||||
|
UIContext -> Table<Raster<CPU>>,
|
||||||
|
UIContext -> Table<Color>,
|
||||||
|
UIContext -> Table<GradientStops>,
|
||||||
|
)]
|
||||||
|
data: impl Node<UIContext, Output = T>,
|
||||||
|
) -> String {
|
||||||
|
let data = data.eval(ui_context.clone()).await;
|
||||||
|
let render_params = RenderParams::default();
|
||||||
|
let mut render = SvgRender::new();
|
||||||
|
data.render_svg(&mut render, &render_params);
|
||||||
|
render.format_svg(DVec2::ZERO, ui_context.resolution.as_dvec2());
|
||||||
|
render.svg.to_svg_string()
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ use graphene_std::application_io::{ImageTexture, SurfaceFrame};
|
|||||||
use graphene_std::brush::brush_cache::BrushCache;
|
use graphene_std::brush::brush_cache::BrushCache;
|
||||||
use graphene_std::brush::brush_stroke::BrushStroke;
|
use graphene_std::brush::brush_stroke::BrushStroke;
|
||||||
use graphene_std::gradient::GradientStops;
|
use graphene_std::gradient::GradientStops;
|
||||||
|
use graphene_std::node_graph_overlay::types::NodeGraphOverlayData;
|
||||||
|
use graphene_std::node_graph_overlay::ui_context::UIContext;
|
||||||
use graphene_std::table::Table;
|
use graphene_std::table::Table;
|
||||||
use graphene_std::transform::Footprint;
|
use graphene_std::transform::Footprint;
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
@@ -251,6 +253,12 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
node_io
|
node_io
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
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<Vector>]),
|
||||||
|
async_node!(graphene_core::node_graph_overlay::DotGridBackgroundNode<_>, input: UIContext, fn_params: [UIContext =>f64]),
|
||||||
|
async_node!(graphene_core::node_graph_overlay::NodeGraphUiExtendNode<_, _>, input: UIContext, fn_params: [UIContext =>Table<Vector>, UIContext =>Table<Vector>]),
|
||||||
|
async_node!(graphene_std::wasm_application_io::RenderNodeGraphUiNode<_>, input: UIContext, fn_params: [UIContext =>Table<Vector>]),
|
||||||
|
async_node!(graphene_core::node_graph_overlay::SendRenderNode<_>, input: UIContext, fn_params: [UIContext => String]),
|
||||||
];
|
];
|
||||||
// =============
|
// =============
|
||||||
// CONVERT NODES
|
// CONVERT NODES
|
||||||
|
|||||||
Reference in New Issue
Block a user