mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
File diff suppressed because one or more lines are too long
Vendored
+34
-34
File diff suppressed because one or more lines are too long
+30
-33
@@ -1617,6 +1617,7 @@ class HouseplanCard extends LitElement {
|
|||||||
private _cleanFloorCache = new Map<string, {
|
private _cleanFloorCache = new Map<string, {
|
||||||
floor: number[][]; geom: any; path: string; area: number;
|
floor: number[][]; geom: any; path: string; area: number;
|
||||||
}>();
|
}>();
|
||||||
|
private _innerContourCache = new Map<string, number[][] | null>();
|
||||||
private _glowClipCache = new Map<string, GlowClipGeometry | null>();
|
private _glowClipCache = new Map<string, GlowClipGeometry | null>();
|
||||||
/** Redacted, bounded dedupe for numerical floor fallbacks (#218). */
|
/** Redacted, bounded dedupe for numerical floor fallbacks (#218). */
|
||||||
private _glowGeometryWarnings = new Set<string>();
|
private _glowGeometryWarnings = new Set<string>();
|
||||||
@@ -11590,6 +11591,26 @@ class HouseplanCard extends LitElement {
|
|||||||
).map((b) => [b.a[0], b.a[1], b.b[0], b.b[1]]);
|
).map((b) => [b.a[0], b.a[1], b.b[0], b.b[1]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Structural room face shared by every fill surface in the same frame. */
|
||||||
|
private _innerRoomContour(
|
||||||
|
space: SpaceModel,
|
||||||
|
roomId: string,
|
||||||
|
openCuts: number[][] = this._openCuts(),
|
||||||
|
roomWalls = this._wallUnionGeometry()?.roomGeom,
|
||||||
|
): number[][] | null {
|
||||||
|
const cutsKey = openCuts.map((cut) => cut.join(',')).join(';');
|
||||||
|
const key = `${space.id}|${this._cfgEpoch}|${roomId}|${cutsKey}`;
|
||||||
|
const cached = lruRead(this._innerContourCache, key);
|
||||||
|
if (cached.hit) return cached.value;
|
||||||
|
const value = innerContourForRoom(
|
||||||
|
space.rooms, roomId, this._spaceWalls, openCuts,
|
||||||
|
this._wallKeyPitch, this._cellCm, this._gridPitch, NORM_W,
|
||||||
|
roomWalls,
|
||||||
|
);
|
||||||
|
lruWrite(this._innerContourCache, key, value, 600);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ATOMIC wall stretch under the cursor for the wall-thickness tool.
|
* The ATOMIC wall stretch under the cursor for the wall-thickness tool.
|
||||||
*
|
*
|
||||||
@@ -12024,11 +12045,7 @@ class HouseplanCard extends LitElement {
|
|||||||
const ownPoly = polys.get(room) || null;
|
const ownPoly = polys.get(room) || null;
|
||||||
if (!fill || !ownPoly) return nothing;
|
if (!fill || !ownPoly) return nothing;
|
||||||
const floor = this._spaceWalls.length && room.id
|
const floor = this._spaceWalls.length && room.id
|
||||||
? (innerContourForRoom(
|
? (this._innerRoomContour(space, room.id, openCuts, roomWalls) || ownPoly)
|
||||||
space.rooms, room.id, this._spaceWalls, openCuts,
|
|
||||||
this._wallKeyPitch, this._cellCm, this._gridPitch, NORM_W,
|
|
||||||
roomWalls,
|
|
||||||
) || ownPoly)
|
|
||||||
: ownPoly;
|
: ownPoly;
|
||||||
const otherPolys = space.rooms
|
const otherPolys = space.rooms
|
||||||
.filter((other) => other !== room)
|
.filter((other) => other !== room)
|
||||||
@@ -12229,11 +12246,7 @@ class HouseplanCard extends LitElement {
|
|||||||
const walls = this._spaceWalls;
|
const walls = this._spaceWalls;
|
||||||
const roomWalls = this._wallUnionGeometry()?.roomGeom;
|
const roomWalls = this._wallUnionGeometry()?.roomGeom;
|
||||||
const floor = walls.length && room.id
|
const floor = walls.length && room.id
|
||||||
? (innerContourForRoom(
|
? (this._innerRoomContour(space, room.id, allOpenCuts, roomWalls) || poly)
|
||||||
space.rooms, room.id, walls, allOpenCuts,
|
|
||||||
this._wallKeyPitch, this._cellCm, this._gridPitch, NORM_W,
|
|
||||||
roomWalls,
|
|
||||||
) || poly)
|
|
||||||
: poly;
|
: poly;
|
||||||
const contours: { axis: number[][]; face: number[][] }[] = [{ axis: poly, face: floor }];
|
const contours: { axis: number[][]; face: number[][] }[] = [{ axis: poly, face: floor }];
|
||||||
for (const island of islandPolys) {
|
for (const island of islandPolys) {
|
||||||
@@ -15301,11 +15314,7 @@ class HouseplanCard extends LitElement {
|
|||||||
const roomWalls = this._wallUnionGeometry()?.roomGeom;
|
const roomWalls = this._wallUnionGeometry()?.roomGeom;
|
||||||
if (walls.length) {
|
if (walls.length) {
|
||||||
for (const r of rooms) {
|
for (const r of rooms) {
|
||||||
const inn = innerContourForRoom(
|
const inn = this._innerRoomContour(space, r.id, openCuts, roomWalls);
|
||||||
space.rooms, r.id, walls, openCuts,
|
|
||||||
this._wallKeyPitch, this._cellCm, this._gridPitch, NORM_W,
|
|
||||||
roomWalls,
|
|
||||||
);
|
|
||||||
if (inn) innerByRoom[r.id] = inn;
|
if (inn) innerByRoom[r.id] = inn;
|
||||||
}
|
}
|
||||||
for (const o of windows) {
|
for (const o of windows) {
|
||||||
@@ -16691,11 +16700,7 @@ class HouseplanCard extends LitElement {
|
|||||||
const roomWalls = this._wallUnionGeometry()?.roomGeom;
|
const roomWalls = this._wallUnionGeometry()?.roomGeom;
|
||||||
const enabledClip = enabled.length === polys.length ? null : enabled.map(({ r, poly }) => {
|
const enabledClip = enabled.length === polys.length ? null : enabled.map(({ r, poly }) => {
|
||||||
const floorPoly = walls.length && r.id
|
const floorPoly = walls.length && r.id
|
||||||
? (innerContourForRoom(
|
? (this._innerRoomContour(space, r.id, openCuts, roomWalls) || poly)
|
||||||
space.rooms, r.id, walls, openCuts,
|
|
||||||
this._wallKeyPitch, this._cellCm, this._gridPitch, NORM_W,
|
|
||||||
roomWalls,
|
|
||||||
) || poly)
|
|
||||||
: poly;
|
: poly;
|
||||||
const clean = this._cleanFloor(r, floorPoly, space).path;
|
const clean = this._cleanFloor(r, floorPoly, space).path;
|
||||||
const holes = islandsOf(
|
const holes = islandsOf(
|
||||||
@@ -17638,6 +17643,8 @@ class HouseplanCard extends LitElement {
|
|||||||
// audit L1: hoisted out of the per-room map — these depend on the
|
// audit L1: hoisted out of the per-room map — these depend on the
|
||||||
// config, not on entity state, and were recomputed per room.
|
// config, not on entity state, and were recomputed per room.
|
||||||
const allZeroCuts = this._openCuts();
|
const allZeroCuts = this._openCuts();
|
||||||
|
const allThickCuts = this._thickWallCuts();
|
||||||
|
const roomWallGeometry = this._wallUnionGeometry()?.roomGeom;
|
||||||
const polyCache = new Map<any, number[][] | null>();
|
const polyCache = new Map<any, number[][] | null>();
|
||||||
const polyOf = (rr: any) => {
|
const polyOf = (rr: any) => {
|
||||||
if (!polyCache.has(rr)) polyCache.set(rr, roomPoly(rr));
|
if (!polyCache.has(rr)) polyCache.set(rr, roomPoly(rr));
|
||||||
@@ -17694,18 +17701,13 @@ class HouseplanCard extends LitElement {
|
|||||||
) <= this._gridPitch * 0.08);
|
) <= this._gridPitch * 0.08);
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
const thickCuts = !isPicked ? this._thickWallCuts() : [];
|
const thickCuts = !isPicked ? allThickCuts : [];
|
||||||
const edgeCuts = zeroCuts.concat(thickCuts);
|
const edgeCuts = zeroCuts.concat(thickCuts);
|
||||||
if (edgeCuts.length) cls += ' noedge';
|
if (edgeCuts.length) cls += ' noedge';
|
||||||
// island rooms punch holes in their parent's fill (evenodd)
|
// island rooms punch holes in their parent's fill (evenodd)
|
||||||
const walls = this._spaceWalls;
|
const walls = this._spaceWalls;
|
||||||
const fillPoly = (walls.length && r.id && myPoly)
|
const fillPoly = (walls.length && r.id && myPoly)
|
||||||
? (innerContourForRoom(
|
? (this._innerRoomContour(space, r.id, allZeroCuts, roomWallGeometry) || myPoly)
|
||||||
space.rooms, r.id, walls,
|
|
||||||
allZeroCuts,
|
|
||||||
this._wallKeyPitch, this._cellCm, this._gridPitch, NORM_W,
|
|
||||||
this._wallUnionGeometry()?.roomGeom,
|
|
||||||
) || myPoly)
|
|
||||||
: myPoly;
|
: myPoly;
|
||||||
const holes = fillPoly ? islandsOf(fillPoly, otherPolys(r)) : [];
|
const holes = fillPoly ? islandsOf(fillPoly, otherPolys(r)) : [];
|
||||||
const pathD = (pts: number[][]) =>
|
const pathD = (pts: number[][]) =>
|
||||||
@@ -18879,12 +18881,7 @@ class HouseplanCard extends LitElement {
|
|||||||
if (!space) return null;
|
if (!space) return null;
|
||||||
const walls = this._spaceWalls;
|
const walls = this._spaceWalls;
|
||||||
const floor = walls.length && r.id
|
const floor = walls.length && r.id
|
||||||
? (innerContourForRoom(
|
? (this._innerRoomContour(space, r.id) || poly)
|
||||||
space.rooms, r.id, walls,
|
|
||||||
this._openCuts(),
|
|
||||||
this._wallKeyPitch, this._cellCm, this._gridPitch, NORM_W,
|
|
||||||
this._wallUnionGeometry()?.roomGeom,
|
|
||||||
) || poly)
|
|
||||||
: poly;
|
: poly;
|
||||||
const clean = this._cleanFloor(r, floor);
|
const clean = this._cleanFloor(r, floor);
|
||||||
const cmPerUnit = this._cellCm / this._gridPitch;
|
const cmPerUnit = this._cellCm / this._gridPitch;
|
||||||
|
|||||||
@@ -64,3 +64,46 @@ test('contract accepts recent optional fields only when their runtime type is va
|
|||||||
/invalid private API types: _cleanFloorCache:map/,
|
/invalid private API types: _cleanFloorCache:map/,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('view render resolves structural wall cuts once per frame, not once per room', () => {
|
||||||
|
const source = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
|
||||||
|
const start = source.indexOf('const allZeroCuts = this._openCuts();');
|
||||||
|
const end = source.indexOf('${this._renderOpeningTunnelFills(space, roomFills)}', start);
|
||||||
|
assert.ok(start >= 0 && end > start, 'view room-render block is present');
|
||||||
|
const roomRender = source.slice(start, end);
|
||||||
|
assert.match(roomRender, /const allThickCuts = this\._thickWallCuts\(\);/);
|
||||||
|
assert.match(roomRender, /const roomWallGeometry = this\._wallUnionGeometry\(\)\?\.roomGeom;/);
|
||||||
|
assert.equal(
|
||||||
|
[...roomRender.matchAll(/this\._thickWallCuts\(\)/g)].length,
|
||||||
|
1,
|
||||||
|
'the structural resolver must stay outside the room map',
|
||||||
|
);
|
||||||
|
assert.match(roomRender, /const thickCuts = !isPicked \? allThickCuts : \[\];/);
|
||||||
|
assert.equal(
|
||||||
|
[...roomRender.matchAll(/this\._wallUnionGeometry\(\)/g)].length,
|
||||||
|
1,
|
||||||
|
'the wall union lookup must stay outside the room map',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('room inner faces are structurally cached and shared by both fill layers', () => {
|
||||||
|
const source = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
|
||||||
|
const helperStart = source.indexOf('private _innerRoomContour(');
|
||||||
|
const helperEnd = source.indexOf('\n /**', helperStart);
|
||||||
|
assert.ok(helperStart >= 0 && helperEnd > helperStart, 'inner-contour cache helper is present');
|
||||||
|
const helper = source.slice(helperStart, helperEnd);
|
||||||
|
assert.match(helper, /lruRead\(this\._innerContourCache, key\)/);
|
||||||
|
assert.match(helper, /lruWrite\(this\._innerContourCache, key, value, 600\)/);
|
||||||
|
|
||||||
|
const glowStart = source.indexOf('private _renderGlowBaseRooms(');
|
||||||
|
const glowEnd = source.indexOf('\n private _renderWallBodies(', glowStart);
|
||||||
|
const glowBase = source.slice(glowStart, glowEnd);
|
||||||
|
assert.equal([...glowBase.matchAll(/innerContourForRoom\(/g)].length, 0);
|
||||||
|
assert.match(glowBase, /this\._innerRoomContour\(space, room\.id, openCuts, roomWalls\)/);
|
||||||
|
|
||||||
|
const viewStart = source.indexOf('const allZeroCuts = this._openCuts();');
|
||||||
|
const viewEnd = source.indexOf('${this._renderOpeningTunnelFills(space, roomFills)}', viewStart);
|
||||||
|
const viewRooms = source.slice(viewStart, viewEnd);
|
||||||
|
assert.equal([...viewRooms.matchAll(/innerContourForRoom\(/g)].length, 0);
|
||||||
|
assert.match(viewRooms, /this\._innerRoomContour\(space, r\.id, allZeroCuts, roomWallGeometry\)/);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user