mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 05:41:34 +00:00
Preserve exact light fields while culling provably distant occluders, reusing physical tube paths across camera updates and excluding LED emitter data from aligned architectural fingerprints. Cover output equivalence, in-place invalidation, resize tags and hyphenated benchmark chunk hashes. Issue: #788 User-Visible: yes
89 lines
4.1 KiB
JavaScript
89 lines
4.1 KiB
JavaScript
// #788: camera renders reuse tube geometry without freezing live state or edits.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { ledFrameFor, ledStripePath, releaseLed } from '../test-build/led-strip-runtime.js';
|
|
import { LED_THICKNESS_D, pathD, visibleStripPath } from '../test-build/led-strip-geometry.js';
|
|
|
|
const strip = () => ({ id: 'led', marker: 'lamp', points: [[2, 1], [6, 1], [6, 4]] });
|
|
const wallFaces = () => ({
|
|
faces: [{ a: [0, 1], b: [10, 1] }],
|
|
inside: ([x, y]) => x > 0 && x < 10 && y < 1,
|
|
epsilon: 1e-5,
|
|
});
|
|
const fresh = (shape, faces, diameter) => {
|
|
const t = LED_THICKNESS_D * diameter;
|
|
const path = visibleStripPath(shape.points, faces, t / 2);
|
|
return { t, path, d: pathD(path) };
|
|
};
|
|
|
|
test('#788: repeated camera reads skip tube classification and reuse identical SVG geometry', () => {
|
|
const shape = strip(), faces = wallFaces();
|
|
let classifications = 0;
|
|
faces.near = () => { classifications++; return faces.faces; };
|
|
const cached = ledStripePath(shape, faces, 4);
|
|
assert.equal(classifications, 1);
|
|
for (let step = 0; step < 100; step++) assert.equal(ledStripePath(shape, faces, 4), cached);
|
|
assert.equal(classifications, 1, 'camera reads must not rerun face classification');
|
|
assert.deepEqual(cached, fresh(shape, faces, 4), 'cached outline and hit geometry equal the uncached derivation');
|
|
});
|
|
|
|
test('#788: face context, epsilon, diameter and exact point edits invalidate the tube cache', () => {
|
|
const shape = strip();
|
|
let faces = wallFaces(), diameter = 4;
|
|
let previous = ledStripePath(shape, faces, diameter);
|
|
const changed = () => {
|
|
const value = ledStripePath(shape, faces, diameter);
|
|
assert.notEqual(value, previous);
|
|
assert.deepEqual(value, fresh(shape, faces, diameter));
|
|
previous = value;
|
|
};
|
|
diameter = 8;
|
|
changed();
|
|
faces = null;
|
|
changed();
|
|
faces = wallFaces();
|
|
changed();
|
|
faces.epsilon *= 2;
|
|
changed();
|
|
shape.points[1][0] = 7;
|
|
changed();
|
|
shape.points = [[1, 2], [8, 2], [8, 5]];
|
|
changed();
|
|
shape.points.push([9, 6]);
|
|
changed();
|
|
const replacement = { ...shape, points: shape.points.map((point) => [...point]) };
|
|
assert.notEqual(ledStripePath(replacement, faces, diameter), previous, 'same id in a new frame owns a new weak cache key');
|
|
});
|
|
|
|
const ownerFixture = () => ({
|
|
isConnected: true,
|
|
_renderDevices: [{ id: 'lamp', name: 'Lamp', primary: 'light.led', entities: ['light.led'],
|
|
space: 's', marker: { id: 'lamp', binding: 'device:lamp' } }],
|
|
_renderPlanHass: { states: { 'light.led': { state: 'on', attributes: {} } } },
|
|
_fillColors: { glow_light: { c: '#ffd27b', a: 0.7 } },
|
|
_cellCm: 5, _gridPitch: 1000 / 240, _config: { icon_size: 3.4 },
|
|
_mode: 'view', _showAll: false,
|
|
_pointInRoom: () => false,
|
|
});
|
|
const spaceFixture = () => ({ id: 's', vb: [0, 0, 1000, 1000], rooms: [],
|
|
led_strips: [{ id: 'led', marker: 'lamp', points: [[0.1, 0.1], [0.4, 0.1]] }] });
|
|
|
|
test('#788: new HA frames keep live state and evict the old frame geometry; disconnect releases it', () => {
|
|
const owner = ownerFixture(), space = spaceFixture();
|
|
const first = ledFrameFor(owner, space, true);
|
|
const firstStrip = first.views[0].strip;
|
|
const firstPath = ledStripePath(firstStrip, first.faces, first.d);
|
|
assert.equal(ledFrameFor(owner, space, true), first, 'unchanged camera inputs reuse the frame');
|
|
assert.equal(ledStripePath(firstStrip, first.faces, first.d), firstPath);
|
|
owner._renderPlanHass = { states: { 'light.led': { state: 'off', attributes: {} } } };
|
|
const second = ledFrameFor(owner, space, true);
|
|
assert.notEqual(second, first);
|
|
assert.equal(second.views[0].state, 'off', 'geometry caching must not freeze the device state');
|
|
assert.notEqual(ledStripePath(firstStrip, first.faces, first.d), firstPath, 'old frame entry was explicitly evicted');
|
|
const secondStrip = second.views[0].strip;
|
|
const secondPath = ledStripePath(secondStrip, second.faces, second.d);
|
|
assert.deepEqual(secondPath, firstPath, 'state changes preserve the physical tube');
|
|
releaseLed(owner);
|
|
assert.notEqual(ledStripePath(secondStrip, second.faces, second.d), secondPath, 'disconnect explicitly evicts current frame entries');
|
|
});
|