-
Notifications
You must be signed in to change notification settings - Fork 0
/
astro.exw
444 lines (396 loc) · 13.5 KB
/
astro.exw
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
-- Mike Manturov, 2010
-- Based on and inspired with Marco Achury's estrella.ex program
-- Keyboard commands (z/x,c,f,-/+,p,0)
-- Inelastic/Elastic/Mixed collision
-- Some demos
-- Print screen function (key 'p')
include output.ew
include dos_rescue.ew as dos_rescue
include mouse_rescue.ew
include routines.e
include printscr.e
include std/error.e
constant NONE = -1
-- Various collision types
function Inelastic(sequence bibj, sequence delta_r, atom r)
sequence p,cm
sequence bi,bj
bi = bibj[1]
bj = bibj[2]
p = bi[VEL]*bi[MAS] + bj[VEL]*bj[MAS]
cm = bi[POS]*bi[MAS] + bj[POS]*bj[MAS]
-- Confluence
bi[MAS] += bj[MAS]
bi[POS] = cm/bi[MAS]
bi[VEL] = p/bi[MAS]
bi[RAD] = power(bi[RAD]*bi[RAD]*bi[RAD] + bj[RAD]*bj[RAD]*bj[RAD], 1/3)
if length(bj) >= COO and sequence(bj[COO]) then
erase(bj[COO])
end if
return {bi}
end function
global atom elasticity
elasticity = 20
function Elastic(sequence bibj, sequence delta_r, atom distance)
sequence F
atom d
sequence bi,bj
bi = bibj[1]
bj = bibj[2]
d = bi[RAD]+bj[RAD]-distance
F = - elasticity * delta_r/distance * d -- the Hook law
bi[VEL] += F/bi[MAS]*dt
bj[VEL] -= F/bj[MAS]*dt
return {bi,bj}
end function
global atom mu
mu = 0.5
function Stokes(sequence bibj, sequence delta_r, atom distance)
sequence F, vel
atom r
sequence bi,bj
bi = bibj[1]
bj = bibj[2]
r = min(bi[RAD],bj[RAD])
vel = bj[VEL]-bi[VEL]
F = -mu*r*vel -- the Stokes force
bi[VEL] -= F/bi[MAS]*dt
bj[VEL] -= (-F)/bj[MAS]*dt
return {bi,bj}
end function
function Slide(sequence bibj, sequence delta_r, atom distance)
sequence F, vel
atom r
sequence bi,bj
bi = bibj[1]
bj = bibj[2]
r = min(bi[RAD],bj[RAD])
vel = scalar_mul_3d(bj[VEL]-bi[VEL], delta_r)/distance * delta_r/distance
F = -mu*r*vel -- the Stokes force
-- if bi[MAS] = 0 then
-- bi[VEL] = bj[VEL]
-- elsif bj[MAS] = 0 then
-- bj[VEL] = bi[VEL]
-- else
bi[VEL] -= F/bi[MAS]*dt
bj[VEL] -= -F/bj[MAS]*dt
-- end if
return {bi,bj}
end function
global object Collisions
-- Collisions = { routine_id("Stokes"), routine_id("Elastic") }
Collisions = { routine_id("Slide"), routine_id("Elastic") }
function Collide(sequence bibj, sequence delta_r, atom distance)
if atom(Collisions) then
if Collisions != NONE then
bibj = call_func(Collisions,{bibj,delta_r,distance})
end if
else
for n = 1 to length(Collisions) do
if Collisions[n] != NONE then
bibj = call_func(Collisions[n],{bibj,delta_r,distance})
end if
end for
end if
return bibj
end function
-- Various far interaction types (without collision)
function MutualGravity(sequence bibj, sequence delta_r, atom distance)
atom gravity
sequence vgravity
sequence bi,bj
bi = bibj[1]
bj = bibj[2]
if distance > 0 then
gravity = G / power(distance,2) -- F = G * Mi * Mj / r^2 -- F0 = G / r^2
vgravity = (delta_r / distance) * gravity
-- by The Third Newton's law, Fi = -Fj
bi[VEL] += vgravity * bj[MAS]*dt -- accel = F / Mi * dt = F0 * Mj * dt
bj[VEL] -= vgravity * bi[MAS]*dt -- accel = - F / Mj * dt = - F0 * Mi * dt
end if
return {bi,bj}
end function
function Gravity(sequence p, atom M, sequence delta_r, atom distance)
atom a
if distance > 0 then
a = G*M/power(distance,2)
p[VEL] += a*dt
end if
return p
end function
global
object Interactions
Interactions = routine_id("MutualGravity")
function Interact(sequence bibj, sequence delta_r, atom distance)
if atom(Interactions) then
if Interactions != NONE then
bibj = call_func(Interactions,{bibj,delta_r,distance})
end if
else
for n = 1 to length(Interactions) do
if Interactions[n] != NONE then
bibj = call_func(Interactions[n],{bibj,delta_r,distance})
end if
end for
end if
return bibj
end function
-- Action of external or inner forces
-- for example:
function Atmosphere(sequence b)
b[VEL] -= 0.01*b[RAD]*b[VEL]/b[MAS]*dt
return {b}
end function
global
object External
External = NONE
-- External = routine_id("Atmosphere")
function ForceExternal(sequence b)
sequence bb
if atom(External) then
if External != NONE then
bb = call_func(External, {b})
else
bb = {b}
end if
else
for n = 1 to length(External) do
if External[n] != NONE then
bb = call_func(External[n],{b})
end if
end for
end if
return bb
end function
constant DYN_DT = 1
integer pause
pause = 0
global
procedure motion(sequence body, atom dt0)
atom t
atom distance, sqdist
sequence b
sequence delta_r
atom key
object mouse_event
integer count,old_count
integer i,j
atom time1, time2, cycles
integer drag,dragged
sequence last_mouse_pos
integer follow,sel
dt = dt0
deselect()
drag = 0
follow = 0
t = 0
dos_rescue:clear_screen()
body = center_mass(body)
body = zero_momentum(body)
center = {0,0}
zoom_restore()
count = length(body)
old_count = 0
for n = 1 to count do
body[n] = redraw(body[n])
end for
cycles = 0
time1 = time()
time2 = time1
while 1 do
if not(pause or drag) then
i = 1
if DYN_DT and old_count = 0 then
old_count = count
end if
while i <= count do
j = i + 1
if i = 1 and External != NONE then
b = ForceExternal(body[i])
body[i] = b[1]
if length(b) > 1 then
body = body[1..count]
for n = 2 to length(b) do
body = append(body,b[n])
body[n] = redraw(body[n])
end for
count = length(body)
end if
end if
while j <= count do
delta_r = body[j][POS] - body[i][POS]
sqdist = scalar1(delta_r)
distance = modulus3d(delta_r)
-- Collision detection
if sqdist < power(body[i][RAD]+body[j][RAD],2) then -- collision occured
b = Collide({body[i],body[j]},delta_r,distance)
if length(b) < 2 then
body[j] = body[count]
body[count] = 0
count -= 1
if length(b) = 0 then
body[i] = body[count]
body[count] = 0
count -= 1
-- body = body[1..count]
else
body[i] = b[1]
end if
else
body[i] = b[1]
body[j] = b[2]
if length(b) > 2 then
body = body[1..count]
-- for n = 3 to length(b) do
-- body &= b[n]
-- end for
body &= b[3..$]
-- count = length(body)
end if
count += length(b)-2
end if
else
b = Interact({body[i],body[j]},delta_r,distance)
body[i] = b[1]
body[j] = b[2]
end if
j += 1
end while -- j <= count
i += 1
end while -- i <= count
if DYN_DT then
if count = old_count then
old_count = 0
else
dt *= count*count/old_count/old_count
old_count = 0
end if
end if
t += dt
cycles += 1
time2 = time()
if time2-time1 >= 0.5 then
cycles *= 2
dos_rescue:position(4,1)
-- dos_rescue:printf(1,"%-4d cycles per second\n",cycles)
if dt*cycles >= year then
dos_rescue:printf(1,"%6.2f years per second \n",dt*cycles/year)
elsif dt*cycles >= day then
dos_rescue:printf(1,"%6.2f days per second \n",dt*cycles/day)
elsif dt*cycles >= hour then
dos_rescue:printf(1,"%6.2f hours per second \n",dt*cycles/hour)
elsif dt*cycles >= minute then
dos_rescue:printf(1,"%6.2f minutes per second\n",dt*cycles/minute)
else
dos_rescue:printf(1,"%6.2f seconds per second\n",dt*cycles)
end if
cycles = 0
time1 = time()
end if
end if
if follow then
if not(pause or drag) then
follow_coord = body[follow][POS]+body[follow][VEL]*dt
end if
else
follow_coord = 0
end if
for n = 1 to count do
if not(pause or drag) then
body[n][POS] += body[n][VEL]*dt
end if
body[n] = redraw(body[n])
end for
draw_selection(body,10)
dos_rescue:position(1,1)
dos_rescue:printf(1,"%d days %02d:%02d:%04.1f \n",format_time(t))
dos_rescue:printf(1,"%d bodies \n",length(body))
dos_rescue:printf(1,"dt = %g sec \n",dt)
sel = get_selection()
if get_selection() then
dos_rescue:position(6,1)
if atom(body[sel][LABEL]) then
dos_rescue:printf(1,"Selected body: #%-10d\n",{body[sel][LABEL]})
else
dos_rescue:printf(1,"Selected body: %-10s\n",{body[sel][LABEL]})
end if
dos_rescue:printf(1,"Mass: %-16g\n",{body[sel][MAS]})
dos_rescue:printf(1,"Radius: %-16g\n",{body[sel][RAD]})
end if
key = dos_rescue:get_key()
if key = 'z' then
zoom_in()
elsif key = 'x' then
zoom_out()
elsif key = '1' then
zoom_restore() -- zoom 1:1
body = refresh(body)
elsif key = 'f' then -- follow selected body
follow = get_selection()
center = {0,0}
body = refresh(body)
draw_selection(body,10)
elsif key = 'P' then -- print screen
print_screen("astro")
elsif key = 'p' then -- pause
pause = not pause
elsif key = '-' then -- slower
dt /= 2
elsif key = '=' or key = '+' then -- faster, but less precious
dt *= 2
elsif key = 331 then -- Left
center[1] += 50/zoom
elsif key = 333 then -- Right
center[1] -= 50/zoom
elsif key = 328 then -- Up
center[2] += 50/zoom
elsif key = 336 then -- Down
center[2] -= 50/zoom
elsif key = 327 then -- Home
center = {0,0}
elsif key = 9 then -- select next body
select_next(count)
elsif key = 8 then -- select previous body
select_previous(count)
elsif key = 27 then -- deselect
deselect()
elsif key = ')' then -- Shift+'0' -- divide by zero:)
crash_message("Program was crashed successfully!\nSee ex.err for debug information.")
? 1/0
elsif key = 13 then -- next demo
exit
end if
mouse_event = get_mouse()
if sequence(mouse_event) then
if mouse_event[1] = LEFT_DOWN then
drag = 1
dragged = 0
last_mouse_pos = mouse_event[2..3]
elsif mouse_event[1] = MOVE then
if drag then
center += (mouse_event[2..3]-last_mouse_pos)/zoom
last_mouse_pos = mouse_event[2..3]
dragged = 1
end if
elsif mouse_event[1] = LEFT_UP then
if not dragged then
select(mouse_event[2],mouse_event[3],10,body,count)
end if
drag = 0
elsif mouse_event[1] = WHEEL_DOWN then
center += (mouse_event[2..3]-screen_center)/zoom*(ZOOM_FACTOR-1)
zoom_out()
elsif mouse_event[1] = WHEEL_UP then
center -= (mouse_event[2..3]-screen_center)/zoom*(ZOOM_FACTOR-1)
zoom_in()
end if
end if
end while
end procedure
include demos/basdemos.e as bas
procedure main()
bas:All()
end procedure
init_graph()
main()
restore_graph()