-
Notifications
You must be signed in to change notification settings - Fork 1
/
embed.js
191 lines (165 loc) · 6.75 KB
/
embed.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
(async () => {
/*░░░░░░░░░░░░░░░░░░░░░░ Galaxy Parameters ░░░░░░░░░░░░░░░░░░░░░░*/
containerIsAlive = true;
blur_factor = 5.0; // star light blur unit-less
blur_star_edge = 1; // star edge blur unit-less
dt = 10e+7 // timestep in years
variable_timestep = true // if time step should vary
g_scale_factor = 10e+0; // gravity constant scale
size = 20000; // light years
velocity_scale = 3; // scale initial kepler velocity
plot_rate = 1; // timesteps
plot_scale = 0.01; // zoom factor (if 1: 1px=1ly)
plummer_bulge = 1000; // light years
show_iter_time = true // boolean, will show ms in console
star_color = ['#f5dedc','#c9ffdd','#c2dcff','#c2fffe','#171006'];
star_mass_max = 5;
star_mass_min = .2;
star_scale_factor = .5; // unit-less
time_delay_ms = 1; // milliseconds
N = 200; // unit-less
T = 10e+12; // years
/* All parameters are in SI-units */
const m_sun = 1.989e+30,
G = 6.67430e-11,
ly = 9.46e+15,
pc = 3.086e+19,
yr = 31.536e+6;
/*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/
/* Canvas UI */
window.subprocessesActive = true;
const scriptTag = document.currentScript;
const parent = scriptTag.parentElement;
const [w,h] = [parent.clientWidth,parent.clientHeight];
const center = [Math.floor(w/2),Math.floor(h/2)];
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
parent.appendChild(canvas);
ctx.canvas.width=w;ctx.canvas.height=h;document.body.style.background='#04050d';
async function cl(){ctx.clearRect(0,0,canvas.width,canvas.height)}
function sleep(t){return new Promise(r=>setTimeout(r,t))}
function draw(x,y,scale,col){
ctx.save();
ctx.fillStyle=col;
ctx.shadowBlur = scale*blur_star_edge;
ctx.beginPath();
ctx.arc(Math.floor(x+center[0]),Math.floor(y+center[1]), scale*star_scale_factor, 0, 2 * Math.PI);
ctx.fill();
ctx.shadowBlur = scale*blur_factor;
ctx.shadowColor = col;
ctx.beginPath();
ctx.arc(Math.floor(x+center[0]),Math.floor(y+center[1]), scale*star_scale_factor, 0, 2 * Math.PI);
ctx.fill();
ctx.restore();
}
/* Integrated Methods */
function sq (x) {
/* Benchmarked with 1000 particles */
return Math.sqrt(x) //
//return x**.5 // 280ms
//return Math.pow(x,.5) //
}
/* Vectors & Gravity */
const Null = [0,0];
function add(v,w,s=1){return [v[0]+s*w[0],v[1]+s*w[1]]}
function dist(v,w){return add(v,w,-1)}
function dot(v,w){return v[0]*w[0]+v[1]*w[1]}
function mag(v){return sq(dot(v,v))}
function scl(a,v){return [a*v[0],a*v[1]]}
function a_plummer(v,w,m,a){
// star at vector w acts on star described by v
r = dist(v,w);
c = g_scale_factor*G*m/(sq((dot(r,r)+a**2)**3));
return scl(c,r)
}
function v0(){
for (s in S) {
r=S[s].slice(1,3);
d=mag(r)
M=0;
for(n in S){
sv=S[n].slice(1,3);
if(mag(sv)<d){
M+=S[n][0]
}
}
kep=sq(G*M*m_sun/d)*velocity_scale;
S[s][3]=-kep/d*r[1];
S[s][4]=kep/d*r[0]
}
}
/* Sampling */
function u(){return Math.random()};
function boxMuller(){x=u();A=sq(-2*Math.log(u()));return[A*Math.cos(2*Math.PI*x),A*Math.sin(2*Math.PI*x)]}
function star(){const[x,y]=boxMuller();const m=u()*(star_mass_max-star_mass_min)+star_mass_min;const col=star_color[Math.floor(u()*5)];return[m,x*size*ly,y*size*ly,0,0,col,[0,0]]}
/* Algorithm */
S = {}
function scatter () {for (i=0;i<N;i++){S[i] = star()}}
async function plot () {for (s in S){draw(Math.floor(plot_scale*S[s][1]/ly),Math.floor(plot_scale*S[s][2]/ly),S[s][0],S[s][5])}}
async function leapFrog () {
t=0;
_dt=dt*yr;
count=0;
exp_smoothing=0.2
mean_iter_time = 0
while (t<T) {
if (!window.subprocessesActive) {
break
}
if (show_iter_time){start_time = Date.now()}
// refresh plot after plot rate was reached
count++;
if (count > plot_rate) {
await cl();
plot();
count=0;
}
stop=Object.values(S).length;
for (i=0;i<stop;i++) {
// determine origin star point
p1 = S[i];
// define kinematic objects for current point
x = p1.slice(1,3);
v = p1.slice(3,5);
a = p1[6];
// console.log(i,x,v,a)
// determine net acceleration
// start iteration from next star id and ascend
start=parseInt(p1)+1;
for(j=start;j<stop;j++){
// interacting point
p2=S[j];
// compute force action with single sun mass
f_a = a_plummer(x,p2.slice(1,3),m_sun,plummer_bulge*ly)
// add reactio contribution directly to interacting star
// multiply with origin star mass
S[j][6]=add(p2[6],f_a,p1[0]);
// add to current acceleration
a = add(a,f_a,-p2[0])
}
// from here on acceleration is not needed anymore so reset it in star object
S[i][6] = Null;
// compute velocity change
dv=scl(_dt*.5,a);
// kick
v_h = add(v,dv)
// drift
S[i][1] = x[0] + v_h[0] * _dt;
S[i][2] = x[1] + v_h[1] * _dt;
// kick
S[i][3] = v_h[0] + dv[0];
S[i][4] = v_h[1] + dv[1];
}
await sleep(time_delay_ms)
t+=dt;
if (show_iter_time){
mean_iter_time=exp_smoothing*(Date.now()-start_time)+(1-exp_smoothing)*mean_iter_time
// console.log('Iteration time:',mean_iter_time,'ms')
}
}
}
// start
scatter();
v0();
leapFrog();
})();