58 lines
3.2 KiB
TypeScript
58 lines
3.2 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { Mesh, MeshBasicMaterial, Raycaster, Vector3 } from 'three';
|
|
import { createTerrainGeometry, terrainGeometryHeightAt } from '../src/scene/terrainGeometry.ts';
|
|
import { applySummitTerrain } from '../src/scene/summitTerrain.ts';
|
|
import { SUMMIT_TERRAIN } from '../src/constants/summitTerrain.ts';
|
|
import { structurePosition } from '../src/scene/structures.ts';
|
|
import { STRUCTURES } from '../src/constants/structures.ts';
|
|
|
|
const meta = JSON.parse(readFileSync(new URL('../public/terrain/gwanaksan.json', import.meta.url), 'utf8'));
|
|
const bytes = readFileSync(new URL('../public/terrain/gwanaksan.f32', import.meta.url));
|
|
const heights = new Float32Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
const start = structurePosition(SUMMIT_TERRAIN.start.latitude, SUMMIT_TERRAIN.start.longitude, meta.origin);
|
|
|
|
test('local reconstruction preserves source data and every vertex outside 200m', () => {
|
|
const geometry = createTerrainGeometry(meta, heights, true);
|
|
const p = geometry.getAttribute('position');
|
|
const before = p.array.slice();
|
|
const source = heights.slice();
|
|
applySummitTerrain(geometry, meta);
|
|
let changed = 0;
|
|
for (let i=0; i<p.count; i++) {
|
|
const distance = Math.hypot(p.getX(i)-start.x, p.getZ(i)-start.z);
|
|
if (distance >= 200) assert.equal(p.getY(i), before[i*3+1]);
|
|
if (p.getY(i) !== before[i*3+1]) changed++;
|
|
}
|
|
assert.ok(changed > 100);
|
|
assert.deepEqual(heights, source);
|
|
geometry.dispose();
|
|
});
|
|
|
|
test('radar building and dome clear the saddle from the estimated eye position', () => {
|
|
const geometry = createTerrainGeometry(meta, heights, true);
|
|
applySummitTerrain(geometry, meta);
|
|
const ground = terrainGeometryHeightAt(geometry,start.x,start.z);
|
|
assert.ok(Math.abs(ground + meta.observerSurfaceElevation - 620) < 1);
|
|
const radar = structurePosition(STRUCTURES.radar.latitude,STRUCTURES.radar.longitude,meta.origin);
|
|
radar.y = terrainGeometryHeightAt(geometry,radar.x,radar.z) + 7;
|
|
const eye = new Vector3(start.x,ground+1.7,start.z);
|
|
assert.ok(radar.y > eye.y + 8, 'radar must stand above observer on its rock');
|
|
const material = new MeshBasicMaterial();
|
|
const mesh = new Mesh(geometry,material);
|
|
const ray = new Raycaster(eye,radar.clone().sub(eye).normalize(),0,eye.distanceTo(radar));
|
|
assert.equal(ray.intersectObject(mesh).length,0,'ridge must not hide radar building');
|
|
for (const [x,z] of [[start.x,start.z],[radar.x,radar.z],[0,65],[start.x+199,start.z],
|
|
[start.x-12,start.z-22],[start.x-2,start.z-45],[start.x+5,start.z-35],
|
|
[start.x-9,start.z-12],[start.x-9,start.z-11],[start.x-9,start.z-13]]) {
|
|
const hit = new Raycaster(new Vector3(x,1000,z),new Vector3(0,-1,0)).intersectObject(mesh)[0];
|
|
assert.ok(Math.abs(hit.point.y-terrainGeometryHeightAt(geometry,x,z))<1e-4);
|
|
}
|
|
const joint = terrainGeometryHeightAt(geometry,start.x-9,start.z-12);
|
|
const shoulders = (terrainGeometryHeightAt(geometry,start.x-9,start.z-11)
|
|
+ terrainGeometryHeightAt(geometry,start.x-9,start.z-13))/2;
|
|
assert.ok(shoulders-joint>.15,'large fracture must be a physical depression, not only a painted line');
|
|
geometry.dispose(); material.dispose();
|
|
});
|