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
    



--
-- demo\rosetta\Polyspiral.exw
-- ===========================
--
-- Space toggles the timer, '+' increases speed (up to 100 FPS), '-' decreases speed
-- 'M' toggles "mod360", which inverts the angle every 360/2PI or so, since sin/cos 
-- accept arguments in radians not degrees (and mod 2*PI changes nothing), producing 
-- non-true polyspirals, but quite interesting nevertheless.
--
with javascript_semantics
constant TITLE = "Polyspiral"
include pGUI.e

Ihandle dlg, canvas, timer
cdCanvas cddbuffer, cdcanvas
atom incr = 0
bool mod360 = false

procedure Polyspiral(atom x1, y1)
    atom angle = incr
    integer len = 5
    incr += 0.05
    if mod360 then
        incr = mod(incr,360)
    end if
    for i=1 to 150 do
        atom x2 = x1 + cos(angle)*len,
             y2 = y1 + sin(angle)*len
        cdCanvasSetForeground(cddbuffer, i*#200+i*#40+i*#10)
        cdCanvasLine(cddbuffer, x1, y1, x2, y2)
        {x1, y1} = {x2, y2}
        len += 3
        angle += incr
        if mod360 then
            angle = mod(angle,360)
        end if
    end for
end procedure

function redraw_cb(Ihandle /*ih*/)
    integer {w, h} = IupGetIntInt(canvas, "DRAWSIZE")
    cdCanvasActivate(cddbuffer)
    cdCanvasClear(cddbuffer)
    Polyspiral(w/2, h/2)
    cdCanvasFlush(cddbuffer)
    integer ms = IupGetInt(timer,"TIME")
    IupSetStrAttribute(dlg, "TITLE", "%s (timer=%d [%g FPS], angle %3.2f%s)",
                       {TITLE,ms,1000/ms,incr,iff(mod360?" (mod360)":"")})
    return IUP_DEFAULT
end function

function timer_cb(Ihandle /*timer*/)
    IupUpdate(canvas)
    return IUP_IGNORE
end function

function map_cb(Ihandle canvas)
    cdcanvas = cdCreateCanvas(CD_IUP, canvas)
    cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)
    cdCanvasSetBackground(cddbuffer, CD_WHITE)
    cdCanvasSetForeground(cddbuffer, CD_GRAY)
    return IUP_DEFAULT
end function

function key_cb(Ihandle /*ih*/, atom c)
    if c=K_ESC then return IUP_CLOSE end if
    if c=' ' then
        IupSetInt(timer,"RUN",not IupGetInt(timer,"RUN"))
    elsif find(c,"+-") then
        IupSetInt(timer,"RUN",false)
        c = -(','-c) -- ('+' ==> +1, '-' ==> -1)
        IupSetInt(timer,"TIME",max(10,IupGetInt(timer,"TIME")+c*10))
        IupSetInt(timer,"RUN",true)
    elsif upper(c)='M' then
        mod360 = not mod360
    end if
    return IUP_CONTINUE
end function

IupOpen()
canvas = IupCanvas("RASTERSIZE=640x640")
IupSetCallbacks(canvas, {"MAP_CB", Icallback("map_cb"),
                         "ACTION", Icallback("redraw_cb")})
timer = IupTimer(Icallback("timer_cb"), 20)
dlg = IupDialog(canvas,`TITLE="%s"`, {TITLE})
IupSetCallback(dlg, "KEY_CB", Icallback("key_cb"))
IupShow(dlg)
IupSetAttribute(canvas, "RASTERSIZE", NULL)
if platform()!=JS then
    IupMainLoop()
    IupClose()
end if
