Three problems from the first cut (user report 2026-09-13), all rooted
and fixed:
1. Boundaries reshaping on zoom — an inward offset normal. In y-down
screen coordinates, monotone chain yields clockwise-on-screen hulls,
whose OUTWARD normal is (dy, -dx); the code used (-dy, dx), so the
14px padding pulled every hull vertex 14-20px INWARD. Zoomed in this
mostly passed; zoomed out, once a cluster projected smaller than ~2x
the pad, vertices crossed the centroid and the polygon self-
intersected — spiky garbage re-forming every frame. Geometry moved to
components/cluster-geometry.ts (exported, unit-tested) with the
corrected normal; tests pin the direction (offset moves every vertex
AWAY from the centroid; stays sane when the hull is smaller than the
pad; hull orientation asserted clockwise-on-screen).
2. GPU ~100% + audible coil whine on any interaction — the hull redraw
was hooked to sigma's afterRender, which fires for EVERY render
including ~a dozen hover-animation frames per hover, and each redraw
rasterized ~32 fills through ctx.filter blur(12px), a GPU-heavy
full-canvas filter. Now: redraws happen ONLY on camera updates and
resizes (hover leaves the hull canvas untouched — verified live),
and the blur is replaced with layered low-alpha fills (soft edge at
a fraction of the cost, no canvas filter at all).
3. Cluster labels cluttering the view — a persisted 'Cluster labels'
checkbox in the rendering settings (hulls stay, text toggles).
377/377 tests, typecheck, lint, build green. Live-verified: hover does
not repaint hulls, zoom does; far zoom-out yields 3 contiguous tinted
regions (no fragmentation); toggle clears/returns labels and persists.