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}