code wiki / (root) / nx_gpe_hair_geometry_gate.nx

nx_gpe_hair_geometry_gate.nx source

↩ module page · 223 lines · 9957 B

1// nx_gpe_hair_geometry_gate.nx -- GATE: prove /world/beach renders hair GEOMETRY that MOVES with 2// the N-segment chain, and prove the same measurement CANNOT pass on rigid hair. 3// 4// WHAT THIS GATE IS FOR. Two capabilities shipped and never met. ref9d.nxa carries real groom 5// geometry (HSTR, 2200 strands x 8 points -- measured by nx_asset_floor_gate, sections=16) and the 6// page drew it RIGID: every hair vertex skinned haw=1 to the head joint, a helmet. nx_hairdyn_lib 7// shipped a spring-bone chain on nx_softdyn's one solver and was imported by NOTHING but its own 8// gate. gpe_hair_geometry is the join. These teeth measure the join rather than asserting it. 9// 10// THE ANTI-VACUITY SUBJECT IS THE LIB'S OWN RIGID CONTROL. hd_tick_rigid is not a mutant I wrote 11// for this gate -- it ships in nx_hairdyn_lib precisely as "the ABSENCE of the solver", the 12// trivial wrong implementation that still has hair in the sense that the joints exist and follow 13// the head. Baking with it yields a table that cannot vary and cannot differ between segments, so 14// every motion tooth below is proven against the exact thing this rung replaces. 15// 16// NO TOLERANCE IS CHOSEN ANYWHERE. Every tooth is a COUNT or an INEQUALITY against zero or one: 17// does the table vary at all, does it supply more than one distinct offset, does the swing change 18// sign. A threshold picked to make a swing "big enough" would be the magic number this estate 19// refuses, and none is needed to separate a chain from a helmet. 20// 21// license_tier: ORIGINAL No hw writes (Rule 26). 22import "nx_syscalls.nx" 23import "nx_gate_verdict.nx" 24import "nx_game_page_emit.nx" 25 26const HGG_BUF: i64 = 1048576 27const HGG_TBL_SLOTS: i64 = 8192 28 29// literal substring search over the emitted page bytes. Local to this gate by the same precedent 30// every sibling follows (ce_find in nx_craft_emit, af_find in nx_asset_floor_gate): the emitted 31// buffer is not NUL-terminated, so a C-string finder cannot be composed here. 32func hgg_find(b: *u8, n: i64, s: *u8) -> i64 { 33 var sl: i64 = 0 34 while s[sl] != (0 as u8) { sl = sl + 1 } 35 if sl == 0 { return 0 - 1 } 36 if n < sl { return 0 - 1 } 37 var i: i64 = 0 38 while i <= n - sl { 39 var j: i64 = 0 40 var ok: i64 = 1 41 while j < sl { 42 if b[i+j] != s[j] { ok = 0 } 43 j = j + 1 44 } 45 if ok == 1 { return i } 46 i = i + 1 47 } 48 return 0 - 1 49} 50 51// how many DIFFERENT values sit in t[off .. off+n-1]. This is the "N offsets, not one" ruler: 52// the single-point driver this rung replaces can only ever produce 1. 53func hgg_distinct(t: *i64, off: i64, n: i64) -> i64 { 54 var d: i64 = 0 55 var i: i64 = 0 56 while i < n { 57 var seen: i64 = 0 58 var k: i64 = 0 59 while k < i { 60 if t[off+k] == t[off+i] { seen = 1 } 61 k = k + 1 62 } 63 if seen == 0 { d = d + 1 } 64 i = i + 1 65 } 66 return d 67} 68 69// 1 if ANY entry differs from its frame-0 value: the table MOVES. 70func hgg_varies(t: *i64, n: i64, f: i64) -> i64 { 71 var fr: i64 = 1 72 while fr < f { 73 var i: i64 = 0 74 while i < n { 75 if t[fr*n+i] != t[i] { return 1 } 76 i = i + 1 77 } 78 fr = fr + 1 79 } 80 return 0 81} 82 83// sign changes of one segment's offset across the table. A table spanning about one natural period 84// must cross zero twice; this MEASURES the span from the data instead of trusting the constant the 85// frame count was derived from. 86func hgg_sign_changes(t: *i64, n: i64, f: i64, seg: i64) -> i64 { 87 var c: i64 = 0 88 var prev: i64 = 0 89 var fr: i64 = 0 90 while fr < f { 91 var s: i64 = 0 92 if t[fr*n+seg] > 0 { s = 1 } 93 if t[fr*n+seg] < 0 { s = 0 - 1 } 94 if s != 0 { 95 if prev != 0 { if s != prev { c = c + 1 } } 96 prev = s 97 } 98 fr = fr + 1 99 } 100 return c 101} 102 103// largest |offset| reached by one segment over the whole table 104func hgg_seg_maxabs(t: *i64, n: i64, f: i64, seg: i64) -> i64 { 105 var mx: i64 = 0 106 var fr: i64 = 0 107 while fr < f { 108 var v: i64 = t[fr*n+seg] 109 if v < 0 { v = 0 - v } 110 if v > mx { mx = v } 111 fr = fr + 1 112 } 113 return mx 114} 115 116func main() -> i64 { 117 let ctr: *i64 = gv_ctr() 118 gv_head("nx_gpe_hair_geometry gate -- hair GEOMETRY driven by the N-segment chain" as *u8) 119 120 let n: i64 = gpe_hair_nseg() 121 let f: i64 = gpe_hair_frames() 122 let m: i64 = f*n 123 gv_puts(" derived: nseg=" as *u8); gv_num(n) 124 gv_puts(" frames=" as *u8); gv_num(f) 125 gv_puts(" entries=" as *u8); gv_num(m) 126 gv_puts(" ticks_per_s=" as *u8); gv_num(hd_ticks_per_s()) 127 gv_puts(" fn_mhz=" as *u8); gv_num(SB_HAIR_FN_MHZ) 128 gv_puts("\n" as *u8) 129 130 // ---- FIXTURE REACHED THE CONDITION (asserted BEFORE any outcome) ---- 131 var dimok: i64 = 0 132 if n > 1 { if f > 1 { if m <= HGG_TBL_SLOTS { dimok = 1 } } } 133 gv_check("fixture-chain-dimensions-usable (nseg>1, frames>1, table fits) -- a zero-entry or single-segment table cannot express either property below" as *u8, dimok, ctr) 134 135 let tbl: *i64 = sys_mmap(HGG_TBL_SLOTS*8) as *i64 136 let rtb: *i64 = sys_mmap(HGG_TBL_SLOTS*8) as *i64 137 let wrote: i64 = gpe_hair_chain_bake(tbl, 0) 138 let rwrote: i64 = gpe_hair_chain_bake(rtb, 1) 139 var wok: i64 = 0 140 if wrote == m { if rwrote == m { wok = 1 } } 141 gv_check("fixture-both-bakes-wrote-every-declared-entry (declared == written, so a short table cannot read as a still one)" as *u8, wok, ctr) 142 143 // ---- THE CHAIN IS A CHAIN ---- 144 let d0: i64 = hgg_distinct(tbl, 0, n) 145 let rd0: i64 = hgg_distinct(rtb, 0, n) 146 gv_puts(" distinct offsets at frame 0: chain=" as *u8); gv_num(d0) 147 gv_puts(" rigid=" as *u8); gv_num(rd0); gv_puts("\n" as *u8) 148 var multi: i64 = 0 149 if d0 > 1 { multi = 1 } 150 gv_check("chain-supplies-N-DISTINCT-offsets-at-one-instant (the single-point driver this rung replaces can only ever supply 1)" as *u8, multi, ctr) 151 152 let vr: i64 = hgg_varies(tbl, n, f) 153 let rvr: i64 = hgg_varies(rtb, n, f) 154 gv_puts(" table varies across frames: chain=" as *u8); gv_num(vr) 155 gv_puts(" rigid=" as *u8); gv_num(rvr); gv_puts("\n" as *u8) 156 gv_check("chain-table-MOVES (some segment offset differs from its frame-0 value)" as *u8, vr, ctr) 157 158 // per-segment swing magnitudes: a chain's segments do NOT all swing alike 159 var s0: i64 = hgg_seg_maxabs(tbl, n, f, 0) 160 var sl: i64 = hgg_seg_maxabs(tbl, n, f, n - 1) 161 gv_puts(" max|offset| q8: seg0=" as *u8); gv_num(s0) 162 gv_puts(" seg" as *u8); gv_num(n - 1); gv_puts("=" as *u8); gv_num(sl); gv_puts("\n" as *u8) 163 var differ: i64 = 0 164 if s0 != sl { differ = 1 } 165 gv_check("chain-segments-swing-DIFFERENTLY (root-most and tip-most magnitudes differ -- N copies of one point cannot)" as *u8, differ, ctr) 166 167 let sc: i64 = hgg_sign_changes(tbl, n, f, n - 1) 168 gv_puts(" tip sign changes across the table: " as *u8); gv_num(sc); gv_puts("\n" as *u8) 169 var osc: i64 = 0 170 if sc >= 2 { osc = 1 } 171 gv_check("table-spans-about-one-oscillation (tip crosses zero at least twice) -- MEASURED from the data, not trusted from the frame-count constant" as *u8, osc, ctr) 172 173 // ---- NEGATIVE CONTROLS: the rigid chain, which is what shipped ---- 174 var rstill: i64 = 0 175 if rvr == 0 { rstill = 1 } 176 var lstill: i64 = 0 177 if vr == 0 { lstill = 1 } 178 gv_bite("neg-control-rigid-hair-CANNOT-move (dynamics removed via the lib's own hd_tick_rigid)" as *u8, rstill, lstill, ctr) 179 180 var rone: i64 = 0 181 if rd0 == 1 { rone = 1 } 182 var lone: i64 = 0 183 if d0 == 1 { lone = 1 } 184 gv_bite("neg-control-rigid-hair-supplies-ONE-offset-not-N" as *u8, rone, lone, ctr) 185 186 // ---- THE EMITTED RENDERER ---- 187 let out: *u8 = sys_mmap(HGG_BUF) as *u8 188 let plen: i64 = gpe_hair_geometry(out, 0) 189 gv_puts(" emitted bytes: " as *u8); gv_num(plen); gv_puts("\n" as *u8) 190 var eok: i64 = 0 191 if plen > 0 { if plen < HGG_BUF { eok = 1 } } 192 gv_check("fixture-emitter-produced-bytes-within-the-buffer (an empty emit would pass every absence tooth below)" as *u8, eok, ctr) 193 194 var hv: i64 = 0 195 if hgg_find(out, plen, "S.HVRT&&S.HTRI" as *u8) >= 0 { hv = 1 } 196 gv_check("emitted-consumes-HVRT/HTRI-groom-MESH (the section nx_nxa_hair writes and NOTHING read)" as *u8, hv, ctr) 197 198 var hs: i64 = 0 199 if hgg_find(out, plen, "S.HSTR" as *u8) >= 0 { hs = 1 } 200 gv_check("emitted-consumes-HSTR-guide-strand-geometry (what ref9d.nxa actually carries today)" as *u8, hs, ctr) 201 202 var nof: i64 = 0 203 if hgg_find(out, plen, "NXHCH[fr*NXHCH_N+hs[v]]" as *u8) >= 0 { nof = 1 } 204 gv_check("emitted-indexes-the-chain-table-BY-VERTEX-SEGMENT (this is the line that consumes N offsets rather than one)" as *u8, nof, ctr) 205 206 var upl: i64 = 0 207 if hgg_find(out, plen, "bufferSubData" as *u8) >= 0 { upl = 1 } 208 gv_check("emitted-UPLOADS-the-displaced-geometry-each-frame (a table nothing uploads moves nothing on screen)" as *u8, upl, ctr) 209 210 var ann: i64 = 0 211 if hgg_find(out, plen, "no-hair-geometry" as *u8) >= 0 { ann = 1 } 212 gv_check("emitted-ANNOUNCES-absent-hair-geometry-rather-than-reporting-success (an asset with no groom must not read as working hair)" as *u8, ann, ctr) 213 214 var wir: i64 = 0 215 if hgg_find(out, plen, "requestAnimationFrame(hpTick)" as *u8) >= 0 { wir = 1 } 216 gv_check("emitted-WIRES-the-hair-probe-into-the-DOM-not-merely-DEFINES-it (measured: __nx_hair_probe once occurred exactly once -- its own definition -- so the rung's only measurement was invisible to every capture)" as *u8, wir, ctr) 217 218 var rigidclaim: i64 = 0 219 if hgg_find(out, plen, "rigid to the head joint" as *u8) < 0 { rigidclaim = 1 } 220 gv_check("neg-control-emitted-page-no-longer-claims-hair-is-RIGID-to-the-head-joint (the superseded block is gone, not merely shadowed)" as *u8, rigidclaim, ctr) 221 222 return gv_verdict("NX-GPE-HAIR-GEOMETRY" as *u8, ctr, "hair geometry consumed from the asset and displaced by the chain" as *u8) 223}