Mandelbrot set with haXe

After some studies about the complex numbers, I saw this pretty thing – the only really nice example for an useful complex number usage.
Looks impressive ? For the first moment, … yeah – but isn’t that hard.


mb

#edit:
Yeehar, yesterday Flash Player 10.1 was released – and the performance increase is incredible!! For the step-size 80 I had 12 fps in average with the old one. With the new one i get the nearly full 30 fps in average … and even if i increase the step-size. Good job, Adobe.

#update:
Now there is an better rendering technique included. Now you can type in the calculation rate, instead of ony clicking up and down. Also you can drag the image and zoom in and out by using your mouse wheel! Now it’s a lot of more fun to explore!

The Mandelbrot set is a is a very simple set, in mathematics
z_{n+1} = z_{n}^2 + c with z_{0}=\langle 0 {;} 0 \rangle

In simple words: Create a for-loop and summarize every result to the old value until the “break condition” occurs. But let’s do this step by step.

Grab the sources

haXe is very similar to actionscript. There are some syntax changes, but nothing too difficult. I’m working in FlashDevelop, so everything I need is created for me. Just start with Project->New Project->”Haxe” AS3 Project. This gives us:

package ;
import flash.display.Sprite;
class Main extends Sprite
{
	static function main()
	{
	}
}

Now, let’s create a custom class which will build our Mandelbrot. Create an Array, a complex number as well as an count attribute. We will need this in our calculations. I wrote the ComplexNumber class, so feel free to download the source-files.

package ;
import flash.display.Bitmap;
import flash.display.BitmapData;
 
class Mandelbrot extends Bitmap
{
	// draw preferences
	public var colorTable(default, null):Array;
 
	// draw variables
	private var cnZ:ComplexNumber;
	private var count:Int;
 
	public function new(width:Int, height:Int)
	{
		super(new BitmapData(width, height, false));
	}
}

Now, let’s create an object of our new class in the Main.hx constructor:

// create Mandelbrot
mb = new Mandelbrot(600, 400);
Lib.current.addChild(mb);

That’s all. Easy, isn’t it?

Let’s go to the interesting part of the application.
Due to the fact that inline methods maybe run faster than normal ones, lets create one. For a more individual method processing we should create some parameters to define the area, which we want to render.

inline public function drawSegment(startX:Int, endX:Int, startY:Int, endY:Int):Void

Remember that formula at the top of the page?
The kicker of this formula is that we have to calculate for every pixel this set. So, create two-for loops which iterate through each pixel of the bitmap, and start the mandelbrot-algorithm.

// create image
for (iX in startX...endX)
	for (iY in startY...endY)

The mandelbrot-algorithm:
z_{0}=\langle 0 {;} 0 \rangle means, create a new complex number with relative and imaginary party equals to 0.
z_{n+1} = z_{n}^2 + c means, add and add and add and …. add an complex number to the start number. But, which complex number ?
We have to add a complex number containing the relative part equal to the x-coordinate and the imaginary part equal to the y-coordinate.

Now, we don’t want to calculate this set to infinity. We create an break condition, the maximum amount of our calculations. Let’s take a look at the iteration.

cnZ = new ComplexNumber(0, 0);
count = 0;
 
while(count < 20) 
{ 
	// z(n+1) = z(n)^2 + c 	
	cnZ = (cnZ.power()).add(new ComplexNumber(iX* 0.01,iY * 0.01));
 	count++;
 
 	if (cnZ.abs() > 2)
		break;
}

With some testings I experienced that 20 is a good number to get good results. 0.01 is a scaling factor, which we need. Else the Mandebrot would too small to see.
You can increase/decrease this factor to zoom in/out.
The Mandelbrot-set is defined: if the length of the complex number is higher than 2, this value is not a part of the Mandelbrot-set. That means for us, if this value is higher than 2, we can break up the current loop.

Now we have to show our results on the screen. But wait, how do we get the colors ? – We have do define a color-table for all iteration-steps. Meaning, for 20 steps we need 20 colors. Let’s create another function to create a color-table for us.

private function createColorTable(iterations:Int = 20):Void
{
	colorTable = new Array();
 
	for (i in 0...iterations)
		colorTable.push(RGB.hsv(colorAngle, 1, (i / iterations)));
}

I modified the RGB class, i found on the web.

Now finally, we can create our output. Set every pixel from our bitmapData. The color is defined by the steps we needed to calculate the Mandelbrot-set.

bitmapData.setPixel(iX + cast(width / 2, Int), iY + cast(height / 2, Int), colorTable[count]);

In the Main.hx class we have to call our function:

mb.drawSegment(cast( -(stage.stageWidth / 2), Int), // -half width
		cast((stage.stageWidth / 2), Int), 	// +half width
		cast( -(stage.stageHeight / 2), Int), // -half height
		cast((stage.stageHeight / 2), Int)); // +half height

That’s all :) .
The complete source from our Mandelbrot-function:

/** the core-function to draw a segment of the Mandelbrot **/
inline private function drawSegment(startX:Int, endX:Int, startY:Int, endY:Int):Void
{
	// create image
	for (iX in startX...endX)
	for (iY in startY...endY)
	{
		cnZ = new ComplexNumber(0, 0);
		count = 0;
 
		while(count < 20) 		
		{ 
			// z(n+1) = z(n)^2 + c
 			cnZ = (cnZ.power()).add(new ComplexNumber(iX * 0.01, iY * 0.01));
 			count++;
 
	 		if (cnZ.abs() > 2)
				break;
		}
		bitmapData.setPixel(iX + cast(width / 2, Int), iY + cast(height / 2, Int), colorTable[count]);
	}
}

Tags: , ,

One Response to “Mandelbrot set with haXe”

  1. Looks like you are a true professional. Did you study about the theme? lol

Leave a Reply