[Arjen Markus] (3 june 2003) During the 4th European Tclers' Meeting, several of us came to speak about applying my idea of [Programmed story telling] to make a tool for children to learn how to program. Richard Suchenwirth suggested using a puppy that you give commands to. Charming an idea as this is, I thought of a simpler approach first (doing the graphics and building in the social behaviour takes some effort, and I am one for fast results nowadays :). Why not define a set of commands for points or dots chasing each other. With a few commands you get interesting curves. In the example below: * A follows a straight line and B follows A at a right angle * C1 to C4 start off at a square and follow each other to give a spiralling motion Technical notes: * If you have a dot with an autonomous movement, the speed may influence the resulting curve for a following dot. * The speed at which dots follow each other is proportional to their distance. This makes the differential equations that are being solved linear. A variation would be to make the speed constant and have only the direction depend on the vector between the two dots. * There is no error checking (fast results, remember?) ---- C # movedots.tcl -- # Define moving dots that can trace each other # package require Tk # dot -- # Define a dot (give it a name) # Arguments: # name Name of the dot (new command) # x X-coordinate at start # y Y-coordinate at start # colour Colour of the line to draw # Result: # None # Side effect: # Command "name" will be created # proc dot {name x y colour} { variable all_dots variable dot_prop lappend all_dots $name set dot_prop($name,x) $x set dot_prop($name,y) $y set dot_prop($name,colour) $colour interp alias {} $name {} DotImpl $name } # DotImpl -- # Handle the commands for a dot (motion etc.) # Arguments: # name Name of the dot # subcomm Subcommand # args Any arguments # Result: # None # Side effect: # Whatever the subcommand means # proc DotImpl {name subcomm args} { variable all_dots variable dot_prop switch -- $subcomm { "moves" { set dot_prop($name,movex) [lindex $args 0] set dot_prop($name,movey) [lindex $args 1] set dot_prop($name,move) "move" } "follows" { set dot_prop($name,follows) [lindex $args 0] set dot_prop($name,move) "follows" set dot_prop($name,angle) 0.0 if { [llength $args] == 3 } { set dot_prop($name,angle) [expr {[lindex $args 2]/180.0*3.1415926}] } } "draw" { DotDraw $name } "update" { set dot_prop($name,x) $dot_prop($name,xn) set dot_prop($name,y) $dot_prop($name,yn) } default { # Ingnore } } } # DotDraw -- # Draw the (new) position of the dot # Arguments: # name Name of the dot (new command) # Result: # None # Side effect: # Line segment drawn, position updated # proc DotDraw {name} { variable all_dots variable dot_prop variable t variable dt if { $dot_prop($name,move) == "move" } { set x [expr $dot_prop($name,movex)] set y [expr $dot_prop($name,movey)] } else { set target $dot_prop($name,follows) set angle $dot_prop($name,angle) set x $dot_prop($name,x) set y $dot_prop($name,y) ;# Updates should be delayed! set tx $dot_prop($target,x) set ty $dot_prop($target,y) set vx [expr {$tx-$x}] set vy [expr {$ty-$y}] set dx [expr {$dt*(cos($angle)*$vx-sin($angle)*$vy)}] set dy [expr {$dt*(sin($angle)*$vx+cos($angle)*$vy)}] set x [expr {$x+$dx}] set y [expr {$y+$dy}] } # Updates should be delayed!! .c create line $dot_prop($name,x) $dot_prop($name,y) $x $y -fill $dot_prop($name,colour) set dot_prop($name,xn) $x set dot_prop($name,yn) $y } # main -- # Main code: # - Create the canvas # - Define some dots # - Draw the tracks they produce # canvas .c -background white -width 500 -height 500 pack .c -fill both set t 0 set dt 0.02 set nosteps 1000 #dot A 450.0 250.0 blue #dot B 470.0 250.0 green #A moves {250.0+200.0*cos($t)} {250.0+200.0*sin($t)} dot A 0.0 200.0 blue dot B 0.0 230.0 green A moves {30*$t} 200.0 B follows A -angle -90 dot C1 400.0 0.0 blue dot C2 400.0 400.0 green dot C3 0.0 400.0 red dot C4 0.0 0.0 magenta C1 follows C2 -angle -10 C2 follows C3 -angle -10 C3 follows C4 -angle -10 C4 follows C1 -angle -10 for { set i 0 } { $i < $nosteps } { incr i } { foreach dot $all_dots { $dot draw } foreach dot $all_dots { $dot update } set t [expr {$t+$dt}] }