- Redrawing the individual pictures is fast enough, so anybody can run the script directly and watch the movie.
- Redrawing them takes either too much time or we need a compact form in which to distribute the result (say as an animated GIF file).
- In a procedure, draw the picture.
- When it completes, change the "time" parameter, use the after command to wait a short interval and then restart the procedure.
- When the picture is finished, save the picture in a bitmap file
- When the animation is complete and we have all pictures stored on disk, use some tool to create the animation file.
exec xwd -id [winfo id $canvas] -out [format "anim%04d.xwd" $count]where "canvas" holds the name of the canvas we draw in and "count" is a counter for the picture. By formatting the names of the bitmap files in four figures, anim0000.xwd, anim0001.xwd, ..., the conversion to an animated GIF file via ImageMagick's convert becomes a single instruction:In the shell, type:
> convert anim*.xwd my_anim.gif(convert will convert all files to GIF files - this is based on the extensions - and as there is more than one input file, it is going to be an animated GIF file.)The script below shows an animation of a bouncing ball. Nothing particularly interesting, but it illustrates the ideas. package require Tk
# 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.
#
proc 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
}
# 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} {
# Here you can insert the code to save the current picture
#
# exec xwd -id [winfo id .cnv] -out [format "anim%04d.xwd" $step]
#
#
# Draw the picture
#
drawBall [expr {0.1*$step}]
#
# Set up the next picture via the [after] command
#
if { $step < 1000 } {
incr step
after 100 [list nextPicture $step]
}
}
# main --
# Set up the canvas, start the loop
#
global cnv_width
global cnv_height
global velo0
global accel
set cnv_width 400
set cnv_height 300
set velo0 70.0 ;# m/s
set accel 10.0 ;# m/s2
;# pixels become m that way :)
canvas .cnv -width $cnv_width -height $cnv_height -background white
pack .cnv -fill both
drawBall 0
#
# Wait until the dust settles - important for saving the frames
# on a slow system
#
tkwait visibility .
nextPicture 0See also: Creating an animated display, part 2

