code wiki / (root) / nx_nxa_anim_lookup_gate.nx

nx_nxa_anim_lookup_gate.nx source

↩ module page · 234 lines · 13365 B

1// nx_nxa_anim_lookup_gate.nx -- GATE for the one NXA clip evaluator (nx_nxa_anim_lib), driven IN-PROCESS on PLANTED 2// keys whose answers are known by arithmetic: lane sign-extension, track indexing that skips legacy channels 3// and refuses a truncated section by name, the bracketing-key nlerp at a known midpoint, the shortest-path 4// flip on a pair whose unflipped blend has the opposite sign, the root rule, the linear drift fit, the walk 5// cadence default, drift-removed rows that read zero on the root, an untracked joint's identity rows, and a 6// full idle hold that reproduces the pose. Every fixture asserts its own condition before its outcome. 7// license_tier: ORIGINAL No hw writes (Rule 26). 8import "nx_syscalls.nx" 9import "nx_nxa_anim_lib.nx" 10import "nx_gate_verdict.nx" 11 12const G_I64: i64 = 8 13const G_NJ: i64 = 3 14const G_ANIM_W: i64 = 24 15const G_SKEL_W: i64 = 24 16const G_SCR_W: i64 = 64 17const G_ROOT_J: i64 = 2 18const G_HEAD_Z: i64 = 150000 19const G_MID_Z: i64 = 50000 20const G_DUR: i64 = 1000 21const G_TMID: i64 = 500 22const G_Q45: i64 = 2896 // the q12 half-root of two: nlerp of identity and a half-turn, normalised 23const G_Q_TOL: i64 = 2 24const G_MM_END: i64 = 300 // joint 0 dt lane at the last key, mm -> 30000 model units 25const G_ROOT_MM: i64 = 100 // root dt lane at the last key, mm -> 10000 model units over 1000 ms 26const G_ROOT_VX16: i64 = 655360 // 10000 units / 1000 ms x 65536 27const G_DUAL_X_J0: i64 = 14480000 // ((15000 - 5000) x 2896)/2: joint 0's midpoint dt 15000 MINUS the root's fitted drift 28 // at t=500 (x0 + vx*t = 5000), which is subtracted from EVERY joint exactly as the 29 // page's animEval did; T = (10000,0,0) under a z-rotation of bind (0,0,150000). 30 // FIRST RUN OF THIS GATE READ 14480000 AGAINST A HAND-COMPUTED 21720000: the 31 // implementation was right and the expectation had forgotten the drift term. 32const G_A_QZ: i64 = 2896 33const G_A_QW: i64 = 2896 34const G_B_QZ: i64 = 0 - 3547 35const G_B_QW: i64 = 2048 36 37func g_lane(v: i64) -> i64 { return v & NA_LANE_MASK } 38func g_key(a: *i64, o: i64, t: i64, qx: i64, qy: i64, qz: i64, qw: i64, dx: i64, dy: i64, dz: i64) -> i64 { 39 a[o] = g_lane(t) | (g_lane(qx) << NA_LANE_B1) | (g_lane(qy) << NA_LANE_B2) | (g_lane(qz) << NA_LANE_B3) 40 a[o + 1] = g_lane(qw) | (g_lane(dx) << NA_LANE_B1) | (g_lane(dy) << NA_LANE_B2) | (g_lane(dz) << NA_LANE_B3) 41 return 0 42} 43 44func g_eval_legacy(anim: *i64, ko: i64, nk: i64, t: i64, q: *i64, d: *i64, scr: *i64) -> i64 { 45 var c: i64 = 0 46 var i: i64 = 1 47 while i < nk { if na_key_t(anim, ko, i) <= t { c = i } i = i + 1 } 48 var k1: i64 = c + 1 49 if k1 >= nk { k1 = c } 50 let t0: i64 = na_key_t(anim, ko, c) 51 let t1: i64 = na_key_t(anim, ko, k1) 52 var u: i64 = 0 53 if t1 > t0 { u = (t - t0)*NA_Q12/(t1 - t0) } 54 if u < 0 { u = 0 } 55 if u > NA_Q12 { u = NA_Q12 } 56 na_key_q(anim, ko, c, q) 57 let b: *i64 = scr 58 na_key_q(anim, ko, k1, b) 59 let dot: i64 = q[0]*b[0] + q[1]*b[1] + q[2]*b[2] + q[3]*b[3] 60 if dot < 0 { b[0] = 0 - b[0]; b[1] = 0 - b[1]; b[2] = 0 - b[2]; b[3] = 0 - b[3] } 61 q[0] = q[0] + (b[0] - q[0])*u/NA_Q12 62 q[1] = q[1] + (b[1] - q[1])*u/NA_Q12 63 q[2] = q[2] + (b[2] - q[2])*u/NA_Q12 64 q[3] = q[3] + (b[3] - q[3])*u/NA_Q12 65 nf_qnorm(q) 66 na_key_d(anim, ko, c, d) 67 let db: *i64 = ((scr as i64) + 32) as *i64 68 na_key_d(anim, ko, k1, db) 69 d[0] = d[0] + (db[0] - d[0])*u/NA_Q12 70 d[1] = d[1] + (db[1] - d[1])*u/NA_Q12 71 d[2] = d[2] + (db[2] - d[2])*u/NA_Q12 72 return 0 73} 74 75 76// Exhaustive bounded timestamps include duplicate keys, unsorted tracks and time reversal. 77// The oracle is the pre-change interpolation routine, independent of the new selectors. 78const L_KEYS: i64 = 5 79const L_TIMES: i64 = 4 80func g_lookup(c: *i64) -> i64 { 81 let a: *i64 = sys_mmap(L_KEYS*NA_CH2_KEY_W*8) as *i64 82 let idx: *i64 = sys_mmap(NA_IDX_W*8) as *i64 83 let ord: *i64 = sys_mmap(8) as *i64 84 let qa: *i64 = sys_mmap(32) as *i64 85 let qb: *i64 = sys_mmap(32) as *i64 86 let da: *i64 = sys_mmap(24) as *i64 87 let db: *i64 = sys_mmap(24) as *i64 88 let sa: *i64 = sys_mmap(NA_SCR_W*8) as *i64 89 let sb: *i64 = sys_mmap(NA_SCR_W*8) as *i64 90 var mismatches: i64 = 0 91 var ordererrors: i64 = 0 92 var samples: i64 = 0 93 var orderedtracks: i64 = 0 94 var fallbacktracks: i64 = 0 95 var nk: i64 = 1 96 var variants: i64 = 1 97 while nk <= L_KEYS { 98 variants = variants*L_TIMES 99 var v: i64 = 0 100 while v < variants { 101 var digits: i64 = v 102 var i: i64 = 0 103 var sorted: i64 = 1 104 var previous: i64 = 0 105 while i < nk { 106 let kt: i64 = digits % L_TIMES 107 digits = digits/L_TIMES 108 if i > 0 { if kt < previous { sorted = 0 } } 109 previous = kt 110 var qw: i64 = NA_Q12 111 if i % 2 == 1 { qw = 0 - NA_Q12 } 112 g_key(a,i*NA_CH2_KEY_W,kt,i*97,0,i*137,qw,i*11,0-i*7,i*3) 113 i = i + 1 114 } 115 idx[0] = 0; idx[1] = nk 116 na_index_order(a,idx,1,ord) 117 if ord[0] != sorted { ordererrors = ordererrors + 1 } 118 if ord[0] == 1 { orderedtracks = orderedtracks + 1 } else { fallbacktracks = fallbacktracks + 1 } 119 var t: i64 = L_TIMES + 1 120 while t >= 0 - 1 { 121 g_eval_legacy(a,0,nk,t,qa,da,sa) 122 if ord[0] == 1 { na_eval_track_ordered(a,0,nk,t,qb,db,sb) } else { na_eval_track(a,0,nk,t,qb,db,sb) } 123 i = 0 124 while i < 4 { if qa[i] != qb[i] { mismatches = mismatches + 1 }; i = i + 1 } 125 i = 0 126 while i < 3 { if da[i] != db[i] { mismatches = mismatches + 1 }; i = i + 1 } 127 samples = samples + 1 128 t = t - 1 129 } 130 v = v + 1 131 } 132 nk = nk + 1 133 } 134 gv_check_eq("exhaustive-track-order-classification" as *u8,ordererrors,0,c) 135 gv_check_eq("exhaustive-legacy-quaternion-translation-parity" as *u8,mismatches,0,c) 136 gv_check_eq("exhaustive-sample-count" as *u8,samples,9548,c) 137 gv_check_eq("ordered-track-coverage" as *u8,orderedtracks,125,c) 138 gv_check_eq("unsorted-fallback-coverage" as *u8,fallbacktracks,1239,c) 139 idx[0] = 0 - 1; idx[1] = 0; ord[0] = 1 140 na_index_order(a,idx,1,ord) 141 gv_check_eq("absent-track-clears-order-sidecar" as *u8,ord[0],0,c) 142 sys_munmap(a as *u8,L_KEYS*NA_CH2_KEY_W*8); sys_munmap(idx as *u8,NA_IDX_W*8); sys_munmap(ord as *u8,8) 143 sys_munmap(qa as *u8,32); sys_munmap(qb as *u8,32); sys_munmap(da as *u8,24); sys_munmap(db as *u8,24) 144 sys_munmap(sa as *u8,NA_SCR_W*8); sys_munmap(sb as *u8,NA_SCR_W*8) 145 return 0 146} 147 148func main() -> i64 { 149 gv_head("=== nx_nxa_anim_lookup_gate -- the NXA clip evaluator proven on planted keys ===" as *u8) 150 let c: *i64 = gv_ctr() 151 // T1 lanes 152 gv_check_eq("i16-lane-sign-extends (planted 0xFFFE at bits 16 reads -2)" as *u8, na_i16(g_lane(0 - 2) << NA_LANE_B1, NA_LANE_B1), 0 - 2, c) 153 gv_check_eq("i16-lane-positive-passes-through (planted 4096 at bits 32)" as *u8, na_i16(4096 << NA_LANE_B2, NA_LANE_B2), 4096, c) 154 // T2 a planted ANIM payload: three tracks -- joint 0 ch2 (identity -> half turn about z, dt 0 -> 300 mm x), 155 // joint 1 ch0 (a legacy verbose track that must be SKIPPED), joint 2 = the root, ch2, linear x drift 156 let anim: *i64 = sys_mmap(G_ANIM_W * G_I64) as *i64 157 anim[0] = 3 158 anim[1] = 0; anim[2] = NA_CH2; anim[3] = 2 159 g_key(anim, 4, 0, 0, 0, 0, NA_Q12, 0, 0, 0) 160 g_key(anim, 6, G_DUR, 0, 0, NA_Q12, 0, G_MM_END, 0, 0) 161 anim[8] = 1; anim[9] = 0; anim[10] = 1 162 anim[11] = 0; anim[12] = 0; anim[13] = 0; anim[14] = 0 163 anim[15] = G_ROOT_J; anim[16] = NA_CH2; anim[17] = 3 164 g_key(anim, 18, 0, 0, 0, 0, NA_Q12, 0, 0, 0) 165 g_key(anim, 20, G_TMID, 0, 0, 0, NA_Q12, G_ROOT_MM/2, 0, 0) 166 g_key(anim, 22, G_DUR, 0, 0, 0, NA_Q12, G_ROOT_MM, 0, 0) 167 let tidx: *i64 = sys_mmap(G_NJ * NA_IDX_W * G_I64) as *i64 168 let dur: i64 = na_index(anim, G_ANIM_W, G_NJ, tidx) 169 gv_check_eq("index-clip-duration-is-the-latest-last-key" as *u8, dur, G_DUR, c) 170 gv_check_eq("index-joint0-key-offset" as *u8, tidx[0], 4, c) 171 gv_check_eq("index-joint0-nkeys" as *u8, tidx[1], 2, c) 172 gv_check_eq("index-skips-a-legacy-channel-0-track (joint 1 untracked)" as *u8, tidx[2], 0 - 1, c) 173 gv_check_eq("index-root-nkeys" as *u8, tidx[5], 3, c) 174 let tidx2: *i64 = sys_mmap(G_NJ * NA_IDX_W * G_I64) as *i64 175 gv_check_eq("neg-control-a-truncated-section-is-REFUSED-by-name (overrun)" as *u8, na_index(anim, G_ANIM_W - 4, G_NJ, tidx2), NA_RC_OVERRUN, c) 176 // T3 the midpoint sample of joint 0: nlerp(identity, half-turn) normalised, dt halfway in model units 177 let q: *i64 = sys_mmap(8 * G_I64) as *i64 178 let d: *i64 = sys_mmap(8 * G_I64) as *i64 179 let scr: *i64 = sys_mmap(G_SCR_W * G_I64) as *i64 180 na_eval_track(anim, tidx[0], tidx[1], G_TMID, q, d, scr) 181 gv_check_near("nlerp-midpoint-qz (q12 half-root-two)" as *u8, q[2], G_Q45, G_Q_TOL, c) 182 gv_check_near("nlerp-midpoint-qw" as *u8, q[3], G_Q45, G_Q_TOL, c) 183 gv_check_eq("dt-lerp-midpoint-in-model-units (mm x 100)" as *u8, d[0], G_MM_END*NA_DT_MM_TO_MODEL/2, c) 184 // T4 shortest path: a = 90 deg about z, b = the NEGATED -120 deg key; dot < 0 so b is flipped and the 185 // blend lands on the short arc with qz POSITIVE; the long arc (no flip) lands with qz NEGATIVE 186 let anim2: *i64 = sys_mmap(8 * G_I64) as *i64 187 anim2[0] = 1; anim2[1] = 0; anim2[2] = NA_CH2; anim2[3] = 2 188 g_key(anim2, 4, 0, 0, 0, G_A_QZ, G_A_QW, 0, 0, 0) 189 g_key(anim2, 6, G_DUR, 0, 0, G_B_QZ, G_B_QW, 0, 0, 0) 190 na_eval_track(anim2, 4, 2, G_TMID, q, d, scr) 191 gv_check("fixture-reached-the-condition: the planted pair has a negative dot (a flip is required)" as *u8, ((G_A_QZ*G_B_QZ + G_A_QW*G_B_QW) < 0) as i64, c) 192 gv_check("shortest-path-flip-fires: the blend takes the short arc (qz > 0)" as *u8, (q[2] > 0) as i64, c) 193 // T5 the root rule, the drift fit and the cadence default on a planted skeleton 194 let skel: *i64 = sys_mmap(G_SKEL_W * G_I64) as *i64 195 var z: i64 = 0 196 while z < G_SKEL_W { skel[z] = 0; z = z + 1 } 197 skel[0*NA_SKEL_W + NA_SKEL_TZ] = G_HEAD_Z 198 skel[1*NA_SKEL_W + NA_SKEL_TZ] = G_MID_Z 199 skel[2*NA_SKEL_W + NA_SKEL_TZ] = NA_ROOT_Z 200 let rj: i64 = na_root_joint(skel, G_NJ) 201 gv_check_eq("root-joint-is-the-bind-nearest-the-pelvis-height" as *u8, rj, G_ROOT_J, c) 202 let rv: *i64 = sys_mmap(NA_RV_W * G_I64) as *i64 203 na_fit_root(anim, tidx, rj, rv, scr) 204 gv_check_eq("drift-fit-vx16 (10000 units over 1000 ms x 65536)" as *u8, rv[2], G_ROOT_VX16, c) 205 gv_check_eq("drift-fit-span" as *u8, rv[5], G_DUR, c) 206 gv_check_eq("cadence-default-when-root-travel-is-under-1.5-blocks" as *u8, na_mpb(anim, tidx, rj, dur, scr), NA_MPB_DEFAULT, c) 207 // T6 rows at the midpoint, no hold: root dual is ZERO (its drift is the fitted line), joint 1 untracked 208 // reads identity, joint 0 carries the known dual 209 let pq: *i64 = sys_mmap(G_NJ * 4 * G_I64) as *i64 210 let pd: *i64 = sys_mmap(G_NJ * 3 * G_I64) as *i64 211 var j: i64 = 0 212 while j < G_NJ { pq[j*4] = 0; pq[j*4 + 1] = 0; pq[j*4 + 2] = 0; pq[j*4 + 3] = NA_Q12; pd[j*3] = 0; pd[j*3 + 1] = 0; pd[j*3 + 2] = 0; j = j + 1 } 213 let out: *i64 = sys_mmap(G_NJ * NA_ROW_W * G_I64) as *i64 214 na_rows(anim, tidx, G_NJ, skel, pq, pd, 0, rv, G_TMID, 0, out, scr) 215 gv_check_eq("root-drift-removed: root dual x is zero at the midpoint" as *u8, out[G_NJ*4 + G_ROOT_J*4], 0, c) 216 gv_check_eq("root-drift-removed: root dual w is zero at the midpoint" as *u8, out[G_NJ*4 + G_ROOT_J*4 + 3], 0, c) 217 gv_check_eq("untracked-joint-real-is-identity" as *u8, out[1*4 + 3], NA_Q12, c) 218 gv_check_eq("untracked-joint-dual-is-zero" as *u8, out[G_NJ*4 + 1*4], 0, c) 219 gv_check_eq("joint0-dual-x-is-the-known-answer" as *u8, out[G_NJ*4], G_DUAL_X_J0, c) 220 gv_check_eq("joint0-dual-y-is-minus-the-known-answer" as *u8, out[G_NJ*4 + 1], 0 - G_DUAL_X_J0, c) 221 // T7 a full idle hold toward the identity pose reproduces the pose rows: real identity, dual zero 222 na_rows(anim, tidx, G_NJ, skel, pq, pd, 1, rv, G_TMID, NA_Q12, out, scr) 223 gv_check_eq("full-idle-hold-joint0-real-is-the-pose" as *u8, out[3], NA_Q12, c) 224 gv_check_eq("full-idle-hold-joint0-dual-is-zero" as *u8, out[G_NJ*4], 0, c) 225 let oversized: *i64 = sys_mmap(32) as *i64 226 oversized[0]=1; oversized[1]=0; oversized[2]=0; oversized[3]=4611686018427387904 227 gv_check_eq("overflowing-legacy-key-count-refused-before-offset-arithmetic" as *u8, na_index(oversized,4,G_NJ,tidx2), NA_RC_OVERRUN,c) 228 g_lookup(c) 229 gv_values_head() 230 gv_kv("dur_ms" as *u8, dur) 231 gv_kv("root_vx16" as *u8, rv[2]) 232 gv_kv("dual_x_j0" as *u8, G_DUAL_X_J0) 233 return gv_verdict("nx_nxa_anim_lookup_gate" as *u8, c, "the NXA clip evaluator proven on planted keys: lanes sign-extend, the index skips legacy channels and refuses a truncated section by name, the midpoint nlerp and dt lerp land on their arithmetic, the shortest-path flip takes the short arc, the root rule and drift fit and cadence default read their planted values, drift-removed root rows are zero, untracked joints read identity, and a full idle hold reproduces the pose" as *u8) 234}