import { test, describe } from 'node:test'; import assert from 'node:assert/strict'; import { Terrain } from '../js/terrain.js'; describe('Terrain', () => { test('heightfield is deterministic across instances (persistent worlds)', () => { const a = new Terrain(2000, 100); const b = new Terrain(2000, 100); a.createMesh(); b.createMesh(); for (const [x, z] of [[0, 0], [317, -812], [-999, 999], [123.5, -45.25]]) { assert.equal(a.getHeightAt(x, z), b.getHeightAt(x, z), `height differs at ${x},${z}`); } }); test('heightfield is deterministic across rebuilds of the same instance', () => { const t = new Terrain(2000, 100); t.createMesh(); const first = t.getHeightAt(317, -812); t.createMesh(); assert.equal(t.getHeightAt(317, -812), first); }); test('airport apron is flattened inside the radius and untouched outside it', () => { const rough = new Terrain(2000, 100); rough.buildHeightData(); // heightfield only, no flattening const flat = new Terrain(2000, 100); flat.createMesh(); // createMesh() flattens a 200 m radius // The apron centre happens to be naturally flat in this world, so assert on the // strongest reduction found anywhere inside the radius rather than on one point. let best = null; for (let x = -190; x <= 190; x += 5) { for (let z = -190; z <= 190; z += 5) { if (Math.hypot(x, z) > 190) continue; const delta = rough.getHeightAt(x, z) - flat.getHeightAt(x, z); if (!best || delta > best.delta) best = { x, z, delta }; } } assert.ok(best && best.delta > 1, `createMesh() did not flatten inside the 200 m radius (best delta: ${best && best.delta})`); // Flattening must stop at the radius, not bleed into the wider world. for (const [x, z] of [[210, 0], [0, 210], [300, 300], [-400, 250]]) { assert.equal(flat.getHeightAt(x, z), rough.getHeightAt(x, z), `flattening leaked outside the radius at ${x},${z}`); } }); test('height outside the heightfield bounds is zero, not garbage', () => { const t = new Terrain(2000, 100); t.createMesh(); for (const [x, z] of [[1500, 0], [0, 1500], [-1500, -1500]]) { assert.equal(t.getHeightAt(x, z), 0, `expected 0 outside bounds at ${x},${z}`); } }); test('heights stay within a sane range for a 2000 m world', () => { const t = new Terrain(2000, 100); t.createMesh(); let min = Infinity; let max = 0; for (let x = -990; x <= 990; x += 30) { for (let z = -990; z <= 990; z += 30) { const h = t.getHeightAt(x, z); min = Math.min(min, h); max = Math.max(max, h); } } assert.ok(min >= 0, `negative terrain height: ${min}`); assert.ok(max > 10 && max < 200, `implausible max terrain height: ${max}`); }); test('mesh has one vertex colour per vertex', () => { const t = new Terrain(2000, 50); const mesh = t.createMesh(); const pos = mesh.geometry.attributes.position; const col = mesh.geometry.attributes.color; assert.equal(col.count, pos.count, 'colour attribute count does not match position count'); assert.equal(col.itemSize, 3); }); });