From e291e77223517971181279ebc8d8af33f75991a2 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Sun, 29 Oct 2023 10:06:32 +0000 Subject: [PATCH 1/4] Improve preview --- document-legacy/src/document.rs | 16 +- document-legacy/src/document_metadata.rs | 2 +- .../navigation/navigation_message_handler.rs | 26 +-- .../graph_operation_message_handler.rs | 14 +- editor/src/node_graph_executor.rs | 71 ++----- .../gcore/src/graphic_element/renderer.rs | 53 ++++++ node-graph/gstd/Cargo.toml | 2 +- node-graph/gstd/src/wasm_application_io.rs | 177 ++++++++++++------ .../interpreted-executor/src/node_registry.rs | 14 +- 9 files changed, 227 insertions(+), 148 deletions(-) diff --git a/document-legacy/src/document.rs b/document-legacy/src/document.rs index 0ad28a26c0..ae6ede3fc1 100644 --- a/document-legacy/src/document.rs +++ b/document-legacy/src/document.rs @@ -60,7 +60,7 @@ impl Default for Document { inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true), NodeInput::Network(concrete!(WasmEditorApi))], implementation: graph_craft::document::DocumentNodeImplementation::Network(NodeNetwork { inputs: vec![3, 0], - outputs: vec![NodeOutput::new(4, 0)], + outputs: vec![NodeOutput::new(3, 0)], nodes: [ DocumentNode { name: "EditorApi".to_string(), @@ -82,16 +82,14 @@ impl Default for Document { implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")), ..Default::default() }, - DocumentNode { - name: "Conversion".to_string(), - inputs: vec![NodeInput::Network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T))))], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IntoNode<_, GraphicGroup>")), - ..Default::default() - }, DocumentNode { name: "RenderNode".to_string(), - inputs: vec![NodeInput::node(0, 0), NodeInput::node(3, 0), NodeInput::node(2, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _>")), + inputs: vec![ + NodeInput::node(0, 0), + NodeInput::Network(graphene_core::Type::Fn(Box::new(concrete!(Footprint)), Box::new(generic!(T)))), + NodeInput::node(2, 0), + ], + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::wasm_application_io::RenderNode<_, _, _>")), ..Default::default() }, ] diff --git a/document-legacy/src/document_metadata.rs b/document-legacy/src/document_metadata.rs index 46c826bd20..f6b8337711 100644 --- a/document-legacy/src/document_metadata.rs +++ b/document-legacy/src/document_metadata.rs @@ -290,7 +290,7 @@ impl core::fmt::Display for LayerNodeIdentifier { } impl LayerNodeIdentifier { - const ROOT: Self = LayerNodeIdentifier::new_unchecked(0); + pub const ROOT: Self = LayerNodeIdentifier::new_unchecked(0); /// Construct a [`LayerNodeIdentifier`] without checking if it is a layer node pub const fn new_unchecked(node_id: NodeId) -> Self { diff --git a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs index 8cda504a36..be678467e3 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -94,7 +94,7 @@ impl MessageHandler, &InputPre responses.add(BroadcastEvent::DocumentIsDirty); responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView); responses.add(PortfolioMessage::UpdateDocumentWidgets); - self.create_document_transform(responses); + self.create_document_transform(ipp.viewport_bounds.center(), responses); } FitViewportToSelection => { if let Some(bounds) = selection_bounds { @@ -214,7 +214,7 @@ impl MessageHandler, &InputPre } SetCanvasRotation { angle_radians } => { self.tilt = angle_radians; - self.create_document_transform(responses); + self.create_document_transform(ipp.viewport_bounds.center(), responses); responses.add(BroadcastEvent::DocumentIsDirty); responses.add(PortfolioMessage::UpdateDocumentWidgets); } @@ -224,7 +224,7 @@ impl MessageHandler, &InputPre responses.add(BroadcastEvent::DocumentIsDirty); responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView); responses.add(PortfolioMessage::UpdateDocumentWidgets); - self.create_document_transform(responses); + self.create_document_transform(ipp.viewport_bounds.center(), responses); } TransformCanvasEnd { abort_transform } => { if abort_transform { @@ -235,12 +235,12 @@ impl MessageHandler, &InputPre } TransformOperation::Pan { pre_commit_pan, .. } => { self.pan = pre_commit_pan; - self.create_document_transform(responses); + self.create_document_transform(ipp.viewport_bounds.center(), responses); } TransformOperation::Zoom { pre_commit_zoom, .. } => { self.zoom = pre_commit_zoom; responses.add(PortfolioMessage::UpdateDocumentWidgets); - self.create_document_transform(responses); + self.create_document_transform(ipp.viewport_bounds.center(), responses); } } } @@ -264,7 +264,7 @@ impl MessageHandler, &InputPre self.pan += transformed_delta; responses.add(BroadcastEvent::CanvasTransformed); responses.add(BroadcastEvent::DocumentIsDirty); - self.create_document_transform(responses); + self.create_document_transform(ipp.viewport_bounds.center(), responses); } TranslateCanvasBegin => { responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing }); @@ -281,7 +281,7 @@ impl MessageHandler, &InputPre self.pan += transformed_delta; responses.add(BroadcastEvent::DocumentIsDirty); - self.create_document_transform(responses); + self.create_document_transform(ipp.viewport_bounds.center(), responses); } WheelCanvasTranslate { use_y_as_x } => { let delta = match use_y_as_x { @@ -381,21 +381,21 @@ impl NavigationMessageHandler { } } - pub fn calculate_offset_transform(&self, offset: DVec2) -> DAffine2 { + pub fn calculate_offset_transform(&self, viewport_centre: DVec2) -> DAffine2 { // Try to avoid fractional coordinates to reduce anti aliasing. let scale = self.snapped_scale(); - let rounded_pan = ((self.pan + offset) * scale).round() / scale - offset; + let rounded_pan = ((self.pan + viewport_centre) * scale).round() / scale - viewport_centre; // TODO: replace with DAffine2::from_scale_angle_translation and fix the errors - let offset_transform = DAffine2::from_translation(offset); + let offset_transform = DAffine2::from_translation(viewport_centre); let scale_transform = DAffine2::from_scale(DVec2::splat(scale)); let angle_transform = DAffine2::from_angle(self.snapped_angle()); let translation_transform = DAffine2::from_translation(rounded_pan); - scale_transform * offset_transform * angle_transform * translation_transform + scale_transform * offset_transform * angle_transform * offset_transform.inverse() * translation_transform } - fn create_document_transform(&self, responses: &mut VecDeque) { - let transform = self.calculate_offset_transform(DVec2::ZERO); + fn create_document_transform(&self, viewport_centre: DVec2, responses: &mut VecDeque) { + let transform = self.calculate_offset_transform(viewport_centre); responses.add(DocumentMessage::UpdateDocumentTransform { transform }); } diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs index bc6ed21205..830950a279 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs @@ -120,7 +120,11 @@ impl<'a> ModifyInputsContext<'a> { // Update the document metadata structure if let Some(new_id) = new_id { - let parent = LayerNodeIdentifier::new(output_node_id, self.network); + let parent = if self.network.nodes.get(&output_node_id).is_some_and(|node| node.name == "Layer") { + LayerNodeIdentifier::new(output_node_id, self.network) + } else { + LayerNodeIdentifier::ROOT + }; let new_child = LayerNodeIdentifier::new(new_id, self.network); parent.push_front_child(self.document_metadata, new_child); self.responses.add(DocumentMessage::DocumentStructureChanged); @@ -562,25 +566,25 @@ impl MessageHandler { let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); - if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) { + if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { modify_inputs.insert_artboard(artboard, layer); } } GraphOperationMessage::NewBitmapLayer { id, image_frame } => { let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); - if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) { + if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { modify_inputs.insert_image_data(image_frame, layer); } } GraphOperationMessage::NewVectorLayer { id, subpaths } => { let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); - if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) { + if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { modify_inputs.insert_vector_data(subpaths, layer); } } GraphOperationMessage::NewTextLayer { id, text, font, size } => { let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); - if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) { + if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { modify_inputs.insert_text(text, font, size, layer); } } diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 421acfea70..edf463cee3 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -9,13 +9,13 @@ use document_legacy::layers::layer_info::{LayerDataType, LayerDataTypeDiscrimina use document_legacy::{LayerId, Operation}; use graph_craft::document::value::TaggedValue; -use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork}; +use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, NodeOutput}; use graph_craft::graphene_compiler::Compiler; use graph_craft::imaginate_input::ImaginatePreferences; use graph_craft::{concrete, Type}; use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig}; use graphene_core::raster::{Image, ImageFrame}; -use graphene_core::renderer::{ClickTarget, SvgSegment, SvgSegmentList}; +use graphene_core::renderer::{ClickTarget, GraphicElementRendered, SvgSegment, SvgSegmentList}; use graphene_core::text::FontCache; use graphene_core::transform::{Footprint, Transform}; use graphene_core::vector::style::ViewMode; @@ -167,7 +167,7 @@ impl NodeRuntime { } } - async fn execute_network<'a>(&'a mut self, path: &[LayerId], graph: NodeNetwork, transform: DAffine2, viewport_resolution: UVec2) -> (Result, MonitorNodes) { + async fn execute_network<'a>(&'a mut self, path: &[LayerId], mut graph: NodeNetwork, transform: DAffine2, viewport_resolution: UVec2) -> (Result, MonitorNodes) { if self.wasm_io.is_none() { self.wasm_io = Some(WasmApplicationIo::new().await); } @@ -197,6 +197,18 @@ impl NodeRuntime { let mut graph_input_hash = DefaultHasher::new(); editor_api.font_cache.hash(&mut graph_input_hash); + // Ensure that the render node is used where applicable to fix previewing + if let Some(mut output) = graph.previous_outputs.take() { + if let Some(NodeOutput { node_id, .. }) = output.first().copied() { + if let Some(output_node) = graph.nodes.get_mut(&node_id).filter(|node| node.name == "Output") { + if graph.outputs[0].node_id != node_id { + output_node.inputs[0] = NodeInput::node(graph.outputs[0].node_id, graph.outputs[0].node_output_index); + } + } + } + graph.outputs = output; + } + let scoped_network = wrap_network_in_scope(graph, graph_input_hash.finish()); let monitor_nodes = scoped_network @@ -540,47 +552,15 @@ impl NodeGraphExecutor { fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec, responses: &mut VecDeque, document_id: u64) -> Result<(), String> { self.last_output_type.insert(layer_path.clone(), Some(node_graph_output.ty())); match node_graph_output { - TaggedValue::VectorData(vector_data) => { - // Update the cached vector data on the layer - let transform = vector_data.transform.to_cols_array(); - responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform }); - responses.add(Operation::SetVectorData { path: layer_path, vector_data }); - } TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform }) => { let transform = transform.to_cols_array(); responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform }); responses.add(Operation::SetSurface { path: layer_path, surface_id }); } - TaggedValue::ImageFrame(ImageFrame { image, transform }) => { - // Don't update the frame's transform if the new transform is DAffine2::ZERO. - let transform = (!transform.abs_diff_eq(DAffine2::ZERO, f64::EPSILON)).then_some(transform.to_cols_array()); - - // If no image was generated, clear the frame - if image.width == 0 || image.height == 0 { - responses.add(DocumentMessage::FrameClear); - - // Update the transform based on the graph output - if let Some(transform) = transform { - responses.add(Operation::SetLayerTransform { path: layer_path, transform }); - } - } else { - // Update the image data - let image_data = vec![Self::to_frontend_image_data(image, transform, &layer_path, None, None)?]; - responses.add(FrontendMessage::UpdateImageData { document_id, image_data }); - } - } - TaggedValue::Artboard(artboard) => { - warn!("Rendered graph produced artboard (which is not currently rendered): {artboard:#?}"); - return Err("Artboard (see console)".to_string()); - } TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::Svg(svg)) => { // Send to frontend - //log::debug!("svg: {svg}"); responses.add(FrontendMessage::UpdateDocumentNodeRender { svg }); responses.add(DocumentMessage::RenderScrollbars); - //responses.add(FrontendMessage::UpdateDocumentNodeRender { svg }); - - //return Err("Graphic group (see console)".to_string()); } TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::CanvasFrame(frame)) => { // Send to frontend @@ -603,27 +583,6 @@ impl NodeGraphExecutor { //return Err("Graphic group (see console)".to_string()); } - TaggedValue::GraphicGroup(graphic_group) => { - use graphene_core::renderer::{GraphicElementRendered, RenderParams, SvgRender}; - - // Setup rendering - let mut render = SvgRender::new(); - let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::BlobUrl, None, false); - - // Render svg - graphic_group.render_svg(&mut render, &render_params); - - // Conctenate the defs and the svg into one string - let mut svg = "".to_string(); - svg.push_str(&render.svg_defs); - svg.push_str(""); - use std::fmt::Write; - write!(svg, "{}", render.svg).unwrap(); - - // Send to frontend - responses.add(FrontendMessage::UpdateDocumentNodeRender { svg }); - responses.add(DocumentMessage::RenderScrollbars); - } _ => { return Err(format!("Invalid node graph output type: {node_graph_output:#?}")); } diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index d04bdc32ec..bad6a12908 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -1,4 +1,5 @@ use crate::raster::{Image, ImageFrame}; +use crate::text; use crate::uuid::{generate_uuid, ManipulatorGroupId}; use crate::{vector::VectorData, Artboard, Color, GraphicElementData, GraphicGroup}; use base64::Engine; @@ -88,6 +89,15 @@ impl SvgRender { self.svg.push(""); } + /// Wraps the svg with ``, which allows for rotation + pub fn wrap_with_transform(&mut self, transform: DAffine2) { + let defs = &self.svg_defs; + + let svg_header = format!(r#"{defs}"#, format_transform_matrix(transform)); + self.svg.insert(0, svg_header.into()); + self.svg.push(""); + } + pub fn leaf_tag(&mut self, name: impl Into, attributes: impl FnOnce(&mut SvgRenderAttrs)) { self.indent(); self.svg.push("<"); @@ -97,6 +107,11 @@ impl SvgRender { self.svg.push("/>"); } + pub fn leaf_node(&mut self, content: impl Into) { + self.indent(); + self.svg.push(content); + } + pub fn parent_tag(&mut self, name: impl Into, attributes: impl FnOnce(&mut SvgRenderAttrs), inner: impl FnOnce(&mut Self)) { let name = name.into(); self.indent(); @@ -359,6 +374,44 @@ impl GraphicElementRendered for GraphicElementData { } } +/// Used to stop rust complaining about upstream traits adding display implementations to `Option`. This would not be an issue as we control that crate. +trait Primative: core::fmt::Display {} +impl Primative for String {} +impl Primative for bool {} +impl Primative for f32 {} +impl Primative for f64 {} + +impl GraphicElementRendered for T { + fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { + render.parent_tag("text", |_| {}, |render| render.leaf_node(format!("{self}"))); + } + fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> { + None + } + fn add_click_targets(&self, _click_targets: &mut Vec) {} +} + +impl GraphicElementRendered for Option { + fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { + let Some(color) = self else { + render.parent_tag("text", |_| {}, |render| render.leaf_node("Empty colour")); + return; + }; + render.leaf_tag("rect", |attributes| { + attributes.push("width", "100"); + attributes.push("height", "100"); + attributes.push("y", "2"); + attributes.push("fill", format!("#{}", color.rgba_hex())); + }); + let colour_info = format!("{:?} #{} {:?}", color, color.rgba_hex(), color.to_rgba8_srgb()); + render.parent_tag("text", |_| {}, |render| render.leaf_node(colour_info)) + } + fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> { + None + } + fn add_click_targets(&self, _click_targets: &mut Vec) {} +} + /// A segment of an svg string to allow for embedding blob urls #[derive(Debug, Clone, PartialEq, Eq)] pub enum SvgSegment { diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 76f6ddc75c..23114ae11d 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -42,7 +42,7 @@ graphene-core = { path = "../gcore", features = [ "alloc", ], default-features = false } dyn-any = { path = "../../libraries/dyn-any", features = ["derive"] } -graph-craft = { path = "../graph-craft" } +graph-craft = { path = "../graph-craft", features = ["serde"] } vulkan-executor = { path = "../vulkan-executor", optional = true } wgpu-executor = { path = "../wgpu-executor", optional = true, version = "0.1" } gpu-executor = { path = "../gpu-executor", optional = true } diff --git a/node-graph/gstd/src/wasm_application_io.rs b/node-graph/gstd/src/wasm_application_io.rs index f03d3b3362..086eed9409 100644 --- a/node-graph/gstd/src/wasm_application_io.rs +++ b/node-graph/gstd/src/wasm_application_io.rs @@ -15,6 +15,7 @@ use graphene_core::{Color, GraphicGroup}; #[cfg(target_arch = "wasm32")] use js_sys::{Object, Reflect}; use std::collections::HashMap; +use std::marker::PhantomData; use std::pin::Pin; use std::sync::Arc; #[cfg(feature = "tokio")] @@ -288,77 +289,129 @@ fn decode_image_node<'a: 'input>(data: Arc<[u8]>) -> ImageFrame { } pub use graph_craft::document::value::RenderOutput; -pub struct RenderNode { +pub struct RenderNode { data: Data, surface_handle: Surface, + parameter: PhantomData, } -#[node_macro::node_fn(RenderNode)] -async fn render_node<'a: 'input, F: Future>( - editor: WasmEditorApi<'a>, - data: impl Node<'input, Footprint, Output = F>, +fn render_svg(data: impl GraphicElementRendered, mut render: SvgRender, render_params: RenderParams, footprint: Footprint) -> RenderOutput { + data.render_svg(&mut render, &render_params); + render.wrap_with_transform(footprint.transform); + RenderOutput::Svg(render.svg.to_string()) +} + +#[cfg(any(feature = "resvg", feature = "vello"))] +fn render_canvas( + data: impl GraphicElementRendered, + mut render: SvgRender, + render_params: RenderParams, + footprint: Footprint, + editor: WasmEditorApi<'_>, surface_handle: Arc>, ) -> RenderOutput { - let footprint = editor.render_config.viewport; - let data = self.data.eval(footprint).await; - let mut render = SvgRender::new(); - let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::Base64, None, false); - let output_format = editor.render_config.export_format; let resolution = footprint.resolution; + data.render_svg(&mut render, &render_params); + // TODO: reenable once we switch to full node graph + let min = footprint.transform.inverse().transform_point2((0., 0.).into()); + let max = footprint.transform.inverse().transform_point2(resolution.as_dvec2()); + render.format_svg(min, max); + let string = render.svg.to_string(); + let array = string.as_bytes(); + let canvas = &surface_handle.surface; + canvas.set_width(resolution.x); + canvas.set_height(resolution.y); + let usvg_tree = data.to_usvg_tree(resolution, [min, max]); + + if let Some(exec) = editor.application_io.gpu_executor() { + todo!() + } else { + let rtree = resvg::Tree::from_usvg(&usvg_tree); + + let pixmap_size = rtree.size.to_int_size(); + let mut pixmap = resvg::tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap(); + rtree.render(resvg::tiny_skia::Transform::default(), &mut pixmap.as_mut()); + let array: Clamped<&[u8]> = Clamped(pixmap.data()); + let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::().unwrap(); + let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(array, pixmap_size.width(), pixmap_size.height()).expect("Failed to construct ImageData"); + context.put_image_data(&image_data, 0.0, 0.0).unwrap(); + } + /* + let preamble = "data:image/svg+xml;base64,"; + let mut base64_string = String::with_capacity(preamble.len() + array.len() * 4); + base64_string.push_str(preamble); + base64::engine::general_purpose::STANDARD.encode_string(array, &mut base64_string); + + let image_data = web_sys::HtmlImageElement::new().unwrap(); + image_data.set_src(base64_string.as_str()); + wasm_bindgen_futures::JsFuture::from(image_data.decode()).await.unwrap(); + context.draw_image_with_html_image_element(&image_data, 0.0, 0.0).unwrap(); + */ + let frame = SurfaceHandleFrame { + surface_handle, + transform: DAffine2::IDENTITY, + }; + RenderOutput::CanvasFrame(frame.into()) +} - match output_format { - ExportFormat::Svg => { - data.render_svg(&mut render, &render_params); - // TODO: reenable once we switch to full node graph - let min = footprint.transform.inverse().transform_point2((0., 0.).into()); - let max = footprint.transform.inverse().transform_point2(resolution.as_dvec2()); - render.format_svg(min, max); - RenderOutput::Svg(render.svg.to_string()) - } - #[cfg(any(feature = "resvg", feature = "vello"))] - ExportFormat::Canvas => { - data.render_svg(&mut render, &render_params); - // TODO: reenable once we switch to full node graph - let min = footprint.transform.inverse().transform_point2((0., 0.).into()); - let max = footprint.transform.inverse().transform_point2(resolution.as_dvec2()); - render.format_svg(min, max); - let string = render.svg.to_string(); - let array = string.as_bytes(); - let canvas = &surface_handle.surface; - canvas.set_width(resolution.x); - canvas.set_height(resolution.y); - let usvg_tree = data.to_usvg_tree(resolution, [min, max]); - - if let Some(exec) = editor.application_io.gpu_executor() { - todo!() - } else { - let rtree = resvg::Tree::from_usvg(&usvg_tree); - - let pixmap_size = rtree.size.to_int_size(); - let mut pixmap = resvg::tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap(); - rtree.render(resvg::tiny_skia::Transform::default(), &mut pixmap.as_mut()); - let array: Clamped<&[u8]> = Clamped(pixmap.data()); - let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::().unwrap(); - let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(array, pixmap_size.width(), pixmap_size.height()).expect("Failed to construct ImageData"); - context.put_image_data(&image_data, 0.0, 0.0).unwrap(); +// Render with the data node taking in Footprint. +impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>> + for RenderNode +where + Data: Node<'input, Footprint, Output = F>, + Surface: Node<'input, (), Output = SurfaceFuture>, + SurfaceFuture: core::future::Future>>, +{ + type Output = core::pin::Pin + 'input>>; + #[inline] + fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output { + Box::pin(async move { + let footprint = editor.render_config.viewport; + let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::Base64, None, false); + let output_format = editor.render_config.export_format; + + match output_format { + ExportFormat::Svg => render_svg(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint), + #[cfg(any(feature = "resvg", feature = "vello"))] + ExportFormat::Canvas => render_canvas(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint, editor, self.surface_handle.eval(()).await), + _ => todo!("Non svg render output for {output_format:?}"), } - /* - let preamble = "data:image/svg+xml;base64,"; - let mut base64_string = String::with_capacity(preamble.len() + array.len() * 4); - base64_string.push_str(preamble); - base64::engine::general_purpose::STANDARD.encode_string(array, &mut base64_string); - - let image_data = web_sys::HtmlImageElement::new().unwrap(); - image_data.set_src(base64_string.as_str()); - wasm_bindgen_futures::JsFuture::from(image_data.decode()).await.unwrap(); - context.draw_image_with_html_image_element(&image_data, 0.0, 0.0).unwrap(); - */ - let frame = SurfaceHandleFrame { - surface_handle, - transform: DAffine2::IDENTITY, - }; - RenderOutput::CanvasFrame(frame.into()) + }) + } +} + +// Render with the data node taking in (). +impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>> + for RenderNode +where + Data: Node<'input, (), Output = F>, + Surface: Node<'input, (), Output = SurfaceFuture>, + SurfaceFuture: core::future::Future>>, +{ + type Output = core::pin::Pin + 'input>>; + #[inline] + fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output { + Box::pin(async move { + let footprint = editor.render_config.viewport; + let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::Base64, None, false); + let output_format = editor.render_config.export_format; + + match output_format { + ExportFormat::Svg => render_svg(self.data.eval(()).await, SvgRender::new(), render_params, footprint), + #[cfg(any(feature = "resvg", feature = "vello"))] + ExportFormat::Canvas => render_canvas(self.data.eval(()).await, SvgRender::new(), render_params, footprint, editor, self.surface_handle.eval(()).await), + _ => todo!("Non svg render output for {output_format:?}"), + } + }) + } +} +#[automatically_derived] +impl RenderNode { + pub const fn new(data: Data, surface_handle: Surface) -> Self { + Self { + data, + surface_handle, + parameter: PhantomData, } - _ => todo!("Non svg render output for {output_format:?}"), } } diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 5476011f74..bda88bce6c 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -639,7 +639,19 @@ fn node_registry() -> HashMap, input: Color, params: [QuantizationChannels]), register_node!(graphene_core::quantization::DeQuantizeNode<_>, input: PackedPixel, params: [QuantizationChannels]), register_node!(graphene_core::ops::CloneNode<_>, input: &QuantizationChannels, params: []), - async_node!(graphene_std::wasm_application_io::RenderNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => graphene_core::Artboard, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => ImageFrame, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => VectorData, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => GraphicGroup, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => graphene_core::Artboard, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => bool, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f32, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f64, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => String, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => Option, () => Arc]), //register_node!(graphene_core::transform::TranformNode<_, _, _, _, _, _>, input: , output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc]), vec![ ( From 7a92bae48bab625c212e19e100cb76fa4d372a66 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Sun, 29 Oct 2023 20:56:40 +0000 Subject: [PATCH 2/4] Improve contrast --- node-graph/gcore/src/graphic_element/renderer.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index bad6a12908..300f8e394e 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -381,9 +381,15 @@ impl Primative for bool {} impl Primative for f32 {} impl Primative for f64 {} +fn text_attributes(attributes: &mut SvgRenderAttrs) { + attributes.push("fill", "white"); + attributes.push("y", "30"); + attributes.push("font-size", "30"); +} + impl GraphicElementRendered for T { fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { - render.parent_tag("text", |_| {}, |render| render.leaf_node(format!("{self}"))); + render.parent_tag("text", text_attributes, |render| render.leaf_node(format!("{self}"))); } fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> { None @@ -400,11 +406,11 @@ impl GraphicElementRendered for Option { render.leaf_tag("rect", |attributes| { attributes.push("width", "100"); attributes.push("height", "100"); - attributes.push("y", "2"); + attributes.push("y", "40"); attributes.push("fill", format!("#{}", color.rgba_hex())); }); let colour_info = format!("{:?} #{} {:?}", color, color.rgba_hex(), color.to_rgba8_srgb()); - render.parent_tag("text", |_| {}, |render| render.leaf_node(colour_info)) + render.parent_tag("text", text_attributes, |render| render.leaf_node(colour_info)) } fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> { None From ffb998049d9aad570b5d85b59528d778f20a47a9 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 30 Oct 2023 13:59:59 +0000 Subject: [PATCH 3/4] Restructure in order to duplicate code --- .../document_node_types.rs | 2 +- editor/src/node_graph_executor.rs | 44 +++++++++++++------ node-graph/gcore/src/application_io.rs | 14 ++++++ node-graph/gcore/src/memo.rs | 17 +++---- node-graph/graph-craft/src/document.rs | 16 +++---- node-graph/graphene-cli/src/main.rs | 2 +- .../interpreted-executor/src/node_registry.rs | 21 ++++++--- 7 files changed, 77 insertions(+), 39 deletions(-) diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index 8ca330e37a..53ddb4621d 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -475,7 +475,7 @@ fn static_nodes() -> Vec { DocumentNodeType { name: "End Scope", category: "Ignore", - identifier: NodeImplementation::proto("graphene_core::memo::EndLetNode<_>"), + identifier: NodeImplementation::proto("graphene_core::memo::EndLetNode<_, _>"), inputs: vec![ DocumentInputType { name: "Scope", diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index edf463cee3..a07ced233e 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -82,6 +82,7 @@ pub(crate) struct GenerationResponse { new_click_targets: HashMap>, new_transforms: HashMap, new_upstream_transforms: HashMap, + transform: DAffine2, } enum NodeGraphUpdate { @@ -160,6 +161,7 @@ impl NodeRuntime { new_click_targets: self.click_targets.clone().into_iter().map(|(id, targets)| (LayerNodeIdentifier::new_unchecked(id), targets)).collect(), new_transforms: self.transforms.clone().into_iter().map(|(id, transform)| (LayerNodeIdentifier::new_unchecked(id), transform)).collect(), new_upstream_transforms: self.upstream_transforms.clone(), + transform, }; self.sender.send_generation_response(response); } @@ -197,18 +199,6 @@ impl NodeRuntime { let mut graph_input_hash = DefaultHasher::new(); editor_api.font_cache.hash(&mut graph_input_hash); - // Ensure that the render node is used where applicable to fix previewing - if let Some(mut output) = graph.previous_outputs.take() { - if let Some(NodeOutput { node_id, .. }) = output.first().copied() { - if let Some(output_node) = graph.nodes.get_mut(&node_id).filter(|node| node.name == "Output") { - if graph.outputs[0].node_id != node_id { - output_node.inputs[0] = NodeInput::node(graph.outputs[0].node_id, graph.outputs[0].node_output_index); - } - } - } - graph.outputs = output; - } - let scoped_network = wrap_network_in_scope(graph, graph_input_hash.finish()); let monitor_nodes = scoped_network @@ -524,6 +514,7 @@ impl NodeGraphExecutor { new_click_targets, new_transforms, new_upstream_transforms, + transform, }) => { self.thumbnails = new_thumbnails; document.metadata.update_transforms(new_transforms, new_upstream_transforms); @@ -531,7 +522,7 @@ impl NodeGraphExecutor { let node_graph_output = result.map_err(|e| format!("Node graph evaluation failed: {e:?}"))?; let execution_context = self.futures.remove(&generation_id).ok_or_else(|| "Invalid generation ID".to_string())?; responses.extend(updates); - self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), responses, execution_context.document_id)?; + self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), transform, responses, execution_context.document_id)?; responses.add(DocumentMessage::LayerChanged { affected_layer_path: execution_context.layer_path, }); @@ -549,7 +540,25 @@ impl NodeGraphExecutor { Ok(()) } - fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec, responses: &mut VecDeque, document_id: u64) -> Result<(), String> { + fn render(render_object: impl GraphicElementRendered, transform: DAffine2, responses: &mut VecDeque) { + use graphene_core::renderer::{RenderParams, SvgRender}; + + // Setup rendering + let mut render = SvgRender::new(); + let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::BlobUrl, None, false); + + // Render svg + render_object.render_svg(&mut render, &render_params); + + // Conctenate the defs and the svg into one string + render.wrap_with_transform(transform); + let svg = render.svg.to_string(); + + // Send to frontend + responses.add(FrontendMessage::UpdateDocumentNodeRender { svg }); + } + + fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec, transform: DAffine2, responses: &mut VecDeque, document_id: u64) -> Result<(), String> { self.last_output_type.insert(layer_path.clone(), Some(node_graph_output.ty())); match node_graph_output { TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform }) => { @@ -583,6 +592,13 @@ impl NodeGraphExecutor { //return Err("Graphic group (see console)".to_string()); } + TaggedValue::Bool(render_object) => Self::render(render_object, transform, responses), + TaggedValue::String(render_object) => Self::render(render_object, transform, responses), + TaggedValue::F32(render_object) => Self::render(render_object, transform, responses), + TaggedValue::F64(render_object) => Self::render(render_object, transform, responses), + TaggedValue::OptionalColor(render_object) => Self::render(render_object, transform, responses), + TaggedValue::VectorData(render_object) => Self::render(render_object, transform, responses), + TaggedValue::ImageFrame(render_object) => Self::render(render_object, transform, responses), _ => { return Err(format!("Invalid node graph output type: {node_graph_output:#?}")); } diff --git a/node-graph/gcore/src/application_io.rs b/node-graph/gcore/src/application_io.rs index a4c04c4418..d3e8c0bf78 100644 --- a/node-graph/gcore/src/application_io.rs +++ b/node-graph/gcore/src/application_io.rs @@ -204,6 +204,20 @@ impl<'a, T> AsRef> for EditorApi<'a, T> { } } +// Required for the EndLetNode +impl<'a, IO> From> for Footprint { + fn from(value: EditorApi<'a, IO>) -> Self { + value.render_config.viewport + } +} + +// Required for the EndLetNode +impl<'a, IO> From> for () { + fn from(_value: EditorApi<'a, IO>) -> Self { + () + } +} + #[derive(Debug, Clone, Copy, Default)] pub struct ExtractImageFrame; diff --git a/node-graph/gcore/src/memo.rs b/node-graph/gcore/src/memo.rs index 21d267128c..94abedc80c 100644 --- a/node-graph/gcore/src/memo.rs +++ b/node-graph/gcore/src/memo.rs @@ -111,23 +111,24 @@ impl LetNode { /// Caches the output of a given Node and acts as a proxy #[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct EndLetNode"); } - /// Wraps the svg with ``, which allows for rotation + /// Wraps the SVG with ``, which allows for rotation pub fn wrap_with_transform(&mut self, transform: DAffine2) { let defs = &self.svg_defs; @@ -375,11 +374,11 @@ impl GraphicElementRendered for GraphicElementData { } /// Used to stop rust complaining about upstream traits adding display implementations to `Option`. This would not be an issue as we control that crate. -trait Primative: core::fmt::Display {} -impl Primative for String {} -impl Primative for bool {} -impl Primative for f32 {} -impl Primative for f64 {} +trait Primitive: core::fmt::Display {} +impl Primitive for String {} +impl Primitive for bool {} +impl Primitive for f32 {} +impl Primitive for f64 {} fn text_attributes(attributes: &mut SvgRenderAttrs) { attributes.push("fill", "white"); @@ -387,34 +386,39 @@ fn text_attributes(attributes: &mut SvgRenderAttrs) { attributes.push("font-size", "30"); } -impl GraphicElementRendered for T { +impl GraphicElementRendered for T { fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { render.parent_tag("text", text_attributes, |render| render.leaf_node(format!("{self}"))); } + fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> { None } + fn add_click_targets(&self, _click_targets: &mut Vec) {} } impl GraphicElementRendered for Option { fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { let Some(color) = self else { - render.parent_tag("text", |_| {}, |render| render.leaf_node("Empty colour")); + render.parent_tag("text", |_| {}, |render| render.leaf_node("Empty color")); return; }; + render.leaf_tag("rect", |attributes| { attributes.push("width", "100"); attributes.push("height", "100"); attributes.push("y", "40"); attributes.push("fill", format!("#{}", color.rgba_hex())); }); - let colour_info = format!("{:?} #{} {:?}", color, color.rgba_hex(), color.to_rgba8_srgb()); - render.parent_tag("text", text_attributes, |render| render.leaf_node(colour_info)) + let color_info = format!("{:?} #{} {:?}", color, color.rgba_hex(), color.to_rgba8_srgb()); + render.parent_tag("text", text_attributes, |render| render.leaf_node(color_info)) } + fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> { None } + fn add_click_targets(&self, _click_targets: &mut Vec) {} } diff --git a/node-graph/gcore/src/raster/brightness_contrast.rs b/node-graph/gcore/src/raster/brightness_contrast.rs index cdcf886b92..65a0e11eea 100644 --- a/node-graph/gcore/src/raster/brightness_contrast.rs +++ b/node-graph/gcore/src/raster/brightness_contrast.rs @@ -65,6 +65,8 @@ pub struct GenerateBrightnessContrastMapperNode { contrast: Contrast, } +// TODO: Replace this node implementation with one that reuses the more generalized Curves adjustment node. +// TODO: It will be necessary to ensure the tests below are faithfully translated in a way that ensures identical results. #[node_macro::node_fn(GenerateBrightnessContrastMapperNode)] fn brightness_contrast_node(_primary: (), brightness: f32, contrast: f32) -> BrightnessContrastMapperNode { // Brightness LUT diff --git a/node-graph/gcore/src/vector/style.rs b/node-graph/gcore/src/vector/style.rs index 8d014bb6dc..27164815d4 100644 --- a/node-graph/gcore/src/vector/style.rs +++ b/node-graph/gcore/src/vector/style.rs @@ -127,12 +127,12 @@ impl Gradient { // Compute the color of the inserted stop let get_color = |index: usize, time: f64| match (self.positions[index].1, self.positions.get(index + 1).and_then(|x| x.1)) { - // Lerp between the nearest colours if applicable + // Lerp between the nearest colors if applicable (Some(a), Some(b)) => a.lerp( b, ((time - self.positions[index].0) / self.positions.get(index + 1).map(|end| end.0 - self.positions[index].0).unwrap_or_default()) as f32, ), - // Use the start or the end colour if applicable + // Use the start or the end color if applicable (Some(v), _) | (_, Some(v)) => v, _ => Color::WHITE, }; diff --git a/node-graph/gstd/src/wasm_application_io.rs b/node-graph/gstd/src/wasm_application_io.rs index 086eed9409..94714b4f9c 100644 --- a/node-graph/gstd/src/wasm_application_io.rs +++ b/node-graph/gstd/src/wasm_application_io.rs @@ -363,18 +363,19 @@ where SurfaceFuture: core::future::Future>>, { type Output = core::pin::Pin + 'input>>; + #[inline] fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output { Box::pin(async move { let footprint = editor.render_config.viewport; let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::Base64, None, false); - let output_format = editor.render_config.export_format; + let output_format = editor.render_config.export_format; match output_format { ExportFormat::Svg => render_svg(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint), #[cfg(any(feature = "resvg", feature = "vello"))] ExportFormat::Canvas => render_canvas(self.data.eval(footprint).await, SvgRender::new(), render_params, footprint, editor, self.surface_handle.eval(()).await), - _ => todo!("Non svg render output for {output_format:?}"), + _ => todo!("Non-SVG render output for {output_format:?}"), } }) } @@ -392,15 +393,17 @@ where #[inline] fn eval(&'input self, editor: WasmEditorApi<'a>) -> Self::Output { Box::pin(async move { + use graphene_core::renderer::ImageRenderMode; + let footprint = editor.render_config.viewport; - let render_params = RenderParams::new(ViewMode::Normal, graphene_core::renderer::ImageRenderMode::Base64, None, false); - let output_format = editor.render_config.export_format; + let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::Base64, None, false); + let output_format = editor.render_config.export_format; match output_format { ExportFormat::Svg => render_svg(self.data.eval(()).await, SvgRender::new(), render_params, footprint), #[cfg(any(feature = "resvg", feature = "vello"))] ExportFormat::Canvas => render_canvas(self.data.eval(()).await, SvgRender::new(), render_params, footprint, editor, self.surface_handle.eval(()).await), - _ => todo!("Non svg render output for {output_format:?}"), + _ => todo!("Non-SVG render output for {output_format:?}"), } }) } diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index fb5700f231..d57e5ac03b 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -11,7 +11,7 @@ use graphene_core::value::{ClonedNode, CopiedNode, ValueNode}; use graphene_core::vector::brush_stroke::BrushStroke; use graphene_core::vector::VectorData; use graphene_core::{application_io::SurfaceHandle, SurfaceFrame, WasmSurfaceHandleFrame}; -use graphene_core::{concrete, generic, GraphicGroup}; +use graphene_core::{concrete, generic, Artboard, GraphicGroup}; use graphene_core::{fn_type, raster::*}; use graphene_core::{Cow, NodeIdentifier, Type}; use graphene_core::{Node, NodeIO, NodeIOTypes}; @@ -276,7 +276,7 @@ fn node_registry() -> HashMap, input: ImageFrame, output: GraphicGroup, params: []), async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: VectorData, output: GraphicGroup, params: []), async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: GraphicGroup, output: GraphicGroup, params: []), - async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: graphene_core::Artboard, output: GraphicGroup, params: []), + async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: Artboard, output: GraphicGroup, params: []), #[cfg(feature = "gpu")] async_node!(graphene_core::ops::IntoNode<_, &WgpuExecutor>, input: WasmEditorApi, output: &WgpuExecutor, params: []), register_node!(graphene_std::raster::MaskImageNode<_, _, _>, input: ImageFrame, params: [ImageFrame]), @@ -559,14 +559,14 @@ fn node_registry() -> HashMap, input: WasmEditorApi, - output: graphene_core::GraphicGroup, - params: [graphene_core::GraphicGroup] + output: GraphicGroup, + params: [GraphicGroup] ), async_node!( graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, - output: graphene_core::Artboard, - params: [graphene_core::Artboard] + output: Artboard, + params: [Artboard] ), async_node!( graphene_core::memo::EndLetNode<_, _>, @@ -649,11 +649,11 @@ fn node_registry() -> HashMap, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc]), - async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => graphene_core::Artboard, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Artboard, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => ImageFrame, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => VectorData, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => GraphicGroup, () => Arc]), - async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => graphene_core::Artboard, () => Arc]), + async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => Artboard, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => bool, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f32, () => Arc]), async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f64, () => Arc]), @@ -759,34 +759,32 @@ fn node_registry() -> HashMap>::new(graphene_std::any::input_node::(args.pop().expect("Not enough arguments provided to construct node"))); let any: DynAnyNode = graphene_std::any::DynAnyNode::new(node); - Box::new(any) as Box NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin> + 'i>>)> + '_> + Box::new(any) as Box NodeIO<'i, graph_craft::proto::Any<'i>, Output = core::pin::Pin> + 'i>>> + '_> }) }, { - let node = >::new((graphene_std::any::PanicNode::<(), VectorData>::new())); + let node = >::new(graphene_std::any::PanicNode::<(), VectorData>::new()); let params = vec![fn_type!((), VectorData)]; let mut node_io = as NodeIO<'_, Footprint>>::to_node_io(&node, params); node_io.input = concrete!(::Static); node_io }, )], - register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [graphene_core::Artboard]), + register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [Artboard]), vec![( NodeIdentifier::new("graphene_core::transform::CullNode<_>"), |args| { Box::pin(async move { let mut args = args.clone(); args.reverse(); - let node = >::new(graphene_std::any::input_node::( - args.pop().expect("Not enough arguments provided to construct node"), - )); + let node = >::new(graphene_std::any::input_node::(args.pop().expect("Not enough arguments provided to construct node"))); let any: DynAnyNode = graphene_std::any::DynAnyNode::new(node); - Box::new(any) as Box NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin> + 'i>>)> + '_> + Box::new(any) as Box NodeIO<'i, graph_craft::proto::Any<'i>, Output = core::pin::Pin> + 'i>>> + '_> }) }, { - let node = >::new((graphene_std::any::PanicNode::<(), graphene_core::GraphicGroup>::new())); - let params = vec![fn_type!((), graphene_core::GraphicGroup)]; + let node = >::new(graphene_std::any::PanicNode::<(), GraphicGroup>::new()); + let params = vec![fn_type!((), GraphicGroup)]; let mut node_io = as NodeIO<'_, Footprint>>::to_node_io(&node, params); node_io.input = concrete!(::Static); node_io @@ -811,12 +809,12 @@ fn node_registry() -> HashMap, input: WasmEditorApi, params: [String, graphene_core::text::Font, f64]), register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []), register_node!(graphene_core::ExtractImageFrame, input: WasmEditorApi, params: []), - async_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => graphene_core::GraphicElementData, () => String, () => BlendMode, () => f32, () => bool, () => bool, () => bool, Footprint => graphene_core::GraphicGroup]), + async_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => graphene_core::GraphicElementData, () => String, () => BlendMode, () => f32, () => bool, () => bool, () => bool, Footprint => GraphicGroup]), register_node!(graphene_core::ToGraphicElementData, input: graphene_core::vector::VectorData, params: []), register_node!(graphene_core::ToGraphicElementData, input: ImageFrame, params: []), - register_node!(graphene_core::ToGraphicElementData, input: graphene_core::GraphicGroup, params: []), - register_node!(graphene_core::ToGraphicElementData, input: graphene_core::Artboard, params: []), - register_node!(graphene_core::ConstructArtboardNode<_, _, _, _>, input: graphene_core::GraphicGroup, params: [glam::IVec2, glam::IVec2, Color, bool]), + register_node!(graphene_core::ToGraphicElementData, input: GraphicGroup, params: []), + register_node!(graphene_core::ToGraphicElementData, input: Artboard, params: []), + register_node!(graphene_core::ConstructArtboardNode<_, _, _, _>, input: GraphicGroup, params: [glam::IVec2, glam::IVec2, Color, bool]), ]; let mut map: HashMap> = HashMap::new(); for (id, c, types) in node_types.into_iter().flatten() {