fix(2.5D): a device icon never moves because its state changed (#711)

Owner's decision in #694: icons must not change position with state.
Since #651 the rigid overlay layout sized a device by its value text and
badges, which change with HA state: a light toggling on changed its width
from 37.2 to 26.7 CSS px and re-laid out all 62 overlays of the dense
scene (~385 of 495 ms of stateUpdate; v1.77.0 had 208 ms).

- iso-scene-render.ts: the layout sees the state-free tile (icon at its
  configured size, no value text, badge or supplemental metrics). An
  HA-only change keeps the layout and refreshes only the visual extent
  that scene bounds read, without a collision search.
- iso-overlays.ts: the rigid-group search skips candidates that already
  lose to the fallback on room, then wall violations (lexicographic
  bound). The result is unchanged — identical placement hash on the dense
  scene — at about 35 % less work.
- docs/ISOMETRIC.md, changelogs; test #711; mutant
  iso-device-layout-follows-state-again (written, not run — #709).

Local isometric-stage3-dense-v1, 3 samples: stateUpdate 522 → 89 ms
(v1.77.0: 208), modelReady 3665 → 3129, switchCycle 5544 → 4700.

Issue: #711
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
Claude
2026-09-30 10:51:53 +03:00
parent 9caa3c2316
commit a372b354c6
7 changed files with 162 additions and 17 deletions
+13
View File
@@ -6201,6 +6201,19 @@ const MUTANT_DEFINITIONS = [
replace: ' const names = [...(selection.direct || []), ...(selection.registered || [])]',
}],
},
// #711: значок не двигается от состояния устройства.
{
id: 'iso-device-layout-follows-state-again',
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
+ '&& node --test --test-name-pattern="#711" test/iso-scene-render.test.mjs',
because: '#711 (owner, 2026-09-30): a device never moves because its HA state changed; a layout '
+ 'footprint that includes state text re-lays out the whole cluster on every toggle',
patches: [{
file: 'src/iso-scene-render.ts',
find: ' kind: \'device\', core, presentation: stateFreePresentation(presentation),',
replace: ' kind: \'device\', core, presentation,',
}],
},
{
id: 'wall-isolated-extra-discarded',
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '