source: Dev/branches/jQueryUI/client/d3/src/geom/hull.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

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.