git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit754ecf7208208ee32f81ec9e05b1efd53ace6a23
parentd5b22a5aee
authorLucas Galante <[email protected]>
date2026-09-14 08:57
feat(relief): Prim::Lattice — a periodic well field carved as one surface

A grid drawn as one Recess ring per cell is N free overlays: at every
crossing four rounded rings stack in colour space and read as several
overlapping effects, and the ring walls (straddling a boundary inflated
by the roll) overlap each other down the rail centre once the roll
exceeds a quarter gap. Shader mode 13 folds the pixel into the period
and measures the nearest cell's edge outward — the union distance of
every well — in ONE profile evaluation, so rails and crossings are true
mitres, and the whole lattice costs one draw with no feature budget.
Mirrored in the height-field export, with a test pinning the rail
centre and the crossing at the plateau.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

 src/backend/window_runner.rs | 26 +++++++++++++++++++-
 src/scene/heightfield.rs     | 56 ++++++++++++++++++++++++++++++++++++++++++--
 src/scene/paint.rs           | 45 ++++++++++++++++++++++++++++++++---
 src/vk/shader2d.wgsl         | 20 ++++++++++++++++
 4 files changed, 141 insertions(+), 6 deletions(-)

diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 7797c3a..a1d69f7 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -1712,7 +1712,7 @@ fn prim_kind(p: &crate::scene::paint::Prim) -> &'static str {
         P::Sphere { .. } => "Sphere", P::Droplet { .. } => "Droplet",
         P::DropletScrim { .. } => "DropletScrim",
         P::ConcaveFillet { .. } => "ConcaveFillet",
-        P::Groove { .. } => "Groove", P::Glow { .. } => "Glow",
+        P::Groove { .. } => "Groove", P::Lattice { .. } => "Lattice", P::Glow { .. } => "Glow",
         P::Text { .. } => "Text", P::Image { .. } => "Image",
     }
 }
@@ -2542,6 +2542,30 @@ pub fn tessellate_display_list(
                     );
                 }
             }
+            Prim::Lattice { rect, period, origin, cell, radius, depth } if shader_plates => {
+                // A periodic well field (shader mode 13): one cover quad over
+                // `rect`; the shader folds each pixel into the period and
+                // measures the nearest cell, so the whole lattice is a single
+                // evaluation. p_rect = one cell's centre + half-extents,
+                // p_radii = the corner radius, p_host.xy = the period; the
+                // host-box fade sides are pushed far out (a lattice never
+                // fades against a host — its own rect bounds it).
+                let (pw, ph) = (period.0.max(1e-3), period.1.max(1e-3));
+                verts.extend(quad_vertices(rect.x, rect.y, rect.width, rect.height, sw, sh, [0.0; 4]));
+                plate = Some(crate::vk::PlatePush {
+                    rect: [origin.0 * scale, origin.1 * scale, cell.0 * 0.5 * scale, cell.1 * 0.5 * scale],
+                    radii: [*radius * scale; 4],
+                    light: [plate_light[0], plate_light[1], plate_light[2], *depth * scale],
+                    material: plate_mat,
+                    host: [pw * scale, ph * scale, 1e6, 1e6],
+                    specular_tint: [1.0, 1.0, 1.0, 0.0],
+                    mode: 13.0,
+                    shape: crate::layout::corner_shape(),
+                });
+            }
+            // Legacy banded path: no periodic wall — the lattice draws nothing
+            // there, like the fillet (A/B comparison path only).
+            Prim::Lattice { .. } => {}
         }
         let end = verts.len() as u32;
         if end == start {
diff --git a/src/scene/heightfield.rs b/src/scene/heightfield.rs
index 347284b..3fddd99 100644
--- a/src/scene/heightfield.rs
+++ b/src/scene/heightfield.rs
@@ -217,6 +217,7 @@ const MODE_FILLET_UP: i32 = 7;
 const MODE_GROOVE: i32 = 8;
 const MODE_TROUGH: i32 = 9;
 const MODE_ROLL: i32 = 11;
+const MODE_LATTICE: i32 = 13;
 
 impl HeightField {
     /// Sample one frame's plate batches, in draw order, over a `width` ×
@@ -237,7 +238,7 @@ impl HeightField {
         for b in batches {
             let Some(p) = b.plate else { continue };
             let mode = p.mode.round() as i32;
-            if !matches!(mode, 1..=9 | 11) {
+            if !matches!(mode, 1..=9 | 11 | 13) {
                 continue;
             }
             let shape = p.shape.clamp(2.0, 16.0);
@@ -253,7 +254,10 @@ impl HeightField {
                     let r = p.rect[2] + t;
                     (p.rect[0] - r, p.rect[1] - r, p.rect[0] + r, p.rect[1] + r)
                 }
-                MODE_GROOVE => (0.0, 0.0, width as f32, height as f32),
+                // A lattice's shading is bounded by its cover quad, which the
+                // batch does not record; the one consumer (cce-grid) covers
+                // its whole surface, so the window is the honest bound.
+                MODE_GROOVE | MODE_LATTICE => (0.0, 0.0, width as f32, height as f32),
                 _ => (
                     p.rect[0] - p.rect[2] - t - 2.0,
                     p.rect[1] - p.rect[3] - t - 2.0,
@@ -330,6 +334,19 @@ impl HeightField {
                             let u = (fd / t + 0.5).clamp(0.0, 1.0);
                             -carve_drop * (1.0 - prof.carve_height(u))
                         }
+                        MODE_LATTICE => {
+                            // Fold into the period about one cell's centre and
+                            // measure that cell — the union distance of every
+                            // well (see the shader's MODE_LATTICE). Positive
+                            // outside the cell; the wall runs from the edge
+                            // outward over t, floor at the edge.
+                            let (px, py) = (p.host[0].max(1e-3), p.host[1].max(1e-3));
+                            let c = (pt.0 - p.rect[0], pt.1 - p.rect[1]);
+                            let c = (c.0 - px * (c.0 / px).round(), c.1 - py * (c.1 / py).round());
+                            let d_out = rr_sdf(c, [0.0, 0.0, p.rect[2], p.rect[3]], p.radii, shape, t);
+                            let u = (1.0 - d_out / t).clamp(0.0, 1.0);
+                            -carve_drop * prof.carve_height(u)
+                        }
                         MODE_FILLET_DOWN | MODE_FILLET_UP => {
                             let (cx, cy) = (pt.0 - p.rect[0], pt.1 - p.rect[1]);
                             let dist = (cx * cx + cy * cy).sqrt().max(1e-4);
@@ -509,6 +526,41 @@ mod tests {
         assert_eq!(hf.px[5 * 100 + 5], 0.0);
     }
 
+    #[test]
+    fn a_lattice_is_one_surface_with_plateau_rails_and_mitred_crossings() {
+        // Period 50, cells 30 wide (gap 20), wall 10 = the half-gap, one
+        // cell centred at (25, 25). p_rect = cell centre + half-extents,
+        // p_host.xy = period, radii 4 (the fixture's).
+        let mut b = plate([25.0, 25.0, 15.0, 15.0], 10.0, [50.0, 50.0, 1e6, 1e6]);
+        b.plate.as_mut().unwrap().mode = 13.0;
+        let hf = HeightField::from_frame(&[b], &[], 100, 100, 1.0);
+        let at = |x: usize, y: usize| hf.px[y * 100 + x];
+        let drop = RECESS_DEPTH * 10.0;
+        // Every cell floor, not just the one the push names: (25,25) and
+        // its period neighbour (75,75).
+        assert!((at(25, 25) + drop).abs() < 1e-3, "floor {}", at(25, 25));
+        assert!((at(75, 75) + drop).abs() < 1e-3, "neighbour floor {}", at(75, 75));
+        // The rail centre line between two cells is exactly one run from
+        // either edge: plateau, not a doubled wall. Pixel centres straddle
+        // the line by half a px (49.5 and 50.5 are each 9.5 from a cell), so
+        // the two samples sit a hair into opposite walls — equal, and within
+        // the wall's first half-px of drop.
+        assert!((at(49, 25) - at(50, 25)).abs() < 1e-3, "rail centre symmetric {} {}", at(49, 25), at(50, 25));
+        assert!(at(50, 25) > -0.1 && at(50, 25) <= 0.0, "rail centre {}", at(50, 25));
+        assert!(at(50, 25) > at(45, 25), "rail centre above the wall");
+        // The crossing where four cells meet is farther from every cell than
+        // the wall runs: plateau — the mitre the per-cell rings never gave.
+        assert!(at(50, 50).abs() < 1e-3, "crossing {}", at(50, 50));
+        // Halfway out the wall is between floor and plateau, on both sides
+        // of the rail (one wall from each cell, symmetric). Pixel centres:
+        // 45.5 is 5.5 past the first cell's edge at 40, 54.5 is 5.5 before
+        // the neighbour's edge at 60.
+        let w1 = at(45, 25);
+        let w2 = at(54, 25);
+        assert!(w1 < 0.0 && w1 > -drop, "wall {w1}");
+        assert!((w1 - w2).abs() < 1e-3, "walls symmetric {w1} {w2}");
+    }
+
     #[test]
     fn resample_keeps_the_face_height() {
         let b = plate([50.0, 50.0, 40.0, 40.0], 8.0, [0.0; 4]);
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index 98e6e27..aef49cf 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -124,14 +124,15 @@ impl PlateSpec {
 /// The **relief primitives** are the members of this enum that describe a lit
 /// surface rather than a flat fill: [`Prim::Bevel`], [`Prim::Plate`],
 /// [`Prim::Recess`], [`Prim::Boss`], [`Prim::Ridge`], [`Prim::ConcaveFillet`],
-/// [`Prim::Groove`] and [`Prim::Sphere`]. They share one lighting model — the
+/// [`Prim::Groove`], [`Prim::Lattice`] and [`Prim::Sphere`]. They share one lighting model — the
 /// DE's light vector, roll width and profile, per-pixel through shader2d's
 /// SDF branch (see `crate::layout::bevel_shader`) — and split in two:
 ///
 /// - **plates** carry their own fill: `Bevel`, `Plate`. Shader mode 1.
 /// - **carves** emit shading ONLY, no fill, over whatever is already painted
-///   beneath: `Recess`, `Boss`, `Ridge`, `ConcaveFillet`, `Groove`. Modes 2-4
-///   and 6-8. (`Sphere`, mode 5, is neither — a lit ball under the same model.)
+///   beneath: `Recess`, `Boss`, `Ridge`, `ConcaveFillet`, `Groove`, `Lattice`.
+///   Modes 2-4, 6-8 and 13. (`Sphere`, mode 5, is neither — a lit ball under
+///   the same model.)
 ///
 /// That split is load-bearing for flat-path hosts, which need one list for the
 /// faces and another for the edges drawn over them (cce-files' `rects` vs
@@ -579,6 +580,24 @@ pub enum Prim {
     /// plate dies into the plate's own rolled edge instead of ending on a hard line.
     /// SDF path only — the legacy banded tessellation draws nothing (like `Ridge`).
     Groove { a: (f32, f32), b: (f32, f32), width: f32, depth: f32, host: Rect },
+    /// A periodic field of identical rounded-box wells — every cell of a grid
+    /// carved into whatever is painted beneath, as ONE surface. The wells
+    /// repeat every `period` (x, y) with one cell centred at `origin`, each
+    /// `cell` (w, h) big with `radius` corners; the wall runs from the cell
+    /// edge OUTWARD over `depth` px (floor at the edge, plateau one run out),
+    /// so a rail between two cells carries one wall from each side and the
+    /// rail face is whatever the runs leave. Shading lands only inside `rect`.
+    ///
+    /// This exists because a lattice drawn as one [`Prim::Recess`] per cell is
+    /// N independent overlays: where four rounded rings meet at a crossing
+    /// their shadings stack in colour space and read as overlapping effects,
+    /// not a junction. Here the pixel is folded into the period and the
+    /// distance is to the NEAREST cell — the union of every well — evaluated
+    /// once, so the rail centre lines and the diagonals at each crossing are
+    /// true mitres, and the cost is one draw regardless of how many cells the
+    /// surface holds (a free carve per cell also runs into the per-frame
+    /// feature budget long before a zoomed-out grid does). SDF path only.
+    Lattice { rect: Rect, period: (f32, f32), origin: (f32, f32), cell: (f32, f32), radius: f32, depth: f32 },
     /// Text in sRGB u8 (the `TextLabel` convention). `font` is a font string for
     /// `get_text_buffer` (family, or "family:size"); `bounds` is a logical `[l, t, r, b]` clip
     /// for the glyph pass (Phase 6: the backend renders these through the glyph pass when the app
@@ -961,6 +980,9 @@ impl PaintCtx {
                 self.concave_fillet(cx, cy, radius, depth, start, raised)
             }
             Prim::Groove { a, b, width, depth, host } => self.groove(a, b, width, depth, host),
+            Prim::Lattice { rect, period, origin, cell, radius, depth } => {
+                self.lattice(rect, period, origin, cell, radius, depth)
+            }
             Prim::Image { image, rect, alpha } => self.image(image, rect, alpha),
         }
         None
@@ -979,6 +1001,23 @@ impl PaintCtx {
         });
     }
 
+    /// A periodic field of rounded wells carved as one surface — see
+    /// [`Prim::Lattice`]. `origin` is any one cell's centre; `rect` bounds the
+    /// shading. All logical px, like every other carve.
+    pub fn lattice(
+        &mut self,
+        rect: Rect,
+        period: (f32, f32),
+        origin: (f32, f32),
+        cell: (f32, f32),
+        radius: f32,
+        depth: f32,
+    ) {
+        let (ox, oy) = self.offset;
+        let rect = self.apply_offset(rect);
+        self.push(Prim::Lattice { rect, period, origin: (origin.0 + ox, origin.1 + oy), cell, radius, depth });
+    }
+
     pub fn border(&mut self, rect: Rect, radii: Radii, fill: [f32; 4], border: [f32; 4], thickness: f32) {
         let rect = self.apply_offset(rect);
         self.push(Prim::Border { rect, radii, fill, border, thickness });
diff --git a/src/vk/shader2d.wgsl b/src/vk/shader2d.wgsl
index 7f7e2da..63ac75e 100644
--- a/src/vk/shader2d.wgsl
+++ b/src/vk/shader2d.wgsl
@@ -139,6 +139,7 @@ const MODE_TROUGH: i32 = 9;       // sunken valley straddling the boundary
 const MODE_DROPLET: i32 = 10;     // hanging water droplet clinging to the box top
 const MODE_ROLL: i32 = 11;        // fill-less rolled perimeter, composited as an overlay
 const MODE_DROPLET_SCRIM: i32 = 12; // flat feathered fill of the droplet silhouette
+const MODE_LATTICE: i32 = 13;     // periodic well field: nearest-cell carve, one evaluation
 // Fillet modes rejoin the shared free-carve path as their flat equivalents.
 const FILLET_TO_STEP: i32 = 4;    // 6 -> RECESS, 7 -> BOSS
 
@@ -674,6 +675,25 @@ fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
         fd = abs(s) - rrect_clip.p_rect.z;
         fgd = nrm * select(-1.0, 1.0, s >= 0.0);
         eff = MODE_RECESS;
+    } else if (mode == MODE_LATTICE) {
+        // MODE_LATTICE: a periodic field of identical rounded wells. Fold
+        // the pixel into the period about one cell's centre (p_rect.xy;
+        // period in p_host.xy) and take the box distance to THAT cell —
+        // identical axis-aligned boxes centred in their period cells, so
+        // the folded cell is always the nearest one and this is the exact
+        // union distance of every well. One profile evaluation, so the
+        // rails between cells and the diagonals at each crossing are true
+        // mitres instead of stacked per-cell overlays. The wall runs from
+        // the cell edge OUTWARD: floor at the edge, plateau one run out —
+        // the +t/2 shifts the shared path's boundary-straddling band so it
+        // spans [edge, edge + t]. Rejoins the free-carve path as a recess.
+        let per = max(rrect_clip.p_host.xy, vec2f(1e-3));
+        var c = frag - rrect_clip.p_rect.xy;
+        c = c - per * round(c / per);
+        let lg = rr_sdf_grad(c, vec4f(0.0, 0.0, rrect_clip.p_rect.zw), rrect_clip.p_radii);
+        fd = -lg.z + 0.5 * t;
+        fgd = lg.xy;
+        eff = MODE_RECESS;
     } else if (mode == MODE_FILLET_DOWN || mode == MODE_FILLET_UP) {
         eff = mode - FILLET_TO_STEP;
         let c = frag - rrect_clip.p_rect.xy;