mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
- settings.volumetric_view (General settings › Display, third switch) replaces the alpha entry, the header projection toggle and the phone-menu item; one rule for card, sidebar page and kiosk; editors stay Flat; backend accepts only a boolean, support package copies it. - Raised tiles in 2.5D View: no ring, rounded body min(0.275 D, 0.3 h), edge 0.1 D with the lab filters, ×1.12 size also in layout/collision, lift 0.075 D, one floor-shadow layer under all markers (theme × floor table), frames hugging tile and edge (Alert > Focus > Selected > Hover), forced colours without edge and shadow (src/iso-tiles.ts, styles/iso-tiles). - Soft sun wash instead of projected Flat wedges (src/iso-sun.ts): same gates and windows as sun_rays, length from elevation, parallelogram along the sun, tone and streaks by floor lightness, sill line. - Walls take the user's wall colour (top opaque, sides × .77/.68/.60); theme rules for walls/openings/labels removed; furniture uses the Flat stroke rule. - Smokes smoke_iso_tiles/iso_sun/iso_theme_walls/volumetric_setting, 16 new mutants, acceptance scenes STAGE6_ACCEPTANCE_SCENARIOS (golden with their Linux CI baselines), ACCEPTANCE.md side by side. Issue: #649 User-Visible: yes
129 lines
5.0 KiB
JavaScript
129 lines
5.0 KiB
JavaScript
/** Issue #258: both persisted midpoint keys resolve one lossless T-junction. */
|
|
import { readFileSync } from 'node:fs';
|
|
import { launch, checkAll, finish } from './serve.mjs';
|
|
|
|
const fixture = JSON.parse(readFileSync(
|
|
new URL('../test/fixtures/258-wall-key-roundtrip.json', import.meta.url), 'utf8',
|
|
));
|
|
const { page, browser } = await launch({ width: 1000, height: 860 }, 1);
|
|
|
|
const result = await page.evaluate(async (source) => {
|
|
const out = {};
|
|
const card = window.__card;
|
|
const root = () => card.shadowRoot || card.renderRoot;
|
|
const frame = () => new Promise((done) =>
|
|
requestAnimationFrame(() => requestAnimationFrame(done)));
|
|
const settle = async () => {
|
|
await card.updateComplete;
|
|
while (card._modeTransitionBusy) await frame();
|
|
await frame();
|
|
};
|
|
const update = async (structural = false) => {
|
|
if (structural) {
|
|
card._cfgEpoch++;
|
|
card._modelCache = null;
|
|
card._frame = null;
|
|
card._wallUnionCache = null;
|
|
card._physicalBodiesCache = null;
|
|
card._lightBarrierCache = null;
|
|
card._isoGeometryCache.clear();
|
|
}
|
|
card.requestUpdate();
|
|
await settle();
|
|
};
|
|
|
|
for (const [variant, key] of [
|
|
['canonical', source.canonical_key],
|
|
['affected', source.affected_key],
|
|
]) {
|
|
const space = structuredClone(source.space);
|
|
space.walls[0].key = key;
|
|
const cfg = {
|
|
...structuredClone(card._serverCfg),
|
|
spaces: [space],
|
|
markers: [],
|
|
};
|
|
card._serverCfg = structuredClone(cfg);
|
|
card._layout = {};
|
|
card._space = space.id;
|
|
await window.__hpHarnessProjection(card, 'flat');
|
|
card._setMode('plan');
|
|
card._tool = 'select';
|
|
await update(true);
|
|
|
|
const prefix = `${variant}_`;
|
|
const persisted = JSON.stringify(card._serverCfg.spaces[0]);
|
|
const path = root().querySelector('[data-hp="wall"]');
|
|
const planD = path?.getAttribute('d') || '';
|
|
const canonical = card._wallUnionGeometry();
|
|
const node = new DOMPoint(source.node[0] * 1000, source.node[1] * 1000);
|
|
const verticalArm = new DOMPoint(node.x, node.y - 12);
|
|
out[`${prefix}fixture_loaded`] = card._spaceModel()?.rooms.length === 2
|
|
&& card._spaceWalls.length === 2;
|
|
out[`${prefix}plan_uses_canonical_path`] = !!planD && canonical?.d === planD;
|
|
out[`${prefix}node_remains_filled`] = !!path?.isPointInFill(node);
|
|
out[`${prefix}incident_arm_remains_filled`] = !!path?.isPointInFill(verticalArm);
|
|
out[`${prefix}paper_remains_solid`] = !!canonical?.paperD
|
|
&& !!root().querySelector('.hp-paper');
|
|
|
|
const model = card._spaceModel();
|
|
const polys = model.rooms.map((room) => ({ r: room, poly: room.poly }));
|
|
const barriers = card._lightBarriers(model, polys, card._physicalBodiesR(model));
|
|
out[`${prefix}light_uses_same_masonry`] = barriers.masonryGeometry.flat(2).length > 0
|
|
&& barriers.occluders.length > 0 && !!barriers.fingerprint;
|
|
|
|
card._setMode('view');
|
|
await update(false);
|
|
out[`${prefix}view_matches_plan`] = root().querySelector('[data-hp="wall"]')
|
|
?.getAttribute('d') === planD;
|
|
card._hoverRoom = { space: space.id, room: model.rooms[0] };
|
|
const hover = card._roomHoverPaths(model);
|
|
out[`${prefix}clean_floor_consumer_present`] = !!hover?.fillD && !!hover.outlineD;
|
|
card._hoverRoom = null;
|
|
|
|
const kioskBefore = card._config.kiosk;
|
|
card._config.kiosk = true;
|
|
await update(false);
|
|
out[`${prefix}kiosk_matches_plan`] = root().querySelector('[data-hp="wall"]')
|
|
?.getAttribute('d') === planD;
|
|
card._config.kiosk = kioskBefore;
|
|
await update(false);
|
|
|
|
await customElements.whenDefined('houseplan-space-card');
|
|
const staticCard = document.createElement('houseplan-space-card');
|
|
const baseCall = card.hass.callWS.bind(card.hass);
|
|
staticCard.setConfig({
|
|
type: 'custom:houseplan-space-card', space: space.id, show_button: false,
|
|
});
|
|
staticCard.hass = { ...card.hass, callWS: async (message) => {
|
|
if (message.type === 'houseplan/config/get') {
|
|
return { config: structuredClone(cfg), rev: 1 };
|
|
}
|
|
if (message.type === 'houseplan/layout/get') return { layout: {}, rev: 1 };
|
|
return baseCall(message);
|
|
} };
|
|
document.body.appendChild(staticCard);
|
|
const started = Date.now();
|
|
while (!staticCard.renderRoot?.querySelector('[data-hp="wall"]')
|
|
&& Date.now() - started < 6000) {
|
|
await new Promise((done) => setTimeout(done, 60));
|
|
}
|
|
await staticCard.updateComplete;
|
|
out[`${prefix}static_matches_plan`] = staticCard.renderRoot
|
|
?.querySelector('[data-hp="wall"]')?.getAttribute('d') === planD;
|
|
staticCard.remove();
|
|
await window.__hpHarnessProjection(card, 'iso');
|
|
await window.__hpEnsureHarnessIsoRuntime(card);
|
|
await update(false);
|
|
out[`${prefix}hidden_iso_uses_masonry`] = !!root().querySelector('[data-hp="iso-walls"]')
|
|
&& card._isoSource().build().walls.flat(2).length > 0;
|
|
out[`${prefix}render_never_writes_config`] = JSON.stringify(
|
|
card._serverCfg.spaces[0],
|
|
) === persisted;
|
|
}
|
|
return out;
|
|
}, fixture);
|
|
|
|
checkAll(result);
|
|
await finish(browser, result);
|