#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Carp; package Turtle; use Math::Trig; use Imager; sub new { my $class = shift; my ($x, $y, $angle, $dir, $step, $color, $file) = @_; my $self = { "xpos" => $x || 20, "ypos" => $y || 800-20, "angle" => $angle || 60, "dir" => $dir || 0, "step" => $step || 4, "color" => $color, "img" => Imager->new( xsize=>1048,ysize=>800 ), "file" => $file || 'turtle.bmp', }; return bless $self, $class; } sub move { my $self = shift; my $rad = deg2rad($self->{"dir"}); $self->{"xpos"} += int 0.5 + (cos($rad) * $self->{"step"}); $self->{"ypos"} += int 0.5 + (sin($rad) * $self->{"step"}); } sub draw { my $self = shift; my $rad = deg2rad($self->{"dir"}); my ($x1, $y1) = ($self->{"xpos"}, $self->{"ypos"}); my $x2 = int 0.5 + ($x1 + cos($rad) * $self->{"step"}); my $y2 = int 0.5 + ($y1 + sin($rad) * $self->{"step"}); # line($x1, $y1, $x2, $y2); # from Imager or GD ... $self->{"img"}->line( x1 => $x1, x2 => $x2, y1 => $y1, y2 => $y2, aa=>1, color => Imager::Color->new( 255, 255, 255 ) ); $self->{"xpos"} = $x2; $self->{"ypos"} = $y2; } sub turn_left { my $self = shift; $self->{"dir"} = $self->{"dir"} + $self->{"angle"} > 360 ? $self->{"dir"} - 360 + $self->{"angle"} : $self->{"dir"} + $self->{"angle"} ; } sub turn_right { my $self = shift; $self->{"dir"} = $self->{"dir"} - $self->{"angle"} < 0 ? $self->{"dir"} + 360 - $self->{"angle"} : $self->{"dir"} - $self->{"angle"} ; } sub to_png { my $self = shift; $self->{"img"}->write(file=>$self->{"file"}) or die $self->{"img"}->errstr; } 1; package Lindenmayer; sub new { my ( $class, $axiom, $rules ) = @_; my $self = { "rules" => __generate_rules($rules), "axiom" => $axiom, "cache" => [$axiom], "iter" => undef, }; bless $self, $class; return $self; } sub create_iterator { my $self = shift; $self->{"iter"} = sub { $self->{"axiom"} = $self->{"rules"}->($self->{"axiom"}); push @{$self->{"cache"}}, $self->{"axiom"}; return $self->{"cache"}->[-1]; }; return $self->{"iter"}; } sub get_index { my $self = shift; my $n = shift || -1; return $self->{"cache"}->[$n] if exists $self->{"cache"}->[$n]; $self->create_iterator() unless defined $self->{"iter"}; $self->{"iter"}->() for $#{$self->{"cache"}}+1 .. $n; return $self->{"cache"}->[$n]; } sub __generate_rules { my $rules = shift; return sub { my @token = split //, shift; return join '', map { exists $rules->{$_} ? $rules->{$_} : $_ } @token; }; } 1; package main; my $rules = { A => "B-A-B", B => "A+B+A" }; my $axiom = "A"; my $sierpinski = Lindenmayer->new($axiom, $rules); my $turtle = Turtle->new; my $turtle_lut = { A => sub{$turtle->draw}, B => sub{$turtle->draw}, "-" => sub{$turtle->turn_left}, "+" => sub{$turtle->turn_right} }; $turtle_lut->{$_}->() for split //, $sierpinski->get_index(8); $turtle->to_png();