import { describe, expect, it } from 'vitest'; import { circleShape, frameShape, hexShape, polygonShape, rectShape, roundedRectShape, scaleShape, shapeFromThree, signedArea, } from './shapes.js'; describe('shape generators', () => { it('rectShape is a CCW unit-ish rectangle', () => { const s = rectShape(2, 4); expect(s.outline).toEqual([ [-1, -2], [1, -2], [1, 2], [-1, 2], ]); expect(signedArea(s.outline)).toBeGreaterThan(0); }); it('polygonShape produces a regular polygon with the right vertex count', () => { const s = polygonShape(6, 2); expect(s.outline).toHaveLength(6); // All vertices on the circle of radius 2. for (const [x, y] of s.outline) { expect(Math.hypot(x!, y!)).toBeCloseTo(2, 5); } expect(signedArea(s.outline)).toBeGreaterThan(0); }); it('hexShape is a pointy-top hexagon', () => { const s = hexShape(1); expect(s.outline).toHaveLength(6); // Pointy-top: a vertex sits at the top (angle 90deg, max y). const top = s.outline.reduce((a, b) => (b[1]! > a[1]! ? b : a)); expect(top[0]).toBeCloseTo(0, 5); expect(top[1]).toBeCloseTo(1, 5); }); it('circleShape approximates a circle', () => { const s = circleShape(3, 64); expect(s.outline).toHaveLength(64); for (const [x, y] of s.outline) { expect(Math.hypot(x!, y!)).toBeCloseTo(3, 5); } }); it('roundedRectShape clamps the corner radius', () => { const s = roundedRectShape(2, 2, 5); // Radius clamped to half the smaller dimension (1). const minX = Math.min(...s.outline.map((p) => p[0]!)); const maxX = Math.max(...s.outline.map((p) => p[0]!)); expect(minX).toBeCloseTo(-1, 5); expect(maxX).toBeCloseTo(1, 5); expect(s.outline.length).toBe(4 * 4); // 4 corners * 4 segments }); it('frameShape has a hole wound CW', () => { const s = frameShape(4, 4, 2); expect(s.outline).toHaveLength(4); expect(s.holes).toHaveLength(1); expect(signedArea(s.outline)).toBeGreaterThan(0); expect(signedArea(s.holes![0]!)).toBeLessThan(0); }); it('scaleShape scales outline and holes', () => { const s = scaleShape(rectShape(2, 2), 2, 3); expect(s.outline).toEqual([ [-2, -3], [2, -3], [2, 3], [-2, 3], ]); const framed = scaleShape(frameShape(4, 4, 2), 2, 2); expect(framed.holes).toHaveLength(1); expect(framed.holes![0]![0]).toEqual([-2, -2]); }); it('shapeFromThree converts three.js vectors', () => { const fake = { getPoints: () => [ { x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, ], getPointsHoles: () => [], }; const s = shapeFromThree(fake); expect(s.outline).toEqual([ [0, 0], [1, 0], [1, 1], ]); expect(s.holes).toBeUndefined(); }); });