In this article, I am going to introduce you to the Fractal Template Library, a C++ header file which contains code to create fractals such as the well-known Mandelbrot Set and the Julia Set. I'm first going to explain how images of fractals are created, and then I'm going to show how to use the Fractal Template Library.
If you want to understand the logic behind creating a fractal, you have to understand complex numbers. “Complex number” sounds intimidating. It is like you have to have a Ph.D. to understand what it means. But I'll show you that, even if you don't know what complex numbers are, you've already used them, can you believe it?
You know integers. Yes you do. I know it. Examples of integers are ..., -2, -1, 0, 1, 2, ... . We can write an integer as a vector: (1) or maybe you prefer angle brackets: <1>. To describe numbers such as 1.2, 2.8 and -0.4, we need two integers. For example, we can write the number 1.2 as the pair (1, 2) or maybe (1, 1/2) (or even (1, 0.2), but in this case, the second element is not an integer but a number between 0 and 1).
We can do all sorts of math with these pairs. For example, let's add the pairs (1, 1/2) and (2, 1/4):
(1 + 2) + (1/2 + 1/4) = (3) + (3/4) = 3.75.
And now, we will multiply them:
((1 × 2) + (1 × 1/4)) + ((1/2 × 2) + (1/2 × 1/4)) =
(2 + 1/4) + (1 + 1/8) = 2.25 + 1.125 = 3.375.
One day, someone was bored and wanted to solve the following problem: what is the value of ‘z’ in the equation z × z = -25? No matter what we try, we cannot find a value: a negative number will always yield a positive result and a positive number won't do the trick either. So the bored man thought: “I'll have to invent a new number!” And he said to himself: “From now on, i2 = -1”. You immediately ask the question: “But what is i?”. And the answer is: no one knows. “But if no one knows what i is, how can we use it?” Well, that is actually very simple: square it! Suppose a certain result r is 9i. You can't do anything with r because you don't know what i is. But r squared is equal to (9i)2, and we know that i2 = -1, so we know that r2 is -81! Of course, if you can't do anything with r2, go take a walk and drink a cup of coffee.
Now that we have our imaginary ‘i’, we can construct a complex number. Recall that we can represent the number 1.2 as the pair (1, 2) or (1, 0.2): we add a certain quantity — in this case, 0.2 — to 1. To construct a complex number, we do exactly the same: we add a certain amount of ‘i’ to a number. For example, the number 1.2 is nothing more than the complex number 1.2 + 0i. This means:
1.2 = 1.2 + 0i = (1.2, 0) = ((1, 1/2), (0, 0)).
See, you have already used complex numbers? I knew it! 1 is a complex number. 1.4 is a complex number, -45.89 and so on. Let's go back to the equation z × z = -25. Let's say z equals the complex number 0 + 5i or (0, 5i):
(0, 5i) × (0, 5i) = -25
(0 × 0 + 0 × 5i) + (5i × 0 + 5i × 5i) = -25
(0 + 0) + (0 + 25i<SUP>2</SUP>) = -25
25i<SUP>2</SUP> = -25
25 × -1 = -25 (because i<SUP>2</SUP> = -1)
-25 = -25
There is a lot more to tell about complex numbers, but we don't need much more to create nice fractals.
To create a fractal, we have to assign a color to each point in the complex plane. A complex number has a real part and an imaginary part. For example, 1 is the real part and 5i is the imaginary part of the complex number 1 + 5i. The point (1, 5) lays in the complex plane at x = 1 and y = 5. And how exactly do we assign a color to a point? If we know that a point “escapes to infinity,” we can calculate a color based on the number of iterations we had to go through. To make things more clear, let's take the Mandelbrot Set as an example. To create an image of this fractal, we have to assign a color to all points in the rectangle with the upper left corner at (-1.5, 1.5) and a width and height of 3. For each “pixel” — or point — we calculate Zn = (Zn - 1)2 + C, where Z and C are both complex numbers and Z is initially set to 0 + 0i and C is set to the current point (the first point will be -1.5 + 1.5i). If the distance from the origin (0, 0) to the point Z becomes greater than 2, we can give it a color (the distance is the length of the vector <-1.5, 1.5>). Suppose the maximum number of iterations is 256, and the distance is greater then 2 in the 200th iteration, then we could assign the color (R=200,G=200,B=200) to this point. Believe it or not, but that's all there is to it.
The Fractal Template Library contains four classes:
fractal
mandelbrot
julia
cactus
These classes are in the namespace fractals. I'm not going to show you how to create the Mandelbrot Set: there is one picture too many on the Internet. Instead, I'm going to show you how to create the San Marco and the Cactus fractal.
fractals
To create the San Marco fractal, compile and run this program:
#include "fractal"
#include <fstream>
using namespace fractals;
int main()
{
// san marco fractal
julia<float> j(400, 400);
j.c = std::complex<float>(-3.0f / 4.0f, 0);
j.render(-1.5, 1.5, 3, 3);
std::ofstream os("sanmarco.tga", std::ios_base::binary);
os << j;
}
To create the Cactus fractal, compile and run this program:
#include "fractal"
#include <fstream>
using namespace fractals;
int main()
{
// cactus fractal
cactus<float> c(400, 400);
c.render(-1.5, 1.5, 3, 3);
std::ofstream os("cactus.tga", std::ios_base::binary);
os << c;
}
The first image is the San Marco fractal and the second one is the Cactus fractal:
Is there something wrong with these pictures? Are these screenshots from DOOM3? No. They are greyscale images. The color of a pixel is in fact the number of iterations! For example, suppose the color of a point is (R=200,G=200,B=200). This means that the distance became larger than 2 in iteration 200. By playing a bit with the levels in your favorite graphics program, you get these beautiful fractals:
To create your own fractal, follow these steps:
init()
Init()
test()
Test()
true
color(_N, _R, _G, _B)
_R
_G
_B
_N
Take a look at the mandelbrot, julia and cactus classes for.