Files
behind_glass/scripts/test-reveal-intersections.mjs
T
horoli e358e24436 Replace project with current server release and Windows setup
Import the 2026-10-09 source release, Fastify/SQLite backend, restored source
assets, and analytics-enabled server build. Replace files from the previous
release while retaining the existing repository history.

Make builds portable across Windows and Linux, configure local startup,
normalize Windows static asset paths, and fix test fixture cleanup and file
URL handling. Exclude local environment settings, databases, dependencies,
validation output, and TypeScript build cache from version control.

Validation: npm ci, static and server builds, 106 arrival tests, and actual
local HTTP checks passed. The full suite passed 467 of 470 tests; three tests
still require QA fixtures omitted from the supplied release, documented in
README-서버실행.md.
2026-10-09 16:43:35 +09:00

73 lines
3.1 KiB
JavaScript

#!/usr/bin/env node
/**
* Independent CPU counterpart of MeshRefraction.ts's bRevealTriangle traversal.
* Compares its hit/miss and world-distance results against Three's Raycaster on
* the actual WindowReveal geometry. This does not execute GLSL or validate GPU
* rendering, texture color, or the photo/reveal nearest-hit selection.
*
* Run from the repository root:
* node scripts/test-reveal-intersections.mjs
* Or supply the repository root:
* node scripts/test-reveal-intersections.mjs /path/to/behind_glass
*/
import assert from 'node:assert/strict';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
const root = path.resolve(process.argv[2] ?? process.cwd());
const THREE = await import(pathToFileURL(path.join(root, 'node_modules/three/build/three.module.js')).href);
const { WindowReveal } = await import(pathToFileURL(path.join(root, 'src/objects/WindowReveal.ts')).href);
const material = new THREE.MeshStandardMaterial({ side: THREE.DoubleSide });
const reveal = new WindowReveal(material);
reveal.update(.928, 1.16, 1.45 * .8 * (3 - .2204) / 3, 1.45 * (3 - .2204) / 3, .6, .2204);
const vertices = reveal.meshes.flatMap(mesh => Array.from({ length: 4 }, (_, i) =>
new THREE.Vector3().fromBufferAttribute(mesh.geometry.attributes.position, i)));
// Independent arithmetic equivalent to the bounded DoubleSide GLSL triangle test.
function triangleDistance(origin, ray, a, b, c) {
const e1 = b.clone().sub(a);
const e2 = c.clone().sub(a);
const p = ray.clone().cross(e2);
const determinant = e1.dot(p);
if (Math.abs(determinant) < 1e-9) return 1e20;
const delta = origin.clone().sub(a);
const u = delta.dot(p) / determinant;
const q = delta.clone().cross(e1);
const v = ray.dot(q) / determinant;
if (u < -.00001 || v < -.00001 || u + v > 1.00001) return 1e20;
const t = e2.dot(q) / determinant;
return t < .000001 ? 1e20 : t;
}
let seed = 17;
const random = () => ((seed = Math.imul(seed, 1664525) + 1013904223 | 0) >>> 0) / 4294967296;
let matched = 0;
let hits = 0;
let maxError = 0;
try {
for (let i = 0; i < 10000; i++) {
const origin = new THREE.Vector3((random() - .5) * .928, (random() - .5) * 1.16, .588);
const ray = new THREE.Vector3(random() - .5, random() - .5, -random() - .1).normalize();
let distance = 1e20;
for (let wall = 0; wall < 4; wall++) {
const a = wall * 4;
distance = Math.min(distance,
triangleDistance(origin, ray, vertices[a], vertices[a + 1], vertices[a + 2]),
triangleDistance(origin, ray, vertices[a], vertices[a + 2], vertices[a + 3]));
}
const expected = new THREE.Raycaster(origin, ray).intersectObjects(reveal.meshes, false)[0];
assert.equal(distance < 1e19, Boolean(expected), `Hit/miss mismatch at deterministic ray ${i}`);
if (expected) {
hits++;
const error = Math.abs(distance - expected.distance);
maxError = Math.max(maxError, error);
assert.ok(error <= 1e-9, `Distance mismatch ${error} at deterministic ray ${i}`);
}
matched++;
}
console.log(JSON.stringify({ matched, hits, misses: matched - hits, maxDistanceError: maxError }));
} finally {
reveal.dispose();
material.dispose();
}