I drew the image above last August. Yesterday I translated it into GLSL source code in Fragmentarium and generalized it to different numbers of points and points to skip when forming the star:

#extension GL_EXT_gpu_shader4 : enable #include "Progressive2D.frag" #group Star uniform int Points; slider[5,8,16] uniform int Stride; slider[2,3,7] const int depth = 128; const float pi = 3.141592653; float a = pi / float(Points); float R = 1.0; float r = R * cos(a * float(Stride)); float f = r / cos(a); mat2 rot = mat2(cos(a), sin(a), -sin(a), cos(a)); bool clockwise(vec2 a, vec2 b, vec2 c) { return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) < 0.0; } vec3 color(vec2 p) { vec2 z = p.yx; bool inside = (Stride & 1) != 1; for (int i = 0; i < depth; ++i) { if (length(z) < f) { z /= f; if ((Stride & 1) != 1) { z *= rot; inside = !inside; } } else if (length(z) < R) { for (int j = 0; j < Points; ++j) { float a0 = 2.0 * float(j) * a; float a1 = 2.0 * float(j + Stride) * a; vec2 p0 = R * vec2(cos(a0), sin(a0)); vec2 p1 = R * vec2(cos(a1), sin(a1)); if (clockwise(p0, p1, z)) { inside = !inside; } } break; } else { inside = true; break; } } return vec3(inside ? 1.0 : 0.0); }

They look like this:

]]>I'm currently enjoying the Linux Audio Conference 2013 in Graz, Austria. On Saturday morning I'll present my paper Lyapunov Space of Coupled FM Oscillators:

I consider two coupled oscillators, each modulating the other's frequency. This system is governed by four parameters: the base frequency and modulation index for each oscillator. For some parameter values the system becomes unstable. I use the Lyapunov exponent to measure the instability. I generate images of the parameter space, implementing the number crunching on graphics hardware using OpenGL. I link the mouse position over the displayed image to realtime audio output, creating an audio-visual browser for the 4D parameter space.

I'll upload the slides and demos soon.

]]>In my previous post on Escher's Butterflies I made a symmetrical plane tiling of butterflies with 4 colours. I was curious whether other numbers of colours could result in a symmetrical triangle vertex colouring. (There's pictures below if my rambling gets too long...)

Starting from a triangular lattice (like isometric graph paper), pick two of the three line directions as axes. Then you can label a point P with unique coordinates (p,q), where p is the number of steps along the first axis and q is the number of steps along the second axis. Set the origin at (0,0), pick your P at (p, q), and form an equilateral triangle with a point Q at (-q, p+q) (for the acute axes I chose; obtuse axes might have different coordinates for Q, haven't checked). Then you can extend the triangle OPQ into its own, larger, triangular lattice.

It turns out that the triangle OPQ has an area p²+q²+pq times the area of the smallest triangles forming the lattice. Calling this number n, we could use n colours in the n smallest triangles inside the OPQ triangle, but that wouldn't help colouring the vertices of the triangular lattice, is the whole point of this exercise.

A simple way to colour the vertices might be to start at one vertex of the OPQ lattice, pick one of the axes, then step along in that direction assigning the next colour to each base lattice vertex - but eventually you will hit another OPQ lattice vertex. By symmetry, we want each OPQ lattice vertex to be assigned the same colour - and any translation or (k pi / 3) rotation of the OPQ lattice across the base lattice should give an O'P'Q' latticce with all its vertices the same colour.

So far we have a row along one axis of colour-assigned base lattice vertices, from one OPQ lattice vertex as far as possible, stopping one base step before the next OPQ lattice vertex on that axis. Now we can copy/paste those assignments to all of the other OPQ lattice vertices. So far so good, but we might not have assigned colours to all the base lattice vertices yet, because the OPQ lattice might not hit all rows of the base lattice - for example with (p,q)=(2,2) the OPQ lattice misses every other row.

Here's where it gets a bit fuzzy because I was hacking on code late at night half-asleep, randomly changing things in the hope that it would work, and eventually it did - so I don't know why it works, or even if it works in absolutely every single case, but it seems fine so far... But the number of rows you need to fill is the greatest common divisor of p and q, and each row has exactly the same length by translational symmetry. So you get a parallelogram of height (p`gcd`q) with some width. Somehow it all works out so that the width is n divided by that height (recall n = p²+q²+pq, and h = p`gcd`q so h divides both p and q so h divides n exactly and w = n / h gives an integer).

Anyway, here's some diagrams that might explain it more clearly, followed by butterfly tessellations in various numbers of colours:

And you can get the code (for both the diagrams and tessellations):

git clone git://gitorious.org/maximus/butterflies.git

Or browse butterflies on Gitorious.

]]>Mandelbrot Set fractal zoom music video available on the Internet Archive:

Made with mightymandel,
using techniques from optimizing zoom animations
to blend image sequences using *Pure-data* and *Gem*.

A Mandelbrot Set colouring algorithm modified from atom domain like this:

colour according to the N that minimizes (log |z| - k N)

When k = 0 you get the regular atom domain, when k > 0 the domains swell and pressure each other.

Video available on the Internet Archive:

Made six months ago with an early unreleased version of mightymandel.

]]>I started writing mightymandel, a Mandelbrot Set explorer using OpenGL 4, back in January, but only got around to beating it into proper shape this week. I'll write more about how it works another time, suffice to say that my GPU is much faster than my CPU.

You can get the GPL3+ code here:

git clone git://gitorious.org/maximus/mightymandel.git

Or browse mightymandel on Gitorious.

Note that you need the literate preprocessor anansi to unpack the single-file source (which is sadly undocumented so far).

]]>