code wiki / _hdl_build / nx_viz3d_stl_gate.nx

nx_viz3d_stl_gate.nx source

↩ module page · 157 lines · 11516 B

1import "nx_gate_gn.nx" 2import "nx_gate_base.nx" 3// nx_viz3d_stl_gate.nx -- GATE for the SOVEREIGN 3D viz, rung V2: a BINARY STL MESH LOADER. STL is the mesh 4// interchange every target eats -- 3D printing/slicers (Klipper direction), CAD, Blender im/export. Binary STL stores 5// coords as IEEE-754 float32; we decode them to fixed-point integers with NO float library (pure bit manipulation = 6// sovereign + no-float), auto-scale the mesh to fit the viewport, and render it as a rotating turntable (the V1 7// pipeline). Proven by a GENERATE -> PARSE -> render round-trip on a known mesh (a tetrahedron), plus a float32 8// codec round-trip. Deliverable: open web_assets/stl_0.html and watch a real STL-loaded mesh spin (0-JS, sovereign). 9// T1 float32 codec round-trips: f32_dec(f32_enc(v)) == v over several signed values. 10// T2 STL generate->parse round-trips: the parsed triangle vertices == the original mesh (count + coords). 11// T3 render the parsed mesh -> 12 sovereign frames written (0 JS) + the refresh chain loops. T4 NEVER-BRICK. 12// T5 LIAR-KILL (corrupt an STL coord byte -> the parsed/rendered mesh diverges). expect_exit: 0 13// license_tier: ORIGINAL 14import "nx_viz3d.nx" 15import "nx_fpga_verilog.nx" 16import "nx_syscalls.nx" 17 18func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 19" as *u8); return ok } 20func has_sub(buf: *u8, len: i64, s: *u8) -> i64 { var sl: i64=0; while s[sl]!=(0 as u8){sl=sl+1} var p: i64=0; while p+sl<=len { var m: i64=1; var j: i64=0; while j<sl { if buf[p+j]!=s[j] {m=0} j=j+1 } if m==1 {return 1} p=p+1 } return 0 } 21func absv(v: i64) -> i64 { if v<0 { return 0-v } return v } 22 23// encode integer `value` as IEEE-754 float32 (little-endian) at buf[off]; returns off+4. 24func f32_enc(buf: *u8, off: i64, value: i64) -> i64 { 25 var v: i64=value; var sign: i64=0 26 if v<0 { sign=1; v=0-v } 27 if v==0 { buf[off]=0 as u8; buf[off+1]=0 as u8; buf[off+2]=0 as u8; buf[off+3]=0 as u8; return off+4 } 28 var m: i64=v; var e: i64=0 29 while m >= 16777216 { m=m>>1; e=e+1 } // >= 2^24 30 while m < 8388608 { m=m<<1; e=e-1 } // < 2^23 31 let iexp: i64 = (23+e)+127 32 let bits: i64 = (sign<<31) | (iexp<<23) | (m & 8388607) 33 buf[off]=(bits&255) as u8; buf[off+1]=((bits>>8)&255) as u8; buf[off+2]=((bits>>16)&255) as u8; buf[off+3]=((bits>>24)&255) as u8 34 return off+4 35} 36// decode IEEE-754 float32 (little-endian) at buf[off] -> integer (rounds toward 0). 37func f32_dec(buf: *u8, off: i64) -> i64 { 38 let bits: i64 = (buf[off] as i64) | ((buf[off+1] as i64)<<8) | ((buf[off+2] as i64)<<16) | ((buf[off+3] as i64)<<24) 39 let sign: i64=(bits>>31)&1; let exp: i64=(bits>>23)&255; let mant: i64=bits&8388607 40 if exp==0 { return 0 } 41 let m: i64 = 8388608 | mant; let e: i64 = exp-127-23 42 var r: i64 = m 43 if e>=0 { r=r<<e } else { r=r>>(0-e) } 44 if sign==1 { r=0-r } 45 return r 46} 47 48func render_mesh(buf: *u8, NT: i64, vx: *i64, vy: *i64, vz: *i64, ang: i64, next: i64, tbl: *i64) -> i64 { 49 let pp: *i64=sys_mmap(16) as *i64 50 var o: i64=0 51 o=vlg_wr_str(buf,o,"<!doctype html><html><head><meta charset='utf-8'><title>Nishi 3D &mdash; STL mesh</title>\x00" as *u8) 52 o=vlg_wr_str(buf,o,"<meta http-equiv='refresh' content='1;url=stl_\x00" as *u8); o=vlg_wr_int(buf,o,next); o=vlg_wr_str(buf,o,".html'>\x00" as *u8) 53 o=vlg_wr_str(buf,o,"<style>body{background:#070707;color:#cfc;font-family:monospace;margin:0;padding:18px;text-align:center}h1{color:#39ff14;font-size:18px}p{color:#8b8;font-size:12px}</style></head><body>\x00" as *u8) 54 o=vlg_wr_str(buf,o,"<h1>Nishi Sovereign 3D &mdash; STL mesh loaded (no-float decode, 0-JS)</h1>\x00" as *u8) 55 o=vlg_wr_str(buf,o,"<svg width='600' height='420' xmlns='http://www.w3.org/2000/svg'><rect x='0' y='0' width='600' height='420' fill='#070707'/>\x00" as *u8) 56 var ti: i64=0 57 while ti<NT { 58 let s0: *i64=sys_mmap(16) as *i64; let s1: *i64=sys_mmap(16) as *i64; let s2: *i64=sys_mmap(16) as *i64 59 vp_project(vx[ti*3+0],vy[ti*3+0],vz[ti*3+0],ang,tbl,s0) 60 vp_project(vx[ti*3+1],vy[ti*3+1],vz[ti*3+1],ang,tbl,s1) 61 vp_project(vx[ti*3+2],vy[ti*3+2],vz[ti*3+2],ang,tbl,s2) 62 o=vlg_wr_str(buf,o,"<polygon points='\x00" as *u8); o=vlg_wr_int(buf,o,s0[0]); o=vlg_wr_str(buf,o,",\x00" as *u8); o=vlg_wr_int(buf,o,s0[1]); o=vlg_wr_str(buf,o," \x00" as *u8); o=vlg_wr_int(buf,o,s1[0]); o=vlg_wr_str(buf,o,",\x00" as *u8); o=vlg_wr_int(buf,o,s1[1]); o=vlg_wr_str(buf,o," \x00" as *u8); o=vlg_wr_int(buf,o,s2[0]); o=vlg_wr_str(buf,o,",\x00" as *u8); o=vlg_wr_int(buf,o,s2[1]) 63 o=vlg_wr_str(buf,o,"' fill='#10460f' stroke='#39ff14' stroke-width='2'/>\x00" as *u8) 64 ti=ti+1 65 } 66 o=vlg_wr_str(buf,o,"</svg><p>frame \x00" as *u8); o=vlg_wr_int(buf,o,ang); o=vlg_wr_str(buf,o,"/12 &middot; \x00" as *u8); o=vlg_wr_int(buf,o,NT); o=vlg_wr_str(buf,o,"-triangle mesh decoded from binary STL (float32 -> fixed-point, no float lib) &middot; 0 JavaScript. Open stl_0.html and watch it spin.</p></body></html>\n\x00" as *u8) 67 buf[o]=0 as u8 68 return o 69} 70 71func main() -> i64 { 72 gw("=== nx_viz3d_stl_gate: SOVEREIGN 3D viz V2 -- a BINARY STL mesh loader (no-float float32 decode) ===\n" as *u8) 73 var pass: i64 = 0; var total: i64 = 0 74 let tbl: *i64=sys_mmap(8*16) as *i64; vp_fill_sin(tbl) 75 76 // T1: float32 codec round-trip 77 let fb: *u8=sys_mmap(64) 78 let tv: *i64=sys_mmap(8*8) as *i64; tv[0]=100; tv[1]=0-100; tv[2]=50; tv[3]=0; tv[4]=12345; tv[5]=0-7; tv[6]=1; tv[7]=4096 79 var fmis: i64=0; var i: i64=0 80 while i<8 { f32_enc(fb, 0, tv[i]); let d: i64=f32_dec(fb, 0); if d != tv[i] { fmis=fmis+1 } i=i+1 } 81 total=total+1; if fmis==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 82 gw("T1 float32 codec round-trips f32_dec(f32_enc(v))==v over 8 signed values, mismatches=" as *u8); gn(fmis); gw("\n" as *u8) 83 84 // build a known mesh (a tetrahedron: 4 triangles, 12 verts) and GENERATE a binary STL 85 let NT: i64 = 4 86 let ovx: *i64=sys_mmap(8*16) as *i64; let ovy: *i64=sys_mmap(8*16) as *i64; let ovz: *i64=sys_mmap(8*16) as *i64 87 // P0(100,100,100) P1(100,-100,-100) P2(-100,100,-100) P3(-100,-100,100) 88 ovx[0]=100;ovy[0]=100;ovz[0]=100; ovx[1]=100;ovy[1]=0-100;ovz[1]=0-100; ovx[2]=0-100;ovy[2]=100;ovz[2]=0-100 // F0 P0,P1,P2 89 ovx[3]=100;ovy[3]=100;ovz[3]=100; ovx[4]=0-100;ovy[4]=100;ovz[4]=0-100; ovx[5]=0-100;ovy[5]=0-100;ovz[5]=100 // F1 P0,P2,P3 90 ovx[6]=100;ovy[6]=100;ovz[6]=100; ovx[7]=0-100;ovy[7]=0-100;ovz[7]=100; ovx[8]=100;ovy[8]=0-100;ovz[8]=0-100 // F2 P0,P3,P1 91 ovx[9]=100;ovy[9]=0-100;ovz[9]=0-100; ovx[10]=0-100;ovy[10]=0-100;ovz[10]=100; ovx[11]=0-100;ovy[11]=100;ovz[11]=0-100 // F3 P1,P3,P2 92 93 let stl: *u8=sys_mmap(4096) 94 var p: i64=0; while p<80 { stl[p]=0 as u8; p=p+1 } // 80-byte header 95 stl[80]=(NT&255) as u8; stl[81]=((NT>>8)&255) as u8; stl[82]=((NT>>16)&255) as u8; stl[83]=((NT>>24)&255) as u8 96 var off: i64=84; var ti: i64=0 97 while ti<NT { 98 off=f32_enc(stl,off,0); off=f32_enc(stl,off,0); off=f32_enc(stl,off,0) // normal = 0 99 var vi: i64=0 100 while vi<3 { let k: i64=ti*3+vi; off=f32_enc(stl,off,ovx[k]); off=f32_enc(stl,off,ovy[k]); off=f32_enc(stl,off,ovz[k]); vi=vi+1 } 101 stl[off]=0 as u8; stl[off+1]=0 as u8; off=off+2 // attribute u16 102 ti=ti+1 103 } 104 let stllen: i64 = off 105 106 // PARSE the binary STL back 107 let pvx: *i64=sys_mmap(8*16) as *i64; let pvy: *i64=sys_mmap(8*16) as *i64; let pvz: *i64=sys_mmap(8*16) as *i64 108 let cnt: i64 = (stl[80] as i64)|((stl[81] as i64)<<8)|((stl[82] as i64)<<16)|((stl[83] as i64)<<24) 109 var o2: i64=84; var t2: i64=0 110 while t2<cnt { 111 o2=o2+12 // skip normal 112 var v2: i64=0 113 while v2<3 { let k: i64=t2*3+v2; pvx[k]=f32_dec(stl,o2); o2=o2+4; pvy[k]=f32_dec(stl,o2); o2=o2+4; pvz[k]=f32_dec(stl,o2); o2=o2+4; v2=v2+1 } 114 o2=o2+2; t2=t2+1 115 } 116 // T2: parsed mesh == original 117 var mmis: i64=0; if cnt != NT { mmis=mmis+1 } 118 var k2: i64=0; while k2<NT*3 { if pvx[k2]!=ovx[k2] {mmis=mmis+1} if pvy[k2]!=ovy[k2] {mmis=mmis+1} if pvz[k2]!=ovz[k2] {mmis=mmis+1} k2=k2+1 } 119 total=total+1; if mmis==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 120 gw("T2 binary STL generate->parse round-trip: \x00" as *u8); gn(cnt); gw(" triangles parsed, vertices == original, mismatches=" as *u8); gn(mmis); gw(" (STL bytes=" as *u8); gn(stllen); gw(")\n" as *u8) 121 122 // auto-scale the parsed mesh to fit (maxabs -> 100); render + write the turntable 123 var mx: i64=1; k2=0; while k2<NT*3 { if absv(pvx[k2])>mx {mx=absv(pvx[k2])} if absv(pvy[k2])>mx {mx=absv(pvy[k2])} if absv(pvz[k2])>mx {mx=absv(pvz[k2])} k2=k2+1 } 124 let sx: *i64=sys_mmap(8*16) as *i64; let sy: *i64=sys_mmap(8*16) as *i64; let sz: *i64=sys_mmap(8*16) as *i64 125 k2=0; while k2<NT*3 { sx[k2]=pvx[k2]*100/mx; sy[k2]=pvy[k2]*100/mx; sz[k2]=pvz[k2]*100/mx; k2=k2+1 } 126 127 let buf: *u8=sys_mmap(16384); let fname: *u8=sys_mmap(64) 128 var wmis: i64=0; var sov: i64=1; var chainok: i64=1; var ang: i64=0 129 while ang<12 { 130 let nxt: i64=(ang+1)%12 131 let len: i64=render_mesh(buf, NT, sx, sy, sz, ang, nxt, tbl) 132 if has_sub(buf,len,"<script\x00" as *u8)==1 { sov=0 } 133 var cf: i64=0; cf=vlg_wr_str(fname,0,"url=stl_\x00" as *u8); cf=vlg_wr_int(fname,cf,nxt); cf=vlg_wr_str(fname,cf,".html\x00" as *u8); fname[cf]=0 as u8 134 if has_sub(buf,len,fname)==0 { chainok=0 } 135 var fo: i64=0; fo=vlg_wr_str(fname,0,"web_assets/stl_\x00" as *u8); fo=vlg_wr_int(fname,fo,ang); fo=vlg_wr_str(fname,fo,".html\x00" as *u8); fname[fo]=0 as u8 136 let fd: i64=sys_openat_wr(fname,420) 137 if fd>=0 { let wr: i64=sys_write(fd,buf,len); sys_close(fd); if wr!=len {wmis=wmis+1} } else { wmis=wmis+1 } 138 ang=ang+1 139 } 140 total=total+1; if sov==1 { if wmis==0 { if chainok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } 141 gw("T3 sovereign (0 <script>) render of the loaded mesh -> web_assets/stl_0..11.html, refresh chain loops, write-fail=" as *u8); gn(wmis); gw("\n" as *u8) 142 143 // T4: never-brick 144 total=total+1; pass=pass+1 145 gw(" [PASS] T4 never-brick (#26): pure-integer float-bit decode + render + write; zero hardware-state writes\n" as *u8) 146 147 // T5: liar-kill -- corrupt the EXPONENT byte of the first coordinate's float32 (a low mantissa byte is below 148 // integer precision and would round back); re-decode -> the parsed vertex must change. 149 stl[84+12+3] = (stl[84+12+3] + 7) as u8 // perturb the exponent byte (MSB, LE) 150 let bx: i64 = f32_dec(stl, 84+12) 151 total=total+1; if bx != ovx[0] { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 152 gw("T5 liar-kill: corrupting an STL coord byte -> parsed vertex x = \x00" as *u8); gn(bx); gw(" (!= original \x00" as *u8); gn(ovx[0]); gw(")\n" as *u8) 153 154 gw("\n=== nx_viz3d_stl_gate " as *u8); gn(pass); gw("/" as *u8); gn(total) 155 if pass == total { gw(" GREEN (a SOVEREIGN binary-STL mesh loader: float32 decoded with no float lib, mesh round-trips + renders rotating -> open web_assets/stl_0.html. The interchange every slicer/CAD/Blender eats.)\n" as *u8); sys_exit(0); return 0 } 156 gw(" RED\n" as *u8); sys_exit(1); return 1 157}