import { describe, expect, it } from 'vitest'; import { loadPhysics, PIXELS_PER_METRE, physicsStatus, type Rapier, toMetres, toPixels, } from '../../src/runtime/physics/engine'; const STEP = 1 / 60; const GRAVITY = 980; /** A 400 × 20 px floor whose top is at y = 290, and a 32 px crate dropped from y = 0. */ function crateOverFloor(rapier: Rapier) { const world = new rapier.World({ x: 0, y: toMetres(GRAVITY) }); world.timestep = STEP; const floor = world.createRigidBody(rapier.RigidBodyDesc.fixed().setTranslation(0, toMetres(300))); world.createCollider(rapier.ColliderDesc.cuboid(toMetres(200), toMetres(10)), floor); const crate = world.createRigidBody(rapier.RigidBodyDesc.dynamic().setTranslation(0, 0)); world.createCollider(rapier.ColliderDesc.cuboid(toMetres(16), toMetres(16)), crate); const run = (steps: number) => { for (let i = 0; i < steps; i++) world.step(); }; const y = () => toPixels(crate.translation().y); return { world, crate, run, y }; } describe('physics engine', () => { it('loads once and reports the Rapier version', async () => { const first = loadPhysics(); expect(loadPhysics()).toBe(first); const rapier = await first; expect(rapier.version()).toBe('0.20.0'); expect(physicsStatus()).toMatchObject({ state: 'ready', version: '0.20.0' }); }); it('converts at 100 px to the metre', () => { expect(PIXELS_PER_METRE).toBe(100); expect(toMetres(980)).toBe(9.8); expect(toPixels(toMetres(123.5))).toBeCloseTo(123.5, 10); }); it('lets a crate fall freely in pixels per second squared', async () => { const { run, y, crate } = crateOverFloor(await loadPhysics()); run(30); // ½ g t² = 122.5 px after half a second; the integrator lands about a pixel past it. expect(Math.abs(y() - 0.5 * GRAVITY * 0.25)).toBeLessThan(1.5); expect(toPixels(crate.linvel().y)).toBeCloseTo(GRAVITY * 0.5, 3); }); it('keeps a crate resting on the floor', async () => { const { run, y, crate } = crateOverFloor(await loadPhysics()); run(180); expect(Math.abs(y() - 274)).toBeLessThan(0.5); expect(Math.abs(toPixels(crate.linvel().y))).toBeLessThan(0.5); run(120); expect(Math.abs(y() - 274)).toBeLessThan(0.5); }); });