feat: add hidden isometric stage 2

Issue: #122
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-08-14 00:43:13 +03:00
parent 4c73e2ccdb
commit 42b3f44c4a
19 changed files with 1051 additions and 53 deletions
+25
View File
@@ -340,6 +340,31 @@ cache guarded by a structural geometry fingerprint. This is computed render
state only: it never rewrites rooms or wall entries, and an HA state tick does
not rebuild topology.
### Hidden Isometric Stage 2 composition (#122)
The hidden `iso` View reuses that masonry but has one bounded structural scene,
not a second house model. `_isoGeometryCache` remains an eight-entry LRU keyed
by room/wall/opening geometry (including opening flips), scale/camera, fixed
wall/floor-edge heights and an algorithm revision. Each value holds wall faces,
the room/exterior slab edge, immutable opening jamb bases and the projected
frame. HA state, theme, hover and filter support are presentation inputs and
never enter this key.
`floorFootprintGeometry()` derives only the union of room floors and exterior
masonry; unlike wall volume, it has no independent partition/column input.
`buildIsoFloorGeometry()` emits visible low faces for outer component rings,
not internal edges or holes. `src/iso-openings.ts` stores jamb/axis topology and
applies `openingAmount()` only during live projection, keeping contact updates
out of the boolean geometry path.
Composition is shared-viewBox SVG: ambient shadow/floor edge → the existing
affine-projected floor/live scene → contact/leaf shadows → wall material and
vertical panels → existing screen-facing HTML overlays. A constant set of
gradients/filters serves every face. Unsupported decoration or forced colours
remove nuance/shadows without changing projection; only structural failure
uses the Stage 1 latched Flat fallback. Details and fixed ratios are recorded in
`docs/adr/122-isometric-stage2-composition.md`.
## Markup editor (v1.4.0+)
State inside the card: `_markup` (mode), `_tool` (draw/partition/column/merge/split/resize/opening/
+76 -1
View File
@@ -1,4 +1,4 @@
# Isometric Stage 1 internals
# Hidden Isometric View internals
Issue [#89](https://github.com/Matysh/houseplan-card/issues/89) implements a
hidden, presentation-only volumetric View experiment. The normative contract is
@@ -90,3 +90,78 @@ the rollback path and does not depend on the iso cache.
Golden references are accepted only from the complete reviewed Linux artifact.
The full `large-house-isometric-v1` performance comparison is also canonical on
the exact Linux CI SHA.
## Stage 2 composition (#122)
Stage 2 evolves the same hidden `iso` experiment; it does not add a flag,
setting or public activation path. The accepted implementation contract is
`docs/specs/122-isometric-stage2.md` and the fixed composition decisions are in
`docs/adr/122-isometric-stage2-composition.md`. The Labs `since: 1.62.0` and
exclusive `expires: 1.65.0` boundary are unchanged.
### One structural scene, live opening leaves
The per-card LRU remains capped at eight entries. Its Stage 2 value contains:
- canonical wall top/sides and the physical-wall contact path;
- a room/exterior floor footprint and its low visible outer faces;
- immutable opening jamb/axis bases, including type, flips and selected wall
face;
- the shared projected frame, including wall/opening tops and the low floor
edge.
The key includes rooms, masonry/opening geometry, flips, scale/camera, wall and
edge heights and algorithm revision. It excludes HA state, theme, hover,
day/night and filter capability. `openingAmount()` is applied only after an LRU
hit by `projectIsoOpening()`, so a contact update projects O(O) leaves without
repeating a wall or floor boolean operation.
`floorFootprintGeometry()` deliberately accepts no independent physical-body
input. The slab is the union of room floors and derived exterior masonry:
internal room boundaries and nested holes make no decorative step, detached
room components keep separate outside edges, while partitions and columns do
not enlarge it.
### Layer order and materials
All geometry roots use one scene `viewBox`. The existing floor/live nodes are
grouped under the Stage 1 affine matrix; HTML anchors still use
`projectPlanPoint()`.
```text
stage background
→ shared ambient shadow + low exterior floor edge
→ existing floor SVG (paper/image, room fills/hover, decor, Glow, sun)
→ shared contact and live leaf shadows
→ canonical wall sides/top + inert vertical opening panels
→ existing HTML devices, labels/cards, locks and vacuum overlays
```
Wall top and side use two shared matte gradients. Ambient, contact and leaf
shadows use three shared filters; definition count is constant per card, never
per face or opening. Forced colours use solid `Canvas`/`CanvasText` faces and
omit decoration. A runtime without the required filter paint keeps solid
structure, floor edge and vertical panels but emits no Stage 2 shadows; this
does not enter the structural fallback latch.
### Vertical openings and display settings
`src/iso-openings.ts` mirrors the existing opening-symbol transform algebra:
door has one jamb-hinged leaf, gate has two leaves with the established
0–10° exterior-face turn, and window has two light neutral casements. Heights
are fixed presentation ratios of `ISO_WALL_HEIGHT`; there is no schema field.
Panels/shadows are pointer- and ARIA-inert. Existing lock badges/cards and HA
actions remain the only interactive opening surface.
- borders visible: vertical panels replace the floor-plane symbols;
- `hide_openings: true`: panels and leaf shadows disappear, while masonry
cuts, Glow/sun and contact/lock meaning remain;
- `show_borders: false`: Stage 2 roots are absent and the established floor
symbols return (subject to `hide_openings`), avoiding floating panels;
- Flat, editors and `houseplan-space-card` retain their old symbols and DOM.
Stage 2 adds no window beam, Glow source, sun renderer, material config,
network request or HA service path. Structural topology/projection exceptions
still use the Stage 1 latched Flat fallback. The known independent exact-SHA
view-toggle performance debt remains tracked in #124; #122 neither weakens its
budget nor treats fallback as benchmark success.
+3 -3
View File
@@ -17,13 +17,13 @@ change must pass through a published beta/RC before stable. Stable release
commits are promotion-only (versions, generated bundles and release/changelog
metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
## Snapshot (2026-08-13)
## Snapshot (2026-08-14)
| Item | State |
|---|---|
| Version | **v1.63.0** everywhere (manifest, const.py, package.json, CARD_VERSION) — stable promotion candidate after published v1.63.0-beta.2 |
| Current local cycle | v1.63.0 promotes the published beta line without product-code changes. v1.63.0-beta.2 preserves the exterior facade when Split starts or ends at a room corner (#123), using one wall geometry for flat/static/isometric rendering and light; v1.63.0-beta.1 contains the empty-plan and opening-reference fixes (#111, #104), plus reviewed process automation #105 and #118–#121. |
| Hidden Labs Stage | #89 Stage 1 ships in v1.63.0-beta.1 as a hidden, expiring `iso` Labs experiment: a fixed near-top orthographic volumetric View. Flat remains default; editors and `houseplan-space-card` remain flat; all existing floor live effects and HA actions are preserved. This is internal, not a public feature. |
| Current local cycle | v1.63.0 is the stable base. #122 Stage 2 is being implemented on `issue/122-isometric-stage2` under an explicit owner arbitration to start while the independent #124 exact-SHA view-toggle performance debt remains open; no Stage 2 release has been published. |
| Hidden Labs Stage | #89 Stage 1 ships in v1.63.0-beta.1. #122 evolves the same hidden, expiring `iso` experiment with matte walls, a low exterior floor edge, restrained shared shadows and live vertical door/window/gate panels. Flat remains default; editors and `houseplan-space-card` remain flat; live floor effects and HA actions remain unchanged. Public activation is explicitly a separate task. |
| Workflow | Owner's rule since 2026-08-07: ordinary fixes/features are made **locally, without tests and without commits**. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Release bodies are short and bilingual (Russian first): only significant user changes get individual bullets, while minor/code-only work is grouped as `Мелкие исправления и улучшения` / `Small fixes and improvements`; every body ends with separate links to the Russian and English changelogs. Detailed RU/EN changelog bullets may link the corresponding closed GitHub Issues; open or partially delivered issues are never presented as shipped. Telegram announcements are sent only for stable releases; beta and RC publication is silent. `docs/RELEASE-NOTES.md` is the current canonical body instance; `npm run release:prerelease -- <tag> --issues=… --yes` is the primary local publication path and the manual `Publish prerelease` workflow is its GitHub-only equivalent once present on `main`. Nothing is copied to the home instance by hand |
| GitHub | https://github.com/Matysh/houseplan-card — [Issues](https://github.com/Matysh/houseplan-card/issues) are the canonical task records and the linked [Project v2](https://github.com/users/Matysh/projects/1) is the canonical priority/status view; both must stay current. `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
| CI | Prerelease publication requires a green exact-SHA Validate: frontend/backend, smoke (including the #73 rAF frame sampler), golden, HACS/Hassfest and a short absolute-ceiling performance smoke. Obsolete same-ref Validate runs are cancelled. Full seven-sample base/candidate performance moved to `performance.yml` (`main` push, weekly, manual); stable release assets fail closed unless Validate and Full Performance are green for the exact tagged SHA and the stable-only CDP compositor screencast finds no empty/black presented frame. |
@@ -0,0 +1,89 @@
# ADR #122 — Stage 2 isometric composition
- Issue: https://github.com/Matysh/houseplan-card/issues/122
- Status: accepted for implementation
- Date: 2026-08-14
- Normative spec: `docs/specs/122-isometric-stage2.md`, revision 1
- Predecessor: `docs/adr/089-isometric-stage1-renderer.md`
## Context
Stage 1 established one fixed affine floor projection, canonical wall volume,
screen-facing HTML anchors and a bounded structural LRU. Stage 2 must add matte
depth, an exterior floor edge, grounding shadows and vertical openings without
creating another plan/light model or making HA state rebuild topology.
The owner explicitly allowed implementation to begin with #124's known
view-toggle performance debt still open. That arbitration removes the DoR
blocker only: the unchanged exact-SHA performance budget remains a gate and
fallback may not be forced to make it green.
## Decision
### Structural/live boundary
The existing eight-entry Iso LRU becomes a structural scene cache. Each entry
contains wall faces, floor footprint/edge, immutable opening bases and one
projected frame. The fingerprint includes opening geometry and flips plus the
fixed wall/edge heights and algorithm revision. It excludes HA state and every
decorative capability.
An opening basis stores its resolved wall face, jamb hinge, closed vector,
quarter-turn vector and fixed height bounds. Rendering applies the existing
`openingAmount()` after the cache lookup. Door/window/gate live changes are
therefore O(O) vector projection and cannot invoke polygon boolean operations.
### Floor footprint
The slab source is the canonical union of rooms plus derived exterior masonry.
Independent partitions, drafts and columns remain canonical wall volume and
light occluders but are not accepted as floor-footprint input. Stage 2 projects
only outer rings down by ten plan units; holes and internal/shared boundaries
cannot become steps. Disconnected outer polygons remain disconnected.
### Composition
Four absolute SVG roots share the effective scene `viewBox`: underlay, the
existing floor scene, shadows and wall/opening volume. The old floor nodes are
not copied; they live under the same affine matrix Stage 1 used. HTML overlays
stay above every SVG root.
The stable order is ambient shadow, floor edge, current floor/live layers,
contact/leaf shadows, wall sides/top, vertical panels, then HTML overlays.
All new geometry is pointer/focus/ARIA inert.
### Material and degradation
Two shared SVG gradients distinguish matte wall top and side. Three shared
filters provide ambient/contact/leaf softness. Definitions are O(1) per card;
there are no per-face filters, raster textures, data URLs or runtime
dependencies.
Filter-paint failure removes shadows only. Forced colours also remove gradient
nuance and use solid system colours. Structural wall/opening/edge geometry and
all existing live layers remain Iso. Only a structural topology/projection
exception enters the established `space|fingerprint` Flat fallback latch.
### Opening presentation and settings
- door: one full jamb-hinged leaf, 92% of wall height;
- gate: two half leaves, 88%, preserving the existing 0–10° face-aware turn;
- window: two light inserts between 27% and 78% of wall height.
These are presentation constants, not persisted fields. With visible borders,
vertical panels replace the old floor symbols. `hide_openings` hides panels and
leaf shadows. With borders disabled, every Stage 2 root is absent and the
existing floor symbols return, so there are no floating vertical leaves.
## Consequences
- Flat, editors, static card, schema, storage keys, i18n, backend and HA actions
are unchanged.
- Glow/spill and sunlight remain the only room/window light models.
- Theme, hover, filter fallback and HA-only updates do not grow the structural
LRU.
- Fit includes structural opening/wall tops and the low floor edge, never blur.
- Golden and browser evidence must review new Iso pixels; Flat and the existing
no-borders baseline remain unchanged.
- Stage 2 stays hidden and expires with the existing `iso` Labs entry unless a
separate owner-reviewed rollout/graduation issue changes that contract.