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 }