import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import { test } from 'node:test'; import { pathToFileURL, fileURLToPath } from 'node:url'; import ts from 'typescript'; async function loadTS(relative) { const file = new URL(relative, import.meta.url); let source = await readFile(file, 'utf8'); source = source.replace(/from ['"]three['"]/g, `from '${pathToFileURL(fileURLToPath(new URL('../node_modules/three/build/three.module.js', import.meta.url)))}'`); const imports = [...source.matchAll(/from ['"](\.[^'"]+)['"]/g)]; for (const match of imports) { const child = new URL(match[1] + '.ts', file); const url = await compile(child); source = source.replace(match[0], `from '${url}'`); } return import(encode(source)); } function encode(source) { return `data:text/javascript;base64,${Buffer.from(ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ES2020, target: ts.ScriptTarget.ES2020 } }).outputText).toString('base64')}`; } async function compile(file) { let source = await readFile(file, 'utf8'); source = source.replace(/from ['"]three['"]/g, `from '${new URL('../node_modules/three/build/three.module.js', import.meta.url)}'`); for (const match of [...source.matchAll(/from ['"](\.[^'"]+)['"]/g)]) source = source.replace(match[0], `from '${await compile(new URL(match[1] + '.ts', file))}'`); return encode(source); } const { comparisonLayout, paneAt } = await loadTS('../src/rendering/ComparisonLayout.ts'); const { createPatternedGlassGeometry, sampleGobangSurface } = await loadTS('../src/rendering/PatternedGlassGeometry.ts'); const { ComparisonMetrics } = await loadTS('../src/rendering/ComparisonMetrics.ts'); test('desktop and mobile comparison preserve a common unchanged projection aspect', () => { for (const [width,height,aspect] of [[1440,900,1.6],[390,844,390/844],[844,390,390/844],[320,320,1]]) { const {panes} = comparisonLayout(width,height,aspect); const [a,b] = panes; assert.equal(a.width,b.width); assert.equal(a.height,b.height); assert.ok(Math.abs(a.width/a.height-aspect)<1e-10); for (const p of panes) { assert.ok(p.x>=0 && p.y>=0); assert.ok(p.x+p.width<=width+1e-8); assert.ok(p.y+p.height<=height+1e-8); } assert.ok(a.x+a.width<=b.x || a.y+a.height<=b.y); assert.equal(paneAt(panes,b.x+b.width/2,b.y+b.height/2),b); } }); test('actual glass has finite front normals, closed edges and the same A back plane', () => { for(const curvature of [-1,1]) { const result=createPatternedGlassGeometry({width:1,height:1.32,grid:20,curvature}); const geometry=result.geometry, pos=geometry.attributes.position, normal=geometry.attributes.normal; assert.ok(geometry.index.count/3<100000,'bounded tessellation, not Blender 275k mesh'); assert.ok(geometry.index.count/3>1000,'real patterned geometry'); assert.ok(pos.array.every(Number.isFinite)); assert.ok(normal.array.every(Number.isFinite)); assert.ok(Math.abs(result.backZ-(-.008-.085/20))<1e-10); assert.ok(result.minThickness>0); assert.ok(result.maxThickness>result.minThickness); const samples=[]; for(let i=0;i.5) samples.push(pos.getZ(i)); assert.ok(Math.max(...samples)-Math.min(...samples)>.002); // Weld attribute seams by position, then require two incident triangles on every edge. const key=i=>[pos.getX(i),pos.getY(i),pos.getZ(i)].map(v=>v.toFixed(6)).join(','); const edges=new Map(); for(let i=0;in===2),'watertight triangulated volume'); geometry.dispose(); } }); test('geometry uses the existing gobang profile and periodic analytic normals', () => { const c={width:1,height:1.32,grid:20,curvature:1}; const p=sampleGobangSurface(.125,.2,c), q=sampleGobangSurface(.175,.2,c); assert.ok(Math.abs(p.height-q.height)<1e-10); const epsilon=1e-6; const derivative=(sampleGobangSurface(.126+epsilon,.203,c).height-sampleGobangSurface(.126-epsilon,.203,c).height)/(2*epsilon*c.width); const center=sampleGobangSurface(.126,.203,c); assert.ok(Math.abs(derivative-center.dx)<1e-5); const inv=sampleGobangSurface(.126,.203,{...c,curvature:-1}); assert.equal(inv.height,-center.height); assert.equal(inv.dx,-center.dx); }); test('metrics keep CPU submission time separate from cadence and never invent GPU memory', () => { const metrics=new ComparisonMetrics(); metrics.record('a',2.5,{calls:4,triangles:100}); metrics.record('b',5,{calls:6,triangles:200}); metrics.frame(100); metrics.frame(120); const snap=metrics.snapshot(); assert.equal(snap.a.cpuMs,2.5); assert.equal(snap.b.cpuMs,5); assert.equal(snap.a.calls,4); assert.equal(snap.b.triangles,200); assert.equal(snap.frameMs,20); assert.equal('gpuMs' in snap,false); assert.equal('gpuMemory' in snap,false); metrics.reset(); assert.equal(metrics.snapshot().frameMs,null); }); test('B uses explicitly extended Three transmission with shared photo/depth, never A refraction shader', async () => { const b=await readFile(new URL('../src/rendering/MeshGlassRenderer.ts',import.meta.url),'utf8'); assert.match(b,/new THREE\.MeshPhysicalMaterial/); assert.match(b,/transmission:\s*1/); assert.doesNotMatch(b,/mosaic\.frag|new THREE\.ShaderMaterial|AiDepthProvider|Worker\(/); assert.match(b,/attachMeshRefraction\(this\.material,depthSource\)/); assert.match(b,/depthSource\.geometry/); assert.match(b,/depthSource\.material/); assert.match(b,/dispose\(\)/); const main=await readFile(new URL('../src/main.ts',import.meta.url),'utf8'); assert.match(main,/import\('\.\/rendering\/PhysicalGlassComparison'\)/,'lazy opt-in compositor'); assert.equal((main.match(/new AiDepthProvider/g)||[]).length,1); assert.match(main,/comparison\?\.dispose/); assert.match(main,/renderLost \|\| aiDepthBusy \|\| document\.hidden/); }); test('tessellation stays bounded on extreme aspect ratios and approximates the height profile', () => { for(const height of [.02,.5,1.32,4,30]) { const shape={width:1,height,grid:24,curvature:1}; const result=createPatternedGlassGeometry(shape),g=result.geometry; assert.ok(g.index.count/3<86000,`hard triangle budget at ratio ${height}`); assert.ok(g.attributes.position.count<50000,`hard vertex budget at ratio ${height}`); if(height===1.32) { let maxError=0; const p=g.attributes.position,n=g.attributes.normal; for(let i=0;i