v1.59.0-beta.1: warm remount keeps your view and dialogs, sun ray rim
Validate / hacs (push) Failing after 7s
Validate / hassfest (push) Failing after 7s
Validate / frontend (push) Successful in 2m47s
Validate / backend (push) Failing after 8m13s
Validate / smoke (push) Failing after 22m41s

This commit is contained in:
Matysh
2026-08-04 18:20:47 +03:00
parent 46f20fe4e1
commit 1397a71f84
23 changed files with 2095 additions and 570 deletions
+53 -93
View File
@@ -1,10 +1,16 @@
# Sunlight that reads on a light plan — the spec
# Sunlight that reads on a light plan — the rejected model
Status: **approved in principle, not implemented.** Planned for the release
after v1.58.0. Owner asked for the spec first (2026-08-04); nothing in this
document is code yet. Builds on docs/SUN.md, which describes what ships today.
Status: **REJECTED by the owner on 2026-08-04.** The model below was
approved in principle a few hours earlier and never became code. The owner
cancelled it in favour of one line out of it — the rim — which IS
implemented and specified in docs/SUN.md, section «The rim».
## The problem
This file is kept for one reason: the analysis of *why* a wedge of light
cannot read on white paper is correct, it is what the rim answers, and it
is the first thing anyone will have to re-derive the next time somebody
proposes «просто сделай лучи поярче».
## The problem (this part still holds)
Today a lit window casts a warm translucent wedge (docs/SUN.md). It reads
beautifully on a dark scene — glow fill, night, a dark theme — and almost
@@ -14,100 +20,54 @@ makes a clear day **white**, and a hand-drawn plan's paper is white too.
The cause is not opacity, it is physics. Painting light means adding
luminance, and white has none left to give. Raising the alpha does not add
contrast, it only tints the paper towards beige until the whole room looks
dirty. Any fix has to take its contrast from something other than brightness.
dirty. Any fix has to take its contrast from something other than
brightness.
## The model: paint the shade, not the light
## What was proposed: paint the shade, not the light
On a light scene the wedges invert. The lit areas stay untouched paper; the
**rest of the room** gets a light, cool veil. Contrast now comes from the
unlit part, which has plenty of room to go darker, and the wedge becomes a
window of clean paper inside a softly shaded room.
On a light scene the wedges would invert. The lit areas stay untouched
paper; the **rest of the room** gets a light, cool veil, so the contrast
comes from the unlit part, which has plenty of room to go darker, and the
wedge becomes a window of clean paper inside a softly shaded room. This is
how architects draw insolation on white sheets, and it is physically
honest: a sunlit room really is brighter where the shaft lands.
This is how architects draw insolation on white sheets, and it stays
physically honest: a sunlit room really is brighter where the shaft lands.
It also rhymes with the mode we already have — glow paints a dark house with
pools of lamp light; this paints a light house with the shade between shafts.
Geometrically it is the same wedge polygon used as a hole — a veil over the
room's polygon, minus the wedges (even-odd, or a mask). `computeSunRays()`
would not change at all; only what we do with the result. Around it the
spec needed: a luminance threshold to choose the model automatically, a
cross-fade whenever that threshold is crossed (theme switch, day turning to
night), a rule keeping the veil *under* the room fills so temperature and
LQI colours keep their identity, and an open question about offering
«свет / тень / авто» explicitly.
Geometrically it is the same wedge polygon, used as a hole: a veil rectangle
over the room's polygon, minus the wedges (an even-odd path, or a mask). The
maths of `computeSunRays()` does not change at all — only what we do with the
result.
**Why the owner dropped it.** It is a second rendering model for one
feature: two code paths, an automatic chooser that must be a pure function
of a background nobody controls, a cross-fade between them, and a veil that
has to coexist with the meaning-carrying room fills without being mistaken
for one — all to solve a problem that a single hairline solves.
## Switching between the two
The spec's other amplifier — a mark on the lit window itself (a thicker
warm stroke plus a short arrow along the sun direction, answering «в какие
окна сейчас светит солнце» without reading the floor) — was not rejected on
its merits. It is simply out of scope; this paragraph is where the idea
stays written down.
Automatic, by the luminance of what the wedge is drawn on (the paper under
the room, or the scene colour where a picture backdrop shows through):
## What was implemented instead (2026-08-04)
- luminance below the threshold → **light model** (today's warm wedge);
- above it → **shade model** (veil with wedge-shaped holes).
A **rim**: a 1 px black hairline along the two SIDE edges of the wedge,
fading to zero on exactly the same gradient axis, the same curve and the
same 85 % threshold as the fill, clipped by the room like the wedge itself,
and living inside the same layer — so the 3° threshold, the two-second
fade, cloud cover and the editors govern it for free.
The threshold sits around mid-grey; the exact value is a visual decision to be
made against real plans, dark and light themes, and the day/night palette.
Whether this is fully automatic or also offered as an explicit choice is an
open question (below).
Light is invisible on white paper; its boundary is not. A boundary costs
one stroke, works on any background, and needs no second model, no
luminance threshold and no cross-fade.
## Two amplifiers, both models
**A rim on the wedge.** Light is invisible but its boundary is not: a hairline
warm stroke along the two side edges of the shaft, fading out with the shaft
itself. It costs one stroke, works on any background, and gives the shape a
"beam" reading even when the fill is nearly transparent. In the shade model
the same rim marks the edge of the veil hole.
**A mark on the window.** Independent of the wedges: a lit window gets a
thicker warm stroke and a short arrow along the sun direction. It answers the
question "which windows is the sun in right now" without reading the floor,
and it survives everything the wedge does not — furniture-heavy plans, room
fills, small kiosk scale, a wedge clipped to almost nothing by a narrow room.
## Colour
The veil is cool and desaturated (a blue-grey), never black: a black veil
reads as a printing defect and fights the room fills. Its opacity is small —
the target is a perceptible step, not a dimmed room; the value is to be tuned
visually, in the same order as the wedge alpha today.
The light model keeps its warm ramp near the horizon (docs/SUN.md), but should
lean on **hue rather than luminance** on medium backgrounds: an amber tint
reads as colour even where it cannot read as brightness.
## What must not break
- **Room fills.** Temperature and LQI fills carry meaning; the veil must not
be mistaken for them, and must not shift their perceived colour more than
marginally. The veil goes under the fills, not over.
- **The 3° threshold and the two-second fade** (docs/SUN.md) apply unchanged
to whichever model is active. Crossing the luminance threshold — a theme
switch, day turning to night — must cross-fade between models, never pop.
- **Cloudiness** keeps dimming the effect, in the shade model by thinning the
veil.
- **Editors** stay neutral, as today: no sun, no day/night.
- **The static card and kiosk** must pick the same model as the full card on
the same plan; the choice is a pure function of the background.
- **prefers-reduced-motion**: no cross-fade, switch instantly.
- **Performance**: one veil path per room per render at most, memoised on the
same key as the wedges.
## Open questions for the owner
1. Automatic switching only, or also an explicit setting «свет / тень / авто»
in the space dialog? (Automatic is one less knob; explicit lets someone on
a light theme keep the warm wedges if they like them.)
2. Does the veil cover the whole room, or only the part of the room the sun
could reach at all (the room's window-facing side)? Whole-room is simpler
and reads as "this room is in shade"; partial is subtler.
3. Should the window mark be always on, or follow the same «Solar rays»
toggle?
## Testing notes (for when this is built)
Unit: the model chooser is a pure function of a colour → assert the switch at
the threshold, both sides, plus junk input. The veil-with-holes geometry:
holes equal the wedges, an unlit room gets a plain veil, a room with no
exterior window gets nothing.
Browser: on a white paper plan a lit wedge is measurably lighter than its
surroundings (sample pixels inside and outside the wedge — the current
implementation would fail this, which is the point); on a dark scene nothing
changes versus today; crossing the threshold cross-fades; room fills keep
their identity; the window mark appears exactly on lit windows.
Full contract and the tuned peak opacity: **docs/SUN.md, «The rim»**.
Implementation: `rayRimEdges()`, `rimStops()`, `rimPeakAlpha()` and
`RIM_MAX_ALPHA` in `src/sun.ts`, plus the `hp-sunrim-N` gradient in
`src/houseplan-card.ts`. Browser proof, including the pixel probe showing
the wedge's side is measurably darker than the paper beside it:
`demo/smoke_sun_rim.mjs`.