The Mandelbrot set
About the code
A famous shape
The complex numbers
z = a + b*i
a and b are real numbers.
i2 = -1
As you know, the square of a number is always positive. So a number like i should not exist but it turns out that
z1 = a1 + b1*i
z2 = a2 + b2*i
When we add them
z1 + z2 = a1 + b1*i + a2 + b2*i
if we group together the real and the imaginary terms
z1 + z2 = (a1 + a2) + (b1 + b2)*i
We get a new complex number which real part is (a1+a2) and which imaginary part is (b1+b2)*i
z1 * z2 = (a1 + b1*i) * (a2 + b2*i)
= a1*a2 + a1*b2*i + b1*a2*i + b1*b2*i2
But i2 = -1. So we get:
z1 * z2 = (a1*a2 - b1*b2) + (a1*b2 + b1*a2)*i
Again we get a complex number with a real and an imaginary parts.
z2 = (a2 - b2) + (2*a*b)*i
The complex plane
The Mandelbrot formula
z0 = 0
zn+1 = zn2 + c
where the values of z are complex numbers.
z0 = 0
z1 = 02 + (2 + 2*i) = 2 + 2*i
z2 = (2 + 2*i)2 + (2 + 2*i)
...
And so on, until z gets too big or we reach the nmax-th term.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "main.h"
#include "Graphics.h"
#include "System.h"
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define MAX_ITERATIONS 256
int main(int argc, char* argv[])
{
// init the window
gfx.init("Mandelbrot", SCREEN_WIDTH, SCREEN_HEIGHT);
gfx.init2D();
gfx.clearScreen(Color(0, 0, 0, SDL_ALPHA_OPAQUE));
for (int y = 0; y < SCREEN_HEIGHT; y++)
for (int x = 0; x < SCREEN_WIDTH; x++)
{
// convert the pixel pos to a complex number
double c_re = (x - SCREEN_WIDTH / 2.0) * 4.0 / SCREEN_WIDTH - 0.7;
double c_im = (y - SCREEN_HEIGHT / 2.0) * 4.0 / SCREEN_WIDTH;
double z_re = 0;
double z_im = 0;
int iteration = 0;
while (z_re * z_re + z_im * z_im <= 16 &&
iteration < MAX_ITERATIONS)
{
// compute the new value
double zn_re = z_re * z_re - z_im * z_im + c_re;
double zn_im = 2 * z_re * z_im + c_im;
// copy back the result to z
z_re = zn_re;
z_im = zn_im;
iteration++;
}
// draw the pixel
Color col;
if (iteration == MAX_ITERATIONS)
col = Color(0, 0, 0);
else
col = Color(iteration, iteration, iteration/2 + 128);
gfx.setPixel(x, y, col);
// display and process the events
gfx.render();
sys.processEvents();
if (sys.isQuitRequested() == true)
exit(EXIT_SUCCESS);
}
// wait until we quit
while (sys.isQuitRequested() == false)
{
sys.processEvents();
sys.wait(50);
}
gfx.quit();
return EXIT_SUCCESS;
}
Download source code
Download executable for Windows
Notice that at each pixel we call gfx.render() because I wanted to make an animation and see how the program works.
Drawing following a pattern
void drawMandelbrot(int offsetx, int offsety)
{
for (int y = 0; y < SCREEN_HEIGHT; y += 8)
{
for (int x = 0; x < SCREEN_WIDTH; x += 8)
{
// convert the pixel pos to a complex number
double c_re = (x + offsetx - SCREEN_WIDTH / 2.0) * 4.0 / SCREEN_WIDTH - 0.7;
double c_im = (y + offsety - SCREEN_HEIGHT / 2.0) * 4.0 / SCREEN_WIDTH;
double z_re = 0;
double z_im = 0;
int iteration = 0;
while (z_re * z_re + z_im * z_im <= 16 &&
iteration < MAX_ITERATIONS)
{
// compute the new value
double zn_re = z_re * z_re - z_im * z_im + c_re;
double zn_im = 2 * z_re * z_im + c_im;
// copy back the result to z
z_re = zn_re;
z_im = zn_im;
iteration++;
}
// draw the pixel
Color col;
if (iteration == MAX_ITERATIONS)
col = Color(0, 0, 0);
else
col = Color(iteration, iteration, iteration/2 + 128);
gfx.setPixel(x + offsetx, y + offsety, col);
}
}
}
Then the main code calls this function with an offset coming from a table.
int main(int argc, char* argv[])
{
// init the window
gfx.init("Mandelbrot 2", SCREEN_WIDTH, SCREEN_HEIGHT);
gfx.init2D();
gfx.clearScreen(Color(0, 0, 0, SDL_ALPHA_OPAQUE));
static Uint8 pattern[64] =
{
0, 32, 8, 40, 2, 34, 10, 42,
48, 16, 56, 24, 50, 18, 58, 26,
12, 44, 4, 36, 14, 46, 6, 38,
60, 28, 52, 20, 62, 30, 54, 22,
3, 35, 11, 43, 1, 33, 9, 41,
51, 19, 59, 27, 49, 17, 57, 25,
15, 47, 7, 39, 13, 45, 5, 37,
63, 31, 55, 23, 61, 29, 53, 21
};
for (int i = 0; i < 64; i++)
{
int j = 0;
while (pattern[j] != i)
j++;
drawMandelbrot(j % 8, j / 8);
// display and process the events
gfx.render();
sys.wait(50);
sys.processEvents();
if (sys.isQuitRequested() == true)
exit(EXIT_SUCCESS);
}
// wait until we quit
while (sys.isQuitRequested() == false)
{
sys.processEvents();
sys.wait(50);
}
gfx.quit();
return EXIT_SUCCESS;
}
Download source code
Download executable for Windows
The Complex class
class Complex
{
public:
Complex(double _re = 0.0, double _im = 0.0);
double squareMag();
double mag();
void operator=(const Complex rhs);
Complex operator+(const Complex rhs);
Complex operator*(const Complex rhs);
double re;
double im;
};
That greatly simplifies the drawing function.
void drawMandelbrot(int offsetx, int offsety)
{
for (int y = 0; y < SCREEN_HEIGHT; y += 8)
{
for (int x = 0; x < SCREEN_WIDTH; x += 8)
{
// convert the pixel pos to a complex number
Complex c;
c.re = (x + offsetx - SCREEN_WIDTH / 2.0) * 4.0 / SCREEN_WIDTH - 0.7;
c.im = (y + offsety - SCREEN_HEIGHT / 2.0) * 4.0 / SCREEN_WIDTH;
Complex z;
int iteration = 0;
while (z.squareMag() <= 16 &&
iteration < MAX_ITERATIONS)
{
// compute the new value
z = z * z + c;
iteration++;
}
// draw the pixel
Color col;
if (iteration == MAX_ITERATIONS)
col = Color(0, 0, 0);
else
col = Color(iteration, iteration, iteration/2 + 128);
gfx.setPixel(x + offsetx, y + offsety, col);
}
}
}
Download source code
Download executable for Windows
And now you can easily change the formula. In example here is what zn+1 = zn3 + c gives:
Changing the colors
// set up the palette
for (int i = 0; i < 256; ++i)
{
float angle = 2.0 * M_PI * (float)i / 256.0;
float offset = 2.0 * M_PI / 3.0;
angle += 40.0 * M_PI / 128.0;
angle *= 1.3;
palette[i].r = 128 + 128 * sin(angle);
palette[i].g = 128 + 128 * sin(angle * 1.1 - offset);
palette[i].b = 128 + 128 * sin(angle * 1.5 + offset);
}
Download source code
Download executable for Windows
And this is what it looks like.
The dark side
// draw the pixel
Color col;
if (iteration == MAX_ITERATIONS)
{
int v = (int)(z.mag() * 128.0) % 256;
col = palette[v];
}
else
{
col = Color(0, 0, 0);
}
gfx.setPixel(x + offsetx, y + offsety, col);
Download source code
Download executable for Windows
And this is what the inside looks like.