fix issues in merge and update to vello pr

This commit is contained in:
mtvare6
2025-09-01 22:27:34 +05:30
parent 49319f9175
commit 8d14aaaded
8 changed files with 69 additions and 1125 deletions

View File

@@ -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

132
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"] }

View File

@@ -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<NodeId>,
introspected_data: Option<Arc<dyn Any + Send + Sync>>,
instances_path: Vec<usize>,
viewing_vector_data_domain: VectorDataDomain,
}
#[message_handler_data]
impl MessageHandler<SpreadsheetMessage, ()> for SpreadsheetMessageHandler {
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, _: ()) {
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<Message>) {
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<usize>,
breadcrumbs: Vec<String>,
vector_data_domain: VectorDataDomain,
}
fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'static>, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
// We simply try random types. TODO: better strategy.
#[allow(clippy::manual_map)]
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, ArtboardGroupTable>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), ArtboardGroupTable>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, VectorDataTable>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), VectorDataTable>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, GraphicGroupTable>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), GraphicGroupTable>>() {
Some(io.output.layout_with_breadcrumb(data))
} else {
None
}
}
fn column_headings(value: &[&str]) -> Vec<WidgetHolder> {
value.iter().map(|text| TextLabel::new(*text).widget_holder()).collect()
}
fn label(x: impl Into<String>) -> Vec<LayoutGroup> {
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<LayoutGroup> {
data.breadcrumbs.push(self.identifier());
self.compute_layout(data)
}
fn compute_layout(&self, data: &mut LayoutData) -> Vec<LayoutGroup>;
}
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<LayoutGroup> {
self.compute_layout(data)
}
fn compute_layout(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
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<LayoutGroup> {
let colinear = self.colinear_manipulators.iter().map(|[a, b]| format!("[{a} / {b}]")).collect::<Vec<_>>().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<Color> {
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<LayoutGroup> {
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<LayoutGroup> {
self.graphic_group.compute_layout(data)
}
}
impl<T: InstanceLayout> InstanceLayout for Instances<T> {
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<LayoutGroup> {
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::<Vec<_>>();
// 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 }]
}
}

View File

@@ -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<GraphicGroupTable, D::Error> {
use serde::Deserialize;
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
pub struct OldGraphicGroup {
elements: Vec<(GraphicElement, Option<NodeId>)>,
transform: DAffine2,
alpha_blending: AlphaBlending,
}
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
pub struct GraphicGroup {
elements: Vec<(GraphicElement, Option<NodeId>)>,
}
pub type OldGraphicGroupTable = Instances<GraphicGroup>;
#[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::<OldGraphicGroupTable>(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::<GraphicGroupTable>(value) {
new_table
} else {
return Err(serde::de::Error::custom("Failed to deserialize GraphicGroupTable"));
}
}
})
}
// TODO: Rename to GraphicElementTable
pub type GraphicGroupTable = Instances<GraphicElement>;
impl From<VectorData> for GraphicGroupTable {
fn from(vector_data: VectorData) -> Self {
Self::new(GraphicElement::VectorData(VectorDataTable::new(vector_data)))
}
}
impl From<VectorDataTable> for GraphicGroupTable {
fn from(vector_data: VectorDataTable) -> Self {
Self::new(GraphicElement::VectorData(vector_data))
}
}
impl From<Image<Color>> for GraphicGroupTable {
fn from(image: Image<Color>) -> Self {
Self::new(GraphicElement::RasterDataCPU(RasterDataTable::<CPU>::new(Raster::new_cpu(image))))
}
}
impl From<RasterDataTable<CPU>> for GraphicGroupTable {
fn from(raster_data_table: RasterDataTable<CPU>) -> Self {
Self::new(GraphicElement::RasterDataCPU(raster_data_table))
}
}
impl From<RasterDataTable<GPU>> for GraphicGroupTable {
fn from(raster_data_table: RasterDataTable<GPU>) -> Self {
Self::new(GraphicElement::RasterDataGPU(raster_data_table))
}
}
impl From<DAffine2> 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 <g> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
GraphicGroup(GraphicGroupTable),
/// A vector shape, equivalent to the SVG <path> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
VectorData(VectorDataTable),
RasterDataCPU(RasterDataTable<CPU>),
RasterDataGPU(RasterDataTable<GPU>),
}
impl Default for GraphicElement {
fn default() -> Self {
Self::GraphicGroup(GraphicGroupTable::default())
}
}
impl From<DAffine2> 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<CPU>> {
match self {
GraphicElement::RasterDataCPU(raster) => Some(raster),
_ => None,
}
}
pub fn as_raster_mut(&mut self) -> Option<&mut RasterDataTable<CPU>> {
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<CPU> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Raster::new_cpu(Image::deserialize(deserializer)?))
}
}
impl serde::Serialize for Raster<CPU> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.data().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for Raster<GPU> {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
unimplemented!()
}
}
impl serde::Serialize for Raster<GPU> {
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
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<ArtboardGroupTable, D::Error> {
use serde::Deserialize;
#[derive(Clone, Default, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
pub struct ArtboardGroup {
pub artboards: Vec<(Artboard, Option<NodeId>)>,
}
#[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<Artboard>;
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<I: 'n + Send + Clone>(
_: impl Ctx,
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>, RasterDataTable<GPU>)] mut stack: Instances<I>,
#[implementations(GraphicElement, VectorData, Raster<CPU>, Raster<GPU>)] element: I,
node_path: Vec<NodeId>,
) -> Instances<I> {
// 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<Data: Into<GraphicElement> + 'n>(
_: impl Ctx,
#[implementations(
GraphicGroupTable,
VectorDataTable,
RasterDataTable<CPU>,
RasterDataTable<GPU>,
DAffine2,
)]
data: Data,
) -> GraphicElement {
data.into()
}
#[node_macro::node(category("General"))]
async fn to_group<Data: Into<GraphicGroupTable> + 'n>(
_: impl Ctx,
#[implementations(
GraphicGroupTable,
VectorDataTable,
RasterDataTable<CPU>,
RasterDataTable<GPU>,
)]
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<Data: Into<GraphicGroupTable> + 'n>(
ctx: impl ExtractAll + CloneVarArgs + Ctx,
#[implementations(
Context -> GraphicGroupTable,
Context -> VectorDataTable,
Context -> RasterDataTable<CPU>,
Context -> RasterDataTable<GPU>,
Context -> DAffine2,
)]
contents: impl Node<Context<'static>, 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<NodeId>) -> 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<Image<Color>> for GraphicElement {
fn from(raster_data: Image<Color>) -> Self {
GraphicElement::RasterDataCPU(RasterDataTable::<CPU>::new(Raster::new_cpu(raster_data)))
}
}
impl From<RasterDataTable<CPU>> for GraphicElement {
fn from(raster_data: RasterDataTable<CPU>) -> Self {
GraphicElement::RasterDataCPU(raster_data)
}
}
impl From<RasterDataTable<GPU>> for GraphicElement {
fn from(raster_data: RasterDataTable<GPU>) -> Self {
GraphicElement::RasterDataGPU(raster_data)
}
}
impl From<Raster<CPU>> for GraphicElement {
fn from(raster_data: Raster<CPU>) -> Self {
GraphicElement::RasterDataCPU(RasterDataTable::new(raster_data))
}
}
impl From<Raster<GPU>> for GraphicElement {
fn from(raster_data: Raster<GPU>) -> Self {
GraphicElement::RasterDataGPU(RasterDataTable::new(raster_data))
}
}
// TODO: Remove this one
impl From<VectorData> for GraphicElement {
fn from(vector_data: VectorData) -> Self {
GraphicElement::VectorData(VectorDataTable::new(vector_data))
}
}
impl From<VectorDataTable> for GraphicElement {
fn from(vector_data: VectorDataTable) -> Self {
GraphicElement::VectorData(vector_data)
}
}
impl From<GraphicGroupTable> 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<T: AtIndex + Clone + Default>(
_: impl Ctx,
/// The collection of data, such as a list or table.
#[implementations(
Vec<Color>,
Vec<Option<Color>>,
Vec<f64>, Vec<u64>,
Vec<DVec2>,
VectorDataTable,
RasterDataTable<CPU>,
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<Self::Output>;
}
impl<T: Clone> AtIndex for Vec<T> {
type Output = T;
fn at_index(&self, index: usize) -> Option<Self::Output> {
self.get(index).cloned()
}
}
impl<T: Clone> AtIndex for Instances<T> {
type Output = Instances<T>;
fn at_index(&self, index: usize) -> Option<Self::Output> {
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
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -588,9 +588,8 @@ impl Render for Table<Graphic> {
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<Graphic> {
if masked {
scene.pop_layer();
scene.pop_layer();
}
if layer {
@@ -909,9 +907,8 @@ impl Render for Table<Vector> {
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<Vector> {
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<Raster<CPU>> {
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<Raster<CPU>> {
if masked {
scene.pop_layer();
scene.pop_layer();
}
if layer {