ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Creating a Mandelbrot-set
http://www.actionscript.org/resources/articles/614/1/Creating-a-Mandelbrot-set/Page1.html
Milan Toth
Milan Toth is the Chief Flash Developer of Jasmin Media Group, he created one of the world's biggest flash media server system. He loves Eclipse and OS X, AS3 and JAVA, sci-fi and horror, metal and electronic.
 
By Milan Toth
Published on May 30, 2007
 
In this article we create very-very beautiful Mandelbrot-sets

A Mandelbrot-set
A Mandelbrot set marks the set of points in the complex plane where z(i) = z(i-1)*z(i-1) + c function does not tend to infinity. These points marked black, but the nearby points, which aren't in the set, is colored according to how fast they tend to infinity. So we can draw very spectacular nutmeats :) And in addition we can zoom in almost continously ( based on the floating-point handling capacity of our platform :).

Complex numbers have two parts: a real and an imaginery part: z = ( r , i )

Addition: z1 + z2 = ( r1 + r2 , i1 + i2 )
Multiplication: z1 * z2 = ( r1 * r2 - i1 * i2 , r1 * i2 + r2 * i1 ).

Based on these operations we can simplify the Mandelbrot function.

z(2) = z(1) * z(1) + c
( z(2)r , z(2)i ) = ( z(1)r , z(1)i )( z(1)r , z(1)i ) + ( cr , ci );
( z(2)r , z(2)i ) = ( z(1)r * z(1)r - z(1)i * z(1)i , z(1)r * z(1)i + z(1)r * z(1)i ) + ( cr , ci );
z(2)r = z(1)r * z(1)r - z(1)i * z(1)i + cr;
z(2)i = z(1)r * z(1)i + z(1)r * z(1)i + ci = 2 * z(1)r * z(1)i + ci;

| zi | > 2 -> we have to stay in the circle with the radius 2

| zi | = sqrt( zr * zr + zi * zi ) > 2; 
| zi | exp( 2 ) = zr * zr + zi * zi > 4

Let's translate it to AS3.

[as]
package
{
   
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
   
   
    public class Mandelbrot extends Sprite
    {
   
        // display dimensions, set this as default size
        private static var DWTH:Number = 550;
        private static var DHTH:Number = 380;
       
        // maximum function cycles
        private static var CYCLES:Number = 128;
       
        // the set's bitmapdata
        private var bitmapData:BitmapData;

       
        public function Mandelbrot ( )
        {
           
            // creating new bitmap instance
            var setBitmap:Bitmap = new Bitmap( );
           
            // creating new bitmapdata
            bitmapData = new BitmapData( DWTH , DHTH , false , 0x000000 );
           
            // attaching bitmapdata to bitmap
            setBitmap.bitmapData = bitmapData;
           
            // attaching bitmap to display list
            addChild( setBitmap );
           
            // start drawing
            drawSet( -2 , 1 , -1 , 1 );
           
        }
       
       
        public function drawSet ( rmin:Number ,     // interval minimum on real axis
                                  rmax:Number ,     // interval maximum on real axis
                                  imin:Number ,     // interval minimum on imaginary axis
                                  imax:Number ):void
        {
           
            // real axis stepping
            var rStep:Number = ( rmax - rmin ) / DWTH;
            // imaginary axis stepping
            var iStep:Number = ( imax - imin ) / DHTH;
           
            // actual real coordinate
            var r:Number;
            // actual imaginary coordinate
            var i:Number;
           
            // looping thorugh every display point
            for ( var px:int = 0 ; px < DWTH ; px++ )
            {
               
                r = rmin + px * rStep;
               
                for ( var py:int = 0 ; py < DHTH ; py++ )
                {
                   
                    i = imin + py * iStep;
                   
                    var color:uint;
                    var cycles:int = getCycles( r , i );
                   
                    // if cycles == CYCLES, the function tends to infinity
                    if ( cycles == CYCLES ) color = 0x000000;
                    // in not, we generate a greenish colour with bitwise left shit
                    else color = cycles << 16 | ( cycles + 50 ) << 8 | cycles;
                   
                    // putting pixel
                    bitmapData.setPixel( px , py , color );
                   
                }
               
            }
           
        }
       
        // calculating function cycle number
       
        public function getCycles ( r:Number , i:Number ):uint
        {
           
            var zr:Number = 0;
            var zi:Number = 0;
           
            var cr:Number = r;
            var ci:Number = i;
           
            // z's old values
            var ozr:Number = 0;
            var ozi:Number = 0;
           
            // z's old square values
            var zrsq:Number = 0;
            var zisq:Number = 0;
           
            for ( var a:int = 0 ; a < CYCLES ; a++ )
            {
               
                zi = ozr * ozi * 2 + ci;
                zr = zrsq - zisq + cr;
               
                zrsq = zr * zr;
                zisq = zi * zi;
               
                ozr = zr;
                ozi = zi;
               
                if ( ( zrsq + zisq ) > 4 ) break;
               
            }   
           
            return a;
           
        }
       
    }
   
}
[/as]

Try it. Beautiful, eh? You can make it more detailed, if you set CYCLES higher, but it will also be slower. Check it under attachments.

And what about that bitwise-shift thing? Simple, we needed a 24 bit length colour code from a number under 128. So, i shifted 128 with 16 bits, to the "red" range ( 2(25) - 2(16) ), then i added 50, because i wanted green to be the dominant color, then shifted left with 8 bits to the green range ( 2(15) - 2(8) ), and i left it in the "blue" range, and made a logical "OR" between these values.

Roto - zoom
Well, let's make our set more spectacular. Let's zoom coordinate interval continously, and also rotate system to make a roto-zoom effect.

For roto-zoom we need five continously changing variables : the interval borders and the angle. Create a new function called rotoZoom, which modifies these values periodically, based on Stage's enterframe event. Rotation happens in drawset, before requesting a new coordinate from getCycles. Let's see:

[as]
package
{
   
    import flash.geom.Point;
    import flash.events.Event;
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
   
   
    public class Mandelbrot extends Sprite
    {
   
        // display dimensions, set this as default size
        private static var DWTH:Number = 200;
        private static var DHTH:Number = 200;
       
        // maximum function cycles
        private static var CYCLES:Number = 30;
       
        private var xmin:Number = -1;
        private var xmax:Number = 1;
        private var ymin:Number = -1;
        private var ymax:Number = 1;
        private var angle:Number = 0;

        private var step:Number = 0.01;
      
        // the set's bitmapdata
        private var bitmapData:BitmapData;

       
        public function Mandelbrot ( )
        {
           
            // creating new bitmap instance
            var setBitmap:Bitmap = new Bitmap( );
           
            // creating new bitmapdata
            bitmapData = new BitmapData( DWTH , DHTH , false , 0x000000 );
           
            // attaching bitmapdata to bitmap
            setBitmap.bitmapData = bitmapData;
           
            // attaching bitmap to display list
            addChild( setBitmap );
           
            // start trigger
            addEventListener( Event.ENTER_FRAME , rotoZoom );
           
        }
       
       
        public function rotoZoom ( event:Event ):void
        {
           
            xmin += step;
            xmax -= step;
            ymin += step;
            ymax -= step;
           
            angle += step;
           
            // start drawing
            drawSet( xmin , xmax , ymin , ymax );
           
        }
       
       
        public function drawSet ( rmin:Number ,     // interval minimum on real axis
                                  rmax:Number ,     // interval maximum on real axis
                                  imin:Number ,     // interval minimum on imaginary axis
                                  imax:Number ):void
        {
           
            // real axis stepping
            var rStep:Number = ( rmax - rmin ) / DWTH;
            // imaginary axis stepping
            var iStep:Number = ( imax - imin ) / DHTH;
           
            // actual real coordinate
            var r:Number;
            // actual imaginary coordinate
            var i:Number;
           
            // looping thorugh every display point
            for ( var px:int = 0 ; px < DWTH ; px++ )
            {
               
                r = rmin + px * rStep;
               
                for ( var py:int = 0 ; py < DHTH ; py++ )
                {
                   
                    i = imin + py * iStep;
                   
                    // needed for polar length
                    var point:Point = new Point( r , i );
                    // calculating new angle
                    var alfa:Number = Math.atan2( i , r ) + angle;
                    // rotating point based on polar coordinate
                    point = Point.polar( point.length , alfa );   
                   
                    var color:uint;
                    var cycles:int = getCycles( point.x , point.y );
                   
                    // if cycles == CYCLES, the function tends to infinity
                    if ( cycles == CYCLES ) color = 0x000000;
                    // in not, we generate a greenish colour with bitwise left shit
                    else color = cycles << 16 | ( cycles + 50 ) << 8 | cycles;
                   
                    // putting pixel
                    bitmapData.setPixel( px , py , color );
                   
                }
               
            }
           
        }
       
        // calculating function cycle number
       
        public function getCycles ( r:Number , i:Number ):uint
        {
           
            var zr:Number = 0;
            var zi:Number = 0;
           
            var cr:Number = r;
            var ci:Number = i;
           
            // z's old values
            var ozr:Number = 0;
            var ozi:Number = 0;
           
            // z's old square values
            var zrsq:Number = 0;
            var zisq:Number = 0;
           
            for ( var a:int = 0 ; a < CYCLES ; a++ )
            {
               
                zi = ozr * ozi * 2 + ci;
                zr = zrsq - zisq + cr;
               
                zrsq = zr * zr;
                zisq = zi * zi;
               
                ozr = zr;
                ozi = zi;
               
                if ( ( zrsq + zisq ) > 4 ) break;
               
            }   
           
            return a;
           
        }
       
    }
   
}
[/as]

It is quite good now. But we should try to zoom to a central point, and we should move all local variables to global scope, because local variables in a loop - hell like this can eat plenty of memory and processor time.

My final version is:

[as]
package
{
   
    import flash.geom.Point;
    import flash.events.Event;
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
   
   
    public class Mandelbrot extends Sprite
    {
   
        // display dimensions, set this as default size
        public static var DWTH:Number = 150;
        public static var DHTH:Number = 150;
       
        // maximum function cycles
        public static var CYCLES:Number = 20;
       
        public var xmin:Number = -2;
        public var xmax:Number = 1;
        public var ymin:Number = -1;
        public var ymax:Number = 1;
       
        public var angle:Number = 0;
        public var step:Number = Math.PI / 64;
       
        public var xcenter:Number = 1;
        public var ycenter:Number = 0;
       
        // the set's bitmapdata
        public var bitmapData:BitmapData;
       
        // variables for function drawSet
        public var rStep:Number;
        public var iStep:Number;
        public var r:Number;
        public var i:Number;
       
        public var tempPoint:Point;
        public var alfaAngle:Number;
       
        public var color:uint;
        public var cycles:int;

        // variables for function getCycles
        public var zr:Number;
        public var zi:Number;
           
        public var cr:Number;
        public var ci:Number;
           
        public var ozr:Number;
        public var ozi:Number;
           
        public var zrsq:Number;
        public var zisq:Number;

       
        public function Mandelbrot ( )
        {
           
            // creating new bitmap instance
            var setBitmap:Bitmap = new Bitmap( );
           
            // creating new bitmapdata
            bitmapData = new BitmapData( DWTH , DHTH , false , 0x000000 );
           
            // attaching bitmapdata to bitmap
            setBitmap.bitmapData = bitmapData;
           
            // attaching bitmap to display list
            addChild( setBitmap );
           
            // start trigger
            addEventListener( Event.ENTER_FRAME , rotoZoom );
           
        }
       
       
        public function rotoZoom ( event:Event ):void
        {
           
            xmin += ( xcenter - xmin ) / 64;
            xmax += ( xcenter - xmax ) / 64;
            ymin += ( ycenter - ymin ) / 64;
            ymax += ( ycenter - ymax ) / 64;
           
            angle += step;
           
            // start drawing
            drawSet( xmin , xmax , ymin , ymax );
           
        }
       
       
        public function drawSet ( rmin:Number ,     // interval minimum on real axis
                                  rmax:Number ,     // interval maximum on real axis
                                  imin:Number ,     // interval minimum on imaginary axis
                                  imax:Number ):void
        {
           
            // real axis stepping
            rStep = ( rmax - rmin ) / DWTH;
            // imaginary axis stepping
            iStep = ( imax - imin ) / DHTH;
           
            // looping thorugh every display point
            for ( var px:int = 0 ; px < DWTH ; px++ )
            {
               
                r = rmin + px * rStep;
               
                for ( var py:int = 0 ; py < DHTH ; py++ )
                {
                   
                    i = imin + py * iStep;
                   
                    // needed for polar length
                    tempPoint = new Point( r , i );
                    // calculating new angle
                    alfaAngle = Math.atan2( i , r ) + angle;
                    // rotating point based on polar coordinate
                    tempPoint = Point.polar( tempPoint.length , alfaAngle );   
                   
                    cycles = getCycles( tempPoint.x , tempPoint.y );
                   
                    // if cycles == CYCLES, the function tends to infinity
                    if ( cycles == CYCLES ) color = 0x000000;
                    // in not, we generate a greenish colour with bitwise left shit
                    else color = cycles << 16 | ( cycles + 50 ) << 8 | cycles;
                   
                    // putting pixel
                    bitmapData.setPixel( px , py , color );
                   
                }
               
            }
           
        }
       
        // calculating function cycle number
       
        public function getCycles ( argR:Number , argI:Number ):uint
        {
           
            zr = 0;
            zi = 0;
           
            cr = argR;
            ci = argI;
           
            ozr = 0;
            ozi = 0;
   
            zrsq = 0;
            zisq = 0;
           
            for ( var a:int = 0 ; a < CYCLES ; a++ )
            {
               
                zi = ozr * ozi * 2 + ci;
                zr = zrsq - zisq + cr;
               
                zrsq = zr * zr;
                zisq = zi * zi;
               
                ozr = zr;
                ozi = zi;
               
                if ( ( zrsq + zisq ) > 4 ) break;
               
            }   
           
            return a;
           
        }
       
    }
   
}

Check it under attachments.
[/as]