git.lucas.co / cce-grid
desktop grid client
git clone https://git.lucas.co/cce-grid.git

commit301735b9d2bb2f8d30f7bbbd1cd140dd06712e27
parentb9530b1f39
authorLucas Galante <[email protected]>
date2026-09-14 08:57
feat: draw the line relief as one Prim::Lattice, not a ring per cell

The per-cell recess rings stacked in colour space at every crossing and
overlapped each other along the rail centre. cce-ui's lattice carve is a
single evaluation over the nearest cell, so junctions are mitres and the
wall runs from each cell edge outward over the roll as documented.

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

 CLAUDE.md   | 19 ++++++++++++-------
 src/main.rs | 38 +++++++++++++++++++++++---------------
 2 files changed, 35 insertions(+), 22 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 2e37d7b..e324f30 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -15,13 +15,18 @@ latched a patch yet, and its gap-colored backdrop always draws beneath as
 the safety net beyond patch edges.
 
 Rendering is a pure function of (patch, style config): flat rounded cells,
-with the relief on the LINES — per-cell `Recess` rings expanded by the
-half-gap, so the walls occupy exactly the half-rail around each cell
-(crest at the rail centerline, inner edge concentric with the cell arc)
-and neighboring rings abut without double-shading. Cell floors carry NO
-relief (user decision); the rails read as raised grout. The compositor
-fallback draws the same expanded-ring chamfer so the client latch never
-swaps the grid's material. `style.surface.desktop.line_relief` overrides
+with the relief on the LINES — ONE `Prim::Lattice` for the whole patch
+(cce-ui shader mode 13): the pixel is folded into the grid period and the
+wall is measured from the NEAREST cell's edge outward over the roll, so
+the rail between two cells and the crossing where four meet are a single
+profile evaluation and join as true mitres. It replaced one `Recess` ring
+per cell (2026-09-14): those were N free overlays whose rounded corners
+stacked in colour space at every crossing and read as several overlapping
+effects, and whose walls — straddling a boundary inflated by the roll —
+overlapped each other down the rail centre whenever the roll exceeded a
+quarter gap. Cell floors carry NO relief (user decision); the rails read as
+raised grout. The compositor fallback still draws its expanded-ring scenefx
+chamfer (a different renderer, shown only until this client latches). `style.surface.desktop.line_relief` overrides
 the lip for the grid alone: a plain integer is the lip width in logical
 px (0 = no lip; unset = follow the DE-wide relief material), and a
 `(relief)` value carries a full custom material — width, depth, and wall
diff --git a/src/main.rs b/src/main.rs
index a5fd3f7..f5fa0d6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -306,8 +306,6 @@ impl GridApp {
         .max(0.0)
             * s;
         let lip = roll >= 0.5;
-        let ring_radius = radius + roll as f32;
-        let ring_depth = (roll as f32).max(1.0);
 
         // One extra ring of cells beyond the patch: a border cell outside the
         // patch still owns the inner half of the boundary rail's shading.
@@ -331,21 +329,31 @@ impl GridApp {
                     height: (len_h * s) as f32,
                 };
                 pc.rounded_rect(rect, radius, (true, true, true, true), st.cell_color);
-                if lip {
-                    let ring = Rect {
-                        x: rect.x - roll as f32,
-                        y: rect.y - roll as f32,
-                        width: rect.width + 2.0 * roll as f32,
-                        height: rect.height + 2.0 * roll as f32,
-                    };
-                    pc.recess(
-                        ring,
-                        (ring_radius, ring_radius, ring_radius, ring_radius),
-                        ring_depth,
-                    );
-                }
             }
         }
+        // The relief on the LINES is ONE primitive for the whole patch: a
+        // lattice carve, folded per pixel to the nearest cell, so the rail
+        // between two cells and the crossing where four meet are a single
+        // profile evaluation — true mitres. This replaced one recess ring per
+        // cell: N free overlays whose rounded corners stacked in colour space
+        // at every crossing and read as overlapping effects, and whose ring
+        // walls (straddling a boundary inflated by the roll) overlapped each
+        // other down the rail centre whenever the roll exceeded a quarter
+        // gap. The wall runs from each cell's edge outward over `roll`.
+        if lip {
+            let origin = (
+                ((inset_x + len_w * 0.5 - p.x) * s) as f32,
+                ((inset_y + len_h * 0.5 - p.y) * s) as f32,
+            );
+            pc.lattice(
+                Rect { x: 0.0, y: 0.0, width: size.width as f32, height: size.height as f32 },
+                ((period_x * s) as f32, (period_y * s) as f32),
+                origin,
+                ((len_w * s) as f32, (len_h * s) as f32),
+                radius,
+                (roll as f32).max(1.0),
+            );
+        }
 
         // Pinned images sit ON the canvas, so they are placed by the same
         // world->patch mapping as the cells and drawn after them. The whole