1import { assert, assertEquals } from "@std/assert";2import * as THREE from "three";3import { createTrail } from "../src/client/trail.ts";45function alphaAt(trail: ReturnType<typeof createTrail>, point: number): number {6 const mesh = trail.obj;7 const colors = mesh.geometry.getAttribute("color") as THREE.BufferAttribute;8 return colors.getW(point * 2);9}1011Deno.test("trail: short histories taper over their actual length", () => {12 const trail = createTrail(0xffffff, { maxPoints: 12, minStep: 0 });13 const camera = new THREE.PerspectiveCamera();14 camera.position.set(0, 4, 8);15 for (let x = 0; x < 4; x++) {16 trail.update(new THREE.Vector3(x, 0, 0), camera);17 }18 assert(alphaAt(trail, 0) > alphaAt(trail, 1));19 assert(alphaAt(trail, 1) > alphaAt(trail, 2));20 assertEquals(alphaAt(trail, 3), 0);21 trail.dispose();22});2324Deno.test("trail: afterglow fades without adding geometry", () => {25 const trail = createTrail(0xffffff, { minStep: 0 });26 const camera = new THREE.PerspectiveCamera();27 camera.position.set(0, 3, 6);28 trail.update(new THREE.Vector3(0, 0, 0), camera);29 trail.update(new THREE.Vector3(1, 0, 0), camera);30 trail.update(new THREE.Vector3(2, 0, 0), camera);31 const beforeCount = trail.obj.geometry.drawRange.count;32 const beforeAlpha = alphaAt(trail, 0);33 trail.fade(camera, 0.4);34 assertEquals(trail.obj.geometry.drawRange.count, beforeCount);35 assert(Math.abs(alphaAt(trail, 0) - beforeAlpha * 0.4) < 1e-6);36 trail.fade(camera, 0);37 assertEquals(trail.obj.geometry.drawRange.count, 0);38 trail.dispose();39});4041Deno.test("trail: warp reset never bridges distant points", () => {42 const trail = createTrail(0xffffff, { minStep: 0, jumpReset: 2 });43 const camera = new THREE.PerspectiveCamera();44 camera.position.set(0, 3, 6);45 trail.update(new THREE.Vector3(0, 0, 0), camera);46 trail.update(new THREE.Vector3(1, 0, 0), camera);47 assert(trail.obj.geometry.drawRange.count > 0);48 trail.update(new THREE.Vector3(20, 0, 0), camera);49 assertEquals(trail.obj.geometry.drawRange.count, 0);50 trail.dispose();51});5253Deno.test("trail: slow rail motion accumulates into new samples", () => {54 const trail = createTrail(0xffffff, { minStep: 0.06 });55 const camera = new THREE.PerspectiveCamera();56 camera.position.set(0, 3, 6);57 // Kinematic/rider motion advances less than minStep on each frame. The58 // sampler must measure from the last committed point, not its moving head.59 for (let i = 0; i <= 12; i++) {60 trail.update(new THREE.Vector3(i * 0.01, 0, 0), camera);61 }62 assert(63 trail.obj.geometry.drawRange.count >= 12,64 `slow movement did not produce trail samples: ${trail.obj.geometry.drawRange.count}`,65 );66 trail.dispose();67});