- add some moons into the sample code.
- add in some trojans (asteroids)
- add ability to center on a particular body
- optimize (with Critcl perhaps?)
package require math::calculus
# gravitational constant
set G .00067
set pi [expr atan(1)*4]
proc grav {o1 o2} {
global objs G pi
# objects are list: {X Y mass id dx dy color}
# returns a vector representing the force on o1
foreach {o1x o1y o1m} $o1 {o2x o2y o2m} $o2 {break}
set dx [expr {$o2x-$o1x}]
set dy [expr {$o2y-$o1y}]
set r2 [expr {$dx*$dx+$dy*$dy}]
if ($r2==0) {set r2 1e-10}
set F [expr {($G*($o1m*$o2m)/$r2)/$o1m}]
if {$dx == 0} {
if {$dy > 0} {
set theta [expr {$pi/2}]
} else {
set theta [expr {-$pi/2}]
}
} elseif {$dx < 0} {
set theta [expr {$pi+atan($dy/$dx)}]
} else {
set theta [expr {atan($dy/$dx)}]
}
set Fx [expr $F*cos($theta)]
set Fy [expr $F*sin($theta)]
return [list $Fx $Fy]
}
proc sumgrav {o} {
# returns the vector sum of all objects on O
global objs
set fv {}
set mo [lindex $objs $o]
foreach obj $objs {
if {[lindex $mo 3] == [lindex $obj 3]} continue
lappend fv [grav $mo $obj]
}
set fx {}
set fy {}
foreach f $fv {
lappend fx [lindex $f 0]
lappend fy [lindex $f 1]
}
return [list [expr [join $fx +]] [expr [join $fy +]]]
}
proc accelerate {t vec} {
foreach {xv yv xa ya} $vec {break}
return [list $xa $ya 0 0]
}
proc translate {t vec} {
foreach {x y xv yv} $vec {break}
return [list $xv $yv 0 0]
}
set ts 3
set trimdist 5000
set time 0
proc move {} {
global objs ts trimdist time
set oobjs {}
for {set o 0} {$o < [llength $objs]} {incr o} {
set ob [lindex $objs $o]
set tr [sumgrav $o]
# current x, y; mass, name; current x,y velocity;
# x,y acceleration
foreach {cx cy mass name cxv cyv} $ob {xa ya} $tr {break}
set avec [list $cxv $cyv $xa $ya]
set res [math::calculus::rungeKuttaStep $time $ts $avec accelerate]
foreach {nxv nyv xa ya} $res {break}
set tvec [list $cx $cy $nxv $nyv]
set res [math::calculus::rungeKuttaStep $time $ts $tvec translate]
foreach {nx ny nxv nyv} $res {break}
if {sqrt($nx*$nx+$ny*$ny) < $trimdist} {
set ob [concat $nx $ny [lrange $ob 2 3] $nxv $nyv [lrange $ob 6 end]]
lappend oobjs $ob
}
}
set objs $oobjs
}
set scale 0.2
set trails 0
set okeep 1000
proc animate {} {
global trails okeep
if {!$trails} {
.c delete all
} else {
set all [.c find all]
if {[llength $all] > $okeep} {
eval .c delete [lrange $all 0 [expr [llength $all]-1000]]
}
}
global objs scale
set ccx [expr [winfo width .c]/2]
set ccy [expr [winfo height .c]/2]
foreach o $objs {
set cx [expr [lindex $o 0]*$scale+$ccx]
set cy [expr [lindex $o 1]*$scale+$ccy]
set cr [expr [lindex $o 2]*$scale/100000]
.c create oval [expr $cx-$cr] [expr $cy-$cr] [expr $cx+$cr] [expr $cy+$cr] -outline [lindex $o 6]
}
}
set playing 1
set delay 25
proc playing {} {
global delay
move
animate
global playing
if {$playing} {after $delay playing}
}
proc float {number} {
expr {$number+0.0}
}
proc addplanet {name distance mass period color} {
# distance is in 1e6 km
# mass is in 1e24 kg
# period is in earth years
global objs
set speed [expr $distance*1000*2*3.14/($period*365*846)]
lappend objs [list 0 [float $distance] [float $mass] $name $speed 0 $color]
}
proc adddebris {tag} {
global objs
set ob [lindex $objs [lsearch $objs *$tag*]]
foreach {ox oy} $ob {sx sy} [lindex $objs 0] {break}
set d 10
set dx [expr $ox-$sx]
set dy [expr $oy-$sy]
set th [expr atan($dy/($dx+1e-10))]
set dist [expr sqrt($dx*$dx+$dy*$dy)]
for {set c 0} {$c < 5} {incr c} {
lappend objs [list [expr cos($th+1.047)*$dist+$d*rand()] \
[expr sin($th+1.047)*$dist+$d*rand()] 1e-10 trojan 0 0 gray]
lappend objs [list [expr cos($th-1.047)*$dist+$d*rand()] \
[expr sin($th-1.047)*$dist+$d*rand()] 1e-10 trojan 0 0 gray]
}
}
proc sysmom {} {
global objs
set mx 0
set my 0
foreach o $objs {
set mx [expr $mx + [lindex $o 2]*[lindex $o 4]]
set my [expr $my + [lindex $o 2]*[lindex $o 5]]
}
list $mx $my
}
proc recenter {} {
global objs
set sun [lindex $objs 0]
set sx [lindex $sun 0]
set sy [lindex $sun 1]
set nobjs {}
foreach o $objs {
lappend nobjs [concat [expr [lindex $o 0]-$sx] [expr [lindex $o 1]-$sy] [lrange $o 2 end]]
}
set objs $nobjs
}
proc setup {} {
canvas .c -background black -width 400 -height 400
frame .panel
pack .c -expand t -fill both
pack .panel
# controls
label .zooml -text "Zoom"
scale .zoom -resolution .005 -orient horizontal \
-from .001 -to 2 -variable scale
grid .zooml .zoom -in .panel -sticky s
label .speedl -text "Delay"
scale .speed -resolution 1 -orient horizontal -from 1 -to 100 -variable delay
grid .speedl .speed -in .panel -sticky s
label .timel -text "Time step"
scale .time -resolution 0.5 -orient horizontal -from 0.5 -to 50 -variable ts
grid .timel .time -in .panel -sticky s
label .playl -text "Animate"
checkbutton .play -command playing -variable playing
grid .playl .play -in .panel
label .traill -text "Show Trails"
checkbutton .trail -variable trails
grid .traill .trail -in .panel
button .clear -command ".c delete all" -text Clear
button .center -command "recenter" -text Recenter
grid .clear .center -in .panel
}
# solar system
# the strange number for the sun's velocity is to try to keep the overall
# momentum of the system to zero
set objs { {0 0 1989000 sun -0.00163355465017 0 yellow} }
addplanet mercury 57.9 .33 .23 red
addplanet venus 108.2 4.86 .613 green
addplanet earth 149 5.97 1 blue
addplanet mars 227 .64 2 red
addplanet jupiter 778 1900.0 11.868 green
addplanet saturn 1429 568.0 29.47 yellow
addplanet uranus 2870 86.8 84.06 blue
addplanet neptune 4504 102.47 164.90 green
addplanet pluto 5913 .012 248.08 grey
setup
playingschlenk Hmm, may i suggest OpenGL support for 3D?
TV Nice, though a bit depressing after a while.. It could be a good idea to have a course screen resolution as a test, I had that when I was a teenager doing this sort of thing, first order Euler or not (or backward, with prediction) then it is clear where roundoffs come from, or (energy) compensate by.Consodering the attraction falls of with a square as a function of distance, it can be considered that using complicated or higher order approximations (unless they are for this purpose) may leave you with more stable (not usually the case with higher order polynomials, though 4 is not that high) or more accurate solutions, they also may not take non-linearity better into account than a lower order solution with a smaller timestep, and in extreme cases, like when planets sheer just past eachother, may be wrong.I didn't look at the code, but would it be doable to make longer, and maybe fading trails ?JR - longer trails is a cinch, just change the value of "okeep". Increasing it too much will eventually bog down the canvas widget tho. Fading trails would be somewhat more work and significantly slower (it would need to run through the whole list of objects on the canvas with each or every few passes)GWM If you need more accuracy you could try the Runge-Kutta method.
See also TclPlanets

