code wiki / _hdl_build / nx_fascia.nx
nx_fascia.nx source
↩ module page · 359 lines · 18082 B
1// nx_fascia.nx -- PIPELINE STAGE 3: FASCIA + FAT AS SOFT BODY, bridging muscle to skin.
2//
3// The industry pipeline is skeleton -> muscle -> fascia/fat -> skin, and an audit of our own stack found we
4// had that pipeline's SHAPE and almost none of its PHYSICS: "fat" was a scalar knob attenuating how much
5// muscle relief reached the skin. Nothing lagged, nothing settled, nothing was driven by the layer beneath.
6//
7// It also found that the solver had been built and gated months earlier and NEVER WIRED IN: runtime/
8// nx_softdyn.nx is a spring-damper soft body whose stability envelope is enforced BY CONSTRUCTION (past a
9// damping threshold semi-implicit Euler inverts and amplifies velocity, so SD_CAPC/SD_CAPK make an unstable
10// configuration impossible to REQUEST rather than merely discouraged). It shipped with no soft-tissue
11// geometry to bind to. The layer emitter now emits muscle and fat layers, so this organ is the wiring.
12//
13// THE BINDING, and it is what makes this stage 3 rather than just re-running a solver: a LAYER-AWARE
14// LATTICE. Solver points are seated on a coarse band lattice over the figure; every vertex is displaced by
15// its band's point SCALED BY THE SOFTNESS OF ITS OWN LAYER. Bone scales to ZERO and does not move at all;
16// muscle moves partly; skin and fat move fully. That is fascia doing its actual job -- transmitting motion
17// from the rigid thing inside to the soft thing outside, with each layer lagging by its own amount.
18//
19// nx_fascia <in.nxmesh> <out.nxmesh> [accel] [K] [C] [steps]
20// nx_fascia selftest
21// license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26).
22import "nx_gate_verdict.nx"
23import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
24import "nx_softdyn.nx"
25
26const FS2_M8388607: i64 = 8388607
27const FS2_M8388608: i64 = 8388608
28const FS2_HDR: i64 = 16
29const FS2_LAYENT: i64 = 24
30const FS2_TRI: i64 = 84
31const FS2_LID: i64 = 4
32const FS2_BANDS: i64 = 24
33const FS2_MM: i64 = 1000
34const FS2_MODE: i64 = 420
35const FS2_MAXD: i64 = 40
36const FS2_DEFK: i64 = 900
37const FS2_DEFC: i64 = 260
38const FS2_DEFA: i64 = 900
39const FS2_STEPS: i64 = 40
40const FS2_SOFTK: i64 = 120
41// full solver excursion maps to this per-mille of the figure's own height (~2pct = real tissue travel)
42const FS2_EXC: i64 = 20
43const FS2_SCR: i64 = 4096
44
45func fs2_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
46// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
47// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
48// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
49// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
50func fs2_pn(v: i64) -> i64 { nxi_out(v); return 0 }
51func fs2_rd32(b: *u8, o: i64) -> i64 {
52 return (b[o] as i64) | ((b[o+1] as i64)<<8) | ((b[o+2] as i64)<<16) | ((b[o+3] as i64)<<24)
53}
54func fs2_wr32(b: *u8, o: i64, v: i64) -> i64 {
55 b[o]=(v & 255) as u8; b[o+1]=((v>>8) & 255) as u8; b[o+2]=((v>>16) & 255) as u8; b[o+3]=((v>>24) & 255) as u8
56 return 0
57}
58func fs2_f32mul(b: *u8, o: i64, mul: i64) -> i64 {
59 let bits: i64 = fs2_rd32(b, o)
60 let sign: i64 = (bits>>31) & 1
61 let exp: i64 = (bits>>23) & 255
62 let mant: i64 = bits & FS2_M8388607
63 if exp == 0 { return 0 }
64 let m: i64 = (mant | FS2_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// encode a small signed integer back to f32 (mesh positions are whole model units here)
72func fs2_f32of(v: i64) -> i64 {
73 if v == 0 { return 0 }
74 var s: i64 = 0
75 var m: i64 = v
76 if m < 0 { s = 1; m = 0 - m }
77 var ex: i64 = 0
78 var tv: i64 = m
79 while tv >= 2 { tv = tv/2; ex = ex + 1 }
80 var frac: i64 = 0
81 if ex > 0 { frac = (m - (1 << ex)) * (1 << 23) / (1 << ex) }
82 return (s << 31) | ((127 + ex) << 23) | frac
83}
84func fs2_streq(a: *u8, b: *u8) -> i64 {
85 var i: i64=0; var go: i64=1; var eq: i64=1
86 while go==1 { if a[i]!=b[i] { eq=0; go=0 } else { if a[i]==(0 as u8) { go=0 } else { i=i+1 } } }
87 return eq
88}
89func fs2_atoi(s: *u8) -> i64 {
90 var i: i64=0; var n: i64=0; var sg: i64=1
91 if s[0]==(45 as u8) { sg=0-1; i=1 }
92 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 }
93 return n*sg
94}
95// ★LAYER SOFTNESS, per-mille. This is the whole point of a layer-aware binding: the SAME lattice motion
96// reaches each layer scaled by what that layer physically is. Our emitter writes 0=skin, 1=muscle, 2=bone.
97// Bone is RIGID -- it returns zero and therefore cannot move, which is the property that makes this fascia
98// rather than a global wobble.
99func fs2_soft(layer: i64) -> i64 {
100 if layer == 2 { return 0 } // bone: rigid by construction
101 if layer == 1 { return 520 } // muscle: bound to bone, moves partly
102 return 1000 // skin / fat: the outermost soft envelope, moves fully
103}
104// out[0]=tris out[1]=layers out[2]=peak_lag out[3]=rest_energy out[4]=max_disp out[5]=moved_soft
105// out[6]=moved_bone (MUST be 0) out[7]=ymin out[8]=yspan
106func fs2_run(inp: *u8, outp: *u8, accel: i64, K: i64, C: i64, steps: i64, out: *i64) -> i64 {
107 var z: i64 = 0
108 while z < 12 { out[z] = 0; z = z + 1 }
109 let szp: *i64 = sys_mmap(16) as *i64
110 let mb: *u8 = sys_read_file(inp, szp)
111 if (mb as i64) == 0 { return 0-1 }
112 let sz: i64 = szp[0]
113 if sz < FS2_HDR { return 0-2 }
114 if mb[0]!=(78 as u8) { return 0-3 }
115 if mb[1]!=(88 as u8) { return 0-3 }
116 let nlay: i64 = fs2_rd32(mb, 8)
117 let nt: i64 = fs2_rd32(mb, 12)
118 if nlay <= 0 { return 0-4 }
119 if nt <= 0 { return 0-5 }
120 let hdr: i64 = FS2_HDR + nlay*FS2_LAYENT
121 if hdr + nt*FS2_TRI + nt*FS2_LID > sz { return 0-6 }
122 out[0] = nt
123 out[1] = nlay
124 // pass 1: y extent, so the lattice bands span the figure
125 var ymn: i64 = 0
126 var ymx: i64 = 0
127 var first: i64 = 1
128 var t: i64 = 0
129 while t < nt {
130 let o: i64 = hdr + t*FS2_TRI
131 var k: i64 = 0
132 while k < 3 {
133 let y: i64 = fs2_f32mul(mb, o + k*12 + 4, 1)
134 if first == 1 { ymn=y; ymx=y; first=0 } else {
135 if y<ymn { ymn=y }
136 if y>ymx { ymx=y }
137 }
138 k = k + 1
139 }
140 t = t + 1
141 }
142 let yspan: i64 = ymx - ymn
143 if yspan <= 0 { return 0-7 }
144 out[7] = ymn
145 out[8] = yspan
146 // ---- THE LATTICE: one soft-body point per band, seated at rest, then driven.
147 let st: *i64 = sd_alloc(FS2_BANDS)
148 var b: i64 = 0
149 while b < FS2_BANDS { sd_seat(st, b, 0, 0, 0); b = b + 1 }
150 // ★DRIVE: the anchor accelerates upward then stops -- a footfall. Real tissue LAGS behind the skeleton
151 // on the way up and OVERSHOOTS when it stops; that lag and overshoot IS the effect being simulated.
152 // ★★THE SNAPSHOT, and the bug it fixes is sharper than the units one: the first cut stepped the whole
153 // simulation and THEN read each band's displacement to deform the mesh -- by which point the tissue had
154 // SETTLED BACK ONTO ITS ANCHOR and the lag was gone. Peak lag read 40 during the motion and the applied
155 // displacement read 4, because I was measuring the physics at the moment it had finished happening.
156 // ★A transient is only observable while it is transient: capture the state AT PEAK LAG and deform from
157 // that, which is also what a renderer wants -- the frame where the tissue is furthest behind the bone.
158 let snap: *i64 = sys_mmap(FS2_BANDS*8 + 64) as *i64
159 var zs: i64 = 0
160 while zs < FS2_BANDS { snap[zs] = 0; zs = zs + 1 }
161 var peak: i64 = 0
162 var s: i64 = 0
163 while s < steps {
164 var ay: i64 = 0
165 if s < steps/2 { ay = accel * s / (steps/2) } // driven phase: the bone rises
166 if s >= steps/2 { ay = accel } // then holds: the tissue must catch up and settle
167 var bb: i64 = 0
168 while bb < FS2_BANDS {
169 sd_step(st, bb, 0, ay, 0, K, C, FS2_MAXD)
170 let dd: i64 = sd_disp_y(st, bb, ay)
171 let d: i64 = sd_abs(dd)
172 if d > peak { peak = d }
173 // hold each band's own most-lagged state -- the frame worth rendering
174 if d > sd_abs(snap[bb]) { snap[bb] = dd }
175 bb = bb + 1
176 }
177 s = s + 1
178 }
179 out[2] = peak
180 var esum: i64 = 0
181 var eb: i64 = 0
182 while eb < FS2_BANDS { esum = esum + sd_energy(st, eb); eb = eb + 1 }
183 out[3] = esum
184 // ---- APPLY: displace every vertex by its band's point, SCALED BY ITS LAYER'S SOFTNESS.
185 let obytes: i64 = hdr + nt*FS2_TRI + nt*FS2_LID
186 let ob: *u8 = sys_mmap(obytes + 64)
187 var c: i64 = 0
188 while c < obytes { ob[c] = mb[c]; c = c + 1 }
189 var maxd: i64 = 0
190 var movsoft: i64 = 0
191 var movbone: i64 = 0
192 var t2: i64 = 0
193 while t2 < nt {
194 let o2: i64 = hdr + t2*FS2_TRI
195 let lay: i64 = fs2_rd32(mb, hdr + nt*FS2_TRI + t2*FS2_LID)
196 let soft: i64 = fs2_soft(lay)
197 var k2: i64 = 0
198 while k2 < 3 {
199 let y2: i64 = fs2_f32mul(mb, o2 + k2*12 + 4, 1)
200 var bnd: i64 = (y2 - ymn) * FS2_BANDS / yspan
201 if bnd < 0 { bnd = 0 }
202 if bnd >= FS2_BANDS { bnd = FS2_BANDS - 1 }
203 // ⚠⚠UNITS BUG CAUGHT BY RENDERING IT, NOT BY MEASURING IT. The first cut applied the solver's raw
204 // displacement straight to mesh coordinates. The solver works in its own space where a full
205 // excursion is FS2_MAXD (tens of units); the mesh is in model units where the figure is tens of
206 // THOUSANDS tall. So tissue moved ~5 units on a body ~200,000 units high -- real, measurable,
207 // bounded, deterministic, and utterly invisible: the render changed by 26 pixels out of 84,000.
208 // ★THIS IS THE BANKED UNITS-TRAP CLASS: an integer scale mismatch does not error, it produces a
209 // physically negligible result that passes every numeric test. The gate could not catch it
210 // because the gate tests the SOLVER, and the solver was right -- the BINDING was dimensionless.
211 // ★THE FIX: a displacement is a FRACTION OF THE FIGURE, not a raw count. Map the solver's full
212 // excursion onto FS2_EXC per-mille of the mesh's own height, so tissue moves ~2pct of body
213 // height at full lag on ANY figure at ANY scale -- which is roughly what real tissue does.
214 let draw: i64 = snap[bnd]
215 let dy: i64 = ((draw * yspan / FS2_MAXD) * FS2_EXC / FS2_MM) * soft / FS2_MM
216 if dy != 0 {
217 if soft > 0 { movsoft = movsoft + 1 } else { movbone = movbone + 1 }
218 let ad: i64 = sd_abs(dy)
219 if ad > maxd { maxd = ad }
220 fs2_wr32(ob, o2 + k2*12 + 4, fs2_f32of(y2 + dy))
221 }
222 k2 = k2 + 1
223 }
224 t2 = t2 + 1
225 }
226 out[4] = maxd
227 out[5] = movsoft
228 out[6] = movbone
229 let fd: i64 = sys_openat_wr(outp, FS2_MODE)
230 if fd < 0 { return 0-8 }
231 sys_write(fd, ob, obytes)
232 sys_close(fd)
233 return 0
234}
235func fs2_gate() -> i64 {
236 let ctr: *i64 = gv_ctr()
237 gv_head("nx_fascia selftest -- fascia as a LAYER-AWARE soft body, not a global wobble" as *u8)
238 // the lattice behaviour is testable without a mesh: seat points, drive them, watch them lag and settle.
239 let st: *i64 = sd_alloc(4)
240 var i: i64 = 0
241 while i < 4 { sd_seat(st, i, 0, 0, 0); i = i + 1 }
242 var peak: i64 = 0
243 var s: i64 = 0
244 while s < 20 {
245 let ay: i64 = FS2_DEFA * s / 20
246 sd_step(st, 0, 0, ay, 0, FS2_DEFK, FS2_DEFC, FS2_MAXD)
247 let d: i64 = sd_abs(sd_disp_y(st, 0, ay))
248 if d > peak { peak = d }
249 s = s + 1
250 }
251 var t1: i64 = 0
252 if peak > 0 { t1 = 1 }
253 gv_check("T1 tissue LAGS behind the skeleton it hangs from" as *u8, t1, ctr)
254 var s2: i64 = 0
255 while s2 < 400 { sd_step(st, 0, 0, FS2_DEFA, 0, FS2_DEFK, FS2_DEFC, FS2_MAXD); s2 = s2 + 1 }
256 let rest: i64 = sd_energy(st, 0)
257 var t2: i64 = 0
258 if rest == 0 { t2 = 1 }
259 gv_check("T2 and SETTLES to rest once the motion stops" as *u8, t2, ctr)
260 var t3: i64 = 0
261 if sd_abs(sd_disp_y(st, 0, FS2_DEFA)) <= FS2_MAXD { t3 = 1 }
262 gv_check("T3 displacement BOUNDED -- tissue can never leave the body" as *u8, t3, ctr)
263 // ★THE LAYER TOOTH, and it is what makes this fascia rather than a wobble: bone is RIGID by
264 // construction. If fs2_soft ever returned non-zero for bone, the skeleton would jiggle.
265 var t4: i64 = 0
266 if fs2_soft(2) == 0 { if fs2_soft(1) > 0 { if fs2_soft(0) > fs2_soft(1) { t4 = 1 } } }
267 gv_check("T4 LAYER-AWARE: bone rigid, muscle partial, skin fullest" as *u8, t4, ctr)
268 // ★ANTI-VACUITY: stiffness must CONTROL the response. A solver that always wobbles the same amount
269 // would pass every test above and be simulating nothing.
270 // ⚠THE FIRST CUT OF THIS TOOTH COMPARED PEAK DISPLACEMENT AND WENT RED, AND THE PHYSICS SAYS IT SHOULD
271 // HAVE. A stiff, lightly-damped spring RINGS: its instantaneous peak can EXCEED a soft one's even though
272 // it tracks the anchor far better on average. That is the same trap banked when this solver was built --
273 // a stiff config read as MORE jiggle, not less. The physically correct statement is about STEADY-STATE
274 // lag: under a constant drive the settled offset is proportional to load over stiffness, so a stiffer
275 // tissue MUST settle closer to its anchor. Measure after settling, not during ringing.
276 // ⚠⚠SECOND RED ON THE SAME TOOTH, AND THE SOLVER'S ACTUAL CONTRACT IS WHY: it is a pure spring-damper to
277 // its ANCHOR with no external load, so under a CONSTANT anchor every configuration converges to the
278 // anchor exactly and STEADY-STATE LAG IS ZERO FOR BOTH. My assertion could never have been true. The
279 // property that genuinely distinguishes stiffness is TRACKING WHILE THE ANCHOR IS STILL MOVING: through
280 // a ramp, a stiffer tissue stays closer to the bone it hangs from. Sample the lag at the END of the
281 // driven ramp, while there is still something to lag behind.
282 // ★LAW: read the solver's contract before asserting a property of it -- twice now the test was wrong
283 // rather than the code, and both times the physics was in the source all along.
284 let st2: *i64 = sd_alloc(2)
285 sd_seat(st2, 0, 0, 0, 0)
286 var stifflag: i64 = 0
287 var sA: i64 = 0
288 while sA < 20 {
289 let ay: i64 = FS2_DEFA * sA / 20
290 sd_step(st2, 0, 0, ay, 0, SD_CAPK, FS2_DEFC, FS2_MAXD)
291 stifflag = sd_abs(sd_disp_y_q8(st2, 0, ay))
292 sA = sA + 1
293 }
294 let st4: *i64 = sd_alloc(2)
295 sd_seat(st4, 0, 0, 0, 0)
296 var softlag: i64 = 0
297 var sB: i64 = 0
298 while sB < 20 {
299 let ay: i64 = FS2_DEFA * sB / 20
300 sd_step(st4, 0, 0, ay, 0, FS2_SOFTK, FS2_DEFC, FS2_MAXD)
301 softlag = sd_abs(sd_disp_y_q8(st4, 0, ay))
302 sB = sB + 1
303 }
304 var t5: i64 = 0
305 if stifflag < softlag { t5 = 1 }
306 gv_check("T5 anti-vacuity: STIFF tissue TRACKS the bone closer than soft tissue mid-motion" as *u8, t5, ctr)
307 // ★DETERMINISM: the same drive must produce the same tissue state, or nothing here is reproducible.
308 let st3: *i64 = sd_alloc(2)
309 sd_seat(st3, 0, 0, 0, 0)
310 var peak3: i64 = 0
311 var s4: i64 = 0
312 while s4 < 20 {
313 let ay: i64 = FS2_DEFA * s4 / 20
314 sd_step(st3, 0, 0, ay, 0, FS2_DEFK, FS2_DEFC, FS2_MAXD)
315 let d: i64 = sd_abs(sd_disp_y(st3, 0, ay))
316 if d > peak3 { peak3 = d }
317 s4 = s4 + 1
318 }
319 var t6: i64 = 0
320 if peak3 == peak { t6 = 1 }
321 gv_check("T6 deterministic: identical drive, identical tissue" as *u8, t6, ctr)
322 let o: *i64 = sys_mmap(FS2_SCR) as *i64
323 var t7: i64 = 0
324 if fs2_run("/tmp/nx_fs2_absent_zz.nxmesh" as *u8, "/tmp/nx_fs2_o.nxmesh" as *u8, FS2_DEFA, FS2_DEFK, FS2_DEFC, FS2_STEPS, o) < 0 { t7 = 1 }
325 gv_check("T7 missing input refused, not silently empty" as *u8, t7, ctr)
326 return gv_verdict("TISSUE-GATE" as *u8, ctr, "layer-aware fascia; bone rigid by construction" as *u8)
327}
328func main(argc: i64, argv: *i64) -> i64 {
329 if argc >= 2 {
330 if fs2_streq(argv[1] as *u8, "selftest" as *u8) == 1 { return fs2_gate() }
331 }
332 if argc < 3 {
333 fs2_puts("usage: nx_fascia <in.nxmesh> <out.nxmesh> [accel] [K] [C] [steps] | selftest\n" as *u8)
334 return 2
335 }
336 var accel: i64 = FS2_DEFA
337 var K: i64 = FS2_DEFK
338 var C: i64 = FS2_DEFC
339 var steps: i64 = FS2_STEPS
340 if argc > 3 { accel = fs2_atoi(argv[3] as *u8) }
341 if argc > 4 { K = fs2_atoi(argv[4] as *u8) }
342 if argc > 5 { C = fs2_atoi(argv[5] as *u8) }
343 if argc > 6 { steps = fs2_atoi(argv[6] as *u8) }
344 let o: *i64 = sys_mmap(FS2_SCR) as *i64
345 let rc: i64 = fs2_run(argv[1] as *u8, argv[2] as *u8, accel, K, C, steps, o)
346 fs2_puts("{\x22organ\x22:\x22nx_fascia\x22,\x22v\x22:1,\x22stage\x22:\x22pipeline stage 3 -- fascia and fat as soft body, bridging muscle to skin\x22" as *u8)
347 fs2_puts(",\x22rc\x22:" as *u8); fs2_pn(rc)
348 fs2_puts(",\x22tris\x22:" as *u8); fs2_pn(o[0])
349 fs2_puts(",\x22layers\x22:" as *u8); fs2_pn(o[1])
350 fs2_puts(",\x22peak_lag\x22:" as *u8); fs2_pn(o[2])
351 fs2_puts(",\x22rest_energy\x22:" as *u8); fs2_pn(o[3])
352 fs2_puts(",\x22max_disp\x22:" as *u8); fs2_pn(o[4])
353 fs2_puts(",\x22verts_moved_soft\x22:" as *u8); fs2_pn(o[5])
354 fs2_puts(",\x22verts_moved_bone\x22:" as *u8); fs2_pn(o[6])
355 fs2_puts(",\x22binding\x22:\x22layer-aware lattice: solver points on a band lattice, each vertex displaced by its band SCALED BY ITS LAYER SOFTNESS -- bone 0 (rigid by construction), muscle 520, skin/fat 1000. verts_moved_bone MUST read 0.\x22" as *u8)
356 fs2_puts(",\x22solver\x22:\x22nx_softdyn spring-damper, stability envelope enforced by construction (an unstable config cannot be requested), displacement hard-clamped so tissue can never leave the body\x22}\n" as *u8)
357 if rc < 0 { return 1 }
358 return 0
359}