The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Tk::Taxis - Perl extension for simulating biological taxes

SYNOPSIS

  use Tk::Taxis;
  my $taxis = $mw->Taxis( -width => 200, -height => 100 )->pack();
  $taxis->configure( -population => 20 );
  $taxis->taxis() while 1;

ABSTRACT

Simulates the biological movement called taxis

DESCRIPTION

Organisms such as bacteria respond to gradients in chemicals, light, etc, by a process called taxis ('movement'). This module captures some of the spirit of this model of organismal movement. Bacteria are unable to measure differential gradients of chemicals along the length of their cells. Instead, they measure the concentration at a given point, move a little, measure it again, then if they find they are running up a favourable concentration gradient, they reduce their tumbling frequency (the probability that they will randomly change direction). In this way, they effect a random walk that is biased up the gradient.

METHODS

Tk::Taxis is a composite widget, so to invoke a new instance, you need to call it in the usual way...

  my $taxis = $mw->Taxis( -option => value )->pack();
  $taxis->configure ( -option => value );
  my $number = $taxis->cget( -option );

or similar. This widget is based on Frame and implements a Canvas. Configurable options are mostly forwarded to the Canvas subwidget, which be directly accessed by the Subwidget('canvas') method. Options specific to the Tk::Taxis widget are listed below. If you try to pass in values too low or high (as specified below), the module will warn and set a default minimum or maximum instead. These options can be set in the constructor, and get/set by the standard cget and configure methods.

  • -width

    Sets the width of the taxis arena in pixels. Defaults to 400 pixels.

  • -height

    Sets the height of the taxis arena. You are advised to set the height and width when constructing the widget, rather than configuring them after the event, as this will result in repeated redrawings of the canvas. Defaults to 400 pixels.

  • -tumble

    This sets the default tumble frequency, i.e. the tumble frequency when the critters are moving down the concentration gradient. Values less than 0 or more than 1 will be truncated to 0 or 1. Defaults to 0.03.

  • -speed

    This sets the speed of the critters. When the critters are moved, the run length is essentially set to rand( diagonal_of_canvas ) * speed * cos rotation. If there is no rotation, the maximum run length will be simply be the diagonal of the canvas multiplied by the speed. If you try to set a speed lower than 2 / diagonal_of_canvas, it will be ignored, and this minimum value will be used instead, otherwise your critters, moving a fractional number of pixels, will sit there and spin like tops. Defaults to 0.006.

  • -images

    This takes a string argument which is the path to a directory containing images to display as critters. If this begins with an @ sign, this will be taken to be a real path. Otherwise, it will be taken to be a default image set. This may currently be 'woodlice' or 'bacteria' (these images are located in directories of the same name in @INC/Tk/Taxis/images/). There must be eight images, named n.gif, ne.gif, e.gif, se.gif, s.gif, sw.gif, w.gif and nw.gif, each showing the critter in a different orientation (n being vertical, e being pointing to the right, etc). These images should all have the same dimensions. Defaults to 'woodlice'.

  • -population

    This takes an integer argument to configure the size of the population. If cget( -population ) is called, the return value depends on context: the total population is returned in scalar context, but in list context, a hash is returned, with keys total, top_left, top, left, bottom_right, etc, indicating the number of critters in each quadrant, half and in total. (This is a slight change from the version 1 API, where left and right counts were returned in list context). Defaults to 20.

  • -fill

  • -fill => 'red'

  • -fill => [ 'red', 'blue' ]

  • -fill => [ [ 'red', 'blue' ], [ 'red', 'blue' ] ]

    This takes arguments to set the fill colour of the quadrants of the arena. The arguments should be standard Tk colour strings, e.g. "red" or "#00FF44". If the argument is a single string, this will be used to fill the whole arena; if an arrayref, the fills will be applied to the left and right repectively; if an arrayref of arrayrefs, the fills will be applied thusly...

          $taxis->configure( -fill => [ 
              [ 'top_left_colour',    'top_right_colour' ],
              [ 'bottom_left_colour', 'bottom_right_colour' ]
          ] ); 

    The left_fill and right_fill methods are available for backwards compatibility with the version 1 API, but they are deprecated.

  • -preference

  • -preference => 100

  • -preference => [ 100, -100 ]

  • -preference => [ $preference_for_right, $preference_for_bottom ]

    This takes arguments indicating the preference the critters have for the right hand side and bottom of the taxis arena. If the argument is a single integer, this indicates a left/right preference and the top/bottom preference will be set to indifference. An arrayref argument can be used to set both left/right and top/bottom preference. The arguments must have an absolute value greater than 1 (values less than this will be reset to 1), but a negative sign can be used to indicate a preference for the top and/or left rather than the bottom and/or right.

    When the critters are moving up the left/right gradient, the probability of a tumble will be reduced. This is achieved by dividing the default tumble by the left/right preference value. Further division by the top/bottom preference value will be carried out independently if a critter is also moving up the top/bottom gradient. Absolute values of 1 will therefore yield indifference.

    Returns an arrayref of the preference values.

  • -calculation

    This takes a coderef argument that determines the value of the top/bottom and left/right gradients. The coderef will not be sanity checked, but must behave as if it were a method of a Tk::Taxis::Critter object. When the coderef is invoked it will be given a critter object as its only argument, allowing the code access to the methods in that class, such as get_boundries and get_pos (see Tk::Taxis::Critter). The coderef must return the x and y gradient values. By default the calculation coderef is...

        sub 
        {
            my ( $critter ) = @_;
            my %boundries   = $critter->get_boundries();
            my ( $x, $y )   = $critter->get_pos();
            return
                $x / $boundries{ width  },  # x gradient value
                $y / $boundries{ height };  # y gradient value
        };

These options can also all be called as similarly named methods. There are also two additional public methods...

  • taxis

    This executes one cycle of the taxis simulation. Embed calls to this in a while loop to run the simulation. See the script eg/woodlice.pl for an example of how to embed an interruptable loop within a handrolled main-loop, or CAVEATS below.

  • refresh

    This refreshes the taxis arena, resizing and recolouring as necessary.

Two final methods available are image_height and image_width, which get/set the height and width of the image used as a critter. It is inadvisable to set these, but the Tk::Taxis::Critter class requires read-only access to them.

CAVEATS

Those used to writing...

  MainLoop();

in every Tk script should note that because the simulation requires its own event loop within the event loop of the main program, this will not work out of the box. The best solution I have found is this...

  # import some semantics 
  use Tk qw( DoOneEvent DONT_WAIT );
  use Tk::Taxis;
  use Time::HiRes;
  my $mw = new MainWindow;
  my $taxis = $mw->Taxis()->pack();

  # this tells us whether we are running the simulation or not
  my $running = 1;
  
  # minimum refresh rate
  my $refresh = 0.02; # 20 ms
  
  # home-rolled event loop
  while ( 1 )
  {
    my $finish = $refresh + Time::HiRes::time;
    $taxis->taxis() if $running;
    while ( Time::HiRes::time < $finish  )
      # take up some slack time if the loop executes too quickly
    { 
      DoOneEvent( DONT_WAIT );
    }
  }
  
  # arrange for a start/stop Button or similar to invoke this callback
  sub start_toggle
  {
        $running = $running ? 0 : 1;
  }

As every call to taxis involves iterating over the entire population, when that population is small, the iterations occur more quickly than when the population is large. This event loop ensures that small populations do not whizz around madly (as would be the case if we used a simple while loop), whilst ensuring that large populations do not cause the script to hang in deep recursion (as would be the case if we used a timed repeat callback and a default MainLoop()).

SEE ALSO

Tk::Taxis::Critter

AUTHOR

Steve Cook, <steve@steve.gb.com>

COPYRIGHT AND LICENSE

Copyright 2005 by Steve Cook

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.