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).
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
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!
Recent Comments