[Arjen Markus] (23 october 2002) As a sequel to the page [Creating an animated display] here is a script that incorporates two separate displays working in the same framework. I added a little user-interface to make it work smoother. ---- # animdemo.tcl -- # A few simple animated displays # # # Animation -- # Namespace to hold all (specific) information # namespace eval ::Animation:: { variable xmouse 0 ;# Make sure they have a value variable ymouse 0 } # StoreMousePosition -- # Store the coordinates of the mouse pointer for later use # # Arguments: # xp X-coordinate of the mouse # yp Y-coordinate of the mouse # # Result: # None # proc ::Animation::StoreMousePosition {xp yp} { variable xmouse variable ymouse set xmouse $xp set ymouse $yp } # drawBall -- # Draw a red circle at a certain height above the green ground # # Arguments: # time Time parameter, used to calculate the actual height # # Result: # None # # Note: # Assume a perfectly elastic collision. The time parameter must # be reduced to the time since the last collision. # # Technique used: redraw the entire picture # proc ::Animation::drawBall {time} { global accel global velo0 global cnv_height global cnv_width set period [expr {2.0*$velo0/$accel}] set time2 [expr {$time - $period * int($time/$period)}] set grass_height 20 set radius 7 set ball_height [expr {$velo0*$time2-0.5*$accel*$time2*$time2}] set pix_height [expr { $cnv_height-$grass_height - $radius - $ball_height}] set xl [expr {0.5*$cnv_width-$radius}] set xr [expr {0.5*$cnv_width+$radius}] set yb [expr {int($pix_height)-$radius}] set yt [expr {int($pix_height)+$radius}] .cnv delete all .cnv create rectangle 0 $cnv_height $cnv_width \ [expr {$cnv_height-$grass_height}] -fill green -outline green .cnv create oval $xl $yb $xr $yt -fill red -outline black } # drawCompassNeedles -- # Draw a set of compass needles that orient themselves to the # current mouse position # # Arguments: # time Time parameter, ignored # # Result: # None # # Note: # The mouse position is stored via the binding to the mouse event # motion. We use only this information to create a new display. # # Technique used: redraw the picture proc ::Animation::drawCompassNeedles {time} { variable xmouse variable ymouse set hlength 14 set hwidth 7 .cnv delete all foreach y {10 50 90 130 170 210 250 290} { foreach x {10 50 90 130 170 210 250 290 330 370} { set dx [expr {$xmouse-$x}] set dy [expr {$ymouse-$y}] if { $dx != 0 || $dy != 0 } { set angle [expr {atan2($dy,$dx)}] } else { set angle 0 } set cosa [expr {cos($angle)}] set sina [expr {sin($angle)}] set x1 [expr {$x+$hlength*$cosa}] set y1 [expr {$y+$hlength*$sina}] set x2 [expr {$x+$hwidth*$sina}] set y2 [expr {$y-$hwidth*$cosa}] set x3 [expr {$x-$hlength*$cosa}] set y3 [expr {$y-$hlength*$sina}] set x4 [expr {$x-$hwidth*$sina}] set y4 [expr {$y+$hwidth*$cosa}] .cnv create polygon $x1 $y1 $x2 $y2 $x4 $y4 \ -fill red -outline black .cnv create polygon $x3 $y3 $x2 $y2 $x4 $y4 \ -fill blue -outline black } } } # nextPicture -- # Prepare to call the next picture, stop after some predefined # number of steps. # # Arguments: # step Step number (converted to time) # # Result: # None # proc nextPicture {step method} { global time_delay global max_steps global stop_anim # # Draw the picture # $method [expr {0.1*$step}] # # Set up the next picture via the [after] command # if { $step < $max_steps && $stop_anim != 1} { incr step after $time_delay [list nextPicture $step $method] } } # stopPicture -- # Stop the animation # # Arguments: # None # # Result: # None # # Side effect: # Sets the variable "stop_anim" to gracefully stop the animation # proc stopPicture {} { global stop_anim set stop_anim 1 } # main -- # Set up the canvas, start the loop # global cnv_width global cnv_height global velo0 global accel # # Canvas size # set cnv_width 400 set cnv_height 300 # # Time delay and maximum duration (steps) # set time_delay 100 ;# Time in ms between pictures set max_steps 1000 ;# Maximum number of steps # # Private variable to stop the animation if wanted # set stop_anim 0 # # Needed for drawBall # set velo0 70.0 ;# m/s set accel 10.0 ;# m/s2 ;# pixels become m that way :) # # Set up the canvas and the buttons # canvas .cnv -width $cnv_width -height $cnv_height -background white frame .frm1 radiobutton .frm1.ball -text "Bouncing ball" \ -variable method -value "::Animation::drawBall" \ -command {stopPicture} radiobutton .frm1.compass -text "Compass needles (following mouse)" \ -variable method -value "::Animation::drawCompassNeedles" \ -command {stopPicture} pack .frm1.ball .frm1.compass -side left frame .frm2 button .frm2.start -text "Start" \ -command {set stop_anim 0; nextPicture 0 $method} button .frm2.stop -text "Stop" -command {stopPicture} pack .frm2.start .frm2.stop -side left pack .frm2 -side bottom -fill x pack .frm1 -side bottom -fill x pack .cnv -fill both bind .cnv {::Animation::StoreMousePosition %x %y} set method ::Animation::drawBall $method 0 # # If you want the animation to start rightaway ... # #nextPicture 0 $method ---- [[ [Arts and cratfs of Tcl-Tk programming] | [Category graphics] ]]