Something went wrong. Try again.
geometric packing game (tetris-ish) on an icosahedron. save boards and completed runs as records in your atproto PDS oscillatory.net/rad-cohesion
Something went wrong. Try again.
3.2 kB · 106 lines
TypeScript
at multiplayer
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107// @vitest-environment happy-domimport { afterEach, expect, it, vi } from "vitest";import { PerspectiveCamera } from "three";import { createCameraControls } from "./cameraControls";
afterEach(() => { vi.restoreAllMocks(); document.body.innerHTML = "";});
function fixture() { const canvas = document.createElement("canvas"); document.body.append(canvas); canvas.setPointerCapture = vi.fn(); canvas.releasePointerCapture = vi.fn(); vi.spyOn(canvas, "getBoundingClientRect").mockReturnValue( new DOMRect(0, 0, 800, 800), );
const camera = new PerspectiveCamera(38, 1, 0.1, 50);
const controls = createCameraControls(camera, canvas);
const pointer = (target: EventTarget, type: string, x: number, y: number) => target.dispatchEvent( Object.assign(new Event(type), { pointerId: 1, pointerType: "mouse", button: 0, pageX: x, pageY: y, }), );
return { camera, controls, canvas, document, pointer };}
it("keeps rotating past both poles and through multiple full turns in every drag direction", () => { for (const [dx, dy] of [ [0, 30], [0, -30], [30, 0], [-30, 0], [20, 20], ]) { const { camera, controls, canvas, document, pointer } = fixture(); const distance = camera.position.length(); pointer(canvas, "pointerdown", 400, 400);
let totalAngle = 0, inverted = false;
for (let i = 1; i <= 120; i++) { const previous = camera.position.clone().normalize(); pointer(document, "pointermove", 400 + dx * i, 400 + dy * i); controls.update();
const angle = Math.acos( Math.min( 1, Math.max(-1, previous.dot(camera.position.clone().normalize())), ), );
expect(angle).toBeGreaterThan(0.05); // A pole clamp would stall here. totalAngle += angle; inverted ||= camera.up.y < 0; expect(camera.position.length()).toBeCloseTo(distance, 8); }
expect(totalAngle).toBeGreaterThan(4 * Math.PI);
if (dy !== 0) expect(inverted).toBe(true); pointer(document, "pointerup", 400 + dx * 120, 400 + dy * 120); controls.dispose(); }});
it("restores the upright home view after rotation and zoom without drifting", () => { const { camera, controls, canvas, document, pointer } = fixture(); const initial = camera.position.clone(); pointer(canvas, "pointerdown", 400, 400); pointer(document, "pointermove", 650, 900); controls.update(); pointer(document, "pointerup", 650, 900);
for (const deltaY of [100000, -100000]) { canvas.dispatchEvent( Object.assign(new Event("wheel", { cancelable: true }), { deltaY, deltaMode: 0, }), ); controls.update(); expect(camera.position.length()).toBeGreaterThanOrEqual(3.8 - 1e-8); expect(camera.position.length()).toBeLessThanOrEqual(12 + 1e-8); }
controls.reset();
for (let i = 0; i < 10; i++) controls.update(); expect(camera.position.distanceTo(initial)).toBeLessThan(1e-8); expect(camera.up.toArray()).toEqual([0, 1, 0]); expect(controls.target.toArray()).toEqual([0, 0, 0]); controls.dispose();});