Mandelbrot-halmaz ActionScript 3-mal

Az ötödik rész előtt lazításképpen írjunk gyorsan egy Mandelbrot-halmaz rajzoló algoritmust. A Mandelbrot halmaz azon pontok halmaza a komplex számsíkon, amelyekre a z(i) = z(i-1)*z(i-1) + c rekurzív függvény a nullához konvergál. Ezeket a pontokat simán feketével jelöljük, viszont a nem a halmazba tartozó környező pontokat aszerint színezzük, hogy milyen gyorsan tartanak a végtelenbe. Magyarul iszonyú dizájnos dióbeleket lehet vele rajzolni, és a végtelenségig tudunk zoomolni benne.

Egy komplex szám valós és képzetes részből áll z = ( r , i ). Összeadásnál külön kell összeadni őket, a szorzásuk meg szimpla vektoriális szorzás: z1 * z2 = ( r1 , i1 )*( r2 , i2 ) = ( r1*r2 – i1*i2 , r1*i2 + r2*i1 ). Ez alapján már gyerekjáték megírni a generátorunkat.

Nagy jóság, hogy az AS3-as virtual machine már igen gyorsan számol, és pörgeti a for ciklusokat, és az AS2-vel ellenétben nem kell időzítenünk a számolást, egy for ciklusból ledarálja az egészet pár másodperc alatt.

Hozzunk létre egy új projectet MandelbrotSet néven, és gépeljünk:

//begin
//
//
package 
{
	//
	//
	//
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.StageScaleMode;
	//
	//
	//
	public class MandelbrotSet extends Sprite
	{
		//
		//
		//
		private var mbCNT:Bitmap;
		private var mbBMP:BitmapData;
		//
		private var maxCycles:int = 128; // meddig pörgessünk egy adott pontot
		//
		private var LEFT:Number = -2; // a komplex számsík valós minimuma
		private var RIGHT:Number = 1; // a komplex számsík valós maximuma
		private var BOTTOM:Number = -1; // a komplex számsík imaginárius minimuma
		private var TOP:Number = 1; // a komlex számsík imaginárius maximuma
		//
		private var WTH:int = 550; // a rajzterünk szélessége
		private var HTH:int = 380; // a rajztér magassága
		//
		private var XSTEPPING:Number = ( RIGHT - LEFT ) / WTH; // a valós rész lépésköze
		private var YSTEPPING:Number = ( TOP - BOTTOM ) / HTH; // az imaginárius rész lépésköze
		//
		//az alábbiakat fölvehetnénk lokális változóknak is, de a nagy számú memóriafoglalás lassítja a futást
		//
		private var zr:Number; //a változó valós változónk
		private var zi:Number; // a változó imaginárius változónk
		private var cr:Number; // a fix pontunk valós része
		private var ci:Number; // a fix pontunk imaginárius része
		private var zrsq:Number; //zr négyzete
		private var zisq:Number; //zi négyzete

Definiáltuk a konstansokat, és a munkaváltozókat, ahol lehetett, ott Number helyett int-et használunk, ugyanis akkor nem kell a procinak lebegőpontos számításokat végeznie, tehát szárnyal. Kell egy BitmapData is, amit egy Bitmap osztály fog tartalmazni.

		//
		//
		//
		public function MandelbrotSet( )
		{
			//
			stage.scaleMode = StageScaleMode.NO_SCALE;
			//
			mbBMP = new BitmapData( WTH , HTH , false , 0x000000 );
			mbCNT = new Bitmap( mbBMP );
			addChild( mbCNT );
			//
			//forral sokkal gyorsabb, mint do while-al
			//
			for ( var a:int = 0 ; a < WTH ; ++a )
				for ( var b:int = 0 ; b < HTH ; ++b )
					calcPoint( a , b );
			//
		}

A konstruktorban fölrántjuk a Bitmap-ünket, és a rajzterünk összes koordinátájára kiszámoljuk a neki megfelelő komplex síkbeli pontra vonatkoztatott függvény értékének megfelelő színt.

		//
		//
		//
		private function calcPoint( xp:int , yp:int ):void
		{
			//
			zr = 0; //a komplex számnunk valós része
			zi = 0; //a komplex számunk imaginárius része
			cr = LEFT + XSTEPPING * xp; //az aktuálisan vizsgált pontunk valós része
			ci = BOTTOM + YSTEPPING * yp; //az aktuálisan vizsgált pontunk imaginárius része
			//
			zrsq = 0;
			zisq = 0;
			//
			for ( var a:int = 0 ; a < maxCycles ; ++a )
			{
				//
				zi = zr * zi * 2 + ci;
				zr = zrsq - zisq + cr;
				//
				zrsq = zr * zr;
				zisq = zi * zi;
				//
				if ( zrsq + zisq > 4 ) //  a végtelenbe tart
				{
					//
					var color:Number = a << 16 | ( a + 50 ) << 8 | a ;
					mbBMP.setPixel( xp , yp , color );
					return;
					//
				}
				//
			}
			//
			mbBMP.setPixel( xp , yp , 0x000000 );
			//
		}
		//
		//
		//
	}
	//
	//
	//
}
//
//
//end

Magát a számolást pedig ez a függvény végzi, ha a rekurzívan generált komplex számunk abszolút értéke ( ami tulajdonképpen a valós és imaginárius oldalhosszúságú derékszögű háromszög átlója ) nagyobb 2-nél, akkor elhagyja az origótól számított 2 sugarú kör területét, és a függvény a végtelenbe tart.

Két kérdés merülhet fel: egyrész honnan jön a zi = zr*zi*2 + ci, és az alatta található sor? A megoldás egyszerű: kézbe kell venni a programozó két legjobb barátját, a papírt és ceruzát, és levezetni a z(i) = z(i-1)z(i-1) + c egyenlőséget a komplex és imaginárius együtthatókkal kb így:

z(2) = z(1)*z(1) + c
(zr2,zi2) = (zr1,zi1)(zr1,zi1) + (cr,ci);
(zr2,zi2) = ( zr1*zr1 - zi1*zi1 , zr1*zi1 + zr1*zi1 ) + ( cr , ci );
zr2 = zr1*zr1 - zi1*zi1 + cr;
zi2 = zr1*zi1 + zr1*zi1 + ci = 2*zr1*zi1 + ci;
|zi| > 2 -> a komplex számunk abszolút értékének az origóközpontú 2 sugarú körben kell maradnia
|zi| = sqrt( zr*zr + zi*zi ) > 2;

A másik ami fölmerülhet: mi a csuda ez a sor:

a << 16 | ( a + 50 ) << 8 | a ;

A válasz szintén egyszerű: egy szimpla, maximum 128 nagyságú számból kellett generálnom valahogy egy 24 bites színkódot, amit zöldesnek szerettem volna, tehát fogtam magam, a 128 alatti számot bitszinten odébbtoltam 16-al, akkor ő fölcsúszott a "piros" ( 2(25) - 2 (16) ) tartományba, aztán hozzáadtam 50-et, hogy ő legyen a domináns szín, és fölcsúsztattam a "zöld" tartományba ( 2(15) - 2 (8) ), és meghagytam a "kék" tartományban is( 2(7) - 2(0) ), a három helyiértéket pedig bitszintű logikai "vagy"-gyal kapcsoltam össze.

Ha van 9-es playered, megnézheted a cuccot itt.

Összehasonlításképpen így nézett ki a dolog AS2-ben.

/hmm, basszus, ez gyorsabb mint gondoltam, lehet hogy érdemes lenne kicsit butítani a felbontáson és real-time zoomot nyomatni, klikk ide :) /

Játszogassatok el a kóddal, kifagyasztani a gépet úgy lehet, hogy a maxCycles-t magasra állítod, zoomolni meg úgy tudsz, hogy a LEFT,RIGHT,TOP,BOTTOM konstansokat a kívánt számsíkrészre húzod össze.

Jó matekozást :)

MilGra

A cikkhez kapcsolódó hozzászólásokat a fórum "Actionscript 3" topicjában várjuk.

HOZZÁSZÓLOK A CIKKHEZ

Kérjük, írja be véleményét!
írja be ide nevét