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).
[kml_flashembed publishmethod=”static” fversion=”10.0.0″ movie=”http://blog.onebyonedesign.com/wp-content/uploads/2010/03/tree.swf” width=”400″ height=”600″ targetclass=”flashmovie”]
[/kml_flashembed]
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. = new Vector.(); private var branchLimit:int = 325; private var trunkLength:int = int(Math.random() * 50 + 130); private var lean2:Vector. = new Vector.(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.(); trunkLength = int(Math.random() * 50 + 130); lean2 = new Vector.(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.):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!
Post a comment