nx_kinetics.nx source
↩ module page · 79 lines · 4485 B
1const O_MAGIC_4096: i64 = 4096
2// nx_kinetics.nx -- BROWSER-RUNNABLE chemical reaction-kinetics simulator (base-relative, integer/no-float, no JS
3// logic). SAME code runs native (base = mmap -> PNG) and in the browser. The CHEMISTRY pillar: first-order decay
4// A->B (d[A]/dt=-k[A]) whose hallmark is a CONSTANT half-life (the exponential law), a reversible reaction A<=>B
5// that settles to equilibrium [B]/[A]=kf/kr, and a zero-order control (constant rate) whose half-life SHRINKS. Mass
6// is conserved exactly ([A]+[B]=const). Validated by nx_kinetics_gate. The render draws the [A] decay curve filling
7// left-to-right plus live [A]/[B] bars. nx_wasm auto-exports every func. license_tier: ORIGINAL
8const W: i64 = 256
9const H: i64 = 256
10const O_FB: i64 = 0
11const O_A: i64 = 524288 // [A] concentration (scaled, A0=1e6)
12const O_B: i64 = 524296 // [B]
13const O_MODE: i64 = 524304 // 0=first-order decay, 1=reversible, 2=zero-order (control)
14const O_KNUM: i64 = 524312 // forward rate numerator
15const O_KDEN: i64 = 524320 // rate denominator
16const O_KRNUM: i64 = 524328 // reverse rate numerator (reversible)
17const O_STEP: i64 = 524336
18const O_HIST: i64 = 524344 // [A] history (one sample per tick)
19const HISTN: i64 = 236
20const A0: i64 = 1000000
21const ZCONST: i64 = 700 // zero-order constant decrement
22const STEPS_PER_TICK: i64 = 20
23
24func rgb(r: i64, g: i64, b: i64) -> i64 { return (r & 255) | ((g & 255) << 8) | ((b & 255) << 16) }
25func putpx(base: i64, px: i64, py: i64, c: i64) -> i64 {
26 if px<0 { return 0 } if px>=W { return 0 } if py<0 { return 0 } if py>=H { return 0 }
27 let fb: *i64 = (base + O_FB) as *i64; fb[py*W+px]=c; return 0
28}
29func 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 }
30func vbar(base: i64, x0: i64, x1: i64, h: i64, c: i64) -> i64 {
31 var y: i64=H-10-h; while y<H-10 { var x: i64=x0; while x<x1 { putpx(base,x,y,c); x=x+1 } y=y+1 } return 0
32}
33
34func init_impl(base: i64) -> i64 {
35 let A: *i64=(base+O_A) as *i64; let B: *i64=(base+O_B) as *i64; let M: *i64=(base+O_MODE) as *i64
36 let KN: *i64=(base+O_KNUM) as *i64; let KD: *i64=(base+O_KDEN) as *i64; let KR: *i64=(base+O_KRNUM) as *i64
37 A[0]=A0; B[0]=0; M[0]=0; KN[0]=1; KD[0]=1000; KR[0]=1
38 let stp: *i64=(base+O_STEP) as *i64; stp[0]=0
39 let HI: *i64=(base+O_HIST) as *i64; var i: i64=0; while i<HISTN { HI[i]=0-1; i=i+1 }
40 return 0
41}
42func step_one_impl(base: i64) -> i64 {
43 let A: *i64=(base+O_A) as *i64; let B: *i64=(base+O_B) as *i64; let M: *i64=(base+O_MODE) as *i64
44 let KN: *i64=(base+O_KNUM) as *i64; let KD: *i64=(base+O_KDEN) as *i64; let KR: *i64=(base+O_KRNUM) as *i64
45 if M[0]==2 { var d: i64=ZCONST; if A[0]<d { d=A[0] } A[0]=A[0]-d; B[0]=B[0]+d; return 0 }
46 if M[0]==1 { let df: i64=A[0]*KN[0]/KD[0]; let dr: i64=B[0]*KR[0]/KD[0]; A[0]=A[0]-df+dr; B[0]=B[0]+df-dr; return 0 }
47 let d: i64=A[0]*KN[0]/KD[0]; A[0]=A[0]-d; B[0]=B[0]+d // first-order
48 return 0
49}
50func tick_impl(base: i64, cmd: i64) -> i64 {
51 if cmd==3 { init_impl(base); return 0 }
52 let stp: *i64=(base+O_STEP) as *i64
53 if stp[0]>=HISTN { init_impl(base); return 0 } // loop the animation
54 var s: i64=0; while s<STEPS_PER_TICK { step_one_impl(base); s=s+1 }
55 let A: *i64=(base+O_A) as *i64; let HI: *i64=(base+O_HIST) as *i64
56 HI[stp[0]]=A[0]; stp[0]=stp[0]+1
57 return 0
58}
59func render_impl(base: i64) -> i64 {
60 let A: *i64=(base+O_A) as *i64; let B: *i64=(base+O_B) as *i64; let HI: *i64=(base+O_HIST) as *i64
61 clear_fb(base, rgb(12,14,22))
62 var gx: i64=0; while gx<W { putpx(base, gx, H-10, rgb(60,66,80)); gx=gx+1 } // baseline
63 // [A] decay curve (history)
64 var x: i64=0
65 while x<HISTN { let a: i64=HI[x]; if a>=0 { let py: i64=(H-12) - a*(H-30)/A0; putpx(base, x+2, py, rgb(120,180,250)); putpx(base, x+2, py+1, rgb(80,120,190)) } x=x+1 }
66 // live [A] (blue) and [B] (green) bars on the right
67 vbar(base, W-26, W-16, A[0]*(H-30)/A0, rgb(90,150,235))
68 vbar(base, W-13, W-3, B[0]*(H-30)/A0, rgb(90,210,140))
69 return 0
70}
71
72// ---- wasm interface ----
73func ww() -> i64 { return W }
74func hh() -> i64 { return H }
75func fb_off() -> i64 { return O_FB }
76func init() -> i64 { return init_impl(0) }
77func tick(cmd: i64) -> i64 { return tick_impl(0, cmd) }
78func render() -> i64 { return render_impl(0) }
79func mem_bytes() -> i64 { return O_HIST + HISTN*8 + O_MAGIC_4096 }