feat(m4): fixed tape line for length calibration (#99)

The photo pans, zooms, and tilts under a line that stays in the middle
of the frame. Scale is the source-pixel distance between the ticks
divided by the declared length, not the mask span.
This commit is contained in:
Ty
2026-09-24 17:56:21 -07:00
parent d49ff2fab5
commit 6000ac2b0f
23 changed files with 643 additions and 1019 deletions
-27
View File
@@ -118,30 +118,3 @@ test('belowFloor: small slab → sourceBelowFloor, outW capped at floor (no upsc
assert.equal(out.sourceBelowFloor, true);
assert.ok(out.width <= 800, `outW ${out.width} must not exceed the floor`);
});
test('calibration rotation turns a landscape slab into an upright crop extent', () => {
const width = 6, height = 4;
const bitset = new Uint8Array(width * height);
for (let y = 1; y <= 2; y++) for (let x = 1; x <= 4; x++) bitset[y * width + x] = 1;
const source = { widthPx: width, heightPx: height };
const mask = maskFor(bitset, { x: 1, y: 1, w: 4, h: 2 });
const photo = makePhoto(width, height, (x, y) =>
bitset[y * width + x] ? [200, 40, 20, 255] : [0, 255, 0, 255],
);
const extent = (rgba: Uint8Array, outW: number, outH: number) => {
let minX = outW, maxX = -1, minY = outH, maxY = -1;
for (let y = 0; y < outH; y++) for (let x = 0; x < outW; x++) {
if (rgba[(y * outW + x) * 4 + 3] === 0) continue;
minX = Math.min(minX, x); maxX = Math.max(maxX, x);
minY = Math.min(minY, y); maxY = Math.max(maxY, y);
}
return { width: maxX - minX + 1, height: maxY - minY + 1 };
};
const unrotated = composeInventoryFrame(photo, mask, source, 1, 0);
const rotated = composeInventoryFrame(photo, mask, source, 1, 90);
assert.ok(extent(unrotated.rgba, unrotated.width, unrotated.height).width > extent(unrotated.rgba, unrotated.width, unrotated.height).height);
assert.ok(extent(rotated.rgba, rotated.width, rotated.height).height > extent(rotated.rgba, rotated.width, rotated.height).width);
for (let i = 0; i < rotated.rgba.length; i += 4) {
if (rotated.rgba[i + 3]) assert.deepEqual([...rotated.rgba.slice(i, i + 3)], [200, 40, 20]);
}
});