diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs index c8a92bcda6..df3906e45a 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs @@ -242,14 +242,14 @@ fn static_nodes() -> Vec { // Secondary (left) input type coercion DocumentNode { inputs: vec![NodeInput::network(generic!(T), 1)], - implementation: DocumentNodeImplementation::proto("graphene_core::graphic_element::ToElementNode"), + implementation: DocumentNodeImplementation::proto("graphene_element_nodes::conversion::ToElementNode"), manual_composition: Some(concrete!(Context)), ..Default::default() }, // Primary (bottom) input type coercion DocumentNode { inputs: vec![NodeInput::network(generic!(T), 0)], - implementation: DocumentNodeImplementation::proto("graphene_core::graphic_element::ToGroupNode"), + implementation: DocumentNodeImplementation::proto("graphene_element_nodes::conversion::ToGroupNode"), manual_composition: Some(concrete!(Context)), ..Default::default() }, @@ -268,7 +268,7 @@ fn static_nodes() -> Vec { NodeInput::node(NodeId(2), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath), ], - implementation: DocumentNodeImplementation::proto("graphene_core::graphic_element::LayerNode"), + implementation: DocumentNodeImplementation::proto("graphene_element_nodes::conversion::LayerNode"), ..Default::default() }, ] @@ -349,7 +349,7 @@ fn static_nodes() -> Vec { // 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::proto("graphene_core::graphic_element::ToArtboardNode"), + implementation: DocumentNodeImplementation::proto("graphene_element_nodes::conversion::ToArtboardNode"), inputs: vec![ NodeInput::network(concrete!(TaggedValue), 1), NodeInput::value(TaggedValue::String(String::from("Artboard")), false), @@ -376,7 +376,7 @@ fn static_nodes() -> Vec { NodeInput::node(NodeId(1), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath), ], - implementation: DocumentNodeImplementation::proto("graphene_core::graphic_element::AppendArtboardNode"), + implementation: DocumentNodeImplementation::proto("graphene_element_nodes::conversion::AppendArtboardNode"), ..Default::default() }, ] diff --git a/editor/src/messages/portfolio/document_migration.rs b/editor/src/messages/portfolio/document_migration.rs index 0683d74191..209948c70f 100644 --- a/editor/src/messages/portfolio/document_migration.rs +++ b/editor/src/messages/portfolio/document_migration.rs @@ -131,6 +131,13 @@ const REPLACEMENTS: &[(&str, &str)] = &[ ("graphene_std::raster::MaskImageNode", "graphene_std::raster::MaskNode"), ("graphene_core::vector::FlattenVectorElementsNode", "graphene_core::vector::FlattenPathNode"), ("graphene_std::vector::BooleanOperationNode", "graphene_path_bool::BooleanOperationNode"), + ("graphene_core::graphic_element::LayerNode", "graphene_element_nodes::conversion::LayerNode"), + ("graphene_core::graphic_element::ToElementNode", "graphene_element_nodes::conversion::ToElementNode"), + ("graphene_core::graphic_element::ToGroupNode", "graphene_element_nodes::conversion::ToGroupNode"), + ("graphene_core::graphic_element::FlattenGroupNode", "graphene_element_nodes::conversion::FlattenGroupNode"), + ("graphene_core::graphic_element::FlattenVectorNode", "graphene_element_nodes::conversion::FlattenVectorNode"), + ("graphene_core::graphic_element::ToArtboardNode", "graphene_element_nodes::conversion::ToArtboardNode"), + ("graphene_core::graphic_element::AppendArtboardNode", "graphene_element_nodes::conversion::AppendArtboardNode"), ]; pub fn document_migration_string_preprocessing(document_serialized_content: String) -> String { diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index d72e28df51..040360b043 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -567,7 +567,9 @@ mod test_artboard { Ok(instrumented) => instrumented, Err(e) => panic!("Failed to evaluate graph: {}", e), }; - instrumented.grab_all_input::(&editor.runtime).collect() + instrumented + .grab_all_input::(&editor.runtime) + .collect() } #[tokio::test] diff --git a/node-graph/gcore/src/graphic_element.rs b/node-graph/gcore/src/graphic_element.rs index 915d82344a..c8df19248e 100644 --- a/node-graph/gcore/src/graphic_element.rs +++ b/node-graph/gcore/src/graphic_element.rs @@ -1,13 +1,12 @@ use crate::blending::AlphaBlending; use crate::bounds::BoundingBox; +use crate::color::Color; use crate::instances::{Instance, Instances}; use crate::math::quad::Quad; use crate::raster::image::Image; use crate::raster_types::{CPU, GPU, Raster, RasterDataTable}; -use crate::transform::TransformMut; use crate::uuid::NodeId; use crate::vector::{VectorData, VectorDataTable}; -use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl}; use dyn_any::DynAny; use glam::{DAffine2, DVec2, IVec2}; use std::hash::Hash; @@ -323,188 +322,6 @@ impl BoundingBox for ArtboardGroupTable { } } -#[node_macro::node(category(""))] -async fn layer( - _: impl Ctx, - #[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable, RasterDataTable)] mut stack: Instances, - #[implementations(GraphicElement, VectorData, Raster, Raster)] element: I, - node_path: Vec, -) -> Instances { - // Get the penultimate element of the node path, or None if the path is too short - let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied(); - - stack.push(Instance { - instance: element, - transform: DAffine2::IDENTITY, - alpha_blending: AlphaBlending::default(), - source_node_id, - }); - - stack -} - -#[node_macro::node(category("Debug"))] -async fn to_element + 'n>( - _: impl Ctx, - #[implementations( - GraphicGroupTable, - VectorDataTable, - RasterDataTable, - RasterDataTable, - )] - data: Data, -) -> GraphicElement { - data.into() -} - -#[node_macro::node(category("General"))] -async fn to_group + 'n>( - _: impl Ctx, - #[implementations( - GraphicGroupTable, - VectorDataTable, - RasterDataTable, - RasterDataTable, - )] - element: Data, -) -> GraphicGroupTable { - element.into() -} - -#[node_macro::node(category("General"))] -async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: bool) -> GraphicGroupTable { - // TODO: Avoid mutable reference, instead return a new GraphicGroupTable? - fn flatten_group(output_group_table: &mut GraphicGroupTable, current_group_table: GraphicGroupTable, fully_flatten: bool, recursion_depth: usize) { - for current_instance in current_group_table.instance_ref_iter() { - let current_element = current_instance.instance.clone(); - let reference = *current_instance.source_node_id; - - let recurse = fully_flatten || recursion_depth == 0; - - match current_element { - // If we're allowed to recurse, flatten any GraphicGroups we encounter - GraphicElement::GraphicGroup(mut current_element) if recurse => { - // Apply the parent group's transform to all child elements - for graphic_element in current_element.instance_mut_iter() { - *graphic_element.transform = *current_instance.transform * *graphic_element.transform; - } - - flatten_group(output_group_table, current_element, fully_flatten, recursion_depth + 1); - } - // Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten - _ => { - output_group_table.push(Instance { - instance: current_element, - transform: *current_instance.transform, - alpha_blending: *current_instance.alpha_blending, - source_node_id: reference, - }); - } - } - } - } - - let mut output = GraphicGroupTable::default(); - flatten_group(&mut output, group, fully_flatten, 0); - - output -} - -#[node_macro::node(category("Vector"))] -async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTable { - // TODO: Avoid mutable reference, instead return a new GraphicGroupTable? - fn flatten_group(output_group_table: &mut VectorDataTable, current_group_table: GraphicGroupTable) { - for current_instance in current_group_table.instance_ref_iter() { - let current_element = current_instance.instance.clone(); - let reference = *current_instance.source_node_id; - - match current_element { - // If we're allowed to recurse, flatten any GraphicGroups we encounter - GraphicElement::GraphicGroup(mut current_element) => { - // Apply the parent group's transform to all child elements - for graphic_element in current_element.instance_mut_iter() { - *graphic_element.transform = *current_instance.transform * *graphic_element.transform; - } - - flatten_group(output_group_table, current_element); - } - // Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten - GraphicElement::VectorData(vector_instance) => { - for current_element in vector_instance.instance_ref_iter() { - output_group_table.push(Instance { - instance: current_element.instance.clone(), - transform: *current_instance.transform * *current_element.transform, - alpha_blending: AlphaBlending { - blend_mode: current_element.alpha_blending.blend_mode, - opacity: current_instance.alpha_blending.opacity * current_element.alpha_blending.opacity, - fill: current_element.alpha_blending.fill, - clip: current_element.alpha_blending.clip, - }, - source_node_id: reference, - }); - } - } - _ => {} - } - } - } - - let mut output = VectorDataTable::default(); - flatten_group(&mut output, group); - - output -} - -#[node_macro::node(category(""))] -async fn to_artboard + 'n>( - ctx: impl ExtractAll + CloneVarArgs + Ctx, - #[implementations( - Context -> GraphicGroupTable, - Context -> VectorDataTable, - Context -> RasterDataTable, - Context -> RasterDataTable, - )] - contents: impl Node, Output = Data>, - label: String, - location: IVec2, - dimensions: IVec2, - background: Color, - clip: bool, -) -> Artboard { - let footprint = ctx.try_footprint().copied(); - let mut new_ctx = OwnedContextImpl::from(ctx); - if let Some(mut footprint) = footprint { - footprint.translate(location.as_dvec2()); - new_ctx = new_ctx.with_footprint(footprint); - } - let graphic_group = contents.eval(new_ctx.into_context()).await; - - Artboard { - graphic_group: graphic_group.into(), - label, - location: location.min(location + dimensions), - dimensions: dimensions.abs(), - background, - clip, - } -} - -#[node_macro::node(category(""))] -async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec) -> ArtboardGroupTable { - // 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(Instance { - instance: artboard, - transform: DAffine2::IDENTITY, - alpha_blending: AlphaBlending::default(), - source_node_id: encapsulating_node_id, - }); - - artboards -} - // TODO: Remove this one impl From> for GraphicElement { fn from(raster_data: Image) -> Self { diff --git a/node-graph/gelement-nodes/src/conversion.rs b/node-graph/gelement-nodes/src/conversion.rs new file mode 100644 index 0000000000..58906da813 --- /dev/null +++ b/node-graph/gelement-nodes/src/conversion.rs @@ -0,0 +1,189 @@ +use glam::{DAffine2, IVec2}; +use graphene_core::instances::{Instance, Instances}; +use graphene_core::raster_types::{CPU, GPU, Raster, RasterDataTable}; +use graphene_core::transform::TransformMut; +use graphene_core::uuid::NodeId; +use graphene_core::vector::{VectorData, VectorDataTable}; +use graphene_core::{AlphaBlending, Artboard, ArtboardGroupTable, CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicElement, GraphicGroupTable, OwnedContextImpl}; + +#[node_macro::node(category(""))] +async fn layer( + _: impl Ctx, + #[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable, RasterDataTable)] mut stack: Instances, + #[implementations(GraphicElement, VectorData, Raster, Raster)] element: I, + node_path: Vec, +) -> Instances { + // Get the penultimate element of the node path, or None if the path is too short + let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied(); + + stack.push(Instance { + instance: element, + transform: DAffine2::IDENTITY, + alpha_blending: AlphaBlending::default(), + source_node_id, + }); + + stack +} + +#[node_macro::node(category("Debug"))] +async fn to_element + 'n>( + _: impl Ctx, + #[implementations( + GraphicGroupTable, + VectorDataTable, + RasterDataTable, + RasterDataTable, + )] + data: Data, +) -> GraphicElement { + data.into() +} + +#[node_macro::node(category("General"))] +async fn to_group + 'n>( + _: impl Ctx, + #[implementations( + GraphicGroupTable, + VectorDataTable, + RasterDataTable, + RasterDataTable, + )] + element: Data, +) -> GraphicGroupTable { + element.into() +} + +#[node_macro::node(category("General"))] +async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: bool) -> GraphicGroupTable { + // TODO: Avoid mutable reference, instead return a new GraphicGroupTable? + fn flatten_group(output_group_table: &mut GraphicGroupTable, current_group_table: GraphicGroupTable, fully_flatten: bool, recursion_depth: usize) { + for current_instance in current_group_table.instance_ref_iter() { + let current_element = current_instance.instance.clone(); + let reference = *current_instance.source_node_id; + + let recurse = fully_flatten || recursion_depth == 0; + + match current_element { + // If we're allowed to recurse, flatten any GraphicGroups we encounter + GraphicElement::GraphicGroup(mut current_element) if recurse => { + // Apply the parent group's transform to all child elements + for graphic_element in current_element.instance_mut_iter() { + *graphic_element.transform = *current_instance.transform * *graphic_element.transform; + } + + flatten_group(output_group_table, current_element, fully_flatten, recursion_depth + 1); + } + // Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten + _ => { + output_group_table.push(Instance { + instance: current_element, + transform: *current_instance.transform, + alpha_blending: *current_instance.alpha_blending, + source_node_id: reference, + }); + } + } + } + } + + let mut output = GraphicGroupTable::default(); + flatten_group(&mut output, group, fully_flatten, 0); + + output +} + +#[node_macro::node(category("Vector"))] +async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTable { + // TODO: Avoid mutable reference, instead return a new GraphicGroupTable? + fn flatten_group(output_group_table: &mut VectorDataTable, current_group_table: GraphicGroupTable) { + for current_instance in current_group_table.instance_ref_iter() { + let current_element = current_instance.instance.clone(); + let reference = *current_instance.source_node_id; + + match current_element { + // If we're allowed to recurse, flatten any GraphicGroups we encounter + GraphicElement::GraphicGroup(mut current_element) => { + // Apply the parent group's transform to all child elements + for graphic_element in current_element.instance_mut_iter() { + *graphic_element.transform = *current_instance.transform * *graphic_element.transform; + } + + flatten_group(output_group_table, current_element); + } + // Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten + GraphicElement::VectorData(vector_instance) => { + for current_element in vector_instance.instance_ref_iter() { + output_group_table.push(Instance { + instance: current_element.instance.clone(), + transform: *current_instance.transform * *current_element.transform, + alpha_blending: AlphaBlending { + blend_mode: current_element.alpha_blending.blend_mode, + opacity: current_instance.alpha_blending.opacity * current_element.alpha_blending.opacity, + fill: current_element.alpha_blending.fill, + clip: current_element.alpha_blending.clip, + }, + source_node_id: reference, + }); + } + } + _ => {} + } + } + } + + let mut output = VectorDataTable::default(); + flatten_group(&mut output, group); + + output +} + +#[node_macro::node(category(""))] +async fn to_artboard + 'n>( + ctx: impl ExtractAll + CloneVarArgs + Ctx, + #[implementations( + Context -> GraphicGroupTable, + Context -> VectorDataTable, + Context -> RasterDataTable, + Context -> RasterDataTable, + )] + contents: impl Node, Output = Data>, + label: String, + location: IVec2, + dimensions: IVec2, + background: Color, + clip: bool, +) -> Artboard { + let footprint = ctx.try_footprint().copied(); + let mut new_ctx = OwnedContextImpl::from(ctx); + if let Some(mut footprint) = footprint { + footprint.translate(location.as_dvec2()); + new_ctx = new_ctx.with_footprint(footprint); + } + let graphic_group = contents.eval(new_ctx.into_context()).await; + + Artboard { + graphic_group: graphic_group.into(), + label, + location: location.min(location + dimensions), + dimensions: dimensions.abs(), + background, + clip, + } +} + +#[node_macro::node(category(""))] +async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec) -> ArtboardGroupTable { + // 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(Instance { + instance: artboard, + transform: DAffine2::IDENTITY, + alpha_blending: AlphaBlending::default(), + source_node_id: encapsulating_node_id, + }); + + artboards +} diff --git a/node-graph/gelement-nodes/src/lib.rs b/node-graph/gelement-nodes/src/lib.rs index 6b4e1b1276..e45e23ca18 100644 --- a/node-graph/gelement-nodes/src/lib.rs +++ b/node-graph/gelement-nodes/src/lib.rs @@ -1,5 +1,6 @@ pub mod animation; pub mod blending_nodes; +pub mod conversion; pub mod instance; pub mod logic; pub mod transform_nodes; diff --git a/node-graph/gstd/src/lib.rs b/node-graph/gstd/src/lib.rs index 1e24ae49d7..e6b8b4bd5c 100644 --- a/node-graph/gstd/src/lib.rs +++ b/node-graph/gstd/src/lib.rs @@ -12,6 +12,7 @@ pub mod wasm_application_io; pub use graphene_application_io as application_io; pub use graphene_core::vector; pub use graphene_core::*; +pub use graphene_element_nodes as element_nodes; pub use graphene_element_nodes::animation; pub use graphene_math_nodes as math_nodes; pub use graphene_path_bool as path_bool;