package
{
import com.bit101.components.HSlider;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.utils.setTimeout;
/**
* Calculating fractional mandelbrot using
* polar vs cartesian form of complex numbers.
*
* Click below slider to zoom in.
*
* @author makc
*/
public class Test extends Sprite
{
public var bmp:BitmapData;
public var pow:HSlider;
public var x0:Number = 0;
public var y0:Number = 0;
public var z0:Number = 100;
public function Test ()
{
addChild (new Bitmap (bmp = new BitmapData (465, 464)));
// quick test of PolarComplex' add()
var foo:PolarComplex = new PolarComplex; foo.init (1, 2);
var bar:PolarComplex = new PolarComplex; bar.init (4, -7);
foo.add (bar); trace (foo.re (), foo.im ()); // 5, -5
pow = new HSlider (this, 10, 10, change);
pow.setSliderParams (2, 3, 2);
pow.width = 445;
pow.tick = 0.01;
render ();
stage.addEventListener (MouseEvent.CLICK, zoom);
}
public function change (whatever:* = null):void {
setTimeout (render, 100);
pow.enabled = false;
}
public function render ():void {
doMandelbrot (x0, y0, z0, pow.value, new CartesianComplex, new CartesianComplex);
doMandelbrot (x0, y0, z0, pow.value, new PolarComplex, new PolarComplex, 232);
pow.enabled = true;
}
public function zoom (whatever:*):void {
if (mouseY > pow.x + pow.height) {
var i:int = mouseX;
var j:int = mouseY % 232;
var cx:Number = x0 + (i - 232.5) / z0;
var cy:Number = y0 + (j - 116.0) / z0;
z0 *= 2;
x0 = cx;
y0 = cy;
change ();
}
}
public function doMandelbrot (centerX:Number, centerY:Number, zoom:Number, power:Number, Z:IComplex, C:IComplex, startY:int = 0):void {
bmp.lock ();
for (var i:int = 0; i < 465; i++)
for (var j:int = 0; j < 232; j++) {
var cx:Number = centerX + (i - 232.5) / zoom;
var cy:Number = centerY + (j - 116.0) / zoom;
C.init (cx, cy);
Z.init (cx, cy);
var m:int = 0, n:int = 20;
while (m < n) {
// Z = Z^P + C
Z.pow (power); Z.add (C);
// bail out?
cx = Z.re ();
cy = Z.im ();
if (cx * cx + cy * cy > 4) break;
m++;
}
bmp.setPixel (i, j + startY, 0x10101 * int (255 * m / n));
}
bmp.unlock ();
}
}
}
interface IComplex {
function init (x:Number, y:Number):void;
function add (c:IComplex):void;
function pow (p:Number):void;
function re ():Number;
function im ():Number;
}
class CartesianComplex implements IComplex {
private var x:Number = 0;
private var y:Number = 0;
public function init (x:Number, y:Number):void {
this.x = x;
this.y = y;
}
public function add (c:IComplex):void {
this.x += c.re ();
this.y += c.im ();
}
public function pow (p:Number):void {
var ap:Number = p * Math.atan2 (y, x);
var rp:Number = Math.pow (x * x + y * y, p / 2);
this.x = rp * Math.cos (ap);
this.y = rp * Math.sin (ap);
}
public function re ():Number { return this.x; }
public function im ():Number { return this.y; }
}
class PolarComplex implements IComplex {
private var a:Number = 0;
private var r:Number = 0;
public function init (x:Number, y:Number):void {
a = Math.atan2 (y, x);
r = Math.sqrt (x * x + y * y);
}
public function add (c:IComplex):void {
// I have no magic formula
// so let's do it hard way
var ax:Number = re ();
var ay:Number = im ();
var bx:Number = c.re ();
var by:Number = c.im ();
// c = a + b
var cx:Number = ax + bx;
var cy:Number = ay + by;
// r = |c|, but normalize a 1st
ax /= r;
ay /= r;
r = Math.sqrt (cx * cx + cy * cy);
// rotate a 90° left and store in b
bx = -ay;
by = +ax;
// transform c into basis a, b
var dx:Number = cx * ax + cy * ay;
var dy:Number = cx * bx + cy * by;
// finally, calculate new angle
a = a + Math.atan2 (dy, dx);
}
public function pow (p:Number):void {
a = a * p;
r = Math.pow (r, p);
}
public function re ():Number { return r * Math.cos (a); }
public function im ():Number { return r * Math.sin (a); }
}