import { test } from 'node:test'; import assert from 'node:assert/strict'; import { minAreaRect, orientedRect, maskSpanAlong } from './rect.ts'; import { measure } from '../measure/measure.ts'; // 100×20 horizontal slab const W = 100, H = 20; const bitset = new Uint8Array(W * H); for (let x = 0; x < W; x++) for (let y = 0; y < H; y++) bitset[y * W + x] = 1; test('maskSpanAlong of a 100×20 slab: X ≈ 99–100, Y ≈ 19–20', () => { const sx = maskSpanAlong(bitset, W, H, 1, 0); const sy = maskSpanAlong(bitset, W, H, 0, 1); assert.ok(sx >= 99 && sx <= 100, `spanX ${sx}`); assert.ok(sy >= 19 && sy <= 20, `spanY ${sy}`); }); test('orientedRect changes angleDeg and preserves longSidePx', () => { const prev = minAreaRect(bitset, W, H); assert.ok(prev, 'minAreaRect returned a box'); const rotated = orientedRect(prev, 45); assert.ok(Math.abs(rotated.angleDeg - 45) < 1e-9); assert.equal(rotated.longSidePx, prev.longSidePx); // The long side (angleDeg 0 → +x) must be preserved by the rotation. const longOf = (c) => Math.hypot(c[3][0] - c[0][0], c[3][1] - c[0][1]); assert.ok(Math.abs(longOf(rotated.corners) - longOf(prev.corners)) < 1e-9); assert.ok(Math.abs(longOf(prev.corners) - prev.longSidePx) < 1e-9); // angle normalization to [0, 180) assert.ok(orientedRect(prev, 190).angleDeg >= 0 && orientedRect(prev, 190).angleDeg < 180); }); test('measure scale uses mask span along the axis, not longSidePx', () => { // Thin slab (20 long, 100 wide): minAreaRect's long side (angleDeg 90) is // the 100-px dimension along +y. Rotate the axis to angleDeg 0 (+x), which // crosses only the 20-px slab width — span 19, not longSidePx 99. const TW = 20, TH = 100; const thin = new Uint8Array(TW * TH).fill(1); const thinBox = minAreaRect(thin, TW, TH); assert.ok(thinBox && thinBox.longSidePx >= 99, `longSide ${thinBox?.longSidePx}`); const rotated = orientedRect(thinBox, 0); const lengthEighths = 24; // 3 in const m = measure(thin, TW, TH, rotated, lengthEighths, 8, TW * TH); // Scale = span along the axis / length. Span ≈ 19 → scale ≈ 6.3 px/in, // far from the old longSidePx/3 ≈ 33. const ex = rotated.corners[0][0] - rotated.corners[3][0]; const ey = rotated.corners[0][1] - rotated.corners[3][1]; const elen = Math.hypot(ex, ey); const span = maskSpanAlong(thin, TW, TH, ex / elen, ey / elen); assert.ok(Math.abs(m.scale - span / (lengthEighths / 8)) < 1e-9); assert.ok(m.scale < 10, `scale ${m.scale} should be span-based, not ${thinBox.longSidePx / 3}`); });