Spinny 3D Trees

I know, I know. Recursive trees are a thing that have been done to death by – well – pretty much everyone.

I just picked up a nice book on Processing the other day though and one of the first examples it gives is a nice recursive tree. In order to better understand the java/processing involved, I thought I’d take the time to convert it to a language I know. Actionscript, that is (in case you were wondering).

After I got it ported and figured out what it was doing and why, I started thinking, that might look kinda cool in 3D. Then I remembered Seb Lee-Delisle had created a ridiculously simple to use 3d drawing api, so I put the two together and came up with the below (roll over to spin the tree around and click to generate a new one).

Get Adobe Flash player

If you’d like to have a play with it yourself, the code’s below (and of course you’ll need Seb’s stuff from the link above).

Main

package {
 
	import com.sebleedelisle.draw3d.Graphics3D;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import geom.Point3D;
 
	/**
	 * 3D recursive tree based on processing example from
	 * "Processing: Creative Coding and Computational Art"
	 * http://www.amazon.com/Processing-Creative-Coding-Computational-Foundation/dp/159059617X/
	 * 
	 * Using Seb Lee-Delisle Graphics3D lib
	 * http://sebleedelisle.com/2009/11/simple-flash-3d-drawing-api/
	 * 
	 * 
	 * @author Devon O.
	 */
	[SWF(width='400', height='600', backgroundColor='#C0C0C0', frameRate='31')]
	public class Main extends Sprite {
 
		public static const DARK_BROWN:uint = 0x5C3317;
 
		private var counter:int = 0;
		private var counter2:int = 0;
		private var xg:Number = 5;
		private var yg:Number = 40;
		private var zg:Number = 5;
		private var trunkSegments:int = int(Math.random() * 4 + 3);
		private var pts:Vector.<Point3D> = new Vector.<Point3D>();
		private var branchLimit:int = 325;
		private var trunkLength:int = int(Math.random() * 50 + 130);
		private var lean2:Vector.<Number> = new Vector.<Number>(trunkSegments + 1, true);
		private var radius:Number = 8;
 
		private var _g3d:Graphics3D;
 
		public function Main():void {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
 
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
 
			// just some quick text
			var tf:TextField = new TextField();
			tf.selectable = false;
			tf.mouseEnabled = false;
			tf.defaultTextFormat = new TextFormat("_sans", 11);
			tf.autoSize = TextFieldAutoSize.LEFT;
			tf.text = "Click to generate a new tree.";
			tf.x = int(stage.stageWidth * .5 + 20);
			tf.y = int(stage.stageHeight - tf.height);
			addChild(tf);
 
			_g3d = new Graphics3D(this);
 
			trunk();
 
			addEventListener(Event.ENTER_FRAME, rotate);
			stage.addEventListener(MouseEvent.CLICK, drawTree);
		}
 
		// set all vars back to original
		private function reset():void {
			counter = 0;
			counter2 = 0;
			xg = 5;
			yg = 40;
			zg = 5;
			trunkSegments = int(Math.random() * 4 + 3);
			pts = new Vector.<Point3D>();
			trunkLength = int(Math.random() * 50 + 130);
			lean2 = new Vector.<Number>(trunkSegments + 1);
			radius = 5;
		}
 
		private function drawTree(event:MouseEvent):void {
			_g3d.clear();
			reset();
			trunk();
		}
 
		private function rotate(event:Event):void {
			var ratio:Number = ((stage.mouseX / stage.stageWidth) - .5) * 2;
			_g3d.rotateY(ratio * 4);
		}
 
		// draws the tree
		private function trunk():void {
			for (var i:int = 0; i < trunkSegments; i++) {
				var lean:Number = randRange(22);
				_g3d.lineStyle(radius + 3, DARK_BROWN);
				_g3d.moveTo2D(stage.stageWidth / 2 + lean2[i], stage.stageHeight - (trunkLength / trunkSegments) * i, 0);
				_g3d.lineTo2D(stage.stageWidth / 2 + lean, stage.stageHeight - (trunkLength / trunkSegments) * (i + 1), 0);
				lean2[i + 1] = lean;
			}
			// set inital branch point from top of trunk
			pts[0] = new Point3D(stage.stageWidth * .5 + lean2[trunkSegments], stage.stageHeight - trunkLength, 0);
 
			//create branches
			branch(pts);
		}
 
		private function branch(pts:Vector.<Point3D>):void {
			var stemCount:int = 2;
 
			//  branchLimit controls complexity of tree
			if (counter2 < branchLimit){
				//set branch thickness
				_g3d.lineStyle(radius, DARK_BROWN);
				// some conditionals change branches as
				// they get further away from the trunk
				if(counter2 < 200) {
					yg -= Math.random() * .354;
					xg -= Math.random() * .625;
					if (radius > 2) radius *= .85;
				} else if (counter2 >= 200) {
 
					// moving into leaf territory now
 
					// at top of tree branches get thinner and more numerous
					stemCount = 2 + int(Math.random() * 5);
 
					// leaf color
					var leafColor:uint = getColor(Math.random() * 60, 50 + Math.random() * 90, Math.random() * 20);
					_g3d.lineStyle(0, leafColor, 230 / 255);
 
					yg -= Math.random() * .75;
					xg *= Math.random() * .20;
					zg *= Math.random() * .20;
				}
				for (var j:int = 0; j < stemCount; j++) {
 
					// randomize branch positions
					var xx:Number = randRange(30);
					var yy:Number = randRange(40);
					var zz:Number = randRange(50);
 
					_g3d.moveTo2D(pts[counter2].x, pts[counter2].y, pts[counter2].z);
					_g3d.lineTo2D(pts[counter2].x + xg + xx, pts[counter2].y - yg + yy, pts[counter2].z + zg + zz);
 
					// fill up pts array to be passed back recursively to branch function
					pts[counter + 1] = new Point3D(pts[counter2].x + xg + xx, pts[counter2].y - yg + yy, pts[counter2].z + zg +zz);
 
					// alternate branches left and right and back and forth
					xg *= -1;
					zg *= -1;
 
					// keep track of nodes
					counter++;
				}
 
				// keeps track of branches
				counter2++;
 
				//recursive call
				branch(pts);
			}
		}
 
		private function getColor(r:Number, g:Number, b:Number):uint {
			return r << 16 | g << 8 | b;
		}
 
		private function randRange(val:int):Number {
			return Math.random() * val + Math.random() * -val;
		}
	}
}

and a simple Point3D class to just store a bit of data

package geom {
	/**
	 * Basic 3D point
	 * @author Devon O.
	 */
	public class Point3D {
 
		private var _x:Number;
		private var _y:Number;
		private var _z:Number;
 
		public function Point3D(x:Number = 0, y:Number = 0, z:Number = 0) {
			_x = x;
			_y = y;
			_z = z;
		}
 
		public function get x():Number { return _x; }
 
		public function set x(value:Number):void {
			_x = value;
		}
 
		public function get y():Number { return _y; }
 
		public function set y(value:Number):void {
			_y = value;
		}
 
		public function get z():Number { return _z; }
 
		public function set z(value:Number):void {
			_z = value;
		}
	}
}

A Saturday morning well spent…

In other news, just discovered this blog’s been shortlisted as Best Irish Tech Blog. Some very nice news!

Leave a Reply

Devon O. Wolfgang

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development”

Contributing Author of “Flash AS3 for Interactive Agencies”

Senior Software Developer in Dublin, Ireland

Portfolio

Bayer Pixel Bender Filter

Bayer Mosaic Filter in Pixel Bender


Now, seeing as how I’ve been trying to learn some Pixel Bender coding lately[...]

Liquitext

Liquid Text Effect


Haven’t been too active around here lately due to a major on going project at work lately as well as the fact that my wife and I are in the slow process of moving homes. Really, the only free time it seems I have these days is during the lunch hour. So, over the past two days, I came up with this fun little toy during lunches[...]

Google’s Text To Speech Engine in Flash

I’m not sure how I managed to miss this, but I just happened to run across a ‘new’ (well, newish), albeit still unofficial, offering of Google today: text-to-speech. You can see what few details there are on this Techcrunch post. Basically, it just boils down to this though – you send your phrase to be [...]

Quick Sound Effects Generator


Need some beeps, boops, or bops, to go with your UI or games [...]

Quick QuickBox2D Tip II – Collision Detection

Custom collision detection/handling in QuickBox2D [...]

None of This Runs Eternal


No Flash/Actionscript stuff here. Just me rambling about the upcoming Current 93 concert [...]

Playing Around with the New UndoManager

Included in the Flex 4.0 SDK and the, just released, Flash Professional CS5 lies a new hidden little gem of a class: flashx.undo.UndoManager (although the Flex 4.0 SDK’s been out for awhile, I have to admit I didn’t even notice this until I installed Flash CS5 and started poking around the documentation looking for new [...]

Making Waves

In a previous post, “Digging into the Microphone in Flash Player 10.1″, reader David Law asked in the comments how it would be possible to save .wav files to the server. I wasn’t sure right offhand, but thought I’d spend my lunch hour yesterday looking into it. Well, after reading this quick tutorial on Adobe [...]

Some Drawing Fun and a Bad Movie You’ll Never See

Earlier today, Dave Gillem posted a link on Facebook to an incredible Processing based drawing application. Thought I’d have a go at reproducing something similar in Flash. Well, I failed miserably, but the results were still interesting enough to check out. You can play around with it below. Just mouse down to draw in the [...]

Animating Bezier Curves


The other day I got the notion in my head that I wanted to draw some bezier curves in an animated fashion. I’m sure this has been done a million times before, but sometimes reinventing the wheel can be a good learning experience [...]

My God, It’s Full of Stars…


Just a quick little thing I got an idea for (i.e. pretty much ripped off) while browsing Processing examples.

Another Year, Another Look

As a few folks may have noticed, the blog is looking a little bit different as of today [...]

The Webcam Warholizer


Just a little bit of quick Friday fun with webcam and bitmapata…

More With the JiglibFlash Terrain

Playing around a bit more with the animated JiglibFlash terrain idea from my last post, I wanted to see how it would work in conjunction with a webcam. So, needless to say, you’ll need a webcam to check out the results below.

Rockin and Rollin with the JiglibFlash Terrain


Finally got the chance to play around with the new JiglibFlash height map terrain, today, and have to say I am very impressed [...]