diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/GeoTemCo/js/Map/Clustering.js	Thu May 28 10:28:12 2015 +0200
@@ -0,0 +1,944 @@
+/*
+* Clustering.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class Vertex, Edge, Triangle, Clustering, BinaryHeap
+ * Dynamic Delaunay clustering algorithm (see GeoTemCo paper)
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+
+function Vertex(x, y, categories, binning) {
+	this.x = x;
+	this.y = y;
+	this.radius
+	this.size = 0;
+	this.elements = [];
+	this.radii = [];
+	this.weights = [];
+	this.legal = true;
+	this.binning = binning;
+	if (categories != undefined) {
+		for (var i = 0; i < categories; i++) {
+			this.elements.push([]);
+			this.weights.push(0);
+		}
+	}
+}
+
+Vertex.prototype.merge = function(v0, v1) {
+	for (var i = 0; i < v0.elements.length; i++) {
+		this.elements[i] = v0.elements[i].concat(v1.elements[i]);
+		this.weights[i] = v0.weights[i] + v1.weights[i];
+		this.size += this.weights[i];
+	}
+}
+
+Vertex.prototype.CalculateRadius = function(resolution) {
+	this.radii = [];
+	for (i in this.elements ) {
+		this.radii.push(this.binning.getRadius(this.weights[i]));
+	}
+	if (this.radii.length == 1) {
+		this.radius = this.radii[0] * resolution;
+	} else {
+		var count = 0;
+		var max1 = 0;
+		var max2 = 0;
+		for (i in this.radii ) {
+			if (this.radii[i] != 0) {
+				count++;
+			}
+			if (this.radii[i] > max1) {
+				if (max1 > max2) {
+					max2 = max1;
+				}
+				max1 = this.radii[i];
+			} else if (this.radii[i] > max2) {
+				max2 = this.radii[i];
+			}
+		}
+		if (count == 1) {
+			this.radius = max1 * resolution;
+		} else if (count == 2) {
+			this.radius = (max1 + max2) * resolution;
+		} else if (count == 3) {
+			var d = (2 / 3 * Math.sqrt(3) - 1) * max1;
+			this.radius = (d + max1 + max2) * resolution;
+		} else if (count == 4) {
+			var d = (Math.sqrt(2) - 1) * max2;
+			this.radius = (d + max1 + max2) * resolution;
+		}
+	}
+}
+
+Vertex.prototype.addElement = function(e, weight, index) {
+	this.elements[index].push(e);
+	this.size += weight;
+	this.weights[index] += weight;
+}
+function Edge(v0, v1) {
+	this.v0 = v0;
+	this.v1 = v1;
+	this.leftFace
+	this.rightFace
+	this.legal = true;
+	this.setLength();
+}
+
+Edge.prototype.setLength = function() {
+	var dx = this.v0.x - this.v1.x;
+	var dy = this.v0.y - this.v1.y;
+	this.length = Math.sqrt(dx * dx + dy * dy);
+}
+
+Edge.prototype.contains = function(v) {
+	if (this.v0 == v || this.v1 == v) {
+		return true;
+	}
+	return false;
+}
+
+Edge.prototype.replaceFace = function(f_old, f_new) {
+	if (this.leftFace == f_old) {
+		this.leftFace = f_new;
+	} else if (this.rightFace == f_old) {
+		this.rightFace = f_new;
+	}
+}
+
+Edge.prototype.setFace = function(f) {
+	if (f.leftOf(this)) {
+		this.leftFace = f;
+	} else {
+		this.rightFace = f;
+	}
+}
+
+Edge.prototype.setFaces = function(f1, f2) {
+	if (f1.leftOf(this)) {
+		this.leftFace = f1;
+		this.rightFace = f2;
+	} else {
+		this.leftFace = f2;
+		this.rightFace = f1;
+	}
+}
+
+Edge.prototype.removeFace = function(f) {
+	if (this.leftFace == f) {
+		this.leftFace = null;
+	} else {
+		this.rightFace = null;
+	}
+}
+
+Edge.prototype.equals = function(e) {
+	if (this.v0 == e.v0 && this.v1 == e.v1 || this.v0 == e.v1 && this.v1 == e.v0) {
+		return true;
+	}
+	return false;
+}
+function Triangle(edges) {
+	this.edges = edges;
+	this.setVertices();
+	this.descendants = [];
+}
+
+Triangle.prototype.getTriple = function(e) {
+	var i = arrayIndex(this.edges, e);
+	return {
+		e_s : this.edges[(i + 1) % 3],
+		e_p : this.edges[(i + 2) % 3],
+		u : this.vertices[(i + 2) % 3]
+	};
+}
+
+Triangle.prototype.leftOf = function(e) {
+	var i = arrayIndex(this.edges, e);
+	if (this.vertices[i].y != this.vertices[(i + 1) % 3].y) {
+		return this.vertices[i].y > this.vertices[(i + 1) % 3].y;
+	}
+	return this.vertices[i].y > this.vertices[(i + 2) % 3].y;
+}
+
+Triangle.prototype.getNext = function(v) {
+	var i = arrayIndex(this.vertices, v);
+	return this.vertices[(i + 1) % 3];
+}
+
+Triangle.prototype.oppositeEdge = function(v) {
+	var i = arrayIndex(this.vertices, v);
+	return this.edges[(i + 1) % 3];
+}
+
+Triangle.prototype.contains = function(v) {
+	return arrayIndex(this.vertices, v) != -1;
+}
+
+Triangle.prototype.replace = function(e_old, e_new) {
+	this.edges[arrayIndex(this.edges, e_old)] = e_new;
+}
+
+Triangle.prototype.setVertices = function() {
+	if (this.edges[1].v0 == this.edges[0].v0 || this.edges[1].v1 == this.edges[0].v0) {
+		this.vertices = [this.edges[0].v1, this.edges[0].v0];
+	} else {
+		this.vertices = [this.edges[0].v0, this.edges[0].v1];
+	}
+	if (this.edges[2].v0 == this.vertices[0]) {
+		this.vertices.push(this.edges[2].v1);
+	} else {
+		this.vertices.push(this.edges[2].v0);
+	}
+}
+
+Triangle.prototype.replaceBy = function(triangles) {
+	this.descendants = triangles;
+	this.edges[0].replaceFace(this, triangles[0]);
+	this.edges[1].replaceFace(this, triangles[1]);
+	this.edges[2].replaceFace(this, triangles[2]);
+}
+
+Triangle.prototype.CalcCircumcircle = function() {
+	var v0 = this.vertices[0];
+	var v1 = this.vertices[1];
+	var v2 = this.vertices[2];
+	var A = v1.x - v0.x;
+	var B = v1.y - v0.y;
+	var C = v2.x - v0.x;
+	var D = v2.y - v0.y;
+	var E = A * (v0.x + v1.x) + B * (v0.y + v1.y);
+	var F = C * (v0.x + v2.x) + D * (v0.y + v2.y);
+	var G = 2.0 * (A * (v2.y - v1.y) - B * (v2.x - v1.x));
+	var cx = (D * E - B * F) / G;
+	var cy = (A * F - C * E) / G;
+	this.center = new Vertex(cx, cy);
+	var dx = this.center.x - v0.x;
+	var dy = this.center.y - v0.y;
+	this.radius_squared = dx * dx + dy * dy;
+};
+
+Triangle.prototype.inCircumcircle = function(v) {
+	if (this.radius_squared == undefined) {
+		this.CalcCircumcircle();
+	}
+	var dx = this.center.x - v.x;
+	var dy = this.center.y - v.y;
+	var dist_squared = dx * dx + dy * dy;
+	return (dist_squared <= this.radius_squared );
+};
+
+Triangle.prototype.interior = function(v) {
+	var v0 = this.vertices[0];
+	var v1 = this.vertices[1];
+	var v2 = this.vertices[2];
+	var dotAB = (v.x - v0.x ) * (v0.y - v1.y ) + (v.y - v0.y ) * (v1.x - v0.x );
+	var dotBC = (v.x - v1.x ) * (v1.y - v2.y ) + (v.y - v1.y ) * (v2.x - v1.x );
+	var dotCA = (v.x - v2.x ) * (v2.y - v0.y ) + (v.y - v2.y ) * (v0.x - v2.x );
+	if (dotAB > 0 || dotBC > 0 || dotCA > 0) {
+		return null;
+	} else if (dotAB < 0 && dotBC < 0 && dotCA < 0) {
+		return this;
+	} else if (dotAB == 0) {
+		if (dotBC == 0) {
+			return this.vertices[1];
+		} else if (dotCA == 0) {
+			return this.vertices[0];
+		}
+		return this.edges[0];
+	} else if (dotBC == 0) {
+		if (dotCA == 0) {
+			return this.vertices[2];
+		}
+		return this.edges[1];
+	} else if (dotCA == 0) {
+		return this.edges[2];
+	}
+};
+
+function Clustering(xMin, yMin, xMax, yMax) {
+	this.triangles = [];
+	this.newTriangles = [];
+	this.bbox = {
+		x1 : xMin,
+		y1 : yMin,
+		x2 : xMax,
+		y2 : yMax
+	};
+	this.CreateBoundingTriangle();
+	this.edges = [];
+	this.vertices = [];
+	this.legalizes = 0;
+	this.collapses = 0;
+}
+
+Clustering.prototype.locate = function(v) {
+	if (this.boundingTriangle.descendants.length == 0) {
+		return this.boundingTriangle;
+	}
+	var triangles = this.boundingTriangle.descendants;
+	while (true) {
+		for (var i = 0; i < triangles.length; i++) {
+			var simplex = triangles[i].interior(v);
+			if (simplex == null) {
+				continue;
+			}
+			if ( simplex instanceof Vertex || this.isLeaf(triangles[i])) {
+				return simplex;
+			}
+			triangles = triangles[i].descendants;
+			break;
+		}
+	}
+}
+
+Clustering.prototype.legalize = function(v, e, t0_old) {
+	if (!e.v0.legal && !e.v1.legal) {
+		return;
+	}
+	this.legalizes++;
+	var flip = false;
+	var t1_old, tr1;
+	if (e.leftFace == t0_old && e.rightFace.inCircumcircle(v)) {
+		flip = true;
+		t1_old = e.rightFace;
+	} else if (e.rightFace == t0_old && e.leftFace.inCircumcircle(v)) {
+		flip = true;
+		t1_old = e.leftFace;
+	}
+	if (flip) {
+		var tr0 = t0_old.getTriple(e);
+		var tr1 = t1_old.getTriple(e);
+		var e_flip = new Edge(tr0.u, tr1.u);
+		var poly = [];
+		poly.push(e.v0);
+		poly.push(e_flip.v0);
+		poly.push(e.v1);
+		poly.push(e_flip.v1);
+		if (!this.JordanTest(poly, e_flip)) {
+			return;
+		}
+		e.legal = false;
+		this.edges.push(e_flip);
+		var t0_new = new Triangle([e_flip, tr0.e_p, tr1.e_s]);
+		var t1_new = new Triangle([e_flip, tr1.e_p, tr0.e_s]);
+		e_flip.setFaces(t0_new, t1_new);
+		tr0.e_p.replaceFace(t0_old, t0_new);
+		tr1.e_s.replaceFace(t1_old, t0_new);
+		tr1.e_p.replaceFace(t1_old, t1_new);
+		tr0.e_s.replaceFace(t0_old, t1_new);
+		t0_old.descendants = [t0_new, t1_new];
+		t1_old.descendants = [t0_new, t1_new];
+		this.legalize(v, t0_new.edges[2], t0_new);
+		this.legalize(v, t1_new.edges[1], t1_new);
+	}
+}
+
+Clustering.prototype.add = function(v) {
+	this.addVertex(v, this.locate(v));
+}
+
+Clustering.prototype.addVertex = function(v, simplex) {
+	if ( simplex instanceof Vertex) {
+		simplex.merge(simplex, v);
+	} else if ( simplex instanceof Edge) {
+		this.vertices.push(v);
+		simplex.legal = false;
+		var tr0 = simplex.leftFace.getTriple(simplex);
+		var tr1 = simplex.rightFace.getTriple(simplex);
+		var e0 = new Edge(v, tr0.u);
+		var e1 = new Edge(v, simplex.leftFace.getNext(tr0.u));
+		var e2 = new Edge(v, tr1.u);
+		var e3 = new Edge(v, simplex.rightFace.getNext(tr1.u));
+		var t0 = new Triangle([e0, tr0.e_p, e1]);
+		var t1 = new Triangle([e1, tr1.e_s, e2]);
+		var t2 = new Triangle([e2, tr1.e_p, e3]);
+		var t3 = new Triangle([e3, tr0.e_s, e0]);
+		simplex.leftFace.descendants = [t0, t3];
+		simplex.rightFace.descendants = [t1, t2];
+		this.edges.push(e0);
+		this.edges.push(e1);
+		this.edges.push(e2);
+		this.edges.push(e3);
+		e0.setFaces(t0, t3);
+		e1.setFaces(t0, t1);
+		e2.setFaces(t1, t2);
+		e3.setFaces(t2, t3);
+		tr0.e_p.replaceFace(simplex.leftFace, t0);
+		tr1.e_s.replaceFace(simplex.rightFace, t1);
+		tr1.e_p.replaceFace(simplex.rightFace, t2);
+		tr0.e_s.replaceFace(simplex.leftFace, t3);
+		this.legalize(v, tr0.e_p, t0);
+		this.legalize(v, tr1.e_s, t1);
+		this.legalize(v, tr1.e_p, t2);
+		this.legalize(v, tr0.e_s, t3);
+	} else {
+		this.vertices.push(v);
+		var e_i = new Edge(simplex.vertices[0], v);
+		var e_j = new Edge(simplex.vertices[1], v);
+		var e_k = new Edge(simplex.vertices[2], v);
+		this.edges.push(e_i);
+		this.edges.push(e_j);
+		this.edges.push(e_k);
+		var t0 = new Triangle([e_i, simplex.edges[0], e_j]);
+		var t1 = new Triangle([e_j, simplex.edges[1], e_k]);
+		var t2 = new Triangle([e_k, simplex.edges[2], e_i]);
+		e_i.setFaces(t0, t2);
+		e_j.setFaces(t0, t1);
+		e_k.setFaces(t1, t2);
+		simplex.replaceBy([t0, t1, t2]);
+		this.legalize(v, simplex.edges[0], t0);
+		this.legalize(v, simplex.edges[1], t1);
+		this.legalize(v, simplex.edges[2], t2);
+	}
+}
+
+Clustering.prototype.isLeaf = function(t) {
+	return t.descendants.length == 0;
+}
+
+Clustering.prototype.CreateBoundingTriangle = function() {
+	var dx = (this.bbox.x2 - this.bbox.x1 ) * 10;
+	var dy = (this.bbox.y2 - this.bbox.y1 ) * 10;
+	var v0 = new Vertex(this.bbox.x1 - dx, this.bbox.y1 - dy * 3);
+	var v1 = new Vertex(this.bbox.x2 + dx * 3, this.bbox.y2 + dy);
+	var v2 = new Vertex(this.bbox.x1 - dx, this.bbox.y2 + dy);
+	var e0 = new Edge(v1, v0);
+	var e1 = new Edge(v0, v2);
+	var e2 = new Edge(v2, v1);
+	v0.legal = false;
+	v1.legal = false;
+	v2.legal = false;
+	this.boundingTriangle = new Triangle([e0, e1, e2]);
+	var inf = new Triangle([e0, e1, e2]);
+	e0.setFaces(this.boundingTriangle, inf);
+	e1.setFaces(this.boundingTriangle, inf);
+	e2.setFaces(this.boundingTriangle, inf);
+}
+
+Clustering.prototype.mergeVertices = function(e) {
+	this.collapses++;
+	var s0 = e.v0.size;
+	var s1 = e.v1.size;
+	var x = (e.v0.x * s0 + e.v1.x * s1 ) / (s0 + s1 );
+	var y = (e.v0.y * s0 + e.v1.y * s1 ) / (s0 + s1 );
+	var v = new Vertex(x, y, e.v0.elements.length, e.v0.binning);
+	v.merge(e.v0, e.v1);
+
+	e.v0.legal = false;
+	e.v1.legal = false;
+
+	var hole = [];
+	var oldFacets = [];
+	e.legal = false;
+
+	var vertices = [];
+	var traverse = function(eLeft, eRight, triangle) {
+		eLeft.legal = false;
+		do {
+			var triple;
+			if (eLeft.leftFace == triangle) {
+				triple = eLeft.rightFace.getTriple(eLeft);
+				oldFacets.push(eLeft.rightFace);
+				triple.e_s.removeFace(eLeft.rightFace);
+				triangle = eLeft.rightFace;
+			} else {
+				triple = eLeft.leftFace.getTriple(eLeft);
+				oldFacets.push(eLeft.leftFace);
+				triple.e_s.removeFace(eLeft.leftFace);
+				triangle = eLeft.leftFace;
+			}
+			if (arrayIndex(hole, triple.e_s) == -1) {
+				hole.push(triple.e_s);
+			}
+			vertices.push(triple.u);
+			eLeft = triple.e_p;
+			eLeft.legal = false;
+		} while( eLeft != eRight );
+	}
+	var tr0 = e.leftFace.getTriple(e);
+	var tr1 = e.rightFace.getTriple(e);
+	oldFacets.push(e.leftFace);
+	oldFacets.push(e.rightFace);
+	traverse(tr0.e_p, tr1.e_s, e.leftFace);
+	traverse(tr1.e_p, tr0.e_s, e.rightFace);
+
+	var hd = new Clustering(this.bbox.x1 - 10, this.bbox.y1 - 10, this.bbox.x2 + 10, this.bbox.y2 + 10);
+	var hull = [];
+	for (var i in hole ) {
+		if (!(hole[i].leftFace == null && hole[i].rightFace == null)) {
+			hull.push(hole[i].v0);
+			hull.push(hole[i].v1);
+		}
+	}
+	var hullVertices = [];
+	var distinct = [];
+	for (var i in vertices ) {
+		if (arrayIndex(distinct, vertices[i]) == -1) {
+			hd.add(vertices[i]);
+			distinct.push(vertices[i]);
+		}
+		if (arrayIndex(hull, vertices[i]) != -1) {
+			hullVertices.push(vertices[i]);
+		}
+	}
+
+	var newFacets = [];
+	var isBoundary = function(e) {
+		for (var i = 0; i < hole.length; i++) {
+			if (hole[i].equals(e)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	var holeEdges = new Array(hole.length);
+	var nonHoleEdges = [];
+
+	for (var i = 0; i < hd.edges.length; i++) {
+		var e = hd.edges[i];
+		var b = isBoundary(e);
+		if (b != -1) {
+			if (!e.legal) {
+				var t1 = e.leftFace.getTriple(e);
+				var t2 = e.rightFace.getTriple(e);
+				var edge = new Edge(t1.u, t2.u);
+				for (var j = 0; j < hd.edges.length; j++) {
+					if (hd.edges[j].equals(edge) && hd.edges[j].legal) {
+						hd.edges[j].legal = false;
+						break;
+					}
+				}
+				t1.e_p.setFace(e.leftFace);
+				t1.e_s.setFace(e.leftFace);
+				t2.e_p.setFace(e.rightFace);
+				t2.e_s.setFace(e.rightFace);
+
+				e.legal = true;
+			}
+			holeEdges[b] = e;
+		} else {
+			nonHoleEdges.push(e);
+		}
+	}
+
+	for (var i = 0; i < holeEdges.length; i++) {
+		var e = holeEdges[i];
+		if (hole[i].leftFace == null) {
+			hole[i].leftFace = e.leftFace;
+			hole[i].leftFace.replace(e, hole[i]);
+			if (arrayIndex(newFacets, hole[i].leftFace) == -1) {
+				newFacets.push(hole[i].leftFace);
+			}
+		}
+		if (hole[i].rightFace == null) {
+			hole[i].rightFace = e.rightFace;
+			hole[i].rightFace.replace(e, hole[i]);
+			if (arrayIndex(newFacets, hole[i].rightFace) == -1) {
+				newFacets.push(hole[i].rightFace);
+			}
+		}
+	}
+
+	for (var i = 0; i < nonHoleEdges.length; i++) {
+		var e = nonHoleEdges[i];
+		if (!e.legal) {
+			continue;
+		}
+		if (this.JordanTest(hullVertices, e)) {
+			this.edges.push(e);
+			if (arrayIndex(newFacets, e.rightFace) == -1) {
+				newFacets.push(e.rightFace);
+			}
+			if (arrayIndex(newFacets, e.leftFace) == -1) {
+				newFacets.push(e.leftFace);
+			}
+		}
+	}
+
+	for (var i in oldFacets ) {
+		oldFacets[i].descendants = newFacets;
+	}
+
+	for (var i = 0; i < newFacets.length; i++) {
+		var simplex = newFacets[i].interior(v);
+		if (simplex == null) {
+			continue;
+		} else {
+			this.addVertex(v, simplex);
+			break;
+		}
+	}
+
+	return v;
+
+}
+
+Clustering.prototype.JordanTest = function(pol, e) {
+	var p = new Vertex((e.v0.x + e.v1.x) * 0.5, (e.v0.y + e.v1.y) * 0.5);
+	var inside = false;
+	var i, j = pol.length - 1;
+	for ( i = 0; i < pol.length; j = i++) {
+		var p1 = pol[i];
+		var p2 = pol[j];
+		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))
+			inside = !inside;
+	}
+	return inside;
+}
+
+Clustering.prototype.mergeForResolution = function(resolution, circleGap, circleOverlap) {
+	this.deleteEdges = new BinaryHeap(function(e) {
+		return e.weight;
+	});
+	this.weightEdges(resolution, circleGap, circleOverlap);
+	var index = 0;
+	while (this.deleteEdges.size() > 0) {
+		var e = this.deleteEdges.pop();
+		if (e.legal) {
+			var l = this.edges.length;
+			var newVertex = this.mergeVertices(e);
+			newVertex.CalculateRadius(resolution);
+			for (var k = l; k < this.edges.length; k++) {
+				var eNew = this.edges[k];
+				if (eNew.legal) {
+					if( circleGap != 0 ){
+						eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius + circleGap * resolution );
+					}
+					else if( circleOverlap.overlap == 0 ){
+						eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius);
+					}
+					else {
+						var r1 = eNew.v0.radius;
+						var r2 = eNew.v1.radius;
+						var r = eNew.length;
+						if( r < r1 + r2 ){
+							if( circleOverlap.type == 'diameter' ){
+								var ol1 = (r2-(r-r1)) / r1 / 2;
+								var ol2 = (r1-(r-r2)) / r2 / 2;
+								var ol = Math.max(ol1,ol2);
+								eNew.weight = circleOverlap.overlap / ol;
+							}
+							if( circleOverlap.type == 'area' ){								
+								if( !(r+r1 < r2 || r+r2 < r1) ){
+									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));
+									var ol1 = A / (Math.PI*r1*r1);
+									var ol2 = A / (Math.PI*r2*r2);
+									var ol = Math.max(ol1,ol2);
+									eNew.weight = circleOverlap.overlap / ol;
+								}
+								else {
+									eNew.weight = 0;
+								}
+							}
+						}
+					}
+					if (eNew.weight < 1) {
+						this.deleteEdges.push(eNew);
+					}
+				}
+			}
+		}
+	}
+}
+
+Clustering.prototype.weightEdges = function(resolution, circleGap, circleOverlap) {
+	for (var i = 0; i < this.vertices.length; i++) {
+		if (this.vertices[i].legal) {
+			this.vertices[i].CalculateRadius(resolution);
+		}
+	}
+	var newEdges = [];
+	for (var i = 0; i < this.edges.length; i++) {
+		var e = this.edges[i];
+		if (e.legal) {
+			if (!e.v0.legal || !e.v1.legal) {
+				e.weight = 1;
+			} else {
+				if( circleGap != 0 ){
+					e.weight = e.length / (e.v0.radius + e.v1.radius + circleGap * resolution );
+				}
+				else if( circleOverlap.overlap == 0 ){
+					e.weight = e.length / (e.v0.radius + e.v1.radius);
+				}
+				else {
+					var r1 = e.v0.radius;
+					var r2 = e.v1.radius;
+					var r = e.length;
+					if( r < r1 + r2 ){
+						if( circleOverlap.type == 'diameter' ){
+							var ol1 = (r2-(r-r1)) / r1 / 2;
+							var ol2 = (r1-(r-r2)) / r2 / 2;
+							var ol = Math.max(ol1,ol2);
+							e.weight = circleOverlap.overlap / ol;
+						}
+						if( circleOverlap.type == 'area' ){
+							if( !(r+r1 < r2 || r+r2 < r1) ){
+								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));
+								var ol1 = A / (Math.PI*r1*r1);
+								var ol2 = A / (Math.PI*r2*r2);
+								var ol = Math.max(ol1,ol2);
+								e.weight = circleOverlap.overlap / ol;
+							}
+							else {
+								e.weight = 0;
+							}
+						}
+					}
+				}
+				if (e.weight < 1) {
+					this.deleteEdges.push(e);
+				}
+			}
+			newEdges.push(e);
+		}
+	}
+	this.edges = newEdges;
+}
+
+Clustering.prototype.ValidityTest = function() {
+	console.info("Test 1: Valid Delaunay ...");
+	/*
+	var leafs = [];
+	var triangles = this.boundingTriangle.descendants;
+	var j = 0;
+	while( triangles.length > j ){
+	var t = triangles[j];
+	if( t.taken == undefined ){
+	t.taken = true;
+	if( this.isLeaf(t) ){
+	leafs.push(t);
+	}
+	else {
+	triangles = triangles.concat(t.descendants);
+	}
+	}
+	j++;
+	}
+	console.info("  Number of Triangles: "+leafs.length);
+
+	var c = 0;
+	for( i in this.edges ){
+	if( this.edges[i].legal ){
+	c++;
+	}
+	}
+	console.info("  Number of Edges: "+c);*/
+	/*
+
+	for( var i=0; i<leafs.length; i++ ){
+	for( var j=0; j<vertices.length; j++ ){
+	if( !leafs[i].contains(vertices[j]) && leafs[i].inCircumcircle(vertices[j]) ){
+	console.info(leafs[i],vertices[j]);
+
+	}
+	}
+	}
+	*/
+
+	//console.info("Test 2: Edges Facets (null) ...");
+	for (i in this.edges ) {
+		var e = this.edges[i];
+		if (e.leftFace == null || e.rightFace == null) {
+			console.info(e);
+			alert();
+		}
+	}
+
+	//console.info("Test 3: Edges Facets ...");
+	var leftOf = function(v1, v2, v) {
+		var x2 = v1.x - v2.x;
+		var x3 = v1.x - v.x;
+		var y2 = v1.y - v2.y;
+		var y3 = v1.y - v.y;
+		if (x2 * y3 - y2 * x3 < 0) {
+			return true;
+		}
+		return false;
+	}
+	var c = 0;
+	for (i in this.edges ) {
+		var e = this.edges[i];
+		var t1 = e.leftFace.getTriple(e);
+		var t2 = e.rightFace.getTriple(e);
+		if (e.v0.y == e.v1.y) {
+			if (t1.u.y > t2.u.y) {
+				console.info("equal y conflict ...");
+				console.info(e);
+				alert();
+				c++;
+			}
+		} else {
+			var v1, v2;
+			if (e.v0.y > e.v1.y) {
+				v1 = e.v0;
+				v2 = e.v1;
+			} else {
+				v1 = e.v1;
+				v2 = e.v0;
+			}
+			if (!leftOf(v1, v2, t1.u)) {
+				console.info("left right conflict ... left is right");
+				console.info(e);
+				alert();
+				c++;
+			}
+			if (leftOf(v1, v2, t2.u)) {
+				console.info("left right conflict ... right is left");
+				console.info(e);
+				alert();
+				c++;
+			}
+		}
+	}
+	//console.info("Number of Edges: "+this.edges.length);
+	//console.info("Number of Conflicts: "+c);
+
+	for (i in this.edges ) {
+		if (this.edges[i].legal) {
+			var e = this.edges[i];
+			var tr0 = e.leftFace.getTriple(e);
+			var tr1 = e.rightFace.getTriple(e);
+			if (!tr0.e_p.legal || !tr0.e_s.legal || !tr1.e_p.legal || !tr1.e_s.legal) {
+				console.info(e);
+				console.info("conflict in edge continuity");
+				return;
+			}
+		}
+	}
+
+}
+function BinaryHeap(scoreFunction) {
+	this.content = [];
+	this.scoreFunction = scoreFunction;
+}
+
+BinaryHeap.prototype = {
+	push : function(element) {
+		// Add the new element to the end of the array.
+		this.content.push(element);
+		// Allow it to bubble up.
+		this.bubbleUp(this.content.length - 1);
+	},
+
+	pop : function() {
+		// Store the first element so we can return it later.
+		var result = this.content[0];
+		// Get the element at the end of the array.
+		var end = this.content.pop();
+		// If there are any elements left, put the end element at the
+		// start, and let it sink down.
+		if (this.content.length > 0) {
+			this.content[0] = end;
+			this.sinkDown(0);
+		}
+		return result;
+	},
+
+	remove : function(node) {
+		var len = this.content.length;
+		// To remove a value, we must search through the array to find
+		// it.
+		for (var i = 0; i < len; i++) {
+			if (this.content[i] == node) {
+				// When it is found, the process seen in 'pop' is repeated
+				// to fill up the hole.
+				var end = this.content.pop();
+				if (i != len - 1) {
+					this.content[i] = end;
+					if (this.scoreFunction(end) < this.scoreFunction(node))
+						this.bubbleUp(i);
+					else
+						this.sinkDown(i);
+				}
+				return;
+			}
+		}
+		throw new Error("Node not found.");
+	},
+
+	size : function() {
+		return this.content.length;
+	},
+
+	bubbleUp : function(n) {
+		// Fetch the element that has to be moved.
+		var element = this.content[n];
+		// When at 0, an element can not go up any further.
+		while (n > 0) {
+			// Compute the parent element's index, and fetch it.
+			var parentN = Math.floor((n + 1) / 2) - 1, parent = this.content[parentN];
+			// Swap the elements if the parent is greater.
+			if (this.scoreFunction(element) < this.scoreFunction(parent)) {
+				this.content[parentN] = element;
+				this.content[n] = parent;
+				// Update 'n' to continue at the new position.
+				n = parentN;
+
+			}
+			// Found a parent that is less, no need to move it further.
+			else {
+				break;
+			}
+		}
+	},
+
+	sinkDown : function(n) {
+		// Look up the target element and its score.
+		var length = this.content.length, element = this.content[n], elemScore = this.scoreFunction(element);
+
+		while (true) {
+			// Compute the indices of the child elements.
+			var child2N = (n + 1) * 2, child1N = child2N - 1;
+			// This is used to store the new position of the element,
+			// if any.
+			var swap = null;
+			// If the first child exists (is inside the array)...
+			if (child1N < length) {
+				// Look it up and compute its score.
+				var child1 = this.content[child1N], child1Score = this.scoreFunction(child1);
+				// If the score is less than our element's, we need to swap.
+				if (child1Score < elemScore)
+					swap = child1N;
+			}
+			// Do the same checks for the other child.
+			if (child2N < length) {
+				var child2 = this.content[child2N], child2Score = this.scoreFunction(child2);
+				if (child2Score < (swap == null ? elemScore : child1Score))
+					swap = child2N;
+			}
+
+			// If the element needs to be moved, swap it, and continue.
+			if (swap != null) {
+				this.content[n] = this.content[swap];
+				this.content[swap] = element;
+				n = swap;
+			}
+			// Otherwise, we are done.
+			else {
+				break;
+			}
+		}
+	}
+};