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:

Without Pruning
With Pruning

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);

Keywords

  • B-Splines 1
  • Bezier 1
  • Canvas 1 2
  • Curves 1
  • Drupal 1
  • Gulp 1
  • JavaScript 1 2
  • PHP 1
  • Xdebug 1