import { describe, expect, it } from 'vitest'; import { boundsFromCorners, computeHomography, isValidCardQuad, orderQuadCorners, } from '../../lib/scanner-card-warp.js'; describe('orderQuadCorners', () => { it('orders corners as top-left, top-right, bottom-right, bottom-left', () => { const points = [ { x: 10, y: 10 }, { x: 90, y: 12 }, { x: 88, y: 140 }, { x: 12, y: 138 }, ]; expect(orderQuadCorners(points)).toEqual([ { x: 10, y: 10 }, { x: 90, y: 12 }, { x: 88, y: 140 }, { x: 12, y: 138 }, ]); }); }); describe('boundsFromCorners', () => { it('returns the enclosing axis-aligned rectangle', () => { const corners = [ { x: 10, y: 20 }, { x: 110, y: 25 }, { x: 105, y: 165 }, { x: 15, y: 160 }, ]; expect(boundsFromCorners(corners)).toEqual({ x: 10, y: 20, width: 100, height: 145, }); }); }); describe('isValidCardQuad', () => { it('accepts a convex card-shaped quad', () => { const corners = [ { x: 0, y: 0 }, { x: 50, y: 0 }, { x: 50, y: 70 }, { x: 0, y: 70 }, ]; expect(isValidCardQuad(corners)).toBe(true); }); it('rejects a quad with implausible aspect ratio', () => { const corners = [ { x: 0, y: 0 }, { x: 200, y: 0 }, { x: 200, y: 70 }, { x: 0, y: 70 }, ]; expect(isValidCardQuad(corners)).toBe(false); }); }); describe('computeHomography', () => { it('maps source corners to the destination rectangle', () => { const src = [ { x: 0, y: 0 }, { x: 100, y: 0 }, { x: 100, y: 140 }, { x: 0, y: 140 }, ]; const matrix = computeHomography(src, 200, 280); const mappedTopLeft = applyForward(matrix, 0, 0); expect(mappedTopLeft.x).toBeCloseTo(0, 4); expect(mappedTopLeft.y).toBeCloseTo(0, 4); const mappedBottomRight = applyForward(matrix, 100, 140); expect(mappedBottomRight.x).toBeCloseTo(200, 4); expect(mappedBottomRight.y).toBeCloseTo(280, 4); }); }); function applyForward(matrix, x, y) { const denom = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2]; return { x: (matrix[0][0] * x + matrix[0][1] * y + matrix[0][2]) / denom, y: (matrix[1][0] * x + matrix[1][1] * y + matrix[1][2]) / denom, }; }