code wiki / _hdl_build / nx_tissue.nx
nx_tissue.nx source
↩ module page · 312 lines · 13734 B
1// nx_tissue.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_tissue <in.nxmesh> <out.nxmesh> [accel] [K] [C] [steps]
20// nx_tissue selftest
21// license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26).
22import "nx_gate_verdict.nx"
23import "nx_softdyn.nx"
24
25const TS_M8388607: i64 = 8388607
26const TS_M8388608: i64 = 8388608
27const TS_HDR: i64 = 16
28const TS_LAYENT: i64 = 24
29const TS_TRI: i64 = 84
30const TS_LID: i64 = 4
31const TS_BANDS: i64 = 24
32const TS_MM: i64 = 1000
33const TS_MODE: i64 = 420
34const TS_MAXD: i64 = 40
35const TS_DEFK: i64 = 900
36const TS_DEFC: i64 = 260
37const TS_DEFA: i64 = 900
38const TS_STEPS: i64 = 40
39const TS_SCR: i64 = 4096
40
41func ts_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
42func ts_pn(v: i64) -> i64 {
43 let b: *u8 = sys_mmap(32); var x: i64=v; var ng: i64=0
44 if x<0 { ng=1; x=0-x }
45 var i: i64=31
46 if x==0 { b[i]=48 as u8; i=i-1 }
47 while x>0 { b[i]=(48+x%10) as u8; x=x/10; i=i-1 }
48 if ng==1 { b[i]=45 as u8; i=i-1 }
49 sys_write(1,(b as i64 + i + 1) as *u8, 31-i); return 0
50}
51func ts_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 ts_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 ts_f32mul(b: *u8, o: i64, mul: i64) -> i64 {
59 let bits: i64 = ts_rd32(b, o)
60 let sign: i64 = (bits>>31) & 1
61 let exp: i64 = (bits>>23) & 255
62 let mant: i64 = bits & TS_M8388607
63 if exp == 0 { return 0 }
64 let m: i64 = (mant | TS_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 ts_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 ts_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 ts_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 ts_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 ts_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 < TS_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 = ts_rd32(mb, 8)
117 let nt: i64 = ts_rd32(mb, 12)
118 if nlay <= 0 { return 0-4 }
119 if nt <= 0 { return 0-5 }
120 let hdr: i64 = TS_HDR + nlay*TS_LAYENT
121 if hdr + nt*TS_TRI + nt*TS_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*TS_TRI
131 var k: i64 = 0
132 while k < 3 {
133 let y: i64 = ts_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(TS_BANDS)
148 var b: i64 = 0
149 while b < TS_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 var peak: i64 = 0
153 var s: i64 = 0
154 while s < steps {
155 var ay: i64 = 0
156 if s < steps/2 { ay = accel * s / (steps/2) } // driven phase: the bone rises
157 if s >= steps/2 { ay = accel } // then holds: the tissue must catch up and settle
158 var bb: i64 = 0
159 while bb < TS_BANDS {
160 sd_step(st, bb, 0, ay, 0, K, C, TS_MAXD)
161 let d: i64 = sd_abs(sd_disp_y(st, bb, ay))
162 if d > peak { peak = d }
163 bb = bb + 1
164 }
165 s = s + 1
166 }
167 out[2] = peak
168 var esum: i64 = 0
169 var eb: i64 = 0
170 while eb < TS_BANDS { esum = esum + sd_energy(st, eb); eb = eb + 1 }
171 out[3] = esum
172 // ---- APPLY: displace every vertex by its band's point, SCALED BY ITS LAYER'S SOFTNESS.
173 let obytes: i64 = hdr + nt*TS_TRI + nt*TS_LID
174 let ob: *u8 = sys_mmap(obytes + 64)
175 var c: i64 = 0
176 while c < obytes { ob[c] = mb[c]; c = c + 1 }
177 var maxd: i64 = 0
178 var movsoft: i64 = 0
179 var movbone: i64 = 0
180 var t2: i64 = 0
181 while t2 < nt {
182 let o2: i64 = hdr + t2*TS_TRI
183 let lay: i64 = ts_rd32(mb, hdr + nt*TS_TRI + t2*TS_LID)
184 let soft: i64 = ts_soft(lay)
185 var k2: i64 = 0
186 while k2 < 3 {
187 let y2: i64 = ts_f32mul(mb, o2 + k2*12 + 4, 1)
188 var bnd: i64 = (y2 - ymn) * TS_BANDS / yspan
189 if bnd < 0 { bnd = 0 }
190 if bnd >= TS_BANDS { bnd = TS_BANDS - 1 }
191 let dy: i64 = sd_disp_y(st, bnd, accel) * soft / TS_MM
192 if dy != 0 {
193 if soft > 0 { movsoft = movsoft + 1 } else { movbone = movbone + 1 }
194 let ad: i64 = sd_abs(dy)
195 if ad > maxd { maxd = ad }
196 ts_wr32(ob, o2 + k2*12 + 4, ts_f32of(y2 + dy))
197 }
198 k2 = k2 + 1
199 }
200 t2 = t2 + 1
201 }
202 out[4] = maxd
203 out[5] = movsoft
204 out[6] = movbone
205 let fd: i64 = sys_openat_wr(outp, TS_MODE)
206 if fd < 0 { return 0-8 }
207 sys_write(fd, ob, obytes)
208 sys_close(fd)
209 return 0
210}
211func ts_gate() -> i64 {
212 let ctr: *i64 = gv_ctr()
213 gv_head("nx_tissue selftest -- fascia as a LAYER-AWARE soft body, not a global wobble" as *u8)
214 // the lattice behaviour is testable without a mesh: seat points, drive them, watch them lag and settle.
215 let st: *i64 = sd_alloc(4)
216 var i: i64 = 0
217 while i < 4 { sd_seat(st, i, 0, 0, 0); i = i + 1 }
218 var peak: i64 = 0
219 var s: i64 = 0
220 while s < 20 {
221 let ay: i64 = TS_DEFA * s / 20
222 sd_step(st, 0, 0, ay, 0, TS_DEFK, TS_DEFC, TS_MAXD)
223 let d: i64 = sd_abs(sd_disp_y(st, 0, ay))
224 if d > peak { peak = d }
225 s = s + 1
226 }
227 var t1: i64 = 0
228 if peak > 0 { t1 = 1 }
229 gv_check("T1 tissue LAGS behind the skeleton it hangs from" as *u8, t1, ctr)
230 var s2: i64 = 0
231 while s2 < 400 { sd_step(st, 0, 0, TS_DEFA, 0, TS_DEFK, TS_DEFC, TS_MAXD); s2 = s2 + 1 }
232 let rest: i64 = sd_energy(st, 0)
233 var t2: i64 = 0
234 if rest == 0 { t2 = 1 }
235 gv_check("T2 and SETTLES to rest once the motion stops" as *u8, t2, ctr)
236 var t3: i64 = 0
237 if sd_abs(sd_disp_y(st, 0, TS_DEFA)) <= TS_MAXD { t3 = 1 }
238 gv_check("T3 displacement BOUNDED -- tissue can never leave the body" as *u8, t3, ctr)
239 // ★THE LAYER TOOTH, and it is what makes this fascia rather than a wobble: bone is RIGID by
240 // construction. If ts_soft ever returned non-zero for bone, the skeleton would jiggle.
241 var t4: i64 = 0
242 if ts_soft(2) == 0 { if ts_soft(1) > 0 { if ts_soft(0) > ts_soft(1) { t4 = 1 } } }
243 gv_check("T4 LAYER-AWARE: bone rigid, muscle partial, skin fullest" as *u8, t4, ctr)
244 // ★ANTI-VACUITY: stiffness must CONTROL the response. A solver that always wobbles the same amount
245 // would pass every test above and be simulating nothing.
246 let st2: *i64 = sd_alloc(2)
247 sd_seat(st2, 0, 0, 0, 0)
248 var stiffpeak: i64 = 0
249 var s3: i64 = 0
250 while s3 < 20 {
251 let ay: i64 = TS_DEFA * s3 / 20
252 sd_step(st2, 0, 0, ay, 0, SD_CAPK, TS_DEFC, TS_MAXD)
253 let d: i64 = sd_abs(sd_disp_y(st2, 0, ay))
254 if d > stiffpeak { stiffpeak = d }
255 s3 = s3 + 1
256 }
257 var t5: i64 = 0
258 if stiffpeak < peak { t5 = 1 }
259 gv_check("T5 anti-vacuity: STIFF tissue lags LESS than soft tissue" as *u8, t5, ctr)
260 // ★DETERMINISM: the same drive must produce the same tissue state, or nothing here is reproducible.
261 let st3: *i64 = sd_alloc(2)
262 sd_seat(st3, 0, 0, 0, 0)
263 var peak3: i64 = 0
264 var s4: i64 = 0
265 while s4 < 20 {
266 let ay: i64 = TS_DEFA * s4 / 20
267 sd_step(st3, 0, 0, ay, 0, TS_DEFK, TS_DEFC, TS_MAXD)
268 let d: i64 = sd_abs(sd_disp_y(st3, 0, ay))
269 if d > peak3 { peak3 = d }
270 s4 = s4 + 1
271 }
272 var t6: i64 = 0
273 if peak3 == peak { t6 = 1 }
274 gv_check("T6 deterministic: identical drive, identical tissue" as *u8, t6, ctr)
275 let o: *i64 = sys_mmap(TS_SCR) as *i64
276 var t7: i64 = 0
277 if ts_run("/tmp/nx_ts_absent_zz.nxmesh" as *u8, "/tmp/nx_ts_o.nxmesh" as *u8, TS_DEFA, TS_DEFK, TS_DEFC, TS_STEPS, o) < 0 { t7 = 1 }
278 gv_check("T7 missing input refused, not silently empty" as *u8, t7, ctr)
279 return gv_verdict("TISSUE-GATE" as *u8, ctr, "layer-aware fascia; bone rigid by construction" as *u8)
280}
281func main(argc: i64, argv: *i64) -> i64 {
282 if argc >= 2 {
283 if ts_streq(argv[1] as *u8, "selftest" as *u8) == 1 { return ts_gate() }
284 }
285 if argc < 3 {
286 ts_puts("usage: nx_tissue <in.nxmesh> <out.nxmesh> [accel] [K] [C] [steps] | selftest\n" as *u8)
287 return 2
288 }
289 var accel: i64 = TS_DEFA
290 var K: i64 = TS_DEFK
291 var C: i64 = TS_DEFC
292 var steps: i64 = TS_STEPS
293 if argc > 3 { accel = ts_atoi(argv[3] as *u8) }
294 if argc > 4 { K = ts_atoi(argv[4] as *u8) }
295 if argc > 5 { C = ts_atoi(argv[5] as *u8) }
296 if argc > 6 { steps = ts_atoi(argv[6] as *u8) }
297 let o: *i64 = sys_mmap(TS_SCR) as *i64
298 let rc: i64 = ts_run(argv[1] as *u8, argv[2] as *u8, accel, K, C, steps, o)
299 ts_puts("{\x22organ\x22:\x22nx_tissue\x22,\x22v\x22:1,\x22stage\x22:\x22pipeline stage 3 -- fascia and fat as soft body, bridging muscle to skin\x22" as *u8)
300 ts_puts(",\x22rc\x22:" as *u8); ts_pn(rc)
301 ts_puts(",\x22tris\x22:" as *u8); ts_pn(o[0])
302 ts_puts(",\x22layers\x22:" as *u8); ts_pn(o[1])
303 ts_puts(",\x22peak_lag\x22:" as *u8); ts_pn(o[2])
304 ts_puts(",\x22rest_energy\x22:" as *u8); ts_pn(o[3])
305 ts_puts(",\x22max_disp\x22:" as *u8); ts_pn(o[4])
306 ts_puts(",\x22verts_moved_soft\x22:" as *u8); ts_pn(o[5])
307 ts_puts(",\x22verts_moved_bone\x22:" as *u8); ts_pn(o[6])
308 ts_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)
309 ts_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)
310 if rc < 0 { return 1 }
311 return 0
312}