import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { Vector3 } from 'three'; import { isCoastalLand, seaHeight, createOceanGeometry } from '../src/scene/coast.ts'; import type { CoastData } from '../src/scene/coast.ts'; import { terrainHeightAt, validateTerrain } from '../src/scene/terrainGeometry.ts'; import { COAST } from '../src/constants/coast.ts'; import { LOCATIONS } from '../src/constants/locations.ts'; import { walkingHeight } from '../src/controls/walking.ts'; import { structurePosition } from '../src/scene/structures.ts'; const base = new URL('../public/terrain/hajodae/', import.meta.url); const coast: CoastData = JSON.parse(readFileSync(new URL('coast.json', base), 'utf8')); const meta = JSON.parse(readFileSync(new URL('metadata.json', base), 'utf8')); const b = readFileSync(new URL('heights.f32', base)); const heights = new Float32Array(b.buffer.slice(b.byteOffset,b.byteOffset+b.byteLength)); test('Hajodae extraction has complete bounded coverage and a dry pavilion origin', () => { validateTerrain(meta, heights, COAST); assert.equal(meta.radiusMeters,1500); assert.equal(heights.byteLength,362404); assert.ok(isCoastalLand(coast,0,0)); assert.ok(terrainHeightAt(meta, heights,0,0)>0); assert.ok(heights.every(Number.isFinite)); assert.equal(meta.corrections.osmHeightCorrection,false); const shore = structurePosition(38.0231,128.72599,coast.origin); assert.ok(isCoastalLand(coast,shore.x,shore.z)); assert.ok(walkingHeight(meta,heights,shore.x,shore.z) >= seaHeight(shore.x,shore.z,meta.observerSurfaceElevation)-0.001, 'the near-shore land sample must not be pulled down by offshore vertices'); assert.equal(isCoastalLand(coast,400,0),false,'east of the beach is sea'); const cape=structurePosition(38.021987,128.7355911,coast.origin); assert.ok(isCoastalLand(coast,cape.x,cape.z),'lighthouse headland is land'); assert.equal(isCoastalLand(coast,1600,0),false,'walking is limited to surveyed coverage'); }); test('Hajodae view uses the pavilion and faces the pine reference east-northeast', () => { assert.deepEqual(meta.origin, {latitude:38.020813,longitude:128.7352564}); assert.deepEqual(LOCATIONS.hajodae.coast.lookTarget, COAST.pine); const pine = structurePosition(COAST.pine.latitude, COAST.pine.longitude, meta.origin); const azimuth = Math.atan2(pine.x,-pine.z)*180/Math.PI; assert.ok(azimuth > 75 && azimuth < 80); }); test('ocean follows terrain curvature and faces upward', () => { const h=meta.observerSurfaceElevation; assert.equal(seaHeight(0,0,h),-h); assert.ok(seaHeight(1000,0,h)<-h); const g=createOceanGeometry(h); const p=g.getAttribute('position'), n=g.getAttribute('normal'); assert.ok(n.getY(Math.floor(n.count/2))>0); for (const i of [0,Math.floor(p.count/2),p.count-1]) { assert.ok(Math.abs(p.getY(i)-seaHeight(p.getX(i),p.getZ(i),h))<0.001); } const v=new Vector3().fromBufferAttribute(p,0); assert.ok(Number.isFinite(v.length())); g.dispose(); });