| 12345678910111213141516171819 |
- // 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;i<g.indices.length;i+=3){const ids=g.indices.slice(i,i+3),ps=ids.map(j=>g.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))
|