Fractals

From NUI Group Community Wiki

Jump to: navigation, search

The quickest explination I can think of is. Fractals are create by using recursion to run the same drawing function, but for every iteration length and thickness decreases a little. Right off the bat, I want to confess that recursion has to be my least understood topic when it come to programming. Though the idea doesn't sound too hard, reloop through the same function a certian number of times.

I started by going to one of my favorite sites for Flash tutorial inspiration, <a href="http://www.kirupa.com/" target="_blank">Kirupa</a>. And came across a nice <a href="http://www.kirupa.com/developer/actionscript/fractal_index.htm" target="_blank">fractal recursion tutorial</a>.

Now, for this tutorial/example it'll probably be best to try something new, because there is a lot more going on than in previous experiments. First if you haven't hooked into my <a href="http://code.google.com/p/multitouchas3experiments/source/checkout" target="_blank">Google Code page multitouchas3experiments via SVN</a>, you can do so here. If you're unfamiliar with that, I've exported all the current files up till today into this <a href="http://multitouchas3experiments.googlecode.com/files/MT-AS3experiments-042909.zip">zip for download</a> [right click, save as...].

Once you've got it on your computer, unzip it if you need to and in the root of that folder, find touchVines.fla. And open that up. Nothing out of the ordinary I hope. Black background, a logo and in the Document Class in the Properties panel, app.demo.MyTouchApp.touchVinesBase. Click on the icon of the pencil.

You should now be looking at file name touchVinesBase.as this is contained in this folder structure: \app\demo\MyTouchApp which you'll see is what is written in the package path. This file is the main Multi-touch nuts and bolts of the application, in fact, if you were to open \app\demo\MyTouchApp\PressAndAppear.as they'd both look nearly identical. So lets take a closer look.

package app.demo.MyTouchApp{

	import flash.events.TUIO;
	import flash.events.TouchEvent;
	import flash.events.Event;
	import app.demo.MyTouchApp.touchVines;
	import flash.display.*;
	import flash.net.*;
	import flash.geom.*;
	//import flash.system.*;
	import fl.transitions.Tween;
	import fl.transitions.easing.*;

	public class touchVinesBase extends Sprite {

		public function touchVinesBase():void {

			//--------connect to TUIO-----------------
			TUIO.init(this,'localhost',3000,'',true);
			trace("MyTouchApp Initialized");
			//----------------------------------------
			addEventListener(TouchEvent.MOUSE_MOVE, onTouchMove);
			addEventListener(TouchEvent.MOUSE_DOWN, onTouchDown);
			//addEventListener(Event.ENTER_FRAME, testMemory)
		}
		public function onTouchDown(e:TouchEvent):void {
			var dynamicObj:touchVines = new touchVines();
			dynamicObj.name = e.ID.toString();
			dynamicObj.x = e.stageX;
			dynamicObj.y = e.stageY;
			addChild(dynamicObj);
			dynamicObj.addEventListener(Event.ENTER_FRAME, checkForRemoval);
		}
		public function onTouchMove(e:TouchEvent):void {
			var target:DisplayObject = getChildByName(e.ID.toString());
			target.x = e.stageX;
			target.y = e.stageY;
		}
		public function checkForRemoval(e:Event):void {
			if (!TUIO.getObjectById(e.target.name)) {
				e.target.removeEventListener(Event.ENTER_FRAME, checkForRemoval);
				removeChild(e.target as touchVines);
			}
		}
		/*public function testMemory(e:Event):void{
		trace("System memory used by this program: " + System.totalMemory);// i finally found something to track memory in tracing
		//on my computer, garbage collecting seems to keep the flash around 7-10Megs of memory usage.
		//without garbage collecting, memmory usage would just keep increasing till the flash halted the computer.

		}*/
	}
}

These are the interactive parts of this AS file which works the same way as PressAndAppear.as. What is happening? The background is listening for touch downs and touch moves. On a touch down in PressAndAppear.as Flash was pulling a movie clip from the library to put onto the stage. in touchVinesBase.as, it is looking for something different.

var dynamicObj:touchVines = new touchVines();

But wait. What is touchVines() ?

touchVines() is another external Actionscript file. Which can be found here: \app\demo\MyTouchApp\touchVines.as locate that file and open it. Creating an external AS file like this is kind of like having a movie clip in the library that is completely code driven. In fact that is what this is. But before you can add this item to the stage with a new variable declaration and an addChild() it still needs to be imported, which is done with this line along with your other import declarations.

import app.demo.MyTouchApp.touchVines;

All the code to get a fractal to animate via the ENTER_FRAME event listener happens in this imported file. And since for now we just want the animation in touchVines.as to happen instantly on a touch down, touchVinesBase.as will control the creation and destruction of all the touchVines().

package app.demo.MyTouchApp{

	import flash.display.*;
	import flash.net.*;
	import flash.geom.*;
	import flash.events.Event;
	import flash.filters.BitmapFilter;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.BlurFilter;

	public class touchVines extends MovieClip {

		public var start_x;
		public var start_y;
		public var length;
		public var size;
		public var end_x;
		public var end_y;
		public var blurVal;
		public var colorSet;
		public var mainHolder:MovieClip = new MovieClip();
		public var fractHoldler:MovieClip = new MovieClip();

		public function touchVines():void {
			mainHolder.addChild(fractHoldler);
			addChild(mainHolder);
			fractHoldler.x = 0;
			fractHoldler.y = 0;

			blurVal = 1;
			start_x = 0;
			start_y = 0;
			length = 100;
			size = 10;
			colorSet = (Math.random()*0xFFFFFF);
			this.addEventListener(Event.ENTER_FRAME, drawFractal);
		}
		public function drawFractal(e:Event):void {
			var angle = randomNumber(1, 360);
			var my_line:MovieClip = new MovieClip();
			fractHoldler.addChild(my_line);
			my_line.graphics.lineStyle(size, colorSet, 1.0, false, "normal", null, CapsStyle.ROUND, 3);
			my_line.graphics.moveTo(start_x, start_y);

			end_x = start_x+length*Math.cos(angle);
			end_y = start_y+length*Math.sin(angle);
			my_line.graphics.lineTo(end_x, end_y);

			start_x = end_x;
			start_y = end_y;

			var filter:BitmapFilter = getBitmapFilter(blurVal);
			var myFilters:Array = new Array();
			myFilters.push(filter);
			my_line.filters = myFilters;

			length = length*.8;
			size = size-1;

			if (size == 0) {
				colorSet = (Math.random()*0xFFFFFF);
				blurVal = randomNumber(1, 5);
				start_x = 0;
				start_y = 0;
				length = 50;
				size = 10;
			}

		}
		public function getBitmapFilter(thisVal):BitmapFilter {
			var blurX:Number = thisVal;
			var blurY:Number = thisVal;
			return new BlurFilter(blurX,blurY,BitmapFilterQuality.HIGH);
		}
		public function randomNumber(low:Number=NaN, high:Number=NaN):Number {
			var low:Number = low;
			var high:Number = high;
			if (isNaN(low)) {
				throw new Error("low must be defined");
			}
			if (isNaN(high)) {
				throw new Error("high must be defined");
			}
			return Math.round(Math.random() * high - low) + low;
		}
	}
}

So this is all the code. Lets see what it does.

	import flash.display.*;
	import flash.net.*;
	import flash.geom.*;
	import flash.events.Event;
	import flash.filters.BitmapFilter;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.BlurFilter;

First it needs to declare everything it needs to work. This you can get from the Kirupa tutorials.

	public class touchVines extends MovieClip {

		public var start_x;
		public var start_y;
		public var length;
		public var size;
		public var end_x;
		public var end_y;
		public var blurVal;
		public var colorSet;
		public var mainHolder:MovieClip = new MovieClip();
		public var fractHoldler:MovieClip = new MovieClip();

Next is lists all the variables it needs to run. Also learned through using Kirupa's tutorial.

		public function touchVines():void {
			mainHolder.addChild(fractHoldler);
			addChild(mainHolder);
			fractHoldler.x = 0;
			fractHoldler.y = 0;

			blurVal = 1;
			start_x = 0;
			start_y = 0;
			length = 100;
			size = 10;
			colorSet = (Math.random()*0xFFFFFF);
			this.addEventListener(Event.ENTER_FRAME, drawFractal);
		}

which leads us to the initial declaration of the values needed for the recursion. Something really cool I found searching around is that you can pick a random HEX color with one line of code, Brilliant!

colorSet = (Math.random()*0xFFFFFF);

So for every frame that goes by, we want a line drawn.

		public function drawFractal(e:Event):void {
			var angle = randomNumber(1, 360);
			var my_line:MovieClip = new MovieClip();
			fractHoldler.addChild(my_line);
			my_line.graphics.lineStyle(size, colorSet, 1.0, false, "normal", null, CapsStyle.ROUND, 3);
			my_line.graphics.moveTo(start_x, start_y);

			end_x = start_x+length*Math.cos(angle);
			end_y = start_y+length*Math.sin(angle);
			my_line.graphics.lineTo(end_x, end_y);

			start_x = end_x;
			start_y = end_y;

			var filter:BitmapFilter = getBitmapFilter(blurVal);
			var myFilters:Array = new Array();
			myFilters.push(filter);
			my_line.filters = myFilters;

			length = length*.8;
			size = size-1;

A lot of the function is based on randomness. But it doesn't have to be. Play around with the equasions to see how much more or even less control you can have over the line being draw. Now this first iteration of the enterframe function will draw the line, but then start decreasing the length and the width (size) of the line. You can also change the angle of the line by setting and equasion like angle = angle*.5 by the lenth and size equasions.

Since the initial declarations of all the public variables were defines outside of all the public classes, all the public functions are able to use the variables. They just change the values at different times. public function touchVines():void { sets the very first values of the variables. And its about time to note that this isn't really the kind of recursion which was talked about in Kirupa. The recursion happening in Kirupa's tutorials happen all in one frame, this code makes one line of the 'vine' pre frame of animation. And the way it would work is that there is no stopping point, if the code were to stop here, the 'vine' would keep growing, but the length and thickness would continue shrinking. Luckily, we can use the size to help by restarting the initial values.

			if (size == 0) {
				colorSet = (Math.random()*0xFFFFFF);
				blurVal = randomNumber(1, 5);
				start_x = 0;
				start_y = 0;
				length = 50;
				size = 10;
			}

Essentially, it's just returning all the values to the starting point as the initially called touchVines() function. As for the rest of the funtions:

		public function getBitmapFilter(thisVal):BitmapFilter {
			var blurX:Number = thisVal;
			var blurY:Number = thisVal;
			return new BlurFilter(blurX,blurY,BitmapFilterQuality.HIGH);
		}

This one just sets a blur value on the line, just for some extra visual spicyness.

		public function randomNumber(low:Number=NaN, high:Number=NaN):Number {
			var low:Number = low;
			var high:Number = high;
			if (isNaN(low)) {
				throw new Error("low must be defined");
			}
			if (isNaN(high)) {
				throw new Error("high must be defined");
			}
			return Math.round(Math.random() * high - low) + low;
		}

And this is a random number generator which you can find with a quick google search.

My best advise I can give it to just experiment with this one. There are a lot of things you can mess with and expand on. Have fun with it.

Happy Experimenting.