mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Generalize layers as merge nodes to enable adjustment layers (#1712)
* WIP, backward traversal issues * Fix some tool issues * Remove debugging * Change some indices * WIP: new artboard node * WIP: add artboard node * WIP: Artboard node and create_artboard * WIP: Artboard node implementation complete * WIP: Artboards input for output node * Complete Artboard node * Generalize LayerNodeIdentifier, monitor_nodes support for Artboard node, adjust ResizeArtboard/ClearArtboards, move alias validation to Rust * Fix misaligned artboard click targets * Generalize/clarify create_layer and insert_between * non-negative dimensions for resize_artboard * Show artboards in layer panel * Generalize create_layer for layer output node * Generalize delete_layer/delete_artboard to NodeGraphMessage::DeleteNodes. Fixed upstream flow Iter * remove old primary_input function * Vertical node visuals, remove is_layer function, rename Layer node to Merge node, toggle display as layer exposed_value_count type fix Vertical node visuals, remove is_layer function, rename Layer node to Merge node, toggle display as layer * Fix demo artwork * Layer display context menu * Automatically select artboard, fix warnings * Improvements to context menu and layer invariant enforcement * Remove display_as_layer and update load_structure * Improve load_structure to show more layers, improve FlowIter, improve layer naming, layer rearrangement validation. * Clean up demo artwork using generalized layers * Improve design of Layers panel and graph nodes * MoveSelectedLayersTo rewrite to support generalized layer nodes * Include artboards in deepest_common_ancestor, fix resize_artboard/delete_artboard, sync artboard tool to layer panel * MoveSelectedLayersTo adjustments * Sync non layer node visibility with metadata * Include non layer nodes when moving/creating layer * Fix group layers and get_post_node_with_index * Include non layer nodes in UngroupSelectedLayers * GroupSelected for all selected nodes, UnGroupSelected position adjustments * Add grouping for layers in different folders * Fix hidden layers * Prevent node from connecting to itself, fix undo automatic node insertion, * Fix undo CreateEmptyFolder, fix grouping nested layer nodes * Formatting * Remove test and check if node is layer from network * Fix undo group layers * Check off roadmap * MoveUpstreamSiblingsToChild adjustments * Replace tabs with spaces, remove mut from argument * Final code review pass --------- Co-authored-by: 0hypercube <0hypercube@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -106,6 +106,34 @@ impl Artboard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains multiple artboards.
|
||||
#[derive(Clone, Default, Debug, Hash, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ArtboardGroup {
|
||||
pub artboards: Vec<Artboard>,
|
||||
}
|
||||
|
||||
impl ArtboardGroup {
|
||||
pub const EMPTY: Self = Self { artboards: Vec::new() };
|
||||
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn add_artboard(&mut self, artboard: Artboard) {
|
||||
self.artboards.push(artboard);
|
||||
}
|
||||
|
||||
pub fn get_graphic_group(&self) -> GraphicGroup {
|
||||
let mut graphic_group = GraphicGroup::EMPTY;
|
||||
for artboard in self.artboards.clone() {
|
||||
let graphic_element: GraphicElement = artboard.into();
|
||||
graphic_group.push(graphic_element);
|
||||
}
|
||||
graphic_group
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConstructLayerNode<GraphicElement, Stack> {
|
||||
graphic_element: GraphicElement,
|
||||
stack: Stack,
|
||||
@@ -157,6 +185,24 @@ async fn construct_artboard<Fut: Future<Output = GraphicGroup>>(
|
||||
clip,
|
||||
}
|
||||
}
|
||||
pub struct AddArtboardNode<Artboard, ArtboardGroup> {
|
||||
artboard: Artboard,
|
||||
artboards: ArtboardGroup,
|
||||
}
|
||||
|
||||
#[node_fn(AddArtboardNode)]
|
||||
async fn add_artboard<Data: Into<Artboard>, Fut1: Future<Output = Data>, Fut2: Future<Output = ArtboardGroup>>(
|
||||
footprint: Footprint,
|
||||
artboard: impl Node<Footprint, Output = Fut1>,
|
||||
mut artboards: impl Node<Footprint, Output = Fut2>,
|
||||
) -> ArtboardGroup {
|
||||
let artboard = self.artboard.eval(footprint).await;
|
||||
let mut artboards = self.artboards.eval(footprint).await;
|
||||
|
||||
artboards.add_artboard(artboard.into());
|
||||
|
||||
artboards
|
||||
}
|
||||
|
||||
impl From<ImageFrame<Color>> for GraphicElement {
|
||||
fn from(mut image_frame: ImageFrame<Color>) -> Self {
|
||||
|
||||
@@ -447,7 +447,7 @@ impl GraphicElementRendered for Artboard {
|
||||
}
|
||||
|
||||
fn add_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
let subpath = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2());
|
||||
let subpath = Subpath::new_rect(self.location.as_dvec2(), self.location.as_dvec2() + self.dimensions.as_dvec2());
|
||||
click_targets.push(ClickTarget { stroke_width: 0., subpath });
|
||||
}
|
||||
|
||||
@@ -456,6 +456,23 @@ impl GraphicElementRendered for Artboard {
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicElementRendered for crate::ArtboardGroup {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
self.get_graphic_group().render_svg(render, render_params);
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||
self.get_graphic_group().bounding_box(transform)
|
||||
}
|
||||
|
||||
fn add_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
self.get_graphic_group().add_click_targets(click_targets);
|
||||
}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
impl GraphicElementRendered for ImageFrame<Color> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
let transform: String = format_transform_matrix(self.transform * render.transform);
|
||||
|
||||
@@ -3,7 +3,8 @@ use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
pub use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::{GraphicGroup, ProtoNodeIdentifier, Type};
|
||||
use graphene_core::vector::VectorData;
|
||||
use graphene_core::{ArtboardGroup, GraphicGroup, ProtoNodeIdentifier, Type};
|
||||
|
||||
use glam::IVec2;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
@@ -76,8 +77,8 @@ pub struct DocumentNode {
|
||||
/// - Concrete example: a node that takes an image as primary input will get that image data from an upstream node that produces image output data and is evaluated first before being fed downstream.
|
||||
///
|
||||
/// This is achieved by automatically inserting `ComposeNode`s, which run the first node with the overall input and then feed the resulting output into the second node.
|
||||
/// The `ComposeNode` is basically a function composition operator: the parentheses in `F(G(x))` or circle math operator in `(G ∘ F)(x)`.
|
||||
/// For flexability, instead of being a language construct, Graphene splits out composition itself as its own low-level node so that behavior can be overridden.
|
||||
/// The `ComposeNode` is basically a function composition operator: the parentheses in `F(G(x))` or circle math operator in `(F ∘ G)(x)`.
|
||||
/// For flexibility, instead of being a language construct, Graphene splits out composition itself as its own low-level node so that behavior can be overridden.
|
||||
/// The `ComposeNode`s are then inserted during the graph rewriting step for nodes that don't opt out with `manual_composition`.
|
||||
/// Instead of node `G` feeding into node `F` feeding as the result back to the caller,
|
||||
/// the graph is rewritten so nodes `G` and `F` both feed as lambdas into the parameters of a `ComposeNode` which calls `F(G(input))` and returns the result to the caller.
|
||||
@@ -159,6 +160,9 @@ pub struct DocumentNode {
|
||||
pub has_primary_output: bool,
|
||||
// A nested document network or a proto-node identifier.
|
||||
pub implementation: DocumentNodeImplementation,
|
||||
/// User chosen state for displaying this as a left-to-right node or bottom-to-top layer.
|
||||
#[serde(default)]
|
||||
pub is_layer: bool,
|
||||
/// Represents the eye icon for hiding/showing the node in the graph UI. When hidden, a node gets replaced with an identity node during the graph flattening step.
|
||||
#[serde(default = "return_true")]
|
||||
pub visible: bool,
|
||||
@@ -212,6 +216,7 @@ impl Default for DocumentNode {
|
||||
manual_composition: Default::default(),
|
||||
has_primary_output: true,
|
||||
implementation: Default::default(),
|
||||
is_layer: false,
|
||||
visible: true,
|
||||
locked: Default::default(),
|
||||
metadata: Default::default(),
|
||||
@@ -348,21 +353,24 @@ impl DocumentNode {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_layer(&self) -> bool {
|
||||
// TODO: Use something more robust than checking against a string.
|
||||
// TODO: Or, more fundamentally separate the concept of a layer from a node.
|
||||
self.name == "Layer"
|
||||
}
|
||||
|
||||
pub fn is_artboard(&self) -> bool {
|
||||
// TODO: Use something more robust than checking against a string.
|
||||
// TODO: Or, more fundamentally separate the concept of a layer from a node.
|
||||
self.name == "Artboard"
|
||||
}
|
||||
|
||||
pub fn is_folder(&self, network: &NodeNetwork) -> bool {
|
||||
let input_connection = self.inputs.get(0).and_then(|input| input.as_node()).and_then(|node_id| network.nodes.get(&node_id));
|
||||
input_connection.map(|node| node.is_layer()).unwrap_or(false)
|
||||
// TODO: Is this redundant with `LayerNodeIdentifier::has_children()`? Consider removing this in favor of that.
|
||||
/// Determines if a document node acting as a layer has any nested children where its secondary input eventually leads to a layer along horizontal flow.
|
||||
pub fn layer_has_child_layers(&self, network: &NodeNetwork) -> bool {
|
||||
if !self.is_layer {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.inputs.iter().skip(1).any(|input| {
|
||||
input.as_node().map_or(false, |node_id| {
|
||||
network.upstream_flow_back_from_nodes(vec![node_id], FlowType::HorizontalFlow).any(|(node, _)| node.is_layer)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,6 +546,16 @@ pub struct NodeNetwork {
|
||||
pub previous_outputs: Option<Vec<NodeOutput>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum FlowType {
|
||||
/// Iterate over all upstream nodes from every input (the primary and all secondary).
|
||||
UpstreamFlow,
|
||||
/// Iterate over nodes connected to the primary input.
|
||||
PrimaryFlow,
|
||||
/// Iterate over the secondary input for layer nodes and primary input for non layer nodes.
|
||||
HorizontalFlow,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for NodeNetwork {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.imports.hash(state);
|
||||
@@ -741,18 +759,18 @@ impl NodeNetwork {
|
||||
self.previous_outputs.as_ref().map(|outputs| outputs.iter().any(|output| output.node_id == node_id))
|
||||
}
|
||||
|
||||
/// Gives an iterator to all nodes connected to the given nodes by all inputs (primary or primary + secondary depending on `only_follow_primary` choice), traversing backwards upstream starting from the given node's inputs.
|
||||
pub fn upstream_flow_back_from_nodes(&self, node_ids: Vec<NodeId>, only_follow_primary: bool) -> impl Iterator<Item = (&DocumentNode, NodeId)> {
|
||||
/// Gives an iterator to all nodes connected to the given nodes (inclusive) by all inputs (primary or primary + secondary depending on `only_follow_primary` choice), traversing backwards upstream starting from the given node's inputs.
|
||||
pub fn upstream_flow_back_from_nodes(&self, node_ids: Vec<NodeId>, flow_type: FlowType) -> impl Iterator<Item = (&DocumentNode, NodeId)> {
|
||||
FlowIter {
|
||||
stack: node_ids,
|
||||
network: self,
|
||||
only_follow_primary,
|
||||
flow_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// In the network `X -> Y -> Z`, `is_node_upstream_of_another_by_primary_flow(Z, X)` returns true.
|
||||
pub fn is_node_upstream_of_another_by_primary_flow(&self, node: NodeId, potentially_upstream_node: NodeId) -> bool {
|
||||
self.upstream_flow_back_from_nodes(vec![node], true).any(|(_, id)| id == potentially_upstream_node)
|
||||
pub fn is_node_upstream_of_another_by_horizontal_flow(&self, node: NodeId, potentially_upstream_node: NodeId) -> bool {
|
||||
self.upstream_flow_back_from_nodes(vec![node], FlowType::HorizontalFlow).any(|(_, id)| id == potentially_upstream_node)
|
||||
}
|
||||
|
||||
/// Check there are no cycles in the graph (this should never happen).
|
||||
@@ -789,11 +807,14 @@ impl NodeNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over the primary inputs of nodes, so in the case of `a -> b -> c`, this would yield `c, b, a` if we started from `c`.
|
||||
/// Iterate over upstream nodes. The behavior changes based on the `flow_type` that's set.
|
||||
/// - [`FlowType::UpstreamFlow`]: iterates over all upstream nodes from every input (the primary and all secondary).
|
||||
/// - [`FlowType::PrimaryFlow`]: iterates along the horizontal inputs of nodes, so in the case of a node chain `a -> b -> c`, this would yield `c, b, a` if we started from `c`.
|
||||
/// - [`FlowType::HorizontalFlow`]: iterates over the secondary input for layer nodes and primary input for non layer nodes.
|
||||
struct FlowIter<'a> {
|
||||
stack: Vec<NodeId>,
|
||||
network: &'a NodeNetwork,
|
||||
only_follow_primary: bool,
|
||||
flow_type: FlowType,
|
||||
}
|
||||
impl<'a> Iterator for FlowIter<'a> {
|
||||
type Item = (&'a DocumentNode, NodeId);
|
||||
@@ -802,8 +823,9 @@ impl<'a> Iterator for FlowIter<'a> {
|
||||
let node_id = self.stack.pop()?;
|
||||
|
||||
if let Some(document_node) = self.network.nodes.get(&node_id) {
|
||||
let take = if self.only_follow_primary { 1 } else { usize::MAX };
|
||||
let inputs = document_node.inputs.iter().take(take);
|
||||
let skip = if self.flow_type == FlowType::HorizontalFlow && document_node.is_layer { 1 } else { 0 };
|
||||
let take = if self.flow_type == FlowType::UpstreamFlow { usize::MAX } else { 1 };
|
||||
let inputs = document_node.inputs.iter().skip(skip).take(take);
|
||||
|
||||
let node_ids = inputs.filter_map(|input| if let NodeInput::Node { node_id, .. } = input { Some(node_id) } else { None });
|
||||
|
||||
@@ -940,12 +962,8 @@ impl NodeNetwork {
|
||||
if !node.visible && node.implementation != identity_node {
|
||||
node.implementation = identity_node;
|
||||
|
||||
if node.is_layer() {
|
||||
// Connect layer node to the graphic group below
|
||||
node.inputs.drain(..1);
|
||||
} else {
|
||||
node.inputs.drain(1..);
|
||||
}
|
||||
// Connect layer node to the graphic group below
|
||||
node.inputs.drain(1..);
|
||||
self.nodes.insert(id, node);
|
||||
|
||||
return;
|
||||
@@ -1169,37 +1187,39 @@ impl NodeNetwork {
|
||||
/// However, in the case of the default input, we must insert a node that takes an input of `Footprint` and returns `GraphicGroup::Empty`, in order to satisfy the type system.
|
||||
/// This is because the standard value node takes in `()`.
|
||||
pub fn resolve_empty_stacks(&mut self) {
|
||||
const EMPTY_STACK: &str = "Empty Stack";
|
||||
for value in [
|
||||
TaggedValue::GraphicGroup(GraphicGroup::EMPTY),
|
||||
TaggedValue::VectorData(VectorData::empty()),
|
||||
TaggedValue::ArtboardGroup(ArtboardGroup::EMPTY),
|
||||
] {
|
||||
const EMPTY_STACK: &str = "Empty Stack";
|
||||
|
||||
let new_id = generate_uuid();
|
||||
let mut used = false;
|
||||
let new_id = generate_uuid();
|
||||
let mut used = false;
|
||||
|
||||
// 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 let NodeInput::Value {
|
||||
tagged_value: TaggedValue::GraphicGroup(graphic_group),
|
||||
..
|
||||
} = input
|
||||
{
|
||||
if *graphic_group == GraphicGroup::EMPTY {
|
||||
*input = NodeInput::node(NodeId(new_id), 0);
|
||||
used = true;
|
||||
// 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 let NodeInput::Value { tagged_value, .. } = input {
|
||||
if *tagged_value == value {
|
||||
*input = NodeInput::node(NodeId(new_id), 0);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only insert the node if necessary.
|
||||
if used {
|
||||
let new_node = DocumentNode {
|
||||
name: EMPTY_STACK.to_string(),
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::transform::CullNode<_>"),
|
||||
manual_composition: Some(concrete!(graphene_core::transform::Footprint)),
|
||||
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(graphene_core::GraphicGroup::EMPTY), false)],
|
||||
..Default::default()
|
||||
};
|
||||
self.nodes.insert(NodeId(new_id), new_node);
|
||||
// Only insert the node if necessary.
|
||||
if used {
|
||||
let new_node = DocumentNode {
|
||||
name: EMPTY_STACK.to_string(),
|
||||
implementation: DocumentNodeImplementation::proto("graphene_core::transform::CullNode<_>"),
|
||||
manual_composition: Some(concrete!(graphene_core::transform::Footprint)),
|
||||
inputs: vec![NodeInput::value(value, false)],
|
||||
..Default::default()
|
||||
};
|
||||
self.nodes.insert(NodeId(new_id), new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ pub enum TaggedValue {
|
||||
DocumentNode(DocumentNode),
|
||||
GraphicGroup(graphene_core::GraphicGroup),
|
||||
Artboard(graphene_core::Artboard),
|
||||
ArtboardGroup(graphene_core::ArtboardGroup),
|
||||
Curve(graphene_core::raster::curve::Curve),
|
||||
IVec2(glam::IVec2),
|
||||
SurfaceFrame(graphene_core::SurfaceFrame),
|
||||
@@ -148,6 +149,7 @@ impl Hash for TaggedValue {
|
||||
Self::DocumentNode(x) => x.hash(state),
|
||||
Self::GraphicGroup(x) => x.hash(state),
|
||||
Self::Artboard(x) => x.hash(state),
|
||||
Self::ArtboardGroup(x) => x.hash(state),
|
||||
Self::Curve(x) => x.hash(state),
|
||||
Self::IVec2(x) => x.hash(state),
|
||||
Self::SurfaceFrame(x) => x.hash(state),
|
||||
@@ -214,6 +216,7 @@ impl<'a> TaggedValue {
|
||||
TaggedValue::DocumentNode(x) => Box::new(x),
|
||||
TaggedValue::GraphicGroup(x) => Box::new(x),
|
||||
TaggedValue::Artboard(x) => Box::new(x),
|
||||
TaggedValue::ArtboardGroup(x) => Box::new(x),
|
||||
TaggedValue::Curve(x) => Box::new(x),
|
||||
TaggedValue::IVec2(x) => Box::new(x),
|
||||
TaggedValue::SurfaceFrame(x) => Box::new(x),
|
||||
@@ -292,6 +295,7 @@ impl<'a> TaggedValue {
|
||||
TaggedValue::DocumentNode(_) => concrete!(crate::document::DocumentNode),
|
||||
TaggedValue::GraphicGroup(_) => concrete!(graphene_core::GraphicGroup),
|
||||
TaggedValue::Artboard(_) => concrete!(graphene_core::Artboard),
|
||||
TaggedValue::ArtboardGroup(_) => concrete!(graphene_core::ArtboardGroup),
|
||||
TaggedValue::Curve(_) => concrete!(graphene_core::raster::curve::Curve),
|
||||
TaggedValue::IVec2(_) => concrete!(glam::IVec2),
|
||||
TaggedValue::SurfaceFrame(_) => concrete!(graphene_core::SurfaceFrame),
|
||||
|
||||
@@ -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, Artboard, GraphicGroup};
|
||||
use graphene_core::{concrete, generic, Artboard, ArtboardGroup, GraphicGroup};
|
||||
use graphene_core::{fn_type, raster::*};
|
||||
use graphene_core::{Cow, ProtoNodeIdentifier, Type};
|
||||
use graphene_core::{Node, NodeIO, NodeIOTypes};
|
||||
@@ -345,6 +345,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicGroup, fn_params: [Footprint => graphene_core::GraphicGroup]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicElement, fn_params: [Footprint => graphene_core::GraphicElement]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: Artboard, fn_params: [Footprint => graphene_core::Artboard]),
|
||||
async_node!(graphene_std::wasm_application_io::LoadResourceNode<_>, input: WasmEditorApi, output: Arc<[u8]>, params: [String]),
|
||||
register_node!(graphene_std::wasm_application_io::DecodeImageNode, input: Arc<[u8]>, params: []),
|
||||
async_node!(graphene_std::wasm_application_io::CreateSurfaceNode, input: WasmEditorApi, output: Arc<SurfaceHandle<<graphene_std::wasm_application_io::WasmApplicationIo as graphene_core::application_io::ApplicationIo>::Surface>>, params: []),
|
||||
@@ -678,6 +679,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
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: [Footprint => ArtboardGroup, () => 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>]),
|
||||
@@ -726,6 +728,25 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
)],
|
||||
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [Artboard]),
|
||||
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [ImageFrame<Color>]),
|
||||
vec![(
|
||||
ProtoNodeIdentifier::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::<ArtboardGroup>(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>>> + '_>
|
||||
})
|
||||
},
|
||||
{
|
||||
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::PanicNode::<(), ArtboardGroup>::new());
|
||||
let params = vec![fn_type!((), ArtboardGroup)];
|
||||
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
|
||||
},
|
||||
)],
|
||||
vec![(
|
||||
ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>"),
|
||||
|args| {
|
||||
@@ -776,6 +797,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
register_node!(graphene_core::ToGraphicElementNode, input: GraphicGroup, params: []),
|
||||
register_node!(graphene_core::ToGraphicElementNode, input: Artboard, params: []),
|
||||
async_node!(graphene_core::ConstructArtboardNode<_, _, _, _, _>, input: Footprint, output: Artboard, fn_params: [Footprint => GraphicGroup, () => glam::IVec2, () => glam::IVec2, () => Color, () => bool]),
|
||||
async_node!(graphene_core::AddArtboardNode<_, _>, input: Footprint, output: ArtboardGroup, fn_params: [Footprint => Artboard, Footprint => ArtboardGroup]),
|
||||
];
|
||||
let mut map: HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
|
||||
for (id, c, types) in node_types.into_iter().flatten() {
|
||||
|
||||
Reference in New Issue
Block a user