Polyspiral

SVG "pseudo-animation"

Sort of an ersatz animation. Write updates to a svg file, most modern viewers will update as the content changes.

use SVG;
my $w = 600;
my $h = 600;

for 3..33  -> $a {
    my $angle = $a/τ;
    my $x1 = $w/2;
    my $y1 = $h/2;
    my @lines;

    for 1..144 {
        my $length = 3 * $_;
        my ($x2, $y2) = ($x1, $y1) «+« |cis($angle * $_).reals».round(.01) »*» $length ;
        @lines.push: 'line' => [:x1($x1.clone), :y1($y1.clone), :x2($x2.clone), :y2($y2.clone),
                                :style("stroke:rgb({hsv2rgb(($_*5 % 360)/360,1,1).join: ','})")];
        ($x1, $y1) = $x2, $y2;
    }

    my $fname = "./polyspiral-perl6.svg".IO.open(:w);
    $fname.say( SVG.serialize(
        svg => [
            width => $w, height => $h, style => 'stroke:rgb(0,0,0)',
            :rect[:width<100%>, :height<100%>, :fill<black>],
            |@lines,
        ],)
    );
    $fname.close;
    sleep .15;
}

sub hsv2rgb ( $h, $s, $v ){ # inputs normalized 0-1
    my $c = $v * $s;
    my $x = $c * (1 - abs( (($h*6) % 2) - 1 ) );
    my $m = $v - $c;
    my ($r, $g, $b) = do given $h {
        when   0..^(1/6) { $c, $x, 0 }
        when 1/6..^(1/3) { $x, $c, 0 }
        when 1/3..^(1/2) { 0, $c, $x }
        when 1/2..^(2/3) { 0, $x, $c }
        when 2/3..^(5/6) { $x, 0, $c }
        when 5/6..1      { $c, 0, $x }
    }
    ( $r, $g, $b ).map: ((*+$m) * 255).Int
}

See polyspiral-perl6.gif (offsite animated gif image)

SDL full animation

Uses the same basic algorithm but fully animated. Use the up / down arrow keys to speed up / slow down the update speed. Use PgUp / PgDn keys to increment / decrement animation speed by large amounts. Use left / right arrow keys to reverse the "direction" of angle change. Press Space bar to toggle animation / reset to minimum speed. Left Control key to toggle stationary / rotating center. Use + / - keys to add remove line segments.

use SDL2::Raw;

my $width  = 900;
my $height = 900;

SDL_Init(VIDEO);

my $window = SDL_CreateWindow(
    'Polyspiral',
    SDL_WINDOWPOS_CENTERED_MASK,
    SDL_WINDOWPOS_CENTERED_MASK,
    $width, $height,
    RESIZABLE
);

my $render = SDL_CreateRenderer($window, -1, ACCELERATED +| PRESENTVSYNC);

my $event = SDL_Event.new;

enum KEY_CODES (
    K_UP     => 82,
    K_DOWN   => 81,
    K_LEFT   => 80,
    K_RIGHT  => 79,
    K_SPACE  => 44,
    K_PGUP   => 75,
    K_PGDN   => 78,
    K_LCTRL  => 224,
    K_PLUS   => 87,
    K_MINUS  => 86,
    K_SPLUS  => 46,
    K_SMINUS => 45,
);

my $angle = 0;
my $lines = 240;
my @rgb = palette($lines);
my ($x1, $y1);
my $dir = 1;
my $rot = 0;
my $incr = .0001/π;
my $step = $incr*70;

main: loop {
    while SDL_PollEvent($event) {
        my $casted_event = SDL_CastEvent($event);
        given $casted_event {
            when *.type == QUIT { last main }
            when *.type == KEYDOWN {
                if KEY_CODES(.scancode) -> $comm {
                    given $comm {
                        when 'K_LEFT'   { $dir = $rot ??  1 !! -1 }
                        when 'K_RIGHT'  { $dir = $rot ?? -1 !!  1 }
                        when 'K_UP'     { $step += $incr }
                        when 'K_DOWN'   { $step -= $incr if $step > $incr }
                        when 'K_PGUP'   { $step += $incr*50 }
                        when 'K_PGDN'   { $step -= $incr*50; $step = $step < $incr ?? $incr !! $step }
                        when 'K_SPACE'  { $step = $step ?? 0 !! $incr }
                        when 'K_LCTRL'  { $rot  = $rot  ?? 0 !! -1; $dir *= -1 }
                        when 'K_PLUS'   { $lines = ($lines + 5) min 360; @rgb = palette($lines) }
                        when 'K_SPLUS'  { $lines = ($lines + 5) min 360; @rgb = palette($lines) }
                        when 'K_MINUS'  { $lines = ($lines - 5) max 60;  @rgb = palette($lines) }
                        when 'K_SMINUS' { $lines = ($lines - 5) max 60;  @rgb = palette($lines) }
                    }
                }
                #say .scancode; # unknown key pressed
            }
            when *.type == WINDOWEVENT {
                if .event == 5 {
                    $width  = .data1;
                    $height = .data2;
                }
            }
        }
    }

    $angle = ($angle + $dir * $step) % τ;
    ($x1, $y1) = $width div 2, $height div 2;
    my $dim = $width min $height;
    my $scale = (2 + .33 * abs(π - $angle)) * $dim / $lines;
    $scale *= ($angle > π) ?? (1 - $angle/τ) !! $angle/τ;
    $scale max= $dim/$lines/$lines;
    for ^$lines {
        my $length = $scale + $scale * $_;
        my ($x2, $y2) = ($x1, $y1) «+« cis(($angle * $rot * $lines) + $angle * $_).reals »*» $length;
        SDL_SetRenderDrawColor($render, |@rgb[$_], 255);
        SDL_RenderDrawLine($render, |($x1, $y1, $x2, $y2)».round(1));
        ($x1, $y1) = $x2, $y2;
    }
    @rgb.=rotate($lines/60);
    SDL_RenderPresent($render);
    SDL_SetRenderDrawColor($render, 0, 0, 0, 0);
    SDL_RenderClear($render);
}

SDL_Quit();

sub palette ($l) { (^$l).map: { hsv2rgb(($_ * 360/$l % 360)/360, 1, 1).list } };

sub hsv2rgb ( $h, $s, $v ){ # inputs normalized 0-1
    my $c = $v * $s;
    my $x = $c * (1 - abs( (($h*6) % 2) - 1 ) );
    my $m = $v - $c;
    my ($r, $g, $b) = do given $h {
        when   0..^(1/6) { $c, $x, 0 }
        when 1/6..^(1/3) { $x, $c, 0 }
        when 1/3..^(1/2) { 0, $c, $x }
        when 1/2..^(2/3) { 0, $x, $c }
        when 2/3..^(5/6) { $x, 0, $c }
        when 5/6..1      { $c, 0, $x }
    }
    ( $r, $g, $b ).map: ((*+$m) * 255).Int
}