Eliminate bare Graphic and Artboard graph data by making Merge and Artboard nodes internally use tables (#2996)

* Eliminate bare Graphic and Artboard graph data by making Merge and Artboard nodes internally use tables

* Make the Extend node user-facing
This commit is contained in:
Keavon Chambers 2025-08-05 02:24:12 -07:00 committed by GitHub
parent 836a110c72
commit 2e1396462c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 292 additions and 200 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -227,25 +227,32 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::Network(NodeNetwork {
exports: vec![NodeInput::node(NodeId(3), 0)],
exports: vec![NodeInput::node(NodeId(4), 0)],
nodes: [
// Secondary (left) input type coercion
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 1)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_element::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
..Default::default()
},
// Primary (bottom) input type coercion
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_group::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_graphic::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
..Default::default()
},
// Secondary (left) input type coercion
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 1)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::wrap_graphic::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
..Default::default()
},
// Store the ID of the parent node (which encapsulates this sub-network) in each row we are extending the table with.
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::source_node_id::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
..Default::default()
},
// The monitor node is used to display a thumbnail in the UI
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
skip_deduplication: true,
@ -253,12 +260,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNode {
manual_composition: Some(generic!(T)),
inputs: vec![
NodeInput::node(NodeId(1), 0),
NodeInput::node(NodeId(2), 0),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
],
implementation: DocumentNodeImplementation::ProtoNode(graphic::layer::IDENTIFIER),
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(3), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
..Default::default()
},
]
@ -275,7 +278,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Graphical Data", "TODO").into(), ("Over", "TODO").into()],
input_metadata: vec![("Base", "TODO").into(), ("Content", "TODO").into()],
output_names: vec!["Out".to_string()],
node_type_metadata: NodeTypePersistentMetadata::layer(IVec2::new(0, 0)),
network_metadata: Some(NodeNetworkMetadata {
@ -283,16 +286,24 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "To Element".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -1)),
display_name: "To Graphic".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)),
..Default::default()
},
..Default::default()
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "To Group".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -3)),
display_name: "Wrap Graphic".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -1)),
..Default::default()
},
..Default::default()
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Source Node ID".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -1)),
..Default::default()
},
..Default::default()
@ -307,7 +318,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Layer".to_string(),
display_name: "Extend".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -3)),
..Default::default()
},
@ -334,12 +345,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::Network(NodeNetwork {
exports: vec![NodeInput::node(NodeId(2), 0)],
exports: vec![NodeInput::node(NodeId(3), 0)],
nodes: [
// Ensure this ID is kept in sync with the ID in set_alias so that the name input is kept in sync with the alias
DocumentNode {
manual_composition: Some(generic!(T)),
implementation: DocumentNodeImplementation::ProtoNode(artboard::to_artboard::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(artboard::create_artboard::IDENTIFIER),
inputs: vec![
NodeInput::network(concrete!(TaggedValue), 1),
NodeInput::value(TaggedValue::String(String::from("Artboard")), false),
@ -350,10 +361,17 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
],
..Default::default()
},
// Store the ID of the parent node (which encapsulates this sub-network) in each row we are extending the table with.
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::source_node_id::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
..Default::default()
},
// The monitor node is used to display a thumbnail in the UI.
// TODO: Check if thumbnail is reversed
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
skip_deduplication: true,
@ -363,10 +381,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
manual_composition: Some(concrete!(Context)),
inputs: vec![
NodeInput::network(graphene_std::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(Table<Artboard>))), 0),
NodeInput::node(NodeId(1), 0),
NodeInput::node(NodeId(2), 0),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
],
implementation: DocumentNodeImplementation::ProtoNode(artboard::append_artboard::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
..Default::default()
},
]
@ -388,8 +406,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![
("Artboards", "TODO").into(),
InputMetadata::with_name_description_override("Contents", "TODO", WidgetOverride::Hidden),
("Base", "TODO").into(),
InputMetadata::with_name_description_override("Content", "TODO", WidgetOverride::Hidden),
InputMetadata::with_name_description_override(
"Location",
"TODO",
@ -422,7 +440,15 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_metadata: [
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "To Artboard".to_string(),
display_name: "Create Artboard".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-21, -3)),
..Default::default()
},
..Default::default()
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Source Node ID".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -3)),
..Default::default()
},
@ -438,7 +464,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNodeMetadata {
persistent_metadata: DocumentNodePersistentMetadata {
display_name: "Append Artboards".to_string(),
display_name: "Extend".to_string(),
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, -4)),
..Default::default()
},

View File

@ -28,30 +28,48 @@ pub struct NodeReplacement<'a> {
}
const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
// graphic element
// artboard
NodeReplacement {
node: graphene_std::artboard::append_artboard::IDENTIFIER,
aliases: &["graphene_core::AddArtboardNode", "graphene_core::graphic_element::AppendArtboardNode"],
node: graphene_std::artboard::create_artboard::IDENTIFIER,
aliases: &[
"graphene_core::ConstructArtboardNode",
"graphene_core::graphic_element::ToArtboardNode",
"graphene_core::artboard::ToArtboardNode",
],
},
// graphic
NodeReplacement {
node: graphene_std::graphic::to_graphic::IDENTIFIER,
aliases: &[
"graphene_core::ToGraphicGroupNode",
"graphene_core::graphic_element::ToGroupNode",
"graphene_core::graphic::ToGroupNode",
],
},
NodeReplacement {
node: graphene_std::artboard::to_artboard::IDENTIFIER,
aliases: &["graphene_core::ConstructArtboardNode", "graphene_core::graphic_element::ToArtboardNode"],
node: graphene_std::graphic::wrap_graphic::IDENTIFIER,
aliases: &[
// Converted from "To Element"
"graphene_core::ToGraphicElementNode",
"graphene_core::graphic_element::ToElementNode",
"graphene_core::graphic::ToElementNode",
],
},
NodeReplacement {
node: graphene_std::graphic::to_element::IDENTIFIER,
aliases: &["graphene_core::ToGraphicElementNode", "graphene_core::graphic_element::ToElementNode"],
node: graphene_std::graphic::legacy_layer_extend::IDENTIFIER,
aliases: &[
"graphene_core::graphic_element::LayerNode",
"graphene_core::graphic::LayerNode",
// Converted from "Append Artboard"
"graphene_core::AddArtboardNode",
"graphene_core::graphic_element::AppendArtboardNode",
"graphene_core::graphic::AppendArtboardNode",
"graphene_core::artboard::AppendArtboardNode",
],
},
NodeReplacement {
node: graphene_std::graphic::to_group::IDENTIFIER,
aliases: &["graphene_core::ToGraphicGroupNode", "graphene_core::graphic_element::ToGroupNode"],
},
NodeReplacement {
node: graphene_std::graphic::layer::IDENTIFIER,
aliases: &["graphene_core::graphic_element::LayerNode"],
},
NodeReplacement {
node: graphene_std::graphic::flatten_group::IDENTIFIER,
aliases: &["graphene_core::graphic_element::FlattenGroupNode"],
node: graphene_std::graphic::flatten_graphic::IDENTIFIER,
aliases: &["graphene_core::graphic_element::FlattenGroupNode", "graphene_core::graphic::FlattenGroupNode"],
},
NodeReplacement {
node: graphene_std::graphic::flatten_vector::IDENTIFIER,
@ -245,8 +263,8 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
aliases: &["graphene_core::ops::SomeNode"],
},
NodeReplacement {
node: graphene_std::debug::unwrap::IDENTIFIER,
aliases: &["graphene_core::ops::UnwrapNode"],
node: graphene_std::debug::unwrap_option::IDENTIFIER,
aliases: &["graphene_core::ops::UnwrapNode", "graphene_core::debug::UnwrapNode"],
},
NodeReplacement {
node: graphene_std::debug::clone::IDENTIFIER,
@ -812,7 +830,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[3].clone(), network_path);
}
// Upgrade artboard name being passed as hidden value input to "To Artboard"
// Upgrade artboard name being passed as hidden value input to "Create Artboard"
if reference == "Artboard" && reset_node_definitions_on_open {
let label = document.network_interface.display_name(node_id, network_path);
document

View File

@ -179,7 +179,13 @@ impl TableRowLayout for Vector {
"Vector"
}
fn identifier(&self) -> String {
format!("Vector ({} points, {} segments)", self.point_domain.ids().len(), self.segment_domain.ids().len())
format!(
"Vector ({} point{}, {} segment{})",
self.point_domain.ids().len(),
if self.point_domain.ids().len() == 1 { "" } else { "s" },
self.segment_domain.ids().len(),
if self.segment_domain.ids().len() == 1 { "" } else { "s" }
)
}
fn compute_layout(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
let colinear = self.colinear_manipulators.iter().map(|[a, b]| format!("[{a} / {b}]")).collect::<Vec<_>>().join(", ");
@ -247,10 +253,10 @@ impl TableRowLayout for Image<Color> {
"Image"
}
fn identifier(&self) -> String {
format!("Image (width={}, height={})", self.width, self.height)
format!("Image ({}x{})", self.width, self.height)
}
fn compute_layout(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
let rows = vec![vec![TextLabel::new(format!("Image (width={}, height={})", self.width, self.height)).widget_holder()]];
let rows = vec![vec![TextLabel::new(format!("Image ({}x{})", self.width, self.height)).widget_holder()]];
vec![LayoutGroup::Table { rows }]
}
}
@ -272,7 +278,7 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
"Table"
}
fn identifier(&self) -> String {
format!("Table<{}> (length={})", T::type_name(), self.len())
format!("Table<{}> ({} row{})", T::type_name(), self.len(), if self.len() == 1 { "" } else { "s" })
}
fn compute_layout(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
if let Some(index) = data.desired_path.get(data.current_depth).copied() {
@ -295,7 +301,7 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
let rotation = if angle == -0. { 0. } else { angle.to_degrees() };
let round = |x: f64| (x * 1e3).round() / 1e3;
vec![
TextLabel::new(format!("{}", index)).widget_holder(),
TextLabel::new(format!("{index}")).widget_holder(),
TextButton::new(row.element.identifier())
.on_update(move |_| SpreadsheetMessage::PushToElementPath { index }.into())
.widget_holder(),
@ -315,7 +321,6 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
rows.insert(0, column_headings(&["", "element", "transform", "alpha_blending", "source_node_id"]));
let table = vec![TextLabel::new("Table:").widget_holder()];
vec![LayoutGroup::Row { widgets: table }, LayoutGroup::Table { rows }]
vec![LayoutGroup::Table { rows }]
}
}

View File

@ -564,13 +564,17 @@ impl Fsm for ArtboardToolFsmState {
#[cfg(test)]
mod test_artboard {
pub use crate::test_utils::test_prelude::*;
use graphene_std::table::Table;
async fn get_artboards(editor: &mut EditorTestUtils) -> Vec<graphene_std::Artboard> {
async fn get_artboards(editor: &mut EditorTestUtils) -> Table<graphene_std::Artboard> {
let instrumented = match editor.eval_graph().await {
Ok(instrumented) => instrumented,
Err(e) => panic!("Failed to evaluate graph: {}", e),
};
instrumented.grab_all_input::<graphene_std::artboard::append_artboard::ArtboardInput>(&editor.runtime).collect()
instrumented
.grab_all_input::<graphene_std::graphic::extend::NewInput<graphene_std::Artboard>>(&editor.runtime)
.flatten()
.collect()
}
#[tokio::test]
@ -582,8 +586,8 @@ mod test_artboard {
let artboards = get_artboards(&mut editor).await;
assert_eq!(artboards.len(), 1);
assert_eq!(artboards[0].location, IVec2::new(10, 0));
assert_eq!(artboards[0].dimensions, IVec2::new(10, 11));
assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(10, 0));
assert_eq!(artboards.get(0).unwrap().element.dimensions, IVec2::new(10, 11));
}
#[tokio::test]
@ -594,8 +598,8 @@ mod test_artboard {
let artboards = get_artboards(&mut editor).await;
assert_eq!(artboards.len(), 1);
assert_eq!(artboards[0].location, IVec2::new(-10, 10));
assert_eq!(artboards[0].dimensions, IVec2::new(20, 20));
assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(-10, 10));
assert_eq!(artboards.get(0).unwrap().element.dimensions, IVec2::new(20, 20));
}
#[tokio::test]
@ -613,9 +617,9 @@ mod test_artboard {
let artboards = get_artboards(&mut editor).await;
assert_eq!(artboards.len(), 1);
assert_eq!(artboards[0].location, IVec2::new(0, 0));
assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(0, 0));
let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 10.);
assert_eq!(artboards[0].dimensions, desired_size.round().as_ivec2());
assert_eq!(artboards.get(0).unwrap().element.dimensions, desired_size.round().as_ivec2());
}
#[tokio::test]
@ -634,9 +638,9 @@ mod test_artboard {
let artboards = get_artboards(&mut editor).await;
assert_eq!(artboards.len(), 1);
assert_eq!(artboards[0].location, DVec2::splat(f64::consts::FRAC_1_SQRT_2 * -10.).as_ivec2());
assert_eq!(artboards.get(0).unwrap().element.location, DVec2::splat(f64::consts::FRAC_1_SQRT_2 * -10.).as_ivec2());
let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 20.);
assert_eq!(artboards[0].dimensions, desired_size.round().as_ivec2());
assert_eq!(artboards.get(0).unwrap().element.dimensions, desired_size.round().as_ivec2());
}
#[tokio::test]

View File

@ -7,7 +7,6 @@ use graph_craft::graphene_compiler::Compiler;
use graph_craft::proto::GraphErrors;
use graph_craft::wasm_application_io::EditorPreferences;
use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::Context;
use graphene_std::application_io::{ImageTexture, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
use graphene_std::memo::IORecord;
use graphene_std::renderer::{Render, RenderParams, SvgRender};
@ -17,6 +16,7 @@ use graphene_std::text::FontCache;
use graphene_std::vector::Vector;
use graphene_std::vector::style::ViewMode;
use graphene_std::wasm_application_io::{RenderOutputType, WasmApplicationIo, WasmEditorApi};
use graphene_std::{Artboard, Context, Graphic};
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
use interpreted_executor::util::wrap_network_in_scope;
use once_cell::sync::Lazy;
@ -308,9 +308,9 @@ impl NodeRuntime {
continue;
};
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, graphene_std::Graphic>>() {
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Graphic>>>() {
Self::process_graphic(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, graphene_std::Artboard>>() {
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Artboard>>>() {
Self::process_graphic(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
// Insert the vector modify if we are dealing with vector data
} else if let Some(record) = introspected_data.downcast_ref::<IORecord<Context, Table<Vector>>>() {

View File

@ -129,7 +129,7 @@
<style lang="scss" global>
.node-catalog {
max-height: 40vh;
max-height: 30vh;
min-width: 250px;
display: flex;
flex-direction: column;

View File

@ -95,7 +95,7 @@ impl BoundingBox for Table<Artboard> {
}
#[node_macro::node(category(""))]
async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
async fn create_artboard<T: Into<Table<Graphic>> + 'n>(
ctx: impl ExtractAll + CloneVarArgs + Ctx,
#[implementations(
Context -> Table<Graphic>,
@ -104,13 +104,13 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
Context -> Table<Raster<GPU>>,
Context -> DAffine2,
)]
contents: impl Node<Context<'static>, Output = Data>,
content: impl Node<Context<'static>, Output = T>,
label: String,
location: DVec2,
dimensions: DVec2,
background: Color,
clip: bool,
) -> Artboard {
) -> Table<Artboard> {
let location = location.as_ivec2();
let footprint = ctx.try_footprint().copied();
@ -119,7 +119,7 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
footprint.translate(location.as_dvec2());
new_ctx = new_ctx.with_footprint(footprint);
}
let group = contents.eval(new_ctx.into_context()).await.into();
let group = content.eval(new_ctx.into_context()).await.into();
let dimensions = dimensions.as_ivec2().max(IVec2::ONE);
@ -127,28 +127,12 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
let dimensions = dimensions.abs();
Artboard {
Table::new_from_element(Artboard {
group,
label,
location,
dimensions,
background,
clip,
}
}
#[node_macro::node(category(""))]
pub async fn append_artboard(_ctx: impl Ctx, mut artboards: Table<Artboard>, artboard: Artboard, node_path: Vec<NodeId>) -> Table<Artboard> {
// Get the penultimate element of the node path, or None if the path is too short.
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
artboards.push(TableRow {
element: artboard,
transform: DAffine2::IDENTITY,
alpha_blending: AlphaBlending::default(),
source_node_id: encapsulating_node_id,
});
artboards
})
}

View File

@ -7,7 +7,7 @@ use glam::{DAffine2, DVec2};
#[node_macro::node(category("Debug"), name("Log to Console"))]
fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, Table<Vector>, DAffine2, Color, Option<Color>)] value: T) -> T {
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
log::debug!("{:#?}", value);
log::debug!("{value:#?}");
value
}
@ -25,7 +25,7 @@ fn some<T>(_: impl Ctx, #[implementations(f64, f32, u32, u64, String, Color)] in
/// Meant for debugging purposes, not general use. Unwraps the input value from an Option, returning the default value if the input is None.
#[node_macro::node(category("Debug"))]
fn unwrap<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<f32>, Option<u32>, Option<u64>, Option<String>, Option<Color>)] input: Option<T>) -> T {
fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<u32>, Option<u64>, Option<String>, Option<Color>)] input: Option<T>) -> T {
input.unwrap_or_default()
}

View File

@ -5,7 +5,7 @@ use crate::raster_types::{CPU, GPU, Raster};
use crate::table::{Table, TableRow};
use crate::uuid::NodeId;
use crate::vector::Vector;
use crate::{Color, Ctx};
use crate::{Artboard, Color, Ctx};
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
use std::hash::Hash;
@ -194,27 +194,66 @@ impl BoundingBox for Table<Graphic> {
}
#[node_macro::node(category(""))]
async fn layer<I: 'n + Send + Clone>(
async fn source_node_id<I: 'n + Send + Clone>(
_: impl Ctx,
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] mut stack: Table<I>,
#[implementations(Graphic, Vector, Raster<CPU>, Raster<GPU>)] element: I,
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] content: Table<I>,
node_path: Vec<NodeId>,
) -> Table<I> {
// Get the penultimate element of the node path, or None if the path is too short
// This is used to get the ID of the user-facing parent layer-style node (which encapsulates this internal node).
let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
stack.push(TableRow {
element,
transform: DAffine2::IDENTITY,
alpha_blending: AlphaBlending::default(),
source_node_id,
});
let mut content = content;
for row in content.iter_mut() {
*row.source_node_id = source_node_id;
}
stack
content
}
#[node_macro::node(category("Debug"))]
async fn to_element<Data: Into<Graphic> + 'n>(
/// Joins two tables of the same type, extending the base table with the rows of the new table.
#[node_macro::node(category("General"))]
async fn extend<I: 'n + Send + Clone>(
_: impl Ctx,
/// The table whose rows will appear at the start of the extended table.
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)]
base: Table<I>,
/// The table whose rows will appear at the end of the extended table.
#[expose]
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)]
new: Table<I>,
) -> Table<I> {
let mut base = base;
base.extend(new);
base
}
// TODO: Eventually remove this document upgrade code
#[node_macro::node(category(""))]
async fn legacy_layer_extend<I: 'n + Send + Clone>(
_: impl Ctx,
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] base: Table<I>,
#[expose]
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)]
new: Table<I>,
nested_node_path: Vec<NodeId>,
) -> Table<I> {
// Get the penultimate element of the node path, or None if the path is too short
// This is used to get the ID of the user-facing parent layer-style node (which encapsulates this internal node).
let source_node_id = nested_node_path.get(nested_node_path.len().wrapping_sub(2)).copied();
let mut base = base;
for row in new.into_iter() {
base.push(TableRow { source_node_id, ..row });
}
base
}
/// Places a table of graphical content into an element of a new wrapper graphic table.
#[node_macro::node(category("General"))]
async fn wrap_graphic<T: Into<Graphic> + 'n>(
_: impl Ctx,
#[implementations(
Table<Graphic>,
@ -223,13 +262,15 @@ async fn to_element<Data: Into<Graphic> + 'n>(
Table<Raster<GPU>>,
DAffine2,
)]
data: Data,
) -> Graphic {
data.into()
content: T,
) -> Table<Graphic> {
Table::new_from_element(content.into())
}
#[node_macro::node(category("General"))]
async fn to_group<Data: Into<Table<Graphic>> + 'n>(
/// Converts a table of graphical content into a graphic table by placing it into an element of a new wrapper graphic table.
/// If it is already a graphic table, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
#[node_macro::node(category("Type Conversion"))]
async fn to_graphic<T: Into<Table<Graphic>> + 'n>(
_: impl Ctx,
#[implementations(
Table<Graphic>,
@ -237,34 +278,34 @@ async fn to_group<Data: Into<Table<Graphic>> + 'n>(
Table<Raster<CPU>>,
Table<Raster<GPU>>,
)]
element: Data,
content: T,
) -> Table<Graphic> {
element.into()
content.into()
}
#[node_macro::node(category("General"))]
async fn flatten_group(_: impl Ctx, group: Table<Graphic>, fully_flatten: bool) -> Table<Graphic> {
async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten: bool) -> Table<Graphic> {
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
fn flatten_group(output_group_table: &mut Table<Graphic>, current_group_table: Table<Graphic>, fully_flatten: bool, recursion_depth: usize) {
for current_row in current_group_table.iter() {
fn flatten_table(output_graphic_table: &mut Table<Graphic>, current_graphic_table: Table<Graphic>, fully_flatten: bool, recursion_depth: usize) {
for current_row in current_graphic_table.iter() {
let current_element = current_row.element.clone();
let reference = *current_row.source_node_id;
let recurse = fully_flatten || recursion_depth == 0;
match current_element {
// If we're allowed to recurse, flatten any groups we encounter
// If we're allowed to recurse, flatten any graphics we encounter
Graphic::Group(mut current_element) if recurse => {
// Apply the parent group's transform to all child elements
// Apply the parent graphic's transform to all child elements
for graphic in current_element.iter_mut() {
*graphic.transform = *current_row.transform * *graphic.transform;
}
flatten_group(output_group_table, current_element, fully_flatten, recursion_depth + 1);
flatten_table(output_graphic_table, current_element, fully_flatten, recursion_depth + 1);
}
// Handle any leaf elements we encounter, which can be either non-group elements or groups that we don't want to flatten
// Push any leaf Graphic elements we encounter, which can be either Graphic table elements beyond the recursion depth, or table elements other than Graphic tables
_ => {
output_group_table.push(TableRow {
output_graphic_table.push(TableRow {
element: current_element,
transform: *current_row.transform,
alpha_blending: *current_row.alpha_blending,
@ -276,33 +317,33 @@ async fn flatten_group(_: impl Ctx, group: Table<Graphic>, fully_flatten: bool)
}
let mut output = Table::new();
flatten_group(&mut output, group, fully_flatten, 0);
flatten_table(&mut output, content, fully_flatten, 0);
output
}
#[node_macro::node(category("Vector"))]
async fn flatten_vector(_: impl Ctx, group: Table<Graphic>) -> Table<Vector> {
async fn flatten_vector(_: impl Ctx, content: Table<Graphic>) -> Table<Vector> {
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
fn flatten_group(output_group_table: &mut Table<Vector>, current_group_table: Table<Graphic>) {
for current_graphic_row in current_group_table.iter() {
fn flatten_table(output_vector_table: &mut Table<Vector>, current_graphic_table: Table<Graphic>) {
for current_graphic_row in current_graphic_table.iter() {
let current_graphic = current_graphic_row.element.clone();
let source_node_id = *current_graphic_row.source_node_id;
match current_graphic {
// If we're allowed to recurse, flatten any groups we encounter
// If we're allowed to recurse, flatten any tables we encounter
Graphic::Group(mut current_graphic_table) => {
// Apply the parent group's transform to all child elements
// Apply the parent graphic's transform to all child elements
for graphic in current_graphic_table.iter_mut() {
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
}
flatten_group(output_group_table, current_graphic_table);
flatten_table(output_vector_table, current_graphic_table);
}
// Handle any leaf elements we encounter, which can be either non-group elements or groups that we don't want to flatten
// Push any leaf Vector elements we encounter
Graphic::Vector(vector_table) => {
for current_vector_row in vector_table.iter() {
output_group_table.push(TableRow {
output_vector_table.push(TableRow {
element: current_vector_row.element.clone(),
transform: *current_graphic_row.transform * *current_vector_row.transform,
alpha_blending: AlphaBlending {
@ -321,7 +362,7 @@ async fn flatten_vector(_: impl Ctx, group: Table<Graphic>) -> Table<Vector> {
}
let mut output = Table::new();
flatten_group(&mut output, group);
flatten_table(&mut output, content);
output
}

View File

@ -9,7 +9,7 @@ use crate::vector::Vector;
use crate::{Context, Ctx};
use glam::{DAffine2, DVec2};
#[node_macro::node(category("Text"))]
#[node_macro::node(category("Type Conversion"))]
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Table<Vector>)] value: T) -> String {
format!("{:?}", value)
}
@ -60,11 +60,10 @@ async fn switch<T, C: Send + 'n + Clone>(
Context -> DVec2,
Context -> DAffine2,
Context -> Table<Artboard>,
Context -> Table<Vector>,
Context -> Table<Graphic>,
Context -> Table<Vector>,
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Graphic,
Context -> Color,
Context -> Option<Color>,
Context -> GradientStops,
@ -81,11 +80,10 @@ async fn switch<T, C: Send + 'n + Clone>(
Context -> DVec2,
Context -> DAffine2,
Context -> Table<Artboard>,
Context -> Table<Vector>,
Context -> Table<Graphic>,
Context -> Table<Vector>,
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Graphic,
Context -> Color,
Context -> Option<Color>,
Context -> GradientStops,

View File

@ -965,8 +965,8 @@ where
// connected to a Flatten Path connected to an if else node, another connection from the cache directly to
// the if else node, and another connection from the cache to a matches type node connected to the if else node.
fn flatten_group(group: &Table<Graphic>, output: &mut TableRowMut<Vector>) {
for (group_index, current_element) in group.iter().enumerate() {
fn flatten_table(output: &mut TableRowMut<Vector>, graphic_table: &Table<Graphic>) {
for (current_index, current_element) in graphic_table.iter().enumerate() {
match current_element.element {
Graphic::Vector(vector) => {
// Loop through every row of the `Table<Vector>` and concatenate each element's subpath into the output `Vector` element.
@ -976,7 +976,7 @@ where
let node_id = current_element.source_node_id.map(|node_id| node_id.0).unwrap_or_default();
let mut hasher = DefaultHasher::new();
(group_index, vector_index, node_id).hash(&mut hasher);
(current_index, vector_index, node_id).hash(&mut hasher);
let collision_hash_seed = hasher.finish();
output.element.concat(other, transform, collision_hash_seed);
@ -985,13 +985,13 @@ where
output.element.style = row.element.style.clone();
}
}
Graphic::Group(group) => {
let mut group = group.clone();
for row in group.iter_mut() {
Graphic::Group(graphic) => {
let mut graphic = graphic.clone();
for row in graphic.iter_mut() {
*row.transform = *current_element.transform * *row.transform;
}
flatten_group(&group, output);
flatten_table(output, &graphic);
}
_ => {}
}
@ -1000,13 +1000,11 @@ where
// Create a table with one empty `Vector` element, then get a mutable reference to it which we append flattened subpaths to
let mut output_table = Table::new_from_element(Vector::default());
let Some(mut output) = output_table.iter_mut().next() else {
return output_table;
};
let Some(mut output) = output_table.iter_mut().next() else { return output_table };
// Flatten the group input into the output `Vector` element
let base_group = Table::new_from_element(Graphic::from(content));
flatten_group(&base_group, &mut output);
// Flatten the graphic input into the output `Vector` element
let base_graphic_table = Table::new_from_element(Graphic::from(content));
flatten_table(&mut output, &base_graphic_table);
// Return the single-row Table<Vector> containing the flattened Vector subpaths
output_table

View File

@ -348,21 +348,21 @@ fn random<U: num_traits::float::Float>(
}
/// Convert a number to an integer of the type u32, which may be the required type for certain node inputs. This will be removed in the future when automatic type conversion is implemented.
#[node_macro::node(name("To u32"), category("Math: Numeric"))]
#[node_macro::node(name("To u32"), category("Type Conversion"))]
fn to_u32<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> u32 {
let value = U::clamp(value, U::from(0.).unwrap(), U::from(u32::MAX as f64).unwrap());
value.to_u32().unwrap()
}
/// Convert a number to an integer of the type u64, which may be the required type for certain node inputs. This will be removed in the future when automatic type conversion is implemented.
#[node_macro::node(name("To u64"), category("Math: Numeric"))]
#[node_macro::node(name("To u64"), category("Type Conversion"))]
fn to_u64<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> u64 {
let value = U::clamp(value, U::from(0.).unwrap(), U::from(u64::MAX as f64).unwrap());
value.to_u64().unwrap()
}
/// Convert an integer to a decimal number of the type f64, which may be the required type for certain node inputs. This will be removed in the future when automatic type conversion is implemented.
#[node_macro::node(name("To f64"), category("Math: Numeric"))]
#[node_macro::node(name("To f64"), category("Type Conversion"))]
fn to_f64<U: num_traits::int::PrimInt>(_: impl Ctx, #[implementations(u32, u64)] value: U) -> f64 {
value.to_f64().unwrap()
}

View File

@ -827,7 +827,7 @@ impl Render for Artboard {
});
}
// Contents group (includes the artwork but not the background)
// Content group (includes the artwork but not the background)
render.parent_tag(
// SVG group tag
"g",
@ -851,7 +851,7 @@ impl Render for Artboard {
attributes.push("clip-path", selector);
}
},
// Artboard contents
// Artboard content
|render| {
self.group.render_svg(render, render_params);
},
@ -1067,9 +1067,9 @@ impl Render for Table<Raster<CPU>> {
metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]);
metadata.upstream_footprints.insert(element_id, footprint);
// TODO: Find a way to handle more than one row of the graphical data table
if let Some(image) = self.iter().next() {
metadata.local_transforms.insert(element_id, *image.transform);
// TODO: Find a way to handle more than one row of the raster table
if let Some(raster) = self.iter().next() {
metadata.local_transforms.insert(element_id, *raster.transform);
}
}
@ -1125,9 +1125,9 @@ impl Render for Table<Raster<GPU>> {
metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]);
metadata.upstream_footprints.insert(element_id, footprint);
// TODO: Find a way to handle more than one row of the graphical data table
if let Some(image) = self.iter().next() {
metadata.local_transforms.insert(element_id, *image.transform);
// TODO: Find a way to handle more than one row of the raster table
if let Some(raster) = self.iter().next() {
metadata.local_transforms.insert(element_id, *raster.transform);
}
}
@ -1165,7 +1165,7 @@ impl Render for Graphic {
}
Graphic::Vector(vector) => {
metadata.upstream_footprints.insert(element_id, footprint);
// TODO: Find a way to handle more than one row of the graphical data table
// TODO: Find a way to handle more than one row of the vector table
if let Some(vector) = vector.iter().next() {
metadata.first_element_source_id.insert(element_id, *vector.source_node_id);
metadata.local_transforms.insert(element_id, *vector.transform);

View File

@ -1,5 +1,5 @@
use dyn_any::StaticType;
use glam::{DAffine2, DVec2, IVec2, UVec2};
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::RenderOutput;
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
use graphene_core::raster::color::Color;
@ -31,25 +31,33 @@ use wgpu_executor::{WgpuSurface, WindowHandle};
// TODO: turn into hashmap
fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
let mut node_types: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![
into_node!(from: Table<Vector>, to: Table<Vector>),
into_node!(from: Table<Vector>, to: Graphic),
into_node!(from: Table<Vector>, to: Table<Graphic>),
// ==========
// INTO NODES
// ==========
into_node!(from: Table<Graphic>, to: Table<Graphic>),
into_node!(from: Table<Graphic>, to: Graphic),
into_node!(from: Table<Vector>, to: Table<Vector>),
into_node!(from: Table<Raster<CPU>>, to: Table<Raster<CPU>>),
// into_node!(from: Table<Raster<CPU>>, to: Table<Raster<SRGBA8>>),
into_node!(from: Table<Raster<CPU>>, to: Graphic),
into_node!(from: Table<Raster<GPU>>, to: Graphic),
#[cfg(feature = "gpu")]
into_node!(from: Table<Raster<GPU>>, to: Table<Raster<GPU>>),
into_node!(from: Table<Vector>, to: Table<Graphic>),
into_node!(from: Table<Raster<CPU>>, to: Table<Graphic>),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Vector>]),
#[cfg(feature = "gpu")]
into_node!(from: Table<Raster<GPU>>, to: Table<Graphic>),
// into_node!(from: Table<Raster<CPU>>, to: Table<Raster<SRGBA8>>),
#[cfg(feature = "gpu")]
into_node!(from: &WasmEditorApi, to: &WgpuExecutor),
// =============
// MONITOR NODES
// =============
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ()]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Artboard>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Artboard]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Vector>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::table::Table<Artboard>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Color]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => String]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => IVec2]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DVec2]),
@ -58,12 +66,11 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => f64]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => u32]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => u64]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ()]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<f64>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BlendMode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_path_bool::BooleanOperation]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Fill]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeCap]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeJoin]),
@ -73,10 +80,12 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Gradient]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::GradientStops]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<graphene_core::uuid::NodeId>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Color]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Box<graphene_core::vector::VectorModification>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]),
// ==========
// MEMO NODES
// ==========
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Image<Color>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Vector>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
@ -88,17 +97,28 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Option<WgpuSurface>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WindowHandle]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => SurfaceFrame]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: UVec2, fn_params: [UVec2 => SurfaceFrame]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => f64]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => String]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Graphic]),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WgpuSurface]),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
// =================
// IMPURE MEMO NODES
// =================
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Artboard>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Vector>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => WgpuSurface]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Option<WgpuSurface>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
// ============
// COMPOSE NODE
// ============
(
ProtoNodeIdentifier::new("graphene_core::structural::ComposeNode"),
|args| {
@ -115,14 +135,9 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
vec![Type::Fn(Box::new(generic!(T)), Box::new(generic!(V))), Type::Fn(Box::new(generic!(V)), Box::new(generic!(U)))],
),
),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WgpuSurface]),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
#[cfg(feature = "gpu")]
into_node!(from: &WasmEditorApi, to: &WgpuExecutor),
// =======================
// CREATE GPU SURFACE NODE
// =======================
#[cfg(feature = "gpu")]
(
ProtoNodeIdentifier::new(stringify!(wgpu_executor::CreateGpuSurfaceNode<_>)),
@ -143,6 +158,9 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
},
),
];
// =============
// CONVERT NODES
// =============
node_types.extend(
[
convert_node!(from: f32, to: numbers),