Pārlūkot izejas kodu

slightly changed Bounds API, added IPoint, IBounds, IPolygon

ncannasse 9 gadi atpakaļ
vecāks
revīzija
4a61628bac
8 mainītis faili ar 478 papildinājumiem un 12 dzēšanām
  1. 3 3
      h2d/Sprite.hx
  2. 9 5
      h2d/col/Bounds.hx
  3. 219 0
      h2d/col/IBounds.hx
  4. 57 0
      h2d/col/IPoint.hx
  5. 178 0
      h2d/col/IPolygon.hx
  6. 4 0
      h2d/col/Point.hx
  7. 6 2
      h2d/col/Polygon.hx
  8. 2 2
      h2d/col/Segments.hx

+ 3 - 3
h2d/Sprite.hx

@@ -480,7 +480,7 @@ class Sprite {
 		add(sxMax, syMax);
 
 		// intersects
-		bounds.intersectWith(view);
+		bounds.doIntersect(view);
 	}
 
 	function drawFilters( ctx : RenderContext ) {
@@ -493,7 +493,7 @@ class Sprite {
 				if( f.boundsExtend > maxExtent ) maxExtent = f.boundsExtend;
 			} else {
 				f.getBounds(this, bounds);
-				total.add(bounds);
+				total.addBounds(bounds);
 			}
 		}
 		if( maxExtent >= 0 ) {
@@ -502,7 +502,7 @@ class Sprite {
 			bounds.yMin -= maxExtent;
 			bounds.xMax += maxExtent;
 			bounds.yMax += maxExtent;
-			total.add(bounds);
+			total.addBounds(bounds);
 		}
 
 		clipBounds(ctx, total);

+ 9 - 5
h2d/col/Bounds.hx

@@ -19,15 +19,19 @@ class Bounds {
 		empty();
 	}
 
-	public inline function collide( b : Bounds ) {
+	public inline function toIBounds( scale = 1. ) {
+		return IBounds.fromValues(Math.floor(x * scale), Math.floor(y * scale), Math.floor(width * scale), Math.floor(height * scale));
+	}
+
+	public inline function intersects( b : Bounds ) {
 		return !(xMin > b.xMax || yMin > b.yMax || xMax < b.xMin || yMax < b.yMin);
 	}
 
-	public inline function include( p : Point ) {
+	public inline function contains( p : Point ) {
 		return p.x >= xMin && p.x < xMax && p.y >= yMin && p.y < yMax;
 	}
 
-	public inline function add( b : Bounds ) {
+	public inline function addBounds( b : Bounds ) {
 		if( b.xMin < xMin ) xMin = b.xMin;
 		if( b.xMax > xMax ) xMax = b.xMax;
 		if( b.yMin < yMin ) yMin = b.yMin;
@@ -65,14 +69,14 @@ class Bounds {
 		yMax = p.y;
 	}
 
-	public inline function intersectWith( b : Bounds ) {
+	public inline function doIntersect( b : Bounds ) {
 		xMin = Math.max(xMin, b.xMin);
 		yMin = Math.max(yMin, b.yMin);
 		xMax = Math.min(xMax, b.xMax);
 		yMax = Math.min(yMax, b.yMax);
 	}
 
-	public inline function unionWith( b : Bounds ) {
+	public inline function doUnion( b : Bounds ) {
 		xMin = Math.min(xMin, b.xMin);
 		yMin = Math.min(yMin, b.yMin);
 		xMax = Math.max(xMax, b.xMax);

+ 219 - 0
h2d/col/IBounds.hx

@@ -0,0 +1,219 @@
+package h2d.col;
+import hxd.Math;
+
+class IBounds {
+
+	public var xMin : Int;
+	public var yMin : Int;
+
+	public var xMax : Int;
+	public var yMax : Int;
+
+
+	public var x(get, set) : Int;
+	public var y(get, set) : Int;
+	public var width(get, set) : Int;
+	public var height(get, set) : Int;
+
+	public inline function new() {
+		empty();
+	}
+
+	public inline function toBounds( scale = 1. ) {
+		return Bounds.fromValues(x * scale, y * scale, width * scale, height * scale);
+	}
+
+	public inline function intersects( b : IBounds ) {
+		return !(xMin > b.xMax || yMin > b.yMax || xMax < b.xMin || yMax < b.yMin);
+	}
+
+	public inline function contains( p : IPoint ) {
+		return p.x >= xMin && p.x < xMax && p.y >= yMin && p.y < yMax;
+	}
+
+	public inline function addBounds( b : IBounds ) {
+		if( b.xMin < xMin ) xMin = b.xMin;
+		if( b.xMax > xMax ) xMax = b.xMax;
+		if( b.yMin < yMin ) yMin = b.yMin;
+		if( b.yMax > yMax ) yMax = b.yMax;
+	}
+
+	public inline function addPoint( p : IPoint ) {
+		if( p.x < xMin ) xMin = p.x;
+		if( p.x > xMax ) xMax = p.x;
+		if( p.y < yMin ) yMin = p.y;
+		if( p.y > yMax ) yMax = p.y;
+	}
+
+	public inline function addPos( x : Int, y : Int ) {
+		if( x < xMin ) xMin = x;
+		if( x > xMax ) xMax = x;
+		if( y < yMin ) yMin = y;
+		if( y > yMax ) yMax = y;
+	}
+
+	public inline function set(x, y, width, height) {
+		this.xMin = x;
+		this.yMin = y;
+		this.xMax = x + width;
+		this.yMax = y + height;
+	}
+
+	public inline function setMin( p : IPoint ) {
+		xMin = p.x;
+		yMin = p.y;
+	}
+
+	public inline function setMax( p : IPoint ) {
+		xMax = p.x;
+		yMax = p.y;
+	}
+
+	public inline function doIntersect( b : IBounds ) {
+		xMin = Math.imax(xMin, b.xMin);
+		yMin = Math.imax(yMin, b.yMin);
+		xMax = Math.imin(xMax, b.xMax);
+		yMax = Math.imin(yMax, b.yMax);
+	}
+
+	public inline function doUnion( b : IBounds ) {
+		xMin = Math.imin(xMin, b.xMin);
+		yMin = Math.imin(yMin, b.yMin);
+		xMax = Math.imax(xMax, b.xMax);
+		yMax = Math.imax(yMax, b.yMax);
+	}
+
+	public function intersection( b : IBounds ) {
+		var i = new Bounds();
+		i.xMin = Math.imax(xMin, b.xMin);
+		i.yMin = Math.imax(yMin, b.yMin);
+		i.xMax = Math.imin(xMax, b.xMax);
+		i.yMax = Math.imin(yMax, b.yMax);
+		if( i.xMax < i.xMin ) i.xMax = i.xMin;
+		if( i.yMax < i.yMin ) i.yMax = i.yMin;
+		return i;
+	}
+
+	public function union( b : IBounds ) {
+		var i = new Bounds();
+		i.xMin = Math.imin(xMin, b.xMin);
+		i.yMin = Math.imin(yMin, b.yMin);
+		i.xMax = Math.imax(xMax, b.xMax);
+		i.yMax = Math.imax(yMax, b.yMax);
+		return i;
+	}
+
+	public function load( b : IBounds ) {
+		xMin = b.xMin;
+		yMin = b.yMin;
+		xMax = b.xMax;
+		yMax = b.yMax;
+	}
+
+	public inline function offset( dx : Int, dy : Int ) {
+		xMin += dx;
+		xMax += dx;
+		yMin += dy;
+		yMax += dy;
+	}
+
+	public inline function getMin() {
+		return new IPoint(xMin, yMin);
+	}
+
+	public inline function getCenter() {
+		return new IPoint((xMin + xMax) >> 1, (yMin + yMax) >> 1);
+	}
+
+	public inline function getSize() {
+		return new IPoint(xMax - xMin, yMax - yMin);
+	}
+
+	public inline function getMax() {
+		return new IPoint(xMax, yMax);
+	}
+
+	public inline function isEmpty() {
+		return xMax <= xMin || yMax <= yMin;
+	}
+
+	public inline function empty() {
+		xMin = 0x7FFFFFFF;
+		yMin = 0x7FFFFFFF;
+		xMax = -2147483648;
+		yMax = -2147483648;
+	}
+
+	public inline function all() {
+		xMin = -2147483648;
+		yMin = -2147483648;
+		xMax = 0x7FFFFFFF;
+		yMax = 0x7FFFFFFF;
+	}
+
+	public inline function clone() {
+		var b = new Bounds();
+		b.xMin = xMin;
+		b.yMin = yMin;
+		b.xMax = xMax;
+		b.yMax = yMax;
+		return b;
+	}
+
+	inline function get_x() {
+		return xMin;
+	}
+
+	inline function get_y() {
+		return yMin;
+	}
+
+	inline function set_x(x:Int) {
+		xMax += x - xMin;
+		return xMin = x;
+	}
+
+	inline function set_y(y:Int) {
+		yMax += y - yMin;
+		return yMin = y;
+	}
+
+	inline function get_width() {
+		return xMax - xMin;
+	}
+
+	inline function get_height() {
+		return yMax - yMin;
+	}
+
+	inline function set_width(w) {
+		xMax = xMin + w;
+		return w;
+	}
+
+	inline function set_height(h) {
+		yMax = yMin + h;
+		return h;
+	}
+
+	public function toString() {
+		return "{" + getMin() + "," + getSize() + "}";
+	}
+
+	public static inline function fromValues( x0 : Int, y0 : Int, width : Int, height : Int ) {
+		var b = new IBounds();
+		b.xMin = x0;
+		b.yMin = y0;
+		b.xMax = x0 + width;
+		b.yMax = y0 + height;
+		return b;
+	}
+
+	public static inline function fromPoints( min : IPoint, max : IPoint ) {
+		var b = new IBounds();
+		b.setMin(min);
+		b.setMax(max);
+		return b;
+	}
+
+}

+ 57 - 0
h2d/col/IPoint.hx

@@ -0,0 +1,57 @@
+package h2d.col;
+import hxd.Math;
+
+class IPoint {
+
+	public var x : Int;
+	public var y : Int;
+
+	public inline function new(x = 0, y = 0) {
+		this.x = x;
+		this.y = y;
+	}
+
+	public inline function toPoint( scale = 1. ) {
+		return new Point(x * scale, y * scale);
+	}
+
+	public inline function distanceSq( p : IPoint ) {
+		var dx = x - p.x;
+		var dy = y - p.y;
+		return dx * dx + dy * dy;
+	}
+
+	public inline function distance( p : IPoint ) {
+		return Math.sqrt(distanceSq(p));
+	}
+
+	public function toString() {
+		return "{" + x + "," + y + "}";
+	}
+
+	public inline function sub( p : IPoint ) {
+		return new Point(x - p.x, y - p.y);
+	}
+
+	public inline function add( p : IPoint ) {
+		return new Point(x + p.x, y + p.y);
+	}
+
+	public inline function dot( p : IPoint ) {
+		return x * p.x + y * p.y;
+	}
+
+	public inline function lengthSq() {
+		return x * x + y * y;
+	}
+
+	public inline function length() {
+		return Math.sqrt(lengthSq());
+	}
+
+	public inline function set(x,y) {
+		this.x = x;
+		this.y = y;
+	}
+
+}

+ 178 - 0
h2d/col/IPolygon.hx

@@ -0,0 +1,178 @@
+package h2d.col;
+import hxd.Math;
+
+abstract IPolygon(Array<IPoint>) from Array<IPoint> to Array<IPoint> {
+
+	public var points(get, never) : Array<IPoint>;
+
+	inline function get_points() return this;
+
+	public inline function new( points ) {
+		this = points;
+	}
+
+	public function toPolygon( scale = 1. ) {
+		return [for( p in points ) p.toPoint(scale)];
+	}
+
+	public function getBounds( ?b : IBounds ) {
+		if( b == null ) b = new IBounds();
+		for( p in points )
+			b.addPoint(p);
+		return b;
+	}
+
+	public function convexHull() {
+		inline function side(p1 : IPoint, p2 : IPoint, p3 : IPoint) {
+			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 contains( 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 ) : IPolygon {
+		var out = [];
+		optimizeRec(points, 0, points.length, out, epsilon);
+		return out;
+	}
+
+	static function optimizeRec( points : Array<IPoint>, index : Int, len : Int, out : Array<IPoint>, epsilon : Float ) {
+		var dmax = 0.;
+		var result = [];
+
+		inline function distPointSeg(p0:IPoint, p1:IPoint, p2:IPoint) {
+			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 : Float, yy : Float;
+
+			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]);
+		}
+	}
+
+}

+ 4 - 0
h2d/col/Point.hx

@@ -11,6 +11,10 @@ class Point {
 		this.y = y;
 	}
 
+	public inline function toIPoint( scale = 1. ) {
+		return new IPoint(Math.floor(x * scale), Math.floor(y * scale));
+	}
+
 	public inline function distanceSq( p : Point ) {
 		var dx = x - p.x;
 		var dy = y - p.y;

+ 6 - 2
h2d/col/Polygon.hx

@@ -11,7 +11,7 @@ abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 		this = points;
 	}
 
-	public function getSegments() : Segments {
+	public function toSegments() : Segments {
 		var segments = [];
 		var p1 = points[points.length - 1];
 		for( p2 in points ) {
@@ -22,6 +22,10 @@ abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 		return segments;
 	}
 
+	public function toIPolygon( scale = 1. ) {
+		return [for( p in points ) p.toIPoint(scale)];
+	}
+
 	public function getBounds( ?b : Bounds ) {
 		if( b == null ) b = new Bounds();
 		for( p in points )
@@ -102,7 +106,7 @@ abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 		this.reverse();
 	}
 
-	public inline function containsPoint( p : Point, isConvex ) {
+	public inline function contains( p : Point, isConvex ) {
 		if( isConvex ) {
 			var p1 = points[points.length - 1];
 			for( p2 in points ) {

+ 2 - 2
h2d/col/Segments.hx

@@ -10,7 +10,7 @@ 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 ) {
+	public function containsPoint( p : Point, isConvex ) {
 		if( isConvex ) {
 			for( s in segments )
 				if( s.side(p) < 0 )
@@ -21,7 +21,7 @@ abstract Segments(Array<Segment>) from Array<Segment> to Array<Segment> {
 		return true;
 	}
 
-	public function getPolygon() : Polygon {
+	public function toPolygon() : Polygon {
 		return [for( s in segments ) new h2d.col.Point(s.x, s.y)];
 	}