if 0 {Richard Suchenwirth 2003-07-13 - As you may have read, I'm not afraid of anything, in Tcl coding at least. Another extended weekend, and I started implementing a thing I long wanted to do: flight simulation in Tcl, and on the iPaq for that (but it should run on any other box too). I'm fully aware this can never compete with professional products, but it may be educational for understanding how things work.
Let's see what properties an airplane might need. First of all, where are we, in 3D space?
Then our plane has an orientation:
...and it moves in space:
So our model has 10 parameters, between which many dependencies exist. For controllers, we have a gas throttle, done as a number 0..9 to be tapped on screen, and the cursor keys for rudders, mimicking the control stick:
Views upon our model: besides numeric display of parameters, I added a very simple "attitude indicator" for visual feedback of horizontal and vertical angles. No objects to be seen on the ground, but at least you can pass through a layer of clouds at 500 ft ;-) The blue line at left is an experiment to display three parameters in a single item: its height represents altitude, its length horizontal speed, while the slope indicates vertical angle. If speed is too low or too high, it turns a warning red. The arrow at bottom right indicates compass heading. }
package require Tk proc main {} { wm geometry . +0+0 pack [canvas .c -bg lightblue] set ::g(canvas) .c .c create window 15 10 -window [button .r -text Reset -command reset] .c create window 230 10 -window [button .x -text X -command exit] .c create poly 0 100 240 100 240 200 0 200 -fill green -tag ground .c create line 110 100 130 100 .c create line 120 95 120 105 .c create line 0 190 2 190 -fill blue -tags multi -arrow last .c create text 225 175 -tag cname .c create line 225 185 225 195 -arrow first -arrowshape {3 5 3} -tag compass .c create rect 0 200 240 300 -fill grey -tag dashboard .c create rect 15 205 25 215 -fill yellow -outline yellow -tag hilite foreach i {0 1 2 3 4 5 6 7 8 9} { .c create text [expr $i*20+20] 210 -text $i -tag gas$i .c bind gas$i <1> "set g(gas) $i" } trace add variable ::g(gas) write {.c coords hilite [.c bbox gas$::g(gas)] ;#} set x 10; set y 230 foreach i {lat lon alt head hor ver fuel spd vspd} { .c create text $x $y -text $i -anchor w incr x 50 ctextvar .c $x $y g($i) if {[incr x 20]>180} {set x 10 ; incr y 13} } bind . <Up> {incr g(hor) -1} bind . <Down> {incr g(hor) 1} bind . <Left> {incr g(ver) -1} bind . <Right> {incr g(ver) 1} reset } #-- canvas text variable, auto-update proc ctextvar {w x y var} { $w create text $x $y -tag var$var trace add variable ::$var write "$w itemconfig var$var -text \$::$var ;#" } proc reset {} { foreach event [after info] {after cancel $event} array set ::g { lat 0 lon 0 alt 0 head 0 hor 0 ver 0 gas 0 spd 0 vspd 0 fuel 5000 } every 250 {recompute; redraw $::g(canvas)} }
if 0 {The behavior of the plane is modelled by recomputing parameters from other parameters - please improve if you know better! Some important points:
}
proc recompute {} { global g if {$g(gas)>4||$g(alt)} { set g(spd) [expr {round($g(spd)+$g(gas)-4-$g(hor)*.5 - abs($g(ver))*.1)}] } if {$g(alt)>0 && $g(spd)<50} { tk_messageBox -message STALL set g(hor) -30 } set g(fuel) [expr {$g(fuel)-$g(gas)/2}] if {$g(fuel)<=0} {set g(fuel) 0; set g(gas) 0} if {$g(spd)>250} {set g(gas) 0} set g(vspd) $g(hor) incr g(alt) $g(vspd) if {$g(alt)<1 && $g(vspd)} land set g(head) [format %.1f [expr {fmod($g(head)+$g(ver)*.1+360,360)}]] set a [expr {($g(head)-90)*acos(-1)/180}] set g(lat) [format %.3f [expr {$g(lat)+cos($a)*$g(spd)*.0001}]] set g(lon) [format %.3f [expr {$g(lon)+sin($a)*$g(spd)*.00005}]] } proc land {} { global g foreach event [after info] {after cancel $event} if {$g(vspd)>-2} { set t "Smooth landing - congrats!" } elseif {$g(vspd)>-8} { set t "Rough landing - plane may need repair" } else { set t "CRASH!!!\nRelatives will be notified" crash } set dist [expr {hypot($g(lat),$g(lon))*10}] append t "\n[format %.1f $dist] miles off target" tk_messageBox -message $t reset } proc crash {} { set c $::g(canvas) $c config -bg red $c itemconfig ground -fill red } proc redraw w { global g set y [expr {100+$g(hor)*3}] set y1 [expr {$y+$g(ver)*3}] set y2 [expr {$y-$g(ver)*3}] $w coords ground 0 $y1 240 $y2 240 200 0 200 if {$g(alt)<500} { $w config -bg lightblue $w itemconfig ground -fill green } elseif {$g(alt)<550} { $w config -bg white $w itemconfig ground -fill white } else { $w config -bg lightblue $w itemconfig ground -fill white } set x1 [expr {5+$g(spd)/2}] set y0 [expr {195-$g(alt)/10}] set y1 [expr {$y0-$g(vspd)*2}] $w coords multi 0 $y0 $x1 $y1 set a [expr {($g(head)+90)*acos(-1)/180.}] set x [expr {cos($a)*6}] set y [expr {sin($a)*6}] $w coords compass [expr {225-$x}] [expr {190-$y}] [expr {225+$x}] [expr {190+$y}] $w itemconfig multi -fill [expr {$g(spd)>60 && $g(spd)<230? "blue": "red"}] $w itemconfig cname -text [compass'name $g(head)] } proc compass'name hdg { lindex {N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW N} [expr {round($hdg/360.*16.)}] } proc every {ms body} {eval $body; after $ms [info level 0]} main
if 0 {Finally, some hints for a successful flight, starting from take-off:
Have a good flight! }
PocketPC | Arts and crafts of Tcl-Tk programming Category Games