Moving dots

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?).

 # 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}]
 }

Theo Verelst Well well, contractive difference equations, it would be good to take the accompanying differential equations, which for as it seems here limited order linear problems (oops, maybe I didn't see right, is the distance linear?) would seems solvable. Maybe even automatically. I remember a big book with diff. eq. that definitely contained images of this kind.