mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-06 22:49:16 +00:00
docs: decide isometric renderer architecture
Issue: #89 User-Visible: no
This commit is contained in:
@@ -0,0 +1,106 @@
|
|||||||
|
# ADR #89 — Stage 1 volumetric renderer
|
||||||
|
|
||||||
|
- Issue: https://github.com/Matysh/houseplan-card/issues/89
|
||||||
|
- Status: accepted for Stage 1 implementation
|
||||||
|
- Date: 2026-08-13
|
||||||
|
- Normative spec: `docs/specs/089-isometric-view-stage1.md`, revision 3
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
House Plan already has one SVG scene with live room fills, Glow/spill, sunlight,
|
||||||
|
backdrop/decor, furniture, opening symbols and vacuum trails, plus screen-facing
|
||||||
|
HTML devices and room cards. Stage 1 must add low wall volume behind a Labs flag
|
||||||
|
without creating a second rendering model or changing HA actions, configuration
|
||||||
|
or service paths.
|
||||||
|
|
||||||
|
Two spikes were evaluated against the existing scene composition:
|
||||||
|
|
||||||
|
1. CSS `perspective`/`preserve-3d` around `.zoomwrap` would put the existing
|
||||||
|
`brightness()` filter, SVG blur and `mix-blend-mode: screen` into a 3D
|
||||||
|
compositing context. It also requires a second approximate formula for HTML
|
||||||
|
overlays.
|
||||||
|
2. One affine floor projection plus explicit SVG wall faces keeps the current
|
||||||
|
live nodes and native hit targets, while pure functions can project the HTML
|
||||||
|
anchors with the identical camera.
|
||||||
|
|
||||||
|
The second spike wins. It is deterministic, has no new runtime dependency and
|
||||||
|
can fail back to the untouched flat path.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
### Camera and coordinate systems
|
||||||
|
|
||||||
|
- Projection is orthographic with `rotDeg = 0` and `tiltDeg = 20`.
|
||||||
|
- Pivot is the fixed plan point `[500, 500]` (`NORM_W / 2`), never content or
|
||||||
|
viewport centre.
|
||||||
|
- `xyScale = 1`; floor Y is multiplied by `cos(20°)` around the pivot.
|
||||||
|
- Logical wall height is `64` plan units and `zScale = 1`. Height therefore
|
||||||
|
moves a top point upward by `64 * sin(20°) ≈ 21.9` scene units: visibly low,
|
||||||
|
inside the 18–22° family approved by the owner.
|
||||||
|
- `projectPlanPoint` and `unprojectFloorPoint` are the sole plan/scene
|
||||||
|
conversion. Client coordinates continue through the existing SVG viewBox.
|
||||||
|
- `projectedFrame` includes the floor frame at `z = 0` and the same corners at
|
||||||
|
wall height. Pan, fit and zoom use this frame in the volumetric view.
|
||||||
|
|
||||||
|
The floor transform is the SVG affine matrix equivalent of
|
||||||
|
`projectPlanPoint(p, 0)`. Existing SVG floor/live nodes are grouped under this
|
||||||
|
one matrix. Screen-facing HTML overlays project their logical anchors through
|
||||||
|
the same pure function before conversion to percentages.
|
||||||
|
|
||||||
|
### Wall geometry and draw order
|
||||||
|
|
||||||
|
- Source geometry is `wallBodiesGeometry(...).geom`, including canonical room
|
||||||
|
wall rings, joined junctions, full-height opening cuts, independent
|
||||||
|
partitions, room drafts and columns.
|
||||||
|
- Rings are normalized by signed area. Every ring edge is visited once, so the
|
||||||
|
side-face count is O(E).
|
||||||
|
- With the fixed camera, a side is visible when its outward plan normal faces
|
||||||
|
the camera. Hole normals are reversed relative to outer rings. Jamb edges are
|
||||||
|
ordinary ring edges and therefore produce the two expected vertical faces
|
||||||
|
at a full-height opening.
|
||||||
|
- Visible side quads are ordered by projected floor depth and a stable
|
||||||
|
polygon/ring/edge index. The top uses the complete projected MultiPolygon
|
||||||
|
with `fill-rule: evenodd`; there is no strip or floor cap through an opening.
|
||||||
|
- Stage 1 order is: transformed floor/live SVG → visible wall sides → wall top
|
||||||
|
→ screen-facing HTML overlays. Markers and room cards deliberately remain
|
||||||
|
above walls and receive no geometric occlusion.
|
||||||
|
|
||||||
|
### Appearance
|
||||||
|
|
||||||
|
- Light theme: top `#f3f3f1`, side `#a8acae`, outline `#d7d9d8`.
|
||||||
|
- Dark theme: top `#596166`, side `#3f474c`, outline `#747d82`.
|
||||||
|
- The current room-selected wall colour is not reused for volumetric material;
|
||||||
|
it remains part of flat/editor rendering. No shadows, floor edges, vertical
|
||||||
|
doors/windows or new window-light layer are added in Stage 1.
|
||||||
|
|
||||||
|
### View state, Labs and fallback
|
||||||
|
|
||||||
|
- `houseplan_card_labs_v1` owns the reusable flag set; `iso` is valid from
|
||||||
|
`1.62.0` and expires at numeric core `1.65.0`.
|
||||||
|
- `houseplan_card_view_v1` stores only `flat|iso` per space. Flat remains the
|
||||||
|
default even while Labs is enabled.
|
||||||
|
- Switching preserves scalar zoom and logical floor centre. Editors always use
|
||||||
|
flat projection; returning to View restores the preferred projection.
|
||||||
|
- Warm state records projection and logical centre. A raw viewBox is adopted
|
||||||
|
only when projection and active Labs contract still match.
|
||||||
|
- Geometry/render exceptions latch flat fallback for the current
|
||||||
|
`(space, fingerprint)`. Preference is retained; explicit retry or a new
|
||||||
|
fingerprint clears the latch.
|
||||||
|
|
||||||
|
### Browser and composition result
|
||||||
|
|
||||||
|
The selected SVG-only composition uses standard affine transforms, paths,
|
||||||
|
`fill-rule: evenodd`, existing filters/clips and existing screen blend. Chromium,
|
||||||
|
Firefox and WebKit all implement these primitives without a CSS 3D flattening
|
||||||
|
boundary. Browser validation remains mandatory through the Stage 1 smoke and
|
||||||
|
golden matrix; a browser-specific failure uses the same latched flat fallback.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Flat rendering takes no Labs geometry path and retains its current DOM.
|
||||||
|
- HA-only updates reuse the content-fingerprinted wall geometry.
|
||||||
|
- `houseplan-space-card`, schemas, backend, imports/exports and service actions
|
||||||
|
are unchanged.
|
||||||
|
- Stage 2 may replace material tokens or add vertical opening elements, but it
|
||||||
|
must not replace this shared projection contract without a new decision.
|
||||||
|
|
||||||
Reference in New Issue
Block a user