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}