import { test } from 'node:test'; import assert from 'node:assert/strict'; import { minAreaRect, orientedRect, rectAlongLine } from './rect.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('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('rectAlongLine sets the approved axis from the owner-positioned tape endpoints', () => { const previous = minAreaRect(bitset, W, H)!; const start: [number, number] = [20, 30]; const end: [number, number] = [80, 70]; const calibrated = rectAlongLine(previous, start, end); const center: [number, number] = [50, 50]; assert.ok(Math.abs(calibrated.angleDeg - (Math.atan2(40, 60) * 180) / Math.PI) < 1e-9); assert.ok(Math.abs(calibrated.longSidePx - Math.hypot(60, 40)) < 1e-9); assert.ok(Math.abs(calibrated.corners.reduce((sum, point) => sum + point[0], 0) / 4 - center[0]) < 1e-9); assert.ok(Math.abs(calibrated.corners.reduce((sum, point) => sum + point[1], 0) / 4 - center[1]) < 1e-9); assert.notEqual(calibrated.angleDeg, previous.angleDeg, 'direction comes from tape endpoints, not mask rectangle'); });