Adding a Lattice

Next we'll add lattice for the vines to grow around. We define the lattice as an array of line segments. For this example we’ll create a T shape.

var lattice = [
  [{x: 50, y: 50}, {x: 350, y: 50}],
  [{x:200, y:50}, {x:200, y:350}]
];

Instead of pruning branches randomly, we prune the ones furthest from the lattice. After we calculate each new control point, we need to determine how far it is from the lattice. The following function returns the distance from a point to a line segment. For a better understanding of how this formula works, read this great article by Dan Sunday. (A basic understanding of vector geometry will be helpful.)

function distancePointToLine(point, line) {
  var L, r, s;

  // Length of line segment
  L = Math.sqrt(Math.pow(line[1].x - line[0].x, 2) +
    Math.pow(line[1].y - line[0].y, 2));

  // Calculate position of projection along line segment
  r = ((point.x - line[0].x) * (line[1].x - line[0].x) +
    (point.y - line[0].y) * (line[1].y - line[0].y))
    / Math.pow(L, 2);

  // Calculate distance of point to projection
  s = ((line[0].y - point.y) * (line[1].x - line[0].x) -
    (line[0].x - point.x) * (line[1].y - line[0].y))
    / Math.pow(L, 2);

  // If perpendicular projection on line segment
  if (r >= 0 && r <= 1) {
    return Math.abs(s) * L;

  // If off line segment
  } else {
    return Math.min(
      Math.sqrt(Math.pow(point.x - line[0].x, 2) + Math.pow(point.y - line[0].y, 2)),
      Math.sqrt(Math.pow(point.x - line[1].x, 2) + Math.pow(point.y - line[1].y, 2))
    );
  }
}

Immediately after calculating the new control point of a new branch, we determine the shortest distance from the point to any of the line segments that make up the lattice. We’ll store this value as a property of the branch.

var distanceToLattice = 100000;
for (var l in lattice) {
  var result = distancePointToLine(branches[j].points[3], lattice[l]);
  if (result < distanceToLattice) distanceToLattice = result;
}

// Add to new branch array
new_branches.push({
  points:[
    branches[j].points[1],
    branches[j].points[2],
    branches[j].points[3],
    {x: x2, y: y2}
  ],
  direction: direction,
  distanceToLattice: distanceToLattice
});

Next we'll sort the branch array in order from closest to furthest from the lattice, and prune the branches that are furthest away.

new_branches.sort(function(a, b) {
  return a.distanceToLattice - b.distanceToLattice;
});
while (new_branches.length > 20) {
  new_branches.pop();
}

The Result

The following is the resulting animation. The T-shaped lattice is indicated by the dotted line.

Keywords

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