code wiki / (root) / nx_simlab.nx

nx_simlab.nx source

↩ module page · 126 lines · 6353 B

1// nx_simlab.nx -- Simulates 2D gravitational orbits using integer arithmetic for both native and browser execution. 2const O_MAGIC_4096: i64 = 4096 3// nx_simlab.nx -- BROWSER-RUNNABLE sovereign physics simulator (base-relative, no float, no mmap, no JS logic). 4// The SAME integer code runs NATIVE (base = mmap -> render a frame to PNG for verify) AND in the browser 5// (base = 0 = our sovereign WASM linear memory). It runs the REAL 2-D gravitational orbit (a = -GM r/|r|^3 via 6// our integer isqrt; velocity scaled by VS=2D so the symplectic half-kick is exact) and lets you watch the 7// SYMPLECTIC leapfrog hold a stable orbit while forward-EULER pumps energy and spirals out -- the live, in-browser 8// face of the S-class solution-verification work. The integer rasterizer draws the central mass, the trajectory 9// trail, the moving body, and an energy-deviation bar into a packed-RGB framebuffer; the browser only blits it. 10// nx_wasm auto-exports every func + the linear memory, so init()/tick(cmd)/render()/ww()/hh()/fb_off() are the 11// wasm interface. license_tier: ORIGINAL 12const W: i64 = 256 13const H: i64 = 256 14const O_FB: i64 = 0 // framebuffer W*H i64 packed-RGB at linear-mem offset 0 (W*H*8 = 524288 bytes) 15const O_ST: i64 = 524288 // scalars: [0]x [1]y [2]vsx [3]vsy [4]mode(0=leapfrog,1=euler) [5]steps [6]E0 [7]tcount [8]thead 16const O_TR: i64 = 524416 // trajectory ring buffer: TRAILN points x 2 i64 (px,py) 17const TRAILN: i64 = 200 18const SC: i64 = 100000 19const GM: i64 = 15625000000000 // (SC/8)^2 * SC -> circular speed SC/8 at r=SC 20const D: i64 = 64 21const VS: i64 = 128 // = 2*D (exact symplectic half-kick) 22const DD2: i64 = 8192 // = 2*D*D (position step divisor) 23const VY0: i64 = 12500 // initial tangential speed (circular) 24const CX: i64 = 128 25const CY: i64 = 128 26const SPX: i64 = 70 // pixels per SC (orbit radius -> 70 px) 27const STEPS_PER_TICK: i64 = 32 28 29func rgb(r: i64, g: i64, b: i64) -> i64 { return (r & 255) | ((g & 255) << 8) | ((b & 255) << 16) } 30func isqrt(N: i64) -> i64 { if N<2 { return N } var x: i64=N; var y: i64=(x+1)/2; while y<x { x=y; y=(x + N/x)/2 } return x } 31 32func putpx(base: i64, px: i64, py: i64, c: i64) -> i64 { 33 if px<0 { return 0 } if px>=W { return 0 } if py<0 { return 0 } if py>=H { return 0 } 34 let fb: *i64 = (base + O_FB) as *i64; fb[py*W+px]=c; return 0 35} 36func disc(base: i64, px: i64, py: i64, r: i64, c: i64) -> i64 { 37 var dy: i64=0-r 38 while dy<=r { var dx: i64=0-r 39 while dx<=r { if dx*dx+dy*dy <= r*r { putpx(base, px+dx, py+dy, c) } dx=dx+1 } 40 dy=dy+1 } 41 return 0 42} 43func clear_fb(base: i64, c: i64) -> i64 { let fb: *i64=(base+O_FB) as *i64; var i: i64=0; while i<W*H { fb[i]=c; i=i+1 } return 0 } 44 45// 2x specific orbital energy: (vsx^2+vsy^2)/VS^2 - 2GM/r. 46func energy2(x: i64, y: i64, vsx: i64, vsy: i64) -> i64 { let r: i64=isqrt(x*x+y*y); let vrx: i64=vsx/VS; let vry: i64=vsy/VS; return vrx*vrx+vry*vry - 2*GM/r } 47 48func reset_impl(base: i64) -> i64 { 49 let st: *i64 = (base+O_ST) as *i64 50 st[0]=SC; st[1]=0; st[2]=0; st[3]=VY0*VS 51 st[5]=0; st[6]=energy2(st[0],st[1],st[2],st[3]); st[7]=0; st[8]=0 52 return 0 53} 54func init_impl(base: i64) -> i64 { let st: *i64=(base+O_ST) as *i64; st[4]=0; reset_impl(base); return 0 } 55 56func step_one(base: i64) -> i64 { 57 let st: *i64 = (base+O_ST) as *i64 58 var x: i64=st[0]; var y: i64=st[1]; var vsx: i64=st[2]; var vsy: i64=st[3] 59 let r2: i64=x*x+y*y 60 if r2 > (4*SC)*(4*SC) { return 0 } // overflow/escape guard (body flew off) 61 let r: i64=isqrt(r2); let r3: i64=r2*r 62 let ax: i64=0 - GM*x/r3; let ay: i64=0 - GM*y/r3 63 if st[4]==0 { 64 let vhx: i64=vsx+ax; let vhy: i64=vsy+ay 65 x=x+vhx/DD2; y=y+vhy/DD2 66 let r2b: i64=x*x+y*y; let rb: i64=isqrt(r2b); let r3b: i64=r2b*rb 67 let axb: i64=0 - GM*x/r3b; let ayb: i64=0 - GM*y/r3b 68 vsx=vhx+axb; vsy=vhy+ayb 69 } else { 70 let nx: i64=x+vsx/DD2; let ny: i64=y+vsy/DD2 71 vsx=vsx+2*ax; vsy=vsy+2*ay; x=nx; y=ny 72 } 73 st[0]=x; st[1]=y; st[2]=vsx; st[3]=vsy; st[5]=st[5]+1 74 return 0 75} 76func record_trail(base: i64) -> i64 { 77 let st: *i64=(base+O_ST) as *i64; let tr: *i64=(base+O_TR) as *i64 78 let px: i64=CX + (st[0]*SPX)/SC; let py: i64=CY - (st[1]*SPX)/SC 79 let h: i64=st[8]; tr[h*2]=px; tr[h*2+1]=py 80 st[8]=(h+1)%TRAILN; if st[7]<TRAILN { st[7]=st[7]+1 } 81 return 0 82} 83func tick_impl(base: i64, cmd: i64) -> i64 { 84 let st: *i64=(base+O_ST) as *i64 85 if cmd==1 { st[4]=0; reset_impl(base); return 0 } // leapfrog (symplectic) 86 if cmd==2 { st[4]=1; reset_impl(base); return 0 } // euler (naive) 87 if cmd==3 { reset_impl(base); return 0 } // reset current mode 88 var i: i64=0; while i<STEPS_PER_TICK { step_one(base); i=i+1 } 89 record_trail(base) 90 return 0 91} 92func render_impl(base: i64) -> i64 { 93 let st: *i64=(base+O_ST) as *i64; let tr: *i64=(base+O_TR) as *i64 94 clear_fb(base, rgb(10,14,24)) 95 // central mass 96 disc(base, CX, CY, 5, rgb(250,210,70)) 97 // trajectory trail (dim, mode-tinted) 98 var tc: i64=0 99 while tc<st[7] { 100 let px: i64=tr[tc*2]; let py: i64=tr[tc*2+1] 101 if st[4]==0 { putpx(base, px, py, rgb(60,120,90)) } else { putpx(base, px, py, rgb(120,70,60)) } 102 tc=tc+1 103 } 104 // body 105 let bpx: i64=CX + (st[0]*SPX)/SC; let bpy: i64=CY - (st[1]*SPX)/SC 106 if st[4]==0 { disc(base, bpx, bpy, 3, rgb(80,230,140)) } else { disc(base, bpx, bpy, 3, rgb(235,90,80)) } 107 // energy-deviation bar along the bottom 108 let e0: i64=st[6]; let cur: i64=energy2(st[0],st[1],st[2],st[3]) 109 var dev: i64=cur-e0; if dev<0 { dev=0-dev } 110 var ae0: i64=e0; if ae0<0 { ae0=0-ae0 } 111 var bw: i64=dev*(W-16)/ae0; if bw>(W-16) { bw=W-16 } 112 var bx: i64=0 113 while bx<bw { var by: i64=H-8 114 while by<H-2 { if st[4]==0 { putpx(base, 8+bx, by, rgb(80,230,140)) } else { putpx(base, 8+bx, by, rgb(235,90,80)) } by=by+1 } 115 bx=bx+1 } 116 return 0 117} 118 119// ---- wasm interface (nx_wasm auto-exports these) ---- 120func ww() -> i64 { return W } 121func hh() -> i64 { return H } 122func fb_off() -> i64 { return O_FB } 123func init() -> i64 { return init_impl(0) } 124func tick(cmd: i64) -> i64 { return tick_impl(0, cmd) } 125func render() -> i64 { return render_impl(0) } 126func mem_bytes() -> i64 { return O_TR + TRAILN*2*8 + O_MAGIC_4096 }