user
A Polyspiral is a spiral made of multiple line segments, whereby each segment is larger (or smaller) than the previous one by a given amount. Each segment also changes direction at a given angle.

Task

Animate a series of polyspirals, by drawing a complete spiral then incrementing the angle, and (after clearing the background) drawing the next, and so on. Every spiral will be a frame of the animation. The animation may stop as it goes full circle or continue indefinitely. The given input values may be varied.

If animation is not practical in your programming environment, you may show a single frame instead.

Pseudo code

```
    set incr to 0.0

    // animation loop
    WHILE true 

        incr = (incr + 0.05) MOD 360
        x = width / 2
        y = height / 2
        length = 5
        angle = incr

        // spiral loop
        FOR 1 TO 150
            drawline
            change direction by angle
            length = length + 3
            angle = (angle + incr) MOD 360
        ENDFOR
    

```
assistant
(formerly Perl 6)

**Works with**: Rakudo version 2018.09

### 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
}

```

Output:

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
}

```