mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
@@ -394,12 +394,28 @@ test('doorway spill golden exposes the opaque-fill failure mode from issue 71',
|
||||
assert.equal(scenario.warmPixelRegion?.x >= 0.5, true);
|
||||
});
|
||||
|
||||
test('stepped exterior PDF golden pins the complete reconstructable dimension chain', () => {
|
||||
const scenario = GOLDEN_SCENARIOS.find((item) =>
|
||||
item.id === 'pdf-export-stepped-dimensions-light');
|
||||
assert.ok(scenario);
|
||||
assert.equal(scenario.pdfExport, true);
|
||||
assert.equal(scenario.pdfSteppedExterior, true);
|
||||
assert.deepEqual(scenario.pdfSemantic.requiredDimensionLabels,
|
||||
['1.20 m', '2.40 m', '3.60 m', '3.75 m', '7.35 m', '9.75 m']);
|
||||
assert.equal(scenario.pdfSemantic.maxDimensionAxisErrorDeg, 0.5);
|
||||
const fixture = prepareGoldenFixture(scenario);
|
||||
const space = fixture.config.spaces.find((item) => item.id === scenario.space);
|
||||
assert.equal(space.rooms[0].poly.length, 8);
|
||||
assert.equal(space.wall_segments.length, 8);
|
||||
assert.equal(space.wall_segments.every((wall) => wall.cm === 15), true);
|
||||
});
|
||||
|
||||
test('sun-ray golden requires browser-painted light from a state-only sun entity', () => {
|
||||
const scenario = GOLDEN_SCENARIOS.find((item) => item.id === 'lighting-sun-window-state-only-dark');
|
||||
assert.ok(scenario);
|
||||
const fixture = prepareGoldenFixture(scenario);
|
||||
const space = fixture.config.spaces.find((item) => item.id === scenario.space);
|
||||
assert.equal(GOLDEN_MATRIX_VERSION, 58);
|
||||
assert.equal(GOLDEN_MATRIX_VERSION, 59);
|
||||
assert.equal(space.settings.sun_rays, true);
|
||||
assert.equal(scenario.northDeg, 90,
|
||||
'the sign-sensitive golden must keep a non-zero north direction');
|
||||
|
||||
@@ -74,6 +74,38 @@ test('allowed extension start is symmetric on every outer and hole boundary', ()
|
||||
}
|
||||
});
|
||||
|
||||
test('external dimension extension may leave its own stepped corner but never re-enter', () => {
|
||||
const step = [[0, 0], [10, 0], [10, 3], [12, 3], [12, 6], [10, 6], [10, 10], [0, 10]];
|
||||
const stepSolid = (point) => pointInRing(point, step);
|
||||
const ownExit = { allowStartExit: true };
|
||||
|
||||
assert.equal(pdfSegmentTouchesGeometry(
|
||||
[10, 3], [14, 3], [step], stepSolid, ownExit,
|
||||
), false, 'the source prefix may follow the incident facade step before reaching free space');
|
||||
assert.equal(pdfSegmentTouchesGeometry(
|
||||
[10, 3], [14, 3], [step], stepSolid, { allowStartBoundary: true },
|
||||
), true, 'the ordinary strict start-boundary mode still rejects collinear overlap');
|
||||
assert.equal(pdfSegmentTouchesGeometry(
|
||||
[0, 5], [12, 5], [square], squareSolid, ownExit,
|
||||
), false, 'the source prefix may cross its contiguous wall body once before exiting');
|
||||
assert.equal(pdfSegmentTouchesGeometry(
|
||||
[0, 5], [10, 5], [square], squareSolid, ownExit,
|
||||
), true, 'a segment ending on the exit boundary never reaches a free interval');
|
||||
|
||||
const foreignWall = [[13, 2], [15, 2], [15, 4], [13, 4]];
|
||||
const combinedSolid = (point) => stepSolid(point) || pointInRing(point, foreignWall);
|
||||
assert.equal(pdfSegmentTouchesGeometry(
|
||||
[10, 3], [16, 3], [step, foreignWall], combinedSolid, ownExit,
|
||||
), true, 'a second wall after the first free interval remains a collision');
|
||||
const tangentWall = [[13, 3], [15, 4], [15, 5], [13, 4]];
|
||||
assert.equal(pdfSegmentTouchesGeometry(
|
||||
[10, 3], [16, 3], [step, tangentWall], combinedSolid, ownExit,
|
||||
), true, 'a later point contact remains a collision even without a solid interval');
|
||||
assert.equal(pdfSegmentTouchesGeometry(
|
||||
[11, 4], [14, 4], [step], stepSolid, ownExit,
|
||||
), true, 'only a segment starting at its source boundary receives the exception');
|
||||
});
|
||||
|
||||
test('degenerate and invalid collision inputs fail deterministically', () => {
|
||||
const box = { minX: 0, minY: 0, maxX: 10, maxY: 10 };
|
||||
assert.equal(pdfSegmentTouchesBox([20, 20], [20, 20], box), false);
|
||||
|
||||
@@ -187,6 +187,44 @@ test('dimensions switch removes the external chain from physical walls', () => {
|
||||
assert.deepEqual(texts(false), [], 'the option removes every dimension value');
|
||||
});
|
||||
|
||||
test('stepped exterior keeps the complete reconstructable dimension chain', () => {
|
||||
const step = [
|
||||
[0.1, 0.1], [0.7, 0.1], [0.7, 0.3], [0.8, 0.3],
|
||||
[0.8, 0.6], [0.7, 0.6], [0.7, 0.9], [0.1, 0.9],
|
||||
];
|
||||
const externalLabels = (poly) => {
|
||||
const raw = polygonRaw('stepped', poly, { cellCm: 5, wallCm: 15, name: '' });
|
||||
const output = buildRawPage(raw,
|
||||
{ dimensions: true, roomNames: false, decor: false, backdrop: false });
|
||||
const architecture = output.commands.find((command) => command.kind === 'path' && command.fill);
|
||||
assert.ok(architecture, 'the stepped physical wall body reaches the PDF scene');
|
||||
const box = commandBox(architecture);
|
||||
const inPlanField = (command) => command.x >= output.planField.minX
|
||||
&& command.x <= output.planField.maxX && command.y >= output.planField.minY
|
||||
&& command.y <= output.planField.maxY;
|
||||
const outsideWallBox = (command) => command.x < box.minX || command.x > box.maxX
|
||||
|| command.y < box.minY || command.y > box.maxY;
|
||||
return output.commands.filter((command) => command.kind === 'text'
|
||||
&& /^\d+(?:[.,]\d+)?\s*(?:m|cm)$/.test(command.text)
|
||||
&& inPlanField(command) && outsideWallBox(command));
|
||||
};
|
||||
|
||||
for (const poly of [step, [...step].reverse()]) {
|
||||
const labels = externalLabels(poly);
|
||||
const values = labels.map((command) => command.text);
|
||||
for (const required of ['1.20 m', '2.40 m', '3.60 m', '3.75 m']) {
|
||||
assert.ok(values.includes(required),
|
||||
`${required} remains in the reconstructable protrusion chain`);
|
||||
}
|
||||
assert.equal(values.filter((value) => value === '1.20 m').length, 1,
|
||||
'the equal upper/lower protrusion depths are locally deduplicated');
|
||||
assert.equal(values.length, 6,
|
||||
'the four protrusion dimensions coexist with the overall width and height');
|
||||
assert.ok(labels.every((command) => Math.abs(command.angle % 90) < 1e-8),
|
||||
'the exterior chain contains no diagonal labels');
|
||||
}
|
||||
});
|
||||
|
||||
test('backdrop is below physical architecture and disappears with its option', () => {
|
||||
const raster = {
|
||||
id: 'backdrop', bytes: new Uint8Array([0xff, 0xd8, 0xff, 0xd9]),
|
||||
|
||||
Reference in New Issue
Block a user