Every once in a while, I spend an hour or two playing with code to generate fractal images. I find these patterns intriguing and the process of working with them can make for a great workout in complex number arithmetic. When I’m playing with code like this, I find it particularly handy to use Python because it handles the complex numbers very neatly and you can produce a good looking fractal with just a few lines of code. For example, here’s a quick Mandelbrot example:
import numpy print 'P2\n800 800\n255' for y in numpy.linspace(-2.0,2.0,800): for x in numpy.linspace(-2.0,2.0,800): c = x + y*(0+1j) z = 0 for p in range(256): z = z*z - c if numpy.abs(z) > 4: break print p, print
Please excuse the lack of explanatory comments, but I’m trying to make the point that you don’t need many lines of code!
I ran the program and piped the output into a plain text pgm file, then converted it to PNG format using ImageMagick’s convert command, as shown below:
python mandelbrot.py > mandelbrot.pgm convert mandelbrot.pgm mandelbrot.png
Ok, here’s the image it produced:
The process of generating each pixel value in an image like the one above involves starting with a complex number determined by the position within the image (the image spans a rectangular region of the complex plane) and iterating a function recursively to generate a sequence of other points on the complex plane. The faster this iterative process “blows up” (by which I mean that the complex numbers get very large) the darker the pixel.
Today, I was playing with this example and I decided to try to visualise something a little different – for each pixel, I repeated the iteration until the magnitude of the complex value passed a threshold (4 as it happens) then chose the pixel colour based on two things: the distance between the last two complex values and the difference in angle between them. I was thinking of this as “how fast the point is moving” and “how fast the point is revolving around the origin”. Neither of these descriptions are really completely appropriate for an iterative process like this which by definition moves in discrete steps, but that’s how I was thinking of it.
Anyway, the image I produced gave me much food for thought, so I figured I’d post it here in case I want to come back to it again. First, here’s the Python code:
import numpy print 'P3\n1280 1280\n255' for y in numpy.linspace(0.0,0.25,1280): for x in numpy.linspace(-2.5,-1.25,1280): c = x + y*(0+1j) z = 0 for p in range(256): newz = z*z + c if numpy.abs(newz) > 4: z = newz - z break else: z = newz r = min(255, int(10 * numpy.abs(z))) g = min(255, 128 + int(10 * numpy.angle(z))) b = min(255, 128 - int(10 * numpy.angle(z))) print r, g, b, print
I ran the program and piped the output into a plain text ppm file, then converted it to PNG format using ImageMagick’s convert command, as shown below:
python colours.py > colours.ppm convert colours.ppm colours.png
This image shows the region of the complex plane where the real part is between -3 and +3 and the imaginary part is between -3j and +3j.
This image shows the region of the complex plane where the real part is between -1.5 and -1.25 and the imaginary part is between 0j and 0.25j.