Files
sunrise/tests/walking.test.ts

43 lines
2.2 KiB
TypeScript

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();
});