# Draw a Fibonacci Swoosh

Title courtesy of Greg Martin

For this challenge, I'll define an arc of size $$\k\$$ as a single piece of a sine wave with a length of $$\k\$$ units and an height of $$\\frac{k}{4}\$$ units:

And I'll define a swoosh of order $$\1\$$ and $$\2\$$ as a single arc of size 1, and recursively define a swoosh of order $$\k\$$ as a swoosh of order $$\k-1\$$ followed by a flipped swoosh of order $$\k-2\$$, with an arc the same length as the previous two swooshes laid end-to-end.

For example, here are the swooshes of order 3, 4, 5, and 6:

You might notice that the length of a swoosh of order $$\n\$$ is the $$\n\$$th fibonacci number, hence the name.

Reference implementation:

let ctx = c.getContext('2d')
const scale = 30;
let drawArc = (length, offset = 0, amp = 1) => {
ctx.moveTo(offset * scale, c.height / 2)
for (i = 0; i <= scale * length; i++) {
ctx.lineTo(i + offset * scale, -Math.sin(Math.PI * i / (scale * length)) * amp * length * scale / 4 + c.height / 2)
}
ctx.stroke()
}

fib = n => fib[n] ||= n <= 2 ? 1 : fib(n - 1) + fib(n - 2)

let swoosh = (order, offset = 0, amp = 1) => {
if (order <= 2) {
drawArc(1, offset, amp);
} else {
swoosh(order - 1, offset, amp)
swoosh(order - 2, offset + fib(order - 1), -amp)
drawArc(fib(order), offset, amp)
}
}

swoosh(8)
<canvas width=1000 height=500 id=c></canvas>

Your challenge is to, given an integer $$\n\$$, draw a fibonacci swoosh of order $$\n\$$ as defined above, either outputting to an image file or drawing on the screen. You may use 0-indexing or 1-indexing. It's fine if the size of your answer's output is limited by screen/canvas size, but your answer should be able to handle at least $$\n=6\$$, and each arc should be at least 10 pixels wide onscreen. Each arc should be recognisable as a sine curve.

This is , shortest wins!

## Testcases

Here's a higher-resolution image for n=6:

And here's an image for n=10:

# JavaScript (ES6), 287286246235 233 bytes

f=
n=><svg viewBox=-1,-${[g(n)*6+1,i*24+2,i*6+g(n-2)*6+2]}><path fill=none stroke=#000 d=M0,0${h(n,-3)}>
g=n=>i=n<3||g(n-1)+g(n-2)
h=(n,s)=>'c'+[4,s,8,t=s*2,12,t,4,0,8,-s,12,-t].map(m=>m*g(n))+(n>2?'m'+[-i*24,0]+h(--n,s)+h(--n,-s):'')
<input type=number oninput=o.innerHTML=f(+this.value) min=1 max=20><div id=o>

Outputs an HTML5 compatible SVG image. Doesn't adjust the stroke-width for large n so the swoosh tends to fade out. n limited to 20 because it's too slow otherwise. Edit: Saved 1 byte thanks to @noodleman. Saved a few bytes thanks to @Ausername. Version with stroke-width adjustment:

f=
n=><svg viewBox=-1,-${[g(n)*24+1,i*96+2,i*24+g(n-2)*24+2]}><path fill=none stroke=#000 stroke-width=${g(n-2)} d=M0,0\${h(n,-3)}>
g=n=>i=n<3?1:g(n-1)+g(n-2)
h=(n,s)=>'c'+[4,s,8,t=s*2,12,t,4,0,8,-s,12,-t].map(m=>m*4*g(n))+(n>2?'m'+[-i*96,0]+h(--n,s)+h(--n,-s):'')
<input type=number oninput=o.innerHTML=f(+this.value) min=1 max=20><div id=o>

• You should be able to save a byte on your fibonacci function g with g=n=>n<3||g(n-1)+g(n-2) Apr 6 at 12:07
• @noodleman Yeah originally I had a raw g(n) somewhere but I think the only one left is in the stroke-width adjustment version.
– Neil
Apr 6 at 12:40
• [e=g(n)*6+1,e*4-2,e+g(n-2)*6+1] saves a few bytes Apr 6 at 20:38
• Also doing the initial call to h with s=-3 saves a few more bytes Apr 6 at 20:41
• -5 more by replacing g with g=n=>i=n<3?6:g(n-1)+g(n-2), removing all *6s, replacing *24 with *4, and adding a /6 in the map call in h (code) Apr 6 at 23:06

# Google Sheets, 499 490 bytes

-9 bytes thanks to @doubleunary

=SORT(SPARKLINE(LET(F,LAMBDA(F,n,IF(n<3,1,F(F,n-1)+F(F,n-2))),D,LAMBDA(l,o,a,LET(s,SEQUENCE(30*l+1,1,),{s+o*30,SIN(PI()*s/(30*l))*a*l})),W,LAMBDA(W,k,o,a,IF(k<3,D(1,o,a),{W(W,k-1,o,a);W(W,k-2,o+F(F,k-1),-a);D(F(F,k),o,a)})),z,W(W,A1,,1),y,INDEX(z,,1),x,TOCOL(SCAN(,ABS({0;y}-y)>1,LAMBDA(a,c,a+c)),2),IF(A1>2,REDUCE(FILTER(z,x=0),SEQUENCE(COUNTUNIQUE(x)-1),LAMBDA(a,i,LET(k,{a;SORT(a,SEQUENCE(ROWS(a)),)},{QUERY(k,"limit "&XMATCH(VLOOKUP(i,{x,z},2,),INDEX(k,,1),,-1));FILTER(z,x=i)}))),z))))


As pointed out by @doubleunary in the comments, it turns out that wasn't a bug but just how the function is designed. When drawing across the given coordinates, it never "lifts the pen" so if it draws $$\x1 \rightarrow x2 \rightarrow x3\$$ and it has to go back to $$\x1\$$, it draws a line from $$\x3 \rightarrow x1\$$ ruining the figure. I addressed this issue by filling the gaps with the intermediate coordinates so $$\x1 \rightarrow x2 \rightarrow x3 \rightarrow x1\$$ becomes $$\x1 \rightarrow x2 \rightarrow x3 \rightarrow x2 \rightarrow x1\$$ but this doubled the size of the formula and it no longer works beyond order=7, unlike the previous formula that worked up to order=18.

# Google Sheets, 250 237 bytes

-13 bytes thanks to @doubleunary

=SORT(SPARKLINE(LET(F,LAMBDA(F,n,IF(n<3,1,F(F,n-1)+F(F,n-2))),D,LAMBDA(l,o,a,LET(s,SEQUENCE(30*l+1,1,),{s+o*30,SIN(PI()*s/(30*l))*a*l})),W,LAMBDA(W,k,o,a,IF(k<3,D(1,o,a),{W(W,k-1,o,a);W(W,k-2,o+F(F,k-1),-a);D(F(F,k),o,a)})),W(W,A1,,1))))


It's just an implementation of the code OP provided.

The straight line connecting the start and end of the wave seems to be a bug with the SPARKLINE function.

• When sparkline() is used like this, it can only draw one continuous line. You could avoid the straight line by "tracking back" to the start position each time you finish a wave. See what happens if you swap the order of sort() and sparkline() and specify a low value for order. Apr 6 at 17:20
• ahh I see that makes sense! I've never used SPARKLINE before so I'm not very familiar with it. I'll look into it.
– z..
Apr 6 at 17:25
• -14 bytes: =sort(sparkline(let(f,lambda(f,n,if(n<3,1,f(f,n-1)+f(f,n-2))),d,lambda(l,o,a,let(s,sequence(30*l+1,1),{s+o*30,sin(pi()*s/(30*l))*a*l})),w,lambda(w,k,o,a,if(k<3,d(1,o,a),{w(w,k-1,o,a);w(w,k-2,o+f(f,k-1),-a);d(f(f,k),o,a)})),w(w,A1,,1)))) Apr 6 at 17:29
• @doubleunary, thanks! I implemented that suggestion (although we still need the , after the 1, or we get this instead of this) and fixed the line.
– z..
Apr 6 at 22:57
• Very nice! Tried to decrease recursion with a reduce() based Fibonacci but that did not help much. You can snip 9 bytes by replacing the second w(w,a1,,1) with z. Apr 7 at 4:54

# K (ngn/k)106 95 bytes

{?|/''i=\:_0.5+w%4%1-{:[x<2;k;o[x-1+b;y+y-b]%2-4*b:_y+y],k:[email protected]*y}[x]'(i:!1+w)%w:2/1,&3+x}


It takes one argument, the order of the swoosh, and outputs the bitmap.

## How it works?

The function

{:[x<2;k;o[x-1+b;y+y-b]%2-4*b:_y+y],k:[email protected]*y}


takes two arguments, the order and normalized x coordinate, then it outputs a list of y coordinates the swoosh of that order passes through at the given x coordinate. It calculates the coordinates recursively.

The rest of the code transforms coordinates into bitmap.

Try it here

Here are the bitmaps for k from 1 to 7 (converted to png).

# C (GCC), 259 257 bytes

w;a;char*m;z(k,s,f,x){k=k>2?z(k-2,z(k-1,s,f),-f):s+100;float W=k-s,q=asin(.75);for(x=0;x<W;++x)if(m)m[(int)(w/2-f*W*(sin(x/W*(3.14-2*q)+q)-.75)+.5)*w+s+x+a]=1;return k;}main(n){w=z(n,0);a=sprintf(m=calloc(w,w+1),"P5 %d %d 1 ",w,w);z(n,0,1);write(1,m,w*w);}


Input is given as argc, outputs a PGM image.

Function z draws a swoosh of order k, starting at the x coordinate s, and returns the x coordinate at which it ends; f can be either -1 or 1, and it determines if the swoosh is flipped or not (respectively). If the output buffer is NULL, it performs a dry run.

How it works:

• first, do a dry run of z to compute the width of the final image; for simplicity, the height is set equal to the width;
• allocate a buffer of proper size, and write the PGM header;
• then re-run z;
• output the buffer.
• I think you can get rid of return k in z(k,s,f,x) as it's already the first argument. Reference
– Blue
Apr 9 at 9:11
• @Blue maybe, apart from the fact that it would be another language (GCC -O0), I've just tried and I can't get it work, and I don't think it's doable without adding less then 9 bytes. Apr 9 at 10:29
• First, I have to introduce another variable (let's call it e) in the signature, since I need the value of k unaltered for the next calls; the same holds for the other parameters, except for x which is modified before the function terminates. This already adds up to +6 bytes, so... I won't try to get it work, you're free to prove me wrong :) Apr 9 at 10:33
• Oh yeah, it took me a second to figure out what is happening, it most certainty won't be worth it. Good job!
– Blue
Apr 9 at 13:41

# Desmos, 153 bytes

c=180
h(n,x)=f(n,x)[1...c]
g(n)=\{n<2:n,g(n-1)+g(n-2)\}
k=g(n)
f(n,x)=\{n<3:[45\sin x\{0<x<c\}],\join(f(n-1,x),-f(n-2,x-cg(n-1)),45k\sin x/k\{0<x<ck\})\}


The fibonacci swoosh of order $$\n\$$ can be displayed by calling h(n,x) (x is a free variable, like in f(x)=x or f(x)=x^2). For example, h(3,x) will display the fibonacci swoosh of order 3.

Note that the graph is set in degrees mode; it will not work in radians mode.

Try It On Desmos!

Try It On Desmos! - Prettified

Second ever Desmos answer using recursion!!! I am still gushing over the fact that Desmos actually added recursion; it's now turing complete and that opens the door for so many challenges that Desmos previously couldn't solve. Just to think that just a couple days ago, this challenge would not be possible to do in Desmos...

Also, this does work up to $$\n=6\$$ (see screenshots below), but for some reason that I am unaware of, it takes an extremely long time for the graph to display anything. I didn't keep track of exactly how long it took to load the graph, but, for me, it was upwards of 30 minutes. This is why I didn't test $$\n>6\$$; it was taking super long for the graphs to load anything. Although in theory, it should be able to handle a couple of cases past $$\n=6\$$. I found that the runtime can be reduced by replacing the c in [1...c] to a smaller number (like 20), but it still takes a while to display the graph.

Also, for some reason that I am also unaware of, simply calling f(n,x) without any modifications will result in the following error: Can't plot a list whose length depends on free variable 'x'.. To fix this, f(n,x) has to be indexed by a range that is dependent on a non-free variable; in this case, it is c, which is defined to be 180. This is precisely what h(n,x) achieves.

Here are the screenshots of the graphs after they finished running.

Prettified:

Golfed: