OK, nu de eigenlijke graphics zoals ze door de schildpad zullen worden
getekend. Ook hier weer eerst het aanmaken van een subwindow, dit keer van
type 'Canvas' (schildersdoek):
my ($xsize, $ysize) = (640, 440); # +/- 200 plus borders
# create canvas for drawings
$canvas = $top->Canvas(-width => $xsize, -height => $ysize, -border => 1);
$canvas->configure(-relief => 'ridge', -background => 'white');
$canvas->pack();
Wat variabelen en hulproutines en zo die later het werk wat makkelijker
maken: 'dsin' en 'dcos' werken in graden in plaats
van radialen, wat wat makkelijker denkt. 'xt' en
'yt' vertalen de coördinaten gezien vanuit de schildpad
(0,0 in het midden, positief is naar boven/rechts) naar de
schermcoördinaten (0,0 in linkerbovenhoek).
use constant PI => 3.14159265358979323;
my $color = "red"; # initial drawing color is red
my $width = 1; # initial line width is 1
my $dir = 0; # initial direction upwards on screen
sub degrees { return $_[0] * PI / 180; }
sub dsin { return sin($_[0] * PI / 180); }
sub dcos { return cos($_[0] * PI / 180); }
sub xt { return $_[0] + $xsize/2; }
sub yt { return $ysize/2 - $_[0]; }
Het wissen van de schildpad op de oude positie en het
opnieuw tekenen op de nieuwe positie is beschreven in de routine
'turtle'. Merk op dat een canvas geen bitmap is, maar een
vector-gebaseerd object, we hoeven dus niet bang te zijn voor witte gaten als
we de oude schildpad wissen, alle gedeeltelijk gewiste objecten worden
automatisch weer netjes volledig getekend. Wissen gaat dus ook niet door er
met witte verf overheen te schilderen, maar door het object te verwijderen
(indien aanwezig). De schildpad bestaat uit twee lijnen en twee rondjes (met
verschil in lijnkleur en vulkleur), waarbij helaas wat lastige formules
gebruikt worden om de schildpad in de goede richting te laten
kijken.......
Omdat het maken van sommige tekeningen heel lang kunnen duren (denk aan
een boom van Pythagoras met diepte 20), check ik ook nog of de gebruiker op
'Stop' heeft gedrukt (zie de knoppenbalk-definitie: de variabele $stop wordt
dan gezet), en genereer dan een foutmelding die netjes wordt opgevangen in de
eerder besproken 'execline' routine.....
# redraw turtle at current pos
sub turtle
{
$canvas->delete($t1) if defined $t1;
$canvas->delete($t2) if defined $t2;
$canvas->delete($t3) if defined $t3;
$canvas->delete($t4) if defined $t4;
$t1 = $canvas->createLine( xt($xpos+15*dcos($dir+45)), yt($ypos+15*dsin($dir+45)),
xt($xpos-15*dcos($dir+45)), yt($ypos-15*dsin($dir+45)), -fill => "dark green",
-width => 2);
$t2 = $canvas->createLine( xt($xpos+15*dcos($dir-45)), yt($ypos+15*dsin($dir-45)),
xt($xpos-15*dcos($dir-45)), yt($ypos-15*dsin($dir-45)), -fill => "dark green",
-width => 2);
$t3 = $canvas->createOval( xt($xpos+10), yt($ypos+10), xt($xpos-10), yt($ypos-10),
-fill => "green", -outline => "dark green", -width => 2);
$t4 = $canvas->createOval( xt($xpos+12*dcos($dir)+4), yt($ypos+12*dsin($dir)+4),
xt($xpos+12*dcos($dir)-4), yt($ypos+12*dsin($dir)-4),
-fill => "green", -outline => "dark green", -width => 2);
if ($stop) { $warn = ""; die "Stop" } # halted by user
return "";
}
Hoe laten we de schildpad nu een lijn tekenen? De gebruikersopdracht
hiervoor is 'vooruit', intern vertaald naar
'forward'. 'forward' doet dit niet in een keer (dan
zou de schildpad ineens ver springen, schildpadden zijn niet zo snel) maar
breekt de lijn in kleine stukjes van lengte 10 om het gevoel van beweging te
geven. De kleine stukjes worden door 'doforward' getekend op het
canvas door de aanroep van 'createLine'. Wordt er geen afstand
meegegeven aan forward dan neemt deze hiervoor de waarde 20 aan (als er geen
argument wordt gegeven is het eerste argument $_[0] niet
gedefinieerd).
sub forward # actual forward routine: break in steps
{ # to give impression of speed
my $dist = $_[0] || 20;
my $steps = 1 + int(abs($dist) / 10);
foreach (1..$steps) { doforward $dist/$steps; }
}
sub doforward # go forward for x (default 20) steps
{
my $step = $_[0] || 20;
my $xnew = $xpos + dcos($dir) * $step;
my $ynew = $ypos + dsin($dir) * $step;
$canvas->createLine( xt($xpos), yt($ypos), xt($xnew), yt($ynew),
-fill => $color, -width => $width);
$xpos = $xnew;
$ypos = $ynew;
turtle();
$top->update;
}
De andere routines zijn dan eigenlijk erg simpel:
sub jump # go forward without drawing for x (default 20) steps
{
my $step = $_[0] || 20;
$xpos = $xpos + dcos($dir) * $step;
$ypos = $ypos + dsin($dir) * $step;
turtle();
}
sub left { $dir += $_[0] || 90; turtle(); } # turn left (degrees)
sub right { left -($_[0] || 90); }
sub erase { $canvas->delete('all'); home(); }
sub home { ($xpos, $ypos, $dir) = (0, 0, 90); turtle(); } # reset at home
sub green { $color = 'green'; }
sub blue { $color = 'blue'; }
sub red { $color = 'red'; }
sub yellow { $color = 'yellow'; }
sub white { $color = 'white'; }
sub black { $color = 'black'; }
sub gray { $color = 'gray'; };
Dat is alles. Hopelijk geeft dit een aardig idee hoe Tk programma's
eenvoudig een user-interface kunnen maken bovenop Perl, met gebruik van items
als knoppen, tekstwindows en canvas. Hierop voortbouwend is het eenvoudig
zelf verder te experimenteren met de in Tk aanwezige elementen. Veel plezier
er mee.