Ver Fonte

optimize convexHull algorithm

bstouls há 9 anos atrás
pai
commit
fe0f4bf4eb
1 ficheiros alterados com 27 adições e 38 exclusões
  1. 27 38
      h2d/col/Polygon.hx

+ 27 - 38
h2d/col/Polygon.hx

@@ -48,52 +48,41 @@ abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 		return b;
 		return b;
 	}
 	}
 
 
-	//sources : https://fr.wikipedia.org/wiki/Parcours_de_Graham
-	//step by step demonstration : http://www.algomation.com/algorithm/graham-scan-convex-hull
+	inline function xSort(a : Point, b : Point) {
+		if(a.x == b.x)
+			return a.y < b.y ? -1 : 1;
+		return a.x < b.x ? -1 : 1;
+	}
+
+	//see Monotone_chain convex hull algorythm
 	public function convexHull() {
 	public function convexHull() {
 		var len = points.length;
 		var len = points.length;
 		if( points.length < 3 )
 		if( points.length < 3 )
 			return points;
 			return points;
 
 
-		//find lowest y points
-		var p0 = points[0];
-		for( p in points ) {
-			if( p.y < p0.y || (p.y == p0.y && p.x < p0.x) )
-				p0 = p;
-		}
-
-		//sort by angle from p0
-		var pts : Array<{p : h2d.col.Point, a : Float}> = [];
-		var p1 = new h2d.col.Point(p0.x + 1e5, p0.y);
-		for(p in points) {
-			if(p.x == p0.x && p.y == p0.y) continue;
-			pts.push({p : p, a : side(p0, p1, p)});
-		}
-		pts.sort(function(pa, pb) return side(p0, pa.p, pb.p) > 0 ? -1 : 1);
-
-		//remove same angle points (includes duplicated points)
-		var index = pts.length - 1;
-		while(index > 0) {
-			var cur = pts[index];
-			var prev = pts[index - 1];
-			if(cur.a == prev.a) {
-				if(Math.distanceSq(cur.p.x - p0.x, cur.p.y - p0.y) < Math.distanceSq(prev.p.x - p0.x, prev.p.y - p0.y))
-					pts.remove(cur);
-				else pts.remove(prev);
-			}
-			index--;
-		}
+		points.sort(xSort);
 
 
-		//set hull
-		var hull = [ p0, pts[0].p, pts[1].p ];
-		for (i in 2...pts.length) {
-			var pi = pts[i].p;
-			while (side(hull[hull.length - 2], hull[hull.length - 1], pi) <= 0)
-				hull.pop();
-			hull.push(pi);
+		var hull = [];
+		var k = 0;
+		for (p in points) {
+			while (k >= 2 && side(hull[k - 2], hull[k - 1], p) <= 0)
+				k--;
+			hull[k++] = p;
 		}
 		}
 
 
-		return hull;
+	   var i = points.length - 2;
+	   var len = k + 1;
+	   while(i >= 0) {
+			var p = points[i];
+			while (k >= len && side(hull[k - 2], hull[k - 1], p) <= 0)
+				k--;
+			hull[k++] = p;
+			i--;
+	   }
+
+	   while( hull.length >= k )
+			hull.pop();
+	   return hull;
 	}
 	}
 
 
 	public function isClockwise() {
 	public function isClockwise() {