Version 0 of Moving objects - first steps with TclOO

Updated 2011-01-19 08:32:02 by arjen

Arjen Markus (19 january 2011) My first steps with TclOO involve simulating a flock of birds ... well, that is how the idea started: I watched several flocks of geese pass overhead in the characteristic V-shape and thought that this flying pattern might make a nice little toy application. The implementation below is only a shadow of such an application as the objects simply follow the leader, whereas in a flock the leading bird sometimes let itself fall behind and another take the lead. Not to mention the joining and forking that also occurs. But such dynamics asked too much of the limited time I had set myself.

This is also the reason for the ugly copying of a piece of code - the initial positioning of a new object wrt the one it will follow.

Anyway, have a look at this single file of objects.

# movingobjects.tcl --
#     Simple experiment with TclOO:
#     Objects moving in a canvas relative to each other
#

pack [canvas .c -width 400 -height 400]

# Auxiliary procedures
#
proc position {time} {
    set xpos  [expr {cos($time) - cos(sqrt(2.0)*$time)}]
    set ypos  [expr {sin($time) - sin(sqrt(2.0)*$time)}]
    set time  [expr {$time + 0.001}]
    set xposn [expr {cos($time) - cos(sqrt(2.0)*$time)}]
    set yposn [expr {sin($time) - sin(sqrt(2.0)*$time)}]

    set dx    [expr {$xposn-$xpos}]
    set dy    [expr {$yposn-$ypos}]
    set dir   [expr {atan2($dy,$dx)}]

    return [list $xpos $ypos $dir]
}

#
# Create the class
#
oo::class create mbody {
    variable isLeading
    variable xpos
    variable ypos
    variable dir
    variable time
    variable cnvobj
    variable leader

    constructor {leading} {
        variable time
        variable xpos
        variable ypos
        variable dir
        variable lead
        variable leader
        variable cnvobj

        set time 0.0

        set xpos 0.0
        set ypos 0.0
        set dir  0.0
        set leader {}

        if { $leading } {
            set cnvobj [.c create oval 4 4 -4 -4 -fill red]
            .c move $cnvobj 200 200
            set lead 1
            my move
        } else {
            set cnvobj [.c create oval 4 4 -4 -4 -fill blue]
            set lead 0
            .c move $cnvobj 200 200
            my follow
        }
    }

    method move {} {
        variable time
        variable xpos
        variable ypos
        variable dir
        variable cnvobj
        variable lead
        variable leader

        set xposo $xpos
        set yposo $ypos

        lassign [position $time] xpos ypos dir

        .c move $cnvobj [expr {($xpos-$xposo) * 80}] [expr {($ypos-$yposo) * 80}]

        set time [expr {$time + 0.03}]

        after 100 [list [self] move]
    }

    method follows {object} {
        variable leader
        variable xpos
        variable ypos
        variable cnvobj

        set leader $object

        lassign [$leader position] xposf yposf dirf

        set xoff  [expr {-0.1*cos($dirf)}]
        set yoff  [expr {0.1*sin($dirf)}]

        set dx    [expr {($xposf + $xoff - $xpos)}]
        set dy    [expr {($yposf + $yoff - $ypos)}]

        set xpos  [expr {$xpos + $dx}]
        set ypos  [expr {$ypos + $dy}]
        set dir   [expr {atan2($dy,$dx)}]

        .c move $cnvobj [expr {$dx * 80}] [expr {$dy * 80}]
    }

    method position {} {
        variable xpos
        variable ypos
        variable dir

        return [list $xpos $ypos $dir]
    }

    method follow {} {
        variable time
        variable xpos
        variable ypos
        variable dir
        variable cnvobj
        variable lead
        variable leader

        if { $leader != {} } {

            lassign [$leader position] xposf yposf dirf

            set xoff  [expr {-0.1*cos($dirf)}]
            set yoff  [expr {0.1*sin($dirf)}]

            set dx    [expr {0.2 * ($xposf + $xoff - $xpos)}]
            set dy    [expr {0.2 * ($yposf + $yoff - $ypos)}]

            set xpos  [expr {$xpos + $dx}]
            set ypos  [expr {$ypos + $dy}]
            set dir   [expr {atan2($dy,$dx)}]

            .c move $cnvobj [expr {$dx * 80}] [expr {$dy * 80}]

            set time [expr {$time + 0.03}]
        }

        after 100 [list [self] follow]
    }
}

#
# Now create a leading object and a buch that will follow it 
# and each other. The Tk event loop does the rest.
#
set mainobj  [mbody new 1]

set leader $mainobj
for { set i 0 } { $i < 10 } { incr i } {
    set follower [mbody new 0]
    $follower follows $leader
    set leader $follower
}