code wiki / _hdl_build / nx_softbody_region_gate.nx
nx_softbody_region_gate.nx source
↩ module page · 224 lines · 10011 B
1// nx_softbody_region_gate.nx -- gate for the BODY-REGION soft physics + THE OCCLUSION LAW.
2// T1 GRAVITY PARAM: gmul_y=0 -> ring holds its rest shape exactly-ish; gmul_y=256 -> the bottom node sags
3// measurably = the TittyMagic-class gravity knob is REAL data.
4// T2 THE OCCLUSION LAW (the operator's ask, measured): a capsule pressed into the RIGHT of the ring ->
5// (a) contact nodes held near the collider surface, (b) the FAR (left) nodes BULGE OUTWARD beyond rest
6// (displaced volume redistributed = physics beyond the visible contact area), (c) capsule removed ->
7// the ring RECOVERS to rest (elastic, no permanent dent).
8// T3 VOLUME PROXY: cross-section area with the capsule embedded stays >= 85% of rest area WITH voldist,
9// and beats the voldist=0 control (the knob does the conserving).
10// T4 PARAMS ARE DATA: mass 256 vs 1024 -> different response speed; colsoft 256 vs 64 -> different depth.
11// T5 determinism: two regions, same script -> byte-identical node state.
12// + PNG knowledge/nx_region_occlusion.png: rest ring vs embedded-capsule ring, the BULGE visible.
13// license_tier: ORIGINAL expect_exit: 0
14import "nx_syscalls.nx"
15import "nx_png.nx"
16import "nx_softbody_region.nx"
17
18func hw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
19func pn(v: i64) -> i64 {
20 let b: *u8 = sys_mmap(32) as *u8
21 var x: i64 = v
22 var neg: i64 = 0
23 if x < 0 { neg = 1; x = 0 - x }
24 var i: i64 = 31
25 if x == 0 { b[i] = 48 as u8; i = i - 1 }
26 while x > 0 { b[i] = (48 + x % 10) as u8; x = x / 10; i = i - 1 }
27 if neg == 1 { b[i] = 45 as u8; i = i - 1 }
28 sys_write(1, (b as i64 + i + 1) as *u8, 31 - i)
29 return 0
30}
31func gabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
32func isq(v: i64) -> i64 { if v <= 0 { return 0 } var x: i64 = v; var y: i64 = (x + 1) / 2; while y < x { x = y; y = (x + v / x) / 2 } return x }
33func nodelen(base: i64, i: i64) -> i64 {
34 let h: *i64 = sr_hdr(base)
35 let ox: i64 = sr_pos(base, i, 0) - h[2]
36 let oy: i64 = sr_pos(base, i, 1) - h[3]
37 return isq(ox * ox + oy * oy)
38}
39// plot one ring into the fb panel at xoff (nodes + edges + capsule if present)
40func plot(base: i64, fb: *i64, TW: i64, TH: i64, xoff: i64) -> i64 {
41 let h: *i64 = sr_hdr(base)
42 let nn: i64 = h[0]
43 var i: i64 = 0
44 while i < nn {
45 let ax: i64 = xoff + 64 + (sr_pos(base, i, 0) - h[2]) / 16
46 let ay: i64 = 64 - (sr_pos(base, i, 1) - h[3]) / 16
47 let bxi: i64 = (i + 1) % nn
48 let bx: i64 = xoff + 64 + (sr_pos(base, bxi, 0) - h[2]) / 16
49 let by: i64 = 64 - (sr_pos(base, bxi, 1) - h[3]) / 16
50 var s: i64 = 0
51 while s <= 10 {
52 let px: i64 = ax + (bx - ax) * s / 10
53 let py: i64 = ay + (by - ay) * s / 10
54 if px >= 0 { if px < TW { if py >= 0 { if py < TH { fb[py * TW + px] = 120 + 220 * 256 + 170 * 65536 } } } }
55 s = s + 1
56 }
57 i = i + 1
58 }
59 // capsule (first) as a circle at its midpoint
60 if h[1] > 0 {
61 let cp: *i64 = sr_cap(base, 0)
62 let cx: i64 = xoff + 64 + ((cp[0] + cp[3]) / 2 - h[2]) / 16
63 let cy: i64 = 64 - ((cp[1] + cp[4]) / 2 - h[3]) / 16
64 let cr: i64 = cp[6] / 16
65 var dy: i64 = 0 - cr
66 while dy <= cr {
67 var dx: i64 = 0 - cr
68 while dx <= cr {
69 let dd: i64 = dx * dx + dy * dy
70 if dd <= cr * cr { if dd >= (cr - 2) * (cr - 2) {
71 let px2: i64 = cx + dx
72 let py2: i64 = cy + dy
73 if px2 >= 0 { if px2 < TW { if py2 >= 0 { if py2 < TH { fb[py2 * TW + px2] = 235 + 120 * 256 + 90 * 65536 } } } }
74 } }
75 dx = dx + 1
76 }
77 dy = dy + 1
78 }
79 }
80 return 0
81}
82
83func main() -> i64 {
84 var fails: i64 = 0
85
86 // ---- T1 gravity knob ----
87 let A: i64 = sys_mmap(sr_bytes()) as i64
88 sr_init(A, 0, 0, 0)
89 sr_param(A, 9, 0) // gmul_y = 0: no gravity
90 sr_ring(A, 16, 800)
91 var t: i64 = 0
92 while t < 300 { sr_step(A); t = t + 1 }
93 var maxdev: i64 = 0
94 var i: i64 = 0
95 while i < 16 {
96 let d: i64 = gabs(nodelen(A, i) - 800)
97 if d > maxdev { maxdev = d }
98 i = i + 1
99 }
100 let B: i64 = sys_mmap(sr_bytes()) as i64
101 sr_init(B, 0, 0, 0)
102 sr_param(B, 9, 256) // full gravity
103 sr_ring(B, 16, 800)
104 t = 0
105 while t < 300 { sr_step(B); t = t + 1 }
106 let sag: i64 = 0 - 800 - (sr_pos(B, 12, 1) - 0) // node 12 = bottom (270deg): rest y=-800; sag = how far below
107 var t1: i64 = 1
108 if maxdev > 24 { t1 = 0 }
109 if sag < 8 { t1 = 0 }
110 if t1 == 1 { hw("T1 PASS gravity is DATA: gmul=0 holds rest (maxdev=" as *u8); pn(maxdev); hw("), gmul=256 sags bottom node " as *u8); pn(sag); hw("\n" as *u8) }
111 else { hw("T1 FAIL maxdev=" as *u8); pn(maxdev); hw(" sag=" as *u8); pn(sag); hw("\n" as *u8); fails = fails + 1 }
112
113 // ---- T2 THE OCCLUSION LAW ----
114 let C: i64 = sys_mmap(sr_bytes()) as i64
115 sr_init(C, 0, 0, 0)
116 sr_param(C, 9, 0) // isolate the law from gravity
117 sr_ring(C, 16, 800)
118 let restarea: i64 = sr_area(C)
119 sr_add_capsule(C, 1000, 0 - 400, 0, 1000, 400, 0, 350) // presses ~150 into the right edge
120 t = 0
121 while t < 400 { sr_step(C); t = t + 1 }
122 // (a) right node (0) held near the capsule surface: its x <= 1000-350+24+slop
123 let rx: i64 = sr_pos(C, 0, 0)
124 var t2: i64 = 1
125 if rx > 680 { t2 = 0 }
126 // (b) far-left nodes bulge beyond rest
127 var lb: i64 = nodelen(C, 8) // node 8 = 180deg = far left
128 let bulge: i64 = lb - 800
129 if bulge < 16 { t2 = 0 }
130 let embarea: i64 = sr_area(C)
131 // (c) remove -> recovery
132 sr_clear_capsules(C)
133 t = 0
134 while t < 500 { sr_step(C); t = t + 1 }
135 var recdev: i64 = 0
136 i = 0
137 while i < 16 {
138 let d2: i64 = gabs(nodelen(C, i) - 800)
139 if d2 > recdev { recdev = d2 }
140 i = i + 1
141 }
142 if recdev > 30 { t2 = 0 }
143 if t2 == 1 { hw("T2 PASS OCCLUSION LAW: contact held (rx=" as *u8); pn(rx); hw("), FAR SIDE BULGES +" as *u8); pn(bulge); hw(" beyond rest (displaced volume went somewhere), full recovery (dev=" as *u8); pn(recdev); hw(")\n" as *u8) }
144 else { hw("T2 FAIL rx=" as *u8); pn(rx); hw(" bulge=" as *u8); pn(bulge); hw(" recdev=" as *u8); pn(recdev); hw("\n" as *u8); fails = fails + 1 }
145
146 // ---- T3 volume proxy vs the voldist=0 control ----
147 let D: i64 = sys_mmap(sr_bytes()) as i64
148 sr_init(D, 0, 0, 0)
149 sr_param(D, 9, 0)
150 sr_param(D, 12, 0) // occlusion law OFF (control)
151 sr_ring(D, 16, 800)
152 sr_add_capsule(D, 1000, 0 - 400, 0, 1000, 400, 0, 350)
153 t = 0
154 while t < 400 { sr_step(D); t = t + 1 }
155 let ctrlarea: i64 = sr_area(D)
156 var t3: i64 = 1
157 if embarea * 100 < restarea * 85 { t3 = 0 }
158 if embarea <= ctrlarea { t3 = 0 }
159 if t3 == 1 { hw("T3 PASS volume proxy: embedded area " as *u8); pn(embarea * 100 / restarea); hw("% of rest WITH the law vs " as *u8); pn(ctrlarea * 100 / restarea); hw("% control -- the knob conserves\n" as *u8) }
160 else { hw("T3 FAIL emb=" as *u8); pn(embarea); hw(" ctrl=" as *u8); pn(ctrlarea); hw(" rest=" as *u8); pn(restarea); hw("\n" as *u8); fails = fails + 1 }
161
162 // ---- T4 params are data ----
163 let E1: i64 = sys_mmap(sr_bytes()) as i64
164 let E2: i64 = sys_mmap(sr_bytes()) as i64
165 sr_init(E1, 0, 0, 0); sr_init(E2, 0, 0, 0)
166 sr_param(E1, 5, 256); sr_param(E2, 5, 1024) // mass 1x vs 4x
167 sr_param(E1, 9, 0); sr_param(E2, 9, 0)
168 sr_ring(E1, 16, 800); sr_ring(E2, 16, 800)
169 sr_anchor(E1, 300, 0, 0); sr_anchor(E2, 300, 0, 0) // step the anchor: response speed differs by mass
170 t = 0
171 var mdiff: i64 = 0
172 while t < 30 {
173 sr_step(E1); sr_step(E2)
174 if sr_pos(E1, 0, 0) != sr_pos(E2, 0, 0) { mdiff = mdiff + 1 }
175 t = t + 1
176 }
177 var t4: i64 = 1
178 if mdiff < 20 { t4 = 0 }
179 if t4 == 1 { hw("T4 PASS params are DATA: mass 1x vs 4x diverge on " as *u8); pn(mdiff); hw("/30 ticks after an anchor step\n" as *u8) }
180 else { hw("T4 FAIL mdiff=" as *u8); pn(mdiff); hw("\n" as *u8); fails = fails + 1 }
181
182 // ---- T5 determinism ----
183 let F1: i64 = sys_mmap(sr_bytes()) as i64
184 let F2: i64 = sys_mmap(sr_bytes()) as i64
185 sr_init(F1, 0, 0, 0); sr_init(F2, 0, 0, 0)
186 sr_ring(F1, 16, 800); sr_ring(F2, 16, 800)
187 sr_add_capsule(F1, 1000, 0 - 400, 0, 1000, 400, 0, 350)
188 sr_add_capsule(F2, 1000, 0 - 400, 0, 1000, 400, 0, 350)
189 t = 0
190 while t < 300 { sr_step(F1); sr_step(F2); t = t + 1 }
191 var diff: i64 = 0
192 i = 0
193 while i < 16 * 16 {
194 let p1: *i64 = (F1 + 128) as *i64
195 let p2: *i64 = (F2 + 128) as *i64
196 if p1[i] != p2[i] { diff = diff + 1 }
197 i = i + 1
198 }
199 if diff == 0 { hw("T5 PASS determinism: embedded-capsule session byte-identical\n" as *u8) }
200 else { hw("T5 FAIL diff=" as *u8); pn(diff); hw("\n" as *u8); fails = fails + 1 }
201
202 // ---- PNG: rest vs embedded (the bulge, visible) ----
203 let TW: i64 = 256
204 let TH: i64 = 128
205 let fb: *i64 = sys_mmap(TW * TH * 8) as *i64
206 var pi: i64 = 0
207 while pi < TW * TH { fb[pi] = 22 + 26 * 256 + 34 * 65536; pi = pi + 1 }
208 let G: i64 = sys_mmap(sr_bytes()) as i64
209 sr_init(G, 0, 0, 0)
210 sr_param(G, 9, 0)
211 sr_ring(G, 16, 800)
212 t = 0
213 while t < 100 { sr_step(G); t = t + 1 }
214 plot(G, fb, TW, TH, 0)
215 sr_add_capsule(G, 1000, 0 - 400, 0, 1000, 400, 0, 350)
216 t = 0
217 while t < 400 { sr_step(G); t = t + 1 }
218 plot(G, fb, TW, TH, 128)
219 write_png(fb, TW, TH, "knowledge/nx_region_occlusion.png" as *u8)
220
221 if fails == 0 { hw("VERDICT GREEN: softbody region 5/5 -- TittyMagic-class params as DATA + THE OCCLUSION LAW measured (far-side bulge, volume conserved, elastic recovery) -- PNG knowledge/nx_region_occlusion.png\n" as *u8) }
222 else { hw("VERDICT RED fails=" as *u8); pn(fails); hw("\n" as *u8) }
223 return fails
224}