Improve previewing node data (#1446)

* Improve preview

* Improve contrast

* Restructure in order to duplicate code

* Code review nits

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube 2023-11-04 09:52:26 +00:00 committed by GitHub
parent c823016316
commit e0ac073805
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 326 additions and 202 deletions

View file

@ -2,7 +2,7 @@
## Purpose of Nodes
Graphite is an image editor which is centred around a node based editing workflow, which allows operations to be visually connected in a graph. This is flexible as it allows all operations to be viewed or modified at any time without losing original data. The node system has been designed to be as general as possible with all data types being representable and a broad selection of nodes for a variety of use cases being planned.
Graphite is an image editor which is centered around a node based editing workflow, which allows operations to be visually connected in a graph. This is flexible as it allows all operations to be viewed or modified at any time without losing original data. The node system has been designed to be as general as possible with all data types being representable and a broad selection of nodes for a variety of use cases being planned.
## The Document Graph

View file

@ -204,6 +204,18 @@ impl<'a, T> AsRef<EditorApi<'a, T>> for EditorApi<'a, T> {
}
}
// Required for the EndLetNode
impl<'a, IO> From<EditorApi<'a, IO>> for Footprint {
fn from(value: EditorApi<'a, IO>) -> Self {
value.render_config.viewport
}
}
// Required for the EndLetNode
impl<'a, IO> From<EditorApi<'a, IO>> for () {
fn from(_value: EditorApi<'a, IO>) -> Self {}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ExtractImageFrame;

View file

@ -88,6 +88,15 @@ impl SvgRender {
self.svg.push("</svg>");
}
/// Wraps the SVG with `<svg><g transform="...">`, which allows for rotation
pub fn wrap_with_transform(&mut self, transform: DAffine2) {
let defs = &self.svg_defs;
let svg_header = format!(r#"<svg xmlns="http://www.w3.org/2000/svg"><defs>{defs}</defs><g transform="{}">"#, format_transform_matrix(transform));
self.svg.insert(0, svg_header.into());
self.svg.push("</g></svg>");
}
pub fn leaf_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs)) {
self.indent();
self.svg.push("<");
@ -97,6 +106,11 @@ impl SvgRender {
self.svg.push("/>");
}
pub fn leaf_node(&mut self, content: impl Into<SvgSegment>) {
self.indent();
self.svg.push(content);
}
pub fn parent_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs), inner: impl FnOnce(&mut Self)) {
let name = name.into();
self.indent();
@ -359,6 +373,55 @@ impl GraphicElementRendered for GraphicElementData {
}
}
/// Used to stop rust complaining about upstream traits adding display implementations to `Option<Color>`. This would not be an issue as we control that crate.
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");
attributes.push("y", "30");
attributes.push("font-size", "30");
}
impl<T: Primitive> 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<ClickTarget>) {}
}
impl GraphicElementRendered for Option<Color> {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
let Some(color) = self else {
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 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<ClickTarget>) {}
}
/// A segment of an svg string to allow for embedding blob urls
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SvgSegment {

View file

@ -111,23 +111,24 @@ impl<T> LetNode<T> {
/// Caches the output of a given Node and acts as a proxy
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EndLetNode<Input> {
pub struct EndLetNode<Input, Parameter> {
input: Input,
paramenter: PhantomData<Parameter>,
}
impl<'i, T: 'i, Input> Node<'i, T> for EndLetNode<Input>
impl<'i, T: 'i, Parameter: 'i + From<T>, Input> Node<'i, T> for EndLetNode<Input, Parameter>
where
Input: Node<'i, ()>,
Input: Node<'i, Parameter>,
{
type Output = <Input>::Output;
fn eval(&'i self, _: T) -> Self::Output {
let result = self.input.eval(());
fn eval(&'i self, t: T) -> Self::Output {
let result = self.input.eval(Parameter::from(t));
result
}
}
impl<Input> EndLetNode<Input> {
pub const fn new(input: Input) -> EndLetNode<Input> {
EndLetNode { input }
impl<Input, Parameter> EndLetNode<Input, Parameter> {
pub const fn new(input: Input) -> EndLetNode<Input, Parameter> {
EndLetNode { input, paramenter: PhantomData }
}
}

View file

@ -65,6 +65,8 @@ pub struct GenerateBrightnessContrastMapperNode<Brightness, Contrast> {
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

View file

@ -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,
};

View file

@ -1019,15 +1019,15 @@ impl NodeNetwork {
// We filter out the newly inserted empty stack in case `resolve_empty_stacks` runs multiple times.
for node in self.nodes.values_mut().filter(|node| node.name != EMPTY_STACK) {
for input in &mut node.inputs {
if matches!(
input,
NodeInput::Value {
tagged_value: TaggedValue::GraphicGroup(GraphicGroup::EMPTY),
..
if let NodeInput::Value {
tagged_value: TaggedValue::GraphicGroup(graphic_group),
..
} = input
{
if *graphic_group == GraphicGroup::EMPTY {
*input = NodeInput::node(new_id, 0);
used = true;
}
) {
*input = NodeInput::node(new_id, 0);
used = true;
}
}
}

View file

@ -141,7 +141,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
inner_network,
DocumentNode {
name: "End Scope".to_string(),
implementation: DocumentNodeImplementation::proto("graphene_core::memo::EndLetNode<_>"),
implementation: DocumentNodeImplementation::proto("graphene_core::memo::EndLetNode<_, _>"),
inputs: vec![NodeInput::node(0, 0), NodeInput::node(1, 0)],
..Default::default()
},

View file

@ -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 }

View file

@ -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,132 @@ fn decode_image_node<'a: 'input>(data: Arc<[u8]>) -> ImageFrame<Color> {
}
pub use graph_craft::document::value::RenderOutput;
pub struct RenderNode<Data, Surface> {
pub struct RenderNode<Data, Surface, Parameter> {
data: Data,
surface_handle: Surface,
parameter: PhantomData<Parameter>,
}
#[node_macro::node_fn(RenderNode)]
async fn render_node<'a: 'input, F: Future<Output = GraphicGroup>>(
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<SurfaceHandle<HtmlCanvasElement>>,
) -> 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]);
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);
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::<CanvasRenderingContext2d>().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 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::<CanvasRenderingContext2d>().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 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 Footprint.
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>>
for RenderNode<Data, Surface, Footprint>
where
Data: Node<'input, Footprint, Output = F>,
Surface: Node<'input, (), Output = SurfaceFuture>,
SurfaceFuture: core::future::Future<Output = Arc<SurfaceHandle<HtmlCanvasElement>>>,
{
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + '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())
}
_ => todo!("Non svg render output for {output_format:?}"),
})
}
}
// Render with the data node taking in ().
impl<'input, 'a: 'input, T: 'input + GraphicElementRendered, F: 'input + Future<Output = T>, Data: 'input, Surface: 'input, SurfaceFuture: 'input> Node<'input, WasmEditorApi<'a>>
for RenderNode<Data, Surface, ()>
where
Data: Node<'input, (), Output = F>,
Surface: Node<'input, (), Output = SurfaceFuture>,
SurfaceFuture: core::future::Future<Output = Arc<SurfaceHandle<HtmlCanvasElement>>>,
{
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = RenderOutput> + 'input>>;
#[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, 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<Data, Surface, Parameter> RenderNode<Data, Surface, Parameter> {
pub const fn new(data: Data, surface_handle: Surface) -> Self {
Self {
data,
surface_handle,
parameter: PhantomData,
}
}
}

View file

@ -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<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
async_node!(graphene_core::ops::IntoNode<_, GraphicGroup>, input: ImageFrame<Color>, 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<Color>, params: [ImageFrame<Color>]),
@ -546,28 +546,35 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f32, f32, f32]),
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
register_node!(graphene_core::memo::LetNode<_>, input: Option<WasmEditorApi>, params: []),
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: VectorData, params: [VectorData]),
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: RenderOutput, params: [RenderOutput]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: ImageFrame<Color>, params: [ImageFrame<Color>]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: VectorData, params: [VectorData]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [RenderOutput]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [f32]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [f64]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [bool]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [String]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, params: [Option<Color>]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame<Color>]),
async_node!(
graphene_core::memo::EndLetNode<_>,
graphene_core::memo::EndLetNode<_, _>,
input: WasmEditorApi,
output: graphene_core::GraphicGroup,
params: [graphene_core::GraphicGroup]
output: GraphicGroup,
params: [GraphicGroup]
),
async_node!(
graphene_core::memo::EndLetNode<_>,
graphene_core::memo::EndLetNode<_, _>,
input: WasmEditorApi,
output: graphene_core::Artboard,
params: [graphene_core::Artboard]
output: Artboard,
params: [Artboard]
),
async_node!(
graphene_core::memo::EndLetNode<_>,
graphene_core::memo::EndLetNode<_, _>,
input: WasmEditorApi,
output: WasmSurfaceHandleFrame,
params: [WasmSurfaceHandleFrame]
),
async_node!(graphene_core::memo::EndLetNode<_>, input: WasmEditorApi, output: SurfaceFrame, params: [SurfaceFrame]),
async_node!(graphene_core::memo::EndLetNode<_, _>, input: WasmEditorApi, output: SurfaceFrame, params: [SurfaceFrame]),
vec![
(
NodeIdentifier::new("graphene_core::memo::RefNode<_, _>"),
@ -639,7 +646,19 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_core::quantization::QuantizeNode<_>, 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<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => ImageFrame<Color>, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => VectorData, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [Footprint => Artboard, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => ImageFrame<Color>, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => VectorData, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => Artboard, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => bool, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f32, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => f64, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => String, () => Arc<WasmSurfaceHandle>]),
async_node!(graphene_std::wasm_application_io::RenderNode<_, _, _>, input: WasmEditorApi, output: RenderOutput, fn_params: [() => Option<Color>, () => Arc<WasmSurfaceHandle>]),
//register_node!(graphene_core::transform::TranformNode<_, _, _, _, _, _>, input: , output: RenderOutput, fn_params: [Footprint => GraphicGroup, () => Arc<WasmSurfaceHandle>]),
vec![
(
@ -740,34 +759,32 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
args.reverse();
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<VectorData>(args.pop().expect("Not enough arguments provided to construct node")));
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>)> + '_>
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>> + '_>
})
},
{
let node = <graphene_core::transform::CullNode<_>>::new((graphene_std::any::PanicNode::<(), VectorData>::new()));
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::PanicNode::<(), VectorData>::new());
let params = vec![fn_type!((), VectorData)];
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
node_io.input = concrete!(<Footprint as StaticType>::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 = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<graphene_core::GraphicGroup>(
args.pop().expect("Not enough arguments provided to construct node"),
));
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<GraphicGroup>(args.pop().expect("Not enough arguments provided to construct node")));
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>)> + '_>
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>> + '_>
})
},
{
let node = <graphene_core::transform::CullNode<_>>::new((graphene_std::any::PanicNode::<(), graphene_core::GraphicGroup>::new()));
let params = vec![fn_type!((), graphene_core::GraphicGroup)];
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::PanicNode::<(), GraphicGroup>::new());
let params = vec![fn_type!((), GraphicGroup)];
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
node_io.input = concrete!(<Footprint as StaticType>::Static);
node_io
@ -792,12 +809,12 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_core::text::TextGenerator<_, _, _>, 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<Color>, 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<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
for (id, c, types) in node_types.into_iter().flatten() {