test: prove optimizer micro-interval boundaries

Issue: #198
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-08-19 21:10:18 +03:00
parent 833e8e5472
commit a797752c89
2 changed files with 56 additions and 0 deletions
+35
View File
@@ -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: [{
+21
View File
@@ -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]] },