// Offline rejection evidence. Never modifies served models. import {readFileSync,writeFileSync} from 'node:fs' import {join,resolve} from 'node:path' import {parseGlb} from './instance-repeated-meshes.mjs' import {decodePrimitive} from './instance-safe-primitives.mjs' const dir=resolve(process.argv[2]),m=JSON.parse(readFileSync(join(dir,'manifest.json'))),bad=[],cells=new Set() for(const r of m.records.filter(r=>r.level==='shell')){ const {json,bin}=parseGlb(readFileSync(join(dir,r.uri)));let failures=0,minDot=1,degenerate=0 for(const p of json.meshes[0].primitives){const g=decodePrimitive(json,bin,p) for(let i=0;ig.positions.slice(j*3,j*3+3)),a=ps[1].map((v,k)=>v-ps[0][k]),b=ps[2].map((v,k)=>v-ps[0][k]),cross=[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]],len=Math.hypot(...cross) if(len<1e-10){degenerate++;continue} for(const j of ids){const n=g.normals.slice(j*3,j*3+3),dot=n.reduce((s,v,k)=>s+v*cross[k],0)/Math.hypot(...n)/len;minDot=Math.min(minDot,dot);if(dot<.9)failures++} } } if(failures||degenerate){r.sourceCells.forEach(c=>cells.add(c));bad.push({uri:r.uri,sourceCells:r.sourceCells,failures,degenerate,minDot})} } const result={failedGroups:bad.length,cells:[...cells],bad} if(process.argv[3])writeFileSync(process.argv[3],JSON.stringify(result,null,2)) console.log(JSON.stringify(result))