Create a random tree generator.
This hack creates a natural-looking tree using the usual suspects (fractals/recursion/repeat-and-scale algorithms). In the next hack, we'll create movement using an embedded hierarchy of movie clips.
For those of us who prefer speaking English, we're going to grow a tree and make it sway in a breeze [Hack #7]. We will do this by re-creating natural phenomena in code.
The first time I went to Flash Forward (http://www.flashforward2004.com), Josh Davis talked about what made him tick. To paraphrase his 45-minute presentation into a single sentence, he said, "Look at nature, and see what it throws up at you, and then think what you can do with the result."
The Web is full of such experiments, and no hacks book would be complete without one or two such excursions.
To get the following information on nature, I had a short conversation with my girlfriend Karen. We have a neat division of labor: she deals with the garden, and I deal with the computer.
Here's what I learned without having to set foot outside. Trees follow a very basic pattern, and this is usually regular. A branch will be straight for a certain length and will then split. The thickness of the parent branch is usually related to the branches that grow from it?normally the cross-section is conserved (the total thickness of the trunk is roughly the same as, or proportional to, the thickness of the branches that sprout from it). This means that a twig grows and splits in exactly the same way as a main branch: the relative dimensions are the same. You know that this self-same process between tree and twig is going on because, if you plant a twig (well, if Karen plants it; mine always dies), you end up with a tree.
With this in mind, I created a random tree generator. Two example results are shown in Figure 1-28.
Both trees (and many more) were created using the same code. Here is treeGen.fla, which is downloadable from the book's web site:
function counter( ) { if (branchCounter == undefined) { branchCounter = 0; } return (branchCounter++); } function grow( ) { // Grow this limb... this.lineStyle(trunkThickness, 0x0, 100); this.moveTo(0, 0); this.lineTo(0, trunkLength); // If this isn't the trunk, change the angle and branch size if (this._name != "trunk") { this._rotation = (Math.random( )*angle) - angle/2; this._xscale *= branchSize; this._yscale *= branchSize; } // Grow buds... var seed = Math.ceil(Math.random( )*branch); for (var i = 0; i < seed; i++) { if (counter( ) < 3000) { var segment = this.createEmptyMovieClip("segment" + i, i); segment.onEnterFrame = grow; segment._y = trunkLength; } } delete (this.onEnterFrame); } // Define the trunk position and set the onEnterFrame handler to grow( ) this.createEmptyMovieClip("trunk", 0); trunk._x = 200; trunk._y = 400; trunk.onEnterFrame = grow; // Tree parameters var angle = 100; var branch = 5; var trunkThickness = 8; var trunkLength = -100; var branchSize = 0.7;
The basic tree shape is defined by the parameters in the last few lines of the listing:
The maximum angle a branch makes with its parent
The maximum number of buds (daughter branches) any branch can have
The thickness of the tree trunk
The tree trunk's length
The ratio between the daughter branch and the parent branch (which makes branches get smaller as you move away from the trunk)
First, we create the trunk and set its position. We then attach grow( ) as its onEnterFrame event handler. As its name suggests, grow( ) makes our empty movie clip grow by doing two things. First, it creates our branch by drawing a vertical line of height trunkLength and thickness trunkThickness. If we are currently drawing the trunk, we leave it as-is, resulting in scene 1. If we are not drawing the trunk, we also rotate it by +/- angle, as seen in scene 2, and scale it by branchSize; as seen in scene 3; all are shown in Figure 1-29.
The code then creates between 1 and branch new buds. The hacky part is that these buds are given the same onEnterFrame event handler as the current one, namely grow( ), so in the next frame, the buds grow their own buds and so on. Here is the portion of grow( ) that spawns a new movie clip for each bud and assigns the onEnterFrame event handler. Our tree could create new branches forever, but we need a limit; otherwise, Flash slows down and eventually crashes. To prevent this, the function counter( ) is used to limit the total number of branches to 3,000.
var seed = Math.ceil(Math.random( )*branch); for (var i = 0; i < seed; i++) { if (counter( ) < 3000) { var segment = this.createEmptyMovieClip("segment" + i, i); segment.onEnterFrame = grow; segment._y = trunkLength; } }
Finally, grow( ) deletes itself, as it needs to run only once per branch.
So, we are using a self-calling function (or, rather, one that creates copies of branches that contain the same function attached to them) to create our fractal tree. Not only do we have a tree consisting of branches and sub-branches, but we also have the same hierarchy reflected in the movie clip timelines. You can see this by using the debugger (although you will have to set the maximum number of branches down from 3000; otherwise, you will be in for a long wait!).
The result is vaguely oriental in its simplicity. However, it doesn't involve motion graphics, and static Java tree generators are a dime a dozen. Therefore, we add movement to our tree in the next hack.