diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index bbe490b12f..58559c4311 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -80,97 +80,6 @@ jobs: npm run install-fonts zola --config config.toml build --minify -<<<<<<< HEAD - - name: 💿 Restore cache of `website/other/dist` directory, if available and `website/other` didn't change - if: steps.changes.outputs.website-other != 'true' - id: cache-website-other-dist - uses: actions/cache/restore@v3 - with: - path: website/other/dist - key: website-other-dist-${{ runner.os }} - - - name: 🟢 Set up Node only if we are going to build in the next step - if: steps.cache-website-other-dist.outputs.cache-hit != 'true' - uses: actions/setup-node@v4 - with: - node-version: "latest" - - - name: 📁 Build `website/other` directory only if changed or not cached - if: steps.cache-website-other-dist.outputs.cache-hit != 'true' - id: build-website-other - run: | - sh website/other/build.sh - - - name: 💾 Save cache of `website/other/dist` directory if it was built above - if: steps.cache-website-other-dist.outputs.cache-hit != 'true' - uses: actions/cache/save@v3 - with: - path: website/other/dist - key: ${{ steps.cache-website-other-dist.outputs.cache-primary-key }} - - - name: 🚚 Move `website/other/dist` contents to `website/public` - run: | - mkdir -p website/public - mv website/other/dist/* website/public - - - name: 💿 Obtain cache of auto-generated code docs artifacts - id: cache-website-code-docs - uses: actions/cache/restore@v3 - with: - path: artifacts - key: website-code-docs - - - name: 📁 Fallback in case auto-generated code docs artifacts weren't cached - if: steps.cache-website-code-docs.outputs.cache-hit != 'true' - run: | - echo "🦀 Initial system version of Rust:" - rustc --version - rustup update stable - echo "🦀 Latest updated version of Rust:" - rustc --version - cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree - mkdir artifacts - mv hierarchical_message_system_tree.txt artifacts/hierarchical_message_system_tree.txt - - - name: 🚚 Move `artifacts` contents to `website/public` - run: | - mv artifacts/* website/public - -||||||| 890da6a3 - - name: 💿 Restore cache of `website/other/dist` directory, if available and `website/other` didn't change - if: steps.changes.outputs.website-other != 'true' - id: cache-website-other-dist - uses: actions/cache/restore@v3 - with: - path: website/other/dist - key: website-other-dist-${{ runner.os }} - - - name: 🟢 Set up Node only if we are going to build in the next step - if: steps.cache-website-other-dist.outputs.cache-hit != 'true' - uses: actions/setup-node@v4 - with: - node-version: "latest" - - - name: 📁 Build `website/other` directory only if changed or not cached - if: steps.cache-website-other-dist.outputs.cache-hit != 'true' - id: build-website-other - run: | - sh website/other/build.sh - - - name: 💾 Save cache of `website/other/dist` directory if it was built above - if: steps.cache-website-other-dist.outputs.cache-hit != 'true' - uses: actions/cache/save@v3 - with: - path: website/other/dist - key: ${{ steps.cache-website-other-dist.outputs.cache-primary-key }} - - - name: 🚚 Move `website/other/dist` contents to `website/public` - run: | - mkdir -p website/public - mv website/other/dist/* website/public - -======= ->>>>>>> master - name: 📤 Publish to Cloudflare Pages id: cloudflare uses: cloudflare/pages-action@1 diff --git a/Cargo.lock b/Cargo.lock index 79c9f1ab8e..44494cbcae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "thiserror 1.0.69", ] @@ -914,6 +914,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -927,8 +937,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types 0.5.0", "libc", ] @@ -940,7 +950,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.9.3", + "core-foundation 0.10.1", "libc", ] @@ -2128,7 +2149,7 @@ dependencies = [ "bytemuck", "cef", "cef-dll-sys", - "core-foundation", + "core-foundation 0.9.4", "derivative", "dirs", "futures", @@ -3130,13 +3151,13 @@ dependencies = [ [[package]] name = "metal" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ "bitflags 2.9.3", "block", - "core-graphics-types", + "core-graphics-types 0.2.0", "foreign-types 0.5.0", "log", "objc", @@ -3188,26 +3209,27 @@ dependencies = [ [[package]] name = "naga" -version = "25.0.1" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632" +checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" dependencies = [ "arrayvec", "bit-set", "bitflags 2.9.3", + "cfg-if", "cfg_aliases", "codespan-reporting", "half", "hashbrown", "hexf-parse", "indexmap", + "libm", "log", "num-traits", "once_cell", "petgraph 0.8.2", "rustc-hash 1.1.0", "spirv", - "strum 0.26.3", "thiserror 2.0.16", "unicode-ident", ] @@ -3253,7 +3275,7 @@ dependencies = [ "bitflags 2.9.3", "jni-sys", "log", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "raw-window-handle", "thiserror 1.0.69", @@ -3265,15 +3287,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" -dependencies = [ - "jni-sys", -] - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -3313,7 +3326,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "strum 0.27.2", + "strum", "syn 2.0.106", ] @@ -3999,7 +4012,6 @@ dependencies = [ "fixedbitset", "hashbrown", "indexmap", - "serde", ] [[package]] @@ -4920,7 +4932,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.9.3", - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", @@ -5255,35 +5267,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - [[package]] name = "strum" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.106", + "strum_macros", ] [[package]] @@ -5386,7 +5376,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.3", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6092,14 +6082,14 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vello" version = "0.5.0" -source = "git+https://github.com/linebender/vello.git?rev=87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b#87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b" +source = "git+https://github.com/DJMcNab/vello.git?branch=luminance-mask-layer#2a6e445d9de397c1b552024cddfd8cb0d5881eaa" dependencies = [ "bytemuck", "futures-intrusive", "log", "peniko", "png 0.17.16", - "skrifa 0.31.3", + "skrifa 0.36.0", "static_assertions", "thiserror 2.0.16", "vello_encoding", @@ -6110,19 +6100,19 @@ dependencies = [ [[package]] name = "vello_encoding" version = "0.5.0" -source = "git+https://github.com/linebender/vello.git?rev=87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b#87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b" +source = "git+https://github.com/DJMcNab/vello.git?branch=luminance-mask-layer#2a6e445d9de397c1b552024cddfd8cb0d5881eaa" dependencies = [ "bytemuck", "guillotiere", "peniko", - "skrifa 0.31.3", + "skrifa 0.36.0", "smallvec", ] [[package]] name = "vello_shaders" version = "0.5.0" -source = "git+https://github.com/linebender/vello.git?rev=87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b#87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b" +source = "git+https://github.com/DJMcNab/vello.git?branch=luminance-mask-layer#2a6e445d9de397c1b552024cddfd8cb0d5881eaa" dependencies = [ "bytemuck", "log", @@ -6394,12 +6384,13 @@ checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "wgpu" -version = "25.0.2" +version = "26.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8fb398f119472be4d80bc3647339f56eb63b2a331f6a3d16e25d8144197dd9" +checksum = "70b6ff82bbf6e9206828e1a3178e851f8c20f1c9028e74dd3a8090741ccd5798" dependencies = [ "arrayvec", "bitflags 2.9.3", + "cfg-if", "cfg_aliases", "document-features", "hashbrown", @@ -6422,9 +6413,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "25.0.2" +version = "26.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b882196f8368511d613c6aeec80655160db6646aebddf8328879a88d54e500" +checksum = "d5f62f1053bd28c2268f42916f31588f81f64796e2ff91b81293515017ca8bd9" dependencies = [ "arrayvec", "bit-set", @@ -6454,27 +6445,27 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "25.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd488b3239b6b7b185c3b045c39ca6bf8af34467a4c5de4e0b1a564135d093d" +checksum = "18ae5fbde6a4cbebae38358aa73fcd6e0f15c6144b67ef5dc91ded0db125dbdf" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "25.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09ad7aceb3818e52539acc679f049d3475775586f3f4e311c30165cf2c00445" +checksum = "d7670e390f416006f746b4600fdd9136455e3627f5bd763abf9a65daa216dd2d" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "25.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba5fb5f7f9c98baa7c889d444f63ace25574833df56f5b817985f641af58e46" +checksum = "720a5cb9d12b3d337c15ff0e24d3e97ed11490ff3f7506e7f3d98c68fa5d6f14" dependencies = [ "wgpu-hal", ] @@ -6500,9 +6491,9 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "25.0.2" +version = "26.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17" +checksum = "7df2c64ac282a91ad7662c90bc4a77d4a2135bc0b2a2da5a4d4e267afc034b9e" dependencies = [ "android_system_properties", "arrayvec", @@ -6513,7 +6504,7 @@ dependencies = [ "bytemuck", "cfg-if", "cfg_aliases", - "core-graphics-types", + "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", "gpu-alloc", @@ -6527,11 +6518,12 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys 0.5.0+25.2.9519653", + "ndk-sys", "objc", "ordered-float", "parking_lot", "portable-atomic", + "portable-atomic-util", "profiling", "range-alloc", "raw-window-handle", @@ -6547,9 +6539,9 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "25.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aa49460c2a8ee8edba3fca54325540d904dd85b2e086ada762767e17d06e8bc" +checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2" dependencies = [ "bitflags 2.9.3", "bytemuck", @@ -6961,7 +6953,7 @@ dependencies = [ "calloop", "cfg_aliases", "concurrent-queue", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "cursor-icon", "dpi", diff --git a/Cargo.toml b/Cargo.toml index 94671aec4c..6a8db38de7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,7 +101,7 @@ quote = "1.0" chrono = "0.4" ron = "0.11" fastnoise-lite = "1.1" -wgpu = { version = "25.0", features = [ +wgpu = { version = "26.0.1", features = [ # We don't have wgpu on multiple threads (yet) https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#wgpu-types-now-send-sync-on-wasm "fragile-send-sync-non-atomic-wasm", "spirv", @@ -132,7 +132,7 @@ web-sys = { version = "=0.3.77", features = [ winit = { version = "0.30", features = ["wayland", "rwh_06"] } url = "2.5" tokio = { version = "1.29", features = ["fs", "macros", "io-std", "rt"] } -vello = { git = "https://github.com/linebender/vello.git", rev = "87cc5bee6d3a34d15017dbbb58634ddc7f33ff9b" } # TODO switch back to stable when a release is made +vello = { git = "https://github.com/DJMcNab/vello.git", branch = "luminance-mask-layer" } # TODO switch back to stable when a release is made resvg = "0.45" usvg = "0.45" rand = { version = "0.9", default-features = false, features = ["std_rng"] } diff --git a/editor/src/messages/portfolio/spreadsheet/spreadsheet_message_handler.rs b/editor/src/messages/portfolio/spreadsheet/spreadsheet_message_handler.rs deleted file mode 100644 index 185347a82e..0000000000 --- a/editor/src/messages/portfolio/spreadsheet/spreadsheet_message_handler.rs +++ /dev/null @@ -1,325 +0,0 @@ -use super::VectorDataDomain; -use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, LayoutTarget, WidgetLayout}; -use crate::messages::prelude::*; -use crate::messages::tool::tool_messages::tool_prelude::*; -use graph_craft::document::NodeId; -use graphene_std::Color; -use graphene_std::Context; -use graphene_std::GraphicGroupTable; -use graphene_std::instances::Instances; -use graphene_std::memo::IORecord; -use graphene_std::raster::Image; -use graphene_std::vector::{VectorData, VectorDataTable}; -use graphene_std::{Artboard, ArtboardGroupTable, GraphicElement}; -use std::any::Any; -use std::sync::Arc; - -/// The spreadsheet UI allows for instance data to be previewed. -#[derive(Default, Debug, Clone, ExtractField)] -pub struct SpreadsheetMessageHandler { - /// Sets whether or not the spreadsheet is drawn. - pub spreadsheet_view_open: bool, - inspect_node: Option, - introspected_data: Option>, - instances_path: Vec, - viewing_vector_data_domain: VectorDataDomain, -} - -#[message_handler_data] -impl MessageHandler for SpreadsheetMessageHandler { - fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque, _: ()) { - match message { - SpreadsheetMessage::ToggleOpen => { - self.spreadsheet_view_open = !self.spreadsheet_view_open; - // Run the graph to grab the data - if self.spreadsheet_view_open { - responses.add(NodeGraphMessage::RunDocumentGraph); - } - // Update checked UI state for open - responses.add(MenuBarMessage::SendLayout); - self.update_layout(responses); - } - - SpreadsheetMessage::UpdateLayout { mut inspect_result } => { - self.inspect_node = Some(inspect_result.inspect_node); - self.introspected_data = inspect_result.take_data(); - self.update_layout(responses) - } - - SpreadsheetMessage::PushToInstancePath { index } => { - self.instances_path.push(index); - self.update_layout(responses); - } - SpreadsheetMessage::TruncateInstancePath { len } => { - self.instances_path.truncate(len); - self.update_layout(responses); - } - - SpreadsheetMessage::ViewVectorDataDomain { domain } => { - self.viewing_vector_data_domain = domain; - self.update_layout(responses); - } - } - } - - fn actions(&self) -> ActionList { - actions!(SpreadsheetMessage;) - } -} - -impl SpreadsheetMessageHandler { - fn update_layout(&mut self, responses: &mut VecDeque) { - responses.add(FrontendMessage::UpdateSpreadsheetState { - node: self.inspect_node, - open: self.spreadsheet_view_open, - }); - if !self.spreadsheet_view_open { - return; - } - let mut layout_data = LayoutData { - current_depth: 0, - desired_path: &mut self.instances_path, - breadcrumbs: Vec::new(), - vector_data_domain: self.viewing_vector_data_domain, - }; - let mut layout = self - .introspected_data - .as_ref() - .map(|instrospected_data| generate_layout(instrospected_data, &mut layout_data)) - .unwrap_or_else(|| Some(label("No data"))) - .unwrap_or_else(|| label("Failed to downcast data")); - - if layout_data.breadcrumbs.len() > 1 { - let breadcrumb = BreadcrumbTrailButtons::new(layout_data.breadcrumbs) - .on_update(|&len| SpreadsheetMessage::TruncateInstancePath { len: len as usize }.into()) - .widget_holder(); - layout.insert(0, LayoutGroup::Row { widgets: vec![breadcrumb] }); - } - - responses.add(LayoutMessage::SendLayout { - layout: Layout::WidgetLayout(WidgetLayout { layout }), - layout_target: LayoutTarget::Spreadsheet, - }); - } -} - -struct LayoutData<'a> { - current_depth: usize, - desired_path: &'a mut Vec, - breadcrumbs: Vec, - vector_data_domain: VectorDataDomain, -} - -fn generate_layout(introspected_data: &Arc, data: &mut LayoutData) -> Option> { - // We simply try random types. TODO: better strategy. - #[allow(clippy::manual_map)] - if let Some(io) = introspected_data.downcast_ref::>() { - Some(io.output.layout_with_breadcrumb(data)) - } else if let Some(io) = introspected_data.downcast_ref::>() { - Some(io.output.layout_with_breadcrumb(data)) - } else if let Some(io) = introspected_data.downcast_ref::>() { - Some(io.output.layout_with_breadcrumb(data)) - } else if let Some(io) = introspected_data.downcast_ref::>() { - Some(io.output.layout_with_breadcrumb(data)) - } else if let Some(io) = introspected_data.downcast_ref::>() { - Some(io.output.layout_with_breadcrumb(data)) - } else if let Some(io) = introspected_data.downcast_ref::>() { - Some(io.output.layout_with_breadcrumb(data)) - } else { - None - } -} - -fn column_headings(value: &[&str]) -> Vec { - value.iter().map(|text| TextLabel::new(*text).widget_holder()).collect() -} - -fn label(x: impl Into) -> Vec { - let error = vec![TextLabel::new(x).widget_holder()]; - vec![LayoutGroup::Row { widgets: error }] -} - -trait InstanceLayout { - fn type_name() -> &'static str; - fn identifier(&self) -> String; - fn layout_with_breadcrumb(&self, data: &mut LayoutData) -> Vec { - data.breadcrumbs.push(self.identifier()); - self.compute_layout(data) - } - fn compute_layout(&self, data: &mut LayoutData) -> Vec; -} - -impl InstanceLayout for GraphicElement { - fn type_name() -> &'static str { - "GraphicElement" - } - fn identifier(&self) -> String { - match self { - Self::GraphicGroup(instances) => instances.identifier(), - Self::VectorData(instances) => instances.identifier(), - Self::RasterDataCPU(_) => "RasterDataCPU".to_string(), - Self::RasterDataGPU(_) => "RasterDataGPU".to_string(), - } - } - // Don't put a breadcrumb for GraphicElement - fn layout_with_breadcrumb(&self, data: &mut LayoutData) -> Vec { - self.compute_layout(data) - } - fn compute_layout(&self, data: &mut LayoutData) -> Vec { - match self { - Self::GraphicGroup(instances) => instances.layout_with_breadcrumb(data), - Self::VectorData(instances) => instances.layout_with_breadcrumb(data), - Self::RasterDataCPU(_) => label("Raster frame not supported"), - Self::RasterDataGPU(_) => label("Raster frame not supported"), - } - } -} - -impl InstanceLayout for VectorData { - fn type_name() -> &'static str { - "VectorData" - } - fn identifier(&self) -> String { - format!("Vector Data (points={}, segments={})", self.point_domain.ids().len(), self.segment_domain.ids().len()) - } - fn compute_layout(&self, data: &mut LayoutData) -> Vec { - let colinear = self.colinear_manipulators.iter().map(|[a, b]| format!("[{a} / {b}]")).collect::>().join(", "); - let colinear = if colinear.is_empty() { "None" } else { &colinear }; - let style = vec![ - TextLabel::new(format!( - "{}\n\nColinear Handle IDs: {}\n\nUpstream Graphic Group Table: {}", - self.style, - colinear, - if self.upstream_graphic_group.is_some() { "Yes" } else { "No" } - )) - .multiline(true) - .widget_holder(), - ]; - - let domain_entries = [VectorDataDomain::Points, VectorDataDomain::Segments, VectorDataDomain::Regions] - .into_iter() - .map(|domain| { - RadioEntryData::new(format!("{domain:?}")) - .label(format!("{domain:?}")) - .on_update(move |_| SpreadsheetMessage::ViewVectorDataDomain { domain }.into()) - }) - .collect(); - let domain = vec![RadioInput::new(domain_entries).selected_index(Some(data.vector_data_domain as u32)).widget_holder()]; - - let mut table_rows = Vec::new(); - match data.vector_data_domain { - VectorDataDomain::Points => { - table_rows.push(column_headings(&["", "position"])); - table_rows.extend( - self.point_domain - .iter() - .map(|(id, position)| vec![TextLabel::new(format!("{}", id.inner())).widget_holder(), TextLabel::new(format!("{}", position)).widget_holder()]), - ); - } - VectorDataDomain::Segments => { - table_rows.push(column_headings(&["", "start_index", "end_index", "handles"])); - table_rows.extend(self.segment_domain.iter().map(|(id, start, end, handles)| { - vec![ - TextLabel::new(format!("{}", id.inner())).widget_holder(), - TextLabel::new(format!("{}", start)).widget_holder(), - TextLabel::new(format!("{}", end)).widget_holder(), - TextLabel::new(format!("{:?}", handles)).widget_holder(), - ] - })); - } - VectorDataDomain::Regions => { - table_rows.push(column_headings(&["", "segment_range", "fill"])); - table_rows.extend(self.region_domain.iter().map(|(id, segment_range, fill)| { - vec![ - TextLabel::new(format!("{}", id.inner())).widget_holder(), - TextLabel::new(format!("{:?}", segment_range)).widget_holder(), - TextLabel::new(format!("{}", fill.inner())).widget_holder(), - ] - })); - } - } - - vec![LayoutGroup::Row { widgets: style }, LayoutGroup::Row { widgets: domain }, LayoutGroup::Table { rows: table_rows }] - } -} - -impl InstanceLayout for Image { - fn type_name() -> &'static str { - "Image" - } - fn identifier(&self) -> String { - format!("Image (width={}, height={})", self.width, self.height) - } - fn compute_layout(&self, _data: &mut LayoutData) -> Vec { - let rows = vec![vec![TextLabel::new(format!("Image (width={}, height={})", self.width, self.height)).widget_holder()]]; - vec![LayoutGroup::Table { rows }] - } -} - -impl InstanceLayout for Artboard { - fn type_name() -> &'static str { - "Artboard" - } - fn identifier(&self) -> String { - self.label.clone() - } - fn compute_layout(&self, data: &mut LayoutData) -> Vec { - self.graphic_group.compute_layout(data) - } -} - -impl InstanceLayout for Instances { - fn type_name() -> &'static str { - "Instances" - } - fn identifier(&self) -> String { - format!("Instances<{}> (length={})", T::type_name(), self.len()) - } - fn compute_layout(&self, data: &mut LayoutData) -> Vec { - if let Some(index) = data.desired_path.get(data.current_depth).copied() { - if let Some(instance) = self.get(index) { - data.current_depth += 1; - let result = instance.instance.layout_with_breadcrumb(data); - data.current_depth -= 1; - return result; - } else { - warn!("Desired path truncated"); - data.desired_path.truncate(data.current_depth); - } - } - - let mut rows = self - .instance_ref_iter() - .enumerate() - .map(|(index, instance)| { - let (scale, angle, translation) = instance.transform.to_scale_angle_translation(); - let rotation = if angle == -0. { 0. } else { angle.to_degrees() }; - let round = |x: f64| (x * 1e3).round() / 1e3; - vec![ - TextLabel::new(format!("{}", index)).widget_holder(), - TextButton::new(instance.instance.identifier()) - .on_update(move |_| SpreadsheetMessage::PushToInstancePath { index }.into()) - .widget_holder(), - // Add the mask information here. We'll simply display "Yes" or "No". - TextLabel::new(if instance.mask.is_some() { "Yes" } else { "No" }.to_string()).widget_holder(), - TextLabel::new(format!( - "Location: ({} px, {} px) — Rotation: {rotation:2}° — Scale: ({}x, {}x)", - round(translation.x), - round(translation.y), - round(scale.x), - round(scale.y) - )) - .widget_holder(), - TextLabel::new(format!("{}", instance.alpha_blending)).widget_holder(), - TextLabel::new(instance.source_node_id.map_or_else(|| "-".to_string(), |id| format!("{}", id.0))).widget_holder(), - ] - }) - .collect::>(); - - // Add "mask" to the column headings - rows.insert(0, column_headings(&["", "instance", "mask", "transform", "alpha_blending", "source_node_id"])); - - let instances = vec![TextLabel::new("Instances:").widget_holder()]; - vec![LayoutGroup::Row { widgets: instances }, LayoutGroup::Table { rows }] - } -} diff --git a/node-graph/gcore/src/graphic_element.rs b/node-graph/gcore/src/graphic_element.rs deleted file mode 100644 index c0e3198bcd..0000000000 --- a/node-graph/gcore/src/graphic_element.rs +++ /dev/null @@ -1,626 +0,0 @@ -use crate::blending::AlphaBlending; -use crate::bounds::BoundingBox; -use crate::instances::{Instance, Instances}; -use crate::math::quad::Quad; -use crate::raster::image::Image; -use crate::raster_types::{CPU, GPU, Raster, RasterDataTable}; -use crate::transform::TransformMut; -use crate::uuid::NodeId; -use crate::vector::{VectorData, VectorDataTable}; -use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl}; -use dyn_any::DynAny; -use glam::{DAffine2, DVec2, IVec2}; -use std::hash::Hash; - -// TODO: Eventually remove this migration document upgrade code -pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result { - use serde::Deserialize; - - #[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)] - pub struct OldGraphicGroup { - elements: Vec<(GraphicElement, Option)>, - transform: DAffine2, - alpha_blending: AlphaBlending, - } - #[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)] - pub struct GraphicGroup { - elements: Vec<(GraphicElement, Option)>, - } - pub type OldGraphicGroupTable = Instances; - - #[derive(serde::Serialize, serde::Deserialize)] - #[serde(untagged)] - enum EitherFormat { - OldGraphicGroup(OldGraphicGroup), - InstanceTable(serde_json::Value), - } - - Ok(match EitherFormat::deserialize(deserializer)? { - EitherFormat::OldGraphicGroup(old) => { - let mut graphic_group_table = GraphicGroupTable::default(); - for (graphic_element, source_node_id) in old.elements { - graphic_group_table.push(Instance { - instance: graphic_element, - mask: None, - transform: old.transform, - alpha_blending: old.alpha_blending, - source_node_id, - }); - } - graphic_group_table - } - EitherFormat::InstanceTable(value) => { - // Try to deserialize as either table format - if let Ok(old_table) = serde_json::from_value::(value.clone()) { - let mut graphic_group_table = GraphicGroupTable::default(); - for instance in old_table.instance_ref_iter() { - for (graphic_element, source_node_id) in &instance.instance.elements { - graphic_group_table.push(Instance { - instance: graphic_element.clone(), - mask: None, - transform: *instance.transform, - alpha_blending: *instance.alpha_blending, - source_node_id: *source_node_id, - }); - } - } - graphic_group_table - } else if let Ok(new_table) = serde_json::from_value::(value) { - new_table - } else { - return Err(serde::de::Error::custom("Failed to deserialize GraphicGroupTable")); - } - } - }) -} - -// TODO: Rename to GraphicElementTable -pub type GraphicGroupTable = Instances; - -impl From for GraphicGroupTable { - fn from(vector_data: VectorData) -> Self { - Self::new(GraphicElement::VectorData(VectorDataTable::new(vector_data))) - } -} -impl From for GraphicGroupTable { - fn from(vector_data: VectorDataTable) -> Self { - Self::new(GraphicElement::VectorData(vector_data)) - } -} -impl From> for GraphicGroupTable { - fn from(image: Image) -> Self { - Self::new(GraphicElement::RasterDataCPU(RasterDataTable::::new(Raster::new_cpu(image)))) - } -} -impl From> for GraphicGroupTable { - fn from(raster_data_table: RasterDataTable) -> Self { - Self::new(GraphicElement::RasterDataCPU(raster_data_table)) - } -} -impl From> for GraphicGroupTable { - fn from(raster_data_table: RasterDataTable) -> Self { - Self::new(GraphicElement::RasterDataGPU(raster_data_table)) - } -} -impl From for GraphicGroupTable { - fn from(_: DAffine2) -> Self { - GraphicGroupTable::default() - } -} - -/// The possible forms of graphical content held in a Vec by the `elements` field of [`GraphicElement`]. -#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] -pub enum GraphicElement { - /// Equivalent to the SVG tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g - GraphicGroup(GraphicGroupTable), - /// A vector shape, equivalent to the SVG tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path - VectorData(VectorDataTable), - RasterDataCPU(RasterDataTable), - RasterDataGPU(RasterDataTable), -} - -impl Default for GraphicElement { - fn default() -> Self { - Self::GraphicGroup(GraphicGroupTable::default()) - } -} - -impl From for GraphicElement { - fn from(_: DAffine2) -> Self { - GraphicElement::default() - } -} - -impl GraphicElement { - pub fn as_group(&self) -> Option<&GraphicGroupTable> { - match self { - GraphicElement::GraphicGroup(group) => Some(group), - _ => None, - } - } - - pub fn as_group_mut(&mut self) -> Option<&mut GraphicGroupTable> { - match self { - GraphicElement::GraphicGroup(group) => Some(group), - _ => None, - } - } - - pub fn as_vector_data(&self) -> Option<&VectorDataTable> { - match self { - GraphicElement::VectorData(data) => Some(data), - _ => None, - } - } - - pub fn as_vector_data_mut(&mut self) -> Option<&mut VectorDataTable> { - match self { - GraphicElement::VectorData(data) => Some(data), - _ => None, - } - } - - pub fn as_raster(&self) -> Option<&RasterDataTable> { - match self { - GraphicElement::RasterDataCPU(raster) => Some(raster), - _ => None, - } - } - - pub fn as_raster_mut(&mut self) -> Option<&mut RasterDataTable> { - match self { - GraphicElement::RasterDataCPU(raster) => Some(raster), - _ => None, - } - } - - pub fn had_clip_enabled(&self) -> bool { - match self { - GraphicElement::VectorData(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip), - GraphicElement::GraphicGroup(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip), - GraphicElement::RasterDataCPU(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip), - GraphicElement::RasterDataGPU(data) => data.instance_ref_iter().all(|instance| instance.alpha_blending.clip), - } - } - - pub fn can_reduce_to_clip_path(&self) -> bool { - match self { - GraphicElement::VectorData(vector_data_table) => vector_data_table.instance_ref_iter().all(|instance_data| { - let style = &instance_data.instance.style; - let alpha_blending = &instance_data.alpha_blending; - (alpha_blending.opacity > 1. - f32::EPSILON) && style.fill().is_opaque() && style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke()) - }), - _ => false, - } - } -} - -impl BoundingBox for GraphicElement { - fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> { - match self { - GraphicElement::VectorData(vector_data) => vector_data.bounding_box(transform, include_stroke), - GraphicElement::RasterDataCPU(raster) => raster.bounding_box(transform, include_stroke), - GraphicElement::RasterDataGPU(raster) => raster.bounding_box(transform, include_stroke), - GraphicElement::GraphicGroup(graphic_group) => graphic_group.bounding_box(transform, include_stroke), - } - } -} - -impl BoundingBox for GraphicGroupTable { - fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> { - self.instance_ref_iter() - .filter_map(|element| element.instance.bounding_box(transform * *element.transform, include_stroke)) - .reduce(Quad::combine_bounds) - } -} - -impl<'de> serde::Deserialize<'de> for Raster { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - Ok(Raster::new_cpu(Image::deserialize(deserializer)?)) - } -} - -impl serde::Serialize for Raster { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.data().serialize(serializer) - } -} -impl<'de> serde::Deserialize<'de> for Raster { - fn deserialize(_deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - unimplemented!() - } -} - -impl serde::Serialize for Raster { - fn serialize(&self, _serializer: S) -> Result - where - S: serde::Serializer, - { - unimplemented!() - } -} - -/// Some [`ArtboardData`] with some optional clipping bounds that can be exported. -#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] -pub struct Artboard { - pub graphic_group: GraphicGroupTable, - pub label: String, - pub location: IVec2, - pub dimensions: IVec2, - pub background: Color, - pub clip: bool, -} - -impl Default for Artboard { - fn default() -> Self { - Self::new(IVec2::ZERO, IVec2::new(1920, 1080)) - } -} - -impl Artboard { - pub fn new(location: IVec2, dimensions: IVec2) -> Self { - Self { - graphic_group: GraphicGroupTable::default(), - label: "Artboard".to_string(), - location: location.min(location + dimensions), - dimensions: dimensions.abs(), - background: Color::WHITE, - clip: false, - } - } -} - -impl BoundingBox for Artboard { - fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> { - let artboard_bounds = (transform * Quad::from_box([self.location.as_dvec2(), self.location.as_dvec2() + self.dimensions.as_dvec2()])).bounding_box(); - if self.clip { - Some(artboard_bounds) - } else { - [self.graphic_group.bounding_box(transform, include_stroke), Some(artboard_bounds)] - .into_iter() - .flatten() - .reduce(Quad::combine_bounds) - } - } -} - -// TODO: Eventually remove this migration document upgrade code -pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result { - use serde::Deserialize; - - #[derive(Clone, Default, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] - pub struct ArtboardGroup { - pub artboards: Vec<(Artboard, Option)>, - } - - #[derive(serde::Serialize, serde::Deserialize)] - #[serde(untagged)] - enum EitherFormat { - ArtboardGroup(ArtboardGroup), - ArtboardGroupTable(ArtboardGroupTable), - } - - Ok(match EitherFormat::deserialize(deserializer)? { - EitherFormat::ArtboardGroup(artboard_group) => { - let mut table = ArtboardGroupTable::default(); - for (artboard, source_node_id) in artboard_group.artboards { - table.push(Instance { - instance: artboard, - mask: None, - transform: DAffine2::IDENTITY, - alpha_blending: AlphaBlending::default(), - source_node_id, - }); - } - table - } - EitherFormat::ArtboardGroupTable(artboard_group_table) => artboard_group_table, - }) -} - -pub type ArtboardGroupTable = Instances; - -impl BoundingBox for ArtboardGroupTable { - fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> { - self.instance_ref_iter() - .filter_map(|instance| instance.instance.bounding_box(transform, include_stroke)) - .reduce(Quad::combine_bounds) - } -} - -#[node_macro::node(category(""))] -async fn layer( - _: impl Ctx, - #[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable, RasterDataTable)] mut stack: Instances, - #[implementations(GraphicElement, VectorData, Raster, Raster)] element: I, - node_path: Vec, -) -> Instances { - // Get the penultimate element of the node path, or None if the path is too short - let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied(); - - stack.push(Instance { - instance: element, - mask: None, - transform: DAffine2::IDENTITY, - alpha_blending: AlphaBlending::default(), - source_node_id, - }); - - stack -} - -#[node_macro::node(category("Debug"))] -async fn to_element + 'n>( - _: impl Ctx, - #[implementations( - GraphicGroupTable, - VectorDataTable, - RasterDataTable, - RasterDataTable, - DAffine2, - )] - data: Data, -) -> GraphicElement { - data.into() -} - -#[node_macro::node(category("General"))] -async fn to_group + 'n>( - _: impl Ctx, - #[implementations( - GraphicGroupTable, - VectorDataTable, - RasterDataTable, - RasterDataTable, - )] - element: Data, -) -> GraphicGroupTable { - element.into() -} - -#[node_macro::node(category("General"))] -async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: bool) -> GraphicGroupTable { - // TODO: Avoid mutable reference, instead return a new GraphicGroupTable? - fn flatten_group(output_group_table: &mut GraphicGroupTable, current_group_table: GraphicGroupTable, fully_flatten: bool, recursion_depth: usize) { - for current_instance in current_group_table.instance_ref_iter() { - let current_element = current_instance.instance.clone(); - let reference = *current_instance.source_node_id; - - let recurse = fully_flatten || recursion_depth == 0; - - match current_element { - // If we're allowed to recurse, flatten any GraphicGroups we encounter - GraphicElement::GraphicGroup(mut current_element) if recurse => { - // Apply the parent group's transform to all child elements - for graphic_element in current_element.instance_mut_iter() { - *graphic_element.transform = *current_instance.transform * *graphic_element.transform; - } - - flatten_group(output_group_table, current_element, fully_flatten, recursion_depth + 1); - } - // Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten - _ => { - output_group_table.push(Instance { - instance: current_element, - mask: None, - transform: *current_instance.transform, - alpha_blending: *current_instance.alpha_blending, - source_node_id: reference, - }); - } - } - } - } - - let mut output = GraphicGroupTable::default(); - flatten_group(&mut output, group, fully_flatten, 0); - - output -} - -#[node_macro::node(category("Vector"))] -async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTable { - // TODO: Avoid mutable reference, instead return a new GraphicGroupTable? - fn flatten_group(output_group_table: &mut VectorDataTable, current_group_table: GraphicGroupTable) { - for current_instance in current_group_table.instance_ref_iter() { - let current_element = current_instance.instance.clone(); - let reference = *current_instance.source_node_id; - - match current_element { - // If we're allowed to recurse, flatten any GraphicGroups we encounter - GraphicElement::GraphicGroup(mut current_element) => { - // Apply the parent group's transform to all child elements - for graphic_element in current_element.instance_mut_iter() { - *graphic_element.transform = *current_instance.transform * *graphic_element.transform; - } - - flatten_group(output_group_table, current_element); - } - // Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten - GraphicElement::VectorData(vector_instance) => { - for current_element in vector_instance.instance_ref_iter() { - output_group_table.push(Instance { - instance: current_element.instance.clone(), - mask: None, - transform: *current_instance.transform * *current_element.transform, - alpha_blending: AlphaBlending { - blend_mode: current_element.alpha_blending.blend_mode, - opacity: current_instance.alpha_blending.opacity * current_element.alpha_blending.opacity, - fill: current_element.alpha_blending.fill, - clip: current_element.alpha_blending.clip, - }, - source_node_id: reference, - }); - } - } - _ => {} - } - } - } - - let mut output = VectorDataTable::default(); - flatten_group(&mut output, group); - - output -} - -#[node_macro::node(category(""))] -async fn to_artboard + 'n>( - ctx: impl ExtractAll + CloneVarArgs + Ctx, - #[implementations( - Context -> GraphicGroupTable, - Context -> VectorDataTable, - Context -> RasterDataTable, - Context -> RasterDataTable, - Context -> DAffine2, - )] - contents: impl Node, Output = Data>, - label: String, - location: DVec2, - dimensions: DVec2, - background: Color, - clip: bool, -) -> Artboard { - let location = location.as_ivec2(); - let dimensions = dimensions.as_ivec2().max(IVec2::ONE); - - let footprint = ctx.try_footprint().copied(); - let mut new_ctx = OwnedContextImpl::from(ctx); - if let Some(mut footprint) = footprint { - footprint.translate(location.as_dvec2()); - new_ctx = new_ctx.with_footprint(footprint); - } - let graphic_group = contents.eval(new_ctx.into_context()).await; - - Artboard { - graphic_group: graphic_group.into(), - label, - location: location.min(location + dimensions), - dimensions: dimensions.abs(), - background, - clip, - } -} - -#[node_macro::node(category(""))] -pub async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec) -> ArtboardGroupTable { - // Get the penultimate element of the node path, or None if the path is too short. - // This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node). - let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied(); - - artboards.push(Instance { - instance: artboard, - mask: None, - transform: DAffine2::IDENTITY, - alpha_blending: AlphaBlending::default(), - source_node_id: encapsulating_node_id, - }); - - artboards -} - -// TODO: Remove this one -impl From> for GraphicElement { - fn from(raster_data: Image) -> Self { - GraphicElement::RasterDataCPU(RasterDataTable::::new(Raster::new_cpu(raster_data))) - } -} -impl From> for GraphicElement { - fn from(raster_data: RasterDataTable) -> Self { - GraphicElement::RasterDataCPU(raster_data) - } -} -impl From> for GraphicElement { - fn from(raster_data: RasterDataTable) -> Self { - GraphicElement::RasterDataGPU(raster_data) - } -} -impl From> for GraphicElement { - fn from(raster_data: Raster) -> Self { - GraphicElement::RasterDataCPU(RasterDataTable::new(raster_data)) - } -} -impl From> for GraphicElement { - fn from(raster_data: Raster) -> Self { - GraphicElement::RasterDataGPU(RasterDataTable::new(raster_data)) - } -} -// TODO: Remove this one -impl From for GraphicElement { - fn from(vector_data: VectorData) -> Self { - GraphicElement::VectorData(VectorDataTable::new(vector_data)) - } -} -impl From for GraphicElement { - fn from(vector_data: VectorDataTable) -> Self { - GraphicElement::VectorData(vector_data) - } -} -impl From for GraphicElement { - fn from(graphic_group: GraphicGroupTable) -> Self { - GraphicElement::GraphicGroup(graphic_group) - } -} - -pub trait ToGraphicElement { - fn to_graphic_element(&self) -> GraphicElement; -} - -/// Returns the value at the specified index in the collection. -/// If that index has no value, the type's default value is returned. -#[node_macro::node(category("General"))] -fn index( - _: impl Ctx, - /// The collection of data, such as a list or table. - #[implementations( - Vec, - Vec>, - Vec, Vec, - Vec, - VectorDataTable, - RasterDataTable, - GraphicGroupTable, - )] - collection: T, - /// The index of the item to retrieve, starting from 0 for the first item. - index: u32, -) -> T::Output -where - T::Output: Clone + Default, -{ - collection.at_index(index as usize).unwrap_or_default() -} - -pub trait AtIndex { - type Output; - fn at_index(&self, index: usize) -> Option; -} -impl AtIndex for Vec { - type Output = T; - - fn at_index(&self, index: usize) -> Option { - self.get(index).cloned() - } -} -impl AtIndex for Instances { - type Output = Instances; - - fn at_index(&self, index: usize) -> Option { - let mut result_table = Self::default(); - if let Some(row) = self.instance_ref_iter().nth(index) { - result_table.push(row.to_instance_cloned()); - Some(result_table) - } else { - None - } - } -} diff --git a/node-graph/gcore/src/table.rs b/node-graph/gcore/src/table.rs index 32a2e680e0..95e00d3b23 100644 --- a/node-graph/gcore/src/table.rs +++ b/node-graph/gcore/src/table.rs @@ -1,7 +1,7 @@ +use crate::Graphic; use crate::bounds::{BoundingBox, RenderBoundingBox}; use crate::transform::ApplyTransform; use crate::uuid::NodeId; -use crate::Graphic; use crate::{AlphaBlending, math::quad::Quad}; use dyn_any::StaticType; use glam::DAffine2; diff --git a/node-graph/graster-nodes/src/std_nodes.rs b/node-graph/graster-nodes/src/std_nodes.rs index 03535a8f6f..7aa4953b31 100644 --- a/node-graph/graster-nodes/src/std_nodes.rs +++ b/node-graph/graster-nodes/src/std_nodes.rs @@ -2,6 +2,7 @@ use crate::adjustments::{CellularDistanceFunction, CellularReturnType, DomainWar use dyn_any::DynAny; use fastnoise_lite; use glam::{DAffine2, DVec2, Vec2}; +use graphene_core::Graphic; use graphene_core::blending::AlphaBlending; use graphene_core::color::Color; use graphene_core::color::{AlphaMut, Channel, LinearChannel, Luminance, RGBMut}; @@ -13,7 +14,6 @@ use graphene_core::raster_types::{CPU, Raster}; use graphene_core::table::{Table, TableRow}; use graphene_core::transform::Transform; use graphene_core::vector::Vector; -use graphene_core::Graphic; use rand::prelude::*; use rand_chacha::ChaCha8Rng; use std::fmt::Debug; diff --git a/node-graph/gsvg-renderer/src/renderer.rs b/node-graph/gsvg-renderer/src/renderer.rs index df5f3a1afa..480fb9194c 100644 --- a/node-graph/gsvg-renderer/src/renderer.rs +++ b/node-graph/gsvg-renderer/src/renderer.rs @@ -588,9 +588,8 @@ impl Render for Table { masked = true; let rect = vello::kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y); - scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); + scene.push_luminance_mask_layer(1., kurbo::Affine::IDENTITY, &rect); mask.render_to_vello(scene, transform, context, &render_params.for_clipper()); - scene.push_layer(peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcIn), 1., kurbo::Affine::IDENTITY, &rect); } } @@ -627,7 +626,6 @@ impl Render for Table { if masked { scene.pop_layer(); - scene.pop_layer(); } if layer { @@ -909,9 +907,8 @@ impl Render for Table { masked = true; let rect = vello::kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y); - scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); + scene.push_luminance_mask_layer(1., kurbo::Affine::IDENTITY, &rect); mask.render_to_vello(scene, element_transform, _context, &render_params.for_clipper()); - scene.push_layer(peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcIn), 1., kurbo::Affine::IDENTITY, &rect); } } @@ -1106,7 +1103,6 @@ impl Render for Table { if masked { scene.pop_layer(); - scene.pop_layer(); } // If we pushed a layer for opacity or a blend mode, we need to pop it @@ -1333,9 +1329,8 @@ impl Render for Table> { masked = true; let rect = vello::kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y); - scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); + scene.push_luminance_mask_layer(1., kurbo::Affine::IDENTITY, &rect); mask.render_to_vello(scene, transform, _context, &render_params.for_clipper()); - scene.push_layer(peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcIn), 1., kurbo::Affine::IDENTITY, &rect); } } @@ -1343,7 +1338,6 @@ impl Render for Table> { if masked { scene.pop_layer(); - scene.pop_layer(); } if layer {