120 lines
5.1 KiB
JavaScript
120 lines
5.1 KiB
JavaScript
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')
|
|
})
|