Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix(handles): mirror the waist — both pieces leave a seam at the same slope
The two pieces met at band_min but each normalized its swell to its own
run, so the slope in PIXELS differed by the ratio of the runs: with a
124px corner arm against a 37px edge bar, the bar left the join over
three times steeper. The heights matched; the directions did not — a
visibly lopsided waist at every seam.
Both sides of a seam now share one curve over one pixel length, the
shorter of the two runs, making the profile mirror-symmetric across the
gap: equal and opposite slopes, a waist that reads as one moulding
pinched rather than two curves butted together. Whichever piece is longer
plateaus at full band beyond that length — under the pad for a corner
arm, through the middle of the side for a long bar — and smoothstep's
flat top keeps the plateau joins and the midpoint crease-free.
Measured at the seam: thickness at matching distances either side agrees
within 1px of rasterization at every probe (4 to 32px out).
Co-Authored-By: Claude Fable 5 <[email protected]>
CLAUDE.md | 6 +++++-
scenefx/render/fx_renderer/shaders/frame.frag | 27 ++++++++++++++++-----------
2 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 10bc5b6..f34d6a4 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -329,7 +329,11 @@ where the old band sat outside them.
a disc centred on the corner arc's centre, smooth-unioned onto the ring so
its inner boundary bows inward with a fillet where it meets the moulding.
Each gap therefore separates two thin tips: the frame pinches at every
- join and thickens toward every piece's middle.
+ join and thickens toward every piece's middle. ONE curve serves both sides
+ of a seam, over the same pixel length on each side (the shorter of the two
+ runs; the longer piece plateaus at full band beyond it), so the waist is
+ mirror-symmetric — normalizing each piece to its own length made the
+ shorter side leave the join visibly steeper.
The gap notches sever only the band; a groove across a pad would read as
damage. Both `get_border_zone` and the drawing treat a pad as its corner's
zone — its tip reaches past the band, so the hit test carries a matching
diff --git a/scenefx/render/fx_renderer/shaders/frame.frag b/scenefx/render/fx_renderer/shaders/frame.frag
index 94f70e5..8124c9b 100644
--- a/scenefx/render/fx_renderer/shaders/frame.frag
+++ b/scenefx/render/fx_renderer/shaders/frame.frag
@@ -104,18 +104,23 @@ void main() {
// minimum lands exactly on the seam.
float cl = min(corner_len, 0.45 * len);
- // The band: band_min at the seams, swelling away from them both ways —
- // an edge bar to `band` at the side's midpoint, a corner arm back toward
- // the apex, where the pad's union takes over. v is 0 at a seam and 1 at
- // a piece's peak; swell_curve pulls the gain early (below 1) or late.
+ // The band: band_min at the seams, swelling away from them both ways.
+ // ONE curve serves both sides of every seam, over the same pixel length
+ // L on each side — the shorter of the corner arm and the edge run — so
+ // the profile is mirror-symmetric across the gap: the two pieces leave
+ // the waist at equal and opposite slopes and read as one moulding
+ // pinched, not two different curves butted together. (Normalizing each
+ // piece to its own length made the shorter side climb that much steeper
+ // at the join — a visibly lopsided waist.) Whichever piece is longer
+ // plateaus at full band past L: under the pad for a corner arm, through
+ // the middle for a long bar. smoothstep's flat top keeps the plateau
+ // joins and the midpoint crease-free; swell_curve pulls the gain early
+ // (below 1) or late.
float half_side = max(0.5 * len, 1e-3);
- float v;
- if (u <= cl) {
- v = 1.0 - u / max(cl, 1e-3);
- } else {
- float run = max(half_side - cl - gap, 1e-3);
- v = clamp((u - cl - gap) / run, 0.0, 1.0);
- }
+ float run = max(half_side - cl - gap, 1e-3);
+ float L = max(min(cl, run), 1e-3);
+ float d = (u <= cl) ? (cl - u) : max(u - cl - gap, 0.0);
+ float v = clamp(d / L, 0.0, 1.0);
float s = pow(smoothstep(0.0, 1.0, v), max(swell_curve, 0.01));
float thickness = mix(band_min, band, s);
float f_ring = thickness - depth;