[249] | 1 | (function(){d3.geom = {}; |
---|
| 2 | /** |
---|
| 3 | * Computes a contour for a given input grid function using the <a |
---|
| 4 | * href="http://en.wikipedia.org/wiki/Marching_squares">marching |
---|
| 5 | * squares</a> algorithm. Returns the contour polygon as an array of points. |
---|
| 6 | * |
---|
| 7 | * @param grid a two-input function(x, y) that returns true for values |
---|
| 8 | * inside the contour and false for values outside the contour. |
---|
| 9 | * @param start an optional starting point [x, y] on the grid. |
---|
| 10 | * @returns polygon [[x1, y1], [x2, y2], âŠ] |
---|
| 11 | */ |
---|
| 12 | d3.geom.contour = function(grid, start) { |
---|
| 13 | var s = start || d3_geom_contourStart(grid), // starting point |
---|
| 14 | c = [], // contour polygon |
---|
| 15 | x = s[0], // current x position |
---|
| 16 | y = s[1], // current y position |
---|
| 17 | dx = 0, // next x direction |
---|
| 18 | dy = 0, // next y direction |
---|
| 19 | pdx = NaN, // previous x direction |
---|
| 20 | pdy = NaN, // previous y direction |
---|
| 21 | i = 0; |
---|
| 22 | |
---|
| 23 | do { |
---|
| 24 | // determine marching squares index |
---|
| 25 | i = 0; |
---|
| 26 | if (grid(x-1, y-1)) i += 1; |
---|
| 27 | if (grid(x, y-1)) i += 2; |
---|
| 28 | if (grid(x-1, y )) i += 4; |
---|
| 29 | if (grid(x, y )) i += 8; |
---|
| 30 | |
---|
| 31 | // determine next direction |
---|
| 32 | if (i === 6) { |
---|
| 33 | dx = pdy === -1 ? -1 : 1; |
---|
| 34 | dy = 0; |
---|
| 35 | } else if (i === 9) { |
---|
| 36 | dx = 0; |
---|
| 37 | dy = pdx === 1 ? -1 : 1; |
---|
| 38 | } else { |
---|
| 39 | dx = d3_geom_contourDx[i]; |
---|
| 40 | dy = d3_geom_contourDy[i]; |
---|
| 41 | } |
---|
| 42 | |
---|
| 43 | // update contour polygon |
---|
| 44 | if (dx != pdx && dy != pdy) { |
---|
| 45 | c.push([x, y]); |
---|
| 46 | pdx = dx; |
---|
| 47 | pdy = dy; |
---|
| 48 | } |
---|
| 49 | |
---|
| 50 | x += dx; |
---|
| 51 | y += dy; |
---|
| 52 | } while (s[0] != x || s[1] != y); |
---|
| 53 | |
---|
| 54 | return c; |
---|
| 55 | }; |
---|
| 56 | |
---|
| 57 | // lookup tables for marching directions |
---|
| 58 | var d3_geom_contourDx = [1, 0, 1, 1,-1, 0,-1, 1,0, 0,0,0,-1, 0,-1,NaN], |
---|
| 59 | d3_geom_contourDy = [0,-1, 0, 0, 0,-1, 0, 0,1,-1,1,1, 0,-1, 0,NaN]; |
---|
| 60 | |
---|
| 61 | function d3_geom_contourStart(grid) { |
---|
| 62 | var x = 0, |
---|
| 63 | y = 0; |
---|
| 64 | |
---|
| 65 | // search for a starting point; begin at origin |
---|
| 66 | // and proceed along outward-expanding diagonals |
---|
| 67 | while (true) { |
---|
| 68 | if (grid(x,y)) { |
---|
| 69 | return [x,y]; |
---|
| 70 | } |
---|
| 71 | if (x === 0) { |
---|
| 72 | x = y + 1; |
---|
| 73 | y = 0; |
---|
| 74 | } else { |
---|
| 75 | x = x - 1; |
---|
| 76 | y = y + 1; |
---|
| 77 | } |
---|
| 78 | } |
---|
| 79 | } |
---|
| 80 | /** |
---|
| 81 | * Computes the 2D convex hull of a set of points using Graham's scanning |
---|
| 82 | * algorithm. The algorithm has been implemented as described in Cormen, |
---|
| 83 | * Leiserson, and Rivest's Introduction to Algorithms. The running time of |
---|
| 84 | * this algorithm is O(n log n), where n is the number of input points. |
---|
| 85 | * |
---|
| 86 | * @param vertices [[x1, y1], [x2, y2], âŠ] |
---|
| 87 | * @returns polygon [[x1, y1], [x2, y2], âŠ] |
---|
| 88 | */ |
---|
| 89 | d3.geom.hull = function(vertices) { |
---|
| 90 | if (vertices.length < 3) return []; |
---|
| 91 | |
---|
| 92 | var len = vertices.length, |
---|
| 93 | plen = len - 1, |
---|
| 94 | points = [], |
---|
| 95 | stack = [], |
---|
| 96 | i, j, h = 0, x1, y1, x2, y2, u, v, a, sp; |
---|
| 97 | |
---|
| 98 | // find the starting ref point: leftmost point with the minimum y coord |
---|
| 99 | for (i=1; i<len; ++i) { |
---|
| 100 | if (vertices[i][1] < vertices[h][1]) { |
---|
| 101 | h = i; |
---|
| 102 | } else if (vertices[i][1] == vertices[h][1]) { |
---|
| 103 | h = (vertices[i][0] < vertices[h][0] ? i : h); |
---|
| 104 | } |
---|
| 105 | } |
---|
| 106 | |
---|
| 107 | // calculate polar angles from ref point and sort |
---|
| 108 | for (i=0; i<len; ++i) { |
---|
| 109 | if (i === h) continue; |
---|
| 110 | y1 = vertices[i][1] - vertices[h][1]; |
---|
| 111 | x1 = vertices[i][0] - vertices[h][0]; |
---|
| 112 | points.push({angle: Math.atan2(y1, x1), index: i}); |
---|
| 113 | } |
---|
| 114 | points.sort(function(a, b) { return a.angle - b.angle; }); |
---|
| 115 | |
---|
| 116 | // toss out duplicate angles |
---|
| 117 | a = points[0].angle; |
---|
| 118 | v = points[0].index; |
---|
| 119 | u = 0; |
---|
| 120 | for (i=1; i<plen; ++i) { |
---|
| 121 | j = points[i].index; |
---|
| 122 | if (a == points[i].angle) { |
---|
| 123 | // keep angle for point most distant from the reference |
---|
| 124 | x1 = vertices[v][0] - vertices[h][0]; |
---|
| 125 | y1 = vertices[v][1] - vertices[h][1]; |
---|
| 126 | x2 = vertices[j][0] - vertices[h][0]; |
---|
| 127 | y2 = vertices[j][1] - vertices[h][1]; |
---|
| 128 | if ((x1*x1 + y1*y1) >= (x2*x2 + y2*y2)) { |
---|
| 129 | points[i].index = -1; |
---|
| 130 | } else { |
---|
| 131 | points[u].index = -1; |
---|
| 132 | a = points[i].angle; |
---|
| 133 | u = i; |
---|
| 134 | v = j; |
---|
| 135 | } |
---|
| 136 | } else { |
---|
| 137 | a = points[i].angle; |
---|
| 138 | u = i; |
---|
| 139 | v = j; |
---|
| 140 | } |
---|
| 141 | } |
---|
| 142 | |
---|
| 143 | // initialize the stack |
---|
| 144 | stack.push(h); |
---|
| 145 | for (i=0, j=0; i<2; ++j) { |
---|
| 146 | if (points[j].index !== -1) { |
---|
| 147 | stack.push(points[j].index); |
---|
| 148 | i++; |
---|
| 149 | } |
---|
| 150 | } |
---|
| 151 | sp = stack.length; |
---|
| 152 | |
---|
| 153 | // do graham's scan |
---|
| 154 | for (; j<plen; ++j) { |
---|
| 155 | if (points[j].index === -1) continue; // skip tossed out points |
---|
| 156 | while (!d3_geom_hullCCW(stack[sp-2], stack[sp-1], points[j].index, vertices)) { |
---|
| 157 | --sp; |
---|
| 158 | } |
---|
| 159 | stack[sp++] = points[j].index; |
---|
| 160 | } |
---|
| 161 | |
---|
| 162 | // construct the hull |
---|
| 163 | var poly = []; |
---|
| 164 | for (i=0; i<sp; ++i) { |
---|
| 165 | poly.push(vertices[stack[i]]); |
---|
| 166 | } |
---|
| 167 | return poly; |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | // are three points in counter-clockwise order? |
---|
| 171 | function d3_geom_hullCCW(i1, i2, i3, v) { |
---|
| 172 | var t, a, b, c, d, e, f; |
---|
| 173 | t = v[i1]; a = t[0]; b = t[1]; |
---|
| 174 | t = v[i2]; c = t[0]; d = t[1]; |
---|
| 175 | t = v[i3]; e = t[0]; f = t[1]; |
---|
| 176 | return ((f-b)*(c-a) - (d-b)*(e-a)) > 0; |
---|
| 177 | } |
---|
| 178 | // Note: requires coordinates to be counterclockwise and convex! |
---|
| 179 | d3.geom.polygon = function(coordinates) { |
---|
| 180 | |
---|
| 181 | coordinates.area = function() { |
---|
| 182 | var i = 0, |
---|
| 183 | n = coordinates.length, |
---|
| 184 | a = coordinates[n - 1][0] * coordinates[0][1], |
---|
| 185 | b = coordinates[n - 1][1] * coordinates[0][0]; |
---|
| 186 | while (++i < n) { |
---|
| 187 | a += coordinates[i - 1][0] * coordinates[i][1]; |
---|
| 188 | b += coordinates[i - 1][1] * coordinates[i][0]; |
---|
| 189 | } |
---|
| 190 | return (b - a) * .5; |
---|
| 191 | }; |
---|
| 192 | |
---|
| 193 | coordinates.centroid = function(k) { |
---|
| 194 | var i = -1, |
---|
| 195 | n = coordinates.length - 1, |
---|
| 196 | x = 0, |
---|
| 197 | y = 0, |
---|
| 198 | a, |
---|
| 199 | b, |
---|
| 200 | c; |
---|
| 201 | if (!arguments.length) k = 1 / (6 * coordinates.area()); |
---|
| 202 | while (++i < n) { |
---|
| 203 | a = coordinates[i]; |
---|
| 204 | b = coordinates[i + 1]; |
---|
| 205 | c = a[0] * b[1] - b[0] * a[1]; |
---|
| 206 | x += (a[0] + b[0]) * c; |
---|
| 207 | y += (a[1] + b[1]) * c; |
---|
| 208 | } |
---|
| 209 | return [x * k, y * k]; |
---|
| 210 | }; |
---|
| 211 | |
---|
| 212 | // The Sutherland-Hodgman clipping algorithm. |
---|
| 213 | coordinates.clip = function(subject) { |
---|
| 214 | var input, |
---|
| 215 | i = -1, |
---|
| 216 | n = coordinates.length, |
---|
| 217 | j, |
---|
| 218 | m, |
---|
| 219 | a = coordinates[n - 1], |
---|
| 220 | b, |
---|
| 221 | c, |
---|
| 222 | d; |
---|
| 223 | while (++i < n) { |
---|
| 224 | input = subject.slice(); |
---|
| 225 | subject.length = 0; |
---|
| 226 | b = coordinates[i]; |
---|
| 227 | c = input[(m = input.length) - 1]; |
---|
| 228 | j = -1; |
---|
| 229 | while (++j < m) { |
---|
| 230 | d = input[j]; |
---|
| 231 | if (d3_geom_polygonInside(d, a, b)) { |
---|
| 232 | if (!d3_geom_polygonInside(c, a, b)) { |
---|
| 233 | subject.push(d3_geom_polygonIntersect(c, d, a, b)); |
---|
| 234 | } |
---|
| 235 | subject.push(d); |
---|
| 236 | } else if (d3_geom_polygonInside(c, a, b)) { |
---|
| 237 | subject.push(d3_geom_polygonIntersect(c, d, a, b)); |
---|
| 238 | } |
---|
| 239 | c = d; |
---|
| 240 | } |
---|
| 241 | a = b; |
---|
| 242 | } |
---|
| 243 | return subject; |
---|
| 244 | }; |
---|
| 245 | |
---|
| 246 | return coordinates; |
---|
| 247 | }; |
---|
| 248 | |
---|
| 249 | function d3_geom_polygonInside(p, a, b) { |
---|
| 250 | return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); |
---|
| 251 | } |
---|
| 252 | |
---|
| 253 | // Intersect two infinite lines cd and ab. |
---|
| 254 | function d3_geom_polygonIntersect(c, d, a, b) { |
---|
| 255 | var x1 = c[0], x2 = d[0], x3 = a[0], x4 = b[0], |
---|
| 256 | y1 = c[1], y2 = d[1], y3 = a[1], y4 = b[1], |
---|
| 257 | x13 = x1 - x3, |
---|
| 258 | x21 = x2 - x1, |
---|
| 259 | x43 = x4 - x3, |
---|
| 260 | y13 = y1 - y3, |
---|
| 261 | y21 = y2 - y1, |
---|
| 262 | y43 = y4 - y3, |
---|
| 263 | ua = (x43 * y13 - y43 * x13) / (y43 * x21 - x43 * y21); |
---|
| 264 | return [x1 + ua * x21, y1 + ua * y21]; |
---|
| 265 | } |
---|
| 266 | // Adapted from Nicolas Garcia Belmonte's JIT implementation: |
---|
| 267 | // http://blog.thejit.org/2010/02/12/voronoi-tessellation/ |
---|
| 268 | // http://blog.thejit.org/assets/voronoijs/voronoi.js |
---|
| 269 | // See lib/jit/LICENSE for details. |
---|
| 270 | |
---|
| 271 | /** |
---|
| 272 | * @param vertices [[x1, y1], [x2, y2], âŠ] |
---|
| 273 | * @returns polygons [[[x1, y1], [x2, y2], âŠ], âŠ] |
---|
| 274 | */ |
---|
| 275 | d3.geom.voronoi = function(vertices) { |
---|
| 276 | var polygons = vertices.map(function() { return []; }); |
---|
| 277 | |
---|
| 278 | // Note: we expect the caller to clip the polygons, if needed. |
---|
| 279 | d3_voronoi_tessellate(vertices, function(e) { |
---|
| 280 | var s1, |
---|
| 281 | s2, |
---|
| 282 | x1, |
---|
| 283 | x2, |
---|
| 284 | y1, |
---|
| 285 | y2; |
---|
| 286 | if (e.a === 1 && e.b >= 0) { |
---|
| 287 | s1 = e.ep.r; |
---|
| 288 | s2 = e.ep.l; |
---|
| 289 | } else { |
---|
| 290 | s1 = e.ep.l; |
---|
| 291 | s2 = e.ep.r; |
---|
| 292 | } |
---|
| 293 | if (e.a === 1) { |
---|
| 294 | y1 = s1 ? s1.y : -1e6; |
---|
| 295 | x1 = e.c - e.b * y1; |
---|
| 296 | y2 = s2 ? s2.y : 1e6; |
---|
| 297 | x2 = e.c - e.b * y2; |
---|
| 298 | } else { |
---|
| 299 | x1 = s1 ? s1.x : -1e6; |
---|
| 300 | y1 = e.c - e.a * x1; |
---|
| 301 | x2 = s2 ? s2.x : 1e6; |
---|
| 302 | y2 = e.c - e.a * x2; |
---|
| 303 | } |
---|
| 304 | var v1 = [x1, y1], |
---|
| 305 | v2 = [x2, y2]; |
---|
| 306 | polygons[e.region.l.index].push(v1, v2); |
---|
| 307 | polygons[e.region.r.index].push(v1, v2); |
---|
| 308 | }); |
---|
| 309 | |
---|
| 310 | // Reconnect the polygon segments into counterclockwise loops. |
---|
| 311 | return polygons.map(function(polygon, i) { |
---|
| 312 | var cx = vertices[i][0], |
---|
| 313 | cy = vertices[i][1]; |
---|
| 314 | polygon.forEach(function(v) { |
---|
| 315 | v.angle = Math.atan2(v[0] - cx, v[1] - cy); |
---|
| 316 | }); |
---|
| 317 | return polygon.sort(function(a, b) { |
---|
| 318 | return a.angle - b.angle; |
---|
| 319 | }).filter(function(d, i) { |
---|
| 320 | return !i || (d.angle - polygon[i - 1].angle > 1e-10); |
---|
| 321 | }); |
---|
| 322 | }); |
---|
| 323 | }; |
---|
| 324 | |
---|
| 325 | var d3_voronoi_opposite = {"l": "r", "r": "l"}; |
---|
| 326 | |
---|
| 327 | function d3_voronoi_tessellate(vertices, callback) { |
---|
| 328 | |
---|
| 329 | var Sites = { |
---|
| 330 | list: vertices |
---|
| 331 | .map(function(v, i) { |
---|
| 332 | return { |
---|
| 333 | index: i, |
---|
| 334 | x: v[0], |
---|
| 335 | y: v[1] |
---|
| 336 | }; |
---|
| 337 | }) |
---|
| 338 | .sort(function(a, b) { |
---|
| 339 | return a.y < b.y ? -1 |
---|
| 340 | : a.y > b.y ? 1 |
---|
| 341 | : a.x < b.x ? -1 |
---|
| 342 | : a.x > b.x ? 1 |
---|
| 343 | : 0; |
---|
| 344 | }), |
---|
| 345 | bottomSite: null |
---|
| 346 | }; |
---|
| 347 | |
---|
| 348 | var EdgeList = { |
---|
| 349 | list: [], |
---|
| 350 | leftEnd: null, |
---|
| 351 | rightEnd: null, |
---|
| 352 | |
---|
| 353 | init: function() { |
---|
| 354 | EdgeList.leftEnd = EdgeList.createHalfEdge(null, "l"); |
---|
| 355 | EdgeList.rightEnd = EdgeList.createHalfEdge(null, "l"); |
---|
| 356 | EdgeList.leftEnd.r = EdgeList.rightEnd; |
---|
| 357 | EdgeList.rightEnd.l = EdgeList.leftEnd; |
---|
| 358 | EdgeList.list.unshift(EdgeList.leftEnd, EdgeList.rightEnd); |
---|
| 359 | }, |
---|
| 360 | |
---|
| 361 | createHalfEdge: function(edge, side) { |
---|
| 362 | return { |
---|
| 363 | edge: edge, |
---|
| 364 | side: side, |
---|
| 365 | vertex: null, |
---|
| 366 | "l": null, |
---|
| 367 | "r": null |
---|
| 368 | }; |
---|
| 369 | }, |
---|
| 370 | |
---|
| 371 | insert: function(lb, he) { |
---|
| 372 | he.l = lb; |
---|
| 373 | he.r = lb.r; |
---|
| 374 | lb.r.l = he; |
---|
| 375 | lb.r = he; |
---|
| 376 | }, |
---|
| 377 | |
---|
| 378 | leftBound: function(p) { |
---|
| 379 | var he = EdgeList.leftEnd; |
---|
| 380 | do { |
---|
| 381 | he = he.r; |
---|
| 382 | } while (he != EdgeList.rightEnd && Geom.rightOf(he, p)); |
---|
| 383 | he = he.l; |
---|
| 384 | return he; |
---|
| 385 | }, |
---|
| 386 | |
---|
| 387 | del: function(he) { |
---|
| 388 | he.l.r = he.r; |
---|
| 389 | he.r.l = he.l; |
---|
| 390 | he.edge = null; |
---|
| 391 | }, |
---|
| 392 | |
---|
| 393 | right: function(he) { |
---|
| 394 | return he.r; |
---|
| 395 | }, |
---|
| 396 | |
---|
| 397 | left: function(he) { |
---|
| 398 | return he.l; |
---|
| 399 | }, |
---|
| 400 | |
---|
| 401 | leftRegion: function(he) { |
---|
| 402 | return he.edge == null |
---|
| 403 | ? Sites.bottomSite |
---|
| 404 | : he.edge.region[he.side]; |
---|
| 405 | }, |
---|
| 406 | |
---|
| 407 | rightRegion: function(he) { |
---|
| 408 | return he.edge == null |
---|
| 409 | ? Sites.bottomSite |
---|
| 410 | : he.edge.region[d3_voronoi_opposite[he.side]]; |
---|
| 411 | } |
---|
| 412 | }; |
---|
| 413 | |
---|
| 414 | var Geom = { |
---|
| 415 | |
---|
| 416 | bisect: function(s1, s2) { |
---|
| 417 | var newEdge = { |
---|
| 418 | region: {"l": s1, "r": s2}, |
---|
| 419 | ep: {"l": null, "r": null} |
---|
| 420 | }; |
---|
| 421 | |
---|
| 422 | var dx = s2.x - s1.x, |
---|
| 423 | dy = s2.y - s1.y, |
---|
| 424 | adx = dx > 0 ? dx : -dx, |
---|
| 425 | ady = dy > 0 ? dy : -dy; |
---|
| 426 | |
---|
| 427 | newEdge.c = s1.x * dx + s1.y * dy |
---|
| 428 | + (dx * dx + dy * dy) * .5; |
---|
| 429 | |
---|
| 430 | if (adx > ady) { |
---|
| 431 | newEdge.a = 1; |
---|
| 432 | newEdge.b = dy / dx; |
---|
| 433 | newEdge.c /= dx; |
---|
| 434 | } else { |
---|
| 435 | newEdge.b = 1; |
---|
| 436 | newEdge.a = dx / dy; |
---|
| 437 | newEdge.c /= dy; |
---|
| 438 | } |
---|
| 439 | |
---|
| 440 | return newEdge; |
---|
| 441 | }, |
---|
| 442 | |
---|
| 443 | intersect: function(el1, el2) { |
---|
| 444 | var e1 = el1.edge, |
---|
| 445 | e2 = el2.edge; |
---|
| 446 | if (!e1 || !e2 || (e1.region.r == e2.region.r)) { |
---|
| 447 | return null; |
---|
| 448 | } |
---|
| 449 | var d = (e1.a * e2.b) - (e1.b * e2.a); |
---|
| 450 | if (Math.abs(d) < 1e-10) { |
---|
| 451 | return null; |
---|
| 452 | } |
---|
| 453 | var xint = (e1.c * e2.b - e2.c * e1.b) / d, |
---|
| 454 | yint = (e2.c * e1.a - e1.c * e2.a) / d, |
---|
| 455 | e1r = e1.region.r, |
---|
| 456 | e2r = e2.region.r, |
---|
| 457 | el, |
---|
| 458 | e; |
---|
| 459 | if ((e1r.y < e2r.y) || |
---|
| 460 | (e1r.y == e2r.y && e1r.x < e2r.x)) { |
---|
| 461 | el = el1; |
---|
| 462 | e = e1; |
---|
| 463 | } else { |
---|
| 464 | el = el2; |
---|
| 465 | e = e2; |
---|
| 466 | } |
---|
| 467 | var rightOfSite = (xint >= e.region.r.x); |
---|
| 468 | if ((rightOfSite && (el.side === "l")) || |
---|
| 469 | (!rightOfSite && (el.side === "r"))) { |
---|
| 470 | return null; |
---|
| 471 | } |
---|
| 472 | return { |
---|
| 473 | x: xint, |
---|
| 474 | y: yint |
---|
| 475 | }; |
---|
| 476 | }, |
---|
| 477 | |
---|
| 478 | rightOf: function(he, p) { |
---|
| 479 | var e = he.edge, |
---|
| 480 | topsite = e.region.r, |
---|
| 481 | rightOfSite = (p.x > topsite.x); |
---|
| 482 | |
---|
| 483 | if (rightOfSite && (he.side === "l")) { |
---|
| 484 | return 1; |
---|
| 485 | } |
---|
| 486 | if (!rightOfSite && (he.side === "r")) { |
---|
| 487 | return 0; |
---|
| 488 | } |
---|
| 489 | if (e.a === 1) { |
---|
| 490 | var dyp = p.y - topsite.y, |
---|
| 491 | dxp = p.x - topsite.x, |
---|
| 492 | fast = 0, |
---|
| 493 | above = 0; |
---|
| 494 | |
---|
| 495 | if ((!rightOfSite && (e.b < 0)) || |
---|
| 496 | (rightOfSite && (e.b >= 0))) { |
---|
| 497 | above = fast = (dyp >= e.b * dxp); |
---|
| 498 | } else { |
---|
| 499 | above = ((p.x + p.y * e.b) > e.c); |
---|
| 500 | if (e.b < 0) { |
---|
| 501 | above = !above; |
---|
| 502 | } |
---|
| 503 | if (!above) { |
---|
| 504 | fast = 1; |
---|
| 505 | } |
---|
| 506 | } |
---|
| 507 | if (!fast) { |
---|
| 508 | var dxs = topsite.x - e.region.l.x; |
---|
| 509 | above = (e.b * (dxp * dxp - dyp * dyp)) < |
---|
| 510 | (dxs * dyp * (1 + 2 * dxp / dxs + e.b * e.b)); |
---|
| 511 | |
---|
| 512 | if (e.b < 0) { |
---|
| 513 | above = !above; |
---|
| 514 | } |
---|
| 515 | } |
---|
| 516 | } else /* e.b == 1 */ { |
---|
| 517 | var yl = e.c - e.a * p.x, |
---|
| 518 | t1 = p.y - yl, |
---|
| 519 | t2 = p.x - topsite.x, |
---|
| 520 | t3 = yl - topsite.y; |
---|
| 521 | |
---|
| 522 | above = (t1 * t1) > (t2 * t2 + t3 * t3); |
---|
| 523 | } |
---|
| 524 | return he.side === "l" ? above : !above; |
---|
| 525 | }, |
---|
| 526 | |
---|
| 527 | endPoint: function(edge, side, site) { |
---|
| 528 | edge.ep[side] = site; |
---|
| 529 | if (!edge.ep[d3_voronoi_opposite[side]]) return; |
---|
| 530 | callback(edge); |
---|
| 531 | }, |
---|
| 532 | |
---|
| 533 | distance: function(s, t) { |
---|
| 534 | var dx = s.x - t.x, |
---|
| 535 | dy = s.y - t.y; |
---|
| 536 | return Math.sqrt(dx * dx + dy * dy); |
---|
| 537 | } |
---|
| 538 | }; |
---|
| 539 | |
---|
| 540 | var EventQueue = { |
---|
| 541 | list: [], |
---|
| 542 | |
---|
| 543 | insert: function(he, site, offset) { |
---|
| 544 | he.vertex = site; |
---|
| 545 | he.ystar = site.y + offset; |
---|
| 546 | for (var i=0, list=EventQueue.list, l=list.length; i<l; i++) { |
---|
| 547 | var next = list[i]; |
---|
| 548 | if (he.ystar > next.ystar || |
---|
| 549 | (he.ystar == next.ystar && |
---|
| 550 | site.x > next.vertex.x)) { |
---|
| 551 | continue; |
---|
| 552 | } else { |
---|
| 553 | break; |
---|
| 554 | } |
---|
| 555 | } |
---|
| 556 | list.splice(i, 0, he); |
---|
| 557 | }, |
---|
| 558 | |
---|
| 559 | del: function(he) { |
---|
| 560 | for (var i=0, ls=EventQueue.list, l=ls.length; i<l && (ls[i] != he); ++i) {} |
---|
| 561 | ls.splice(i, 1); |
---|
| 562 | }, |
---|
| 563 | |
---|
| 564 | empty: function() { return EventQueue.list.length === 0; }, |
---|
| 565 | |
---|
| 566 | nextEvent: function(he) { |
---|
| 567 | for (var i=0, ls=EventQueue.list, l=ls.length; i<l; ++i) { |
---|
| 568 | if (ls[i] == he) return ls[i+1]; |
---|
| 569 | } |
---|
| 570 | return null; |
---|
| 571 | }, |
---|
| 572 | |
---|
| 573 | min: function() { |
---|
| 574 | var elem = EventQueue.list[0]; |
---|
| 575 | return { |
---|
| 576 | x: elem.vertex.x, |
---|
| 577 | y: elem.ystar |
---|
| 578 | }; |
---|
| 579 | }, |
---|
| 580 | |
---|
| 581 | extractMin: function() { |
---|
| 582 | return EventQueue.list.shift(); |
---|
| 583 | } |
---|
| 584 | }; |
---|
| 585 | |
---|
| 586 | EdgeList.init(); |
---|
| 587 | Sites.bottomSite = Sites.list.shift(); |
---|
| 588 | |
---|
| 589 | var newSite = Sites.list.shift(), newIntStar; |
---|
| 590 | var lbnd, rbnd, llbnd, rrbnd, bisector; |
---|
| 591 | var bot, top, temp, p, v; |
---|
| 592 | var e, pm; |
---|
| 593 | |
---|
| 594 | while (true) { |
---|
| 595 | if (!EventQueue.empty()) { |
---|
| 596 | newIntStar = EventQueue.min(); |
---|
| 597 | } |
---|
| 598 | if (newSite && (EventQueue.empty() |
---|
| 599 | || newSite.y < newIntStar.y |
---|
| 600 | || (newSite.y == newIntStar.y |
---|
| 601 | && newSite.x < newIntStar.x))) { //new site is smallest |
---|
| 602 | lbnd = EdgeList.leftBound(newSite); |
---|
| 603 | rbnd = EdgeList.right(lbnd); |
---|
| 604 | bot = EdgeList.rightRegion(lbnd); |
---|
| 605 | e = Geom.bisect(bot, newSite); |
---|
| 606 | bisector = EdgeList.createHalfEdge(e, "l"); |
---|
| 607 | EdgeList.insert(lbnd, bisector); |
---|
| 608 | p = Geom.intersect(lbnd, bisector); |
---|
| 609 | if (p) { |
---|
| 610 | EventQueue.del(lbnd); |
---|
| 611 | EventQueue.insert(lbnd, p, Geom.distance(p, newSite)); |
---|
| 612 | } |
---|
| 613 | lbnd = bisector; |
---|
| 614 | bisector = EdgeList.createHalfEdge(e, "r"); |
---|
| 615 | EdgeList.insert(lbnd, bisector); |
---|
| 616 | p = Geom.intersect(bisector, rbnd); |
---|
| 617 | if (p) { |
---|
| 618 | EventQueue.insert(bisector, p, Geom.distance(p, newSite)); |
---|
| 619 | } |
---|
| 620 | newSite = Sites.list.shift(); |
---|
| 621 | } else if (!EventQueue.empty()) { //intersection is smallest |
---|
| 622 | lbnd = EventQueue.extractMin(); |
---|
| 623 | llbnd = EdgeList.left(lbnd); |
---|
| 624 | rbnd = EdgeList.right(lbnd); |
---|
| 625 | rrbnd = EdgeList.right(rbnd); |
---|
| 626 | bot = EdgeList.leftRegion(lbnd); |
---|
| 627 | top = EdgeList.rightRegion(rbnd); |
---|
| 628 | v = lbnd.vertex; |
---|
| 629 | Geom.endPoint(lbnd.edge, lbnd.side, v); |
---|
| 630 | Geom.endPoint(rbnd.edge, rbnd.side, v); |
---|
| 631 | EdgeList.del(lbnd); |
---|
| 632 | EventQueue.del(rbnd); |
---|
| 633 | EdgeList.del(rbnd); |
---|
| 634 | pm = "l"; |
---|
| 635 | if (bot.y > top.y) { |
---|
| 636 | temp = bot; |
---|
| 637 | bot = top; |
---|
| 638 | top = temp; |
---|
| 639 | pm = "r"; |
---|
| 640 | } |
---|
| 641 | e = Geom.bisect(bot, top); |
---|
| 642 | bisector = EdgeList.createHalfEdge(e, pm); |
---|
| 643 | EdgeList.insert(llbnd, bisector); |
---|
| 644 | Geom.endPoint(e, d3_voronoi_opposite[pm], v); |
---|
| 645 | p = Geom.intersect(llbnd, bisector); |
---|
| 646 | if (p) { |
---|
| 647 | EventQueue.del(llbnd); |
---|
| 648 | EventQueue.insert(llbnd, p, Geom.distance(p, bot)); |
---|
| 649 | } |
---|
| 650 | p = Geom.intersect(bisector, rrbnd); |
---|
| 651 | if (p) { |
---|
| 652 | EventQueue.insert(bisector, p, Geom.distance(p, bot)); |
---|
| 653 | } |
---|
| 654 | } else { |
---|
| 655 | break; |
---|
| 656 | } |
---|
| 657 | }//end while |
---|
| 658 | |
---|
| 659 | for (lbnd = EdgeList.right(EdgeList.leftEnd); |
---|
| 660 | lbnd != EdgeList.rightEnd; |
---|
| 661 | lbnd = EdgeList.right(lbnd)) { |
---|
| 662 | callback(lbnd.edge); |
---|
| 663 | } |
---|
| 664 | } |
---|
| 665 | /** |
---|
| 666 | * @param vertices [[x1, y1], [x2, y2], âŠ] |
---|
| 667 | * @returns triangles [[[x1, y1], [x2, y2], [x3, y3]], âŠ] |
---|
| 668 | */ |
---|
| 669 | d3.geom.delaunay = function(vertices) { |
---|
| 670 | var edges = vertices.map(function() { return []; }), |
---|
| 671 | triangles = []; |
---|
| 672 | |
---|
| 673 | // Use the Voronoi tessellation to determine Delaunay edges. |
---|
| 674 | d3_voronoi_tessellate(vertices, function(e) { |
---|
| 675 | edges[e.region.l.index].push(vertices[e.region.r.index]); |
---|
| 676 | }); |
---|
| 677 | |
---|
| 678 | // Reconnect the edges into counterclockwise triangles. |
---|
| 679 | edges.forEach(function(edge, i) { |
---|
| 680 | var v = vertices[i], |
---|
| 681 | cx = v[0], |
---|
| 682 | cy = v[1]; |
---|
| 683 | edge.forEach(function(v) { |
---|
| 684 | v.angle = Math.atan2(v[0] - cx, v[1] - cy); |
---|
| 685 | }); |
---|
| 686 | edge.sort(function(a, b) { |
---|
| 687 | return a.angle - b.angle; |
---|
| 688 | }); |
---|
| 689 | for (var j = 0, m = edge.length - 1; j < m; j++) { |
---|
| 690 | triangles.push([v, edge[j], edge[j + 1]]); |
---|
| 691 | } |
---|
| 692 | }); |
---|
| 693 | |
---|
| 694 | return triangles; |
---|
| 695 | }; |
---|
| 696 | // Constructs a new quadtree for the specified array of points. A quadtree is a |
---|
| 697 | // two-dimensional recursive spatial subdivision. This implementation uses |
---|
| 698 | // square partitions, dividing each square into four equally-sized squares. Each |
---|
| 699 | // point exists in a unique node; if multiple points are in the same position, |
---|
| 700 | // some points may be stored on internal nodes rather than leaf nodes. Quadtrees |
---|
| 701 | // can be used to accelerate various spatial operations, such as the Barnes-Hut |
---|
| 702 | // approximation for computing n-body forces, or collision detection. |
---|
| 703 | d3.geom.quadtree = function(points, x1, y1, x2, y2) { |
---|
| 704 | var p, |
---|
| 705 | i = -1, |
---|
| 706 | n = points.length; |
---|
| 707 | |
---|
| 708 | // Type conversion for deprecated API. |
---|
| 709 | if (n && isNaN(points[0].x)) points = points.map(d3_geom_quadtreePoint); |
---|
| 710 | |
---|
| 711 | // Allow bounds to be specified explicitly. |
---|
| 712 | if (arguments.length < 5) { |
---|
| 713 | if (arguments.length === 3) { |
---|
| 714 | y2 = x2 = y1; |
---|
| 715 | y1 = x1; |
---|
| 716 | } else { |
---|
| 717 | x1 = y1 = Infinity; |
---|
| 718 | x2 = y2 = -Infinity; |
---|
| 719 | |
---|
| 720 | // Compute bounds. |
---|
| 721 | while (++i < n) { |
---|
| 722 | p = points[i]; |
---|
| 723 | if (p.x < x1) x1 = p.x; |
---|
| 724 | if (p.y < y1) y1 = p.y; |
---|
| 725 | if (p.x > x2) x2 = p.x; |
---|
| 726 | if (p.y > y2) y2 = p.y; |
---|
| 727 | } |
---|
| 728 | |
---|
| 729 | // Squarify the bounds. |
---|
| 730 | var dx = x2 - x1, |
---|
| 731 | dy = y2 - y1; |
---|
| 732 | if (dx > dy) y2 = y1 + dx; |
---|
| 733 | else x2 = x1 + dy; |
---|
| 734 | } |
---|
| 735 | } |
---|
| 736 | |
---|
| 737 | // Recursively inserts the specified point p at the node n or one of its |
---|
| 738 | // descendants. The bounds are defined by [x1, x2] and [y1, y2]. |
---|
| 739 | function insert(n, p, x1, y1, x2, y2) { |
---|
| 740 | if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid points |
---|
| 741 | if (n.leaf) { |
---|
| 742 | var v = n.point; |
---|
| 743 | if (v) { |
---|
| 744 | // If the point at this leaf node is at the same position as the new |
---|
| 745 | // point we are adding, we leave the point associated with the |
---|
| 746 | // internal node while adding the new point to a child node. This |
---|
| 747 | // avoids infinite recursion. |
---|
| 748 | if ((Math.abs(v.x - p.x) + Math.abs(v.y - p.y)) < .01) { |
---|
| 749 | insertChild(n, p, x1, y1, x2, y2); |
---|
| 750 | } else { |
---|
| 751 | n.point = null; |
---|
| 752 | insertChild(n, v, x1, y1, x2, y2); |
---|
| 753 | insertChild(n, p, x1, y1, x2, y2); |
---|
| 754 | } |
---|
| 755 | } else { |
---|
| 756 | n.point = p; |
---|
| 757 | } |
---|
| 758 | } else { |
---|
| 759 | insertChild(n, p, x1, y1, x2, y2); |
---|
| 760 | } |
---|
| 761 | } |
---|
| 762 | |
---|
| 763 | // Recursively inserts the specified point p into a descendant of node n. The |
---|
| 764 | // bounds are defined by [x1, x2] and [y1, y2]. |
---|
| 765 | function insertChild(n, p, x1, y1, x2, y2) { |
---|
| 766 | // Compute the split point, and the quadrant in which to insert p. |
---|
| 767 | var sx = (x1 + x2) * .5, |
---|
| 768 | sy = (y1 + y2) * .5, |
---|
| 769 | right = p.x >= sx, |
---|
| 770 | bottom = p.y >= sy, |
---|
| 771 | i = (bottom << 1) + right; |
---|
| 772 | |
---|
| 773 | // Recursively insert into the child node. |
---|
| 774 | n.leaf = false; |
---|
| 775 | n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); |
---|
| 776 | |
---|
| 777 | // Update the bounds as we recurse. |
---|
| 778 | if (right) x1 = sx; else x2 = sx; |
---|
| 779 | if (bottom) y1 = sy; else y2 = sy; |
---|
| 780 | insert(n, p, x1, y1, x2, y2); |
---|
| 781 | } |
---|
| 782 | |
---|
| 783 | // Create the root node. |
---|
| 784 | var root = d3_geom_quadtreeNode(); |
---|
| 785 | |
---|
| 786 | root.add = function(p) { |
---|
| 787 | insert(root, p, x1, y1, x2, y2); |
---|
| 788 | }; |
---|
| 789 | |
---|
| 790 | root.visit = function(f) { |
---|
| 791 | d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2); |
---|
| 792 | }; |
---|
| 793 | |
---|
| 794 | // Insert all points. |
---|
| 795 | points.forEach(root.add); |
---|
| 796 | return root; |
---|
| 797 | }; |
---|
| 798 | |
---|
| 799 | function d3_geom_quadtreeNode() { |
---|
| 800 | return { |
---|
| 801 | leaf: true, |
---|
| 802 | nodes: [], |
---|
| 803 | point: null |
---|
| 804 | }; |
---|
| 805 | } |
---|
| 806 | |
---|
| 807 | function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { |
---|
| 808 | if (!f(node, x1, y1, x2, y2)) { |
---|
| 809 | var sx = (x1 + x2) * .5, |
---|
| 810 | sy = (y1 + y2) * .5, |
---|
| 811 | children = node.nodes; |
---|
| 812 | if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); |
---|
| 813 | if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); |
---|
| 814 | if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); |
---|
| 815 | if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); |
---|
| 816 | } |
---|
| 817 | } |
---|
| 818 | |
---|
| 819 | function d3_geom_quadtreePoint(p) { |
---|
| 820 | return { |
---|
| 821 | x: p[0], |
---|
| 822 | y: p[1] |
---|
| 823 | }; |
---|
| 824 | } |
---|
| 825 | })(); |
---|