#!/usr/bin/env perl
use strict;
use warnings;

# https://en.wikipedia.org/wiki/Lindenmayer_System

use Data::Turtle;
use GD;

my $repeat   = shift || 6;
my $start    = shift || 'X';
my $distance = shift || 3;
my $theta    = shift || 25;
my $center   = shift || 0;
my $width    = shift || 500;
my $height   = shift || 500;

my %rule = (
    # Branches: start=X, distance=20, theta=20
#    X => 'YF[-X][+X]',
#    Y => 'F',

    # Koch curve: start=F, distance=10, theta=90
#    F => 'F+F-F-F+F',

    # Fractal plant: start=X, distance=3, theta=25
    X => 'F[-X][X]F[-X]+FX',
    F => 'FF',

    # Dragon curve: start=FX, distance=10, theta=90
#    X => 'X+YF+',
#    Y => '-FX-Y',

    # Sierpiński arrowhead curve: start=F, distance=1, theta=60
#    F => 'G-F-G',
#    G => 'F+G+F',

    # Sierpiński triangle: start=F-G-G, distance=4, theta=120
#    F => 'F-G+F+G-F',
#    G => 'GG',
);

my $turtle = Data::Turtle->new(
    width  => $width,
    height => $height,
    x      => $width / 2,
    y      => $center ? $height / 2 : $height,
);

my $img = GD::Image->new( $width, $height );

my %color = (
    white => $img->colorAllocate( 255, 255, 255 ),
    black => $img->colorAllocate( 0, 0, 0 ),
);

$img->transparent( $color{white} );
$img->interlaced('true');

my @statestack;

my %translate = (
    'F' => sub {
        my @line = $turtle->forward($distance);
        if ( $turtle->pen_status == 1 && @line ) {
            $img->setThickness( $line[5] );
            $img->line( @line[ 0 .. 3 ], $color{ $line[4] } );
        }
    },
    'G' => sub {
        my @line = $turtle->forward($distance);
        if ( $turtle->pen_status == 1 && @line ) {
            $img->setThickness( $line[5] );
            $img->line( @line[ 0 .. 3 ], $color{ $line[4] } );
        }
    },
    '-' => sub { $turtle->turn( - $theta ) },
    '+' => sub { $turtle->turn($theta) },
    'M' => sub { $turtle->mirror },
    '[' => sub { push @statestack, [ $turtle->get_state ] },
    ']' => sub { $turtle->set_state( @{ pop @statestack } ) },
);

for ( 1 .. $repeat ) {
    $start =~ s/(.)/defined($rule{$1}) ? $rule{$1} : $1/eg;
}
warn "$start\n";

for my $command ( split //, $start ) {
    $translate{$command}->() if exists $translate{$command};
}

print $img->png;
