source: Dev/trunk/d3/src/geom/hull.js @ 76

Last change on this file since 76 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 2.6 KB
Line 
1/**
2 * Computes the 2D convex hull of a set of points using Graham's scanning
3 * algorithm. The algorithm has been implemented as described in Cormen,
4 * Leiserson, and Rivest's Introduction to Algorithms. The running time of
5 * this algorithm is O(n log n), where n is the number of input points.
6 *
7 * @param vertices [[x1, y1], [x2, y2], 
]
8 * @returns polygon [[x1, y1], [x2, y2], 
]
9 */
10d3.geom.hull = function(vertices) {
11  if (vertices.length < 3) return [];
12
13  var len = vertices.length,
14      plen = len - 1,
15      points = [],
16      stack = [],
17      i, j, h = 0, x1, y1, x2, y2, u, v, a, sp;
18
19  // find the starting ref point: leftmost point with the minimum y coord
20  for (i=1; i<len; ++i) {
21    if (vertices[i][1] < vertices[h][1]) {
22      h = i;
23    } else if (vertices[i][1] == vertices[h][1]) {
24      h = (vertices[i][0] < vertices[h][0] ? i : h);
25    }
26  }
27
28  // calculate polar angles from ref point and sort
29  for (i=0; i<len; ++i) {
30    if (i === h) continue;
31    y1 = vertices[i][1] - vertices[h][1];
32    x1 = vertices[i][0] - vertices[h][0];
33    points.push({angle: Math.atan2(y1, x1), index: i});
34  }
35  points.sort(function(a, b) { return a.angle - b.angle; });
36
37  // toss out duplicate angles
38  a = points[0].angle;
39  v = points[0].index;
40  u = 0;
41  for (i=1; i<plen; ++i) {
42    j = points[i].index;
43    if (a == points[i].angle) {
44      // keep angle for point most distant from the reference
45      x1 = vertices[v][0] - vertices[h][0];
46      y1 = vertices[v][1] - vertices[h][1];
47      x2 = vertices[j][0] - vertices[h][0];
48      y2 = vertices[j][1] - vertices[h][1];
49      if ((x1*x1 + y1*y1) >= (x2*x2 + y2*y2)) {
50        points[i].index = -1;
51      } else {
52        points[u].index = -1;
53        a = points[i].angle;
54        u = i;
55        v = j;
56      }
57    } else {
58      a = points[i].angle;
59      u = i;
60      v = j;
61    }
62  }
63
64  // initialize the stack
65  stack.push(h);
66  for (i=0, j=0; i<2; ++j) {
67    if (points[j].index !== -1) {
68      stack.push(points[j].index);
69      i++;
70    }
71  }
72  sp = stack.length;
73
74  // do graham's scan
75  for (; j<plen; ++j) {
76    if (points[j].index === -1) continue; // skip tossed out points
77    while (!d3_geom_hullCCW(stack[sp-2], stack[sp-1], points[j].index, vertices)) {
78      --sp;
79    }
80    stack[sp++] = points[j].index;
81  }
82
83  // construct the hull
84  var poly = [];
85  for (i=0; i<sp; ++i) {
86    poly.push(vertices[stack[i]]);
87  }
88  return poly;
89}
90
91// are three points in counter-clockwise order?
92function d3_geom_hullCCW(i1, i2, i3, v) {
93  var t, a, b, c, d, e, f;
94  t = v[i1]; a = t[0]; b = t[1];
95  t = v[i2]; c = t[0]; d = t[1];
96  t = v[i3]; e = t[0]; f = t[1];
97  return ((f-b)*(c-a) - (d-b)*(e-a)) > 0;
98}
Note: See TracBrowser for help on using the repository browser.