From aab0fcf84c3e4f607fe9316be89e9adc0e8fcef5 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sat, 13 Jan 2024 04:15:36 -0800 Subject: [PATCH] Update graph UI and improve simplicity and robustness of sending graph and layer panel updates (#1564) * WIP * Fix loading the structure of layers * Fix broken indents * Remove debugging stuff * Fix displaying errors and node graph UI fixes/improvements * Fix compilation failure --------- Co-authored-by: 0hypercube <0hypercube@gmail.com> --- .../procedural-string-lights.graphite | 2 +- editor/src/dispatcher.rs | 27 +- .../messages/dialog/dialog_message_handler.rs | 2 +- .../src/messages/frontend/frontend_message.rs | 8 +- editor/src/messages/frontend/utility_types.rs | 7 - .../portfolio/document/document_message.rs | 1 - .../document/document_message_handler.rs | 131 +++--- .../graph_operation_message_handler.rs | 21 +- .../document/node_graph/node_graph_message.rs | 7 +- .../node_graph/node_graph_message_handler.rs | 232 ++++++---- .../document/utility_types/layer_panel.rs | 5 +- .../portfolio/document/utility_types/misc.rs | 2 - .../messages/portfolio/portfolio_message.rs | 3 - .../portfolio/portfolio_message_handler.rs | 21 +- .../tool/common_functionality/pivot.rs | 2 +- .../src/messages/tool/tool_message_handler.rs | 7 +- .../messages/tool/tool_messages/path_tool.rs | 36 +- .../tool/tool_messages/select_tool.rs | 10 +- .../transform_layer_message_handler.rs | 2 +- editor/src/node_graph_executor.rs | 407 ++++++++---------- frontend/src-tauri/src/main.rs | 22 +- frontend/src/components/panels/Layers.svelte | 88 +++- frontend/src/components/views/Graph.svelte | 317 +++++++++----- frontend/src/state-providers/node-graph.ts | 19 +- frontend/src/state-providers/portfolio.ts | 16 - frontend/src/wasm-communication/messages.ts | 95 +--- frontend/wasm/src/editor_api.rs | 32 +- node-graph/gcore/src/application_io.rs | 1 + .../gcore/src/graphic_element/renderer.rs | 102 ++--- node-graph/gcore/src/memo.rs | 1 + node-graph/graph-craft/src/document.rs | 16 +- node-graph/gstd/src/wasm_application_io.rs | 6 +- website/content/learn/_index.md | 1 + 33 files changed, 836 insertions(+), 813 deletions(-) diff --git a/demo-artwork/procedural-string-lights.graphite b/demo-artwork/procedural-string-lights.graphite index 3edb8354..a537b9fd 100644 --- a/demo-artwork/procedural-string-lights.graphite +++ b/demo-artwork/procedural-string-lights.graphite @@ -1 +1 @@ -{"network":{"inputs":[],"outputs":[{"node_id":0,"node_output_index":0}],"nodes":{"5510431876617456229":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":15965443467772892221,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[674.6856534818174,385.8339304530032]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[17.212796374136204,27.61219418351016]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-35,34]},"skip_deduplication":false,"world_state_hash":0},"12437938754636694806":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":11130614062948033510,"output_index":0,"lambda":false}},{"Node":{"node_id":14944076597430308366,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,24]},"skip_deduplication":false,"world_state_hash":0},"12790418218700356896":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":14972365039974884537,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[6,19]},"skip_deduplication":false,"world_state_hash":0},"1005063698578632777":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":1609067788579460449,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[503.7983132089414,104.19466945763644]},"exposed":false}},{"Value":{"tagged_value":{"F32":-0.2920178},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[110.93416360223218,110.93416360223218]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[-1.6012712240474374e-16,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,58]},"skip_deduplication":false,"world_state_hash":0},"3471929742275053204":{"alias":"Red Lights","name":"Layer","inputs":[{"Node":{"node_id":4279275325545336233,"output_index":0,"lambda":false}},{"Node":{"node_id":9015611177809361449,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,31]},"skip_deduplication":false,"world_state_hash":0},"13371003476981866369":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":13449829627181121540,"output_index":0,"lambda":false}},{"Node":{"node_id":16765094648901305481,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,34]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Output","inputs":[{"Node":{"node_id":12790418218700356896,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"graphene_core::application_io::EditorApi","size":184,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[3,0],"outputs":[{"node_id":3,"node_output_index":0}],"nodes":{"3":{"alias":"","name":"RenderNode","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Generic":"T"}]}},{"Node":{"node_id":2,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_std::wasm_application_io::RenderNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"EditorApi","inputs":[{"Network":{"Concrete":{"name":"graphene_core::application_io::EditorApi","size":184,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Cache","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"()","size":0,"align":1}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MemoNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Create Canvas","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_std::wasm_application_io::CreateSurfaceNode"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[10,17]},"skip_deduplication":false,"world_state_hash":0},"1279981353152889207":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":17339085479159577045,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[427.7531240523346,720.8882779290919]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.9777789484064812,1.9777789484064812]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,67]},"skip_deduplication":false,"world_state_hash":0},"9015611177809361449":{"alias":"Blue Lights","name":"Layer","inputs":[{"Node":{"node_id":15465970086096837636,"output_index":0,"lambda":false}},{"Node":{"node_id":11411423299989984040,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,41]},"skip_deduplication":false,"world_state_hash":0},"11130614062948033510":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":5510431876617456229,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.16470589,"green":0.8862745,"blue":0.4117647,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.4915209831246563,0.3534391534391531]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.49551110871305326,0.9968253968253964]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[250.61867799546343,0.0,0.0,472.4999999999999,809.8156610022683,336.0000000000002]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.96484375,"green":1.0,"blue":0.9765626,"alpha":1.0}],[1.0,{"red":0.16470589,"green":0.8862745,"blue":0.4117647,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,24]},"skip_deduplication":false,"world_state_hash":0},"15889416971203221938":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":7331575674671647159,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Solid"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.89411765,"green":0.654902,"blue":0.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,61]},"skip_deduplication":false,"world_state_hash":0},"15465970086096837636":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":8051777741243064505,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,41]},"skip_deduplication":false,"world_state_hash":0},"4534782777857480744":{"alias":"Star Base","name":"Layer","inputs":[{"Node":{"node_id":11407829185230551056,"output_index":0,"lambda":false}},{"Node":{"node_id":183562335973647865,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,61]},"skip_deduplication":false,"world_state_hash":0},"655907162126315358":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[408.0,304.5999999999999],"in_handle":[408.0,304.5999999999999],"out_handle":[408.0,304.5999999999999],"id":14295162783162130675},{"anchor":[579.0,325.5999999999999],"in_handle":[579.0,325.5999999999999],"out_handle":[579.0,325.5999999999999],"id":17235651057772975540},{"anchor":[365.0,406.5999999999999],"in_handle":[365.0,406.5999999999999],"out_handle":[365.0,406.5999999999999],"id":11062587772478366343},{"anchor":[476.0,480.5999999999999],"in_handle":[476.0,480.5999999999999],"out_handle":[476.0,480.5999999999999],"id":13905111493229779048},{"anchor":[631.0,472.5999999999999],"in_handle":[631.0,472.5999999999999],"out_handle":[631.0,472.5999999999999],"id":9818595361384120279},{"anchor":[487.0,567.5999999999999],"in_handle":[487.0,567.5999999999999],"out_handle":[487.0,567.5999999999999],"id":6558449122167484557},{"anchor":[296.0,645.5999999999999],"in_handle":[296.0,645.5999999999999],"out_handle":[296.0,645.5999999999999],"id":984077780188855675},{"anchor":[467.0,705.5999999999999],"in_handle":[467.0,705.5999999999999],"out_handle":[467.0,705.5999999999999],"id":13728222455061026873},{"anchor":[742.0,737.5999999999999],"in_handle":[742.0,737.5999999999999],"out_handle":[742.0,737.5999999999999],"id":13024161242572909643},{"anchor":[447.0,858.5999999999999],"in_handle":[447.0,858.5999999999999],"out_handle":[447.0,858.5999999999999],"id":6910875099560930892},{"anchor":[178.0,858.5999999999999],"in_handle":[178.0,858.5999999999999],"out_handle":[178.0,858.5999999999999],"id":11714154815938872622}],"closed":false}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-55,35]},"skip_deduplication":false,"world_state_hash":0},"17339085479159577045":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[88.4444444444444,151.55555555555554],"in_handle":[88.4444444444444,151.55555555555554],"out_handle":[88.4444444444444,151.55555555555554],"id":14237658187363817063},{"anchor":[122.22222222222224,196.4444444444444],"in_handle":[90.22222222222224,192.88888888888889],"out_handle":[122.22222222222224,196.4444444444444],"id":10365474939830860786},{"anchor":[123.55555555555554,199.1111111111111],"in_handle":[123.55555555555554,199.1111111111111],"out_handle":[123.55555555555554,199.1111111111111],"id":4109246593800547547},{"anchor":[28.296296296296305,198.96296296296293],"in_handle":[28.296296296296305,198.96296296296293],"out_handle":[28.296296296296305,198.96296296296293],"id":3671345045987042142},{"anchor":[28.296296296296305,197.33333333333331],"in_handle":[28.296296296296305,197.33333333333331],"out_handle":[28.296296296296305,197.33333333333331],"id":8747947169598042921},{"anchor":[58.962962962962976,152.74074074074073],"in_handle":[64.88888888888889,198.5185185185186],"out_handle":[58.962962962962976,152.74074074074073],"id":10921007090975078447},{"anchor":[66.51851851851853,147.1111111111111],"in_handle":[66.51851851851853,147.1111111111111],"out_handle":[66.51851851851853,147.1111111111111],"id":10733773696880555352}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,67]},"skip_deduplication":false,"world_state_hash":0},"9387390658960403506":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":6315702878481394765,"output_index":0,"lambda":false}},{"Node":{"node_id":15087384306214154157,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,44]},"skip_deduplication":false,"world_state_hash":0},"7331575674671647159":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":8309013977031955578,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[499.32116017766305,139.74585648084397]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[25.393705016577044,25.003032631706716]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,61]},"skip_deduplication":false,"world_state_hash":0},"15888652418311111787":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":2908374490615384647,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,67]},"skip_deduplication":false,"world_state_hash":0},"15087384306214154157":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":2550166071846864271,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,47]},"skip_deduplication":false,"world_state_hash":0},"3773322085315920844":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":1005063698578632777,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":1.0,"green":0.0,"blue":1.0,"alpha":0.5}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.4703098217208352,0.4995258072961386]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.9924395932459462,0.5005395053456176]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[237.34320332463173,-71.34811668265112,75.01984946235177,249.55741247555255,995.9008094918244,343.8953521035491]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":1.0,"green":0.94460994,"blue":0.79296875,"alpha":1.0}],[1.0,{"red":0.89411765,"green":0.654902,"blue":0.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,58]},"skip_deduplication":false,"world_state_hash":0},"13449829627181121540":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":5510431876617456229,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.8862745,"green":0.16470589,"blue":0.16470589,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.4915209831246563,0.36613756613756576]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.49551110871305326,0.9947089947089944]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[250.61867799546343,0.0,0.0,472.4999999999999,809.8156610022683,336.0000000000002]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.91796875,"green":0.68489075,"blue":0.68489075,"alpha":1.0}],[1.0,{"red":0.8862745,"green":0.16470589,"blue":0.16470589,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,34]},"skip_deduplication":false,"world_state_hash":0},"8309013977031955578":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[0.0,0.0],"in_handle":[0.0,0.0],"out_handle":[0.0,0.0],"id":1980203248399978225},{"anchor":[1.0,0.0],"in_handle":[1.0,0.0],"out_handle":[1.0,0.0],"id":6040860589979410580},{"anchor":[1.0,1.0],"in_handle":[1.0,1.0],"out_handle":[1.0,1.0],"id":5680533130302393152},{"anchor":[0.0,1.0],"in_handle":[0.0,1.0],"out_handle":[0.0,1.0],"id":13547805951820623445}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,61]},"skip_deduplication":false,"world_state_hash":0},"234528620577149363":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"F32":300.0},"exposed":false}},{"Value":{"tagged_value":{"F32":200.0},"exposed":false}},{"Value":{"tagged_value":{"F32":100.0},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2,2,2,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Lengths of Segments of Subpaths","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::LengthsOfSegmentsOfSubpaths"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Identity","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"bool","size":1,"align":1}}},{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SamplePoints<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,41]},"skip_deduplication":false,"world_state_hash":0},"665049002420596388":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[-103.55555555555554,126.66666666666664],"in_handle":[-37.77777777777774,139.1111111111111],"out_handle":[-103.55555555555554,126.66666666666664],"id":5137685238696701713},{"anchor":[-67.55555555555554,158.22222222222217],"in_handle":[-98.22222222222216,155.5555555555555],"out_handle":[-36.888888888888914,160.88888888888886],"id":10349149476918669943},{"anchor":[45.77777777777777,145.33333333333331],"in_handle":[-5.333333333333314,160.0],"out_handle":[45.77777777777777,145.33333333333331],"id":15777752779448746803},{"anchor":[18.66666666666663,157.77777777777771],"in_handle":[27.11111111111103,156.8888888888888],"out_handle":[18.66666666666663,157.77777777777771],"id":14911865974833225081},{"anchor":[66.22222222222223,152.88888888888886],"in_handle":[34.666666666666686,171.1111111111111],"out_handle":[66.22222222222223,152.88888888888886],"id":14106726470037375831},{"anchor":[123.99999999999994,148.4444444444444],"in_handle":[110.66666666666656,162.66666666666663],"out_handle":[123.99999999999994,148.4444444444444],"id":11411841702773087978},{"anchor":[229.33333333333331,152.4444444444444],"in_handle":[204.4444444444444,167.5555555555555],"out_handle":[254.22222222222223,137.33333333333331],"id":4172777474455727102},{"anchor":[252.4444444444444,124.88888888888886],"in_handle":[252.4444444444444,124.88888888888886],"out_handle":[252.4444444444444,124.88888888888886],"id":2435884934966232555},{"anchor":[156.4444444444444,97.33333333333331],"in_handle":[192.4444444444444,139.1111111111111],"out_handle":[156.4444444444444,97.33333333333331],"id":47350109196208952},{"anchor":[216.4444444444444,72.4444444444444],"in_handle":[182.22222222222211,108.4444444444444],"out_handle":[216.4444444444444,72.4444444444444],"id":12672076925927621110},{"anchor":[136.88888888888886,39.111111111111086],"in_handle":[163.5555555555555,83.55555555555554],"out_handle":[136.88888888888886,39.111111111111086],"id":3095729341078152823},{"anchor":[199.5555555555555,10.666666666666629],"in_handle":[175.5555555555555,42.22222222222217],"out_handle":[199.5555555555555,10.666666666666629],"id":11967596188552629860},{"anchor":[153.77777777777777,-1.3333333333333712],"in_handle":[172.4444444444444,14.666666666666629],"out_handle":[153.77777777777777,-1.3333333333333712],"id":11671566486943985619},{"anchor":[183.11111111111103,-19.111111111111143],"in_handle":[169.77777777777766,-4.888888888888914],"out_handle":[183.11111111111103,-19.111111111111143],"id":3038942643602818950},{"anchor":[111.11111111111114,-60.888888888888914],"in_handle":[140.4444444444445,-3.1111111111111427],"out_handle":[111.11111111111114,-60.888888888888914],"id":13626227150877454323},{"anchor":[159.5555555555555,-75.55555555555557],"in_handle":[147.11111111111103,-47.111111111111114],"out_handle":[159.5555555555555,-75.55555555555557],"id":1119098226818031829},{"anchor":[123.99999999999994,-84.00000000000003],"in_handle":[130.66666666666657,-70.66666666666671],"out_handle":[123.99999999999994,-84.00000000000003],"id":10997408063317098666},{"anchor":[145.77777777777777,-97.7777777777778],"in_handle":[135.5555555555556,-85.33333333333334],"out_handle":[145.77777777777777,-97.7777777777778],"id":2339009349782210168},{"anchor":[90.66666666666664,-129.33333333333334],"in_handle":[103.99999999999994,-88.0],"out_handle":[90.66666666666664,-129.33333333333334],"id":9431773360702063651},{"anchor":[128.4444444444444,-142.22222222222223],"in_handle":[118.66666666666656,-118.66666666666666],"out_handle":[128.4444444444444,-142.22222222222223],"id":1989378433791183960},{"anchor":[76.0,-202.22222222222223],"in_handle":[89.33333333333337,-132.0],"out_handle":[76.0,-202.22222222222223],"id":16944713475106576916},{"anchor":[71.11111111111109,-201.7777777777778],"in_handle":[71.11111111111109,-201.7777777777778],"out_handle":[71.11111111111109,-201.7777777777778],"id":4948582696306250614},{"anchor":[19.555555555555543,-139.55555555555557],"in_handle":[57.77777777777777,-134.66666666666669],"out_handle":[19.555555555555543,-139.55555555555557],"id":3181603876853011370},{"anchor":[57.77777777777777,-129.33333333333334],"in_handle":[22.22222222222223,-118.22222222222224],"out_handle":[57.77777777777777,-129.33333333333334],"id":15558279495719164749},{"anchor":[0.8888888888888573,-94.66666666666669],"in_handle":[48.4444444444444,-89.7777777777778],"out_handle":[0.8888888888888573,-94.66666666666669],"id":15075453980114134632},{"anchor":[22.66666666666663,-81.33333333333337],"in_handle":[11.555555555555486,-83.11111111111117],"out_handle":[22.66666666666663,-81.33333333333337],"id":5981516608977551106},{"anchor":[-11.111111111111144,-72.00000000000003],"in_handle":[15.111111111111086,-69.33333333333337],"out_handle":[-11.111111111111144,-72.00000000000003],"id":3581874514940550643},{"anchor":[39.111111111111086,-58.22222222222226],"in_handle":[-0.4444444444444571,-45.77777777777783],"out_handle":[39.111111111111086,-58.22222222222226],"id":8795573422394602362},{"anchor":[-32.888888888888914,-14.666666666666686],"in_handle":[21.777777777777715,-8.0],"out_handle":[-32.888888888888914,-14.666666666666686],"id":7814715082243043442},{"anchor":[-9.333333333333371,0.0],"in_handle":[-27.111111111111143,-3.555555555555543],"out_handle":[-9.333333333333371,0.0],"id":5320482707593397404},{"anchor":[-53.33333333333337,13.777777777777771],"in_handle":[-18.222222222222285,15.111111111111144],"out_handle":[-53.33333333333337,13.777777777777771],"id":17942055575296353161},{"anchor":[9.333333333333314,38.66666666666663],"in_handle":[-32.888888888888914,43.99999999999994],"out_handle":[9.333333333333314,38.66666666666663],"id":10624636379930636709},{"anchor":[-78.22222222222223,75.55555555555554],"in_handle":[-23.111111111111143,85.33333333333331],"out_handle":[-78.22222222222223,75.55555555555554],"id":289637618588071417},{"anchor":[-5.333333333333371,99.11111111111109],"in_handle":[-69.77777777777783,102.22222222222224],"out_handle":[-5.333333333333371,99.11111111111109],"id":5831793300190377775}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[10349149476918669943,4172777474455727102,17203642997636495534]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,64]},"skip_deduplication":false,"world_state_hash":0},"15440793559080567610":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"F32":300.0},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2,2,2,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Lengths of Segments of Subpaths","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::LengthsOfSegmentsOfSubpaths"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Identity","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"bool","size":1,"align":1}}},{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SamplePoints<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,21]},"skip_deduplication":false,"world_state_hash":0},"1930981090497863830":{"alias":"","name":"Copy to Points","inputs":[{"Node":{"node_id":15440793559080567610,"output_index":0,"lambda":false}},{"Node":{"node_id":12437938754636694806,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::CopyToPoints<_, _>"}},"metadata":{"position":[-18,21]},"skip_deduplication":false,"world_state_hash":0},"15965443467772892221":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[0.5,0.0],"in_handle":[0.07318650757716938,0.0],"out_handle":[0.9268134924228306,0.0],"id":17411061371239299476},{"anchor":[1.2735144382576031,0.5],"in_handle":[1.2735144382576031,0.22410761111049293],"out_handle":[1.2735144382576031,0.7758923888895062],"id":9374539045640554469},{"anchor":[0.4999999999999982,1.8181818181818177],"in_handle":[0.7603282378542868,1.8181818181818177],"out_handle":[0.2396717621457114,1.8181818181818177],"id":11738099752431315156},{"anchor":[-0.27351443825760313,0.5],"in_handle":[-0.27351443825760313,0.7758923888895062],"out_handle":[-0.27351443825760313,0.22410761111049293],"id":6048384928646807781}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[17411061371239299476,9374539045640554469,11738099752431315156,6048384928646807781]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,34]},"skip_deduplication":false,"world_state_hash":0},"8051777741243064505":{"alias":"","name":"Copy to Points","inputs":[{"Node":{"node_id":234528620577149363,"output_index":0,"lambda":false}},{"Node":{"node_id":9387390658960403506,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::CopyToPoints<_, _>"}},"metadata":{"position":[-18,41]},"skip_deduplication":false,"world_state_hash":0},"4331062027851128032":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":17502675364388740750,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":1.0,"green":0.0,"blue":1.0,"alpha":0.5}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.3769992978075994,0.1888891278143931]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.9861902161192166,0.9200728483862376]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[801.0625,0.0,0.0,820.5871973335948,698.0,185.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.1764706,"green":0.25882354,"blue":0.32156864,"alpha":1.0}],[1.0,{"red":0.16577148,"green":0.37890625,"blue":0.36788198,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,64]},"skip_deduplication":false,"world_state_hash":0},"16765094648901305481":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":2550166071846864271,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,37]},"skip_deduplication":false,"world_state_hash":0},"1609067788579460449":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[1.0,0.5],"in_handle":[1.0,0.5],"out_handle":[1.0,0.5],"id":8537272728237707047},{"anchor":[0.7022542485937369,0.6469463130731183],"in_handle":[0.7022542485937369,0.6469463130731183],"out_handle":[0.7022542485937369,0.6469463130731183],"id":1482685404339037426},{"anchor":[0.6545084971874737,0.9755282581475768],"in_handle":[0.6545084971874737,0.9755282581475768],"out_handle":[0.6545084971874737,0.9755282581475768],"id":1620480325020014679},{"anchor":[0.42274575140626314,0.7377641290737884],"in_handle":[0.42274575140626314,0.7377641290737884],"out_handle":[0.42274575140626314,0.7377641290737884],"id":10542706155850513475},{"anchor":[0.09549150281252632,0.7938926261462367],"in_handle":[0.09549150281252632,0.7938926261462367],"out_handle":[0.09549150281252632,0.7938926261462367],"id":12109594763546059584},{"anchor":[0.25,0.5],"in_handle":[0.25,0.5],"out_handle":[0.25,0.5],"id":9696915690640326047},{"anchor":[0.09549150281252627,0.2061073738537635],"in_handle":[0.09549150281252627,0.2061073738537635],"out_handle":[0.09549150281252627,0.2061073738537635],"id":18010527605457217766},{"anchor":[0.42274575140626314,0.2622358709262116],"in_handle":[0.42274575140626314,0.2622358709262116],"out_handle":[0.42274575140626314,0.2622358709262116],"id":14000031798497855347},{"anchor":[0.6545084971874736,0.02447174185242318],"in_handle":[0.6545084971874736,0.02447174185242318],"out_handle":[0.6545084971874736,0.02447174185242318],"id":14944354297122388339},{"anchor":[0.7022542485937369,0.35305368692688166],"in_handle":[0.7022542485937369,0.35305368692688166],"out_handle":[0.7022542485937369,0.35305368692688166],"id":17647837293038806680}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,58]},"skip_deduplication":false,"world_state_hash":0},"183562335973647865":{"alias":"Tree","name":"Layer","inputs":[{"Node":{"node_id":17242155852300745672,"output_index":0,"lambda":false}},{"Node":{"node_id":4248875763694880456,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,64]},"skip_deduplication":false,"world_state_hash":0},"6315702878481394765":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":5510431876617456229,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.16470589,"green":0.54901963,"blue":0.8862745,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.49551110871305326,0.3428571428571424]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5034913598898481,0.9947089947089944]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[250.61867799546343,0.0,0.0,472.4999999999999,809.8156610022683,336.0000000000002]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.7104492,"green":0.83106995,"blue":0.9375,"alpha":1.0}],[1.0,{"red":0.16470589,"green":0.54901963,"blue":0.8862745,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,44]},"skip_deduplication":false,"world_state_hash":0},"3353108093362009815":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":897792489865808013,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,-33.59999999999991]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,50]},"skip_deduplication":false,"world_state_hash":0},"4248875763694880456":{"alias":"Tree Stump","name":"Layer","inputs":[{"Node":{"node_id":15888652418311111787,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,67]},"skip_deduplication":false,"world_state_hash":0},"2908374490615384647":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":1279981353152889207,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Solid"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.078431375,"green":0.14901961,"blue":0.20784314,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,67]},"skip_deduplication":false,"world_state_hash":0},"17502675364388740750":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":665049002420596388,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[426.3181429104577,532.1958874904515]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.9777789484064812,1.9777789484064812]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,64]},"skip_deduplication":false,"world_state_hash":0},"6966673029998204780":{"alias":"Star","name":"Layer","inputs":[{"Node":{"node_id":1488551925732670841,"output_index":0,"lambda":false}},{"Node":{"node_id":4534782777857480744,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,58]},"skip_deduplication":false,"world_state_hash":0},"10118219203151732555":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[0.20700027123845288,8.881784197001252e-16],"in_handle":[0.20700027123845288,8.881784197001252e-16],"out_handle":[0.20700027123845288,8.881784197001252e-16],"id":1958734888954831773},{"anchor":[0.7929997287615471,8.881784197001252e-16],"in_handle":[0.7929997287615471,8.881784197001252e-16],"out_handle":[0.7929997287615471,8.881784197001252e-16],"id":17346008195852984323},{"anchor":[0.95924568875469,1.0],"in_handle":[0.95924568875469,1.0],"out_handle":[0.95924568875469,1.0],"id":10466619161429748464},{"anchor":[0.04075431124530837,1.0],"in_handle":[0.04075431124530837,1.0],"out_handle":[0.04075431124530837,1.0],"id":3124419244608947754}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,37]},"skip_deduplication":false,"world_state_hash":0},"12158802623647295905":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":1930981090497863830,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,21]},"skip_deduplication":false,"world_state_hash":0},"11411423299989984040":{"alias":"Wire (Drag Points w/ Path Tool)","name":"Layer","inputs":[{"Node":{"node_id":3353108093362009815,"output_index":0,"lambda":false}},{"Node":{"node_id":6966673029998204780,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,50]},"skip_deduplication":false,"world_state_hash":0},"16739612568321095920":{"alias":"Green Lights","name":"Layer","inputs":[{"Node":{"node_id":12158802623647295905,"output_index":0,"lambda":false}},{"Node":{"node_id":3471929742275053204,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,21]},"skip_deduplication":false,"world_state_hash":0},"14972365039974884537":{"alias":"","name":"Artboard","inputs":[{"Node":{"node_id":16739612568321095920,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"IVec2":[0,0]},"exposed":false}},{"Value":{"tagged_value":{"IVec2":[1000,1000]},"exposed":false}},{"Value":{"tagged_value":{"Color":{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"Bool":false},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructArtboardNode<_, _, _, _, _>"}},"metadata":{"position":[0,19]},"skip_deduplication":false,"world_state_hash":0},"11407829185230551056":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":15889416971203221938,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,61]},"skip_deduplication":false,"world_state_hash":0},"9651014509053896646":{"alias":"","name":"Copy to Points","inputs":[{"Node":{"node_id":2800556534906834421,"output_index":0,"lambda":false}},{"Node":{"node_id":13371003476981866369,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::CopyToPoints<_, _>"}},"metadata":{"position":[-18,31]},"skip_deduplication":false,"world_state_hash":0},"897792489865808013":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.18629456,"green":0.18054199,"blue":0.2265625,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"F32":8.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Round"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-41,50]},"skip_deduplication":false,"world_state_hash":0},"2550166071846864271":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":11777015489064910999,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Solid"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.33553314,"green":0.33325195,"blue":0.40625,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,37]},"skip_deduplication":false,"world_state_hash":0},"2800556534906834421":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"F32":300.0},"exposed":false}},{"Value":{"tagged_value":{"F32":100.0},"exposed":false}},{"Value":{"tagged_value":{"F32":200.0},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2,2,2,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Lengths of Segments of Subpaths","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::LengthsOfSegmentsOfSubpaths"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Identity","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"bool","size":1,"align":1}}},{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SamplePoints<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,31]},"skip_deduplication":false,"world_state_hash":0},"4279275325545336233":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":9651014509053896646,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,31]},"skip_deduplication":false,"world_state_hash":0},"14944076597430308366":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":2550166071846864271,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,27]},"skip_deduplication":false,"world_state_hash":0},"1488551925732670841":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":3773322085315920844,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,58]},"skip_deduplication":false,"world_state_hash":0},"17242155852300745672":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":4331062027851128032,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,64]},"skip_deduplication":false,"world_state_hash":0},"3958246774416220131":{"alias":"","name":"Splines from Points","inputs":[{"Node":{"node_id":655907162126315358,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SplinesFromPointsNode"}},"metadata":{"position":[-49,35]},"skip_deduplication":false,"world_state_hash":0},"11777015489064910999":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":10118219203151732555,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[674.6856534818176,354.3276280850499]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[19.575820179122296,20.31995768166037]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-35,37]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[5580793685805333643],"previous_outputs":[{"node_id":0,"node_output_index":0}]},"name":"procedural-string-lights.graphite","commit_hash":"3a455c0f5b2316efdf84dbcbec59c49ec5bb039d","navigation":{"pan":[-499.7378727406811,-500.14712183888855],"tilt":0.0,"zoom":1.0},"document_mode":"DesignMode","view_mode":"Normal","overlays_visible":true,"rulers_visible":true,"collapsed":[]} \ No newline at end of file +{"network":{"inputs":[],"outputs":[{"node_id":0,"node_output_index":0}],"nodes":{"5510431876617456229":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":15965443467772892221,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[674.6856534818174,385.8339304530032]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[17.212796374136204,27.61219418351016]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-35,34]},"skip_deduplication":false,"world_state_hash":0},"12437938754636694806":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":11130614062948033510,"output_index":0,"lambda":false}},{"Node":{"node_id":14944076597430308366,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,24]},"skip_deduplication":false,"world_state_hash":0},"12790418218700356896":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":14972365039974884537,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[6,19]},"skip_deduplication":false,"world_state_hash":0},"1005063698578632777":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":1609067788579460449,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[503.7983132089414,104.19466945763644]},"exposed":false}},{"Value":{"tagged_value":{"F32":-0.2920178},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[110.93416360223218,110.93416360223218]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[-1.6012712240474374e-16,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,58]},"skip_deduplication":false,"world_state_hash":0},"3471929742275053204":{"alias":"Red Lights","name":"Layer","inputs":[{"Node":{"node_id":4279275325545336233,"output_index":0,"lambda":false}},{"Node":{"node_id":9015611177809361449,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,31]},"skip_deduplication":false,"world_state_hash":0},"13371003476981866369":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":13449829627181121540,"output_index":0,"lambda":false}},{"Node":{"node_id":16765094648901305481,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,34]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Output","inputs":[{"Node":{"node_id":12790418218700356896,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"graphene_core::application_io::EditorApi","size":184,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[3,0],"outputs":[{"node_id":3,"node_output_index":0}],"nodes":{"3":{"alias":"","name":"RenderNode","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Generic":"T"}]}},{"Node":{"node_id":2,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_std::wasm_application_io::RenderNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"EditorApi","inputs":[{"Network":{"Concrete":{"name":"graphene_core::application_io::EditorApi","size":184,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Cache","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"()","size":0,"align":1}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MemoNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Create Canvas","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_std::wasm_application_io::CreateSurfaceNode"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[10,17]},"skip_deduplication":false,"world_state_hash":0},"1279981353152889207":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":17339085479159577045,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[427.7531240523346,720.8882779290919]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.9777789484064812,1.9777789484064812]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,67]},"skip_deduplication":false,"world_state_hash":0},"9015611177809361449":{"alias":"Blue Lights","name":"Layer","inputs":[{"Node":{"node_id":15465970086096837636,"output_index":0,"lambda":false}},{"Node":{"node_id":11411423299989984040,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,41]},"skip_deduplication":false,"world_state_hash":0},"11130614062948033510":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":5510431876617456229,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.16470589,"green":0.8862745,"blue":0.4117647,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.4915209831246563,0.3534391534391531]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.49551110871305326,0.9968253968253964]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[250.61867799546343,0.0,0.0,472.4999999999999,809.8156610022683,336.0000000000002]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.96484375,"green":1.0,"blue":0.9765626,"alpha":1.0}],[1.0,{"red":0.16470589,"green":0.8862745,"blue":0.4117647,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,24]},"skip_deduplication":false,"world_state_hash":0},"15889416971203221938":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":7331575674671647159,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Solid"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.89411765,"green":0.654902,"blue":0.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,61]},"skip_deduplication":false,"world_state_hash":0},"15465970086096837636":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":8051777741243064505,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,41]},"skip_deduplication":false,"world_state_hash":0},"4534782777857480744":{"alias":"Star Base","name":"Layer","inputs":[{"Node":{"node_id":11407829185230551056,"output_index":0,"lambda":false}},{"Node":{"node_id":183562335973647865,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,61]},"skip_deduplication":false,"world_state_hash":0},"655907162126315358":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[408.0,304.5999999999999],"in_handle":[408.0,304.5999999999999],"out_handle":[408.0,304.5999999999999],"id":14295162783162130675},{"anchor":[579.0,325.5999999999999],"in_handle":[579.0,325.5999999999999],"out_handle":[579.0,325.5999999999999],"id":17235651057772975540},{"anchor":[365.0,406.5999999999999],"in_handle":[365.0,406.5999999999999],"out_handle":[365.0,406.5999999999999],"id":11062587772478366343},{"anchor":[476.0,480.5999999999999],"in_handle":[476.0,480.5999999999999],"out_handle":[476.0,480.5999999999999],"id":13905111493229779048},{"anchor":[631.0,472.5999999999999],"in_handle":[631.0,472.5999999999999],"out_handle":[631.0,472.5999999999999],"id":9818595361384120279},{"anchor":[487.0,567.5999999999999],"in_handle":[487.0,567.5999999999999],"out_handle":[487.0,567.5999999999999],"id":6558449122167484557},{"anchor":[296.0,645.5999999999999],"in_handle":[296.0,645.5999999999999],"out_handle":[296.0,645.5999999999999],"id":984077780188855675},{"anchor":[467.0,705.5999999999999],"in_handle":[467.0,705.5999999999999],"out_handle":[467.0,705.5999999999999],"id":13728222455061026873},{"anchor":[742.0,737.5999999999999],"in_handle":[742.0,737.5999999999999],"out_handle":[742.0,737.5999999999999],"id":13024161242572909643},{"anchor":[447.0,858.5999999999999],"in_handle":[447.0,858.5999999999999],"out_handle":[447.0,858.5999999999999],"id":6910875099560930892},{"anchor":[178.0,858.5999999999999],"in_handle":[178.0,858.5999999999999],"out_handle":[178.0,858.5999999999999],"id":11714154815938872622}],"closed":false}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-55,35]},"skip_deduplication":false,"world_state_hash":0},"17339085479159577045":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[88.4444444444444,151.55555555555554],"in_handle":[88.4444444444444,151.55555555555554],"out_handle":[88.4444444444444,151.55555555555554],"id":14237658187363817063},{"anchor":[122.22222222222224,196.4444444444444],"in_handle":[90.22222222222224,192.88888888888889],"out_handle":[122.22222222222224,196.4444444444444],"id":10365474939830860786},{"anchor":[123.55555555555554,199.1111111111111],"in_handle":[123.55555555555554,199.1111111111111],"out_handle":[123.55555555555554,199.1111111111111],"id":4109246593800547547},{"anchor":[28.296296296296305,198.96296296296293],"in_handle":[28.296296296296305,198.96296296296293],"out_handle":[28.296296296296305,198.96296296296293],"id":3671345045987042142},{"anchor":[28.296296296296305,197.33333333333331],"in_handle":[28.296296296296305,197.33333333333331],"out_handle":[28.296296296296305,197.33333333333331],"id":8747947169598042921},{"anchor":[58.962962962962976,152.74074074074073],"in_handle":[64.88888888888889,198.5185185185186],"out_handle":[58.962962962962976,152.74074074074073],"id":10921007090975078447},{"anchor":[66.51851851851853,147.1111111111111],"in_handle":[66.51851851851853,147.1111111111111],"out_handle":[66.51851851851853,147.1111111111111],"id":10733773696880555352}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,67]},"skip_deduplication":false,"world_state_hash":0},"9387390658960403506":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":6315702878481394765,"output_index":0,"lambda":false}},{"Node":{"node_id":15087384306214154157,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,44]},"skip_deduplication":false,"world_state_hash":0},"7331575674671647159":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":8309013977031955578,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[499.32116017766305,139.74585648084397]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[25.393705016577044,25.003032631706716]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,61]},"skip_deduplication":false,"world_state_hash":0},"15888652418311111787":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":2908374490615384647,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,67]},"skip_deduplication":false,"world_state_hash":0},"15087384306214154157":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":2550166071846864271,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,47]},"skip_deduplication":false,"world_state_hash":0},"3773322085315920844":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":1005063698578632777,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":1.0,"green":0.0,"blue":1.0,"alpha":0.5}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.4703098217208352,0.4995258072961386]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.9924395932459462,0.5005395053456176]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[237.34320332463173,-71.34811668265112,75.01984946235177,249.55741247555255,995.9008094918244,343.8953521035491]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":1.0,"green":0.94460994,"blue":0.79296875,"alpha":1.0}],[1.0,{"red":0.89411765,"green":0.654902,"blue":0.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,58]},"skip_deduplication":false,"world_state_hash":0},"13449829627181121540":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":5510431876617456229,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.8862745,"green":0.16470589,"blue":0.16470589,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.4915209831246563,0.36613756613756576]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.49551110871305326,0.9947089947089944]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[250.61867799546343,0.0,0.0,472.4999999999999,809.8156610022683,336.0000000000002]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.91796875,"green":0.68489075,"blue":0.68489075,"alpha":1.0}],[1.0,{"red":0.8862745,"green":0.16470589,"blue":0.16470589,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,34]},"skip_deduplication":false,"world_state_hash":0},"8309013977031955578":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[0.0,0.0],"in_handle":[0.0,0.0],"out_handle":[0.0,0.0],"id":1980203248399978225},{"anchor":[1.0,0.0],"in_handle":[1.0,0.0],"out_handle":[1.0,0.0],"id":6040860589979410580},{"anchor":[1.0,1.0],"in_handle":[1.0,1.0],"out_handle":[1.0,1.0],"id":5680533130302393152},{"anchor":[0.0,1.0],"in_handle":[0.0,1.0],"out_handle":[0.0,1.0],"id":13547805951820623445}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,61]},"skip_deduplication":false,"world_state_hash":0},"234528620577149363":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"F32":300.0},"exposed":false}},{"Value":{"tagged_value":{"F32":200.0},"exposed":false}},{"Value":{"tagged_value":{"F32":100.0},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2,2,2,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Lengths of Segments of Subpaths","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::LengthsOfSegmentsOfSubpaths"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Identity","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"bool","size":1,"align":1}}},{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SamplePoints<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,41]},"skip_deduplication":false,"world_state_hash":0},"665049002420596388":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[-103.55555555555554,126.66666666666664],"in_handle":[-37.77777777777774,139.1111111111111],"out_handle":[-103.55555555555554,126.66666666666664],"id":5137685238696701713},{"anchor":[-67.55555555555554,158.22222222222217],"in_handle":[-98.22222222222216,155.5555555555555],"out_handle":[-36.888888888888914,160.88888888888886],"id":10349149476918669943},{"anchor":[45.77777777777777,145.33333333333331],"in_handle":[-5.333333333333314,160.0],"out_handle":[45.77777777777777,145.33333333333331],"id":15777752779448746803},{"anchor":[18.66666666666663,157.77777777777771],"in_handle":[27.11111111111103,156.8888888888888],"out_handle":[18.66666666666663,157.77777777777771],"id":14911865974833225081},{"anchor":[66.22222222222223,152.88888888888886],"in_handle":[34.666666666666686,171.1111111111111],"out_handle":[66.22222222222223,152.88888888888886],"id":14106726470037375831},{"anchor":[123.99999999999994,148.4444444444444],"in_handle":[110.66666666666656,162.66666666666663],"out_handle":[123.99999999999994,148.4444444444444],"id":11411841702773087978},{"anchor":[229.33333333333331,152.4444444444444],"in_handle":[204.4444444444444,167.5555555555555],"out_handle":[254.22222222222223,137.33333333333331],"id":4172777474455727102},{"anchor":[252.4444444444444,124.88888888888886],"in_handle":[252.4444444444444,124.88888888888886],"out_handle":[252.4444444444444,124.88888888888886],"id":2435884934966232555},{"anchor":[156.4444444444444,97.33333333333331],"in_handle":[192.4444444444444,139.1111111111111],"out_handle":[156.4444444444444,97.33333333333331],"id":47350109196208952},{"anchor":[216.4444444444444,72.4444444444444],"in_handle":[182.22222222222211,108.4444444444444],"out_handle":[216.4444444444444,72.4444444444444],"id":12672076925927621110},{"anchor":[136.88888888888886,39.111111111111086],"in_handle":[163.5555555555555,83.55555555555554],"out_handle":[136.88888888888886,39.111111111111086],"id":3095729341078152823},{"anchor":[199.5555555555555,10.666666666666629],"in_handle":[175.5555555555555,42.22222222222217],"out_handle":[199.5555555555555,10.666666666666629],"id":11967596188552629860},{"anchor":[153.77777777777777,-1.3333333333333712],"in_handle":[172.4444444444444,14.666666666666629],"out_handle":[153.77777777777777,-1.3333333333333712],"id":11671566486943985619},{"anchor":[183.11111111111103,-19.111111111111143],"in_handle":[169.77777777777766,-4.888888888888914],"out_handle":[183.11111111111103,-19.111111111111143],"id":3038942643602818950},{"anchor":[111.11111111111114,-60.888888888888914],"in_handle":[140.4444444444445,-3.1111111111111427],"out_handle":[111.11111111111114,-60.888888888888914],"id":13626227150877454323},{"anchor":[159.5555555555555,-75.55555555555557],"in_handle":[147.11111111111103,-47.111111111111114],"out_handle":[159.5555555555555,-75.55555555555557],"id":1119098226818031829},{"anchor":[123.99999999999994,-84.00000000000003],"in_handle":[130.66666666666657,-70.66666666666671],"out_handle":[123.99999999999994,-84.00000000000003],"id":10997408063317098666},{"anchor":[145.77777777777777,-97.7777777777778],"in_handle":[135.5555555555556,-85.33333333333334],"out_handle":[145.77777777777777,-97.7777777777778],"id":2339009349782210168},{"anchor":[90.66666666666664,-129.33333333333334],"in_handle":[103.99999999999994,-88.0],"out_handle":[90.66666666666664,-129.33333333333334],"id":9431773360702063651},{"anchor":[128.4444444444444,-142.22222222222223],"in_handle":[118.66666666666656,-118.66666666666666],"out_handle":[128.4444444444444,-142.22222222222223],"id":1989378433791183960},{"anchor":[76.0,-202.22222222222223],"in_handle":[89.33333333333337,-132.0],"out_handle":[76.0,-202.22222222222223],"id":16944713475106576916},{"anchor":[71.11111111111109,-201.7777777777778],"in_handle":[71.11111111111109,-201.7777777777778],"out_handle":[71.11111111111109,-201.7777777777778],"id":4948582696306250614},{"anchor":[19.555555555555543,-139.55555555555557],"in_handle":[57.77777777777777,-134.66666666666669],"out_handle":[19.555555555555543,-139.55555555555557],"id":3181603876853011370},{"anchor":[57.77777777777777,-129.33333333333334],"in_handle":[22.22222222222223,-118.22222222222224],"out_handle":[57.77777777777777,-129.33333333333334],"id":15558279495719164749},{"anchor":[0.8888888888888573,-94.66666666666669],"in_handle":[48.4444444444444,-89.7777777777778],"out_handle":[0.8888888888888573,-94.66666666666669],"id":15075453980114134632},{"anchor":[22.66666666666663,-81.33333333333337],"in_handle":[11.555555555555486,-83.11111111111117],"out_handle":[22.66666666666663,-81.33333333333337],"id":5981516608977551106},{"anchor":[-11.111111111111144,-72.00000000000003],"in_handle":[15.111111111111086,-69.33333333333337],"out_handle":[-11.111111111111144,-72.00000000000003],"id":3581874514940550643},{"anchor":[39.111111111111086,-58.22222222222226],"in_handle":[-0.4444444444444571,-45.77777777777783],"out_handle":[39.111111111111086,-58.22222222222226],"id":8795573422394602362},{"anchor":[-32.888888888888914,-14.666666666666686],"in_handle":[21.777777777777715,-8.0],"out_handle":[-32.888888888888914,-14.666666666666686],"id":7814715082243043442},{"anchor":[-9.333333333333371,0.0],"in_handle":[-27.111111111111143,-3.555555555555543],"out_handle":[-9.333333333333371,0.0],"id":5320482707593397404},{"anchor":[-53.33333333333337,13.777777777777771],"in_handle":[-18.222222222222285,15.111111111111144],"out_handle":[-53.33333333333337,13.777777777777771],"id":17942055575296353161},{"anchor":[9.333333333333314,38.66666666666663],"in_handle":[-32.888888888888914,43.99999999999994],"out_handle":[9.333333333333314,38.66666666666663],"id":10624636379930636709},{"anchor":[-78.22222222222223,75.55555555555554],"in_handle":[-23.111111111111143,85.33333333333331],"out_handle":[-78.22222222222223,75.55555555555554],"id":289637618588071417},{"anchor":[-5.333333333333371,99.11111111111109],"in_handle":[-69.77777777777783,102.22222222222224],"out_handle":[-5.333333333333371,99.11111111111109],"id":5831793300190377775}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[10349149476918669943,4172777474455727102,17203642997636495534]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,64]},"skip_deduplication":false,"world_state_hash":0},"15440793559080567610":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"F32":300.0},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2,2,2,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Lengths of Segments of Subpaths","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::LengthsOfSegmentsOfSubpaths"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Identity","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"bool","size":1,"align":1}}},{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SamplePoints<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,21]},"skip_deduplication":false,"world_state_hash":0},"1930981090497863830":{"alias":"","name":"Copy to Points","inputs":[{"Node":{"node_id":15440793559080567610,"output_index":0,"lambda":false}},{"Node":{"node_id":12437938754636694806,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::CopyToPoints<_, _>"}},"metadata":{"position":[-18,21]},"skip_deduplication":false,"world_state_hash":0},"15965443467772892221":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[0.5,0.0],"in_handle":[0.07318650757716938,0.0],"out_handle":[0.9268134924228306,0.0],"id":17411061371239299476},{"anchor":[1.2735144382576031,0.5],"in_handle":[1.2735144382576031,0.22410761111049293],"out_handle":[1.2735144382576031,0.7758923888895062],"id":9374539045640554469},{"anchor":[0.4999999999999982,1.8181818181818177],"in_handle":[0.7603282378542868,1.8181818181818177],"out_handle":[0.2396717621457114,1.8181818181818177],"id":11738099752431315156},{"anchor":[-0.27351443825760313,0.5],"in_handle":[-0.27351443825760313,0.7758923888895062],"out_handle":[-0.27351443825760313,0.22410761111049293],"id":6048384928646807781}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[17411061371239299476,9374539045640554469,11738099752431315156,6048384928646807781]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,34]},"skip_deduplication":false,"world_state_hash":0},"8051777741243064505":{"alias":"","name":"Copy to Points","inputs":[{"Node":{"node_id":234528620577149363,"output_index":0,"lambda":false}},{"Node":{"node_id":9387390658960403506,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::CopyToPoints<_, _>"}},"metadata":{"position":[-18,41]},"skip_deduplication":false,"world_state_hash":0},"4331062027851128032":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":17502675364388740750,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":1.0,"green":0.0,"blue":1.0,"alpha":0.5}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.3769992978075994,0.1888891278143931]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.9861902161192166,0.9200728483862376]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[801.0625,0.0,0.0,820.5871973335948,698.0,185.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.1764706,"green":0.25882354,"blue":0.32156864,"alpha":1.0}],[1.0,{"red":0.16577148,"green":0.37890625,"blue":0.36788198,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,64]},"skip_deduplication":false,"world_state_hash":0},"16765094648901305481":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":2550166071846864271,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,37]},"skip_deduplication":false,"world_state_hash":0},"1609067788579460449":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[1.0,0.5],"in_handle":[1.0,0.5],"out_handle":[1.0,0.5],"id":8537272728237707047},{"anchor":[0.7022542485937369,0.6469463130731183],"in_handle":[0.7022542485937369,0.6469463130731183],"out_handle":[0.7022542485937369,0.6469463130731183],"id":1482685404339037426},{"anchor":[0.6545084971874737,0.9755282581475768],"in_handle":[0.6545084971874737,0.9755282581475768],"out_handle":[0.6545084971874737,0.9755282581475768],"id":1620480325020014679},{"anchor":[0.42274575140626314,0.7377641290737884],"in_handle":[0.42274575140626314,0.7377641290737884],"out_handle":[0.42274575140626314,0.7377641290737884],"id":10542706155850513475},{"anchor":[0.09549150281252632,0.7938926261462367],"in_handle":[0.09549150281252632,0.7938926261462367],"out_handle":[0.09549150281252632,0.7938926261462367],"id":12109594763546059584},{"anchor":[0.25,0.5],"in_handle":[0.25,0.5],"out_handle":[0.25,0.5],"id":9696915690640326047},{"anchor":[0.09549150281252627,0.2061073738537635],"in_handle":[0.09549150281252627,0.2061073738537635],"out_handle":[0.09549150281252627,0.2061073738537635],"id":18010527605457217766},{"anchor":[0.42274575140626314,0.2622358709262116],"in_handle":[0.42274575140626314,0.2622358709262116],"out_handle":[0.42274575140626314,0.2622358709262116],"id":14000031798497855347},{"anchor":[0.6545084971874736,0.02447174185242318],"in_handle":[0.6545084971874736,0.02447174185242318],"out_handle":[0.6545084971874736,0.02447174185242318],"id":14944354297122388339},{"anchor":[0.7022542485937369,0.35305368692688166],"in_handle":[0.7022542485937369,0.35305368692688166],"out_handle":[0.7022542485937369,0.35305368692688166],"id":17647837293038806680}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-36,58]},"skip_deduplication":false,"world_state_hash":0},"183562335973647865":{"alias":"Tree","name":"Layer","inputs":[{"Node":{"node_id":17242155852300745672,"output_index":0,"lambda":false}},{"Node":{"node_id":4248875763694880456,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,64]},"skip_deduplication":false,"world_state_hash":0},"6315702878481394765":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":5510431876617456229,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Gradient"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.16470589,"green":0.54901963,"blue":0.8862745,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Radial"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.49551110871305326,0.3428571428571424]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5034913598898481,0.9947089947089944]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[250.61867799546343,0.0,0.0,472.4999999999999,809.8156610022683,336.0000000000002]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.7104492,"green":0.83106995,"blue":0.9375,"alpha":1.0}],[1.0,{"red":0.16470589,"green":0.54901963,"blue":0.8862745,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,44]},"skip_deduplication":false,"world_state_hash":0},"3353108093362009815":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":897792489865808013,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,-33.59999999999991]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,50]},"skip_deduplication":false,"world_state_hash":0},"4248875763694880456":{"alias":"Tree Stump","name":"Layer","inputs":[{"Node":{"node_id":15888652418311111787,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,67]},"skip_deduplication":false,"world_state_hash":0},"2908374490615384647":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":1279981353152889207,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Solid"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.078431375,"green":0.14901961,"blue":0.20784314,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-20,67]},"skip_deduplication":false,"world_state_hash":0},"17502675364388740750":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":665049002420596388,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[426.3181429104577,532.1958874904515]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.9777789484064812,1.9777789484064812]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-28,64]},"skip_deduplication":false,"world_state_hash":0},"6966673029998204780":{"alias":"Star","name":"Layer","inputs":[{"Node":{"node_id":1488551925732670841,"output_index":0,"lambda":false}},{"Node":{"node_id":4534782777857480744,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,58]},"skip_deduplication":false,"world_state_hash":0},"10118219203151732555":{"alias":"","name":"Shape","inputs":[{"Value":{"tagged_value":{"Subpaths":[{"manipulator_groups":[{"anchor":[0.20700027123845288,8.881784197001252e-16],"in_handle":[0.20700027123845288,8.881784197001252e-16],"out_handle":[0.20700027123845288,8.881784197001252e-16],"id":1958734888954831773},{"anchor":[0.7929997287615471,8.881784197001252e-16],"in_handle":[0.7929997287615471,8.881784197001252e-16],"out_handle":[0.7929997287615471,8.881784197001252e-16],"id":17346008195852984323},{"anchor":[0.95924568875469,1.0],"in_handle":[0.95924568875469,1.0],"out_handle":[0.95924568875469,1.0],"id":10466619161429748464},{"anchor":[0.04075431124530837,1.0],"in_handle":[0.04075431124530837,1.0],"out_handle":[0.04075431124530837,1.0],"id":3124419244608947754}],"closed":true}]},"exposed":false}},{"Value":{"tagged_value":{"ManipulatorGroupIds":[]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,0],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Cull","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::CullNode<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Path Generator","inputs":[{"Network":{"Concrete":{"name":"alloc::vec::Vec>","size":12,"align":4}}},{"Network":{"Concrete":{"name":"alloc::vec::Vec","size":12,"align":4}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::generator_nodes::PathGenerator<_>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,37]},"skip_deduplication":false,"world_state_hash":0},"12158802623647295905":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":1930981090497863830,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,21]},"skip_deduplication":false,"world_state_hash":0},"11411423299989984040":{"alias":"Wire (Drag Points w/ Path Tool)","name":"Layer","inputs":[{"Node":{"node_id":3353108093362009815,"output_index":0,"lambda":false}},{"Node":{"node_id":6966673029998204780,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,50]},"skip_deduplication":false,"world_state_hash":0},"16739612568321095920":{"alias":"Green Lights","name":"Layer","inputs":[{"Node":{"node_id":12158802623647295905,"output_index":0,"lambda":false}},{"Node":{"node_id":3471929742275053204,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-4,21]},"skip_deduplication":false,"world_state_hash":0},"14972365039974884537":{"alias":"","name":"Artboard","inputs":[{"Node":{"node_id":16739612568321095920,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"IVec2":[0,0]},"exposed":false}},{"Value":{"tagged_value":{"IVec2":[1000,1000]},"exposed":false}},{"Value":{"tagged_value":{"Color":{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"Bool":false},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructArtboardNode<_, _, _, _, _>"}},"metadata":{"position":[0,19]},"skip_deduplication":false,"world_state_hash":0},"11407829185230551056":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":15889416971203221938,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,61]},"skip_deduplication":false,"world_state_hash":0},"9651014509053896646":{"alias":"","name":"Copy to Points","inputs":[{"Node":{"node_id":2800556534906834421,"output_index":0,"lambda":false}},{"Node":{"node_id":13371003476981866369,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::CopyToPoints<_, _>"}},"metadata":{"position":[-18,31]},"skip_deduplication":false,"world_state_hash":0},"897792489865808013":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.18629456,"green":0.18054199,"blue":0.2265625,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"F32":8.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Round"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-41,50]},"skip_deduplication":false,"world_state_hash":0},"2550166071846864271":{"alias":"","name":"Fill","inputs":[{"Node":{"node_id":11777015489064910999,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"FillType":"Solid"},"exposed":false}},{"Value":{"tagged_value":{"OptionalColor":{"red":0.33553314,"green":0.33325195,"blue":0.40625,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,0.5]},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"GradientPositions":[[0.0,{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}],[1.0,{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]]},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-29,37]},"skip_deduplication":false,"world_state_hash":0},"2800556534906834421":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":3958246774416220131,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"F32":300.0},"exposed":false}},{"Value":{"tagged_value":{"F32":100.0},"exposed":false}},{"Value":{"tagged_value":{"F32":200.0},"exposed":false}},{"Value":{"tagged_value":{"Bool":true},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2,2,2,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Lengths of Segments of Subpaths","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::LengthsOfSegmentsOfSubpaths"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Identity","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ops::IdentityNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"2":{"alias":"","name":"Sample Points","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"bool","size":1,"align":1}}},{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SamplePoints<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-41,31]},"skip_deduplication":false,"world_state_hash":0},"4279275325545336233":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":9651014509053896646,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[8.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[1.0,1.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-11,31]},"skip_deduplication":false,"world_state_hash":0},"14944076597430308366":{"alias":"","name":"Layer","inputs":[{"Node":{"node_id":2550166071846864271,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"GraphicGroup":{"elements":[],"transform":[1.0,0.0,0.0,1.0,0.0,0.0],"alpha_blending":{"opacity":1.0,"blend_mode":"Normal"}}},"exposed":true}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Network":{"inputs":[0,2],"outputs":[{"node_id":2,"node_output_index":0}],"nodes":{"2":{"alias":"","name":"ConstructLayer","inputs":[{"Node":{"node_id":1,"output_index":0,"lambda":false}},{"Network":{"Fn":[{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},{"Concrete":{"name":"graphene_core::graphic_element::GraphicGroup","size":72,"align":8}}]}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ConstructLayerNode<_, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"0":{"alias":"","name":"To Graphic Element","inputs":[{"Network":{"Generic":"T"}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::ToGraphicElementNode"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0},"1":{"alias":"","name":"Monitor","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-22,27]},"skip_deduplication":false,"world_state_hash":0},"1488551925732670841":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":3773322085315920844,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,58]},"skip_deduplication":false,"world_state_hash":0},"17242155852300745672":{"alias":"","name":"Stroke","inputs":[{"Node":{"node_id":4331062027851128032,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"OptionalColor":null},"exposed":false}},{"Value":{"tagged_value":{"F32":5.0},"exposed":false}},{"Value":{"tagged_value":{"VecF32":[]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"LineCap":"Butt"},"exposed":false}},{"Value":{"tagged_value":{"LineJoin":"Miter"},"exposed":false}},{"Value":{"tagged_value":{"F32":4.0},"exposed":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>"}},"metadata":{"position":[-12,64]},"skip_deduplication":false,"world_state_hash":0},"3958246774416220131":{"alias":"","name":"Splines from Points","inputs":[{"Node":{"node_id":655907162126315358,"output_index":0,"lambda":false}}],"manual_composition":null,"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::vector::SplinesFromPointsNode"}},"metadata":{"position":[-49,35]},"skip_deduplication":false,"world_state_hash":0},"11777015489064910999":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":10118219203151732555,"output_index":0,"lambda":false}},{"Value":{"tagged_value":{"DVec2":[674.6856534818176,354.3276280850499]},"exposed":false}},{"Value":{"tagged_value":{"F32":0.0},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[19.575820179122296,20.31995768166037]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.0,0.0]},"exposed":false}},{"Value":{"tagged_value":{"DVec2":[0.5,0.5]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Network":{"inputs":[0,1,1,1,1,1],"outputs":[{"node_id":1,"node_output_index":0}],"nodes":{"0":{"alias":"","name":"Monitor","inputs":[{"Network":{"Concrete":{"name":"graphene_core::vector::vector_data::VectorData","size":256,"align":8}}}],"manual_composition":{"Generic":"T"},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::memo::MonitorNode<_, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":true,"world_state_hash":0},"1":{"alias":"","name":"Transform","inputs":[{"Node":{"node_id":0,"output_index":0,"lambda":false}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"f32","size":4,"align":4}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}},{"Network":{"Concrete":{"name":"glam::f64::dvec2::DVec2","size":16,"align":8}}}],"manual_composition":{"Concrete":{"name":"graphene_core::transform::Footprint","size":72,"align":8}},"has_primary_output":true,"implementation":{"Unresolved":{"name":"graphene_core::transform::TransformNode<_, _, _, _, _, _>"}},"metadata":{"position":[0,0]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":null}},"metadata":{"position":[-35,37]},"skip_deduplication":false,"world_state_hash":0}},"disabled":[],"previous_outputs":[{"node_id":0,"node_output_index":0}]},"name":"procedural-string-lights.graphite","commit_hash":"3a455c0f5b2316efdf84dbcbec59c49ec5bb039d","navigation":{"pan":[-499.7378727406811,-500.14712183888855],"tilt":0.0,"zoom":1.0},"document_mode":"DesignMode","view_mode":"Normal","overlays_visible":true,"rulers_visible":true,"collapsed":[]} \ No newline at end of file diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 9e132078..a3b4a713 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -32,16 +32,15 @@ pub struct DispatcherMessageHandlers { /// The last occurrence of the message in the message queue is sufficient to ensure correct behavior. /// In addition, these messages do not change any state in the backend (aside from caches). const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[ - MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel( PropertiesPanelMessageDiscriminant::Refresh, ))), MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::DocumentStructureChanged)), + MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Draw))), + MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::DocumentIsDirty)), MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure), MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad), - MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::DocumentIsDirty)), - MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Draw))), ]; impl Dispatcher { @@ -119,7 +118,7 @@ impl Dispatcher { } Frontend(message) => { // Handle these messages immediately by returning early - if let FrontendMessage::UpdateImageData { .. } | FrontendMessage::TriggerFontLoad { .. } | FrontendMessage::TriggerRefreshBoundsOfViewports = message { + if let FrontendMessage::TriggerFontLoad { .. } | FrontendMessage::TriggerRefreshBoundsOfViewports = message { self.responses.push(message); self.cleanup_queues(false); @@ -307,8 +306,8 @@ mod test { }); let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().clone(); - let layers_before_copy = document_before_copy.metadata.all_layers().collect::>(); - let layers_after_copy = document_after_copy.metadata.all_layers().collect::>(); + let layers_before_copy = document_before_copy.document_metadata.all_layers().collect::>(); + let layers_after_copy = document_after_copy.document_metadata.all_layers().collect::>(); assert_eq!(layers_before_copy.len(), 3); assert_eq!(layers_after_copy.len(), 4); @@ -330,7 +329,7 @@ mod test { let mut editor = create_editor_with_three_layers(); let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().clone(); - let shape_id = document_before_copy.metadata.all_layers().nth(1).unwrap(); + let shape_id = document_before_copy.document_metadata.all_layers().nth(1).unwrap(); editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![shape_id.to_node()] }); editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal }); @@ -342,8 +341,8 @@ mod test { let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().clone(); - let layers_before_copy = document_before_copy.metadata.all_layers().collect::>(); - let layers_after_copy = document_after_copy.metadata.all_layers().collect::>(); + let layers_before_copy = document_before_copy.document_metadata.all_layers().collect::>(); + let layers_after_copy = document_after_copy.document_metadata.all_layers().collect::>(); assert_eq!(layers_before_copy.len(), 3); assert_eq!(layers_after_copy.len(), 4); @@ -385,8 +384,8 @@ mod test { let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().clone(); - let layers_before_copy = document_before_copy.metadata.all_layers().collect::>(); - let layers_after_copy = document_after_copy.metadata.all_layers().collect::>(); + let layers_before_copy = document_before_copy.document_metadata.all_layers().collect::>(); + let layers_after_copy = document_after_copy.document_metadata.all_layers().collect::>(); let [original_folder, original_freehand, original_line, original_ellipse, original_polygon, original_rect] = layers_before_copy[..] else { panic!("Layers before incorrect"); }; @@ -414,7 +413,7 @@ mod test { let mut editor = create_editor_with_three_layers(); let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().clone(); - let mut layers = document_before_copy.metadata.all_layers(); + let mut layers = document_before_copy.document_metadata.all_layers(); let rect_id = layers.next().expect("rectangle"); let shape_id = layers.next().expect("shape"); let ellipse_id = layers.next().expect("ellipse"); @@ -438,8 +437,8 @@ mod test { let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().clone(); - let layers_before_copy = document_before_copy.metadata.all_layers().collect::>(); - let layers_after_copy = document_after_copy.metadata.all_layers().collect::>(); + let layers_before_copy = document_before_copy.document_metadata.all_layers().collect::>(); + let layers_after_copy = document_after_copy.document_metadata.all_layers().collect::>(); assert_eq!(layers_before_copy.len(), 3); assert_eq!(layers_after_copy.len(), 6); diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 70892bb1..1ea50858 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -75,7 +75,7 @@ impl MessageHandler> for DialogMessageHandler { if let Some(document) = portfolio.active_document() { let mut index = 0; let artboards = document - .metadata + .document_metadata .all_layers() .filter(|&layer| is_layer_fed_by_node_of_name(layer, &document.network, "Artboard")) .map(|layer| { diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 20a61a6c..8448e462 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -1,4 +1,4 @@ -use super::utility_types::{FrontendDocumentDetails, FrontendImageData, MouseCursorIcon}; +use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon}; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::node_graph::{FrontendNode, FrontendNodeLink, FrontendNodeType}; use crate::messages::portfolio::document::utility_types::layer_panel::{JsRawBuffer, LayerPanelEntry, RawBuffer}; @@ -172,12 +172,6 @@ pub enum FrontendMessage { #[serde(rename = "setColorChoice")] set_color_choice: Option, }, - UpdateImageData { - #[serde(rename = "documentId")] - document_id: DocumentId, - #[serde(rename = "imageData")] - image_data: Vec, - }, UpdateInputHints { #[serde(rename = "hintData")] hint_data: HintData, diff --git a/editor/src/messages/frontend/utility_types.rs b/editor/src/messages/frontend/utility_types.rs index c51b4959..5b7a8194 100644 --- a/editor/src/messages/frontend/utility_types.rs +++ b/editor/src/messages/frontend/utility_types.rs @@ -13,13 +13,6 @@ pub struct FrontendDocumentDetails { pub id: DocumentId, } -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)] -pub struct FrontendImageData { - pub mime: String, - #[serde(skip)] - pub image_data: std::sync::Arc>, -} - #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, specta::Type)] pub enum MouseCursorIcon { #[default] diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index 53c480e5..4afc3500 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -87,7 +87,6 @@ pub enum DocumentMessage { RenameDocument { new_name: String, }, - RenderDocument, RenderRulers, RenderScrollbars, SaveDocument, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index f0263f39..029262b4 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -85,7 +85,7 @@ pub struct DocumentMessageHandler { #[serde(skip)] layer_range_selection_reference: Option, #[serde(skip)] - pub metadata: DocumentMetadata, + pub document_metadata: DocumentMetadata, } impl Default for DocumentMessageHandler { @@ -121,7 +121,7 @@ impl Default for DocumentMessageHandler { graph_view_overlay_open: false, snapping_state: SnappingState::default(), layer_range_selection_reference: None, - metadata: Default::default(), + document_metadata: Default::default(), } } } @@ -245,7 +245,13 @@ impl MessageHandler> for DocumentMessageHand self.navigation_handler.process_message( message, responses, - (&self.metadata, document_bounds, ipp, self.selected_visible_layers_bounding_box_viewport(), &mut self.navigation), + ( + &self.document_metadata, + document_bounds, + ipp, + self.selected_visible_layers_bounding_box_viewport(), + &mut self.navigation, + ), ); } #[remain::unsorted] @@ -259,7 +265,7 @@ impl MessageHandler> for DocumentMessageHand executor, document_name: self.name.as_str(), document_network: &self.network, - document_metadata: &mut self.metadata, + document_metadata: &mut self.document_metadata, }; self.properties_panel_message_handler .process_message(message, responses, (persistent_data, properties_panel_message_handler_data)); @@ -271,7 +277,7 @@ impl MessageHandler> for DocumentMessageHand responses, NodeGraphHandlerData { document_network: &mut self.network, - document_metadata: &mut self.metadata, + document_metadata: &mut self.document_metadata, document_id, document_name: self.name.as_str(), collapsed: &mut self.collapsed, @@ -281,13 +287,13 @@ impl MessageHandler> for DocumentMessageHand ); } #[remain::unsorted] - GraphOperation(message) => GraphOperationMessageHandler.process_message(message, responses, (&mut self.network, &mut self.metadata, &mut self.collapsed, &mut self.node_graph_handler)), + GraphOperation(message) => GraphOperationMessageHandler.process_message(message, responses, (&mut self.network, &mut self.document_metadata, &mut self.collapsed, &mut self.node_graph_handler)), // Messages AbortTransaction => { if !self.undo_in_progress { self.undo(responses); - responses.extend([RenderDocument.into(), DocumentStructureChanged.into()]); + responses.add(OverlaysMessage::Draw); } } AlignSelectedLayers { axis, aggregate } => { @@ -375,12 +381,9 @@ impl MessageHandler> for DocumentMessageHand DocumentStructureChanged => { self.update_layers_panel_options_bar_widgets(responses); + self.document_metadata.load_structure(&self.network); let data_buffer: RawBuffer = self.serialize_root(); responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer }); - - if self.graph_view_overlay_open { - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); - } } DuplicateSelectedLayers => { // TODO: Reimplement selected layer duplication @@ -414,7 +417,7 @@ impl MessageHandler> for DocumentMessageHand self.graph_view_overlay_open = open; if open { - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); + responses.add(NodeGraphMessage::SendGraph); } responses.add(FrontendMessage::TriggerGraphViewOverlay { open }); } @@ -503,7 +506,7 @@ impl MessageHandler> for DocumentMessageHand }); } // Nudge resize - else if let Some([existing_top_left, existing_bottom_right]) = self.metadata.bounding_box_document(layer) { + else if let Some([existing_top_left, existing_bottom_right]) = self.document_metadata.bounding_box_document(layer) { let size = existing_bottom_right - existing_top_left; let new_size = size + if opposite_corner { -delta } else { delta }; let enlargement_factor = new_size / size; @@ -572,19 +575,15 @@ impl MessageHandler> for DocumentMessageHand } Redo => { responses.add(SelectToolMessage::Abort); - responses.add(DocumentHistoryForward); + responses.add(DocumentMessage::DocumentHistoryForward); responses.add(BroadcastEvent::DocumentIsDirty); - responses.add(RenderDocument); - responses.add(DocumentStructureChanged); + responses.add(OverlaysMessage::Draw); } RenameDocument { new_name } => { self.name = new_name; responses.add(PortfolioMessage::UpdateOpenDocumentsList); responses.add(NodeGraphMessage::UpdateNewNodeGraph); } - RenderDocument => { - responses.add(OverlaysMessage::Draw); - } RenderRulers => { let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom); @@ -753,11 +752,10 @@ impl MessageHandler> for DocumentMessageHand Undo => { self.undo_in_progress = true; responses.add(BroadcastEvent::ToolAbort); - responses.add(DocumentHistoryBackward); + responses.add(DocumentMessage::DocumentHistoryBackward); responses.add(BroadcastEvent::DocumentIsDirty); - responses.add(RenderDocument); - responses.add(DocumentStructureChanged); - responses.add(UndoFinished); + responses.add(OverlaysMessage::Draw); + responses.add(DocumentMessage::UndoFinished); } UndoFinished => self.undo_in_progress = false, UngroupSelectedLayers => { @@ -786,7 +784,7 @@ impl MessageHandler> for DocumentMessageHand responses.add(DocumentMessage::CommitTransaction); } UpdateDocumentTransform { transform } => { - self.metadata.document_to_viewport = transform; + self.document_metadata.document_to_viewport = transform; responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderScrollbars); responses.add(NodeGraphMessage::RunDocumentGraph); @@ -813,35 +811,43 @@ impl MessageHandler> for DocumentMessageHand impl DocumentMessageHandler { pub fn layer_visible(&self, layer: LayerNodeIdentifier) -> bool { - !layer.ancestors(&self.metadata).any(|layer| self.network.disabled.contains(&layer.to_node())) + !layer.ancestors(&self.document_metadata).any(|layer| self.network.disabled.contains(&layer.to_node())) } pub fn selected_visible_layers(&self) -> impl Iterator + '_ { - self.metadata.selected_layers().filter(|&layer| self.layer_visible(layer)) + self.document_metadata.selected_layers().filter(|&layer| self.layer_visible(layer)) } /// Runs an intersection test with all layers and a viewport space quad pub fn intersect_quad<'a>(&'a self, viewport_quad: graphene_core::renderer::Quad, network: &'a NodeNetwork) -> impl Iterator + 'a { - let document_quad = self.metadata.document_to_viewport.inverse() * viewport_quad; - self.metadata + let document_quad = self.document_metadata.document_to_viewport.inverse() * viewport_quad; + self.document_metadata .root() - .decendants(&self.metadata) + .decendants(&self.document_metadata) .filter(|&layer| self.layer_visible(layer)) .filter(|&layer| !is_artboard(layer, network)) - .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) - .filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.metadata.transform_to_document(*layer)))) + .filter_map(|layer| self.document_metadata.click_target(layer).map(|targets| (layer, targets))) + .filter(move |(layer, target)| { + target + .iter() + .any(move |target| target.intersect_rectangle(document_quad, self.document_metadata.transform_to_document(*layer))) + }) .map(|(layer, _)| layer) } /// Find all of the layers that were clicked on from a viewport space location pub fn click_xray(&self, viewport_location: DVec2) -> impl Iterator + '_ { - let point = self.metadata.document_to_viewport.inverse().transform_point2(viewport_location); - self.metadata + let point = self.document_metadata.document_to_viewport.inverse().transform_point2(viewport_location); + self.document_metadata .root() - .decendants(&self.metadata) + .decendants(&self.document_metadata) .filter(|&layer| self.layer_visible(layer)) - .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) - .filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.metadata.transform_to_document(*layer)))) + .filter_map(|layer| self.document_metadata.click_target(layer).map(|targets| (layer, targets))) + .filter(move |(layer, target)| { + target + .iter() + .any(|target: &ClickTarget| target.intersect_point(point, self.document_metadata.transform_to_document(*layer))) + }) .map(|(layer, _)| layer) } @@ -853,7 +859,7 @@ impl DocumentMessageHandler { /// Get the combined bounding box of the click targets of the selected visible layers in viewport space pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> { self.selected_visible_layers() - .filter_map(|layer| self.metadata.bounding_box_viewport(layer)) + .filter_map(|layer| self.document_metadata.bounding_box_viewport(layer)) .reduce(graphene_core::renderer::Quad::combine_bounds) } @@ -862,7 +868,7 @@ impl DocumentMessageHandler { } pub fn metadata(&self) -> &DocumentMetadata { - &self.metadata + &self.document_metadata } pub fn serialize_document(&self) -> String { @@ -878,7 +884,7 @@ impl DocumentMessageHandler { pub fn with_name(name: String, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque) -> Self { let mut document = Self { name, ..Self::default() }; let transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2., DVec2::ZERO, 0., 1.); - document.metadata.document_to_viewport = transform; + document.document_metadata.document_to_viewport = transform; responses.add(DocumentMessage::UpdateDocumentTransform { transform }); document @@ -897,23 +903,24 @@ impl DocumentMessageHandler { std::iter::empty() } - fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec, data: &mut Vec, path: &mut Vec) { + /// Called recursively by the entry function [`serialize_root`]. + fn serialize_structure(&self, folder: LayerNodeIdentifier, structure_section: &mut Vec, data_section: &mut Vec, path: &mut Vec) { let mut space = 0; for layer_node in folder.children(self.metadata()) { - data.push(layer_node); + data_section.push(layer_node.to_node().0); space += 1; if layer_node.has_children(self.metadata()) && !self.collapsed.contains(&layer_node) { path.push(layer_node); // TODO: Skip if folder is not expanded. - structure.push(LayerNodeIdentifier::new_unchecked(NodeId(space))); - self.serialize_structure(layer_node, structure, data, path); + structure_section.push(space); + self.serialize_structure(layer_node, structure_section, data_section, path); space = 0; path.pop(); } } - structure.push(LayerNodeIdentifier::new_unchecked(NodeId(space | 1 << 63))); + structure_section.push(space | 1 << 63); } /// Serializes the layer structure into a condensed 1D structure. @@ -921,17 +928,18 @@ impl DocumentMessageHandler { /// # Format /// It is a string of numbers broken into three sections: /// - /// | Data | Description | Length | - /// |--------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|------------------| - /// | `4,` `2, 1, -2, -0,` `16533113728871998040,3427872634365736244,18115028555707261608,15878401910454357952,449479075714955186` | Encoded example data | | - /// | `L` = `4` = `structure.len()` | `L`, the length of the **Structure** section | First value | - /// | **Structure** section = `2, 1, -2, -0` | The **Structure** section | Next `L` values | - /// | **Data** section = `16533113728871998040, 3427872634365736244, 18115028555707261608, 15878401910454357952, 449479075714955186` | The **Data** section (layer IDs) | Remaining values | + /// | Data | Description | Length | + /// |------------------------------------------------------------------------------------------------------------------------------ |---------------------------------------------------------------|------------------| + /// | `4,` `2, 1, -2, -0,` `16533113728871998040,3427872634365736244,18115028555707261608,15878401910454357952,449479075714955186` | Encoded example data | | + /// | _____________________________________________________________________________________________________________________________ | _____________________________________________________________ | ________________ | + /// | **Length** section: `4` | Length of the **Structure** section (`L` = `structure.len()`) | First value | + /// | **Structure** section: `2, 1, -2, -0` | The **Structure** section | Next `L` values | + /// | **Data** section: `16533113728871998040, 3427872634365736244, 18115028555707261608, 15878401910454357952, 449479075714955186` | The **Data** section (layer IDs) | Remaining values | /// /// The data section lists the layer IDs for all folders/layers in the tree as read from top to bottom. /// The structure section lists signed numbers. The sign indicates a folder indentation change (`+` is down a level, `-` is up a level). /// The numbers in the structure block encode the indentation. For example: - /// - `2` means read two element from the data section, then place a `[`. + /// - `2` means read two elements from the data section, then place a `[`. /// - `-x` means read `x` elements from the data section and then insert a `]`. /// /// ```text @@ -949,14 +957,16 @@ impl DocumentMessageHandler { /// [3427872634365736244,18115028555707261608,449479075714955186] /// ``` pub fn serialize_root(&self) -> RawBuffer { - let mut structure = vec![LayerNodeIdentifier::ROOT]; - let mut data = Vec::new(); - self.serialize_structure(self.metadata().root(), &mut structure, &mut data, &mut vec![]); + let mut structure_section = vec![LayerNodeIdentifier::ROOT.to_node().0]; + let mut data_section = Vec::new(); + self.serialize_structure(self.metadata().root(), &mut structure_section, &mut data_section, &mut vec![]); - structure[0] = LayerNodeIdentifier::new_unchecked(NodeId(structure.len() as u64 - 1)); - structure.extend(data); + // Remove the ROOT element. Prepend `L`, the length (excluding the ROOT) of the structure section (which happens to be where the ROOT element was). + structure_section[0] = structure_section.len() as u64 - 1; + // Append the data section to the end. + structure_section.extend(data_section); - structure.iter().map(|id| id.to_node().0).collect::>().as_slice().into() + structure_section.as_slice().into() } /// Places a document into the history system @@ -1000,9 +1010,6 @@ impl DocumentMessageHandler { if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { self.document_redo_history.pop_front(); } - - responses.add(DocumentMessage::DocumentStructureChanged); - responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); } pub fn redo(&mut self, responses: &mut VecDeque) { @@ -1018,9 +1025,6 @@ impl DocumentMessageHandler { if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { self.document_undo_history.pop_front(); } - - responses.add(DocumentMessage::DocumentStructureChanged); - responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); } pub fn current_hash(&self) -> Option { @@ -1410,7 +1414,6 @@ impl DocumentMessageHandler { Redo, SelectAllLayers, DeselectAllLayers, - RenderDocument, SaveDocument, SetSnapping, DebugPrintDocument, diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs index bb339a21..f81c71af 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs @@ -83,8 +83,8 @@ impl<'a> ModifyInputsContext<'a> { pub fn insert_node_before(&mut self, new_id: NodeId, node_id: NodeId, input_index: usize, mut document_node: DocumentNode, offset: IVec2) -> Option { assert!(!self.document_network.nodes.contains_key(&new_id), "Creating already existing node"); - let post_node = self.document_network.nodes.get_mut(&node_id)?; + let post_node = self.document_network.nodes.get_mut(&node_id)?; post_node.inputs[input_index] = NodeInput::node(new_id, 0); document_node.metadata.position = post_node.metadata.position + offset; self.document_network.nodes.insert(new_id, document_node); @@ -153,7 +153,6 @@ impl<'a> ModifyInputsContext<'a> { }; let new_child = LayerNodeIdentifier::new(new_id, self.document_network); parent.push_front_child(self.document_metadata, new_child); - self.responses.add(DocumentMessage::DocumentStructureChanged); } new_id @@ -181,7 +180,7 @@ impl<'a> ModifyInputsContext<'a> { ], Default::default(), ); - self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); + self.responses.add(NodeGraphMessage::RunDocumentGraph); self.insert_node_before(NodeId(generate_uuid()), layer, 0, artboard_node, IVec2::new(-8, 0)) } @@ -202,7 +201,7 @@ impl<'a> ModifyInputsContext<'a> { self.insert_node_before(transform_id, fill_id, 0, transform, IVec2::new(-8, 0)); let shape_id = NodeId(generate_uuid()); self.insert_node_before(shape_id, transform_id, 0, shape, IVec2::new(-8, 0)); - self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); + self.responses.add(NodeGraphMessage::RunDocumentGraph); } fn insert_text(&mut self, text: String, font: Font, size: f32, layer: NodeId) { @@ -227,7 +226,7 @@ impl<'a> ModifyInputsContext<'a> { self.insert_node_before(transform_id, fill_id, 0, transform, IVec2::new(-8, 0)); let text_id = NodeId(generate_uuid()); self.insert_node_before(text_id, transform_id, 0, text, IVec2::new(-8, 0)); - self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); + self.responses.add(NodeGraphMessage::RunDocumentGraph); } fn insert_image_data(&mut self, image_frame: ImageFrame, layer: NodeId) { @@ -243,7 +242,7 @@ impl<'a> ModifyInputsContext<'a> { let image_id = NodeId(generate_uuid()); self.insert_node_before(image_id, transform_id, 0, image, IVec2::new(-8, 0)); - self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); + self.responses.add(NodeGraphMessage::RunDocumentGraph); } fn shift_upstream(&mut self, node_id: NodeId, shift: IVec2) { @@ -317,11 +316,6 @@ impl<'a> ModifyInputsContext<'a> { if !skip_rerender { self.responses.add(NodeGraphMessage::RunDocumentGraph); - } else { - // Code was removed from here which cleared the frame - } - if existing_node_id.is_none() { - self.responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); } } @@ -567,8 +561,7 @@ impl<'a> ModifyInputsContext<'a> { self.document_metadata.retain_selected_nodes(|id| !delete_nodes.contains(id)); self.responses.add(BroadcastEvent::SelectionChanged); - self.responses.add(DocumentMessage::DocumentStructureChanged); - self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true }); + self.responses.add(NodeGraphMessage::RunDocumentGraph); } } @@ -702,7 +695,7 @@ impl MessageHandler, }, - SendGraph { - should_rerender: bool, - }, + SendGraph, SetInputValue { node_id: NodeId, input_index: usize, @@ -86,6 +84,7 @@ pub enum NodeGraphMessage { input_index: usize, value: TaggedValue, }, + /// Move all the downstream nodes to the right in the graph to allow space for a newly inserted node ShiftNode { node_id: NodeId, }, diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index da321ed9..4f81b5ac 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -4,7 +4,9 @@ use crate::application::generate_uuid; use crate::messages::input_mapper::utility_types::macros::action_keys; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier}; +use crate::messages::portfolio::document::utility_types::layer_panel::{LayerClassification, LayerPanelEntry}; use crate::messages::prelude::*; + use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput, Source}; use graph_craft::proto::GraphErrors; @@ -68,6 +70,7 @@ pub struct FrontendGraphInput { name: String, #[serde(rename = "resolvedType")] resolved_type: Option, + connected: Option, } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] @@ -77,13 +80,14 @@ pub struct FrontendGraphOutput { name: String, #[serde(rename = "resolvedType")] resolved_type: Option, + connected: Option, } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] pub struct FrontendNode { + pub id: graph_craft::document::NodeId, #[serde(rename = "isLayer")] pub is_layer: bool, - pub id: graph_craft::document::NodeId, pub alias: String, pub name: String, #[serde(rename = "primaryInput")] @@ -274,14 +278,9 @@ impl NodeGraphMessageHandler { } } - fn send_graph(&self, network: &NodeNetwork, graph_view_overlay_open: bool, responses: &mut VecDeque) { - responses.add(PropertiesPanelMessage::Refresh); + fn send_graph(&self, network: &NodeNetwork, graph_view_overlay_open: bool, document_metadata: &mut DocumentMetadata, collapsed: &Vec, responses: &mut VecDeque) { + document_metadata.load_structure(&network); - if !graph_view_overlay_open { - return; - } - - // List of links in format (link_start, link_end, link_end_input_index) let links = network .nodes .iter() @@ -306,52 +305,111 @@ impl NodeGraphMessageHandler { }) .collect::>(); + let connected_node_to_output_lookup = links.iter().map(|link| ((link.link_start, link.link_start_output_index), link.link_end)).collect::>(); + let mut nodes = Vec::new(); - for (id, node) in &network.nodes { - let node_path = vec![*id]; + for (&node_id, node) in &network.nodes { + let node_path = vec![node_id]; // TODO: This should be based on the graph runtime type inference system in order to change the colors of node connectors to match the data type in use - let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) else { + let Some(document_node_definition) = document_node_types::resolve_document_node_type(&node.name) else { warn!("Node '{}' does not exist in library", node.name); continue; }; // Inputs - let mut inputs = node.inputs.iter().zip(node_type.inputs.iter().enumerate().map(|(index, input_type)| { - let index = node.inputs.iter().take(index).filter(|input| input.is_exposed()).count(); - FrontendGraphInput { - data_type: input_type.data_type, - name: input_type.name.to_string(), - resolved_type: self.resolved_types.inputs.get(&Source { node: node_path.clone(), index }).map(|input| format!("{input:?}")), - } - })); + let mut inputs = { + let frontend_graph_inputs = document_node_definition.inputs.iter().enumerate().map(|(index, input_type)| { + // Convert the index in all inputs to the index in only the exposed inputs + let index = node.inputs.iter().take(index).filter(|input| input.is_exposed()).count(); + + FrontendGraphInput { + data_type: input_type.data_type, + name: input_type.name.to_string(), + resolved_type: self.resolved_types.inputs.get(&Source { node: node_path.clone(), index }).map(|input| format!("{input:?}")), + connected: None, + } + }); + + node.inputs.iter().zip(frontend_graph_inputs).map(|(node_input, mut frontend_graph_input)| { + if let NodeInput::Node { node_id: connected_node_id, .. } = node_input { + frontend_graph_input.connected = Some(*connected_node_id); + } + (node_input, frontend_graph_input) + }) + }; let primary_input = inputs.next().filter(|(input, _)| input.is_exposed()).map(|(_, input_type)| input_type); let exposed_inputs = inputs.filter(|(input, _)| input.is_exposed()).map(|(_, input_type)| input_type).collect(); // Outputs - let mut outputs = node_type.outputs.iter().enumerate().map(|(index, output_type)| FrontendGraphOutput { + let mut outputs = document_node_definition.outputs.iter().enumerate().map(|(index, output_type)| FrontendGraphOutput { data_type: output_type.data_type, name: output_type.name.to_string(), resolved_type: self.resolved_types.outputs.get(&Source { node: node_path.clone(), index }).map(|output| format!("{output:?}")), + connected: connected_node_to_output_lookup.get(&(node_id, index)).copied(), }); - let primary_output = if node.has_primary_output { outputs.next() } else { None }; + let primary_output = node.has_primary_output.then(|| outputs.next()).flatten(); + let exposed_outputs = outputs.collect::>(); + // Errors let errors = self.node_graph_errors.iter().find(|error| error.node_path.starts_with(&node_path)).map(|error| error.error.clone()); + nodes.push(FrontendNode { + id: node_id, is_layer: node.is_layer(), - id: *id, alias: node.alias.clone(), name: node.name.clone(), primary_input, exposed_inputs, primary_output, - exposed_outputs: outputs.collect::>(), + exposed_outputs, position: node.metadata.position.into(), - previewed: network.outputs_contain(*id), - disabled: network.disabled.contains(id), + previewed: network.outputs_contain(node_id), + disabled: network.disabled.contains(&node_id), errors: errors.map(|e| format!("{e:?}")), - }) + }); + + if node.is_layer() { + let layer = LayerNodeIdentifier::new(node_id, network); + let layer_classification = { + if document_metadata.is_artboard(layer) { + LayerClassification::Artboard + } else if document_metadata.is_folder(layer) { + LayerClassification::Folder + } else { + LayerClassification::Layer + } + // TODO: Maybe switch to this below if perhaps it's simpler? + // if node.is_artboard() { + // LayerClassification::Artboard + // } else if node.is_folder(network) { + // LayerClassification::Folder + // } else { + // LayerClassification::Layer + // } + }; + + let data = LayerPanelEntry { + id: node_id, + layer_classification, + expanded: layer.has_children(document_metadata) && !collapsed.contains(&layer), + depth: layer.ancestors(document_metadata).count() - 1, + parent_id: layer.parent(document_metadata).map(|parent| parent.to_node()), + // TODO: Remove and take this from the graph data in the frontend similar to thumbnail? + name: network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(), + // TODO: Remove and take this from the graph data in the frontend similar to thumbnail? + tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() }, + // TODO: Remove and take this from the graph data in the frontend similar to thumbnail? + disabled: network.disabled.contains(&node_id), + }; + responses.add(FrontendMessage::UpdateDocumentLayerDetails { data }); + } } - responses.add(FrontendMessage::UpdateNodeGraph { nodes, links }); + + responses.add(DocumentMessage::DocumentStructureChanged); + if graph_view_overlay_open { + responses.add(FrontendMessage::UpdateNodeGraph { nodes, links }); + } + responses.add(PropertiesPanelMessage::Refresh); } /// Updates the frontend's selection state in line with the backend @@ -474,7 +532,7 @@ impl<'a> MessageHandler> for NodeGrap fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque, data: NodeGraphHandlerData<'a>) { let NodeGraphHandlerData { document_network, - document_metadata: metadata, + document_metadata, document_id, collapsed, graph_view_overlay_open, @@ -487,15 +545,14 @@ impl<'a> MessageHandler> for NodeGrap on: BroadcastEvent::SelectionChanged, send: Box::new(NodeGraphMessage::SelectedNodesUpdated.into()), }); - load_network_structure(document_network, metadata, collapsed); - responses.add(DocumentMessage::DocumentStructureChanged); + load_network_structure(document_network, document_metadata, collapsed); } NodeGraphMessage::SelectedNodesUpdated => { - self.update_selection_action_buttons(document_network, metadata, responses); - self.update_selected(document_network, metadata, responses); - if metadata.selected_layers().count() <= 1 { + self.update_selection_action_buttons(document_network, document_metadata, responses); + self.update_selected(document_network, document_metadata, responses); + if document_metadata.selected_layers().count() <= 1 { responses.add(DocumentMessage::SetRangeSelectionLayer { - new_layer: metadata.selected_layers().next(), + new_layer: document_metadata.selected_layers().next(), }); } responses.add(NodeGraphMessage::RunDocumentGraph); @@ -520,15 +577,15 @@ impl<'a> MessageHandler> for NodeGrap error!("Failed to find actual index of connector index {input_node_connector_index} on node {input_node:#?}"); return; }; - responses.add(DocumentMessage::DocumentStructureChanged); responses.add(DocumentMessage::StartTransaction); let input = NodeInput::node(output_node, output_node_connector_index); responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input }); - let should_rerender = network.connected_to_output(node_id); - responses.add(NodeGraphMessage::SendGraph { should_rerender }); + if network.connected_to_output(node_id) { + responses.add(NodeGraphMessage::RunDocumentGraph); + } } NodeGraphMessage::Copy => { let Some(network) = document_network.nested_network(&self.network) else { @@ -537,7 +594,7 @@ impl<'a> MessageHandler> for NodeGrap }; // Collect the selected nodes - let new_ids = &metadata.selected_nodes().copied().enumerate().map(|(new, old)| (old, NodeId(new as u64))).collect(); + let new_ids = &document_metadata.selected_nodes().copied().enumerate().map(|(new, old)| (old, NodeId(new as u64))).collect(); let copied_nodes: Vec<_> = Self::copy_nodes(network, new_ids).collect(); // Prefix to show that this is nodes @@ -564,28 +621,24 @@ impl<'a> MessageHandler> for NodeGrap graph_craft::document::DocumentNodeMetadata::position((x, y)), ); responses.add(NodeGraphMessage::InsertNode { node_id, document_node }); - - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); } NodeGraphMessage::Cut => { responses.add(NodeGraphMessage::Copy); responses.add(NodeGraphMessage::DeleteSelectedNodes { reconnect: true }); } NodeGraphMessage::DeleteNode { node_id, reconnect } => { - self.remove_node(document_network, metadata, node_id, responses, reconnect); + self.remove_node(document_network, document_metadata, node_id, responses, reconnect); } NodeGraphMessage::DeleteSelectedNodes { reconnect } => { responses.add(DocumentMessage::StartTransaction); - for node_id in metadata.selected_nodes().copied() { + for node_id in document_metadata.selected_nodes().copied() { responses.add(NodeGraphMessage::DeleteNode { node_id, reconnect }); } - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); - if let Some(network) = document_network.nested_network(&self.network) { // Only generate node graph if one of the selected nodes is connected to the output - if metadata.selected_nodes().any(|&node_id| network.connected_to_output(node_id)) { + if document_metadata.selected_nodes().any(|&node_id| network.connected_to_output(node_id)) { responses.add(NodeGraphMessage::RunDocumentGraph); } } @@ -615,34 +668,35 @@ impl<'a> MessageHandler> for NodeGrap } responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input }); - let should_rerender = network.connected_to_output(node_id); - responses.add(NodeGraphMessage::SendGraph { should_rerender }); + if network.connected_to_output(node_id) { + responses.add(NodeGraphMessage::RunDocumentGraph); + } } - NodeGraphMessage::DoubleClickNode { node } => { + NodeGraphMessage::EnterNestedNetwork { node } => { if let Some(network) = document_network.nested_network(&self.network) { if network.nodes.get(&node).and_then(|node| node.implementation.get_network()).is_some() { self.network.push(node); } } if let Some(network) = document_network.nested_network(&self.network) { - self.send_graph(network, graph_view_overlay_open, responses); + self.send_graph(network, graph_view_overlay_open, document_metadata, collapsed, responses); } - self.update_selected(document_network, metadata, responses); + self.update_selected(document_network, document_metadata, responses); } NodeGraphMessage::DuplicateSelectedNodes => { if let Some(network) = document_network.nested_network(&self.network) { responses.add(DocumentMessage::StartTransaction); - let new_ids = &metadata.selected_nodes().map(|&id| (id, NodeId(generate_uuid()))).collect(); + let new_ids = &document_metadata.selected_nodes().map(|&id| (id, NodeId(generate_uuid()))).collect(); - metadata.clear_selected_nodes(); + document_metadata.clear_selected_nodes(); responses.add(BroadcastEvent::SelectionChanged); // Copy the selected nodes let copied_nodes = Self::copy_nodes(network, new_ids).collect::>(); // Select the new nodes - metadata.add_selected_nodes(copied_nodes.iter().map(|(node_id, _)| *node_id)); + document_metadata.add_selected_nodes(copied_nodes.iter().map(|(node_id, _)| *node_id)); responses.add(BroadcastEvent::SelectionChanged); for (node_id, mut document_node) in copied_nodes { @@ -653,22 +707,20 @@ impl<'a> MessageHandler> for NodeGrap responses.add(NodeGraphMessage::InsertNode { node_id, document_node }); } - self.send_graph(network, graph_view_overlay_open, responses); - self.update_selected(document_network, metadata, responses); - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); + self.update_selected(document_network, document_metadata, responses); } } NodeGraphMessage::ExitNestedNetwork { depth_of_nesting } => { - metadata.clear_selected_nodes(); + document_metadata.clear_selected_nodes(); responses.add(BroadcastEvent::SelectionChanged); for _ in 0..depth_of_nesting { self.network.pop(); } if let Some(network) = document_network.nested_network(&self.network) { - self.send_graph(network, graph_view_overlay_open, responses); + self.send_graph(network, graph_view_overlay_open, document_metadata, collapsed, responses); } - self.update_selected(document_network, metadata, responses); + self.update_selected(document_network, document_metadata, responses); } NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => { let Some(network) = document_network.nested_network(&self.network) else { @@ -696,8 +748,6 @@ impl<'a> MessageHandler> for NodeGrap } responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input }); - let should_rerender = network.connected_to_output(node_id); - responses.add(NodeGraphMessage::SendGraph { should_rerender }); responses.add(PropertiesPanelMessage::Refresh); } NodeGraphMessage::InsertNode { node_id, document_node } => { @@ -711,12 +761,12 @@ impl<'a> MessageHandler> for NodeGrap return; }; - for node_id in metadata.selected_nodes() { + for node_id in document_metadata.selected_nodes() { if let Some(node) = network.nodes.get_mut(node_id) { node.metadata.position += IVec2::new(displacement_x, displacement_y) } } - self.send_graph(network, graph_view_overlay_open, responses); + self.send_graph(network, graph_view_overlay_open, document_metadata, collapsed, responses); } NodeGraphMessage::PasteNodes { serialized_nodes } => { let Some(network) = document_network.nested_network(&self.network) else { @@ -762,29 +812,26 @@ impl<'a> MessageHandler> for NodeGrap let nodes = new_ids.values().copied().collect(); responses.add(NodeGraphMessage::SelectedNodesSet { nodes }); - - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); } - NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::SubmitGraphRender { document_id }), + NodeGraphMessage::RunDocumentGraph => { + responses.add(PortfolioMessage::SubmitGraphRender { document_id }); + } NodeGraphMessage::SelectedNodesAdd { nodes } => { - metadata.add_selected_nodes(nodes); + document_metadata.add_selected_nodes(nodes); responses.add(BroadcastEvent::SelectionChanged); } NodeGraphMessage::SelectedNodesRemove { nodes } => { - metadata.retain_selected_nodes(|node| !nodes.contains(node)); + document_metadata.retain_selected_nodes(|node| !nodes.contains(node)); responses.add(BroadcastEvent::SelectionChanged); } NodeGraphMessage::SelectedNodesSet { nodes } => { - metadata.set_selected_nodes(nodes); + document_metadata.set_selected_nodes(nodes); responses.add(BroadcastEvent::SelectionChanged); responses.add(PropertiesPanelMessage::Refresh); } - NodeGraphMessage::SendGraph { should_rerender } => { + NodeGraphMessage::SendGraph => { if let Some(network) = document_network.nested_network(&self.network) { - self.send_graph(network, graph_view_overlay_open, responses); - if should_rerender { - responses.add(NodeGraphMessage::RunDocumentGraph); - } + self.send_graph(network, graph_view_overlay_open, document_metadata, collapsed, responses); } } NodeGraphMessage::SetInputValue { node_id, input_index, value } => { @@ -811,7 +858,7 @@ impl<'a> MessageHandler> for NodeGrap let structure_changed = node_input.as_node().is_some() || input.as_node().is_some(); *node_input = input; if structure_changed { - load_network_structure(document_network, metadata, collapsed); + load_network_structure(document_network, document_metadata, collapsed); } } } @@ -837,6 +884,7 @@ impl<'a> MessageHandler> for NodeGrap } } } + // Move all the downstream nodes to the right in the graph to allow space for a newly inserted node NodeGraphMessage::ShiftNode { node_id } => { let Some(network) = document_network.nested_network_mut(&self.network) else { warn!("No network"); @@ -883,14 +931,15 @@ impl<'a> MessageHandler> for NodeGrap stack.extend(outwards_links.get(&id).unwrap_or(&Vec::new()).iter().copied()) } } - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); + + self.send_graph(network, graph_view_overlay_open, document_metadata, collapsed, responses); } NodeGraphMessage::ToggleSelectedHidden => { if let Some(network) = document_network.nested_network(&self.network) { responses.add(DocumentMessage::StartTransaction); - let new_hidden = !metadata.selected_nodes().any(|id| network.disabled.contains(id)); - for &node_id in metadata.selected_nodes() { + let new_hidden = !document_metadata.selected_nodes().any(|id| network.disabled.contains(id)); + for &node_id in document_metadata.selected_nodes() { responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden }); } } @@ -908,14 +957,13 @@ impl<'a> MessageHandler> for NodeGrap } else if !network.inputs.contains(&node_id) && !network.original_outputs().iter().any(|output| output.node_id == node_id) { network.disabled.push(node_id); } - self.send_graph(network, graph_view_overlay_open, responses); // Only generate node graph if one of the selected nodes is connected to the output if network.connected_to_output(node_id) { responses.add(NodeGraphMessage::RunDocumentGraph); } } - self.update_selection_action_buttons(document_network, metadata, responses); + self.update_selection_action_buttons(document_network, document_metadata, responses); } NodeGraphMessage::SetName { node_id, name } => { responses.add(DocumentMessage::StartTransaction); @@ -925,7 +973,8 @@ impl<'a> MessageHandler> for NodeGrap if let Some(network) = document_network.nested_network_mut(&self.network) { if let Some(node) = network.nodes.get_mut(&node_id) { node.alias = name; - responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); + + self.send_graph(network, graph_view_overlay_open, document_metadata, collapsed, responses); } } } @@ -944,37 +993,30 @@ impl<'a> MessageHandler> for NodeGrap } else { return; } - self.send_graph(network, graph_view_overlay_open, responses); } - self.update_selection_action_buttons(document_network, metadata, responses); + + self.update_selection_action_buttons(document_network, document_metadata, responses); + responses.add(NodeGraphMessage::RunDocumentGraph); } NodeGraphMessage::UpdateNewNodeGraph => { if let Some(network) = document_network.nested_network(&self.network) { - metadata.clear_selected_nodes(); + document_metadata.clear_selected_nodes(); responses.add(BroadcastEvent::SelectionChanged); - self.send_graph(network, graph_view_overlay_open, responses); + self.send_graph(network, graph_view_overlay_open, document_metadata, collapsed, responses); let node_types = document_node_types::collect_node_types(); responses.add(FrontendMessage::UpdateNodeTypes { node_types }); } - self.update_selected(document_network, metadata, responses); + self.update_selected(document_network, document_metadata, responses); } NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => { - let changed = self.resolved_types != resolved_types || self.node_graph_errors != node_graph_errors; - self.resolved_types = resolved_types; self.node_graph_errors = node_graph_errors; - - if changed { - if let Some(network) = document_network.nested_network(&self.network) { - self.send_graph(network, graph_view_overlay_open, responses) - } - } } } - self.has_selection = metadata.has_selected_nodes(); + self.has_selection = document_metadata.has_selected_nodes(); } fn actions(&self) -> ActionList { diff --git a/editor/src/messages/portfolio/document/utility_types/layer_panel.rs b/editor/src/messages/portfolio/document/utility_types/layer_panel.rs index 5858c526..b4f04730 100644 --- a/editor/src/messages/portfolio/document/utility_types/layer_panel.rs +++ b/editor/src/messages/portfolio/document/utility_types/layer_panel.rs @@ -39,15 +39,14 @@ pub enum LayerClassification { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, specta::Type)] pub struct LayerPanelEntry { + pub id: NodeId, pub name: String, pub tooltip: String, #[serde(rename = "layerClassification")] pub layer_classification: LayerClassification, - pub selected: bool, pub expanded: bool, + pub disabled: bool, #[serde(rename = "parentId")] pub parent_id: Option, - pub id: NodeId, pub depth: usize, - pub thumbnail: String, } diff --git a/editor/src/messages/portfolio/document/utility_types/misc.rs b/editor/src/messages/portfolio/document/utility_types/misc.rs index 6700544a..c50cfd35 100644 --- a/editor/src/messages/portfolio/document/utility_types/misc.rs +++ b/editor/src/messages/portfolio/document/utility_types/misc.rs @@ -1,5 +1,3 @@ -pub use super::layer_panel::LayerPanelEntry; - use glam::DVec2; use serde::{Deserialize, Serialize}; use std::fmt; diff --git a/editor/src/messages/portfolio/portfolio_message.rs b/editor/src/messages/portfolio/portfolio_message.rs index 72ecad72..c728dba7 100644 --- a/editor/src/messages/portfolio/portfolio_message.rs +++ b/editor/src/messages/portfolio/portfolio_message.rs @@ -95,9 +95,6 @@ pub enum PortfolioMessage { SelectDocument { document_id: DocumentId, }, - SetActiveDocument { - document_id: DocumentId, - }, SubmitDocumentExport { file_name: String, file_type: FileType, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 3e983ab9..1dec2986 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -139,8 +139,6 @@ impl MessageHandler { let target_document = self.documents.get(&document_id).unwrap(); @@ -420,6 +418,7 @@ impl MessageHandler { + // Auto-save the document we are leaving if let Some(document) = self.active_document() { if !document.is_auto_saved() { responses.add(PortfolioMessage::AutoSaveDocument { @@ -429,27 +428,20 @@ impl MessageHandler { - self.active_document_id = Some(document_id); - responses.add(MenuBarMessage::SendLayout); - } PortfolioMessage::SubmitDocumentExport { file_name, file_type, @@ -619,7 +611,6 @@ impl PortfolioMessageHandler { responses.add(ToolMessage::InitTools); responses.add(NodeGraphMessage::Init); responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() }); - responses.add(DocumentMessage::DocumentStructureChanged); responses.add(PropertiesPanelMessage::Clear); responses.add(NodeGraphMessage::UpdateNewNodeGraph); } diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index d10feb48..4a0be8e6 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -66,7 +66,7 @@ impl Pivot { // If more than one layer is selected we use the AABB with the mean of the pivots let xy_summation = document .selected_visible_layers() - .map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network, &document.metadata)) + .map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network, &document.document_metadata)) .reduce(|a, b| a + b) .unwrap_or_default(); diff --git a/editor/src/messages/tool/tool_message_handler.rs b/editor/src/messages/tool/tool_message_handler.rs index 596efb05..5b5d650e 100644 --- a/editor/src/messages/tool/tool_message_handler.rs +++ b/editor/src/messages/tool/tool_message_handler.rs @@ -144,9 +144,10 @@ impl MessageHandler { // Subscribe the transform layer to selection change events - let send = Box::new(TransformLayerMessage::SelectionChanged.into()); - let on = BroadcastEvent::SelectionChanged; - responses.add(BroadcastMessage::SubscribeEvent { send, on }); + responses.add(BroadcastMessage::SubscribeEvent { + on: BroadcastEvent::SelectionChanged, + send: Box::new(TransformLayerMessage::SelectionChanged.into()), + }); let tool_data = &mut self.tool_state.tool_data; let document_data = &self.tool_state.document_tool_data; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 4dbe057a..7f859e4a 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -223,7 +223,7 @@ impl PathToolData { let _selected_layers = shape_editor.selected_layers().cloned().collect::>(); // Select the first point within the threshold (in pixels) - if let Some(selected_points) = shape_editor.select_point(&document.network, &document.metadata, input.mouse.position, SELECTION_THRESHOLD, shift) { + if let Some(selected_points) = shape_editor.select_point(&document.network, &document.document_metadata, input.mouse.position, SELECTION_THRESHOLD, shift) { self.start_dragging_point(selected_points, input, document, responses); responses.add(OverlaysMessage::Draw); @@ -298,7 +298,7 @@ impl PathToolData { // Move the selected points with the mouse let snapped_position = self.snap_manager.snap_position(responses, document, input.mouse.position); - shape_editor.move_selected_points(&document.network, &document.metadata, snapped_position - self.previous_mouse_position, shift, responses); + shape_editor.move_selected_points(&document.network, &document.document_metadata, snapped_position - self.previous_mouse_position, shift, responses); self.previous_mouse_position = snapped_position; } } @@ -362,7 +362,12 @@ impl Fsm for PathToolFsmState { if tool_data.drag_start_pos == tool_data.previous_mouse_position { responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] }); } else { - shape_editor.select_all_in_quad(&document.network, &document.metadata, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed); + shape_editor.select_all_in_quad( + &document.network, + &document.document_metadata, + [tool_data.drag_start_pos, tool_data.previous_mouse_position], + !shift_pressed, + ); } responses.add(OverlaysMessage::Draw); @@ -376,7 +381,12 @@ impl Fsm for PathToolFsmState { if tool_data.drag_start_pos == tool_data.previous_mouse_position { responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] }); } else { - shape_editor.select_all_in_quad(&document.network, &document.metadata, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed); + shape_editor.select_all_in_quad( + &document.network, + &document.document_metadata, + [tool_data.drag_start_pos, tool_data.previous_mouse_position], + !shift_pressed, + ); } responses.add(OverlaysMessage::Draw); responses.add(PathToolMessage::SelectedPointUpdated); @@ -388,16 +398,16 @@ impl Fsm for PathToolFsmState { let shift_pressed = input.keyboard.get(shift_mirror_distance as usize); let nearest_point = shape_editor - .find_nearest_point_indices(&document.network, &document.metadata, input.mouse.position, SELECTION_THRESHOLD) + .find_nearest_point_indices(&document.network, &document.document_metadata, input.mouse.position, SELECTION_THRESHOLD) .map(|(_, nearest_point)| nearest_point); - shape_editor.delete_selected_handles_with_zero_length(&document.network, &document.metadata, &tool_data.opposing_handle_lengths, responses); + shape_editor.delete_selected_handles_with_zero_length(&document.network, &document.document_metadata, &tool_data.opposing_handle_lengths, responses); if tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD && !shift_pressed { let clicked_selected = shape_editor.selected_points().any(|&point| nearest_point == Some(point)); if clicked_selected { shape_editor.deselect_all(); - shape_editor.select_point(&document.network, &document.metadata, input.mouse.position, SELECTION_THRESHOLD, false); + shape_editor.select_point(&document.network, &document.document_metadata, input.mouse.position, SELECTION_THRESHOLD, false); responses.add(OverlaysMessage::Draw); } } @@ -418,9 +428,9 @@ impl Fsm for PathToolFsmState { } (_, PathToolMessage::InsertPoint) => { // First we try and flip the sharpness (if they have clicked on an anchor) - if !shape_editor.flip_sharp(&document.network, &document.metadata, input.mouse.position, SELECTION_TOLERANCE, responses) { + if !shape_editor.flip_sharp(&document.network, &document.document_metadata, input.mouse.position, SELECTION_TOLERANCE, responses) { // If not, then we try and split the path that may have been clicked upon - shape_editor.split(&document.network, &document.metadata, input.mouse.position, SELECTION_TOLERANCE, responses); + shape_editor.split(&document.network, &document.document_metadata, input.mouse.position, SELECTION_TOLERANCE, responses); } responses.add(PathToolMessage::SelectedPointUpdated); @@ -433,7 +443,7 @@ impl Fsm for PathToolFsmState { } (_, PathToolMessage::PointerMove { .. }) => self, (_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => { - shape_editor.move_selected_points(&document.network, &document.metadata, (delta_x, delta_y).into(), true, responses); + shape_editor.move_selected_points(&document.network, &document.document_metadata, (delta_x, delta_y).into(), true, responses); PathToolFsmState::Ready } @@ -444,18 +454,18 @@ impl Fsm for PathToolFsmState { } (_, PathToolMessage::SelectedPointXChanged { new_x }) => { if let Some(&SingleSelectedPoint { coordinates, id, layer, .. }) = tool_data.selection_status.as_one() { - shape_editor.reposition_control_point(&id, responses, &document.network, &document.metadata, DVec2::new(new_x, coordinates.y), layer); + shape_editor.reposition_control_point(&id, responses, &document.network, &document.document_metadata, DVec2::new(new_x, coordinates.y), layer); } PathToolFsmState::Ready } (_, PathToolMessage::SelectedPointYChanged { new_y }) => { if let Some(&SingleSelectedPoint { coordinates, id, layer, .. }) = tool_data.selection_status.as_one() { - shape_editor.reposition_control_point(&id, responses, &document.network, &document.metadata, DVec2::new(coordinates.x, new_y), layer); + shape_editor.reposition_control_point(&id, responses, &document.network, &document.document_metadata, DVec2::new(coordinates.x, new_y), layer); } PathToolFsmState::Ready } (_, PathToolMessage::SelectedPointUpdated) => { - tool_data.selection_status = get_selection_status(&document.network, &document.metadata, shape_editor); + tool_data.selection_status = get_selection_status(&document.network, &document.document_metadata, shape_editor); self } (_, PathToolMessage::ManipulatorAngleMakeSmooth) => { diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index cb8f8af9..d0c5ced4 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -491,7 +491,7 @@ impl Fsm for SelectToolFsmState { &tool_data.layers_dragging, responses, &document.network, - &document.metadata, + &document.document_metadata, None, &ToolType::Select, ); @@ -510,7 +510,7 @@ impl Fsm for SelectToolFsmState { &selected, responses, &document.network, - &document.metadata, + &document.document_metadata, None, &ToolType::Select, ); @@ -630,7 +630,7 @@ impl Fsm for SelectToolFsmState { selected, responses, &document.network, - &document.metadata, + &document.document_metadata, None, &ToolType::Select, ); @@ -665,7 +665,7 @@ impl Fsm for SelectToolFsmState { &tool_data.layers_dragging, responses, &document.network, - &document.metadata, + &document.document_metadata, None, &ToolType::Select, ); @@ -832,7 +832,7 @@ impl Fsm for SelectToolFsmState { &tool_data.layers_dragging, responses, &document.network, - &document.metadata, + &document.document_metadata, None, &ToolType::Select, ); diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index 06d2024f..a5b04ac0 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -56,7 +56,7 @@ impl<'a> MessageHandler> for TransformL &selected_layers, responses, &document.network, - &document.metadata, + &document.document_metadata, Some(shape_editor), &tool_data.active_tool_type, ); diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index f219639a..55aa52dc 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -1,10 +1,7 @@ use crate::consts::FILE_SAVE_SUFFIX; -use crate::messages::frontend::utility_types::FrontendImageData; use crate::messages::frontend::utility_types::{ExportBounds, FileType}; use crate::messages::portfolio::document::node_graph::wrap_network_in_scope; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::portfolio::document::utility_types::layer_panel::LayerClassification; -use crate::messages::portfolio::document::utility_types::misc::LayerPanelEntry; use crate::messages::prelude::*; use graph_craft::concrete; @@ -15,8 +12,9 @@ use graph_craft::imaginate_input::ImaginatePreferences; use graph_craft::proto::GraphErrors; use graphene_core::application_io::{NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig}; use graphene_core::memo::IORecord; -use graphene_core::raster::{Image, ImageFrame}; -use graphene_core::renderer::{ClickTarget, GraphicElementRendered, SvgSegmentList}; +use graphene_core::raster::ImageFrame; +use graphene_core::renderer::{ClickTarget, GraphicElementRendered, ImageRenderMode, RenderParams, SvgRender}; +use graphene_core::renderer::{RenderSvgSegmentList, SvgSegment}; use graphene_core::text::FontCache; use graphene_core::transform::{Footprint, Transform}; use graphene_core::vector::style::ViewMode; @@ -27,28 +25,45 @@ use interpreted_executor::dynamic_executor::{DynamicExecutor, ResolvedDocumentNo use glam::{DAffine2, DVec2, UVec2}; use std::cell::RefCell; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hash; +use std::hash::Hasher; use std::rc::Rc; use std::sync::mpsc::{Receiver, Sender}; use std::sync::Arc; +/// Persistent data between graph executions. It's updated via message passing from the editor thread with [`NodeRuntimeMessage`]`. +/// Some of these fields are put into a [`WasmEditorApi`] which is passed to the final compiled graph network upon each execution. +/// Once the implementation is finished, this will live in a separate thread. Right now it's part of the main JS thread, but its own separate JS stack frame independent from the editor. pub struct NodeRuntime { - pub(crate) executor: DynamicExecutor, - font_cache: FontCache, + executor: DynamicExecutor, receiver: Receiver, sender: InternalNodeGraphUpdateSender, - wasm_io: Option, + + /// Font data (for rendering text) made available to the graph through the [`WasmEditorApi`]. + font_cache: FontCache, + /// Imaginate preferences made available to the graph through the [`WasmEditorApi`]. imaginate_preferences: ImaginatePreferences, - pub(crate) thumbnails: HashMap, - pub(crate) click_targets: HashMap>, - pub(crate) upstream_transforms: HashMap, - pub(crate) resolved_types: ResolvedDocumentNodeTypes, - pub(crate) node_graph_errors: GraphErrors, + + /// Gives access to APIs like a rendering surface (native window handle or HTML5 canvas) and WGPU (which becomes WebGPU on web). + wasm_application_io: Option, graph_hash: Option, + node_graph_errors: GraphErrors, + resolved_types: ResolvedDocumentNodeTypes, monitor_nodes: Vec>, + + // TODO: Remove, it doesn't need to be persisted anymore + /// The current renders of the thumbnails for layer nodes. + thumbnail_renders: HashMap>, + /// The current click targets for layer nodes. + click_targets: HashMap>, + /// The current upstream transforms for nodes. + upstream_transforms: HashMap, } +/// Messages passed from the editor thread to the node runtime thread. enum NodeRuntimeMessage { - GenerationRequest(GenerationRequest), + ExecutionRequest(ExecutionRequest), FontCacheUpdate(FontCache), ImaginatePreferencesUpdate(ImaginatePreferences), } @@ -63,17 +78,16 @@ pub struct ExportConfig { pub size: DVec2, } -pub(crate) struct GenerationRequest { - generation_id: u64, +pub(crate) struct ExecutionRequest { + execution_id: u64, graph: NodeNetwork, render_config: RenderConfig, } -pub(crate) struct GenerationResponse { - generation_id: u64, +pub(crate) struct ExecutionResponse { + execution_id: u64, result: Result, - updates: VecDeque, - new_thumbnails: HashMap, + responses: VecDeque, new_click_targets: HashMap>, new_upstream_transforms: HashMap, resolved_types: ResolvedDocumentNodeTypes, @@ -82,15 +96,15 @@ pub(crate) struct GenerationResponse { } enum NodeGraphUpdate { - GenerationResponse(GenerationResponse), + ExecutionResponse(ExecutionResponse), NodeGraphUpdateMessage(NodeGraphUpdateMessage), } struct InternalNodeGraphUpdateSender(Sender); impl InternalNodeGraphUpdateSender { - fn send_generation_response(&self, response: GenerationResponse) { - self.0.send(NodeGraphUpdate::GenerationResponse(response)).expect("Failed to send response") + fn send_generation_response(&self, response: ExecutionResponse) { + self.0.send(NodeGraphUpdate::ExecutionResponse(response)).expect("Failed to send response") } } @@ -106,30 +120,33 @@ thread_local! { impl NodeRuntime { fn new(receiver: Receiver, sender: Sender) -> Self { - let executor = DynamicExecutor::default(); Self { - executor, + executor: DynamicExecutor::default(), receiver, sender: InternalNodeGraphUpdateSender(sender), + font_cache: FontCache::default(), imaginate_preferences: Default::default(), - thumbnails: Default::default(), - wasm_io: None, - click_targets: HashMap::new(), + + wasm_application_io: None, graph_hash: None, - upstream_transforms: HashMap::new(), - resolved_types: ResolvedDocumentNodeTypes::default(), node_graph_errors: Vec::new(), + resolved_types: ResolvedDocumentNodeTypes::default(), monitor_nodes: Vec::new(), + + thumbnail_renders: Default::default(), + click_targets: HashMap::new(), + upstream_transforms: HashMap::new(), } } + pub async fn run(&mut self) { let mut requests = self.receiver.try_iter().collect::>(); // TODO: Currently we still render the document after we submit the node graph execution request. // This should be avoided in the future. requests.reverse(); requests.dedup_by_key(|x| match x { - NodeRuntimeMessage::GenerationRequest(x) => Some(x.graph.current_hash()), + NodeRuntimeMessage::ExecutionRequest(x) => Some(x.graph.current_hash()), _ => None, }); requests.reverse(); @@ -137,49 +154,45 @@ impl NodeRuntime { match request { NodeRuntimeMessage::FontCacheUpdate(font_cache) => self.font_cache = font_cache, NodeRuntimeMessage::ImaginatePreferencesUpdate(preferences) => self.imaginate_preferences = preferences, - NodeRuntimeMessage::GenerationRequest(GenerationRequest { - generation_id, graph, render_config, .. + NodeRuntimeMessage::ExecutionRequest(ExecutionRequest { + execution_id, graph, render_config, .. }) => { let transform = render_config.viewport.transform; + let result = self.execute_network(graph, render_config).await; + let mut responses = VecDeque::new(); + self.process_monitor_nodes(&mut responses); - self.update_thumbnails(&mut responses); - self.update_upstream_transforms(); - - let response = GenerationResponse { - generation_id, + self.sender.send_generation_response(ExecutionResponse { + execution_id, result, - updates: responses, - new_thumbnails: self.thumbnails.clone(), + responses, new_click_targets: self.click_targets.clone().into_iter().map(|(id, targets)| (LayerNodeIdentifier::new_unchecked(id), targets)).collect(), new_upstream_transforms: self.upstream_transforms.clone(), resolved_types: self.resolved_types.clone(), node_graph_errors: core::mem::take(&mut self.node_graph_errors), transform, - }; - self.sender.send_generation_response(response); + }); } } } } async fn execute_network<'a>(&'a mut self, graph: NodeNetwork, render_config: RenderConfig) -> Result { - if self.wasm_io.is_none() { - self.wasm_io = Some(WasmApplicationIo::new().await); + if self.wasm_application_io.is_none() { + self.wasm_application_io = Some(WasmApplicationIo::new().await); } + let editor_api = WasmEditorApi { font_cache: &self.font_cache, - application_io: self.wasm_io.as_ref().unwrap(), - node_graph_message_sender: &self.sender, imaginate_preferences: &self.imaginate_preferences, + application_io: self.wasm_application_io.as_ref().unwrap(), + node_graph_message_sender: &self.sender, render_config, image_frame: None, }; - use std::collections::hash_map::DefaultHasher; - use std::hash::Hash; - use std::hash::Hasher; // Required to ensure that the appropriate protonodes are reinserted when the Editor API changes. let mut graph_input_hash = DefaultHasher::new(); editor_api.font_cache.hash(&mut graph_input_hash); @@ -243,85 +256,85 @@ impl NodeRuntime { Ok(result) } - /// Recomputes the thumbnails for the layers in the graph, modifying the state and updating the UI. - pub fn update_thumbnails(&mut self, responses: &mut VecDeque) { - let mut image_data: Vec<_> = Vec::new(); - self.thumbnails.retain(|id, _| self.monitor_nodes.iter().any(|node_path| node_path.contains(id))); - for node_path in &self.monitor_nodes { - let Some(node_id) = node_path.get(node_path.len() - 2).copied() else { + /// Updates state data + pub fn process_monitor_nodes(&mut self, responses: &mut VecDeque) { + // TODO: Consider optimizing this since it's currently O(m*n^2), with a sort it could be made O(m * n*log(n)) + self.thumbnail_renders.retain(|id, _| self.monitor_nodes.iter().any(|monitor_node_path| monitor_node_path.contains(id))); + + for monitor_node_path in &self.monitor_nodes { + // The monitor nodes are located within a document node, and are thus children in that network, so this gets the parent document node's ID + let Some(parent_network_node_id) = monitor_node_path.get(monitor_node_path.len() - 2).copied() else { warn!("Monitor node has invalid node id"); - continue; - }; - let Some(value) = self.executor.introspect(node_path).flatten() else { - warn!("Failed to introspect monitor node for thumbnail"); + continue; }; - let Some(io_data) = value.downcast_ref::>() else { + // Extract the monitor node's stored `GraphicElement` data. + let Some(introspected_data) = self.executor.introspect(monitor_node_path).flatten() else { + // TODO: Fix the root of the issue causing the spam of this warning (this at least temporarily disables it in release builds) + #[cfg(debug_assertions)] + warn!("Failed to introspect monitor node"); + continue; }; - let graphic_element = &io_data.output; - use graphene_core::renderer::*; - let bounds = graphic_element.bounding_box(DAffine2::IDENTITY); - let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::BlobUrl, bounds, true, false, false); - let mut render = SvgRender::new(); - graphic_element.render_svg(&mut render, &render_params); - let [min, max] = bounds.unwrap_or_default(); - render.format_svg(min, max); - let click_targets = self.click_targets.entry(node_id).or_default(); - click_targets.clear(); - // Add the graphic element data's click targets to the click targets vector - graphic_element.add_click_targets(click_targets); + // If this is `GraphicElement` data: + // Regenerate click targets and thumbnails for the layers in the graph, modifying the state and updating the UI. + if let Some(io_data) = introspected_data.downcast_ref::>() { + let graphic_element = &io_data.output; - let old_thumbnail = self.thumbnails.entry(node_id).or_default(); - if *old_thumbnail != render.svg { - responses.add(FrontendMessage::UpdateNodeThumbnail { - id: node_id, - value: render.svg.to_string(), - }); - *old_thumbnail = render.svg; + // UPDATE CLICK TARGETS + + // Get the previously stored click targets and wipe them out, then regenerate them + let click_targets = self.click_targets.entry(parent_network_node_id).or_default(); + click_targets.clear(); + graphic_element.add_click_targets(click_targets); + + // RENDER THUMBNAIL + + let bounds = graphic_element.bounding_box(DAffine2::IDENTITY); + + // Render the thumbnail from a `GraphicElement` into an SVG string + let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::Base64, bounds, true, false, false); + let mut render = SvgRender::new(); + graphic_element.render_svg(&mut render, &render_params); + + // And give the SVG a viewbox and outer ... wrapper tag + let [min, max] = bounds.unwrap_or_default(); + render.format_svg(min, max); + + // UPDATE FRONTEND THUMBNAIL + + let new_thumbnail_svg = render.svg; + let old_thumbnail_svg = self.thumbnail_renders.entry(parent_network_node_id).or_default(); + + if old_thumbnail_svg != &new_thumbnail_svg { + responses.add(FrontendMessage::UpdateNodeThumbnail { + id: parent_network_node_id, + value: new_thumbnail_svg.to_svg_string(), + }); + *old_thumbnail_svg = new_thumbnail_svg; + } } - let resize = Some(DVec2::splat(100.)); - image_data.extend(render.image_data.into_iter().filter_map(|(_, image)| NodeGraphExecutor::to_frontend_image_data(image, resize).ok())) - } - if !image_data.is_empty() { - responses.add(FrontendMessage::UpdateImageData { - document_id: DocumentId(0), - image_data, - }); - } - } - - pub fn update_upstream_transforms(&mut self) { - for node_path in &self.monitor_nodes { - let Some(node_id) = node_path.get(node_path.len() - 2).copied() else { - warn!("Monitor node has invalid node id"); - continue; - }; - let Some(value) = self.executor.introspect(node_path).flatten() else { - warn!("Failed to introspect monitor node for upstream transforms"); - continue; - }; - - fn try_downcast(value: &dyn std::any::Any) -> Option<(Footprint, DAffine2)> { - let io_data = value.downcast_ref::>()?; - let transform = io_data.output.transform(); - Some((io_data.input, transform)) + // If this is `VectorData`, `ImageFrame`, or `GraphicElement` data: + // Update the stored upstream transforms for this layer/node. + if let Some(transform) = { + fn try_downcast(value: &dyn std::any::Any) -> Option<(Footprint, DAffine2)> { + let io_data = value.downcast_ref::>()?; + let transform = io_data.output.transform(); + Some((io_data.input, transform)) + } + None.or_else(|| try_downcast::(introspected_data.as_ref())) + .or_else(|| try_downcast::>(introspected_data.as_ref())) + .or_else(|| try_downcast::(introspected_data.as_ref())) + } { + self.upstream_transforms.insert(parent_network_node_id, transform); } - - let Some(transform) = try_downcast::(value.as_ref()) - .or_else(|| try_downcast::>(value.as_ref())) - .or_else(|| try_downcast::(value.as_ref())) - else { - warn!("Failed to downcast transform input"); - continue; - }; - self.upstream_transforms.insert(node_id, transform); } } } + pub fn introspect_node(path: &[NodeId]) -> Option> { NODE_RUNTIME .try_with(|runtime| { @@ -384,15 +397,15 @@ impl Default for NodeGraphExecutor { impl NodeGraphExecutor { /// Execute the network by flattening it and creating a borrow stack. fn queue_execution(&self, network: NodeNetwork, render_config: RenderConfig) -> u64 { - let generation_id = generate_uuid(); - let request = GenerationRequest { + let execution_id = generate_uuid(); + let request = ExecutionRequest { graph: network, - generation_id, + execution_id, render_config, }; - self.sender.send(NodeRuntimeMessage::GenerationRequest(request)).expect("Failed to send generation request"); + self.sender.send(NodeRuntimeMessage::ExecutionRequest(request)).expect("Failed to send generation request"); - generation_id + execution_id } pub fn introspect_node(&self, path: &[NodeId]) -> Option> { @@ -429,36 +442,6 @@ impl NodeGraphExecutor { Some(extract_data(downcasted)) } - /// Encodes an image into a format using the image crate - fn encode_img(image: Image, resize: Option, format: image::ImageOutputFormat) -> Result<(Vec, (u32, u32)), String> { - use image::{ImageBuffer, Rgba}; - use std::io::Cursor; - - let (result_bytes, width, height) = image.to_flat_u8(); - - let mut output: ImageBuffer, _> = image::ImageBuffer::from_raw(width, height, result_bytes).ok_or_else(|| "Invalid image size".to_string())?; - if let Some(size) = resize { - let size = size.as_uvec2(); - if size.x > 0 && size.y > 0 { - output = image::imageops::resize(&output, size.x, size.y, image::imageops::Triangle); - } - } - let size = output.dimensions(); - let mut image_data: Vec = Vec::new(); - output.write_to(&mut Cursor::new(&mut image_data), format).map_err(|e| e.to_string())?; - Ok::<_, String>((image_data, size)) - } - - /// Generate a new [`FrontendImageData`] from the [`Image`]. - fn to_frontend_image_data(image: Image, resize: Option) -> Result { - let (image_data, _size) = Self::encode_img(image, resize, image::ImageOutputFormat::Bmp)?; - - let mime = "image/bmp".to_string(); - let image_data = std::sync::Arc::new(image_data); - - Ok(FrontendImageData { image_data, mime }) - } - /// Evaluates a node graph, computing the entire graph pub fn submit_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, viewport_resolution: UVec2) -> Result<(), String> { // Get the node graph layer @@ -466,7 +449,7 @@ impl NodeGraphExecutor { let render_config = RenderConfig { viewport: Footprint { - transform: document.metadata.document_to_viewport, + transform: document.document_metadata.document_to_viewport, resolution: viewport_resolution, ..Default::default() }, @@ -480,9 +463,9 @@ impl NodeGraphExecutor { }; // Execute the node graph - let generation_id = self.queue_execution(network, render_config); + let execution_id = self.queue_execution(network, render_config); - self.futures.insert(generation_id, ExecutionContext { export_config: None }); + self.futures.insert(execution_id, ExecutionContext { export_config: None }); Ok(()) } @@ -515,9 +498,9 @@ impl NodeGraphExecutor { export_config.size = size; // Execute the node graph - let generation_id = self.queue_execution(network, render_config); + let execution_id = self.queue_execution(network, render_config); let execution_context = ExecutionContext { export_config: Some(export_config) }; - self.futures.insert(generation_id, execution_context); + self.futures.insert(execution_id, execution_context); Ok(()) } @@ -552,89 +535,59 @@ impl NodeGraphExecutor { } pub fn poll_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, responses: &mut VecDeque) -> Result<(), String> { - let DocumentMessageHandler { - network: document_network, - metadata: document_metadata, - collapsed, - .. - } = document; - let results = self.receiver.try_iter().collect::>(); for response in results { match response { - NodeGraphUpdate::GenerationResponse(GenerationResponse { - generation_id, - result, - updates, - new_thumbnails, - new_click_targets, - new_upstream_transforms, - resolved_types, - node_graph_errors, - transform, - }) => { + NodeGraphUpdate::ExecutionResponse(execution_response) => { + let ExecutionResponse { + execution_id, + result, + responses: existing_responses, + new_click_targets, + new_upstream_transforms, + resolved_types, + node_graph_errors, + transform, + } = execution_response; + + responses.extend(existing_responses); responses.add(NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors }); - let node_graph_output = result.map_err(|e| format!("Node graph evaluation failed: {e:?}"))?; - let execution_context = self.futures.remove(&generation_id).ok_or_else(|| "Invalid generation ID".to_string())?; - - if let Some(export_config) = execution_context.export_config { - return self.export(node_graph_output, export_config, responses); - } - - for (&node_id, svg) in &new_thumbnails { - if !document_network.nodes.contains_key(&node_id) { - warn!("Missing node"); - continue; - } - let layer = LayerNodeIdentifier::new(node_id, document_network); - responses.add(FrontendMessage::UpdateDocumentLayerDetails { - data: LayerPanelEntry { - name: document_network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(), - tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() }, - layer_classification: if document_metadata.is_artboard(layer) { - LayerClassification::Artboard - } else if document_metadata.is_folder(layer) { - LayerClassification::Folder - } else { - LayerClassification::Layer - }, - expanded: layer.has_children(document_metadata) && !collapsed.contains(&layer), - selected: document_metadata.selected_layers_contains(layer), - parent_id: layer.parent(document_metadata).map(|parent| parent.to_node()), - id: node_id, - depth: layer.ancestors(document_metadata).count() - 1, - thumbnail: svg.to_string(), - }, - }); - } - document_metadata.update_transforms(new_upstream_transforms); - document_metadata.update_click_targets(new_click_targets); - responses.extend(updates); - self.process_node_graph_output(node_graph_output, transform, responses)?; - responses.add(DocumentMessage::RenderDocument); - responses.add(DocumentMessage::DocumentStructureChanged); + responses.add(NodeGraphMessage::SendGraph); responses.add(BroadcastEvent::DocumentIsDirty); responses.add(OverlaysMessage::Draw); + + let node_graph_output = result.map_err(|e| format!("Node graph evaluation failed: {e:?}"))?; + + document.document_metadata.update_transforms(new_upstream_transforms); + document.document_metadata.update_click_targets(new_click_targets); + + let execution_context = self.futures.remove(&execution_id).ok_or_else(|| "Invalid generation ID".to_string())?; + if let Some(export_config) = execution_context.export_config { + // Special handling for exporting the artwork + self.export(node_graph_output, export_config, responses)? + } else { + self.process_node_graph_output(node_graph_output, transform, responses)? + } + } + NodeGraphUpdate::NodeGraphUpdateMessage(NodeGraphUpdateMessage::ImaginateStatusUpdate) => { + responses.add(DocumentMessage::PropertiesPanel(PropertiesPanelMessage::Refresh)); } - NodeGraphUpdate::NodeGraphUpdateMessage(NodeGraphUpdateMessage::ImaginateStatusUpdate) => responses.add(DocumentMessage::PropertiesPanel(PropertiesPanelMessage::Refresh)), } } Ok(()) } - fn render(render_object: impl GraphicElementRendered, transform: DAffine2, responses: &mut VecDeque) { - use graphene_core::renderer::{ImageRenderMode, RenderParams, SvgRender}; - + fn debug_render(render_object: impl GraphicElementRendered, transform: DAffine2, responses: &mut VecDeque) { // Setup rendering let mut render = SvgRender::new(); - let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::BlobUrl, None, false, false, false); + let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::Base64, None, false, false, false); // Render SVG render_object.render_svg(&mut render, &render_params); // Concatenate the defs and the SVG into one string render.wrap_with_transform(transform, None); - let svg = render.svg.to_string(); + let svg = render.svg.to_svg_string(); // Send to frontend responses.add(FrontendMessage::UpdateDocumentArtwork { svg }); @@ -667,16 +620,16 @@ impl NodeGraphExecutor { ); responses.add(FrontendMessage::UpdateDocumentArtwork { svg }); } - TaggedValue::Bool(render_object) => Self::render(render_object, transform, responses), - TaggedValue::String(render_object) => Self::render(render_object, transform, responses), - TaggedValue::F32(render_object) => Self::render(render_object, transform, responses), - TaggedValue::F64(render_object) => Self::render(render_object, transform, responses), - TaggedValue::OptionalColor(render_object) => Self::render(render_object, transform, responses), - TaggedValue::VectorData(render_object) => Self::render(render_object, transform, responses), - TaggedValue::GraphicGroup(render_object) => Self::render(render_object, transform, responses), - TaggedValue::Artboard(render_object) => Self::render(render_object, transform, responses), - TaggedValue::ImageFrame(render_object) => Self::render(render_object, transform, responses), - TaggedValue::Palette(render_object) => Self::render(render_object, transform, responses), + TaggedValue::Bool(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::String(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::F32(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::F64(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::OptionalColor(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::VectorData(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::GraphicGroup(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::Artboard(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::ImageFrame(render_object) => Self::debug_render(render_object, transform, responses), + TaggedValue::Palette(render_object) => Self::debug_render(render_object, transform, responses), _ => { return Err(format!("Invalid node graph output type: {node_graph_output:#?}")); } diff --git a/frontend/src-tauri/src/main.rs b/frontend/src-tauri/src/main.rs index f53cbf3c..ae5f0b8f 100644 --- a/frontend/src-tauri/src/main.rs +++ b/frontend/src-tauri/src/main.rs @@ -1,7 +1,6 @@ #![cfg_attr(all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows")] use graphite_editor::application::Editor; -use graphite_editor::messages::frontend::utility_types::FrontendImageData; use graphite_editor::messages::prelude::*; // use axum::body::StreamBody; @@ -90,32 +89,15 @@ fn handle_message(message: String) -> String { editor.as_mut().unwrap().handle_message(message) }); - // Sends a FrontendMessage to JavaScript - fn send_frontend_message_to_js(message: FrontendMessage) -> FrontendMessage { - // Special case for update image data to avoid serialization times. - if let FrontendMessage::UpdateImageData { document_id, image_data } = message { - let mut stub_data = Vec::with_capacity(image_data.len()); - for image in image_data { - stub_data.push(FrontendImageData { - mime: image.mime.clone(), - image_data: Arc::new(Vec::new()), - }); - } - FrontendMessage::UpdateImageData { document_id, image_data: stub_data } - } else { - message - } - } - for response in &responses { - let serialized = ron::to_string(&send_frontend_message_to_js(response.clone())).unwrap(); + let serialized = ron::to_string(&response.clone()).unwrap(); if let Err(error) = ron::from_str::(&serialized) { log::error!("Error deserializing message: {error}"); } } // Process any `FrontendMessage` responses resulting from the backend processing the dispatched message - let result: Vec<_> = responses.into_iter().map(send_frontend_message_to_js).collect(); + let result: Vec<_> = responses.into_iter().collect(); ron::to_string(&result).expect("Failed to serialize FrontendMessage") } diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index c0f88b4e..373cacad 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -2,10 +2,11 @@ import { getContext, onMount, tick } from "svelte"; import { beginDraggingElement } from "@graphite/io-managers/drag"; + import type { NodeGraphState } from "@graphite/state-providers/node-graph"; import { platformIsMac } from "@graphite/utility-functions/platform"; import type { Editor } from "@graphite/wasm-communication/editor"; import { defaultWidgetLayout, patchWidgetLayout, UpdateDocumentLayerDetails, UpdateDocumentLayerStructureJs, UpdateLayersPanelOptionsLayout } from "@graphite/wasm-communication/messages"; - import type { LayerClassification, LayerPanelEntry } from "@graphite/wasm-communication/messages"; + import type { DataBuffer, LayerClassification, LayerPanelEntry } from "@graphite/wasm-communication/messages"; import LayoutCol from "@graphite/components/layout/LayoutCol.svelte"; import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; @@ -20,8 +21,6 @@ entry: LayerPanelEntry; }; - let list: LayoutCol | undefined; - const RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT = 20; const INSERT_MARK_OFFSET = 2; @@ -35,6 +34,9 @@ }; const editor = getContext("editor"); + const nodeGraph = getContext("nodeGraph"); + + let list: LayoutCol | undefined; // Layer data let layerCache = new Map(); // TODO: replace with BigUint64Array as index @@ -56,7 +58,8 @@ }); editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerStructureJs, (updateDocumentLayerStructure) => { - rebuildLayerHierarchy(updateDocumentLayerStructure); + const structure = newUpdateDocumentLayerStructure(updateDocumentLayerStructure.dataBuffer); + rebuildLayerHierarchy(structure); }); editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (updateDocumentLayerDetails) => { @@ -67,6 +70,65 @@ }); }); + type DocumentLayerStructure = { + layerId: bigint; + children: DocumentLayerStructure[]; + }; + + function newUpdateDocumentLayerStructure(dataBuffer: DataBuffer): DocumentLayerStructure { + const pointerNum = Number(dataBuffer.pointer); + const lengthNum = Number(dataBuffer.length); + + const wasmMemoryBuffer = editor.raw.buffer; + + // Decode the folder structure encoding + const encoding = new DataView(wasmMemoryBuffer, pointerNum, lengthNum); + + // The structure section indicates how to read through the upcoming layer list and assign depths to each layer + const structureSectionLength = Number(encoding.getBigUint64(0, true)); + const structureSectionMsbSigned = new DataView(wasmMemoryBuffer, pointerNum + 8, structureSectionLength * 8); + + // The layer IDs section lists each layer ID sequentially in the tree, as it will show up in the panel + const layerIdsSection = new DataView(wasmMemoryBuffer, pointerNum + 8 + structureSectionLength * 8); + + let layersEncountered = 0; + let currentFolder: DocumentLayerStructure = { layerId: BigInt(-1), children: [] }; + const currentFolderStack = [currentFolder]; + + for (let i = 0; i < structureSectionLength; i += 1) { + const msbSigned = structureSectionMsbSigned.getBigUint64(i * 8, true); + const msbMask = BigInt(1) << BigInt(64 - 1); + + // Set the MSB to 0 to clear the sign and then read the number as usual + const numberOfLayersAtThisDepth = msbSigned & ~msbMask; + + // Store child folders in the current folder (until we are interrupted by an indent) + for (let j = 0; j < numberOfLayersAtThisDepth; j += 1) { + const layerId = layerIdsSection.getBigUint64(layersEncountered * 8, true); + layersEncountered += 1; + + const childLayer: DocumentLayerStructure = { layerId, children: [] }; + currentFolder.children.push(childLayer); + } + + // Check the sign of the MSB, where a 1 is a negative (outward) indent + const subsequentDirectionOfDepthChange = (msbSigned & msbMask) === BigInt(0); + // Inward + if (subsequentDirectionOfDepthChange) { + currentFolderStack.push(currentFolder); + currentFolder = currentFolder.children[currentFolder.children.length - 1]; + } + // Outward + else { + const popped = currentFolderStack.pop(); + if (!popped) throw Error("Too many negative indents in the folder structure"); + if (popped) currentFolder = popped; + } + } + + return currentFolder; + } + function toggleLayerVisibility(id: bigint) { editor.instance.toggleLayerVisibility(id); } @@ -222,11 +284,11 @@ async function dragStart(event: DragEvent, listing: LayerListingInfo) { const layer = listing.entry; dragInPanel = true; - if (!layer.selected) { + if (!$nodeGraph.selected.includes(layer.id)) { fakeHighlight = layer.id; } const select = () => { - if (!layer.selected) selectLayer(listing, false, false); + if (!$nodeGraph.selected.includes(layer.id)) selectLayer(listing, false, false); }; const target = (event.target instanceof HTMLElement && event.target) || undefined; @@ -263,7 +325,7 @@ dragInPanel = false; } - function rebuildLayerHierarchy(updateDocumentLayerStructure: UpdateDocumentLayerStructureJs) { + function rebuildLayerHierarchy(updateDocumentLayerStructure: DocumentLayerStructure) { const layerWithNameBeingEdited = layers.find((layer: LayerListingInfo) => layer.editingName); const layerIdWithNameBeingEdited = layerWithNameBeingEdited?.entry.id; @@ -271,7 +333,7 @@ layers = []; // Build the new layer hierarchy - const recurse = (folder: UpdateDocumentLayerStructureJs) => { + const recurse = (folder: DocumentLayerStructure) => { folder.children.forEach((item, index) => { const mapping = layerCache.get(String(item.layerId)); if (mapping) { @@ -313,7 +375,7 @@ - {@html listing.entry.thumbnail} + {#if $nodeGraph.thumbnails.has(listing.entry.id)} + {@html $nodeGraph.thumbnails.get(listing.entry.id)} + {/if} {/if} onEditLayerName(listing)}> @@ -353,8 +417,8 @@ class={"visibility"} action={(e) => (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())} size={24} - icon={(() => true)() ? "EyeVisible" : "EyeHidden"} - tooltip={(() => true)() ? "Visible" : "Hidden"} + icon={listing.entry.disabled ? "EyeHidden" : "EyeVisible"} + tooltip={listing.entry.disabled ? "Disabled" : "Enabled"} /> {/each} diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index a3571920..d0336bf0 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -1,15 +1,15 @@
(node.isLayer ? [{ node, nodeIndex }] : [])) as { node, nodeIndex } (nodeIndex)} {@const clipPathId = String(Math.random()).substring(2)} {@const stackDataInput = node.exposedInputs[0]} + {@const extraWidthToReachGridMultiple = 8} + {@const labelWidthGridCells = Math.ceil(((layerNameLabelWidths?.[String(node.id)] || 0) - extraWidthToReachGridMultiple) / 24)}
{#if node.errors} @@ -709,19 +772,24 @@ bind:this={inputs[nodeIndex][0]} > {#if node.primaryInput} - {dataTypeTooltip(node.primaryInput)} + {`${dataTypeTooltip(node.primaryInput)}\nConnected to ${node.primaryInput?.connected || "nothing"}`} + {/if} + {#if node.primaryInput?.connected} + + {:else} + {/if} -
{#if $nodeGraph.thumbnails.has(node.id)} {@html $nodeGraph.thumbnails.get(node.id)} {/if} + {#if node.primaryOutput} - {dataTypeTooltip(node.primaryOutput)} - + {`${dataTypeTooltip(node.primaryOutput)}\nConnected to ${node.primaryOutput.connected || "nothing"}`} + {#if node.primaryOutput.connected} + + {#if $nodeGraph.nodes.find((n) => n.id === node.primaryOutput?.connected)?.isLayer} + + {/if} + {:else} + + {/if} {/if} + - {dataTypeTooltip(stackDataInput)} - + {`${dataTypeTooltip(stackDataInput)}\nConnected to ${stackDataInput.connected || "nothing"}`} + {#if stackDataInput.connected} + + {#if $nodeGraph.nodes.find((n) => n.id === stackDataInput.connected)?.isLayer} + + {/if} + {:else} + + {/if}
- {node.alias || "Layer"} + + {node.alias || "Layer"} +
+ (toggleLayerVisibility(node.id), e?.stopPropagation())} + size={24} + icon={node.disabled ? "EyeHidden" : "EyeVisible"} + tooltip={node.disabled ? "Disabled" : "Enabled"} + /> - + + @@ -767,11 +860,11 @@ {@const clipPathId = String(Math.random()).substring(2)}
- {dataTypeTooltip(node.primaryInput)} - + {`${dataTypeTooltip(node.primaryInput)}\nConnected to ${node.primaryInput.connected || "nothing"}`} + {#if node.primaryInput.connected} + + {:else} + + {/if} {/if} {#each node.exposedInputs as parameter, index} @@ -826,8 +923,12 @@ style:--data-color-dim={`var(--color-data-${parameter.dataType}-dim)`} bind:this={inputs[nodeIndex][index + 1]} > - {dataTypeTooltip(parameter)} - + {`${dataTypeTooltip(parameter)}\nConnected to ${parameter.connected || "nothing"}`} + {#if parameter.connected} + + {:else} + + {/if} {/if} {/each} @@ -845,8 +946,12 @@ style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType}-dim)`} bind:this={outputs[nodeIndex][0]} > - {dataTypeTooltip(node.primaryOutput)} - + {`${dataTypeTooltip(node.primaryOutput)}\nConnected to ${node.primaryOutput.connected || "nothing"}`} + {#if node.primaryOutput.connected} + + {:else} + + {/if} {/if} {#each node.exposedOutputs as parameter, outputIndex} @@ -860,8 +965,12 @@ style:--data-color-dim={`var(--color-data-${parameter.dataType}-dim)`} bind:this={outputs[nodeIndex][outputIndex + (node.primaryOutput ? 1 : 0)]} > - {dataTypeTooltip(parameter)} - + {`${dataTypeTooltip(parameter)}\nConnected to ${parameter.connected || "nothing"}`} + {#if parameter.connected} + + {:else} + + {/if} {/each}
@@ -1097,7 +1206,6 @@ } .port { - fill: var(--data-color); // Double the intended value because of margin collapsing, but for the first and last we divide it by two as intended margin: calc(24px - 8px) 0; width: 8px; @@ -1112,7 +1220,10 @@ .layer { border-radius: 8px; - width: 216px; + --half-visibility-button: 12px; + --extra-width-to-reach-grid-multiple: 8px; + // Keep this equation in sync with the equivalent one in the Svelte template `` above + width: calc(36px + 72px + 8px + 24px * Max(3, var(--label-width)) + 8px + var(--half-visibility-button) + var(--extra-width-to-reach-grid-multiple)); &::after { border: 1px solid var(--color-5-dullgray); @@ -1160,25 +1271,33 @@ margin: 0 auto; left: 0; right: 0; + height: 12px; &.top { - top: -9px; + top: -13px; } &.bottom { - bottom: -9px; + bottom: -13px; } } } .details { - margin-left: 12px; + margin: 0 8px; - .text-label { + span { + white-space: nowrap; line-height: 48px; } } + .visibility { + position: absolute; + right: calc(-1 * var(--half-visibility-button)); + } + + .visibility, .input.ports, .input.ports .port { position: absolute; @@ -1186,6 +1305,10 @@ top: 0; bottom: 0; } + + .input.ports .port { + left: 24px; + } } .node { diff --git a/frontend/src/state-providers/node-graph.ts b/frontend/src/state-providers/node-graph.ts index 83c756e7..d35c3565 100644 --- a/frontend/src/state-providers/node-graph.ts +++ b/frontend/src/state-providers/node-graph.ts @@ -1,7 +1,16 @@ import { writable } from "svelte/store"; import { type Editor } from "@graphite/wasm-communication/editor"; -import { type FrontendNode, type FrontendNodeLink, type FrontendNodeType, UpdateNodeGraph, UpdateNodeTypes, UpdateNodeThumbnail, UpdateZoomWithScroll } from "@graphite/wasm-communication/messages"; +import { + type FrontendNode, + type FrontendNodeLink, + type FrontendNodeType, + UpdateNodeGraph, + UpdateNodeTypes, + UpdateNodeThumbnail, + UpdateZoomWithScroll, + UpdateNodeGraphSelection, +} from "@graphite/wasm-communication/messages"; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function createNodeGraphState(editor: Editor) { @@ -11,6 +20,7 @@ export function createNodeGraphState(editor: Editor) { nodeTypes: [] as FrontendNodeType[], zoomWithScroll: false as boolean, thumbnails: new Map(), + selected: [] as bigint[], }); // Set up message subscriptions on creation @@ -19,6 +29,7 @@ export function createNodeGraphState(editor: Editor) { state.nodes = updateNodeGraph.nodes; state.links = updateNodeGraph.links; const newThumbnails = new Map(); + // Transfer over any preexisting thumbnails from itself state.nodes.forEach((node) => { const thumbnail = state.thumbnails.get(node.id); if (thumbnail) newThumbnails.set(node.id, thumbnail); @@ -45,6 +56,12 @@ export function createNodeGraphState(editor: Editor) { return state; }); }); + editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSelection, (updateNodeGraphSelection) => { + update((state) => { + state.selected = updateNodeGraphSelection.selected; + return state; + }); + }); return { subscribe, diff --git a/frontend/src/state-providers/portfolio.ts b/frontend/src/state-providers/portfolio.ts index 46c14da0..80182ccd 100644 --- a/frontend/src/state-providers/portfolio.ts +++ b/frontend/src/state-providers/portfolio.ts @@ -17,7 +17,6 @@ import { TriggerOpenDocument, TriggerRevokeBlobUrl, UpdateActiveDocument, - UpdateImageData, UpdateOpenDocumentsList, } from "@graphite/wasm-communication/messages"; @@ -100,21 +99,6 @@ export function createPortfolioState(editor: Editor) { // Fail silently if there's an error rasterizing the SVG, such as a zero-sized image } }); - editor.subscriptions.subscribeJsMessage(UpdateImageData, (updateImageData) => { - updateImageData.imageData.forEach(async (element) => { - const buffer = new Uint8Array(element.imageData.values()).buffer; - const blob = new Blob([buffer], { type: element.mime }); - - const blobURL = URL.createObjectURL(blob); - - // Pre-decode the image so it is ready to be drawn instantly once it's placed into the viewport SVG - const image = new Image(); - image.src = blobURL; - await image.decode(); - - // editor.instance.setImageBlobURL(updateImageData.documentId, element.path, element.nodeId, blobURL, image.naturalWidth, image.naturalHeight, element.transform); - }); - }); editor.subscriptions.subscribeJsMessage(TriggerRevokeBlobUrl, async (triggerRevokeBlobUrl) => { URL.revokeObjectURL(triggerRevokeBlobUrl.url); }); diff --git a/frontend/src/wasm-communication/messages.ts b/frontend/src/wasm-communication/messages.ts index e86c89bc..c202096a 100644 --- a/frontend/src/wasm-communication/messages.ts +++ b/frontend/src/wasm-communication/messages.ts @@ -90,6 +90,8 @@ export class FrontendGraphInput { readonly name!: string; readonly resolvedType!: string | undefined; + + readonly connected!: bigint | undefined; } export class FrontendGraphOutput { @@ -98,6 +100,8 @@ export class FrontendGraphOutput { readonly name!: string; readonly resolvedType!: string | undefined; + + readonly connected!: bigint | undefined; } export class FrontendNode { @@ -566,72 +570,13 @@ export class TriggerSavePreferences extends JsMessage { export class DocumentChanged extends JsMessage {} -export class UpdateDocumentLayerStructureJs extends JsMessage { - constructor( - readonly layerId: bigint, - readonly children: UpdateDocumentLayerStructureJs[], - ) { - super(); - } -} - -type DataBuffer = { +export type DataBuffer = { pointer: bigint; length: bigint; }; -export function newUpdateDocumentLayerStructure(input: { dataBuffer: DataBuffer }, wasm: WasmRawInstance): UpdateDocumentLayerStructureJs { - const pointerNum = Number(input.dataBuffer.pointer); - const lengthNum = Number(input.dataBuffer.length); - - const wasmMemoryBuffer = wasm.buffer; - - // Decode the folder structure encoding - const encoding = new DataView(wasmMemoryBuffer, pointerNum, lengthNum); - - // The structure section indicates how to read through the upcoming layer list and assign depths to each layer - const structureSectionLength = Number(encoding.getBigUint64(0, true)); - const structureSectionMsbSigned = new DataView(wasmMemoryBuffer, pointerNum + 8, structureSectionLength * 8); - - // The layer IDs section lists each layer ID sequentially in the tree, as it will show up in the panel - const layerIdsSection = new DataView(wasmMemoryBuffer, pointerNum + 8 + structureSectionLength * 8); - - let layersEncountered = 0; - let currentFolder = new UpdateDocumentLayerStructureJs(BigInt(-1), []); - const currentFolderStack = [currentFolder]; - - for (let i = 0; i < structureSectionLength; i += 1) { - const msbSigned = structureSectionMsbSigned.getBigUint64(i * 8, true); - const msbMask = BigInt(1) << BigInt(64 - 1); - - // Set the MSB to 0 to clear the sign and then read the number as usual - const numberOfLayersAtThisDepth = msbSigned & ~msbMask; - - // Store child folders in the current folder (until we are interrupted by an indent) - for (let j = 0; j < numberOfLayersAtThisDepth; j += 1) { - const layerId = layerIdsSection.getBigUint64(layersEncountered * 8, true); - layersEncountered += 1; - - const childLayer = new UpdateDocumentLayerStructureJs(layerId, []); - currentFolder.children.push(childLayer); - } - - // Check the sign of the MSB, where a 1 is a negative (outward) indent - const subsequentDirectionOfDepthChange = (msbSigned & msbMask) === BigInt(0); - // Inward - if (subsequentDirectionOfDepthChange) { - currentFolderStack.push(currentFolder); - currentFolder = currentFolder.children[currentFolder.children.length - 1]; - } - // Outward - else { - const popped = currentFolderStack.pop(); - if (!popped) throw Error("Too many negative indents in the folder structure"); - if (popped) currentFolder = popped; - } - } - - return currentFolder; +export class UpdateDocumentLayerStructureJs extends JsMessage { + readonly dataBuffer!: DataBuffer; } export class DisplayEditableTextbox extends JsMessage { @@ -653,13 +598,6 @@ export class DisplayEditableTextboxTransform extends JsMessage { readonly transform!: number[]; } -export class UpdateImageData extends JsMessage { - readonly documentId!: bigint; - - @Type(() => FrontendImageData) - readonly imageData!: FrontendImageData[]; -} - export class DisplayRemoveEditableTextbox extends JsMessage {} export class UpdateDocumentLayerDetails extends JsMessage { @@ -675,28 +613,20 @@ export class LayerPanelEntry { layerClassification!: LayerClassification; + expanded!: boolean; + + disabled!: boolean; + parentId!: bigint | undefined; id!: bigint; @Transform(({ value }: { value: bigint }) => Number(value)) depth!: number; - - expanded!: boolean; - - selected!: boolean; - - thumbnail!: string; } export type LayerClassification = "Folder" | "Artboard" | "Layer"; -export class FrontendImageData { - readonly mime!: string; - - readonly imageData!: Uint8Array; -} - export class DisplayDialogDismiss extends JsMessage {} export class Font { @@ -1373,12 +1303,11 @@ export const messageMakers: Record = { UpdateDocumentArtwork, UpdateDocumentBarLayout, UpdateDocumentLayerDetails, - UpdateDocumentLayerStructureJs: newUpdateDocumentLayerStructure, + UpdateDocumentLayerStructureJs, UpdateDocumentModeLayout, UpdateDocumentRulers, UpdateDocumentScrollbars, UpdateEyedropperSamplingState, - UpdateImageData, UpdateInputHints, UpdateLayersPanelOptionsLayout, UpdateMenuBarLayout, diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 60afc9f7..db98e011 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -159,27 +159,6 @@ impl JsEditorHandle { // Sends a FrontendMessage to JavaScript fn send_frontend_message_to_js(&self, mut message: FrontendMessage) { - // Special case for update image data to avoid serialization times. - if let FrontendMessage::UpdateImageData { document_id: _, image_data: _ } = message { - // for image in image_data { - // #[cfg(not(feature = "tauri"))] - // { - // let transform = if let Some(transform_val) = image.transform { - // let transform = js_sys::Float64Array::new_with_length(6); - // transform.copy_from(&transform_val); - // transform - // } else { - // js_sys::Float64Array::default() - // }; - // } - // #[cfg(feature = "tauri")] - // { - // let identifier = format!("http://localhost:3001/image/{:?}_{}", image.path, document_id); - // fetchImage(image.path.clone(), image.node_id, image.mime, document_id, identifier); - // } - // } - return; - } if let FrontendMessage::UpdateDocumentLayerStructure { data_buffer } = message { message = FrontendMessage::UpdateDocumentLayerStructureJs { data_buffer: data_buffer.into() }; } @@ -633,9 +612,8 @@ impl JsEditorHandle { /// Notifies the backend that the user selected a node in the node graph #[wasm_bindgen(js_name = selectNodes)] - pub fn select_nodes(&self, nodes: Option>) { - let nodes = nodes.map(|nodes| nodes.into_iter().map(|id| NodeId(id)).collect::>()); - let nodes = nodes.unwrap_or_default(); + pub fn select_nodes(&self, nodes: Vec) { + let nodes = nodes.into_iter().map(|id| NodeId(id)).collect::>(); let message = NodeGraphMessage::SelectedNodesSet { nodes }; self.dispatch(message); } @@ -648,10 +626,10 @@ impl JsEditorHandle { } /// Notifies the backend that the user double clicked a node - #[wasm_bindgen(js_name = doubleClickNode)] - pub fn double_click_node(&self, node: u64) { + #[wasm_bindgen(js_name = enterNestedNetwork)] + pub fn enter_nested_network(&self, node: u64) { let node = NodeId(node); - let message = NodeGraphMessage::DoubleClickNode { node }; + let message = NodeGraphMessage::EnterNestedNetwork { node }; self.dispatch(message); } diff --git a/node-graph/gcore/src/application_io.rs b/node-graph/gcore/src/application_io.rs index b409f91b..e98eacf8 100644 --- a/node-graph/gcore/src/application_io.rs +++ b/node-graph/gcore/src/application_io.rs @@ -158,6 +158,7 @@ pub struct RenderConfig { } pub struct EditorApi<'a, Io> { + // TODO: Is `image_frame` still used? I think it's only ever set to None. pub image_frame: Option>, pub font_cache: &'a FontCache, pub application_io: &'a Io, diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index 0f063eb9..a961f03b 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -60,7 +60,7 @@ impl ClickTarget { /// Mutable state used whilst rendering to an SVG pub struct SvgRender { - pub svg: SvgSegmentList, + pub svg: Vec, pub svg_defs: String, pub transform: DAffine2, pub image_data: Vec<(u64, Image)>, @@ -70,7 +70,7 @@ pub struct SvgRender { impl SvgRender { pub fn new() -> Self { Self { - svg: SvgSegmentList::default(), + svg: Vec::default(), svg_defs: String::new(), transform: DAffine2::IDENTITY, image_data: Vec::new(), @@ -79,8 +79,8 @@ impl SvgRender { } pub fn indent(&mut self) { - self.svg.push("\n"); - self.svg.push("\t".repeat(self.indent)); + self.svg.push("\n".into()); + self.svg.push("\t".repeat(self.indent).into()); } /// Add an outer `...` tag with a `viewBox` and the `` @@ -90,7 +90,7 @@ impl SvgRender { let defs = &self.svg_defs; let svg_header = format!(r#"{defs}"#,); self.svg.insert(0, svg_header.into()); - self.svg.push(""); + self.svg.push("".into()); } /// Wraps the SVG with `...`, which allows for rotation @@ -106,43 +106,43 @@ impl SvgRender { format_transform_matrix(transform) ); self.svg.insert(0, svg_header.into()); - self.svg.push(""); + self.svg.push("".into()); } pub fn leaf_tag(&mut self, name: impl Into, attributes: impl FnOnce(&mut SvgRenderAttrs)) { self.indent(); - self.svg.push("<"); - self.svg.push(name); + self.svg.push("<".into()); + self.svg.push(name.into()); attributes(&mut SvgRenderAttrs(self)); - self.svg.push("/>"); + self.svg.push("/>".into()); } pub fn leaf_node(&mut self, content: impl Into) { self.indent(); - self.svg.push(content); + self.svg.push(content.into()); } pub fn parent_tag(&mut self, name: impl Into, attributes: impl FnOnce(&mut SvgRenderAttrs), inner: impl FnOnce(&mut Self)) { let name = name.into(); self.indent(); - self.svg.push("<"); + self.svg.push("<".into()); self.svg.push(name.clone()); // Wraps `self` in a newtype (1-tuple) which is then mutated by the `attributes` closure attributes(&mut SvgRenderAttrs(self)); - self.svg.push(">"); + self.svg.push(">".into()); let length = self.svg.len(); self.indent += 1; inner(self); self.indent -= 1; if self.svg.len() != length { self.indent(); - self.svg.push(""); + self.svg.push(">".into()); } else { self.svg.pop(); - self.svg.push("/>"); + self.svg.push("/>".into()); } } } @@ -154,8 +154,6 @@ impl Default for SvgRender { } pub enum ImageRenderMode { - BlobUrl, - Canvas, Base64, } @@ -209,10 +207,10 @@ pub trait GraphicElementRendered { fn add_click_targets(&self, click_targets: &mut Vec); fn to_usvg_node(&self) -> usvg::Node { let mut render = SvgRender::new(); - let render_params = RenderParams::new(crate::vector::style::ViewMode::Normal, ImageRenderMode::BlobUrl, None, false, false, false); + let render_params = RenderParams::new(crate::vector::style::ViewMode::Normal, ImageRenderMode::Base64, None, false, false, false); self.render_svg(&mut render, &render_params); render.format_svg(DVec2::ZERO, DVec2::ONE); - let svg = render.svg.to_string(); + let svg = render.svg.to_svg_string(); let opt = usvg::Options::default(); @@ -384,7 +382,7 @@ impl GraphicElementRendered for Artboard { }, |render| { // TODO: Use the artboard's layer name - render.svg.push("Artboard"); + render.svg.push("Artboard".into()); }, ); } @@ -448,22 +446,8 @@ impl GraphicElementRendered for Artboard { impl GraphicElementRendered for ImageFrame { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { let transform: String = format_transform_matrix(self.transform * render.transform); - let uuid = generate_uuid(); match render_params.image_render_mode { - ImageRenderMode::BlobUrl => { - render.leaf_tag("image", move |attributes| { - attributes.push("width", 1.to_string()); - attributes.push("height", 1.to_string()); - attributes.push("preserveAspectRatio", "none"); - attributes.push("transform", transform); - attributes.push("href", SvgSegment::BlobUrl(uuid)); - if self.alpha_blending.blend_mode != BlendMode::default() { - attributes.push("style", self.alpha_blending.blend_mode.render()); - } - }); - render.image_data.push((uuid, self.image.clone())) - } ImageRenderMode::Base64 => { let image = &self.image; if image.data.is_empty() { @@ -486,9 +470,6 @@ impl GraphicElementRendered for ImageFrame { } }); } - ImageRenderMode::Canvas => { - todo!("Canvas rendering is not yet implemented") - } } } @@ -683,34 +664,21 @@ impl From<&'static str> for SvgSegment { } } -/// A list of [`SvgSegment`]s. -/// -/// Can be modified with `list.push("hello".into())`. Use `list.to_string()` to convert the segments into one string. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct SvgSegmentList(Vec); - -impl core::ops::Deref for SvgSegmentList { - type Target = Vec; - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl core::ops::DerefMut for SvgSegmentList { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } +pub trait RenderSvgSegmentList { + fn to_svg_string(&self) -> String; } -impl core::fmt::Display for SvgSegmentList { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { +impl RenderSvgSegmentList for Vec { + fn to_svg_string(&self) -> String { + let mut result = String::new(); for segment in self.iter() { - f.write_str(match segment { + result.push_str(match segment { SvgSegment::Slice(x) => x, SvgSegment::String(x) => x, SvgSegment::BlobUrl(_) => "", - })?; + }); } - Ok(()) + result } } @@ -718,22 +686,16 @@ pub struct SvgRenderAttrs<'a>(&'a mut SvgRender); impl<'a> SvgRenderAttrs<'a> { pub fn push_complex(&mut self, name: impl Into, value: impl FnOnce(&mut SvgRender)) { - self.0.svg.push(" "); - self.0.svg.push(name); - self.0.svg.push("=\""); + self.0.svg.push(" ".into()); + self.0.svg.push(name.into()); + self.0.svg.push("=\"".into()); value(self.0); - self.0.svg.push("\""); + self.0.svg.push("\"".into()); } pub fn push(&mut self, name: impl Into, value: impl Into) { - self.push_complex(name, move |renderer| renderer.svg.push(value)); + self.push_complex(name, move |renderer| renderer.svg.push(value.into())); } pub fn push_val(&mut self, value: impl Into) { - self.0.svg.push(value); - } -} - -impl SvgSegmentList { - pub fn push(&mut self, value: impl Into) { - self.0.push(value.into()); + self.0.svg.push(value.into()); } } diff --git a/node-graph/gcore/src/memo.rs b/node-graph/gcore/src/memo.rs index bb6a6e97..81d0c4a4 100644 --- a/node-graph/gcore/src/memo.rs +++ b/node-graph/gcore/src/memo.rs @@ -45,6 +45,7 @@ impl MemoNode { } } +/// Stores both what a node was called with and what it returned. #[derive(Clone, Debug)] pub struct IORecord { pub input: I, diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 40150759..68aa0f87 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -347,6 +347,11 @@ impl DocumentNode { // 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) + } } /// Represents the possible inputs to a node. @@ -485,14 +490,19 @@ impl NodeOutput { #[derive(Clone, Debug, Default, PartialEq, DynAny)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -/// A network of nodes containing each [`DocumentNode`] and its ID, as well as a list of input nodes and [`NodeOutput`]s +/// A network (subgraph) of nodes containing each [`DocumentNode`] and its ID, as well as a list of input nodes and [`NodeOutput`]s pub struct NodeNetwork { + /// The list of nodes that are imported into this network from the parent network. Each is a reference to the node that the input is connected to. + /// Presently, only one is supported— use an Identity node to split an input to multiple user nodes (although this could be changed in the future). pub inputs: Vec, + /// The list of data outputs that are exported from this network to the parent network. Each is a reference to the node, and its output index, that is the source of the output data. pub outputs: Vec, + /// The list of all nodes in this network. pub nodes: HashMap, - /// These nodes are replaced with identity nodes during the graph flattening step + /// Nodes that the user has disabled/hidden with the visibility eye icon. + /// These nodes get replaced with Identity nodes during the graph flattening step. pub disabled: Vec, - /// In the case when a new node is chosen as a temporary output, this stores what it used to be so it can be restored later + /// In the case when another node is previewed (chosen by the user as a temporary output), this stores what it used to be so it can be restored later. pub previous_outputs: Option>, } diff --git a/node-graph/gstd/src/wasm_application_io.rs b/node-graph/gstd/src/wasm_application_io.rs index 0b5fbf81..0e8e44aa 100644 --- a/node-graph/gstd/src/wasm_application_io.rs +++ b/node-graph/gstd/src/wasm_application_io.rs @@ -2,7 +2,7 @@ use dyn_any::StaticType; use graphene_core::application_io::{ApplicationError, ApplicationIo, ExportFormat, RenderConfig, ResourceFuture, SurfaceHandle, SurfaceHandleFrame, SurfaceId}; use graphene_core::raster::Image; use graphene_core::raster::{color::SRGBA8, ImageFrame}; -use graphene_core::renderer::{format_transform_matrix, GraphicElementRendered, ImageRenderMode, RenderParams, SvgRender}; +use graphene_core::renderer::{format_transform_matrix, GraphicElementRendered, ImageRenderMode, RenderParams, RenderSvgSegmentList, SvgRender}; use graphene_core::transform::Footprint; use graphene_core::Color; use graphene_core::Node; @@ -303,7 +303,7 @@ fn render_svg(data: impl GraphicElementRendered, mut render: SvgRender, render_p data.render_svg(&mut render, &render_params); render.wrap_with_transform(footprint.transform, Some(footprint.resolution.as_dvec2())); - RenderOutput::Svg(render.svg.to_string()) + RenderOutput::Svg(render.svg.to_svg_string()) } #[cfg(any(feature = "resvg", feature = "vello"))] @@ -321,7 +321,7 @@ fn render_canvas( 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 string = render.svg.to_svg_string(); let array = string.as_bytes(); let canvas = &surface_handle.surface; canvas.set_width(resolution.x); diff --git a/website/content/learn/_index.md b/website/content/learn/_index.md index f10b4adc..bfaf935d 100644 --- a/website/content/learn/_index.md +++ b/website/content/learn/_index.md @@ -5,6 +5,7 @@ page_template = "book.html" [extra] book = true +js = ["video-embed.js"] +++