mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
98 lines
4.2 KiB
JavaScript
98 lines
4.2 KiB
JavaScript
/** Issue #177/#478: overlap guards operate on canonical wall partitions. */
|
|
import { launch, checkAll, finish } from './serve.mjs';
|
|
|
|
const { page, browser } = await launch({ width: 1100, height: 820 }, 1);
|
|
|
|
const result = await page.evaluate(async () => {
|
|
const out = {};
|
|
const card = window.__card;
|
|
const update = async () => { card.requestUpdate(); await card.updateComplete; };
|
|
const ring = (x0, y0, x1, y1) => [[x0, y0], [x1, y0], [x1, y1], [x0, y1]];
|
|
const closed = (points) => [...points, [...points[0]]];
|
|
const roomAt = (name, points) => ({
|
|
id: `r-${name}`, name,
|
|
poly: points.map((point) => [point[0] / 1000, point[1] / 1000]),
|
|
});
|
|
const space = () => card._serverCfg.spaces[0];
|
|
const reset = async (rooms = []) => {
|
|
card._serverCfg = {
|
|
spaces: [{
|
|
id: 'overlap', title: 'Overlap', cell_cm: 5, view_box: [0, 0, 1, 0.7],
|
|
rooms: structuredClone(rooms), openings: [], partitions: [], wall_columns: [],
|
|
}],
|
|
markers: [], settings: {},
|
|
};
|
|
card._space = 'overlap'; card._layout = {}; card._cfgEpoch++;
|
|
card._modelCache = null; card._frame = null; card._geometryHistory.clear();
|
|
card._path = []; card._activeWallChainId = null;
|
|
card._activeWallChainPartitionIds = []; card._wallChainSegmentCms = [];
|
|
card._wallFaceBatch = null; card._roomDialog = false;
|
|
card._setMode('plan'); card._tool = 'draw'; await update();
|
|
};
|
|
const click = async (x, y) => {
|
|
const stage = (card.shadowRoot || card.renderRoot).querySelector('.stage');
|
|
const rect = stage.getBoundingClientRect();
|
|
const view = card._viewOr(card._baseVb());
|
|
card._markupClick(new MouseEvent('click', {
|
|
clientX: rect.left + ((x - view.x) / view.w) * rect.width,
|
|
clientY: rect.top + ((y - view.y) / view.h) * rect.height,
|
|
bubbles: true,
|
|
}));
|
|
await update();
|
|
};
|
|
const draw = async (points) => {
|
|
for (const point of points) await click(point[0], point[1]);
|
|
};
|
|
|
|
const base = ring(200, 200, 400, 400);
|
|
await reset([roomAt('base', base)]);
|
|
await draw(closed(base));
|
|
out.duplicateOffersNothing = !card._wallFaceBatch && !card._roomDialog;
|
|
out.duplicateKeepsOneRoom = space().rooms.length === 1;
|
|
|
|
await reset([roomAt('base', base)]);
|
|
const roomShape = () => (space().rooms || []).map(({ wall_ids: _wallIds, ...room }) => room);
|
|
const configBefore = JSON.stringify(roomShape());
|
|
const drawnRing = ring(300, 300, 500, 500);
|
|
await draw(closed(drawnRing));
|
|
const offered = card._wallFaceBatch?.candidates || [];
|
|
out.partialOverlapDecomposesIntoFaces = offered.length === 3;
|
|
out.partialOverlapNeverOffersDrawnRing = offered.length > 0
|
|
&& offered.every((candidate) => candidate.area < 40000 - 1);
|
|
out.partialOverlapFacesSumToUnion = Math.abs(
|
|
offered.reduce((sum, candidate) => sum + candidate.area, 0) - 70000,
|
|
) < 1;
|
|
out.partialOverlapKeepsRoomsUntilDecision = JSON.stringify(roomShape()) === configBefore;
|
|
|
|
await reset([roomAt('outer', ring(100, 100, 600, 600))]);
|
|
await draw(closed(ring(200, 200, 300, 300)));
|
|
out.innerNestingIsOffered = card._wallFaceBatch?.candidates.length === 1;
|
|
card._nameSel = 'Island'; card._saveRoom(); await update();
|
|
out.innerNestingCreatesIslandRoom = space().rooms.length === 2
|
|
&& space().rooms.some((room) => room.name === 'Island');
|
|
|
|
await reset([roomAt('inner', ring(300, 300, 400, 400))]);
|
|
await draw(closed(ring(150, 150, 600, 600)));
|
|
out.outerNestingIsOffered = card._wallFaceBatch?.candidates.length === 1;
|
|
card._nameSel = 'Around'; card._saveRoom(); await update();
|
|
out.outerNestingCreatesRoom = space().rooms.length === 2
|
|
&& space().rooms.some((room) => room.name === 'Around');
|
|
|
|
await reset([roomAt('base', base)]);
|
|
const clash = ring(300, 300, 500, 500);
|
|
card._wallFaceBatch = {
|
|
candidates: [{ key: 'manual', area: 1, ring: clash }],
|
|
index: 0,
|
|
decisions: [{ candidate: { key: 'manual', area: 1, ring: clash }, create: true }],
|
|
activePath: closed(clash), activeCms: [15, 15, 15, 15], activePartitionIds: [],
|
|
};
|
|
const roomsBeforeGuard = space().rooms.length;
|
|
card._applyWallFaceBatch(); await update();
|
|
out.applyGuardRejectsOverlap = space().rooms.length === roomsBeforeGuard;
|
|
out.applyGuardClosesBatch = !card._wallFaceBatch;
|
|
return out;
|
|
});
|
|
|
|
checkAll(result);
|
|
await finish(browser, result);
|