mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
test: prove optimizer micro-interval boundaries
Issue: #198 User-Visible: no
This commit is contained in:
@@ -124,6 +124,41 @@ test('micro-interval cleanup preserves ambiguous and topological boundaries', ()
|
||||
);
|
||||
});
|
||||
|
||||
test('micro-interval cleanup is endpoint, input-order and room-order invariant without mutation', () => {
|
||||
const fixture = microIntervalFixture();
|
||||
const detached = {
|
||||
id: 'r2', poly: [[0.85, 0.85], [0.95, 0.85], [0.95, 0.95], [0.85, 0.95]],
|
||||
};
|
||||
const semantic = (walls) => walls.map((wall) => {
|
||||
const ends = [wall.a, wall.b]
|
||||
.map((point) => point.map((value) => Number(value.toFixed(12))).join(','))
|
||||
.sort();
|
||||
return `${ends[0]}|${ends[1]}|${wall.cm}`;
|
||||
}).sort();
|
||||
|
||||
const baseline = collapseIsolatedWallThicknessIslands(
|
||||
[fixture.rooms[0], detached], fixture.walls, [], S, 5, S,
|
||||
);
|
||||
const reversedMiddle = {
|
||||
...fixture.walls[1],
|
||||
a: [...fixture.walls[1].b],
|
||||
b: [...fixture.walls[1].a],
|
||||
};
|
||||
const rooms = [detached, {
|
||||
...fixture.rooms[0], poly: [...fixture.rooms[0].poly].reverse().map((point) => [...point]),
|
||||
}];
|
||||
const walls = [fixture.walls[2], reversedMiddle, fixture.walls[0]]
|
||||
.map((wall) => ({ ...wall, a: [...wall.a], b: [...wall.b] }));
|
||||
const beforeRooms = structuredClone(rooms);
|
||||
const beforeWalls = structuredClone(walls);
|
||||
|
||||
const result = collapseIsolatedWallThicknessIslands(rooms, walls, [], S, 5, S);
|
||||
assert.deepEqual(rooms, beforeRooms, 'helper must not mutate rooms or their winding');
|
||||
assert.deepEqual(walls, beforeWalls, 'helper must not mutate the walls array or entries');
|
||||
assert.deepEqual(semantic(result), semantic(baseline));
|
||||
assert.equal(result.find((wall) => wall.a?.[0] === 0.5 || wall.b?.[0] === 0.5)?.cm, 22);
|
||||
});
|
||||
|
||||
test('optimizePlans migrates, aligns and canonicalises idempotently', () => {
|
||||
const config = {
|
||||
spaces: [{
|
||||
|
||||
@@ -365,6 +365,27 @@ test('different solid thicknesses remain separate atomic keys', () => {
|
||||
assert.ok(!next.some((w) => w.key === wallKey([5, 0], [5, 10], pitch)));
|
||||
});
|
||||
|
||||
test('lossless wall helpers preserve an isolated sub-half-step thickness island outside Optimize', () => {
|
||||
const y = 0.2, split = 0.5, length = pitch / 3;
|
||||
const rooms = [{ id: 'r1', poly: [[0.2, y], [0.8, y], [0.8, 0.8], [0.2, 0.8]] }];
|
||||
const walls = [
|
||||
{ key: wallKey([0.2, y], [split, y], pitch), a: [0.2, y], b: [split, y], cm: 22 },
|
||||
{ key: wallKey([split, y], [split + length, y], pitch),
|
||||
a: [split, y], b: [split + length, y], cm: 15 },
|
||||
{ key: wallKey([split + length, y], [0.8, y], pitch),
|
||||
a: [split + length, y], b: [0.8, y], cm: 22 },
|
||||
];
|
||||
const before = structuredClone(walls);
|
||||
const byKey = (entries) => structuredClone(entries)
|
||||
.sort((left, right) => left.key.localeCompare(right.key));
|
||||
|
||||
const normalized = normalizeWallIntervals(rooms, walls, [], pitch, cellCm, GRID_PITCH);
|
||||
const degraded = degradeWalls(walls, rooms, pitch);
|
||||
assert.deepEqual(byKey(normalized), byKey(before), 'runtime/editor normalization remains lossless');
|
||||
assert.deepEqual(byKey(degraded), byKey(before), 'runtime/editor degradation must not infer island removal');
|
||||
assert.deepEqual(walls, before, 'direct lossless helpers must not mutate persisted input');
|
||||
});
|
||||
|
||||
test('closing the sole geometric split preserves different thicknesses', () => {
|
||||
const rooms = [
|
||||
{ id: 'a', poly: [[0, 0], [5, 0], [5, 10], [0, 10]] },
|
||||
|
||||
Reference in New Issue
Block a user