import test from 'node:test' import assert from 'node:assert/strict' import { loadModule } from './load.mjs' // Kamera: Zoom, Verschieben, Umkehr-Transformation, Figuren-Treffer (Ansicht = Welt/Zoom, Bruchteile 0..1) const C = await loadModule('src/components/workers/engine/camera.js') const close = (a, b) => Math.abs(a - b) < 1e-9 test('fitZoom: ab 560 px 1, darunter wachsend bis 2,2', () => { assert.equal(C.fitZoom(1280), 1) assert.equal(C.fitZoom(600), 1) assert.equal(C.fitZoom(560), 1) assert.ok(close(C.fitZoom(390), 560 / 390)) assert.equal(C.fitZoom(200), 2.2) }) test('fitZoom: ungültige Breite gibt 1, nie NaN', () => { for (const w of [0, -5, NaN, undefined, null, Infinity]) assert.equal(C.fitZoom(w), 1, String(w)) }) test('Zoomgrenzen und Startkamera', () => { assert.deepEqual([C.ZOOM_MIN, C.ZOOM_MAX], [1, 3]) assert.deepEqual(C.createCamera(), { zoom: 1, x: 0, y: 0 }) assert.notEqual(C.createCamera(), C.createCamera()) }) test('zoomAt hält den Punkt unter dem Zeiger fest, klemmt Zoom und Rand', () => { const p0 = C.viewToWorld(C.createCamera(), 0.25, 0.75) const cam = C.zoomAt(C.createCamera(), 2, 0.25, 0.75) assert.equal(cam.zoom, 2) const p1 = C.viewToWorld(cam, 0.25, 0.75) assert.ok(Math.abs(p1.x - p0.x) < 1e-9 && Math.abs(p1.y - p0.y) < 1e-9) assert.equal(C.zoomAt(C.createCamera(), 10, 0.5, 0.5).zoom, 3) assert.equal(C.zoomAt(C.createCamera(), 0.1, 0.5, 0.5).zoom, 1) const edge = C.panBy({ zoom: 2, x: 0, y: 0 }, 5, 5) assert.deepEqual([edge.x, edge.y], [160, 128]) const back = C.panBy({ zoom: 2, x: 10, y: 10 }, -5, -5) assert.deepEqual([back.x, back.y], [0, 0]) }) test('viewToWorld und worldToView sind zueinander invers; centerOn', () => { const cam = { zoom: 2.5, x: 40, y: 60 } const w = C.viewToWorld(cam, 0.3, 0.6) const v = C.worldToView(cam, w.x, w.y) assert.ok(Math.abs(v.vx - 0.3) < 1e-9 && Math.abs(v.vy - 0.6) < 1e-9) const c = C.centerOn({ zoom: 2, x: 0, y: 0 }, 160, 128) assert.deepEqual([c.x, c.y], [80, 64]) }) test('pickAgent: oberste Figur gewinnt, Polster vergrößert die Fläche, Treffer nur im Kasten', () => { const boxes = [{ id: 'a', box: { x: 10, y: 10, w: 12, h: 16 } }, { id: 'b', box: { x: 16, y: 10, w: 12, h: 16 } }] assert.equal(C.pickAgent(boxes, 18, 14), 'b') assert.equal(C.pickAgent(boxes, 11, 14), 'a') assert.equal(C.pickAgent(boxes, 40, 14), null) assert.equal(C.pickAgent(boxes, 30, 14, 0), null) assert.equal(C.pickAgent(boxes, 30, 14, 3), 'b') }) // ---- Ergänzungen: Verhalten, das die Tests oben nicht berühren ---- test('Sichtfeld ist Welt/Zoom: bei Zoom 1 gibt es nichts zu verschieben', () => { const cam = C.panBy(C.createCamera(), 0.4, -0.3) assert.deepEqual([cam.zoom, cam.x, cam.y], [1, 0, 0]) assert.deepEqual(C.viewToWorld(C.createCamera(), 1, 1), { x: 320, y: 256 }) assert.deepEqual(C.viewToWorld({ zoom: 2, x: 160, y: 128 }, 1, 1), { x: 320, y: 256 }) }) test('panBy verschiebt in Bruchteilen der Ansicht und klemmt am Rand', () => { const cam = C.panBy({ zoom: 2, x: 40, y: 30 }, 0.25, 0.5) assert.deepEqual([cam.x, cam.y], [80, 94]) }) test('zoomAt zurück auf 1 setzt die Kamera auf den Ursprung zurück (Rand wird geklemmt)', () => { const cam = C.zoomAt({ zoom: 3, x: 100, y: 80 }, 0.01, 0.5, 0.5) assert.deepEqual([cam.zoom, cam.x, cam.y], [1, 0, 0]) }) test('zoomAt am Rand: der Punkt unter dem Zeiger bleibt, solange der Rand nicht klemmt', () => { const start = { zoom: 2, x: 80, y: 64 } const p0 = C.viewToWorld(start, 0.6, 0.4) const cam = C.zoomAt({ ...start }, 1.5, 0.6, 0.4) assert.equal(cam.zoom, 3) const p1 = C.viewToWorld(cam, 0.6, 0.4) assert.ok(close(p1.x, p0.x) && close(p1.y, p0.y)) }) test('clampCamera klemmt Zoom und Lage, auch mit eigener Welt; Aufrufer bekommt dieselbe Kamera zurück', () => { const cam = { zoom: 5, x: -10, y: 900 } assert.equal(C.clampCamera(cam), cam) assert.deepEqual([cam.zoom, cam.x, cam.y], [3, 0, 256 - 256 / 3]) const klein = C.clampCamera({ zoom: 2, x: 99, y: 99 }, { w: 100, h: 50 }) assert.deepEqual([klein.x, klein.y], [50, 25]) }) test('centerOn klemmt in den Ecken', () => { const c = C.centerOn({ zoom: 2, x: 80, y: 64 }, 0, 0) assert.deepEqual([c.x, c.y], [0, 0]) const d = C.centerOn({ zoom: 2, x: 0, y: 0 }, 320, 256) assert.deepEqual([d.x, d.y], [160, 128]) }) test('ungültige Eingaben (NaN, Infinity) machen die Kamera nie kaputt', () => { const a = C.zoomAt({ zoom: 2, x: 20, y: 10 }, NaN, 0.5, 0.5) assert.deepEqual([a.zoom, a.x, a.y], [2, 20, 10]) const b = C.panBy({ zoom: 2, x: 20, y: 10 }, NaN, Infinity) assert.ok(Number.isFinite(b.x) && Number.isFinite(b.y)) assert.equal(b.x, 20) const c = C.clampCamera({ zoom: NaN, x: NaN, y: NaN }) assert.deepEqual([c.zoom, c.x, c.y], [1, 0, 0]) }) test('pickAgent: leere Liste, kaputte Einträge und Rand des Kastens', () => { assert.equal(C.pickAgent([], 5, 5), null) assert.equal(C.pickAgent(undefined, 5, 5), null) const boxes = [{ id: 'a', box: { x: 10, y: 10, w: 12, h: 16 } }, { id: 'kaputt' }, null] assert.equal(C.pickAgent(boxes, 22, 26), 'a') assert.equal(C.pickAgent(boxes, 23, 26), null) assert.equal(C.pickAgent(boxes, 23, 26, 1), 'a') })