Mercurial > hg > mpiwg_geobrowser
comparison lib/GeoTemCo/js/Map/Clustering.js @ 0:b57c7821382f
initial
author | Dirk Wintergruen <dwinter@mpiwg-berlin.mpg.de> |
---|---|
date | Thu, 28 May 2015 10:28:12 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:b57c7821382f |
---|---|
1 /* | |
2 * Clustering.js | |
3 * | |
4 * Copyright (c) 2012, Stefan Jänicke. All rights reserved. | |
5 * | |
6 * This library is free software; you can redistribute it and/or | |
7 * modify it under the terms of the GNU Lesser General Public | |
8 * License as published by the Free Software Foundation; either | |
9 * version 3 of the License, or (at your option) any later version. | |
10 * | |
11 * This library is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * Lesser General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU Lesser General Public | |
17 * License along with this library; if not, write to the Free Software | |
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 * MA 02110-1301 USA | |
20 */ | |
21 | |
22 /** | |
23 * @class Vertex, Edge, Triangle, Clustering, BinaryHeap | |
24 * Dynamic Delaunay clustering algorithm (see GeoTemCo paper) | |
25 * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de) | |
26 * @release 1.0 | |
27 * @release date: 2012-07-27 | |
28 * @version date: 2012-07-27 | |
29 */ | |
30 | |
31 function Vertex(x, y, categories, binning) { | |
32 this.x = x; | |
33 this.y = y; | |
34 this.radius | |
35 this.size = 0; | |
36 this.elements = []; | |
37 this.radii = []; | |
38 this.weights = []; | |
39 this.legal = true; | |
40 this.binning = binning; | |
41 if (categories != undefined) { | |
42 for (var i = 0; i < categories; i++) { | |
43 this.elements.push([]); | |
44 this.weights.push(0); | |
45 } | |
46 } | |
47 } | |
48 | |
49 Vertex.prototype.merge = function(v0, v1) { | |
50 for (var i = 0; i < v0.elements.length; i++) { | |
51 this.elements[i] = v0.elements[i].concat(v1.elements[i]); | |
52 this.weights[i] = v0.weights[i] + v1.weights[i]; | |
53 this.size += this.weights[i]; | |
54 } | |
55 } | |
56 | |
57 Vertex.prototype.CalculateRadius = function(resolution) { | |
58 this.radii = []; | |
59 for (i in this.elements ) { | |
60 this.radii.push(this.binning.getRadius(this.weights[i])); | |
61 } | |
62 if (this.radii.length == 1) { | |
63 this.radius = this.radii[0] * resolution; | |
64 } else { | |
65 var count = 0; | |
66 var max1 = 0; | |
67 var max2 = 0; | |
68 for (i in this.radii ) { | |
69 if (this.radii[i] != 0) { | |
70 count++; | |
71 } | |
72 if (this.radii[i] > max1) { | |
73 if (max1 > max2) { | |
74 max2 = max1; | |
75 } | |
76 max1 = this.radii[i]; | |
77 } else if (this.radii[i] > max2) { | |
78 max2 = this.radii[i]; | |
79 } | |
80 } | |
81 if (count == 1) { | |
82 this.radius = max1 * resolution; | |
83 } else if (count == 2) { | |
84 this.radius = (max1 + max2) * resolution; | |
85 } else if (count == 3) { | |
86 var d = (2 / 3 * Math.sqrt(3) - 1) * max1; | |
87 this.radius = (d + max1 + max2) * resolution; | |
88 } else if (count == 4) { | |
89 var d = (Math.sqrt(2) - 1) * max2; | |
90 this.radius = (d + max1 + max2) * resolution; | |
91 } | |
92 } | |
93 } | |
94 | |
95 Vertex.prototype.addElement = function(e, weight, index) { | |
96 this.elements[index].push(e); | |
97 this.size += weight; | |
98 this.weights[index] += weight; | |
99 } | |
100 function Edge(v0, v1) { | |
101 this.v0 = v0; | |
102 this.v1 = v1; | |
103 this.leftFace | |
104 this.rightFace | |
105 this.legal = true; | |
106 this.setLength(); | |
107 } | |
108 | |
109 Edge.prototype.setLength = function() { | |
110 var dx = this.v0.x - this.v1.x; | |
111 var dy = this.v0.y - this.v1.y; | |
112 this.length = Math.sqrt(dx * dx + dy * dy); | |
113 } | |
114 | |
115 Edge.prototype.contains = function(v) { | |
116 if (this.v0 == v || this.v1 == v) { | |
117 return true; | |
118 } | |
119 return false; | |
120 } | |
121 | |
122 Edge.prototype.replaceFace = function(f_old, f_new) { | |
123 if (this.leftFace == f_old) { | |
124 this.leftFace = f_new; | |
125 } else if (this.rightFace == f_old) { | |
126 this.rightFace = f_new; | |
127 } | |
128 } | |
129 | |
130 Edge.prototype.setFace = function(f) { | |
131 if (f.leftOf(this)) { | |
132 this.leftFace = f; | |
133 } else { | |
134 this.rightFace = f; | |
135 } | |
136 } | |
137 | |
138 Edge.prototype.setFaces = function(f1, f2) { | |
139 if (f1.leftOf(this)) { | |
140 this.leftFace = f1; | |
141 this.rightFace = f2; | |
142 } else { | |
143 this.leftFace = f2; | |
144 this.rightFace = f1; | |
145 } | |
146 } | |
147 | |
148 Edge.prototype.removeFace = function(f) { | |
149 if (this.leftFace == f) { | |
150 this.leftFace = null; | |
151 } else { | |
152 this.rightFace = null; | |
153 } | |
154 } | |
155 | |
156 Edge.prototype.equals = function(e) { | |
157 if (this.v0 == e.v0 && this.v1 == e.v1 || this.v0 == e.v1 && this.v1 == e.v0) { | |
158 return true; | |
159 } | |
160 return false; | |
161 } | |
162 function Triangle(edges) { | |
163 this.edges = edges; | |
164 this.setVertices(); | |
165 this.descendants = []; | |
166 } | |
167 | |
168 Triangle.prototype.getTriple = function(e) { | |
169 var i = arrayIndex(this.edges, e); | |
170 return { | |
171 e_s : this.edges[(i + 1) % 3], | |
172 e_p : this.edges[(i + 2) % 3], | |
173 u : this.vertices[(i + 2) % 3] | |
174 }; | |
175 } | |
176 | |
177 Triangle.prototype.leftOf = function(e) { | |
178 var i = arrayIndex(this.edges, e); | |
179 if (this.vertices[i].y != this.vertices[(i + 1) % 3].y) { | |
180 return this.vertices[i].y > this.vertices[(i + 1) % 3].y; | |
181 } | |
182 return this.vertices[i].y > this.vertices[(i + 2) % 3].y; | |
183 } | |
184 | |
185 Triangle.prototype.getNext = function(v) { | |
186 var i = arrayIndex(this.vertices, v); | |
187 return this.vertices[(i + 1) % 3]; | |
188 } | |
189 | |
190 Triangle.prototype.oppositeEdge = function(v) { | |
191 var i = arrayIndex(this.vertices, v); | |
192 return this.edges[(i + 1) % 3]; | |
193 } | |
194 | |
195 Triangle.prototype.contains = function(v) { | |
196 return arrayIndex(this.vertices, v) != -1; | |
197 } | |
198 | |
199 Triangle.prototype.replace = function(e_old, e_new) { | |
200 this.edges[arrayIndex(this.edges, e_old)] = e_new; | |
201 } | |
202 | |
203 Triangle.prototype.setVertices = function() { | |
204 if (this.edges[1].v0 == this.edges[0].v0 || this.edges[1].v1 == this.edges[0].v0) { | |
205 this.vertices = [this.edges[0].v1, this.edges[0].v0]; | |
206 } else { | |
207 this.vertices = [this.edges[0].v0, this.edges[0].v1]; | |
208 } | |
209 if (this.edges[2].v0 == this.vertices[0]) { | |
210 this.vertices.push(this.edges[2].v1); | |
211 } else { | |
212 this.vertices.push(this.edges[2].v0); | |
213 } | |
214 } | |
215 | |
216 Triangle.prototype.replaceBy = function(triangles) { | |
217 this.descendants = triangles; | |
218 this.edges[0].replaceFace(this, triangles[0]); | |
219 this.edges[1].replaceFace(this, triangles[1]); | |
220 this.edges[2].replaceFace(this, triangles[2]); | |
221 } | |
222 | |
223 Triangle.prototype.CalcCircumcircle = function() { | |
224 var v0 = this.vertices[0]; | |
225 var v1 = this.vertices[1]; | |
226 var v2 = this.vertices[2]; | |
227 var A = v1.x - v0.x; | |
228 var B = v1.y - v0.y; | |
229 var C = v2.x - v0.x; | |
230 var D = v2.y - v0.y; | |
231 var E = A * (v0.x + v1.x) + B * (v0.y + v1.y); | |
232 var F = C * (v0.x + v2.x) + D * (v0.y + v2.y); | |
233 var G = 2.0 * (A * (v2.y - v1.y) - B * (v2.x - v1.x)); | |
234 var cx = (D * E - B * F) / G; | |
235 var cy = (A * F - C * E) / G; | |
236 this.center = new Vertex(cx, cy); | |
237 var dx = this.center.x - v0.x; | |
238 var dy = this.center.y - v0.y; | |
239 this.radius_squared = dx * dx + dy * dy; | |
240 }; | |
241 | |
242 Triangle.prototype.inCircumcircle = function(v) { | |
243 if (this.radius_squared == undefined) { | |
244 this.CalcCircumcircle(); | |
245 } | |
246 var dx = this.center.x - v.x; | |
247 var dy = this.center.y - v.y; | |
248 var dist_squared = dx * dx + dy * dy; | |
249 return (dist_squared <= this.radius_squared ); | |
250 }; | |
251 | |
252 Triangle.prototype.interior = function(v) { | |
253 var v0 = this.vertices[0]; | |
254 var v1 = this.vertices[1]; | |
255 var v2 = this.vertices[2]; | |
256 var dotAB = (v.x - v0.x ) * (v0.y - v1.y ) + (v.y - v0.y ) * (v1.x - v0.x ); | |
257 var dotBC = (v.x - v1.x ) * (v1.y - v2.y ) + (v.y - v1.y ) * (v2.x - v1.x ); | |
258 var dotCA = (v.x - v2.x ) * (v2.y - v0.y ) + (v.y - v2.y ) * (v0.x - v2.x ); | |
259 if (dotAB > 0 || dotBC > 0 || dotCA > 0) { | |
260 return null; | |
261 } else if (dotAB < 0 && dotBC < 0 && dotCA < 0) { | |
262 return this; | |
263 } else if (dotAB == 0) { | |
264 if (dotBC == 0) { | |
265 return this.vertices[1]; | |
266 } else if (dotCA == 0) { | |
267 return this.vertices[0]; | |
268 } | |
269 return this.edges[0]; | |
270 } else if (dotBC == 0) { | |
271 if (dotCA == 0) { | |
272 return this.vertices[2]; | |
273 } | |
274 return this.edges[1]; | |
275 } else if (dotCA == 0) { | |
276 return this.edges[2]; | |
277 } | |
278 }; | |
279 | |
280 function Clustering(xMin, yMin, xMax, yMax) { | |
281 this.triangles = []; | |
282 this.newTriangles = []; | |
283 this.bbox = { | |
284 x1 : xMin, | |
285 y1 : yMin, | |
286 x2 : xMax, | |
287 y2 : yMax | |
288 }; | |
289 this.CreateBoundingTriangle(); | |
290 this.edges = []; | |
291 this.vertices = []; | |
292 this.legalizes = 0; | |
293 this.collapses = 0; | |
294 } | |
295 | |
296 Clustering.prototype.locate = function(v) { | |
297 if (this.boundingTriangle.descendants.length == 0) { | |
298 return this.boundingTriangle; | |
299 } | |
300 var triangles = this.boundingTriangle.descendants; | |
301 while (true) { | |
302 for (var i = 0; i < triangles.length; i++) { | |
303 var simplex = triangles[i].interior(v); | |
304 if (simplex == null) { | |
305 continue; | |
306 } | |
307 if ( simplex instanceof Vertex || this.isLeaf(triangles[i])) { | |
308 return simplex; | |
309 } | |
310 triangles = triangles[i].descendants; | |
311 break; | |
312 } | |
313 } | |
314 } | |
315 | |
316 Clustering.prototype.legalize = function(v, e, t0_old) { | |
317 if (!e.v0.legal && !e.v1.legal) { | |
318 return; | |
319 } | |
320 this.legalizes++; | |
321 var flip = false; | |
322 var t1_old, tr1; | |
323 if (e.leftFace == t0_old && e.rightFace.inCircumcircle(v)) { | |
324 flip = true; | |
325 t1_old = e.rightFace; | |
326 } else if (e.rightFace == t0_old && e.leftFace.inCircumcircle(v)) { | |
327 flip = true; | |
328 t1_old = e.leftFace; | |
329 } | |
330 if (flip) { | |
331 var tr0 = t0_old.getTriple(e); | |
332 var tr1 = t1_old.getTriple(e); | |
333 var e_flip = new Edge(tr0.u, tr1.u); | |
334 var poly = []; | |
335 poly.push(e.v0); | |
336 poly.push(e_flip.v0); | |
337 poly.push(e.v1); | |
338 poly.push(e_flip.v1); | |
339 if (!this.JordanTest(poly, e_flip)) { | |
340 return; | |
341 } | |
342 e.legal = false; | |
343 this.edges.push(e_flip); | |
344 var t0_new = new Triangle([e_flip, tr0.e_p, tr1.e_s]); | |
345 var t1_new = new Triangle([e_flip, tr1.e_p, tr0.e_s]); | |
346 e_flip.setFaces(t0_new, t1_new); | |
347 tr0.e_p.replaceFace(t0_old, t0_new); | |
348 tr1.e_s.replaceFace(t1_old, t0_new); | |
349 tr1.e_p.replaceFace(t1_old, t1_new); | |
350 tr0.e_s.replaceFace(t0_old, t1_new); | |
351 t0_old.descendants = [t0_new, t1_new]; | |
352 t1_old.descendants = [t0_new, t1_new]; | |
353 this.legalize(v, t0_new.edges[2], t0_new); | |
354 this.legalize(v, t1_new.edges[1], t1_new); | |
355 } | |
356 } | |
357 | |
358 Clustering.prototype.add = function(v) { | |
359 this.addVertex(v, this.locate(v)); | |
360 } | |
361 | |
362 Clustering.prototype.addVertex = function(v, simplex) { | |
363 if ( simplex instanceof Vertex) { | |
364 simplex.merge(simplex, v); | |
365 } else if ( simplex instanceof Edge) { | |
366 this.vertices.push(v); | |
367 simplex.legal = false; | |
368 var tr0 = simplex.leftFace.getTriple(simplex); | |
369 var tr1 = simplex.rightFace.getTriple(simplex); | |
370 var e0 = new Edge(v, tr0.u); | |
371 var e1 = new Edge(v, simplex.leftFace.getNext(tr0.u)); | |
372 var e2 = new Edge(v, tr1.u); | |
373 var e3 = new Edge(v, simplex.rightFace.getNext(tr1.u)); | |
374 var t0 = new Triangle([e0, tr0.e_p, e1]); | |
375 var t1 = new Triangle([e1, tr1.e_s, e2]); | |
376 var t2 = new Triangle([e2, tr1.e_p, e3]); | |
377 var t3 = new Triangle([e3, tr0.e_s, e0]); | |
378 simplex.leftFace.descendants = [t0, t3]; | |
379 simplex.rightFace.descendants = [t1, t2]; | |
380 this.edges.push(e0); | |
381 this.edges.push(e1); | |
382 this.edges.push(e2); | |
383 this.edges.push(e3); | |
384 e0.setFaces(t0, t3); | |
385 e1.setFaces(t0, t1); | |
386 e2.setFaces(t1, t2); | |
387 e3.setFaces(t2, t3); | |
388 tr0.e_p.replaceFace(simplex.leftFace, t0); | |
389 tr1.e_s.replaceFace(simplex.rightFace, t1); | |
390 tr1.e_p.replaceFace(simplex.rightFace, t2); | |
391 tr0.e_s.replaceFace(simplex.leftFace, t3); | |
392 this.legalize(v, tr0.e_p, t0); | |
393 this.legalize(v, tr1.e_s, t1); | |
394 this.legalize(v, tr1.e_p, t2); | |
395 this.legalize(v, tr0.e_s, t3); | |
396 } else { | |
397 this.vertices.push(v); | |
398 var e_i = new Edge(simplex.vertices[0], v); | |
399 var e_j = new Edge(simplex.vertices[1], v); | |
400 var e_k = new Edge(simplex.vertices[2], v); | |
401 this.edges.push(e_i); | |
402 this.edges.push(e_j); | |
403 this.edges.push(e_k); | |
404 var t0 = new Triangle([e_i, simplex.edges[0], e_j]); | |
405 var t1 = new Triangle([e_j, simplex.edges[1], e_k]); | |
406 var t2 = new Triangle([e_k, simplex.edges[2], e_i]); | |
407 e_i.setFaces(t0, t2); | |
408 e_j.setFaces(t0, t1); | |
409 e_k.setFaces(t1, t2); | |
410 simplex.replaceBy([t0, t1, t2]); | |
411 this.legalize(v, simplex.edges[0], t0); | |
412 this.legalize(v, simplex.edges[1], t1); | |
413 this.legalize(v, simplex.edges[2], t2); | |
414 } | |
415 } | |
416 | |
417 Clustering.prototype.isLeaf = function(t) { | |
418 return t.descendants.length == 0; | |
419 } | |
420 | |
421 Clustering.prototype.CreateBoundingTriangle = function() { | |
422 var dx = (this.bbox.x2 - this.bbox.x1 ) * 10; | |
423 var dy = (this.bbox.y2 - this.bbox.y1 ) * 10; | |
424 var v0 = new Vertex(this.bbox.x1 - dx, this.bbox.y1 - dy * 3); | |
425 var v1 = new Vertex(this.bbox.x2 + dx * 3, this.bbox.y2 + dy); | |
426 var v2 = new Vertex(this.bbox.x1 - dx, this.bbox.y2 + dy); | |
427 var e0 = new Edge(v1, v0); | |
428 var e1 = new Edge(v0, v2); | |
429 var e2 = new Edge(v2, v1); | |
430 v0.legal = false; | |
431 v1.legal = false; | |
432 v2.legal = false; | |
433 this.boundingTriangle = new Triangle([e0, e1, e2]); | |
434 var inf = new Triangle([e0, e1, e2]); | |
435 e0.setFaces(this.boundingTriangle, inf); | |
436 e1.setFaces(this.boundingTriangle, inf); | |
437 e2.setFaces(this.boundingTriangle, inf); | |
438 } | |
439 | |
440 Clustering.prototype.mergeVertices = function(e) { | |
441 this.collapses++; | |
442 var s0 = e.v0.size; | |
443 var s1 = e.v1.size; | |
444 var x = (e.v0.x * s0 + e.v1.x * s1 ) / (s0 + s1 ); | |
445 var y = (e.v0.y * s0 + e.v1.y * s1 ) / (s0 + s1 ); | |
446 var v = new Vertex(x, y, e.v0.elements.length, e.v0.binning); | |
447 v.merge(e.v0, e.v1); | |
448 | |
449 e.v0.legal = false; | |
450 e.v1.legal = false; | |
451 | |
452 var hole = []; | |
453 var oldFacets = []; | |
454 e.legal = false; | |
455 | |
456 var vertices = []; | |
457 var traverse = function(eLeft, eRight, triangle) { | |
458 eLeft.legal = false; | |
459 do { | |
460 var triple; | |
461 if (eLeft.leftFace == triangle) { | |
462 triple = eLeft.rightFace.getTriple(eLeft); | |
463 oldFacets.push(eLeft.rightFace); | |
464 triple.e_s.removeFace(eLeft.rightFace); | |
465 triangle = eLeft.rightFace; | |
466 } else { | |
467 triple = eLeft.leftFace.getTriple(eLeft); | |
468 oldFacets.push(eLeft.leftFace); | |
469 triple.e_s.removeFace(eLeft.leftFace); | |
470 triangle = eLeft.leftFace; | |
471 } | |
472 if (arrayIndex(hole, triple.e_s) == -1) { | |
473 hole.push(triple.e_s); | |
474 } | |
475 vertices.push(triple.u); | |
476 eLeft = triple.e_p; | |
477 eLeft.legal = false; | |
478 } while( eLeft != eRight ); | |
479 } | |
480 var tr0 = e.leftFace.getTriple(e); | |
481 var tr1 = e.rightFace.getTriple(e); | |
482 oldFacets.push(e.leftFace); | |
483 oldFacets.push(e.rightFace); | |
484 traverse(tr0.e_p, tr1.e_s, e.leftFace); | |
485 traverse(tr1.e_p, tr0.e_s, e.rightFace); | |
486 | |
487 var hd = new Clustering(this.bbox.x1 - 10, this.bbox.y1 - 10, this.bbox.x2 + 10, this.bbox.y2 + 10); | |
488 var hull = []; | |
489 for (var i in hole ) { | |
490 if (!(hole[i].leftFace == null && hole[i].rightFace == null)) { | |
491 hull.push(hole[i].v0); | |
492 hull.push(hole[i].v1); | |
493 } | |
494 } | |
495 var hullVertices = []; | |
496 var distinct = []; | |
497 for (var i in vertices ) { | |
498 if (arrayIndex(distinct, vertices[i]) == -1) { | |
499 hd.add(vertices[i]); | |
500 distinct.push(vertices[i]); | |
501 } | |
502 if (arrayIndex(hull, vertices[i]) != -1) { | |
503 hullVertices.push(vertices[i]); | |
504 } | |
505 } | |
506 | |
507 var newFacets = []; | |
508 var isBoundary = function(e) { | |
509 for (var i = 0; i < hole.length; i++) { | |
510 if (hole[i].equals(e)) { | |
511 return i; | |
512 } | |
513 } | |
514 return -1; | |
515 } | |
516 var holeEdges = new Array(hole.length); | |
517 var nonHoleEdges = []; | |
518 | |
519 for (var i = 0; i < hd.edges.length; i++) { | |
520 var e = hd.edges[i]; | |
521 var b = isBoundary(e); | |
522 if (b != -1) { | |
523 if (!e.legal) { | |
524 var t1 = e.leftFace.getTriple(e); | |
525 var t2 = e.rightFace.getTriple(e); | |
526 var edge = new Edge(t1.u, t2.u); | |
527 for (var j = 0; j < hd.edges.length; j++) { | |
528 if (hd.edges[j].equals(edge) && hd.edges[j].legal) { | |
529 hd.edges[j].legal = false; | |
530 break; | |
531 } | |
532 } | |
533 t1.e_p.setFace(e.leftFace); | |
534 t1.e_s.setFace(e.leftFace); | |
535 t2.e_p.setFace(e.rightFace); | |
536 t2.e_s.setFace(e.rightFace); | |
537 | |
538 e.legal = true; | |
539 } | |
540 holeEdges[b] = e; | |
541 } else { | |
542 nonHoleEdges.push(e); | |
543 } | |
544 } | |
545 | |
546 for (var i = 0; i < holeEdges.length; i++) { | |
547 var e = holeEdges[i]; | |
548 if (hole[i].leftFace == null) { | |
549 hole[i].leftFace = e.leftFace; | |
550 hole[i].leftFace.replace(e, hole[i]); | |
551 if (arrayIndex(newFacets, hole[i].leftFace) == -1) { | |
552 newFacets.push(hole[i].leftFace); | |
553 } | |
554 } | |
555 if (hole[i].rightFace == null) { | |
556 hole[i].rightFace = e.rightFace; | |
557 hole[i].rightFace.replace(e, hole[i]); | |
558 if (arrayIndex(newFacets, hole[i].rightFace) == -1) { | |
559 newFacets.push(hole[i].rightFace); | |
560 } | |
561 } | |
562 } | |
563 | |
564 for (var i = 0; i < nonHoleEdges.length; i++) { | |
565 var e = nonHoleEdges[i]; | |
566 if (!e.legal) { | |
567 continue; | |
568 } | |
569 if (this.JordanTest(hullVertices, e)) { | |
570 this.edges.push(e); | |
571 if (arrayIndex(newFacets, e.rightFace) == -1) { | |
572 newFacets.push(e.rightFace); | |
573 } | |
574 if (arrayIndex(newFacets, e.leftFace) == -1) { | |
575 newFacets.push(e.leftFace); | |
576 } | |
577 } | |
578 } | |
579 | |
580 for (var i in oldFacets ) { | |
581 oldFacets[i].descendants = newFacets; | |
582 } | |
583 | |
584 for (var i = 0; i < newFacets.length; i++) { | |
585 var simplex = newFacets[i].interior(v); | |
586 if (simplex == null) { | |
587 continue; | |
588 } else { | |
589 this.addVertex(v, simplex); | |
590 break; | |
591 } | |
592 } | |
593 | |
594 return v; | |
595 | |
596 } | |
597 | |
598 Clustering.prototype.JordanTest = function(pol, e) { | |
599 var p = new Vertex((e.v0.x + e.v1.x) * 0.5, (e.v0.y + e.v1.y) * 0.5); | |
600 var inside = false; | |
601 var i, j = pol.length - 1; | |
602 for ( i = 0; i < pol.length; j = i++) { | |
603 var p1 = pol[i]; | |
604 var p2 = pol[j]; | |
605 if ((((p1.y <= p.y) && (p.y < p2.y)) || ((p2.y <= p.y) && (p.y < p1.y))) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) | |
606 inside = !inside; | |
607 } | |
608 return inside; | |
609 } | |
610 | |
611 Clustering.prototype.mergeForResolution = function(resolution, circleGap, circleOverlap) { | |
612 this.deleteEdges = new BinaryHeap(function(e) { | |
613 return e.weight; | |
614 }); | |
615 this.weightEdges(resolution, circleGap, circleOverlap); | |
616 var index = 0; | |
617 while (this.deleteEdges.size() > 0) { | |
618 var e = this.deleteEdges.pop(); | |
619 if (e.legal) { | |
620 var l = this.edges.length; | |
621 var newVertex = this.mergeVertices(e); | |
622 newVertex.CalculateRadius(resolution); | |
623 for (var k = l; k < this.edges.length; k++) { | |
624 var eNew = this.edges[k]; | |
625 if (eNew.legal) { | |
626 if( circleGap != 0 ){ | |
627 eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius + circleGap * resolution ); | |
628 } | |
629 else if( circleOverlap.overlap == 0 ){ | |
630 eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius); | |
631 } | |
632 else { | |
633 var r1 = eNew.v0.radius; | |
634 var r2 = eNew.v1.radius; | |
635 var r = eNew.length; | |
636 if( r < r1 + r2 ){ | |
637 if( circleOverlap.type == 'diameter' ){ | |
638 var ol1 = (r2-(r-r1)) / r1 / 2; | |
639 var ol2 = (r1-(r-r2)) / r2 / 2; | |
640 var ol = Math.max(ol1,ol2); | |
641 eNew.weight = circleOverlap.overlap / ol; | |
642 } | |
643 if( circleOverlap.type == 'area' ){ | |
644 if( !(r+r1 < r2 || r+r2 < r1) ){ | |
645 var A = r2*r2*Math.acos((r*r+r2*r2-r1*r1)/(2*r*r2))+r1*r1*Math.acos((r*r+r1*r1-r2*r2)/(2*r*r1))-1/2*Math.sqrt((-r+r1+r2)*(r-r1+r2)*(r+r1-r2)*(r+r1+r2)); | |
646 var ol1 = A / (Math.PI*r1*r1); | |
647 var ol2 = A / (Math.PI*r2*r2); | |
648 var ol = Math.max(ol1,ol2); | |
649 eNew.weight = circleOverlap.overlap / ol; | |
650 } | |
651 else { | |
652 eNew.weight = 0; | |
653 } | |
654 } | |
655 } | |
656 } | |
657 if (eNew.weight < 1) { | |
658 this.deleteEdges.push(eNew); | |
659 } | |
660 } | |
661 } | |
662 } | |
663 } | |
664 } | |
665 | |
666 Clustering.prototype.weightEdges = function(resolution, circleGap, circleOverlap) { | |
667 for (var i = 0; i < this.vertices.length; i++) { | |
668 if (this.vertices[i].legal) { | |
669 this.vertices[i].CalculateRadius(resolution); | |
670 } | |
671 } | |
672 var newEdges = []; | |
673 for (var i = 0; i < this.edges.length; i++) { | |
674 var e = this.edges[i]; | |
675 if (e.legal) { | |
676 if (!e.v0.legal || !e.v1.legal) { | |
677 e.weight = 1; | |
678 } else { | |
679 if( circleGap != 0 ){ | |
680 e.weight = e.length / (e.v0.radius + e.v1.radius + circleGap * resolution ); | |
681 } | |
682 else if( circleOverlap.overlap == 0 ){ | |
683 e.weight = e.length / (e.v0.radius + e.v1.radius); | |
684 } | |
685 else { | |
686 var r1 = e.v0.radius; | |
687 var r2 = e.v1.radius; | |
688 var r = e.length; | |
689 if( r < r1 + r2 ){ | |
690 if( circleOverlap.type == 'diameter' ){ | |
691 var ol1 = (r2-(r-r1)) / r1 / 2; | |
692 var ol2 = (r1-(r-r2)) / r2 / 2; | |
693 var ol = Math.max(ol1,ol2); | |
694 e.weight = circleOverlap.overlap / ol; | |
695 } | |
696 if( circleOverlap.type == 'area' ){ | |
697 if( !(r+r1 < r2 || r+r2 < r1) ){ | |
698 var A = r2*r2*Math.acos((r*r+r2*r2-r1*r1)/(2*r*r2))+r1*r1*Math.acos((r*r+r1*r1-r2*r2)/(2*r*r1))-1/2*Math.sqrt((-r+r1+r2)*(r-r1+r2)*(r+r1-r2)*(r+r1+r2)); | |
699 var ol1 = A / (Math.PI*r1*r1); | |
700 var ol2 = A / (Math.PI*r2*r2); | |
701 var ol = Math.max(ol1,ol2); | |
702 e.weight = circleOverlap.overlap / ol; | |
703 } | |
704 else { | |
705 e.weight = 0; | |
706 } | |
707 } | |
708 } | |
709 } | |
710 if (e.weight < 1) { | |
711 this.deleteEdges.push(e); | |
712 } | |
713 } | |
714 newEdges.push(e); | |
715 } | |
716 } | |
717 this.edges = newEdges; | |
718 } | |
719 | |
720 Clustering.prototype.ValidityTest = function() { | |
721 console.info("Test 1: Valid Delaunay ..."); | |
722 /* | |
723 var leafs = []; | |
724 var triangles = this.boundingTriangle.descendants; | |
725 var j = 0; | |
726 while( triangles.length > j ){ | |
727 var t = triangles[j]; | |
728 if( t.taken == undefined ){ | |
729 t.taken = true; | |
730 if( this.isLeaf(t) ){ | |
731 leafs.push(t); | |
732 } | |
733 else { | |
734 triangles = triangles.concat(t.descendants); | |
735 } | |
736 } | |
737 j++; | |
738 } | |
739 console.info(" Number of Triangles: "+leafs.length); | |
740 | |
741 var c = 0; | |
742 for( i in this.edges ){ | |
743 if( this.edges[i].legal ){ | |
744 c++; | |
745 } | |
746 } | |
747 console.info(" Number of Edges: "+c);*/ | |
748 /* | |
749 | |
750 for( var i=0; i<leafs.length; i++ ){ | |
751 for( var j=0; j<vertices.length; j++ ){ | |
752 if( !leafs[i].contains(vertices[j]) && leafs[i].inCircumcircle(vertices[j]) ){ | |
753 console.info(leafs[i],vertices[j]); | |
754 | |
755 } | |
756 } | |
757 } | |
758 */ | |
759 | |
760 //console.info("Test 2: Edges Facets (null) ..."); | |
761 for (i in this.edges ) { | |
762 var e = this.edges[i]; | |
763 if (e.leftFace == null || e.rightFace == null) { | |
764 console.info(e); | |
765 alert(); | |
766 } | |
767 } | |
768 | |
769 //console.info("Test 3: Edges Facets ..."); | |
770 var leftOf = function(v1, v2, v) { | |
771 var x2 = v1.x - v2.x; | |
772 var x3 = v1.x - v.x; | |
773 var y2 = v1.y - v2.y; | |
774 var y3 = v1.y - v.y; | |
775 if (x2 * y3 - y2 * x3 < 0) { | |
776 return true; | |
777 } | |
778 return false; | |
779 } | |
780 var c = 0; | |
781 for (i in this.edges ) { | |
782 var e = this.edges[i]; | |
783 var t1 = e.leftFace.getTriple(e); | |
784 var t2 = e.rightFace.getTriple(e); | |
785 if (e.v0.y == e.v1.y) { | |
786 if (t1.u.y > t2.u.y) { | |
787 console.info("equal y conflict ..."); | |
788 console.info(e); | |
789 alert(); | |
790 c++; | |
791 } | |
792 } else { | |
793 var v1, v2; | |
794 if (e.v0.y > e.v1.y) { | |
795 v1 = e.v0; | |
796 v2 = e.v1; | |
797 } else { | |
798 v1 = e.v1; | |
799 v2 = e.v0; | |
800 } | |
801 if (!leftOf(v1, v2, t1.u)) { | |
802 console.info("left right conflict ... left is right"); | |
803 console.info(e); | |
804 alert(); | |
805 c++; | |
806 } | |
807 if (leftOf(v1, v2, t2.u)) { | |
808 console.info("left right conflict ... right is left"); | |
809 console.info(e); | |
810 alert(); | |
811 c++; | |
812 } | |
813 } | |
814 } | |
815 //console.info("Number of Edges: "+this.edges.length); | |
816 //console.info("Number of Conflicts: "+c); | |
817 | |
818 for (i in this.edges ) { | |
819 if (this.edges[i].legal) { | |
820 var e = this.edges[i]; | |
821 var tr0 = e.leftFace.getTriple(e); | |
822 var tr1 = e.rightFace.getTriple(e); | |
823 if (!tr0.e_p.legal || !tr0.e_s.legal || !tr1.e_p.legal || !tr1.e_s.legal) { | |
824 console.info(e); | |
825 console.info("conflict in edge continuity"); | |
826 return; | |
827 } | |
828 } | |
829 } | |
830 | |
831 } | |
832 function BinaryHeap(scoreFunction) { | |
833 this.content = []; | |
834 this.scoreFunction = scoreFunction; | |
835 } | |
836 | |
837 BinaryHeap.prototype = { | |
838 push : function(element) { | |
839 // Add the new element to the end of the array. | |
840 this.content.push(element); | |
841 // Allow it to bubble up. | |
842 this.bubbleUp(this.content.length - 1); | |
843 }, | |
844 | |
845 pop : function() { | |
846 // Store the first element so we can return it later. | |
847 var result = this.content[0]; | |
848 // Get the element at the end of the array. | |
849 var end = this.content.pop(); | |
850 // If there are any elements left, put the end element at the | |
851 // start, and let it sink down. | |
852 if (this.content.length > 0) { | |
853 this.content[0] = end; | |
854 this.sinkDown(0); | |
855 } | |
856 return result; | |
857 }, | |
858 | |
859 remove : function(node) { | |
860 var len = this.content.length; | |
861 // To remove a value, we must search through the array to find | |
862 // it. | |
863 for (var i = 0; i < len; i++) { | |
864 if (this.content[i] == node) { | |
865 // When it is found, the process seen in 'pop' is repeated | |
866 // to fill up the hole. | |
867 var end = this.content.pop(); | |
868 if (i != len - 1) { | |
869 this.content[i] = end; | |
870 if (this.scoreFunction(end) < this.scoreFunction(node)) | |
871 this.bubbleUp(i); | |
872 else | |
873 this.sinkDown(i); | |
874 } | |
875 return; | |
876 } | |
877 } | |
878 throw new Error("Node not found."); | |
879 }, | |
880 | |
881 size : function() { | |
882 return this.content.length; | |
883 }, | |
884 | |
885 bubbleUp : function(n) { | |
886 // Fetch the element that has to be moved. | |
887 var element = this.content[n]; | |
888 // When at 0, an element can not go up any further. | |
889 while (n > 0) { | |
890 // Compute the parent element's index, and fetch it. | |
891 var parentN = Math.floor((n + 1) / 2) - 1, parent = this.content[parentN]; | |
892 // Swap the elements if the parent is greater. | |
893 if (this.scoreFunction(element) < this.scoreFunction(parent)) { | |
894 this.content[parentN] = element; | |
895 this.content[n] = parent; | |
896 // Update 'n' to continue at the new position. | |
897 n = parentN; | |
898 | |
899 } | |
900 // Found a parent that is less, no need to move it further. | |
901 else { | |
902 break; | |
903 } | |
904 } | |
905 }, | |
906 | |
907 sinkDown : function(n) { | |
908 // Look up the target element and its score. | |
909 var length = this.content.length, element = this.content[n], elemScore = this.scoreFunction(element); | |
910 | |
911 while (true) { | |
912 // Compute the indices of the child elements. | |
913 var child2N = (n + 1) * 2, child1N = child2N - 1; | |
914 // This is used to store the new position of the element, | |
915 // if any. | |
916 var swap = null; | |
917 // If the first child exists (is inside the array)... | |
918 if (child1N < length) { | |
919 // Look it up and compute its score. | |
920 var child1 = this.content[child1N], child1Score = this.scoreFunction(child1); | |
921 // If the score is less than our element's, we need to swap. | |
922 if (child1Score < elemScore) | |
923 swap = child1N; | |
924 } | |
925 // Do the same checks for the other child. | |
926 if (child2N < length) { | |
927 var child2 = this.content[child2N], child2Score = this.scoreFunction(child2); | |
928 if (child2Score < (swap == null ? elemScore : child1Score)) | |
929 swap = child2N; | |
930 } | |
931 | |
932 // If the element needs to be moved, swap it, and continue. | |
933 if (swap != null) { | |
934 this.content[n] = this.content[swap]; | |
935 this.content[swap] = element; | |
936 n = swap; | |
937 } | |
938 // Otherwise, we are done. | |
939 else { | |
940 break; | |
941 } | |
942 } | |
943 } | |
944 }; |