scripts/mascot-bodies/geometry.test.mjs
Rama main · réplica de lectura del repositorio original.
import { describe, expect, it } from "vitest";
import { applyFit, boundsOf, fitTransform, flatten } from "./geometry.ts";
const FACE_BOX = 228.541;
/** A 100x100 box at the origin, as four degenerate cubics. */
const BOX =
"M0 0 C33.33 0 66.67 0 100 0 C100 33.33 100 66.67 100 100 " +
"C66.67 100 33.33 100 0 100 C0 66.67 0 33.33 0 0 Z";
describe("flatten", () => {
it("walks a closed path into one polyline", () => {
const polylines = flatten(BOX);
expect(polylines).toHaveLength(1);
expect(polylines[0].length).toBeGreaterThan(4);
});
it("keeps every sampled point on the box it came from", () => {
for (const [x, y] of flatten(BOX)[0]) {
const onEdge =
Math.abs(x) < 0.01 || Math.abs(x - 100) < 0.01 ||
Math.abs(y) < 0.01 || Math.abs(y - 100) < 0.01;
expect(onEdge, `(${x}, ${y}) left the box`).toBe(true);
}
});
});
describe("boundsOf", () => {
it("measures the drawn extent", () => {
expect(boundsOf(flatten(BOX))).toEqual({
minX: expect.closeTo(0, 2),
minY: expect.closeTo(0, 2),
maxX: expect.closeTo(100, 2),
maxY: expect.closeTo(100, 2),
});
});
});
describe("fitTransform", () => {
it("scales a square to fill the face box", () => {
const fit = fitTransform({ minX: 0, minY: 0, maxX: 100, maxY: 100 });
expect(fit.scale).toBeCloseTo(FACE_BOX / 100, 5);
});
it("centres the narrow axis of a tall shape", () => {
const fit = fitTransform({ minX: 0, minY: 0, maxX: 50, maxY: 100 });
expect(fit.scale).toBeCloseTo(FACE_BOX / 100, 5);
// 50 wide at that scale leaves half the box spare, split evenly.
expect(fit.tx).toBeCloseTo(FACE_BOX / 4, 3);
expect(fit.ty).toBeCloseTo(0, 3);
});
it("emits the transform in the format the renderer already parses", () => {
const fit = fitTransform({ minX: 0, minY: 0, maxX: 100, maxY: 100 });
expect(fit.transform).toMatch(/^translate\(-?[\d.]+ -?[\d.]+\) scale\([\d.]+\)$/);
});
});
describe("applyFit", () => {
it("lands the shape inside the face box", () => {
const fit = fitTransform({ minX: 0, minY: 0, maxX: 100, maxY: 100 });
const bounds = boundsOf(applyFit(flatten(BOX), fit));
expect(bounds.minX).toBeGreaterThanOrEqual(-0.01);
expect(bounds.minY).toBeGreaterThanOrEqual(-0.01);
expect(bounds.maxX).toBeLessThanOrEqual(FACE_BOX + 0.01);
expect(bounds.maxY).toBeLessThanOrEqual(FACE_BOX + 0.01);
});
});