code wiki / _hdl_build / nx_bodybench.nx

nx_bodybench.nx source

↩ module page · 532 lines · 29598 B

1// nx_bodybench.nx -- ★SOVEREIGN BODY JUDGE, registerable as an MCP tool. Moves the shape ruler OFF the dev 2// laptop (it was PowerShell + System.Drawing) INTO a NishiLang organ that runs on the NAS: any agent, plan or 3// workflow can now measure a generated body against the BodyParts3D oracle with ONE call and get a permil 4// verdict back as JSON -- no image round-trip, no third-party runtime, no human in the loop. That is what 5// "it needs to scale" means: the measurement is a service, not a person at a keyboard. 6// 7// OOP, tagged-dispatch (NOT struct fn-pointer fields -- seq715: obj.fn(args) compiles and does NOT call): 8// * MeshView -- an object that renders ONE mesh at ONE yaw into coverage + per-pixel normal buffers 9// (a minimal measurement rasterizer: projection + edge-function fill + z-buffer + normal; 10// no shading/shadow/floor/texture -- those are display concerns, not measurement). 11// * Judge -- a small hierarchy dispatched by KIND: J_SILH (silhouette IoU) and J_NORMAL (normal-field 12// angular agreement). judge_eval(kind, ...) is the polymorphic entry; add a kind to extend. 13// * BodyBench -- orchestrates: renders ours+oracle at yaw 0 and 90, runs each judge, emits per-judge permil 14// and the HONEST HEADLINE = MIN over judges (hold several judges, publish the minimum). 15// 16// nx_bodybench <ours.nxmesh> <oracle.nxmesh> [W] [H] 17// -> {"front_iou":..,"side_iou":..,"shape":..,"headline":..,"self_iou":1000,"self_shape":1000, ...} 18// The self_* fields are a built-in NON-VACUITY proof: ours-vs-ours must score 1000/1000 or the ruler is broken. 19// license_tier: ORIGINAL expect_exit: 0 20import "nx_syscalls.nx" 21import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 22import "nx_vecmath.nx" // shared integer math (2026-08-24): bb_isqrt now aliases vm_isqrt, one owner 23 24const BB_Q14: i64 = 16384 25const BB_ZFAR: i64 = 2000000000 26const BB_BIG: i64 = 2000000000 27const BB_M8388607: i64 = 8388607 28const BB_M8388608: i64 = 8388608 29const BB_POSQ0: i64 = 4096 30const BB_TARGET: i64 = 200000 31const BB_MAGIC_40500: i64 = 40500 32const BB_ACOSN: i64 = 8192 // acos LUT resolution over cos in [-Q14,+Q14] 33const BB_DETR: i64 = 2 // detail-judge window radius -> a (2R+1)^2 = 5x5 neighbourhood; big 34 // enough to catch muscle/feature-scale normal variation, small enough 35 // to stay local. The one magic knob of the detail judge (rule 11). 36 37func bb_streq(a: *u8, b: *u8) -> i64 { 38 var i: i64 = 0 39 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 40 if b[i] != (0 as u8) { return 0 } 41 return 1 42} 43func bb_satoi(s: *u8) -> i64 { 44 var i: i64 = 0; var n: i64 = 0; var sg: i64 = 1 45 if s[0] == (45 as u8) { sg = 0 - 1; i = 1 } 46 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { n = n*10 + (c-48) } } i = i + 1 } 47 return n*sg 48} 49func bb_hw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n+1 } sys_write(1, s, n); return 0 } 50// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 51// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 52// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 53// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 54func bb_pn(v: i64) -> i64 { nxi_out(v); return 0 } 55func bb_rdbits(b: *u8, o: i64) -> i64 { 56 return (b[o] as i64) | ((b[o+1] as i64)<<8) | ((b[o+2] as i64)<<16) | ((b[o+3] as i64)<<24) 57} 58func bb_f32mul(b: *u8, o: i64, mul: i64) -> i64 { 59 let bits: i64 = bb_rdbits(b, o) 60 let sign: i64 = (bits>>31) & 1 61 let exp: i64 = (bits>>23) & 255 62 let mant: i64 = bits & BB_M8388607 63 if exp == 0 { return 0 } 64 let m: i64 = (mant | BB_M8388608) * mul 65 var e: i64 = exp - 127 - 23 66 var v: i64 = 0 67 if e >= 0 { v = m << e } else { let sh: i64 = 0 - e; v = (m + (1 << (sh-1))) >> sh } 68 if sign == 1 { v = 0 - v } 69 return v 70} 71// RETIRED ONTO THE SHARED OWNER 2026-08-24 (was a private Newton floor-sqrt; vm_isqrt is gate-proven exact). 72func bb_isqrt(v: i64) -> i64 { return vm_isqrt(v) } 73func bb_wrap(d: i64) -> i64 { var x: i64 = d % 360; if x < 0 { x = x + 360 } return x } 74func bb_min(a: i64, b: i64) -> i64 { if a < b { return a } return b } 75func bb_max(a: i64, b: i64) -> i64 { if a > b { return a } return b } 76// Bhaskara-I degree sine, Q14, exact at 0/30/90/150/180 -- our own integer trig, no float, no table literal. 77func bb_sin_fill(t: *i64) -> i64 { 78 var d: i64 = 0 79 while d < 180 { let P: i64 = d*(180-d); t[d] = BB_Q14*4*P/BB_MAGIC_40500; t[d+180] = 0-t[d]; d = d+1 } 80 return 0 81} 82// acos LUT: degree (0..180) as a function of cos, quantised into BB_ACOSN bins over cos in [-Q14,+Q14]. 83// Built by walking degrees (cos is monotonically decreasing) and forward-filling, so every bin resolves. 84func bb_acos_fill(acosT: *i64, sinT: *i64) -> i64 { 85 var i: i64 = 0 86 while i < BB_ACOSN { acosT[i] = 0-1; i = i+1 } 87 var d: i64 = 0 88 while d <= 180 { 89 let c: i64 = sinT[bb_wrap(d+90)] // cos(d) in Q14, +Q14 at 0, -Q14 at 180 90 var idx: i64 = (c + BB_Q14)*(BB_ACOSN-1)/(2*BB_Q14) 91 if idx < 0 { idx = 0 } 92 if idx >= BB_ACOSN { idx = BB_ACOSN-1 } 93 acosT[idx] = d 94 d = d + 1 95 } 96 // forward-fill from the high-cos end (idx high = small angle) downward 97 var last: i64 = 0 98 var j: i64 = BB_ACOSN-1 99 while j >= 0 { if acosT[j] < 0 { acosT[j] = last } else { last = acosT[j] } j = j - 1 } 100 return 0 101} 102func bb_acos_deg(acosT: *i64, dotq: i64) -> i64 { 103 var c: i64 = dotq 104 if c > BB_Q14 { c = BB_Q14 } 105 if c < 0-BB_Q14 { c = 0-BB_Q14 } 106 var idx: i64 = (c + BB_Q14)*(BB_ACOSN-1)/(2*BB_Q14) 107 if idx < 0 { idx = 0 } 108 if idx >= BB_ACOSN { idx = BB_ACOSN-1 } 109 return acosT[idx] 110} 111 112// ============================ MeshView ============================ 113// Render ONE mesh at ONE yaw into cov[] (0/1) and nbx/nby/nbz[] (Q14 camera-facing normal per pixel). 114// Framing REPLICATES nx_anat_sov 'fit' mode EXACTLY: scale-invariant precision (pass 0), full AABB (pass 1), 115// height-only camera distance -- so two meshes of equal stature render at equal on-screen height and the 116// silhouette IoU measures SHAPE, not scale. (This is the fair-ruler fix that stopped the ruler moving with 117// the thing it measured.) Returns the coverage pixel count. 118func mv_render(mb: *u8, ntris: i64, triBase: i64, lidBase: i64, W: i64, H: i64, yaw: i64, sinT: *i64, 119 cov: *i64, nbx: *i64, nby: *i64, nbz: *i64) -> i64 { 120 let ysin: i64 = sinT[bb_wrap(yaw)] 121 let ycos: i64 = sinT[bb_wrap(yaw+90)] 122 // pass 0: scale-invariant working precision 123 var q0mn: i64 = BB_BIG; var q0mx: i64 = 0-BB_BIG 124 var t0: i64 = 0 125 while t0 < ntris { 126 let o0: i64 = triBase + t0*84 127 var c0: i64 = 0 128 while c0 < 3 { let vq: i64 = bb_f32mul(mb, o0 + c0*4, BB_POSQ0); if vq<q0mn {q0mn=vq} if vq>q0mx {q0mx=vq} c0 = c0+1 } 129 t0 = t0 + 1 130 } 131 var span0: i64 = q0mx - q0mn 132 if span0 < 1 { span0 = 1 } 133 var posq: i64 = BB_POSQ0 * BB_TARGET / span0 134 if posq < 1 { posq = 1 } 135 // pass 1: full AABB -> centroid + halfH 136 var mnx: i64 = BB_BIG; var mny: i64 = BB_BIG; var mnz: i64 = BB_BIG 137 var mxx: i64 = 0-BB_BIG; var mxy: i64 = 0-BB_BIG; var mxz: i64 = 0-BB_BIG 138 var t: i64 = 0 139 while t < ntris { 140 var v: i64 = 0 141 while v < 3 { 142 let o: i64 = triBase + t*84 + v*12 143 let x: i64 = bb_f32mul(mb,o,posq); let y: i64 = bb_f32mul(mb,o+4,posq); let z: i64 = bb_f32mul(mb,o+8,posq) 144 if x<mnx {mnx=x} if x>mxx {mxx=x} if y<mny {mny=y} if y>mxy {mxy=y} if z<mnz {mnz=z} if z>mxz {mxz=z} 145 v = v + 1 146 } 147 t = t + 1 148 } 149 let cx0: i64 = (mnx+mxx)/2; let cy0: i64 = (mny+mxy)/2; let cz0: i64 = (mnz+mxz)/2 150 let halfH: i64 = (mxy-mny)/2 151 let FOCAL: i64 = H 152 var dist: i64 = 2*halfH*118/100 // 'fit': height-only framing 153 if dist < 1 { dist = 1 } 154 let cxh: i64 = W/2; let cyh: i64 = H/2 155 // clear buffers 156 let npx: i64 = W*H 157 let zb: *i64 = sys_mmap(npx*8) as *i64 158 var p: i64 = 0 159 while p < npx { cov[p]=0; zb[p]=BB_ZFAR; nbx[p]=0; nby[p]=0; nbz[p]=BB_Q14; p = p+1 } 160 // pass 2: rasterize (measurement only) 161 var cnt: i64 = 0 162 t = 0 163 while t < ntris { 164 let ob: i64 = triBase + t*84 165 let rx0v: i64 = bb_f32mul(mb,ob,posq)-cx0; let y0: i64 = bb_f32mul(mb,ob+4,posq)-cy0; let rz0v: i64 = bb_f32mul(mb,ob+8,posq)-cz0 166 let rx1v: i64 = bb_f32mul(mb,ob+12,posq)-cx0; let y1: i64 = bb_f32mul(mb,ob+16,posq)-cy0; let rz1v: i64 = bb_f32mul(mb,ob+20,posq)-cz0 167 let rx2v: i64 = bb_f32mul(mb,ob+24,posq)-cx0; let y2: i64 = bb_f32mul(mb,ob+28,posq)-cy0; let rz2v: i64 = bb_f32mul(mb,ob+32,posq)-cz0 168 let x0: i64 = (rx0v*ycos + rz0v*ysin)/BB_Q14; let z0: i64 = (rz0v*ycos - rx0v*ysin)/BB_Q14 169 let x1: i64 = (rx1v*ycos + rz1v*ysin)/BB_Q14; let z1: i64 = (rz1v*ycos - rx1v*ysin)/BB_Q14 170 let x2: i64 = (rx2v*ycos + rz2v*ysin)/BB_Q14; let z2: i64 = (rz2v*ycos - rx2v*ysin)/BB_Q14 171 let d0: i64 = dist - z0; let d1: i64 = dist - z1; let d2: i64 = dist - z2 172 if d0 > 0 { if d1 > 0 { if d2 > 0 { 173 let sx0: i64 = cxh + (x0*FOCAL)/d0; let sy0: i64 = cyh - (y0*FOCAL)/d0 174 let sx1: i64 = cxh + (x1*FOCAL)/d1; let sy1: i64 = cyh - (y1*FOCAL)/d1 175 let sx2: i64 = cxh + (x2*FOCAL)/d2; let sy2: i64 = cyh - (y2*FOCAL)/d2 176 var area: i64 = (sx1-sx0)*(sy2-sy0) - (sx2-sx0)*(sy1-sy0) 177 if area != 0 { 178 let q0x: i64 = bb_f32mul(mb,ob+36,BB_Q14); let n0y: i64 = bb_f32mul(mb,ob+40,BB_Q14); let q0z: i64 = bb_f32mul(mb,ob+44,BB_Q14) 179 let q1x: i64 = bb_f32mul(mb,ob+48,BB_Q14); let n1y: i64 = bb_f32mul(mb,ob+52,BB_Q14); let q1z: i64 = bb_f32mul(mb,ob+56,BB_Q14) 180 let q2x: i64 = bb_f32mul(mb,ob+60,BB_Q14); let n2y: i64 = bb_f32mul(mb,ob+64,BB_Q14); let q2z: i64 = bb_f32mul(mb,ob+68,BB_Q14) 181 let n0x: i64 = (q0x*ycos + q0z*ysin)/BB_Q14; let n0z: i64 = (q0z*ycos - q0x*ysin)/BB_Q14 182 let n1x: i64 = (q1x*ycos + q1z*ysin)/BB_Q14; let n1z: i64 = (q1z*ycos - q1x*ysin)/BB_Q14 183 let n2x: i64 = (q2x*ycos + q2z*ysin)/BB_Q14; let n2z: i64 = (q2z*ycos - q2x*ysin)/BB_Q14 184 var bxmn: i64 = bb_max(0, bb_min(sx0, bb_min(sx1,sx2))) 185 var bxmx: i64 = bb_min(W-1, bb_max(sx0, bb_max(sx1,sx2))) 186 var bymn: i64 = bb_max(0, bb_min(sy0, bb_min(sy1,sy2))) 187 var bymx: i64 = bb_min(H-1, bb_max(sy0, bb_max(sy1,sy2))) 188 var sgn: i64 = 1 189 if area < 0 { sgn = 0-1 } 190 let aabs: i64 = area*sgn 191 var py2: i64 = bymn 192 while py2 <= bymx { 193 var px2: i64 = bxmn 194 while px2 <= bxmx { 195 let e0: i64 = ((sx2-sx1)*(py2-sy1) - (sy2-sy1)*(px2-sx1))*sgn 196 let e1: i64 = ((sx0-sx2)*(py2-sy2) - (sy0-sy2)*(px2-sx2))*sgn 197 let e2: i64 = ((sx1-sx0)*(py2-sy0) - (sy1-sy0)*(px2-sx0))*sgn 198 if e0 >= 0 { if e1 >= 0 { if e2 >= 0 { 199 let depth: i64 = (e0*d0 + e1*d1 + e2*d2)/aabs 200 let idx: i64 = py2*W + px2 201 if depth < zb[idx] { 202 var nx: i64 = (e0*n0x + e1*n1x + e2*n2x)/aabs 203 var ny: i64 = (e0*n0y + e1*n1y + e2*n2y)/aabs 204 var nz: i64 = (e0*n0z + e1*n1z + e2*n2z)/aabs 205 var nl: i64 = bb_isqrt(nx*nx + ny*ny + nz*nz) 206 if nl < 1 { nl = 1 } 207 nx = nx*BB_Q14/nl; ny = ny*BB_Q14/nl; nz = nz*BB_Q14/nl 208 if nz < 0 { nx = 0-nx; ny = 0-ny; nz = 0-nz } 209 if cov[idx] == 0 { cnt = cnt + 1 } 210 zb[idx] = depth; cov[idx] = 1; nbx[idx] = nx; nby[idx] = ny; nbz[idx] = nz 211 } 212 }}} 213 px2 = px2 + 1 214 } 215 py2 = py2 + 1 216 } 217 } 218 }}} 219 t = t + 1 220 } 221 return cnt 222} 223 224// ============================ Judge ============================ 225const J_SILH: i64 = 1 226const J_NORMAL: i64 = 2 227// Polymorphic entry. Returns a permil (0..1000). Extend by adding a KIND and a branch. 228func judge_eval(kind: i64, npx: i64, covA: *i64, covB: *i64, 229 nax: *i64, nay: *i64, naz: *i64, nbx: *i64, nby: *i64, nbz: *i64, acosT: *i64) -> i64 { 230 var p: i64 = 0 231 if kind == J_SILH { 232 var inter: i64 = 0; var uni: i64 = 0 233 while p < npx { 234 let a: i64 = covA[p]; let b: i64 = covB[p] 235 if a==1 { if b==1 { inter = inter+1 } } 236 if a==1 { uni = uni+1 } else { if b==1 { uni = uni+1 } } 237 p = p + 1 238 } 239 if uni < 1 { uni = 1 } 240 return 1000*inter/uni 241 } 242 if kind == J_NORMAL { 243 var sumdeg: i64 = 0; var cnt: i64 = 0 244 while p < npx { 245 if covA[p]==1 { if covB[p]==1 { 246 let dotq: i64 = (nax[p]*nbx[p] + nay[p]*nby[p] + naz[p]*nbz[p])/BB_Q14 247 sumdeg = sumdeg + bb_acos_deg(acosT, dotq) 248 cnt = cnt + 1 249 }} 250 p = p + 1 251 } 252 if cnt < 1 { cnt = 1 } 253 let meandeg: i64 = sumdeg/cnt 254 var permil: i64 = 1000 - meandeg*1000/90 // matches the laptop ruler: 1 - meanDeg/90 255 if permil < 0 { permil = 0 } 256 return permil 257 } 258 return 0 259} 260 261// ★DETAIL JUDGE (surface-structure agreement). Silhouette IoU and front-view normal agreement are both 262// BLIND to missing surface detail: a smooth mannequin overlaps a real human's outline ~85% and its 263// mostly-camera-facing normals agree ~93%, so those judges cannot see that the face, hands, feet and muscle 264// definition are ABSENT. This fills a per-pixel LOCAL NORMAL VARIANCE (how busy the surface is in a small 265// window) -- high where anatomy creates normal swings (nostril, knuckle, muscle border), ~0 on a smooth tube. 266func bb_detail_fill(W: i64, H: i64, cov: *i64, nx: *i64, ny: *i64, nz: *i64, out: *i64, 267 ovx: *i64, ovy: *i64, ovz: *i64) -> i64 { 268 var y: i64 = 0 269 while y < H { 270 var x: i64 = 0 271 while x < W { 272 let p: i64 = y*W + x 273 if cov[p] == 1 { 274 var sx: i64 = 0; var sy: i64 = 0; var sz: i64 = 0 275 var qx: i64 = 0; var qy: i64 = 0; var qz: i64 = 0; var c: i64 = 0 276 var dy: i64 = 0-BB_DETR 277 while dy <= BB_DETR { 278 var dx: i64 = 0-BB_DETR 279 while dx <= BB_DETR { 280 let yy: i64 = y+dy; let xx: i64 = x+dx 281 if yy>=0 { if yy<H { if xx>=0 { if xx<W { 282 let q: i64 = yy*W+xx 283 if cov[q]==1 { 284 let ax: i64 = nx[q]; let ay: i64 = ny[q]; let az: i64 = nz[q] 285 sx=sx+ax; sy=sy+ay; sz=sz+az 286 qx=qx+ax*ax/BB_Q14; qy=qy+ay*ay/BB_Q14; qz=qz+az*az/BB_Q14 287 c=c+1 288 } 289 }}}} 290 dx=dx+1 291 } 292 dy=dy+1 293 } 294 if c < 2 { out[p]=0; ovx[p]=0; ovy[p]=0; ovz[p]=0 } else { 295 var vx: i64 = qx/c - (sx/c)*(sx/c)/BB_Q14 // var = E[n^2]-E[n]^2, n^2 pre-scaled by Q14 296 var vy: i64 = qy/c - (sy/c)*(sy/c)/BB_Q14 297 var vz: i64 = qz/c - (sz/c)*(sz/c)/BB_Q14 298 if vx < 0 { vx = 0 } 299 if vy < 0 { vy = 0 } 300 if vz < 0 { vz = 0 } 301 // the per-axis composition is the STRUCTURE SIGNATURE of the busyness: quantization 302 // terracing concentrates variance in one axis pattern, real anatomy mixes axes. Kept 303 // per-pixel so the score can compare busyness DIRECTION, not just magnitude. 304 ovx[p] = vx; ovy[p] = vy; ovz[p] = vz 305 out[p] = vx+vy+vz 306 } 307 } else { out[p]=0; ovx[p]=0; ovy[p]=0; ovz[p]=0 } 308 x=x+1 309 } 310 y=y+1 311 } 312 return 0 313} 314// score the two detail fields as an intersection-over-union in VARIANCE space over the shared coverage: 315// identical detail -> 1000; smooth-where-oracle-is-detailed -> punished; AND noisy-where-oracle-is-smooth -> 316// punished symmetrically, so it CANNOT be gamed by adding noise (the FBM-texture Goodhart that once fooled 317// the statistical grader scores ZERO here unless the noise lands exactly where the oracle has real detail). 318func bb_detail_score(npx: i64, covA: *i64, covB: *i64, dA: *i64, dB: *i64, 319 nax: *i64, nay: *i64, naz: *i64, nbx2: *i64, nby2: *i64, nbz2: *i64, acosT: *i64) -> i64 { 320 var num: i64 = 0; var den: i64 = 0; var p: i64 = 0 321 while p < npx { 322 if covA[p]==1 { if covB[p]==1 { 323 var df: i64 = dA[p]-dB[p]; if df<0 { df=0-df } 324 num = num + df 325 den = den + dA[p] + dB[p] 326 // ★AGREEMENT TERM (2026-08-10): magnitude-matched busyness only counts as agreement if the 327 // NORMALS actually agree. Two same-day convictions forced this: onion-ring terracing scored 328 // as detail (142 vs the honest 73 once the rings died), and a prior applied in the WRONG 329 // frame beat the same data correctly framed (249 vs 115) -- wrong-but-busy counted wherever 330 // the oracle was busy. The matched portion min(dA,dB) is discounted by the angular 331 // disagreement of the actual normals at this pixel: 90 degrees converts all of it to 332 // mismatch, 0 keeps it. Self-compare stays exactly 1000 (same normals, 0 degrees), so the 333 // built-in non-vacuity proof is preserved by construction. 334 // v2: STRUCTURE agreement -- the arrays now carry per-axis VARIANCE COMPOSITION, not point 335 // normals. v1 used point normals and its own bite test refuted it (wrong-frame prior fell 336 // only 249 to 244: on the back of a head the base normals agree even where the busyness is 337 // wrong). Cosine between the variance vectors: identical composition keeps the credit; 338 // orthogonal (axis-concentrated terracing vs mixed real anatomy) converts all of it to 339 // mismatch. Self-compare: identical vectors, cosine 1 -> non-vacuity preserved. 340 var mn: i64 = dA[p]; if dB[p] < mn { mn = dB[p] } 341 if mn > 0 { 342 let dvv: i64 = nax[p]*nbx2[p] + nay[p]*nby2[p] + naz[p]*nbz2[p] 343 var na2: i64 = bb_isqrt(nax[p]*nax[p] + nay[p]*nay[p] + naz[p]*naz[p]) 344 var nb2: i64 = bb_isqrt(nbx2[p]*nbx2[p] + nby2[p]*nby2[p] + nbz2[p]*nbz2[p]) 345 if na2 < 1 { na2 = 1 } 346 if nb2 < 1 { nb2 = 1 } 347 var fc: i64 = dvv/na2*BB_Q14/nb2 348 if fc > BB_Q14 { fc = BB_Q14 } 349 num = num + 2*mn*(BB_Q14-fc)/BB_Q14 350 } 351 }} 352 p = p + 1 353 } 354 if den < 1 { den = 1 } 355 var s: i64 = 1000 - 1000*num/den 356 if s < 0 { s = 0 } 357 return s 358} 359 360// HEAD-REGION DETAIL. A whole-body average cannot see a head-sized change: four new facial bases moved the 361// body number by -1 while the face visibly gained shadowed eye sockets. This restricts the detail score to 362// the top band of the rendered figure. 363// NOTE ON THE STANDING LAW "a horizontal image band is not an anatomical region": that law was earned on 364// MID-body bands, where a row merely CONTAINS the arms and hands. It does not apply to the top band -- at 365// the crown of a standing figure nothing but the head is present, so here the band IS the region. Stated 366// explicitly because the general law is right and this is a genuine exception, not an oversight. 367func bb_detail_head(W: i64, H: i64, covA: *i64, covB: *i64, dA: *i64, dB: *i64, 368 nax: *i64, nay: *i64, naz: *i64, nbx2: *i64, nby2: *i64, nbz2: *i64, acosT: *i64, frac: i64) -> i64 { 369 var ymin: i64 = 0-1 370 var ymax: i64 = 0-1 371 var y: i64 = 0 372 while y < H { 373 var x: i64 = 0 374 var any: i64 = 0 375 while x < W { let p: i64 = y*W+x; if covA[p]==1 { any=1 } else { if covB[p]==1 { any=1 } } x=x+1 } 376 if any == 1 { if ymin < 0 { ymin = y } ymax = y } 377 y = y+1 378 } 379 if ymin < 0 { return 0 } 380 let ycut: i64 = ymin + (ymax-ymin)*frac/1000 381 var num: i64 = 0 382 var den: i64 = 0 383 y = ymin 384 while y <= ycut { 385 var x2: i64 = 0 386 while x2 < W { 387 let p2: i64 = y*W+x2 388 if covA[p2]==1 { if covB[p2]==1 { 389 var df: i64 = dA[p2]-dB[p2] 390 if df < 0 { df = 0-df } 391 num = num + df 392 den = den + dA[p2] + dB[p2] 393 // same structure-agreement discount as bb_detail_score v2 (variance-vector cosine) 394 var mn: i64 = dA[p2]; if dB[p2] < mn { mn = dB[p2] } 395 if mn > 0 { 396 let dvv: i64 = nax[p2]*nbx2[p2] + nay[p2]*nby2[p2] + naz[p2]*nbz2[p2] 397 var na2: i64 = bb_isqrt(nax[p2]*nax[p2] + nay[p2]*nay[p2] + naz[p2]*naz[p2]) 398 var nb2: i64 = bb_isqrt(nbx2[p2]*nbx2[p2] + nby2[p2]*nby2[p2] + nbz2[p2]*nbz2[p2]) 399 if na2 < 1 { na2 = 1 } 400 if nb2 < 1 { nb2 = 1 } 401 var fc: i64 = dvv/na2*BB_Q14/nb2 402 if fc > BB_Q14 { fc = BB_Q14 } 403 num = num + 2*mn*(BB_Q14-fc)/BB_Q14 404 } 405 }} 406 x2 = x2+1 407 } 408 y = y+1 409 } 410 if den < 1 { den = 1 } 411 var sc: i64 = 1000 - 1000*num/den 412 if sc < 0 { sc = 0 } 413 return sc 414} 415func bb_emit_kv(name: *u8, v: i64, last: i64) -> i64 { 416 bb_hw("\x22" as *u8); bb_hw(name); bb_hw("\x22:" as *u8); bb_pn(v) 417 if last == 0 { bb_hw("," as *u8) } 418 return 0 419} 420 421func main(argc: i64, argv: *i64) -> i64 { 422 if argc < 3 { bb_hw("{\x22error\x22:\x22usage: nx_bodybench <ours.nxmesh> <oracle.nxmesh> [W] [H]\x22}\n" as *u8); return 2 } 423 let oursp: *u8 = argv[1] as *u8 424 let oracp: *u8 = argv[2] as *u8 425 var W: i64 = 620 426 var H: i64 = 950 427 if argc > 3 { W = bb_satoi(argv[3] as *u8) } 428 if argc > 4 { H = bb_satoi(argv[4] as *u8) } 429 let sinT: *i64 = sys_mmap(400*8) as *i64 430 bb_sin_fill(sinT) 431 let acosT: *i64 = sys_mmap(BB_ACOSN*8) as *i64 432 bb_acos_fill(acosT, sinT) 433 434 let l1: *i64 = sys_mmap(16) as *i64 435 let l2: *i64 = sys_mmap(16) as *i64 436 let mo: *u8 = sys_read_file(oursp, l1) 437 let mr: *u8 = sys_read_file(oracp, l2) 438 if (mo as i64) == 0 { bb_hw("{\x22error\x22:\x22cannot read ours\x22}\n" as *u8); return 3 } 439 if (mr as i64) == 0 { bb_hw("{\x22error\x22:\x22cannot read oracle\x22}\n" as *u8); return 3 } 440 let oNt: i64 = bb_rdbits(mo, 12); let oNl: i64 = bb_rdbits(mo, 8) 441 let rNt: i64 = bb_rdbits(mr, 12); let rNl: i64 = bb_rdbits(mr, 8) 442 let oTB: i64 = 16 + oNl*24; let oLB: i64 = oTB + oNt*84 443 let rTB: i64 = 16 + rNl*24; let rLB: i64 = rTB + rNt*84 444 445 let npx: i64 = W*H 446 // four coverage buffers (ours/oracle at yaw 0 and 90) + normals for the yaw-0 pair 447 let coA0: *i64 = sys_mmap(npx*8) as *i64 448 let coB0: *i64 = sys_mmap(npx*8) as *i64 449 let coA9: *i64 = sys_mmap(npx*8) as *i64 450 let coB9: *i64 = sys_mmap(npx*8) as *i64 451 let axn: *i64 = sys_mmap(npx*8) as *i64; let ayn: *i64 = sys_mmap(npx*8) as *i64; let azn: *i64 = sys_mmap(npx*8) as *i64 452 let bxn: *i64 = sys_mmap(npx*8) as *i64; let byn: *i64 = sys_mmap(npx*8) as *i64; let bzn: *i64 = sys_mmap(npx*8) as *i64 453 // scratch normals for the yaw-90 renders (silhouette only needs coverage) 454 let sxn: *i64 = sys_mmap(npx*8) as *i64; let syn: *i64 = sys_mmap(npx*8) as *i64; let szn: *i64 = sys_mmap(npx*8) as *i64 455 456 mv_render(mo, oNt, oTB, oLB, W, H, 0, sinT, coA0, axn, ayn, azn) 457 mv_render(mr, rNt, rTB, rLB, W, H, 0, sinT, coB0, bxn, byn, bzn) 458 mv_render(mo, oNt, oTB, oLB, W, H, 90, sinT, coA9, sxn, syn, szn) 459 mv_render(mr, rNt, rTB, rLB, W, H, 90, sinT, coB9, sxn, syn, szn) 460 461 let front: i64 = judge_eval(J_SILH, npx, coA0, coB0, axn,ayn,azn, bxn,byn,bzn, acosT) 462 let side: i64 = judge_eval(J_SILH, npx, coA9, coB9, axn,ayn,azn, bxn,byn,bzn, acosT) 463 let shape: i64 = judge_eval(J_NORMAL, npx, coA0, coB0, axn,ayn,azn, bxn,byn,bzn, acosT) 464 // ★DETAIL judge -- MULTI-VIEW so it credits the WHOLE surface, not just the front. Front-only detail was 465 // blind to back muscles and let a body with a defined front but a smooth back read the same as a fully 466 // detailed one. Detail is now the mean of FRONT (yaw 0) and BACK (yaw 180) surface-structure agreement. 467 let dA: *i64 = sys_mmap(npx*8) as *i64 468 let dB: *i64 = sys_mmap(npx*8) as *i64 469 // per-axis variance composition buffers (the busyness STRUCTURE signature; reused per view) 470 let vAx: *i64 = sys_mmap(npx*8) as *i64; let vAy: *i64 = sys_mmap(npx*8) as *i64; let vAz: *i64 = sys_mmap(npx*8) as *i64 471 let vBx: *i64 = sys_mmap(npx*8) as *i64; let vBy: *i64 = sys_mmap(npx*8) as *i64; let vBz: *i64 = sys_mmap(npx*8) as *i64 472 bb_detail_fill(W, H, coA0, axn, ayn, azn, dA, vAx, vAy, vAz) 473 bb_detail_fill(W, H, coB0, bxn, byn, bzn, dB, vBx, vBy, vBz) 474 let detail_front: i64 = bb_detail_score(npx, coA0, coB0, dA, dB, vAx,vAy,vAz, vBx,vBy,vBz, acosT) 475 // head is roughly the top 13 percent of a standing figure (1 of 7.5 heads) 476 let detail_head: i64 = bb_detail_head(W, H, coA0, coB0, dA, dB, vAx,vAy,vAz, vBx,vBy,vBz, acosT, 130) 477 478 // self-checks use the FRONT normals -- compute them BEFORE the back render overwrites axn/bxn. 479 let self_iou: i64 = judge_eval(J_SILH, npx, coA0, coA0, axn,ayn,azn, axn,ayn,azn, acosT) 480 let self_shp: i64 = judge_eval(J_NORMAL, npx, coA0, coA0, axn,ayn,azn, axn,ayn,azn, acosT) 481 let self_det: i64 = bb_detail_score(npx, coA0, coA0, dA, dA, vAx,vAy,vAz, vAx,vAy,vAz, acosT) 482 483 // BACK view (yaw 180): reuse the normal buffers now that the front judges are done. 484 let coA18: *i64 = sys_mmap(npx*8) as *i64 485 let coB18: *i64 = sys_mmap(npx*8) as *i64 486 mv_render(mo, oNt, oTB, oLB, W, H, 180, sinT, coA18, axn, ayn, azn) 487 mv_render(mr, rNt, rTB, rLB, W, H, 180, sinT, coB18, bxn, byn, bzn) 488 let dA2: *i64 = sys_mmap(npx*8) as *i64 489 let dB2: *i64 = sys_mmap(npx*8) as *i64 490 bb_detail_fill(W, H, coA18, axn, ayn, azn, dA2, vAx, vAy, vAz) 491 bb_detail_fill(W, H, coB18, bxn, byn, bzn, dB2, vBx, vBy, vBz) 492 let detail_back: i64 = bb_detail_score(npx, coA18, coB18, dA2, dB2, vAx,vAy,vAz, vBx,vBy,vBz, acosT) 493 let detail: i64 = (detail_front + detail_back)/2 494 // THREE-QUARTER VIEW (yaw 45). A front view is structurally blind to LATERAL surface: anatomically 495 // correct ribs are zero at the sternum and live on the flank, so front-only detail scored them DOWN 496 // (381 -> 378) for being right. Judging structure with a camera that cannot see it is a broken 497 // instrument, not a failed feature. ADDITIVE field -- the headline is untouched so every number 498 // published before this stays comparable. 499 let coA45: *i64 = sys_mmap(npx*8) as *i64 500 let coB45: *i64 = sys_mmap(npx*8) as *i64 501 mv_render(mo, oNt, oTB, oLB, W, H, 45, sinT, coA45, axn, ayn, azn) 502 mv_render(mr, rNt, rTB, rLB, W, H, 45, sinT, coB45, bxn, byn, bzn) 503 let dA3: *i64 = sys_mmap(npx*8) as *i64 504 let dB3: *i64 = sys_mmap(npx*8) as *i64 505 bb_detail_fill(W, H, coA45, axn, ayn, azn, dA3, vAx, vAy, vAz) 506 bb_detail_fill(W, H, coB45, bxn, byn, bzn, dB3, vBx, vBy, vBz) 507 let detail_q: i64 = bb_detail_score(npx, coA45, coB45, dA3, dB3, vAx,vAy,vAz, vBx,vBy,vBz, acosT) 508 let quarter_iou: i64 = judge_eval(J_SILH, npx, coA45, coB45, axn,ayn,azn, bxn,byn,bzn, acosT) 509 510 // HONEST HEADLINE = MIN over ALL judges, detail included. Silhouette/shape flatter a smooth mannequin; 511 // detail is what drags the number down to what the eye actually sees. 512 var headline: i64 = bb_min(front, bb_min(side, bb_min(shape, detail))) 513 514 bb_hw("{" as *u8) 515 bb_emit_kv("front_iou" as *u8, front, 0) 516 bb_emit_kv("side_iou" as *u8, side, 0) 517 bb_emit_kv("shape" as *u8, shape, 0) 518 bb_emit_kv("detail" as *u8, detail, 0) 519 bb_emit_kv("detail_front" as *u8, detail_front, 0) 520 bb_emit_kv("detail_back" as *u8, detail_back, 0) 521 bb_emit_kv("detail_quarter" as *u8, detail_q, 0) 522 bb_emit_kv("detail_head" as *u8, detail_head, 0) 523 bb_emit_kv("quarter_iou" as *u8, quarter_iou, 0) 524 bb_emit_kv("headline" as *u8, headline, 0) 525 bb_emit_kv("self_iou" as *u8, self_iou, 0) 526 bb_emit_kv("self_shape" as *u8, self_shp, 0) 527 bb_emit_kv("self_detail" as *u8, self_det, 0) 528 bb_emit_kv("W" as *u8, W, 0) 529 bb_emit_kv("H" as *u8, H, 0) 530 bb_hw("\x22judges\x22:\x22front_iou,side_iou,shape,DETAIL=mean(front,back) (MIN=headline); detail = ORIENTATION-GATED local-normal-variance agreement vs oracle: busyness must match AND point the same way (wrong-but-busy and quantization terracing both score as mismatch)\x22,\x22ruler\x22:\x22sovereign nx_bodybench -- dual-render coverage+normal, height-fit framing, integer acos LUT; NO image round-trip, NO 3rd-party\x22}\n" as *u8) 531 return 0 532}