From 02c354a2395717270dc5b6cee608d6995b6496b4 Mon Sep 17 00:00:00 2001 From: Universe Date: Tue, 10 Mar 2026 19:30:46 +0900 Subject: [PATCH 01/16] feat(cg): viewport culling at draw time Skip off-screen layers during draw by building a visible-node set from the R-tree frame plan and passing it to new culled-draw methods in Painter. Camera pan/zoom on a 4900-node scene is 67x faster when zoomed in (~1% visible) and 1529x faster when the viewport is empty. - Add draw_layer_list_culled / draw_render_commands_culled to Painter - Build HashSet from FramePlan regions in draw_layers_with_scene_cache - Add viewport_culling correctness tests (3 integration tests) - Add bench_viewport_culling criterion benchmark (5K and 50K nodes) --- crates/grida-canvas/Cargo.toml | 4 + .../benches/bench_viewport_culling.rs | 248 ++++++++++++++++++ crates/grida-canvas/src/painter/painter.rs | 62 +++++ crates/grida-canvas/src/runtime/scene.rs | 12 +- crates/grida-canvas/tests/viewport_culling.rs | 191 ++++++++++++++ 5 files changed, 516 insertions(+), 1 deletion(-) create mode 100644 crates/grida-canvas/benches/bench_viewport_culling.rs create mode 100644 crates/grida-canvas/tests/viewport_culling.rs diff --git a/crates/grida-canvas/Cargo.toml b/crates/grida-canvas/Cargo.toml index cbd34a0584..c0fee360b1 100644 --- a/crates/grida-canvas/Cargo.toml +++ b/crates/grida-canvas/Cargo.toml @@ -74,3 +74,7 @@ harness = false name = "bench_mipmap" harness = false +[[bench]] +name = "bench_viewport_culling" +harness = false + diff --git a/crates/grida-canvas/benches/bench_viewport_culling.rs b/crates/grida-canvas/benches/bench_viewport_culling.rs new file mode 100644 index 0000000000..224411823e --- /dev/null +++ b/crates/grida-canvas/benches/bench_viewport_culling.rs @@ -0,0 +1,248 @@ +//! Benchmark: viewport culling during camera pan/zoom. +//! +//! Measures the cost of rendering a single frame after a camera transform +//! change. The scene is loaded once during setup; only the queue + flush +//! (frame planning + draw) is timed. +//! +//! Scenarios: +//! - "all_visible": camera shows the entire scene (no culling benefit) +//! - "partial": camera shows ~25% of the scene +//! - "corner": camera shows a small corner (~6% of nodes) +//! - "zoomed_in": zoomed into a small region (~1-2% visible) +//! - "empty": camera pointed at empty space (0 visible nodes) + +use cg::cg::prelude::*; +use cg::node::factory::NodeFactory; +use cg::node::scene_graph::{Parent, SceneGraph}; +use cg::node::schema::*; +use cg::runtime::camera::Camera2D; +use cg::runtime::scene::{Backend, Renderer}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +/// Create a grid scene with `cols * rows` rectangles spread in world space. +/// Each rectangle is 20x20 with 10px gap, starting at (0,0). +fn create_grid_scene(cols: u32, rows: u32) -> Scene { + let nf = NodeFactory::new(); + let mut graph = SceneGraph::new(); + + let size = 20.0f32; + let gap = 10.0f32; + + for y in 0..rows { + for x in 0..cols { + let mut rect = nf.create_rectangle_node(); + rect.transform = math2::transform::AffineTransform::new( + x as f32 * (size + gap), + y as f32 * (size + gap), + 0.0, + ); + rect.size = Size { + width: size, + height: size, + }; + rect.set_fill(Paint::Solid(SolidPaint { + color: CGColor::from_rgba( + ((x * 7) % 255) as u8, + ((y * 11) % 255) as u8, + 180, + 255, + ), + blend_mode: BlendMode::default(), + active: true, + })); + graph.append_child(Node::Rectangle(rect), Parent::Root); + } + } + + Scene { + name: "Grid Scene".to_string(), + graph, + background_color: Some(CGColor::WHITE), + } +} + +/// Helper: load scene, warm the picture cache with one initial render, +/// then return the renderer ready for camera-only benchmarking. +fn setup_renderer(scene: Scene, vp_width: i32, vp_height: i32) -> Renderer { + let mut renderer = Renderer::new( + Backend::new_from_raster(vp_width, vp_height), + None, + Camera2D::new(Size { + width: vp_width as f32, + height: vp_height as f32, + }), + ); + renderer.load_scene(scene); + // Warm: render one frame so picture cache is populated + renderer.queue_unstable(); + let _ = renderer.flush(); + renderer +} + +/// Simulate a camera move and render one frame. Returns flush stats. +fn pan_and_render(renderer: &mut Renderer, tx: f32, ty: f32) { + renderer.camera.translate(tx, ty); + renderer.queue_unstable(); + let _ = renderer.flush(); +} + +fn bench_viewport_culling(c: &mut Criterion) { + let vp_w = 1000; + let vp_h = 1000; + + // --- 5K nodes (70x70 grid, ~5000 nodes) --- + // Scene spans 70 * 30 = 2100 x 2100 world pixels + let cols_5k = 70u32; + let rows_5k = 70u32; + let scene_extent_5k = cols_5k as f32 * 30.0; // ~2100 + + let mut group = c.benchmark_group("viewport_culling_5k"); + group.sample_size(200); + group.measurement_time(std::time::Duration::from_secs(10)); + + // All visible: camera centered, zoom out to show everything + group.bench_function("all_visible", |b| { + let scene = create_grid_scene(cols_5k, rows_5k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + // Center camera on scene, zoom out so everything fits + renderer.camera.set_center(scene_extent_5k / 2.0, scene_extent_5k / 2.0); + renderer.camera.set_zoom(vp_w as f32 / scene_extent_5k * 0.9); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 1.0f32; + b.iter(|| { + dx = -dx; // oscillate to avoid no-op detection + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + // Partial: camera at (0,0), zoom=1 → viewport sees ~1000x1000 of 2100x2100 + // That's roughly (1000/30)^2 / 4900 ≈ 22% of nodes + group.bench_function("partial_25pct", |b| { + let scene = create_grid_scene(cols_5k, rows_5k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + renderer.camera.set_center(0.0, 0.0); + renderer.camera.set_zoom(1.0); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 1.0f32; + b.iter(|| { + dx = -dx; + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + // Corner: camera at top-left corner, zoom=2 → viewport sees 500x500 world + // That's ~(500/30)^2 / 4900 ≈ 5.6% of nodes + group.bench_function("corner_5pct", |b| { + let scene = create_grid_scene(cols_5k, rows_5k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + renderer.camera.set_center(0.0, 0.0); + renderer.camera.set_zoom(2.0); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 1.0f32; + b.iter(|| { + dx = -dx; + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + // Zoomed in: camera at center, zoom=10 → viewport sees 100x100 world + // That's ~(100/30)^2 / 4900 ≈ 0.2% of nodes + group.bench_function("zoomed_in_1pct", |b| { + let scene = create_grid_scene(cols_5k, rows_5k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + renderer.camera.set_center(scene_extent_5k / 2.0, scene_extent_5k / 2.0); + renderer.camera.set_zoom(10.0); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 0.5f32; + b.iter(|| { + dx = -dx; + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + // Empty: camera pointed far away from the scene (nothing visible) + group.bench_function("empty_offscreen", |b| { + let scene = create_grid_scene(cols_5k, rows_5k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + renderer.camera.set_center(99999.0, 99999.0); + renderer.camera.set_zoom(1.0); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 1.0f32; + b.iter(|| { + dx = -dx; + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + group.finish(); + + // --- 50K nodes (224x224 grid, ~50176 nodes) --- + // Scene spans 224 * 30 = 6720 x 6720 world pixels + let cols_50k = 224u32; + let rows_50k = 224u32; + let scene_extent_50k = cols_50k as f32 * 30.0; + + let mut group = c.benchmark_group("viewport_culling_50k"); + group.sample_size(50); + group.measurement_time(std::time::Duration::from_secs(15)); + + group.bench_function("all_visible", |b| { + let scene = create_grid_scene(cols_50k, rows_50k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + renderer.camera.set_center(scene_extent_50k / 2.0, scene_extent_50k / 2.0); + renderer.camera.set_zoom(vp_w as f32 / scene_extent_50k * 0.9); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 1.0f32; + b.iter(|| { + dx = -dx; + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + group.bench_function("zoomed_in_1pct", |b| { + let scene = create_grid_scene(cols_50k, rows_50k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + renderer.camera.set_center(scene_extent_50k / 2.0, scene_extent_50k / 2.0); + renderer.camera.set_zoom(10.0); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 0.5f32; + b.iter(|| { + dx = -dx; + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + group.bench_function("empty_offscreen", |b| { + let scene = create_grid_scene(cols_50k, rows_50k); + let mut renderer = setup_renderer(scene, vp_w, vp_h); + renderer.camera.set_center(99999.0, 99999.0); + renderer.camera.set_zoom(1.0); + renderer.queue_stable(); + let _ = renderer.flush(); + + let mut dx = 1.0f32; + b.iter(|| { + dx = -dx; + pan_and_render(black_box(&mut renderer), dx, 0.0); + }) + }); + + group.finish(); +} + +criterion_group!(benches, bench_viewport_culling); +criterion_main!(benches); diff --git a/crates/grida-canvas/src/painter/painter.rs b/crates/grida-canvas/src/painter/painter.rs index bfd0f9de30..62f85764ce 100644 --- a/crates/grida-canvas/src/painter/painter.rs +++ b/crates/grida-canvas/src/painter/painter.rs @@ -19,6 +19,7 @@ use skia_safe::{ Shader, }; use std::cell::RefCell; +use std::collections::HashSet; use std::rc::Rc; /// A painter that handles all drawing operations for nodes, @@ -1500,6 +1501,67 @@ impl<'a> Painter<'a> { self.draw_render_commands(&list.commands); } + /// Draw only the layers whose IDs appear in `visible`, skipping off-screen + /// content. Mask groups are drawn unconditionally to preserve clip correctness. + pub fn draw_layer_list_culled(&self, list: &LayerList, visible: &HashSet) { + if !self.policy.is_wireframe() { + self.draw_render_commands_culled(&list.commands, visible); + } else { + self.draw_layer_list_outline_culled(list, visible); + } + } + + fn draw_render_commands_culled( + &self, + commands: &[PainterRenderCommand], + visible: &HashSet, + ) { + for command in commands { + match command { + PainterRenderCommand::Draw(layer) => { + if !visible.contains(layer.id()) { + continue; + } + // Prefer cached picture if available + if let Some(scene_cache) = self.scene_cache { + if let Some(pic) = + scene_cache.get_node_picture_variant(layer.id(), self.variant_key) + { + self.canvas.draw_picture(pic, None, None); + *self.cache_hits.borrow_mut() += 1; + continue; + } + } + self.draw_layer(layer) + } + PainterRenderCommand::MaskGroup(group) => { + self.draw_mask_group_or_passthrough(group) + } + } + } + } + + fn draw_layer_list_outline_culled(&self, list: &LayerList, visible: &HashSet) { + let Some(style) = self.policy.outline_style() else { + return; + }; + + for entry in &list.layers { + if !visible.contains(&entry.id) { + continue; + } + if let Some(scene_cache) = self.scene_cache { + if let Some(pic) = scene_cache.get_node_picture_variant(&entry.id, self.variant_key) + { + self.canvas.draw_picture(pic, None, None); + *self.cache_hits.borrow_mut() += 1; + continue; + } + } + self.draw_layer_outline(&entry.layer, style); + } + } + fn draw_layer_list_outline(&self, list: &LayerList) { let Some(style) = self.policy.outline_style() else { // Non-standard policy without an outline style: nothing to draw. diff --git a/crates/grida-canvas/src/runtime/scene.rs b/crates/grida-canvas/src/runtime/scene.rs index 384547c1cf..a66d4a3c01 100644 --- a/crates/grida-canvas/src/runtime/scene.rs +++ b/crates/grida-canvas/src/runtime/scene.rs @@ -236,6 +236,16 @@ impl Renderer { fn draw_layers_with_scene_cache(&mut self, canvas: &Canvas, plan: &FramePlan) -> usize { self.prefill_picture_cache_for_plan(plan); + // Collect visible node IDs from the frame plan so the painter can skip + // off-screen layers. The plan's regions contain indices into the flat + // layer list produced by the R-tree viewport query. + let visible_ids: HashSet = plan + .regions + .iter() + .flat_map(|(_, indices)| indices.iter()) + .filter_map(|idx| self.scene_cache.layers.layers.get(*idx).map(|e| e.id)) + .collect(); + let painter = Painter::new_with_scene_cache( canvas, &self.fonts, @@ -243,7 +253,7 @@ impl Renderer { &self.scene_cache, self.config.render_policy, ); - painter.draw_layer_list(&self.scene_cache.layers); + painter.draw_layer_list_culled(&self.scene_cache.layers, &visible_ids); painter.cache_picture_hits() } diff --git a/crates/grida-canvas/tests/viewport_culling.rs b/crates/grida-canvas/tests/viewport_culling.rs new file mode 100644 index 0000000000..2e3ece1e7a --- /dev/null +++ b/crates/grida-canvas/tests/viewport_culling.rs @@ -0,0 +1,191 @@ +//! Tests that viewport culling actually reduces the number of layers drawn. +//! +//! Creates a grid of nodes, positions the camera to show only a portion, +//! and verifies that `cache_picture_used` reflects only visible nodes +//! rather than the full scene. + +use cg::cg::prelude::*; +use cg::node::factory::NodeFactory; +use cg::node::scene_graph::{Parent, SceneGraph}; +use cg::node::schema::*; +use cg::runtime::camera::Camera2D; +use cg::runtime::scene::{Backend, FrameFlushResult, Renderer}; + +/// Create a 10x10 grid of 20x20 rectangles with 10px gap. +/// Scene spans 300x300 world pixels (10 * 30). +fn create_10x10_grid() -> Scene { + let nf = NodeFactory::new(); + let mut graph = SceneGraph::new(); + let size = 20.0f32; + let gap = 10.0f32; + + for y in 0..10u32 { + for x in 0..10u32 { + let mut rect = nf.create_rectangle_node(); + rect.transform = math2::transform::AffineTransform::new( + x as f32 * (size + gap), + y as f32 * (size + gap), + 0.0, + ); + rect.size = Size { + width: size, + height: size, + }; + rect.set_fill(Paint::Solid(SolidPaint::RED)); + graph.append_child(Node::Rectangle(rect), Parent::Root); + } + } + + Scene { + name: "10x10 grid".to_string(), + graph, + background_color: None, + } +} + +fn make_renderer(scene: Scene, vp_w: i32, vp_h: i32) -> Renderer { + let mut renderer = Renderer::new( + Backend::new_from_raster(vp_w, vp_h), + None, + Camera2D::new(Size { + width: vp_w as f32, + height: vp_h as f32, + }), + ); + renderer.load_scene(scene); + renderer +} + +fn flush_and_get_stats( + renderer: &mut Renderer, +) -> cg::runtime::scene::FrameFlushStats { + match renderer.flush() { + FrameFlushResult::OK(stats) => stats, + other => panic!("Expected OK flush, got {:?}", match other { + FrameFlushResult::NoPending => "NoPending", + FrameFlushResult::NoFrame => "NoFrame", + FrameFlushResult::NoScene => "NoScene", + _ => "OK", + }), + } +} + +#[test] +fn zoomed_in_draws_fewer_layers_than_zoomed_out() { + // Scene: 100 nodes in a 300x300 world area. + // Viewport: 200x200 pixels. + let scene = create_10x10_grid(); + let mut renderer = make_renderer(scene, 200, 200); + + // --- Frame 1: zoomed out, everything visible --- + // Center on scene, zoom out so 300x300 world fits in 200x200 viewport + renderer.camera.set_center(150.0, 150.0); + renderer.camera.set_zoom(200.0 / 350.0); // ~0.57, shows full scene + renderer.queue_stable(); + let stats_all = flush_and_get_stats(&mut renderer); + + // --- Frame 2: zoomed in, only a small corner visible --- + // Zoom to 4x at top-left: viewport shows 50x50 world pixels + // That covers ~2x2 = 4 nodes out of 100 + renderer.camera.set_center(0.0, 0.0); + renderer.camera.set_zoom(4.0); + renderer.queue_stable(); + let stats_zoomed = flush_and_get_stats(&mut renderer); + + let all_display_list = stats_all.frame.display_list_size_estimated; + let zoomed_display_list = stats_zoomed.frame.display_list_size_estimated; + + // The zoomed-in frame should have a significantly smaller display list + // (the frame plan only includes visible layers from R-tree query) + assert!( + zoomed_display_list < all_display_list, + "Zoomed-in display list ({zoomed_display_list}) should be smaller than \ + zoomed-out display list ({all_display_list})" + ); + + // Zoomed-in should see at most ~25% of total nodes + // (50x50 world area out of 300x300) + assert!( + zoomed_display_list <= all_display_list / 2, + "Zoomed-in display list ({zoomed_display_list}) should be at most half of \ + zoomed-out ({all_display_list}), got ratio {:.2}", + zoomed_display_list as f64 / all_display_list as f64 + ); + + println!( + "all_visible: display_list={all_display_list}, pic_used={}", + stats_all.draw.cache_picture_used + ); + println!( + "zoomed_in: display_list={zoomed_display_list}, pic_used={}", + stats_zoomed.draw.cache_picture_used + ); +} + +#[test] +fn offscreen_camera_draws_zero_layers() { + let scene = create_10x10_grid(); + let mut renderer = make_renderer(scene, 200, 200); + + // Point camera far away from the scene (scene is at 0..300) + renderer.camera.set_center(9999.0, 9999.0); + renderer.camera.set_zoom(1.0); + renderer.queue_stable(); + let stats = flush_and_get_stats(&mut renderer); + + assert_eq!( + stats.frame.display_list_size_estimated, 0, + "Off-screen camera should have empty display list, got {}", + stats.frame.display_list_size_estimated + ); + + // With culling, zero pictures should be used + assert_eq!( + stats.draw.cache_picture_used, 0, + "Off-screen camera should use 0 cached pictures, got {}", + stats.draw.cache_picture_used + ); +} + +#[test] +fn pan_changes_visible_set() { + // Scene: 100 nodes in 300x300 world. + // Viewport: 100x100 at zoom=1 → sees ~100x100 world area → ~3x3 = 9 nodes max. + let scene = create_10x10_grid(); + let mut renderer = make_renderer(scene, 100, 100); + + // Pan to top-left corner + renderer.camera.set_center(0.0, 0.0); + renderer.camera.set_zoom(1.0); + renderer.queue_stable(); + let stats_topleft = flush_and_get_stats(&mut renderer); + + // Pan to bottom-right corner + renderer.camera.set_center(270.0, 270.0); + renderer.camera.set_zoom(1.0); + renderer.queue_stable(); + let stats_bottomright = flush_and_get_stats(&mut renderer); + + // Both should show a small subset of 100 nodes + assert!( + stats_topleft.frame.display_list_size_estimated < 50, + "Top-left view should show fewer than 50 nodes, got {}", + stats_topleft.frame.display_list_size_estimated + ); + assert!( + stats_bottomright.frame.display_list_size_estimated < 50, + "Bottom-right view should show fewer than 50 nodes, got {}", + stats_bottomright.frame.display_list_size_estimated + ); + + println!( + "top_left: display_list={}, pic_used={}", + stats_topleft.frame.display_list_size_estimated, + stats_topleft.draw.cache_picture_used, + ); + println!( + "bottom_right: display_list={}, pic_used={}", + stats_bottomright.frame.display_list_size_estimated, + stats_bottomright.draw.cache_picture_used, + ); +} From 605e06f17047bcc2817a465739d76d2d3f388e1b Mon Sep 17 00:00:00 2001 From: Universe Date: Tue, 10 Mar 2026 19:32:42 +0900 Subject: [PATCH 02/16] feat(cg): headless GPU rendering module Add HeadlessGpu for windowless GPU-backed Skia rendering, useful for benchmarks, tests, CLI tools, and future SDK usage. Gated behind the native-gl-context feature flag so cg remains platform-agnostic by default. - Add cg::window::headless module (CGL on macOS, EGL on Linux) - Add headless_gpu example with per-scenario timing stats - Add glutin + raw-window-handle as optional deps behind native-gl-context - Enable native-gl-context in grida-dev --- Cargo.lock | 2 + crates/grida-canvas/Cargo.toml | 9 + crates/grida-canvas/examples/headless_gpu.rs | 113 ++++++++ crates/grida-canvas/src/window/headless.rs | 265 +++++++++++++++++++ crates/grida-canvas/src/window/mod.rs | 2 + crates/grida-dev/Cargo.toml | 1 + 6 files changed, 392 insertions(+) create mode 100644 crates/grida-canvas/examples/headless_gpu.rs create mode 100644 crates/grida-canvas/src/window/headless.rs diff --git a/Cargo.lock b/Cargo.lock index 80a7a3ebdb..6ba92a5f52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -497,11 +497,13 @@ dependencies = [ "figma-api", "futures", "gl", + "glutin", "grida-text-edit", "imgref", "json-patch", "math2", "pulldown-cmark", + "raw-window-handle", "rendiff", "reqwest", "rstar", diff --git a/crates/grida-canvas/Cargo.toml b/crates/grida-canvas/Cargo.toml index c0fee360b1..190fbce774 100644 --- a/crates/grida-canvas/Cargo.toml +++ b/crates/grida-canvas/Cargo.toml @@ -49,6 +49,10 @@ futures = "0.3.31" # (+3mb@opt-level=3 +1.7mb@opt-level=z wasm32-unknown-emscripten) figma-api = { version = "0.31.4", optional = true } +# native GL context creation (headless GPU rendering, CLI, SDK) +glutin = { version = "0.32.0", optional = true } +raw-window-handle = { version = "0.6.0", optional = true } + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] reqwest = "0.12.19" tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } @@ -58,6 +62,7 @@ default = [] figma = ["figma-api"] web = [] native-clock-tick = [] +native-gl-context = ["dep:glutin", "dep:raw-window-handle"] [dev-dependencies] criterion = "0.5" @@ -78,3 +83,7 @@ harness = false name = "bench_viewport_culling" harness = false +[[example]] +name = "headless_gpu" +required-features = ["native-gl-context"] + diff --git a/crates/grida-canvas/examples/headless_gpu.rs b/crates/grida-canvas/examples/headless_gpu.rs new file mode 100644 index 0000000000..13ee1cd661 --- /dev/null +++ b/crates/grida-canvas/examples/headless_gpu.rs @@ -0,0 +1,113 @@ +//! Headless GPU rendering demo. +//! +//! Renders a 4900-node scene using the GPU backend without opening a window, +//! and prints per-frame timing stats for various camera positions. +//! +//! Usage: +//! cargo run -p cg --example headless_gpu --features native-gl-context [--release] + +use cg::cg::prelude::*; +use cg::node::factory::NodeFactory; +use cg::node::scene_graph::{Parent, SceneGraph}; +use cg::node::schema::*; +use cg::runtime::scene::FrameFlushResult; +use cg::window::headless::HeadlessGpu; +use std::time::Instant; + +fn create_grid_scene(cols: u32, rows: u32) -> Scene { + let nf = NodeFactory::new(); + let mut graph = SceneGraph::new(); + let size = 20.0f32; + let gap = 10.0f32; + for y in 0..rows { + for x in 0..cols { + let mut rect = nf.create_rectangle_node(); + rect.transform = math2::transform::AffineTransform::new( + x as f32 * (size + gap), + y as f32 * (size + gap), + 0.0, + ); + rect.size = Size { width: size, height: size }; + rect.set_fill(Paint::Solid(SolidPaint { + color: CGColor::from_rgba( + ((x * 7) % 255) as u8, + ((y * 11) % 255) as u8, + 180, 255, + ), + blend_mode: BlendMode::default(), + active: true, + })); + graph.append_child(Node::Rectangle(rect), Parent::Root); + } + } + Scene { + name: "Grid".to_string(), + graph, + background_color: Some(CGColor::WHITE), + } +} + +fn main() { + println!("=== Headless GPU Rendering Demo ===\n"); + + let mut gpu = HeadlessGpu::new(1000, 1000).expect("GPU init"); + gpu.print_gl_info(); + + let scene = create_grid_scene(70, 70); + let mut renderer = gpu.create_renderer(); + renderer.load_scene(scene); + println!("Loaded scene: 4900 nodes\n"); + + let scenarios: Vec<(&str, f32, f32, f32)> = vec![ + ("all_visible (zoom out)", 1050.0, 1050.0, 0.45), + ("partial (~25%)", 0.0, 0.0, 1.0), + ("corner (~5%)", 0.0, 0.0, 2.0), + ("zoomed_in (~1%)", 1050.0, 1050.0, 10.0), + ("empty (offscreen)", 9999.0, 9999.0, 1.0), + ]; + + let num_frames = 100; + + for (name, cx, cy, zoom) in &scenarios { + renderer.camera.set_center(*cx, *cy); + renderer.camera.set_zoom(*zoom); + + // Warm up + for _ in 0..5 { + renderer.camera.translate(0.1, 0.0); + renderer.queue_unstable(); + let _ = renderer.flush(); + } + + // Measure + let start = Instant::now(); + let mut total_frame_us = 0u64; + let mut display_list_total = 0usize; + let mut pic_used_total = 0usize; + + for i in 0..num_frames { + let dx = if i % 2 == 0 { 1.0 } else { -1.0 }; + renderer.camera.translate(dx, 0.0); + renderer.queue_unstable(); + if let FrameFlushResult::OK(stats) = renderer.flush() { + total_frame_us += stats.frame_duration.as_micros() as u64; + display_list_total += stats.frame.display_list_size_estimated; + pic_used_total += stats.draw.cache_picture_used; + } + } + + let wall_ms = start.elapsed().as_secs_f64() * 1000.0; + let avg_frame_us = total_frame_us / num_frames as u64; + let avg_dl = display_list_total / num_frames; + let avg_pic = pic_used_total / num_frames; + let fps = 1_000_000.0 / avg_frame_us as f64; + + println!( + " {name:<25} | avg {avg_frame_us:>6} \u{00b5}s ({fps:>7.1} fps) | \ + dl: {avg_dl:>5} | pic: {avg_pic:>5} | wall: {wall_ms:.1} ms", + ); + } + + drop(renderer); + println!("\nDone."); +} diff --git a/crates/grida-canvas/src/window/headless.rs b/crates/grida-canvas/src/window/headless.rs new file mode 100644 index 0000000000..6df297875e --- /dev/null +++ b/crates/grida-canvas/src/window/headless.rs @@ -0,0 +1,265 @@ +//! Headless GPU rendering context. +//! +//! Creates an OpenGL context and GPU-backed Skia surface without opening a +//! window. Useful for benchmarks, tests, CLI tools, and SDK usage that need +//! GPU rendering but no display. +//! +//! Requires the `native-gl-context` feature. +//! +//! # Usage +//! +//! ```rust,ignore +//! use cg::window::headless::HeadlessGpu; +//! +//! let mut gpu = HeadlessGpu::new(1000, 1000).expect("GPU init"); +//! let mut renderer = gpu.create_renderer(); +//! renderer.load_scene(scene); +//! renderer.queue_stable(); +//! let _ = renderer.flush(); +//! ``` +//! +//! # Platform support +//! +//! | OS | Backend | Method | +//! |---------|---------|------------------------------------| +//! | macOS | CGL | `make_current_surfaceless` | +//! | Linux | EGL | `make_current_surfaceless` | +//! | Windows | — | not yet supported (needs WGL hack) | + +use crate::node::schema::Size; +use crate::runtime::camera::Camera2D; +use crate::runtime::scene::{Backend, Renderer}; +use gl::types::*; +use glutin::config::ConfigTemplateBuilder; +use glutin::context::{ContextApi, ContextAttributesBuilder, Version}; +use glutin::display::GlDisplay; +use skia_safe::gpu; +use std::ffi::CString; + +/// A headless GPU rendering context owning the GL state and Skia surface. +/// +/// Drop order matters: `surface` must be dropped before `gr_context`, and +/// `gr_context` before `_gl_context`. Rust drops fields in declaration order, +/// so the field order here is intentional. +pub struct HeadlessGpu { + pub surface: skia_safe::Surface, + pub gr_context: gpu::DirectContext, + /// Keep the GL context alive. Never accessed directly. + _gl_context: glutin::context::PossiblyCurrentContext, + width: i32, + height: i32, +} + +impl HeadlessGpu { + /// Create a new headless GPU context with an offscreen surface of the + /// given dimensions. + pub fn new(width: i32, height: i32) -> Result { + let (gl_context, gr_context) = create_headless_gl_context()?; + let mut gr = gr_context; + let surface = create_gpu_surface(&mut gr, width, height)?; + Ok(Self { + surface, + gr_context: gr, + _gl_context: gl_context, + width, + height, + }) + } + + /// Build a [`Renderer`] that draws to this GPU surface. + /// + /// The renderer borrows the surface via raw pointer, so `self` must + /// outlive the returned `Renderer`. + pub fn create_renderer(&mut self) -> Renderer { + let surface_ptr: *mut skia_safe::Surface = &mut self.surface; + Renderer::new( + Backend::GL(surface_ptr), + None, + Camera2D::new(Size { + width: self.width as f32, + height: self.height as f32, + }), + ) + } + + /// Print GL vendor, renderer, and version to stdout. + pub fn print_gl_info(&self) { + println!(" GL Vendor: {}", gl_string(gl::VENDOR)); + println!(" GL Renderer: {}", gl_string(gl::RENDERER)); + println!(" GL Version: {}", gl_string(gl::VERSION)); + } + + /// Width of the offscreen surface in pixels. + pub fn width(&self) -> i32 { + self.width + } + + /// Height of the offscreen surface in pixels. + pub fn height(&self) -> i32 { + self.height + } +} + +// --------------------------------------------------------------------------- +// Platform-specific GL bootstrap +// --------------------------------------------------------------------------- + +fn create_headless_gl_context( +) -> Result<(glutin::context::PossiblyCurrentContext, gpu::DirectContext), String> { + use glutin::display::DisplayApiPreference; + + #[cfg(target_os = "macos")] + let pref = DisplayApiPreference::Cgl; + #[cfg(target_os = "linux")] + let pref = DisplayApiPreference::Egl; + #[cfg(target_os = "windows")] + return Err("Headless GPU is not yet supported on Windows".into()); + + #[cfg(not(target_os = "windows"))] + { + // 1. Display + let display = unsafe { + glutin::display::Display::new(platform_raw_display_handle(), pref) + .map_err(|e| format!("glutin display: {e}"))? + }; + + // 2. Config (pixel format) + let template = ConfigTemplateBuilder::new() + .with_alpha_size(8) + .with_stencil_size(8) + .with_depth_size(24); + let gl_config = unsafe { + display + .find_configs(template.build()) + .map_err(|e| format!("find configs: {e}"))? + .next() + .ok_or("no GL config available")? + }; + + // 3. Context (no window handle) + let ctx_attrs = ContextAttributesBuilder::new() + .with_context_api(ContextApi::OpenGl(Some(Version::new(3, 3)))) + .build(None); + let fallback = ContextAttributesBuilder::new() + .with_context_api(ContextApi::OpenGl(None)) + .build(None); + let not_current = unsafe { + display + .create_context(&gl_config, &ctx_attrs) + .or_else(|_| display.create_context(&gl_config, &fallback)) + .map_err(|e| format!("create context: {e}"))? + }; + + // 4. Make current (surfaceless — no window, no pbuffer) + let gl_context = make_current_surfaceless(not_current)?; + + // 5. Load GL function pointers + gl::load_with(|s| { + let Ok(cstr) = CString::new(s) else { + return std::ptr::null(); + }; + display.get_proc_address(cstr.as_c_str()) + }); + + // 6. Skia GL interface + DirectContext + let interface = gpu::gl::Interface::new_load_with(|name| { + if name == "eglGetCurrentDisplay" { + return std::ptr::null(); + } + let Ok(cstr) = CString::new(name) else { + return std::ptr::null(); + }; + display.get_proc_address(cstr.as_c_str()) + }) + .ok_or("failed to create Skia GL interface")?; + + let gr_context = gpu::direct_contexts::make_gl(interface, None) + .ok_or("failed to create Skia DirectContext")?; + + Ok((gl_context, gr_context)) + } +} + +/// Create an offscreen GPU-backed Skia surface. Skia manages its own FBO. +fn create_gpu_surface( + gr_context: &mut gpu::DirectContext, + width: i32, + height: i32, +) -> Result { + gpu::surfaces::render_target( + gr_context, + gpu::Budgeted::Yes, + &skia_safe::ImageInfo::new_n32_premul((width, height), None), + Some(0), + gpu::SurfaceOrigin::TopLeft, + None, + false, + None, + ) + .ok_or_else(|| "failed to create GPU render target surface".into()) +} + +// --------------------------------------------------------------------------- +// Platform helpers +// --------------------------------------------------------------------------- + +#[cfg(target_os = "macos")] +fn platform_raw_display_handle() -> raw_window_handle::RawDisplayHandle { + use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle}; + RawDisplayHandle::AppKit(AppKitDisplayHandle::new()) +} + +#[cfg(target_os = "linux")] +fn platform_raw_display_handle() -> raw_window_handle::RawDisplayHandle { + use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle}; + RawDisplayHandle::Xlib(XlibDisplayHandle::new(None, 0)) +} + +/// Make a GL context current without binding it to any surface. +#[cfg(target_os = "macos")] +fn make_current_surfaceless( + ctx: glutin::context::NotCurrentContext, +) -> Result { + match ctx { + glutin::context::NotCurrentContext::Cgl(cgl) => { + let current = cgl + .make_current_surfaceless() + .map_err(|e| format!("CGL make_current_surfaceless: {e}"))?; + Ok(glutin::context::PossiblyCurrentContext::Cgl(current)) + } + #[allow(unreachable_patterns)] + _ => Err("expected CGL context on macOS".into()), + } +} + +#[cfg(target_os = "linux")] +fn make_current_surfaceless( + ctx: glutin::context::NotCurrentContext, +) -> Result { + match ctx { + glutin::context::NotCurrentContext::Egl(egl) => { + let current = egl + .make_current_surfaceless() + .map_err(|e| format!("EGL make_current_surfaceless: {e}"))?; + Ok(glutin::context::PossiblyCurrentContext::Egl(current)) + } + #[allow(unreachable_patterns)] + _ => Err("expected EGL context on Linux".into()), + } +} + +// --------------------------------------------------------------------------- +// Utility +// --------------------------------------------------------------------------- + +fn gl_string(name: GLenum) -> String { + unsafe { + let ptr = gl::GetString(name); + if ptr.is_null() { + return "".to_string(); + } + std::ffi::CStr::from_ptr(ptr as *const _) + .to_string_lossy() + .into_owned() + } +} diff --git a/crates/grida-canvas/src/window/mod.rs b/crates/grida-canvas/src/window/mod.rs index ea7bcb3009..31cec13a9c 100644 --- a/crates/grida-canvas/src/window/mod.rs +++ b/crates/grida-canvas/src/window/mod.rs @@ -1,5 +1,7 @@ pub mod application; pub mod command; +#[cfg(feature = "native-gl-context")] +pub mod headless; pub mod input; pub mod state; diff --git a/crates/grida-dev/Cargo.toml b/crates/grida-dev/Cargo.toml index 9a3be0bde0..9538fec493 100644 --- a/crates/grida-dev/Cargo.toml +++ b/crates/grida-dev/Cargo.toml @@ -11,6 +11,7 @@ cg = { path = "../grida-canvas", features = [ "figma", "web", "native-clock-tick", + "native-gl-context", ] } math2 = { path = "../math2" } anyhow = "1.0" From e1a179829fb0c67949e92d11e8f4a930ad61c67f Mon Sep 17 00:00:00 2001 From: Universe Date: Tue, 10 Mar 2026 19:34:12 +0900 Subject: [PATCH 03/16] perf(cg): replace HashSet with Vec bitset for visibility culling Use a Vec indexed by NodeId (sequential u64) instead of HashSet for the per-frame visible-node set. This eliminates hashing overhead and HashMap bucket allocation, giving O(1) array lookups with better cache locality on every draw call. --- crates/grida-canvas/src/painter/painter.rs | 18 +++++++++-------- crates/grida-canvas/src/runtime/scene.rs | 23 +++++++++++++++++----- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/crates/grida-canvas/src/painter/painter.rs b/crates/grida-canvas/src/painter/painter.rs index 62f85764ce..9a6c5b99f8 100644 --- a/crates/grida-canvas/src/painter/painter.rs +++ b/crates/grida-canvas/src/painter/painter.rs @@ -19,7 +19,6 @@ use skia_safe::{ Shader, }; use std::cell::RefCell; -use std::collections::HashSet; use std::rc::Rc; /// A painter that handles all drawing operations for nodes, @@ -1501,9 +1500,10 @@ impl<'a> Painter<'a> { self.draw_render_commands(&list.commands); } - /// Draw only the layers whose IDs appear in `visible`, skipping off-screen - /// content. Mask groups are drawn unconditionally to preserve clip correctness. - pub fn draw_layer_list_culled(&self, list: &LayerList, visible: &HashSet) { + /// Draw only the layers whose node ID is marked `true` in the `visible` + /// bitset (indexed by `NodeId as usize`), skipping off-screen content. + /// Mask groups are drawn unconditionally to preserve clip correctness. + pub fn draw_layer_list_culled(&self, list: &LayerList, visible: &[bool]) { if !self.policy.is_wireframe() { self.draw_render_commands_culled(&list.commands, visible); } else { @@ -1514,12 +1514,13 @@ impl<'a> Painter<'a> { fn draw_render_commands_culled( &self, commands: &[PainterRenderCommand], - visible: &HashSet, + visible: &[bool], ) { for command in commands { match command { PainterRenderCommand::Draw(layer) => { - if !visible.contains(layer.id()) { + let id = *layer.id() as usize; + if id >= visible.len() || !visible[id] { continue; } // Prefer cached picture if available @@ -1541,13 +1542,14 @@ impl<'a> Painter<'a> { } } - fn draw_layer_list_outline_culled(&self, list: &LayerList, visible: &HashSet) { + fn draw_layer_list_outline_culled(&self, list: &LayerList, visible: &[bool]) { let Some(style) = self.policy.outline_style() else { return; }; for entry in &list.layers { - if !visible.contains(&entry.id) { + let id = entry.id as usize; + if id >= visible.len() || !visible[id] { continue; } if let Some(scene_cache) = self.scene_cache { diff --git a/crates/grida-canvas/src/runtime/scene.rs b/crates/grida-canvas/src/runtime/scene.rs index a66d4a3c01..d0e86ffa6b 100644 --- a/crates/grida-canvas/src/runtime/scene.rs +++ b/crates/grida-canvas/src/runtime/scene.rs @@ -239,12 +239,25 @@ impl Renderer { // Collect visible node IDs from the frame plan so the painter can skip // off-screen layers. The plan's regions contain indices into the flat // layer list produced by the R-tree viewport query. - let visible_ids: HashSet = plan - .regions + // + // We use a Vec indexed by NodeId (which is a sequential u64) + // instead of a HashSet for O(1) lookups without hashing overhead. + let max_id = self + .scene_cache + .layers + .layers .iter() - .flat_map(|(_, indices)| indices.iter()) - .filter_map(|idx| self.scene_cache.layers.layers.get(*idx).map(|e| e.id)) - .collect(); + .map(|e| e.id as usize) + .max() + .unwrap_or(0); + let mut visible_ids = vec![false; max_id + 1]; + for (_, indices) in &plan.regions { + for idx in indices { + if let Some(entry) = self.scene_cache.layers.layers.get(*idx) { + visible_ids[entry.id as usize] = true; + } + } + } let painter = Painter::new_with_scene_cache( canvas, From fc8ee1e11b7969a22790efa35c7cfa3ede96eaec Mon Sep 17 00:00:00 2001 From: Universe Date: Tue, 10 Mar 2026 19:35:05 +0900 Subject: [PATCH 04/16] perf(cg): remove unnecessary sort of R-tree indices in frame plan The R-tree query returns layer indices in arbitrary order. The previous sort ensured Z-order, but with culled drawing the Z-order comes from the command tree traversal, not the index array. The indices are only used to build the visibility bitset and prefill the picture cache, neither of which is order-dependent. --- crates/grida-canvas/src/runtime/scene.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/grida-canvas/src/runtime/scene.rs b/crates/grida-canvas/src/runtime/scene.rs index d0e86ffa6b..7c1bfafa8e 100644 --- a/crates/grida-canvas/src/runtime/scene.rs +++ b/crates/grida-canvas/src/runtime/scene.rs @@ -771,11 +771,7 @@ impl Renderer { let mut regions: Vec<(rect::Rectangle, Vec)> = Vec::new(); for rect in painter_region { - let mut indices = self.scene_cache.intersects(rect); - - // TODO: sort is expensive - indices.sort(); - + let indices = self.scene_cache.intersects(rect); regions.push((rect, indices)); } From 0ff7abf33f18b6774626be5d3470c3db15487466 Mon Sep 17 00:00:00 2001 From: Universe Date: Tue, 10 Mar 2026 21:13:28 +0900 Subject: [PATCH 05/16] fix: update environment variable for local WASM serving Changed the environment variable from NEXT_PUBLIC_GRIDA_WASM_SERVE_URL to NEXT_PUBLIC_GRIDA_WASM_DEV_SERVE_URL in the .env.example file and updated the locateFile function to use the new variable for local WASM file serving. This change clarifies the purpose of the variable for development environments. --- editor/.env.example | 2 +- editor/grida-canvas/backends/wasm-locate-file.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/editor/.env.example b/editor/.env.example index 273a06c616..9d133c06bd 100644 --- a/editor/.env.example +++ b/editor/.env.example @@ -25,7 +25,7 @@ NEXT_PUBLIC_SUPABASE_URL_RR_AP_NORTHEAST_2=... # [insiders config] # flag to use unsafe sandbox (set 1 to enable) NEXT_PUBLIC_GRIDA_UNSAFE_DEVELOPER_SANDBOX='0' -NEXT_PUBLIC_GRIDA_WASM_SERVE_URL='http://localhost:4020/dist' # set to force using local wasm binary +NEXT_PUBLIC_GRIDA_WASM_DEV_SERVE_URL='http://localhost:4020/dist' # set to use locally served wasm (dev) NEXT_PUBLIC_GRIDA_WASM_VERBOSE='0' # set 1 to inspect (log) the wasm api NEXT_PUBLIC_GRIDA_USE_INSIDERS_AUTH='1' NEXT_PUBLIC_GRIDA_LOCALHOST_REGION="us-west-1" diff --git a/editor/grida-canvas/backends/wasm-locate-file.ts b/editor/grida-canvas/backends/wasm-locate-file.ts index 7ce36872df..18fd07ed2d 100644 --- a/editor/grida-canvas/backends/wasm-locate-file.ts +++ b/editor/grida-canvas/backends/wasm-locate-file.ts @@ -7,8 +7,8 @@ type Args = Parameters; */ export default function locateFile(...args: Args) { const [path, version] = args; - if (process.env.NEXT_PUBLIC_GRIDA_WASM_SERVE_URL) { - return `${process.env.NEXT_PUBLIC_GRIDA_WASM_SERVE_URL}/${path}`; + if (process.env.NEXT_PUBLIC_GRIDA_WASM_DEV_SERVE_URL) { + return `${process.env.NEXT_PUBLIC_GRIDA_WASM_DEV_SERVE_URL}/${path}`; } else if (process.env.NODE_ENV === "development") { return `http://localhost:4020/dist/${path}`; } else { From b11fedc1c60d30a8601712e157c207a505c6bb9b Mon Sep 17 00:00:00 2001 From: Universe Date: Tue, 10 Mar 2026 22:33:21 +0900 Subject: [PATCH 06/16] revert(cg): remove viewport culling from painter and scene Reverts the draw-time culling introduced in: - feat(cg): viewport culling at draw time - perf(cg): replace HashSet with Vec bitset for visibility culling - perf(cg): remove unnecessary sort of R-tree indices in frame plan Removes draw_layer_list_culled / draw_render_commands_culled / draw_layer_list_outline_culled from Painter, restores the draw_layer_list call in draw_layers_with_scene_cache, and re-adds the indices.sort() in the frame plan builder for Z-order correctness. Made-with: Cursor --- crates/grida-canvas/src/painter/painter.rs | 64 ---------------------- crates/grida-canvas/src/runtime/scene.rs | 31 ++--------- 2 files changed, 6 insertions(+), 89 deletions(-) diff --git a/crates/grida-canvas/src/painter/painter.rs b/crates/grida-canvas/src/painter/painter.rs index 9a6c5b99f8..bfd0f9de30 100644 --- a/crates/grida-canvas/src/painter/painter.rs +++ b/crates/grida-canvas/src/painter/painter.rs @@ -1500,70 +1500,6 @@ impl<'a> Painter<'a> { self.draw_render_commands(&list.commands); } - /// Draw only the layers whose node ID is marked `true` in the `visible` - /// bitset (indexed by `NodeId as usize`), skipping off-screen content. - /// Mask groups are drawn unconditionally to preserve clip correctness. - pub fn draw_layer_list_culled(&self, list: &LayerList, visible: &[bool]) { - if !self.policy.is_wireframe() { - self.draw_render_commands_culled(&list.commands, visible); - } else { - self.draw_layer_list_outline_culled(list, visible); - } - } - - fn draw_render_commands_culled( - &self, - commands: &[PainterRenderCommand], - visible: &[bool], - ) { - for command in commands { - match command { - PainterRenderCommand::Draw(layer) => { - let id = *layer.id() as usize; - if id >= visible.len() || !visible[id] { - continue; - } - // Prefer cached picture if available - if let Some(scene_cache) = self.scene_cache { - if let Some(pic) = - scene_cache.get_node_picture_variant(layer.id(), self.variant_key) - { - self.canvas.draw_picture(pic, None, None); - *self.cache_hits.borrow_mut() += 1; - continue; - } - } - self.draw_layer(layer) - } - PainterRenderCommand::MaskGroup(group) => { - self.draw_mask_group_or_passthrough(group) - } - } - } - } - - fn draw_layer_list_outline_culled(&self, list: &LayerList, visible: &[bool]) { - let Some(style) = self.policy.outline_style() else { - return; - }; - - for entry in &list.layers { - let id = entry.id as usize; - if id >= visible.len() || !visible[id] { - continue; - } - if let Some(scene_cache) = self.scene_cache { - if let Some(pic) = scene_cache.get_node_picture_variant(&entry.id, self.variant_key) - { - self.canvas.draw_picture(pic, None, None); - *self.cache_hits.borrow_mut() += 1; - continue; - } - } - self.draw_layer_outline(&entry.layer, style); - } - } - fn draw_layer_list_outline(&self, list: &LayerList) { let Some(style) = self.policy.outline_style() else { // Non-standard policy without an outline style: nothing to draw. diff --git a/crates/grida-canvas/src/runtime/scene.rs b/crates/grida-canvas/src/runtime/scene.rs index 7c1bfafa8e..384547c1cf 100644 --- a/crates/grida-canvas/src/runtime/scene.rs +++ b/crates/grida-canvas/src/runtime/scene.rs @@ -236,29 +236,6 @@ impl Renderer { fn draw_layers_with_scene_cache(&mut self, canvas: &Canvas, plan: &FramePlan) -> usize { self.prefill_picture_cache_for_plan(plan); - // Collect visible node IDs from the frame plan so the painter can skip - // off-screen layers. The plan's regions contain indices into the flat - // layer list produced by the R-tree viewport query. - // - // We use a Vec indexed by NodeId (which is a sequential u64) - // instead of a HashSet for O(1) lookups without hashing overhead. - let max_id = self - .scene_cache - .layers - .layers - .iter() - .map(|e| e.id as usize) - .max() - .unwrap_or(0); - let mut visible_ids = vec![false; max_id + 1]; - for (_, indices) in &plan.regions { - for idx in indices { - if let Some(entry) = self.scene_cache.layers.layers.get(*idx) { - visible_ids[entry.id as usize] = true; - } - } - } - let painter = Painter::new_with_scene_cache( canvas, &self.fonts, @@ -266,7 +243,7 @@ impl Renderer { &self.scene_cache, self.config.render_policy, ); - painter.draw_layer_list_culled(&self.scene_cache.layers, &visible_ids); + painter.draw_layer_list(&self.scene_cache.layers); painter.cache_picture_hits() } @@ -771,7 +748,11 @@ impl Renderer { let mut regions: Vec<(rect::Rectangle, Vec)> = Vec::new(); for rect in painter_region { - let indices = self.scene_cache.intersects(rect); + let mut indices = self.scene_cache.intersects(rect); + + // TODO: sort is expensive + indices.sort(); + regions.push((rect, indices)); } From 4897f17438d567961d2f276ec9855f72b46306a9 Mon Sep 17 00:00:00 2001 From: Universe Date: Wed, 11 Mar 2026 02:23:17 +0900 Subject: [PATCH 07/16] format save GRID magic bits --- packages/grida-canvas-io/format.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grida-canvas-io/format.ts b/packages/grida-canvas-io/format.ts index da3fb2eaca..9dbfd56682 100644 --- a/packages/grida-canvas-io/format.ts +++ b/packages/grida-canvas-io/format.ts @@ -4518,7 +4518,7 @@ export namespace format { fbs.GridaFile.addDocument(builder, documentOffset); const rootOffset = fbs.GridaFile.endGridaFile(builder); - builder.finish(rootOffset); + builder.finish(rootOffset, "GRID"); return builder.asUint8Array(); } From 057686793c511f0c1a96607c5a603a0de4b7486d Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 12 Mar 2026 01:38:57 +0900 Subject: [PATCH 08/16] Update schema version to 0.91.0-beta+20260311 across multiple files --- crates/grida-canvas-wasm/example/demo.grida1 | 2 +- crates/grida-canvas-wasm/example/gradient.grida1 | 2 +- crates/grida-canvas-wasm/example/rectangle.grida1 | 2 +- .../lib/__test__/environment-node-raster-export.test.ts | 2 +- crates/grida-canvas/src/io/io_grida.rs | 6 +++--- editor/grida-canvas/editor.ts | 2 +- editor/public/examples/canvas/blank.grida1 | 2 +- editor/public/examples/canvas/component-01.grida1 | 2 +- editor/public/examples/canvas/globals-01.grida1 | 2 +- editor/public/examples/canvas/helloworld.grida1 | 2 +- editor/public/examples/canvas/hero-main-demo.grida1 | 2 +- editor/public/examples/canvas/layout-01.grida1 | 2 +- .../examples/canvas/poster-happy-new-year-2026.grida1 | 2 +- editor/scaffolds/editor/editor.tsx | 2 +- editor/scaffolds/editor/init.ts | 4 ++-- editor/scaffolds/editor/sync/agent-startpage.sync.tsx | 2 +- packages/grida-canvas-schema/grida.ts | 2 +- packages/grida-format/src/__tests__/index.test.ts | 4 ++-- 18 files changed, 22 insertions(+), 22 deletions(-) diff --git a/crates/grida-canvas-wasm/example/demo.grida1 b/crates/grida-canvas-wasm/example/demo.grida1 index 9cc296743c..42877cfdf8 100644 --- a/crates/grida-canvas-wasm/example/demo.grida1 +++ b/crates/grida-canvas-wasm/example/demo.grida1 @@ -9889,5 +9889,5 @@ "main" ] }, - "version": "0.90.0-beta+20260108" + "version": "0.91.0-beta+20260311" } \ No newline at end of file diff --git a/crates/grida-canvas-wasm/example/gradient.grida1 b/crates/grida-canvas-wasm/example/gradient.grida1 index dd00e65a6c..f336470f86 100644 --- a/crates/grida-canvas-wasm/example/gradient.grida1 +++ b/crates/grida-canvas-wasm/example/gradient.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "gradient-rect": { diff --git a/crates/grida-canvas-wasm/example/rectangle.grida1 b/crates/grida-canvas-wasm/example/rectangle.grida1 index 2d4507033b..e084bb2848 100644 --- a/crates/grida-canvas-wasm/example/rectangle.grida1 +++ b/crates/grida-canvas-wasm/example/rectangle.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "rectangle": { diff --git a/crates/grida-canvas-wasm/lib/__test__/environment-node-raster-export.test.ts b/crates/grida-canvas-wasm/lib/__test__/environment-node-raster-export.test.ts index 051095d2ad..da5d34bb29 100644 --- a/crates/grida-canvas-wasm/lib/__test__/environment-node-raster-export.test.ts +++ b/crates/grida-canvas-wasm/lib/__test__/environment-node-raster-export.test.ts @@ -76,7 +76,7 @@ describe("raster export (node)", () => { const imageBytes = new Uint8Array(readFileSync(imagePath)); const doc = { - version: "0.90.0-beta+20260108", + version: "0.91.0-beta+20260311", document: { nodes: { "image-rect": { diff --git a/crates/grida-canvas/src/io/io_grida.rs b/crates/grida-canvas/src/io/io_grida.rs index 81e402ac23..f0256b82a7 100644 --- a/crates/grida-canvas/src/io/io_grida.rs +++ b/crates/grida-canvas/src/io/io_grida.rs @@ -3214,7 +3214,7 @@ mod tests { #[test] fn parse_grida_file_new_format() { let json = r#"{ - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "main": { @@ -3271,7 +3271,7 @@ mod tests { fn parse_grida_file_with_container_children() { // Test that container nodes with children in links work correctly let json = r#"{ - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "main": { @@ -3338,7 +3338,7 @@ mod tests { fn test_nested_children_population() { // Test that deeply nested children get properly populated from links let json = r#"{ - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "main": { diff --git a/editor/grida-canvas/editor.ts b/editor/grida-canvas/editor.ts index 9f2274b071..e3f81881f5 100644 --- a/editor/grida-canvas/editor.ts +++ b/editor/grida-canvas/editor.ts @@ -3149,7 +3149,7 @@ export class Editor : document; const p = JSON.stringify({ - version: "0.90.0-beta+20260108", + version: "0.91.0-beta+20260311", document: payloadDocument, }); surface.loadScene(p); diff --git a/editor/public/examples/canvas/blank.grida1 b/editor/public/examples/canvas/blank.grida1 index b544e846de..b3b55a6fd0 100644 --- a/editor/public/examples/canvas/blank.grida1 +++ b/editor/public/examples/canvas/blank.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "blank": { diff --git a/editor/public/examples/canvas/component-01.grida1 b/editor/public/examples/canvas/component-01.grida1 index 20c863f2bd..a317e9d619 100644 --- a/editor/public/examples/canvas/component-01.grida1 +++ b/editor/public/examples/canvas/component-01.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "component": { diff --git a/editor/public/examples/canvas/globals-01.grida1 b/editor/public/examples/canvas/globals-01.grida1 index bb610c2eb6..793211f93d 100644 --- a/editor/public/examples/canvas/globals-01.grida1 +++ b/editor/public/examples/canvas/globals-01.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "root": { diff --git a/editor/public/examples/canvas/helloworld.grida1 b/editor/public/examples/canvas/helloworld.grida1 index 9776368082..0b51a553d6 100644 --- a/editor/public/examples/canvas/helloworld.grida1 +++ b/editor/public/examples/canvas/helloworld.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "454:341": { diff --git a/editor/public/examples/canvas/hero-main-demo.grida1 b/editor/public/examples/canvas/hero-main-demo.grida1 index d5e7aece5c..3d8ab01642 100644 --- a/editor/public/examples/canvas/hero-main-demo.grida1 +++ b/editor/public/examples/canvas/hero-main-demo.grida1 @@ -9889,5 +9889,5 @@ "main" ] }, - "version": "0.90.0-beta+20260108" + "version": "0.91.0-beta+20260311" } \ No newline at end of file diff --git a/editor/public/examples/canvas/layout-01.grida1 b/editor/public/examples/canvas/layout-01.grida1 index f95d0f3509..df3bd6dfac 100644 --- a/editor/public/examples/canvas/layout-01.grida1 +++ b/editor/public/examples/canvas/layout-01.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "nodes": { "173:49": { diff --git a/editor/public/examples/canvas/poster-happy-new-year-2026.grida1 b/editor/public/examples/canvas/poster-happy-new-year-2026.grida1 index 8a79f73d78..f9cda71acb 100644 --- a/editor/public/examples/canvas/poster-happy-new-year-2026.grida1 +++ b/editor/public/examples/canvas/poster-happy-new-year-2026.grida1 @@ -1,5 +1,5 @@ { - "version": "0.90.0-beta+20260108", + "version": "0.91.0-beta+20260311", "document": { "links": { "main": [ diff --git a/editor/scaffolds/editor/editor.tsx b/editor/scaffolds/editor/editor.tsx index 2d91dd8543..88bcf10a0d 100644 --- a/editor/scaffolds/editor/editor.tsx +++ b/editor/scaffolds/editor/editor.tsx @@ -159,7 +159,7 @@ async function saveHostedGridaCanvasDocument( .update({ data: document ? ({ - __schema_version: "0.90.0-beta+20260108", + __schema_version: "0.91.0-beta+20260311", ...document, } satisfies CanvasDocumentSnapshotSchema as {}) : null, diff --git a/editor/scaffolds/editor/init.ts b/editor/scaffolds/editor/init.ts index 35db4e89f7..18fff03370 100644 --- a/editor/scaffolds/editor/init.ts +++ b/editor/scaffolds/editor/init.ts @@ -316,7 +316,7 @@ function __init_canvas( // check the version if ( (data as SchemaMayVaryDocumentServerObject).__schema_version !== - "0.90.0-beta+20260108" + "0.91.0-beta+20260311" ) { return { __schema_version: (data as SchemaMayVaryDocumentServerObject) @@ -349,7 +349,7 @@ function __init_form_start_page_state( // check the version if ( - (data as FormStartPageSchema).__schema_version !== "0.90.0-beta+20260108" + (data as FormStartPageSchema).__schema_version !== "0.91.0-beta+20260311" ) { return { __schema_version: (data as FormStartPageSchema).__schema_version, diff --git a/editor/scaffolds/editor/sync/agent-startpage.sync.tsx b/editor/scaffolds/editor/sync/agent-startpage.sync.tsx index f91ca5359a..661a66559b 100644 --- a/editor/scaffolds/editor/sync/agent-startpage.sync.tsx +++ b/editor/scaffolds/editor/sync/agent-startpage.sync.tsx @@ -30,7 +30,7 @@ export function useSyncFormAgentStartPage() { .update({ start_page: debounced ? ({ - __schema_version: "0.90.0-beta+20260108", + __schema_version: "0.91.0-beta+20260311", template_id: startpagestate!.template_id, ...debounced, } satisfies FormStartPageSchema as {}) diff --git a/packages/grida-canvas-schema/grida.ts b/packages/grida-canvas-schema/grida.ts index 8b66f7eea0..471f8657cc 100644 --- a/packages/grida-canvas-schema/grida.ts +++ b/packages/grida-canvas-schema/grida.ts @@ -530,7 +530,7 @@ export namespace grida { } export namespace grida.program.document { - export const SCHEMA_VERSION = "0.90.0-beta+20260108"; + export const SCHEMA_VERSION = "0.91.0-beta+20260311"; /** * JSON-serializable value type diff --git a/packages/grida-format/src/__tests__/index.test.ts b/packages/grida-format/src/__tests__/index.test.ts index e02fc1b4d4..e306e1a04e 100644 --- a/packages/grida-format/src/__tests__/index.test.ts +++ b/packages/grida-format/src/__tests__/index.test.ts @@ -15,7 +15,7 @@ describe("@grida/format", () => { const builder = new flatbuffers.Builder(1024); // Build schema version string - const schemaVersion = builder.createString("0.90.0-beta+20260108"); + const schemaVersion = builder.createString("0.91.0-beta+20260311"); // Build empty arrays for nodes, links, scenes const nodesOffset = fbs.CanvasDocument.createNodesVector(builder, []); @@ -46,7 +46,7 @@ describe("@grida/format", () => { const document = gridaFile.document(); expect(document).toBeDefined(); - expect(document?.schemaVersion()).toBe("0.90.0-beta+20260108"); + expect(document?.schemaVersion()).toBe("0.91.0-beta+20260311"); expect(document?.nodesLength()).toBe(0); expect(document?.scenesLength()).toBe(0); }); From 2ae75628ea4e41a6725cd8a30879808e3551e3e0 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 12 Mar 2026 14:03:36 +0900 Subject: [PATCH 09/16] Enhance image source handling in editor and query modules - Updated image source collection to exclude `system://` built-in resources, ensuring they are not embedded in archives. - Introduced `persistable_image_srcs()` method to filter out non-persistable image sources, improving clarity and functionality in image management. This change refines how image sources are processed, aligning with the overall architecture for resource handling. --- editor/grida-canvas/editor.ts | 8 ++++++-- editor/grida-canvas/query/index.ts | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/editor/grida-canvas/editor.ts b/editor/grida-canvas/editor.ts index e3f81881f5..81c4541fa1 100644 --- a/editor/grida-canvas/editor.ts +++ b/editor/grida-canvas/editor.ts @@ -2891,9 +2891,11 @@ export class Editor * - OPFS persistence (playground `Cmd/Ctrl+S`) * * What it does: - * - Collects **used** image paint `src` references (res://images/). + * - Collects **used** image paint `src` references (res://images/), excluding system:// built-ins. * - Parses ref from each src, resolves bytes from WASM, produces `images/.` entries. * - Document keeps `src` unchanged (no rewrite). + * - `system://` images (built-in runtime resources) are excluded — they are + * auto-registered by the WASM runtime on load and must not be embedded. * * For DOM backend (hosted HTML flow), we keep external `src` values as-is and do not embed. * @@ -2912,7 +2914,9 @@ export class Editor .document as grida.program.document.Document; const images: Record = {}; - const usedSrcs = new dq.DocumentStateQuery(snapshot).image_srcs(); + const usedSrcs = new dq.DocumentStateQuery( + snapshot + ).persistable_image_srcs(); if (this.backend !== "canvas" || !this._m_wasm_canvas_scene) { throw new Error( "`archivedir()` requires the canvas/WASM backend to be mounted." diff --git a/editor/grida-canvas/query/index.ts b/editor/grida-canvas/query/index.ts index d3e5b55acc..4e0b65eaad 100644 --- a/editor/grida-canvas/query/index.ts +++ b/editor/grida-canvas/query/index.ts @@ -654,8 +654,7 @@ export namespace dq { /** * Returns unique image `src` references used by image paints. * - * - Persisted: typically `hex16` (content hash id) - * - Runtime: typically `blob:` or `mem://...` + * Includes all protocol schemes (`res://`, `system://`, etc.). */ image_srcs(): string[] { return Array.from( @@ -666,6 +665,19 @@ export namespace dq { ) ); } + + /** + * Returns unique image `src` references that must be persisted in archives. + * + * Excludes `system://` images (built-in resources baked into the runtime) + * since they are auto-registered on load and must not be embedded in + * `.grida` archives. + */ + persistable_image_srcs(): string[] { + return this.image_srcs().filter( + (src) => !src.startsWith("system://") + ); + } } // note: paint extraction uses `editor.resolvePaints` for correctness across all node variants From 620377803774d44c7b44d87a1dcef278a9f55ea8 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 12 Mar 2026 18:08:51 +0900 Subject: [PATCH 10/16] Add CHANGELOG.md and update grida.fbs for NodeSlot wrapper - Introduced `CHANGELOG.md` to document changes for version `0.91.0-beta+20260311`, including breaking changes to `CanvasDocument` and the addition of a Rust decoder. - Updated `grida.fbs` to replace the `[Node]` vector with a `[NodeSlot]` wrapper table to support Rust FlatBuffers codegen, ensuring compatibility. - Adjusted field IDs in `CanvasDocument` to reflect the new structure and maintain consistency. --- format/CHANGELOG.md | 20 ++++++++ format/README.md | 10 +++- format/TODO.md | 8 +++ format/grida.fbs | 20 ++++++-- packages/grida-canvas-io/format.ts | 82 +++++++++++++++++------------- 5 files changed, 99 insertions(+), 41 deletions(-) create mode 100644 format/CHANGELOG.md diff --git a/format/CHANGELOG.md b/format/CHANGELOG.md new file mode 100644 index 0000000000..d2ab578bee --- /dev/null +++ b/format/CHANGELOG.md @@ -0,0 +1,20 @@ +# `format/grida.fbs` Changelog + +## `0.91.0-beta+20260311` ⚠️ Breaking + +- **`NodeSlot` wrapper table added**: `flatc --rust` does not support `[Node]` + (vector of unions) directly. The canonical workaround is wrapping each union + entry in a table — identical in spirit to `PaintStackItem` wrapping `Paint`. + See: +- **`CanvasDocument.nodes`** field type changed: `[Node]` → `[NodeSlot]`. +- **`CanvasDocument` field IDs reindexed**: `nodes` → `id:1`, `scenes` → `id:2` + (previously `id:2` / `id:3`). Any document serialized with `0.90.x` is **not + readable** by a `0.91.x` decoder without migration. +- **Rust decoder added**: `crates/grida-canvas/src/io/io_grida_fbs.rs` decodes + raw `.grida` FlatBuffers binaries into the internal `Scene` representation. + +## `0.90.0-beta+20260108` + +- Initial stable FlatBuffers schema. Replaced the legacy JSON/ZIP `.grida` format. +- File identifier `"GRID"`, extension `"grida"`. +- Production format: ZIP archive containing a FlatBuffers binary. diff --git a/format/README.md b/format/README.md index b7ad0b219b..63d5715419 100644 --- a/format/README.md +++ b/format/README.md @@ -54,5 +54,11 @@ python3 bin/activate-flatc -- --ts --ts-no-import-ext -o /tmp/grida-fbs-gen/ts f python3 bin/activate-flatc -- --rust -o /tmp/grida-fbs-gen/rust format/grida.fbs ``` -> Note: In-repo generated code locations + automation scripts are intentionally -> not committed yet; we’ll add them once the schema stabilizes. +> In-repo generated code + automation: +> +> - **TypeScript**: `packages/grida-format/` — regenerated on `pnpm build` via `prebuild`. +> - **Rust**: `crates/grida-canvas/src/io/generated/` — regenerated on `pnpm build` via `prebuild`. + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md). diff --git a/format/TODO.md b/format/TODO.md index 4caae921cc..5a1bd2ede4 100644 --- a/format/TODO.md +++ b/format/TODO.md @@ -24,3 +24,11 @@ This document tracks **design flaws and improvement candidates** in the current We should migrate to a table-backed vertex model (e.g. `[VectorNetworkVertex]`) where each vertex can evolve safely over time while preserving explicit intent. **Breaking.** --- + +### 2. `CanonicalEllipticalShapeRingSectorParameters` missing `corner_radius` + +The ellipse arc's `corner_radius` field (`EllipseNodeRec.corner_radius: Option`) controls rounding of arc endpoints. It exists in the Rust runtime model but has no corresponding field in `CanonicalEllipticalShapeRingSectorParameters`. The value is silently lost on FBS roundtrip. + +**Fix**: Add `corner_radius: float = 0.0;` to `CanonicalEllipticalShapeRingSectorParameters` in `grida.fbs`, then update encoder/decoder. **Evolution.** + +--- diff --git a/format/grida.fbs b/format/grida.fbs index d6957f5c98..00ce7f40e5 100644 --- a/format/grida.fbs +++ b/format/grida.fbs @@ -1281,7 +1281,7 @@ table LayerTrait { /// Default is `pass_through`. blend_mode:LayerBlendMode = PassThrough; - /// Rust: `LayerMaskType` (union; default is Image(alpha) since it's the first union member) + /// Rust: `LayerMaskType` (union; NONE means the node is not a mask) mask_type:LayerMaskType; /// layer effects. @@ -1513,15 +1513,29 @@ union Node { // Document root // ----------------------------------------------------------------------------- +/// Wrapper table for a single Node union entry in a flat node vector. +/// +/// FlatBuffers Rust codegen does not support `[Node]` (vector of unions) directly. +/// Using `[NodeSlot]` (vector of tables each containing a union field) is the +/// canonical workaround — identical in spirit to `PaintStackItem` wrapping `Paint`. +/// See: https://github.com/google/flatbuffers/issues/8011 +table NodeSlot { + node:Node; +} + table CanvasDocument { /// Schema version string (keep in sync with TS `grida.program.document.SCHEMA_VERSION`). schema_version:string (id: 0); /// Flat node repository. - nodes:[Node] (id: 2); + /// + /// Stored as `[NodeSlot]` (vector of wrapper tables) rather than `[Node]` + /// (vector of unions) to maintain compatibility with Rust FlatBuffers codegen, + /// which does not support vectors of unions directly. + nodes:[NodeSlot] (id: 1); /// Scene node ids (scene nodes are also stored in `nodes`). - scenes:[NodeIdentifier] (id: 3); + scenes:[NodeIdentifier] (id: 2); } /// Top-level wrapper (allows future multi-document bundles, signatures, etc.) diff --git a/packages/grida-canvas-io/format.ts b/packages/grida-canvas-io/format.ts index 9dbfd56682..47f8fb430f 100644 --- a/packages/grida-canvas-io/format.ts +++ b/packages/grida-canvas-io/format.ts @@ -2,7 +2,7 @@ import grida from "@grida/schema"; import cg from "@grida/cg"; import type cmath from "@grida/cmath"; import * as fbs from "@grida/format"; -import { unionToPaint, unionListToNode, unionToFeBlur } from "@grida/format"; +import { unionToPaint, unionToNode, unionToFeBlur } from "@grida/format"; import type { vn } from "@grida/schema"; import * as flatbuffers from "flatbuffers"; import { generateNKeysBetween } from "@grida/sequence"; @@ -960,15 +960,31 @@ export namespace format { // Nodes don't have blend_mode directly - it's not part of the TS node model const blendMode: fbs.LayerBlendMode = fbs.LayerBlendMode.PassThrough; - // Encode mask_type (LayerMaskType union) - // Default to Image(Alpha) - create LayerMaskTypeImage - fbs.LayerMaskTypeImage.startLayerMaskTypeImage(builder); - fbs.LayerMaskTypeImage.addImageMaskType( - builder, - fbs.ImageMaskType.Alpha - ); - const maskTypeOffset = - fbs.LayerMaskTypeImage.endLayerMaskTypeImage(builder); + // Encode mask_type (LayerMaskType union) — only when the node is actually a mask + const nodeWithMask = node as grida.program.nodes.Node & + Partial; + let maskTypeOffset: flatbuffers.Offset | undefined = undefined; + let maskTypeEnum: fbs.LayerMaskType = fbs.LayerMaskType.NONE; + if (nodeWithMask.mask != null) { + if (nodeWithMask.mask === "geometry") { + fbs.LayerMaskTypeGeometry.startLayerMaskTypeGeometry(builder); + maskTypeOffset = + fbs.LayerMaskTypeGeometry.endLayerMaskTypeGeometry(builder); + maskTypeEnum = fbs.LayerMaskType.LayerMaskTypeGeometry; + } else { + // ImageMaskType: "alpha" | "luminance" + fbs.LayerMaskTypeImage.startLayerMaskTypeImage(builder); + fbs.LayerMaskTypeImage.addImageMaskType( + builder, + nodeWithMask.mask === "luminance" + ? fbs.ImageMaskType.Luminance + : fbs.ImageMaskType.Alpha + ); + maskTypeOffset = + fbs.LayerMaskTypeImage.endLayerMaskTypeImage(builder); + maskTypeEnum = fbs.LayerMaskType.LayerMaskTypeImage; + } + } // Encode effects let effectsOffset: flatbuffers.Offset | undefined = undefined; @@ -1027,12 +1043,11 @@ export namespace format { // 3. Blend mode fbs.LayerTrait.addBlendMode(builder, blendMode); - // 4. Mask type - fbs.LayerTrait.addMaskTypeType( - builder, - fbs.LayerMaskType.LayerMaskTypeImage - ); - fbs.LayerTrait.addMaskType(builder, maskTypeOffset); + // 4. Mask type (only set when the node is a mask) + if (maskTypeOffset !== undefined) { + fbs.LayerTrait.addMaskTypeType(builder, maskTypeEnum); + fbs.LayerTrait.addMaskType(builder, maskTypeOffset); + } // 5. Effects if (effectsOffset !== undefined) { @@ -4430,8 +4445,7 @@ export namespace format { // Deterministic ordering: sort by string id nodeIds.sort(); - const nodeOffsets: flatbuffers.Offset[] = []; - const nodeTypes: fbs.Node[] = []; + const nodeSlotOffsets: flatbuffers.Offset[] = []; for (const nodeId of nodeIds) { const node = document.nodes[nodeId]!; const parentRef = nodeToParentRef.get(nodeId); @@ -4483,18 +4497,16 @@ export namespace format { parentRef, layoutOffset ); - nodeOffsets.push(nodeOffset); - nodeTypes.push(nodeType); + // Wrap in NodeSlot table (avoids vector-of-unions, enables Rust codegen) + fbs.NodeSlot.startNodeSlot(builder); + fbs.NodeSlot.addNodeType(builder, nodeType); + fbs.NodeSlot.addNode(builder, nodeOffset); + nodeSlotOffsets.push(fbs.NodeSlot.endNodeSlot(builder)); } - // Create both nodesType and nodes vectors for union - const nodesTypeOffset = fbs.CanvasDocument.createNodesTypeVector( - builder, - nodeTypes - ); const nodesOffset = fbs.CanvasDocument.createNodesVector( builder, - nodeOffsets + nodeSlotOffsets ); // Encode scenes array @@ -4508,7 +4520,6 @@ export namespace format { // Build CanvasDocument table fbs.CanvasDocument.startCanvasDocument(builder); fbs.CanvasDocument.addSchemaVersion(builder, schemaVersionOffset); - fbs.CanvasDocument.addNodesType(builder, nodesTypeOffset); fbs.CanvasDocument.addNodes(builder, nodesOffset); fbs.CanvasDocument.addScenes(builder, scenesOffset); const documentOffset = fbs.CanvasDocument.endCanvasDocument(builder); @@ -5438,15 +5449,14 @@ export namespace format { const nodeCount = document.nodesLength(); for (let i = 0; i < nodeCount; i++) { - const nodeType = document.nodesType(i); - if (nodeType === null || nodeType === fbs.Node.NONE) continue; - - // Get typed node table using union - const typedNode = unionListToNode( - nodeType, - (index: number, obj: any) => document.nodes(index, obj), - i - ); + const slot = document.nodes(i); + if (!slot) continue; + + const nodeType = slot.nodeType(); + if (nodeType === fbs.Node.NONE) continue; + + // Unwrap NodeSlot to get the typed node table + const typedNode = unionToNode(nodeType, (obj: any) => slot.node(obj)); if (!typedNode) continue; // SceneNode is special - it doesn't use LayerTrait From bb8d105a9d5b6afb06894976b50b8c1921d3ff2d Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 12 Mar 2026 18:11:35 +0900 Subject: [PATCH 11/16] Update FlatBuffers integration and add freshness check workflow - Added `flatbuffers` dependency to the project for improved FlatBuffers support. - Updated `Cargo.lock` to include new packages and versions, including `flatbuffers` and `rustc_version`. - Introduced a GitHub Actions workflow (`check-generated-fbs.yml`) to ensure the freshness of committed FlatBuffers bindings, preventing stale generated files. - Added `io_grida_fbs.rs` for decoding `.grida` FlatBuffers binaries into Rust structures. - Updated `Cargo.toml` and `package.json` to support new generation scripts for FlatBuffers. - Committed generated FlatBuffers bindings to ensure compatibility with the Rust crate, allowing `cargo build` to function without requiring `flatc` in a clean checkout. --- .github/workflows/check-generated-fbs.yml | 62 + Cargo.lock | 50 +- bin/activate-flatc | 7 +- crates/grida-canvas/Cargo.toml | 1 + crates/grida-canvas/package.json | 4 + .../src/io/generated/.gitattributes | 1 + crates/grida-canvas/src/io/generated/grida.rs | 18899 ++++++++++++++++ crates/grida-canvas/src/io/generated/mod.rs | 18 + crates/grida-canvas/src/io/io_grida_fbs.rs | 3281 +++ crates/grida-canvas/src/io/mod.rs | 4 + crates/grida-dev/Cargo.toml | 1 + 11 files changed, 22323 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/check-generated-fbs.yml create mode 100644 crates/grida-canvas/src/io/generated/.gitattributes create mode 100644 crates/grida-canvas/src/io/generated/grida.rs create mode 100644 crates/grida-canvas/src/io/generated/mod.rs create mode 100644 crates/grida-canvas/src/io/io_grida_fbs.rs diff --git a/.github/workflows/check-generated-fbs.yml b/.github/workflows/check-generated-fbs.yml new file mode 100644 index 0000000000..348581ab8f --- /dev/null +++ b/.github/workflows/check-generated-fbs.yml @@ -0,0 +1,62 @@ +# ----------------------------------------------------------------- +# Freshness check for committed FlatBuffers bindings +# ----------------------------------------------------------------- +# +# Why this exists +# --------------- +# The Rust FlatBuffers bindings (`crates/grida-canvas/src/io/generated/grida.rs`) +# are committed to git so that `cargo build` works from a clean checkout without +# requiring `flatc`. However, because the file is generated from `format/grida.fbs`, +# it can become stale if someone edits the schema but forgets to regenerate. +# +# This workflow re-runs codegen and asserts that the committed file is identical +# to what `flatc` would produce. If it differs, the check fails with a clear +# error message telling the contributor how to fix it. +# +# When does it run +# ---------------- +# Only on PRs / pushes that touch the schema, the generated output, the codegen +# tooling, or the TS format package (which shares the same schema source). +# +# How to fix a failure +# -------------------- +# pnpm --filter @crates/grida-canvas generate +# git add crates/grida-canvas/src/io/generated/grida.rs +# git commit +# ----------------------------------------------------------------- + +name: check generated flatbuffers + +on: + push: + branches: + - main + pull_request: + paths: + - "format/grida.fbs" + - "crates/grida-canvas/src/io/generated/**" + - "packages/grida-format/src/**" + - "bin/activate-flatc" + +jobs: + rust-bindings: + name: rust fbs freshness + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Regenerate Rust FlatBuffers bindings + run: | + python3 bin/activate-flatc -- --rust \ + -o crates/grida-canvas/src/io/generated \ + format/grida.fbs + mv crates/grida-canvas/src/io/generated/grida_generated.rs \ + crates/grida-canvas/src/io/generated/grida.rs + + - name: Check for uncommitted changes + run: | + if ! git diff --exit-code crates/grida-canvas/src/io/generated/grida.rs; then + echo "::error::Generated Rust FlatBuffers bindings are stale. Run 'pnpm --filter @crates/grida-canvas generate' and commit the result." + exit 1 + fi diff --git a/Cargo.lock b/Cargo.lock index 6ba92a5f52..70e92a7cf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -495,6 +495,7 @@ dependencies = [ "clap", "criterion", "figma-api", + "flatbuffers", "futures", "gl", "glutin", @@ -730,9 +731,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1172,6 +1173,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" +dependencies = [ + "bitflags 2.9.1", + "rustc_version", +] + [[package]] name = "flate2" version = "1.1.1" @@ -1574,6 +1585,7 @@ dependencies = [ "tokio", "toml 0.9.8", "winit", + "zip", ] [[package]] @@ -3653,6 +3665,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -3836,6 +3857,12 @@ dependencies = [ "to_shmem_derive", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.226" @@ -4829,6 +4856,12 @@ dependencies = [ "core_maths", ] +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + [[package]] name = "typenum" version = "1.18.0" @@ -5884,6 +5917,19 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "zip" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004" +dependencies = [ + "crc32fast", + "flate2", + "indexmap 2.11.4", + "memchr", + "typed-path", +] + [[package]] name = "zune-core" version = "0.4.12" diff --git a/bin/activate-flatc b/bin/activate-flatc index 74debc074b..4ee37f9fec 100644 --- a/bin/activate-flatc +++ b/bin/activate-flatc @@ -5,9 +5,10 @@ Grida: FlatBuffers compiler (flatc) bootstrapper Why this exists --------------- -We intentionally do NOT commit generated FlatBuffers bindings to git (see -`packages/grida-format/.gitignore`). The package build step runs `flatc` (see -`packages/grida-format/package.json`), so CI and Vercel builds must have a +Generated FlatBuffers bindings are committed for the Rust crate +(`crates/grida-canvas/src/io/generated/grida.rs`) so that `cargo build` works +from a clean checkout. The TypeScript package (`packages/grida-format`) +regenerates at build time. In both cases CI and Vercel builds need a deterministic way to obtain `flatc`. FlatBuffers does not ship `flatc` via npm, so this script installs a prebuilt diff --git a/crates/grida-canvas/Cargo.toml b/crates/grida-canvas/Cargo.toml index 190fbce774..968fcc460f 100644 --- a/crates/grida-canvas/Cargo.toml +++ b/crates/grida-canvas/Cargo.toml @@ -22,6 +22,7 @@ gl = "0.14.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" json-patch = "4.1.0" +flatbuffers = "25.9.23" # text editing grida-text-edit = { path = "../grida-text-edit" } diff --git a/crates/grida-canvas/package.json b/crates/grida-canvas/package.json index a3d97ea174..9cde75502d 100644 --- a/crates/grida-canvas/package.json +++ b/crates/grida-canvas/package.json @@ -4,6 +4,10 @@ "description": "turbo ci rust wrapper", "scripts": { "build": "cargo build --release", + "flatc:clean": "rm -f src/io/generated/grida.rs", + "flatc:generate": "pnpm flatc:clean && python3 ../../bin/activate-flatc -- --rust -o src/io/generated ../../format/grida.fbs && mv src/io/generated/grida_generated.rs src/io/generated/grida.rs", + "generate": "pnpm flatc:generate", + "prebuild": "pnpm flatc:generate", "test": "cargo test" } } diff --git a/crates/grida-canvas/src/io/generated/.gitattributes b/crates/grida-canvas/src/io/generated/.gitattributes new file mode 100644 index 0000000000..2c1875e3d1 --- /dev/null +++ b/crates/grida-canvas/src/io/generated/.gitattributes @@ -0,0 +1 @@ +grida.rs linguist-generated=true diff --git a/crates/grida-canvas/src/io/generated/grida.rs b/crates/grida-canvas/src/io/generated/grida.rs new file mode 100644 index 0000000000..8cae8f9d85 --- /dev/null +++ b/crates/grida-canvas/src/io/generated/grida.rs @@ -0,0 +1,18899 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; + + +#[allow(unused_imports, dead_code)] +pub mod grida { + + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_NODE_TYPE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_NODE_TYPE: u8 = 14; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_NODE_TYPE: [NodeType; 15] = [ + NodeType::Exception, + NodeType::Scene, + NodeType::Group, + NodeType::InitialContainer, + NodeType::Container, + NodeType::BooleanOperation, + NodeType::Rectangle, + NodeType::Ellipse, + NodeType::Polygon, + NodeType::RegularPolygon, + NodeType::RegularStarPolygon, + NodeType::Path, + NodeType::Line, + NodeType::Vector, + NodeType::TextSpan, +]; + +/// node type +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct NodeType(pub u8); +#[allow(non_upper_case_globals)] +impl NodeType { + pub const Exception: Self = Self(0); + pub const Scene: Self = Self(1); + pub const Group: Self = Self(2); + pub const InitialContainer: Self = Self(3); + pub const Container: Self = Self(4); + pub const BooleanOperation: Self = Self(5); + pub const Rectangle: Self = Self(6); + pub const Ellipse: Self = Self(7); + pub const Polygon: Self = Self(8); + pub const RegularPolygon: Self = Self(9); + pub const RegularStarPolygon: Self = Self(10); + pub const Path: Self = Self(11); + pub const Line: Self = Self(12); + pub const Vector: Self = Self(13); + pub const TextSpan: Self = Self(14); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 14; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Exception, + Self::Scene, + Self::Group, + Self::InitialContainer, + Self::Container, + Self::BooleanOperation, + Self::Rectangle, + Self::Ellipse, + Self::Polygon, + Self::RegularPolygon, + Self::RegularStarPolygon, + Self::Path, + Self::Line, + Self::Vector, + Self::TextSpan, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Exception => Some("Exception"), + Self::Scene => Some("Scene"), + Self::Group => Some("Group"), + Self::InitialContainer => Some("InitialContainer"), + Self::Container => Some("Container"), + Self::BooleanOperation => Some("BooleanOperation"), + Self::Rectangle => Some("Rectangle"), + Self::Ellipse => Some("Ellipse"), + Self::Polygon => Some("Polygon"), + Self::RegularPolygon => Some("RegularPolygon"), + Self::RegularStarPolygon => Some("RegularStarPolygon"), + Self::Path => Some("Path"), + Self::Line => Some("Line"), + Self::Vector => Some("Vector"), + Self::TextSpan => Some("TextSpan"), + _ => None, + } + } +} +impl ::core::fmt::Debug for NodeType { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for NodeType { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for NodeType { + type Output = NodeType; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for NodeType { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for NodeType { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for NodeType {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_BASIC_SHAPE_NODE_TYPE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_BASIC_SHAPE_NODE_TYPE: u8 = 5; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_BASIC_SHAPE_NODE_TYPE: [BasicShapeNodeType; 6] = [ + BasicShapeNodeType::Rectangle, + BasicShapeNodeType::Ellipse, + BasicShapeNodeType::Polygon, + BasicShapeNodeType::RegularPolygon, + BasicShapeNodeType::RegularStarPolygon, + BasicShapeNodeType::Path, +]; + +/// shape node type (flag within BasicShapeNode) +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct BasicShapeNodeType(pub u8); +#[allow(non_upper_case_globals)] +impl BasicShapeNodeType { + pub const Rectangle: Self = Self(0); + pub const Ellipse: Self = Self(1); + pub const Polygon: Self = Self(2); + pub const RegularPolygon: Self = Self(3); + pub const RegularStarPolygon: Self = Self(4); + pub const Path: Self = Self(5); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 5; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Rectangle, + Self::Ellipse, + Self::Polygon, + Self::RegularPolygon, + Self::RegularStarPolygon, + Self::Path, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Rectangle => Some("Rectangle"), + Self::Ellipse => Some("Ellipse"), + Self::Polygon => Some("Polygon"), + Self::RegularPolygon => Some("RegularPolygon"), + Self::RegularStarPolygon => Some("RegularStarPolygon"), + Self::Path => Some("Path"), + _ => None, + } + } +} +impl ::core::fmt::Debug for BasicShapeNodeType { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for BasicShapeNodeType { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for BasicShapeNodeType { + type Output = BasicShapeNodeType; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for BasicShapeNodeType { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for BasicShapeNodeType { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for BasicShapeNodeType {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_BLEND_MODE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_BLEND_MODE: u8 = 15; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_BLEND_MODE: [BlendMode; 16] = [ + BlendMode::Normal, + BlendMode::Multiply, + BlendMode::Screen, + BlendMode::Overlay, + BlendMode::Darken, + BlendMode::Lighten, + BlendMode::ColorDodge, + BlendMode::ColorBurn, + BlendMode::HardLight, + BlendMode::SoftLight, + BlendMode::Difference, + BlendMode::Exclusion, + BlendMode::Hue, + BlendMode::Saturation, + BlendMode::Color, + BlendMode::Luminosity, +]; + +/// Rust: `BlendMode` (does not include pass-through) +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct BlendMode(pub u8); +#[allow(non_upper_case_globals)] +impl BlendMode { + pub const Normal: Self = Self(0); + pub const Multiply: Self = Self(1); + pub const Screen: Self = Self(2); + pub const Overlay: Self = Self(3); + pub const Darken: Self = Self(4); + pub const Lighten: Self = Self(5); + pub const ColorDodge: Self = Self(6); + pub const ColorBurn: Self = Self(7); + pub const HardLight: Self = Self(8); + pub const SoftLight: Self = Self(9); + pub const Difference: Self = Self(10); + pub const Exclusion: Self = Self(11); + pub const Hue: Self = Self(12); + pub const Saturation: Self = Self(13); + pub const Color: Self = Self(14); + pub const Luminosity: Self = Self(15); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 15; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Normal, + Self::Multiply, + Self::Screen, + Self::Overlay, + Self::Darken, + Self::Lighten, + Self::ColorDodge, + Self::ColorBurn, + Self::HardLight, + Self::SoftLight, + Self::Difference, + Self::Exclusion, + Self::Hue, + Self::Saturation, + Self::Color, + Self::Luminosity, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Normal => Some("Normal"), + Self::Multiply => Some("Multiply"), + Self::Screen => Some("Screen"), + Self::Overlay => Some("Overlay"), + Self::Darken => Some("Darken"), + Self::Lighten => Some("Lighten"), + Self::ColorDodge => Some("ColorDodge"), + Self::ColorBurn => Some("ColorBurn"), + Self::HardLight => Some("HardLight"), + Self::SoftLight => Some("SoftLight"), + Self::Difference => Some("Difference"), + Self::Exclusion => Some("Exclusion"), + Self::Hue => Some("Hue"), + Self::Saturation => Some("Saturation"), + Self::Color => Some("Color"), + Self::Luminosity => Some("Luminosity"), + _ => None, + } + } +} +impl ::core::fmt::Debug for BlendMode { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for BlendMode { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for BlendMode { + type Output = BlendMode; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for BlendMode { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for BlendMode { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for BlendMode {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_LAYER_BLEND_MODE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_LAYER_BLEND_MODE: u8 = 100; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_LAYER_BLEND_MODE: [LayerBlendMode; 17] = [ + LayerBlendMode::Normal, + LayerBlendMode::Multiply, + LayerBlendMode::Screen, + LayerBlendMode::Overlay, + LayerBlendMode::Darken, + LayerBlendMode::Lighten, + LayerBlendMode::ColorDodge, + LayerBlendMode::ColorBurn, + LayerBlendMode::HardLight, + LayerBlendMode::SoftLight, + LayerBlendMode::Difference, + LayerBlendMode::Exclusion, + LayerBlendMode::Hue, + LayerBlendMode::Saturation, + LayerBlendMode::Color, + LayerBlendMode::Luminosity, + LayerBlendMode::PassThrough, +]; + +/// Archive model: flattened blend mode with pass-through included. +/// +/// This duplicates `BlendMode` variants and adds `pass_through`, avoiding a union/table wrapper. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct LayerBlendMode(pub u8); +#[allow(non_upper_case_globals)] +impl LayerBlendMode { + pub const Normal: Self = Self(0); + pub const Multiply: Self = Self(1); + pub const Screen: Self = Self(2); + pub const Overlay: Self = Self(3); + pub const Darken: Self = Self(4); + pub const Lighten: Self = Self(5); + pub const ColorDodge: Self = Self(6); + pub const ColorBurn: Self = Self(7); + pub const HardLight: Self = Self(8); + pub const SoftLight: Self = Self(9); + pub const Difference: Self = Self(10); + pub const Exclusion: Self = Self(11); + pub const Hue: Self = Self(12); + pub const Saturation: Self = Self(13); + pub const Color: Self = Self(14); + pub const Luminosity: Self = Self(15); + /// Archive-only sentinel: corresponds to Rust `LayerBlendMode::PassThrough`. + /// Set to 100 to avoid confusing it with `BlendMode` ids. + pub const PassThrough: Self = Self(100); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 100; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Normal, + Self::Multiply, + Self::Screen, + Self::Overlay, + Self::Darken, + Self::Lighten, + Self::ColorDodge, + Self::ColorBurn, + Self::HardLight, + Self::SoftLight, + Self::Difference, + Self::Exclusion, + Self::Hue, + Self::Saturation, + Self::Color, + Self::Luminosity, + Self::PassThrough, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Normal => Some("Normal"), + Self::Multiply => Some("Multiply"), + Self::Screen => Some("Screen"), + Self::Overlay => Some("Overlay"), + Self::Darken => Some("Darken"), + Self::Lighten => Some("Lighten"), + Self::ColorDodge => Some("ColorDodge"), + Self::ColorBurn => Some("ColorBurn"), + Self::HardLight => Some("HardLight"), + Self::SoftLight => Some("SoftLight"), + Self::Difference => Some("Difference"), + Self::Exclusion => Some("Exclusion"), + Self::Hue => Some("Hue"), + Self::Saturation => Some("Saturation"), + Self::Color => Some("Color"), + Self::Luminosity => Some("Luminosity"), + Self::PassThrough => Some("PassThrough"), + _ => None, + } + } +} +impl ::core::fmt::Debug for LayerBlendMode { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for LayerBlendMode { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for LayerBlendMode { + type Output = LayerBlendMode; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for LayerBlendMode { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for LayerBlendMode { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for LayerBlendMode {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_AXIS: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_AXIS: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_AXIS: [Axis; 2] = [ + Axis::Horizontal, + Axis::Vertical, +]; + +/// Rust: `Axis` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct Axis(pub u8); +#[allow(non_upper_case_globals)] +impl Axis { + pub const Horizontal: Self = Self(0); + pub const Vertical: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Horizontal, + Self::Vertical, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Horizontal => Some("Horizontal"), + Self::Vertical => Some("Vertical"), + _ => None, + } + } +} +impl ::core::fmt::Debug for Axis { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for Axis { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for Axis { + type Output = Axis; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for Axis { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for Axis { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for Axis {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_TILE_MODE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_TILE_MODE: u8 = 3; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_TILE_MODE: [TileMode; 4] = [ + TileMode::Clamp, + TileMode::Repeated, + TileMode::Mirror, + TileMode::Decal, +]; + +/// Rust: `TileMode` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct TileMode(pub u8); +#[allow(non_upper_case_globals)] +impl TileMode { + pub const Clamp: Self = Self(0); + pub const Repeated: Self = Self(1); + pub const Mirror: Self = Self(2); + pub const Decal: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Clamp, + Self::Repeated, + Self::Mirror, + Self::Decal, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Clamp => Some("Clamp"), + Self::Repeated => Some("Repeated"), + Self::Mirror => Some("Mirror"), + Self::Decal => Some("Decal"), + _ => None, + } + } +} +impl ::core::fmt::Debug for TileMode { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for TileMode { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for TileMode { + type Output = TileMode; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for TileMode { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for TileMode { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for TileMode {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_BOX_FIT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_BOX_FIT: u8 = 3; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_BOX_FIT: [BoxFit; 4] = [ + BoxFit::Contain, + BoxFit::Cover, + BoxFit::Fill, + BoxFit::None, +]; + +/// Rust: `math2::box_fit::BoxFit` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct BoxFit(pub u8); +#[allow(non_upper_case_globals)] +impl BoxFit { + pub const Contain: Self = Self(0); + pub const Cover: Self = Self(1); + pub const Fill: Self = Self(2); + pub const None: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Contain, + Self::Cover, + Self::Fill, + Self::None, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Contain => Some("Contain"), + Self::Cover => Some("Cover"), + Self::Fill => Some("Fill"), + Self::None => Some("None"), + _ => None, + } + } +} +impl ::core::fmt::Debug for BoxFit { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for BoxFit { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for BoxFit { + type Output = BoxFit; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for BoxFit { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for BoxFit { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for BoxFit {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_IMAGE_REPEAT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_IMAGE_REPEAT: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_IMAGE_REPEAT: [ImageRepeat; 3] = [ + ImageRepeat::RepeatX, + ImageRepeat::RepeatY, + ImageRepeat::Repeat, +]; + +/// Rust: `ImageRepeat` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct ImageRepeat(pub u8); +#[allow(non_upper_case_globals)] +impl ImageRepeat { + pub const RepeatX: Self = Self(0); + pub const RepeatY: Self = Self(1); + pub const Repeat: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::RepeatX, + Self::RepeatY, + Self::Repeat, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::RepeatX => Some("RepeatX"), + Self::RepeatY => Some("RepeatY"), + Self::Repeat => Some("Repeat"), + _ => None, + } + } +} +impl ::core::fmt::Debug for ImageRepeat { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for ImageRepeat { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for ImageRepeat { + type Output = ImageRepeat; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for ImageRepeat { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for ImageRepeat { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for ImageRepeat {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_LAYOUT_MODE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_LAYOUT_MODE: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_LAYOUT_MODE: [LayoutMode; 2] = [ + LayoutMode::Normal, + LayoutMode::Flex, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct LayoutMode(pub u8); +#[allow(non_upper_case_globals)] +impl LayoutMode { + pub const Normal: Self = Self(0); + pub const Flex: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Normal, + Self::Flex, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Normal => Some("Normal"), + Self::Flex => Some("Flex"), + _ => None, + } + } +} +impl ::core::fmt::Debug for LayoutMode { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for LayoutMode { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for LayoutMode { + type Output = LayoutMode; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for LayoutMode { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for LayoutMode { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for LayoutMode {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_LAYOUT_WRAP: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_LAYOUT_WRAP: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_LAYOUT_WRAP: [LayoutWrap; 3] = [ + LayoutWrap::None, + LayoutWrap::Wrap, + LayoutWrap::NoWrap, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct LayoutWrap(pub u8); +#[allow(non_upper_case_globals)] +impl LayoutWrap { + pub const None: Self = Self(0); + pub const Wrap: Self = Self(1); + pub const NoWrap: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::None, + Self::Wrap, + Self::NoWrap, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::None => Some("None"), + Self::Wrap => Some("Wrap"), + Self::NoWrap => Some("NoWrap"), + _ => None, + } + } +} +impl ::core::fmt::Debug for LayoutWrap { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for LayoutWrap { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for LayoutWrap { + type Output = LayoutWrap; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for LayoutWrap { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for LayoutWrap { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for LayoutWrap {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_LAYOUT_POSITIONING: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_LAYOUT_POSITIONING: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_LAYOUT_POSITIONING: [LayoutPositioning; 2] = [ + LayoutPositioning::Auto, + LayoutPositioning::Absolute, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct LayoutPositioning(pub u8); +#[allow(non_upper_case_globals)] +impl LayoutPositioning { + pub const Auto: Self = Self(0); + pub const Absolute: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Auto, + Self::Absolute, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Auto => Some("Auto"), + Self::Absolute => Some("Absolute"), + _ => None, + } + } +} +impl ::core::fmt::Debug for LayoutPositioning { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for LayoutPositioning { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for LayoutPositioning { + type Output = LayoutPositioning; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for LayoutPositioning { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for LayoutPositioning { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for LayoutPositioning {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_MAIN_AXIS_ALIGNMENT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_MAIN_AXIS_ALIGNMENT: u8 = 7; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_MAIN_AXIS_ALIGNMENT: [MainAxisAlignment; 8] = [ + MainAxisAlignment::None, + MainAxisAlignment::Start, + MainAxisAlignment::End, + MainAxisAlignment::Center, + MainAxisAlignment::SpaceBetween, + MainAxisAlignment::SpaceAround, + MainAxisAlignment::SpaceEvenly, + MainAxisAlignment::Stretch, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct MainAxisAlignment(pub u8); +#[allow(non_upper_case_globals)] +impl MainAxisAlignment { + pub const None: Self = Self(0); + pub const Start: Self = Self(1); + pub const End: Self = Self(2); + pub const Center: Self = Self(3); + pub const SpaceBetween: Self = Self(4); + pub const SpaceAround: Self = Self(5); + pub const SpaceEvenly: Self = Self(6); + pub const Stretch: Self = Self(7); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 7; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::None, + Self::Start, + Self::End, + Self::Center, + Self::SpaceBetween, + Self::SpaceAround, + Self::SpaceEvenly, + Self::Stretch, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::None => Some("None"), + Self::Start => Some("Start"), + Self::End => Some("End"), + Self::Center => Some("Center"), + Self::SpaceBetween => Some("SpaceBetween"), + Self::SpaceAround => Some("SpaceAround"), + Self::SpaceEvenly => Some("SpaceEvenly"), + Self::Stretch => Some("Stretch"), + _ => None, + } + } +} +impl ::core::fmt::Debug for MainAxisAlignment { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for MainAxisAlignment { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for MainAxisAlignment { + type Output = MainAxisAlignment; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for MainAxisAlignment { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for MainAxisAlignment { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for MainAxisAlignment {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_CROSS_AXIS_ALIGNMENT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_CROSS_AXIS_ALIGNMENT: u8 = 4; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_CROSS_AXIS_ALIGNMENT: [CrossAxisAlignment; 5] = [ + CrossAxisAlignment::None, + CrossAxisAlignment::Start, + CrossAxisAlignment::End, + CrossAxisAlignment::Center, + CrossAxisAlignment::Stretch, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct CrossAxisAlignment(pub u8); +#[allow(non_upper_case_globals)] +impl CrossAxisAlignment { + pub const None: Self = Self(0); + pub const Start: Self = Self(1); + pub const End: Self = Self(2); + pub const Center: Self = Self(3); + pub const Stretch: Self = Self(4); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 4; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::None, + Self::Start, + Self::End, + Self::Center, + Self::Stretch, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::None => Some("None"), + Self::Start => Some("Start"), + Self::End => Some("End"), + Self::Center => Some("Center"), + Self::Stretch => Some("Stretch"), + _ => None, + } + } +} +impl ::core::fmt::Debug for CrossAxisAlignment { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for CrossAxisAlignment { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for CrossAxisAlignment { + type Output = CrossAxisAlignment; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for CrossAxisAlignment { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for CrossAxisAlignment { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for CrossAxisAlignment {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_STROKE_ALIGN: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_STROKE_ALIGN: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_STROKE_ALIGN: [StrokeAlign; 3] = [ + StrokeAlign::Inside, + StrokeAlign::Center, + StrokeAlign::Outside, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct StrokeAlign(pub u8); +#[allow(non_upper_case_globals)] +impl StrokeAlign { + pub const Inside: Self = Self(0); + pub const Center: Self = Self(1); + pub const Outside: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Inside, + Self::Center, + Self::Outside, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Inside => Some("Inside"), + Self::Center => Some("Center"), + Self::Outside => Some("Outside"), + _ => None, + } + } +} +impl ::core::fmt::Debug for StrokeAlign { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for StrokeAlign { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for StrokeAlign { + type Output = StrokeAlign; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for StrokeAlign { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for StrokeAlign { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for StrokeAlign {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_STROKE_CAP: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_STROKE_CAP: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_STROKE_CAP: [StrokeCap; 3] = [ + StrokeCap::Butt, + StrokeCap::Round, + StrokeCap::Square, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct StrokeCap(pub u8); +#[allow(non_upper_case_globals)] +impl StrokeCap { + pub const Butt: Self = Self(0); + pub const Round: Self = Self(1); + pub const Square: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Butt, + Self::Round, + Self::Square, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Butt => Some("Butt"), + Self::Round => Some("Round"), + Self::Square => Some("Square"), + _ => None, + } + } +} +impl ::core::fmt::Debug for StrokeCap { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for StrokeCap { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for StrokeCap { + type Output = StrokeCap; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for StrokeCap { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for StrokeCap { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for StrokeCap {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_STROKE_JOIN: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_STROKE_JOIN: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_STROKE_JOIN: [StrokeJoin; 3] = [ + StrokeJoin::Miter, + StrokeJoin::Round, + StrokeJoin::Bevel, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct StrokeJoin(pub u8); +#[allow(non_upper_case_globals)] +impl StrokeJoin { + pub const Miter: Self = Self(0); + pub const Round: Self = Self(1); + pub const Bevel: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Miter, + Self::Round, + Self::Bevel, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Miter => Some("Miter"), + Self::Round => Some("Round"), + Self::Bevel => Some("Bevel"), + _ => None, + } + } +} +impl ::core::fmt::Debug for StrokeJoin { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for StrokeJoin { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for StrokeJoin { + type Output = StrokeJoin; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for StrokeJoin { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for StrokeJoin { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for StrokeJoin {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_STROKE_MARKER_PRESET: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_STROKE_MARKER_PRESET: u8 = 6; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_STROKE_MARKER_PRESET: [StrokeMarkerPreset; 7] = [ + StrokeMarkerPreset::None, + StrokeMarkerPreset::RightTriangleOpen, + StrokeMarkerPreset::EquilateralTriangle, + StrokeMarkerPreset::Circle, + StrokeMarkerPreset::Square, + StrokeMarkerPreset::Diamond, + StrokeMarkerPreset::VerticalBar, +]; + +/// Marker decoration placed at stroke endpoints or vector vertices. +/// +/// Unlike StrokeCap (which maps to native backend caps like Skia PaintCap), +/// StrokeMarkerPreset represents explicit marker geometry drawn on top of the +/// stroke path. When a decoration is present at an endpoint, the renderer +/// uses Butt cap at that endpoint and draws the marker geometry instead. +/// +/// See: docs/wg/feat-2d/curve-decoration.md +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct StrokeMarkerPreset(pub u8); +#[allow(non_upper_case_globals)] +impl StrokeMarkerPreset { + /// No decoration (endpoint uses the node's stroke_cap as normal). + pub const None: Self = Self(0); + /// Right triangle (90° at tip), open stroked chevron — not filled. + pub const RightTriangleOpen: Self = Self(1); + /// Filled equilateral triangle pointing forward. + pub const EquilateralTriangle: Self = Self(2); + /// Filled circle. + pub const Circle: Self = Self(3); + /// Filled axis-aligned square. + pub const Square: Self = Self(4); + /// Filled diamond (square rotated 45°). + pub const Diamond: Self = Self(5); + /// Filled vertical bar (rectangle) perpendicular to the stroke. + pub const VerticalBar: Self = Self(6); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 6; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::None, + Self::RightTriangleOpen, + Self::EquilateralTriangle, + Self::Circle, + Self::Square, + Self::Diamond, + Self::VerticalBar, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::None => Some("None"), + Self::RightTriangleOpen => Some("RightTriangleOpen"), + Self::EquilateralTriangle => Some("EquilateralTriangle"), + Self::Circle => Some("Circle"), + Self::Square => Some("Square"), + Self::Diamond => Some("Diamond"), + Self::VerticalBar => Some("VerticalBar"), + _ => None, + } + } +} +impl ::core::fmt::Debug for StrokeMarkerPreset { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for StrokeMarkerPreset { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for StrokeMarkerPreset { + type Output = StrokeMarkerPreset; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for StrokeMarkerPreset { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for StrokeMarkerPreset { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for StrokeMarkerPreset {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_TEXT_ALIGN: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_TEXT_ALIGN: u8 = 3; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_TEXT_ALIGN: [TextAlign; 4] = [ + TextAlign::Left, + TextAlign::Right, + TextAlign::Center, + TextAlign::Justify, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct TextAlign(pub u8); +#[allow(non_upper_case_globals)] +impl TextAlign { + pub const Left: Self = Self(0); + pub const Right: Self = Self(1); + pub const Center: Self = Self(2); + pub const Justify: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Left, + Self::Right, + Self::Center, + Self::Justify, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Left => Some("Left"), + Self::Right => Some("Right"), + Self::Center => Some("Center"), + Self::Justify => Some("Justify"), + _ => None, + } + } +} +impl ::core::fmt::Debug for TextAlign { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for TextAlign { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for TextAlign { + type Output = TextAlign; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for TextAlign { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for TextAlign { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for TextAlign {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_TEXT_ALIGN_VERTICAL: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_TEXT_ALIGN_VERTICAL: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_TEXT_ALIGN_VERTICAL: [TextAlignVertical; 3] = [ + TextAlignVertical::Top, + TextAlignVertical::Center, + TextAlignVertical::Bottom, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct TextAlignVertical(pub u8); +#[allow(non_upper_case_globals)] +impl TextAlignVertical { + pub const Top: Self = Self(0); + pub const Center: Self = Self(1); + pub const Bottom: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Top, + Self::Center, + Self::Bottom, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Top => Some("Top"), + Self::Center => Some("Center"), + Self::Bottom => Some("Bottom"), + _ => None, + } + } +} +impl ::core::fmt::Debug for TextAlignVertical { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for TextAlignVertical { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for TextAlignVertical { + type Output = TextAlignVertical; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for TextAlignVertical { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for TextAlignVertical { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for TextAlignVertical {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_BOOLEAN_PATH_OPERATION: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_BOOLEAN_PATH_OPERATION: u8 = 3; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_BOOLEAN_PATH_OPERATION: [BooleanPathOperation; 4] = [ + BooleanPathOperation::Union, + BooleanPathOperation::Intersection, + BooleanPathOperation::Difference, + BooleanPathOperation::Xor, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct BooleanPathOperation(pub u8); +#[allow(non_upper_case_globals)] +impl BooleanPathOperation { + pub const Union: Self = Self(0); + pub const Intersection: Self = Self(1); + pub const Difference: Self = Self(2); + pub const Xor: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Union, + Self::Intersection, + Self::Difference, + Self::Xor, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Union => Some("Union"), + Self::Intersection => Some("Intersection"), + Self::Difference => Some("Difference"), + Self::Xor => Some("Xor"), + _ => None, + } + } +} +impl ::core::fmt::Debug for BooleanPathOperation { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for BooleanPathOperation { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for BooleanPathOperation { + type Output = BooleanPathOperation; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for BooleanPathOperation { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for BooleanPathOperation { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for BooleanPathOperation {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_SCENE_CONSTRAINTS_CHILDREN: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_SCENE_CONSTRAINTS_CHILDREN: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_SCENE_CONSTRAINTS_CHILDREN: [SceneConstraintsChildren; 2] = [ + SceneConstraintsChildren::Single, + SceneConstraintsChildren::Multiple, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct SceneConstraintsChildren(pub u8); +#[allow(non_upper_case_globals)] +impl SceneConstraintsChildren { + pub const Single: Self = Self(0); + pub const Multiple: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Single, + Self::Multiple, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Single => Some("Single"), + Self::Multiple => Some("Multiple"), + _ => None, + } + } +} +impl ::core::fmt::Debug for SceneConstraintsChildren { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for SceneConstraintsChildren { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for SceneConstraintsChildren { + type Output = SceneConstraintsChildren; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for SceneConstraintsChildren { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for SceneConstraintsChildren { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for SceneConstraintsChildren {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_FILL_RULE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_FILL_RULE: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_FILL_RULE: [FillRule; 2] = [ + FillRule::NonZero, + FillRule::EvenOdd, +]; + +/// Rust: `FillRule` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct FillRule(pub u8); +#[allow(non_upper_case_globals)] +impl FillRule { + pub const NonZero: Self = Self(0); + pub const EvenOdd: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NonZero, + Self::EvenOdd, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NonZero => Some("NonZero"), + Self::EvenOdd => Some("EvenOdd"), + _ => None, + } + } +} +impl ::core::fmt::Debug for FillRule { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for FillRule { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for FillRule { + type Output = FillRule; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for FillRule { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for FillRule { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for FillRule {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_IMAGE_MASK_TYPE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_IMAGE_MASK_TYPE: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_IMAGE_MASK_TYPE: [ImageMaskType; 2] = [ + ImageMaskType::Alpha, + ImageMaskType::Luminance, +]; + +/// Rust: `ImageMaskType` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct ImageMaskType(pub u8); +#[allow(non_upper_case_globals)] +impl ImageMaskType { + pub const Alpha: Self = Self(0); + pub const Luminance: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Alpha, + Self::Luminance, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Alpha => Some("Alpha"), + Self::Luminance => Some("Luminance"), + _ => None, + } + } +} +impl ::core::fmt::Debug for ImageMaskType { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for ImageMaskType { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for ImageMaskType { + type Output = ImageMaskType; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for ImageMaskType { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for ImageMaskType { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for ImageMaskType {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_LAYER_MASK_TYPE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_LAYER_MASK_TYPE: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_LAYER_MASK_TYPE: [LayerMaskType; 3] = [ + LayerMaskType::NONE, + LayerMaskType::LayerMaskTypeImage, + LayerMaskType::LayerMaskTypeGeometry, +]; + +/// Rust: `LayerMaskType` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct LayerMaskType(pub u8); +#[allow(non_upper_case_globals)] +impl LayerMaskType { + pub const NONE: Self = Self(0); + pub const LayerMaskTypeImage: Self = Self(1); + pub const LayerMaskTypeGeometry: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::LayerMaskTypeImage, + Self::LayerMaskTypeGeometry, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::LayerMaskTypeImage => Some("LayerMaskTypeImage"), + Self::LayerMaskTypeGeometry => Some("LayerMaskTypeGeometry"), + _ => None, + } + } +} +impl ::core::fmt::Debug for LayerMaskType { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for LayerMaskType { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for LayerMaskType { + type Output = LayerMaskType; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for LayerMaskType { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for LayerMaskType { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for LayerMaskType {} +pub struct LayerMaskTypeUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_TEXT_TRANSFORM: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_TEXT_TRANSFORM: u8 = 3; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_TEXT_TRANSFORM: [TextTransform; 4] = [ + TextTransform::None, + TextTransform::Uppercase, + TextTransform::Lowercase, + TextTransform::Capitalize, +]; + +/// Rust: `TextTransform` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct TextTransform(pub u8); +#[allow(non_upper_case_globals)] +impl TextTransform { + pub const None: Self = Self(0); + pub const Uppercase: Self = Self(1); + pub const Lowercase: Self = Self(2); + pub const Capitalize: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::None, + Self::Uppercase, + Self::Lowercase, + Self::Capitalize, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::None => Some("None"), + Self::Uppercase => Some("Uppercase"), + Self::Lowercase => Some("Lowercase"), + Self::Capitalize => Some("Capitalize"), + _ => None, + } + } +} +impl ::core::fmt::Debug for TextTransform { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for TextTransform { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for TextTransform { + type Output = TextTransform; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for TextTransform { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for TextTransform { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for TextTransform {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_TEXT_DECORATION_LINE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_TEXT_DECORATION_LINE: u8 = 3; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_TEXT_DECORATION_LINE: [TextDecorationLine; 4] = [ + TextDecorationLine::None, + TextDecorationLine::Underline, + TextDecorationLine::Overline, + TextDecorationLine::LineThrough, +]; + +/// Rust: `TextDecorationLine` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct TextDecorationLine(pub u8); +#[allow(non_upper_case_globals)] +impl TextDecorationLine { + pub const None: Self = Self(0); + pub const Underline: Self = Self(1); + pub const Overline: Self = Self(2); + pub const LineThrough: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::None, + Self::Underline, + Self::Overline, + Self::LineThrough, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::None => Some("None"), + Self::Underline => Some("Underline"), + Self::Overline => Some("Overline"), + Self::LineThrough => Some("LineThrough"), + _ => None, + } + } +} +impl ::core::fmt::Debug for TextDecorationLine { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for TextDecorationLine { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for TextDecorationLine { + type Output = TextDecorationLine; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for TextDecorationLine { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for TextDecorationLine { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for TextDecorationLine {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_TEXT_DECORATION_STYLE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_TEXT_DECORATION_STYLE: u8 = 4; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_TEXT_DECORATION_STYLE: [TextDecorationStyle; 5] = [ + TextDecorationStyle::Solid, + TextDecorationStyle::Double, + TextDecorationStyle::Dotted, + TextDecorationStyle::Dashed, + TextDecorationStyle::Wavy, +]; + +/// Rust: `TextDecorationStyle` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct TextDecorationStyle(pub u8); +#[allow(non_upper_case_globals)] +impl TextDecorationStyle { + pub const Solid: Self = Self(0); + pub const Double: Self = Self(1); + pub const Dotted: Self = Self(2); + pub const Dashed: Self = Self(3); + pub const Wavy: Self = Self(4); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 4; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Solid, + Self::Double, + Self::Dotted, + Self::Dashed, + Self::Wavy, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Solid => Some("Solid"), + Self::Double => Some("Double"), + Self::Dotted => Some("Dotted"), + Self::Dashed => Some("Dashed"), + Self::Wavy => Some("Wavy"), + _ => None, + } + } +} +impl ::core::fmt::Debug for TextDecorationStyle { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for TextDecorationStyle { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for TextDecorationStyle { + type Output = TextDecorationStyle; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for TextDecorationStyle { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for TextDecorationStyle { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for TextDecorationStyle {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_FONT_OPTICAL_SIZING_KIND: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_FONT_OPTICAL_SIZING_KIND: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_FONT_OPTICAL_SIZING_KIND: [FontOpticalSizingKind; 3] = [ + FontOpticalSizingKind::Auto, + FontOpticalSizingKind::None, + FontOpticalSizingKind::Fixed, +]; + +/// Rust: `FontOpticalSizing` (Auto | None | Fixed(f32)) +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct FontOpticalSizingKind(pub u8); +#[allow(non_upper_case_globals)] +impl FontOpticalSizingKind { + pub const Auto: Self = Self(0); + pub const None: Self = Self(1); + pub const Fixed: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Auto, + Self::None, + Self::Fixed, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Auto => Some("Auto"), + Self::None => Some("None"), + Self::Fixed => Some("Fixed"), + _ => None, + } + } +} +impl ::core::fmt::Debug for FontOpticalSizingKind { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for FontOpticalSizingKind { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for FontOpticalSizingKind { + type Output = FontOpticalSizingKind; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for FontOpticalSizingKind { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for FontOpticalSizingKind { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for FontOpticalSizingKind {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_TEXT_DIMENSION_KIND: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_TEXT_DIMENSION_KIND: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_TEXT_DIMENSION_KIND: [TextDimensionKind; 3] = [ + TextDimensionKind::Normal, + TextDimensionKind::Fixed, + TextDimensionKind::Factor, +]; + +/// `TextLineHeight` (Normal | Fixed(f32) | Factor(f32)) +/// `TextLetterSpacing` (Normal | Fixed(f32) | Factor(f32)) +/// `TextWordSpacing` (Normal | Fixed(f32) | Factor(f32)) +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct TextDimensionKind(pub u8); +#[allow(non_upper_case_globals)] +impl TextDimensionKind { + pub const Normal: Self = Self(0); + pub const Fixed: Self = Self(1); + pub const Factor: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Normal, + Self::Fixed, + Self::Factor, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Normal => Some("Normal"), + Self::Fixed => Some("Fixed"), + Self::Factor => Some("Factor"), + _ => None, + } + } +} +impl ::core::fmt::Debug for TextDimensionKind { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for TextDimensionKind { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for TextDimensionKind { + type Output = TextDimensionKind; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for TextDimensionKind { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for TextDimensionKind { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for TextDimensionKind {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_RESOURCE_REF: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_RESOURCE_REF: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_RESOURCE_REF: [ResourceRef; 3] = [ + ResourceRef::NONE, + ResourceRef::ResourceRefHASH, + ResourceRef::ResourceRefRID, +]; + +/// Rust: `ResourceRef` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct ResourceRef(pub u8); +#[allow(non_upper_case_globals)] +impl ResourceRef { + pub const NONE: Self = Self(0); + pub const ResourceRefHASH: Self = Self(1); + pub const ResourceRefRID: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::ResourceRefHASH, + Self::ResourceRefRID, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::ResourceRefHASH => Some("ResourceRefHASH"), + Self::ResourceRefRID => Some("ResourceRefRID"), + _ => None, + } + } +} +impl ::core::fmt::Debug for ResourceRef { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for ResourceRef { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for ResourceRef { + type Output = ResourceRef; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for ResourceRef { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for ResourceRef { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for ResourceRef {} +pub struct ResourceRefUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_IMAGE_PAINT_FIT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_IMAGE_PAINT_FIT: u8 = 3; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_IMAGE_PAINT_FIT: [ImagePaintFit; 4] = [ + ImagePaintFit::NONE, + ImagePaintFit::ImagePaintFitFit, + ImagePaintFit::ImagePaintFitTransform, + ImagePaintFit::ImagePaintFitTile, +]; + +/// Rust: `ImagePaintFit` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct ImagePaintFit(pub u8); +#[allow(non_upper_case_globals)] +impl ImagePaintFit { + pub const NONE: Self = Self(0); + pub const ImagePaintFitFit: Self = Self(1); + pub const ImagePaintFitTransform: Self = Self(2); + pub const ImagePaintFitTile: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::ImagePaintFitFit, + Self::ImagePaintFitTransform, + Self::ImagePaintFitTile, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::ImagePaintFitFit => Some("ImagePaintFitFit"), + Self::ImagePaintFitTransform => Some("ImagePaintFitTransform"), + Self::ImagePaintFitTile => Some("ImagePaintFitTile"), + _ => None, + } + } +} +impl ::core::fmt::Debug for ImagePaintFit { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for ImagePaintFit { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for ImagePaintFit { + type Output = ImagePaintFit; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for ImagePaintFit { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for ImagePaintFit { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for ImagePaintFit {} +pub struct ImagePaintFitUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_PAINT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_PAINT: u8 = 6; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_PAINT: [Paint; 7] = [ + Paint::NONE, + Paint::SolidPaint, + Paint::LinearGradientPaint, + Paint::RadialGradientPaint, + Paint::SweepGradientPaint, + Paint::DiamondGradientPaint, + Paint::ImagePaint, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct Paint(pub u8); +#[allow(non_upper_case_globals)] +impl Paint { + pub const NONE: Self = Self(0); + pub const SolidPaint: Self = Self(1); + pub const LinearGradientPaint: Self = Self(2); + pub const RadialGradientPaint: Self = Self(3); + pub const SweepGradientPaint: Self = Self(4); + pub const DiamondGradientPaint: Self = Self(5); + pub const ImagePaint: Self = Self(6); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 6; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::SolidPaint, + Self::LinearGradientPaint, + Self::RadialGradientPaint, + Self::SweepGradientPaint, + Self::DiamondGradientPaint, + Self::ImagePaint, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::SolidPaint => Some("SolidPaint"), + Self::LinearGradientPaint => Some("LinearGradientPaint"), + Self::RadialGradientPaint => Some("RadialGradientPaint"), + Self::SweepGradientPaint => Some("SweepGradientPaint"), + Self::DiamondGradientPaint => Some("DiamondGradientPaint"), + Self::ImagePaint => Some("ImagePaint"), + _ => None, + } + } +} +impl ::core::fmt::Debug for Paint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for Paint { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for Paint { + type Output = Paint; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for Paint { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for Paint { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for Paint {} +pub struct PaintUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_EDGE_POINT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_EDGE_POINT: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_EDGE_POINT: [EdgePoint; 3] = [ + EdgePoint::NONE, + EdgePoint::EdgePointPosition2D, + EdgePoint::EdgePointNodeAnchor, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct EdgePoint(pub u8); +#[allow(non_upper_case_globals)] +impl EdgePoint { + pub const NONE: Self = Self(0); + pub const EdgePointPosition2D: Self = Self(1); + pub const EdgePointNodeAnchor: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::EdgePointPosition2D, + Self::EdgePointNodeAnchor, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::EdgePointPosition2D => Some("EdgePointPosition2D"), + Self::EdgePointNodeAnchor => Some("EdgePointNodeAnchor"), + _ => None, + } + } +} +impl ::core::fmt::Debug for EdgePoint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for EdgePoint { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for EdgePoint { + type Output = EdgePoint; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for EdgePoint { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for EdgePoint { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for EdgePoint {} +pub struct EdgePointUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_FE_BLUR: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_FE_BLUR: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_FE_BLUR: [FeBlur; 3] = [ + FeBlur::NONE, + FeBlur::FeGaussianBlur, + FeBlur::FeProgressiveBlur, +]; + +/// Rust: `FeBlur` (enum) +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct FeBlur(pub u8); +#[allow(non_upper_case_globals)] +impl FeBlur { + pub const NONE: Self = Self(0); + pub const FeGaussianBlur: Self = Self(1); + pub const FeProgressiveBlur: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::FeGaussianBlur, + Self::FeProgressiveBlur, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::FeGaussianBlur => Some("FeGaussianBlur"), + Self::FeProgressiveBlur => Some("FeProgressiveBlur"), + _ => None, + } + } +} +impl ::core::fmt::Debug for FeBlur { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for FeBlur { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for FeBlur { + type Output = FeBlur; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for FeBlur { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for FeBlur { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for FeBlur {} +pub struct FeBlurUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_FILTER_SHADOW_EFFECT_KIND: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_FILTER_SHADOW_EFFECT_KIND: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_FILTER_SHADOW_EFFECT_KIND: [FilterShadowEffectKind; 2] = [ + FilterShadowEffectKind::DropShadow, + FilterShadowEffectKind::InnerShadow, +]; + +/// Rust: `FilterShadowEffect` (enum) +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct FilterShadowEffectKind(pub u8); +#[allow(non_upper_case_globals)] +impl FilterShadowEffectKind { + pub const DropShadow: Self = Self(0); + pub const InnerShadow: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::DropShadow, + Self::InnerShadow, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::DropShadow => Some("DropShadow"), + Self::InnerShadow => Some("InnerShadow"), + _ => None, + } + } +} +impl ::core::fmt::Debug for FilterShadowEffectKind { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for FilterShadowEffectKind { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for FilterShadowEffectKind { + type Output = FilterShadowEffectKind; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for FilterShadowEffectKind { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for FilterShadowEffectKind { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for FilterShadowEffectKind {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_NOISE_EFFECT_COLORS_KIND: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_NOISE_EFFECT_COLORS_KIND: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_NOISE_EFFECT_COLORS_KIND: [NoiseEffectColorsKind; 3] = [ + NoiseEffectColorsKind::Mono, + NoiseEffectColorsKind::Duo, + NoiseEffectColorsKind::Multi, +]; + +/// Rust: `NoiseEffectColors` (enum) +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct NoiseEffectColorsKind(pub u8); +#[allow(non_upper_case_globals)] +impl NoiseEffectColorsKind { + pub const Mono: Self = Self(0); + pub const Duo: Self = Self(1); + pub const Multi: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Mono, + Self::Duo, + Self::Multi, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Mono => Some("Mono"), + Self::Duo => Some("Duo"), + Self::Multi => Some("Multi"), + _ => None, + } + } +} +impl ::core::fmt::Debug for NoiseEffectColorsKind { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for NoiseEffectColorsKind { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for NoiseEffectColorsKind { + type Output = NoiseEffectColorsKind; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for NoiseEffectColorsKind { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for NoiseEffectColorsKind { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for NoiseEffectColorsKind {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_CANONICAL_LAYER_SHAPE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_CANONICAL_LAYER_SHAPE: u8 = 6; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_CANONICAL_LAYER_SHAPE: [CanonicalLayerShape; 7] = [ + CanonicalLayerShape::NONE, + CanonicalLayerShape::CanonicalShapeRectangular, + CanonicalLayerShape::CanonicalShapeElliptical, + CanonicalLayerShape::CanonicalShapePointsPolygon, + CanonicalLayerShape::CanonicalShapeRegularPolygon, + CanonicalLayerShape::CanonicalShapeRegularStarPolygon, + CanonicalLayerShape::CanonicalShapePath, +]; + +/// Canonical layer shape descriptor (geometry-only, layout-resolved). +/// +/// `CanonicalLayerShape` is a *minimal*, *layout-independent* description of a +/// primitive shape used by layer-backed nodes (e.g. `BasicShapeNode`). +/// +/// Core semantics: +/// - This union intentionally does NOT encode size (no width/height). +/// - The concrete geometry is produced by mapping the selected shape variant into +/// the node’s resolved layout box at render time. +/// - Shape-specific parameters are expressed in normalized or ratio form: +/// * points/polygon/path use 0..1 normalized coordinates (scaled to the box) +/// * ellipse ring/sector uses ratios + angles (interpreted in the box) +/// - All styling (fills, strokes, effects, corner traits, etc.) lives outside the +/// shape descriptor on the owning node/traits. This union is geometry-only. +/// +/// Evolution rules: +/// - New primitive shape variants may be added by introducing a new `CanonicalShape*` +/// table and adding it to this union (additive change). +/// - Consumers must tolerate unknown union members (e.g. skip, treat as `UnknownNode`, +/// or fall back to a rectangle) rather than hard-failing, to preserve forward +/// compatibility. +/// +/// Notes: +/// - The “Canonical” prefix indicates the representation is the stable, box-mapped +/// geometry form used in the archive, not a fully resolved mesh/path at a fixed size. +/// - Even when a variant currently has no fields (e.g. rectangular), it is kept as a +/// distinct member to preserve explicit author intent and allow future extensions +/// without changing other variants. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct CanonicalLayerShape(pub u8); +#[allow(non_upper_case_globals)] +impl CanonicalLayerShape { + pub const NONE: Self = Self(0); + pub const CanonicalShapeRectangular: Self = Self(1); + pub const CanonicalShapeElliptical: Self = Self(2); + pub const CanonicalShapePointsPolygon: Self = Self(3); + pub const CanonicalShapeRegularPolygon: Self = Self(4); + pub const CanonicalShapeRegularStarPolygon: Self = Self(5); + pub const CanonicalShapePath: Self = Self(6); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 6; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::CanonicalShapeRectangular, + Self::CanonicalShapeElliptical, + Self::CanonicalShapePointsPolygon, + Self::CanonicalShapeRegularPolygon, + Self::CanonicalShapeRegularStarPolygon, + Self::CanonicalShapePath, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::CanonicalShapeRectangular => Some("CanonicalShapeRectangular"), + Self::CanonicalShapeElliptical => Some("CanonicalShapeElliptical"), + Self::CanonicalShapePointsPolygon => Some("CanonicalShapePointsPolygon"), + Self::CanonicalShapeRegularPolygon => Some("CanonicalShapeRegularPolygon"), + Self::CanonicalShapeRegularStarPolygon => Some("CanonicalShapeRegularStarPolygon"), + Self::CanonicalShapePath => Some("CanonicalShapePath"), + _ => None, + } + } +} +impl ::core::fmt::Debug for CanonicalLayerShape { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for CanonicalLayerShape { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for CanonicalLayerShape { + type Output = CanonicalLayerShape; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for CanonicalLayerShape { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for CanonicalLayerShape { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for CanonicalLayerShape {} +pub struct CanonicalLayerShapeUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_LAYOUT_DIMENSION_UNIT: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_LAYOUT_DIMENSION_UNIT: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_LAYOUT_DIMENSION_UNIT: [LayoutDimensionUnit; 2] = [ + LayoutDimensionUnit::LengthPx, + LayoutDimensionUnit::Percentage, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct LayoutDimensionUnit(pub u8); +#[allow(non_upper_case_globals)] +impl LayoutDimensionUnit { + pub const LengthPx: Self = Self(0); + pub const Percentage: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::LengthPx, + Self::Percentage, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::LengthPx => Some("LengthPx"), + Self::Percentage => Some("Percentage"), + _ => None, + } + } +} +impl ::core::fmt::Debug for LayoutDimensionUnit { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for LayoutDimensionUnit { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for LayoutDimensionUnit { + type Output = LayoutDimensionUnit; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for LayoutDimensionUnit { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for LayoutDimensionUnit { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for LayoutDimensionUnit {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_POSITIONING_SIDE_OFFSET_KIND: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_POSITIONING_SIDE_OFFSET_KIND: u8 = 1; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_POSITIONING_SIDE_OFFSET_KIND: [PositioningSideOffsetKind; 2] = [ + PositioningSideOffsetKind::Px, + PositioningSideOffsetKind::Percent, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct PositioningSideOffsetKind(pub u8); +#[allow(non_upper_case_globals)] +impl PositioningSideOffsetKind { + pub const Px: Self = Self(0); + pub const Percent: Self = Self(1); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 1; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Px, + Self::Percent, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Px => Some("Px"), + Self::Percent => Some("Percent"), + _ => None, + } + } +} +impl ::core::fmt::Debug for PositioningSideOffsetKind { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for PositioningSideOffsetKind { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for PositioningSideOffsetKind { + type Output = PositioningSideOffsetKind; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for PositioningSideOffsetKind { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for PositioningSideOffsetKind { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for PositioningSideOffsetKind {} +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_LAYOUT_POSITIONING_BASIS: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_LAYOUT_POSITIONING_BASIS: u8 = 2; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_LAYOUT_POSITIONING_BASIS: [LayoutPositioningBasis; 3] = [ + LayoutPositioningBasis::NONE, + LayoutPositioningBasis::LayoutPositioningCartesian, + LayoutPositioningBasis::LayoutPositioningInset, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct LayoutPositioningBasis(pub u8); +#[allow(non_upper_case_globals)] +impl LayoutPositioningBasis { + pub const NONE: Self = Self(0); + pub const LayoutPositioningCartesian: Self = Self(1); + pub const LayoutPositioningInset: Self = Self(2); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::LayoutPositioningCartesian, + Self::LayoutPositioningInset, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::LayoutPositioningCartesian => Some("LayoutPositioningCartesian"), + Self::LayoutPositioningInset => Some("LayoutPositioningInset"), + _ => None, + } + } +} +impl ::core::fmt::Debug for LayoutPositioningBasis { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for LayoutPositioningBasis { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for LayoutPositioningBasis { + type Output = LayoutPositioningBasis; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for LayoutPositioningBasis { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for LayoutPositioningBasis { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for LayoutPositioningBasis {} +pub struct LayoutPositioningBasisUnionTableOffset {} + +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MIN_NODE: u8 = 0; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +pub const ENUM_MAX_NODE: u8 = 10; +#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_NODE: [Node; 11] = [ + Node::NONE, + Node::UnknownNode, + Node::SceneNode, + Node::GroupNode, + Node::InitialContainerNode, + Node::ContainerNode, + Node::BooleanOperationNode, + Node::BasicShapeNode, + Node::LineNode, + Node::VectorNode, + Node::TextSpanNode, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct Node(pub u8); +#[allow(non_upper_case_globals)] +impl Node { + pub const NONE: Self = Self(0); + pub const UnknownNode: Self = Self(1); + pub const SceneNode: Self = Self(2); + pub const GroupNode: Self = Self(3); + pub const InitialContainerNode: Self = Self(4); + pub const ContainerNode: Self = Self(5); + pub const BooleanOperationNode: Self = Self(6); + pub const BasicShapeNode: Self = Self(7); + pub const LineNode: Self = Self(8); + pub const VectorNode: Self = Self(9); + pub const TextSpanNode: Self = Self(10); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 10; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::UnknownNode, + Self::SceneNode, + Self::GroupNode, + Self::InitialContainerNode, + Self::ContainerNode, + Self::BooleanOperationNode, + Self::BasicShapeNode, + Self::LineNode, + Self::VectorNode, + Self::TextSpanNode, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::UnknownNode => Some("UnknownNode"), + Self::SceneNode => Some("SceneNode"), + Self::GroupNode => Some("GroupNode"), + Self::InitialContainerNode => Some("InitialContainerNode"), + Self::ContainerNode => Some("ContainerNode"), + Self::BooleanOperationNode => Some("BooleanOperationNode"), + Self::BasicShapeNode => Some("BasicShapeNode"), + Self::LineNode => Some("LineNode"), + Self::VectorNode => Some("VectorNode"), + Self::TextSpanNode => Some("TextSpanNode"), + _ => None, + } + } +} +impl ::core::fmt::Debug for Node { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for Node { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for Node { + type Output = Node; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for Node { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for Node { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for Node {} +pub struct NodeUnionTableOffset {} + +/// Rust: `CGPoint { x: f32, y: f32 }` +// struct CGPoint, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct CGPoint(pub [u8; 8]); +impl Default for CGPoint { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for CGPoint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("CGPoint") + .field("x", &self.x()) + .field("y", &self.y()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for CGPoint {} +impl<'a> ::flatbuffers::Follow<'a> for CGPoint { + type Inner = &'a CGPoint; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a CGPoint>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a CGPoint { + type Inner = &'a CGPoint; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for CGPoint { + type Output = CGPoint; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const CGPoint as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for CGPoint { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> CGPoint { + #[allow(clippy::too_many_arguments)] + pub fn new( + x: f32, + y: f32, + ) -> Self { + let mut s = Self([0; 8]); + s.set_x(x); + s.set_y(y); + s + } + + pub fn x(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_x(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn y(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_y(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// `Alignment(pub f32, pub f32)` (cNDC, range typically [-1, 1]) +/// +/// Alignment(0,0) is the center of the rectangle. +// struct Alignment, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct Alignment(pub [u8; 8]); +impl Default for Alignment { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for Alignment { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("Alignment") + .field("x", &self.x()) + .field("y", &self.y()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for Alignment {} +impl<'a> ::flatbuffers::Follow<'a> for Alignment { + type Inner = &'a Alignment; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a Alignment>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a Alignment { + type Inner = &'a Alignment; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for Alignment { + type Output = Alignment; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const Alignment as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for Alignment { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> Alignment { + #[allow(clippy::too_many_arguments)] + pub fn new( + x: f32, + y: f32, + ) -> Self { + let mut s = Self([0; 8]); + s.set_x(x); + s.set_y(y); + s + } + + pub fn x(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_x(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn y(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_y(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: `Uv(pub f32, pub f32)` (normalized [0, 1] domain) +// struct UV, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct UV(pub [u8; 8]); +impl Default for UV { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for UV { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("UV") + .field("u", &self.u()) + .field("v", &self.v()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for UV {} +impl<'a> ::flatbuffers::Follow<'a> for UV { + type Inner = &'a UV; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a UV>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a UV { + type Inner = &'a UV; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for UV { + type Output = UV; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const UV as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for UV { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> UV { + #[allow(clippy::too_many_arguments)] + pub fn new( + u: f32, + v: f32, + ) -> Self { + let mut s = Self([0; 8]); + s.set_u(u); + s.set_v(v); + s + } + + pub fn u(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_u(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn v(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_v(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: (no dedicated cg struct) used throughout as (w,h). Keep for schema convenience. +// struct CGSize, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct CGSize(pub [u8; 8]); +impl Default for CGSize { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for CGSize { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("CGSize") + .field("width", &self.width()) + .field("height", &self.height()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for CGSize {} +impl<'a> ::flatbuffers::Follow<'a> for CGSize { + type Inner = &'a CGSize; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a CGSize>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a CGSize { + type Inner = &'a CGSize; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for CGSize { + type Output = CGSize; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const CGSize as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for CGSize { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> CGSize { + #[allow(clippy::too_many_arguments)] + pub fn new( + width: f32, + height: f32, + ) -> Self { + let mut s = Self([0; 8]); + s.set_width(width); + s.set_height(height); + s + } + + pub fn width(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_width(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn height(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_height(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// RGBA in linear float space (0..1). +// struct RGBA32F, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct RGBA32F(pub [u8; 16]); +impl Default for RGBA32F { + fn default() -> Self { + Self([0; 16]) + } +} +impl ::core::fmt::Debug for RGBA32F { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("RGBA32F") + .field("r", &self.r()) + .field("g", &self.g()) + .field("b", &self.b()) + .field("a", &self.a()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for RGBA32F {} +impl<'a> ::flatbuffers::Follow<'a> for RGBA32F { + type Inner = &'a RGBA32F; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a RGBA32F>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a RGBA32F { + type Inner = &'a RGBA32F; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for RGBA32F { + type Output = RGBA32F; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const RGBA32F as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for RGBA32F { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> RGBA32F { + #[allow(clippy::too_many_arguments)] + pub fn new( + r: f32, + g: f32, + b: f32, + a: f32, + ) -> Self { + let mut s = Self([0; 16]); + s.set_r(r); + s.set_g(g); + s.set_b(b); + s.set_a(a); + s + } + + pub fn r(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_r(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn g(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_g(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn b(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[8..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_b(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[8..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn a(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[12..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_a(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[12..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: `CGTransform2D { m00..m12: f32 }` (and compatible with math2 `AffineTransform`). +/// +/// Matrix layout: +/// [ m00 m01 m02 ] +/// [ m10 m11 m12 ] +/// [ 0 0 1 ] +/// +/// Struct representation (no defaults in FlatBuffers structs). +// struct CGTransform2D, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct CGTransform2D(pub [u8; 24]); +impl Default for CGTransform2D { + fn default() -> Self { + Self([0; 24]) + } +} +impl ::core::fmt::Debug for CGTransform2D { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("CGTransform2D") + .field("m00", &self.m00()) + .field("m01", &self.m01()) + .field("m02", &self.m02()) + .field("m10", &self.m10()) + .field("m11", &self.m11()) + .field("m12", &self.m12()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for CGTransform2D {} +impl<'a> ::flatbuffers::Follow<'a> for CGTransform2D { + type Inner = &'a CGTransform2D; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a CGTransform2D>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a CGTransform2D { + type Inner = &'a CGTransform2D; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for CGTransform2D { + type Output = CGTransform2D; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const CGTransform2D as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for CGTransform2D { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> CGTransform2D { + #[allow(clippy::too_many_arguments)] + pub fn new( + m00: f32, + m01: f32, + m02: f32, + m10: f32, + m11: f32, + m12: f32, + ) -> Self { + let mut s = Self([0; 24]); + s.set_m00(m00); + s.set_m01(m01); + s.set_m02(m02); + s.set_m10(m10); + s.set_m11(m11); + s.set_m12(m12); + s + } + + pub fn m00(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_m00(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn m01(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_m01(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn m02(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[8..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_m02(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[8..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn m10(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[12..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_m10(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[12..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn m11(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[16..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_m11(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[16..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn m12(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[20..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_m12(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[20..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: `Radius { rx: f32, ry: f32 }` +// struct CGRadius, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct CGRadius(pub [u8; 8]); +impl Default for CGRadius { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for CGRadius { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("CGRadius") + .field("rx", &self.rx()) + .field("ry", &self.ry()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for CGRadius {} +impl<'a> ::flatbuffers::Follow<'a> for CGRadius { + type Inner = &'a CGRadius; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a CGRadius>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a CGRadius { + type Inner = &'a CGRadius; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for CGRadius { + type Output = CGRadius; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const CGRadius as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for CGRadius { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> CGRadius { + #[allow(clippy::too_many_arguments)] + pub fn new( + rx: f32, + ry: f32, + ) -> Self { + let mut s = Self([0; 8]); + s.set_rx(rx); + s.set_ry(ry); + s + } + + pub fn rx(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_rx(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn ry(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_ry(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +// struct EdgeInsets, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct EdgeInsets(pub [u8; 16]); +impl Default for EdgeInsets { + fn default() -> Self { + Self([0; 16]) + } +} +impl ::core::fmt::Debug for EdgeInsets { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("EdgeInsets") + .field("top", &self.top()) + .field("right", &self.right()) + .field("bottom", &self.bottom()) + .field("left", &self.left()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for EdgeInsets {} +impl<'a> ::flatbuffers::Follow<'a> for EdgeInsets { + type Inner = &'a EdgeInsets; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a EdgeInsets>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a EdgeInsets { + type Inner = &'a EdgeInsets; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for EdgeInsets { + type Output = EdgeInsets; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const EdgeInsets as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for EdgeInsets { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> EdgeInsets { + #[allow(clippy::too_many_arguments)] + pub fn new( + top: f32, + right: f32, + bottom: f32, + left: f32, + ) -> Self { + let mut s = Self([0; 16]); + s.set_top(top); + s.set_right(right); + s.set_bottom(bottom); + s.set_left(left); + s + } + + pub fn top(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_top(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn right(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_right(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn bottom(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[8..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_bottom(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[8..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn left(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[12..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_left(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[12..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: `RectangularCornerRadius { tl,tr,bl,br: Radius }` +// struct RectangularCornerRadius, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct RectangularCornerRadius(pub [u8; 32]); +impl Default for RectangularCornerRadius { + fn default() -> Self { + Self([0; 32]) + } +} +impl ::core::fmt::Debug for RectangularCornerRadius { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("RectangularCornerRadius") + .field("tl", &self.tl()) + .field("tr", &self.tr()) + .field("bl", &self.bl()) + .field("br", &self.br()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for RectangularCornerRadius {} +impl<'a> ::flatbuffers::Follow<'a> for RectangularCornerRadius { + type Inner = &'a RectangularCornerRadius; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a RectangularCornerRadius>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a RectangularCornerRadius { + type Inner = &'a RectangularCornerRadius; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for RectangularCornerRadius { + type Output = RectangularCornerRadius; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const RectangularCornerRadius as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for RectangularCornerRadius { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> RectangularCornerRadius { + #[allow(clippy::too_many_arguments)] + pub fn new( + tl: &CGRadius, + tr: &CGRadius, + bl: &CGRadius, + br: &CGRadius, + ) -> Self { + let mut s = Self([0; 32]); + s.set_tl(tl); + s.set_tr(tr); + s.set_bl(bl); + s.set_br(br); + s + } + + pub fn tl(&self) -> &CGRadius { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[0..].as_ptr() as *const CGRadius) } + } + + #[allow(clippy::identity_op)] + pub fn set_tl(&mut self, x: &CGRadius) { + self.0[0..0 + 8].copy_from_slice(&x.0) + } + + pub fn tr(&self) -> &CGRadius { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[8..].as_ptr() as *const CGRadius) } + } + + #[allow(clippy::identity_op)] + pub fn set_tr(&mut self, x: &CGRadius) { + self.0[8..8 + 8].copy_from_slice(&x.0) + } + + pub fn bl(&self) -> &CGRadius { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[16..].as_ptr() as *const CGRadius) } + } + + #[allow(clippy::identity_op)] + pub fn set_bl(&mut self, x: &CGRadius) { + self.0[16..16 + 8].copy_from_slice(&x.0) + } + + pub fn br(&self) -> &CGRadius { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[24..].as_ptr() as *const CGRadius) } + } + + #[allow(clippy::identity_op)] + pub fn set_br(&mut self, x: &CGRadius) { + self.0[24..24 + 8].copy_from_slice(&x.0) + } + +} + +/// Per-side stroke widths (archive model). +/// +/// Struct representation. Note: structs cannot be null/omitted; all-zeros can be +/// interpreted as "not used" by codec/runtime if desired. +// struct RectangularStrokeWidth, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct RectangularStrokeWidth(pub [u8; 16]); +impl Default for RectangularStrokeWidth { + fn default() -> Self { + Self([0; 16]) + } +} +impl ::core::fmt::Debug for RectangularStrokeWidth { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("RectangularStrokeWidth") + .field("stroke_top_width", &self.stroke_top_width()) + .field("stroke_right_width", &self.stroke_right_width()) + .field("stroke_bottom_width", &self.stroke_bottom_width()) + .field("stroke_left_width", &self.stroke_left_width()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for RectangularStrokeWidth {} +impl<'a> ::flatbuffers::Follow<'a> for RectangularStrokeWidth { + type Inner = &'a RectangularStrokeWidth; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a RectangularStrokeWidth>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a RectangularStrokeWidth { + type Inner = &'a RectangularStrokeWidth; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for RectangularStrokeWidth { + type Output = RectangularStrokeWidth; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const RectangularStrokeWidth as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for RectangularStrokeWidth { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> RectangularStrokeWidth { + #[allow(clippy::too_many_arguments)] + pub fn new( + stroke_top_width: f32, + stroke_right_width: f32, + stroke_bottom_width: f32, + stroke_left_width: f32, + ) -> Self { + let mut s = Self([0; 16]); + s.set_stroke_top_width(stroke_top_width); + s.set_stroke_right_width(stroke_right_width); + s.set_stroke_bottom_width(stroke_bottom_width); + s.set_stroke_left_width(stroke_left_width); + s + } + + pub fn stroke_top_width(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_stroke_top_width(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn stroke_right_width(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_stroke_right_width(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn stroke_bottom_width(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[8..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_stroke_bottom_width(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[8..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn stroke_left_width(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[12..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_stroke_left_width(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[12..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// OpenType feature tag (4-byte ASCII, standard OpenType format). +/// +/// OpenType feature tags are exactly 4 characters, stored as 4 bytes. +/// This matches the binary OpenType format where tags are stored as [u8; 4]. +/// +/// Examples: +/// "kern" = {a:0x6B, b:0x65, c:0x72, d:0x6E} +/// "liga" = {a:0x6C, b:0x69, c:0x67, d:0x61} +/// "ss01" = {a:0x73, b:0x73, c:0x30, d:0x31} +/// +/// Codecs should convert between string tags (e.g., "kern") and this struct. +/// This approach allows any 4-character OpenType tag without schema evolution. +// struct OpenTypeFeatureTag, aligned to 1 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct OpenTypeFeatureTag(pub [u8; 4]); +impl Default for OpenTypeFeatureTag { + fn default() -> Self { + Self([0; 4]) + } +} +impl ::core::fmt::Debug for OpenTypeFeatureTag { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("OpenTypeFeatureTag") + .field("a", &self.a()) + .field("b", &self.b()) + .field("c", &self.c()) + .field("d", &self.d()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for OpenTypeFeatureTag {} +impl<'a> ::flatbuffers::Follow<'a> for OpenTypeFeatureTag { + type Inner = &'a OpenTypeFeatureTag; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a OpenTypeFeatureTag>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a OpenTypeFeatureTag { + type Inner = &'a OpenTypeFeatureTag; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for OpenTypeFeatureTag { + type Output = OpenTypeFeatureTag; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const OpenTypeFeatureTag as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(1) + } +} + +impl<'a> ::flatbuffers::Verifiable for OpenTypeFeatureTag { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> OpenTypeFeatureTag { + #[allow(clippy::too_many_arguments)] + pub fn new( + a: u8, + b: u8, + c: u8, + d: u8, + ) -> Self { + let mut s = Self([0; 4]); + s.set_a(a); + s.set_b(b); + s.set_c(c); + s.set_d(d); + s + } + + pub fn a(&self) -> u8 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_a(&mut self, x: u8) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn b(&self) -> u8 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[1..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_b(&mut self, x: u8) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[1..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn c(&self) -> u8 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[2..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_c(&mut self, x: u8) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[2..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn d(&self) -> u8 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[3..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_d(&mut self, x: u8) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[3..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: `FontWeight(pub u32)` +// struct FontWeight, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct FontWeight(pub [u8; 4]); +impl Default for FontWeight { + fn default() -> Self { + Self([0; 4]) + } +} +impl ::core::fmt::Debug for FontWeight { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("FontWeight") + .field("value", &self.value()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for FontWeight {} +impl<'a> ::flatbuffers::Follow<'a> for FontWeight { + type Inner = &'a FontWeight; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a FontWeight>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a FontWeight { + type Inner = &'a FontWeight; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for FontWeight { + type Output = FontWeight; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const FontWeight as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for FontWeight { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> FontWeight { + #[allow(clippy::too_many_arguments)] + pub fn new( + value: u32, + ) -> Self { + let mut s = Self([0; 4]); + s.set_value(value); + s + } + + pub fn value(&self) -> u32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_value(&mut self, x: u32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +// struct FontOpticalSizing, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct FontOpticalSizing(pub [u8; 8]); +impl Default for FontOpticalSizing { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for FontOpticalSizing { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("FontOpticalSizing") + .field("kind", &self.kind()) + .field("value", &self.value()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for FontOpticalSizing {} +impl<'a> ::flatbuffers::Follow<'a> for FontOpticalSizing { + type Inner = &'a FontOpticalSizing; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a FontOpticalSizing>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a FontOpticalSizing { + type Inner = &'a FontOpticalSizing; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for FontOpticalSizing { + type Output = FontOpticalSizing; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const FontOpticalSizing as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for FontOpticalSizing { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> FontOpticalSizing { + #[allow(clippy::too_many_arguments)] + pub fn new( + kind: FontOpticalSizingKind, + value: f32, + ) -> Self { + let mut s = Self([0; 8]); + s.set_kind(kind); + s.set_value(value); + s + } + + pub fn kind(&self) -> FontOpticalSizingKind { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_kind(&mut self, x: FontOpticalSizingKind) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn value(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_value(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +// struct GradientStop, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct GradientStop(pub [u8; 20]); +impl Default for GradientStop { + fn default() -> Self { + Self([0; 20]) + } +} +impl ::core::fmt::Debug for GradientStop { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("GradientStop") + .field("stop_offset", &self.stop_offset()) + .field("stop_color", &self.stop_color()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for GradientStop {} +impl<'a> ::flatbuffers::Follow<'a> for GradientStop { + type Inner = &'a GradientStop; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a GradientStop>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a GradientStop { + type Inner = &'a GradientStop; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for GradientStop { + type Output = GradientStop; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const GradientStop as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for GradientStop { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> GradientStop { + #[allow(clippy::too_many_arguments)] + pub fn new( + stop_offset: f32, + stop_color: &RGBA32F, + ) -> Self { + let mut s = Self([0; 20]); + s.set_stop_offset(stop_offset); + s.set_stop_color(stop_color); + s + } + + pub fn stop_offset(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_stop_offset(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn stop_color(&self) -> &RGBA32F { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[4..].as_ptr() as *const RGBA32F) } + } + + #[allow(clippy::identity_op)] + pub fn set_stop_color(&mut self, x: &RGBA32F) { + self.0[4..4 + 16].copy_from_slice(&x.0) + } + +} + +/// `ImageFilters` normalized to -0+ (center|default 0) +// struct ImageFilters, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct ImageFilters(pub [u8; 28]); +impl Default for ImageFilters { + fn default() -> Self { + Self([0; 28]) + } +} +impl ::core::fmt::Debug for ImageFilters { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("ImageFilters") + .field("exposure", &self.exposure()) + .field("contrast", &self.contrast()) + .field("saturation", &self.saturation()) + .field("temperature", &self.temperature()) + .field("tint", &self.tint()) + .field("highlights", &self.highlights()) + .field("shadows", &self.shadows()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for ImageFilters {} +impl<'a> ::flatbuffers::Follow<'a> for ImageFilters { + type Inner = &'a ImageFilters; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a ImageFilters>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a ImageFilters { + type Inner = &'a ImageFilters; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for ImageFilters { + type Output = ImageFilters; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const ImageFilters as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for ImageFilters { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> ImageFilters { + #[allow(clippy::too_many_arguments)] + pub fn new( + exposure: f32, + contrast: f32, + saturation: f32, + temperature: f32, + tint: f32, + highlights: f32, + shadows: f32, + ) -> Self { + let mut s = Self([0; 28]); + s.set_exposure(exposure); + s.set_contrast(contrast); + s.set_saturation(saturation); + s.set_temperature(temperature); + s.set_tint(tint); + s.set_highlights(highlights); + s.set_shadows(shadows); + s + } + + pub fn exposure(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_exposure(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn contrast(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_contrast(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn saturation(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[8..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_saturation(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[8..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn temperature(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[12..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_temperature(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[12..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn tint(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[16..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_tint(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[16..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn highlights(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[20..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_highlights(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[20..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn shadows(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[24..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_shadows(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[24..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: `ImageTile` +// struct ImageTile, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct ImageTile(pub [u8; 8]); +impl Default for ImageTile { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for ImageTile { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("ImageTile") + .field("scale", &self.scale()) + .field("repeat", &self.repeat()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for ImageTile {} +impl<'a> ::flatbuffers::Follow<'a> for ImageTile { + type Inner = &'a ImageTile; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a ImageTile>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a ImageTile { + type Inner = &'a ImageTile; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for ImageTile { + type Output = ImageTile; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const ImageTile as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for ImageTile { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> ImageTile { + #[allow(clippy::too_many_arguments)] + pub fn new( + scale: f32, + repeat: ImageRepeat, + ) -> Self { + let mut s = Self([0; 8]); + s.set_scale(scale); + s.set_repeat(repeat); + s + } + + pub fn scale(&self) -> f32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_scale(&mut self, x: f32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn repeat(&self) -> ImageRepeat { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_repeat(&mut self, x: ImageRepeat) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + +} + +/// Rust: `(f32, f32)` vertex +/// +/// Stored as CGPoint (same coordinate space as node local geometry). +/// Indexed by position in the `vertices` array. +// struct VectorNetworkVertex, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct VectorNetworkVertex(pub [u8; 8]); +impl Default for VectorNetworkVertex { + fn default() -> Self { + Self([0; 8]) + } +} +impl ::core::fmt::Debug for VectorNetworkVertex { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("VectorNetworkVertex") + .field("vertex_position", &self.vertex_position()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for VectorNetworkVertex {} +impl<'a> ::flatbuffers::Follow<'a> for VectorNetworkVertex { + type Inner = &'a VectorNetworkVertex; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a VectorNetworkVertex>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a VectorNetworkVertex { + type Inner = &'a VectorNetworkVertex; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for VectorNetworkVertex { + type Output = VectorNetworkVertex; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const VectorNetworkVertex as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for VectorNetworkVertex { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> VectorNetworkVertex { + #[allow(clippy::too_many_arguments)] + pub fn new( + vertex_position: &CGPoint, + ) -> Self { + let mut s = Self([0; 8]); + s.set_vertex_position(vertex_position); + s + } + + pub fn vertex_position(&self) -> &CGPoint { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[0..].as_ptr() as *const CGPoint) } + } + + #[allow(clippy::identity_op)] + pub fn set_vertex_position(&mut self, x: &CGPoint) { + self.0[0..0 + 8].copy_from_slice(&x.0) + } + +} + +/// Rust: `VectorNetworkSegment { a, b, ta, tb }` +/// +/// `tangent_a` / `tangent_b` are relative tangent vectors used for cubic béziers. +/// When both tangents are zero, the segment is a straight line. +// struct VectorNetworkSegment, aligned to 4 +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq)] +pub struct VectorNetworkSegment(pub [u8; 24]); +impl Default for VectorNetworkSegment { + fn default() -> Self { + Self([0; 24]) + } +} +impl ::core::fmt::Debug for VectorNetworkSegment { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct("VectorNetworkSegment") + .field("segment_vertex_a", &self.segment_vertex_a()) + .field("segment_vertex_b", &self.segment_vertex_b()) + .field("tangent_a", &self.tangent_a()) + .field("tangent_b", &self.tangent_b()) + .finish() + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for VectorNetworkSegment {} +impl<'a> ::flatbuffers::Follow<'a> for VectorNetworkSegment { + type Inner = &'a VectorNetworkSegment; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { <&'a VectorNetworkSegment>::follow(buf, loc) } + } +} +impl<'a> ::flatbuffers::Follow<'a> for &'a VectorNetworkSegment { + type Inner = &'a VectorNetworkSegment; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + unsafe { ::flatbuffers::follow_cast_ref::(buf, loc) } + } +} +impl<'b> ::flatbuffers::Push for VectorNetworkSegment { + type Output = VectorNetworkSegment; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + let src = unsafe { ::core::slice::from_raw_parts(self as *const VectorNetworkSegment as *const u8, ::size()) }; + dst.copy_from_slice(src); + } + #[inline] + fn alignment() -> ::flatbuffers::PushAlignment { + ::flatbuffers::PushAlignment::new(4) + } +} + +impl<'a> ::flatbuffers::Verifiable for VectorNetworkSegment { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.in_buffer::(pos) + } +} + +impl<'a> VectorNetworkSegment { + #[allow(clippy::too_many_arguments)] + pub fn new( + segment_vertex_a: u32, + segment_vertex_b: u32, + tangent_a: &CGPoint, + tangent_b: &CGPoint, + ) -> Self { + let mut s = Self([0; 24]); + s.set_segment_vertex_a(segment_vertex_a); + s.set_segment_vertex_b(segment_vertex_b); + s.set_tangent_a(tangent_a); + s.set_tangent_b(tangent_b); + s + } + + pub fn segment_vertex_a(&self) -> u32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[0..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_segment_vertex_a(&mut self, x: u32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[0..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn segment_vertex_b(&self) -> u32 { + let mut mem = ::core::mem::MaybeUninit::<::Scalar>::uninit(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + ::flatbuffers::EndianScalar::from_little_endian(unsafe { + ::core::ptr::copy_nonoverlapping( + self.0[4..].as_ptr(), + mem.as_mut_ptr() as *mut u8, + ::core::mem::size_of::<::Scalar>(), + ); + mem.assume_init() + }) + } + + pub fn set_segment_vertex_b(&mut self, x: u32) { + let x_le = ::flatbuffers::EndianScalar::to_little_endian(x); + // Safety: + // Created from a valid Table for this object + // Which contains a valid value in this slot + unsafe { + ::core::ptr::copy_nonoverlapping( + &x_le as *const _ as *const u8, + self.0[4..].as_mut_ptr(), + ::core::mem::size_of::<::Scalar>(), + ); + } + } + + pub fn tangent_a(&self) -> &CGPoint { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[8..].as_ptr() as *const CGPoint) } + } + + #[allow(clippy::identity_op)] + pub fn set_tangent_a(&mut self, x: &CGPoint) { + self.0[8..8 + 8].copy_from_slice(&x.0) + } + + pub fn tangent_b(&self) -> &CGPoint { + // Safety: + // Created from a valid Table for this object + // Which contains a valid struct in this slot + unsafe { &*(self.0[16..].as_ptr() as *const CGPoint) } + } + + #[allow(clippy::identity_op)] + pub fn set_tangent_b(&mut self, x: &CGPoint) { + self.0[16..16 + 8].copy_from_slice(&x.0) + } + +} + +pub enum NoneOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// `None` +pub struct None<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for None<'a> { + type Inner = None<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> None<'a> { + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + None { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + _args: &'args NoneArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = NoneBuilder::new(_fbb); + builder.finish() + } + +} + +impl ::flatbuffers::Verifiable for None<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .finish(); + Ok(()) + } +} +pub struct NoneArgs { +} +impl<'a> Default for NoneArgs { + #[inline] + fn default() -> Self { + NoneArgs { + } + } +} + +pub struct NoneBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> NoneBuilder<'a, 'b, A> { + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> NoneBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NoneBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for None<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("None"); + ds.finish() + } +} +pub enum AutoOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Explicit length value models (archive input model; CSS-ish). +/// +/// NOTE: FlatBuffers unions can only include tables, not scalars/structs. +pub struct Auto<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for Auto<'a> { + type Inner = Auto<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> Auto<'a> { + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + Auto { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + _args: &'args AutoArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = AutoBuilder::new(_fbb); + builder.finish() + } + +} + +impl ::flatbuffers::Verifiable for Auto<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .finish(); + Ok(()) + } +} +pub struct AutoArgs { +} +impl<'a> Default for AutoArgs { + #[inline] + fn default() -> Self { + AutoArgs { + } + } +} + +pub struct AutoBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> AutoBuilder<'a, 'b, A> { + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> AutoBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + AutoBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for Auto<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("Auto"); + ds.finish() + } +} +pub enum NodeIdentifierOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node identifier (temporary: string-based for compatibility with current editor). +/// +/// TODO: Update to use packed u32 (actor:8 | counter:24) for better performance. +/// Range: 0..=4_294_967_295. +/// +/// Current implementation uses string IDs to match TS editor model. +/// Future: migrate to struct NodeIdentifier { packed:uint; } for efficiency. +pub struct NodeIdentifier<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for NodeIdentifier<'a> { + type Inner = NodeIdentifier<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> NodeIdentifier<'a> { + pub const VT_ID: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + NodeIdentifier { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args NodeIdentifierArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = NodeIdentifierBuilder::new(_fbb); + if let Some(x) = args.id { builder.add_id(x); } + builder.finish() + } + + + /// Required: unique identifier string for the node. + #[inline] + pub fn id(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(NodeIdentifier::VT_ID, None).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for NodeIdentifier<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("id", Self::VT_ID, true)? + .finish(); + Ok(()) + } +} +pub struct NodeIdentifierArgs<'a> { + pub id: Option<::flatbuffers::WIPOffset<&'a str>>, +} +impl<'a> Default for NodeIdentifierArgs<'a> { + #[inline] + fn default() -> Self { + NodeIdentifierArgs { + id: None, // required field + } + } +} + +pub struct NodeIdentifierBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> NodeIdentifierBuilder<'a, 'b, A> { + #[inline] + pub fn add_id(&mut self, id: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(NodeIdentifier::VT_ID, id); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> NodeIdentifierBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NodeIdentifierBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, NodeIdentifier::VT_ID,"id"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for NodeIdentifier<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("NodeIdentifier"); + ds.field("id", &self.id()); + ds.finish() + } +} +pub enum ParentReferenceOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Parent reference with fractional index position for ordering. +/// +/// Uses fractional indexing (orderable strings) to enable conflict-free +/// ordering in collaborative systems. The position string is designed to +/// sort correctly when compared lexicographically. +/// +/// Examples of fractional index strings: "!", " ~\"", "Qd&", "QeU", "Qe7", "QeO" +/// +/// Reference: Figma uses this same pattern (see `ParentIndex` in fig.kiwi). +/// This enables: +/// - Flat node structure (no nested children arrays) +/// - Efficient node moves (single node update, no parent reordering) +/// - Concurrent editing friendly ordering +/// +/// Note: Table (not struct) because it contains NodeIdentifier (a table). +/// This field is optional (can be null for root nodes). +pub struct ParentReference<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ParentReference<'a> { + type Inner = ParentReference<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ParentReference<'a> { + pub const VT_PARENT_ID: ::flatbuffers::VOffsetT = 4; + pub const VT_POSITION: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ParentReference { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ParentReferenceArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ParentReferenceBuilder::new(_fbb); + if let Some(x) = args.position { builder.add_position(x); } + if let Some(x) = args.parent_id { builder.add_parent_id(x); } + builder.finish() + } + + + /// Parent node identifier + #[inline] + pub fn parent_id(&self) -> NodeIdentifier<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(ParentReference::VT_PARENT_ID, None).unwrap()} + } + /// Fractional index position string for ordering among siblings. + /// Empty string means "unsorted" or "default position". + /// Children are sorted by lexicographic comparison of position strings. + #[inline] + pub fn position(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(ParentReference::VT_POSITION, None)} + } +} + +impl ::flatbuffers::Verifiable for ParentReference<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("parent_id", Self::VT_PARENT_ID, true)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("position", Self::VT_POSITION, false)? + .finish(); + Ok(()) + } +} +pub struct ParentReferenceArgs<'a> { + pub parent_id: Option<::flatbuffers::WIPOffset>>, + pub position: Option<::flatbuffers::WIPOffset<&'a str>>, +} +impl<'a> Default for ParentReferenceArgs<'a> { + #[inline] + fn default() -> Self { + ParentReferenceArgs { + parent_id: None, // required field + position: None, + } + } +} + +pub struct ParentReferenceBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ParentReferenceBuilder<'a, 'b, A> { + #[inline] + pub fn add_parent_id(&mut self, parent_id: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(ParentReference::VT_PARENT_ID, parent_id); + } + #[inline] + pub fn add_position(&mut self, position: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(ParentReference::VT_POSITION, position); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ParentReferenceBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ParentReferenceBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, ParentReference::VT_PARENT_ID,"parent_id"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ParentReference<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ParentReference"); + ds.field("parent_id", &self.parent_id()); + ds.field("position", &self.position()); + ds.finish() + } +} +pub enum LayerMaskTypeImageOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `LayerMaskType::Image(ImageMaskType)` +pub struct LayerMaskTypeImage<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayerMaskTypeImage<'a> { + type Inner = LayerMaskTypeImage<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayerMaskTypeImage<'a> { + pub const VT_IMAGE_MASK_TYPE: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayerMaskTypeImage { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayerMaskTypeImageArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayerMaskTypeImageBuilder::new(_fbb); + builder.add_image_mask_type(args.image_mask_type); + builder.finish() + } + + + #[inline] + pub fn image_mask_type(&self) -> ImageMaskType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayerMaskTypeImage::VT_IMAGE_MASK_TYPE, Some(ImageMaskType::Alpha)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for LayerMaskTypeImage<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("image_mask_type", Self::VT_IMAGE_MASK_TYPE, false)? + .finish(); + Ok(()) + } +} +pub struct LayerMaskTypeImageArgs { + pub image_mask_type: ImageMaskType, +} +impl<'a> Default for LayerMaskTypeImageArgs { + #[inline] + fn default() -> Self { + LayerMaskTypeImageArgs { + image_mask_type: ImageMaskType::Alpha, + } + } +} + +pub struct LayerMaskTypeImageBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayerMaskTypeImageBuilder<'a, 'b, A> { + #[inline] + pub fn add_image_mask_type(&mut self, image_mask_type: ImageMaskType) { + self.fbb_.push_slot::(LayerMaskTypeImage::VT_IMAGE_MASK_TYPE, image_mask_type, ImageMaskType::Alpha); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayerMaskTypeImageBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayerMaskTypeImageBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayerMaskTypeImage<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayerMaskTypeImage"); + ds.field("image_mask_type", &self.image_mask_type()); + ds.finish() + } +} +pub enum LayerMaskTypeGeometryOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `LayerMaskType::Geometry` +pub struct LayerMaskTypeGeometry<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayerMaskTypeGeometry<'a> { + type Inner = LayerMaskTypeGeometry<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayerMaskTypeGeometry<'a> { + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayerMaskTypeGeometry { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + _args: &'args LayerMaskTypeGeometryArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayerMaskTypeGeometryBuilder::new(_fbb); + builder.finish() + } + +} + +impl ::flatbuffers::Verifiable for LayerMaskTypeGeometry<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .finish(); + Ok(()) + } +} +pub struct LayerMaskTypeGeometryArgs { +} +impl<'a> Default for LayerMaskTypeGeometryArgs { + #[inline] + fn default() -> Self { + LayerMaskTypeGeometryArgs { + } + } +} + +pub struct LayerMaskTypeGeometryBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayerMaskTypeGeometryBuilder<'a, 'b, A> { + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayerMaskTypeGeometryBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayerMaskTypeGeometryBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayerMaskTypeGeometry<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayerMaskTypeGeometry"); + ds.finish() + } +} +pub enum TextDimensionOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct TextDimension<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for TextDimension<'a> { + type Inner = TextDimension<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> TextDimension<'a> { + pub const VT_KIND: ::flatbuffers::VOffsetT = 4; + pub const VT_VALUE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + TextDimension { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TextDimensionArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = TextDimensionBuilder::new(_fbb); + if let Some(x) = args.value { builder.add_value(x); } + builder.add_kind(args.kind); + builder.finish() + } + + + #[inline] + pub fn kind(&self) -> TextDimensionKind { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextDimension::VT_KIND, Some(TextDimensionKind::Normal)).unwrap()} + } + #[inline] + pub fn value(&self) -> Option { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextDimension::VT_VALUE, None)} + } +} + +impl ::flatbuffers::Verifiable for TextDimension<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("kind", Self::VT_KIND, false)? + .visit_field::("value", Self::VT_VALUE, false)? + .finish(); + Ok(()) + } +} +pub struct TextDimensionArgs { + pub kind: TextDimensionKind, + pub value: Option, +} +impl<'a> Default for TextDimensionArgs { + #[inline] + fn default() -> Self { + TextDimensionArgs { + kind: TextDimensionKind::Normal, + value: None, + } + } +} + +pub struct TextDimensionBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> TextDimensionBuilder<'a, 'b, A> { + #[inline] + pub fn add_kind(&mut self, kind: TextDimensionKind) { + self.fbb_.push_slot::(TextDimension::VT_KIND, kind, TextDimensionKind::Normal); + } + #[inline] + pub fn add_value(&mut self, value: f32) { + self.fbb_.push_slot_always::(TextDimension::VT_VALUE, value); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> TextDimensionBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TextDimensionBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for TextDimension<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("TextDimension"); + ds.field("kind", &self.kind()); + ds.field("value", &self.value()); + ds.finish() + } +} +pub enum FontFeatureOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FontFeature { tag: String, value: bool }` +pub struct FontFeature<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FontFeature<'a> { + type Inner = FontFeature<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FontFeature<'a> { + pub const VT_OPEN_TYPE_FEATURE_TAG: ::flatbuffers::VOffsetT = 4; + pub const VT_OPEN_TYPE_FEATURE_VALUE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FontFeature { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FontFeatureArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FontFeatureBuilder::new(_fbb); + if let Some(x) = args.open_type_feature_tag { builder.add_open_type_feature_tag(x); } + builder.add_open_type_feature_value(args.open_type_feature_value); + builder.finish() + } + + + /// OpenType feature tag (4-byte encoding). + /// Codecs should convert between string ("kern") and OpenTypeFeatureTag struct. + #[inline] + pub fn open_type_feature_tag(&self) -> Option<&'a OpenTypeFeatureTag> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FontFeature::VT_OPEN_TYPE_FEATURE_TAG, None)} + } + #[inline] + pub fn open_type_feature_value(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FontFeature::VT_OPEN_TYPE_FEATURE_VALUE, Some(false)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for FontFeature<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("open_type_feature_tag", Self::VT_OPEN_TYPE_FEATURE_TAG, false)? + .visit_field::("open_type_feature_value", Self::VT_OPEN_TYPE_FEATURE_VALUE, false)? + .finish(); + Ok(()) + } +} +pub struct FontFeatureArgs<'a> { + pub open_type_feature_tag: Option<&'a OpenTypeFeatureTag>, + pub open_type_feature_value: bool, +} +impl<'a> Default for FontFeatureArgs<'a> { + #[inline] + fn default() -> Self { + FontFeatureArgs { + open_type_feature_tag: None, + open_type_feature_value: false, + } + } +} + +pub struct FontFeatureBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FontFeatureBuilder<'a, 'b, A> { + #[inline] + pub fn add_open_type_feature_tag(&mut self, open_type_feature_tag: &OpenTypeFeatureTag) { + self.fbb_.push_slot_always::<&OpenTypeFeatureTag>(FontFeature::VT_OPEN_TYPE_FEATURE_TAG, open_type_feature_tag); + } + #[inline] + pub fn add_open_type_feature_value(&mut self, open_type_feature_value: bool) { + self.fbb_.push_slot::(FontFeature::VT_OPEN_TYPE_FEATURE_VALUE, open_type_feature_value, false); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FontFeatureBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FontFeatureBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FontFeature<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FontFeature"); + ds.field("open_type_feature_tag", &self.open_type_feature_tag()); + ds.field("open_type_feature_value", &self.open_type_feature_value()); + ds.finish() + } +} +pub enum FontVariationOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FontVariation { axis: String, value: f32 }` +pub struct FontVariation<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FontVariation<'a> { + type Inner = FontVariation<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FontVariation<'a> { + pub const VT_VARIATION_AXIS: ::flatbuffers::VOffsetT = 4; + pub const VT_VARIATION_VALUE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FontVariation { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FontVariationArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FontVariationBuilder::new(_fbb); + builder.add_variation_value(args.variation_value); + if let Some(x) = args.variation_axis { builder.add_variation_axis(x); } + builder.finish() + } + + + #[inline] + pub fn variation_axis(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(FontVariation::VT_VARIATION_AXIS, None).unwrap()} + } + #[inline] + pub fn variation_value(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FontVariation::VT_VARIATION_VALUE, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for FontVariation<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("variation_axis", Self::VT_VARIATION_AXIS, true)? + .visit_field::("variation_value", Self::VT_VARIATION_VALUE, false)? + .finish(); + Ok(()) + } +} +pub struct FontVariationArgs<'a> { + pub variation_axis: Option<::flatbuffers::WIPOffset<&'a str>>, + pub variation_value: f32, +} +impl<'a> Default for FontVariationArgs<'a> { + #[inline] + fn default() -> Self { + FontVariationArgs { + variation_axis: None, // required field + variation_value: 0.0, + } + } +} + +pub struct FontVariationBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FontVariationBuilder<'a, 'b, A> { + #[inline] + pub fn add_variation_axis(&mut self, variation_axis: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(FontVariation::VT_VARIATION_AXIS, variation_axis); + } + #[inline] + pub fn add_variation_value(&mut self, variation_value: f32) { + self.fbb_.push_slot::(FontVariation::VT_VARIATION_VALUE, variation_value, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FontVariationBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FontVariationBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, FontVariation::VT_VARIATION_AXIS,"variation_axis"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FontVariation<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FontVariation"); + ds.field("variation_axis", &self.variation_axis()); + ds.field("variation_value", &self.variation_value()); + ds.finish() + } +} +pub enum TextDecorationRecOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `TextDecorationRec` +/// +/// Archive model notes: +/// - `decoration_color` uses RGBA32F (float space) to match this schema's color choice. +pub struct TextDecorationRec<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for TextDecorationRec<'a> { + type Inner = TextDecorationRec<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> TextDecorationRec<'a> { + pub const VT_TEXT_DECORATION_LINE: ::flatbuffers::VOffsetT = 4; + pub const VT_TEXT_DECORATION_STYLE: ::flatbuffers::VOffsetT = 6; + pub const VT_TEXT_DECORATION_SKIP_INK: ::flatbuffers::VOffsetT = 8; + pub const VT_TEXT_DECORATION_THICKNESS: ::flatbuffers::VOffsetT = 10; + pub const VT_TEXT_DECORATION_COLOR: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + TextDecorationRec { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TextDecorationRecArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = TextDecorationRecBuilder::new(_fbb); + if let Some(x) = args.text_decoration_color { builder.add_text_decoration_color(x); } + builder.add_text_decoration_thickness(args.text_decoration_thickness); + builder.add_text_decoration_skip_ink(args.text_decoration_skip_ink); + builder.add_text_decoration_style(args.text_decoration_style); + builder.add_text_decoration_line(args.text_decoration_line); + builder.finish() + } + + + #[inline] + pub fn text_decoration_line(&self) -> TextDecorationLine { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextDecorationRec::VT_TEXT_DECORATION_LINE, Some(TextDecorationLine::None)).unwrap()} + } + #[inline] + pub fn text_decoration_style(&self) -> TextDecorationStyle { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextDecorationRec::VT_TEXT_DECORATION_STYLE, Some(TextDecorationStyle::Solid)).unwrap()} + } + #[inline] + pub fn text_decoration_skip_ink(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextDecorationRec::VT_TEXT_DECORATION_SKIP_INK, Some(true)).unwrap()} + } + #[inline] + pub fn text_decoration_thickness(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextDecorationRec::VT_TEXT_DECORATION_THICKNESS, Some(1.0)).unwrap()} + } + /// decoration color override, if unset, inherits + #[inline] + pub fn text_decoration_color(&self) -> Option<&'a RGBA32F> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextDecorationRec::VT_TEXT_DECORATION_COLOR, None)} + } +} + +impl ::flatbuffers::Verifiable for TextDecorationRec<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("text_decoration_line", Self::VT_TEXT_DECORATION_LINE, false)? + .visit_field::("text_decoration_style", Self::VT_TEXT_DECORATION_STYLE, false)? + .visit_field::("text_decoration_skip_ink", Self::VT_TEXT_DECORATION_SKIP_INK, false)? + .visit_field::("text_decoration_thickness", Self::VT_TEXT_DECORATION_THICKNESS, false)? + .visit_field::("text_decoration_color", Self::VT_TEXT_DECORATION_COLOR, false)? + .finish(); + Ok(()) + } +} +pub struct TextDecorationRecArgs<'a> { + pub text_decoration_line: TextDecorationLine, + pub text_decoration_style: TextDecorationStyle, + pub text_decoration_skip_ink: bool, + pub text_decoration_thickness: f32, + pub text_decoration_color: Option<&'a RGBA32F>, +} +impl<'a> Default for TextDecorationRecArgs<'a> { + #[inline] + fn default() -> Self { + TextDecorationRecArgs { + text_decoration_line: TextDecorationLine::None, + text_decoration_style: TextDecorationStyle::Solid, + text_decoration_skip_ink: true, + text_decoration_thickness: 1.0, + text_decoration_color: None, + } + } +} + +pub struct TextDecorationRecBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> TextDecorationRecBuilder<'a, 'b, A> { + #[inline] + pub fn add_text_decoration_line(&mut self, text_decoration_line: TextDecorationLine) { + self.fbb_.push_slot::(TextDecorationRec::VT_TEXT_DECORATION_LINE, text_decoration_line, TextDecorationLine::None); + } + #[inline] + pub fn add_text_decoration_style(&mut self, text_decoration_style: TextDecorationStyle) { + self.fbb_.push_slot::(TextDecorationRec::VT_TEXT_DECORATION_STYLE, text_decoration_style, TextDecorationStyle::Solid); + } + #[inline] + pub fn add_text_decoration_skip_ink(&mut self, text_decoration_skip_ink: bool) { + self.fbb_.push_slot::(TextDecorationRec::VT_TEXT_DECORATION_SKIP_INK, text_decoration_skip_ink, true); + } + #[inline] + pub fn add_text_decoration_thickness(&mut self, text_decoration_thickness: f32) { + self.fbb_.push_slot::(TextDecorationRec::VT_TEXT_DECORATION_THICKNESS, text_decoration_thickness, 1.0); + } + #[inline] + pub fn add_text_decoration_color(&mut self, text_decoration_color: &RGBA32F) { + self.fbb_.push_slot_always::<&RGBA32F>(TextDecorationRec::VT_TEXT_DECORATION_COLOR, text_decoration_color); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> TextDecorationRecBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TextDecorationRecBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for TextDecorationRec<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("TextDecorationRec"); + ds.field("text_decoration_line", &self.text_decoration_line()); + ds.field("text_decoration_style", &self.text_decoration_style()); + ds.field("text_decoration_skip_ink", &self.text_decoration_skip_ink()); + ds.field("text_decoration_thickness", &self.text_decoration_thickness()); + ds.field("text_decoration_color", &self.text_decoration_color()); + ds.finish() + } +} +pub enum TextStyleRecOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// `TextStyleRec {}` +pub struct TextStyleRec<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for TextStyleRec<'a> { + type Inner = TextStyleRec<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> TextStyleRec<'a> { + pub const VT_TEXT_DECORATION: ::flatbuffers::VOffsetT = 4; + pub const VT_FONT_FAMILY: ::flatbuffers::VOffsetT = 6; + pub const VT_FONT_SIZE: ::flatbuffers::VOffsetT = 8; + pub const VT_FONT_WEIGHT: ::flatbuffers::VOffsetT = 10; + pub const VT_FONT_WIDTH: ::flatbuffers::VOffsetT = 12; + pub const VT_FONT_STYLE_ITALIC: ::flatbuffers::VOffsetT = 14; + pub const VT_FONT_KERNING: ::flatbuffers::VOffsetT = 16; + pub const VT_FONT_OPTICAL_SIZING: ::flatbuffers::VOffsetT = 18; + pub const VT_FONT_FEATURES: ::flatbuffers::VOffsetT = 20; + pub const VT_FONT_VARIATIONS: ::flatbuffers::VOffsetT = 22; + pub const VT_LETTER_SPACING: ::flatbuffers::VOffsetT = 24; + pub const VT_WORD_SPACING: ::flatbuffers::VOffsetT = 26; + pub const VT_LINE_HEIGHT: ::flatbuffers::VOffsetT = 28; + pub const VT_TEXT_TRANSFORM: ::flatbuffers::VOffsetT = 30; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + TextStyleRec { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TextStyleRecArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = TextStyleRecBuilder::new(_fbb); + if let Some(x) = args.line_height { builder.add_line_height(x); } + if let Some(x) = args.word_spacing { builder.add_word_spacing(x); } + if let Some(x) = args.letter_spacing { builder.add_letter_spacing(x); } + if let Some(x) = args.font_variations { builder.add_font_variations(x); } + if let Some(x) = args.font_features { builder.add_font_features(x); } + if let Some(x) = args.font_optical_sizing { builder.add_font_optical_sizing(x); } + builder.add_font_width(args.font_width); + if let Some(x) = args.font_weight { builder.add_font_weight(x); } + builder.add_font_size(args.font_size); + if let Some(x) = args.font_family { builder.add_font_family(x); } + if let Some(x) = args.text_decoration { builder.add_text_decoration(x); } + builder.add_text_transform(args.text_transform); + builder.add_font_kerning(args.font_kerning); + builder.add_font_style_italic(args.font_style_italic); + builder.finish() + } + + + #[inline] + pub fn text_decoration(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextStyleRec::VT_TEXT_DECORATION, None)} + } + #[inline] + pub fn font_family(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(TextStyleRec::VT_FONT_FAMILY, None).unwrap()} + } + #[inline] + pub fn font_size(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextStyleRec::VT_FONT_SIZE, Some(14.0)).unwrap()} + } + #[inline] + pub fn font_weight(&self) -> &'a FontWeight { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextStyleRec::VT_FONT_WEIGHT, None).unwrap()} + } + #[inline] + pub fn font_width(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextStyleRec::VT_FONT_WIDTH, Some(0.0)).unwrap()} + } + #[inline] + pub fn font_style_italic(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextStyleRec::VT_FONT_STYLE_ITALIC, Some(false)).unwrap()} + } + #[inline] + pub fn font_kerning(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextStyleRec::VT_FONT_KERNING, Some(true)).unwrap()} + } + #[inline] + pub fn font_optical_sizing(&self) -> Option<&'a FontOpticalSizing> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextStyleRec::VT_FONT_OPTICAL_SIZING, None)} + } + #[inline] + pub fn font_features(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(TextStyleRec::VT_FONT_FEATURES, None)} + } + #[inline] + pub fn font_variations(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(TextStyleRec::VT_FONT_VARIATIONS, None)} + } + #[inline] + pub fn letter_spacing(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextStyleRec::VT_LETTER_SPACING, None)} + } + #[inline] + pub fn word_spacing(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextStyleRec::VT_WORD_SPACING, None)} + } + #[inline] + pub fn line_height(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextStyleRec::VT_LINE_HEIGHT, None)} + } + #[inline] + pub fn text_transform(&self) -> TextTransform { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextStyleRec::VT_TEXT_TRANSFORM, Some(TextTransform::None)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for TextStyleRec<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("text_decoration", Self::VT_TEXT_DECORATION, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("font_family", Self::VT_FONT_FAMILY, true)? + .visit_field::("font_size", Self::VT_FONT_SIZE, false)? + .visit_field::("font_weight", Self::VT_FONT_WEIGHT, true)? + .visit_field::("font_width", Self::VT_FONT_WIDTH, false)? + .visit_field::("font_style_italic", Self::VT_FONT_STYLE_ITALIC, false)? + .visit_field::("font_kerning", Self::VT_FONT_KERNING, false)? + .visit_field::("font_optical_sizing", Self::VT_FONT_OPTICAL_SIZING, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("font_features", Self::VT_FONT_FEATURES, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("font_variations", Self::VT_FONT_VARIATIONS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("letter_spacing", Self::VT_LETTER_SPACING, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("word_spacing", Self::VT_WORD_SPACING, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("line_height", Self::VT_LINE_HEIGHT, false)? + .visit_field::("text_transform", Self::VT_TEXT_TRANSFORM, false)? + .finish(); + Ok(()) + } +} +pub struct TextStyleRecArgs<'a> { + pub text_decoration: Option<::flatbuffers::WIPOffset>>, + pub font_family: Option<::flatbuffers::WIPOffset<&'a str>>, + pub font_size: f32, + pub font_weight: Option<&'a FontWeight>, + pub font_width: f32, + pub font_style_italic: bool, + pub font_kerning: bool, + pub font_optical_sizing: Option<&'a FontOpticalSizing>, + pub font_features: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub font_variations: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub letter_spacing: Option<::flatbuffers::WIPOffset>>, + pub word_spacing: Option<::flatbuffers::WIPOffset>>, + pub line_height: Option<::flatbuffers::WIPOffset>>, + pub text_transform: TextTransform, +} +impl<'a> Default for TextStyleRecArgs<'a> { + #[inline] + fn default() -> Self { + TextStyleRecArgs { + text_decoration: None, + font_family: None, // required field + font_size: 14.0, + font_weight: None, // required field + font_width: 0.0, + font_style_italic: false, + font_kerning: true, + font_optical_sizing: None, + font_features: None, + font_variations: None, + letter_spacing: None, + word_spacing: None, + line_height: None, + text_transform: TextTransform::None, + } + } +} + +pub struct TextStyleRecBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> TextStyleRecBuilder<'a, 'b, A> { + #[inline] + pub fn add_text_decoration(&mut self, text_decoration: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextStyleRec::VT_TEXT_DECORATION, text_decoration); + } + #[inline] + pub fn add_font_family(&mut self, font_family: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(TextStyleRec::VT_FONT_FAMILY, font_family); + } + #[inline] + pub fn add_font_size(&mut self, font_size: f32) { + self.fbb_.push_slot::(TextStyleRec::VT_FONT_SIZE, font_size, 14.0); + } + #[inline] + pub fn add_font_weight(&mut self, font_weight: &FontWeight) { + self.fbb_.push_slot_always::<&FontWeight>(TextStyleRec::VT_FONT_WEIGHT, font_weight); + } + #[inline] + pub fn add_font_width(&mut self, font_width: f32) { + self.fbb_.push_slot::(TextStyleRec::VT_FONT_WIDTH, font_width, 0.0); + } + #[inline] + pub fn add_font_style_italic(&mut self, font_style_italic: bool) { + self.fbb_.push_slot::(TextStyleRec::VT_FONT_STYLE_ITALIC, font_style_italic, false); + } + #[inline] + pub fn add_font_kerning(&mut self, font_kerning: bool) { + self.fbb_.push_slot::(TextStyleRec::VT_FONT_KERNING, font_kerning, true); + } + #[inline] + pub fn add_font_optical_sizing(&mut self, font_optical_sizing: &FontOpticalSizing) { + self.fbb_.push_slot_always::<&FontOpticalSizing>(TextStyleRec::VT_FONT_OPTICAL_SIZING, font_optical_sizing); + } + #[inline] + pub fn add_font_features(&mut self, font_features: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(TextStyleRec::VT_FONT_FEATURES, font_features); + } + #[inline] + pub fn add_font_variations(&mut self, font_variations: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(TextStyleRec::VT_FONT_VARIATIONS, font_variations); + } + #[inline] + pub fn add_letter_spacing(&mut self, letter_spacing: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextStyleRec::VT_LETTER_SPACING, letter_spacing); + } + #[inline] + pub fn add_word_spacing(&mut self, word_spacing: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextStyleRec::VT_WORD_SPACING, word_spacing); + } + #[inline] + pub fn add_line_height(&mut self, line_height: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextStyleRec::VT_LINE_HEIGHT, line_height); + } + #[inline] + pub fn add_text_transform(&mut self, text_transform: TextTransform) { + self.fbb_.push_slot::(TextStyleRec::VT_TEXT_TRANSFORM, text_transform, TextTransform::None); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> TextStyleRecBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TextStyleRecBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, TextStyleRec::VT_FONT_FAMILY,"font_family"); + self.fbb_.required(o, TextStyleRec::VT_FONT_WEIGHT,"font_weight"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for TextStyleRec<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("TextStyleRec"); + ds.field("text_decoration", &self.text_decoration()); + ds.field("font_family", &self.font_family()); + ds.field("font_size", &self.font_size()); + ds.field("font_weight", &self.font_weight()); + ds.field("font_width", &self.font_width()); + ds.field("font_style_italic", &self.font_style_italic()); + ds.field("font_kerning", &self.font_kerning()); + ds.field("font_optical_sizing", &self.font_optical_sizing()); + ds.field("font_features", &self.font_features()); + ds.field("font_variations", &self.font_variations()); + ds.field("letter_spacing", &self.letter_spacing()); + ds.field("word_spacing", &self.word_spacing()); + ds.field("line_height", &self.line_height()); + ds.field("text_transform", &self.text_transform()); + ds.finish() + } +} +pub enum SolidPaintOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct SolidPaint<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for SolidPaint<'a> { + type Inner = SolidPaint<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> SolidPaint<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_COLOR: ::flatbuffers::VOffsetT = 6; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + SolidPaint { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args SolidPaintArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = SolidPaintBuilder::new(_fbb); + if let Some(x) = args.color { builder.add_color(x); } + builder.add_blend_mode(args.blend_mode); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SolidPaint::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn color(&self) -> Option<&'a RGBA32F> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SolidPaint::VT_COLOR, None)} + } + #[inline] + pub fn blend_mode(&self) -> BlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SolidPaint::VT_BLEND_MODE, Some(BlendMode::Normal)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for SolidPaint<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("color", Self::VT_COLOR, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .finish(); + Ok(()) + } +} +pub struct SolidPaintArgs<'a> { + pub active: bool, + pub color: Option<&'a RGBA32F>, + pub blend_mode: BlendMode, +} +impl<'a> Default for SolidPaintArgs<'a> { + #[inline] + fn default() -> Self { + SolidPaintArgs { + active: true, + color: None, + blend_mode: BlendMode::Normal, + } + } +} + +pub struct SolidPaintBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> SolidPaintBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(SolidPaint::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_color(&mut self, color: &RGBA32F) { + self.fbb_.push_slot_always::<&RGBA32F>(SolidPaint::VT_COLOR, color); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: BlendMode) { + self.fbb_.push_slot::(SolidPaint::VT_BLEND_MODE, blend_mode, BlendMode::Normal); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> SolidPaintBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + SolidPaintBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for SolidPaint<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("SolidPaint"); + ds.field("active", &self.active()); + ds.field("color", &self.color()); + ds.field("blend_mode", &self.blend_mode()); + ds.finish() + } +} +pub enum LinearGradientPaintOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct LinearGradientPaint<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LinearGradientPaint<'a> { + type Inner = LinearGradientPaint<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LinearGradientPaint<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_XY1: ::flatbuffers::VOffsetT = 6; + pub const VT_XY2: ::flatbuffers::VOffsetT = 8; + pub const VT_TILE_MODE: ::flatbuffers::VOffsetT = 10; + pub const VT_TRANSFORM: ::flatbuffers::VOffsetT = 12; + pub const VT_STOPS: ::flatbuffers::VOffsetT = 14; + pub const VT_OPACITY: ::flatbuffers::VOffsetT = 16; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 18; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LinearGradientPaint { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LinearGradientPaintArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LinearGradientPaintBuilder::new(_fbb); + builder.add_opacity(args.opacity); + if let Some(x) = args.stops { builder.add_stops(x); } + if let Some(x) = args.transform { builder.add_transform(x); } + if let Some(x) = args.xy2 { builder.add_xy2(x); } + if let Some(x) = args.xy1 { builder.add_xy1(x); } + builder.add_blend_mode(args.blend_mode); + builder.add_tile_mode(args.tile_mode); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LinearGradientPaint::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn xy1(&self) -> Option<&'a Alignment> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LinearGradientPaint::VT_XY1, None)} + } + #[inline] + pub fn xy2(&self) -> Option<&'a Alignment> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LinearGradientPaint::VT_XY2, None)} + } + #[inline] + pub fn tile_mode(&self) -> TileMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LinearGradientPaint::VT_TILE_MODE, Some(TileMode::Clamp)).unwrap()} + } + /// transform of the gradient, if empty(unset) canonical is identity + #[inline] + pub fn transform(&self) -> Option<&'a CGTransform2D> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LinearGradientPaint::VT_TRANSFORM, None)} + } + #[inline] + pub fn stops(&self) -> Option<::flatbuffers::Vector<'a, GradientStop>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, GradientStop>>>(LinearGradientPaint::VT_STOPS, None)} + } + #[inline] + pub fn opacity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LinearGradientPaint::VT_OPACITY, Some(1.0)).unwrap()} + } + #[inline] + pub fn blend_mode(&self) -> BlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LinearGradientPaint::VT_BLEND_MODE, Some(BlendMode::Normal)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for LinearGradientPaint<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("xy1", Self::VT_XY1, false)? + .visit_field::("xy2", Self::VT_XY2, false)? + .visit_field::("tile_mode", Self::VT_TILE_MODE, false)? + .visit_field::("transform", Self::VT_TRANSFORM, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, GradientStop>>>("stops", Self::VT_STOPS, false)? + .visit_field::("opacity", Self::VT_OPACITY, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .finish(); + Ok(()) + } +} +pub struct LinearGradientPaintArgs<'a> { + pub active: bool, + pub xy1: Option<&'a Alignment>, + pub xy2: Option<&'a Alignment>, + pub tile_mode: TileMode, + pub transform: Option<&'a CGTransform2D>, + pub stops: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, GradientStop>>>, + pub opacity: f32, + pub blend_mode: BlendMode, +} +impl<'a> Default for LinearGradientPaintArgs<'a> { + #[inline] + fn default() -> Self { + LinearGradientPaintArgs { + active: true, + xy1: None, + xy2: None, + tile_mode: TileMode::Clamp, + transform: None, + stops: None, + opacity: 1.0, + blend_mode: BlendMode::Normal, + } + } +} + +pub struct LinearGradientPaintBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LinearGradientPaintBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(LinearGradientPaint::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_xy1(&mut self, xy1: &Alignment) { + self.fbb_.push_slot_always::<&Alignment>(LinearGradientPaint::VT_XY1, xy1); + } + #[inline] + pub fn add_xy2(&mut self, xy2: &Alignment) { + self.fbb_.push_slot_always::<&Alignment>(LinearGradientPaint::VT_XY2, xy2); + } + #[inline] + pub fn add_tile_mode(&mut self, tile_mode: TileMode) { + self.fbb_.push_slot::(LinearGradientPaint::VT_TILE_MODE, tile_mode, TileMode::Clamp); + } + #[inline] + pub fn add_transform(&mut self, transform: &CGTransform2D) { + self.fbb_.push_slot_always::<&CGTransform2D>(LinearGradientPaint::VT_TRANSFORM, transform); + } + #[inline] + pub fn add_stops(&mut self, stops: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , GradientStop>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(LinearGradientPaint::VT_STOPS, stops); + } + #[inline] + pub fn add_opacity(&mut self, opacity: f32) { + self.fbb_.push_slot::(LinearGradientPaint::VT_OPACITY, opacity, 1.0); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: BlendMode) { + self.fbb_.push_slot::(LinearGradientPaint::VT_BLEND_MODE, blend_mode, BlendMode::Normal); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LinearGradientPaintBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LinearGradientPaintBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LinearGradientPaint<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LinearGradientPaint"); + ds.field("active", &self.active()); + ds.field("xy1", &self.xy1()); + ds.field("xy2", &self.xy2()); + ds.field("tile_mode", &self.tile_mode()); + ds.field("transform", &self.transform()); + ds.field("stops", &self.stops()); + ds.field("opacity", &self.opacity()); + ds.field("blend_mode", &self.blend_mode()); + ds.finish() + } +} +pub enum RadialGradientPaintOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct RadialGradientPaint<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for RadialGradientPaint<'a> { + type Inner = RadialGradientPaint<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> RadialGradientPaint<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_TRANSFORM: ::flatbuffers::VOffsetT = 6; + pub const VT_STOPS: ::flatbuffers::VOffsetT = 8; + pub const VT_OPACITY: ::flatbuffers::VOffsetT = 10; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 12; + pub const VT_TILE_MODE: ::flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + RadialGradientPaint { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args RadialGradientPaintArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = RadialGradientPaintBuilder::new(_fbb); + builder.add_opacity(args.opacity); + if let Some(x) = args.stops { builder.add_stops(x); } + if let Some(x) = args.transform { builder.add_transform(x); } + builder.add_tile_mode(args.tile_mode); + builder.add_blend_mode(args.blend_mode); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RadialGradientPaint::VT_ACTIVE, Some(true)).unwrap()} + } + /// transform of the gradient, if empty(unset) canonical is identity + #[inline] + pub fn transform(&self) -> Option<&'a CGTransform2D> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RadialGradientPaint::VT_TRANSFORM, None)} + } + #[inline] + pub fn stops(&self) -> Option<::flatbuffers::Vector<'a, GradientStop>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, GradientStop>>>(RadialGradientPaint::VT_STOPS, None)} + } + #[inline] + pub fn opacity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RadialGradientPaint::VT_OPACITY, Some(1.0)).unwrap()} + } + #[inline] + pub fn blend_mode(&self) -> BlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RadialGradientPaint::VT_BLEND_MODE, Some(BlendMode::Normal)).unwrap()} + } + #[inline] + pub fn tile_mode(&self) -> TileMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RadialGradientPaint::VT_TILE_MODE, Some(TileMode::Clamp)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for RadialGradientPaint<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("transform", Self::VT_TRANSFORM, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, GradientStop>>>("stops", Self::VT_STOPS, false)? + .visit_field::("opacity", Self::VT_OPACITY, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .visit_field::("tile_mode", Self::VT_TILE_MODE, false)? + .finish(); + Ok(()) + } +} +pub struct RadialGradientPaintArgs<'a> { + pub active: bool, + pub transform: Option<&'a CGTransform2D>, + pub stops: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, GradientStop>>>, + pub opacity: f32, + pub blend_mode: BlendMode, + pub tile_mode: TileMode, +} +impl<'a> Default for RadialGradientPaintArgs<'a> { + #[inline] + fn default() -> Self { + RadialGradientPaintArgs { + active: true, + transform: None, + stops: None, + opacity: 1.0, + blend_mode: BlendMode::Normal, + tile_mode: TileMode::Clamp, + } + } +} + +pub struct RadialGradientPaintBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> RadialGradientPaintBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(RadialGradientPaint::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_transform(&mut self, transform: &CGTransform2D) { + self.fbb_.push_slot_always::<&CGTransform2D>(RadialGradientPaint::VT_TRANSFORM, transform); + } + #[inline] + pub fn add_stops(&mut self, stops: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , GradientStop>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(RadialGradientPaint::VT_STOPS, stops); + } + #[inline] + pub fn add_opacity(&mut self, opacity: f32) { + self.fbb_.push_slot::(RadialGradientPaint::VT_OPACITY, opacity, 1.0); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: BlendMode) { + self.fbb_.push_slot::(RadialGradientPaint::VT_BLEND_MODE, blend_mode, BlendMode::Normal); + } + #[inline] + pub fn add_tile_mode(&mut self, tile_mode: TileMode) { + self.fbb_.push_slot::(RadialGradientPaint::VT_TILE_MODE, tile_mode, TileMode::Clamp); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> RadialGradientPaintBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + RadialGradientPaintBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for RadialGradientPaint<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("RadialGradientPaint"); + ds.field("active", &self.active()); + ds.field("transform", &self.transform()); + ds.field("stops", &self.stops()); + ds.field("opacity", &self.opacity()); + ds.field("blend_mode", &self.blend_mode()); + ds.field("tile_mode", &self.tile_mode()); + ds.finish() + } +} +pub enum DiamondGradientPaintOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct DiamondGradientPaint<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for DiamondGradientPaint<'a> { + type Inner = DiamondGradientPaint<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> DiamondGradientPaint<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_TRANSFORM: ::flatbuffers::VOffsetT = 6; + pub const VT_STOPS: ::flatbuffers::VOffsetT = 8; + pub const VT_OPACITY: ::flatbuffers::VOffsetT = 10; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + DiamondGradientPaint { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args DiamondGradientPaintArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = DiamondGradientPaintBuilder::new(_fbb); + builder.add_opacity(args.opacity); + if let Some(x) = args.stops { builder.add_stops(x); } + if let Some(x) = args.transform { builder.add_transform(x); } + builder.add_blend_mode(args.blend_mode); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(DiamondGradientPaint::VT_ACTIVE, Some(true)).unwrap()} + } + /// transform of the gradient, if empty(unset) canonical is identity + #[inline] + pub fn transform(&self) -> Option<&'a CGTransform2D> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(DiamondGradientPaint::VT_TRANSFORM, None)} + } + #[inline] + pub fn stops(&self) -> Option<::flatbuffers::Vector<'a, GradientStop>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, GradientStop>>>(DiamondGradientPaint::VT_STOPS, None)} + } + #[inline] + pub fn opacity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(DiamondGradientPaint::VT_OPACITY, Some(1.0)).unwrap()} + } + #[inline] + pub fn blend_mode(&self) -> BlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(DiamondGradientPaint::VT_BLEND_MODE, Some(BlendMode::Normal)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for DiamondGradientPaint<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("transform", Self::VT_TRANSFORM, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, GradientStop>>>("stops", Self::VT_STOPS, false)? + .visit_field::("opacity", Self::VT_OPACITY, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .finish(); + Ok(()) + } +} +pub struct DiamondGradientPaintArgs<'a> { + pub active: bool, + pub transform: Option<&'a CGTransform2D>, + pub stops: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, GradientStop>>>, + pub opacity: f32, + pub blend_mode: BlendMode, +} +impl<'a> Default for DiamondGradientPaintArgs<'a> { + #[inline] + fn default() -> Self { + DiamondGradientPaintArgs { + active: true, + transform: None, + stops: None, + opacity: 1.0, + blend_mode: BlendMode::Normal, + } + } +} + +pub struct DiamondGradientPaintBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> DiamondGradientPaintBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(DiamondGradientPaint::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_transform(&mut self, transform: &CGTransform2D) { + self.fbb_.push_slot_always::<&CGTransform2D>(DiamondGradientPaint::VT_TRANSFORM, transform); + } + #[inline] + pub fn add_stops(&mut self, stops: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , GradientStop>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(DiamondGradientPaint::VT_STOPS, stops); + } + #[inline] + pub fn add_opacity(&mut self, opacity: f32) { + self.fbb_.push_slot::(DiamondGradientPaint::VT_OPACITY, opacity, 1.0); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: BlendMode) { + self.fbb_.push_slot::(DiamondGradientPaint::VT_BLEND_MODE, blend_mode, BlendMode::Normal); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> DiamondGradientPaintBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + DiamondGradientPaintBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for DiamondGradientPaint<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("DiamondGradientPaint"); + ds.field("active", &self.active()); + ds.field("transform", &self.transform()); + ds.field("stops", &self.stops()); + ds.field("opacity", &self.opacity()); + ds.field("blend_mode", &self.blend_mode()); + ds.finish() + } +} +pub enum SweepGradientPaintOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct SweepGradientPaint<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for SweepGradientPaint<'a> { + type Inner = SweepGradientPaint<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> SweepGradientPaint<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_TRANSFORM: ::flatbuffers::VOffsetT = 6; + pub const VT_STOPS: ::flatbuffers::VOffsetT = 8; + pub const VT_OPACITY: ::flatbuffers::VOffsetT = 10; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + SweepGradientPaint { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args SweepGradientPaintArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = SweepGradientPaintBuilder::new(_fbb); + builder.add_opacity(args.opacity); + if let Some(x) = args.stops { builder.add_stops(x); } + if let Some(x) = args.transform { builder.add_transform(x); } + builder.add_blend_mode(args.blend_mode); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SweepGradientPaint::VT_ACTIVE, Some(true)).unwrap()} + } + /// transform of the gradient, if empty(unset) canonical is identity + #[inline] + pub fn transform(&self) -> Option<&'a CGTransform2D> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SweepGradientPaint::VT_TRANSFORM, None)} + } + #[inline] + pub fn stops(&self) -> Option<::flatbuffers::Vector<'a, GradientStop>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, GradientStop>>>(SweepGradientPaint::VT_STOPS, None)} + } + #[inline] + pub fn opacity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SweepGradientPaint::VT_OPACITY, Some(1.0)).unwrap()} + } + #[inline] + pub fn blend_mode(&self) -> BlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SweepGradientPaint::VT_BLEND_MODE, Some(BlendMode::Normal)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for SweepGradientPaint<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("transform", Self::VT_TRANSFORM, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, GradientStop>>>("stops", Self::VT_STOPS, false)? + .visit_field::("opacity", Self::VT_OPACITY, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .finish(); + Ok(()) + } +} +pub struct SweepGradientPaintArgs<'a> { + pub active: bool, + pub transform: Option<&'a CGTransform2D>, + pub stops: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, GradientStop>>>, + pub opacity: f32, + pub blend_mode: BlendMode, +} +impl<'a> Default for SweepGradientPaintArgs<'a> { + #[inline] + fn default() -> Self { + SweepGradientPaintArgs { + active: true, + transform: None, + stops: None, + opacity: 1.0, + blend_mode: BlendMode::Normal, + } + } +} + +pub struct SweepGradientPaintBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> SweepGradientPaintBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(SweepGradientPaint::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_transform(&mut self, transform: &CGTransform2D) { + self.fbb_.push_slot_always::<&CGTransform2D>(SweepGradientPaint::VT_TRANSFORM, transform); + } + #[inline] + pub fn add_stops(&mut self, stops: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , GradientStop>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(SweepGradientPaint::VT_STOPS, stops); + } + #[inline] + pub fn add_opacity(&mut self, opacity: f32) { + self.fbb_.push_slot::(SweepGradientPaint::VT_OPACITY, opacity, 1.0); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: BlendMode) { + self.fbb_.push_slot::(SweepGradientPaint::VT_BLEND_MODE, blend_mode, BlendMode::Normal); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> SweepGradientPaintBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + SweepGradientPaintBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for SweepGradientPaint<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("SweepGradientPaint"); + ds.field("active", &self.active()); + ds.field("transform", &self.transform()); + ds.field("stops", &self.stops()); + ds.field("opacity", &self.opacity()); + ds.field("blend_mode", &self.blend_mode()); + ds.finish() + } +} +pub enum ResourceRefHASHOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `ResourceRef::HASH(String)` +pub struct ResourceRefHASH<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ResourceRefHASH<'a> { + type Inner = ResourceRefHASH<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ResourceRefHASH<'a> { + pub const VT_HASH: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ResourceRefHASH { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ResourceRefHASHArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ResourceRefHASHBuilder::new(_fbb); + if let Some(x) = args.hash { builder.add_hash(x); } + builder.finish() + } + + + #[inline] + pub fn hash(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(ResourceRefHASH::VT_HASH, None)} + } +} + +impl ::flatbuffers::Verifiable for ResourceRefHASH<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("hash", Self::VT_HASH, false)? + .finish(); + Ok(()) + } +} +pub struct ResourceRefHASHArgs<'a> { + pub hash: Option<::flatbuffers::WIPOffset<&'a str>>, +} +impl<'a> Default for ResourceRefHASHArgs<'a> { + #[inline] + fn default() -> Self { + ResourceRefHASHArgs { + hash: None, + } + } +} + +pub struct ResourceRefHASHBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ResourceRefHASHBuilder<'a, 'b, A> { + #[inline] + pub fn add_hash(&mut self, hash: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(ResourceRefHASH::VT_HASH, hash); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ResourceRefHASHBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ResourceRefHASHBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ResourceRefHASH<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ResourceRefHASH"); + ds.field("hash", &self.hash()); + ds.finish() + } +} +pub enum ResourceRefRIDOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `ResourceRef::RID(String)` +pub struct ResourceRefRID<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ResourceRefRID<'a> { + type Inner = ResourceRefRID<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ResourceRefRID<'a> { + pub const VT_RID: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ResourceRefRID { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ResourceRefRIDArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ResourceRefRIDBuilder::new(_fbb); + if let Some(x) = args.rid { builder.add_rid(x); } + builder.finish() + } + + + #[inline] + pub fn rid(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(ResourceRefRID::VT_RID, None)} + } +} + +impl ::flatbuffers::Verifiable for ResourceRefRID<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("rid", Self::VT_RID, false)? + .finish(); + Ok(()) + } +} +pub struct ResourceRefRIDArgs<'a> { + pub rid: Option<::flatbuffers::WIPOffset<&'a str>>, +} +impl<'a> Default for ResourceRefRIDArgs<'a> { + #[inline] + fn default() -> Self { + ResourceRefRIDArgs { + rid: None, + } + } +} + +pub struct ResourceRefRIDBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ResourceRefRIDBuilder<'a, 'b, A> { + #[inline] + pub fn add_rid(&mut self, rid: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(ResourceRefRID::VT_RID, rid); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ResourceRefRIDBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ResourceRefRIDBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ResourceRefRID<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ResourceRefRID"); + ds.field("rid", &self.rid()); + ds.finish() + } +} +pub enum ImagePaintFitFitOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `ImagePaintFit::Fit(BoxFit)` +pub struct ImagePaintFitFit<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ImagePaintFitFit<'a> { + type Inner = ImagePaintFitFit<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ImagePaintFitFit<'a> { + pub const VT_BOX_FIT: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ImagePaintFitFit { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ImagePaintFitFitArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ImagePaintFitFitBuilder::new(_fbb); + builder.add_box_fit(args.box_fit); + builder.finish() + } + + + #[inline] + pub fn box_fit(&self) -> BoxFit { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaintFitFit::VT_BOX_FIT, Some(BoxFit::Cover)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for ImagePaintFitFit<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("box_fit", Self::VT_BOX_FIT, false)? + .finish(); + Ok(()) + } +} +pub struct ImagePaintFitFitArgs { + pub box_fit: BoxFit, +} +impl<'a> Default for ImagePaintFitFitArgs { + #[inline] + fn default() -> Self { + ImagePaintFitFitArgs { + box_fit: BoxFit::Cover, + } + } +} + +pub struct ImagePaintFitFitBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ImagePaintFitFitBuilder<'a, 'b, A> { + #[inline] + pub fn add_box_fit(&mut self, box_fit: BoxFit) { + self.fbb_.push_slot::(ImagePaintFitFit::VT_BOX_FIT, box_fit, BoxFit::Cover); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ImagePaintFitFitBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ImagePaintFitFitBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ImagePaintFitFit<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ImagePaintFitFit"); + ds.field("box_fit", &self.box_fit()); + ds.finish() + } +} +pub enum ImagePaintFitTransformOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `ImagePaintFit::Transform(AffineTransform)` +pub struct ImagePaintFitTransform<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ImagePaintFitTransform<'a> { + type Inner = ImagePaintFitTransform<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ImagePaintFitTransform<'a> { + pub const VT_TRANSFORM: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ImagePaintFitTransform { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ImagePaintFitTransformArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ImagePaintFitTransformBuilder::new(_fbb); + if let Some(x) = args.transform { builder.add_transform(x); } + builder.finish() + } + + + #[inline] + pub fn transform(&self) -> Option<&'a CGTransform2D> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaintFitTransform::VT_TRANSFORM, None)} + } +} + +impl ::flatbuffers::Verifiable for ImagePaintFitTransform<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("transform", Self::VT_TRANSFORM, false)? + .finish(); + Ok(()) + } +} +pub struct ImagePaintFitTransformArgs<'a> { + pub transform: Option<&'a CGTransform2D>, +} +impl<'a> Default for ImagePaintFitTransformArgs<'a> { + #[inline] + fn default() -> Self { + ImagePaintFitTransformArgs { + transform: None, + } + } +} + +pub struct ImagePaintFitTransformBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ImagePaintFitTransformBuilder<'a, 'b, A> { + #[inline] + pub fn add_transform(&mut self, transform: &CGTransform2D) { + self.fbb_.push_slot_always::<&CGTransform2D>(ImagePaintFitTransform::VT_TRANSFORM, transform); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ImagePaintFitTransformBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ImagePaintFitTransformBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ImagePaintFitTransform<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ImagePaintFitTransform"); + ds.field("transform", &self.transform()); + ds.finish() + } +} +pub enum ImagePaintFitTileOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `ImagePaintFit::Tile(ImageTile)` +pub struct ImagePaintFitTile<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ImagePaintFitTile<'a> { + type Inner = ImagePaintFitTile<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ImagePaintFitTile<'a> { + pub const VT_TILE: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ImagePaintFitTile { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ImagePaintFitTileArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ImagePaintFitTileBuilder::new(_fbb); + if let Some(x) = args.tile { builder.add_tile(x); } + builder.finish() + } + + + #[inline] + pub fn tile(&self) -> Option<&'a ImageTile> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaintFitTile::VT_TILE, None)} + } +} + +impl ::flatbuffers::Verifiable for ImagePaintFitTile<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("tile", Self::VT_TILE, false)? + .finish(); + Ok(()) + } +} +pub struct ImagePaintFitTileArgs<'a> { + pub tile: Option<&'a ImageTile>, +} +impl<'a> Default for ImagePaintFitTileArgs<'a> { + #[inline] + fn default() -> Self { + ImagePaintFitTileArgs { + tile: None, + } + } +} + +pub struct ImagePaintFitTileBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ImagePaintFitTileBuilder<'a, 'b, A> { + #[inline] + pub fn add_tile(&mut self, tile: &ImageTile) { + self.fbb_.push_slot_always::<&ImageTile>(ImagePaintFitTile::VT_TILE, tile); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ImagePaintFitTileBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ImagePaintFitTileBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ImagePaintFitTile<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ImagePaintFitTile"); + ds.field("tile", &self.tile()); + ds.finish() + } +} +pub enum ImagePaintOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `ImagePaint` +pub struct ImagePaint<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ImagePaint<'a> { + type Inner = ImagePaint<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ImagePaint<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_IMAGE_TYPE: ::flatbuffers::VOffsetT = 6; + pub const VT_IMAGE: ::flatbuffers::VOffsetT = 8; + pub const VT_QUARTER_TURNS: ::flatbuffers::VOffsetT = 10; + pub const VT_ALIGNEMENT: ::flatbuffers::VOffsetT = 12; + pub const VT_FIT_TYPE: ::flatbuffers::VOffsetT = 14; + pub const VT_FIT: ::flatbuffers::VOffsetT = 16; + pub const VT_OPACITY: ::flatbuffers::VOffsetT = 18; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 20; + pub const VT_FILTERS: ::flatbuffers::VOffsetT = 22; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ImagePaint { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ImagePaintArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ImagePaintBuilder::new(_fbb); + if let Some(x) = args.filters { builder.add_filters(x); } + builder.add_opacity(args.opacity); + if let Some(x) = args.fit { builder.add_fit(x); } + if let Some(x) = args.alignement { builder.add_alignement(x); } + if let Some(x) = args.image { builder.add_image(x); } + builder.add_blend_mode(args.blend_mode); + builder.add_fit_type(args.fit_type); + builder.add_quarter_turns(args.quarter_turns); + builder.add_image_type(args.image_type); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn image_type(&self) -> ResourceRef { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_IMAGE_TYPE, Some(ResourceRef::NONE)).unwrap()} + } + #[inline] + pub fn image(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(ImagePaint::VT_IMAGE, None)} + } + #[inline] + pub fn quarter_turns(&self) -> u8 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_QUARTER_TURNS, Some(0)).unwrap()} + } + /// NOTE: Rust field name is `alignement` (typo preserved for 1:1) + #[inline] + pub fn alignement(&self) -> Option<&'a Alignment> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_ALIGNEMENT, None)} + } + #[inline] + pub fn fit_type(&self) -> ImagePaintFit { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_FIT_TYPE, Some(ImagePaintFit::NONE)).unwrap()} + } + #[inline] + pub fn fit(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(ImagePaint::VT_FIT, None)} + } + #[inline] + pub fn opacity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_OPACITY, Some(1.0)).unwrap()} + } + #[inline] + pub fn blend_mode(&self) -> BlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_BLEND_MODE, Some(BlendMode::Normal)).unwrap()} + } + #[inline] + pub fn filters(&self) -> Option<&'a ImageFilters> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ImagePaint::VT_FILTERS, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn image_as_resource_ref_hash(&self) -> Option> { + if self.image_type() == ResourceRef::ResourceRefHASH { + self.image().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { ResourceRefHASH::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn image_as_resource_ref_rid(&self) -> Option> { + if self.image_type() == ResourceRef::ResourceRefRID { + self.image().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { ResourceRefRID::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn fit_as_image_paint_fit_fit(&self) -> Option> { + if self.fit_type() == ImagePaintFit::ImagePaintFitFit { + self.fit().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { ImagePaintFitFit::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn fit_as_image_paint_fit_transform(&self) -> Option> { + if self.fit_type() == ImagePaintFit::ImagePaintFitTransform { + self.fit().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { ImagePaintFitTransform::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn fit_as_image_paint_fit_tile(&self) -> Option> { + if self.fit_type() == ImagePaintFit::ImagePaintFitTile { + self.fit().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { ImagePaintFitTile::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for ImagePaint<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_union::("image_type", Self::VT_IMAGE_TYPE, "image", Self::VT_IMAGE, false, |key, v, pos| { + match key { + ResourceRef::ResourceRefHASH => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("ResourceRef::ResourceRefHASH", pos), + ResourceRef::ResourceRefRID => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("ResourceRef::ResourceRefRID", pos), + _ => Ok(()), + } + })? + .visit_field::("quarter_turns", Self::VT_QUARTER_TURNS, false)? + .visit_field::("alignement", Self::VT_ALIGNEMENT, false)? + .visit_union::("fit_type", Self::VT_FIT_TYPE, "fit", Self::VT_FIT, false, |key, v, pos| { + match key { + ImagePaintFit::ImagePaintFitFit => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("ImagePaintFit::ImagePaintFitFit", pos), + ImagePaintFit::ImagePaintFitTransform => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("ImagePaintFit::ImagePaintFitTransform", pos), + ImagePaintFit::ImagePaintFitTile => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("ImagePaintFit::ImagePaintFitTile", pos), + _ => Ok(()), + } + })? + .visit_field::("opacity", Self::VT_OPACITY, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .visit_field::("filters", Self::VT_FILTERS, false)? + .finish(); + Ok(()) + } +} +pub struct ImagePaintArgs<'a> { + pub active: bool, + pub image_type: ResourceRef, + pub image: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, + pub quarter_turns: u8, + pub alignement: Option<&'a Alignment>, + pub fit_type: ImagePaintFit, + pub fit: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, + pub opacity: f32, + pub blend_mode: BlendMode, + pub filters: Option<&'a ImageFilters>, +} +impl<'a> Default for ImagePaintArgs<'a> { + #[inline] + fn default() -> Self { + ImagePaintArgs { + active: true, + image_type: ResourceRef::NONE, + image: None, + quarter_turns: 0, + alignement: None, + fit_type: ImagePaintFit::NONE, + fit: None, + opacity: 1.0, + blend_mode: BlendMode::Normal, + filters: None, + } + } +} + +pub struct ImagePaintBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ImagePaintBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(ImagePaint::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_image_type(&mut self, image_type: ResourceRef) { + self.fbb_.push_slot::(ImagePaint::VT_IMAGE_TYPE, image_type, ResourceRef::NONE); + } + #[inline] + pub fn add_image(&mut self, image: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(ImagePaint::VT_IMAGE, image); + } + #[inline] + pub fn add_quarter_turns(&mut self, quarter_turns: u8) { + self.fbb_.push_slot::(ImagePaint::VT_QUARTER_TURNS, quarter_turns, 0); + } + #[inline] + pub fn add_alignement(&mut self, alignement: &Alignment) { + self.fbb_.push_slot_always::<&Alignment>(ImagePaint::VT_ALIGNEMENT, alignement); + } + #[inline] + pub fn add_fit_type(&mut self, fit_type: ImagePaintFit) { + self.fbb_.push_slot::(ImagePaint::VT_FIT_TYPE, fit_type, ImagePaintFit::NONE); + } + #[inline] + pub fn add_fit(&mut self, fit: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(ImagePaint::VT_FIT, fit); + } + #[inline] + pub fn add_opacity(&mut self, opacity: f32) { + self.fbb_.push_slot::(ImagePaint::VT_OPACITY, opacity, 1.0); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: BlendMode) { + self.fbb_.push_slot::(ImagePaint::VT_BLEND_MODE, blend_mode, BlendMode::Normal); + } + #[inline] + pub fn add_filters(&mut self, filters: &ImageFilters) { + self.fbb_.push_slot_always::<&ImageFilters>(ImagePaint::VT_FILTERS, filters); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ImagePaintBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ImagePaintBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ImagePaint<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ImagePaint"); + ds.field("active", &self.active()); + ds.field("image_type", &self.image_type()); + match self.image_type() { + ResourceRef::ResourceRefHASH => { + if let Some(x) = self.image_as_resource_ref_hash() { + ds.field("image", &x) + } else { + ds.field("image", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + ResourceRef::ResourceRefRID => { + if let Some(x) = self.image_as_resource_ref_rid() { + ds.field("image", &x) + } else { + ds.field("image", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("image", &x) + }, + }; + ds.field("quarter_turns", &self.quarter_turns()); + ds.field("alignement", &self.alignement()); + ds.field("fit_type", &self.fit_type()); + match self.fit_type() { + ImagePaintFit::ImagePaintFitFit => { + if let Some(x) = self.fit_as_image_paint_fit_fit() { + ds.field("fit", &x) + } else { + ds.field("fit", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + ImagePaintFit::ImagePaintFitTransform => { + if let Some(x) = self.fit_as_image_paint_fit_transform() { + ds.field("fit", &x) + } else { + ds.field("fit", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + ImagePaintFit::ImagePaintFitTile => { + if let Some(x) = self.fit_as_image_paint_fit_tile() { + ds.field("fit", &x) + } else { + ds.field("fit", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("fit", &x) + }, + }; + ds.field("opacity", &self.opacity()); + ds.field("blend_mode", &self.blend_mode()); + ds.field("filters", &self.filters()); + ds.finish() + } +} +pub enum PaintStackItemOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct PaintStackItem<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for PaintStackItem<'a> { + type Inner = PaintStackItem<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> PaintStackItem<'a> { + pub const VT_PAINT_TYPE: ::flatbuffers::VOffsetT = 4; + pub const VT_PAINT: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + PaintStackItem { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args PaintStackItemArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = PaintStackItemBuilder::new(_fbb); + if let Some(x) = args.paint { builder.add_paint(x); } + builder.add_paint_type(args.paint_type); + builder.finish() + } + + + #[inline] + pub fn paint_type(&self) -> Paint { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(PaintStackItem::VT_PAINT_TYPE, Some(Paint::NONE)).unwrap()} + } + #[inline] + pub fn paint(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(PaintStackItem::VT_PAINT, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn paint_as_solid_paint(&self) -> Option> { + if self.paint_type() == Paint::SolidPaint { + self.paint().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { SolidPaint::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn paint_as_linear_gradient_paint(&self) -> Option> { + if self.paint_type() == Paint::LinearGradientPaint { + self.paint().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LinearGradientPaint::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn paint_as_radial_gradient_paint(&self) -> Option> { + if self.paint_type() == Paint::RadialGradientPaint { + self.paint().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { RadialGradientPaint::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn paint_as_sweep_gradient_paint(&self) -> Option> { + if self.paint_type() == Paint::SweepGradientPaint { + self.paint().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { SweepGradientPaint::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn paint_as_diamond_gradient_paint(&self) -> Option> { + if self.paint_type() == Paint::DiamondGradientPaint { + self.paint().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { DiamondGradientPaint::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn paint_as_image_paint(&self) -> Option> { + if self.paint_type() == Paint::ImagePaint { + self.paint().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { ImagePaint::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for PaintStackItem<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_union::("paint_type", Self::VT_PAINT_TYPE, "paint", Self::VT_PAINT, false, |key, v, pos| { + match key { + Paint::SolidPaint => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Paint::SolidPaint", pos), + Paint::LinearGradientPaint => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Paint::LinearGradientPaint", pos), + Paint::RadialGradientPaint => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Paint::RadialGradientPaint", pos), + Paint::SweepGradientPaint => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Paint::SweepGradientPaint", pos), + Paint::DiamondGradientPaint => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Paint::DiamondGradientPaint", pos), + Paint::ImagePaint => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Paint::ImagePaint", pos), + _ => Ok(()), + } + })? + .finish(); + Ok(()) + } +} +pub struct PaintStackItemArgs { + pub paint_type: Paint, + pub paint: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, +} +impl<'a> Default for PaintStackItemArgs { + #[inline] + fn default() -> Self { + PaintStackItemArgs { + paint_type: Paint::NONE, + paint: None, + } + } +} + +pub struct PaintStackItemBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> PaintStackItemBuilder<'a, 'b, A> { + #[inline] + pub fn add_paint_type(&mut self, paint_type: Paint) { + self.fbb_.push_slot::(PaintStackItem::VT_PAINT_TYPE, paint_type, Paint::NONE); + } + #[inline] + pub fn add_paint(&mut self, paint: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(PaintStackItem::VT_PAINT, paint); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> PaintStackItemBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + PaintStackItemBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for PaintStackItem<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("PaintStackItem"); + ds.field("paint_type", &self.paint_type()); + match self.paint_type() { + Paint::SolidPaint => { + if let Some(x) = self.paint_as_solid_paint() { + ds.field("paint", &x) + } else { + ds.field("paint", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Paint::LinearGradientPaint => { + if let Some(x) = self.paint_as_linear_gradient_paint() { + ds.field("paint", &x) + } else { + ds.field("paint", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Paint::RadialGradientPaint => { + if let Some(x) = self.paint_as_radial_gradient_paint() { + ds.field("paint", &x) + } else { + ds.field("paint", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Paint::SweepGradientPaint => { + if let Some(x) = self.paint_as_sweep_gradient_paint() { + ds.field("paint", &x) + } else { + ds.field("paint", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Paint::DiamondGradientPaint => { + if let Some(x) = self.paint_as_diamond_gradient_paint() { + ds.field("paint", &x) + } else { + ds.field("paint", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Paint::ImagePaint => { + if let Some(x) = self.paint_as_image_paint() { + ds.field("paint", &x) + } else { + ds.field("paint", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("paint", &x) + }, + }; + ds.finish() + } +} +pub enum Guide2DOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct Guide2D<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for Guide2D<'a> { + type Inner = Guide2D<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> Guide2D<'a> { + pub const VT_AXIS: ::flatbuffers::VOffsetT = 4; + pub const VT_GUIDE_OFFSET: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + Guide2D { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args Guide2DArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = Guide2DBuilder::new(_fbb); + builder.add_guide_offset(args.guide_offset); + builder.add_axis(args.axis); + builder.finish() + } + + + #[inline] + pub fn axis(&self) -> Axis { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Guide2D::VT_AXIS, Some(Axis::Horizontal)).unwrap()} + } + #[inline] + pub fn guide_offset(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Guide2D::VT_GUIDE_OFFSET, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for Guide2D<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("axis", Self::VT_AXIS, false)? + .visit_field::("guide_offset", Self::VT_GUIDE_OFFSET, false)? + .finish(); + Ok(()) + } +} +pub struct Guide2DArgs { + pub axis: Axis, + pub guide_offset: f32, +} +impl<'a> Default for Guide2DArgs { + #[inline] + fn default() -> Self { + Guide2DArgs { + axis: Axis::Horizontal, + guide_offset: 0.0, + } + } +} + +pub struct Guide2DBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> Guide2DBuilder<'a, 'b, A> { + #[inline] + pub fn add_axis(&mut self, axis: Axis) { + self.fbb_.push_slot::(Guide2D::VT_AXIS, axis, Axis::Horizontal); + } + #[inline] + pub fn add_guide_offset(&mut self, guide_offset: f32) { + self.fbb_.push_slot::(Guide2D::VT_GUIDE_OFFSET, guide_offset, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> Guide2DBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + Guide2DBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for Guide2D<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("Guide2D"); + ds.field("axis", &self.axis()); + ds.field("guide_offset", &self.guide_offset()); + ds.finish() + } +} +pub enum EdgePointPosition2DOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct EdgePointPosition2D<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for EdgePointPosition2D<'a> { + type Inner = EdgePointPosition2D<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> EdgePointPosition2D<'a> { + pub const VT_X: ::flatbuffers::VOffsetT = 4; + pub const VT_Y: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + EdgePointPosition2D { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args EdgePointPosition2DArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = EdgePointPosition2DBuilder::new(_fbb); + builder.add_y(args.y); + builder.add_x(args.x); + builder.finish() + } + + + #[inline] + pub fn x(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(EdgePointPosition2D::VT_X, Some(0.0)).unwrap()} + } + #[inline] + pub fn y(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(EdgePointPosition2D::VT_Y, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for EdgePointPosition2D<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("x", Self::VT_X, false)? + .visit_field::("y", Self::VT_Y, false)? + .finish(); + Ok(()) + } +} +pub struct EdgePointPosition2DArgs { + pub x: f32, + pub y: f32, +} +impl<'a> Default for EdgePointPosition2DArgs { + #[inline] + fn default() -> Self { + EdgePointPosition2DArgs { + x: 0.0, + y: 0.0, + } + } +} + +pub struct EdgePointPosition2DBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> EdgePointPosition2DBuilder<'a, 'b, A> { + #[inline] + pub fn add_x(&mut self, x: f32) { + self.fbb_.push_slot::(EdgePointPosition2D::VT_X, x, 0.0); + } + #[inline] + pub fn add_y(&mut self, y: f32) { + self.fbb_.push_slot::(EdgePointPosition2D::VT_Y, y, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> EdgePointPosition2DBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + EdgePointPosition2DBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for EdgePointPosition2D<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("EdgePointPosition2D"); + ds.field("x", &self.x()); + ds.field("y", &self.y()); + ds.finish() + } +} +pub enum EdgePointNodeAnchorOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct EdgePointNodeAnchor<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for EdgePointNodeAnchor<'a> { + type Inner = EdgePointNodeAnchor<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> EdgePointNodeAnchor<'a> { + pub const VT_TARGET: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + EdgePointNodeAnchor { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args EdgePointNodeAnchorArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = EdgePointNodeAnchorBuilder::new(_fbb); + if let Some(x) = args.target { builder.add_target(x); } + builder.finish() + } + + + #[inline] + pub fn target(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(EdgePointNodeAnchor::VT_TARGET, None)} + } +} + +impl ::flatbuffers::Verifiable for EdgePointNodeAnchor<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("target", Self::VT_TARGET, false)? + .finish(); + Ok(()) + } +} +pub struct EdgePointNodeAnchorArgs<'a> { + pub target: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for EdgePointNodeAnchorArgs<'a> { + #[inline] + fn default() -> Self { + EdgePointNodeAnchorArgs { + target: None, + } + } +} + +pub struct EdgePointNodeAnchorBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> EdgePointNodeAnchorBuilder<'a, 'b, A> { + #[inline] + pub fn add_target(&mut self, target: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(EdgePointNodeAnchor::VT_TARGET, target); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> EdgePointNodeAnchorBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + EdgePointNodeAnchorBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for EdgePointNodeAnchor<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("EdgePointNodeAnchor"); + ds.field("target", &self.target()); + ds.finish() + } +} +pub enum Edge2DOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct Edge2D<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for Edge2D<'a> { + type Inner = Edge2D<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> Edge2D<'a> { + pub const VT_ID: ::flatbuffers::VOffsetT = 4; + pub const VT_A_TYPE: ::flatbuffers::VOffsetT = 6; + pub const VT_A: ::flatbuffers::VOffsetT = 8; + pub const VT_B_TYPE: ::flatbuffers::VOffsetT = 10; + pub const VT_B: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + Edge2D { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args Edge2DArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = Edge2DBuilder::new(_fbb); + if let Some(x) = args.b { builder.add_b(x); } + if let Some(x) = args.a { builder.add_a(x); } + if let Some(x) = args.id { builder.add_id(x); } + builder.add_b_type(args.b_type); + builder.add_a_type(args.a_type); + builder.finish() + } + + + #[inline] + pub fn id(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(Edge2D::VT_ID, None)} + } + #[inline] + pub fn a_type(&self) -> EdgePoint { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Edge2D::VT_A_TYPE, Some(EdgePoint::NONE)).unwrap()} + } + #[inline] + pub fn a(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(Edge2D::VT_A, None)} + } + #[inline] + pub fn b_type(&self) -> EdgePoint { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Edge2D::VT_B_TYPE, Some(EdgePoint::NONE)).unwrap()} + } + #[inline] + pub fn b(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(Edge2D::VT_B, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn a_as_edge_point_position_2_d(&self) -> Option> { + if self.a_type() == EdgePoint::EdgePointPosition2D { + self.a().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { EdgePointPosition2D::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn a_as_edge_point_node_anchor(&self) -> Option> { + if self.a_type() == EdgePoint::EdgePointNodeAnchor { + self.a().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { EdgePointNodeAnchor::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn b_as_edge_point_position_2_d(&self) -> Option> { + if self.b_type() == EdgePoint::EdgePointPosition2D { + self.b().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { EdgePointPosition2D::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn b_as_edge_point_node_anchor(&self) -> Option> { + if self.b_type() == EdgePoint::EdgePointNodeAnchor { + self.b().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { EdgePointNodeAnchor::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for Edge2D<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("id", Self::VT_ID, false)? + .visit_union::("a_type", Self::VT_A_TYPE, "a", Self::VT_A, false, |key, v, pos| { + match key { + EdgePoint::EdgePointPosition2D => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("EdgePoint::EdgePointPosition2D", pos), + EdgePoint::EdgePointNodeAnchor => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("EdgePoint::EdgePointNodeAnchor", pos), + _ => Ok(()), + } + })? + .visit_union::("b_type", Self::VT_B_TYPE, "b", Self::VT_B, false, |key, v, pos| { + match key { + EdgePoint::EdgePointPosition2D => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("EdgePoint::EdgePointPosition2D", pos), + EdgePoint::EdgePointNodeAnchor => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("EdgePoint::EdgePointNodeAnchor", pos), + _ => Ok(()), + } + })? + .finish(); + Ok(()) + } +} +pub struct Edge2DArgs<'a> { + pub id: Option<::flatbuffers::WIPOffset<&'a str>>, + pub a_type: EdgePoint, + pub a: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, + pub b_type: EdgePoint, + pub b: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, +} +impl<'a> Default for Edge2DArgs<'a> { + #[inline] + fn default() -> Self { + Edge2DArgs { + id: None, + a_type: EdgePoint::NONE, + a: None, + b_type: EdgePoint::NONE, + b: None, + } + } +} + +pub struct Edge2DBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> Edge2DBuilder<'a, 'b, A> { + #[inline] + pub fn add_id(&mut self, id: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(Edge2D::VT_ID, id); + } + #[inline] + pub fn add_a_type(&mut self, a_type: EdgePoint) { + self.fbb_.push_slot::(Edge2D::VT_A_TYPE, a_type, EdgePoint::NONE); + } + #[inline] + pub fn add_a(&mut self, a: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(Edge2D::VT_A, a); + } + #[inline] + pub fn add_b_type(&mut self, b_type: EdgePoint) { + self.fbb_.push_slot::(Edge2D::VT_B_TYPE, b_type, EdgePoint::NONE); + } + #[inline] + pub fn add_b(&mut self, b: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(Edge2D::VT_B, b); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> Edge2DBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + Edge2DBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for Edge2D<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("Edge2D"); + ds.field("id", &self.id()); + ds.field("a_type", &self.a_type()); + match self.a_type() { + EdgePoint::EdgePointPosition2D => { + if let Some(x) = self.a_as_edge_point_position_2_d() { + ds.field("a", &x) + } else { + ds.field("a", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + EdgePoint::EdgePointNodeAnchor => { + if let Some(x) = self.a_as_edge_point_node_anchor() { + ds.field("a", &x) + } else { + ds.field("a", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("a", &x) + }, + }; + ds.field("b_type", &self.b_type()); + match self.b_type() { + EdgePoint::EdgePointPosition2D => { + if let Some(x) = self.b_as_edge_point_position_2_d() { + ds.field("b", &x) + } else { + ds.field("b", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + EdgePoint::EdgePointNodeAnchor => { + if let Some(x) = self.b_as_edge_point_node_anchor() { + ds.field("b", &x) + } else { + ds.field("b", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("b", &x) + }, + }; + ds.finish() + } +} +pub enum VectorNetworkLoopOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `VectorNetworkLoop(pub Vec)` +/// +/// A closed contour defined by indices into `segments`. +pub struct VectorNetworkLoop<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for VectorNetworkLoop<'a> { + type Inner = VectorNetworkLoop<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> VectorNetworkLoop<'a> { + pub const VT_LOOP_SEGMENT_INDICES: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + VectorNetworkLoop { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args VectorNetworkLoopArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = VectorNetworkLoopBuilder::new(_fbb); + if let Some(x) = args.loop_segment_indices { builder.add_loop_segment_indices(x); } + builder.finish() + } + + + #[inline] + pub fn loop_segment_indices(&self) -> Option<::flatbuffers::Vector<'a, u32>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, u32>>>(VectorNetworkLoop::VT_LOOP_SEGMENT_INDICES, None)} + } +} + +impl ::flatbuffers::Verifiable for VectorNetworkLoop<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, u32>>>("loop_segment_indices", Self::VT_LOOP_SEGMENT_INDICES, false)? + .finish(); + Ok(()) + } +} +pub struct VectorNetworkLoopArgs<'a> { + pub loop_segment_indices: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, u32>>>, +} +impl<'a> Default for VectorNetworkLoopArgs<'a> { + #[inline] + fn default() -> Self { + VectorNetworkLoopArgs { + loop_segment_indices: None, + } + } +} + +pub struct VectorNetworkLoopBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> VectorNetworkLoopBuilder<'a, 'b, A> { + #[inline] + pub fn add_loop_segment_indices(&mut self, loop_segment_indices: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , u32>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNetworkLoop::VT_LOOP_SEGMENT_INDICES, loop_segment_indices); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> VectorNetworkLoopBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + VectorNetworkLoopBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for VectorNetworkLoop<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("VectorNetworkLoop"); + ds.field("loop_segment_indices", &self.loop_segment_indices()); + ds.finish() + } +} +pub enum VectorNetworkRegionOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `VectorNetworkRegion { loops, fill_rule, fills }` +/// +/// Archive model uses `region_fill_paints` (empty = no fill) instead of Option wrappers. +pub struct VectorNetworkRegion<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for VectorNetworkRegion<'a> { + type Inner = VectorNetworkRegion<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> VectorNetworkRegion<'a> { + pub const VT_REGION_LOOPS: ::flatbuffers::VOffsetT = 4; + pub const VT_REGION_FILL_RULE: ::flatbuffers::VOffsetT = 6; + pub const VT_REGION_FILL_PAINTS: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + VectorNetworkRegion { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args VectorNetworkRegionArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = VectorNetworkRegionBuilder::new(_fbb); + if let Some(x) = args.region_fill_paints { builder.add_region_fill_paints(x); } + if let Some(x) = args.region_loops { builder.add_region_loops(x); } + builder.add_region_fill_rule(args.region_fill_rule); + builder.finish() + } + + + #[inline] + pub fn region_loops(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(VectorNetworkRegion::VT_REGION_LOOPS, None)} + } + #[inline] + pub fn region_fill_rule(&self) -> FillRule { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(VectorNetworkRegion::VT_REGION_FILL_RULE, Some(FillRule::NonZero)).unwrap()} + } + #[inline] + pub fn region_fill_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(VectorNetworkRegion::VT_REGION_FILL_PAINTS, None)} + } +} + +impl ::flatbuffers::Verifiable for VectorNetworkRegion<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("region_loops", Self::VT_REGION_LOOPS, false)? + .visit_field::("region_fill_rule", Self::VT_REGION_FILL_RULE, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("region_fill_paints", Self::VT_REGION_FILL_PAINTS, false)? + .finish(); + Ok(()) + } +} +pub struct VectorNetworkRegionArgs<'a> { + pub region_loops: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub region_fill_rule: FillRule, + pub region_fill_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, +} +impl<'a> Default for VectorNetworkRegionArgs<'a> { + #[inline] + fn default() -> Self { + VectorNetworkRegionArgs { + region_loops: None, + region_fill_rule: FillRule::NonZero, + region_fill_paints: None, + } + } +} + +pub struct VectorNetworkRegionBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> VectorNetworkRegionBuilder<'a, 'b, A> { + #[inline] + pub fn add_region_loops(&mut self, region_loops: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNetworkRegion::VT_REGION_LOOPS, region_loops); + } + #[inline] + pub fn add_region_fill_rule(&mut self, region_fill_rule: FillRule) { + self.fbb_.push_slot::(VectorNetworkRegion::VT_REGION_FILL_RULE, region_fill_rule, FillRule::NonZero); + } + #[inline] + pub fn add_region_fill_paints(&mut self, region_fill_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNetworkRegion::VT_REGION_FILL_PAINTS, region_fill_paints); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> VectorNetworkRegionBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + VectorNetworkRegionBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for VectorNetworkRegion<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("VectorNetworkRegion"); + ds.field("region_loops", &self.region_loops()); + ds.field("region_fill_rule", &self.region_fill_rule()); + ds.field("region_fill_paints", &self.region_fill_paints()); + ds.finish() + } +} +pub enum VectorNetworkDataOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `VectorNetwork { vertices, segments, regions }` +pub struct VectorNetworkData<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for VectorNetworkData<'a> { + type Inner = VectorNetworkData<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> VectorNetworkData<'a> { + pub const VT_VERTICES: ::flatbuffers::VOffsetT = 4; + pub const VT_SEGMENTS: ::flatbuffers::VOffsetT = 6; + pub const VT_REGIONS: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + VectorNetworkData { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args VectorNetworkDataArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = VectorNetworkDataBuilder::new(_fbb); + if let Some(x) = args.regions { builder.add_regions(x); } + if let Some(x) = args.segments { builder.add_segments(x); } + if let Some(x) = args.vertices { builder.add_vertices(x); } + builder.finish() + } + + + #[inline] + pub fn vertices(&self) -> Option<::flatbuffers::Vector<'a, CGPoint>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, CGPoint>>>(VectorNetworkData::VT_VERTICES, None)} + } + #[inline] + pub fn segments(&self) -> Option<::flatbuffers::Vector<'a, VectorNetworkSegment>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, VectorNetworkSegment>>>(VectorNetworkData::VT_SEGMENTS, None)} + } + #[inline] + pub fn regions(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(VectorNetworkData::VT_REGIONS, None)} + } +} + +impl ::flatbuffers::Verifiable for VectorNetworkData<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, CGPoint>>>("vertices", Self::VT_VERTICES, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, VectorNetworkSegment>>>("segments", Self::VT_SEGMENTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("regions", Self::VT_REGIONS, false)? + .finish(); + Ok(()) + } +} +pub struct VectorNetworkDataArgs<'a> { + pub vertices: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, CGPoint>>>, + pub segments: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, VectorNetworkSegment>>>, + pub regions: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, +} +impl<'a> Default for VectorNetworkDataArgs<'a> { + #[inline] + fn default() -> Self { + VectorNetworkDataArgs { + vertices: None, + segments: None, + regions: None, + } + } +} + +pub struct VectorNetworkDataBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> VectorNetworkDataBuilder<'a, 'b, A> { + #[inline] + pub fn add_vertices(&mut self, vertices: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , CGPoint>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNetworkData::VT_VERTICES, vertices); + } + #[inline] + pub fn add_segments(&mut self, segments: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , VectorNetworkSegment>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNetworkData::VT_SEGMENTS, segments); + } + #[inline] + pub fn add_regions(&mut self, regions: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNetworkData::VT_REGIONS, regions); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> VectorNetworkDataBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + VectorNetworkDataBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for VectorNetworkData<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("VectorNetworkData"); + ds.field("vertices", &self.vertices()); + ds.field("segments", &self.segments()); + ds.field("regions", &self.regions()); + ds.finish() + } +} +pub enum FeGaussianBlurOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FeGaussianBlur { radius: f32 }` +pub struct FeGaussianBlur<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FeGaussianBlur<'a> { + type Inner = FeGaussianBlur<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FeGaussianBlur<'a> { + pub const VT_RADIUS: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FeGaussianBlur { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FeGaussianBlurArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FeGaussianBlurBuilder::new(_fbb); + builder.add_radius(args.radius); + builder.finish() + } + + + #[inline] + pub fn radius(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeGaussianBlur::VT_RADIUS, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for FeGaussianBlur<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("radius", Self::VT_RADIUS, false)? + .finish(); + Ok(()) + } +} +pub struct FeGaussianBlurArgs { + pub radius: f32, +} +impl<'a> Default for FeGaussianBlurArgs { + #[inline] + fn default() -> Self { + FeGaussianBlurArgs { + radius: 0.0, + } + } +} + +pub struct FeGaussianBlurBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FeGaussianBlurBuilder<'a, 'b, A> { + #[inline] + pub fn add_radius(&mut self, radius: f32) { + self.fbb_.push_slot::(FeGaussianBlur::VT_RADIUS, radius, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FeGaussianBlurBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FeGaussianBlurBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FeGaussianBlur<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FeGaussianBlur"); + ds.field("radius", &self.radius()); + ds.finish() + } +} +pub enum FeProgressiveBlurOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FeProgressiveBlur { start: Alignment, end: Alignment, radius, radius2 }` +pub struct FeProgressiveBlur<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FeProgressiveBlur<'a> { + type Inner = FeProgressiveBlur<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FeProgressiveBlur<'a> { + pub const VT_START: ::flatbuffers::VOffsetT = 4; + pub const VT_END: ::flatbuffers::VOffsetT = 6; + pub const VT_RADIUS: ::flatbuffers::VOffsetT = 8; + pub const VT_RADIUS2: ::flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FeProgressiveBlur { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FeProgressiveBlurArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FeProgressiveBlurBuilder::new(_fbb); + builder.add_radius2(args.radius2); + builder.add_radius(args.radius); + if let Some(x) = args.end { builder.add_end(x); } + if let Some(x) = args.start { builder.add_start(x); } + builder.finish() + } + + + #[inline] + pub fn start(&self) -> Option<&'a Alignment> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeProgressiveBlur::VT_START, None)} + } + #[inline] + pub fn end(&self) -> Option<&'a Alignment> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeProgressiveBlur::VT_END, None)} + } + #[inline] + pub fn radius(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeProgressiveBlur::VT_RADIUS, Some(0.0)).unwrap()} + } + #[inline] + pub fn radius2(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeProgressiveBlur::VT_RADIUS2, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for FeProgressiveBlur<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("start", Self::VT_START, false)? + .visit_field::("end", Self::VT_END, false)? + .visit_field::("radius", Self::VT_RADIUS, false)? + .visit_field::("radius2", Self::VT_RADIUS2, false)? + .finish(); + Ok(()) + } +} +pub struct FeProgressiveBlurArgs<'a> { + pub start: Option<&'a Alignment>, + pub end: Option<&'a Alignment>, + pub radius: f32, + pub radius2: f32, +} +impl<'a> Default for FeProgressiveBlurArgs<'a> { + #[inline] + fn default() -> Self { + FeProgressiveBlurArgs { + start: None, + end: None, + radius: 0.0, + radius2: 0.0, + } + } +} + +pub struct FeProgressiveBlurBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FeProgressiveBlurBuilder<'a, 'b, A> { + #[inline] + pub fn add_start(&mut self, start: &Alignment) { + self.fbb_.push_slot_always::<&Alignment>(FeProgressiveBlur::VT_START, start); + } + #[inline] + pub fn add_end(&mut self, end: &Alignment) { + self.fbb_.push_slot_always::<&Alignment>(FeProgressiveBlur::VT_END, end); + } + #[inline] + pub fn add_radius(&mut self, radius: f32) { + self.fbb_.push_slot::(FeProgressiveBlur::VT_RADIUS, radius, 0.0); + } + #[inline] + pub fn add_radius2(&mut self, radius2: f32) { + self.fbb_.push_slot::(FeProgressiveBlur::VT_RADIUS2, radius2, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FeProgressiveBlurBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FeProgressiveBlurBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FeProgressiveBlur<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FeProgressiveBlur"); + ds.field("start", &self.start()); + ds.field("end", &self.end()); + ds.field("radius", &self.radius()); + ds.field("radius2", &self.radius2()); + ds.finish() + } +} +pub enum FeLayerBlurOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FeLayerBlur { blur: FeBlur, active: bool }` +pub struct FeLayerBlur<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FeLayerBlur<'a> { + type Inner = FeLayerBlur<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FeLayerBlur<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_BLUR_TYPE: ::flatbuffers::VOffsetT = 6; + pub const VT_BLUR: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FeLayerBlur { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FeLayerBlurArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FeLayerBlurBuilder::new(_fbb); + if let Some(x) = args.blur { builder.add_blur(x); } + builder.add_blur_type(args.blur_type); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLayerBlur::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn blur_type(&self) -> FeBlur { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLayerBlur::VT_BLUR_TYPE, Some(FeBlur::NONE)).unwrap()} + } + #[inline] + pub fn blur(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(FeLayerBlur::VT_BLUR, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn blur_as_fe_gaussian_blur(&self) -> Option> { + if self.blur_type() == FeBlur::FeGaussianBlur { + self.blur().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { FeGaussianBlur::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn blur_as_fe_progressive_blur(&self) -> Option> { + if self.blur_type() == FeBlur::FeProgressiveBlur { + self.blur().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { FeProgressiveBlur::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for FeLayerBlur<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_union::("blur_type", Self::VT_BLUR_TYPE, "blur", Self::VT_BLUR, false, |key, v, pos| { + match key { + FeBlur::FeGaussianBlur => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("FeBlur::FeGaussianBlur", pos), + FeBlur::FeProgressiveBlur => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("FeBlur::FeProgressiveBlur", pos), + _ => Ok(()), + } + })? + .finish(); + Ok(()) + } +} +pub struct FeLayerBlurArgs { + pub active: bool, + pub blur_type: FeBlur, + pub blur: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, +} +impl<'a> Default for FeLayerBlurArgs { + #[inline] + fn default() -> Self { + FeLayerBlurArgs { + active: true, + blur_type: FeBlur::NONE, + blur: None, + } + } +} + +pub struct FeLayerBlurBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FeLayerBlurBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(FeLayerBlur::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_blur_type(&mut self, blur_type: FeBlur) { + self.fbb_.push_slot::(FeLayerBlur::VT_BLUR_TYPE, blur_type, FeBlur::NONE); + } + #[inline] + pub fn add_blur(&mut self, blur: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(FeLayerBlur::VT_BLUR, blur); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FeLayerBlurBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FeLayerBlurBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FeLayerBlur<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FeLayerBlur"); + ds.field("active", &self.active()); + ds.field("blur_type", &self.blur_type()); + match self.blur_type() { + FeBlur::FeGaussianBlur => { + if let Some(x) = self.blur_as_fe_gaussian_blur() { + ds.field("blur", &x) + } else { + ds.field("blur", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + FeBlur::FeProgressiveBlur => { + if let Some(x) = self.blur_as_fe_progressive_blur() { + ds.field("blur", &x) + } else { + ds.field("blur", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("blur", &x) + }, + }; + ds.finish() + } +} +pub enum FeBackdropBlurOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FeBackdropBlur { blur: FeBlur, active: bool }` +pub struct FeBackdropBlur<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FeBackdropBlur<'a> { + type Inner = FeBackdropBlur<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FeBackdropBlur<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_BLUR_TYPE: ::flatbuffers::VOffsetT = 6; + pub const VT_BLUR: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FeBackdropBlur { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FeBackdropBlurArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FeBackdropBlurBuilder::new(_fbb); + if let Some(x) = args.blur { builder.add_blur(x); } + builder.add_blur_type(args.blur_type); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeBackdropBlur::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn blur_type(&self) -> FeBlur { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeBackdropBlur::VT_BLUR_TYPE, Some(FeBlur::NONE)).unwrap()} + } + #[inline] + pub fn blur(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(FeBackdropBlur::VT_BLUR, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn blur_as_fe_gaussian_blur(&self) -> Option> { + if self.blur_type() == FeBlur::FeGaussianBlur { + self.blur().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { FeGaussianBlur::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn blur_as_fe_progressive_blur(&self) -> Option> { + if self.blur_type() == FeBlur::FeProgressiveBlur { + self.blur().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { FeProgressiveBlur::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for FeBackdropBlur<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_union::("blur_type", Self::VT_BLUR_TYPE, "blur", Self::VT_BLUR, false, |key, v, pos| { + match key { + FeBlur::FeGaussianBlur => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("FeBlur::FeGaussianBlur", pos), + FeBlur::FeProgressiveBlur => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("FeBlur::FeProgressiveBlur", pos), + _ => Ok(()), + } + })? + .finish(); + Ok(()) + } +} +pub struct FeBackdropBlurArgs { + pub active: bool, + pub blur_type: FeBlur, + pub blur: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, +} +impl<'a> Default for FeBackdropBlurArgs { + #[inline] + fn default() -> Self { + FeBackdropBlurArgs { + active: true, + blur_type: FeBlur::NONE, + blur: None, + } + } +} + +pub struct FeBackdropBlurBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FeBackdropBlurBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(FeBackdropBlur::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_blur_type(&mut self, blur_type: FeBlur) { + self.fbb_.push_slot::(FeBackdropBlur::VT_BLUR_TYPE, blur_type, FeBlur::NONE); + } + #[inline] + pub fn add_blur(&mut self, blur: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(FeBackdropBlur::VT_BLUR, blur); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FeBackdropBlurBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FeBackdropBlurBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FeBackdropBlur<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FeBackdropBlur"); + ds.field("active", &self.active()); + ds.field("blur_type", &self.blur_type()); + match self.blur_type() { + FeBlur::FeGaussianBlur => { + if let Some(x) = self.blur_as_fe_gaussian_blur() { + ds.field("blur", &x) + } else { + ds.field("blur", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + FeBlur::FeProgressiveBlur => { + if let Some(x) = self.blur_as_fe_progressive_blur() { + ds.field("blur", &x) + } else { + ds.field("blur", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("blur", &x) + }, + }; + ds.finish() + } +} +pub enum FeShadowOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FeShadow { dx, dy, blur, spread, color, active }` +pub struct FeShadow<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FeShadow<'a> { + type Inner = FeShadow<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FeShadow<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_DX: ::flatbuffers::VOffsetT = 6; + pub const VT_DY: ::flatbuffers::VOffsetT = 8; + pub const VT_BLUR: ::flatbuffers::VOffsetT = 10; + pub const VT_SPREAD: ::flatbuffers::VOffsetT = 12; + pub const VT_COLOR: ::flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FeShadow { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FeShadowArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FeShadowBuilder::new(_fbb); + if let Some(x) = args.color { builder.add_color(x); } + builder.add_spread(args.spread); + builder.add_blur(args.blur); + builder.add_dy(args.dy); + builder.add_dx(args.dx); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeShadow::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn dx(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeShadow::VT_DX, Some(0.0)).unwrap()} + } + #[inline] + pub fn dy(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeShadow::VT_DY, Some(0.0)).unwrap()} + } + #[inline] + pub fn blur(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeShadow::VT_BLUR, Some(0.0)).unwrap()} + } + #[inline] + pub fn spread(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeShadow::VT_SPREAD, Some(0.0)).unwrap()} + } + #[inline] + pub fn color(&self) -> Option<&'a RGBA32F> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeShadow::VT_COLOR, None)} + } +} + +impl ::flatbuffers::Verifiable for FeShadow<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("dx", Self::VT_DX, false)? + .visit_field::("dy", Self::VT_DY, false)? + .visit_field::("blur", Self::VT_BLUR, false)? + .visit_field::("spread", Self::VT_SPREAD, false)? + .visit_field::("color", Self::VT_COLOR, false)? + .finish(); + Ok(()) + } +} +pub struct FeShadowArgs<'a> { + pub active: bool, + pub dx: f32, + pub dy: f32, + pub blur: f32, + pub spread: f32, + pub color: Option<&'a RGBA32F>, +} +impl<'a> Default for FeShadowArgs<'a> { + #[inline] + fn default() -> Self { + FeShadowArgs { + active: true, + dx: 0.0, + dy: 0.0, + blur: 0.0, + spread: 0.0, + color: None, + } + } +} + +pub struct FeShadowBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FeShadowBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(FeShadow::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_dx(&mut self, dx: f32) { + self.fbb_.push_slot::(FeShadow::VT_DX, dx, 0.0); + } + #[inline] + pub fn add_dy(&mut self, dy: f32) { + self.fbb_.push_slot::(FeShadow::VT_DY, dy, 0.0); + } + #[inline] + pub fn add_blur(&mut self, blur: f32) { + self.fbb_.push_slot::(FeShadow::VT_BLUR, blur, 0.0); + } + #[inline] + pub fn add_spread(&mut self, spread: f32) { + self.fbb_.push_slot::(FeShadow::VT_SPREAD, spread, 0.0); + } + #[inline] + pub fn add_color(&mut self, color: &RGBA32F) { + self.fbb_.push_slot_always::<&RGBA32F>(FeShadow::VT_COLOR, color); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FeShadowBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FeShadowBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FeShadow<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FeShadow"); + ds.field("active", &self.active()); + ds.field("dx", &self.dx()); + ds.field("dy", &self.dy()); + ds.field("blur", &self.blur()); + ds.field("spread", &self.spread()); + ds.field("color", &self.color()); + ds.finish() + } +} +pub enum FilterShadowEffectOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Struct-tagged representation of Rust `FilterShadowEffect`. +pub struct FilterShadowEffect<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FilterShadowEffect<'a> { + type Inner = FilterShadowEffect<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FilterShadowEffect<'a> { + pub const VT_KIND: ::flatbuffers::VOffsetT = 4; + pub const VT_SHADOW: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FilterShadowEffect { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FilterShadowEffectArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FilterShadowEffectBuilder::new(_fbb); + if let Some(x) = args.shadow { builder.add_shadow(x); } + builder.add_kind(args.kind); + builder.finish() + } + + + #[inline] + pub fn kind(&self) -> FilterShadowEffectKind { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FilterShadowEffect::VT_KIND, Some(FilterShadowEffectKind::DropShadow)).unwrap()} + } + #[inline] + pub fn shadow(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(FilterShadowEffect::VT_SHADOW, None)} + } +} + +impl ::flatbuffers::Verifiable for FilterShadowEffect<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("kind", Self::VT_KIND, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("shadow", Self::VT_SHADOW, false)? + .finish(); + Ok(()) + } +} +pub struct FilterShadowEffectArgs<'a> { + pub kind: FilterShadowEffectKind, + pub shadow: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for FilterShadowEffectArgs<'a> { + #[inline] + fn default() -> Self { + FilterShadowEffectArgs { + kind: FilterShadowEffectKind::DropShadow, + shadow: None, + } + } +} + +pub struct FilterShadowEffectBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FilterShadowEffectBuilder<'a, 'b, A> { + #[inline] + pub fn add_kind(&mut self, kind: FilterShadowEffectKind) { + self.fbb_.push_slot::(FilterShadowEffect::VT_KIND, kind, FilterShadowEffectKind::DropShadow); + } + #[inline] + pub fn add_shadow(&mut self, shadow: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(FilterShadowEffect::VT_SHADOW, shadow); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FilterShadowEffectBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FilterShadowEffectBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FilterShadowEffect<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FilterShadowEffect"); + ds.field("kind", &self.kind()); + ds.field("shadow", &self.shadow()); + ds.finish() + } +} +pub enum NoiseEffectColorsOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Struct-tagged representation of Rust `NoiseEffectColors`. +pub struct NoiseEffectColors<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for NoiseEffectColors<'a> { + type Inner = NoiseEffectColors<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> NoiseEffectColors<'a> { + pub const VT_KIND: ::flatbuffers::VOffsetT = 4; + pub const VT_MONO_COLOR: ::flatbuffers::VOffsetT = 6; + pub const VT_DUO_COLOR1: ::flatbuffers::VOffsetT = 8; + pub const VT_DUO_COLOR2: ::flatbuffers::VOffsetT = 10; + pub const VT_MULTI_OPACITY: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + NoiseEffectColors { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args NoiseEffectColorsArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = NoiseEffectColorsBuilder::new(_fbb); + builder.add_multi_opacity(args.multi_opacity); + if let Some(x) = args.duo_color2 { builder.add_duo_color2(x); } + if let Some(x) = args.duo_color1 { builder.add_duo_color1(x); } + if let Some(x) = args.mono_color { builder.add_mono_color(x); } + builder.add_kind(args.kind); + builder.finish() + } + + + #[inline] + pub fn kind(&self) -> NoiseEffectColorsKind { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(NoiseEffectColors::VT_KIND, Some(NoiseEffectColorsKind::Mono)).unwrap()} + } + #[inline] + pub fn mono_color(&self) -> Option<&'a RGBA32F> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(NoiseEffectColors::VT_MONO_COLOR, None)} + } + #[inline] + pub fn duo_color1(&self) -> Option<&'a RGBA32F> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(NoiseEffectColors::VT_DUO_COLOR1, None)} + } + #[inline] + pub fn duo_color2(&self) -> Option<&'a RGBA32F> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(NoiseEffectColors::VT_DUO_COLOR2, None)} + } + #[inline] + pub fn multi_opacity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(NoiseEffectColors::VT_MULTI_OPACITY, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for NoiseEffectColors<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("kind", Self::VT_KIND, false)? + .visit_field::("mono_color", Self::VT_MONO_COLOR, false)? + .visit_field::("duo_color1", Self::VT_DUO_COLOR1, false)? + .visit_field::("duo_color2", Self::VT_DUO_COLOR2, false)? + .visit_field::("multi_opacity", Self::VT_MULTI_OPACITY, false)? + .finish(); + Ok(()) + } +} +pub struct NoiseEffectColorsArgs<'a> { + pub kind: NoiseEffectColorsKind, + pub mono_color: Option<&'a RGBA32F>, + pub duo_color1: Option<&'a RGBA32F>, + pub duo_color2: Option<&'a RGBA32F>, + pub multi_opacity: f32, +} +impl<'a> Default for NoiseEffectColorsArgs<'a> { + #[inline] + fn default() -> Self { + NoiseEffectColorsArgs { + kind: NoiseEffectColorsKind::Mono, + mono_color: None, + duo_color1: None, + duo_color2: None, + multi_opacity: 0.0, + } + } +} + +pub struct NoiseEffectColorsBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> NoiseEffectColorsBuilder<'a, 'b, A> { + #[inline] + pub fn add_kind(&mut self, kind: NoiseEffectColorsKind) { + self.fbb_.push_slot::(NoiseEffectColors::VT_KIND, kind, NoiseEffectColorsKind::Mono); + } + #[inline] + pub fn add_mono_color(&mut self, mono_color: &RGBA32F) { + self.fbb_.push_slot_always::<&RGBA32F>(NoiseEffectColors::VT_MONO_COLOR, mono_color); + } + #[inline] + pub fn add_duo_color1(&mut self, duo_color1: &RGBA32F) { + self.fbb_.push_slot_always::<&RGBA32F>(NoiseEffectColors::VT_DUO_COLOR1, duo_color1); + } + #[inline] + pub fn add_duo_color2(&mut self, duo_color2: &RGBA32F) { + self.fbb_.push_slot_always::<&RGBA32F>(NoiseEffectColors::VT_DUO_COLOR2, duo_color2); + } + #[inline] + pub fn add_multi_opacity(&mut self, multi_opacity: f32) { + self.fbb_.push_slot::(NoiseEffectColors::VT_MULTI_OPACITY, multi_opacity, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> NoiseEffectColorsBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NoiseEffectColorsBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for NoiseEffectColors<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("NoiseEffectColors"); + ds.field("kind", &self.kind()); + ds.field("mono_color", &self.mono_color()); + ds.field("duo_color1", &self.duo_color1()); + ds.field("duo_color2", &self.duo_color2()); + ds.field("multi_opacity", &self.multi_opacity()); + ds.finish() + } +} +pub enum FeNoiseEffectOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FeNoiseEffect` +pub struct FeNoiseEffect<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FeNoiseEffect<'a> { + type Inner = FeNoiseEffect<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FeNoiseEffect<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_NOISE_SIZE: ::flatbuffers::VOffsetT = 6; + pub const VT_DENSITY: ::flatbuffers::VOffsetT = 8; + pub const VT_NUM_OCTAVES: ::flatbuffers::VOffsetT = 10; + pub const VT_SEED: ::flatbuffers::VOffsetT = 12; + pub const VT_COLORING: ::flatbuffers::VOffsetT = 14; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 16; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FeNoiseEffect { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FeNoiseEffectArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FeNoiseEffectBuilder::new(_fbb); + if let Some(x) = args.coloring { builder.add_coloring(x); } + builder.add_seed(args.seed); + builder.add_num_octaves(args.num_octaves); + builder.add_density(args.density); + builder.add_noise_size(args.noise_size); + builder.add_blend_mode(args.blend_mode); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeNoiseEffect::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn noise_size(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeNoiseEffect::VT_NOISE_SIZE, Some(0.0)).unwrap()} + } + #[inline] + pub fn density(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeNoiseEffect::VT_DENSITY, Some(0.0)).unwrap()} + } + #[inline] + pub fn num_octaves(&self) -> i32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeNoiseEffect::VT_NUM_OCTAVES, Some(0)).unwrap()} + } + #[inline] + pub fn seed(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeNoiseEffect::VT_SEED, Some(0.0)).unwrap()} + } + #[inline] + pub fn coloring(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(FeNoiseEffect::VT_COLORING, None)} + } + #[inline] + pub fn blend_mode(&self) -> BlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeNoiseEffect::VT_BLEND_MODE, Some(BlendMode::Normal)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for FeNoiseEffect<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("noise_size", Self::VT_NOISE_SIZE, false)? + .visit_field::("density", Self::VT_DENSITY, false)? + .visit_field::("num_octaves", Self::VT_NUM_OCTAVES, false)? + .visit_field::("seed", Self::VT_SEED, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("coloring", Self::VT_COLORING, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .finish(); + Ok(()) + } +} +pub struct FeNoiseEffectArgs<'a> { + pub active: bool, + pub noise_size: f32, + pub density: f32, + pub num_octaves: i32, + pub seed: f32, + pub coloring: Option<::flatbuffers::WIPOffset>>, + pub blend_mode: BlendMode, +} +impl<'a> Default for FeNoiseEffectArgs<'a> { + #[inline] + fn default() -> Self { + FeNoiseEffectArgs { + active: true, + noise_size: 0.0, + density: 0.0, + num_octaves: 0, + seed: 0.0, + coloring: None, + blend_mode: BlendMode::Normal, + } + } +} + +pub struct FeNoiseEffectBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FeNoiseEffectBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(FeNoiseEffect::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_noise_size(&mut self, noise_size: f32) { + self.fbb_.push_slot::(FeNoiseEffect::VT_NOISE_SIZE, noise_size, 0.0); + } + #[inline] + pub fn add_density(&mut self, density: f32) { + self.fbb_.push_slot::(FeNoiseEffect::VT_DENSITY, density, 0.0); + } + #[inline] + pub fn add_num_octaves(&mut self, num_octaves: i32) { + self.fbb_.push_slot::(FeNoiseEffect::VT_NUM_OCTAVES, num_octaves, 0); + } + #[inline] + pub fn add_seed(&mut self, seed: f32) { + self.fbb_.push_slot::(FeNoiseEffect::VT_SEED, seed, 0.0); + } + #[inline] + pub fn add_coloring(&mut self, coloring: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(FeNoiseEffect::VT_COLORING, coloring); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: BlendMode) { + self.fbb_.push_slot::(FeNoiseEffect::VT_BLEND_MODE, blend_mode, BlendMode::Normal); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FeNoiseEffectBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FeNoiseEffectBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FeNoiseEffect<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FeNoiseEffect"); + ds.field("active", &self.active()); + ds.field("noise_size", &self.noise_size()); + ds.field("density", &self.density()); + ds.field("num_octaves", &self.num_octaves()); + ds.field("seed", &self.seed()); + ds.field("coloring", &self.coloring()); + ds.field("blend_mode", &self.blend_mode()); + ds.finish() + } +} +pub enum FeLiquidGlassOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `FeLiquidGlass` +pub struct FeLiquidGlass<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for FeLiquidGlass<'a> { + type Inner = FeLiquidGlass<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> FeLiquidGlass<'a> { + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 4; + pub const VT_LIGHT_INTENSITY: ::flatbuffers::VOffsetT = 6; + pub const VT_LIGHT_ANGLE: ::flatbuffers::VOffsetT = 8; + pub const VT_REFRACTION: ::flatbuffers::VOffsetT = 10; + pub const VT_DEPTH: ::flatbuffers::VOffsetT = 12; + pub const VT_DISPERSION: ::flatbuffers::VOffsetT = 14; + pub const VT_BLUR_RADIUS: ::flatbuffers::VOffsetT = 16; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + FeLiquidGlass { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FeLiquidGlassArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = FeLiquidGlassBuilder::new(_fbb); + builder.add_blur_radius(args.blur_radius); + builder.add_dispersion(args.dispersion); + builder.add_depth(args.depth); + builder.add_refraction(args.refraction); + builder.add_light_angle(args.light_angle); + builder.add_light_intensity(args.light_intensity); + builder.add_active(args.active); + builder.finish() + } + + + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLiquidGlass::VT_ACTIVE, Some(true)).unwrap()} + } + #[inline] + pub fn light_intensity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLiquidGlass::VT_LIGHT_INTENSITY, Some(0.0)).unwrap()} + } + #[inline] + pub fn light_angle(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLiquidGlass::VT_LIGHT_ANGLE, Some(0.0)).unwrap()} + } + #[inline] + pub fn refraction(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLiquidGlass::VT_REFRACTION, Some(0.0)).unwrap()} + } + #[inline] + pub fn depth(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLiquidGlass::VT_DEPTH, Some(0.0)).unwrap()} + } + #[inline] + pub fn dispersion(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLiquidGlass::VT_DISPERSION, Some(0.0)).unwrap()} + } + #[inline] + pub fn blur_radius(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(FeLiquidGlass::VT_BLUR_RADIUS, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for FeLiquidGlass<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::("light_intensity", Self::VT_LIGHT_INTENSITY, false)? + .visit_field::("light_angle", Self::VT_LIGHT_ANGLE, false)? + .visit_field::("refraction", Self::VT_REFRACTION, false)? + .visit_field::("depth", Self::VT_DEPTH, false)? + .visit_field::("dispersion", Self::VT_DISPERSION, false)? + .visit_field::("blur_radius", Self::VT_BLUR_RADIUS, false)? + .finish(); + Ok(()) + } +} +pub struct FeLiquidGlassArgs { + pub active: bool, + pub light_intensity: f32, + pub light_angle: f32, + pub refraction: f32, + pub depth: f32, + pub dispersion: f32, + pub blur_radius: f32, +} +impl<'a> Default for FeLiquidGlassArgs { + #[inline] + fn default() -> Self { + FeLiquidGlassArgs { + active: true, + light_intensity: 0.0, + light_angle: 0.0, + refraction: 0.0, + depth: 0.0, + dispersion: 0.0, + blur_radius: 0.0, + } + } +} + +pub struct FeLiquidGlassBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> FeLiquidGlassBuilder<'a, 'b, A> { + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(FeLiquidGlass::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_light_intensity(&mut self, light_intensity: f32) { + self.fbb_.push_slot::(FeLiquidGlass::VT_LIGHT_INTENSITY, light_intensity, 0.0); + } + #[inline] + pub fn add_light_angle(&mut self, light_angle: f32) { + self.fbb_.push_slot::(FeLiquidGlass::VT_LIGHT_ANGLE, light_angle, 0.0); + } + #[inline] + pub fn add_refraction(&mut self, refraction: f32) { + self.fbb_.push_slot::(FeLiquidGlass::VT_REFRACTION, refraction, 0.0); + } + #[inline] + pub fn add_depth(&mut self, depth: f32) { + self.fbb_.push_slot::(FeLiquidGlass::VT_DEPTH, depth, 0.0); + } + #[inline] + pub fn add_dispersion(&mut self, dispersion: f32) { + self.fbb_.push_slot::(FeLiquidGlass::VT_DISPERSION, dispersion, 0.0); + } + #[inline] + pub fn add_blur_radius(&mut self, blur_radius: f32) { + self.fbb_.push_slot::(FeLiquidGlass::VT_BLUR_RADIUS, blur_radius, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> FeLiquidGlassBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FeLiquidGlassBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for FeLiquidGlass<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("FeLiquidGlass"); + ds.field("active", &self.active()); + ds.field("light_intensity", &self.light_intensity()); + ds.field("light_angle", &self.light_angle()); + ds.field("refraction", &self.refraction()); + ds.field("depth", &self.depth()); + ds.field("dispersion", &self.dispersion()); + ds.field("blur_radius", &self.blur_radius()); + ds.finish() + } +} +pub enum LayerEffectsOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Effects trait used by nodes (Rust: `LayerEffects`). +/// +/// Note: must be a table because it contains vectors (FlatBuffers structs cannot contain vectors). +pub struct LayerEffects<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayerEffects<'a> { + type Inner = LayerEffects<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayerEffects<'a> { + pub const VT_FE_BLUR: ::flatbuffers::VOffsetT = 4; + pub const VT_FE_BACKDROP_BLUR: ::flatbuffers::VOffsetT = 6; + pub const VT_FE_GLASS: ::flatbuffers::VOffsetT = 8; + pub const VT_FE_SHADOWS: ::flatbuffers::VOffsetT = 10; + pub const VT_FE_NOISES: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayerEffects { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayerEffectsArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayerEffectsBuilder::new(_fbb); + if let Some(x) = args.fe_noises { builder.add_fe_noises(x); } + if let Some(x) = args.fe_shadows { builder.add_fe_shadows(x); } + if let Some(x) = args.fe_glass { builder.add_fe_glass(x); } + if let Some(x) = args.fe_backdrop_blur { builder.add_fe_backdrop_blur(x); } + if let Some(x) = args.fe_blur { builder.add_fe_blur(x); } + builder.finish() + } + + + #[inline] + pub fn fe_blur(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayerEffects::VT_FE_BLUR, None)} + } + #[inline] + pub fn fe_backdrop_blur(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayerEffects::VT_FE_BACKDROP_BLUR, None)} + } + #[inline] + pub fn fe_glass(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayerEffects::VT_FE_GLASS, None)} + } + #[inline] + pub fn fe_shadows(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(LayerEffects::VT_FE_SHADOWS, None)} + } + #[inline] + pub fn fe_noises(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(LayerEffects::VT_FE_NOISES, None)} + } +} + +impl ::flatbuffers::Verifiable for LayerEffects<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("fe_blur", Self::VT_FE_BLUR, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("fe_backdrop_blur", Self::VT_FE_BACKDROP_BLUR, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("fe_glass", Self::VT_FE_GLASS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("fe_shadows", Self::VT_FE_SHADOWS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("fe_noises", Self::VT_FE_NOISES, false)? + .finish(); + Ok(()) + } +} +pub struct LayerEffectsArgs<'a> { + pub fe_blur: Option<::flatbuffers::WIPOffset>>, + pub fe_backdrop_blur: Option<::flatbuffers::WIPOffset>>, + pub fe_glass: Option<::flatbuffers::WIPOffset>>, + pub fe_shadows: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub fe_noises: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, +} +impl<'a> Default for LayerEffectsArgs<'a> { + #[inline] + fn default() -> Self { + LayerEffectsArgs { + fe_blur: None, + fe_backdrop_blur: None, + fe_glass: None, + fe_shadows: None, + fe_noises: None, + } + } +} + +pub struct LayerEffectsBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayerEffectsBuilder<'a, 'b, A> { + #[inline] + pub fn add_fe_blur(&mut self, fe_blur: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayerEffects::VT_FE_BLUR, fe_blur); + } + #[inline] + pub fn add_fe_backdrop_blur(&mut self, fe_backdrop_blur: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayerEffects::VT_FE_BACKDROP_BLUR, fe_backdrop_blur); + } + #[inline] + pub fn add_fe_glass(&mut self, fe_glass: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayerEffects::VT_FE_GLASS, fe_glass); + } + #[inline] + pub fn add_fe_shadows(&mut self, fe_shadows: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(LayerEffects::VT_FE_SHADOWS, fe_shadows); + } + #[inline] + pub fn add_fe_noises(&mut self, fe_noises: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(LayerEffects::VT_FE_NOISES, fe_noises); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayerEffectsBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayerEffectsBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayerEffects<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayerEffects"); + ds.field("fe_blur", &self.fe_blur()); + ds.field("fe_backdrop_blur", &self.fe_backdrop_blur()); + ds.field("fe_glass", &self.fe_glass()); + ds.field("fe_shadows", &self.fe_shadows()); + ds.field("fe_noises", &self.fe_noises()); + ds.finish() + } +} +pub enum StrokeStyleOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct StrokeStyle<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for StrokeStyle<'a> { + type Inner = StrokeStyle<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> StrokeStyle<'a> { + pub const VT_STROKE_ALIGN: ::flatbuffers::VOffsetT = 4; + pub const VT_STROKE_CAP: ::flatbuffers::VOffsetT = 6; + pub const VT_STROKE_JOIN: ::flatbuffers::VOffsetT = 8; + pub const VT_STROKE_MITER_LIMIT: ::flatbuffers::VOffsetT = 10; + pub const VT_STROKE_DASH_ARRAY: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + StrokeStyle { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args StrokeStyleArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = StrokeStyleBuilder::new(_fbb); + if let Some(x) = args.stroke_dash_array { builder.add_stroke_dash_array(x); } + builder.add_stroke_miter_limit(args.stroke_miter_limit); + builder.add_stroke_join(args.stroke_join); + builder.add_stroke_cap(args.stroke_cap); + builder.add_stroke_align(args.stroke_align); + builder.finish() + } + + + #[inline] + pub fn stroke_align(&self) -> StrokeAlign { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(StrokeStyle::VT_STROKE_ALIGN, Some(StrokeAlign::Inside)).unwrap()} + } + #[inline] + pub fn stroke_cap(&self) -> StrokeCap { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(StrokeStyle::VT_STROKE_CAP, Some(StrokeCap::Butt)).unwrap()} + } + #[inline] + pub fn stroke_join(&self) -> StrokeJoin { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(StrokeStyle::VT_STROKE_JOIN, Some(StrokeJoin::Miter)).unwrap()} + } + #[inline] + pub fn stroke_miter_limit(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(StrokeStyle::VT_STROKE_MITER_LIMIT, Some(4.0)).unwrap()} + } + /// dash array in logical pixels. Empty or omitted means "no dash". + #[inline] + pub fn stroke_dash_array(&self) -> Option<::flatbuffers::Vector<'a, f32>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, f32>>>(StrokeStyle::VT_STROKE_DASH_ARRAY, None)} + } +} + +impl ::flatbuffers::Verifiable for StrokeStyle<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("stroke_align", Self::VT_STROKE_ALIGN, false)? + .visit_field::("stroke_cap", Self::VT_STROKE_CAP, false)? + .visit_field::("stroke_join", Self::VT_STROKE_JOIN, false)? + .visit_field::("stroke_miter_limit", Self::VT_STROKE_MITER_LIMIT, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, f32>>>("stroke_dash_array", Self::VT_STROKE_DASH_ARRAY, false)? + .finish(); + Ok(()) + } +} +pub struct StrokeStyleArgs<'a> { + pub stroke_align: StrokeAlign, + pub stroke_cap: StrokeCap, + pub stroke_join: StrokeJoin, + pub stroke_miter_limit: f32, + pub stroke_dash_array: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, f32>>>, +} +impl<'a> Default for StrokeStyleArgs<'a> { + #[inline] + fn default() -> Self { + StrokeStyleArgs { + stroke_align: StrokeAlign::Inside, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: 4.0, + stroke_dash_array: None, + } + } +} + +pub struct StrokeStyleBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> StrokeStyleBuilder<'a, 'b, A> { + #[inline] + pub fn add_stroke_align(&mut self, stroke_align: StrokeAlign) { + self.fbb_.push_slot::(StrokeStyle::VT_STROKE_ALIGN, stroke_align, StrokeAlign::Inside); + } + #[inline] + pub fn add_stroke_cap(&mut self, stroke_cap: StrokeCap) { + self.fbb_.push_slot::(StrokeStyle::VT_STROKE_CAP, stroke_cap, StrokeCap::Butt); + } + #[inline] + pub fn add_stroke_join(&mut self, stroke_join: StrokeJoin) { + self.fbb_.push_slot::(StrokeStyle::VT_STROKE_JOIN, stroke_join, StrokeJoin::Miter); + } + #[inline] + pub fn add_stroke_miter_limit(&mut self, stroke_miter_limit: f32) { + self.fbb_.push_slot::(StrokeStyle::VT_STROKE_MITER_LIMIT, stroke_miter_limit, 4.0); + } + #[inline] + pub fn add_stroke_dash_array(&mut self, stroke_dash_array: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , f32>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(StrokeStyle::VT_STROKE_DASH_ARRAY, stroke_dash_array); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> StrokeStyleBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + StrokeStyleBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for StrokeStyle<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("StrokeStyle"); + ds.field("stroke_align", &self.stroke_align()); + ds.field("stroke_cap", &self.stroke_cap()); + ds.field("stroke_join", &self.stroke_join()); + ds.field("stroke_miter_limit", &self.stroke_miter_limit()); + ds.field("stroke_dash_array", &self.stroke_dash_array()); + ds.finish() + } +} +pub enum VariableWidthStopOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// A single stop in a variable-width stroke profile. +/// +/// - `u` is the normalized position along the stroke in [0, 1]. +/// - `r` is the half-width ("radius") at this position in pixels. +pub struct VariableWidthStop<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for VariableWidthStop<'a> { + type Inner = VariableWidthStop<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> VariableWidthStop<'a> { + pub const VT_U: ::flatbuffers::VOffsetT = 4; + pub const VT_R: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + VariableWidthStop { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args VariableWidthStopArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = VariableWidthStopBuilder::new(_fbb); + builder.add_r(args.r); + builder.add_u(args.u); + builder.finish() + } + + + #[inline] + pub fn u(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(VariableWidthStop::VT_U, Some(0.0)).unwrap()} + } + #[inline] + pub fn r(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(VariableWidthStop::VT_R, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for VariableWidthStop<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("u", Self::VT_U, false)? + .visit_field::("r", Self::VT_R, false)? + .finish(); + Ok(()) + } +} +pub struct VariableWidthStopArgs { + pub u: f32, + pub r: f32, +} +impl<'a> Default for VariableWidthStopArgs { + #[inline] + fn default() -> Self { + VariableWidthStopArgs { + u: 0.0, + r: 0.0, + } + } +} + +pub struct VariableWidthStopBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> VariableWidthStopBuilder<'a, 'b, A> { + #[inline] + pub fn add_u(&mut self, u: f32) { + self.fbb_.push_slot::(VariableWidthStop::VT_U, u, 0.0); + } + #[inline] + pub fn add_r(&mut self, r: f32) { + self.fbb_.push_slot::(VariableWidthStop::VT_R, r, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> VariableWidthStopBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + VariableWidthStopBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for VariableWidthStop<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("VariableWidthStop"); + ds.field("u", &self.u()); + ds.field("r", &self.r()); + ds.finish() + } +} +pub enum VariableWidthProfileOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Variable-width stroke profile. +/// +/// Notes: +/// - This matches TS `cg.VariableWidthProfile` and Rust `cg::varwidth::{WidthStop, VarWidthProfile}` at the wire level. +/// - `base` is intentionally not stored here; renderers should derive a base half-width from the node's `stroke_width` +/// when `stops` is empty (see Rust `VarWidthSampler` behavior). +pub struct VariableWidthProfile<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for VariableWidthProfile<'a> { + type Inner = VariableWidthProfile<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> VariableWidthProfile<'a> { + pub const VT_STOPS: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + VariableWidthProfile { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args VariableWidthProfileArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = VariableWidthProfileBuilder::new(_fbb); + if let Some(x) = args.stops { builder.add_stops(x); } + builder.finish() + } + + + #[inline] + pub fn stops(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(VariableWidthProfile::VT_STOPS, None)} + } +} + +impl ::flatbuffers::Verifiable for VariableWidthProfile<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("stops", Self::VT_STOPS, false)? + .finish(); + Ok(()) + } +} +pub struct VariableWidthProfileArgs<'a> { + pub stops: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, +} +impl<'a> Default for VariableWidthProfileArgs<'a> { + #[inline] + fn default() -> Self { + VariableWidthProfileArgs { + stops: None, + } + } +} + +pub struct VariableWidthProfileBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> VariableWidthProfileBuilder<'a, 'b, A> { + #[inline] + pub fn add_stops(&mut self, stops: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VariableWidthProfile::VT_STOPS, stops); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> VariableWidthProfileBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + VariableWidthProfileBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for VariableWidthProfile<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("VariableWidthProfile"); + ds.field("stops", &self.stops()); + ds.finish() + } +} +pub enum StrokeGeometryTraitOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct StrokeGeometryTrait<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for StrokeGeometryTrait<'a> { + type Inner = StrokeGeometryTrait<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> StrokeGeometryTrait<'a> { + pub const VT_STROKE_WIDTH: ::flatbuffers::VOffsetT = 4; + pub const VT_STROKE_STYLE: ::flatbuffers::VOffsetT = 6; + pub const VT_STROKE_WIDTH_PROFILE: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + StrokeGeometryTrait { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args StrokeGeometryTraitArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = StrokeGeometryTraitBuilder::new(_fbb); + if let Some(x) = args.stroke_width_profile { builder.add_stroke_width_profile(x); } + if let Some(x) = args.stroke_style { builder.add_stroke_style(x); } + builder.add_stroke_width(args.stroke_width); + builder.finish() + } + + + #[inline] + pub fn stroke_width(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(StrokeGeometryTrait::VT_STROKE_WIDTH, Some(0.0)).unwrap()} + } + #[inline] + pub fn stroke_style(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(StrokeGeometryTrait::VT_STROKE_STYLE, None)} + } + #[inline] + pub fn stroke_width_profile(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(StrokeGeometryTrait::VT_STROKE_WIDTH_PROFILE, None)} + } +} + +impl ::flatbuffers::Verifiable for StrokeGeometryTrait<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("stroke_width", Self::VT_STROKE_WIDTH, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_style", Self::VT_STROKE_STYLE, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_width_profile", Self::VT_STROKE_WIDTH_PROFILE, false)? + .finish(); + Ok(()) + } +} +pub struct StrokeGeometryTraitArgs<'a> { + pub stroke_width: f32, + pub stroke_style: Option<::flatbuffers::WIPOffset>>, + pub stroke_width_profile: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for StrokeGeometryTraitArgs<'a> { + #[inline] + fn default() -> Self { + StrokeGeometryTraitArgs { + stroke_width: 0.0, + stroke_style: None, + stroke_width_profile: None, + } + } +} + +pub struct StrokeGeometryTraitBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> StrokeGeometryTraitBuilder<'a, 'b, A> { + #[inline] + pub fn add_stroke_width(&mut self, stroke_width: f32) { + self.fbb_.push_slot::(StrokeGeometryTrait::VT_STROKE_WIDTH, stroke_width, 0.0); + } + #[inline] + pub fn add_stroke_style(&mut self, stroke_style: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(StrokeGeometryTrait::VT_STROKE_STYLE, stroke_style); + } + #[inline] + pub fn add_stroke_width_profile(&mut self, stroke_width_profile: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(StrokeGeometryTrait::VT_STROKE_WIDTH_PROFILE, stroke_width_profile); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> StrokeGeometryTraitBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + StrokeGeometryTraitBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for StrokeGeometryTrait<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("StrokeGeometryTrait"); + ds.field("stroke_width", &self.stroke_width()); + ds.field("stroke_style", &self.stroke_style()); + ds.field("stroke_width_profile", &self.stroke_width_profile()); + ds.finish() + } +} +pub enum RectangularStrokeGeometryTraitOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct RectangularStrokeGeometryTrait<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for RectangularStrokeGeometryTrait<'a> { + type Inner = RectangularStrokeGeometryTrait<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> RectangularStrokeGeometryTrait<'a> { + pub const VT_RECTANGULAR_STROKE_WIDTH: ::flatbuffers::VOffsetT = 4; + pub const VT_STROKE_STYLE: ::flatbuffers::VOffsetT = 6; + pub const VT_STROKE_WIDTH_PROFILE: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + RectangularStrokeGeometryTrait { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args RectangularStrokeGeometryTraitArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = RectangularStrokeGeometryTraitBuilder::new(_fbb); + if let Some(x) = args.stroke_width_profile { builder.add_stroke_width_profile(x); } + if let Some(x) = args.stroke_style { builder.add_stroke_style(x); } + if let Some(x) = args.rectangular_stroke_width { builder.add_rectangular_stroke_width(x); } + builder.finish() + } + + + #[inline] + pub fn rectangular_stroke_width(&self) -> Option<&'a RectangularStrokeWidth> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RectangularStrokeGeometryTrait::VT_RECTANGULAR_STROKE_WIDTH, None)} + } + #[inline] + pub fn stroke_style(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(RectangularStrokeGeometryTrait::VT_STROKE_STYLE, None)} + } + #[inline] + pub fn stroke_width_profile(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(RectangularStrokeGeometryTrait::VT_STROKE_WIDTH_PROFILE, None)} + } +} + +impl ::flatbuffers::Verifiable for RectangularStrokeGeometryTrait<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("rectangular_stroke_width", Self::VT_RECTANGULAR_STROKE_WIDTH, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_style", Self::VT_STROKE_STYLE, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_width_profile", Self::VT_STROKE_WIDTH_PROFILE, false)? + .finish(); + Ok(()) + } +} +pub struct RectangularStrokeGeometryTraitArgs<'a> { + pub rectangular_stroke_width: Option<&'a RectangularStrokeWidth>, + pub stroke_style: Option<::flatbuffers::WIPOffset>>, + pub stroke_width_profile: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for RectangularStrokeGeometryTraitArgs<'a> { + #[inline] + fn default() -> Self { + RectangularStrokeGeometryTraitArgs { + rectangular_stroke_width: None, + stroke_style: None, + stroke_width_profile: None, + } + } +} + +pub struct RectangularStrokeGeometryTraitBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> RectangularStrokeGeometryTraitBuilder<'a, 'b, A> { + #[inline] + pub fn add_rectangular_stroke_width(&mut self, rectangular_stroke_width: &RectangularStrokeWidth) { + self.fbb_.push_slot_always::<&RectangularStrokeWidth>(RectangularStrokeGeometryTrait::VT_RECTANGULAR_STROKE_WIDTH, rectangular_stroke_width); + } + #[inline] + pub fn add_stroke_style(&mut self, stroke_style: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(RectangularStrokeGeometryTrait::VT_STROKE_STYLE, stroke_style); + } + #[inline] + pub fn add_stroke_width_profile(&mut self, stroke_width_profile: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(RectangularStrokeGeometryTrait::VT_STROKE_WIDTH_PROFILE, stroke_width_profile); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> RectangularStrokeGeometryTraitBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + RectangularStrokeGeometryTraitBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for RectangularStrokeGeometryTrait<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("RectangularStrokeGeometryTrait"); + ds.field("rectangular_stroke_width", &self.rectangular_stroke_width()); + ds.field("stroke_style", &self.stroke_style()); + ds.field("stroke_width_profile", &self.stroke_width_profile()); + ds.finish() + } +} +pub enum CornerRadiusTraitOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct CornerRadiusTrait<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CornerRadiusTrait<'a> { + type Inner = CornerRadiusTrait<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CornerRadiusTrait<'a> { + pub const VT_CORNER_RADIUS: ::flatbuffers::VOffsetT = 4; + pub const VT_CORNER_SMOOTHING: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CornerRadiusTrait { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CornerRadiusTraitArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CornerRadiusTraitBuilder::new(_fbb); + builder.add_corner_smoothing(args.corner_smoothing); + if let Some(x) = args.corner_radius { builder.add_corner_radius(x); } + builder.finish() + } + + + #[inline] + pub fn corner_radius(&self) -> Option<&'a CGRadius> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CornerRadiusTrait::VT_CORNER_RADIUS, None)} + } + #[inline] + pub fn corner_smoothing(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CornerRadiusTrait::VT_CORNER_SMOOTHING, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for CornerRadiusTrait<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("corner_radius", Self::VT_CORNER_RADIUS, false)? + .visit_field::("corner_smoothing", Self::VT_CORNER_SMOOTHING, false)? + .finish(); + Ok(()) + } +} +pub struct CornerRadiusTraitArgs<'a> { + pub corner_radius: Option<&'a CGRadius>, + pub corner_smoothing: f32, +} +impl<'a> Default for CornerRadiusTraitArgs<'a> { + #[inline] + fn default() -> Self { + CornerRadiusTraitArgs { + corner_radius: None, + corner_smoothing: 0.0, + } + } +} + +pub struct CornerRadiusTraitBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CornerRadiusTraitBuilder<'a, 'b, A> { + #[inline] + pub fn add_corner_radius(&mut self, corner_radius: &CGRadius) { + self.fbb_.push_slot_always::<&CGRadius>(CornerRadiusTrait::VT_CORNER_RADIUS, corner_radius); + } + #[inline] + pub fn add_corner_smoothing(&mut self, corner_smoothing: f32) { + self.fbb_.push_slot::(CornerRadiusTrait::VT_CORNER_SMOOTHING, corner_smoothing, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CornerRadiusTraitBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CornerRadiusTraitBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CornerRadiusTrait<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CornerRadiusTrait"); + ds.field("corner_radius", &self.corner_radius()); + ds.field("corner_smoothing", &self.corner_smoothing()); + ds.finish() + } +} +pub enum RectangularCornerRadiusTraitOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct RectangularCornerRadiusTrait<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for RectangularCornerRadiusTrait<'a> { + type Inner = RectangularCornerRadiusTrait<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> RectangularCornerRadiusTrait<'a> { + pub const VT_RECTANGULAR_CORNER_RADIUS: ::flatbuffers::VOffsetT = 4; + pub const VT_CORNER_SMOOTHING: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + RectangularCornerRadiusTrait { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args RectangularCornerRadiusTraitArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = RectangularCornerRadiusTraitBuilder::new(_fbb); + builder.add_corner_smoothing(args.corner_smoothing); + if let Some(x) = args.rectangular_corner_radius { builder.add_rectangular_corner_radius(x); } + builder.finish() + } + + + #[inline] + pub fn rectangular_corner_radius(&self) -> Option<&'a RectangularCornerRadius> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RectangularCornerRadiusTrait::VT_RECTANGULAR_CORNER_RADIUS, None)} + } + #[inline] + pub fn corner_smoothing(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(RectangularCornerRadiusTrait::VT_CORNER_SMOOTHING, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for RectangularCornerRadiusTrait<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("rectangular_corner_radius", Self::VT_RECTANGULAR_CORNER_RADIUS, false)? + .visit_field::("corner_smoothing", Self::VT_CORNER_SMOOTHING, false)? + .finish(); + Ok(()) + } +} +pub struct RectangularCornerRadiusTraitArgs<'a> { + pub rectangular_corner_radius: Option<&'a RectangularCornerRadius>, + pub corner_smoothing: f32, +} +impl<'a> Default for RectangularCornerRadiusTraitArgs<'a> { + #[inline] + fn default() -> Self { + RectangularCornerRadiusTraitArgs { + rectangular_corner_radius: None, + corner_smoothing: 0.0, + } + } +} + +pub struct RectangularCornerRadiusTraitBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> RectangularCornerRadiusTraitBuilder<'a, 'b, A> { + #[inline] + pub fn add_rectangular_corner_radius(&mut self, rectangular_corner_radius: &RectangularCornerRadius) { + self.fbb_.push_slot_always::<&RectangularCornerRadius>(RectangularCornerRadiusTrait::VT_RECTANGULAR_CORNER_RADIUS, rectangular_corner_radius); + } + #[inline] + pub fn add_corner_smoothing(&mut self, corner_smoothing: f32) { + self.fbb_.push_slot::(RectangularCornerRadiusTrait::VT_CORNER_SMOOTHING, corner_smoothing, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> RectangularCornerRadiusTraitBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + RectangularCornerRadiusTraitBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for RectangularCornerRadiusTrait<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("RectangularCornerRadiusTrait"); + ds.field("rectangular_corner_radius", &self.rectangular_corner_radius()); + ds.field("corner_smoothing", &self.corner_smoothing()); + ds.finish() + } +} +pub enum CanonicalEllipticalShapeRingSectorParametersOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Rust: `EllipticalRingSectorShape` (arc/ring sector data for ellipses) +pub struct CanonicalEllipticalShapeRingSectorParameters<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanonicalEllipticalShapeRingSectorParameters<'a> { + type Inner = CanonicalEllipticalShapeRingSectorParameters<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanonicalEllipticalShapeRingSectorParameters<'a> { + pub const VT_INNER_RADIUS_RATIO: ::flatbuffers::VOffsetT = 4; + pub const VT_START_ANGLE: ::flatbuffers::VOffsetT = 6; + pub const VT_ANGLE: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanonicalEllipticalShapeRingSectorParameters { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CanonicalEllipticalShapeRingSectorParametersArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanonicalEllipticalShapeRingSectorParametersBuilder::new(_fbb); + builder.add_angle(args.angle); + builder.add_start_angle(args.start_angle); + builder.add_inner_radius_ratio(args.inner_radius_ratio); + builder.finish() + } + + + /// Inner radius ratio (0..1) + #[inline] + pub fn inner_radius_ratio(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalEllipticalShapeRingSectorParameters::VT_INNER_RADIUS_RATIO, Some(0.0)).unwrap()} + } + /// Start angle in degrees + #[inline] + pub fn start_angle(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalEllipticalShapeRingSectorParameters::VT_START_ANGLE, Some(0.0)).unwrap()} + } + /// Sweep angle in degrees (end_angle = start_angle + angle) + #[inline] + pub fn angle(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalEllipticalShapeRingSectorParameters::VT_ANGLE, Some(360.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for CanonicalEllipticalShapeRingSectorParameters<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("inner_radius_ratio", Self::VT_INNER_RADIUS_RATIO, false)? + .visit_field::("start_angle", Self::VT_START_ANGLE, false)? + .visit_field::("angle", Self::VT_ANGLE, false)? + .finish(); + Ok(()) + } +} +pub struct CanonicalEllipticalShapeRingSectorParametersArgs { + pub inner_radius_ratio: f32, + pub start_angle: f32, + pub angle: f32, +} +impl<'a> Default for CanonicalEllipticalShapeRingSectorParametersArgs { + #[inline] + fn default() -> Self { + CanonicalEllipticalShapeRingSectorParametersArgs { + inner_radius_ratio: 0.0, + start_angle: 0.0, + angle: 360.0, + } + } +} + +pub struct CanonicalEllipticalShapeRingSectorParametersBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanonicalEllipticalShapeRingSectorParametersBuilder<'a, 'b, A> { + #[inline] + pub fn add_inner_radius_ratio(&mut self, inner_radius_ratio: f32) { + self.fbb_.push_slot::(CanonicalEllipticalShapeRingSectorParameters::VT_INNER_RADIUS_RATIO, inner_radius_ratio, 0.0); + } + #[inline] + pub fn add_start_angle(&mut self, start_angle: f32) { + self.fbb_.push_slot::(CanonicalEllipticalShapeRingSectorParameters::VT_START_ANGLE, start_angle, 0.0); + } + #[inline] + pub fn add_angle(&mut self, angle: f32) { + self.fbb_.push_slot::(CanonicalEllipticalShapeRingSectorParameters::VT_ANGLE, angle, 360.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanonicalEllipticalShapeRingSectorParametersBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanonicalEllipticalShapeRingSectorParametersBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanonicalEllipticalShapeRingSectorParameters<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanonicalEllipticalShapeRingSectorParameters"); + ds.field("inner_radius_ratio", &self.inner_radius_ratio()); + ds.field("start_angle", &self.start_angle()); + ds.field("angle", &self.angle()); + ds.finish() + } +} +pub enum CanonicalShapeRectangularOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// rectangle, rounded rectangle +pub struct CanonicalShapeRectangular<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanonicalShapeRectangular<'a> { + type Inner = CanonicalShapeRectangular<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanonicalShapeRectangular<'a> { + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanonicalShapeRectangular { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + _args: &'args CanonicalShapeRectangularArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanonicalShapeRectangularBuilder::new(_fbb); + builder.finish() + } + +} + +impl ::flatbuffers::Verifiable for CanonicalShapeRectangular<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .finish(); + Ok(()) + } +} +pub struct CanonicalShapeRectangularArgs { +} +impl<'a> Default for CanonicalShapeRectangularArgs { + #[inline] + fn default() -> Self { + CanonicalShapeRectangularArgs { + } + } +} + +pub struct CanonicalShapeRectangularBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanonicalShapeRectangularBuilder<'a, 'b, A> { + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanonicalShapeRectangularBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanonicalShapeRectangularBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanonicalShapeRectangular<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanonicalShapeRectangular"); + ds.finish() + } +} +pub enum CanonicalShapeEllipticalOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// circle, ellipse by default and also can represent ring, sector, ring+sector +pub struct CanonicalShapeElliptical<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanonicalShapeElliptical<'a> { + type Inner = CanonicalShapeElliptical<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanonicalShapeElliptical<'a> { + pub const VT_RING_SECTOR_DATA: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanonicalShapeElliptical { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CanonicalShapeEllipticalArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanonicalShapeEllipticalBuilder::new(_fbb); + if let Some(x) = args.ring_sector_data { builder.add_ring_sector_data(x); } + builder.finish() + } + + + #[inline] + pub fn ring_sector_data(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(CanonicalShapeElliptical::VT_RING_SECTOR_DATA, None)} + } +} + +impl ::flatbuffers::Verifiable for CanonicalShapeElliptical<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("ring_sector_data", Self::VT_RING_SECTOR_DATA, false)? + .finish(); + Ok(()) + } +} +pub struct CanonicalShapeEllipticalArgs<'a> { + pub ring_sector_data: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for CanonicalShapeEllipticalArgs<'a> { + #[inline] + fn default() -> Self { + CanonicalShapeEllipticalArgs { + ring_sector_data: None, + } + } +} + +pub struct CanonicalShapeEllipticalBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanonicalShapeEllipticalBuilder<'a, 'b, A> { + #[inline] + pub fn add_ring_sector_data(&mut self, ring_sector_data: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(CanonicalShapeElliptical::VT_RING_SECTOR_DATA, ring_sector_data); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanonicalShapeEllipticalBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanonicalShapeEllipticalBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanonicalShapeElliptical<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanonicalShapeElliptical"); + ds.field("ring_sector_data", &self.ring_sector_data()); + ds.finish() + } +} +pub enum CanonicalShapePointsPolygonOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// polygon (any shapes defined by points) +pub struct CanonicalShapePointsPolygon<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanonicalShapePointsPolygon<'a> { + type Inner = CanonicalShapePointsPolygon<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanonicalShapePointsPolygon<'a> { + pub const VT_POINTS: ::flatbuffers::VOffsetT = 4; + pub const VT_FILL_RULE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanonicalShapePointsPolygon { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CanonicalShapePointsPolygonArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanonicalShapePointsPolygonBuilder::new(_fbb); + if let Some(x) = args.points { builder.add_points(x); } + builder.add_fill_rule(args.fill_rule); + builder.finish() + } + + + #[inline] + pub fn points(&self) -> Option<::flatbuffers::Vector<'a, CGPoint>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, CGPoint>>>(CanonicalShapePointsPolygon::VT_POINTS, None)} + } + #[inline] + pub fn fill_rule(&self) -> FillRule { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalShapePointsPolygon::VT_FILL_RULE, Some(FillRule::NonZero)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for CanonicalShapePointsPolygon<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, CGPoint>>>("points", Self::VT_POINTS, false)? + .visit_field::("fill_rule", Self::VT_FILL_RULE, false)? + .finish(); + Ok(()) + } +} +pub struct CanonicalShapePointsPolygonArgs<'a> { + pub points: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, CGPoint>>>, + pub fill_rule: FillRule, +} +impl<'a> Default for CanonicalShapePointsPolygonArgs<'a> { + #[inline] + fn default() -> Self { + CanonicalShapePointsPolygonArgs { + points: None, + fill_rule: FillRule::NonZero, + } + } +} + +pub struct CanonicalShapePointsPolygonBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanonicalShapePointsPolygonBuilder<'a, 'b, A> { + #[inline] + pub fn add_points(&mut self, points: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , CGPoint>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(CanonicalShapePointsPolygon::VT_POINTS, points); + } + #[inline] + pub fn add_fill_rule(&mut self, fill_rule: FillRule) { + self.fbb_.push_slot::(CanonicalShapePointsPolygon::VT_FILL_RULE, fill_rule, FillRule::NonZero); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanonicalShapePointsPolygonBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanonicalShapePointsPolygonBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanonicalShapePointsPolygon<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanonicalShapePointsPolygon"); + ds.field("points", &self.points()); + ds.field("fill_rule", &self.fill_rule()); + ds.finish() + } +} +pub enum CanonicalShapeRegularPolygonOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// regular polygon +pub struct CanonicalShapeRegularPolygon<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanonicalShapeRegularPolygon<'a> { + type Inner = CanonicalShapeRegularPolygon<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanonicalShapeRegularPolygon<'a> { + pub const VT_POINT_COUNT: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanonicalShapeRegularPolygon { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CanonicalShapeRegularPolygonArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanonicalShapeRegularPolygonBuilder::new(_fbb); + builder.add_point_count(args.point_count); + builder.finish() + } + + + #[inline] + pub fn point_count(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalShapeRegularPolygon::VT_POINT_COUNT, Some(0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for CanonicalShapeRegularPolygon<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("point_count", Self::VT_POINT_COUNT, false)? + .finish(); + Ok(()) + } +} +pub struct CanonicalShapeRegularPolygonArgs { + pub point_count: u32, +} +impl<'a> Default for CanonicalShapeRegularPolygonArgs { + #[inline] + fn default() -> Self { + CanonicalShapeRegularPolygonArgs { + point_count: 0, + } + } +} + +pub struct CanonicalShapeRegularPolygonBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanonicalShapeRegularPolygonBuilder<'a, 'b, A> { + #[inline] + pub fn add_point_count(&mut self, point_count: u32) { + self.fbb_.push_slot::(CanonicalShapeRegularPolygon::VT_POINT_COUNT, point_count, 0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanonicalShapeRegularPolygonBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanonicalShapeRegularPolygonBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanonicalShapeRegularPolygon<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanonicalShapeRegularPolygon"); + ds.field("point_count", &self.point_count()); + ds.finish() + } +} +pub enum CanonicalShapeRegularStarPolygonOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// regular star-ish polygon +pub struct CanonicalShapeRegularStarPolygon<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanonicalShapeRegularStarPolygon<'a> { + type Inner = CanonicalShapeRegularStarPolygon<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanonicalShapeRegularStarPolygon<'a> { + pub const VT_POINT_COUNT: ::flatbuffers::VOffsetT = 4; + pub const VT_INNER_RADIUS_RATIO: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanonicalShapeRegularStarPolygon { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CanonicalShapeRegularStarPolygonArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanonicalShapeRegularStarPolygonBuilder::new(_fbb); + builder.add_inner_radius_ratio(args.inner_radius_ratio); + builder.add_point_count(args.point_count); + builder.finish() + } + + + #[inline] + pub fn point_count(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalShapeRegularStarPolygon::VT_POINT_COUNT, Some(0)).unwrap()} + } + #[inline] + pub fn inner_radius_ratio(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalShapeRegularStarPolygon::VT_INNER_RADIUS_RATIO, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for CanonicalShapeRegularStarPolygon<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("point_count", Self::VT_POINT_COUNT, false)? + .visit_field::("inner_radius_ratio", Self::VT_INNER_RADIUS_RATIO, false)? + .finish(); + Ok(()) + } +} +pub struct CanonicalShapeRegularStarPolygonArgs { + pub point_count: u32, + pub inner_radius_ratio: f32, +} +impl<'a> Default for CanonicalShapeRegularStarPolygonArgs { + #[inline] + fn default() -> Self { + CanonicalShapeRegularStarPolygonArgs { + point_count: 0, + inner_radius_ratio: 0.0, + } + } +} + +pub struct CanonicalShapeRegularStarPolygonBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanonicalShapeRegularStarPolygonBuilder<'a, 'b, A> { + #[inline] + pub fn add_point_count(&mut self, point_count: u32) { + self.fbb_.push_slot::(CanonicalShapeRegularStarPolygon::VT_POINT_COUNT, point_count, 0); + } + #[inline] + pub fn add_inner_radius_ratio(&mut self, inner_radius_ratio: f32) { + self.fbb_.push_slot::(CanonicalShapeRegularStarPolygon::VT_INNER_RADIUS_RATIO, inner_radius_ratio, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanonicalShapeRegularStarPolygonBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanonicalShapeRegularStarPolygonBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanonicalShapeRegularStarPolygon<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanonicalShapeRegularStarPolygon"); + ds.field("point_count", &self.point_count()); + ds.field("inner_radius_ratio", &self.inner_radius_ratio()); + ds.finish() + } +} +pub enum CanonicalShapePathOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// path +pub struct CanonicalShapePath<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanonicalShapePath<'a> { + type Inner = CanonicalShapePath<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanonicalShapePath<'a> { + pub const VT_D: ::flatbuffers::VOffsetT = 4; + pub const VT_FILL_RULE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanonicalShapePath { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CanonicalShapePathArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanonicalShapePathBuilder::new(_fbb); + if let Some(x) = args.d { builder.add_d(x); } + builder.add_fill_rule(args.fill_rule); + builder.finish() + } + + + #[inline] + pub fn d(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(CanonicalShapePath::VT_D, None)} + } + #[inline] + pub fn fill_rule(&self) -> FillRule { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CanonicalShapePath::VT_FILL_RULE, Some(FillRule::NonZero)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for CanonicalShapePath<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("d", Self::VT_D, false)? + .visit_field::("fill_rule", Self::VT_FILL_RULE, false)? + .finish(); + Ok(()) + } +} +pub struct CanonicalShapePathArgs<'a> { + pub d: Option<::flatbuffers::WIPOffset<&'a str>>, + pub fill_rule: FillRule, +} +impl<'a> Default for CanonicalShapePathArgs<'a> { + #[inline] + fn default() -> Self { + CanonicalShapePathArgs { + d: None, + fill_rule: FillRule::NonZero, + } + } +} + +pub struct CanonicalShapePathBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanonicalShapePathBuilder<'a, 'b, A> { + #[inline] + pub fn add_d(&mut self, d: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(CanonicalShapePath::VT_D, d); + } + #[inline] + pub fn add_fill_rule(&mut self, fill_rule: FillRule) { + self.fbb_.push_slot::(CanonicalShapePath::VT_FILL_RULE, fill_rule, FillRule::NonZero); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanonicalShapePathBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanonicalShapePathBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanonicalShapePath<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanonicalShapePath"); + ds.field("d", &self.d()); + ds.field("fill_rule", &self.fill_rule()); + ds.finish() + } +} +pub enum LayoutDimensionValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Dimension value. (width/height) +/// +/// Canonical mapping: +/// - TS `number` or `{type:"length", unit:"px"}` -> `Px` +/// - TS `{type:"percentage"}` -> `Percent` +pub struct LayoutDimensionValue<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayoutDimensionValue<'a> { + type Inner = LayoutDimensionValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayoutDimensionValue<'a> { + pub const VT_UNIT: ::flatbuffers::VOffsetT = 4; + pub const VT_VALUE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayoutDimensionValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayoutDimensionValueArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayoutDimensionValueBuilder::new(_fbb); + if let Some(x) = args.value { builder.add_value(x); } + builder.add_unit(args.unit); + builder.finish() + } + + + #[inline] + pub fn unit(&self) -> LayoutDimensionUnit { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutDimensionValue::VT_UNIT, Some(LayoutDimensionUnit::LengthPx)).unwrap()} + } + #[inline] + pub fn value(&self) -> Option { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutDimensionValue::VT_VALUE, None)} + } +} + +impl ::flatbuffers::Verifiable for LayoutDimensionValue<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("unit", Self::VT_UNIT, false)? + .visit_field::("value", Self::VT_VALUE, false)? + .finish(); + Ok(()) + } +} +pub struct LayoutDimensionValueArgs { + pub unit: LayoutDimensionUnit, + pub value: Option, +} +impl<'a> Default for LayoutDimensionValueArgs { + #[inline] + fn default() -> Self { + LayoutDimensionValueArgs { + unit: LayoutDimensionUnit::LengthPx, + value: None, + } + } +} + +pub struct LayoutDimensionValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayoutDimensionValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_unit(&mut self, unit: LayoutDimensionUnit) { + self.fbb_.push_slot::(LayoutDimensionValue::VT_UNIT, unit, LayoutDimensionUnit::LengthPx); + } + #[inline] + pub fn add_value(&mut self, value: f32) { + self.fbb_.push_slot_always::(LayoutDimensionValue::VT_VALUE, value); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayoutDimensionValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayoutDimensionValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayoutDimensionValue<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayoutDimensionValue"); + ds.field("unit", &self.unit()); + ds.field("value", &self.value()); + ds.finish() + } +} +pub enum LayoutDimensionStyleOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// `LayoutDimensionStyle {}` +pub struct LayoutDimensionStyle<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayoutDimensionStyle<'a> { + type Inner = LayoutDimensionStyle<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayoutDimensionStyle<'a> { + pub const VT_LAYOUT_TARGET_WIDTH: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYOUT_TARGET_HEIGHT: ::flatbuffers::VOffsetT = 6; + pub const VT_LAYOUT_TARGET_ASPECT_RATIO: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayoutDimensionStyle { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayoutDimensionStyleArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayoutDimensionStyleBuilder::new(_fbb); + if let Some(x) = args.layout_target_aspect_ratio { builder.add_layout_target_aspect_ratio(x); } + if let Some(x) = args.layout_target_height { builder.add_layout_target_height(x); } + if let Some(x) = args.layout_target_width { builder.add_layout_target_width(x); } + builder.finish() + } + + + /// unset = auto + #[inline] + pub fn layout_target_width(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutDimensionStyle::VT_LAYOUT_TARGET_WIDTH, None)} + } + /// unset = auto + #[inline] + pub fn layout_target_height(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutDimensionStyle::VT_LAYOUT_TARGET_HEIGHT, None)} + } + /// Preferred layout aspect ratio. + /// + /// Represents a proportional relationship between width and height expressed + /// as a ratio pair (e.g. `(16, 9)`, `(4, 3)`, `(1, 1)`). + /// + /// (0, 0) represents and is handled as "unset". + /// + /// Notes: + /// - This does not define geometry by itself. It is a sizing preference that + /// layout engines may consult when resolving under-specified dimensions + /// (e.g. when either width or height is "auto") or when proportional sizing + /// is explicitly required by the layout model. + /// - When both width and height are definitively specified, this should have + /// no effect and must not override explicit dimensions. + /// - Layout engines may ignore this if aspect-ratio-aware sizing is not supported. + /// + /// Encoding: + /// - Stored as a tuple (width, height) for parity with TS `layout_target_aspect_ratio?: [number, number]`. + #[inline] + pub fn layout_target_aspect_ratio(&self) -> Option<&'a CGSize> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutDimensionStyle::VT_LAYOUT_TARGET_ASPECT_RATIO, None)} + } +} + +impl ::flatbuffers::Verifiable for LayoutDimensionStyle<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layout_target_width", Self::VT_LAYOUT_TARGET_WIDTH, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layout_target_height", Self::VT_LAYOUT_TARGET_HEIGHT, false)? + .visit_field::("layout_target_aspect_ratio", Self::VT_LAYOUT_TARGET_ASPECT_RATIO, false)? + .finish(); + Ok(()) + } +} +pub struct LayoutDimensionStyleArgs<'a> { + pub layout_target_width: Option<::flatbuffers::WIPOffset>>, + pub layout_target_height: Option<::flatbuffers::WIPOffset>>, + pub layout_target_aspect_ratio: Option<&'a CGSize>, +} +impl<'a> Default for LayoutDimensionStyleArgs<'a> { + #[inline] + fn default() -> Self { + LayoutDimensionStyleArgs { + layout_target_width: None, + layout_target_height: None, + layout_target_aspect_ratio: None, + } + } +} + +pub struct LayoutDimensionStyleBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayoutDimensionStyleBuilder<'a, 'b, A> { + #[inline] + pub fn add_layout_target_width(&mut self, layout_target_width: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutDimensionStyle::VT_LAYOUT_TARGET_WIDTH, layout_target_width); + } + #[inline] + pub fn add_layout_target_height(&mut self, layout_target_height: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutDimensionStyle::VT_LAYOUT_TARGET_HEIGHT, layout_target_height); + } + #[inline] + pub fn add_layout_target_aspect_ratio(&mut self, layout_target_aspect_ratio: &CGSize) { + self.fbb_.push_slot_always::<&CGSize>(LayoutDimensionStyle::VT_LAYOUT_TARGET_ASPECT_RATIO, layout_target_aspect_ratio); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayoutDimensionStyleBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayoutDimensionStyleBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayoutDimensionStyle<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayoutDimensionStyle"); + ds.field("layout_target_width", &self.layout_target_width()); + ds.field("layout_target_height", &self.layout_target_height()); + ds.field("layout_target_aspect_ratio", &self.layout_target_aspect_ratio()); + ds.finish() + } +} +pub enum LayoutPositioningCartesianOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// x,y static positioning model +pub struct LayoutPositioningCartesian<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayoutPositioningCartesian<'a> { + type Inner = LayoutPositioningCartesian<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayoutPositioningCartesian<'a> { + pub const VT_X: ::flatbuffers::VOffsetT = 4; + pub const VT_Y: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayoutPositioningCartesian { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayoutPositioningCartesianArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayoutPositioningCartesianBuilder::new(_fbb); + builder.add_y(args.y); + builder.add_x(args.x); + builder.finish() + } + + + #[inline] + pub fn x(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutPositioningCartesian::VT_X, Some(0.0)).unwrap()} + } + #[inline] + pub fn y(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutPositioningCartesian::VT_Y, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for LayoutPositioningCartesian<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("x", Self::VT_X, false)? + .visit_field::("y", Self::VT_Y, false)? + .finish(); + Ok(()) + } +} +pub struct LayoutPositioningCartesianArgs { + pub x: f32, + pub y: f32, +} +impl<'a> Default for LayoutPositioningCartesianArgs { + #[inline] + fn default() -> Self { + LayoutPositioningCartesianArgs { + x: 0.0, + y: 0.0, + } + } +} + +pub struct LayoutPositioningCartesianBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayoutPositioningCartesianBuilder<'a, 'b, A> { + #[inline] + pub fn add_x(&mut self, x: f32) { + self.fbb_.push_slot::(LayoutPositioningCartesian::VT_X, x, 0.0); + } + #[inline] + pub fn add_y(&mut self, y: f32) { + self.fbb_.push_slot::(LayoutPositioningCartesian::VT_Y, y, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayoutPositioningCartesianBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayoutPositioningCartesianBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayoutPositioningCartesian<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayoutPositioningCartesian"); + ds.field("x", &self.x()); + ds.field("y", &self.y()); + ds.finish() + } +} +pub enum PositioningSideOffsetValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct PositioningSideOffsetValue<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for PositioningSideOffsetValue<'a> { + type Inner = PositioningSideOffsetValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> PositioningSideOffsetValue<'a> { + pub const VT_KIND: ::flatbuffers::VOffsetT = 4; + pub const VT_VALUE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + PositioningSideOffsetValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args PositioningSideOffsetValueArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = PositioningSideOffsetValueBuilder::new(_fbb); + if let Some(x) = args.value { builder.add_value(x); } + builder.add_kind(args.kind); + builder.finish() + } + + + #[inline] + pub fn kind(&self) -> PositioningSideOffsetKind { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(PositioningSideOffsetValue::VT_KIND, Some(PositioningSideOffsetKind::Px)).unwrap()} + } + #[inline] + pub fn value(&self) -> Option { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(PositioningSideOffsetValue::VT_VALUE, None)} + } +} + +impl ::flatbuffers::Verifiable for PositioningSideOffsetValue<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("kind", Self::VT_KIND, false)? + .visit_field::("value", Self::VT_VALUE, false)? + .finish(); + Ok(()) + } +} +pub struct PositioningSideOffsetValueArgs { + pub kind: PositioningSideOffsetKind, + pub value: Option, +} +impl<'a> Default for PositioningSideOffsetValueArgs { + #[inline] + fn default() -> Self { + PositioningSideOffsetValueArgs { + kind: PositioningSideOffsetKind::Px, + value: None, + } + } +} + +pub struct PositioningSideOffsetValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> PositioningSideOffsetValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_kind(&mut self, kind: PositioningSideOffsetKind) { + self.fbb_.push_slot::(PositioningSideOffsetValue::VT_KIND, kind, PositioningSideOffsetKind::Px); + } + #[inline] + pub fn add_value(&mut self, value: f32) { + self.fbb_.push_slot_always::(PositioningSideOffsetValue::VT_VALUE, value); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> PositioningSideOffsetValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + PositioningSideOffsetValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for PositioningSideOffsetValue<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("PositioningSideOffsetValue"); + ds.field("kind", &self.kind()); + ds.field("value", &self.value()); + ds.finish() + } +} +pub enum LayoutPositioningInsetOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// left, top, right, bottom positioning model (css standard) +pub struct LayoutPositioningInset<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayoutPositioningInset<'a> { + type Inner = LayoutPositioningInset<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayoutPositioningInset<'a> { + pub const VT_TOP: ::flatbuffers::VOffsetT = 4; + pub const VT_RIGHT: ::flatbuffers::VOffsetT = 6; + pub const VT_BOTTOM: ::flatbuffers::VOffsetT = 8; + pub const VT_LEFT: ::flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayoutPositioningInset { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayoutPositioningInsetArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayoutPositioningInsetBuilder::new(_fbb); + if let Some(x) = args.left { builder.add_left(x); } + if let Some(x) = args.bottom { builder.add_bottom(x); } + if let Some(x) = args.right { builder.add_right(x); } + if let Some(x) = args.top { builder.add_top(x); } + builder.finish() + } + + + #[inline] + pub fn top(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutPositioningInset::VT_TOP, None)} + } + #[inline] + pub fn right(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutPositioningInset::VT_RIGHT, None)} + } + #[inline] + pub fn bottom(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutPositioningInset::VT_BOTTOM, None)} + } + #[inline] + pub fn left(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutPositioningInset::VT_LEFT, None)} + } +} + +impl ::flatbuffers::Verifiable for LayoutPositioningInset<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("top", Self::VT_TOP, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("right", Self::VT_RIGHT, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("bottom", Self::VT_BOTTOM, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("left", Self::VT_LEFT, false)? + .finish(); + Ok(()) + } +} +pub struct LayoutPositioningInsetArgs<'a> { + pub top: Option<::flatbuffers::WIPOffset>>, + pub right: Option<::flatbuffers::WIPOffset>>, + pub bottom: Option<::flatbuffers::WIPOffset>>, + pub left: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for LayoutPositioningInsetArgs<'a> { + #[inline] + fn default() -> Self { + LayoutPositioningInsetArgs { + top: None, + right: None, + bottom: None, + left: None, + } + } +} + +pub struct LayoutPositioningInsetBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayoutPositioningInsetBuilder<'a, 'b, A> { + #[inline] + pub fn add_top(&mut self, top: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutPositioningInset::VT_TOP, top); + } + #[inline] + pub fn add_right(&mut self, right: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutPositioningInset::VT_RIGHT, right); + } + #[inline] + pub fn add_bottom(&mut self, bottom: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutPositioningInset::VT_BOTTOM, bottom); + } + #[inline] + pub fn add_left(&mut self, left: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutPositioningInset::VT_LEFT, left); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayoutPositioningInsetBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayoutPositioningInsetBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayoutPositioningInset<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayoutPositioningInset"); + ds.field("top", &self.top()); + ds.field("right", &self.right()); + ds.field("bottom", &self.bottom()); + ds.field("left", &self.left()); + ds.finish() + } +} +pub enum LayoutContainerStyleOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct LayoutContainerStyle<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayoutContainerStyle<'a> { + type Inner = LayoutContainerStyle<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayoutContainerStyle<'a> { + pub const VT_LAYOUT_MODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYOUT_DIRECTION: ::flatbuffers::VOffsetT = 6; + pub const VT_LAYOUT_WRAP: ::flatbuffers::VOffsetT = 8; + pub const VT_LAYOUT_MAIN_AXIS_ALIGNMENT: ::flatbuffers::VOffsetT = 10; + pub const VT_LAYOUT_CROSS_AXIS_ALIGNMENT: ::flatbuffers::VOffsetT = 12; + pub const VT_LAYOUT_PADDING: ::flatbuffers::VOffsetT = 14; + pub const VT_LAYOUT_MAIN_AXIS_GAP: ::flatbuffers::VOffsetT = 16; + pub const VT_LAYOUT_CROSS_AXIS_GAP: ::flatbuffers::VOffsetT = 18; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayoutContainerStyle { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayoutContainerStyleArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayoutContainerStyleBuilder::new(_fbb); + builder.add_layout_cross_axis_gap(args.layout_cross_axis_gap); + builder.add_layout_main_axis_gap(args.layout_main_axis_gap); + if let Some(x) = args.layout_padding { builder.add_layout_padding(x); } + builder.add_layout_cross_axis_alignment(args.layout_cross_axis_alignment); + builder.add_layout_main_axis_alignment(args.layout_main_axis_alignment); + builder.add_layout_wrap(args.layout_wrap); + builder.add_layout_direction(args.layout_direction); + builder.add_layout_mode(args.layout_mode); + builder.finish() + } + + + #[inline] + pub fn layout_mode(&self) -> LayoutMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_MODE, Some(LayoutMode::Normal)).unwrap()} + } + #[inline] + pub fn layout_direction(&self) -> Axis { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_DIRECTION, Some(Axis::Horizontal)).unwrap()} + } + #[inline] + pub fn layout_wrap(&self) -> LayoutWrap { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_WRAP, Some(LayoutWrap::None)).unwrap()} + } + #[inline] + pub fn layout_main_axis_alignment(&self) -> MainAxisAlignment { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_MAIN_AXIS_ALIGNMENT, Some(MainAxisAlignment::None)).unwrap()} + } + #[inline] + pub fn layout_cross_axis_alignment(&self) -> CrossAxisAlignment { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_CROSS_AXIS_ALIGNMENT, Some(CrossAxisAlignment::None)).unwrap()} + } + #[inline] + pub fn layout_padding(&self) -> Option<&'a EdgeInsets> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_PADDING, None)} + } + #[inline] + pub fn layout_main_axis_gap(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_MAIN_AXIS_GAP, Some(0.0)).unwrap()} + } + #[inline] + pub fn layout_cross_axis_gap(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutContainerStyle::VT_LAYOUT_CROSS_AXIS_GAP, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for LayoutContainerStyle<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("layout_mode", Self::VT_LAYOUT_MODE, false)? + .visit_field::("layout_direction", Self::VT_LAYOUT_DIRECTION, false)? + .visit_field::("layout_wrap", Self::VT_LAYOUT_WRAP, false)? + .visit_field::("layout_main_axis_alignment", Self::VT_LAYOUT_MAIN_AXIS_ALIGNMENT, false)? + .visit_field::("layout_cross_axis_alignment", Self::VT_LAYOUT_CROSS_AXIS_ALIGNMENT, false)? + .visit_field::("layout_padding", Self::VT_LAYOUT_PADDING, false)? + .visit_field::("layout_main_axis_gap", Self::VT_LAYOUT_MAIN_AXIS_GAP, false)? + .visit_field::("layout_cross_axis_gap", Self::VT_LAYOUT_CROSS_AXIS_GAP, false)? + .finish(); + Ok(()) + } +} +pub struct LayoutContainerStyleArgs<'a> { + pub layout_mode: LayoutMode, + pub layout_direction: Axis, + pub layout_wrap: LayoutWrap, + pub layout_main_axis_alignment: MainAxisAlignment, + pub layout_cross_axis_alignment: CrossAxisAlignment, + pub layout_padding: Option<&'a EdgeInsets>, + pub layout_main_axis_gap: f32, + pub layout_cross_axis_gap: f32, +} +impl<'a> Default for LayoutContainerStyleArgs<'a> { + #[inline] + fn default() -> Self { + LayoutContainerStyleArgs { + layout_mode: LayoutMode::Normal, + layout_direction: Axis::Horizontal, + layout_wrap: LayoutWrap::None, + layout_main_axis_alignment: MainAxisAlignment::None, + layout_cross_axis_alignment: CrossAxisAlignment::None, + layout_padding: None, + layout_main_axis_gap: 0.0, + layout_cross_axis_gap: 0.0, + } + } +} + +pub struct LayoutContainerStyleBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayoutContainerStyleBuilder<'a, 'b, A> { + #[inline] + pub fn add_layout_mode(&mut self, layout_mode: LayoutMode) { + self.fbb_.push_slot::(LayoutContainerStyle::VT_LAYOUT_MODE, layout_mode, LayoutMode::Normal); + } + #[inline] + pub fn add_layout_direction(&mut self, layout_direction: Axis) { + self.fbb_.push_slot::(LayoutContainerStyle::VT_LAYOUT_DIRECTION, layout_direction, Axis::Horizontal); + } + #[inline] + pub fn add_layout_wrap(&mut self, layout_wrap: LayoutWrap) { + self.fbb_.push_slot::(LayoutContainerStyle::VT_LAYOUT_WRAP, layout_wrap, LayoutWrap::None); + } + #[inline] + pub fn add_layout_main_axis_alignment(&mut self, layout_main_axis_alignment: MainAxisAlignment) { + self.fbb_.push_slot::(LayoutContainerStyle::VT_LAYOUT_MAIN_AXIS_ALIGNMENT, layout_main_axis_alignment, MainAxisAlignment::None); + } + #[inline] + pub fn add_layout_cross_axis_alignment(&mut self, layout_cross_axis_alignment: CrossAxisAlignment) { + self.fbb_.push_slot::(LayoutContainerStyle::VT_LAYOUT_CROSS_AXIS_ALIGNMENT, layout_cross_axis_alignment, CrossAxisAlignment::None); + } + #[inline] + pub fn add_layout_padding(&mut self, layout_padding: &EdgeInsets) { + self.fbb_.push_slot_always::<&EdgeInsets>(LayoutContainerStyle::VT_LAYOUT_PADDING, layout_padding); + } + #[inline] + pub fn add_layout_main_axis_gap(&mut self, layout_main_axis_gap: f32) { + self.fbb_.push_slot::(LayoutContainerStyle::VT_LAYOUT_MAIN_AXIS_GAP, layout_main_axis_gap, 0.0); + } + #[inline] + pub fn add_layout_cross_axis_gap(&mut self, layout_cross_axis_gap: f32) { + self.fbb_.push_slot::(LayoutContainerStyle::VT_LAYOUT_CROSS_AXIS_GAP, layout_cross_axis_gap, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayoutContainerStyleBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayoutContainerStyleBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayoutContainerStyle<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayoutContainerStyle"); + ds.field("layout_mode", &self.layout_mode()); + ds.field("layout_direction", &self.layout_direction()); + ds.field("layout_wrap", &self.layout_wrap()); + ds.field("layout_main_axis_alignment", &self.layout_main_axis_alignment()); + ds.field("layout_cross_axis_alignment", &self.layout_cross_axis_alignment()); + ds.field("layout_padding", &self.layout_padding()); + ds.field("layout_main_axis_gap", &self.layout_main_axis_gap()); + ds.field("layout_cross_axis_gap", &self.layout_cross_axis_gap()); + ds.finish() + } +} +pub enum LayoutChildStyleOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct LayoutChildStyle<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayoutChildStyle<'a> { + type Inner = LayoutChildStyle<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayoutChildStyle<'a> { + pub const VT_LAYOUT_POSITIONING: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYOUT_GROW: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayoutChildStyle { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayoutChildStyleArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayoutChildStyleBuilder::new(_fbb); + builder.add_layout_grow(args.layout_grow); + builder.add_layout_positioning(args.layout_positioning); + builder.finish() + } + + + #[inline] + pub fn layout_positioning(&self) -> LayoutPositioning { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutChildStyle::VT_LAYOUT_POSITIONING, Some(LayoutPositioning::Auto)).unwrap()} + } + #[inline] + pub fn layout_grow(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutChildStyle::VT_LAYOUT_GROW, Some(0.0)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for LayoutChildStyle<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("layout_positioning", Self::VT_LAYOUT_POSITIONING, false)? + .visit_field::("layout_grow", Self::VT_LAYOUT_GROW, false)? + .finish(); + Ok(()) + } +} +pub struct LayoutChildStyleArgs { + pub layout_positioning: LayoutPositioning, + pub layout_grow: f32, +} +impl<'a> Default for LayoutChildStyleArgs { + #[inline] + fn default() -> Self { + LayoutChildStyleArgs { + layout_positioning: LayoutPositioning::Auto, + layout_grow: 0.0, + } + } +} + +pub struct LayoutChildStyleBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayoutChildStyleBuilder<'a, 'b, A> { + #[inline] + pub fn add_layout_positioning(&mut self, layout_positioning: LayoutPositioning) { + self.fbb_.push_slot::(LayoutChildStyle::VT_LAYOUT_POSITIONING, layout_positioning, LayoutPositioning::Auto); + } + #[inline] + pub fn add_layout_grow(&mut self, layout_grow: f32) { + self.fbb_.push_slot::(LayoutChildStyle::VT_LAYOUT_GROW, layout_grow, 0.0); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayoutChildStyleBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayoutChildStyleBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayoutChildStyle<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayoutChildStyle"); + ds.field("layout_positioning", &self.layout_positioning()); + ds.field("layout_grow", &self.layout_grow()); + ds.finish() + } +} +pub enum LayoutStyleOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct LayoutStyle<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayoutStyle<'a> { + type Inner = LayoutStyle<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayoutStyle<'a> { + pub const VT_LAYOUT_POSITION_TYPE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYOUT_POSITION: ::flatbuffers::VOffsetT = 6; + pub const VT_LAYOUT_DIMENSIONS: ::flatbuffers::VOffsetT = 8; + pub const VT_LAYOUT_CONTAINER: ::flatbuffers::VOffsetT = 10; + pub const VT_LAYOUT_CHILD: ::flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayoutStyle { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayoutStyleArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayoutStyleBuilder::new(_fbb); + if let Some(x) = args.layout_child { builder.add_layout_child(x); } + if let Some(x) = args.layout_container { builder.add_layout_container(x); } + if let Some(x) = args.layout_dimensions { builder.add_layout_dimensions(x); } + if let Some(x) = args.layout_position { builder.add_layout_position(x); } + builder.add_layout_position_type(args.layout_position_type); + builder.finish() + } + + + #[inline] + pub fn layout_position_type(&self) -> LayoutPositioningBasis { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayoutStyle::VT_LAYOUT_POSITION_TYPE, Some(LayoutPositioningBasis::NONE)).unwrap()} + } + #[inline] + pub fn layout_position(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(LayoutStyle::VT_LAYOUT_POSITION, None)} + } + #[inline] + pub fn layout_dimensions(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutStyle::VT_LAYOUT_DIMENSIONS, None)} + } + #[inline] + pub fn layout_container(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutStyle::VT_LAYOUT_CONTAINER, None)} + } + #[inline] + pub fn layout_child(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayoutStyle::VT_LAYOUT_CHILD, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn layout_position_as_layout_positioning_cartesian(&self) -> Option> { + if self.layout_position_type() == LayoutPositioningBasis::LayoutPositioningCartesian { + self.layout_position().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LayoutPositioningCartesian::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn layout_position_as_layout_positioning_inset(&self) -> Option> { + if self.layout_position_type() == LayoutPositioningBasis::LayoutPositioningInset { + self.layout_position().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LayoutPositioningInset::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for LayoutStyle<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_union::("layout_position_type", Self::VT_LAYOUT_POSITION_TYPE, "layout_position", Self::VT_LAYOUT_POSITION, false, |key, v, pos| { + match key { + LayoutPositioningBasis::LayoutPositioningCartesian => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("LayoutPositioningBasis::LayoutPositioningCartesian", pos), + LayoutPositioningBasis::LayoutPositioningInset => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("LayoutPositioningBasis::LayoutPositioningInset", pos), + _ => Ok(()), + } + })? + .visit_field::<::flatbuffers::ForwardsUOffset>("layout_dimensions", Self::VT_LAYOUT_DIMENSIONS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layout_container", Self::VT_LAYOUT_CONTAINER, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layout_child", Self::VT_LAYOUT_CHILD, false)? + .finish(); + Ok(()) + } +} +pub struct LayoutStyleArgs<'a> { + pub layout_position_type: LayoutPositioningBasis, + pub layout_position: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, + pub layout_dimensions: Option<::flatbuffers::WIPOffset>>, + pub layout_container: Option<::flatbuffers::WIPOffset>>, + pub layout_child: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for LayoutStyleArgs<'a> { + #[inline] + fn default() -> Self { + LayoutStyleArgs { + layout_position_type: LayoutPositioningBasis::NONE, + layout_position: None, + layout_dimensions: None, + layout_container: None, + layout_child: None, + } + } +} + +pub struct LayoutStyleBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayoutStyleBuilder<'a, 'b, A> { + #[inline] + pub fn add_layout_position_type(&mut self, layout_position_type: LayoutPositioningBasis) { + self.fbb_.push_slot::(LayoutStyle::VT_LAYOUT_POSITION_TYPE, layout_position_type, LayoutPositioningBasis::NONE); + } + #[inline] + pub fn add_layout_position(&mut self, layout_position: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(LayoutStyle::VT_LAYOUT_POSITION, layout_position); + } + #[inline] + pub fn add_layout_dimensions(&mut self, layout_dimensions: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutStyle::VT_LAYOUT_DIMENSIONS, layout_dimensions); + } + #[inline] + pub fn add_layout_container(&mut self, layout_container: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutStyle::VT_LAYOUT_CONTAINER, layout_container); + } + #[inline] + pub fn add_layout_child(&mut self, layout_child: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayoutStyle::VT_LAYOUT_CHILD, layout_child); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayoutStyleBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayoutStyleBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayoutStyle<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayoutStyle"); + ds.field("layout_position_type", &self.layout_position_type()); + match self.layout_position_type() { + LayoutPositioningBasis::LayoutPositioningCartesian => { + if let Some(x) = self.layout_position_as_layout_positioning_cartesian() { + ds.field("layout_position", &x) + } else { + ds.field("layout_position", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + LayoutPositioningBasis::LayoutPositioningInset => { + if let Some(x) = self.layout_position_as_layout_positioning_inset() { + ds.field("layout_position", &x) + } else { + ds.field("layout_position", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("layout_position", &x) + }, + }; + ds.field("layout_dimensions", &self.layout_dimensions()); + ds.field("layout_container", &self.layout_container()); + ds.field("layout_child", &self.layout_child()); + ds.finish() + } +} +pub enum TextSpanNodePropertiesOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct TextSpanNodeProperties<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for TextSpanNodeProperties<'a> { + type Inner = TextSpanNodeProperties<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> TextSpanNodeProperties<'a> { + pub const VT_STROKE_GEOMETRY: ::flatbuffers::VOffsetT = 4; + pub const VT_FILL_PAINTS: ::flatbuffers::VOffsetT = 6; + pub const VT_STROKE_PAINTS: ::flatbuffers::VOffsetT = 8; + pub const VT_TEXT: ::flatbuffers::VOffsetT = 10; + pub const VT_TEXT_STYLE: ::flatbuffers::VOffsetT = 12; + pub const VT_TEXT_ALIGN: ::flatbuffers::VOffsetT = 14; + pub const VT_TEXT_ALIGN_VERTICAL: ::flatbuffers::VOffsetT = 16; + pub const VT_MAX_LINES: ::flatbuffers::VOffsetT = 18; + pub const VT_ELLIPSIS: ::flatbuffers::VOffsetT = 20; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + TextSpanNodeProperties { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TextSpanNodePropertiesArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = TextSpanNodePropertiesBuilder::new(_fbb); + if let Some(x) = args.ellipsis { builder.add_ellipsis(x); } + builder.add_max_lines(args.max_lines); + if let Some(x) = args.text_style { builder.add_text_style(x); } + if let Some(x) = args.text { builder.add_text(x); } + if let Some(x) = args.stroke_paints { builder.add_stroke_paints(x); } + if let Some(x) = args.fill_paints { builder.add_fill_paints(x); } + if let Some(x) = args.stroke_geometry { builder.add_stroke_geometry(x); } + builder.add_text_align_vertical(args.text_align_vertical); + builder.add_text_align(args.text_align); + builder.finish() + } + + + #[inline] + pub fn stroke_geometry(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextSpanNodeProperties::VT_STROKE_GEOMETRY, None)} + } + #[inline] + pub fn fill_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(TextSpanNodeProperties::VT_FILL_PAINTS, None)} + } + #[inline] + pub fn stroke_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(TextSpanNodeProperties::VT_STROKE_PAINTS, None)} + } + #[inline] + pub fn text(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(TextSpanNodeProperties::VT_TEXT, None)} + } + #[inline] + pub fn text_style(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextSpanNodeProperties::VT_TEXT_STYLE, None)} + } + #[inline] + pub fn text_align(&self) -> TextAlign { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextSpanNodeProperties::VT_TEXT_ALIGN, Some(TextAlign::Left)).unwrap()} + } + #[inline] + pub fn text_align_vertical(&self) -> TextAlignVertical { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextSpanNodeProperties::VT_TEXT_ALIGN_VERTICAL, Some(TextAlignVertical::Top)).unwrap()} + } + #[inline] + pub fn max_lines(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(TextSpanNodeProperties::VT_MAX_LINES, Some(0)).unwrap()} + } + #[inline] + pub fn ellipsis(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(TextSpanNodeProperties::VT_ELLIPSIS, None)} + } +} + +impl ::flatbuffers::Verifiable for TextSpanNodeProperties<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_geometry", Self::VT_STROKE_GEOMETRY, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("fill_paints", Self::VT_FILL_PAINTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("stroke_paints", Self::VT_STROKE_PAINTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("text", Self::VT_TEXT, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("text_style", Self::VT_TEXT_STYLE, false)? + .visit_field::("text_align", Self::VT_TEXT_ALIGN, false)? + .visit_field::("text_align_vertical", Self::VT_TEXT_ALIGN_VERTICAL, false)? + .visit_field::("max_lines", Self::VT_MAX_LINES, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("ellipsis", Self::VT_ELLIPSIS, false)? + .finish(); + Ok(()) + } +} +pub struct TextSpanNodePropertiesArgs<'a> { + pub stroke_geometry: Option<::flatbuffers::WIPOffset>>, + pub fill_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub stroke_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub text: Option<::flatbuffers::WIPOffset<&'a str>>, + pub text_style: Option<::flatbuffers::WIPOffset>>, + pub text_align: TextAlign, + pub text_align_vertical: TextAlignVertical, + pub max_lines: u32, + pub ellipsis: Option<::flatbuffers::WIPOffset<&'a str>>, +} +impl<'a> Default for TextSpanNodePropertiesArgs<'a> { + #[inline] + fn default() -> Self { + TextSpanNodePropertiesArgs { + stroke_geometry: None, + fill_paints: None, + stroke_paints: None, + text: None, + text_style: None, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: 0, + ellipsis: None, + } + } +} + +pub struct TextSpanNodePropertiesBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> TextSpanNodePropertiesBuilder<'a, 'b, A> { + #[inline] + pub fn add_stroke_geometry(&mut self, stroke_geometry: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextSpanNodeProperties::VT_STROKE_GEOMETRY, stroke_geometry); + } + #[inline] + pub fn add_fill_paints(&mut self, fill_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(TextSpanNodeProperties::VT_FILL_PAINTS, fill_paints); + } + #[inline] + pub fn add_stroke_paints(&mut self, stroke_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(TextSpanNodeProperties::VT_STROKE_PAINTS, stroke_paints); + } + #[inline] + pub fn add_text(&mut self, text: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(TextSpanNodeProperties::VT_TEXT, text); + } + #[inline] + pub fn add_text_style(&mut self, text_style: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextSpanNodeProperties::VT_TEXT_STYLE, text_style); + } + #[inline] + pub fn add_text_align(&mut self, text_align: TextAlign) { + self.fbb_.push_slot::(TextSpanNodeProperties::VT_TEXT_ALIGN, text_align, TextAlign::Left); + } + #[inline] + pub fn add_text_align_vertical(&mut self, text_align_vertical: TextAlignVertical) { + self.fbb_.push_slot::(TextSpanNodeProperties::VT_TEXT_ALIGN_VERTICAL, text_align_vertical, TextAlignVertical::Top); + } + #[inline] + pub fn add_max_lines(&mut self, max_lines: u32) { + self.fbb_.push_slot::(TextSpanNodeProperties::VT_MAX_LINES, max_lines, 0); + } + #[inline] + pub fn add_ellipsis(&mut self, ellipsis: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(TextSpanNodeProperties::VT_ELLIPSIS, ellipsis); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> TextSpanNodePropertiesBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TextSpanNodePropertiesBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for TextSpanNodeProperties<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("TextSpanNodeProperties"); + ds.field("stroke_geometry", &self.stroke_geometry()); + ds.field("fill_paints", &self.fill_paints()); + ds.field("stroke_paints", &self.stroke_paints()); + ds.field("text", &self.text()); + ds.field("text_style", &self.text_style()); + ds.field("text_align", &self.text_align()); + ds.field("text_align_vertical", &self.text_align_vertical()); + ds.field("max_lines", &self.max_lines()); + ds.field("ellipsis", &self.ellipsis()); + ds.finish() + } +} +pub enum SystemNodeTraitOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Universal node trait. +pub struct SystemNodeTrait<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for SystemNodeTrait<'a> { + type Inner = SystemNodeTrait<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> SystemNodeTrait<'a> { + pub const VT_ID: ::flatbuffers::VOffsetT = 4; + pub const VT_ACTIVE: ::flatbuffers::VOffsetT = 6; + pub const VT_NAME: ::flatbuffers::VOffsetT = 8; + pub const VT_LOCKED: ::flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + SystemNodeTrait { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args SystemNodeTraitArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = SystemNodeTraitBuilder::new(_fbb); + if let Some(x) = args.name { builder.add_name(x); } + if let Some(x) = args.id { builder.add_id(x); } + builder.add_locked(args.locked); + builder.add_active(args.active); + builder.finish() + } + + + /// unique identifier for the node. (required) + #[inline] + pub fn id(&self) -> NodeIdentifier<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(SystemNodeTrait::VT_ID, None).unwrap()} + } + /// whether the node is active. (visible, active) + #[inline] + pub fn active(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SystemNodeTrait::VT_ACTIVE, Some(true)).unwrap()} + } + /// name of the node. (optional) + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(SystemNodeTrait::VT_NAME, None)} + } + /// whether the node is locked. (locked, editor) + #[inline] + pub fn locked(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SystemNodeTrait::VT_LOCKED, Some(false)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for SystemNodeTrait<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("id", Self::VT_ID, true)? + .visit_field::("active", Self::VT_ACTIVE, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("name", Self::VT_NAME, false)? + .visit_field::("locked", Self::VT_LOCKED, false)? + .finish(); + Ok(()) + } +} +pub struct SystemNodeTraitArgs<'a> { + pub id: Option<::flatbuffers::WIPOffset>>, + pub active: bool, + pub name: Option<::flatbuffers::WIPOffset<&'a str>>, + pub locked: bool, +} +impl<'a> Default for SystemNodeTraitArgs<'a> { + #[inline] + fn default() -> Self { + SystemNodeTraitArgs { + id: None, // required field + active: true, + name: None, + locked: false, + } + } +} + +pub struct SystemNodeTraitBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> SystemNodeTraitBuilder<'a, 'b, A> { + #[inline] + pub fn add_id(&mut self, id: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(SystemNodeTrait::VT_ID, id); + } + #[inline] + pub fn add_active(&mut self, active: bool) { + self.fbb_.push_slot::(SystemNodeTrait::VT_ACTIVE, active, true); + } + #[inline] + pub fn add_name(&mut self, name: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(SystemNodeTrait::VT_NAME, name); + } + #[inline] + pub fn add_locked(&mut self, locked: bool) { + self.fbb_.push_slot::(SystemNodeTrait::VT_LOCKED, locked, false); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> SystemNodeTraitBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + SystemNodeTraitBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, SystemNodeTrait::VT_ID,"id"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for SystemNodeTrait<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("SystemNodeTrait"); + ds.field("id", &self.id()); + ds.field("active", &self.active()); + ds.field("name", &self.name()); + ds.field("locked", &self.locked()); + ds.finish() + } +} +pub enum LayerTraitOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Shared layer fields used by all layer-node variants. +/// Layer is what usually user think of as a node. each layer is non-virtual, a real render target. +pub struct LayerTrait<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LayerTrait<'a> { + type Inner = LayerTrait<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LayerTrait<'a> { + pub const VT_PARENT: ::flatbuffers::VOffsetT = 4; + pub const VT_OPACITY: ::flatbuffers::VOffsetT = 6; + pub const VT_BLEND_MODE: ::flatbuffers::VOffsetT = 8; + pub const VT_MASK_TYPE_TYPE: ::flatbuffers::VOffsetT = 10; + pub const VT_MASK_TYPE: ::flatbuffers::VOffsetT = 12; + pub const VT_EFFECTS: ::flatbuffers::VOffsetT = 14; + pub const VT_LAYOUT: ::flatbuffers::VOffsetT = 16; + pub const VT_POST_LAYOUT_TRANSFORM: ::flatbuffers::VOffsetT = 18; + pub const VT_POST_LAYOUT_TRANSFORM_ORIGIN: ::flatbuffers::VOffsetT = 20; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LayerTrait { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LayerTraitArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LayerTraitBuilder::new(_fbb); + if let Some(x) = args.post_layout_transform_origin { builder.add_post_layout_transform_origin(x); } + if let Some(x) = args.post_layout_transform { builder.add_post_layout_transform(x); } + if let Some(x) = args.layout { builder.add_layout(x); } + if let Some(x) = args.effects { builder.add_effects(x); } + if let Some(x) = args.mask_type { builder.add_mask_type(x); } + builder.add_opacity(args.opacity); + if let Some(x) = args.parent { builder.add_parent(x); } + builder.add_mask_type_type(args.mask_type_type); + builder.add_blend_mode(args.blend_mode); + builder.finish() + } + + + /// Parent reference (optional; root nodes omit this). + /// + /// When present, this node is a child of the referenced parent. + /// Children are ordered by `position` (lexicographic sort). + /// + /// Reference: Aligned with Figma's `ParentIndex` pattern (fig.kiwi). + /// This replaces the previous `children:[NodeIdentifier]` array model. + /// + /// To compute children of a parent node: + /// 1. Filter all nodes where `parent.parent_id == parent.id` + /// 2. Sort by `parent.position` (lexicographic comparison) + #[inline] + pub fn parent(&self) -> ParentReference<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayerTrait::VT_PARENT, None).unwrap()} + } + /// opacity of the layer. + #[inline] + pub fn opacity(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayerTrait::VT_OPACITY, Some(1.0)).unwrap()} + } + /// Blend mode (archive model). + /// + /// Default is `pass_through`. + #[inline] + pub fn blend_mode(&self) -> LayerBlendMode { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayerTrait::VT_BLEND_MODE, Some(LayerBlendMode::PassThrough)).unwrap()} + } + #[inline] + pub fn mask_type_type(&self) -> LayerMaskType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayerTrait::VT_MASK_TYPE_TYPE, Some(LayerMaskType::NONE)).unwrap()} + } + /// Rust: `LayerMaskType` (union; NONE means the node is not a mask) + #[inline] + pub fn mask_type(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(LayerTrait::VT_MASK_TYPE, None)} + } + /// layer effects. + #[inline] + pub fn effects(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayerTrait::VT_EFFECTS, None)} + } + /// Geometry transform baseline (identity by default). + /// Layout (optional, depending on node type / usage). + #[inline] + pub fn layout(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LayerTrait::VT_LAYOUT, None)} + } + /// post-layout transform, this reflects how css transform is applied after the layout is resolved. this is currently how grida handles rotation. in the future, this might change. + #[inline] + pub fn post_layout_transform(&self) -> Option<&'a CGTransform2D> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayerTrait::VT_POST_LAYOUT_TRANSFORM, None)} + } + #[inline] + pub fn post_layout_transform_origin(&self) -> Option<&'a Alignment> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LayerTrait::VT_POST_LAYOUT_TRANSFORM_ORIGIN, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn mask_type_as_layer_mask_type_image(&self) -> Option> { + if self.mask_type_type() == LayerMaskType::LayerMaskTypeImage { + self.mask_type().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LayerMaskTypeImage::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn mask_type_as_layer_mask_type_geometry(&self) -> Option> { + if self.mask_type_type() == LayerMaskType::LayerMaskTypeGeometry { + self.mask_type().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LayerMaskTypeGeometry::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for LayerTrait<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("parent", Self::VT_PARENT, true)? + .visit_field::("opacity", Self::VT_OPACITY, false)? + .visit_field::("blend_mode", Self::VT_BLEND_MODE, false)? + .visit_union::("mask_type_type", Self::VT_MASK_TYPE_TYPE, "mask_type", Self::VT_MASK_TYPE, false, |key, v, pos| { + match key { + LayerMaskType::LayerMaskTypeImage => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("LayerMaskType::LayerMaskTypeImage", pos), + LayerMaskType::LayerMaskTypeGeometry => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("LayerMaskType::LayerMaskTypeGeometry", pos), + _ => Ok(()), + } + })? + .visit_field::<::flatbuffers::ForwardsUOffset>("effects", Self::VT_EFFECTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layout", Self::VT_LAYOUT, false)? + .visit_field::("post_layout_transform", Self::VT_POST_LAYOUT_TRANSFORM, false)? + .visit_field::("post_layout_transform_origin", Self::VT_POST_LAYOUT_TRANSFORM_ORIGIN, false)? + .finish(); + Ok(()) + } +} +pub struct LayerTraitArgs<'a> { + pub parent: Option<::flatbuffers::WIPOffset>>, + pub opacity: f32, + pub blend_mode: LayerBlendMode, + pub mask_type_type: LayerMaskType, + pub mask_type: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, + pub effects: Option<::flatbuffers::WIPOffset>>, + pub layout: Option<::flatbuffers::WIPOffset>>, + pub post_layout_transform: Option<&'a CGTransform2D>, + pub post_layout_transform_origin: Option<&'a Alignment>, +} +impl<'a> Default for LayerTraitArgs<'a> { + #[inline] + fn default() -> Self { + LayerTraitArgs { + parent: None, // required field + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask_type_type: LayerMaskType::NONE, + mask_type: None, + effects: None, + layout: None, + post_layout_transform: None, + post_layout_transform_origin: None, + } + } +} + +pub struct LayerTraitBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LayerTraitBuilder<'a, 'b, A> { + #[inline] + pub fn add_parent(&mut self, parent: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayerTrait::VT_PARENT, parent); + } + #[inline] + pub fn add_opacity(&mut self, opacity: f32) { + self.fbb_.push_slot::(LayerTrait::VT_OPACITY, opacity, 1.0); + } + #[inline] + pub fn add_blend_mode(&mut self, blend_mode: LayerBlendMode) { + self.fbb_.push_slot::(LayerTrait::VT_BLEND_MODE, blend_mode, LayerBlendMode::PassThrough); + } + #[inline] + pub fn add_mask_type_type(&mut self, mask_type_type: LayerMaskType) { + self.fbb_.push_slot::(LayerTrait::VT_MASK_TYPE_TYPE, mask_type_type, LayerMaskType::NONE); + } + #[inline] + pub fn add_mask_type(&mut self, mask_type: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(LayerTrait::VT_MASK_TYPE, mask_type); + } + #[inline] + pub fn add_effects(&mut self, effects: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayerTrait::VT_EFFECTS, effects); + } + #[inline] + pub fn add_layout(&mut self, layout: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LayerTrait::VT_LAYOUT, layout); + } + #[inline] + pub fn add_post_layout_transform(&mut self, post_layout_transform: &CGTransform2D) { + self.fbb_.push_slot_always::<&CGTransform2D>(LayerTrait::VT_POST_LAYOUT_TRANSFORM, post_layout_transform); + } + #[inline] + pub fn add_post_layout_transform_origin(&mut self, post_layout_transform_origin: &Alignment) { + self.fbb_.push_slot_always::<&Alignment>(LayerTrait::VT_POST_LAYOUT_TRANSFORM_ORIGIN, post_layout_transform_origin); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LayerTraitBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LayerTraitBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, LayerTrait::VT_PARENT,"parent"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LayerTrait<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LayerTrait"); + ds.field("parent", &self.parent()); + ds.field("opacity", &self.opacity()); + ds.field("blend_mode", &self.blend_mode()); + ds.field("mask_type_type", &self.mask_type_type()); + match self.mask_type_type() { + LayerMaskType::LayerMaskTypeImage => { + if let Some(x) = self.mask_type_as_layer_mask_type_image() { + ds.field("mask_type", &x) + } else { + ds.field("mask_type", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + LayerMaskType::LayerMaskTypeGeometry => { + if let Some(x) = self.mask_type_as_layer_mask_type_geometry() { + ds.field("mask_type", &x) + } else { + ds.field("mask_type", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("mask_type", &x) + }, + }; + ds.field("effects", &self.effects()); + ds.field("layout", &self.layout()); + ds.field("post_layout_transform", &self.post_layout_transform()); + ds.field("post_layout_transform_origin", &self.post_layout_transform_origin()); + ds.finish() + } +} +pub enum UnknownNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Placeholder, unused. +pub struct UnknownNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for UnknownNode<'a> { + type Inner = UnknownNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> UnknownNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + UnknownNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args UnknownNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = UnknownNodeBuilder::new(_fbb); + if let Some(x) = args.node { builder.add_node(x); } + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(UnknownNode::VT_NODE, None).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for UnknownNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .finish(); + Ok(()) + } +} +pub struct UnknownNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for UnknownNodeArgs<'a> { + #[inline] + fn default() -> Self { + UnknownNodeArgs { + node: None, // required field + } + } +} + +pub struct UnknownNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> UnknownNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(UnknownNode::VT_NODE, node); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> UnknownNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + UnknownNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, UnknownNode::VT_NODE,"node"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for UnknownNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("UnknownNode"); + ds.field("node", &self.node()); + ds.finish() + } +} +pub enum SceneNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Scene. +/// +/// SceneNode is special and doesn't use NodeCommon like other nodes. +/// It inlines only the relevant fields from NodeCommon. +pub struct SceneNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for SceneNode<'a> { + type Inner = SceneNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> SceneNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_CONSTRAINTS_CHILDREN: ::flatbuffers::VOffsetT = 6; + pub const VT_SCENE_BACKGROUND_COLOR: ::flatbuffers::VOffsetT = 8; + pub const VT_GUIDES: ::flatbuffers::VOffsetT = 10; + pub const VT_EDGES: ::flatbuffers::VOffsetT = 12; + pub const VT_POSITION: ::flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + SceneNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args SceneNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = SceneNodeBuilder::new(_fbb); + if let Some(x) = args.position { builder.add_position(x); } + if let Some(x) = args.edges { builder.add_edges(x); } + if let Some(x) = args.guides { builder.add_guides(x); } + if let Some(x) = args.scene_background_color { builder.add_scene_background_color(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.add_constraints_children(args.constraints_children); + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(SceneNode::VT_NODE, None).unwrap()} + } + /// Scene-specific properties. + #[inline] + pub fn constraints_children(&self) -> SceneConstraintsChildren { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SceneNode::VT_CONSTRAINTS_CHILDREN, Some(SceneConstraintsChildren::Single)).unwrap()} + } + /// Scene background color (solid color only, RGBA32F). + /// + /// This is a simple solid color background. For gradient or image backgrounds, + /// use a background container node instead. + #[inline] + pub fn scene_background_color(&self) -> Option<&'a RGBA32F> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(SceneNode::VT_SCENE_BACKGROUND_COLOR, None)} + } + #[inline] + pub fn guides(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(SceneNode::VT_GUIDES, None)} + } + #[inline] + pub fn edges(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(SceneNode::VT_EDGES, None)} + } + /// Fractional index position string for ordering among siblings. + /// Empty string means "unsorted" or "default position". + /// Children are sorted by lexicographic comparison of position strings. + #[inline] + pub fn position(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(SceneNode::VT_POSITION, None)} + } +} + +impl ::flatbuffers::Verifiable for SceneNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::("constraints_children", Self::VT_CONSTRAINTS_CHILDREN, false)? + .visit_field::("scene_background_color", Self::VT_SCENE_BACKGROUND_COLOR, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("guides", Self::VT_GUIDES, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("edges", Self::VT_EDGES, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("position", Self::VT_POSITION, false)? + .finish(); + Ok(()) + } +} +pub struct SceneNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub constraints_children: SceneConstraintsChildren, + pub scene_background_color: Option<&'a RGBA32F>, + pub guides: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub edges: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub position: Option<::flatbuffers::WIPOffset<&'a str>>, +} +impl<'a> Default for SceneNodeArgs<'a> { + #[inline] + fn default() -> Self { + SceneNodeArgs { + node: None, // required field + constraints_children: SceneConstraintsChildren::Single, + scene_background_color: None, + guides: None, + edges: None, + position: None, + } + } +} + +pub struct SceneNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> SceneNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(SceneNode::VT_NODE, node); + } + #[inline] + pub fn add_constraints_children(&mut self, constraints_children: SceneConstraintsChildren) { + self.fbb_.push_slot::(SceneNode::VT_CONSTRAINTS_CHILDREN, constraints_children, SceneConstraintsChildren::Single); + } + #[inline] + pub fn add_scene_background_color(&mut self, scene_background_color: &RGBA32F) { + self.fbb_.push_slot_always::<&RGBA32F>(SceneNode::VT_SCENE_BACKGROUND_COLOR, scene_background_color); + } + #[inline] + pub fn add_guides(&mut self, guides: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(SceneNode::VT_GUIDES, guides); + } + #[inline] + pub fn add_edges(&mut self, edges: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(SceneNode::VT_EDGES, edges); + } + #[inline] + pub fn add_position(&mut self, position: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(SceneNode::VT_POSITION, position); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> SceneNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + SceneNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, SceneNode::VT_NODE,"node"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for SceneNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("SceneNode"); + ds.field("node", &self.node()); + ds.field("constraints_children", &self.constraints_children()); + ds.field("scene_background_color", &self.scene_background_color()); + ds.field("guides", &self.guides()); + ds.field("edges", &self.edges()); + ds.field("position", &self.position()); + ds.finish() + } +} +pub enum InitialContainerNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Initial container. +pub struct InitialContainerNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for InitialContainerNode<'a> { + type Inner = InitialContainerNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> InitialContainerNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + InitialContainerNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args InitialContainerNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = InitialContainerNodeBuilder::new(_fbb); + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(InitialContainerNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(InitialContainerNode::VT_LAYER, None).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for InitialContainerNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .finish(); + Ok(()) + } +} +pub struct InitialContainerNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for InitialContainerNodeArgs<'a> { + #[inline] + fn default() -> Self { + InitialContainerNodeArgs { + node: None, // required field + layer: None, // required field + } + } +} + +pub struct InitialContainerNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> InitialContainerNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(InitialContainerNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(InitialContainerNode::VT_LAYER, layer); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> InitialContainerNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + InitialContainerNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, InitialContainerNode::VT_NODE,"node"); + self.fbb_.required(o, InitialContainerNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for InitialContainerNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("InitialContainerNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.finish() + } +} +pub enum ContainerNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Container. +pub struct ContainerNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for ContainerNode<'a> { + type Inner = ContainerNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> ContainerNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + pub const VT_STROKE_GEOMETRY: ::flatbuffers::VOffsetT = 8; + pub const VT_CORNER_RADIUS: ::flatbuffers::VOffsetT = 10; + pub const VT_FILL_PAINTS: ::flatbuffers::VOffsetT = 12; + pub const VT_STROKE_PAINTS: ::flatbuffers::VOffsetT = 14; + pub const VT_CLIPS_CONTENT: ::flatbuffers::VOffsetT = 16; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + ContainerNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ContainerNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = ContainerNodeBuilder::new(_fbb); + if let Some(x) = args.stroke_paints { builder.add_stroke_paints(x); } + if let Some(x) = args.fill_paints { builder.add_fill_paints(x); } + if let Some(x) = args.corner_radius { builder.add_corner_radius(x); } + if let Some(x) = args.stroke_geometry { builder.add_stroke_geometry(x); } + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.add_clips_content(args.clips_content); + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(ContainerNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(ContainerNode::VT_LAYER, None).unwrap()} + } + #[inline] + pub fn stroke_geometry(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(ContainerNode::VT_STROKE_GEOMETRY, None)} + } + #[inline] + pub fn corner_radius(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(ContainerNode::VT_CORNER_RADIUS, None)} + } + #[inline] + pub fn fill_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(ContainerNode::VT_FILL_PAINTS, None)} + } + #[inline] + pub fn stroke_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(ContainerNode::VT_STROKE_PAINTS, None)} + } + /// Content-only clip flag (children only). + #[inline] + pub fn clips_content(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(ContainerNode::VT_CLIPS_CONTENT, Some(false)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for ContainerNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_geometry", Self::VT_STROKE_GEOMETRY, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("corner_radius", Self::VT_CORNER_RADIUS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("fill_paints", Self::VT_FILL_PAINTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("stroke_paints", Self::VT_STROKE_PAINTS, false)? + .visit_field::("clips_content", Self::VT_CLIPS_CONTENT, false)? + .finish(); + Ok(()) + } +} +pub struct ContainerNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, + pub stroke_geometry: Option<::flatbuffers::WIPOffset>>, + pub corner_radius: Option<::flatbuffers::WIPOffset>>, + pub fill_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub stroke_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub clips_content: bool, +} +impl<'a> Default for ContainerNodeArgs<'a> { + #[inline] + fn default() -> Self { + ContainerNodeArgs { + node: None, // required field + layer: None, // required field + stroke_geometry: None, + corner_radius: None, + fill_paints: None, + stroke_paints: None, + clips_content: false, + } + } +} + +pub struct ContainerNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> ContainerNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(ContainerNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(ContainerNode::VT_LAYER, layer); + } + #[inline] + pub fn add_stroke_geometry(&mut self, stroke_geometry: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(ContainerNode::VT_STROKE_GEOMETRY, stroke_geometry); + } + #[inline] + pub fn add_corner_radius(&mut self, corner_radius: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(ContainerNode::VT_CORNER_RADIUS, corner_radius); + } + #[inline] + pub fn add_fill_paints(&mut self, fill_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(ContainerNode::VT_FILL_PAINTS, fill_paints); + } + #[inline] + pub fn add_stroke_paints(&mut self, stroke_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(ContainerNode::VT_STROKE_PAINTS, stroke_paints); + } + #[inline] + pub fn add_clips_content(&mut self, clips_content: bool) { + self.fbb_.push_slot::(ContainerNode::VT_CLIPS_CONTENT, clips_content, false); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> ContainerNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ContainerNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, ContainerNode::VT_NODE,"node"); + self.fbb_.required(o, ContainerNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for ContainerNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("ContainerNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.field("stroke_geometry", &self.stroke_geometry()); + ds.field("corner_radius", &self.corner_radius()); + ds.field("fill_paints", &self.fill_paints()); + ds.field("stroke_paints", &self.stroke_paints()); + ds.field("clips_content", &self.clips_content()); + ds.finish() + } +} +pub enum GroupNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Group. +pub struct GroupNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for GroupNode<'a> { + type Inner = GroupNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> GroupNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + GroupNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args GroupNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = GroupNodeBuilder::new(_fbb); + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(GroupNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(GroupNode::VT_LAYER, None).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for GroupNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .finish(); + Ok(()) + } +} +pub struct GroupNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for GroupNodeArgs<'a> { + #[inline] + fn default() -> Self { + GroupNodeArgs { + node: None, // required field + layer: None, // required field + } + } +} + +pub struct GroupNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> GroupNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(GroupNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(GroupNode::VT_LAYER, layer); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> GroupNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + GroupNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, GroupNode::VT_NODE,"node"); + self.fbb_.required(o, GroupNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for GroupNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("GroupNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.finish() + } +} +pub enum BooleanOperationNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Boolean operation. +pub struct BooleanOperationNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for BooleanOperationNode<'a> { + type Inner = BooleanOperationNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> BooleanOperationNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + pub const VT_OP: ::flatbuffers::VOffsetT = 8; + pub const VT_CORNER_RADIUS: ::flatbuffers::VOffsetT = 10; + pub const VT_FILL_PAINTS: ::flatbuffers::VOffsetT = 12; + pub const VT_STROKE_GEOMETRY: ::flatbuffers::VOffsetT = 14; + pub const VT_STROKE_PAINTS: ::flatbuffers::VOffsetT = 16; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + BooleanOperationNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args BooleanOperationNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = BooleanOperationNodeBuilder::new(_fbb); + if let Some(x) = args.stroke_paints { builder.add_stroke_paints(x); } + if let Some(x) = args.stroke_geometry { builder.add_stroke_geometry(x); } + if let Some(x) = args.fill_paints { builder.add_fill_paints(x); } + if let Some(x) = args.corner_radius { builder.add_corner_radius(x); } + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.add_op(args.op); + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BooleanOperationNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BooleanOperationNode::VT_LAYER, None).unwrap()} + } + #[inline] + pub fn op(&self) -> BooleanPathOperation { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BooleanOperationNode::VT_OP, Some(BooleanPathOperation::Union)).unwrap()} + } + #[inline] + pub fn corner_radius(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BooleanOperationNode::VT_CORNER_RADIUS, None)} + } + #[inline] + pub fn fill_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(BooleanOperationNode::VT_FILL_PAINTS, None)} + } + #[inline] + pub fn stroke_geometry(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BooleanOperationNode::VT_STROKE_GEOMETRY, None)} + } + #[inline] + pub fn stroke_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(BooleanOperationNode::VT_STROKE_PAINTS, None)} + } +} + +impl ::flatbuffers::Verifiable for BooleanOperationNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .visit_field::("op", Self::VT_OP, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("corner_radius", Self::VT_CORNER_RADIUS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("fill_paints", Self::VT_FILL_PAINTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_geometry", Self::VT_STROKE_GEOMETRY, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("stroke_paints", Self::VT_STROKE_PAINTS, false)? + .finish(); + Ok(()) + } +} +pub struct BooleanOperationNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, + pub op: BooleanPathOperation, + pub corner_radius: Option<::flatbuffers::WIPOffset>>, + pub fill_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub stroke_geometry: Option<::flatbuffers::WIPOffset>>, + pub stroke_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, +} +impl<'a> Default for BooleanOperationNodeArgs<'a> { + #[inline] + fn default() -> Self { + BooleanOperationNodeArgs { + node: None, // required field + layer: None, // required field + op: BooleanPathOperation::Union, + corner_radius: None, + fill_paints: None, + stroke_geometry: None, + stroke_paints: None, + } + } +} + +pub struct BooleanOperationNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> BooleanOperationNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BooleanOperationNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BooleanOperationNode::VT_LAYER, layer); + } + #[inline] + pub fn add_op(&mut self, op: BooleanPathOperation) { + self.fbb_.push_slot::(BooleanOperationNode::VT_OP, op, BooleanPathOperation::Union); + } + #[inline] + pub fn add_corner_radius(&mut self, corner_radius: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BooleanOperationNode::VT_CORNER_RADIUS, corner_radius); + } + #[inline] + pub fn add_fill_paints(&mut self, fill_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(BooleanOperationNode::VT_FILL_PAINTS, fill_paints); + } + #[inline] + pub fn add_stroke_geometry(&mut self, stroke_geometry: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BooleanOperationNode::VT_STROKE_GEOMETRY, stroke_geometry); + } + #[inline] + pub fn add_stroke_paints(&mut self, stroke_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(BooleanOperationNode::VT_STROKE_PAINTS, stroke_paints); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> BooleanOperationNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + BooleanOperationNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, BooleanOperationNode::VT_NODE,"node"); + self.fbb_.required(o, BooleanOperationNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for BooleanOperationNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("BooleanOperationNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.field("op", &self.op()); + ds.field("corner_radius", &self.corner_radius()); + ds.field("fill_paints", &self.fill_paints()); + ds.field("stroke_geometry", &self.stroke_geometry()); + ds.field("stroke_paints", &self.stroke_paints()); + ds.finish() + } +} +pub enum BasicShapeNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct BasicShapeNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for BasicShapeNode<'a> { + type Inner = BasicShapeNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> BasicShapeNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + pub const VT_TYPE_: ::flatbuffers::VOffsetT = 8; + pub const VT_SHAPE_TYPE: ::flatbuffers::VOffsetT = 10; + pub const VT_SHAPE: ::flatbuffers::VOffsetT = 12; + pub const VT_CORNER_RADIUS: ::flatbuffers::VOffsetT = 14; + pub const VT_CORNER_SMOOTHING: ::flatbuffers::VOffsetT = 16; + pub const VT_FILL_PAINTS: ::flatbuffers::VOffsetT = 18; + pub const VT_STROKE_STYLE: ::flatbuffers::VOffsetT = 20; + pub const VT_STROKE_WIDTH: ::flatbuffers::VOffsetT = 22; + pub const VT_STROKE_WIDTH_PROFILE: ::flatbuffers::VOffsetT = 24; + pub const VT_RECTANGULAR_CORNER_RADIUS: ::flatbuffers::VOffsetT = 26; + pub const VT_RECTANGULAR_STROKE_WIDTH: ::flatbuffers::VOffsetT = 28; + pub const VT_STROKE_PAINTS: ::flatbuffers::VOffsetT = 30; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + BasicShapeNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args BasicShapeNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = BasicShapeNodeBuilder::new(_fbb); + if let Some(x) = args.stroke_paints { builder.add_stroke_paints(x); } + if let Some(x) = args.rectangular_stroke_width { builder.add_rectangular_stroke_width(x); } + if let Some(x) = args.rectangular_corner_radius { builder.add_rectangular_corner_radius(x); } + if let Some(x) = args.stroke_width_profile { builder.add_stroke_width_profile(x); } + builder.add_stroke_width(args.stroke_width); + if let Some(x) = args.stroke_style { builder.add_stroke_style(x); } + if let Some(x) = args.fill_paints { builder.add_fill_paints(x); } + builder.add_corner_smoothing(args.corner_smoothing); + builder.add_corner_radius(args.corner_radius); + if let Some(x) = args.shape { builder.add_shape(x); } + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.add_shape_type(args.shape_type); + builder.add_type_(args.type_); + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BasicShapeNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BasicShapeNode::VT_LAYER, None).unwrap()} + } + /// shape of the type, must align with the shape. this is kept to keep the node type semantics. + #[inline] + pub fn type_(&self) -> BasicShapeNodeType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BasicShapeNode::VT_TYPE_, Some(BasicShapeNodeType::Rectangle)).unwrap()} + } + #[inline] + pub fn shape_type(&self) -> CanonicalLayerShape { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BasicShapeNode::VT_SHAPE_TYPE, Some(CanonicalLayerShape::NONE)).unwrap()} + } + #[inline] + pub fn shape(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(BasicShapeNode::VT_SHAPE, None)} + } + #[inline] + pub fn corner_radius(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BasicShapeNode::VT_CORNER_RADIUS, Some(0.0)).unwrap()} + } + #[inline] + pub fn corner_smoothing(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BasicShapeNode::VT_CORNER_SMOOTHING, Some(0.0)).unwrap()} + } + #[inline] + pub fn fill_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(BasicShapeNode::VT_FILL_PAINTS, None)} + } + #[inline] + pub fn stroke_style(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BasicShapeNode::VT_STROKE_STYLE, None)} + } + #[inline] + pub fn stroke_width(&self) -> f32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BasicShapeNode::VT_STROKE_WIDTH, Some(0.0)).unwrap()} + } + #[inline] + pub fn stroke_width_profile(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(BasicShapeNode::VT_STROKE_WIDTH_PROFILE, None)} + } + #[inline] + pub fn rectangular_corner_radius(&self) -> Option<&'a RectangularCornerRadius> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BasicShapeNode::VT_RECTANGULAR_CORNER_RADIUS, None)} + } + #[inline] + pub fn rectangular_stroke_width(&self) -> Option<&'a RectangularStrokeWidth> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(BasicShapeNode::VT_RECTANGULAR_STROKE_WIDTH, None)} + } + #[inline] + pub fn stroke_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(BasicShapeNode::VT_STROKE_PAINTS, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn shape_as_canonical_shape_rectangular(&self) -> Option> { + if self.shape_type() == CanonicalLayerShape::CanonicalShapeRectangular { + self.shape().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CanonicalShapeRectangular::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn shape_as_canonical_shape_elliptical(&self) -> Option> { + if self.shape_type() == CanonicalLayerShape::CanonicalShapeElliptical { + self.shape().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CanonicalShapeElliptical::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn shape_as_canonical_shape_points_polygon(&self) -> Option> { + if self.shape_type() == CanonicalLayerShape::CanonicalShapePointsPolygon { + self.shape().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CanonicalShapePointsPolygon::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn shape_as_canonical_shape_regular_polygon(&self) -> Option> { + if self.shape_type() == CanonicalLayerShape::CanonicalShapeRegularPolygon { + self.shape().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CanonicalShapeRegularPolygon::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn shape_as_canonical_shape_regular_star_polygon(&self) -> Option> { + if self.shape_type() == CanonicalLayerShape::CanonicalShapeRegularStarPolygon { + self.shape().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CanonicalShapeRegularStarPolygon::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn shape_as_canonical_shape_path(&self) -> Option> { + if self.shape_type() == CanonicalLayerShape::CanonicalShapePath { + self.shape().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CanonicalShapePath::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for BasicShapeNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .visit_field::("type_", Self::VT_TYPE_, false)? + .visit_union::("shape_type", Self::VT_SHAPE_TYPE, "shape", Self::VT_SHAPE, false, |key, v, pos| { + match key { + CanonicalLayerShape::CanonicalShapeRectangular => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("CanonicalLayerShape::CanonicalShapeRectangular", pos), + CanonicalLayerShape::CanonicalShapeElliptical => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("CanonicalLayerShape::CanonicalShapeElliptical", pos), + CanonicalLayerShape::CanonicalShapePointsPolygon => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("CanonicalLayerShape::CanonicalShapePointsPolygon", pos), + CanonicalLayerShape::CanonicalShapeRegularPolygon => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("CanonicalLayerShape::CanonicalShapeRegularPolygon", pos), + CanonicalLayerShape::CanonicalShapeRegularStarPolygon => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("CanonicalLayerShape::CanonicalShapeRegularStarPolygon", pos), + CanonicalLayerShape::CanonicalShapePath => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("CanonicalLayerShape::CanonicalShapePath", pos), + _ => Ok(()), + } + })? + .visit_field::("corner_radius", Self::VT_CORNER_RADIUS, false)? + .visit_field::("corner_smoothing", Self::VT_CORNER_SMOOTHING, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("fill_paints", Self::VT_FILL_PAINTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_style", Self::VT_STROKE_STYLE, false)? + .visit_field::("stroke_width", Self::VT_STROKE_WIDTH, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_width_profile", Self::VT_STROKE_WIDTH_PROFILE, false)? + .visit_field::("rectangular_corner_radius", Self::VT_RECTANGULAR_CORNER_RADIUS, false)? + .visit_field::("rectangular_stroke_width", Self::VT_RECTANGULAR_STROKE_WIDTH, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("stroke_paints", Self::VT_STROKE_PAINTS, false)? + .finish(); + Ok(()) + } +} +pub struct BasicShapeNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, + pub type_: BasicShapeNodeType, + pub shape_type: CanonicalLayerShape, + pub shape: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, + pub corner_radius: f32, + pub corner_smoothing: f32, + pub fill_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub stroke_style: Option<::flatbuffers::WIPOffset>>, + pub stroke_width: f32, + pub stroke_width_profile: Option<::flatbuffers::WIPOffset>>, + pub rectangular_corner_radius: Option<&'a RectangularCornerRadius>, + pub rectangular_stroke_width: Option<&'a RectangularStrokeWidth>, + pub stroke_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, +} +impl<'a> Default for BasicShapeNodeArgs<'a> { + #[inline] + fn default() -> Self { + BasicShapeNodeArgs { + node: None, // required field + layer: None, // required field + type_: BasicShapeNodeType::Rectangle, + shape_type: CanonicalLayerShape::NONE, + shape: None, + corner_radius: 0.0, + corner_smoothing: 0.0, + fill_paints: None, + stroke_style: None, + stroke_width: 0.0, + stroke_width_profile: None, + rectangular_corner_radius: None, + rectangular_stroke_width: None, + stroke_paints: None, + } + } +} + +pub struct BasicShapeNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> BasicShapeNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BasicShapeNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BasicShapeNode::VT_LAYER, layer); + } + #[inline] + pub fn add_type_(&mut self, type_: BasicShapeNodeType) { + self.fbb_.push_slot::(BasicShapeNode::VT_TYPE_, type_, BasicShapeNodeType::Rectangle); + } + #[inline] + pub fn add_shape_type(&mut self, shape_type: CanonicalLayerShape) { + self.fbb_.push_slot::(BasicShapeNode::VT_SHAPE_TYPE, shape_type, CanonicalLayerShape::NONE); + } + #[inline] + pub fn add_shape(&mut self, shape: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(BasicShapeNode::VT_SHAPE, shape); + } + #[inline] + pub fn add_corner_radius(&mut self, corner_radius: f32) { + self.fbb_.push_slot::(BasicShapeNode::VT_CORNER_RADIUS, corner_radius, 0.0); + } + #[inline] + pub fn add_corner_smoothing(&mut self, corner_smoothing: f32) { + self.fbb_.push_slot::(BasicShapeNode::VT_CORNER_SMOOTHING, corner_smoothing, 0.0); + } + #[inline] + pub fn add_fill_paints(&mut self, fill_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(BasicShapeNode::VT_FILL_PAINTS, fill_paints); + } + #[inline] + pub fn add_stroke_style(&mut self, stroke_style: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BasicShapeNode::VT_STROKE_STYLE, stroke_style); + } + #[inline] + pub fn add_stroke_width(&mut self, stroke_width: f32) { + self.fbb_.push_slot::(BasicShapeNode::VT_STROKE_WIDTH, stroke_width, 0.0); + } + #[inline] + pub fn add_stroke_width_profile(&mut self, stroke_width_profile: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(BasicShapeNode::VT_STROKE_WIDTH_PROFILE, stroke_width_profile); + } + #[inline] + pub fn add_rectangular_corner_radius(&mut self, rectangular_corner_radius: &RectangularCornerRadius) { + self.fbb_.push_slot_always::<&RectangularCornerRadius>(BasicShapeNode::VT_RECTANGULAR_CORNER_RADIUS, rectangular_corner_radius); + } + #[inline] + pub fn add_rectangular_stroke_width(&mut self, rectangular_stroke_width: &RectangularStrokeWidth) { + self.fbb_.push_slot_always::<&RectangularStrokeWidth>(BasicShapeNode::VT_RECTANGULAR_STROKE_WIDTH, rectangular_stroke_width); + } + #[inline] + pub fn add_stroke_paints(&mut self, stroke_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(BasicShapeNode::VT_STROKE_PAINTS, stroke_paints); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> BasicShapeNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + BasicShapeNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, BasicShapeNode::VT_NODE,"node"); + self.fbb_.required(o, BasicShapeNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for BasicShapeNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("BasicShapeNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.field("type_", &self.type_()); + ds.field("shape_type", &self.shape_type()); + match self.shape_type() { + CanonicalLayerShape::CanonicalShapeRectangular => { + if let Some(x) = self.shape_as_canonical_shape_rectangular() { + ds.field("shape", &x) + } else { + ds.field("shape", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + CanonicalLayerShape::CanonicalShapeElliptical => { + if let Some(x) = self.shape_as_canonical_shape_elliptical() { + ds.field("shape", &x) + } else { + ds.field("shape", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + CanonicalLayerShape::CanonicalShapePointsPolygon => { + if let Some(x) = self.shape_as_canonical_shape_points_polygon() { + ds.field("shape", &x) + } else { + ds.field("shape", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + CanonicalLayerShape::CanonicalShapeRegularPolygon => { + if let Some(x) = self.shape_as_canonical_shape_regular_polygon() { + ds.field("shape", &x) + } else { + ds.field("shape", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + CanonicalLayerShape::CanonicalShapeRegularStarPolygon => { + if let Some(x) = self.shape_as_canonical_shape_regular_star_polygon() { + ds.field("shape", &x) + } else { + ds.field("shape", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + CanonicalLayerShape::CanonicalShapePath => { + if let Some(x) = self.shape_as_canonical_shape_path() { + ds.field("shape", &x) + } else { + ds.field("shape", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("shape", &x) + }, + }; + ds.field("corner_radius", &self.corner_radius()); + ds.field("corner_smoothing", &self.corner_smoothing()); + ds.field("fill_paints", &self.fill_paints()); + ds.field("stroke_style", &self.stroke_style()); + ds.field("stroke_width", &self.stroke_width()); + ds.field("stroke_width_profile", &self.stroke_width_profile()); + ds.field("rectangular_corner_radius", &self.rectangular_corner_radius()); + ds.field("rectangular_stroke_width", &self.rectangular_stroke_width()); + ds.field("stroke_paints", &self.stroke_paints()); + ds.finish() + } +} +pub enum LineNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Line. +pub struct LineNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for LineNode<'a> { + type Inner = LineNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> LineNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + pub const VT_STROKE_GEOMETRY: ::flatbuffers::VOffsetT = 8; + pub const VT_STROKE_PAINTS: ::flatbuffers::VOffsetT = 10; + pub const VT_MARKER_START_SHAPE: ::flatbuffers::VOffsetT = 12; + pub const VT_MARKER_END_SHAPE: ::flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + LineNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LineNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = LineNodeBuilder::new(_fbb); + if let Some(x) = args.stroke_paints { builder.add_stroke_paints(x); } + if let Some(x) = args.stroke_geometry { builder.add_stroke_geometry(x); } + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.add_marker_end_shape(args.marker_end_shape); + builder.add_marker_start_shape(args.marker_start_shape); + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LineNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LineNode::VT_LAYER, None).unwrap()} + } + #[inline] + pub fn stroke_geometry(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(LineNode::VT_STROKE_GEOMETRY, None)} + } + #[inline] + pub fn stroke_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(LineNode::VT_STROKE_PAINTS, None)} + } + #[inline] + pub fn marker_start_shape(&self) -> StrokeMarkerPreset { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LineNode::VT_MARKER_START_SHAPE, Some(StrokeMarkerPreset::None)).unwrap()} + } + #[inline] + pub fn marker_end_shape(&self) -> StrokeMarkerPreset { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LineNode::VT_MARKER_END_SHAPE, Some(StrokeMarkerPreset::None)).unwrap()} + } +} + +impl ::flatbuffers::Verifiable for LineNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_geometry", Self::VT_STROKE_GEOMETRY, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("stroke_paints", Self::VT_STROKE_PAINTS, false)? + .visit_field::("marker_start_shape", Self::VT_MARKER_START_SHAPE, false)? + .visit_field::("marker_end_shape", Self::VT_MARKER_END_SHAPE, false)? + .finish(); + Ok(()) + } +} +pub struct LineNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, + pub stroke_geometry: Option<::flatbuffers::WIPOffset>>, + pub stroke_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub marker_start_shape: StrokeMarkerPreset, + pub marker_end_shape: StrokeMarkerPreset, +} +impl<'a> Default for LineNodeArgs<'a> { + #[inline] + fn default() -> Self { + LineNodeArgs { + node: None, // required field + layer: None, // required field + stroke_geometry: None, + stroke_paints: None, + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + } + } +} + +pub struct LineNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> LineNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LineNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LineNode::VT_LAYER, layer); + } + #[inline] + pub fn add_stroke_geometry(&mut self, stroke_geometry: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(LineNode::VT_STROKE_GEOMETRY, stroke_geometry); + } + #[inline] + pub fn add_stroke_paints(&mut self, stroke_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(LineNode::VT_STROKE_PAINTS, stroke_paints); + } + #[inline] + pub fn add_marker_start_shape(&mut self, marker_start_shape: StrokeMarkerPreset) { + self.fbb_.push_slot::(LineNode::VT_MARKER_START_SHAPE, marker_start_shape, StrokeMarkerPreset::None); + } + #[inline] + pub fn add_marker_end_shape(&mut self, marker_end_shape: StrokeMarkerPreset) { + self.fbb_.push_slot::(LineNode::VT_MARKER_END_SHAPE, marker_end_shape, StrokeMarkerPreset::None); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> LineNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LineNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, LineNode::VT_NODE,"node"); + self.fbb_.required(o, LineNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for LineNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("LineNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.field("stroke_geometry", &self.stroke_geometry()); + ds.field("stroke_paints", &self.stroke_paints()); + ds.field("marker_start_shape", &self.marker_start_shape()); + ds.field("marker_end_shape", &self.marker_end_shape()); + ds.finish() + } +} +pub enum VectorNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Vector. +pub struct VectorNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for VectorNode<'a> { + type Inner = VectorNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> VectorNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + pub const VT_STROKE_GEOMETRY: ::flatbuffers::VOffsetT = 8; + pub const VT_STROKE_PAINTS: ::flatbuffers::VOffsetT = 10; + pub const VT_MARKER_START_SHAPE: ::flatbuffers::VOffsetT = 12; + pub const VT_MARKER_END_SHAPE: ::flatbuffers::VOffsetT = 14; + pub const VT_CORNER_RADIUS: ::flatbuffers::VOffsetT = 16; + pub const VT_FILL_PAINTS: ::flatbuffers::VOffsetT = 18; + pub const VT_VECTOR_NETWORK_DATA: ::flatbuffers::VOffsetT = 20; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + VectorNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args VectorNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = VectorNodeBuilder::new(_fbb); + if let Some(x) = args.vector_network_data { builder.add_vector_network_data(x); } + if let Some(x) = args.fill_paints { builder.add_fill_paints(x); } + if let Some(x) = args.corner_radius { builder.add_corner_radius(x); } + if let Some(x) = args.stroke_paints { builder.add_stroke_paints(x); } + if let Some(x) = args.stroke_geometry { builder.add_stroke_geometry(x); } + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.add_marker_end_shape(args.marker_end_shape); + builder.add_marker_start_shape(args.marker_start_shape); + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(VectorNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(VectorNode::VT_LAYER, None).unwrap()} + } + #[inline] + pub fn stroke_geometry(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(VectorNode::VT_STROKE_GEOMETRY, None)} + } + #[inline] + pub fn stroke_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(VectorNode::VT_STROKE_PAINTS, None)} + } + #[inline] + pub fn marker_start_shape(&self) -> StrokeMarkerPreset { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(VectorNode::VT_MARKER_START_SHAPE, Some(StrokeMarkerPreset::None)).unwrap()} + } + #[inline] + pub fn marker_end_shape(&self) -> StrokeMarkerPreset { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(VectorNode::VT_MARKER_END_SHAPE, Some(StrokeMarkerPreset::None)).unwrap()} + } + #[inline] + pub fn corner_radius(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(VectorNode::VT_CORNER_RADIUS, None)} + } + #[inline] + pub fn fill_paints(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(VectorNode::VT_FILL_PAINTS, None)} + } + #[inline] + pub fn vector_network_data(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(VectorNode::VT_VECTOR_NETWORK_DATA, None)} + } +} + +impl ::flatbuffers::Verifiable for VectorNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("stroke_geometry", Self::VT_STROKE_GEOMETRY, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("stroke_paints", Self::VT_STROKE_PAINTS, false)? + .visit_field::("marker_start_shape", Self::VT_MARKER_START_SHAPE, false)? + .visit_field::("marker_end_shape", Self::VT_MARKER_END_SHAPE, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("corner_radius", Self::VT_CORNER_RADIUS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("fill_paints", Self::VT_FILL_PAINTS, false)? + .visit_field::<::flatbuffers::ForwardsUOffset>("vector_network_data", Self::VT_VECTOR_NETWORK_DATA, false)? + .finish(); + Ok(()) + } +} +pub struct VectorNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, + pub stroke_geometry: Option<::flatbuffers::WIPOffset>>, + pub stroke_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub marker_start_shape: StrokeMarkerPreset, + pub marker_end_shape: StrokeMarkerPreset, + pub corner_radius: Option<::flatbuffers::WIPOffset>>, + pub fill_paints: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub vector_network_data: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for VectorNodeArgs<'a> { + #[inline] + fn default() -> Self { + VectorNodeArgs { + node: None, // required field + layer: None, // required field + stroke_geometry: None, + stroke_paints: None, + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + corner_radius: None, + fill_paints: None, + vector_network_data: None, + } + } +} + +pub struct VectorNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> VectorNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(VectorNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(VectorNode::VT_LAYER, layer); + } + #[inline] + pub fn add_stroke_geometry(&mut self, stroke_geometry: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(VectorNode::VT_STROKE_GEOMETRY, stroke_geometry); + } + #[inline] + pub fn add_stroke_paints(&mut self, stroke_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNode::VT_STROKE_PAINTS, stroke_paints); + } + #[inline] + pub fn add_marker_start_shape(&mut self, marker_start_shape: StrokeMarkerPreset) { + self.fbb_.push_slot::(VectorNode::VT_MARKER_START_SHAPE, marker_start_shape, StrokeMarkerPreset::None); + } + #[inline] + pub fn add_marker_end_shape(&mut self, marker_end_shape: StrokeMarkerPreset) { + self.fbb_.push_slot::(VectorNode::VT_MARKER_END_SHAPE, marker_end_shape, StrokeMarkerPreset::None); + } + #[inline] + pub fn add_corner_radius(&mut self, corner_radius: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(VectorNode::VT_CORNER_RADIUS, corner_radius); + } + #[inline] + pub fn add_fill_paints(&mut self, fill_paints: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(VectorNode::VT_FILL_PAINTS, fill_paints); + } + #[inline] + pub fn add_vector_network_data(&mut self, vector_network_data: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(VectorNode::VT_VECTOR_NETWORK_DATA, vector_network_data); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> VectorNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + VectorNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, VectorNode::VT_NODE,"node"); + self.fbb_.required(o, VectorNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for VectorNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("VectorNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.field("stroke_geometry", &self.stroke_geometry()); + ds.field("stroke_paints", &self.stroke_paints()); + ds.field("marker_start_shape", &self.marker_start_shape()); + ds.field("marker_end_shape", &self.marker_end_shape()); + ds.field("corner_radius", &self.corner_radius()); + ds.field("fill_paints", &self.fill_paints()); + ds.field("vector_network_data", &self.vector_network_data()); + ds.finish() + } +} +pub enum TextSpanNodeOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Node variant: Text span. +pub struct TextSpanNode<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for TextSpanNode<'a> { + type Inner = TextSpanNode<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> TextSpanNode<'a> { + pub const VT_NODE: ::flatbuffers::VOffsetT = 4; + pub const VT_LAYER: ::flatbuffers::VOffsetT = 6; + pub const VT_PROPERTIES: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + TextSpanNode { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TextSpanNodeArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = TextSpanNodeBuilder::new(_fbb); + if let Some(x) = args.properties { builder.add_properties(x); } + if let Some(x) = args.layer { builder.add_layer(x); } + if let Some(x) = args.node { builder.add_node(x); } + builder.finish() + } + + + #[inline] + pub fn node(&self) -> SystemNodeTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextSpanNode::VT_NODE, None).unwrap()} + } + #[inline] + pub fn layer(&self) -> LayerTrait<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextSpanNode::VT_LAYER, None).unwrap()} + } + #[inline] + pub fn properties(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(TextSpanNode::VT_PROPERTIES, None)} + } +} + +impl ::flatbuffers::Verifiable for TextSpanNode<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("node", Self::VT_NODE, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("layer", Self::VT_LAYER, true)? + .visit_field::<::flatbuffers::ForwardsUOffset>("properties", Self::VT_PROPERTIES, false)? + .finish(); + Ok(()) + } +} +pub struct TextSpanNodeArgs<'a> { + pub node: Option<::flatbuffers::WIPOffset>>, + pub layer: Option<::flatbuffers::WIPOffset>>, + pub properties: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for TextSpanNodeArgs<'a> { + #[inline] + fn default() -> Self { + TextSpanNodeArgs { + node: None, // required field + layer: None, // required field + properties: None, + } + } +} + +pub struct TextSpanNodeBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> TextSpanNodeBuilder<'a, 'b, A> { + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextSpanNode::VT_NODE, node); + } + #[inline] + pub fn add_layer(&mut self, layer: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextSpanNode::VT_LAYER, layer); + } + #[inline] + pub fn add_properties(&mut self, properties: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(TextSpanNode::VT_PROPERTIES, properties); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> TextSpanNodeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TextSpanNodeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, TextSpanNode::VT_NODE,"node"); + self.fbb_.required(o, TextSpanNode::VT_LAYER,"layer"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for TextSpanNode<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("TextSpanNode"); + ds.field("node", &self.node()); + ds.field("layer", &self.layer()); + ds.field("properties", &self.properties()); + ds.finish() + } +} +pub enum NodeSlotOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Wrapper table for a single Node union entry in a flat node vector. +/// +/// FlatBuffers Rust codegen does not support `[Node]` (vector of unions) directly. +/// Using `[NodeSlot]` (vector of tables each containing a union field) is the +/// canonical workaround — identical in spirit to `PaintStackItem` wrapping `Paint`. +/// See: https://github.com/google/flatbuffers/issues/8011 +pub struct NodeSlot<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for NodeSlot<'a> { + type Inner = NodeSlot<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> NodeSlot<'a> { + pub const VT_NODE_TYPE: ::flatbuffers::VOffsetT = 4; + pub const VT_NODE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + NodeSlot { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args NodeSlotArgs + ) -> ::flatbuffers::WIPOffset> { + let mut builder = NodeSlotBuilder::new(_fbb); + if let Some(x) = args.node { builder.add_node(x); } + builder.add_node_type(args.node_type); + builder.finish() + } + + + #[inline] + pub fn node_type(&self) -> Node { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(NodeSlot::VT_NODE_TYPE, Some(Node::NONE)).unwrap()} + } + #[inline] + pub fn node(&self) -> Option<::flatbuffers::Table<'a>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>(NodeSlot::VT_NODE, None)} + } + #[inline] + #[allow(non_snake_case)] + pub fn node_as_unknown_node(&self) -> Option> { + if self.node_type() == Node::UnknownNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { UnknownNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_scene_node(&self) -> Option> { + if self.node_type() == Node::SceneNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { SceneNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_group_node(&self) -> Option> { + if self.node_type() == Node::GroupNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { GroupNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_initial_container_node(&self) -> Option> { + if self.node_type() == Node::InitialContainerNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { InitialContainerNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_container_node(&self) -> Option> { + if self.node_type() == Node::ContainerNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { ContainerNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_boolean_operation_node(&self) -> Option> { + if self.node_type() == Node::BooleanOperationNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { BooleanOperationNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_basic_shape_node(&self) -> Option> { + if self.node_type() == Node::BasicShapeNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { BasicShapeNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_line_node(&self) -> Option> { + if self.node_type() == Node::LineNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LineNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_vector_node(&self) -> Option> { + if self.node_type() == Node::VectorNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { VectorNode::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn node_as_text_span_node(&self) -> Option> { + if self.node_type() == Node::TextSpanNode { + self.node().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { TextSpanNode::init_from_table(t) } + }) + } else { + None + } + } + +} + +impl ::flatbuffers::Verifiable for NodeSlot<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_union::("node_type", Self::VT_NODE_TYPE, "node", Self::VT_NODE, false, |key, v, pos| { + match key { + Node::UnknownNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::UnknownNode", pos), + Node::SceneNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::SceneNode", pos), + Node::GroupNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::GroupNode", pos), + Node::InitialContainerNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::InitialContainerNode", pos), + Node::ContainerNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::ContainerNode", pos), + Node::BooleanOperationNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::BooleanOperationNode", pos), + Node::BasicShapeNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::BasicShapeNode", pos), + Node::LineNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::LineNode", pos), + Node::VectorNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::VectorNode", pos), + Node::TextSpanNode => v.verify_union_variant::<::flatbuffers::ForwardsUOffset>("Node::TextSpanNode", pos), + _ => Ok(()), + } + })? + .finish(); + Ok(()) + } +} +pub struct NodeSlotArgs { + pub node_type: Node, + pub node: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, +} +impl<'a> Default for NodeSlotArgs { + #[inline] + fn default() -> Self { + NodeSlotArgs { + node_type: Node::NONE, + node: None, + } + } +} + +pub struct NodeSlotBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> NodeSlotBuilder<'a, 'b, A> { + #[inline] + pub fn add_node_type(&mut self, node_type: Node) { + self.fbb_.push_slot::(NodeSlot::VT_NODE_TYPE, node_type, Node::NONE); + } + #[inline] + pub fn add_node(&mut self, node: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(NodeSlot::VT_NODE, node); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> NodeSlotBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NodeSlotBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for NodeSlot<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("NodeSlot"); + ds.field("node_type", &self.node_type()); + match self.node_type() { + Node::UnknownNode => { + if let Some(x) = self.node_as_unknown_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::SceneNode => { + if let Some(x) = self.node_as_scene_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::GroupNode => { + if let Some(x) = self.node_as_group_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::InitialContainerNode => { + if let Some(x) = self.node_as_initial_container_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::ContainerNode => { + if let Some(x) = self.node_as_container_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::BooleanOperationNode => { + if let Some(x) = self.node_as_boolean_operation_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::BasicShapeNode => { + if let Some(x) = self.node_as_basic_shape_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::LineNode => { + if let Some(x) = self.node_as_line_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::VectorNode => { + if let Some(x) = self.node_as_vector_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + Node::TextSpanNode => { + if let Some(x) = self.node_as_text_span_node() { + ds.field("node", &x) + } else { + ds.field("node", &"InvalidFlatbuffer: Union discriminant does not match value.") + } + }, + _ => { + let x: Option<()> = None; + ds.field("node", &x) + }, + }; + ds.finish() + } +} +pub enum CanvasDocumentOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct CanvasDocument<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for CanvasDocument<'a> { + type Inner = CanvasDocument<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> CanvasDocument<'a> { + pub const VT_SCHEMA_VERSION: ::flatbuffers::VOffsetT = 4; + pub const VT_NODES: ::flatbuffers::VOffsetT = 6; + pub const VT_SCENES: ::flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + CanvasDocument { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CanvasDocumentArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = CanvasDocumentBuilder::new(_fbb); + if let Some(x) = args.scenes { builder.add_scenes(x); } + if let Some(x) = args.nodes { builder.add_nodes(x); } + if let Some(x) = args.schema_version { builder.add_schema_version(x); } + builder.finish() + } + + + /// Schema version string (keep in sync with TS `grida.program.document.SCHEMA_VERSION`). + #[inline] + pub fn schema_version(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<&str>>(CanvasDocument::VT_SCHEMA_VERSION, None)} + } + /// Flat node repository. + /// + /// Stored as `[NodeSlot]` (vector of wrapper tables) rather than `[Node]` + /// (vector of unions) to maintain compatibility with Rust FlatBuffers codegen, + /// which does not support vectors of unions directly. + #[inline] + pub fn nodes(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(CanvasDocument::VT_NODES, None)} + } + /// Scene node ids (scene nodes are also stored in `nodes`). + #[inline] + pub fn scenes(&self) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>(CanvasDocument::VT_SCENES, None)} + } +} + +impl ::flatbuffers::Verifiable for CanvasDocument<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("schema_version", Self::VT_SCHEMA_VERSION, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("nodes", Self::VT_NODES, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>>>("scenes", Self::VT_SCENES, false)? + .finish(); + Ok(()) + } +} +pub struct CanvasDocumentArgs<'a> { + pub schema_version: Option<::flatbuffers::WIPOffset<&'a str>>, + pub nodes: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, + pub scenes: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>>>, +} +impl<'a> Default for CanvasDocumentArgs<'a> { + #[inline] + fn default() -> Self { + CanvasDocumentArgs { + schema_version: None, + nodes: None, + scenes: None, + } + } +} + +pub struct CanvasDocumentBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> CanvasDocumentBuilder<'a, 'b, A> { + #[inline] + pub fn add_schema_version(&mut self, schema_version: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(CanvasDocument::VT_SCHEMA_VERSION, schema_version); + } + #[inline] + pub fn add_nodes(&mut self, nodes: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(CanvasDocument::VT_NODES, nodes); + } + #[inline] + pub fn add_scenes(&mut self, scenes: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b , ::flatbuffers::ForwardsUOffset>>>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>(CanvasDocument::VT_SCENES, scenes); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> CanvasDocumentBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CanvasDocumentBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for CanvasDocument<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("CanvasDocument"); + ds.field("schema_version", &self.schema_version()); + ds.field("nodes", &self.nodes()); + ds.field("scenes", &self.scenes()); + ds.finish() + } +} +pub enum GridaFileOffset {} +#[derive(Copy, Clone, PartialEq)] + +/// Top-level wrapper (allows future multi-document bundles, signatures, etc.) +pub struct GridaFile<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for GridaFile<'a> { + type Inner = GridaFile<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: unsafe { ::flatbuffers::Table::new(buf, loc) } } + } +} + +impl<'a> GridaFile<'a> { + pub const VT_DOCUMENT: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + GridaFile { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: ::flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args GridaFileArgs<'args> + ) -> ::flatbuffers::WIPOffset> { + let mut builder = GridaFileBuilder::new(_fbb); + if let Some(x) = args.document { builder.add_document(x); } + builder.finish() + } + + + #[inline] + pub fn document(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::<::flatbuffers::ForwardsUOffset>(GridaFile::VT_DOCUMENT, None)} + } +} + +impl ::flatbuffers::Verifiable for GridaFile<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, pos: usize + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset>("document", Self::VT_DOCUMENT, false)? + .finish(); + Ok(()) + } +} +pub struct GridaFileArgs<'a> { + pub document: Option<::flatbuffers::WIPOffset>>, +} +impl<'a> Default for GridaFileArgs<'a> { + #[inline] + fn default() -> Self { + GridaFileArgs { + document: None, + } + } +} + +pub struct GridaFileBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> GridaFileBuilder<'a, 'b, A> { + #[inline] + pub fn add_document(&mut self, document: ::flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset>(GridaFile::VT_DOCUMENT, document); + } + #[inline] + pub fn new(_fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>) -> GridaFileBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + GridaFileBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for GridaFile<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("GridaFile"); + ds.field("document", &self.document()); + ds.finish() + } +} +#[inline] +/// Verifies that a buffer of bytes contains a `GridaFile` +/// and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_grida_file_unchecked`. +pub fn root_as_grida_file(buf: &[u8]) -> Result, ::flatbuffers::InvalidFlatbuffer> { + ::flatbuffers::root::(buf) +} +#[inline] +/// Verifies that a buffer of bytes contains a size prefixed +/// `GridaFile` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `size_prefixed_root_as_grida_file_unchecked`. +pub fn size_prefixed_root_as_grida_file(buf: &[u8]) -> Result, ::flatbuffers::InvalidFlatbuffer> { + ::flatbuffers::size_prefixed_root::(buf) +} +#[inline] +/// Verifies, with the given options, that a buffer of bytes +/// contains a `GridaFile` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_grida_file_unchecked`. +pub fn root_as_grida_file_with_opts<'b, 'o>( + opts: &'o ::flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result, ::flatbuffers::InvalidFlatbuffer> { + ::flatbuffers::root_with_opts::>(opts, buf) +} +#[inline] +/// Verifies, with the given verifier options, that a buffer of +/// bytes contains a size prefixed `GridaFile` and returns +/// it. Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_grida_file_unchecked`. +pub fn size_prefixed_root_as_grida_file_with_opts<'b, 'o>( + opts: &'o ::flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result, ::flatbuffers::InvalidFlatbuffer> { + ::flatbuffers::size_prefixed_root_with_opts::>(opts, buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a GridaFile and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid `GridaFile`. +pub unsafe fn root_as_grida_file_unchecked(buf: &[u8]) -> GridaFile<'_> { + unsafe { ::flatbuffers::root_unchecked::(buf) } +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a size prefixed GridaFile and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid size prefixed `GridaFile`. +pub unsafe fn size_prefixed_root_as_grida_file_unchecked(buf: &[u8]) -> GridaFile<'_> { + unsafe { ::flatbuffers::size_prefixed_root_unchecked::(buf) } +} +pub const GRIDA_FILE_IDENTIFIER: &str = "GRID"; + +#[inline] +pub fn grida_file_buffer_has_identifier(buf: &[u8]) -> bool { + ::flatbuffers::buffer_has_identifier(buf, GRIDA_FILE_IDENTIFIER, false) +} + +#[inline] +pub fn grida_file_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool { + ::flatbuffers::buffer_has_identifier(buf, GRIDA_FILE_IDENTIFIER, true) +} + +pub const GRIDA_FILE_EXTENSION: &str = "grida"; + +#[inline] +pub fn finish_grida_file_buffer<'a, 'b, A: ::flatbuffers::Allocator + 'a>( + fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + root: ::flatbuffers::WIPOffset>) { + fbb.finish(root, Some(GRIDA_FILE_IDENTIFIER)); +} + +#[inline] +pub fn finish_size_prefixed_grida_file_buffer<'a, 'b, A: ::flatbuffers::Allocator + 'a>(fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, root: ::flatbuffers::WIPOffset>) { + fbb.finish_size_prefixed(root, Some(GRIDA_FILE_IDENTIFIER)); +} +} // pub mod grida + diff --git a/crates/grida-canvas/src/io/generated/mod.rs b/crates/grida-canvas/src/io/generated/mod.rs new file mode 100644 index 0000000000..ab7de45c21 --- /dev/null +++ b/crates/grida-canvas/src/io/generated/mod.rs @@ -0,0 +1,18 @@ +//! Auto-generated FlatBuffers bindings — **committed to git**. +//! +//! Generated by `flatc --rust` from `format/grida.fbs`. Do not edit by hand. +//! This file is committed because `io_grida_fbs.rs` and the test fixtures +//! compile against its types; keeping it in-tree lets `cargo build` work from +//! a clean checkout without requiring `flatc`. +//! +//! To regenerate (from the repo root): +//! +//! ```sh +//! pnpm --filter @crates/grida-canvas generate +//! ``` +//! +//! CI verifies freshness — if `grida.fbs` changes but this file is stale, +//! the `check-generated-fbs` workflow will fail. + +#[allow(dead_code, unused_imports, clippy::all)] +pub mod grida; diff --git a/crates/grida-canvas/src/io/io_grida_fbs.rs b/crates/grida-canvas/src/io/io_grida_fbs.rs new file mode 100644 index 0000000000..1919a49378 --- /dev/null +++ b/crates/grida-canvas/src/io/io_grida_fbs.rs @@ -0,0 +1,3281 @@ +//! FlatBuffers (`.grida`) → Rust runtime decoder. +//! +//! Converts a `GridaFile` FlatBuffers binary into a `Scene`. +//! +//! The FBS document stores nodes in a flat `[NodeSlot]` list. Each layer node +//! carries a `ParentReference` (parent id + fractional-index position string) +//! so the tree can be reconstructed after decoding. Scene nodes are the roots. +//! +//! ## Usage +//! +//! ```no_run +//! use cg::io::io_grida_fbs; +//! let bytes = std::fs::read("example.grida").unwrap(); +//! let scene = io_grida_fbs::decode(&bytes).unwrap(); +//! ``` + +use std::collections::HashMap; + +use math2::{box_fit::BoxFit, transform::AffineTransform}; + +use crate::cg::{ + alignment::Alignment, + color::CGColor, + fe::{ + FeBackdropBlur, FeBlur, FeGaussianBlur, FeLayerBlur, FeLiquidGlass, FeNoiseEffect, + FeShadow, FilterShadowEffect, NoiseEffectColors, + }, + stroke_dasharray::StrokeDashArray, + stroke_width::{RectangularStrokeWidth, SingularStrokeWidth, StrokeWidth}, + tilemode::TileMode, + types::{ + Axis, BlendMode, BooleanPathOperation, CGPoint, ContainerClipFlag, CornerSmoothing, + CrossAxisAlignment, EdgeInsets, FontWeight, GradientStop, ImageFilters, ImagePaint, + ImagePaintFit, LayerBlendMode, LayerMaskType, LayoutGap, LayoutMode, LayoutPositioning, + LayoutWrap, LinearGradientPaint, MainAxisAlignment, Paint, Paints, RadialGradientPaint, + RectangularCornerRadius, ResourceRef, SolidPaint, StrokeAlign, StrokeCap, StrokeJoin, + StrokeMarkerPreset, StrokeMiterLimit, SweepGradientPaint, TextAlign, TextAlignVertical, + TextStyleRec, + }, +}; +use crate::node::{ + id::NodeIdGenerator, + scene_graph::SceneGraph, + schema::{ + BooleanPathOperationNodeRec, ContainerNodeRec, EllipseNodeRec, GroupNodeRec, + InitialContainerNodeRec, LayerEffects, LayoutChildStyle, LayoutContainerStyle, + LayoutDimensionStyle, LayoutPositioningBasis, LineNodeRec, Node, RectangleNodeRec, + RegularPolygonNodeRec, RegularStarPolygonNodeRec, Scene, Size, StrokeStyle, + TextSpanNodeRec, VectorNodeRec, + }, +}; +use crate::vectornetwork::{ + VectorNetwork, VectorNetworkLoop, VectorNetworkRegion, VectorNetworkSegment, +}; + +use super::generated::grida::grida as fbs; + +// ───────────────────────────────────────────────────────────────────────────── +// Error type +// ───────────────────────────────────────────────────────────────────────────── + +#[derive(Debug)] +pub enum FbsDecodeError { + InvalidBuffer(flatbuffers::InvalidFlatbuffer), + MissingDocument, + MissingScene, +} + +impl std::fmt::Display for FbsDecodeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + FbsDecodeError::InvalidBuffer(e) => write!(f, "invalid FlatBuffer: {e}"), + FbsDecodeError::MissingDocument => write!(f, "GridaFile.document is null"), + FbsDecodeError::MissingScene => write!(f, "document has no scenes"), + } + } +} + +impl std::error::Error for FbsDecodeError {} + +impl From for FbsDecodeError { + fn from(e: flatbuffers::InvalidFlatbuffer) -> Self { + FbsDecodeError::InvalidBuffer(e) + } +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Shared intermediate structs — decoded once, threaded into every node builder +// ═════════════════════════════════════════════════════════════════════════════ + +/// Fields common to every layer-bearing node, decoded from `LayerTrait`. +struct LayerCommon { + active: bool, + opacity: f32, + blend_mode: LayerBlendMode, + mask: Option, + effects: LayerEffects, + /// Rotation in degrees — only used by Container/InitialContainer nodes + /// that store rotation as a plain f32 field. For shape/line/text nodes + /// use `rotation_cos_sin` instead to avoid lossy degree conversion. + rotation: f32, + /// Raw (cos, sin) extracted directly from the FBS `CGTransform2D` matrix. + /// Used by shape/line/text/group decoders to build transforms without + /// lossy `atan2 → to_degrees → to_radians → sin_cos` round-trips. + rotation_cos_sin: (f32, f32), + layout_child: Option, +} + +/// Spatial properties for shape-like nodes (everything except Container and +/// InitialContainer) — position, size, and the transform that bakes them together. +struct ShapeLayout { + x: f32, + y: f32, + size: Size, + /// `from_box_center(x, y, w, h, rotation)` — the canonical shape transform. + transform: AffineTransform, + width: Option, + height: Option, +} + +fn decode_layer_common(sys: &fbs::SystemNodeTrait<'_>, layer: &fbs::LayerTrait<'_>) -> LayerCommon { + let layout = layer.layout(); + let plt = layer.post_layout_transform(); + LayerCommon { + active: sys.active(), + opacity: layer.opacity(), + blend_mode: decode_layer_blend_mode(layer.blend_mode()), + mask: decode_mask_type(layer.mask_type_type()), + effects: decode_layer_effects(layer.effects()), + rotation: extract_rotation_degrees(plt), + rotation_cos_sin: extract_rotation_cos_sin(plt), + layout_child: layout.as_ref().and_then(decode_layout_child_style), + } +} + +fn decode_shape_layout(layer: &fbs::LayerTrait<'_>, cos_sin: (f32, f32)) -> ShapeLayout { + let layout = layer.layout(); + let (x, y) = layout.as_ref().map(decode_layout_xy).unwrap_or((0.0, 0.0)); + let (w, h) = layout.as_ref().map(decode_dimensions).unwrap_or((None, None)); + let size = Size { + width: w.unwrap_or(0.0), + height: h.unwrap_or(0.0), + }; + let (cos, sin) = cos_sin; + let transform = + AffineTransform::from_box_center_raw(x, y, size.width, size.height, cos, sin); + ShapeLayout { + x, + y, + size, + transform, + width: w, + height: h, + } +} + +fn singular_stroke_width(w: f32) -> SingularStrokeWidth { + if w == 0.0 { + SingularStrokeWidth(None) + } else { + SingularStrokeWidth(Some(w)) + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Public entry point +// ───────────────────────────────────────────────────────────────────────────── + +/// Result of decoding a `.grida` FlatBuffers binary that also carries the +/// ID mapping needed for re-encoding. +pub struct DecodeResult { + /// The decoded scenes (typically one). + pub scenes: Vec, + /// Mapping from internal `NodeId` to the original string IDs stored in the + /// FBS file. Required for [`encode`] round-trips. + pub id_map: HashMap, + /// The string IDs of the scene nodes, in document order. + pub scene_ids: Vec, + /// Mapping from internal `NodeId` to the original fractional-index position + /// string stored in the FBS file. Required for [`encode`] round-trips so + /// that child ordering is preserved exactly. + pub position_map: HashMap, +} + +/// Decode a `.grida` FlatBuffers binary into a `Scene`. +/// +/// Picks the first scene listed in `CanvasDocument.scenes`. If the document +/// contains multiple scenes, use `decode_all` and index into the result. +pub fn decode(bytes: &[u8]) -> Result { + let scenes = decode_all(bytes)?; + scenes + .into_iter() + .next() + .ok_or(FbsDecodeError::MissingScene) +} + +/// Decode a `.grida` FlatBuffers binary and return the full [`DecodeResult`] +/// including the ID mapping needed for round-trip encoding. +pub fn decode_with_id_map(bytes: &[u8]) -> Result { + decode_all_inner(bytes) +} + +/// Decode a `.grida` FlatBuffers binary into all `Scene`s present in the document. +pub fn decode_all(bytes: &[u8]) -> Result, FbsDecodeError> { + decode_all_inner(bytes).map(|r| r.scenes) +} + +fn decode_all_inner(bytes: &[u8]) -> Result { + let grida_file = flatbuffers::root::(bytes)?; + let document = grida_file + .document() + .ok_or(FbsDecodeError::MissingDocument)?; + + // ── 1. Collect scene node IDs (scene ordering) ────────────────────────── + let mut scene_ids_ordered: Vec = Vec::new(); + if let Some(scenes_vec) = document.scenes() { + for i in 0..scenes_vec.len() { + scene_ids_ordered.push(scenes_vec.get(i).id().to_owned()); + } + } + + // ── 2. Decode all node slots ───────────────────────────────────────────── + + struct NodeEntry { + id: String, + parent: Option<(String, String)>, // (parent_id, fractional-index position) + node: Node, + } + + struct SceneMeta { + #[allow(dead_code)] + id: String, + name: String, + background_color: Option, + } + + let mut node_entries: Vec = Vec::new(); + let mut scene_metas: HashMap = HashMap::new(); + + /// Helper macro: every layer-bearing node type follows the same pattern of + /// extracting `sys`, `layer`, `id`, `parent` from the slot, then calling a + /// decoder. This macro eliminates that boilerplate and makes it impossible + /// to forget any step. + macro_rules! decode_layer_node { + ($slot:expr, $accessor:ident, $decode_fn:expr) => { + if let Some(typed) = $slot.$accessor() { + let sys = typed.node(); + let layer = typed.layer(); + let id = sys.id().id().to_owned(); + let parent = decode_parent_ref(&layer); + let lc = decode_layer_common(&sys, &layer); + let node = $decode_fn(&lc, &layer, &typed); + node_entries.push(NodeEntry { id, parent, node }); + } + }; + } + + if let Some(nodes_vec) = document.nodes() { + for i in 0..nodes_vec.len() { + let slot = nodes_vec.get(i); + match slot.node_type() { + fbs::Node::SceneNode => { + if let Some(sn) = slot.node_as_scene_node() { + let sys = sn.node(); + let id = sys.id().id().to_owned(); + let name = sys.name().unwrap_or("").to_owned(); + let bg = sn + .scene_background_color() + .map(decode_rgba32f_to_cg_color); + scene_metas.insert( + id.clone(), + SceneMeta { + id, + name, + background_color: bg, + }, + ); + } + } + fbs::Node::GroupNode => { + decode_layer_node!(slot, node_as_group_node, decode_group_node); + } + fbs::Node::ContainerNode => { + decode_layer_node!(slot, node_as_container_node, decode_container_node); + } + fbs::Node::InitialContainerNode => { + decode_layer_node!( + slot, + node_as_initial_container_node, + decode_initial_container_node + ); + } + fbs::Node::BasicShapeNode => { + decode_layer_node!(slot, node_as_basic_shape_node, decode_basic_shape_node); + } + fbs::Node::VectorNode => { + decode_layer_node!(slot, node_as_vector_node, decode_vector_node); + } + fbs::Node::LineNode => { + decode_layer_node!(slot, node_as_line_node, decode_line_node); + } + fbs::Node::TextSpanNode => { + decode_layer_node!(slot, node_as_text_span_node, decode_text_span_node); + } + fbs::Node::BooleanOperationNode => { + decode_layer_node!( + slot, + node_as_boolean_operation_node, + decode_boolean_operation_node + ); + } + fbs::Node::UnknownNode | fbs::Node::NONE | _ => {} + } + } + } + + // ── 3. Build ID mapping (string → internal NodeId) ─────────────────────── + let mut string_to_internal_id: HashMap = HashMap::new(); + let mut id_generator = NodeIdGenerator::new(); + + for e in &node_entries { + string_to_internal_id + .entry(e.id.clone()) + .or_insert_with(|| id_generator.next()); + } + for (sid, _) in &scene_metas { + string_to_internal_id + .entry(sid.clone()) + .or_insert_with(|| id_generator.next()); + } + + let get_id = |s: &String| string_to_internal_id.get(s).copied(); + + // ── 4. Build children_by_parent (sorted by fractional index) ───────────── + let mut children_by_parent: HashMap> = HashMap::new(); + for e in &node_entries { + if let Some((parent_id, position)) = &e.parent { + children_by_parent + .entry(parent_id.clone()) + .or_default() + .push((e.id.clone(), position.clone())); + } + } + for children in children_by_parent.values_mut() { + children.sort_by(|a, b| a.1.cmp(&b.1)); + } + + let node_pairs: Vec<_> = node_entries + .iter() + .filter_map(|e| Some((get_id(&e.id)?, e.node.clone()))) + .collect(); + + let internal_links: HashMap<_, Vec<_>> = children_by_parent + .iter() + .filter_map(|(parent_str, children)| { + let parent_internal = get_id(parent_str)?; + let child_internals: Vec<_> = children + .iter() + .filter_map(|(child_str, _)| get_id(child_str)) + .collect(); + if child_internals.is_empty() { + None + } else { + Some((parent_internal, child_internals)) + } + }) + .collect(); + + // ── 5. Produce one Scene per listed scene id ────────────────────────────── + let mut scenes: Vec = Vec::new(); + + let iter: Box> = if !scene_ids_ordered.is_empty() { + Box::new(scene_ids_ordered.iter()) + } else { + Box::new(scene_metas.keys()) + }; + + for scene_id_str in iter { + let meta = scene_metas.get(scene_id_str); + let name = meta.map(|m| m.name.clone()).unwrap_or_default(); + let background_color = meta.and_then(|m| m.background_color); + + let roots_strings = children_by_parent + .get(scene_id_str) + .cloned() + .unwrap_or_default(); + let roots_internal: Vec<_> = roots_strings + .iter() + .filter_map(|(child_str, _)| get_id(child_str)) + .collect(); + + let graph = SceneGraph::new_from_snapshot( + node_pairs.clone(), + internal_links.clone(), + roots_internal, + ); + + scenes.push(Scene { + name, + graph, + background_color, + }); + } + + // Build the reverse id map (internal → string) for round-trip encoding. + let id_map: HashMap = string_to_internal_id + .iter() + .map(|(s, &nid)| (nid, s.clone())) + .collect(); + + // Build the position map (internal NodeId → original position string) + // so the encoder can preserve child ordering exactly. + let mut position_map: HashMap = HashMap::new(); + for e in &node_entries { + if let Some((_parent_id, position)) = &e.parent { + if let Some(&nid) = string_to_internal_id.get(&e.id) { + position_map.insert(nid, position.clone()); + } + } + } + + Ok(DecodeResult { + scenes, + id_map, + scene_ids: scene_ids_ordered, + position_map, + }) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Hierarchy helpers +// ───────────────────────────────────────────────────────────────────────────── + +fn decode_parent_ref(layer: &fbs::LayerTrait<'_>) -> Option<(String, String)> { + let parent_ref = layer.parent(); + let parent_id = parent_ref.parent_id().id().to_owned(); + if parent_id.is_empty() { + return None; + } + let position = parent_ref.position().unwrap_or("").to_owned(); + Some((parent_id, position)) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Color helpers +// ───────────────────────────────────────────────────────────────────────────── + +fn decode_rgba32f_to_cg_color(rgba: &fbs::RGBA32F) -> CGColor { + CGColor { + r: (rgba.r().clamp(0.0, 1.0) * 255.0).round() as u8, + g: (rgba.g().clamp(0.0, 1.0) * 255.0).round() as u8, + b: (rgba.b().clamp(0.0, 1.0) * 255.0).round() as u8, + a: (rgba.a().clamp(0.0, 1.0) * 255.0).round() as u8, + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Enum mapping macro + all enum conversions +// ───────────────────────────────────────────────────────────────────────────── + +/// Generate a matched pair of `decode_*` and `encode_*` functions for a +/// 1-to-1 enum mapping between an FBS type and a Rust type. +/// +/// Syntax: +/// `enum_map!(decode_fn, encode_fn, FbsType, RustType, default, { Variant1, Variant2, … });` +/// +/// The `default` is returned by `decode_fn` when the FBS value doesn't match +/// any listed variant (forward-compat for new enum members added later). +macro_rules! enum_map { + ($decode:ident, $encode:ident, $fbs:ty, $rust:ty, $default:expr, { $($v:ident),+ $(,)? }) => { + fn $decode(v: $fbs) -> $rust { + match v { $( <$fbs>::$v => <$rust>::$v, )+ _ => $default, } + } + fn $encode(v: $rust) -> $fbs { + match v { $( <$rust>::$v => <$fbs>::$v, )+ } + } + }; +} + +enum_map!(decode_blend_mode, encode_blend_mode, fbs::BlendMode, BlendMode, BlendMode::Normal, { + Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, + HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity, +}); + +fn decode_layer_blend_mode(fbs_mode: fbs::LayerBlendMode) -> LayerBlendMode { + match fbs_mode { + fbs::LayerBlendMode::PassThrough => LayerBlendMode::PassThrough, + other => LayerBlendMode::Blend(decode_blend_mode(fbs::BlendMode(other.0))), + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Paint decoding +// ───────────────────────────────────────────────────────────────────────────── + +fn decode_gradient_stops(stops: flatbuffers::Vector<'_, fbs::GradientStop>) -> Vec { + (0..stops.len()) + .map(|i| { + let s = stops.get(i); + let color = decode_rgba32f_to_cg_color(s.stop_color()); + GradientStop { + offset: s.stop_offset(), + color, + } + }) + .collect() +} + +fn decode_fbs_transform(t: &fbs::CGTransform2D) -> AffineTransform { + AffineTransform::from_acebdf(t.m00(), t.m01(), t.m02(), t.m10(), t.m11(), t.m12()) +} + +fn extract_rotation_degrees(transform: Option<&fbs::CGTransform2D>) -> f32 { + match transform { + Some(t) => t.m10().atan2(t.m00()).to_degrees(), + None => 0.0, + } +} + +/// Extract the raw (cos, sin) pair directly from the `CGTransform2D` matrix, +/// avoiding the lossy `atan2 → to_degrees → to_radians → sin_cos` chain. +fn extract_rotation_cos_sin(transform: Option<&fbs::CGTransform2D>) -> (f32, f32) { + match transform { + Some(t) => (t.m00(), t.m10()), + None => (1.0, 0.0), + } +} + +fn decode_paint_item(item: &fbs::PaintStackItem<'_>) -> Option { + match item.paint_type() { + fbs::Paint::SolidPaint => { + let sp = item.paint_as_solid_paint()?; + let color = sp + .color() + .map(decode_rgba32f_to_cg_color) + .unwrap_or(CGColor::TRANSPARENT); + Some(Paint::Solid(SolidPaint { + active: sp.active(), + color, + blend_mode: decode_blend_mode(sp.blend_mode()), + })) + } + fbs::Paint::LinearGradientPaint => { + let lgp = item.paint_as_linear_gradient_paint()?; + let stops = lgp.stops().map(decode_gradient_stops).unwrap_or_default(); + let transform = lgp + .transform() + .map(decode_fbs_transform) + .unwrap_or_default(); + let xy1 = lgp + .xy1() + .map(|a| Alignment(a.x(), a.y())) + .unwrap_or(Alignment::CENTER_LEFT); + let xy2 = lgp + .xy2() + .map(|a| Alignment(a.x(), a.y())) + .unwrap_or(Alignment::CENTER_RIGHT); + Some(Paint::LinearGradient(LinearGradientPaint { + active: lgp.active(), + xy1, + xy2, + tile_mode: TileMode::default(), + transform, + stops, + opacity: lgp.opacity(), + blend_mode: decode_blend_mode(lgp.blend_mode()), + })) + } + fbs::Paint::RadialGradientPaint => { + let rgp = item.paint_as_radial_gradient_paint()?; + let stops = rgp.stops().map(decode_gradient_stops).unwrap_or_default(); + let transform = rgp + .transform() + .map(decode_fbs_transform) + .unwrap_or_default(); + Some(Paint::RadialGradient(RadialGradientPaint { + active: rgp.active(), + transform, + stops, + opacity: rgp.opacity(), + blend_mode: decode_blend_mode(rgp.blend_mode()), + tile_mode: TileMode::default(), + })) + } + fbs::Paint::SweepGradientPaint => { + let sgp = item.paint_as_sweep_gradient_paint()?; + let stops = sgp.stops().map(decode_gradient_stops).unwrap_or_default(); + let transform = sgp + .transform() + .map(decode_fbs_transform) + .unwrap_or_default(); + Some(Paint::SweepGradient(SweepGradientPaint { + active: sgp.active(), + transform, + stops, + opacity: sgp.opacity(), + blend_mode: decode_blend_mode(sgp.blend_mode()), + })) + } + fbs::Paint::ImagePaint => { + let ip = item.paint_as_image_paint()?; + let image_ref = if let Some(hash_ref) = ip.image_as_resource_ref_hash() { + ResourceRef::HASH(hash_ref.hash().unwrap_or("").to_owned()) + } else if let Some(rid_ref) = ip.image_as_resource_ref_rid() { + ResourceRef::RID(rid_ref.rid().unwrap_or("").to_owned()) + } else { + return None; + }; + let alignement = ip + .alignement() + .map(|a| Alignment(a.x(), a.y())) + .unwrap_or(Alignment::CENTER); + let fit = decode_image_paint_fit(&ip); + Some(Paint::Image(ImagePaint { + active: ip.active(), + image: image_ref, + quarter_turns: ip.quarter_turns(), + alignement, + fit, + opacity: ip.opacity(), + blend_mode: decode_blend_mode(ip.blend_mode()), + filters: ImageFilters::default(), + })) + } + _ => None, + } +} + +fn decode_image_paint_fit(ip: &fbs::ImagePaint<'_>) -> ImagePaintFit { + match ip.fit_type() { + fbs::ImagePaintFit::ImagePaintFitFit => { + let box_fit = ip + .fit_as_image_paint_fit_fit() + .map(|f| decode_box_fit(f.box_fit())) + .unwrap_or(BoxFit::Cover); + ImagePaintFit::Fit(box_fit) + } + fbs::ImagePaintFit::ImagePaintFitTransform => { + let transform = ip + .fit_as_image_paint_fit_transform() + .and_then(|f| f.transform()) + .map(decode_fbs_transform) + .unwrap_or_default(); + ImagePaintFit::Transform(transform) + } + _ => ImagePaintFit::Fit(BoxFit::Cover), + } +} + +enum_map!(decode_box_fit, encode_box_fit, fbs::BoxFit, BoxFit, BoxFit::Cover, { + Contain, Cover, Fill, None, +}); + +fn decode_paints_vec( + vec: Option>>>, +) -> Paints { + let items = match vec { + Some(v) => v, + None => return Paints::new(Vec::::new()), + }; + let paints: Vec<_> = (0..items.len()) + .filter_map(|i| decode_paint_item(&items.get(i))) + .collect(); + Paints::new(paints) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Effects decoding +// ───────────────────────────────────────────────────────────────────────────── + +/// Decode a blur union (currently always Gaussian) from the FBS blur payload. +fn decode_fe_blur(gaussian: Option>) -> FeBlur { + FeBlur::Gaussian(FeGaussianBlur { + radius: gaussian.map(|g| g.radius()).unwrap_or(0.0), + }) +} + +fn decode_layer_effects(effects: Option>) -> LayerEffects { + let effects = match effects { + Some(e) => e, + None => return LayerEffects::default(), + }; + + let mut out = LayerEffects::default(); + + if let Some(lb) = effects.fe_blur() { + out.blur = Some(FeLayerBlur { + blur: decode_fe_blur(lb.blur_as_fe_gaussian_blur()), + active: lb.active(), + }); + } + + if let Some(bb) = effects.fe_backdrop_blur() { + out.backdrop_blur = Some(FeBackdropBlur { + blur: decode_fe_blur(bb.blur_as_fe_gaussian_blur()), + active: bb.active(), + }); + } + + if let Some(shadows_vec) = effects.fe_shadows() { + for i in 0..shadows_vec.len() { + let effect = shadows_vec.get(i); + if let Some(shadow_fbs) = effect.shadow() { + let shadow = decode_fe_shadow(&shadow_fbs); + if effect.kind() == fbs::FilterShadowEffectKind::InnerShadow { + out.shadows.push(FilterShadowEffect::InnerShadow(shadow)); + } else { + out.shadows.push(FilterShadowEffect::DropShadow(shadow)); + } + } + } + } + + if let Some(lg) = effects.fe_glass() { + out.glass = Some(FeLiquidGlass { + light_intensity: lg.light_intensity(), + light_angle: lg.light_angle(), + refraction: lg.refraction(), + depth: lg.depth(), + dispersion: lg.dispersion(), + blur_radius: lg.blur_radius(), + active: lg.active(), + }); + } + + if let Some(noises_vec) = effects.fe_noises() { + for i in 0..noises_vec.len() { + let ne = noises_vec.get(i); + out.noises.push(decode_fe_noise(&ne)); + } + } + + out +} + +fn decode_fe_shadow(s: &fbs::FeShadow<'_>) -> FeShadow { + FeShadow { + dx: s.dx(), + dy: s.dy(), + blur: s.blur(), + spread: s.spread(), + color: s.color().map(decode_rgba32f_to_cg_color).unwrap_or(CGColor { r: 0, g: 0, b: 0, a: 64 }), + active: s.active(), + } +} + +fn decode_fe_noise(ne: &fbs::FeNoiseEffect<'_>) -> FeNoiseEffect { + const DEFAULT_MONO: CGColor = CGColor { r: 0, g: 0, b: 0, a: 64 }; + let default_mono = || NoiseEffectColors::Mono { color: DEFAULT_MONO }; + + let coloring = ne + .coloring() + .map(|c| match c.kind() { + fbs::NoiseEffectColorsKind::Mono => NoiseEffectColors::Mono { + color: c.mono_color().map(decode_rgba32f_to_cg_color).unwrap_or(DEFAULT_MONO), + }, + fbs::NoiseEffectColorsKind::Duo => NoiseEffectColors::Duo { + color1: c.duo_color1().map(decode_rgba32f_to_cg_color).unwrap_or(CGColor { r: 0, g: 0, b: 0, a: 128 }), + color2: c.duo_color2().map(decode_rgba32f_to_cg_color).unwrap_or(CGColor { r: 255, g: 255, b: 255, a: 128 }), + }, + fbs::NoiseEffectColorsKind::Multi => NoiseEffectColors::Multi { opacity: c.multi_opacity() }, + _ => default_mono(), + }) + .unwrap_or_else(default_mono); + + FeNoiseEffect { + active: ne.active(), + noise_size: ne.noise_size(), + density: ne.density(), + num_octaves: ne.num_octaves(), + seed: ne.seed(), + coloring, + blend_mode: decode_blend_mode(ne.blend_mode()), + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Stroke decoding +// ───────────────────────────────────────────────────────────────────────────── + +fn decode_stroke_style_from_fbs(ss: Option>) -> StrokeStyle { + let ss = match ss { + Some(s) => s, + None => return StrokeStyle::default(), + }; + StrokeStyle { + stroke_align: decode_stroke_align(ss.stroke_align()), + stroke_cap: decode_stroke_cap(ss.stroke_cap()), + stroke_join: decode_stroke_join(ss.stroke_join()), + stroke_miter_limit: StrokeMiterLimit(ss.stroke_miter_limit()), + stroke_dash_array: ss + .stroke_dash_array() + .filter(|v| v.len() > 0) + .map(|v| StrokeDashArray((0..v.len()).map(|i| v.get(i)).collect())), + } +} + +enum_map!(decode_stroke_align, encode_stroke_align, fbs::StrokeAlign, StrokeAlign, StrokeAlign::Center, { + Inside, Center, Outside, +}); +enum_map!(decode_stroke_cap, encode_stroke_cap, fbs::StrokeCap, StrokeCap, StrokeCap::Butt, { + Butt, Round, Square, +}); +enum_map!(decode_stroke_join, encode_stroke_join, fbs::StrokeJoin, StrokeJoin, StrokeJoin::Miter, { + Miter, Round, Bevel, +}); +enum_map!(decode_stroke_marker, encode_stroke_marker, fbs::StrokeMarkerPreset, StrokeMarkerPreset, StrokeMarkerPreset::None, { + None, RightTriangleOpen, EquilateralTriangle, Circle, Square, Diamond, VerticalBar, +}); +enum_map!(decode_boolean_path_op, encode_boolean_path_op, fbs::BooleanPathOperation, BooleanPathOperation, BooleanPathOperation::Union, { + Union, Intersection, Difference, Xor, +}); +enum_map!(decode_text_align, encode_text_align, fbs::TextAlign, TextAlign, TextAlign::Left, { + Left, Center, Right, Justify, +}); +enum_map!(decode_text_align_vertical, encode_text_align_vertical, fbs::TextAlignVertical, TextAlignVertical, TextAlignVertical::Top, { + Top, Center, Bottom, +}); + +/// Decode a `StrokeGeometryTrait` into `(StrokeStyle, f32 stroke_width)`. +fn decode_stroke_geometry_trait(sg: Option>) -> (StrokeStyle, f32) { + let sg = match sg { + Some(s) => s, + None => return (StrokeStyle::default(), 0.0), + }; + let style = decode_stroke_style_from_fbs(sg.stroke_style()); + (style, sg.stroke_width()) +} + +/// Decode a `RectangularStrokeGeometryTrait` into `(StrokeStyle, StrokeWidth)`. +fn decode_rectangular_stroke_geometry( + sg: Option>, +) -> (StrokeStyle, StrokeWidth) { + let sg = match sg { + Some(s) => s, + None => return (StrokeStyle::default(), StrokeWidth::None), + }; + let style = decode_stroke_style_from_fbs(sg.stroke_style()); + let width = match sg.rectangular_stroke_width() { + Some(rsw) => { + let top = rsw.stroke_top_width(); + let right = rsw.stroke_right_width(); + let bottom = rsw.stroke_bottom_width(); + let left = rsw.stroke_left_width(); + if top == right && right == bottom && bottom == left { + if top == 0.0 { + StrokeWidth::None + } else { + StrokeWidth::Uniform(top) + } + } else { + StrokeWidth::Rectangular(RectangularStrokeWidth { + stroke_top_width: top, + stroke_right_width: right, + stroke_bottom_width: bottom, + stroke_left_width: left, + }) + } + } + None => StrokeWidth::None, + }; + (style, width) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Corner radius / smoothing +// ───────────────────────────────────────────────────────────────────────────── + +fn decode_corner_radius(cr: Option>) -> RectangularCornerRadius { + use crate::cg::types::Radius; + match cr.and_then(|t| t.rectangular_corner_radius()) { + Some(rcr) => RectangularCornerRadius { + tl: Radius::elliptical(rcr.tl().rx(), rcr.tl().ry()), + tr: Radius::elliptical(rcr.tr().rx(), rcr.tr().ry()), + bl: Radius::elliptical(rcr.bl().rx(), rcr.bl().ry()), + br: Radius::elliptical(rcr.br().rx(), rcr.br().ry()), + }, + None => RectangularCornerRadius::default(), + } +} + +fn decode_corner_smoothing(cr: Option>) -> CornerSmoothing { + match cr { + Some(cr) => CornerSmoothing(cr.corner_smoothing()), + None => CornerSmoothing::default(), + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Layout decoding +// ───────────────────────────────────────────────────────────────────────────── + +/// Extract (x, y) from the layout position, regardless of whether it is encoded +/// as Cartesian or Inset. For Inset, `left` and `top` are used as x and y +/// (matching the JSON path where `layout_inset_left`/`layout_inset_top` are the +/// position of non-container shapes). +fn decode_layout_xy(ls: &fbs::LayoutStyle<'_>) -> (f32, f32) { + match ls.layout_position_type() { + fbs::LayoutPositioningBasis::LayoutPositioningCartesian => ls + .layout_position_as_layout_positioning_cartesian() + .map(|c| (c.x(), c.y())) + .unwrap_or((0.0, 0.0)), + fbs::LayoutPositioningBasis::LayoutPositioningInset => ls + .layout_position_as_layout_positioning_inset() + .map(|inset| { + let left = inset.left().and_then(|v| v.value()).unwrap_or(0.0); + let top = inset.top().and_then(|v| v.value()).unwrap_or(0.0); + (left, top) + }) + .unwrap_or((0.0, 0.0)), + _ => (0.0, 0.0), + } +} + +fn decode_layout_position(ls: &fbs::LayoutStyle<'_>) -> LayoutPositioningBasis { + match ls.layout_position_type() { + fbs::LayoutPositioningBasis::LayoutPositioningCartesian => { + if let Some(cart) = ls.layout_position_as_layout_positioning_cartesian() { + LayoutPositioningBasis::Cartesian(CGPoint::new(cart.x(), cart.y())) + } else { + LayoutPositioningBasis::zero() + } + } + fbs::LayoutPositioningBasis::LayoutPositioningInset => { + if let Some(inset) = ls.layout_position_as_layout_positioning_inset() { + let top = inset.top().and_then(|v| v.value()).unwrap_or(0.0); + let right = inset.right().and_then(|v| v.value()).unwrap_or(0.0); + let bottom = inset.bottom().and_then(|v| v.value()).unwrap_or(0.0); + let left = inset.left().and_then(|v| v.value()).unwrap_or(0.0); + LayoutPositioningBasis::Inset(EdgeInsets { + top, + right, + bottom, + left, + }) + } else { + LayoutPositioningBasis::zero() + } + } + _ => LayoutPositioningBasis::zero(), + } +} + +fn decode_dimensions(ls: &fbs::LayoutStyle<'_>) -> (Option, Option) { + match ls.layout_dimensions() { + Some(dim) => { + let w = decode_dimension_value(dim.layout_target_width()); + let h = decode_dimension_value(dim.layout_target_height()); + (w, h) + } + None => (None, None), + } +} + +fn decode_dimension_value(dv: Option>) -> Option { + let dv = dv?; + match dv.unit() { + fbs::LayoutDimensionUnit::LengthPx => dv.value(), + _ => None, + } +} + +fn decode_layout_dimension_style(ls: &fbs::LayoutStyle<'_>) -> LayoutDimensionStyle { + let dim = ls.layout_dimensions(); + LayoutDimensionStyle { + layout_target_width: dim + .as_ref() + .and_then(|d| decode_dimension_value(d.layout_target_width())), + layout_target_height: dim + .as_ref() + .and_then(|d| decode_dimension_value(d.layout_target_height())), + layout_min_width: None, + layout_max_width: None, + layout_min_height: None, + layout_max_height: None, + layout_target_aspect_ratio: None, + } +} + +fn decode_layout_container_style(ls: &fbs::LayoutStyle<'_>) -> LayoutContainerStyle { + match ls.layout_container() { + Some(lc) => { + let layout_mode = if lc.layout_mode() == fbs::LayoutMode::Flex { + LayoutMode::Flex + } else { + LayoutMode::Normal + }; + let layout_direction = if lc.layout_direction() == fbs::Axis::Vertical { + Axis::Vertical + } else { + Axis::Horizontal + }; + let layout_wrap = match lc.layout_wrap() { + fbs::LayoutWrap::Wrap => Some(LayoutWrap::Wrap), + fbs::LayoutWrap::NoWrap => Some(LayoutWrap::NoWrap), + _ => None, + }; + let layout_main_axis_alignment = + decode_main_axis_alignment(lc.layout_main_axis_alignment()); + let layout_cross_axis_alignment = + decode_cross_axis_alignment(lc.layout_cross_axis_alignment()); + let layout_padding = lc.layout_padding().map(|p| EdgeInsets { + top: p.top(), + right: p.right(), + bottom: p.bottom(), + left: p.left(), + }); + let main_gap = lc.layout_main_axis_gap(); + let cross_gap = lc.layout_cross_axis_gap(); + let layout_gap = if main_gap > 0.0 || cross_gap > 0.0 { + Some(LayoutGap { + main_axis_gap: main_gap, + cross_axis_gap: cross_gap, + }) + } else { + None + }; + LayoutContainerStyle { + layout_mode, + layout_direction, + layout_wrap, + layout_main_axis_alignment, + layout_cross_axis_alignment, + layout_padding, + layout_gap, + } + } + None => LayoutContainerStyle::default(), + } +} + +/// Like `enum_map!` but the decode function returns `Option` — `None` +/// for any FBS value not in the listed variants (e.g. the FBS `None` sentinel). +macro_rules! enum_map_opt { + ($decode:ident, $encode:ident, $fbs:ty, $rust:ty, { $($v:ident),+ $(,)? }) => { + fn $decode(v: $fbs) -> Option<$rust> { + match v { $( <$fbs>::$v => Some(<$rust>::$v), )+ _ => None, } + } + fn $encode(v: $rust) -> $fbs { + match v { $( <$rust>::$v => <$fbs>::$v, )+ } + } + }; +} + +enum_map_opt!(decode_main_axis_alignment, encode_main_axis_alignment, fbs::MainAxisAlignment, MainAxisAlignment, { + Start, Center, End, SpaceBetween, SpaceAround, SpaceEvenly, Stretch, +}); +enum_map_opt!(decode_cross_axis_alignment, encode_cross_axis_alignment, fbs::CrossAxisAlignment, CrossAxisAlignment, { + Start, Center, End, Stretch, +}); + +fn decode_layout_child_style(ls: &fbs::LayoutStyle<'_>) -> Option { + let lc = ls.layout_child()?; + let layout_positioning = match lc.layout_positioning() { + fbs::LayoutPositioning::Absolute => LayoutPositioning::Absolute, + _ => LayoutPositioning::Auto, + }; + Some(LayoutChildStyle { + layout_grow: lc.layout_grow(), + layout_positioning, + }) +} + +fn decode_mask_type(fbs_mask_type: fbs::LayerMaskType) -> Option { + use crate::cg::types::ImageMaskType; + match fbs_mask_type { + fbs::LayerMaskType::NONE => None, + fbs::LayerMaskType::LayerMaskTypeImage => Some(LayerMaskType::Image(ImageMaskType::Alpha)), + fbs::LayerMaskType::LayerMaskTypeGeometry => Some(LayerMaskType::Geometry), + _ => None, + } +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Node-specific decoders +// +// Every function below receives a pre-decoded `LayerCommon` so the shared +// fields (active, opacity, blend_mode, mask, effects, rotation, layout_child) +// are decoded exactly once and cannot diverge between node types. +// ═════════════════════════════════════════════════════════════════════════════ + +fn decode_vector_network(vnd: Option>) -> VectorNetwork { + let Some(vnd) = vnd else { + return VectorNetwork::default(); + }; + + let vertices: Vec<(f32, f32)> = vnd + .vertices() + .map(|v| (0..v.len()).map(|i| { let p = v.get(i); (p.x(), p.y()) }).collect()) + .unwrap_or_default(); + + let segments: Vec = vnd + .segments() + .map(|s| { + (0..s.len()) + .map(|i| { + let seg = s.get(i); + VectorNetworkSegment { + a: seg.segment_vertex_a() as usize, + b: seg.segment_vertex_b() as usize, + ta: (seg.tangent_a().x(), seg.tangent_a().y()), + tb: (seg.tangent_b().x(), seg.tangent_b().y()), + } + }) + .collect() + }) + .unwrap_or_default(); + + let regions: Vec = vnd + .regions() + .map(|rs| { + (0..rs.len()) + .filter_map(|i| { + let region = rs.get(i); + let loops: Vec = region + .region_loops() + .map(|ls| { + (0..ls.len()) + .filter_map(|j| { + let lp = ls.get(j); + lp.loop_segment_indices().map(|idx| { + VectorNetworkLoop( + (0..idx.len()).map(|k| idx.get(k) as usize).collect(), + ) + }) + }) + .collect() + }) + .unwrap_or_default(); + let fill_rule = match region.region_fill_rule() { + fbs::FillRule::EvenOdd => crate::cg::types::FillRule::EvenOdd, + _ => crate::cg::types::FillRule::NonZero, + }; + let fills = region + .region_fill_paints() + .map(|p| decode_paints_vec(Some(p))); + Some(VectorNetworkRegion { + loops, + fill_rule, + fills, + }) + }) + .collect() + }) + .unwrap_or_default(); + + VectorNetwork { + vertices, + segments, + regions, + } +} + +fn decode_group_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + _gn: &fbs::GroupNode<'_>, +) -> Node { + let sl = decode_shape_layout(layer, lc.rotation_cos_sin); + Node::Group(GroupNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform: Some(sl.transform), + }) +} + +fn decode_container_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + cn: &fbs::ContainerNode<'_>, +) -> Node { + let layout = layer.layout(); + let position = layout.as_ref().map(decode_layout_position).unwrap_or_default(); + let layout_container = layout.as_ref().map(decode_layout_container_style).unwrap_or_default(); + let layout_dimensions = layout.as_ref().map(decode_layout_dimension_style).unwrap_or_default(); + let (stroke_style, stroke_width) = decode_rectangular_stroke_geometry(cn.stroke_geometry()); + + Node::Container(ContainerNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + rotation: lc.rotation, + position, + layout_container, + layout_dimensions, + layout_child: lc.layout_child.clone(), + corner_radius: decode_corner_radius(cn.corner_radius()), + corner_smoothing: decode_corner_smoothing(cn.corner_radius()), + fills: decode_paints_vec(cn.fill_paints()), + strokes: decode_paints_vec(cn.stroke_paints()), + stroke_style, + stroke_width, + effects: lc.effects.clone(), + clip: cn.clips_content() as ContainerClipFlag, + }) +} + +fn decode_initial_container_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + _icn: &fbs::InitialContainerNode<'_>, +) -> Node { + let layout = layer.layout(); + let lcs = layout.as_ref().map(decode_layout_container_style).unwrap_or_default(); + Node::InitialContainer(InitialContainerNodeRec { + active: lc.active, + layout_mode: lcs.layout_mode, + layout_direction: lcs.layout_direction, + layout_wrap: lcs.layout_wrap.unwrap_or(LayoutWrap::NoWrap), + layout_main_axis_alignment: lcs.layout_main_axis_alignment.unwrap_or(MainAxisAlignment::Start), + layout_cross_axis_alignment: lcs.layout_cross_axis_alignment.unwrap_or(CrossAxisAlignment::Start), + padding: lcs.layout_padding.unwrap_or_default(), + layout_gap: lcs.layout_gap.unwrap_or(LayoutGap { + main_axis_gap: 0.0, + cross_axis_gap: 0.0, + }), + }) +} + +fn decode_basic_shape_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + bsn: &fbs::BasicShapeNode<'_>, +) -> Node { + use fbs::CanonicalLayerShape as BST; + + let sl = decode_shape_layout(layer, lc.rotation_cos_sin); + let fills = decode_paints_vec(bsn.fill_paints()); + let strokes = decode_paints_vec(bsn.stroke_paints()); + // For rectangles the TS encoder writes per-corner rectangular_corner_radius; + // for polygons/stars it writes the scalar corner_radius field. + let corner_radius = { + use crate::cg::types::Radius; + if let Some(rcr) = bsn.rectangular_corner_radius() { + RectangularCornerRadius { + tl: Radius::elliptical(rcr.tl().rx(), rcr.tl().ry()), + tr: Radius::elliptical(rcr.tr().rx(), rcr.tr().ry()), + bl: Radius::elliptical(rcr.bl().rx(), rcr.bl().ry()), + br: Radius::elliptical(rcr.br().rx(), rcr.br().ry()), + } + } else { + let r = Radius::circular(bsn.corner_radius()); + RectangularCornerRadius { tl: r, tr: r, bl: r, br: r } + } + }; + let stroke_style = decode_stroke_style_from_fbs(bsn.stroke_style()); + let stroke_width_f32 = bsn.stroke_width(); + + match bsn.shape_type() { + BST::CanonicalShapeRectangular => Node::Rectangle(RectangleNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform: sl.transform, + size: sl.size, + corner_radius, + corner_smoothing: CornerSmoothing::default(), + fills, + strokes, + stroke_style, + stroke_width: if stroke_width_f32 == 0.0 { + StrokeWidth::None + } else { + StrokeWidth::Uniform(stroke_width_f32) + }, + effects: lc.effects.clone(), + layout_child: lc.layout_child.clone(), + }), + BST::CanonicalShapeElliptical => { + let ring = bsn + .shape_as_canonical_shape_elliptical() + .and_then(|s| s.ring_sector_data()); + let (inner_radius, start_angle, angle) = match ring { + Some(r) => { + let ir = r.inner_radius_ratio(); + let sa = r.start_angle(); + let a = r.angle(); + ( + if ir != 0.0 { Some(ir) } else { None }, + sa, + if a != 360.0 { Some(a) } else { None }, + ) + } + None => (None, 0.0, None), + }; + Node::Ellipse(EllipseNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform: sl.transform, + size: sl.size, + fills, + strokes, + stroke_style, + stroke_width: singular_stroke_width(stroke_width_f32), + inner_radius, + start_angle, + angle, + // corner_radius is not in the FBS schema; defaults to None + corner_radius: None, + effects: lc.effects.clone(), + layout_child: lc.layout_child.clone(), + }) + } + BST::CanonicalShapeRegularPolygon => { + let point_count = bsn + .shape_as_canonical_shape_regular_polygon() + .map(|s| s.point_count() as usize) + .unwrap_or(5); + Node::RegularPolygon(RegularPolygonNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform: sl.transform, + size: sl.size, + point_count, + corner_radius: corner_radius.tl.rx, + fills, + strokes, + stroke_style, + stroke_width: singular_stroke_width(stroke_width_f32), + effects: lc.effects.clone(), + layout_child: lc.layout_child.clone(), + }) + } + BST::CanonicalShapeRegularStarPolygon => { + let (point_count, inner_radius) = bsn + .shape_as_canonical_shape_regular_star_polygon() + .map(|s| (s.point_count() as usize, s.inner_radius_ratio())) + .unwrap_or((5, 0.4)); + Node::RegularStarPolygon(RegularStarPolygonNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform: sl.transform, + size: sl.size, + point_count, + inner_radius, + corner_radius: corner_radius.tl.rx, + fills, + strokes, + stroke_style, + stroke_width: singular_stroke_width(stroke_width_f32), + effects: lc.effects.clone(), + layout_child: lc.layout_child.clone(), + }) + } + _ => Node::Vector(VectorNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform: sl.transform, + network: VectorNetwork::default(), + corner_radius: 0.0, + fills, + strokes, + stroke_width: stroke_width_f32, + stroke_width_profile: None, + stroke_align: stroke_style.stroke_align, + stroke_cap: stroke_style.stroke_cap, + stroke_join: stroke_style.stroke_join, + stroke_miter_limit: stroke_style.stroke_miter_limit, + stroke_dash_array: stroke_style.stroke_dash_array, + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + layout_child: lc.layout_child.clone(), + effects: lc.effects.clone(), + }), + } +} + +fn decode_vector_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + vn: &fbs::VectorNode<'_>, +) -> Node { + let sl = decode_shape_layout(layer, lc.rotation_cos_sin); + let (stroke_style, stroke_width_f32) = decode_stroke_geometry_trait(vn.stroke_geometry()); + + Node::Vector(VectorNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform: sl.transform, + network: decode_vector_network(vn.vector_network_data()), + corner_radius: 0.0, + fills: decode_paints_vec(vn.fill_paints()), + strokes: decode_paints_vec(vn.stroke_paints()), + stroke_width: stroke_width_f32, + stroke_width_profile: None, + stroke_align: stroke_style.stroke_align, + stroke_cap: stroke_style.stroke_cap, + stroke_join: stroke_style.stroke_join, + stroke_miter_limit: stroke_style.stroke_miter_limit, + stroke_dash_array: stroke_style.stroke_dash_array, + marker_start_shape: decode_stroke_marker(vn.marker_start_shape()), + marker_end_shape: decode_stroke_marker(vn.marker_end_shape()), + layout_child: lc.layout_child.clone(), + effects: lc.effects.clone(), + }) +} + +fn decode_line_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + ln: &fbs::LineNode<'_>, +) -> Node { + let sl = decode_shape_layout(layer, lc.rotation_cos_sin); + let sg = ln.stroke_geometry(); + let stroke_width = sg.as_ref().map(|s| s.stroke_width()).unwrap_or(0.0); + let stroke_cap = sg + .as_ref() + .and_then(|s| s.stroke_style()) + .map(|ss| decode_stroke_cap(ss.stroke_cap())) + .unwrap_or_default(); + let miter_limit = sg + .as_ref() + .and_then(|s| s.stroke_style()) + .map(|ss| StrokeMiterLimit(ss.stroke_miter_limit())) + .unwrap_or_default(); + let stroke_dash_array = sg + .as_ref() + .and_then(|s| s.stroke_style()) + .and_then(|ss| { + ss.stroke_dash_array() + .filter(|v| v.len() > 0) + .map(|v| StrokeDashArray((0..v.len()).map(|i| v.get(i)).collect())) + }); + + // Lines use translation + rotation (no center-origin) and height=0. + // Use raw cos/sin to avoid lossy degree conversion. + let (cos, sin) = lc.rotation_cos_sin; + let transform = AffineTransform::from_translation_rotation_raw(sl.x, sl.y, cos, sin); + let size = Size { + width: sl.size.width, + height: 0.0, + }; + + Node::Line(LineNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + transform, + size, + strokes: decode_paints_vec(ln.stroke_paints()), + stroke_width, + stroke_cap, + stroke_miter_limit: miter_limit, + stroke_dash_array, + _data_stroke_align: StrokeAlign::Center, + effects: lc.effects.clone(), + layout_child: lc.layout_child.clone(), + marker_start_shape: decode_stroke_marker(ln.marker_start_shape()), + marker_end_shape: decode_stroke_marker(ln.marker_end_shape()), + }) +} + +fn decode_boolean_operation_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + bon: &fbs::BooleanOperationNode<'_>, +) -> Node { + let sl = decode_shape_layout(layer, lc.rotation_cos_sin); + let op = decode_boolean_path_op(bon.op()); + let corner_r = bon + .corner_radius() + .and_then(|cr| cr.corner_radius()) + .map(|r| r.rx()); + let (stroke_style, stroke_width_f32) = decode_stroke_geometry_trait(bon.stroke_geometry()); + + Node::BooleanOperation(BooleanPathOperationNodeRec { + active: lc.active, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + effects: lc.effects.clone(), + transform: Some(sl.transform), + op, + corner_radius: corner_r, + fills: decode_paints_vec(bon.fill_paints()), + strokes: decode_paints_vec(bon.stroke_paints()), + stroke_style, + stroke_width: singular_stroke_width(stroke_width_f32), + }) +} + +fn decode_text_span_node( + lc: &LayerCommon, + layer: &fbs::LayerTrait<'_>, + tn: &fbs::TextSpanNode<'_>, +) -> Node { + let sl = decode_shape_layout(layer, lc.rotation_cos_sin); + // Text uses translation + rotation (no center-origin). + // Use raw cos/sin to avoid lossy degree conversion. + let (cos, sin) = lc.rotation_cos_sin; + let transform = AffineTransform::from_translation_rotation_raw(sl.x, sl.y, cos, sin); + + let props = tn.properties(); + let text = props + .as_ref() + .and_then(|p| p.text()) + .unwrap_or("") + .to_owned(); + + let text_style = props + .as_ref() + .and_then(|p| p.text_style()) + .map(|ts| { + let mut rec = TextStyleRec::from_font(ts.font_family(), ts.font_size()); + rec.font_weight = FontWeight(ts.font_weight().value()); + rec + }) + .unwrap_or_else(|| TextStyleRec::from_font("Inter", 14.0)); + + let text_align = props + .as_ref() + .map(|p| decode_text_align(p.text_align())) + .unwrap_or(TextAlign::Left); + + let text_align_vertical = props + .as_ref() + .map(|p| decode_text_align_vertical(p.text_align_vertical())) + .unwrap_or(TextAlignVertical::Top); + + let fill_paints = props + .as_ref() + .map(|p| decode_paints_vec(p.fill_paints())) + .unwrap_or_else(|| Paints::new(Vec::::new())); + let stroke_paints = props + .as_ref() + .map(|p| decode_paints_vec(p.stroke_paints())) + .unwrap_or_else(|| Paints::new(Vec::::new())); + let stroke_width = props + .as_ref() + .and_then(|p| p.stroke_geometry()) + .map(|sg| sg.stroke_width()) + .unwrap_or(0.0); + let stroke_align = props + .as_ref() + .and_then(|p| p.stroke_geometry()) + .and_then(|sg| sg.stroke_style()) + .map(|ss| decode_stroke_align(ss.stroke_align())) + .unwrap_or(StrokeAlign::Center); + + Node::TextSpan(TextSpanNodeRec { + active: lc.active, + transform, + width: sl.width, + height: sl.height, + layout_child: lc.layout_child.clone(), + text, + text_style, + text_align, + text_align_vertical, + max_lines: None, + ellipsis: None, + fills: fill_paints, + strokes: stroke_paints, + stroke_width, + stroke_align, + opacity: lc.opacity, + blend_mode: lc.blend_mode, + mask: lc.mask, + effects: lc.effects.clone(), + }) +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Encoder — Scene → FlatBuffers binary (`.grida`) +// +// The encode path is the exact inverse of the decode path above. +// Given a `Scene` and an ID mapping, it produces a `.grida` FlatBuffers +// binary that round-trips through decode identically. +// ═════════════════════════════════════════════════════════════════════════════ + +use crate::node::schema::NodeId; + +/// Encode a `Scene` into a `.grida` FlatBuffers binary. +/// +/// - `scene`: the scene to encode. +/// - `scene_id`: the string ID for the scene node (e.g. `"scene1"`). +/// - `id_map`: maps internal `NodeId` → string IDs. +/// +/// Returns the encoded bytes (including the `"GRID"` file identifier). +pub fn encode( + scene: &Scene, + scene_id: &str, + id_map: &HashMap, + position_map: &HashMap, +) -> Vec { + let mut fbb = flatbuffers::FlatBufferBuilder::with_capacity(4096); + + // ── 1. Encode all nodes ───────────────────────────────────────────────── + let mut node_slot_offsets = Vec::new(); + + // 1a. Scene node + let scene_slot = encode_scene_node(&mut fbb, scene, scene_id); + node_slot_offsets.push(scene_slot); + + // 1b. Layer nodes — walk the tree in order (roots first, then children) + for root_id in scene.graph.roots() { + encode_tree_recursive( + &mut fbb, + &scene.graph, + root_id, + scene_id, + id_map, + position_map, + &mut node_slot_offsets, + ); + } + + // ── 2. Build nodes vector ─────────────────────────────────────────────── + let nodes_vec = fbb.create_vector(&node_slot_offsets); + + // ── 3. Build scenes array ─────────────────────────────────────────────── + let scene_id_str = fbb.create_string(scene_id); + let scene_nid = fbs::NodeIdentifier::create( + &mut fbb, + &fbs::NodeIdentifierArgs { id: Some(scene_id_str) }, + ); + let scenes_vec = fbb.create_vector(&[scene_nid]); + + // ── 4. Build CanvasDocument ───────────────────────────────────────────── + let schema_version_str = fbb.create_string("0.0.0"); + let doc = fbs::CanvasDocument::create( + &mut fbb, + &fbs::CanvasDocumentArgs { + schema_version: Some(schema_version_str), + nodes: Some(nodes_vec), + scenes: Some(scenes_vec), + }, + ); + + // ── 5. Build GridaFile root ───────────────────────────────────────────── + let root = fbs::GridaFile::create( + &mut fbb, + &fbs::GridaFileArgs { document: Some(doc) }, + ); + + fbb.finish(root, Some("GRID")); + fbb.finished_data().to_vec() +} + +/// Encode multiple scenes into a single `.grida` FlatBuffers binary. +/// +/// Each entry is `(scene_id, scene, id_map, position_map)`. +/// All scenes share the same flat `nodes` vector; each scene's nodes +/// are prefixed with a scene-type NodeSlot that references `scene_id`. +pub fn encode_multi( + entries: &[( + &str, + &Scene, + &HashMap, + &HashMap, + )], +) -> Vec { + let mut fbb = flatbuffers::FlatBufferBuilder::with_capacity(8192); + let mut node_slot_offsets = Vec::new(); + let mut scene_nids = Vec::new(); + + for (scene_id, scene, id_map, position_map) in entries { + // Scene node + let scene_slot = encode_scene_node(&mut fbb, scene, scene_id); + node_slot_offsets.push(scene_slot); + + // Layer nodes + for root_id in scene.graph.roots() { + encode_tree_recursive( + &mut fbb, + &scene.graph, + root_id, + scene_id, + id_map, + position_map, + &mut node_slot_offsets, + ); + } + + // Scene identifier + let scene_id_str = fbb.create_string(scene_id); + let scene_nid = fbs::NodeIdentifier::create( + &mut fbb, + &fbs::NodeIdentifierArgs { + id: Some(scene_id_str), + }, + ); + scene_nids.push(scene_nid); + } + + let nodes_vec = fbb.create_vector(&node_slot_offsets); + let scenes_vec = fbb.create_vector(&scene_nids); + let schema_version_str = fbb.create_string("0.0.0"); + let doc = fbs::CanvasDocument::create( + &mut fbb, + &fbs::CanvasDocumentArgs { + schema_version: Some(schema_version_str), + nodes: Some(nodes_vec), + scenes: Some(scenes_vec), + }, + ); + let root = fbs::GridaFile::create( + &mut fbb, + &fbs::GridaFileArgs { document: Some(doc) }, + ); + fbb.finish(root, Some("GRID")); + fbb.finished_data().to_vec() +} + +/// Recursively encode a node and all its children into NodeSlots. +fn encode_tree_recursive<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + graph: &SceneGraph, + node_id: &NodeId, + parent_id: &str, + id_map: &HashMap, + position_map: &HashMap, + out: &mut Vec>>, +) { + let node = match graph.get_node(node_id) { + Ok(n) => n, + Err(_) => return, + }; + let string_id = match id_map.get(node_id) { + Some(s) => s.as_str(), + None => return, + }; + + // Use the original position string from the FBS file if available. + // Fall back to generating base-62-style position strings that stay + // lexicographically sorted: a0..a9, aa..az, b00..b0z, etc. + let position = match position_map.get(node_id) { + Some(pos) => pos.clone(), + None => { + let siblings = graph + .get_children(&graph.get_parent(node_id).unwrap_or(0)) + .map(|v| v.as_slice()) + .unwrap_or(&[]); + let idx = siblings.iter().position(|id| id == node_id).unwrap_or(0); + generate_fractional_position(idx) + } + }; + + let slot = encode_node(fbb, node, string_id, parent_id, &position); + out.push(slot); + + // Recurse into children + if let Some(children) = graph.get_children(node_id) { + for child_id in children.clone() { + encode_tree_recursive(fbb, graph, &child_id, string_id, id_map, position_map, out); + } + } +} + +/// Generate a lexicographically sortable position string for child index `idx`. +/// Produces: a0, a1, ..., a9, aa, ab, ..., az, b00, b01, ..., b0z, b10, ... +/// This matches the pattern used by fractional-indexing libraries. +fn generate_fractional_position(idx: usize) -> String { + if idx < 36 { + // a0..a9, aa..az (single char after 'a') + let ch = if idx < 10 { + (b'0' + idx as u8) as char + } else { + (b'a' + (idx - 10) as u8) as char + }; + format!("a{ch}") + } else { + // b00..b0z, b10..b1z, etc. + let rem = idx - 36; + let hi = rem / 36; + let lo = rem % 36; + let hi_ch = if hi < 10 { + (b'0' + hi as u8) as char + } else { + (b'a' + (hi - 10) as u8) as char + }; + let lo_ch = if lo < 10 { + (b'0' + lo as u8) as char + } else { + (b'a' + (lo - 10) as u8) as char + }; + format!("b{hi_ch}{lo_ch}") + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Scene node encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_scene_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + scene: &Scene, + scene_id: &str, +) -> flatbuffers::WIPOffset> { + let sys = encode_system_node_trait(fbb, scene_id, &scene.name, true, false); + + let bg = scene.background_color.map(|c| encode_color_to_rgba32f(&c)); + + let sn = fbs::SceneNode::create(fbb, &fbs::SceneNodeArgs { + node: Some(sys), + scene_background_color: bg.as_ref(), + ..Default::default() + }); + + make_node_slot(fbb, fbs::Node::SceneNode, sn.as_union_value()) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Dispatch: Node enum → typed encoder +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + node: &Node, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + match node { + Node::Container(r) => encode_container_node(fbb, r, node_id, parent_id, position), + Node::InitialContainer(r) => { + encode_initial_container_node(fbb, r, node_id, parent_id, position) + } + Node::Rectangle(r) => encode_basic_shape_node( + fbb, + node_id, + parent_id, + position, + fbs::CanonicalLayerShape::CanonicalShapeRectangular, + fbs::BasicShapeNodeType::Rectangle, + BasicShapeFields::Rectangle(r), + ), + Node::Ellipse(r) => encode_basic_shape_node( + fbb, + node_id, + parent_id, + position, + fbs::CanonicalLayerShape::CanonicalShapeElliptical, + fbs::BasicShapeNodeType::Ellipse, + BasicShapeFields::Ellipse(r), + ), + Node::RegularPolygon(r) => encode_basic_shape_node( + fbb, + node_id, + parent_id, + position, + fbs::CanonicalLayerShape::CanonicalShapeRegularPolygon, + fbs::BasicShapeNodeType::RegularPolygon, + BasicShapeFields::RegularPolygon(r), + ), + Node::RegularStarPolygon(r) => encode_basic_shape_node( + fbb, + node_id, + parent_id, + position, + fbs::CanonicalLayerShape::CanonicalShapeRegularStarPolygon, + fbs::BasicShapeNodeType::RegularStarPolygon, + BasicShapeFields::RegularStarPolygon(r), + ), + Node::Group(r) => encode_group_node(fbb, r, node_id, parent_id, position), + Node::Line(r) => encode_line_node(fbb, r, node_id, parent_id, position), + Node::Vector(r) => encode_vector_node(fbb, r, node_id, parent_id, position), + Node::TextSpan(r) => encode_text_span_node(fbb, r, node_id, parent_id, position), + Node::BooleanOperation(r) => { + encode_boolean_operation_node(fbb, r, node_id, parent_id, position) + } + // Fallback: encode as UnknownNode + _ => { + let sys = encode_system_node_trait(fbb, node_id, "", true, false); + let un = fbs::UnknownNode::create(fbb, &fbs::UnknownNodeArgs { node: Some(sys) }); + make_node_slot(fbb, fbs::Node::UnknownNode, un.as_union_value()) + } + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Shared helper: SystemNodeTrait +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_system_node_trait<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + id: &str, + name: &str, + active: bool, + locked: bool, +) -> flatbuffers::WIPOffset> { + let id_str = fbb.create_string(id); + let nid = fbs::NodeIdentifier::create(fbb, &fbs::NodeIdentifierArgs { id: Some(id_str) }); + let name_str = if !name.is_empty() { + Some(fbb.create_string(name)) + } else { + None + }; + fbs::SystemNodeTrait::create( + fbb, + &fbs::SystemNodeTraitArgs { + id: Some(nid), + active, + name: name_str, + locked, + }, + ) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Shared helper: LayerTrait +// ───────────────────────────────────────────────────────────────────────────── + +struct LayerTraitInput<'a, 'b> { + parent_id: &'b str, + position: &'b str, + opacity: f32, + blend_mode: LayerBlendMode, + mask: Option, + effects: &'b LayerEffects, + /// Post-layout transform matrix (cos/sin values directly from the source + /// transform, avoiding lossy radians→degrees→radians conversion). + post_layout_transform: Option, + layout: Option>>, +} + +fn encode_layer_trait<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + input: &LayerTraitInput<'a, '_>, +) -> flatbuffers::WIPOffset> { + // Parent reference + let parent_id_str = fbb.create_string(input.parent_id); + let parent_nid = + fbs::NodeIdentifier::create(fbb, &fbs::NodeIdentifierArgs { id: Some(parent_id_str) }); + let pos_str = fbb.create_string(input.position); + let parent_ref = fbs::ParentReference::create( + fbb, + &fbs::ParentReferenceArgs { + parent_id: Some(parent_nid), + position: Some(pos_str), + }, + ); + + // Blend mode + let fbs_blend_mode = encode_layer_blend_mode(input.blend_mode); + + // Mask type — union requires BOTH discriminant and payload + let (fbs_mask_disc, fbs_mask_payload) = encode_mask_type(fbb, input.mask); + + // Effects + let effects = encode_layer_effects(fbb, input.effects); + + fbs::LayerTrait::create( + fbb, + &fbs::LayerTraitArgs { + parent: Some(parent_ref), + opacity: input.opacity, + blend_mode: fbs_blend_mode, + mask_type_type: fbs_mask_disc, + mask_type: fbs_mask_payload, + effects, + layout: input.layout, + post_layout_transform: input.post_layout_transform.as_ref(), + post_layout_transform_origin: None, + }, + ) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Color encoding +// ───────────────────────────────────────────────────────────────────────────── + +/// Build a `CGTransform2D` rotation matrix from rotation in degrees. +/// Used for container nodes which store rotation as a separate scalar. +fn rotation_degrees_to_transform(degrees: f32) -> Option { + if degrees == 0.0 { + None + } else { + let rad = degrees.to_radians(); + let (sin, cos) = rad.sin_cos(); + Some(fbs::CGTransform2D::new(cos, -sin, 0.0, sin, cos, 0.0)) + } +} + +/// Build a `CGTransform2D` rotation matrix directly from an `AffineTransform`'s +/// cos/sin components, avoiding the lossy radians→degrees→radians conversion. +/// Returns `None` only when the rotation is **exactly** identity (cos=1, sin=0), +/// preserving even tiny near-zero rotations for perfect round-trip fidelity. +fn affine_to_rotation_transform(t: &AffineTransform) -> Option { + let cos = t.matrix[0][0]; + let sin = t.matrix[1][0]; + // Only skip when exactly identity — even sub-epsilon rotations must survive + // round-trips so that decode(encode(decode(bytes))) is bit-identical. + if cos == 1.0 && sin == 0.0 { + None + } else { + Some(fbs::CGTransform2D::new(cos, -sin, 0.0, sin, cos, 0.0)) + } +} + +fn encode_color_to_rgba32f(c: &CGColor) -> fbs::RGBA32F { + fbs::RGBA32F::new( + c.r as f32 / 255.0, + c.g as f32 / 255.0, + c.b as f32 / 255.0, + c.a as f32 / 255.0, + ) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Blend mode encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_layer_blend_mode(lbm: LayerBlendMode) -> fbs::LayerBlendMode { + match lbm { + LayerBlendMode::PassThrough => fbs::LayerBlendMode::PassThrough, + LayerBlendMode::Blend(bm) => fbs::LayerBlendMode(encode_blend_mode(bm).0), + } +} + +fn encode_mask_type<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + mask: Option, +) -> (fbs::LayerMaskType, Option>) { + match mask { + None => (fbs::LayerMaskType::NONE, None), + Some(LayerMaskType::Image(imt)) => { + let fbs_imt = match imt { + crate::cg::types::ImageMaskType::Alpha => fbs::ImageMaskType::Alpha, + crate::cg::types::ImageMaskType::Luminance => fbs::ImageMaskType::Luminance, + }; + let table = fbs::LayerMaskTypeImage::create( + fbb, + &fbs::LayerMaskTypeImageArgs { + image_mask_type: fbs_imt, + }, + ); + ( + fbs::LayerMaskType::LayerMaskTypeImage, + Some(table.as_union_value()), + ) + } + Some(LayerMaskType::Geometry) => { + let table = fbs::LayerMaskTypeGeometry::create( + fbb, + &fbs::LayerMaskTypeGeometryArgs {}, + ); + ( + fbs::LayerMaskType::LayerMaskTypeGeometry, + Some(table.as_union_value()), + ) + } + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Paint encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_paints<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + paints: &Paints, +) -> Option>>>> { + if paints.is_empty() { + return None; + } + let items: Vec<_> = paints + .as_slice() + .iter() + .filter_map(|p| encode_paint_item(fbb, p)) + .collect(); + Some(fbb.create_vector(&items)) +} + +fn encode_paint_item<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + paint: &Paint, +) -> Option>> { + match paint { + Paint::Solid(sp) => { + let color = encode_color_to_rgba32f(&sp.color); + let solid = fbs::SolidPaint::create(fbb, &fbs::SolidPaintArgs { + active: sp.active, + color: Some(&color), + blend_mode: encode_blend_mode(sp.blend_mode), + }); + Some(fbs::PaintStackItem::create(fbb, &fbs::PaintStackItemArgs { + paint_type: fbs::Paint::SolidPaint, + paint: Some(solid.as_union_value()), + })) + } + Paint::LinearGradient(lg) => { + let stops = encode_gradient_stops(fbb, &lg.stops); + let xy1 = fbs::Alignment::new(lg.xy1.0, lg.xy1.1); + let xy2 = fbs::Alignment::new(lg.xy2.0, lg.xy2.1); + let transform = encode_affine_to_cg_transform(&lg.transform); + let lgp = fbs::LinearGradientPaint::create(fbb, &fbs::LinearGradientPaintArgs { + active: lg.active, + xy1: Some(&xy1), + xy2: Some(&xy2), + stops: Some(stops), + opacity: lg.opacity, + blend_mode: encode_blend_mode(lg.blend_mode), + transform: Some(&transform), + ..Default::default() + }); + Some(fbs::PaintStackItem::create(fbb, &fbs::PaintStackItemArgs { + paint_type: fbs::Paint::LinearGradientPaint, + paint: Some(lgp.as_union_value()), + })) + } + Paint::RadialGradient(rg) => { + let stops = encode_gradient_stops(fbb, &rg.stops); + let transform = encode_affine_to_cg_transform(&rg.transform); + let rgp = fbs::RadialGradientPaint::create(fbb, &fbs::RadialGradientPaintArgs { + active: rg.active, + stops: Some(stops), + opacity: rg.opacity, + blend_mode: encode_blend_mode(rg.blend_mode), + transform: Some(&transform), + ..Default::default() + }); + Some(fbs::PaintStackItem::create(fbb, &fbs::PaintStackItemArgs { + paint_type: fbs::Paint::RadialGradientPaint, + paint: Some(rgp.as_union_value()), + })) + } + Paint::SweepGradient(sg) => { + let stops = encode_gradient_stops(fbb, &sg.stops); + let transform = encode_affine_to_cg_transform(&sg.transform); + let sgp = fbs::SweepGradientPaint::create(fbb, &fbs::SweepGradientPaintArgs { + active: sg.active, + stops: Some(stops), + opacity: sg.opacity, + blend_mode: encode_blend_mode(sg.blend_mode), + transform: Some(&transform), + }); + Some(fbs::PaintStackItem::create(fbb, &fbs::PaintStackItemArgs { + paint_type: fbs::Paint::SweepGradientPaint, + paint: Some(sgp.as_union_value()), + })) + } + Paint::Image(ip) => { + let image_ref_offset = match &ip.image { + ResourceRef::HASH(h) => { + let hash_str = fbb.create_string(h); + let href = fbs::ResourceRefHASH::create(fbb, &fbs::ResourceRefHASHArgs { hash: Some(hash_str) }); + (fbs::ResourceRef::ResourceRefHASH, href.as_union_value()) + } + ResourceRef::RID(r) => { + let rid_str = fbb.create_string(r); + let rref = fbs::ResourceRefRID::create(fbb, &fbs::ResourceRefRIDArgs { rid: Some(rid_str) }); + (fbs::ResourceRef::ResourceRefRID, rref.as_union_value()) + } + }; + let alignment = fbs::Alignment::new(ip.alignement.0, ip.alignement.1); + let (fit_type, fit_value) = encode_image_paint_fit(fbb, &ip.fit); + let ip_offset = fbs::ImagePaint::create(fbb, &fbs::ImagePaintArgs { + active: ip.active, + image_type: image_ref_offset.0, + image: Some(image_ref_offset.1), + quarter_turns: ip.quarter_turns, + alignement: Some(&alignment), + fit_type, + fit: Some(fit_value), + opacity: ip.opacity, + blend_mode: encode_blend_mode(ip.blend_mode), + ..Default::default() + }); + Some(fbs::PaintStackItem::create(fbb, &fbs::PaintStackItemArgs { + paint_type: fbs::Paint::ImagePaint, + paint: Some(ip_offset.as_union_value()), + })) + } + _ => None, + } +} + +fn encode_image_paint_fit<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + fit: &ImagePaintFit, +) -> (fbs::ImagePaintFit, flatbuffers::WIPOffset) { + match fit { + ImagePaintFit::Fit(box_fit) => { + let f = fbs::ImagePaintFitFit::create(fbb, &fbs::ImagePaintFitFitArgs { box_fit: encode_box_fit(*box_fit) }); + (fbs::ImagePaintFit::ImagePaintFitFit, f.as_union_value()) + } + ImagePaintFit::Transform(t) => { + let ct = encode_affine_to_cg_transform(t); + let f = fbs::ImagePaintFitTransform::create(fbb, &fbs::ImagePaintFitTransformArgs { transform: Some(&ct) }); + (fbs::ImagePaintFit::ImagePaintFitTransform, f.as_union_value()) + } + ImagePaintFit::Tile(tile) => { + let fbs_repeat = match tile.repeat { + crate::cg::types::ImageRepeat::RepeatX => fbs::ImageRepeat::RepeatX, + crate::cg::types::ImageRepeat::RepeatY => fbs::ImageRepeat::RepeatY, + crate::cg::types::ImageRepeat::Repeat => fbs::ImageRepeat::Repeat, + }; + let fbs_tile = fbs::ImageTile::new(tile.scale, fbs_repeat); + let f = fbs::ImagePaintFitTile::create(fbb, &fbs::ImagePaintFitTileArgs { tile: Some(&fbs_tile) }); + (fbs::ImagePaintFit::ImagePaintFitTile, f.as_union_value()) + } + } +} + +fn encode_gradient_stops<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + stops: &[GradientStop], +) -> flatbuffers::WIPOffset> { + let fbs_stops: Vec<_> = stops + .iter() + .map(|s| { + let color = encode_color_to_rgba32f(&s.color); + fbs::GradientStop::new(s.offset, &color) + }) + .collect(); + fbb.create_vector(&fbs_stops) +} + +fn encode_affine_to_cg_transform(t: &AffineTransform) -> fbs::CGTransform2D { + fbs::CGTransform2D::new( + t.matrix[0][0], // m00 = a + t.matrix[0][1], // m01 = c + t.matrix[0][2], // m02 = tx + t.matrix[1][0], // m10 = b + t.matrix[1][1], // m11 = d + t.matrix[1][2], // m12 = ty + ) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Effects encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_layer_effects<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + effects: &LayerEffects, +) -> Option>> { + let has_any = effects.blur.is_some() + || effects.backdrop_blur.is_some() + || !effects.shadows.is_empty() + || effects.glass.is_some() + || !effects.noises.is_empty(); + if !has_any { + return None; + } + + let blur_offset = effects.blur.as_ref().map(|lb| { + let radius = match &lb.blur { FeBlur::Gaussian(g) => g.radius, _ => 0.0 }; + let gaussian = fbs::FeGaussianBlur::create(fbb, &fbs::FeGaussianBlurArgs { radius }); + fbs::FeLayerBlur::create(fbb, &fbs::FeLayerBlurArgs { + active: lb.active, + blur_type: fbs::FeBlur::FeGaussianBlur, + blur: Some(gaussian.as_union_value()), + }) + }); + + let backdrop_blur_offset = effects.backdrop_blur.as_ref().map(|bb| { + let radius = match &bb.blur { FeBlur::Gaussian(g) => g.radius, _ => 0.0 }; + let gaussian = fbs::FeGaussianBlur::create(fbb, &fbs::FeGaussianBlurArgs { radius }); + fbs::FeBackdropBlur::create(fbb, &fbs::FeBackdropBlurArgs { + active: bb.active, + blur_type: fbs::FeBlur::FeGaussianBlur, + blur: Some(gaussian.as_union_value()), + }) + }); + + let shadows_offset = if effects.shadows.is_empty() { + None + } else { + let shadow_items: Vec<_> = effects.shadows.iter().map(|s| encode_filter_shadow_effect(fbb, s)).collect(); + Some(fbb.create_vector(&shadow_items)) + }; + + let glass_offset = effects.glass.as_ref().map(|lg| { + fbs::FeLiquidGlass::create(fbb, &fbs::FeLiquidGlassArgs { + active: lg.active, + light_intensity: lg.light_intensity, + light_angle: lg.light_angle, + refraction: lg.refraction, + depth: lg.depth, + dispersion: lg.dispersion, + blur_radius: lg.blur_radius, + }) + }); + + let noises_offset = if effects.noises.is_empty() { + None + } else { + let noise_items: Vec<_> = effects.noises.iter().map(|n| encode_fe_noise_effect(fbb, n)).collect(); + Some(fbb.create_vector(&noise_items)) + }; + + Some(fbs::LayerEffects::create(fbb, &fbs::LayerEffectsArgs { + fe_blur: blur_offset, + fe_backdrop_blur: backdrop_blur_offset, + fe_shadows: shadows_offset, + fe_glass: glass_offset, + fe_noises: noises_offset, + })) +} + +fn encode_filter_shadow_effect<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + effect: &FilterShadowEffect, +) -> flatbuffers::WIPOffset> { + let (kind, shadow) = match effect { + FilterShadowEffect::DropShadow(s) => (fbs::FilterShadowEffectKind::DropShadow, s), + FilterShadowEffect::InnerShadow(s) => (fbs::FilterShadowEffectKind::InnerShadow, s), + }; + let color = encode_color_to_rgba32f(&shadow.color); + let shadow_offset = fbs::FeShadow::create(fbb, &fbs::FeShadowArgs { + active: shadow.active, dx: shadow.dx, dy: shadow.dy, + blur: shadow.blur, spread: shadow.spread, color: Some(&color), + }); + fbs::FilterShadowEffect::create(fbb, &fbs::FilterShadowEffectArgs { + kind, shadow: Some(shadow_offset), + }) +} + +fn encode_fe_noise_effect<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + noise: &FeNoiseEffect, +) -> flatbuffers::WIPOffset> { + let coloring = encode_noise_colors(fbb, &noise.coloring); + fbs::FeNoiseEffect::create(fbb, &fbs::FeNoiseEffectArgs { + active: noise.active, noise_size: noise.noise_size, density: noise.density, + num_octaves: noise.num_octaves, seed: noise.seed, coloring: Some(coloring), + blend_mode: encode_blend_mode(noise.blend_mode), + }) +} + +fn encode_noise_colors<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + colors: &NoiseEffectColors, +) -> flatbuffers::WIPOffset> { + match colors { + NoiseEffectColors::Mono { color } => { + let c = encode_color_to_rgba32f(color); + fbs::NoiseEffectColors::create(fbb, &fbs::NoiseEffectColorsArgs { + kind: fbs::NoiseEffectColorsKind::Mono, mono_color: Some(&c), ..Default::default() + }) + } + NoiseEffectColors::Duo { color1, color2 } => { + let c1 = encode_color_to_rgba32f(color1); + let c2 = encode_color_to_rgba32f(color2); + fbs::NoiseEffectColors::create(fbb, &fbs::NoiseEffectColorsArgs { + kind: fbs::NoiseEffectColorsKind::Duo, duo_color1: Some(&c1), duo_color2: Some(&c2), ..Default::default() + }) + } + NoiseEffectColors::Multi { opacity } => { + fbs::NoiseEffectColors::create(fbb, &fbs::NoiseEffectColorsArgs { + kind: fbs::NoiseEffectColorsKind::Multi, multi_opacity: *opacity, ..Default::default() + }) + } + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Stroke encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_stroke_style<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + ss: &StrokeStyle, +) -> flatbuffers::WIPOffset> { + let dash = ss.stroke_dash_array.as_ref().map(|da| { + let floats: Vec = da.0.clone(); + fbb.create_vector(&floats) + }); + fbs::StrokeStyle::create( + fbb, + &fbs::StrokeStyleArgs { + stroke_align: encode_stroke_align(ss.stroke_align), + stroke_cap: encode_stroke_cap(ss.stroke_cap), + stroke_join: encode_stroke_join(ss.stroke_join), + stroke_miter_limit: ss.stroke_miter_limit.0, + stroke_dash_array: dash, + }, + ) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Layout encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_px_offset<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + v: f32, +) -> flatbuffers::WIPOffset> { + fbs::PositioningSideOffsetValue::create( + fbb, + &fbs::PositioningSideOffsetValueArgs { + kind: fbs::PositioningSideOffsetKind::Px, + value: Some(v), + }, + ) +} + +fn encode_dim_px<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + v: Option, +) -> Option>> { + v.map(|px| { + fbs::LayoutDimensionValue::create( + fbb, + &fbs::LayoutDimensionValueArgs { + unit: fbs::LayoutDimensionUnit::LengthPx, + value: Some(px), + }, + ) + }) +} + +fn encode_dimensions<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + w: Option, + h: Option, +) -> flatbuffers::WIPOffset> { + let dim_w = encode_dim_px(fbb, w); + let dim_h = encode_dim_px(fbb, h); + fbs::LayoutDimensionStyle::create( + fbb, + &fbs::LayoutDimensionStyleArgs { + layout_target_width: dim_w, + layout_target_height: dim_h, + layout_target_aspect_ratio: None, + }, + ) +} + +fn encode_layout_child_style<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + child: &Option, +) -> Option>> { + child.as_ref().map(|lc| { + let pos = match lc.layout_positioning { + LayoutPositioning::Absolute => fbs::LayoutPositioning::Absolute, + LayoutPositioning::Auto => fbs::LayoutPositioning::Auto, + }; + fbs::LayoutChildStyle::create( + fbb, + &fbs::LayoutChildStyleArgs { + layout_grow: lc.layout_grow, + layout_positioning: pos, + }, + ) + }) +} + +fn encode_layout_position<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + pos: &LayoutPositioningBasis, +) -> ( + fbs::LayoutPositioningBasis, + Option>, +) { + match pos { + LayoutPositioningBasis::Cartesian(p) => { + let cart = fbs::LayoutPositioningCartesian::create( + fbb, + &fbs::LayoutPositioningCartesianArgs { x: p.x, y: p.y }, + ); + ( + fbs::LayoutPositioningBasis::LayoutPositioningCartesian, + Some(cart.as_union_value()), + ) + } + LayoutPositioningBasis::Inset(ei) => { + let left = encode_px_offset(fbb, ei.left); + let top = encode_px_offset(fbb, ei.top); + let right = encode_px_offset(fbb, ei.right); + let bottom = encode_px_offset(fbb, ei.bottom); + let inset = fbs::LayoutPositioningInset::create( + fbb, + &fbs::LayoutPositioningInsetArgs { + left: Some(left), + top: Some(top), + right: Some(right), + bottom: Some(bottom), + }, + ); + ( + fbs::LayoutPositioningBasis::LayoutPositioningInset, + Some(inset.as_union_value()), + ) + } + #[allow(deprecated)] + _ => (fbs::LayoutPositioningBasis::NONE, None), + } +} + +// ─── LayoutStyle builders ──────────────────────────────────────────────────── + +/// Build a LayoutStyle for shape nodes (position as Inset, dimensions, child style). +fn encode_shape_layout<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + x: f32, + y: f32, + width: Option, + height: Option, + layout_child: &Option, +) -> flatbuffers::WIPOffset> { + let left = encode_px_offset(fbb, x); + let top = encode_px_offset(fbb, y); + let inset = fbs::LayoutPositioningInset::create( + fbb, + &fbs::LayoutPositioningInsetArgs { + left: Some(left), + top: Some(top), + right: None, + bottom: None, + }, + ); + let dims = encode_dimensions(fbb, width, height); + let child = encode_layout_child_style(fbb, layout_child); + fbs::LayoutStyle::create( + fbb, + &fbs::LayoutStyleArgs { + layout_position_type: fbs::LayoutPositioningBasis::LayoutPositioningInset, + layout_position: Some(inset.as_union_value()), + layout_dimensions: Some(dims), + layout_container: None, + layout_child: child, + }, + ) +} + +/// Build a LayoutStyle for container nodes (position, dimensions, container style, child style). +fn encode_container_layout<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + position: &LayoutPositioningBasis, + dimensions: &LayoutDimensionStyle, + container_style: &LayoutContainerStyle, + layout_child: &Option, +) -> flatbuffers::WIPOffset> { + let (pos_type, pos_offset) = encode_layout_position(fbb, position); + + // Container style — use FBS `::None` sentinels for unset optional enum fields + // so the decoder can distinguish "not set" from "explicitly set to default". + let padding = container_style.layout_padding.as_ref().map(|p| { + fbs::EdgeInsets::new(p.top, p.right, p.bottom, p.left) + }); + let lc_fbs = Some(fbs::LayoutContainerStyle::create( + fbb, + &fbs::LayoutContainerStyleArgs { + layout_mode: match container_style.layout_mode { + LayoutMode::Flex => fbs::LayoutMode::Flex, + LayoutMode::Normal => fbs::LayoutMode::Normal, + }, + layout_direction: match container_style.layout_direction { + Axis::Horizontal => fbs::Axis::Horizontal, + Axis::Vertical => fbs::Axis::Vertical, + }, + layout_wrap: container_style + .layout_wrap + .map(|w| match w { + LayoutWrap::Wrap => fbs::LayoutWrap::Wrap, + LayoutWrap::NoWrap => fbs::LayoutWrap::NoWrap, + }) + .unwrap_or(fbs::LayoutWrap::None), + layout_main_axis_alignment: container_style + .layout_main_axis_alignment + .map(encode_main_axis_alignment) + .unwrap_or(fbs::MainAxisAlignment::None), + layout_cross_axis_alignment: container_style + .layout_cross_axis_alignment + .map(encode_cross_axis_alignment) + .unwrap_or(fbs::CrossAxisAlignment::None), + layout_padding: padding.as_ref(), + layout_main_axis_gap: container_style + .layout_gap + .as_ref() + .map(|g| g.main_axis_gap) + .unwrap_or(0.0), + layout_cross_axis_gap: container_style + .layout_gap + .as_ref() + .map(|g| g.cross_axis_gap) + .unwrap_or(0.0), + }, + )); + + let dims = encode_dimensions(fbb, dimensions.layout_target_width, dimensions.layout_target_height); + let child = encode_layout_child_style(fbb, layout_child); + fbs::LayoutStyle::create( + fbb, + &fbs::LayoutStyleArgs { + layout_position_type: pos_type, + layout_position: pos_offset, + layout_dimensions: Some(dims), + layout_container: lc_fbs, + layout_child: child, + }, + ) +} + +// ─── Shared encoder helpers ────────────────────────────────────────────────── + +/// Wrap a typed FBS node into a `NodeSlot`. +fn make_node_slot<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + node_type: fbs::Node, + node: flatbuffers::WIPOffset, +) -> flatbuffers::WIPOffset> { + fbs::NodeSlot::create( + fbb, + &fbs::NodeSlotArgs { + node_type, + node: Some(node), + }, + ) +} + +/// Create a `StrokeGeometryTrait` from a `StrokeStyle` and a scalar width. +fn encode_stroke_geometry<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + ss: &StrokeStyle, + width: f32, +) -> flatbuffers::WIPOffset> { + let stroke_style_offset = encode_stroke_style(fbb, ss); + fbs::StrokeGeometryTrait::create( + fbb, + &fbs::StrokeGeometryTraitArgs { + stroke_style: Some(stroke_style_offset), + stroke_width: width, + stroke_width_profile: None, + }, + ) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Node-specific encoders +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_container_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + r: &ContainerNodeRec, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + let sys = encode_system_node_trait(fbb, node_id, "", r.active, false); + let layout = encode_container_layout( + fbb, + &r.position, + &r.layout_dimensions, + &r.layout_container, + &r.layout_child, + ); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity: r.opacity, + blend_mode: r.blend_mode, + mask: r.mask, + effects: &r.effects, + post_layout_transform: rotation_degrees_to_transform(r.rotation), + layout: Some(layout), + }, + ); + + // Corner radius + let rcr = encode_rectangular_corner_radius(&r.corner_radius); + let cr_trait = fbs::RectangularCornerRadiusTrait::create( + fbb, + &fbs::RectangularCornerRadiusTraitArgs { + rectangular_corner_radius: Some(&rcr), + corner_smoothing: r.corner_smoothing.0, + }, + ); + + // Stroke geometry + let stroke_style_offset = encode_stroke_style(fbb, &r.stroke_style); + let rsw = encode_rectangular_stroke_width(&r.stroke_width); + let sg = fbs::RectangularStrokeGeometryTrait::create( + fbb, + &fbs::RectangularStrokeGeometryTraitArgs { + stroke_style: Some(stroke_style_offset), + rectangular_stroke_width: rsw.as_ref(), + stroke_width_profile: None, + }, + ); + + let fill_offsets = encode_paints(fbb, &r.fills); + let stroke_offsets = encode_paints(fbb, &r.strokes); + + let cn = fbs::ContainerNode::create(fbb, &fbs::ContainerNodeArgs { + node: Some(sys), layer: Some(layer), + corner_radius: Some(cr_trait), stroke_geometry: Some(sg), + fill_paints: fill_offsets, stroke_paints: stroke_offsets, + clips_content: r.clip, + }); + make_node_slot(fbb, fbs::Node::ContainerNode, cn.as_union_value()) +} + +fn encode_initial_container_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + r: &InitialContainerNodeRec, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + let sys = encode_system_node_trait(fbb, node_id, "", r.active, false); + + // Build container-style layout + let container_style = LayoutContainerStyle { + layout_mode: r.layout_mode, + layout_direction: r.layout_direction, + layout_wrap: Some(r.layout_wrap), + layout_main_axis_alignment: Some(r.layout_main_axis_alignment), + layout_cross_axis_alignment: Some(r.layout_cross_axis_alignment), + layout_padding: Some(r.padding), + layout_gap: Some(r.layout_gap), + }; + let layout = encode_container_layout( + fbb, + &LayoutPositioningBasis::zero(), + &LayoutDimensionStyle::default(), + &container_style, + &None, + ); + let default_effects = LayerEffects::default(); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: &default_effects, + post_layout_transform: None, + layout: Some(layout), + }, + ); + + let icn = fbs::InitialContainerNode::create(fbb, &fbs::InitialContainerNodeArgs { + node: Some(sys), layer: Some(layer), + }); + make_node_slot(fbb, fbs::Node::InitialContainerNode, icn.as_union_value()) +} + +fn encode_group_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + r: &GroupNodeRec, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + let (x, y) = match &r.transform { + Some(t) => (t.x(), t.y()), + None => (0.0, 0.0), + }; + let plt = r.transform.as_ref().and_then(affine_to_rotation_transform); + + let sys = encode_system_node_trait(fbb, node_id, "", r.active, false); + let layout = encode_shape_layout(fbb, x, y, None, None, &None); + let default_effects = LayerEffects::default(); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity: r.opacity, + blend_mode: r.blend_mode, + mask: r.mask, + effects: &default_effects, + post_layout_transform: plt, + layout: Some(layout), + }, + ); + + let gn = fbs::GroupNode::create(fbb, &fbs::GroupNodeArgs { + node: Some(sys), layer: Some(layer), + }); + make_node_slot(fbb, fbs::Node::GroupNode, gn.as_union_value()) +} + +/// Unified data source for BasicShapeNode encoding. +enum BasicShapeFields<'a> { + Rectangle(&'a RectangleNodeRec), + Ellipse(&'a EllipseNodeRec), + RegularPolygon(&'a RegularPolygonNodeRec), + RegularStarPolygon(&'a RegularStarPolygonNodeRec), +} + +fn encode_basic_shape_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + node_id: &str, + parent_id: &str, + position: &str, + shape_type: fbs::CanonicalLayerShape, + node_type: fbs::BasicShapeNodeType, + fields: BasicShapeFields<'_>, +) -> flatbuffers::WIPOffset> { + // Extract common fields from the variant + let (active, opacity, blend_mode, mask, effects, transform, size, fills, strokes, layout_child) = + match &fields { + BasicShapeFields::Rectangle(r) => ( + r.active, r.opacity, r.blend_mode, r.mask, &r.effects, &r.transform, &r.size, + &r.fills, &r.strokes, &r.layout_child, + ), + BasicShapeFields::Ellipse(e) => ( + e.active, e.opacity, e.blend_mode, e.mask, &e.effects, &e.transform, &e.size, + &e.fills, &e.strokes, &e.layout_child, + ), + BasicShapeFields::RegularPolygon(p) => ( + p.active, p.opacity, p.blend_mode, p.mask, &p.effects, &p.transform, &p.size, + &p.fills, &p.strokes, &p.layout_child, + ), + BasicShapeFields::RegularStarPolygon(s) => ( + s.active, s.opacity, s.blend_mode, s.mask, &s.effects, &s.transform, &s.size, + &s.fills, &s.strokes, &s.layout_child, + ), + }; + + // Reverse-engineer x, y from from_box_center transform + let (x, y) = reverse_from_box_center(transform, size.width, size.height); + let plt = affine_to_rotation_transform(transform); + + let sys = encode_system_node_trait(fbb, node_id, "", active, false); + let layout = encode_shape_layout( + fbb, + x, + y, + Some(size.width), + Some(size.height), + layout_child, + ); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity, + blend_mode, + mask, + effects, + post_layout_transform: plt, + layout: Some(layout), + }, + ); + + let fill_offsets = encode_paints(fbb, fills); + let stroke_offsets = encode_paints(fbb, strokes); + + // Corner radius (scalar for polygon/star, rectangular for rectangle) + let scalar_cr = match &fields { + BasicShapeFields::Rectangle(r) => r.corner_radius.tl.rx, + BasicShapeFields::Ellipse(_) => 0.0, + BasicShapeFields::RegularPolygon(p) => p.corner_radius, + BasicShapeFields::RegularStarPolygon(s) => s.corner_radius, + }; + let rect_cr = match &fields { + BasicShapeFields::Rectangle(r) => Some(encode_rectangular_corner_radius(&r.corner_radius)), + _ => None, + }; + + // Stroke + let (stroke_width_f32, stroke_style) = match &fields { + BasicShapeFields::Rectangle(r) => (r.stroke_width.max(), &r.stroke_style), + BasicShapeFields::Ellipse(e) => (e.stroke_width.0.unwrap_or(0.0), &e.stroke_style), + BasicShapeFields::RegularPolygon(p) => (p.stroke_width.0.unwrap_or(0.0), &p.stroke_style), + BasicShapeFields::RegularStarPolygon(s) => { + (s.stroke_width.0.unwrap_or(0.0), &s.stroke_style) + } + }; + let stroke_style_offset = encode_stroke_style(fbb, stroke_style); + + // Shape descriptor + let shape_offset = match &fields { + BasicShapeFields::Rectangle(_) => { + fbs::CanonicalShapeRectangular::create(fbb, &fbs::CanonicalShapeRectangularArgs {}).as_union_value() + } + BasicShapeFields::Ellipse(e) => { + let inner = e.inner_radius.unwrap_or(0.0); + let angle = e.angle.unwrap_or(360.0); + let ring_sector_data = if inner != 0.0 || angle != 360.0 || e.start_angle != 0.0 { + Some(fbs::CanonicalEllipticalShapeRingSectorParameters::create( + fbb, + &fbs::CanonicalEllipticalShapeRingSectorParametersArgs { + inner_radius_ratio: inner, + start_angle: e.start_angle, + angle, + }, + )) + } else { + None + }; + fbs::CanonicalShapeElliptical::create( + fbb, + &fbs::CanonicalShapeEllipticalArgs { ring_sector_data }, + ) + .as_union_value() + } + BasicShapeFields::RegularPolygon(p) => { + fbs::CanonicalShapeRegularPolygon::create(fbb, &fbs::CanonicalShapeRegularPolygonArgs { + point_count: p.point_count as u32, + }).as_union_value() + } + BasicShapeFields::RegularStarPolygon(s) => { + fbs::CanonicalShapeRegularStarPolygon::create(fbb, &fbs::CanonicalShapeRegularStarPolygonArgs { + point_count: s.point_count as u32, + inner_radius_ratio: s.inner_radius, + }).as_union_value() + } + }; + + let bsn = fbs::BasicShapeNode::create(fbb, &fbs::BasicShapeNodeArgs { + node: Some(sys), layer: Some(layer), + type_: node_type, shape_type, shape: Some(shape_offset), + corner_radius: scalar_cr, fill_paints: fill_offsets, + stroke_style: Some(stroke_style_offset), stroke_width: stroke_width_f32, + rectangular_corner_radius: rect_cr.as_ref(), + stroke_paints: stroke_offsets, + ..Default::default() + }); + + make_node_slot(fbb, fbs::Node::BasicShapeNode, bsn.as_union_value()) +} + +fn encode_line_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + r: &LineNodeRec, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + // Lines use AffineTransform::new(x, y, rotation) + let x = r.transform.x(); + let y = r.transform.y(); + let plt = affine_to_rotation_transform(&r.transform); + + let sys = encode_system_node_trait(fbb, node_id, "", r.active, false); + let layout = encode_shape_layout(fbb, x, y, Some(r.size.width), None, &r.layout_child); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity: r.opacity, + blend_mode: r.blend_mode, + mask: r.mask, + effects: &r.effects, + post_layout_transform: plt, + layout: Some(layout), + }, + ); + + let sg = encode_stroke_geometry(fbb, &StrokeStyle { + stroke_align: r._data_stroke_align, + stroke_cap: r.stroke_cap, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: r.stroke_miter_limit, + stroke_dash_array: r.stroke_dash_array.clone(), + }, r.stroke_width); + + let stroke_offsets = encode_paints(fbb, &r.strokes); + + let ln = fbs::LineNode::create(fbb, &fbs::LineNodeArgs { + node: Some(sys), layer: Some(layer), stroke_geometry: Some(sg), + stroke_paints: stroke_offsets, + marker_start_shape: encode_stroke_marker(r.marker_start_shape), + marker_end_shape: encode_stroke_marker(r.marker_end_shape), + }); + make_node_slot(fbb, fbs::Node::LineNode, ln.as_union_value()) +} + +fn encode_vector_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + r: &VectorNodeRec, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + let x = r.transform.x(); + let y = r.transform.y(); + let plt = affine_to_rotation_transform(&r.transform); + + let sys = encode_system_node_trait(fbb, node_id, "", r.active, false); + let layout = encode_shape_layout(fbb, x, y, None, None, &r.layout_child); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity: r.opacity, + blend_mode: r.blend_mode, + mask: r.mask, + effects: &r.effects, + post_layout_transform: plt, + layout: Some(layout), + }, + ); + + // Vector network + let vn = encode_vector_network(fbb, &r.network); + + let sg = encode_stroke_geometry(fbb, &StrokeStyle { + stroke_align: r.stroke_align, + stroke_cap: r.stroke_cap, + stroke_join: r.stroke_join, + stroke_miter_limit: r.stroke_miter_limit, + stroke_dash_array: r.stroke_dash_array.clone(), + }, r.stroke_width); + + let fill_offsets = encode_paints(fbb, &r.fills); + let stroke_offsets = encode_paints(fbb, &r.strokes); + + let vn_node = fbs::VectorNode::create(fbb, &fbs::VectorNodeArgs { + node: Some(sys), layer: Some(layer), stroke_geometry: Some(sg), + stroke_paints: stroke_offsets, fill_paints: fill_offsets, + vector_network_data: vn, + marker_start_shape: encode_stroke_marker(r.marker_start_shape), + marker_end_shape: encode_stroke_marker(r.marker_end_shape), + ..Default::default() + }); + make_node_slot(fbb, fbs::Node::VectorNode, vn_node.as_union_value()) +} + +fn encode_text_span_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + r: &TextSpanNodeRec, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + let x = r.transform.x(); + let y = r.transform.y(); + let plt = affine_to_rotation_transform(&r.transform); + + let sys = encode_system_node_trait(fbb, node_id, "", r.active, false); + let layout = encode_shape_layout(fbb, x, y, r.width, r.height, &r.layout_child); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity: r.opacity, + blend_mode: r.blend_mode, + mask: r.mask, + effects: &r.effects, + post_layout_transform: plt, + layout: Some(layout), + }, + ); + + // Text style + let font_family_str = fbb.create_string(&r.text_style.font_family); + let font_weight = fbs::FontWeight::new(r.text_style.font_weight.0); + let text_style = fbs::TextStyleRec::create(fbb, &fbs::TextStyleRecArgs { + font_family: Some(font_family_str), + font_size: r.text_style.font_size, + font_weight: Some(&font_weight), + ..Default::default() + }); + + let text_str = fbb.create_string(&r.text); + let fill_offsets = encode_paints(fbb, &r.fills); + let stroke_offsets = encode_paints(fbb, &r.strokes); + + let sg = encode_stroke_geometry(fbb, &StrokeStyle { + stroke_align: r.stroke_align, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: StrokeMiterLimit::default(), + stroke_dash_array: None, + }, r.stroke_width); + + let props = fbs::TextSpanNodeProperties::create(fbb, &fbs::TextSpanNodePropertiesArgs { + text: Some(text_str), text_style: Some(text_style), + text_align: encode_text_align(r.text_align), + text_align_vertical: encode_text_align_vertical(r.text_align_vertical), + fill_paints: fill_offsets, stroke_paints: stroke_offsets, + stroke_geometry: Some(sg), + ..Default::default() + }); + + let tn = fbs::TextSpanNode::create(fbb, &fbs::TextSpanNodeArgs { + node: Some(sys), layer: Some(layer), properties: Some(props), + }); + make_node_slot(fbb, fbs::Node::TextSpanNode, tn.as_union_value()) +} + +fn encode_boolean_operation_node<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + r: &BooleanPathOperationNodeRec, + node_id: &str, + parent_id: &str, + position: &str, +) -> flatbuffers::WIPOffset> { + let sys = encode_system_node_trait(fbb, node_id, "", r.active, false); + let layout = encode_shape_layout(fbb, 0.0, 0.0, None, None, &None); + let layer = encode_layer_trait( + fbb, + &LayerTraitInput { + parent_id, + position, + opacity: r.opacity, + blend_mode: r.blend_mode, + mask: r.mask, + effects: &r.effects, + post_layout_transform: None, + layout: Some(layout), + }, + ); + + let op = encode_boolean_path_op(r.op); + + // Corner radius + let cr_offset = r.corner_radius.map(|cr| { + let radius = fbs::CGRadius::new(cr, cr); + let cr_trait = fbs::CornerRadiusTrait::create( + fbb, + &fbs::CornerRadiusTraitArgs { + corner_radius: Some(&radius), + corner_smoothing: 0.0, + }, + ); + cr_trait + }); + + let sg = encode_stroke_geometry(fbb, &r.stroke_style, r.stroke_width.0.unwrap_or(0.0)); + let fill_offsets = encode_paints(fbb, &r.fills); + let stroke_offsets = encode_paints(fbb, &r.strokes); + + let bon = fbs::BooleanOperationNode::create( + fbb, + &fbs::BooleanOperationNodeArgs { + node: Some(sys), + layer: Some(layer), + op, + corner_radius: cr_offset, + fill_paints: fill_offsets, + stroke_geometry: Some(sg), + stroke_paints: stroke_offsets, + }, + ); + make_node_slot(fbb, fbs::Node::BooleanOperationNode, bon.as_union_value()) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Vector network encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_vector_network<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + network: &VectorNetwork, +) -> Option>> { + if network.vertices.is_empty() && network.segments.is_empty() && network.regions.is_empty() { + return None; + } + + let vertices: Vec = network + .vertices + .iter() + .map(|(x, y)| fbs::CGPoint::new(*x, *y)) + .collect(); + let vertices_vec = fbb.create_vector(&vertices); + + let segments: Vec = network + .segments + .iter() + .map(|s| { + let ta = fbs::CGPoint::new(s.ta.0, s.ta.1); + let tb = fbs::CGPoint::new(s.tb.0, s.tb.1); + fbs::VectorNetworkSegment::new(s.a as u32, s.b as u32, &ta, &tb) + }) + .collect(); + let segments_vec = fbb.create_vector(&segments); + + let region_offsets: Vec<_> = network + .regions + .iter() + .map(|r| encode_vector_network_region(fbb, r)) + .collect(); + let regions_vec = fbb.create_vector(®ion_offsets); + + Some(fbs::VectorNetworkData::create( + fbb, + &fbs::VectorNetworkDataArgs { + vertices: Some(vertices_vec), + segments: Some(segments_vec), + regions: Some(regions_vec), + }, + )) +} + +fn encode_vector_network_region<'a, A: flatbuffers::Allocator + 'a>( + fbb: &mut flatbuffers::FlatBufferBuilder<'a, A>, + region: &VectorNetworkRegion, +) -> flatbuffers::WIPOffset> { + let loops: Vec<_> = region + .loops + .iter() + .map(|l| { + let indices: Vec = l.0.iter().map(|&i| i as u32).collect(); + let indices_vec = fbb.create_vector(&indices); + fbs::VectorNetworkLoop::create( + fbb, + &fbs::VectorNetworkLoopArgs { + loop_segment_indices: Some(indices_vec), + }, + ) + }) + .collect(); + let loops_vec = fbb.create_vector(&loops); + + let fill_rule = match region.fill_rule { + crate::cg::types::FillRule::EvenOdd => fbs::FillRule::EvenOdd, + crate::cg::types::FillRule::NonZero => fbs::FillRule::NonZero, + }; + + let fills_offset = region + .fills + .as_ref() + .and_then(|p| encode_paints(fbb, p)); + + fbs::VectorNetworkRegion::create( + fbb, + &fbs::VectorNetworkRegionArgs { + region_loops: Some(loops_vec), + region_fill_rule: fill_rule, + region_fill_paints: fills_offset, + }, + ) +} + +// ───────────────────────────────────────────────────────────────────────────── +// Geometry helpers for encoding +// ───────────────────────────────────────────────────────────────────────────── + +fn encode_rectangular_corner_radius( + cr: &RectangularCornerRadius, +) -> fbs::RectangularCornerRadius { + let tl = fbs::CGRadius::new(cr.tl.rx, cr.tl.ry); + let tr = fbs::CGRadius::new(cr.tr.rx, cr.tr.ry); + let bl = fbs::CGRadius::new(cr.bl.rx, cr.bl.ry); + let br = fbs::CGRadius::new(cr.br.rx, cr.br.ry); + fbs::RectangularCornerRadius::new(&tl, &tr, &bl, &br) +} + +fn encode_rectangular_stroke_width(sw: &StrokeWidth) -> Option { + match sw { + StrokeWidth::None => Some(fbs::RectangularStrokeWidth::new(0.0, 0.0, 0.0, 0.0)), + StrokeWidth::Uniform(w) => Some(fbs::RectangularStrokeWidth::new(*w, *w, *w, *w)), + StrokeWidth::Rectangular(rsw) => Some(fbs::RectangularStrokeWidth::new( + rsw.stroke_top_width, + rsw.stroke_right_width, + rsw.stroke_bottom_width, + rsw.stroke_left_width, + )), + } +} + +/// Reverse-engineer (x, y) from an `AffineTransform` that was created via +/// `AffineTransform::from_box_center(x, y, w, h, rotation_deg)`. +/// +/// `from_box_center` = `from_box(x, y, w, h, deg, 0.5, 0.5)`: +/// tx = x + (w/2)*(1-cos) + sin*(h/2) +/// ty = y + (h/2)*(1-cos) - sin*(w/2) +/// +/// So: x = tx - (w/2)*(1-cos) - sin*(h/2) +/// y = ty - (h/2)*(1-cos) + sin*(w/2) +fn reverse_from_box_center(t: &AffineTransform, w: f32, h: f32) -> (f32, f32) { + let cos = t.matrix[0][0]; + let sin = t.matrix[1][0]; + let tx = t.matrix[0][2]; + let ty = t.matrix[1][2]; + let x = tx - (w / 2.0) * (1.0 - cos) - sin * (h / 2.0); + let y = ty - (h / 2.0) * (1.0 - cos) + sin * (w / 2.0); + (x, y) +} diff --git a/crates/grida-canvas/src/io/mod.rs b/crates/grida-canvas/src/io/mod.rs index 94b5a8297b..e59de298fd 100644 --- a/crates/grida-canvas/src/io/mod.rs +++ b/crates/grida-canvas/src/io/mod.rs @@ -1,6 +1,10 @@ #[cfg(feature = "figma")] pub mod io_figma; +pub mod generated; + +pub mod io_grida_fbs; + pub mod id_converter; pub mod io_css; pub mod io_grida; diff --git a/crates/grida-dev/Cargo.toml b/crates/grida-dev/Cargo.toml index 9538fec493..8faf234970 100644 --- a/crates/grida-dev/Cargo.toml +++ b/crates/grida-dev/Cargo.toml @@ -47,3 +47,4 @@ indicatif = "0.17" toml = "0.9.8" glob = "0.3.3" arboard = "3" +zip = { version = "8.2.0", default-features = false, features = ["deflate-flate2"] } From 36cd8ab51882e3c60a131b4d078f0bc8d97d5286 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 12 Mar 2026 18:12:46 +0900 Subject: [PATCH 12/16] Add scene navigation and .grida file support - Introduced `NextScene` and `PrevScene` commands to facilitate scene navigation using PageUp/PageDown keys. - Implemented `nodes_iter` method in `SceneGraph` for iterating over nodes. - Added `grida_file.rs` for handling `.grida` file format detection and decoding, supporting both raw FlatBuffers and ZIP archives. - Updated `main.rs` to load multiple scenes and manage scene switching. - Enhanced demo window functions to support displaying multiple scenes. These changes improve the user experience by allowing seamless navigation between scenes and integrating .grida file support for scene loading. --- crates/grida-canvas/src/node/scene_graph.rs | 8 ++ crates/grida-canvas/src/window/application.rs | 4 +- crates/grida-canvas/src/window/command.rs | 2 + crates/grida-dev/src/grida_file.rs | 79 +++++++++++++++++++ crates/grida-dev/src/main.rs | 45 ++++++----- .../src/platform/native_application.rs | 49 +++++++++++- crates/grida-dev/src/platform/native_demo.rs | 23 +++++- crates/math2/src/transform.rs | 36 +++++++++ 8 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 crates/grida-dev/src/grida_file.rs diff --git a/crates/grida-canvas/src/node/scene_graph.rs b/crates/grida-canvas/src/node/scene_graph.rs index 817858b4b7..1c9f07a607 100644 --- a/crates/grida-canvas/src/node/scene_graph.rs +++ b/crates/grida-canvas/src/node/scene_graph.rs @@ -263,6 +263,14 @@ impl SceneGraph { self.nodes.is_empty() } + /// Iterate over all `(NodeId, &Node)` pairs in the graph. + /// + /// The iteration order is not guaranteed; callers should use `roots()` + + /// `get_children()` if they need tree order. + pub fn nodes_iter(&self) -> impl Iterator { + self.nodes.iter() + } + // ------------------------------------------------------------------------- // Tree Traversal Methods // ------------------------------------------------------------------------- diff --git a/crates/grida-canvas/src/window/application.rs b/crates/grida-canvas/src/window/application.rs index d6f5101f3a..7bb1b72365 100644 --- a/crates/grida-canvas/src/window/application.rs +++ b/crates/grida-canvas/src/window/application.rs @@ -295,7 +295,9 @@ impl ApplicationApi for UnknownTargetApplication { } } } - ApplicationCommand::None => {} + ApplicationCommand::None + | ApplicationCommand::NextScene + | ApplicationCommand::PrevScene => {} } false diff --git a/crates/grida-canvas/src/window/command.rs b/crates/grida-canvas/src/window/command.rs index df974c6832..8063accc99 100644 --- a/crates/grida-canvas/src/window/command.rs +++ b/crates/grida-canvas/src/window/command.rs @@ -7,4 +7,6 @@ pub enum ApplicationCommand { Pan { tx: f32, ty: f32 }, ToggleDebugMode, TryCopyAsPNG, + NextScene, + PrevScene, } diff --git a/crates/grida-dev/src/grida_file.rs b/crates/grida-dev/src/grida_file.rs new file mode 100644 index 0000000000..ff7990faee --- /dev/null +++ b/crates/grida-dev/src/grida_file.rs @@ -0,0 +1,79 @@ +//! `.grida` file format detection and decoding. +//! +//! A `.grida` file is one of two variants: +//! - **Raw FlatBuffers** – identified by the `"GRID"` file identifier at bytes 4–7. +//! - **ZIP archive** – identified by PK magic bytes; contains `manifest.json` and +//! `document.grida` (a raw FlatBuffers binary) plus optional `images/` and `bitmaps/`. + +use anyhow::{anyhow, Context, Result}; +use cg::io::io_grida_fbs; +use cg::node::schema::Scene; + +/// Detected variant of a `.grida` file. +enum Format { + RawFbs, + Zip, + Unknown, +} + +fn detect(bytes: &[u8]) -> Format { + // ZIP magic: PK\x03\x04 or PK\x05\x06 + if bytes.len() >= 4 + && bytes[0] == 0x50 + && bytes[1] == 0x4b + && (bytes[2] == 0x03 || bytes[2] == 0x05) + { + return Format::Zip; + } + // Raw FlatBuffers: file identifier "GRID" at bytes 4–7 + if bytes.len() >= 8 && &bytes[4..8] == b"GRID" { + return Format::RawFbs; + } + Format::Unknown +} + +/// Extracts the raw FlatBuffers bytes from a `.grida` ZIP archive. +fn extract_fbs_from_zip(bytes: &[u8]) -> Result> { + use std::io::{Cursor, Read}; + let cursor = Cursor::new(bytes); + let mut archive = zip::ZipArchive::new(cursor).context("failed to open .grida ZIP")?; + + anyhow::ensure!( + archive.by_name("manifest.json").is_ok(), + ".grida ZIP is missing manifest.json" + ); + + let mut doc_file = archive + .by_name("document.grida") + .context(".grida ZIP is missing document.grida")?; + let mut fbs_bytes = Vec::with_capacity(doc_file.size() as usize); + doc_file + .read_to_end(&mut fbs_bytes) + .context("failed to read document.grida from ZIP")?; + Ok(fbs_bytes) +} + +/// Decodes a `.grida` file (raw FlatBuffers or ZIP) into a [`Scene`]. +/// +/// If the file contains multiple scenes, only the first is returned. +/// Use [`decode_all`] to get all scenes. +pub fn decode(bytes: &[u8]) -> Result { + decode_all(bytes)? + .into_iter() + .next() + .ok_or_else(|| anyhow!("no scenes found in .grida file")) +} + +/// Decodes a `.grida` file into all [`Scene`]s it contains. +pub fn decode_all(bytes: &[u8]) -> Result> { + match detect(bytes) { + Format::RawFbs => io_grida_fbs::decode_all(bytes).map_err(|err| anyhow!("{err}")), + Format::Zip => { + let fbs_bytes = extract_fbs_from_zip(bytes)?; + io_grida_fbs::decode_all(&fbs_bytes).map_err(|err| anyhow!("{err}")) + } + Format::Unknown => Err(anyhow!( + "unrecognized file format: expected a .grida FlatBuffers binary or ZIP archive" + )), + } +} diff --git a/crates/grida-dev/src/main.rs b/crates/grida-dev/src/main.rs index b5a606ecda..274012d6e9 100644 --- a/crates/grida-dev/src/main.rs +++ b/crates/grida-dev/src/main.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result}; use cg::cg::prelude::*; use cg::cg::types::ResourceRef; use cg::helpers::webfont_helper::{find_font_files, load_webfonts_metadata}; -use cg::io::{io_figma::FigmaConverter, io_grida}; +use cg::io::io_figma::FigmaConverter; use cg::node::factory::NodeFactory; use cg::node::scene_graph::{Parent, SceneGraph}; use cg::node::schema::{Node, Scene, Size}; @@ -17,8 +17,9 @@ use figma_api::apis::{ use futures::channel::mpsc; use futures::future::join_all; use grida_dev::platform::native_demo::{ - run_demo_window, run_demo_window_with, run_demo_window_with_drop, + run_demo_window, run_demo_window_multi, run_demo_window_with, run_demo_window_with_drop, }; +mod grida_file; mod reftest; use image::image_dimensions; use math2::transform::AffineTransform; @@ -124,8 +125,13 @@ async fn main() -> Result<()> { } async fn run_scene(source: &str) -> Result<()> { - let scene = load_scene_from_source(source).await?; - run_demo_window(scene).await; + let scenes = load_scenes_from_source(source).await?; + if scenes.len() > 1 { + println!("Loaded {} scenes (PageUp/PageDown to switch)", scenes.len()); + run_demo_window_multi(scenes).await; + } else { + run_demo_window(scenes.into_iter().next().unwrap()).await; + } Ok(()) } @@ -277,26 +283,29 @@ async fn run_master() -> Result<()> { } async fn load_scene_from_source(source: &str) -> Result { - let data = if is_url(source) { + let bytes = read_source_bytes(source).await?; + grida_file::decode(&bytes) +} + +async fn load_scenes_from_source(source: &str) -> Result> { + let bytes = read_source_bytes(source).await?; + grida_file::decode_all(&bytes) +} + +async fn read_source_bytes(source: &str) -> Result> { + if is_url(source) { reqwest::get(source) .await .with_context(|| format!("failed to download scene from {source}"))? - .text() + .bytes() .await - .context("failed to read downloaded scene body")? + .context("failed to read downloaded scene body") + .map(|b| b.to_vec()) } else { - async_fs::read_to_string(source) + async_fs::read(source) .await - .with_context(|| format!("failed to read scene file at {source}"))? - }; - - let file = - io_grida::parse(&data).context("failed to parse scene JSON. expected a `.grida` export")?; - - let mut converter = cg::io::id_converter::IdConverter::new(); - converter - .convert_json_canvas_file(file) - .map_err(|err| anyhow!(err)) + .with_context(|| format!("failed to read scene file at {source}")) + } } async fn load_figma_scene(args: &FigmaArgs) -> Result<(Scene, FigmaConverter)> { diff --git a/crates/grida-dev/src/platform/native_application.rs b/crates/grida-dev/src/platform/native_application.rs index d6c92abba4..cfa1a108c3 100644 --- a/crates/grida-dev/src/platform/native_application.rs +++ b/crates/grida-dev/src/platform/native_application.rs @@ -1,5 +1,5 @@ use super::winit::{winit_window, WinitResult}; -use cg::node::schema::Size; +use cg::node::schema::{Scene, Size}; use cg::resources::{FontMessage, ImageMessage}; use cg::runtime::camera::Camera2D; use cg::window::application::{ApplicationApi, HostEvent, UnknownTargetApplication}; @@ -72,7 +72,11 @@ fn handle_key_pressed( _ => ApplicationCommand::None, } } else { - ApplicationCommand::None + match key { + Key::Named(winit::keyboard::NamedKey::PageDown) => ApplicationCommand::NextScene, + Key::Named(winit::keyboard::NamedKey::PageUp) => ApplicationCommand::PrevScene, + _ => ApplicationCommand::None, + } } } @@ -84,6 +88,10 @@ pub struct NativeApplication { pub(crate) modifiers: winit::keyboard::ModifiersState, file_drop_tx: Option>, fit_scene_on_load: bool, + /// All scenes loaded from the file (for PageUp/PageDown switching). + pub(crate) scenes: Vec, + /// Index of the currently displayed scene in `scenes`. + pub(crate) scene_index: usize, } impl NativeApplication { @@ -154,6 +162,8 @@ impl NativeApplication { modifiers: winit::keyboard::ModifiersState::default(), file_drop_tx, fit_scene_on_load, + scenes: Vec::new(), + scene_index: 0, }; std::thread::spawn(move || loop { @@ -217,8 +227,39 @@ impl NativeApplicationHandler for NativeApplication { } } - match handle_window_event(&event, &self.modifiers) { - cmd => { + let cmd = handle_window_event(&event, &self.modifiers); + match &cmd { + ApplicationCommand::NextScene | ApplicationCommand::PrevScene => { + if !self.scenes.is_empty() { + let new_index = match &cmd { + ApplicationCommand::NextScene => { + (self.scene_index + 1) % self.scenes.len() + } + ApplicationCommand::PrevScene => { + (self.scene_index + self.scenes.len() - 1) % self.scenes.len() + } + _ => unreachable!(), + }; + if new_index != self.scene_index { + self.scene_index = new_index; + let scene = self.scenes[new_index].clone(); + let title = format!( + "[{}/{}] {}", + new_index + 1, + self.scenes.len(), + scene.name, + ); + eprintln!("{title}"); + let renderer = self.app.renderer_mut(); + renderer.load_scene(scene); + fit_camera_to_scene(renderer); + renderer.queue_unstable(); + self.window.request_redraw(); + self.window.set_title(&title); + } + } + } + _ => { let is_copy_png = matches!(cmd, ApplicationCommand::TryCopyAsPNG); let ok = self.app.command(cmd); diff --git a/crates/grida-dev/src/platform/native_demo.rs b/crates/grida-dev/src/platform/native_demo.rs index e3676c341b..d9cacc6638 100644 --- a/crates/grida-dev/src/platform/native_demo.rs +++ b/crates/grida-dev/src/platform/native_demo.rs @@ -13,6 +13,16 @@ pub async fn run_demo_window(scene: Scene) { run_demo_window_with(scene, |_, _, _, _| {}).await; } +/// Run a demo window with multiple scenes (PageUp/PageDown to switch). +#[allow(dead_code)] +pub async fn run_demo_window_multi(scenes: Vec) { + let first = scenes + .first() + .cloned() + .expect("run_demo_window_multi requires at least one scene"); + run_demo_window_core_multi(first, scenes, |_, _, _, _| {}, None).await; +} + pub async fn run_demo_window_with(scene: Scene, init: F) where F: FnOnce( @@ -22,7 +32,7 @@ where winit::event_loop::EventLoopProxy, ), { - run_demo_window_core(scene, init, None).await; + run_demo_window_core_multi(scene.clone(), vec![scene], init, None).await; } pub async fn run_demo_window_with_drop(scene: Scene, init: F, drop_tx: UnboundedSender) @@ -34,11 +44,12 @@ where winit::event_loop::EventLoopProxy, ), { - run_demo_window_core(scene, init, Some(drop_tx)).await; + run_demo_window_core_multi(scene.clone(), vec![scene], init, Some(drop_tx)).await; } -async fn run_demo_window_core( +async fn run_demo_window_core_multi( scene: Scene, + all_scenes: Vec, init: F, file_drop_tx: Option>, ) where @@ -99,6 +110,12 @@ async fn run_demo_window_core( let renderer = app.app.renderer_mut(); renderer.load_scene(scene.clone()); } + app.scenes = all_scenes; + app.scene_index = 0; + if app.scenes.len() > 1 { + let title = format!("[1/{}] {}", app.scenes.len(), scene.name); + app.window.set_title(&title); + } app.app.devtools_rendering_set_show_fps_meter(true); app.app.devtools_rendering_set_show_stats(true); app.app.devtools_rendering_set_show_hit_testing(true); diff --git a/crates/math2/src/transform.rs b/crates/math2/src/transform.rs index b278c2e427..fb64e5cc11 100644 --- a/crates/math2/src/transform.rs +++ b/crates/math2/src/transform.rs @@ -95,6 +95,42 @@ impl AffineTransform { Self::from_box(x, y, w, h, deg, 0.5, 0.5) } + /// Like [`from_box`] but takes pre-computed `cos` and `sin` values instead + /// of a degree angle. This avoids the lossy `degrees → radians → sin_cos` + /// round-trip when the original cos/sin are available (e.g. from a stored + /// rotation matrix). + pub fn from_box_raw( + x: f32, + y: f32, + w: f32, + h: f32, + cos: f32, + sin: f32, + origin_x: f32, + origin_y: f32, + ) -> Self { + let ox = w * origin_x; + let oy = h * origin_y; + let tx = x + ox * (1.0 - cos) + sin * oy; + let ty = y + oy * (1.0 - cos) - sin * ox; + Self { + matrix: [[cos, -sin, tx], [sin, cos, ty]], + } + } + + /// Like [`from_box_center`] but takes pre-computed `cos` and `sin`. + pub fn from_box_center_raw(x: f32, y: f32, w: f32, h: f32, cos: f32, sin: f32) -> Self { + Self::from_box_raw(x, y, w, h, cos, sin, 0.5, 0.5) + } + + /// Creates a transform with translation and rotation from raw cos/sin. + /// Equivalent to `new(tx, ty, radians)` but without computing sin_cos. + pub fn from_translation_rotation_raw(tx: f32, ty: f32, cos: f32, sin: f32) -> Self { + Self { + matrix: [[cos, -sin, tx], [sin, cos, ty]], + } + } + pub fn x(&self) -> f32 { self.matrix[0][2] } From 710240ef15abf0fd3a46bfaae441d93ebac6f3ed Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 12 Mar 2026 18:14:00 +0900 Subject: [PATCH 13/16] add FlatBuffers test fixtures - Updated the README.md to provide a clearer overview of Grida Canvas, including its capabilities, rendering methods, and API details. - Introduced new test fixtures for FlatBuffers, including `fbs_fixtures.rs` for packing scenes and `fbs_roundtrip.rs` for in-memory round-trip tests of the `.grida` FlatBuffers encoder/decoder. - Added various fixture files to cover different node types and effects, ensuring comprehensive testing of the rendering engine's features. These changes improve documentation clarity and enhance testing coverage for the Grida Canvas rendering engine. --- crates/grida-canvas/README.md | 134 +- crates/grida-canvas/tests/fbs_fixtures.rs | 36 + crates/grida-canvas/tests/fbs_roundtrip.rs | 1697 +++++++++++++++++ crates/grida-canvas/tests/fixtures/README.md | 382 ++++ .../tests/fixtures/l0_boolean_operation.rs | 88 + .../tests/fixtures/l0_container.rs | 190 ++ .../grida-canvas/tests/fixtures/l0_effects.rs | 77 + .../tests/fixtures/l0_effects_glass.rs | 65 + .../grida-canvas/tests/fixtures/l0_group.rs | 83 + .../grida-canvas/tests/fixtures/l0_image.rs | 37 + .../tests/fixtures/l0_image_filters.rs | 80 + .../tests/fixtures/l0_layout_flex.rs | 121 ++ .../tests/fixtures/l0_layout_position.rs | 70 + .../tests/fixtures/l0_layout_transform.rs | 38 + .../grida-canvas/tests/fixtures/l0_masks.rs | 75 + .../grida-canvas/tests/fixtures/l0_paints.rs | 14 + .../tests/fixtures/l0_paints_stack.rs | 70 + .../tests/fixtures/l0_shape_arc.rs | 111 ++ .../tests/fixtures/l0_shape_polygon.rs | 162 ++ .../grida-canvas/tests/fixtures/l0_shapes.rs | 86 + .../grida-canvas/tests/fixtures/l0_strokes.rs | 81 + .../tests/fixtures/l0_strokes_rect.rs | 157 ++ crates/grida-canvas/tests/fixtures/l0_type.rs | 246 +++ .../tests/fixtures/l0_type_features.rs | 181 ++ .../tests/fixtures/l0_type_fvar.rs | 161 ++ .../grida-canvas/tests/fixtures/l0_vector.rs | 152 ++ crates/grida-canvas/tests/fixtures/mod.rs | 363 ++++ fixtures/test-grida/L0.grida | Bin 0 -> 48616 bytes fixtures/test-grida/README.md | 17 +- 29 files changed, 4935 insertions(+), 39 deletions(-) create mode 100644 crates/grida-canvas/tests/fbs_fixtures.rs create mode 100644 crates/grida-canvas/tests/fbs_roundtrip.rs create mode 100644 crates/grida-canvas/tests/fixtures/README.md create mode 100644 crates/grida-canvas/tests/fixtures/l0_boolean_operation.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_container.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_effects.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_effects_glass.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_group.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_image.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_image_filters.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_layout_flex.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_layout_position.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_layout_transform.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_masks.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_paints.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_paints_stack.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_shape_arc.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_shape_polygon.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_shapes.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_strokes.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_strokes_rect.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_type.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_type_features.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_type_fvar.rs create mode 100644 crates/grida-canvas/tests/fixtures/l0_vector.rs create mode 100644 crates/grida-canvas/tests/fixtures/mod.rs create mode 100644 fixtures/test-grida/L0.grida diff --git a/crates/grida-canvas/README.md b/crates/grida-canvas/README.md index 03bcccea8f..50c970bb84 100644 --- a/crates/grida-canvas/README.md +++ b/crates/grida-canvas/README.md @@ -2,52 +2,116 @@ Grida Canvas Rendering Backend Example -Grida Canvas core graphics implements High-level Node & Property based Graphics API that supports mordern design techniques. +Grida Canvas is a **safe, high-performance 2D real-time rendering engine** for the Grida design tool. It provides a node- and property-based graphics API with support for modern design techniques. -## Rendering +- **Rendering**: [`skia-safe`](https://rust-skia.github.io/doc/skia_safe/) for painting +- **Geometry & math**: [`math2`](../math2/README.md) for transforms, rects, and common operations -**2D Nodes** +## Capabilities -- [ ] TextSpan -- [ ] Text (Text with mixed styles) -- [ ] Image -- [ ] Bitmap (for bitmap drawing) -- [ ] Group -- [ ] Container (Frame) -- [ ] Rectangle -- [ ] Ellipse -- [ ] Polygon -- [ ] RegularPolygon -- [ ] RegularStarPolygon -- [ ] Path (SVG Path) -- [ ] Vector (Vector Network) -- [ ] Line +### 2D nodes -**Meta** +| Node | Status | +| ------------------------------------------- | ------ | +| Container (Frame) | ✅ | +| Group | ✅ | +| Rectangle | ✅ | +| Ellipse (incl. arc, ring, sector) | ✅ | +| Polygon, RegularPolygon, RegularStarPolygon | ✅ | +| Line | ✅ | +| Path (SVG path) | ✅ | +| Vector (vector network) | ✅ | +| BooleanOperation | ✅ | +| TextSpan | ✅ | +| Image | ✅ | +| InitialContainer, Error | ✅ | -- [ ] Mask -- [ ] Clip +### Paints & effects -**Styles & Effects** +- **Fills**: Solid, LinearGradient, RadialGradient, SweepGradient, Image (with fit and image filters) +- **Strokes**: Stroke width (uniform/variable), dash array, caps/joins, stroke alignment, rect-specific stroke widths, markers +- **Effects**: Layer blur, backdrop blur, drop/inner shadow, liquid glass, noise +- **Blend modes** on layers and paints -- [ ] SolidPaint -- [ ] LinearGradientPaint -- [ ] RadialGradientPaint -- [ ] DropShadow -- [ ] BoxShadow -- [ ] BlendMode +### Layout -## API +- **Taffy**-based layout: flex (direction, alignment, gap, padding), positioning (inset, width/height), transform -**Camera** +### Meta -- [ ] 2D Camera +- **Masks** (layer and vector masks) -**Pipeline & API** +### Pipeline & runtime -- [ ] load font -- [ ] load image +- **Camera**: 2D camera (pan, zoom, viewport) +- **Resources**: Font loading (`FontRepository`), image loading (`ImageRepository`), embedded fonts (e.g. Geist, Geist Mono) +- **Export**: PNG, JPEG, WEBP, BMP, PDF, SVG -## Interactivity +### Interactivity -- [ ] Hit testing +- **Hit testing**: `HitTester` for point-in-node and hit-test queries + +## I/O + +| Module | Description | +| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `io::io_grida_fbs` | Encode/decode `.grida` FlatBuffers binaries. Bindings are generated from `format/grida.fbs` via `flatc --rust` (see repo root and `format/README.md`). | +| `io::io_grida` | Decode Grida JSON documents into a `Scene`. | +| `io::io_grida_patch` | Apply JSON Patch to a decoded scene. | +| `io::io_figma` | Decode Figma API JSON into a `Scene` (feature-gated: `--features figma`). | +| `io::io_svg`, `io::io_css`, `io::io_markdown` | Additional import/parsing helpers. | + +### Test fixtures + +Raw FlatBuffers test fixtures live in [`fixtures/test-grida/`](../../fixtures/test-grida/) at the repo root. In-memory round-trip tests are in `cargo test --package cg --test fbs_roundtrip`. See the fixtures directory README for format and version details. + +## NodeID system + +The canvas uses a dual-ID system: + +- **NodeId** (`u64`): Internal counter-based IDs for high-performance operations. Ephemeral and not serialized. +- **UserNodeId** (`String`): External user-provided IDs for public APIs. Stable and serialized in `.grida` files. + +See [AGENTS.md](./AGENTS.md#nodeid-system) for details and migration notes. + +## Testing & development + +```sh +# run tests +cargo test + +# run fmt +cargo fmt + +# run check +cargo check +cargo check --all-targets --all-features + +# run clippy (no deps — skips re-checking skia etc.) +cargo clippy --no-deps --all-targets --all-features + +# run build +cargo build + +# run examples (many require window/GPU; headless_gpu requires native-gl-context) +cargo run --example +cargo run --example headless_gpu --features native-gl-context +``` + +## Tools + +### `tool_io_grida` — Grida file validator + +CLI for validating `.grida` files and debugging parsing. + +```sh +cargo run --example tool_io_grida +``` + +Validates file structure, reports node counts and types, and surfaces decode errors. See [examples/tool_io_grida.rs](./examples/tool_io_grida.rs) for full documentation. + +## Package docs + +```sh +cargo doc --package skia-safe --open --no-deps +``` diff --git a/crates/grida-canvas/tests/fbs_fixtures.rs b/crates/grida-canvas/tests/fbs_fixtures.rs new file mode 100644 index 0000000000..c0eaaebfdc --- /dev/null +++ b/crates/grida-canvas/tests/fbs_fixtures.rs @@ -0,0 +1,36 @@ +//! L0 fixture writer — packs all L0 scenes into a single `L0.grida`. +//! +//! To regenerate: +//! cargo test --package cg --test fbs_fixtures -- write_l0 + +mod fixtures; + +/// Pack all L0 scenes into `L0.grida`. +#[test] +fn write_l0() { + let scenes: Vec<(&str, _)> = vec![ + ("L0-shapes", fixtures::l0_shapes::build()), + ("L0-shape-arc", fixtures::l0_shape_arc::build()), + ("L0-shape-polygon", fixtures::l0_shape_polygon::build()), + ("L0-vector", fixtures::l0_vector::build()), + ("L0-paints", fixtures::l0_paints::build()), + ("L0-paints-stack", fixtures::l0_paints_stack::build()), + ("L0-strokes", fixtures::l0_strokes::build()), + ("L0-strokes-rect", fixtures::l0_strokes_rect::build()), + ("L0-image", fixtures::l0_image::build()), + ("L0-image-filters", fixtures::l0_image_filters::build()), + ("L0-effects", fixtures::l0_effects::build()), + ("L0-effects-glass", fixtures::l0_effects_glass::build()), + ("L0-type", fixtures::l0_type::build()), + ("L0-type-fvar", fixtures::l0_type_fvar::build()), + ("L0-type-features", fixtures::l0_type_features::build()), + ("L0-masks", fixtures::l0_masks::build()), + ("L0-boolean-operation", fixtures::l0_boolean_operation::build()), + ("L0-container", fixtures::l0_container::build()), + ("L0-group", fixtures::l0_group::build()), + ("L0-layout-position", fixtures::l0_layout_position::build()), + ("L0-layout-flex", fixtures::l0_layout_flex::build()), + ("L0-layout-transform", fixtures::l0_layout_transform::build()), + ]; + fixtures::write_multi_fixture(&scenes, "L0"); +} diff --git a/crates/grida-canvas/tests/fbs_roundtrip.rs b/crates/grida-canvas/tests/fbs_roundtrip.rs new file mode 100644 index 0000000000..6cce8e08d5 --- /dev/null +++ b/crates/grida-canvas/tests/fbs_roundtrip.rs @@ -0,0 +1,1697 @@ +//! In-memory round-trip tests for the `.grida` FlatBuffers encoder/decoder. +//! +//! Test binary: `fbs_roundtrip` (this file). Each `gen_*` test builds a scene +//! in Rust, encodes → decodes → compares every node's `Debug` output. No files +//! are written to disk. +//! +//! - **Compile-time breakage**: any schema change will cause a build error. +//! - **Exhaustive coverage**: every node type, paint variant, effect kind, +//! and stroke/corner/layout option is exercised. + +use std::collections::HashMap; + +use cg::cg::alignment::Alignment; +use cg::cg::color::CGColor; +use cg::cg::fe::*; +use cg::cg::stroke_dasharray::StrokeDashArray; +use cg::cg::stroke_width::{RectangularStrokeWidth, SingularStrokeWidth, StrokeWidth}; +use cg::cg::tilemode::TileMode; +use cg::cg::types::*; +use cg::io::io_grida_fbs; +use cg::node::scene_graph::SceneGraph; +use cg::node::schema::*; +use cg::vectornetwork::*; +use math2::transform::AffineTransform; + +// ═════════════════════════════════════════════════════════════════════════════ +// Helpers +// ═════════════════════════════════════════════════════════════════════════════ + +/// Build a `Scene` from a list of `(id, node)` pairs and a parent→children map. +/// All top-level entries (those whose id is NOT a child of any other entry) become roots. +fn build_scene( + name: &str, + bg: Option, + nodes: Vec<(NodeId, Node)>, + links: HashMap>, + roots: Vec, +) -> Scene { + let graph = SceneGraph::new_from_snapshot(nodes, links, roots); + Scene { + name: name.to_owned(), + graph, + background_color: bg, + } +} + +/// Build `id_map` and `position_map` for a scene, matching the convention used +/// by the TS encoder (string node IDs and fractional-index position keys). +fn build_maps( + scene: &Scene, + id_map: &mut HashMap, + position_map: &mut HashMap, +) { + fn walk( + graph: &SceneGraph, + nid: &NodeId, + counter: &mut usize, + id_map: &mut HashMap, + position_map: &mut HashMap, + ) { + id_map.entry(*nid).or_insert_with(|| format!("n{nid}")); + if let Some(children) = graph.get_children(nid) { + for (i, child) in children.clone().iter().enumerate() { + // Zero-pad to ensure lexicographic order matches insertion order + let pos = format!("a{i:04}"); + position_map.insert(*child, pos); + walk(graph, child, counter, id_map, position_map); + } + } + *counter += 1; + } + let mut counter = 0usize; + for root in scene.graph.roots() { + walk(&scene.graph, &root, &mut counter, id_map, position_map); + } +} + +/// Encode a scene, then decode it, and assert every node's `Debug` output matches. +fn assert_roundtrip_scene(scene: &Scene, scene_id: &str, label: &str) { + let mut id_map: HashMap = HashMap::new(); + let mut position_map: HashMap = HashMap::new(); + build_maps(scene, &mut id_map, &mut position_map); + + // Encode + let bytes = io_grida_fbs::encode(scene, scene_id, &id_map, &position_map); + assert!(!bytes.is_empty(), "{label}: encoded bytes empty"); + + // Decode + let dr = io_grida_fbs::decode_with_id_map(&bytes) + .unwrap_or_else(|e| panic!("{label}: decode failed: {e}")); + assert!(!dr.scenes.is_empty(), "{label}: no scenes decoded"); + let scene2 = &dr.scenes[0]; + + // Compare scene name + assert_eq!(scene.name, scene2.name, "{label}: scene name mismatch"); + + // Compare background color + assert_eq!( + format!("{:?}", scene.background_color), + format!("{:?}", scene2.background_color), + "{label}: background_color mismatch" + ); + + // Collect and compare all nodes + let nodes1 = collect_debug(&scene.graph); + let nodes2 = collect_debug(&scene2.graph); + assert_eq!( + nodes1.len(), + nodes2.len(), + "{label}: node count mismatch ({} vs {})", + nodes1.len(), + nodes2.len() + ); + for (i, (d1, d2)) in nodes1.iter().zip(nodes2.iter()).enumerate() { + assert_eq!( + d1, d2, + "{label}: node[{i}] mismatch.\n--- original ---\n{d1}\n--- decoded ---\n{d2}" + ); + } + + // Also verify encode stability: encode again and compare bytes + let bytes2 = io_grida_fbs::encode(scene2, &dr.scene_ids[0], &dr.id_map, &dr.position_map); + let dr2 = io_grida_fbs::decode_with_id_map(&bytes2) + .unwrap_or_else(|e| panic!("{label}: decode 2 failed: {e}")); + let bytes3 = io_grida_fbs::encode( + &dr2.scenes[0], + &dr2.scene_ids[0], + &dr2.id_map, + &dr2.position_map, + ); + assert_eq!( + bytes2, bytes3, + "{label}: encode not stable (len {} vs {})", + bytes2.len(), + bytes3.len() + ); +} + +fn collect_debug(graph: &SceneGraph) -> Vec { + let mut out = Vec::new(); + for root in graph.roots() { + collect_debug_preorder(graph, &root, &mut out); + } + out +} + +fn collect_debug_preorder(graph: &SceneGraph, nid: &NodeId, out: &mut Vec) { + if let Ok(node) = graph.get_node(nid) { + out.push(format!("{node:?}")); + if let Some(children) = graph.get_children(nid) { + for child in children.clone() { + collect_debug_preorder(graph, &child, out); + } + } + } +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Paint builders +// ═════════════════════════════════════════════════════════════════════════════ + +fn solid(r: u8, g: u8, b: u8, a: u8) -> Paint { + Paint::Solid(SolidPaint { + active: true, + color: CGColor { r, g, b, a }, + blend_mode: BlendMode::Normal, + }) +} + +fn linear_gradient() -> Paint { + Paint::LinearGradient(LinearGradientPaint { + active: true, + xy1: Alignment::CENTER_LEFT, + xy2: Alignment::CENTER_RIGHT, + tile_mode: TileMode::default(), + transform: AffineTransform::default(), + stops: vec![ + GradientStop { + offset: 0.0, + color: CGColor { + r: 255, + g: 0, + b: 0, + a: 255, + }, + }, + GradientStop { + offset: 1.0, + color: CGColor { + r: 0, + g: 0, + b: 255, + a: 255, + }, + }, + ], + opacity: 1.0, + blend_mode: BlendMode::Normal, + }) +} + +fn radial_gradient() -> Paint { + Paint::RadialGradient(RadialGradientPaint { + active: true, + transform: AffineTransform::default(), + stops: vec![ + GradientStop { + offset: 0.0, + color: CGColor { + r: 255, + g: 255, + b: 0, + a: 255, + }, + }, + GradientStop { + offset: 1.0, + color: CGColor { + r: 0, + g: 128, + b: 0, + a: 200, + }, + }, + ], + opacity: 0.8, + blend_mode: BlendMode::Screen, + tile_mode: TileMode::default(), + }) +} + +fn sweep_gradient() -> Paint { + Paint::SweepGradient(SweepGradientPaint { + active: true, + transform: AffineTransform::default(), + stops: vec![ + GradientStop { + offset: 0.0, + color: CGColor { + r: 0, + g: 255, + b: 255, + a: 255, + }, + }, + GradientStop { + offset: 0.5, + color: CGColor { + r: 255, + g: 0, + b: 255, + a: 255, + }, + }, + GradientStop { + offset: 1.0, + color: CGColor { + r: 255, + g: 255, + b: 0, + a: 255, + }, + }, + ], + opacity: 0.9, + blend_mode: BlendMode::Overlay, + }) +} + +fn image_paint() -> Paint { + Paint::Image(ImagePaint { + active: true, + image: ResourceRef::HASH("abc123hash".to_owned()), + quarter_turns: 1, + alignement: Alignment::TOP_LEFT, + fit: ImagePaintFit::Fit(math2::box_fit::BoxFit::Cover), + opacity: 0.95, + blend_mode: BlendMode::Multiply, + filters: ImageFilters::default(), + }) +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Effect builders +// ═════════════════════════════════════════════════════════════════════════════ + +fn full_effects() -> LayerEffects { + LayerEffects { + blur: Some(FeLayerBlur { + blur: FeBlur::Gaussian(FeGaussianBlur { radius: 4.0 }), + active: true, + }), + backdrop_blur: Some(FeBackdropBlur { + blur: FeBlur::Gaussian(FeGaussianBlur { radius: 8.0 }), + active: true, + }), + shadows: vec![ + FilterShadowEffect::DropShadow(FeShadow { + dx: 2.0, + dy: 3.0, + blur: 6.0, + spread: 1.0, + color: CGColor { + r: 0, + g: 0, + b: 0, + a: 128, + }, + active: true, + }), + FilterShadowEffect::InnerShadow(FeShadow { + dx: -1.0, + dy: -1.0, + blur: 2.0, + spread: 0.0, + color: CGColor { + r: 255, + g: 255, + b: 255, + a: 64, + }, + active: false, + }), + ], + glass: Some(FeLiquidGlass { + light_intensity: 0.7, + light_angle: 45.0, + refraction: 1.5, + depth: 0.3, + dispersion: 0.1, + blur_radius: 3.0, + active: true, + }), + noises: vec![ + FeNoiseEffect { + noise_size: 100.0, + density: 0.5, + num_octaves: 4, + seed: 42.0, + coloring: NoiseEffectColors::Mono { + color: CGColor { + r: 128, + g: 128, + b: 128, + a: 80, + }, + }, + active: true, + blend_mode: BlendMode::Overlay, + }, + FeNoiseEffect { + noise_size: 50.0, + density: 0.8, + num_octaves: 2, + seed: 7.0, + coloring: NoiseEffectColors::Duo { + color1: CGColor { + r: 0, + g: 0, + b: 0, + a: 200, + }, + color2: CGColor { + r: 255, + g: 255, + b: 255, + a: 200, + }, + }, + active: true, + blend_mode: BlendMode::SoftLight, + }, + FeNoiseEffect { + noise_size: 200.0, + density: 0.3, + num_octaves: 1, + seed: 0.0, + coloring: NoiseEffectColors::Multi { opacity: 0.6 }, + active: false, + blend_mode: BlendMode::Normal, + }, + ], + } +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Stroke / corner radius builders +// ═════════════════════════════════════════════════════════════════════════════ + +fn dashed_stroke_style() -> StrokeStyle { + StrokeStyle { + stroke_align: StrokeAlign::Outside, + stroke_cap: StrokeCap::Round, + stroke_join: StrokeJoin::Round, + stroke_miter_limit: StrokeMiterLimit(8.0), + stroke_dash_array: Some(StrokeDashArray(vec![5.0, 3.0, 1.0, 3.0])), + } +} + +fn per_side_corner_radius() -> RectangularCornerRadius { + RectangularCornerRadius { + tl: Radius::elliptical(10.0, 10.0), + tr: Radius::elliptical(20.0, 15.0), + bl: Radius::elliptical(5.0, 5.0), + br: Radius::elliptical(0.0, 0.0), + } +} + +fn rectangular_stroke_width() -> StrokeWidth { + StrokeWidth::Rectangular(RectangularStrokeWidth { + stroke_top_width: 1.0, + stroke_right_width: 2.0, + stroke_bottom_width: 3.0, + stroke_left_width: 4.0, + }) +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Tests — one per node type / feature area +// ═════════════════════════════════════════════════════════════════════════════ + +// ─── Rectangle ────────────────────────────────────────────────────────────── + +#[test] +fn gen_rectangle_basic() { + let rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 0.85, + blend_mode: LayerBlendMode::Blend(BlendMode::Multiply), + mask: None, + transform: AffineTransform::from_box_center(50.0, 60.0, 200.0, 100.0, 30.0), + size: Size { + width: 200.0, + height: 100.0, + }, + corner_radius: per_side_corner_radius(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(255, 0, 0, 255), linear_gradient()]), + strokes: Paints::new(vec![solid(0, 0, 255, 255)]), + stroke_style: dashed_stroke_style(), + stroke_width: StrokeWidth::Uniform(2.5), + effects: full_effects(), + layout_child: Some(LayoutChildStyle { + layout_grow: 1.0, + layout_positioning: LayoutPositioning::Absolute, + }), + }); + + let scene = build_scene( + "Rectangle Test", + Some(CGColor { + r: 240, + g: 240, + b: 240, + a: 255, + }), + vec![(1, rect)], + HashMap::new(), + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "rectangle_basic"); +} + +#[test] +fn gen_rectangle_per_side_stroke() { + let rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 80.0, 80.0, 0.0), + size: Size { + width: 80.0, + height: 80.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(200, 200, 200, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle { + stroke_align: StrokeAlign::Inside, + stroke_cap: StrokeCap::Square, + stroke_join: StrokeJoin::Bevel, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + }, + // Note: BasicShapeNode encodes stroke_width as a single f32 via `max()`, + // so per-side (Rectangular) stroke widths are collapsed on roundtrip. + // Use Uniform here; Container nodes support Rectangular stroke widths. + stroke_width: StrokeWidth::Uniform(4.0), + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene("RectStroke", None, vec![(1, rect)], HashMap::new(), vec![1]); + assert_roundtrip_scene(&scene, "s1", "rectangle_per_side_stroke"); +} + +// ─── Ellipse ──────────────────────────────────────────────────────────────── + +#[test] +fn gen_ellipse() { + let ellipse = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 0.6, + blend_mode: LayerBlendMode::Blend(BlendMode::Screen), + mask: None, + transform: AffineTransform::from_box_center(100.0, 100.0, 90.0, 60.0, 0.0), + size: Size { + width: 90.0, + height: 60.0, + }, + fills: Paints::new(vec![radial_gradient()]), + strokes: Paints::new(vec![solid(128, 0, 128, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(Some(1.5)), + inner_radius: None, + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene("Ellipse Test", None, vec![(1, ellipse)], HashMap::new(), vec![1]); + assert_roundtrip_scene(&scene, "s1", "ellipse"); +} + +#[test] +fn gen_ellipse_arc() { + // Donut (inner_radius only) + let donut = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + size: Size { width: 100.0, height: 100.0 }, + fills: Paints::new(vec![solid(0, 200, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: Some(0.5), + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + // Pie wedge (start_angle + angle) + let pie = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(120.0, 0.0, 100.0, 100.0, 0.0), + size: Size { width: 100.0, height: 100.0 }, + fills: Paints::new(vec![solid(200, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 45.0, + angle: Some(90.0), + // Note: corner_radius is not in the FBS schema, so it won't roundtrip. + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + // Arc sector (inner_radius + start_angle + angle) + let arc = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(240.0, 0.0, 100.0, 100.0, 0.0), + size: Size { width: 100.0, height: 100.0 }, + fills: Paints::new(vec![solid(0, 0, 200, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: Some(0.6), + start_angle: 0.0, + angle: Some(270.0), + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene( + "Ellipse Arcs", + None, + vec![(1, donut), (2, pie), (3, arc)], + HashMap::new(), + vec![1, 2, 3], + ); + assert_roundtrip_scene(&scene, "s1", "ellipse_arc"); +} + +// ─── Regular Polygon ──────────────────────────────────────────────────────── + +#[test] +fn gen_regular_polygon() { + let poly = Node::RegularPolygon(RegularPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + size: Size { + width: 100.0, + height: 100.0, + }, + point_count: 6, + corner_radius: 5.0, + fills: Paints::new(vec![sweep_gradient()]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + let scene = build_scene("Polygon", None, vec![(1, poly)], HashMap::new(), vec![1]); + assert_roundtrip_scene(&scene, "s1", "regular_polygon"); +} + +// ─── Regular Star Polygon ─────────────────────────────────────────────────── + +#[test] +fn gen_regular_star_polygon() { + let star = Node::RegularStarPolygon(RegularStarPolygonNodeRec { + active: true, + opacity: 0.9, + blend_mode: LayerBlendMode::Blend(BlendMode::Overlay), + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(50.0, 50.0, 120.0, 120.0, 15.0), + size: Size { + width: 120.0, + height: 120.0, + }, + point_count: 5, + inner_radius: 0.4, + corner_radius: 3.0, + fills: Paints::new(vec![solid(255, 215, 0, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(Some(2.0)), + layout_child: None, + }); + + let scene = build_scene("Star", None, vec![(1, star)], HashMap::new(), vec![1]); + assert_roundtrip_scene(&scene, "s1", "regular_star_polygon"); +} + +// ─── Line ─────────────────────────────────────────────────────────────────── + +#[test] +fn gen_line() { + let line = Node::Line(LineNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::new(10.0, 20.0, 45.0), + size: Size { + width: 200.0, + height: 0.0, + }, + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width: 3.0, + stroke_cap: StrokeCap::Round, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: Some(StrokeDashArray(vec![10.0, 5.0])), + _data_stroke_align: StrokeAlign::Center, + marker_start_shape: StrokeMarkerPreset::Circle, + marker_end_shape: StrokeMarkerPreset::EquilateralTriangle, + layout_child: None, + }); + + let scene = build_scene("Line", None, vec![(1, line)], HashMap::new(), vec![1]); + assert_roundtrip_scene(&scene, "s1", "line"); +} + +// ─── Text Span ────────────────────────────────────────────────────────────── + +#[test] +fn gen_text_span() { + let text = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(30.0, 40.0, 0.0), + width: Some(250.0), + height: Some(80.0), + layout_child: Some(LayoutChildStyle { + layout_grow: 0.0, + layout_positioning: LayoutPositioning::Auto, + }), + text: "Hello, Grida!".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Roboto", 24.0); + ts.font_weight = FontWeight(700); + ts + }, + text_align: TextAlign::Center, + text_align_vertical: TextAlignVertical::Center, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![solid(255, 0, 0, 128)]), + stroke_width: 0.5, + stroke_align: StrokeAlign::Outside, + opacity: 0.95, + blend_mode: LayerBlendMode::Blend(BlendMode::Normal), + mask: None, + effects: LayerEffects { + blur: None, + backdrop_blur: None, + shadows: vec![FilterShadowEffect::DropShadow(FeShadow { + dx: 1.0, + dy: 1.0, + blur: 2.0, + spread: 0.0, + color: CGColor { + r: 0, + g: 0, + b: 0, + a: 100, + }, + active: true, + })], + glass: None, + noises: vec![], + }, + }); + + let scene = build_scene("Text", None, vec![(1, text)], HashMap::new(), vec![1]); + assert_roundtrip_scene(&scene, "s1", "text_span"); +} + +// ─── Vector Network ───────────────────────────────────────────────────────── + +#[test] +fn gen_vector_node() { + let vector = Node::Vector(VectorNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + network: VectorNetwork { + vertices: vec![(0.0, 0.0), (100.0, 0.0), (100.0, 100.0), (0.0, 100.0)], + segments: vec![ + VectorNetworkSegment { + a: 0, + b: 1, + ta: (30.0, 0.0), + tb: (-30.0, 0.0), + }, + VectorNetworkSegment { + a: 1, + b: 2, + ta: (0.0, 30.0), + tb: (0.0, -30.0), + }, + VectorNetworkSegment { + a: 2, + b: 3, + ta: (-30.0, 0.0), + tb: (30.0, 0.0), + }, + VectorNetworkSegment { + a: 3, + b: 0, + ta: (0.0, -30.0), + tb: (0.0, 30.0), + }, + ], + regions: vec![VectorNetworkRegion { + loops: vec![VectorNetworkLoop(vec![0, 1, 2, 3])], + fill_rule: FillRule::EvenOdd, + fills: Some(Paints::new(vec![solid(100, 200, 50, 255)])), + }], + }, + corner_radius: 0.0, + fills: Paints::new(vec![solid(50, 100, 200, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width: 1.5, + stroke_width_profile: None, + stroke_align: StrokeAlign::Center, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + marker_start_shape: StrokeMarkerPreset::Diamond, + marker_end_shape: StrokeMarkerPreset::VerticalBar, + layout_child: None, + }); + + let scene = build_scene("Vector", None, vec![(1, vector)], HashMap::new(), vec![1]); + assert_roundtrip_scene(&scene, "s1", "vector_node"); +} + +// ─── Boolean Operation ────────────────────────────────────────────────────── + +#[test] +fn gen_boolean_operation() { + // boolean op contains children (shapes), but the BooleanPathOperationNodeRec + // itself only holds the op metadata; children are in the graph links. + let bool_op = Node::BooleanOperation(BooleanPathOperationNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: Some(AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0)), + op: BooleanPathOperation::Intersection, + corner_radius: Some(4.0), + fills: Paints::new(vec![solid(200, 100, 50, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(Some(1.0)), + }); + + let child_rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 60.0, 60.0, 0.0), + size: Size { + width: 60.0, + height: 60.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64]); + + let scene = build_scene( + "Boolean", + None, + vec![(1, bool_op), (2, child_rect)], + links, + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "boolean_operation"); +} + +// ─── Group ────────────────────────────────────────────────────────────────── + +#[test] +fn gen_group() { + let group = Node::Group(GroupNodeRec { + active: true, + opacity: 0.8, + blend_mode: LayerBlendMode::Blend(BlendMode::Darken), + mask: None, + transform: Some(AffineTransform::from_box_center(10.0, 20.0, 0.0, 0.0, 45.0)), + }); + + let child_ellipse = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 50.0, 50.0, 0.0), + size: Size { + width: 50.0, + height: 50.0, + }, + fills: Paints::new(vec![solid(0, 255, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64]); + + let scene = build_scene( + "Group", + None, + vec![(1, group), (2, child_ellipse)], + links, + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "group"); +} + +// ─── Container ────────────────────────────────────────────────────────────── + +#[test] +fn gen_container_with_layout() { + let container = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 10.0, + right: 0.0, + bottom: 0.0, + left: 20.0, + }), + layout_container: LayoutContainerStyle { + layout_mode: LayoutMode::Flex, + layout_direction: Axis::Horizontal, + layout_wrap: Some(LayoutWrap::Wrap), + layout_main_axis_alignment: Some(MainAxisAlignment::SpaceBetween), + layout_cross_axis_alignment: Some(CrossAxisAlignment::Center), + layout_padding: Some(EdgeInsets { + top: 8.0, + right: 12.0, + bottom: 8.0, + left: 12.0, + }), + layout_gap: Some(LayoutGap { + main_axis_gap: 10.0, + cross_axis_gap: 5.0, + }), + }, + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(400.0), + layout_target_height: Some(300.0), + layout_min_width: None, + layout_max_width: None, + layout_min_height: None, + layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: per_side_corner_radius(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(245, 245, 245, 255)]), + strokes: Paints::new(vec![solid(200, 200, 200, 255)]), + stroke_style: StrokeStyle { + stroke_align: StrokeAlign::Inside, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + }, + stroke_width: rectangular_stroke_width(), + effects: LayerEffects::default(), + clip: true, + }); + + // A child that tests layout_child properties + let child = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 80.0, 40.0, 0.0), + size: Size { + width: 80.0, + height: 40.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(100, 149, 237, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: Some(LayoutChildStyle { + layout_grow: 1.0, + layout_positioning: LayoutPositioning::Auto, + }), + }); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64]); + + let scene = build_scene( + "Container Layout", + None, + vec![(1, container), (2, child)], + links, + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "container_with_layout"); +} + +#[test] +fn gen_container_rotated() { + let container = Node::Container(ContainerNodeRec { + active: true, + opacity: 0.5, + blend_mode: LayerBlendMode::Blend(BlendMode::ColorBurn), + mask: None, + // Use 90.0 (exact multiple of 90) to avoid cos/sin float precision + // loss during roundtrip. Non-cardinal angles like 30.0 lose precision + // (e.g. 30.0 → 30.000002) when stored as cos/sin in the transform matrix. + rotation: 90.0, + position: LayoutPositioningBasis::Cartesian(CGPoint::new(100.0, 50.0)), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(200.0), + layout_target_height: Some(150.0), + layout_min_width: None, + layout_max_width: None, + layout_min_height: None, + layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![image_paint()]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: full_effects(), + clip: false, + }); + + let scene = build_scene( + "RotatedContainer", + None, + vec![(1, container)], + HashMap::new(), + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "container_rotated"); +} + +// ─── InitialContainer ─────────────────────────────────────────────────────── + +#[test] +fn gen_initial_container() { + let ic = Node::InitialContainer(InitialContainerNodeRec { + active: true, + layout_mode: LayoutMode::Flex, + layout_direction: Axis::Vertical, + layout_wrap: LayoutWrap::NoWrap, + layout_main_axis_alignment: MainAxisAlignment::Center, + layout_cross_axis_alignment: CrossAxisAlignment::Stretch, + padding: EdgeInsets { + top: 16.0, + right: 16.0, + bottom: 16.0, + left: 16.0, + }, + layout_gap: LayoutGap { + main_axis_gap: 8.0, + cross_axis_gap: 0.0, + }, + }); + + let child = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 50.0, 0.0), + size: Size { + width: 100.0, + height: 50.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(50, 150, 50, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64]); + + let scene = build_scene( + "InitialContainer", + None, + vec![(1, ic), (2, child)], + links, + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "initial_container"); +} + +// ─── Masks ────────────────────────────────────────────────────────────────── + +#[test] +fn gen_mask_types() { + let image_mask = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: Some(LayerMaskType::Image(ImageMaskType::Alpha)), + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + size: Size { + width: 100.0, + height: 100.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(255, 255, 255, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let geo_mask = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: Some(LayerMaskType::Geometry), + transform: AffineTransform::from_box_center(50.0, 50.0, 80.0, 80.0, 0.0), + size: Size { + width: 80.0, + height: 80.0, + }, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene( + "Masks", + None, + vec![(1, image_mask), (2, geo_mask)], + HashMap::new(), + vec![1, 2], + ); + assert_roundtrip_scene(&scene, "s1", "mask_types"); +} + +// ─── All blend modes ──────────────────────────────────────────────────────── + +#[test] +fn gen_all_blend_modes() { + let blend_modes = [ + BlendMode::Normal, + BlendMode::Multiply, + BlendMode::Screen, + BlendMode::Overlay, + BlendMode::Darken, + BlendMode::Lighten, + BlendMode::ColorDodge, + BlendMode::ColorBurn, + BlendMode::HardLight, + BlendMode::SoftLight, + BlendMode::Difference, + BlendMode::Exclusion, + BlendMode::Hue, + BlendMode::Saturation, + BlendMode::Color, + BlendMode::Luminosity, + ]; + + let mut nodes = Vec::new(); + for (i, &bm) in blend_modes.iter().enumerate() { + let id = (i + 1) as u64; + nodes.push(( + id, + Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::Blend(bm), + mask: None, + transform: AffineTransform::from_box_center( + (i as f32) * 20.0, + 0.0, + 10.0, + 10.0, + 0.0, + ), + size: Size { + width: 10.0, + height: 10.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(128, 128, 128, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }), + )); + } + + // Also test PassThrough + nodes.push(( + 100, + Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 50.0, 10.0, 10.0, 0.0), + size: Size { + width: 10.0, + height: 10.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }), + )); + + let roots: Vec = nodes.iter().map(|(id, _)| *id).collect(); + let scene = build_scene("BlendModes", None, nodes, HashMap::new(), roots); + assert_roundtrip_scene(&scene, "s1", "all_blend_modes"); +} + +// ─── All paint types on a single node ─────────────────────────────────────── + +#[test] +fn gen_all_paint_types() { + let rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + size: Size { + width: 100.0, + height: 100.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![ + solid(255, 0, 0, 255), + linear_gradient(), + radial_gradient(), + sweep_gradient(), + image_paint(), + ]), + strokes: Paints::new(vec![solid(0, 0, 0, 255), linear_gradient()]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene( + "AllPaints", + None, + vec![(1, rect)], + HashMap::new(), + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "all_paint_types"); +} + +// ─── All effects on a single node ─────────────────────────────────────────── + +#[test] +fn gen_all_effects() { + let rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + size: Size { + width: 100.0, + height: 100.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(200, 200, 200, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: full_effects(), + layout_child: None, + }); + + let scene = build_scene( + "AllEffects", + None, + vec![(1, rect)], + HashMap::new(), + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "all_effects"); +} + +// ─── Inactive / edge cases ────────────────────────────────────────────────── + +#[test] +fn gen_inactive_node() { + let rect = Node::Rectangle(RectangleNodeRec { + active: false, + opacity: 0.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 1.0, 1.0, 0.0), + size: Size { + width: 1.0, + height: 1.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene( + "Inactive", + None, + vec![(1, rect)], + HashMap::new(), + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "inactive_node"); +} + +// ─── Deep nesting ─────────────────────────────────────────────────────────── + +#[test] +fn gen_deep_nesting() { + // Container → Group → Container → Rectangle + let c1 = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Cartesian(CGPoint::new(0.0, 0.0)), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(500.0), + layout_target_height: Some(500.0), + layout_min_width: None, + layout_max_width: None, + layout_min_height: None, + layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(255, 255, 255, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + clip: true, + }); + + let g = Node::Group(GroupNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: Some(AffineTransform::from_box_center(0.0, 0.0, 0.0, 0.0, 0.0)), + }); + + let c2 = Node::Container(ContainerNodeRec { + active: true, + opacity: 0.9, + blend_mode: LayerBlendMode::Blend(BlendMode::Normal), + mask: None, + // Use 0.0 to avoid cos/sin → atan2 float precision loss on roundtrip. + // Non-cardinal angles lose precision (e.g. 15.0 → 15.000001), and + // 180.0 aliases to -180.0 via atan2. + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 50.0, + right: 50.0, + bottom: 50.0, + left: 50.0, + }), + layout_container: LayoutContainerStyle { + layout_mode: LayoutMode::Flex, + layout_direction: Axis::Vertical, + layout_wrap: None, + layout_main_axis_alignment: Some(MainAxisAlignment::Start), + layout_cross_axis_alignment: None, + layout_padding: None, + layout_gap: None, + }, + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(200.0), + layout_target_height: Some(200.0), + layout_min_width: None, + layout_max_width: None, + layout_min_height: None, + layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![linear_gradient()]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + clip: false, + }); + + let leaf = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(10.0, 10.0, 50.0, 50.0, 0.0), + size: Size { + width: 50.0, + height: 50.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(255, 0, 128, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64]); + links.insert(2u64, vec![3u64]); + links.insert(3u64, vec![4u64]); + + let scene = build_scene( + "DeepNesting", + Some(CGColor { + r: 30, + g: 30, + b: 30, + a: 255, + }), + vec![(1, c1), (2, g), (3, c2), (4, leaf)], + links, + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "deep_nesting"); +} + +// ─── All stroke marker presets ────────────────────────────────────────────── + +#[test] +fn gen_all_stroke_markers() { + let markers = [ + (StrokeMarkerPreset::None, StrokeMarkerPreset::RightTriangleOpen), + (StrokeMarkerPreset::EquilateralTriangle, StrokeMarkerPreset::Circle), + (StrokeMarkerPreset::Square, StrokeMarkerPreset::Diamond), + (StrokeMarkerPreset::VerticalBar, StrokeMarkerPreset::None), + ]; + + let mut nodes = Vec::new(); + for (i, (start, end)) in markers.iter().enumerate() { + let id = (i + 1) as u64; + nodes.push(( + id, + Node::Line(LineNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::new(0.0, (i as f32) * 30.0, 0.0), + size: Size { + width: 100.0, + height: 0.0, + }, + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width: 2.0, + stroke_cap: StrokeCap::Butt, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + _data_stroke_align: StrokeAlign::Center, + marker_start_shape: *start, + marker_end_shape: *end, + layout_child: None, + }), + )); + } + + let roots: Vec = nodes.iter().map(|(id, _)| *id).collect(); + let scene = build_scene("Markers", None, nodes, HashMap::new(), roots); + assert_roundtrip_scene(&scene, "s1", "all_stroke_markers"); +} + +// ─── Boolean operation variants ───────────────────────────────────────────── + +#[test] +fn gen_all_boolean_ops() { + let ops = [ + BooleanPathOperation::Union, + BooleanPathOperation::Intersection, + BooleanPathOperation::Difference, + BooleanPathOperation::Xor, + ]; + + let mut nodes = Vec::new(); + for (i, &op) in ops.iter().enumerate() { + let id = (i + 1) as u64; + nodes.push(( + id, + Node::BooleanOperation(BooleanPathOperationNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + // The encoder ignores transform; the decoder always returns + // Some(from_translation_rotation_raw(0,0,1,0)) which has -0.0 + // in the -sin slot. Match the exact decoded representation. + transform: Some(AffineTransform::from_translation_rotation_raw( + 0.0, 0.0, 1.0, 0.0, + )), + op, + corner_radius: None, + fills: Paints::new(vec![solid(100, 100, 100, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + }), + )); + } + + let roots: Vec = nodes.iter().map(|(id, _)| *id).collect(); + let scene = build_scene("BoolOps", None, nodes, HashMap::new(), roots); + assert_roundtrip_scene(&scene, "s1", "all_boolean_ops"); +} + +// ─── Text alignment variants ──────────────────────────────────────────────── + +#[test] +fn gen_text_align_variants() { + let aligns = [ + (TextAlign::Left, TextAlignVertical::Top), + (TextAlign::Center, TextAlignVertical::Center), + (TextAlign::Right, TextAlignVertical::Bottom), + (TextAlign::Justify, TextAlignVertical::Top), + ]; + + let mut nodes = Vec::new(); + for (i, &(h, v)) in aligns.iter().enumerate() { + let id = (i + 1) as u64; + nodes.push(( + id, + Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, (i as f32) * 50.0, 0.0), + width: Some(200.0), + height: None, + layout_child: None, + text: format!("Align {i}"), + text_style: TextStyleRec::from_font("Inter", 16.0), + text_align: h, + text_align_vertical: v, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }), + )); + } + + let roots: Vec = nodes.iter().map(|(id, _)| *id).collect(); + let scene = build_scene("TextAligns", None, nodes, HashMap::new(), roots); + assert_roundtrip_scene(&scene, "s1", "text_align_variants"); +} + +// ─── Image paint with ResourceRef::RID ────────────────────────────────────── + +#[test] +fn gen_image_paint_rid() { + let rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 200.0, 150.0, 0.0), + size: Size { + width: 200.0, + height: 150.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![Paint::Image(ImagePaint { + active: true, + image: ResourceRef::RID("res://images/photo.jpg".to_owned()), + quarter_turns: 0, + alignement: Alignment::CENTER, + fit: ImagePaintFit::Fit(math2::box_fit::BoxFit::Contain), + opacity: 1.0, + blend_mode: BlendMode::Normal, + filters: ImageFilters::default(), + })]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene( + "ImagePaintRID", + None, + vec![(1, rect)], + HashMap::new(), + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "image_paint_rid"); +} + +// ─── Image paint with transform fit ───────────────────────────────────────── + +#[test] +fn gen_image_paint_transform_fit() { + let rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + size: Size { + width: 100.0, + height: 100.0, + }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![Paint::Image(ImagePaint { + active: true, + image: ResourceRef::HASH("def456".to_owned()), + quarter_turns: 2, + alignement: Alignment::BOTTOM_RIGHT, + fit: ImagePaintFit::Transform(AffineTransform::from_acebdf( + 0.5, 0.0, 10.0, 0.0, 0.5, 20.0, + )), + opacity: 0.8, + blend_mode: BlendMode::Screen, + filters: ImageFilters::default(), + })]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let scene = build_scene( + "ImageTransformFit", + None, + vec![(1, rect)], + HashMap::new(), + vec![1], + ); + assert_roundtrip_scene(&scene, "s1", "image_paint_transform_fit"); +} diff --git a/crates/grida-canvas/tests/fixtures/README.md b/crates/grida-canvas/tests/fixtures/README.md new file mode 100644 index 0000000000..00217d68c0 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/README.md @@ -0,0 +1,382 @@ +# `.grida` Test Fixtures + +Generated by the Rust test suite: + +```sh +cargo test --package cg --test fbs_fixtures -- write_l0 +``` + +Each fixture is a self-contained `.grida` file focusing on a single feature area. +The naming convention is `L0-.grida` where `L0` indicates baseline coverage. + +--- + +## L0-shapes + +One of each basic shape type with a plain solid fill. No effects, no strokes (except line), no layout tricks. + +``` +scene "L0 Shapes" +├─ rectangle 200×100 red solid +├─ ellipse 100×80 blue solid +├─ regular_polygon 100×100 hexagon (6 sides), green solid, corner_radius=5 +├─ regular_star 120×120 5-point star, inner_radius=0.4, gold solid +├─ line 200px black stroke 2px +├─ text_span "Hello, Grida!" Inter 20px, black solid +└─ vector 100×100 closed bezier quad (4 segments), purple fill + black stroke 1px +``` + +**Exercises:** all 7 leaf node types that the encoder supports. + +--- + +## L0-shape-arc + +Ellipse arcs demonstrating inner_radius, start_angle, sweep angle, and corner_radius. + +``` +scene "L0 Shape Arc" +├─ ellipse full circle (defaults) +├─ ellipse semicircle (angle=180°) +├─ ellipse donut (inner_radius=0.5) +├─ ellipse pie wedge (start_angle=45°, angle=90°) +└─ ellipse rounded arc (inner_radius=0.6, angle=270°, corner_radius=8) +``` + +**Exercises:** EllipseNodeRec arc fields: inner_radius, start_angle, angle, corner_radius. + +--- + +## L0-shape-polygon + +Regular polygons and star polygons with varying point counts and parameters. + +``` +scene "L0 Shape Polygon" +├─ regular_polygon triangle (3 sides) +├─ regular_polygon pentagon (5, corner_radius=10) +├─ regular_polygon hexagon (6, corner_radius=5) +├─ regular_polygon octagon (8) +├─ regular_star 4-point star (inner_radius=0.3, sharp) +├─ regular_star 5-point star (inner_radius=0.4, corner_radius=3) +├─ regular_star 6-point star (inner_radius=0.5) +└─ regular_star 8-point star (inner_radius=0.7, corner_radius=6) +``` + +**Exercises:** RegularPolygonNodeRec (point_count, corner_radius), RegularStarPolygonNodeRec (inner_radius, corner_radius). + +--- + +## L0-vector + +Vector network nodes: closed shapes, open paths, region fills, variable-width strokes. + +``` +scene "L0 Vector" +├─ vector closed bezier quad (4 curved segments, EvenOdd region, blue fill) +├─ vector open path (3 vertices, 2 segments, no region, markers) +├─ vector triangle with explicit region fill + dashed stroke (NonZero) +└─ vector open path with variable-width stroke profile (3 stops) +``` + +**Exercises:** VectorNetwork (segments, loops, regions), FillRule (EvenOdd, NonZero), region fills, VarWidthProfile, markers on vectors. + +--- + +## L0-paints + +One rectangle per paint type. Each rectangle uses exactly one fill to isolate the paint variant. + +``` +scene "L0 Paints" +├─ rectangle solid (red, full opacity) +├─ rectangle linear gradient (red→blue, left→right) +├─ rectangle radial gradient (yellow center→green edge, 80% opacity) +├─ rectangle sweep gradient (cyan→magenta→yellow, 3 stops) +└─ rectangle image paint (HASH ref, BoxFit::Cover) +``` + +**Exercises:** Solid, LinearGradient, RadialGradient, SweepGradient, ImagePaint. + +--- + +## L0-paints-stack + +A single rectangle with multiple fills stacked bottom-to-top, demonstrating paint compositing. + +``` +scene "L0 Paints Stack" +└─ rectangle 200×200 + fill[0]: solid white (base) + fill[1]: linear gradient (red→transparent, left→right) + fill[2]: radial gradient (yellow center→transparent edge) + fill[3]: sweep gradient (cyan→magenta→transparent) + fill[4]: image paint (HASH ref, 50% opacity) +``` + +**Exercises:** multiple fills on one node, paint opacity, compositing order. + +--- + +## L0-strokes + +Stroke options on basic shapes: alignment, cap, join, dash array, markers. + +``` +scene "L0 Strokes" +├─ rectangle stroke_align=Center, cap=Butt, join=Miter, 2px black +├─ rectangle stroke_align=Inside, cap=Round, join=Round, 3px blue +├─ rectangle stroke_align=Outside, cap=Square, join=Bevel, 4px green +├─ rectangle dashed stroke [10, 5], cap=Round, 2px black +├─ line marker_start=Circle, marker_end=EquilateralTriangle, 2px +└─ line marker_start=Diamond, marker_end=VerticalBar, 2px +``` + +**Exercises:** StrokeAlign (3), StrokeCap (3), StrokeJoin (3), StrokeDashArray, StrokeMarkerPreset (5 of 7). + +--- + +## L0-strokes-rect + +Container-specific rectangular (per-side) stroke widths and per-side corner radii. + +``` +scene "L0 Strokes Rect" +├─ container 300×200 rectangular stroke (top=1, right=2, bottom=3, left=4), black +└─ container 300×200 uniform stroke 2px + per-side corner radii (tl=0, tr=8, bl=16, br=24) +``` + +**Exercises:** StrokeWidth::Rectangular, per-side RectangularCornerRadius with varying radii. + +--- + +## L0-image + +Image paint-specific properties: fit modes, resource references, quarter turns, alignment. + +``` +scene "L0 Image" +├─ rectangle ImagePaint, HASH ref, BoxFit::Cover, alignment=CENTER +├─ rectangle ImagePaint, RID ref, BoxFit::Contain, alignment=CENTER +├─ rectangle ImagePaint, Transform fit (scale=0.5, translate 10,20) +└─ rectangle ImagePaint, quarter_turns=2, alignment=BOTTOM_RIGHT, Screen blend +``` + +**Exercises:** ResourceRef (HASH, RID), ImagePaintFit (Fit/Cover, Fit/Contain, Transform), quarter_turns, Alignment, paint-level blend_mode. + +--- + +## L0-image-filters + +Image paint with non-default ImageFilters: exposure, contrast, saturation, temperature, tint, highlights, shadows. + +``` +scene "L0 Image Filters" +├─ rectangle bright + warm (exposure=0.5, temperature=0.4) +├─ rectangle high contrast + desaturated (contrast=0.25, saturation=-0.8) +├─ rectangle cool tint + shadow lift (temperature=-0.6, tint=-0.3, shadows=0.5) +└─ rectangle all 7 filter channels non-zero +``` + +**Exercises:** ImageFilters (exposure, contrast, saturation, temperature, tint, highlights, shadows). + +--- + +## L0-effects + +Each effect type on its own rectangle, one per node. Solid grey fill so the effect is visible. + +``` +scene "L0 Effects" +├─ rectangle blur: gaussian, radius=5 +├─ rectangle backdrop_blur: gaussian, radius=8 +├─ rectangle drop_shadow: dx=2, dy=2, blur=4, spread=0, black +├─ rectangle inner_shadow: dx=1, dy=1, blur=3, spread=0, grey +└─ rectangle noise: mono coloring, intensity=0.3 +``` + +**Exercises:** FeLayerBlur, FeBackdropBlur, FeShadow (DropShadow), FeShadow (InnerShadow), FeNoiseEffect. + +--- + +## L0-effects-glass + +Liquid glass effect centered over alternating black/white stripes with even +padding (matching the golden reference pattern). The 380×380 stripe background +is drawn first; the 300×300 glass panel sits centered with 40 px padding on +every side. + +``` +scene "L0 Effects Glass" +├─ rectangle 380×380 fill: white (background) +├─ 10× rectangle 20×380 fill: black (even stripes over white bg) +└─ rectangle 300×300 corner_radius=60 glass: refraction=1.0, depth=100, + dispersion=1.0, blur_radius=0, light_intensity=0.7 +``` + +**Exercises:** FeLiquidGlass with maximum refraction and chromatic aberration +over a stripe pattern (same approach as `golden_liquid_glass.rs`). + +--- + +## L0-type + +Core typography: font sizes, weights, alignment, decoration, spacing, transforms, stroke, effects. + +``` +scene "L0 Type" +├─ text_span "Regular 16px" Inter 16px, weight=400, Left/Top +├─ text_span "Bold 24px" Inter 24px, weight=700, Center/Center +├─ text_span "Right Aligned" Inter 16px, Right/Bottom +├─ text_span "Justified Text..." Inter 14px, Justify/Top, width=200 +├─ text_span "With Stroke" Inter 20px, red stroke 1px Outside +├─ text_span "Drop Shadow" Inter 20px, drop shadow +├─ text_span "Max 2 Lines..." Inter 14px, max_lines=2, ellipsis="…" +├─ text_span "Tracked + Tall..." Inter 16px, letter_spacing=4, line_height=2× +├─ text_span "Underlined Text" Inter 18px, underline red 2px, skip_ink +└─ text_span "uppercase transform" Inter 16px, TextTransform::Uppercase +``` + +**Exercises:** TextAlign (Left, Center, Right, Justify), TextAlignVertical (Top, Center, Bottom), text stroke + stroke_align, drop shadow, max_lines, ellipsis, letter_spacing, line_height, TextDecorationRec (underline, color, style, skip_ink, thickness), TextTransform. + +--- + +## L0-type-fvar + +Variable font axes: weight, width, optical sizing, custom fvar axes, italic. + +``` +scene "L0 Type fvar" +├─ text_span "Thin 100" Inter weight=100 +├─ text_span "Light 300" Inter weight=300 +├─ text_span "Regular 400" Inter weight=400 +├─ text_span "Bold 700" Inter weight=700 +├─ text_span "Black 900" Inter weight=900 +├─ text_span "Condensed (width=75)" Inter font_width=75 +├─ text_span "Optical Size Fixed(48)" Inter font_optical_sizing=Fixed(48) +├─ text_span "Custom Axes..." Roboto Flex, wght=600 wdth=80 GRAD=50 +└─ text_span "Italic Style" Inter italic=true +``` + +**Exercises:** FontWeight (100–900), font_width, FontOpticalSizing (Auto, Fixed), FontVariation (custom axes), font_style_italic. + +--- + +## L0-type-features + +OpenType font features: ligatures, small caps, stylistic sets, tabular numbers, kerning. + +``` +scene "L0 Type Features" +├─ text_span "ffi ffl — liga off" Inter, liga=false +├─ text_span "ffi ffl — liga on" Inter, liga=true +├─ text_span "Small Caps Text — smcp" Inter, smcp=true +├─ text_span "Stylistic Set 01 — ss01" Inter, ss01=true +├─ text_span "0123456789 — tnum + zero" Inter, tnum=true zero=true +└─ text_span "AVAW Typography — kern off" Inter, font_kerning=false +``` + +**Exercises:** FontFeature (liga, smcp, ss01, tnum, zero), font_kerning toggle. + +--- + +## L0-masks + +Mask composition with each mask type. Each mask + its target are inside a dedicated group to avoid masking unrelated siblings. + +``` +scene "L0 Masks" +├─ group (image mask demo) +│ ├─ rectangle 100×100 red solid (content) +│ └─ rectangle 100×100 white solid (mask: Image/Alpha) +└─ group (geometry mask demo) + ├─ rectangle 100×100 blue solid (content) + └─ ellipse 80×80 black solid (mask: Geometry) +``` + +**Exercises:** LayerMaskType::Image(Alpha), LayerMaskType::Geometry, mask scoping via group. + +--- + +## L0-boolean-operation + +All four boolean path operations, each with two overlapping child rectangles. + +``` +scene "L0 Boolean Operation" +├─ boolean_op Union (red, 2 overlapping rects) +├─ boolean_op Intersection (blue, 2 overlapping rects) +├─ boolean_op Difference (green, 2 overlapping rects) +└─ boolean_op Xor (yellow, 2 overlapping rects) +``` + +**Exercises:** BooleanPathOperation (Union, Intersection, Difference, Xor), BooleanPathOperationNodeRec with child shapes. + +--- + +## L0-layout-position + +Positioning modes for shapes within a container. No flex — uses Normal layout mode. + +``` +scene "L0 Layout Position" +└─ container 400×300 Normal layout, light grey fill + ├─ rectangle 80×60 Cartesian position (x=20, y=20) + ├─ rectangle 80×60 Inset position (top=50, left=150) + └─ rectangle 80×60 Absolute positioning (layout_child, inset top=150, left=20) +``` + +**Exercises:** LayoutPositioningBasis::Cartesian, LayoutPositioningBasis::Inset, LayoutPositioning::Absolute. + +--- + +## L0-layout-flex + +Flex layout demonstration with two containers showing different configurations. + +``` +scene "L0 Layout Flex" +├─ container 500×80 horizontal flex, gap=10, main=SpaceBetween, cross=Center +│ ├─ rectangle 100×50 (auto) +│ ├─ rectangle 100×50 (auto) +│ └─ rectangle fill×50 (flex_grow=1) +└─ container 200×300 vertical flex, main=Center, cross=Stretch, padding=16, gap=8 + ├─ rectangle auto×40 + └─ rectangle auto×40 +``` + +**Exercises:** LayoutMode::Flex, Axis (Horizontal, Vertical), MainAxisAlignment::SpaceBetween, MainAxisAlignment::Center, CrossAxisAlignment::Center, CrossAxisAlignment::Stretch, layout_grow, LayoutGap, padding. + +--- + +## L0-layout-transform + +Rotation and affine transforms on containers and shapes. + +``` +scene "L0 Layout Transform" +├─ container 200×150 rotated 90°, Cartesian at (300, 50), blue fill +├─ rectangle 120×80 rotated 45°, at (50, 50), red fill +└─ line 200px rotated 45°, at (400, 200), black stroke 2px +``` + +**Exercises:** Container rotation, shape rotation via AffineTransform, line rotation. + +--- + +## Not included (rationale) + +| Topic | Reason | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Blend modes | All 16 + PassThrough are exhaustively covered by the `gen_all_blend_modes` in-memory test. A fixture file would be 17 identical rectangles — low visual value. | +| Deep nesting | Covered by `gen_deep_nesting` (4 levels). Nesting is structural, not visual. | +| Inactive / edge cases | Covered by `gen_inactive_node`. An invisible node in a visual fixture adds nothing. | + +--- + +## Regenerating + +```sh +# regenerate L0.grida (all scenes) +cargo test --package cg --test fbs_fixtures -- write_l0 +``` diff --git a/crates/grida-canvas/tests/fixtures/l0_boolean_operation.rs b/crates/grida-canvas/tests/fixtures/l0_boolean_operation.rs new file mode 100644 index 0000000000..09555fd66c --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_boolean_operation.rs @@ -0,0 +1,88 @@ +use super::*; +use cg::cg::stroke_width::SingularStrokeWidth; +use math2::transform::AffineTransform; +use std::collections::HashMap; + +/// All four boolean operations, each with two overlapping child rectangles. +pub fn build() -> Scene { + let ops = [ + (BooleanPathOperation::Union, solid(220, 59, 59, 255)), + (BooleanPathOperation::Intersection, solid(59, 100, 220, 255)), + (BooleanPathOperation::Difference, solid(59, 180, 75, 255)), + (BooleanPathOperation::Xor, solid(255, 200, 40, 255)), + ]; + + let gap = 160.0; + let mut nodes: Vec<(u64, Node)> = Vec::new(); + let mut links: HashMap> = HashMap::new(); + let mut roots: Vec = Vec::new(); + let mut next_id = 1u64; + + for (i, (op, fill)) in ops.iter().enumerate() { + let x = (i as f32) * gap; + let bool_id = next_id; + next_id += 1; + + let bool_node = Node::BooleanOperation(BooleanPathOperationNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: Some(AffineTransform::from_box_center(x, 0.0, 0.0, 0.0, 0.0)), + op: *op, + corner_radius: None, + fills: Paints::new(vec![fill.clone()]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(Some(1.0)), + }); + + // Two overlapping rectangles as children + let child_a_id = next_id; + next_id += 1; + let child_a = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 80.0, 80.0, 0.0), + size: Size { width: 80.0, height: 80.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let child_b_id = next_id; + next_id += 1; + let child_b = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(40.0, 40.0, 80.0, 80.0, 0.0), + size: Size { width: 80.0, height: 80.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + nodes.push((bool_id, bool_node)); + nodes.push((child_a_id, child_a)); + nodes.push((child_b_id, child_b)); + links.insert(bool_id, vec![child_a_id, child_b_id]); + roots.push(bool_id); + } + + build_scene("L0 Boolean Operation", None, nodes, links, roots) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_container.rs b/crates/grida-canvas/tests/fixtures/l0_container.rs new file mode 100644 index 0000000000..7836a533e5 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_container.rs @@ -0,0 +1,190 @@ +use super::*; +use std::collections::HashMap; + +/// Container-specific features: clip on/off, nesting, InitialContainer. +pub fn build() -> Scene { + // ── [1] Outer container (clip=true) ───────────────────────────────── + // Child rectangle intentionally overflows to demonstrate clipping. + let clip_on = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(200.0), + layout_target_height: Some(150.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: { + use cg::cg::types::Radius; + let r = Radius::circular(12.0); + RectangularCornerRadius { tl: r, tr: r, bl: r, br: r } + }, + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(235, 240, 255, 255)]), + strokes: Paints::new(vec![solid(100, 120, 200, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: true, + }); + + // Overflowing child — should be clipped by parent + let overflow_rect = rect(60.0, 80.0, 200.0, 120.0, solid(220, 59, 59, 200)); + + // ── [3] Same container but clip=false ─────────────────────────────── + let clip_off = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 0.0, right: 0.0, bottom: 0.0, left: 240.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(200.0), + layout_target_height: Some(150.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: { + use cg::cg::types::Radius; + let r = Radius::circular(12.0); + RectangularCornerRadius { tl: r, tr: r, bl: r, br: r } + }, + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(235, 255, 240, 255)]), + strokes: Paints::new(vec![solid(100, 200, 120, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: false, + }); + + // Same overflowing child — should be visible beyond parent bounds + let overflow_rect2 = rect(60.0, 80.0, 200.0, 120.0, solid(59, 180, 75, 200)); + + // ── [5] Nested containers (3 levels deep) ────────────────────────── + let outer = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 180.0, right: 0.0, bottom: 0.0, left: 0.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(400.0), + layout_target_height: Some(250.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(245, 245, 245, 255)]), + strokes: Paints::new(vec![solid(180, 180, 180, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: false, + }); + + let middle = Node::Container(ContainerNodeRec { + active: true, + opacity: 0.9, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 20.0, right: 0.0, bottom: 0.0, left: 20.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(300.0), + layout_target_height: Some(180.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: { + use cg::cg::types::Radius; + let r = Radius::circular(8.0); + RectangularCornerRadius { tl: r, tr: r, bl: r, br: r } + }, + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(220, 230, 255, 255)]), + strokes: Paints::new(vec![solid(150, 170, 220, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: true, + }); + + let inner = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 15.0, right: 0.0, bottom: 0.0, left: 15.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(200.0), + layout_target_height: Some(120.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(255, 240, 230, 255)]), + strokes: Paints::new(vec![solid(220, 180, 150, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: false, + }); + + // Leaf inside the innermost container + let leaf = rect(10.0, 10.0, 80.0, 60.0, solid(128, 60, 200, 255)); + + // ── Tree ──────────────────────────────────────────────────────────── + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64]); // clip_on → overflow_rect + links.insert(3u64, vec![4u64]); // clip_off → overflow_rect2 + links.insert(5u64, vec![6u64]); // outer → middle + links.insert(6u64, vec![7u64]); // middle → inner + links.insert(7u64, vec![8u64]); // inner → leaf + + build_scene( + "L0 Container", + None, + vec![ + (1, clip_on), (2, overflow_rect), + (3, clip_off), (4, overflow_rect2), + (5, outer), (6, middle), (7, inner), (8, leaf), + ], + links, + vec![1, 3, 5], + ) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_effects.rs b/crates/grida-canvas/tests/fixtures/l0_effects.rs new file mode 100644 index 0000000000..0ca9c3c534 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_effects.rs @@ -0,0 +1,77 @@ +use super::*; +use cg::cg::color::CGColor; +use cg::cg::fe::*; + +fn effect_rect(x: f32, effects: LayerEffects) -> Node { + Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(x, 0.0, 150.0, 150.0, 0.0), + size: Size { width: 150.0, height: 150.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(180, 180, 180, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects, + layout_child: None, + }) +} + +pub fn build() -> Scene { + let gap = 170.0; + + let blur = effect_rect(0.0, LayerEffects { + blur: Some(FeLayerBlur { + active: true, + blur: FeBlur::Gaussian(FeGaussianBlur { radius: 5.0 }), + }), + ..LayerEffects::default() + }); + + let backdrop = effect_rect(gap, LayerEffects { + backdrop_blur: Some(FeBackdropBlur { + active: true, + blur: FeBlur::Gaussian(FeGaussianBlur { radius: 8.0 }), + }), + ..LayerEffects::default() + }); + + let drop_shadow = effect_rect(gap * 2.0, LayerEffects { + shadows: vec![FilterShadowEffect::DropShadow(FeShadow { + dx: 2.0, dy: 2.0, blur: 4.0, spread: 0.0, + color: CGColor { r: 0, g: 0, b: 0, a: 255 }, + active: true, + })], + ..LayerEffects::default() + }); + + let inner_shadow = effect_rect(gap * 3.0, LayerEffects { + shadows: vec![FilterShadowEffect::InnerShadow(FeShadow { + dx: 1.0, dy: 1.0, blur: 3.0, spread: 0.0, + color: CGColor { r: 128, g: 128, b: 128, a: 200 }, + active: true, + })], + ..LayerEffects::default() + }); + + let noise = effect_rect(gap * 4.0, LayerEffects { + noises: vec![FeNoiseEffect { + active: true, + noise_size: 1.0, + density: 0.3, + num_octaves: 4, + seed: 42.0, + coloring: NoiseEffectColors::Mono { + color: CGColor { r: 0, g: 0, b: 0, a: 64 }, + }, + blend_mode: BlendMode::Normal, + }], + ..LayerEffects::default() + }); + + flat_scene("L0 Effects", vec![blur, backdrop, drop_shadow, inner_shadow, noise]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_effects_glass.rs b/crates/grida-canvas/tests/fixtures/l0_effects_glass.rs new file mode 100644 index 0000000000..5a82b05e3a --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_effects_glass.rs @@ -0,0 +1,65 @@ +use super::*; +use cg::cg::fe::*; + +pub fn build() -> Scene { + // Note: rect(x, y, w, h, ..) uses from_box_center which, at 0° rotation, + // places the rect's top-left at (x, y). + + let glass_size = 300.0; + let padding = 40.0; + let total = glass_size + padding * 2.0; // 380 + + // ── Background ───────────────────────────────────────────────────── + // White base + black stripes → alternating black/white pattern. + // Top-left at (0, 0), spans [0 .. total] in both axes. + let bg = rect(0.0, 0.0, total, total, solid(255, 255, 255, 255)); + + let stripe_w = 10.0; + let n_stripes = (total / stripe_w).ceil() as i32; + let mut nodes: Vec = vec![bg]; + for i in 0..n_stripes { + if i % 2 != 0 { + continue; // white gap covered by bg + } + let x = i as f32 * stripe_w; // top-left x within [0 .. total] + nodes.push(rect(x, 0.0, stripe_w, total, solid(0, 0, 0, 255))); + } + + // ── Glass panel ──────────────────────────────────────────────────── + // Inset by `padding` on every side → top-left at (padding, padding), + // centered within the stripe background. + // Parameters match the golden reference (golden_liquid_glass.rs). + let glass_rect = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center( + padding, padding, glass_size, glass_size, 0.0, + ), + size: Size { width: glass_size, height: glass_size }, + corner_radius: RectangularCornerRadius::circular(60.0), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects { + glass: Some(FeLiquidGlass { + active: true, + light_intensity: 0.7, + light_angle: 45.0, + refraction: 1.0, + depth: 100.0, + blur_radius: 0.0, + dispersion: 1.0, + }), + ..LayerEffects::default() + }, + layout_child: None, + }); + + nodes.push(glass_rect); + + flat_scene("L0 Effects Glass", nodes) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_group.rs b/crates/grida-canvas/tests/fixtures/l0_group.rs new file mode 100644 index 0000000000..185150bc9c --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_group.rs @@ -0,0 +1,83 @@ +use super::*; +use math2::transform::AffineTransform; +use std::collections::HashMap; + +/// Group node features: grouping children, opacity inheritance, blend modes, nesting. +pub fn build() -> Scene { + // ── [1] Simple group with two children ────────────────────────────── + let g1 = Node::Group(GroupNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: Some(AffineTransform::from_box_center(0.0, 0.0, 0.0, 0.0, 0.0)), + }); + let g1_a = rect(0.0, 0.0, 80.0, 80.0, solid(220, 59, 59, 255)); + let g1_b = rect(40.0, 40.0, 80.0, 80.0, solid(59, 100, 220, 200)); + + // ── [4] Group with reduced opacity (children inherit) ─────────────── + let g2 = Node::Group(GroupNodeRec { + active: true, + opacity: 0.4, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: Some(AffineTransform::from_box_center(200.0, 0.0, 0.0, 0.0, 0.0)), + }); + let g2_a = rect(0.0, 0.0, 80.0, 80.0, solid(220, 59, 59, 255)); + let g2_b = rect(40.0, 40.0, 80.0, 80.0, solid(59, 100, 220, 255)); + + // ── [7] Group with blend mode ─────────────────────────────────────── + let g3 = Node::Group(GroupNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::Blend(BlendMode::Multiply), + mask: None, + transform: Some(AffineTransform::from_box_center(400.0, 0.0, 0.0, 0.0, 0.0)), + }); + let g3_a = rect(0.0, 0.0, 100.0, 100.0, solid(255, 200, 40, 255)); + let g3_b = rect(30.0, 30.0, 100.0, 100.0, solid(59, 100, 220, 255)); + + // ── [10] Nested groups ────────────────────────────────────────────── + let g_outer = Node::Group(GroupNodeRec { + active: true, + opacity: 0.8, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: Some(AffineTransform::from_box_center(0.0, 160.0, 0.0, 0.0, 0.0)), + }); + + let g_inner = Node::Group(GroupNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::Blend(BlendMode::Screen), + mask: None, + transform: Some(AffineTransform::from_box_center(20.0, 20.0, 0.0, 0.0, 0.0)), + }); + + let leaf1 = rect(0.0, 0.0, 60.0, 60.0, solid(59, 180, 75, 255)); + let leaf2 = rect(30.0, 30.0, 60.0, 60.0, solid(128, 60, 200, 255)); + + // Sibling of g_inner inside g_outer + let sibling = ellipse(150.0, 20.0, 80.0, 80.0, solid(220, 120, 60, 255)); + + // ── Tree ──────────────────────────────────────────────────────────── + let mut links = HashMap::new(); + links.insert(1u64, vec![2, 3]); // g1 → children + links.insert(4u64, vec![5, 6]); // g2 → children + links.insert(7u64, vec![8, 9]); // g3 → children + links.insert(10u64, vec![11, 14]); // g_outer → g_inner + sibling + links.insert(11u64, vec![12, 13]); // g_inner → leaves + + build_scene( + "L0 Group", + None, + vec![ + (1, g1), (2, g1_a), (3, g1_b), + (4, g2), (5, g2_a), (6, g2_b), + (7, g3), (8, g3_a), (9, g3_b), + (10, g_outer), (11, g_inner), (12, leaf1), (13, leaf2), (14, sibling), + ], + links, + vec![1, 4, 7, 10], + ) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_image.rs b/crates/grida-canvas/tests/fixtures/l0_image.rs new file mode 100644 index 0000000000..439cf6848f --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_image.rs @@ -0,0 +1,37 @@ +use super::*; +use cg::cg::alignment::Alignment; +use math2::box_fit::BoxFit; +use math2::transform::AffineTransform; + +pub fn build() -> Scene { + let s = 150.0; + let gap = 170.0; + let img = || ResourceRef::HASH(SYSTEM_IMAGE.to_owned()); + + // Cover fit + let r1 = rect(0.0, 0.0, s, s, + image_paint_with(img(), ImagePaintFit::Fit(BoxFit::Cover))); + + // Contain fit + let r2 = rect(gap, 0.0, s, s, + image_paint_with(img(), ImagePaintFit::Fit(BoxFit::Contain))); + + // Transform fit (scale + offset) + let r3 = rect(gap * 2.0, 0.0, s, s, + image_paint_with(img(), ImagePaintFit::Transform(AffineTransform::new(10.0, 20.0, 0.0)))); + + // Quarter turns + alignment + Screen blend + let r4 = rect(gap * 3.0, 0.0, s, s, + Paint::Image(ImagePaint { + active: true, + image: img(), + fit: ImagePaintFit::Fit(BoxFit::Cover), + filters: ImageFilters::default(), + opacity: 1.0, + blend_mode: BlendMode::Screen, + quarter_turns: 2, + alignement: Alignment::BOTTOM_RIGHT, + })); + + flat_scene("L0 Image", vec![r1, r2, r3, r4]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_image_filters.rs b/crates/grida-canvas/tests/fixtures/l0_image_filters.rs new file mode 100644 index 0000000000..5912556953 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_image_filters.rs @@ -0,0 +1,80 @@ +use super::*; +use cg::cg::alignment::Alignment; +use math2::box_fit::BoxFit; + +/// Image paint with non-default filter values: exposure, contrast, saturation, etc. +pub fn build() -> Scene { + let s = 150.0; + let gap = 170.0; + + // Bright + warm + let bright_warm = rect(0.0, 0.0, s, s, Paint::Image(ImagePaint { + active: true, + image: ResourceRef::HASH(SYSTEM_IMAGE.to_owned()), + fit: ImagePaintFit::Fit(BoxFit::Cover), + filters: ImageFilters { + exposure: 0.5, + temperature: 0.4, + ..ImageFilters::default() + }, + opacity: 1.0, + blend_mode: BlendMode::Normal, + quarter_turns: 0, + alignement: Alignment::CENTER, + })); + + // High contrast + desaturated + let contrast_desat = rect(gap, 0.0, s, s, Paint::Image(ImagePaint { + active: true, + image: ResourceRef::HASH(SYSTEM_IMAGE.to_owned()), + fit: ImagePaintFit::Fit(BoxFit::Cover), + filters: ImageFilters { + contrast: 0.25, + saturation: -0.8, + ..ImageFilters::default() + }, + opacity: 1.0, + blend_mode: BlendMode::Normal, + quarter_turns: 0, + alignement: Alignment::CENTER, + })); + + // Cool tint + shadow lift + let cool_shadow = rect(gap * 2.0, 0.0, s, s, Paint::Image(ImagePaint { + active: true, + image: ResourceRef::HASH(SYSTEM_IMAGE.to_owned()), + fit: ImagePaintFit::Fit(BoxFit::Cover), + filters: ImageFilters { + temperature: -0.6, + tint: -0.3, + shadows: 0.5, + ..ImageFilters::default() + }, + opacity: 1.0, + blend_mode: BlendMode::Normal, + quarter_turns: 0, + alignement: Alignment::CENTER, + })); + + // All filters non-zero + let all_filters = rect(gap * 3.0, 0.0, s, s, Paint::Image(ImagePaint { + active: true, + image: ResourceRef::HASH(SYSTEM_IMAGE.to_owned()), + fit: ImagePaintFit::Fit(BoxFit::Cover), + filters: ImageFilters { + exposure: 0.2, + contrast: 0.1, + saturation: 0.3, + temperature: -0.2, + tint: 0.15, + highlights: -0.4, + shadows: 0.3, + }, + opacity: 1.0, + blend_mode: BlendMode::Normal, + quarter_turns: 0, + alignement: Alignment::CENTER, + })); + + flat_scene("L0 Image Filters", vec![bright_warm, contrast_desat, cool_shadow, all_filters]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_layout_flex.rs b/crates/grida-canvas/tests/fixtures/l0_layout_flex.rs new file mode 100644 index 0000000000..185d531916 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_layout_flex.rs @@ -0,0 +1,121 @@ +use super::*; +use std::collections::HashMap; + +pub fn build() -> Scene { + // Horizontal flex container: SpaceBetween, Center + let h_container = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, + }), + layout_container: LayoutContainerStyle { + layout_mode: LayoutMode::Flex, + layout_direction: Axis::Horizontal, + layout_wrap: Some(LayoutWrap::NoWrap), + layout_main_axis_alignment: Some(MainAxisAlignment::SpaceBetween), + layout_cross_axis_alignment: Some(CrossAxisAlignment::Center), + layout_padding: None, + layout_gap: Some(LayoutGap { main_axis_gap: 10.0, cross_axis_gap: 0.0 }), + }, + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(500.0), + layout_target_height: Some(80.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(240, 240, 245, 255)]), + strokes: Paints::new(vec![solid(200, 200, 210, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: false, + }); + + let h1 = rect(0.0, 0.0, 100.0, 50.0, solid(220, 59, 59, 255)); + let h2 = rect(0.0, 0.0, 100.0, 50.0, solid(59, 100, 220, 255)); + // flex_grow=1 + let h3 = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 50.0, 0.0), + size: Size { width: 100.0, height: 50.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(59, 180, 75, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: Some(LayoutChildStyle { + layout_grow: 1.0, + layout_positioning: LayoutPositioning::Auto, + }), + }); + + // Vertical flex container: Center, Stretch + let v_container = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 100.0, right: 0.0, bottom: 0.0, left: 0.0, + }), + layout_container: LayoutContainerStyle { + layout_mode: LayoutMode::Flex, + layout_direction: Axis::Vertical, + layout_wrap: Some(LayoutWrap::NoWrap), + layout_main_axis_alignment: Some(MainAxisAlignment::Center), + layout_cross_axis_alignment: Some(CrossAxisAlignment::Stretch), + layout_padding: Some(EdgeInsets { + top: 16.0, right: 16.0, bottom: 16.0, left: 16.0, + }), + layout_gap: Some(LayoutGap { main_axis_gap: 8.0, cross_axis_gap: 0.0 }), + }, + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(200.0), + layout_target_height: Some(200.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(245, 240, 240, 255)]), + strokes: Paints::new(vec![solid(210, 200, 200, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: false, + }); + + let v1 = rect(0.0, 0.0, 80.0, 40.0, solid(255, 200, 40, 255)); + let v2 = rect(0.0, 0.0, 80.0, 40.0, solid(128, 60, 200, 255)); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64, 3u64, 4u64]); + links.insert(5u64, vec![6u64, 7u64]); + + build_scene( + "L0 Layout Flex", + None, + vec![ + (1, h_container), (2, h1), (3, h2), (4, h3), + (5, v_container), (6, v1), (7, v2), + ], + links, + vec![1, 5], + ) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_layout_position.rs b/crates/grida-canvas/tests/fixtures/l0_layout_position.rs new file mode 100644 index 0000000000..121e2c10c0 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_layout_position.rs @@ -0,0 +1,70 @@ +use super::*; +use std::collections::HashMap; + +pub fn build() -> Scene { + let container = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, + }), + layout_container: LayoutContainerStyle::default(), // Normal mode + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(400.0), + layout_target_height: Some(300.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(245, 245, 245, 255)]), + strokes: Paints::new(vec![solid(200, 200, 200, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(1.0), + effects: LayerEffects::default(), + clip: false, + }); + + // Child at Cartesian (20, 20) + let c1 = rect(20.0, 20.0, 80.0, 60.0, solid(220, 59, 59, 255)); + + // Child at Inset (top=50, left=150) — same x,y via inset + let c2 = rect(150.0, 50.0, 80.0, 60.0, solid(59, 100, 220, 255)); + + // Child with layout_child Absolute positioning + let c3 = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(20.0, 150.0, 80.0, 60.0, 0.0), + size: Size { width: 80.0, height: 60.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(59, 180, 75, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: Some(LayoutChildStyle { + layout_grow: 0.0, + layout_positioning: LayoutPositioning::Absolute, + }), + }); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64, 3u64, 4u64]); + + build_scene( + "L0 Layout Position", + None, + vec![(1, container), (2, c1), (3, c2), (4, c3)], + links, + vec![1], + ) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_layout_transform.rs b/crates/grida-canvas/tests/fixtures/l0_layout_transform.rs new file mode 100644 index 0000000000..8aee256c0a --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_layout_transform.rs @@ -0,0 +1,38 @@ +use super::*; + +pub fn build() -> Scene { + // Container rotated 90°, Cartesian position + let rotated_container = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 90.0, + position: LayoutPositioningBasis::Cartesian(CGPoint::new(300.0, 50.0)), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(200.0), + layout_target_height: Some(150.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(59, 100, 220, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + clip: false, + }); + + // Rectangle rotated 45° + let rotated_rect = rect_rotated(50.0, 50.0, 120.0, 80.0, 45.0, solid(220, 59, 59, 255)); + + // Line rotated 45° + let rotated_line = line(400.0, 200.0, 200.0, 45.0, 2.0); + + flat_scene("L0 Layout Transform", vec![rotated_container, rotated_rect, rotated_line]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_masks.rs b/crates/grida-canvas/tests/fixtures/l0_masks.rs new file mode 100644 index 0000000000..b4d64aa926 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_masks.rs @@ -0,0 +1,75 @@ +use super::*; +use cg::cg::stroke_width::SingularStrokeWidth; +use math2::transform::AffineTransform; +use std::collections::HashMap; + +pub fn build() -> Scene { + // Group 1: image mask (alpha) + let group_img = Node::Group(GroupNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: Some(AffineTransform::from_box_center(0.0, 0.0, 0.0, 0.0, 0.0)), + }); + let content_img = rect(0.0, 0.0, 100.0, 100.0, solid(220, 59, 59, 255)); + let mask_img = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: Some(LayerMaskType::Image(ImageMaskType::Alpha)), + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + size: Size { width: 100.0, height: 100.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(255, 255, 255, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + // Group 2: geometry mask + let group_geo = Node::Group(GroupNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: Some(AffineTransform::from_box_center(150.0, 0.0, 0.0, 0.0, 0.0)), + }); + let content_geo = rect(0.0, 0.0, 100.0, 100.0, solid(59, 100, 220, 255)); + let mask_geo = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: Some(LayerMaskType::Geometry), + transform: AffineTransform::from_box_center(10.0, 10.0, 80.0, 80.0, 0.0), + size: Size { width: 80.0, height: 80.0 }, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + let mut links = HashMap::new(); + links.insert(1u64, vec![2u64, 3u64]); + links.insert(4u64, vec![5u64, 6u64]); + + build_scene( + "L0 Masks", + None, + vec![ + (1, group_img), (2, content_img), (3, mask_img), + (4, group_geo), (5, content_geo), (6, mask_geo), + ], + links, + vec![1, 4], + ) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_paints.rs b/crates/grida-canvas/tests/fixtures/l0_paints.rs new file mode 100644 index 0000000000..4c54a0d563 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_paints.rs @@ -0,0 +1,14 @@ +use super::*; + +pub fn build() -> Scene { + let s = 150.0; + let gap = 170.0; + + let solid_rect = rect(0.0, 0.0, s, s, solid(220, 59, 59, 255)); + let linear_rect = rect(gap, 0.0, s, s, linear_gradient()); + let radial_rect = rect(gap * 2.0, 0.0, s, s, radial_gradient()); + let sweep_rect = rect(gap * 3.0, 0.0, s, s, sweep_gradient()); + let image_rect = rect(gap * 4.0, 0.0, s, s, image_paint()); + + flat_scene("L0 Paints", vec![solid_rect, linear_rect, radial_rect, sweep_rect, image_rect]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_paints_stack.rs b/crates/grida-canvas/tests/fixtures/l0_paints_stack.rs new file mode 100644 index 0000000000..1929dea919 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_paints_stack.rs @@ -0,0 +1,70 @@ +use super::*; +use cg::cg::alignment::Alignment; +use cg::cg::color::CGColor; +use cg::cg::tilemode::TileMode; +use math2::box_fit::BoxFit; +use math2::transform::AffineTransform; + +pub fn build() -> Scene { + // Single rectangle with 5 stacked fills + let stacked = Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, 200.0, 200.0, 0.0), + size: Size { width: 200.0, height: 200.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![ + // [0] solid white base + solid(255, 255, 255, 255), + // [1] linear gradient red→transparent + Paint::LinearGradient(LinearGradientPaint { + active: true, + xy1: Alignment::CENTER_LEFT, + xy2: Alignment::CENTER_RIGHT, + tile_mode: TileMode::default(), + transform: AffineTransform::default(), + stops: vec![ + GradientStop { offset: 0.0, color: CGColor { r: 255, g: 0, b: 0, a: 255 } }, + GradientStop { offset: 1.0, color: CGColor { r: 255, g: 0, b: 0, a: 0 } }, + ], + opacity: 1.0, + blend_mode: BlendMode::Normal, + }), + // [2] radial gradient yellow center→transparent + Paint::RadialGradient(RadialGradientPaint { + active: true, + transform: AffineTransform::default(), + stops: vec![ + GradientStop { offset: 0.0, color: CGColor { r: 255, g: 255, b: 0, a: 255 } }, + GradientStop { offset: 1.0, color: CGColor { r: 255, g: 255, b: 0, a: 0 } }, + ], + opacity: 1.0, + blend_mode: BlendMode::Normal, + tile_mode: TileMode::default(), + }), + // [3] sweep gradient + sweep_gradient(), + // [4] image paint at 50% opacity + Paint::Image(ImagePaint { + active: true, + image: ResourceRef::HASH(SYSTEM_IMAGE.to_owned()), + fit: ImagePaintFit::Fit(BoxFit::Cover), + filters: ImageFilters::default(), + opacity: 0.5, + blend_mode: BlendMode::Normal, + quarter_turns: 0, + alignement: Alignment::CENTER, + }), + ]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }); + + flat_scene("L0 Paints Stack", vec![stacked]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_shape_arc.rs b/crates/grida-canvas/tests/fixtures/l0_shape_arc.rs new file mode 100644 index 0000000000..b978e50d19 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_shape_arc.rs @@ -0,0 +1,111 @@ +use super::*; +use cg::cg::stroke_width::SingularStrokeWidth; +use math2::transform::AffineTransform; + +/// Ellipse arc variants: full, semicircle, donut, pie wedge, rounded arc. +pub fn build() -> Scene { + let s = 120.0; + let gap = 140.0; + + // Full ellipse (defaults — no arc) + let full = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(0.0, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + fills: Paints::new(vec![solid(59, 100, 220, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + // Semicircle (180° sweep) + let semi = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(gap, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + fills: Paints::new(vec![solid(220, 59, 59, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 0.0, + angle: Some(180.0), + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + // Donut (inner_radius = 0.5) + let donut = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(gap * 2.0, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + fills: Paints::new(vec![solid(59, 180, 75, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: Some(0.5), + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + // Pie wedge (90° sweep, start at 45°) + let pie = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(gap * 3.0, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + fills: Paints::new(vec![solid(255, 200, 40, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 45.0, + angle: Some(90.0), + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }); + + // Rounded arc (270° sweep, inner_radius 0.6, corner_radius 8) + let rounded_arc = Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(gap * 4.0, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + fills: Paints::new(vec![solid(128, 60, 200, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(Some(2.0)), + inner_radius: Some(0.6), + start_angle: 0.0, + angle: Some(270.0), + corner_radius: Some(8.0), + effects: LayerEffects::default(), + layout_child: None, + }); + + flat_scene("L0 Shape Arc", vec![full, semi, donut, pie, rounded_arc]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_shape_polygon.rs b/crates/grida-canvas/tests/fixtures/l0_shape_polygon.rs new file mode 100644 index 0000000000..75f5fd30ed --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_shape_polygon.rs @@ -0,0 +1,162 @@ +use super::*; +use cg::cg::stroke_width::SingularStrokeWidth; +use math2::transform::AffineTransform; + +/// Regular polygons and star polygons with varying point counts and parameters. +pub fn build() -> Scene { + let s = 120.0; + let gap = 140.0; + + // Triangle (3 sides) + let tri = Node::RegularPolygon(RegularPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(0.0, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 3, + corner_radius: 0.0, + fills: Paints::new(vec![solid(220, 59, 59, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + // Pentagon (5 sides, rounded corners) + let pent = Node::RegularPolygon(RegularPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(gap, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 5, + corner_radius: 10.0, + fills: Paints::new(vec![solid(59, 100, 220, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(Some(1.5)), + layout_child: None, + }); + + // Hexagon (6 sides) + let hex = Node::RegularPolygon(RegularPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(gap * 2.0, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 6, + corner_radius: 5.0, + fills: Paints::new(vec![solid(59, 180, 75, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + // Octagon (8 sides) + let oct = Node::RegularPolygon(RegularPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(gap * 3.0, 0.0, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 8, + corner_radius: 0.0, + fills: Paints::new(vec![solid(255, 200, 40, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + // 4-point star (sharp) + let star4 = Node::RegularStarPolygon(RegularStarPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(0.0, gap, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 4, + inner_radius: 0.3, + corner_radius: 0.0, + fills: Paints::new(vec![solid(128, 60, 200, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + // 5-point star (classic) + let star5 = Node::RegularStarPolygon(RegularStarPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(gap, gap, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 5, + inner_radius: 0.4, + corner_radius: 3.0, + fills: Paints::new(vec![solid(255, 215, 0, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(Some(1.0)), + layout_child: None, + }); + + // 6-point star (Star of David shape) + let star6 = Node::RegularStarPolygon(RegularStarPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(gap * 2.0, gap, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 6, + inner_radius: 0.5, + corner_radius: 0.0, + fills: Paints::new(vec![solid(59, 180, 180, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + // 8-point star (rounded) + let star8 = Node::RegularStarPolygon(RegularStarPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(gap * 3.0, gap, s, s, 0.0), + size: Size { width: s, height: s }, + point_count: 8, + inner_radius: 0.7, + corner_radius: 6.0, + fills: Paints::new(vec![solid(220, 120, 60, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + flat_scene( + "L0 Shape Polygon", + vec![tri, pent, hex, oct, star4, star5, star6, star8], + ) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_shapes.rs b/crates/grida-canvas/tests/fixtures/l0_shapes.rs new file mode 100644 index 0000000000..444289ca52 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_shapes.rs @@ -0,0 +1,86 @@ +use super::*; +use cg::vectornetwork::*; +use math2::transform::AffineTransform; + +pub fn build() -> Scene { + let rectangle = rect(0.0, 0.0, 200.0, 100.0, solid(220, 59, 59, 255)); + + let ell = ellipse(220.0, 10.0, 100.0, 80.0, solid(59, 100, 220, 255)); + + let polygon = Node::RegularPolygon(RegularPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(340.0, 0.0, 100.0, 100.0, 0.0), + size: Size { width: 100.0, height: 100.0 }, + point_count: 6, + corner_radius: 5.0, + fills: Paints::new(vec![solid(59, 180, 75, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + let star = Node::RegularStarPolygon(RegularStarPolygonNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(460.0, 0.0, 120.0, 120.0, 0.0), + size: Size { width: 120.0, height: 120.0 }, + point_count: 5, + inner_radius: 0.4, + corner_radius: 3.0, + fills: Paints::new(vec![solid(255, 200, 40, 255)]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + layout_child: None, + }); + + let ln = line(0.0, 140.0, 200.0, 0.0, 2.0); + + let txt = text(220.0, 130.0, "Hello, Grida!", 20.0, 400); + + let vector = Node::Vector(VectorNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(460.0, 130.0, 100.0, 100.0, 0.0), + network: VectorNetwork { + vertices: vec![(0.0, 0.0), (100.0, 0.0), (100.0, 100.0), (0.0, 100.0)], + segments: vec![ + VectorNetworkSegment { a: 0, b: 1, ta: (30.0, 0.0), tb: (-30.0, 0.0) }, + VectorNetworkSegment { a: 1, b: 2, ta: (0.0, 30.0), tb: (0.0, -30.0) }, + VectorNetworkSegment { a: 2, b: 3, ta: (-30.0, 0.0), tb: (30.0, 0.0) }, + VectorNetworkSegment { a: 3, b: 0, ta: (0.0, -30.0), tb: (0.0, 30.0) }, + ], + regions: vec![VectorNetworkRegion { + loops: vec![VectorNetworkLoop(vec![0, 1, 2, 3])], + fill_rule: FillRule::EvenOdd, + fills: None, + }], + }, + corner_radius: 0.0, + fills: Paints::new(vec![solid(128, 60, 200, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width: 1.0, + stroke_width_profile: None, + stroke_align: StrokeAlign::Center, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + layout_child: None, + }); + + flat_scene("L0 Shapes", vec![rectangle, ell, polygon, star, ln, txt, vector]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_strokes.rs b/crates/grida-canvas/tests/fixtures/l0_strokes.rs new file mode 100644 index 0000000000..0e4853ece9 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_strokes.rs @@ -0,0 +1,81 @@ +use super::*; +use cg::cg::stroke_dasharray::StrokeDashArray; +use math2::transform::AffineTransform; + +fn stroked_rect( + x: f32, y: f32, + align: StrokeAlign, cap: StrokeCap, join: StrokeJoin, + width: f32, color: Paint, dash: Option, +) -> Node { + Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(x, y, 120.0, 80.0, 0.0), + size: Size { width: 120.0, height: 80.0 }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(240, 240, 240, 255)]), + strokes: Paints::new(vec![color]), + stroke_style: StrokeStyle { + stroke_align: align, + stroke_cap: cap, + stroke_join: join, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: dash, + }, + stroke_width: StrokeWidth::Uniform(width), + effects: LayerEffects::default(), + layout_child: None, + }) +} + +fn marker_line( + x: f32, y: f32, + start: StrokeMarkerPreset, end: StrokeMarkerPreset, +) -> Node { + Node::Line(LineNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::new(x, y, 0.0), + size: Size { width: 150.0, height: 0.0 }, + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width: 2.0, + stroke_cap: StrokeCap::Round, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + _data_stroke_align: StrokeAlign::Center, + marker_start_shape: start, + marker_end_shape: end, + layout_child: None, + }) +} + +pub fn build() -> Scene { + let gap = 140.0; + + let r1 = stroked_rect(0.0, 0.0, + StrokeAlign::Center, StrokeCap::Butt, StrokeJoin::Miter, + 2.0, solid(0, 0, 0, 255), None); + let r2 = stroked_rect(gap, 0.0, + StrokeAlign::Inside, StrokeCap::Round, StrokeJoin::Round, + 3.0, solid(59, 100, 220, 255), None); + let r3 = stroked_rect(gap * 2.0, 0.0, + StrokeAlign::Outside, StrokeCap::Square, StrokeJoin::Bevel, + 4.0, solid(59, 180, 75, 255), None); + let r4 = stroked_rect(gap * 3.0, 0.0, + StrokeAlign::Center, StrokeCap::Round, StrokeJoin::Miter, + 2.0, solid(0, 0, 0, 255), + Some(StrokeDashArray(vec![10.0, 5.0]))); + + let l1 = marker_line(0.0, 120.0, + StrokeMarkerPreset::Circle, StrokeMarkerPreset::EquilateralTriangle); + let l2 = marker_line(200.0, 120.0, + StrokeMarkerPreset::Diamond, StrokeMarkerPreset::VerticalBar); + + flat_scene("L0 Strokes", vec![r1, r2, r3, r4, l1, l2]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_strokes_rect.rs b/crates/grida-canvas/tests/fixtures/l0_strokes_rect.rs new file mode 100644 index 0000000000..4af15eef9b --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_strokes_rect.rs @@ -0,0 +1,157 @@ +use super::*; +use cg::cg::stroke_width::RectangularStrokeWidth; + +pub fn build() -> Scene { + // Container with per-side stroke widths + let c1 = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(300.0), + layout_target_height: Some(200.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(250, 250, 250, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Rectangular(RectangularStrokeWidth { + stroke_top_width: 1.0, stroke_right_width: 2.0, stroke_bottom_width: 3.0, stroke_left_width: 4.0, + }), + effects: LayerEffects::default(), + clip: false, + }); + + // Container with uniform stroke + per-side corner radii + let c2 = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 0.0, right: 0.0, bottom: 0.0, left: 320.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(300.0), + layout_target_height: Some(200.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: { + use cg::cg::types::Radius; + RectangularCornerRadius { + tl: Radius::circular(0.0), + tr: Radius::circular(8.0), + bl: Radius::circular(16.0), + br: Radius::circular(24.0), + } + }, + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(250, 250, 250, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::Uniform(2.0), + effects: LayerEffects::default(), + clip: false, + }); + + // Container with varying per-side stroke widths + dashed pattern + let c3 = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 220.0, right: 0.0, bottom: 0.0, left: 0.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(300.0), + layout_target_height: Some(200.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(250, 250, 250, 255)]), + strokes: Paints::new(vec![solid(220, 59, 59, 255)]), + stroke_style: StrokeStyle { + stroke_align: StrokeAlign::Inside, + stroke_cap: StrokeCap::Round, + stroke_join: StrokeJoin::Round, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: Some(cg::cg::stroke_dasharray::StrokeDashArray(vec![12.0, 6.0, 4.0, 6.0])), + }, + stroke_width: StrokeWidth::Rectangular(RectangularStrokeWidth { + stroke_top_width: 2.0, stroke_right_width: 4.0, stroke_bottom_width: 6.0, stroke_left_width: 8.0, + }), + effects: LayerEffects::default(), + clip: false, + }); + + // Container with per-side corners + per-side stroke widths + dashed pattern + let c4 = Node::Container(ContainerNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + rotation: 0.0, + position: LayoutPositioningBasis::Inset(EdgeInsets { + top: 220.0, right: 0.0, bottom: 0.0, left: 320.0, + }), + layout_container: LayoutContainerStyle::default(), + layout_dimensions: LayoutDimensionStyle { + layout_target_width: Some(300.0), + layout_target_height: Some(200.0), + layout_min_width: None, layout_max_width: None, + layout_min_height: None, layout_max_height: None, + layout_target_aspect_ratio: None, + }, + layout_child: None, + corner_radius: { + use cg::cg::types::Radius; + RectangularCornerRadius { + tl: Radius::circular(12.0), + tr: Radius::circular(0.0), + bl: Radius::circular(0.0), + br: Radius::circular(12.0), + } + }, + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![solid(250, 250, 250, 255)]), + strokes: Paints::new(vec![solid(59, 100, 220, 255)]), + stroke_style: StrokeStyle { + stroke_align: StrokeAlign::Center, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: Some(cg::cg::stroke_dasharray::StrokeDashArray(vec![8.0, 4.0])), + }, + stroke_width: StrokeWidth::Rectangular(RectangularStrokeWidth { + stroke_top_width: 1.0, stroke_right_width: 3.0, stroke_bottom_width: 5.0, stroke_left_width: 3.0, + }), + effects: LayerEffects::default(), + clip: false, + }); + + let pairs = vec![(1u64, c1), (2u64, c2), (3u64, c3), (4u64, c4)]; + build_scene("L0 Strokes Rect", None, pairs, HashMap::new(), vec![1, 2, 3, 4]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_type.rs b/crates/grida-canvas/tests/fixtures/l0_type.rs new file mode 100644 index 0000000000..0ba74abf2a --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_type.rs @@ -0,0 +1,246 @@ +use super::*; +use cg::cg::color::CGColor; +use cg::cg::fe::*; + +fn tspan( + x: f32, y: f32, + content: &str, + font_size: f32, + font_weight: u32, + h_align: TextAlign, + v_align: TextAlignVertical, + width: Option, + height: Option, +) -> Node { + Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(x, y, 0.0), + width, + height, + layout_child: None, + text: content.to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", font_size); + ts.font_weight = FontWeight(font_weight); + ts + }, + text_align: h_align, + text_align_vertical: v_align, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }) +} + +pub fn build() -> Scene { + let y_gap = 50.0; + + // Regular 16px, Left/Top + let t1 = tspan(0.0, 0.0, + "Regular 16px", 16.0, 400, + TextAlign::Left, TextAlignVertical::Top, None, None); + + // Bold 24px, Center/Center + let t2 = tspan(0.0, y_gap, + "Bold 24px", 24.0, 700, + TextAlign::Center, TextAlignVertical::Center, + Some(300.0), Some(40.0)); + + // Right aligned + let t3 = tspan(0.0, y_gap * 2.0, + "Right Aligned", 16.0, 400, + TextAlign::Right, TextAlignVertical::Bottom, + Some(300.0), Some(30.0)); + + // Justified text + let t4 = tspan(0.0, y_gap * 3.0, + "Justified Text with enough words to wrap across multiple lines for demonstration.", + 14.0, 400, + TextAlign::Justify, TextAlignVertical::Top, + Some(200.0), None); + + // Text with stroke + let t5 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 5.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "With Stroke".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_weight = FontWeight(400); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![solid(220, 59, 59, 255)]), + stroke_width: 1.0, + stroke_align: StrokeAlign::Outside, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Text with drop shadow + let t6 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 6.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "Drop Shadow".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_weight = FontWeight(400); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects { + shadows: vec![FilterShadowEffect::DropShadow(FeShadow { + dx: 2.0, dy: 2.0, blur: 4.0, spread: 0.0, + color: CGColor { r: 0, g: 0, b: 0, a: 153 }, + active: true, + })], + ..LayerEffects::default() + }, + }); + + // Max lines + ellipsis + let t7 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 7.0, 0.0), + width: Some(120.0), + height: None, + layout_child: None, + text: "Max 2 Lines with truncation and ellipsis at the end".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 14.0); + ts.font_weight = FontWeight(400); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: Some(2), + ellipsis: Some("\u{2026}".to_owned()), + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Letter spacing + line height + let t8 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 8.5, 0.0), + width: Some(300.0), + height: None, + layout_child: None, + text: "Tracked + Tall Line Height".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 16.0); + ts.letter_spacing = TextLetterSpacing::Fixed(4.0); + ts.line_height = TextLineHeight::Factor(2.0); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Underline decoration (red, 2px thick) + let t9 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 10.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "Underlined Text".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 18.0); + ts.text_decoration = Some(TextDecorationRec { + text_decoration_line: TextDecorationLine::Underline, + text_decoration_color: Some(CGColor { r: 220, g: 59, b: 59, a: 255 }), + text_decoration_style: Some(TextDecorationStyle::Solid), + text_decoration_skip_ink: Some(true), + text_decoration_thinkness: Some(2.0), + }); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // UPPERCASE transform + let t10 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 11.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "uppercase transform".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 16.0); + ts.text_transform = TextTransform::Uppercase; + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + flat_scene("L0 Type", vec![t1, t2, t3, t4, t5, t6, t7, t8, t9, t10]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_type_features.rs b/crates/grida-canvas/tests/fixtures/l0_type_features.rs new file mode 100644 index 0000000000..cc8cbde935 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_type_features.rs @@ -0,0 +1,181 @@ +use super::*; + +/// OpenType font features: ligatures, small caps, stylistic sets, etc. +pub fn build() -> Scene { + let y_gap = 50.0; + + // Ligatures disabled + let t1 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, 0.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "ffi ffl — liga off".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_features = Some(vec![ + FontFeature { tag: "liga".to_owned(), value: false }, + ]); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Ligatures enabled (default, explicit) + let t2 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap, 0.0), + width: None, + height: None, + layout_child: None, + text: "ffi ffl — liga on".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_features = Some(vec![ + FontFeature { tag: "liga".to_owned(), value: true }, + ]); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Small caps + let t3 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 2.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "Small Caps Text — smcp".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_features = Some(vec![ + FontFeature { tag: "smcp".to_owned(), value: true }, + ]); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Stylistic set 01 + let t4 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 3.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "Stylistic Set 01 — ss01".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_features = Some(vec![ + FontFeature { tag: "ss01".to_owned(), value: true }, + ]); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Tabular numbers + slashed zero + let t5 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 4.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "0123456789 — tnum + zero".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_features = Some(vec![ + FontFeature { tag: "tnum".to_owned(), value: true }, + FontFeature { tag: "zero".to_owned(), value: true }, + ]); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + // Kerning off + let t6 = Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, y_gap * 5.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "AVAW Typography — kern off".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_kerning = false; + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }); + + flat_scene("L0 Type Features", vec![t1, t2, t3, t4, t5, t6]) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_type_fvar.rs b/crates/grida-canvas/tests/fixtures/l0_type_fvar.rs new file mode 100644 index 0000000000..0652f3b7d3 --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_type_fvar.rs @@ -0,0 +1,161 @@ +use super::*; + +/// Variable font axes: weight, width, optical sizing, and custom fvar axes. +pub fn build() -> Scene { + let y_gap = 50.0; + + // Weight axis via font_weight (100..900) + let weights = [ + (100, "Thin 100"), + (300, "Light 300"), + (400, "Regular 400"), + (700, "Bold 700"), + (900, "Black 900"), + ]; + + let mut nodes: Vec = Vec::new(); + + for (i, (w, label)) in weights.iter().enumerate() { + nodes.push(Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, (i as f32) * y_gap, 0.0), + width: None, + height: None, + layout_child: None, + text: label.to_string(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_weight = FontWeight(*w); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + })); + } + + let base_y = (weights.len() as f32) * y_gap; + + // Width axis (font_width field — high-level wdth exposure) + nodes.push(Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, base_y, 0.0), + width: None, + height: None, + layout_child: None, + text: "Condensed (width=75)".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_width = Some(75.0); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + })); + + // Optical sizing + nodes.push(Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, base_y + y_gap, 0.0), + width: None, + height: None, + layout_child: None, + text: "Optical Size Fixed(48)".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_optical_sizing = FontOpticalSizing::Fixed(48.0); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + })); + + // Custom fvar axes via font_variations + nodes.push(Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, base_y + y_gap * 2.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "Custom Axes: wght=600 wdth=80 GRAD=50".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Roboto Flex", 20.0); + ts.font_variations = Some(vec![ + FontVariation { axis: "wght".to_owned(), value: 600.0 }, + FontVariation { axis: "wdth".to_owned(), value: 80.0 }, + FontVariation { axis: "GRAD".to_owned(), value: 50.0 }, + ]); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + })); + + // Italic style + nodes.push(Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(0.0, base_y + y_gap * 3.0, 0.0), + width: None, + height: None, + layout_child: None, + text: "Italic Style".to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", 20.0); + ts.font_style_italic = true; + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + })); + + flat_scene("L0 Type fvar", nodes) +} diff --git a/crates/grida-canvas/tests/fixtures/l0_vector.rs b/crates/grida-canvas/tests/fixtures/l0_vector.rs new file mode 100644 index 0000000000..a6e47ef10c --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/l0_vector.rs @@ -0,0 +1,152 @@ +use super::*; +use cg::cg::stroke_dasharray::StrokeDashArray; +use cg::vectornetwork::*; +use math2::transform::AffineTransform; + +/// Vector network nodes: closed shape, open path, multi-region, variable-width stroke. +pub fn build() -> Scene { + // Closed bezier quad (4 curved segments forming a rounded square) + let closed = Node::Vector(VectorNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(0.0, 0.0, 100.0, 100.0, 0.0), + network: VectorNetwork { + vertices: vec![(0.0, 0.0), (100.0, 0.0), (100.0, 100.0), (0.0, 100.0)], + segments: vec![ + VectorNetworkSegment { a: 0, b: 1, ta: (30.0, 0.0), tb: (-30.0, 0.0) }, + VectorNetworkSegment { a: 1, b: 2, ta: (0.0, 30.0), tb: (0.0, -30.0) }, + VectorNetworkSegment { a: 2, b: 3, ta: (-30.0, 0.0), tb: (30.0, 0.0) }, + VectorNetworkSegment { a: 3, b: 0, ta: (0.0, -30.0), tb: (0.0, 30.0) }, + ], + regions: vec![VectorNetworkRegion { + loops: vec![VectorNetworkLoop(vec![0, 1, 2, 3])], + fill_rule: FillRule::EvenOdd, + fills: None, + }], + }, + corner_radius: 0.0, + fills: Paints::new(vec![solid(59, 100, 220, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width: 1.5, + stroke_width_profile: None, + stroke_align: StrokeAlign::Center, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + layout_child: None, + }); + + // Open path (3 vertices, 2 segments, no region) + let open = Node::Vector(VectorNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(140.0, 0.0, 120.0, 80.0, 0.0), + network: VectorNetwork { + vertices: vec![(0.0, 80.0), (60.0, 0.0), (120.0, 80.0)], + segments: vec![ + VectorNetworkSegment { a: 0, b: 1, ta: (20.0, -30.0), tb: (-20.0, 10.0) }, + VectorNetworkSegment { a: 1, b: 2, ta: (20.0, 10.0), tb: (-20.0, -30.0) }, + ], + regions: vec![], + }, + corner_radius: 0.0, + fills: Paints::new(vec![]), + strokes: Paints::new(vec![solid(220, 59, 59, 255)]), + stroke_width: 3.0, + stroke_width_profile: None, + stroke_align: StrokeAlign::Center, + stroke_cap: StrokeCap::Round, + stroke_join: StrokeJoin::Round, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + marker_start_shape: StrokeMarkerPreset::Circle, + marker_end_shape: StrokeMarkerPreset::EquilateralTriangle, + layout_child: None, + }); + + // Region with explicit fill + dashed stroke + let region_fill = Node::Vector(VectorNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(0.0, 140.0, 100.0, 100.0, 0.0), + network: VectorNetwork { + vertices: vec![(50.0, 0.0), (100.0, 100.0), (0.0, 100.0)], + segments: vec![ + VectorNetworkSegment { a: 0, b: 1, ta: (0.0, 0.0), tb: (0.0, 0.0) }, + VectorNetworkSegment { a: 1, b: 2, ta: (0.0, 0.0), tb: (0.0, 0.0) }, + VectorNetworkSegment { a: 2, b: 0, ta: (0.0, 0.0), tb: (0.0, 0.0) }, + ], + regions: vec![VectorNetworkRegion { + loops: vec![VectorNetworkLoop(vec![0, 1, 2])], + fill_rule: FillRule::NonZero, + fills: Some(Paints::new(vec![solid(255, 200, 40, 255)])), + }], + }, + corner_radius: 0.0, + fills: Paints::new(vec![solid(59, 180, 75, 255)]), + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width: 2.0, + stroke_width_profile: None, + stroke_align: StrokeAlign::Center, + stroke_cap: StrokeCap::Butt, + stroke_join: StrokeJoin::Miter, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: Some(StrokeDashArray(vec![8.0, 4.0])), + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + layout_child: None, + }); + + // Variable-width stroke profile + let varwidth = Node::Vector(VectorNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::from_box_center(140.0, 140.0, 120.0, 80.0, 0.0), + network: VectorNetwork { + vertices: vec![(0.0, 40.0), (60.0, 0.0), (120.0, 40.0), (60.0, 80.0)], + segments: vec![ + VectorNetworkSegment { a: 0, b: 1, ta: (20.0, -20.0), tb: (-20.0, 10.0) }, + VectorNetworkSegment { a: 1, b: 2, ta: (20.0, 10.0), tb: (-20.0, -20.0) }, + VectorNetworkSegment { a: 2, b: 3, ta: (-20.0, 20.0), tb: (20.0, -10.0) }, + ], + regions: vec![], + }, + corner_radius: 0.0, + fills: Paints::new(vec![]), + strokes: Paints::new(vec![solid(128, 60, 200, 255)]), + stroke_width: 4.0, + stroke_width_profile: Some(cg::cg::varwidth::VarWidthProfile { + base: 2.0, + stops: vec![ + cg::cg::varwidth::WidthStop { u: 0.0, r: 1.0 }, + cg::cg::varwidth::WidthStop { u: 0.5, r: 6.0 }, + cg::cg::varwidth::WidthStop { u: 1.0, r: 1.0 }, + ], + }), + stroke_align: StrokeAlign::Center, + stroke_cap: StrokeCap::Round, + stroke_join: StrokeJoin::Round, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + layout_child: None, + }); + + flat_scene("L0 Vector", vec![closed, open, region_fill, varwidth]) +} diff --git a/crates/grida-canvas/tests/fixtures/mod.rs b/crates/grida-canvas/tests/fixtures/mod.rs new file mode 100644 index 0000000000..7ede1491ce --- /dev/null +++ b/crates/grida-canvas/tests/fixtures/mod.rs @@ -0,0 +1,363 @@ +//! Shared helpers for L0 fixture generators. + +use std::collections::HashMap; + +use cg::cg::alignment::Alignment; +use cg::cg::color::CGColor; +use cg::cg::stroke_width::{SingularStrokeWidth, StrokeWidth}; +use cg::cg::tilemode::TileMode; +use cg::cg::types::*; +use math2::box_fit::BoxFit; +use cg::io::io_grida_fbs; +use cg::node::scene_graph::SceneGraph; +use cg::node::schema::*; +use math2::transform::AffineTransform; + +pub mod l0_boolean_operation; +pub mod l0_container; +pub mod l0_effects; +pub mod l0_group; +pub mod l0_effects_glass; +pub mod l0_image; +pub mod l0_image_filters; +pub mod l0_layout_flex; +pub mod l0_layout_position; +pub mod l0_layout_transform; +pub mod l0_masks; +pub mod l0_paints; +pub mod l0_paints_stack; +pub mod l0_shape_arc; +pub mod l0_shape_polygon; +pub mod l0_shapes; +pub mod l0_strokes; +pub mod l0_strokes_rect; +pub mod l0_type; +pub mod l0_type_features; +pub mod l0_type_fvar; +pub mod l0_vector; + +// ═════════════════════════════════════════════════════════════════════════════ +// Scene building +// ═════════════════════════════════════════════════════════════════════════════ + +/// Build a `Scene` from `(id, node)` pairs, parent→children links, and root ids. +pub fn build_scene( + name: &str, + bg: Option, + nodes: Vec<(NodeId, Node)>, + links: HashMap>, + roots: Vec, +) -> Scene { + let graph = SceneGraph::new_from_snapshot(nodes, links, roots); + Scene { + name: name.to_owned(), + graph, + background_color: bg, + } +} + +/// Build id_map and position_map with a prefix to avoid collisions in multi-scene files. every node ID to avoid collisions in multi-scene files. +pub fn build_maps_prefixed( + scene: &Scene, + id_map: &mut HashMap, + position_map: &mut HashMap, + prefix: &str, +) { + fn walk( + graph: &SceneGraph, + nid: &NodeId, + counter: &mut usize, + id_map: &mut HashMap, + position_map: &mut HashMap, + prefix: &str, + ) { + id_map.entry(*nid).or_insert_with(|| format!("{prefix}n{nid}")); + if let Some(children) = graph.get_children(nid) { + for (i, child) in children.clone().iter().enumerate() { + let pos = format!("a{i:04}"); + position_map.insert(*child, pos); + walk(graph, child, counter, id_map, position_map, prefix); + } + } + *counter += 1; + } + let mut counter = 0usize; + for root in scene.graph.roots() { + walk(&scene.graph, &root, &mut counter, id_map, position_map, prefix); + } +} + +fn fixtures_dir() -> std::path::PathBuf { + let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); + manifest.join("../../fixtures/test-grida") +} + +/// Encode multiple scenes into a single `.grida` file and write to disk. +/// Each entry is `(scene_id_suffix, scene)`. The scene_id is derived from the scene name. +pub fn write_multi_fixture(scenes: &[(&str, Scene)], name: &str) { + let mut entries_data: Vec<( + String, + HashMap, + HashMap, + )> = Vec::new(); + + for (i, (_, scene)) in scenes.iter().enumerate() { + let scene_id = format!("scene{}", i + 1); + let mut id_map = HashMap::new(); + let mut position_map = HashMap::new(); + // Prefix node IDs with scene index to avoid collisions across scenes + // in the shared flat node list. + build_maps_prefixed(scene, &mut id_map, &mut position_map, &format!("s{}_", i)); + entries_data.push((scene_id, id_map, position_map)); + } + + let entries: Vec<( + &str, + &Scene, + &HashMap, + &HashMap, + )> = scenes + .iter() + .zip(entries_data.iter()) + .map(|((_, scene), (scene_id, id_map, position_map))| { + (scene_id.as_str(), scene, id_map, position_map) + }) + .collect(); + + let bytes = io_grida_fbs::encode_multi(&entries); + assert!(!bytes.is_empty(), "{name}: encoded bytes empty"); + + // Verify decode: all scenes should be recoverable + let decoded = io_grida_fbs::decode_all(&bytes) + .unwrap_or_else(|e| panic!("{name}: decode failed: {e}")); + assert_eq!( + decoded.len(), + scenes.len(), + "{name}: expected {} scenes, got {}", + scenes.len(), + decoded.len() + ); + for (i, ((_, original), decoded_scene)) in scenes.iter().zip(decoded.iter()).enumerate() { + assert_eq!( + original.name, decoded_scene.name, + "{name}: scene[{i}] name mismatch" + ); + } + + let dir = fixtures_dir(); + std::fs::create_dir_all(&dir).unwrap(); + let path = dir.join(format!("{name}.grida")); + std::fs::write(&path, &bytes).unwrap(); + eprintln!( + "Wrote {} bytes ({} scenes) to {}", + bytes.len(), + scenes.len(), + path.display() + ); +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Paint builders +// ═════════════════════════════════════════════════════════════════════════════ + +pub fn solid(r: u8, g: u8, b: u8, a: u8) -> Paint { + Paint::Solid(SolidPaint { + active: true, + color: CGColor { r, g, b, a }, + blend_mode: BlendMode::Normal, + }) +} + +pub fn linear_gradient() -> Paint { + Paint::LinearGradient(LinearGradientPaint { + active: true, + xy1: Alignment::CENTER_LEFT, + xy2: Alignment::CENTER_RIGHT, + tile_mode: TileMode::default(), + transform: AffineTransform::default(), + stops: vec![ + GradientStop { offset: 0.0, color: CGColor { r: 255, g: 0, b: 0, a: 255 } }, + GradientStop { offset: 1.0, color: CGColor { r: 0, g: 0, b: 255, a: 255 } }, + ], + opacity: 1.0, + blend_mode: BlendMode::Normal, + }) +} + +pub fn radial_gradient() -> Paint { + Paint::RadialGradient(RadialGradientPaint { + active: true, + transform: AffineTransform::default(), + stops: vec![ + GradientStop { offset: 0.0, color: CGColor { r: 255, g: 255, b: 0, a: 255 } }, + GradientStop { offset: 1.0, color: CGColor { r: 0, g: 128, b: 0, a: 200 } }, + ], + opacity: 0.8, + blend_mode: BlendMode::Normal, + tile_mode: TileMode::default(), + }) +} + +pub fn sweep_gradient() -> Paint { + Paint::SweepGradient(SweepGradientPaint { + active: true, + transform: AffineTransform::default(), + stops: vec![ + GradientStop { offset: 0.0, color: CGColor { r: 0, g: 255, b: 255, a: 255 } }, + GradientStop { offset: 0.5, color: CGColor { r: 255, g: 0, b: 255, a: 255 } }, + GradientStop { offset: 1.0, color: CGColor { r: 255, g: 255, b: 0, a: 255 } }, + ], + opacity: 1.0, + blend_mode: BlendMode::Normal, + }) +} + +/// The system checker image bundled in the renderer. Use this for all fixture +/// image paints so they actually render in the native demo. +pub const SYSTEM_IMAGE: &str = "system://images/checker-16-strip-L98L92.png"; + +pub fn image_paint() -> Paint { + image_paint_with(ResourceRef::HASH(SYSTEM_IMAGE.to_owned()), ImagePaintFit::Fit(BoxFit::Cover)) +} + +pub fn image_paint_with(image: ResourceRef, fit: ImagePaintFit) -> Paint { + Paint::Image(ImagePaint { + active: true, + image, + fit, + filters: ImageFilters::default(), + opacity: 1.0, + blend_mode: BlendMode::Normal, + quarter_turns: 0, + alignement: Alignment::CENTER, + }) +} + +// ═════════════════════════════════════════════════════════════════════════════ +// Node builders +// ═════════════════════════════════════════════════════════════════════════════ + +/// Simple rectangle with one fill. +pub fn rect(x: f32, y: f32, w: f32, h: f32, fill: Paint) -> Node { + Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(x, y, w, h, 0.0), + size: Size { width: w, height: h }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![fill]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }) +} + +/// Rectangle with rotation. +pub fn rect_rotated(x: f32, y: f32, w: f32, h: f32, rotation: f32, fill: Paint) -> Node { + Node::Rectangle(RectangleNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(x, y, w, h, rotation), + size: Size { width: w, height: h }, + corner_radius: RectangularCornerRadius::default(), + corner_smoothing: CornerSmoothing(0.0), + fills: Paints::new(vec![fill]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: StrokeWidth::None, + effects: LayerEffects::default(), + layout_child: None, + }) +} + +/// Simple ellipse with one fill. +pub fn ellipse(x: f32, y: f32, w: f32, h: f32, fill: Paint) -> Node { + Node::Ellipse(EllipseNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + transform: AffineTransform::from_box_center(x, y, w, h, 0.0), + size: Size { width: w, height: h }, + fills: Paints::new(vec![fill]), + strokes: Paints::new(vec![]), + stroke_style: StrokeStyle::default(), + stroke_width: SingularStrokeWidth(None), + inner_radius: None, + start_angle: 0.0, + angle: None, + corner_radius: None, + effects: LayerEffects::default(), + layout_child: None, + }) +} + +/// Simple line. +pub fn line(x: f32, y: f32, length: f32, rotation: f32, stroke_width: f32) -> Node { + Node::Line(LineNodeRec { + active: true, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + transform: AffineTransform::new(x, y, rotation), + size: Size { width: length, height: 0.0 }, + strokes: Paints::new(vec![solid(0, 0, 0, 255)]), + stroke_width, + stroke_cap: StrokeCap::Butt, + stroke_miter_limit: StrokeMiterLimit(4.0), + stroke_dash_array: None, + _data_stroke_align: StrokeAlign::Center, + marker_start_shape: StrokeMarkerPreset::None, + marker_end_shape: StrokeMarkerPreset::None, + layout_child: None, + }) +} + +/// Simple text span. +pub fn text(x: f32, y: f32, content: &str, font_size: f32, font_weight: u32) -> Node { + Node::TextSpan(TextSpanNodeRec { + active: true, + transform: AffineTransform::new(x, y, 0.0), + width: None, + height: None, + layout_child: None, + text: content.to_owned(), + text_style: { + let mut ts = TextStyleRec::from_font("Inter", font_size); + ts.font_weight = FontWeight(font_weight); + ts + }, + text_align: TextAlign::Left, + text_align_vertical: TextAlignVertical::Top, + max_lines: None, + ellipsis: None, + fills: Paints::new(vec![solid(0, 0, 0, 255)]), + strokes: Paints::new(vec![]), + stroke_width: 0.0, + stroke_align: StrokeAlign::Center, + opacity: 1.0, + blend_mode: LayerBlendMode::PassThrough, + mask: None, + effects: LayerEffects::default(), + }) +} + +/// Simple flat scene: all nodes at the root level, auto-assigned positions. +pub fn flat_scene(name: &str, nodes: Vec) -> Scene { + let mut pairs = Vec::new(); + for (i, node) in nodes.into_iter().enumerate() { + pairs.push(((i + 1) as u64, node)); + } + let roots: Vec = pairs.iter().map(|(id, _)| *id).collect(); + build_scene(name, None, pairs, HashMap::new(), roots) +} + + diff --git a/fixtures/test-grida/L0.grida b/fixtures/test-grida/L0.grida new file mode 100644 index 0000000000000000000000000000000000000000..5247463765f42c8a4128ecfb8f43efb325b1574a GIT binary patch literal 48616 zcmch=4}6tZdLDQr%d&=QDW$5GvRd}mt5TIHj=(abVF{A%RTzz{5Q0^Px+){EBnw6s zswCT(5PFAFqEbp(hh-Q-T!m6f33aGLDW$|=Sglv}DqgM2x>}cdwM2C&QM?MFE@2q7 z&-4EI^y$0CUbOw~$M5i*fA4$V_c`x5-@kjWib4qA-0-c2Rq5&J0_5L+etNnX*n_kH z=}DxjXYdbLinQrM2xs07q2P1~6K{nu`TY=vkAzV6cS9K1AHs=j2={h{Fy0qJ$zKbh zWqk<6YeN`Y6T(zY2)8Pc|8tNjLEk?a!m+{-hVn!B4d@QP6~fMc8A99T5Z3<-q)Q<* z{&S3VF@(Fn8Nv~y!FPcF6zO~j`yf;WTSop#2-nYH{9g~@;90c&W9U5-LgzmUp$m3) z{X_VIRP_%+xcX}$T>1MUtcLC5zY5tCA+-Jq#yAd{W9S2aMvsP21fS0RatIA?!pFlQ zoceo^ITS)Q{A@S~-~SHq0QwGuQ1MH!>u;fqH1#*3XD`P49%TMT2z5x4zlgfsX#WMY zzXAJ@>UM&c*CF?62u=Oqpci%^HU0GvYI;H_-vJ%nAw1{;XWxZ?+hO}Q*whJKFT?i^ z%uze|Z39Oy!LF?#q_#k3EBN0G`@sEZ3vBqS(1}#_BJ^wu;rzE@??$vWhp_tv$ZY`c zKac)D2OHLbd!(kHMSTN2QHBNpmI$krh)YB2`p zxZtNzw;FaJ-CYH)zX7|DifYjIb;=B1d2Kac!>8$#vNAzVm7=Dz`-NT-%S=YJhSe;N4tuQ2vv z)csj-z6de*UxL3<*n+hC&%nH3yfWizJCIC&qI3= z?05>i6=F`F1iw$fUZlo+aFmDspm5jE==wpo(Dzb%cl(MJ;l|qOX%vgNuKNGd^z?Ng zs0?s5unwYv1>cl@d8}LAD0$@k$krZKJ#}o~3_XCnGe4f5P9f8Pl>LRm+R^KO<+qUy zz*?4>ABG&<|| zmmw$$eJhrKryFy7L-jtz_Ga}f?;i54NSI#X0n_@GM>S+?ken};AtZcteQSF9-`!}b z{e#J_HB4ZmcIe>@H-A*{TrHAtV|n3b0=6Z&S%bPHH%oP_{BjJ)wlb7mjykY{)02Wz z=@PU#PeJ4w(?y2IP4`K|h$qB<0epb!C#nDJ+tbsGse{O4iG(I3*1PXj+E;w<#9mFG z|JCsD8gfA2hu;ugkeE3rzY}urenHzzFU^&Nav9I-i!ke0Dj|i`fb`67{dd2KaefJ2Q z@^DV|OFWm`I-G#cR;0&?=K{zUA^DuL3>CWmo2I9~mkqUlIMsuBZIn5^gK^0fmTx$L zJm-V8++_cAvq^3Qy0fw|Fg_zYsts24W?v`7oy!cIS(6> zoRF*esHc2nFF5&J?Tyy+B+Aao1!+sAuLusNCxFh$sMKNqGu#fO?q0)a44mJttL?I$ zum9robU&Org}$vwJCSG`W!={T@z3Ue-SqUo{&0i%>htgYDr5cH(C_pA_$uQ>68Raz zSj2eZIQ@{jENv)!$?+ok(OlA zBgGIFh7>dug|CHq;ZI<9DMO7Md3pIyKz+7;b_(_9H|QGX9Me9W-b1VC#WkK`kKk~6 zr8mI^Cj-*fm_95xoW2BfPEJW3uBi>z0BPG=!-x}H%bZajBxX{u zoqo)PlK|NQB>f*RHi1XyN%x4(aIXQ-Wk`uZHH1{_q z?swd?oRbpp02RVX6Z-ic$Ni4|&S5NKJkiD$$eoln6u#t0qaXccEYl|E#rL_jsH494 zK6gU(=C4ORWpR5GDxk-&nWx0J!E_OJHSX^0gR^pm`&}{0JX;rv#vc5=3Z{ESU zzpe85Li8(qn>y>0o1!lS!#-Oy3nO&FlwK_aVb*4D6#* zI)-r43ERM@qDy9CzoKu7jaUG`q`W;4-eJeh?Bc0J11A94g0_0B+xk-le($; zal>99b?-8q;pEwHLv7dnAGXtv3H;ZJ#Q3j5szCaxqZ+a`NX`k%kkR#zwZUgV@%xOY z(e9j#z=k9z9CsP&lbkG6y%8rxC_5*k;tTeF!(d}Do+qUa&wCB`1F8Fv;S47$!jz8j z1lv22?*}q}{DJtC?{@D)+ZWk=Ge>7tcwcqq(-!J#11E#1^Eo>x?Su7443`4g?-|1x z`<92pkVTW&SA=|z>D={4`d;LBMw)6lcjahvZhXJn6W7OQz}O2qR9|FU_-oVCt5N5+ zRY-d(Q>FdFHPb<#Xyc4+RiP5HtYZBs&Ce;G_Ca<4$>WJ-sFZlRvN`v9q8IHxFJ;Il z=Y{*uF4#}Ibf3k(gJ(lqRc{nen^1PnQqne@t}t8=bWT=F-Bf*p;YX(XljIn9uTTej zF|2TMN%M2!j%*9_IO-xL>>K*ShToaqhmyIJ%(}#lG_`!JroHJXO5eWlZNp)DQKOU!`+&n(;T)y zwiU_ekY$)HaeZV%=6XZ*S92Gq}*!)n}5sorP~t59~%?uoy+K9)X% z=?WWzdsB(jrP5`FZ=3Fm$uaN@aS`l=h{W|_&CiLG9?141IVUVbuW)i^YwmUOX0$se zMaU;PVO)2>{v;>sRByyd1Io@xv9#g(*l;z_5#i(Q7cns&Oz9@!Ipge|tZrc#@5D6oKjTv%79UX>I z3?n9nRhP_H9o8JW&1t?;kS#~@`C^&6IPRvl*)h!P{rPD3`MLxflJmvAxfJ!3kL*R| zL$x=Wue&HaXIG?cFg;{A33N`zq%M^nH|zyc_b$U|4BYG8P}_x*X4sBybK;~LvNcG~ z3Cq;Qal6`cWB6&bJ0~NsA<0P<_R*YKo(7MPqfzBdx1P}P=)hjWu zsblUjPy^Z3NFD<$!wiXmo|kj)3znkYV_+0Ed{GQMgL?X=JbYcQSxQxJ6a&R5`&?WX zUwYCv1P9XvHU_z#lDbs=J;MV)>VDHO8UyF&yE=y4@8A9Y^mHSfI)%QiNIQ|LkSOcE zmcu_5S=Z93@vhhVrL~BbTJP5s>$ea6*v4n4zxQc}^qas~tcxe=JOa6or45BIId-9+ zb4r`Oh||6BjXEQLaV{HE-Lvlb+aT|}Rw54u)765FnR3{Esw?-NzggPBfgyF^=|O#$ z;X@xAU(jmeJF$-xz-#9UZl_{|apR zqIp@2`dOUdeA**+g<13R0R4Pk)UWEyi1>Fja|OPw?#`W;Nohx18&U^87g1k;{=}mw zbL_Vap4lI<7pmS_e0RlUtC7cVz3Fa>ybPLS*9-P-G_A( ze+R(%S%r3w!3o&#MKPF0J>?^Nac#6r^+qw6LfN^#CB96hrv&ji2OEPiSRitP>0-g2 z^joI;baD*71CT!9Nxx4|;fkvmyL}1n+{-Y73!x83-dDXFSP#G1k3J0aXlybr=|AJl zx!M`)cOLy1n;w_!w_uQZa%1x<en&dioowe@*`ukJP zE3ShxJG%R~Z0m0CMKQc){o=kH$f^5!m{i>o_q#uraq=!f9$Sz+2d<#9uQ=-dI`$`| zLL`r6&R;T?dEYl5^%2L*SIWD9&&|Jbw6l#i`P^KPw!!oj!wDeApwFXHmr9QrZU<7n z*HCGV4{rp62h}JA^az9T<+hF>X z;Rw+Eyd-tFE;QT#r0y3DXZ&0quE0Jt6|#NZeaSyDhI|ha=X4Vi+kDMUYJXXq<%nA} z#g3V^S<!@=cu4;Q2Hhd9C9a{}&c&G|PYOl<9vt3(1Zd@;dpJhm&m@k~y0pyeO&A9&5Y|wR! zxW@JU2Wmq!-?YhjI4Es{=_7_$fbQoRsY|8L8>WHu^LfK)3_NQ-r*kHLuGaZ}?DZR7 zg?^;>po=!p&mQEHezKlE(U!;t_<2!ni2S5Y?q|QW4W@S+o&vg`hougm|1w+#r0x}l z(HJ$@Ct)9lW&6?}OFsY0;(AE?OI$yA?a^^P8S7X6`s`~&o@;hWyT)||^xQ_7_A{<& zll$p${h8SwkL!z>0`&RLN?j^FV7M4aKc6;? z{KWnFKFkH0#Lp6)@5lCY3_5Ngy@lj{HX@(&lW|R-XiH=R{5*udsEYigP3~utv<;@4 z4R-_G&rYes8ZyiWQujQ=Xbjx@Sl_}$Xy?0-GP%ZL^yqb#Ut@h)te@YhalkQdy&-uj zvtwotAB*){uxs{nTgG~uv}>%FK+i`g(0zwz z)An%Ca0!rhFExy~0sjN)i^Ra9Y4!0E`l9Lo^|i?j=om*jiR3ZRgnTjv80%+~HlXrN z^hHw?1GLF`STAjZ=}m@NpvOR$)TPq>hJ`@-Io~ky6ZalHml>|uMYu|#9$x8jC2$>l*&4X;3mPBPKe=1-6t-h#4cv(J zEBVFQ_hVcGozkvrU=j3uj56)u8rX-t`|WGs6Z0j$2Hw*#qBYP8Jts^L*T8_bhr@v$=KuiTZVf2r2aqmoo)y$)G?yiD?-`lU{vhJ`wzo_Hjh1xfmHonX-n1bH{FK} zqcL!Zr2^Osi6=PrZuIX0GJo6jRU&@`c^}u; z!)K;zCgxU#k953fJ@i2LInzVCj%j-s6fDiG0CKEVhS6Lh9!{uTdR>q;enEIY`qL(I zR)O?Y$6e^Thvaj`GGt_~CM7@nIRnqlCAK3yWvjx^&E$ad2=hV{Y&r+|;O`Cw_(H?&M%|NlW^AfJ@s<}eOr+p zhqDUERw6lPEJKB^|Gl|;X(`%0zOTZDBqyA!C8$qwQmA?(P9C6-b222p;QIlFcY)5y zxYXeq$Z!{sy7wAJoZx&jp<_sVw;ud?Q8LtnAq|Lm5&@Ae@ZHd~*#J-s<~ z&xPx%cb<%UV0!uiWRD=R{MLZ_$?J_uU(z}DIWPO0Gjra&Px9#HN4Aza z>NnPZJJtL~&yJUSxAb(rk-C{ZlKO6YZ+B`(M+bWjVm$J~_b`W{n4fL=R(HQVxOyJ? z=_C2S3mcOBuQR)5{CzI0RGT9HM^P>|Jsjtr_`WL>v|o7BbR0KS{;R*!jdQ^R?IS#; z{|@#D$R9=`7w`S;**vwNt{MH=wiP}B@|>pzs+O;Kw)(l1U$6Ow zP}JZ3>MNe3+?@>s^!8|U!Q!x^cKc?6i07Dw{x@_`6Ne2&89dG-|)_vFW&J7;%IXL4*1u93A+(vpJd%&XOFRfKGCB7Cyx}I;eVtqaj_3X!avp z%YEU6SGII@r7~N3`clp9JNuQQS6=F2uVRe#D2^^dw{z5re3GNfW>cJ_lWI@I(Q4SV z)AVqhHfbMDcWJ-y&~y}{O#dQ|a9-+Ee}tpA58-=xXdgggzZKvt&QUY!u9}Vyur5)I z;^@5XQIxx->kv6VvbBF2*^Z8Fsg8~=;ihZb%Ue?T@O%Zvcoa7uLZ@@H8~G$RADJz2 zZr)WpB5pRpmLsNz<7B11F+HID!eSt8e%dhN2Ir@J>W6Ui{@=s%8uUAe#Qy8RRh*km zsJmf0ZezYtjJO%GeTi}f8e7Quk*zI3HkTXSryDWOqxiW4z0S`G@#wr}Zw zwYOc)m+Zs#xLqf6>Z+Y9;s5#n9`B+cvk!@~A7DP>{I5q{3+!Rrc5nuW_;0fH%(i_R zx5)XCt)<`J>fh4E4GvpfJ6osh3GzwayUmUn|Ej}AwISkt3gt@E!*Lc# z`*0?u{lYt@DvWNZICvYA)=`ggpNs^8h(w<`7e%boo{y|St*^?F->=TFz*V_zHU8-Dt^ zD)gJexN*Ml6DeWCj;%ZTcckRYgl}Oi=d2O=Bxfhh#yDpO)V_$ba@g2vdN@{-wEsq? zS^I@gO~+@35oa}cAEN#VXGec|_F7&H4rz~{FS(Y_Kqq;*2)_UkXUA-9vuzZ67a(?I zYfDl3xgMM=w{)dm*tQFSzinrGTSaxvU&M0)j1%W5e$DkR^g2JC$S3)^X7gV8|)c0JsjttwC~Cs(SBhmki0x&81aK^(!=VD@bl6CFg<+>{jx~x|04K`^V5X7 zTc+c4%sGk?KV!BxQEq|87IJ=MYrlqUx@*fz-%Wj^stWl==yp!_Ju&+_{SI_FCnu0k za`L&^5a;9*9Y5k^C-#I3ribGUN_%7GjP?twfV4Gj7;%DY&{Mjf2q(9X&gP^W93?q< zU^?c(C#Z}#nY4Y0FSkj&niUOEXurUswmEj*W&9C%ERkq;o2qJpBG_PWFPMBqxjk z>RJMy01+p_))m`oG=`A#BU>x`Yx~Quc5UfRRfCou=ypz$_j8M(%Q+dscu7u{m<@4G z3U&O5lR=bkpr3Oxj66Qwo*os%y_f0OZWwWbYs?YVC7dk$!E8>Bf}G9c|oIRGy31fhMuZ2&5h?5HCK{#pE zbv26<+Sc5;tvj_`*UmBMc23&R7GFDQ=yFabFfL`o(SfET`_-uwJ;6EONyw5p9KY$m(7a-#MnC)F`8`U+7oF96h^J)*?0KBxNuRYZt{g%`MjG??| zbDpESq?Y|nnJp-20Ld?0P+-b)l+%U8%} zf4bBU;pUwav#+yPf~z<;>mfU2IzGf&Mls^%g6(0{KdEaMIX|+s_!+(MmvQRv?ZPLd z+T=btR6xJ-ayW;V524R_*^PXXmyhDU$oJ2l4ezQA5igry#}U)RakA2WKQo~H!eSup zecDjt5xn3!d!PCsyuAOb=!ZSzF#59pI`9LOuO|>WbUB4wL z)3y^*e>5`$q)v``o^{#lnM=}!&%XfKcY|TX7p}1{=-woJ-G2+uOTpJE^kx4&;4jV> z+zQ3$&$h+z4-oP7!1g${RcnkP=SQ{{ra2u*#d*SGa|VyV5_{gS+POKs_H zOSN}(ZR_dV)|cASpX%>yPqlZqg^3@jU&-_1D$I#<$nQcVIb36Q#yMQ7^B8fs)%uK! z?NjL@(@75bT@L(`X~z5thpaEpee^Nn5ZB@NRIhNjW)SZf!C@AC*zX!RjdR$9y7lPK zw$0!d5OG*%Ym;p|H6D@kL$4nidUy1sUg+G?w&V419=bKga-Ro({BQBuLWq*1Hl+Sf zbWE5gc3n3cI#G|RI!#$(bR7S|pRJ`$Np6puY@FM@YJ0SX%V7T|)Ok!cNc+)DllBXr zn2t{kBR+B6-l)C{pG&}J1&qE5t4hSO`FNwxcij4Zo4dh(h=>O$IybWg_7_tEcx zCsgIT?`LeSljm{lW%17zW9DkT-)O8~k;XeD{LuOMHskn({@xwmZ4Y&@^-=S|{l(+; z2=YlzKZCurC(h|dI)21y4{SYWdN|H8Y2TF@)P7+FkUCZwMx5fh{{&>wB%IzniMfH3 z6R03}3&1G(WRAbFL)&mX5s%wJR1`r6^_Rj=Koqz4RJUBuRB243*^g}P&m;Rho=I%$ z*k-2?UHr1`zYFGcZp04&3&pUl7{I+AN zt^Ji9-F-Mc^F94D&{-@t(;~H={3iE_t(Z6Gy%_l?sC+Ds7Yr9k7mvQox>wi z*O)#bi02JhFMX*{C0 zANovogWcaQUnz8jX6S!Z%sqrY=j#me?zi)mk2?Ak=j*oG5bcTkuoqo5JsjtpwucLb zYk}0U&M@Ky_b%skACqU{-PV2_$UV`=wR@Lv$lBw!oXn|P5ek%-r&7>ShF_M`or!xY zwR+r^No=B+cLV>`AXOsaiC((GkoiZYofu;`(!0Nl=h5g-4x5nn|GN4b{=@Y2v(G+T ziz_kc;O8;;>)x0j$Rs)L00-`?{MPmI6=9wF5XEFeOpZGEn@%N$tAQSq6;hWq>>N=FqR-CdmISwCMk?2zul5&B}mmK>bJAmZpY?jeoV)Tl}Z%jH{ z+lzME%Q4E3PxA2^Y)|sBS#?HyG{@wq1E2QIlnbWnn}E(ojnv`dO7KGFL(~0nd<@~E zDy&vpg^vrG&l`u0tw@i<#}UXLLvlV?h6>?h1bY|qu_If%7wyhRCGtr=_QUohAKj`m z;-f7lM;+v&S};}L0(3qar4IYQ;MokXL#X?9d<^n|-`Z(XTZNAy&CiLCQ;!l59CeV72EkN)7ts0GBz2f$!NJTO z(|tcahVW4nTGUqIhMG51torZmZu7bUr$z4!fS<(M&#&y5||n-Ya~39e)c#Y!yBxG(RUkh9EnPg^Z8bjLR-F+a zr($x{K|cBgQ}ri+&c}e%;Tm6XZ{}$r`B-K+!^d;s;2E)1_$W}j=fuYZ^xQ&nK3H}> z%FgEU@gdrskKM>8`S>VqtMT!!>Wuif5R;=0^07}aReuiXd>oTHy#EpG$*cg9k5z^< zd{l=M%7O4vqIS=TkGs%w56Ssp+4-o%^#}5?BU^hD?as$R_&{|=d<@0p zsDpeQ5lq!z0XiRNqz>10f-RXekbFFEIK#)Y;hb_He3Yx*bK;`_vPDSF2g?{z&M&La z5;r9c>m{jgQaN)`*XZm>hMG zj|+mS`Wry!V_53&4nVLvvlU3)+YM*XLjsR9hoH?#AS(gM17Lrs^kw&c~S4;kkgeWqN_sy~}WhkE(D(?G`?o z)$Td*Q4QG|B~H=Bv(#j{=lkjylN4b-`5q1EBM9 zOX_g{D_ES_3#9G?hK0!Y@%jVr876fM;iFCMo)aGpkZnYAK3H}>cAwAXV+Gosj~g++ z{GNFw>XLjcQJoPVB`7-|)ImNb1XK0JroR|Q+><(FM1Cs$rs+Oz81aD{*atd>@X@b! z&xwy_$ZkS%K3H}>4*t_zKGJA+J|<&+IUj3Lm*nFa)fw?ojL4E_f~oq|K|+ zF*)iWALW90{sDA8YNQU&KeR3Vq3QnEFyaHx##gJY!p8;8&xwyCkUfUve6Z|%jQq1) zKK7#B`KUxb$;W=!p5&ujbw+%&#pI}id{hhK`3KPXXp}lU{}3FkziGN}8%BKKxp$M= zDtruSeolOxg6tV2=YwVEVXx2Ubc$92umiH{4Ay@cd^uL4G@hP#2zN2k=``G;XXkhF8*GslPAv&8{tdeW(iTleBkxP93g)Rlo4va7j6825J4!QHfNARDtwWM>%9G zkeqLpAtU!kWyt&Qy%ClBg?NWU3-#dhbH3B*MB+Q20jZuZ&%7mzzI+Ev368<1?0lce z8p`syJGls$*W*9!ZN1tjb1lg)NUW2WKFA4$0?$ zWvGxj7{8o52c-{iO^Ob*xfAL9L-pU~Z^h)#ppD#Ws;m1R{@$G@{!UdEeUp5C4BwM{ zUQ?YBpO<2C)ImN649^3d&l6IIcc6mPnUz55USk;XiF3>#d_|M+x#(Xi2Q_ox^B(j( zKyp4Pjx39Y4lr;L(AxnMEp&qPIY<8)&cV>Vhtf;S_2$h8fWs!=Vn9NLga+ z$kyJ1T$1NB>XJM!Q@>~ z`8+z>DMpbU3`cc0Ky4-KS z7VNeD@pb;9jv4W_J|;&URAEjQ^?Jn8}aESl zO-p{4`Mf;t0e;ZWF7#Q5@tMb;QVxA@^!2yDvg)UPYTGMYUT*LEsh2w2@ma*)pIpB3 zC-D)PZ9PBP^o^RPZ>)Hxr~Bp50a@}$8y6s-oTu%kcgFv!ut9B#=BY6zM;)A}MTT`i zpQlQx!<%Qp>CAhk`vXIMcDWCK(u*-xbyfTpKF?@g;(icqkHhBxWDg=apDaU#@Yy}_ z#e7~tpRKTQE__~utn=B5e3H-0rZ>*#N%VtF5ue9ma@0XSI}Hy5ozLA;htIVNp3Rg3 zY4Z|8=M&p*$o^2q5I*l|eolOjLG}ic^U1RF`R>1GJ~uv#uT4+CV>Uf*d=^61`8-Q9-HC${A<3cENg+Z+E! z$IiD;;rNrm_-(xe za?ip(Y>(T!#Oh?<=X(J>lg+ArjepJO@%ru(WFKcg8H4N%B%iPA>RTZ{BLyC!yNf4r zMl6)8x~EW!SBhiP;9|1>@QzS;V?^SA`EK99q&CpnK#qmE<5IhY3-R7LX` zPIjF-oI!|P~!9G>eS z+koUeuU5U$Z&~p(_JwjKQjEFR-?Adhw84*J$rR%ARQ|i`hMG(=&#nK6s#kvFz4J%vs?R zesq6VttV_LwVN;&5x3xV*i(?|>NrmNQ}I*ub6>zwkA8JqrN95Swl>WCul4yD$LH=G z#`ih!{iP6fN&j0!Z;(Iahb>x4`oul2-j_ZFz>fjt5vGP(N z$9~4p=ZNQs!_WzkIXe9CtN#|lRqZca-TNTP%gpy;ZpHeg{@2_&%3>^xTNh8Xu?jk0 z(>9dkz+A{$;dHU97UO9zh&rr^ZXIO1^$1MakxOPNc;)(BDuD0&BOQY(8e_~fRtP# zbt*6X@5FjVm)J4Ge`l=UAo}I55q?9;*N9vX)p4(4%+vPaKc?=&P>x>QYrpC^jqx}i z*m}cy1h>z8Z+wG(aa|JGb^w)VX0l6fi+U3qy+Aw&Kvkw(#; z&t2{vYS0jY1Vq40>GX2)y%Frj*j zG{*JYH+;v!{g!s&Yh&{Fk=G-a^gX5iitn4S_N06CU$*v~>aL?rMfgXZw2}MwGRQgy z*U>M@!852!a!{({MI3CfKBF=Yu5WOS03y>gvcfO8)W zthTjXjlw>pIwTx*^8c6oM~*hhxq{~{J!{eX_<1W| z%wBOM)^7y;_;*1(1)NX!qTP9|L_W#ue)yW?wOe&Y*N$y5IqHC&>1x5LbPLcqZj?Ig zA%Y)fZkq1fhR!iLY*L?u<4!%7=9R$tBWPoL8xpyL4|M`x@*jOV2YvVd0Ji1l=R1ez zbqwL~*8iUS8}{Chdl6JfzXc!Va`+zFox>jFlN`Pef0G=(qdFrF&&1@YgB{BFrz-$IXcDeg|C>i&YgfklwV|KKB$08s812sK5d27Tx&(DeP776M#OZ^W z9CeV>5y7eSU7&M1E_GPfg2kC#KO92}E;OnzqVACC1KM!zJ-d(iG2S0JC{crScS za=cx2MjW@sVXFN!H=yWn9U6SoD7=hez^p&PL+7wF;>M>8p-=uHjv;FUKkLr5(WZNW@bk2_uLCBoW7p#kTH#|qFZAOV zO2YHjFK+ARWF0PSuBvWXA2m00efV|Fmd ztl_fhzGf)%ho4F2wPX3elQD$j6PlmXJBrUPY;2Lajp1Y7w^86m`nzc?q!rSNv^CF&4#pI}iJog)tBjTv%jIGuSKNZrc} zoo8Mj9{gm6=Ys!J@_GCnkuzv}oLHWKo?A%HGs{pRzl~8G%8@0@DY*)8UQua>@=p7I zXQZk2%lAQk{w&l|ke{1c44a+TA&i;ibqVT{ycVjT5w8zoa@0XyM-1-*o!4=x!}X8g z*~~5=b?-HFUU^M9p<_t=wu~#Ub9u+M8nSgr&MV7MFYnJ5*qtM? zhwk@#F|PAmiF}gh{qQ%*bGO;5vyVMFCPy9Qx!RB%InRw!hx;GFWtp3%`?jI;%{FYaWnTh?3-wJK2{^2c!P+J<<;~rRrIy`v>dgS%9>$PUWO8+NAx5t`mLJ zc%z8KV+!cy!StTrotf(jrDB zcKTYcKt5;w!6WU9))41keVdto_*Z9oWd5Zc^RNBFd#2+9L!W=1ODeTbwD$CGxcSpW z+PFy89=yosyKO#GW!%u_CM4%^XYBpk`QOXk({5m`Va3+bKYe!bBz&@G7g&pT@LZ?? zEwrEh@^C&mhOVipAAb2-ocf{98~TCc_?jC`--eCO^%3NgTz>{Gl3ahJennjOz_)Xz zhqfNm_An?|oLK>+j#Y-vHP0s}APbPSw&(wzo<0x98j)I2NnMQLP9*wE9V6ObVz}Tx zJbJC&jrFTUKdv?Y4Q>9WTEKl+R@$%DQs+9XAr7$>2;`fPbS+)2;~30$qAxk%?+z`(dgDCN-V#}RI;Ty) zuWOTk!y3<`WG~J~yK{FAI+ENKqKTehnPt~T#Z|-X@M;-M0oZ)q#`+ZgFiZjE42Qx1M>GxJc_ZyGR z!jRf6eoy^~It!kBfco7?^qKo8InccRHhddosN;7=M%I+TSLa}X@TMFz^4a^WW9EEa z6YJL_{j#-V|K{7Z+C5vl6z%k-5@{4;xzAk7TdHvXhq`2p@OlEx5m!YhyBu|pt1-i1 z`Z=$YQdgY0Yq%ds-G>aFE4~Yx(lLaq*FICOhEY#`;xV!h{iuWAKdOd*&QFzcA~Dhl z`S0QWkL$w!zWWliyWhhx``z!SO>aC#=Bdue?|`!VO&#=m)bJjV`f2Ng)D>qY1@Zn1 zNSpT?y5D?;aZAS#zq@76exhIq^$keW#p^2KHONwb1$=X#%Y`S6i58KaeQmT5?e6oH znChM`=OM#Mp!+-~bs>xkewgV6Qui)H_nFrmH`H$Nx%UsL z^NE6C)HfoLU!3#hdXj!$hCJ;{!9VxAEOu^do|@SY{F-zg+THI97|Z9({hp7yq~D*b zO_AS|F*)j>-&YJLfbRFG)Zz0wf(J9(fz;h==zjAW?Yi1+?>j2weFv|d^!M!ew^dW< z#iQJEPZZokX(y8I5vlqHvwd$@iP9n6b|-D+B+ZP&8h>bJ*wYgvpg1?)Z*{rm`_=3=H>GOGApfk4kIo2 z5209w|G2I>##=!C_HhWml$pR7J`dwMp3K9dyW}W-%*1HXA4!|^t3$uvj^=ADCPy8duQP_DKws-uqz>nG!QzIEK>E1ZQ05CiX~KQ|s@g3v zy7oU(XI}md)VCmU?kkbXkv@PdZRWic>-oe)pEL9KCNHGPjdNC_JP8*~qI33spO1Fu z;vB|La#4sn+8gcnnD@`r&WMYNm>hMGiwlM~fX>CR)ZrRJ5Z8Y|>fUbXTyVc1QM-kU z4WBC)BdBjeqR)&M?rEn@_HEG_YD?gw`@TT@)jh2dI{X^Y_xy)3-_IbQ^qt%!eZQ@C zMZS;6y6;0$=YAc`Y(|;7I}F`-?)k%NxA?y9KdJ9ysNaM{-z$((NFPDg z4TlNPjmQy1(47ud3a0AGycc z&jK0il%dbjeqG=D5{lup^^4njIavqgi&S^)SG%@wlX2l@v4w`E#cg@ z$vh~xC;nJ?Mynq)d+@G(8-r zP1^G_U4ku{hpq!<`r!QWzLVd102Fate$G}c*3a3FppN@=4HD&?j}s~*v3gx%AC1w$ z&(+TtJr>h->O0@kKc(ves@{eU_nY5^Nc#Pb*&6rzxQ-ir2J<89(=7Fkncb$7Wud3Y)pSpMm-9zx(`M4?^=zew&>nVA?@;2I?4}NAf$;UfpYn+ec zsx#u_a7<1=_d1+zl|IAi0ig5IuX16x;MvT4AbBV?^jP5aiuH~7(Dw++2b!@${5?Wm z{xHgoNaURMbG%{rI)dapu#DE`NcW)RdHh}UqkJV&C-O-?u9>ZIK5Uc^&Ihks24p>ir&7qz{tbFm2phzi-=MF8 z;{&LnAB->JR~?vYjL(0%$IwRZn>;I$6CB1LBT@NFUib0uHx!Eo>5u=4@Kt|Sc2t@NK?>2FC>U3SB!q0GeOt3M--JX7^2mD{pKR9(U4oq5o zVEMS&aMIAZ=QR}f(HIA^-_zQ87=dv>8}z=3BkFyVUtiffY`GuqL+>J!Vb6FB{Xd?v BocI6$ literal 0 HcmV?d00001 diff --git a/fixtures/test-grida/README.md b/fixtures/test-grida/README.md index 6cc392e29a..c2208347ab 100644 --- a/fixtures/test-grida/README.md +++ b/fixtures/test-grida/README.md @@ -4,15 +4,18 @@ This directory contains **meaningful** `.grida` files used for **testing**. ### File formats -- **`.grida` files**: Modern format using ZIP/FlatBuffer binary format. These are the current production format. +- **`.grida` files**: Modern format. Two sub-variants: + - **ZIP/FlatBuffer**: Production format. ZIP archive containing a FlatBuffers binary with "GRID" magic header. + - **Raw FlatBuffer**: Bare FlatBuffers binary (no ZIP wrapper). Used for Rust decoder unit tests (`crates/grida-canvas/src/io/io_grida_fbs.rs`). - **`.grida1.zip` files**: Legacy/test-only format containing JSON snapshots in a ZIP archive. Used for internal testing and fixtures. Not part of the public `.grida` file format specification. ### Naming convention - **Prefix**: `d[n]` is a simple counter (`d1`, `d2`, `d3`, ...). - **Schema version specifier**: we encode the schema version **build metadata date** as `yyyymmdd`. - - Example: schema version `0.90.0-beta+20260108` → version specifier `20260108` + - Example: schema version `0.91.0-beta+20260311` → version specifier `20260311` - **Note**: this `yyyymmdd` is **not** the authoring date of the file. +- **`basic.grida`**: Raw FlatBuffer fixture for Rust decoder tests. Exercises every node type, paint variant, effect, stroke option, mask type, and layout configuration in one scene. In-memory round-trip coverage is in `cargo test --package cg --test fbs_roundtrip`. No version suffix (regenerated as needed). ### Support expectations (important) @@ -22,10 +25,16 @@ This directory contains **meaningful** `.grida` files used for **testing**. ### Changelog +- **0.91.0-beta+20260311** ⚠️ Breaking FBS schema change: + - Added `NodeSlot` wrapper table in `grida.fbs` to work around a `flatc --rust` limitation (Rust codegen does not support `[Node]` — vectors of unions). + - `CanvasDocument.nodes` field type changed: `[Node]` → `[NodeSlot]`. + - `CanvasDocument` field IDs reindexed as a result: `nodes` moved to `id:1`, `scenes` moved to `id:2`. + - Added Rust FlatBuffers decoder: `crates/grida-canvas/src/io/io_grida_fbs.rs`. + - **0.90.0-beta+20260108**: Migrated from legacy `.grida` (JSON/ZIP) format to new `.grida` (ZIP/FlatBuffer) binary format. Legacy snapshot files are now stored as `.grida1.zip` for test fixtures.
-`grida1` `0.89.0` -> `0.90.0` +grida1 0.89.00.90.0 field renames | `0.89.0` | `0.90.0` | | ---------------------- | ----------------------------- | @@ -51,4 +60,4 @@ This directory contains **meaningful** `.grida` files used for **testing**. --- -> Current Version: `0.90.0-beta+20260108` (last updated: 2026-01-08) +> Current Version: `0.91.0-beta+20260311` (last updated: 2026-03-11) From 58b6068fc4ac54aa6a29666dfc8c54409fe04a13 Mon Sep 17 00:00:00 2001 From: Universe Date: Thu, 12 Mar 2026 18:57:49 +0900 Subject: [PATCH 14/16] Implement OPFS data quarantine mechanism - Enhanced error handling in the CanvasPlayground component to quarantine stale files when document data becomes unreadable, preserving user data for potential future migration. - Introduced a new `quarantine` method in the grida-canvas-io package to move document files into a `_quarantine` directory, ensuring that original files are removed to prevent data loss after schema changes. - Improved documentation within the quarantine method to clarify its purpose and usage. These changes improve data integrity and user experience by safeguarding against data corruption. --- .../playground/playground.tsx | 18 +++- packages/grida-canvas-io/index.ts | 82 +++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/editor/grida-canvas-hosted/playground/playground.tsx b/editor/grida-canvas-hosted/playground/playground.tsx index b71c792ecd..862624c761 100644 --- a/editor/grida-canvas-hosted/playground/playground.tsx +++ b/editor/grida-canvas-hosted/playground/playground.tsx @@ -497,11 +497,25 @@ export default function CanvasPlayground({ } } } catch (error) { - // File not found or other error - continue to fallback if (error instanceof Error && error.message.includes("not found")) { // File doesn't exist yet - this is fine, continue to fallback } else { - console.error("Failed to load from OPFS:", error); + // Decode failure (likely a schema change) or other corruption. + // Quarantine the stale files so the user's data is preserved + // for possible future migration, then fall through to the + // default document. + console.warn( + "OPFS document unreadable (possible schema change), quarantining:", + error + ); + try { + await opfs?.quarantine(); + } catch (quarantineError) { + console.error( + "Failed to quarantine stale OPFS data:", + quarantineError + ); + } } } diff --git a/packages/grida-canvas-io/index.ts b/packages/grida-canvas-io/index.ts index 2531c9218f..1a13509f9c 100644 --- a/packages/grida-canvas-io/index.ts +++ b/packages/grida-canvas-io/index.ts @@ -1434,6 +1434,88 @@ export namespace io { const file = await fileHandle.getFile(); return new Uint8Array(await file.arrayBuffer()); } + + /** + * Quarantines all document files into `_quarantine/