Simulating Vines
We can simulate vines by drawing successive b-splines joined together. We'll refer to each of these b-splines as a branch. We define a branch using the four control points that make up the b-spline, as well as the current direction of growth, in degrees. Branches that are currently drawing will be stored in an array. We’ll start with a single branch, starting at the point (200, 400), growing straight-up.
// Create branch array and add first branch
var branches = [];
branches.push({
points: [{x: 200, y: 400}, {x: 200, y: 400}, {x: 200, y: 400}, {x: 200, y: 400}],
direction: 0
});
After the branch finishes drawing (when t = 1), we replace it with a new branch. The new branch will be made up of the last three control points from the previous branch, plus one new point. The new point will be a specific distance and direction from the last control point. To make things interesting, we’ll randomly vary the distance and direction. To make things really interesting, we’ll replace it with two b-splines, growing in different directions.
function animatedVines(context, branches, t) {
// Draw b-spline segment for each branch
for (var i in branches) {
/** CODE TO DRAW B-SPLINE SEGMENT GOES HERE **/
}
// If finished drawing branch
if (t >= 1) {
// Create new branch array
var new_branches = [];
for (var j in branches) {
// Replace each existing branch with two branches
for (var k = 0; k < 2; k++) {
// Generate random length and direction
var direction = branches[j].direction - (Math.random() * 180 - 90);
var length = Math.random() * 20 + 5;
// Calculate new point
var new_point = {
x: branches[j].points[3].x + Math.sin(Math.PI * direction / 180) * length;
y: branches[j].points[3].y - Math.cos(Math.PI * direction / 180) * length;
}
// Add to new branch array
new_branches.push({
points: [
branches[j].points[1],
branches[j].points[2],
branches[j].points[3],
new_point
],
direction: direction
});
}
}
If at each iteration we replace every branch with two, our vine will very quickly turn into a bush. To prevent this, we randomly prune the branches down to a maximum of twenty at any given time.
while (new_branches.length > 10) {
new_branches.splice(Math.floor(Math.random() * new_branches.length), 1);
}
Here is the effect:
Finally, we start over again with the new branches and t = 0, or we continue with the old.
// Start things off with the new set
requestAnimationFrame(function() {
animatedVines(context, new_branches, 0);
});
// Not finished drawing the old set
} else {
requestAnimationFrame(function() {
animatedVines(context, branches, t + 0.1);
});
}
}
// Kick things off!
animatedVines(context, branches, 0);