Files
houseplan-card/docs/ISOMETRIC.md
T
2026-08-14 12:11:08 +00:00

174 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
`docs/specs/089-isometric-view-stage1.md`; the fixed rendering decisions are in
`docs/adr/089-isometric-stage1-renderer.md`.
## Activation and lifetime
The feature exists only while the `iso` entry in `src/labs.ts` is live. For the
v1.62 cycle it can be enabled with either `?hp-labs=iso` or
`#hp-labs=iso&space=<id>`. Query operations are applied first and hash operations
second. `-iso` removes this flag and `off` clears every Labs flag. A known URL
operation is persisted in `houseplan_card_labs_v1`; the URL itself is not
rewritten.
The selected presentation is stored per space in `houseplan_card_view_v1`. Flat
is always the initial default. Kiosk has no toggle but reads the saved preference.
Editors and `houseplan-space-card` are always flat.
The current registry entry is live from numeric core `1.62.0` and expires at
`1.65.0`. Thus `1.65.0-beta.1` is already expired. Malformed versions, malformed
entries and duplicate ids fail closed. Labs never gate schemas, migrations,
stores, service calls or network requests.
## Coordinate systems
- Plan points are the existing 1000-unit logical floor coordinates.
- Scene points are coordinates in the effective SVG viewBox.
- HTML devices, vacuum pucks, room labels/cards and lock markers stay
screen-facing. Their anchors use the same `projectPlanPoint()` snapshot as the
SVG floor.
- `clientToScenePoint()` maps a client point to the current scene; floor hit
testing then uses `unprojectFloorPoint()`.
Stage 1 uses a fixed orthographic affine camera:
```text
rotDeg=0, tiltDeg=20, xyScale=1, zScale=1, origin=[500,500]
wallHeight=64 plan units
```
There is no perspective, free rotation or user tilt. Switching projections
preserves scalar zoom and converts the view centre through logical floor space.
`projectedFrame()` includes both floor corners and wall-top corners so fit/home
cannot clip the volume.
## Geometry and composition
`src/iso-projection.ts` owns pure projection math. `src/iso-walls.ts` consumes
the canonical `wallBodiesGeometry()` MultiPolygon after openings and extra
physical bodies have been resolved. It normalizes outer/hole winding, builds one
evenodd top path and at most one visible side per ring edge, then uses a stable
depth/order tie-break. Complexity is O(E) in canonical ring edges.
For connected drafts and partitions those extras already contain computed
bounded junction patches. Isometric wall tops/sides therefore use the same
seamless L/T footprint as flat full/static cards; raw per-record rectangles are
reserved for editor identity and never projected as competing wall faces.
The content fingerprint includes rooms, wall geometry/thickness, open cuts,
openings, partitions, drafts, columns, scale/grid inputs, camera, wall height and
algorithm version. It deliberately excludes `_cfgEpoch`, HA state, hover and
`show_borders`. The per-card LRU is capped at eight scenes. Pan, zoom and HA-only
updates reuse it.
Composition remains SVG-first:
1. the existing floor SVG and all its current live layers;
2. explicit visible wall sides and wall top;
3. screen-facing HTML overlays.
The floor keeps the same nodes and order for paper/backdrop, room fills/hover,
Glow/spill, sun, decor/furniture, opening symbols and vacuum path/outline. Stage
1 does not add a second light source/layer. Markers and room cards intentionally
remain above walls without geometric occlusion.
## Failure boundary
Projection/topology failures latch flat fallback for the current
`space|fingerprint`. One diagnostic is emitted without config, entity ids or URL
data. The saved iso preference is retained; an explicit iso request retries the
fingerprint, and changed geometry receives a new fingerprint. Flat rendering is
the rollback path and does not depend on the iso cache.
## Deliberate Stage 1 limits
- door, window and gate keep their current floor-plane symbol and live state;
- no vertical leaf/window panels, sill model or new window light;
- no floor-edge extrusion, shadows, photorealistic materials or marker
occlusion;
- no volumetric editor and no volumetric `houseplan-space-card`;
- no YAML/config option or public settings surface.
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 and Stage 1 projected frame return (subject to `hide_openings`),
avoiding floating panels or an invisible Stage 2 bound that reframes them;
- 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.