mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
560849a1a71dff094e135b03fe81d1da3758cdaf
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
02ae7f9588 |
DEV-EB173-01: a shaft of light fades along the wall's normal
Audit finding P2. At a grazing sun the wedge lost the two invariants it was supposed to keep: one end of the GLASS started at opacity 0, and the two sides of one shaft came out 5.41 and 84.19 long — the long one 31 % LONGER than the pre-cut 64, not 30 % shorter. The cause was the axis. The gradient ran along `dir` from the middle of the window span, so the geometry had to be skewed (each end extruded by a different amount) to make both far corners land on the same offset. That buys the iso-alpha far edge with the other two requirements. The light is a bundle of PARALLEL rays: the distance a point has travelled from the glass is depth/cos, an affine function of the point, whose level sets are lines PARALLEL TO THE WALL. So the correct linear gradient runs along the wall's INWARD NORMAL, starts on the window line and is `len·cos(incidence)` long — SunRay.normal / SunRay.depth. A point `source + dir·u` then lands on offset u/len, whichever ray it rode in on. All three invariants hold at once: * the whole pane of glass is at depth 0 → peak alpha end to end; * alpha depends only on how far that point's own ray has run; * rayQuad() is an honest parallelogram again (both ends extruded by the same `len`), and its far edge — parallel to the wall — IS the gradient's last iso-alpha line, so a bright kerb is impossible by construction and the −30 % holds for every side of every wedge. windowLit() gets a real threshold instead of the 1e-9 epsilon: RAY_MIN_COS = 0.05, i.e. the sun must clear the plane of the wall by ~2.9°. Below it glass reflects nearly everything and the shaft would be a sliver thinner than the wall it came through — nothing is drawn, and the gradient axis can never degenerate to a point. Tests: rayQuad now asserts equal, full-length sides and a wall-parallel far edge; new unit tests replay the auditor's repro with his numbers (both sides 44.8, offsets 0 at both ends of the glass, offset = travel / len for arbitrary rays) and the RAY_MIN_COS cut-off. smoke_sun_soft measures the same facts off the DOM gradient end to end and fails by name on the old bundle (9 named failures). docs/SUN.md carries the new contract and the finding. |
||
|
|
eb17396006 |
Sunlight has hard sides again and fades only along the ray
Owner, 2026-08-04, on yesterday's attempt: «с лучами солнца ты сделал фигню —
не надо размывать их боковые грани».
They are right.
|
||
|
|
22b588e116 |
Sunlight is 30% shorter and always dissolves into nothing
Owner, 2026-08-04: «лучи от солнца сделать короче на 30%, проверить, чтобы они всегда плавно рассеивались (сейчас есть ощущение, что они упираются во что-то невидимое)». SHORTER. `rayLength` is now the v1.56 curve times RAY_LENGTH_K = 0.7 — 1.75 window lengths at sunrise, 0.56 at the zenith. Scaling the whole curve instead of re-picking the constants keeps the shape the owner approved: a low sun still reaches three times further than a high one. WHAT THEY WERE BUMPING INTO. Nothing invisible — the wedge's own outline, in three places at once. 1. The gradient runs ALONG the sun, so its iso-alpha lines are perpendicular to the sun, while the wedge's far edge is parallel to the WALL. The two coincide only for a sun hitting the glass dead-on; at any other angle one half of that far edge was cut while it still carried colour — a straight bright kerb hanging in the middle of the floor. The single `100% → alpha 0` stop hid this from the reader of the code and from nobody else. 2. The two SIDES of the wedge had no falloff at all: two razor lines from the window into the room, brightest exactly where they are most visible. 3. Where the room outline clips the wedge — the opposite wall, the inner corner of an L, and above all an OPEN (virtual) boundary, which has no wall drawn at all — the shaft was chopped at whatever alpha it still had. WHAT IT IS NOW. The gradient still spans the FULL wedge (geometry and gradient must describe the same shaft), but `rayStops()` eases it to a hard zero at RAY_FADE_END = 85% of the length, so the last 15% of every wedge is guaranteed empty and a shaft ending in mid-air has nothing left to draw an edge with. Each wedge is then drawn inside `<g filter clip-path>`: SVG applies the filter FIRST and the clip SECOND, so a Gaussian blur of `raySoftness(len)` (7% of the shaft, clamped 3…18 render units) feathers the sides and the tip and the room outline cuts that feather off. Light still never crosses a wall — but where it reaches one, the kerb is a soft ramp that reads as light landing ON the wall. Clipping by the room is untouched; only its visible edge changed. Tests: unit — rayLength pinned at exactly 70% of the old curve at ten elevations, rayStops (monotone, dead at/after 85%, bright at the glass), raySoftness clamps. Smoke — demo/smoke_sun_soft.mjs, which fails on the previous tip (lowSunIs70Percent, highSunIs70Percent, gradientSpansWholeWedge, deadWellBeforeTheEnd, everyWedgeFeathered). Stills: demo/shot_sun_short.mjs. |