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 than2 / 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, namedn.gif
,ne.gif
,e.gif
,se.gif
,s.gif
,sw.gif
,w.gif
andnw.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 keystotal
,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
andright_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 asget_boundries
andget_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 scripteg/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
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.