import assert from 'node:assert/strict'; import test from 'node:test'; import { readFileSync } from 'node:fs'; import { Mesh, MeshBasicMaterial, Raycaster, Vector3 } from 'three'; import { walkStep, walkingCoordinates, walkingHeight } from '../src/controls/walking.ts'; import { createTerrainGeometry } from '../src/scene/terrainGeometry.ts'; import { structurePosition } from '../src/scene/structures.ts'; test('walking approaches a destination horizontally and stops without overshooting', () => { const origin=new Vector3(0,2,0), target=new Vector3(3,100,4); const next=walkStep(origin,target,0.05,100); assert.ok(Math.abs(next.x-0.12)<1e-8); assert.ok(Math.abs(next.z-0.16)<1e-8); assert.equal(next.y,2); assert.deepEqual(walkStep(origin,new Vector3(0.05,100,0),0.05,100),new Vector3(0.05,2,0)); assert.deepEqual(walkStep(origin,origin,0.05,100),origin); }); test('walking caps stalled frames and stays inside terrain coverage',()=>{ const result=walkStep(new Vector3(9.9,0,0),new Vector3(20,0,0),30,10); assert.equal(result.x,10); assert.equal(walkStep(new Vector3(),new Vector3(10,0,0),30,10).x,0.2); }); test('walking coordinate conversion is inverse to structure placement',()=>{ const origin={latitude:37.445,longitude:126.964}; const geo=walkingCoordinates(500,-300,origin); const local=structurePosition(geo.latitude,geo.longitude,origin); assert.ok(Math.abs(local.x-500)<1e-6 && Math.abs(local.z+300)<1e-6); }); test('walking feet match actual DSM triangles including curvature',()=>{ const meta=JSON.parse(readFileSync(new URL('../public/terrain/gwanaksan.json',import.meta.url),'utf8')); const b=readFileSync(new URL('../public/terrain/gwanaksan.f32',import.meta.url)); const heights=new Float32Array(b.buffer,b.byteOffset,b.byteLength/4); const geometry=createTerrainGeometry(meta,heights), material=new MeshBasicMaterial(); const mesh=new Mesh(geometry,material); mesh.updateMatrixWorld(true); for(const [x,z] of [[0,0],[8,5],[21,23],[180,-213],[2000,1850]]){ const ray=new Raycaster(new Vector3(x,10000,z),new Vector3(0,-1,0)); const hit=ray.intersectObject(mesh)[0]; assert.ok(hit); assert.ok(Math.abs(hit.point.y-walkingHeight(meta,heights,x,z))<0.001); } geometry.dispose();material.dispose(); });