import assert from 'node:assert/strict'; import test from 'node:test'; import { readFileSync } from 'node:fs'; import { BoxGeometry, Group, Mesh, MeshStandardMaterial, Vector3 } from 'three'; import { createSummitMarker } from '../src/scene/summitMarker.ts'; import { summitPosition } from '../src/controls/summitCoordinates.ts'; import { SUMMIT_TERRAIN } from '../src/constants/summitTerrain.ts'; import { SUMMIT_PLATEAU } from '../src/constants/summitPlateau.ts'; test('marker placement matches the map pin plus the estimated satellite offset', () => { const spec = JSON.parse(readFileSync(new URL('../scripts/constants/marker-location.json', import.meta.url), 'utf8')); const pin = summitPosition(spec.pin.latitude, spec.pin.longitude, SUMMIT_TERRAIN.start); const expected = [pin.x + spec.offsetFromPinMeters[0], pin.z + spec.offsetFromPinMeters[1]]; SUMMIT_PLATEAU.marker.offset.forEach((value, axis) => assert.ok(Math.abs(value-expected[axis]) < .001)); }); test('exported marker embeds the current baked textures, not stale packed images', () => { const glb = readFileSync(new URL('../public/models/summit/summit-marker.glb', import.meta.url)); const jsonLength = glb.readUInt32LE(12); const document = JSON.parse(glb.subarray(20, 20 + jsonLength).toString()); const binaryStart = 28 + jsonLength; assert.equal(document.images.length, 4); for (const image of document.images) { const view = document.bufferViews[image.bufferView]; const start = binaryStart + (view.byteOffset ?? 0); const embedded = glb.subarray(start, start + view.byteLength); const source = readFileSync(new URL(`../public/terrain/materials/${image.name}.png`, import.meta.url)); assert.ok(embedded.equals(source), `${image.name} must match the latest bake`); } }); test('marker seats its foot on sloping bedrock without deforming the stone', () => { const asset = new Group(); const mesh = new Mesh(new BoxGeometry(4, 4, 2), new MeshStandardMaterial()); mesh.position.y = 2; asset.add(mesh); const groundAt = (x: number, z: number) => 12 + .3*x - .2*z; const rock = new MeshStandardMaterial(); const marker = createSummitMarker(asset, {x:0,z:0}, groundAt, rock); marker.group.updateMatrixWorld(true); const position = mesh.geometry.getAttribute('position'); const point = new Vector3(); let nearestContact = Infinity; for (let i=0; i .05, `base must stay below terrain: ${burial}`); assert.equal(position.getY(i),-2,'base geometry remains unchanged'); nearestContact = Math.min(nearestContact,burial); } else { assert.equal(position.getY(i),2,'upper geometry remains unchanged'); } } assert.ok(nearestContact < .3,'the stone is seated close to its lowest contact'); marker.dispose(); rock.dispose(); });