BG: drawn-plan paper follows the room contours, not their bounding box

Owner: the white backing must hug the ROOMS — an L-shaped house or detached
buildings grew a white square around the plan. Drawn plans now paper one
opaque shape per room (paperRoomShapes in logic.ts) in exactly the room's own
geometry — polygon points / rounded rect verbatim — so the union of the stack
is the paper: islands paint over their parent, open (virtual) boundaries
change nothing, and the scene bg_color / daynight sky reaches the exterior
walls, shows in the L's pocket and between buildings. Image plans keep the
backdrop-image rect (the canvas IS the paper). A live resize preview
(_rszPreview) feeds _renderCfg, so the paper moves WITH a dragged wall.
Static space-card follows the same contract. Paper is fill-only (stroke:none).

smoke_bg_color §11–13 rewritten: L-shaped + detached test rooms, paper-per-
room DOM checks, resize-preview wiring, pixel probes (acid in the pocket and
between buildings, none inside rooms); §13 injects the snapshot directly —
the module-level config-store cache made the old WS mock a no-op. Was 8 red
on the previous build, green now. docs/SUN.md + docs/TESTING.md contract
updated; unit test for paperRoomShapes.
This commit is contained in:
Matysh
2026-08-03 08:38:48 +03:00
parent a8bb145ffb
commit c024c6d75a
12 changed files with 398 additions and 261 deletions
+22 -1
View File
@@ -14,7 +14,7 @@ import {
roomFillModeOf,
contentUrl, chunk, referencedContentUrls, MAX_SIGN_PATHS,
interiorPoint,
segmentCm, formatLength, roomEdges, roomPoly, pointOnBoundary, pointStrictlyInside, roomsOverlap,
segmentCm, formatLength, roomEdges, roomPoly, paperRoomShapes, pointOnBoundary, pointStrictlyInside, roomsOverlap,
mergeRooms, splitRoom, polygonArea, closestPointOnBoundary, isActiveState, snapToWall, openingAmount, fillColorsOf, lerpColor, roomFillStyle, stateIcon, lightColorOf, isAlarmState, parseRoomRef, diffNewDevices, poleOfInaccessibility, runServiceFor, TOGGLE_SAFE_DOMAINS } from '../test-build/logic.js';
import {
iconFor, compileIconRules, isValidPattern, iconFromDeviceClasses,
@@ -959,3 +959,24 @@ test('tap action run: explicit-only, with runnable targets (owner spec 2026-07-2
// locks and alarms stay forbidden, run or not
assert.equal(resolveTapAction('toggle', undefined, 'lock'), 'info');
});
test('paperRoomShapes: paper follows room contours, one shape per room (owner 2026-08-03)', () => {
const L = [[100, 100], [600, 100], [600, 350], [350, 350], [350, 600], [100, 600]];
const rooms = [
{ id: 'L', poly: L }, // L-shaped: polygon, verbatim
{ id: 'q', x: 650, y: 650, w: 200, h: 100 }, // legacy rect: same rounded rect the room draws
{ id: 'bad' }, // no geometry -> no paper
];
const shapes = paperRoomShapes(rooms);
assert.equal(shapes.length, 2, 'one shape per room WITH geometry');
// the polygon uses exactly the room's own points string — same coordinates,
// so the paper can never peek past a wall
assert.deepEqual(shapes[0], { poly: L.map((p) => p.join(',')).join(' ') });
// the rect mirrors the room shape incl. its corner rounding rx
assert.deepEqual(shapes[1], { rect: { x: 650, y: 650, w: 200, h: 100, rx: 100 * 0.03 } });
// degenerate inputs never throw and produce no paper
assert.deepEqual(paperRoomShapes([]), []);
assert.deepEqual(paperRoomShapes(null), []);
assert.deepEqual(paperRoomShapes([{ poly: [[0, 0], [1, 1]] }]), [], 'a 2-point poly is not a surface');
});