code wiki / (root) / nx_nxa_joints.nx

nx_nxa_joints.nx source

↩ module page · 260 lines · 12228 B

1// nx_nxa_joints.nx -- SOVEREIGN STRUCTURAL JOINT IDENTIFICATION over an NXA SKEL. 2// 3// WHY: NXA's SKEL carries NO JOINT NAMES, by design (nx_nxa_rig_emit: "SKEL carries no names by 4// design"; nx_nxa_anim: "names exist in the FBX even though SKEL stays nameless by design"). So a 5// consumer that needs "where is the head / a breast / a thigh" must derive it from GEOMETRY. 6// 7// That derivation already exists -- but ONLY as JavaScript emitted into the browser viewer by 8// nx_nxa_rig_emit. There is no NishiLang function for it, so nothing server-side can ask the 9// question. This is that function, and it is meant to be the CANONICAL one: the JS emitter can 10// later emit the indices this computes instead of re-deriving them in the browser, which removes 11// the cross-language duplicate rather than adding to it (rule 15). 12// 13// METHOD (same shape as the proven JS): height fraction over the rig's own extent + laterality. 14// h01(j) = (y - ymin) / (ymax - ymin) in permil, so 0 = lowest joint, 1000 = highest 15// lat(j) = x relative to the midline, in permil of height (sign = side) 16// Nothing here is a magic anatomical constant: the bands are derived from THIS rig's extent, so a 17// taller/shorter/differently-scaled rig lands in the same normalised space. 18// 19// OUTPUT is the anchor set for nx_arousal_skin_lib's sk_thresh_field_rig -- (x, y, site) triples -- 20// so skin optics can run over a real imported rig instead of only the hardcoded SDF body. 21// Sites (Masters & Johnson propagation order): 0 submammary 1 breast 2 torso 3 face 4 extremities 22// 5 genital. knowledge/arousal_rig_anchors.conf documents the anatomy->site truth this must AGREE 23// WITH; it is the ORACLE for these rules, not their input (it is name-keyed and NXA has no names). 24// 25// usage: nx_nxa_joints <file.nxa> 26// license_tier: ORIGINAL expect_exit: 0 27import "nx_syscalls.nx" 28import "nx_nxa.nx" 29 30const NJ_MAGIC_1000: i64 = 1000 31const NJ_WORDS_PER_JOINT: i64 = 8 // [parent][x][y][z][qx][qy][qz][qw] 32 33func njw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 34func njn(v: i64) -> i64 { 35 let t: *u8 = sys_mmap(32) 36 var m: i64 = v 37 var w: i64 = 0 38 if m < 0 { t[w] = 45 as u8; w = w + 1; m = 0 - m } 39 if m == 0 { t[w] = 48 as u8; sys_write(1, t, w + 1); return 0 } 40 let d: *u8 = sys_mmap(32) 41 var k: i64 = 0 42 while m > 0 { d[k] = ((48 + (m - (m / 10) * 10)) as u8); m = m / 10; k = k + 1 } 43 var j: i64 = 0 44 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 } 45 sys_write(1, t, w) 46 return 0 47} 48 49// ---- anchor emission: append (x, y, site) into caller arrays ---- 50func nj_push(ax: *i64, ay: *i64, st: *i64, n: *i64, x: i64, y: i64, site: i64) -> i64 { 51 let i: i64 = n[0] 52 ax[i] = x 53 ay[i] = y 54 st[i] = site 55 n[0] = i + 1 56 return 0 57} 58 59func main(argc: i64, argv: *i64) -> i64 { 60 if argc < 2 { njw("usage: nx_nxa_joints <file.nxa>\n" as *u8); return 2 } 61 let path: *u8 = argv[1] as *u8 62 let fd: i64 = sys_openat_rd(path) 63 if fd < 0 { njw("open failed\n" as *u8); return 9 } 64 let CAP: i64 = 67108864 65 let b: *u8 = sys_mmap(CAP) 66 var flen: i64 = 0 67 var go: i64 = 1 68 while go == 1 { 69 let r: i64 = sys_read(fd, ((b as i64) + flen) as *u8, CAP - flen) 70 if r <= 0 { go = 0 } else { flen = flen + r; if flen >= CAP { go = 0 } } 71 } 72 sys_close(fd) 73 if flen < 64 { njw("too small\n" as *u8); return 9 } 74 75 // nxa_find VERIFIES magic, version and the section checksum before returning (refuse-corrupt law) 76 let swo: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8)) 77 if swo < 0 { njw("no SKEL (or corrupt/future-version) rc=" as *u8); njn(swo); njw("\n" as *u8); return 4 } 78 let w: *i64 = b as *i64 79 let nj: i64 = w[swo] 80 if nj <= 0 { njw("SKEL empty\n" as *u8); return 4 } 81 if nj > 4096 { njw("SKEL implausible joint count\n" as *u8); return 4 } 82 83 // ---- pass 1: extents on ALL THREE AXES, then DETECT which is up ---- 84 // ⚠Do NOT assume an axis. Measured on a real CC rig: assuming word 2 was up produced a joint 85 // whose LATERAL offset was 1.39x the supposed body height -- impossible for a humanoid, and the 86 // face band then matched nothing (gate exit 62). FBX exporters differ on Y-up vs Z-up, so the 87 // rig must be asked. A humanoid's longest extent IS its height; the next longest is the arm 88 // span across the body. The proven JS in nx_nxa_rig_emit does the same (`const H=ex[up]`). 89 let mn: *i64 = sys_mmap(32) as *i64 90 let mx: *i64 = sys_mmap(32) as *i64 91 var first: i64 = 1 92 var j: i64 = 0 93 while j < nj { 94 let base: i64 = swo + 1 + j * NJ_WORDS_PER_JOINT 95 var a: i64 = 0 96 while a < 3 { 97 let v: i64 = w[base + 1 + a] 98 if first == 1 { mn[a] = v; mx[a] = v } 99 else { 100 if v < mn[a] { mn[a] = v } 101 if v > mx[a] { mx[a] = v } 102 } 103 a = a + 1 104 } 105 first = 0 106 j = j + 1 107 } 108 var upax: i64 = 0 109 var latax: i64 = 1 110 var bestspan: i64 = mx[0] - mn[0] 111 var a2: i64 = 1 112 while a2 < 3 { 113 let s2: i64 = mx[a2] - mn[a2] 114 if s2 > bestspan { bestspan = s2; upax = a2 } 115 a2 = a2 + 1 116 } 117 // lateral = the widest of the two remaining axes (arm span beats front-back depth) 118 var bestlat: i64 = 0 - 1 119 a2 = 0 120 while a2 < 3 { 121 if a2 != upax { 122 let s3: i64 = mx[a2] - mn[a2] 123 if s3 > bestlat { bestlat = s3; latax = a2 } 124 } 125 a2 = a2 + 1 126 } 127 let ymin: i64 = mn[upax] 128 let ymax: i64 = mx[upax] 129 let hspan: i64 = ymax - ymin 130 if hspan <= 0 { njw("degenerate rig extent -- cannot normalise\n" as *u8); return 5 } 131 njw("axes up=" as *u8); njn(upax); njw(" lat=" as *u8); njn(latax); njw("\n" as *u8) 132 133 njw("nxa_joints joints=" as *u8); njn(nj) 134 njw(" ymin=" as *u8); njn(ymin) 135 njw(" ymax=" as *u8); njn(ymax) 136 njw(" span_mm=" as *u8); njn(hspan); njw("\n" as *u8) 137 138 // ---- pass 2: structural anchor selection ---- 139 // Bands are fractions of THIS rig's own extent, so scale/units cancel. 140 let ax: *i64 = sys_mmap(64 * 8) as *i64 141 let ay: *i64 = sys_mmap(64 * 8) as *i64 142 let st: *i64 = sys_mmap(64 * 8) as *i64 143 let n: *i64 = sys_mmap(16) as *i64 144 n[0] = 0 145 146 // per-site extreme trackers: pick the joint best matching each site's structural signature 147 var bestGenY: i64 = 0 148 var bestGenX: i64 = 0 149 var haveGen: i64 = 0 150 var bestHeadY: i64 = 0 151 var bestHeadX: i64 = 0 152 var haveHead: i64 = 0 153 154 j = 0 155 while j < nj { 156 let base: i64 = swo + 1 + j * NJ_WORDS_PER_JOINT 157 let x: i64 = w[base + 1 + latax] 158 let y: i64 = w[base + 1 + upax] 159 let h: i64 = ((y - ymin) * NJ_MAGIC_1000) / hspan // 0..1000 160 var axl: i64 = x 161 if axl < 0 { axl = 0 - axl } 162 let lat: i64 = (axl * NJ_MAGIC_1000) / hspan // permil of height 163 164 // DIAGNOSTIC: emit every joint's normalised position. The bands below must be DERIVED from 165 // this distribution, never guessed -- a band tuned until the gate greens measures the tuning, 166 // not the anatomy. 167 njw("J " as *u8); njn(j) 168 njw(" h=" as *u8); njn(h) 169 njw(" lat=" as *u8); njn(lat) 170 njw(" par=" as *u8); njn(w[base]); njw("\n" as *u8) 171 172 // GENITAL (5): nearest the midline in the pelvic band. Lowest threshold + 1.4x gain site, 173 // so a misplaced anchor here distorts the whole staircase -- take the MOST medial candidate. 174 if h > 400 { if h < 560 { if lat < 60 { 175 if haveGen == 0 { bestGenY = y; bestGenX = x; haveGen = 1 } 176 else { if lat < 30 { bestGenY = y; bestGenX = x } } 177 } } } 178 179 // FACE (3): the highest midline joint. 180 if h > 880 { if lat < 80 { 181 if haveHead == 0 { bestHeadY = y; bestHeadX = x; haveHead = 1 } 182 else { if y > bestHeadY { bestHeadY = y; bestHeadX = x } } 183 } } 184 185 // BREAST (1): DERIVED FROM MEASUREMENT, not tuned. Dumped every joint's (h, lat) on the real 186 // rig and the breast pair is unmistakable STRUCTURALLY: J76/J78 at h=813 lat=53/54, identical 187 // height, SAME PARENT (J38, the midline chest at h=762 lat=0), with their own children 188 // J77/J79 at the same place = the nipple joints. 189 // My first band (h 700-810, lat 60-200) missed them on BOTH bounds -- by 3 and by 7. A near 190 // miss, which is why it produced a plausible partial result instead of an obvious failure. 191 // Bounds below bracket the measured pair with margin; separation verified against the 192 // neighbours in that height range: J75 lat=156 and J54/J82 lat=225 stay excluded. 193 if h > 780 { if h < 850 { if lat > 30 { if lat < 100 { 194 nj_push(ax, ay, st, n, x, y, 1) 195 } } } } 196 197 // SUBMAMMARY (0): midline, just below the breast band -- the classic sex-flush origin. 198 if h > 620 { if h < 700 { if lat < 60 { 199 nj_push(ax, ay, st, n, x, y, 0) 200 } } } 201 202 // TORSO (2): midline, between pelvis and submammary. 203 if h > 540 { if h < 620 { if lat < 60 { 204 nj_push(ax, ay, st, n, x, y, 2) 205 } } } 206 207 // EXTREMITIES (4): strongly lateral (arms) at any height, or the thigh band. 208 if lat > 200 { nj_push(ax, ay, st, n, x, y, 4) } 209 if h > 380 { if h < 520 { if lat > 60 { if lat < 200 { 210 nj_push(ax, ay, st, n, x, y, 4) 211 } } } } 212 213 j = j + 1 214 } 215 if haveGen == 1 { nj_push(ax, ay, st, n, bestGenX, bestGenY, 5) } 216 if haveHead == 1 { nj_push(ax, ay, st, n, bestHeadX, bestHeadY, 3) } 217 218 // ---- report + self-check ---- 219 var seen: i64 = 0 220 var i: i64 = 0 221 while i < n[0] { 222 njw(" anchor x=" as *u8); njn(ax[i]) 223 njw(" y=" as *u8); njn(ay[i]) 224 njw(" site=" as *u8); njn(st[i]); njw("\n" as *u8) 225 // set-bit per site so a missing site is detectable without a second pass 226 if st[i] == 0 { if seen - (seen / 2) * 2 == 0 { seen = seen + 1 } } 227 if st[i] == 1 { if (seen / 2) - (seen / 4) * 2 == 0 { seen = seen + 2 } } 228 if st[i] == 2 { if (seen / 4) - (seen / 8) * 2 == 0 { seen = seen + 4 } } 229 if st[i] == 3 { if (seen / 8) - (seen / 16) * 2 == 0 { seen = seen + 8 } } 230 if st[i] == 4 { if (seen / 16) - (seen / 32) * 2 == 0 { seen = seen + 16 } } 231 if st[i] == 5 { if (seen / 32) - (seen / 64) * 2 == 0 { seen = seen + 32 } } 232 i = i + 1 233 } 234 njw("anchors=" as *u8); njn(n[0]) 235 njw(" site_mask=" as *u8); njn(seen); njw("\n" as *u8) 236 237 // T1 anchors were produced at all 238 if n[0] <= 0 { njw("FAIL no anchors\n" as *u8); return 60 } 239 // T0 EVERY site must be anchored (mask 63 = sites 0..5). 240 // ⚠This tooth was originally genital+face ONLY, and it passed GREEN on a run whose mask was 61 -- 241 // site 1 (breast) missing entirely. A field interpolating between anchors CANNOT produce a site 242 // whose anchor is absent, so a missing site silently deletes a stage of the propagation order. 243 // knowledge/arousal_rig_anchors.conf is the ORACLE: it records that CC_Base_L/R_Breast DO exist 244 // on this rig, so an absent breast anchor means MY BAND IS WRONG, not that the rig lacks it. 245 // ★LAW: a tooth that checks a SUBSET of the required outputs reports success for partial work. 246 if seen != 63 { 247 njw("FAIL site_mask=" as *u8); njn(seen) 248 njw(" expected 63 -- a site has NO anchor; bands do not fit this rig\n" as *u8) 249 return 64 250 } 251 // T2 the two SINGLETON sites must exist -- they carry the staircase ends (genital onset leads, 252 // face trails). Missing either means the bands do not fit this rig and the field would be wrong. 253 if haveGen == 0 { njw("FAIL no genital anchor\n" as *u8); return 61 } 254 if haveHead == 0 { njw("FAIL no face anchor\n" as *u8); return 62 } 255 // T3 genital must sit BELOW face -- catches an inverted up-axis, which would silently mirror 256 // the entire propagation order rather than error. 257 if bestGenY >= bestHeadY { njw("FAIL genital not below face (up-axis inverted?)\n" as *u8); return 63 } 258 njw("NXA-JOINTS OK\n" as *u8) 259 return 0 260}