Browse Source

changed polygon and segment

ncannasse 9 years ago
parent
commit
705f9b43f6
4 changed files with 239 additions and 78 deletions
  1. 0 77
      h2d/col/Poly.hx
  2. 185 0
      h2d/col/Polygon.hx
  3. 1 1
      h2d/col/Segment.hx
  4. 53 0
      h2d/col/Segments.hx

+ 0 - 77
h2d/col/Poly.hx

@@ -1,77 +0,0 @@
-package h2d.col;
-import hxd.Math;
-
-class Poly {
-
-	public var points : Array<Point>;
-	var segments : Array<Seg>;
-
-	public function new( points ) {
-		this.points = points;
-	}
-
-	public function isConvex() {
-		for( i in 0...points.length ) {
-			var p1 = points[i];
-			var p2 = points[(i + 1) % points.length];
-			var p3 = points[(i + 2) % points.length];
-			if( (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) < 0 )
-				return false;
-		}
-		return true;
-	}
-
-	public function calculateArea() {
-		var s = 0.;
-		for( i in 0...points.length ) {
-			var p = points[i];
-			var n = points[(i + 1) % points.length];
-			s += p.y * n.x - p.x * n.y;
-		}
-		return s * 0.5;
-	}
-
-	public function getSegments() {
-		if( segments != null )
-			return segments;
-		segments = [];
-		for( i in 0...points.length ) {
-			var s = new Seg(points[i], points[(i + 1) % points.length]);
-			segments.push(s);
-		}
-		return segments;
-	}
-
-	public function hasPoint( p : Point ) {
-		for( s in getSegments() )
-			if( s.side(p) < 0 )
-				return false;
-		return true;
-	}
-
-	public function project( p : Point ) : Point {
-		var dmin = 1e20, smin = null;
-		for( s in getSegments() ) {
-			var d = s.distanceSq(p);
-			if( d < dmin ) {
-				dmin = d;
-				smin = s;
-			}
-		}
-		return smin.project(p);
-	}
-
-	public function distanceSq( p : Point ) {
-		var dmin = 1e20;
-		for( s in getSegments() ) {
-			var d = s.distanceSq(p);
-			if( d < dmin ) dmin = d;
-		}
-		return dmin;
-	}
-
-	public inline function distance( p : Point ) {
-		return Math.sqrt(distanceSq(p));
-	}
-
-}

+ 185 - 0
h2d/col/Polygon.hx

@@ -0,0 +1,185 @@
+package h2d.col;
+import hxd.Math;
+
+abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
+
+	public var points(get, never) : Array<Point>;
+
+	inline function get_points() return this;
+
+	public inline function new( points ) {
+		this = points;
+	}
+
+	public function getSegments() : Segments {
+		var segments = [];
+		var p1 = points[points.length - 1];
+		for( p2 in points ) {
+			var s = new Segment(p1, p2);
+			segments.push(s);
+			p1 = p2;
+		}
+		return segments;
+	}
+
+	public function getBounds( ?b : Bounds ) {
+		if( b == null ) b = new Bounds();
+		for( p in points )
+			b.addPoint(p);
+		return b;
+	}
+
+	public function convexHull() {
+		inline function side(p1 : Point, p2 : Point, p3 : Point) {
+			return (p2.y - p1.y) * (p3.x - p2.x) - (p2.x - p1.x) * (p3.y - p2.y);
+		}
+
+		var len = points.length;
+		if( len < 3 )
+			throw "convexHull() needs at least 3 points";
+
+		var first = 0;
+		var firstX = points[first].x;
+		for( i in 1...points.length ) {
+			var px = points[i].x;
+			if( px < firstX ) {
+				first = i;
+				firstX = px;
+			}
+		}
+
+		var hull = [];
+		var curr = first;
+		var next = 0;
+		do {
+			hull.push(points[curr]);
+			next = (curr + 1) % len;
+			for( i in 0...len ) {
+			   if( side(points[i], points[curr], points[next]) > 0 )
+				   next = i;
+			}
+			curr = next;
+		} while( curr != first );
+		return hull;
+	}
+
+	public function isClockwise() {
+		var sum = 0.;
+		var p1 = points[points.length - 1];
+		for( p2 in points ) {
+			sum += (p2.x - p1.x) * (p2.y + p1.y);
+			p1 = p2;
+		}
+		return sum < 0; // Y axis is negative compared to classic maths
+	}
+
+	public function area() {
+		var sum = 0.;
+		var p1 = points[points.length - 1];
+		for( p2 in points ) {
+			sum += p1.y * p2.x - p2.x * p1.y;
+			p1 = p2;
+		}
+		return sum * 0.5;
+	}
+
+	public function isConvex() {
+		var p1 = points[points.length - 2];
+		var p2 = points[points.length - 1];
+		var p3 = points[0];
+		var side = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) > 0;
+		for( i in 1...points.length ) {
+			p1 = p2;
+			p2 = p3;
+			p3 = points[i];
+			if( ((p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) > 0) != side )
+				return false;
+		}
+		return true;
+	}
+
+	public function reverse() : Void {
+		this.reverse();
+	}
+
+	public inline function containsPoint( p : Point, isConvex ) {
+		if( isConvex ) {
+			var p1 = points[points.length - 1];
+			for( p2 in points ) {
+				var side = (p2.x - p1.x) * (p.y - p1.y) - (p2.y - p1.y) * (p.x - p1.x);
+				if( side < 0 )
+					return false;
+				p1 = p2;
+			}
+			return true;
+		} else {
+			throw "TODO";
+		}
+	}
+
+	/**
+		Creates a new optimized polygon by eliminating almost colinear edges according to epsilon distance.
+	**/
+	public function optimize( epsilon : Float ) : Polygon {
+		var out = [];
+		optimizeRec(points, 0, points.length, out, epsilon);
+		return out;
+	}
+
+	static function optimizeRec( points : Array<Point>, index : Int, len : Int, out : Array<Point>, epsilon : Float ) {
+		var dmax = 0.;
+		var result = [];
+
+		inline function distPointSeg(p0:Point, p1:Point, p2:Point) {
+			var A = p0.x - p1.x;
+			var B = p0.y - p1.y;
+			var C = p2.x - p1.x;
+			var D = p2.y - p1.y;
+
+			var dot = A * C + B * D;
+			var dist = C * C + D * D;
+			var param = -1.;
+			if (dist != 0)
+			  param = dot / dist;
+
+			var xx, yy;
+
+			if (param < 0) {
+				xx = p1.x;
+				yy = p1.y;
+			}
+			else if (param > 1) {
+				xx = p2.x;
+				yy = p2.y;
+			}
+			else {
+				xx = p1.x + param * C;
+				yy = p1.y + param * D;
+			}
+
+			var dx = p0.x - xx;
+			var dy = p0.y - yy;
+			return dx * dx + dy * dy;
+		}
+
+		var pfirst = points[index];
+		var plast = points[len - 1];
+		for( i in index+1...len - 1 ) {
+			var d = distPointSeg(points[i], pfirst, plast);
+			if(d > dmax) {
+				index = i;
+				dmax = d;
+			}
+		}
+
+		if( dmax >= epsilon ) {
+			optimizeRec(points, 0, index, out, epsilon);
+			out.pop();
+			optimizeRec(points, index, len, out, epsilon);
+		} else {
+			out.push(points[index]);
+			out.push(points[len - 1]);
+		}
+	}
+
+}

+ 1 - 1
h2d/col/Seg.hx → h2d/col/Segment.hx

@@ -1,7 +1,7 @@
 package h2d.col;
 import hxd.Math;
 
-class Seg {
+class Segment {
 
 	public var x : Float;
 	public var y : Float;

+ 53 - 0
h2d/col/Segments.hx

@@ -0,0 +1,53 @@
+package h2d.col;
+import hxd.Math;
+
+/**
+	Another way to represent a Polygon. Segments must be connected.
+	This allows efficient distance calculus.
+**/
+abstract Segments(Array<Segment>) from Array<Segment> to Array<Segment> {
+
+	public var segments(get, never) : Array<Segment>;
+	inline function get_segments() return this;
+
+	public function hasPoint( p : Point, isConvex ) {
+		if( isConvex ) {
+			for( s in segments )
+				if( s.side(p) < 0 )
+					return false;
+		} else {
+			throw "TODO";
+		}
+		return true;
+	}
+
+	public function getPolygon() : Polygon {
+		return [for( s in segments ) new h2d.col.Point(s.x, s.y)];
+	}
+
+	public function project( p : Point ) : Point {
+		var dmin = 1e20, smin = null;
+		for( s in segments ) {
+			var d = s.distanceSq(p);
+			if( d < dmin ) {
+				dmin = d;
+				smin = s;
+			}
+		}
+		return smin.project(p);
+	}
+
+	public function distanceSq( p : Point ) {
+		var dmin = 1e20;
+		for( s in segments ) {
+			var d = s.distanceSq(p);
+			if( d < dmin ) dmin = d;
+		}
+		return dmin;
+	}
+
+	public inline function distance( p : Point ) {
+		return Math.sqrt(distanceSq(p));
+	}
+
+}