Explorar el Código

integrated clipper operations to polygons

ncannasse hace 9 años
padre
commit
806e03b0a0

+ 4 - 0
h2d/col/IPoint.hx

@@ -54,4 +54,8 @@ class IPoint {
 		this.y = y;
 	}
 
+	public inline function clone() {
+		return new IPoint(x, y);
+	}
+
 }

+ 58 - 4
h2d/col/IPolygon.hx

@@ -1,14 +1,29 @@
 package h2d.col;
 import hxd.Math;
 
+enum OffsetKind {
+	Square;
+	Miter;
+	Round( arc : Float );
+}
+
 abstract IPolygon(Array<IPoint>) from Array<IPoint> to Array<IPoint> {
 
 	public var points(get, never) : Array<IPoint>;
-
+	public var length(get, never) : Int;
+	inline function get_length() return this.length;
 	inline function get_points() return this;
 
-	public inline function new( points ) {
-		this = points;
+	public inline function new( ?points ) {
+		this = points == null ? [] : points;
+	}
+
+	public inline function addPoint( p : IPoint ) {
+		this.push(p);
+	}
+
+	public inline function iterator() {
+		return new hxd.impl.ArrayIterator(this);
 	}
 
 	public function toPolygon( scale = 1. ) {
@@ -22,6 +37,45 @@ abstract IPolygon(Array<IPoint>) from Array<IPoint> to Array<IPoint> {
 		return b;
 	}
 
+	public function union( p : IPolygon, withHoles = false ) : IPolygons {
+		var c = new hxd.clipper.Clipper();
+		if( !withHoles ) c.resultKind = NoHoles;
+		c.addPolygon(this, Clip);
+		c.addPolygon(p,Clip);
+		return c.execute(Union, NonZero, NonZero);
+	}
+
+	public inline function intersection( p : IPolygon, withHoles = false ) : IPolygons {
+		return clipperOp(p, Intersection, withHoles);
+	}
+
+	public inline function subtraction( p : IPolygon, withHoles = false ) : IPolygons {
+		return clipperOp(p, Difference, withHoles);
+	}
+
+	public function offset( delta : Float, kind : OffsetKind, withHoles = false ) : IPolygons {
+		var c = new hxd.clipper.Clipper.ClipperOffset();
+		switch( kind ) {
+		case Square:
+			c.addPolygon(this, Square, ClosedPol);
+		case Miter:
+			c.addPolygon(this, Miter, ClosedPol);
+		case Round(arc):
+			c.ArcTolerance = arc;
+			c.addPolygon(this, Round, ClosedPol);
+		}
+		if( !withHoles ) c.resultKind = NoHoles;
+		return c.execute(delta);
+	}
+
+	function clipperOp( p : IPolygon, op, withHoles ) : IPolygons {
+		var c = new hxd.clipper.Clipper();
+		if( !withHoles ) c.resultKind = NoHoles;
+		c.addPolygon(this, Subject);
+		c.addPolygon(p, Clip);
+		return c.execute(op, NonZero, NonZero);
+	}
+
 	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);
@@ -95,7 +149,7 @@ abstract IPolygon(Array<IPoint>) from Array<IPoint> to Array<IPoint> {
 		this.reverse();
 	}
 
-	public inline function contains( p : Point, isConvex ) {
+	public function contains( p : Point, isConvex ) {
 		if( isConvex ) {
 			var p1 = points[points.length - 1];
 			for( p2 in points ) {

+ 81 - 0
h2d/col/IPolygons.hx

@@ -0,0 +1,81 @@
+package h2d.col;
+import hxd.Math;
+
+abstract IPolygons(Array<IPolygon>) from Array<IPolygon> to Array<IPolygon> {
+
+	public var polygons(get, never) : Array<IPolygon>;
+	public var length(get, never) : Int;
+	inline function get_length() return this.length;
+
+	inline function get_polygons() return this;
+
+	public inline function new( polygons ) {
+		this = polygons;
+	}
+
+	public inline function iterator() {
+		return new hxd.impl.ArrayIterator(this);
+	}
+
+	public function toPolygons( scale = 1. ) : Polygons {
+		return [for( p in polygons ) p.toPolygon(scale)];
+	}
+
+	public function getBounds( ?b : IBounds ) {
+		if( b == null ) b = new IBounds();
+		for( p in polygons )
+			p.getBounds(b);
+		return b;
+	}
+
+	public function union( p : IPolygons, withHoles = false ) : IPolygons {
+		var c = new hxd.clipper.Clipper();
+		if( !withHoles ) c.resultKind = NoHoles;
+		c.addPolygons(this, Clip);
+		c.addPolygons(p, Clip);
+		return c.execute(Union, NonZero, NonZero);
+	}
+
+	public inline function intersection( p : IPolygons, withHoles = false ) : IPolygons {
+		return clipperOp(p, Intersection, withHoles);
+	}
+
+	public inline function subtraction( p : IPolygons, withHoles = false ) : IPolygons {
+		return clipperOp(p, Difference, withHoles);
+	}
+
+	public function offset( delta : Float, kind : IPolygon.OffsetKind, withHoles = false ) : IPolygons {
+		var c = new hxd.clipper.Clipper.ClipperOffset();
+		switch( kind ) {
+		case Square:
+			c.addPolygons(this, Square, ClosedPol);
+		case Miter:
+			c.addPolygons(this, Miter, ClosedPol);
+		case Round(arc):
+			c.ArcTolerance = arc;
+			c.addPolygons(this, Round, ClosedPol);
+		}
+		if( !withHoles ) c.resultKind = NoHoles;
+		return c.execute(delta);
+	}
+
+	function clipperOp( p : IPolygons, op, withHoles ) : IPolygons {
+		var c = new hxd.clipper.Clipper();
+		if( !withHoles ) c.resultKind = NoHoles;
+		c.addPolygons(this, Subject);
+		c.addPolygons(p, Clip);
+		return c.execute(op, NonZero, NonZero);
+	}
+
+	public function contains( p : Point, isConvex ) {
+		for( pl in polygons )
+			if( pl.contains(p, isConvex) )
+				return true;
+		return false;
+	}
+
+	public function optimize( epsilon : Float ) : IPolygons {
+		return [for( p in polygons ) p.optimize(epsilon)];
+	}
+
+}

+ 4 - 0
h2d/col/Point.hx

@@ -66,4 +66,8 @@ class Point {
 		y *= f;
 	}
 
+	public inline function clone() {
+		return new Point(x, y);
+	}
+
 }

+ 22 - 4
h2d/col/Polygon.hx

@@ -4,11 +4,29 @@ import hxd.Math;
 abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 
 	public var points(get, never) : Array<Point>;
-
+	public var length(get, never) : Int;
+	inline function get_length() return this.length;
 	inline function get_points() return this;
 
-	public inline function new( points ) {
-		this = points;
+	public inline function new( ?points ) {
+		this = points == null ? [] : points;
+	}
+
+	public inline function iterator() {
+		return new hxd.impl.ArrayIterator(this);
+	}
+
+	public inline function addPoint( p : Point ) {
+		this.push(p);
+	}
+
+	/**
+		Uses EarCut algorithm to quickly triangulate the polygon.
+		This will not create the best triangulation possible but is quite solid wrt self-intersections and merged points.
+		Returns the points indexes
+	**/
+	public function fastTriangulate() {
+		return new hxd.earcut.Earcut().triangulate(points);
 	}
 
 	public function toSegments() : Segments {
@@ -106,7 +124,7 @@ abstract Polygon(Array<Point>) from Array<Point> to Array<Point> {
 		this.reverse();
 	}
 
-	public inline function contains( p : Point, isConvex ) {
+	public function contains( p : Point, isConvex ) {
 		if( isConvex ) {
 			var p1 = points[points.length - 1];
 			for( p2 in points ) {

+ 41 - 0
h2d/col/Polygons.hx

@@ -0,0 +1,41 @@
+package h2d.col;
+import hxd.Math;
+
+abstract Polygons(Array<Polygon>) from Array<Polygon> to Array<Polygon> {
+
+	public var polygons(get, never) : Array<Polygon>;
+	public var length(get, never) : Int;
+	inline function get_length() return this.length;
+	inline function get_polygons() return this;
+
+	public inline function new( polygons ) {
+		this = polygons;
+	}
+
+	public inline function iterator() {
+		return new hxd.impl.ArrayIterator(this);
+	}
+
+	public function toIPolygons( scale = 1. ) : IPolygons {
+		return [for( p in polygons ) p.toIPolygon(scale)];
+	}
+
+	public function getBounds( ?b : Bounds ) {
+		if( b == null ) b = new Bounds();
+		for( p in polygons )
+			p.getBounds(b);
+		return b;
+	}
+
+	public function contains( p : Point, isConvex ) {
+		for( pl in polygons )
+			if( pl.contains(p, isConvex) )
+				return true;
+		return false;
+	}
+
+	public function optimize( epsilon : Float ) : Polygons {
+		return [for( p in polygons ) p.optimize(epsilon)];
+	}
+
+}

+ 6 - 0
h2d/col/Segments.hx

@@ -9,6 +9,12 @@ abstract Segments(Array<Segment>) from Array<Segment> to Array<Segment> {
 
 	public var segments(get, never) : Array<Segment>;
 	inline function get_segments() return this;
+	public var length(get, never) : Int;
+	inline function get_length() return this.length;
+
+	public inline function iterator() {
+		return new hxd.impl.ArrayIterator(this);
+	}
 
 	public function containsPoint( p : Point, isConvex ) {
 		if( isConvex ) {

+ 1 - 14
hxd/clipper/Point.hx

@@ -1,16 +1,3 @@
 package hxd.clipper;
 
-class Point {
-	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 clone() {
-		return new Point(x, y);
-	}
-	function toString() {
-		return "{" + x + "," + y + "}";
-	}
-}
+typedef Point = h2d.col.IPoint;

+ 1 - 84
hxd/clipper/Polygon.hx

@@ -1,86 +1,3 @@
 package hxd.clipper;
 
-abstract Polygon(Array<Point>) {
-
-	public var length(get, never): Int;
-	public var points(get, never) : Array<Point>;
-
-	public inline function new(?pts) {
-		this = pts == null ? [] : pts;
-	}
-
-	public inline function add(x, y) {
-		this.push(new Point(x, y));
-	}
-
-	public inline function addPoint(p) {
-		this.push(p);
-	}
-
-	inline function get_points() : Array<Point> {
-		return this;
-	}
-
-	inline function get_length() {
-		return this.length;
-	}
-
-	public inline function reverse() {
-		this.reverse();
-	}
-
-	public function getArea() {
-		var highI = this.length - 1;
-		if (highI < 2) return 0.;
-		/*if (FullRangeNeeded(poly))
-		{
-			var a:Int128 = new Int128(0);
-			a = Int128.Int128Mul(poly[highI].x + poly[0].x, poly[0].y - poly[highI].y);
-			for ( i in 1...highI+1 )
-				a += Int128.Int128Mul(poly[i - 1].x + poly[i].x, poly[i].y - poly[i - 1].y);
-			return a.ToDouble() / 2;
-		}
-		else
-		*/
-		{
-			var area:Float = (this[highI].x + this[0].x) * 1.0 * (this[0].y * 1.0 - this[highI].y);
-			for ( i in 1...highI+1 )
-				area += (this[i - 1].x + this[i].x) * 1.0 * (this[i].y - this[i -1].y);
-			return area * 0.5;
-		}
-	}
-
-	public inline function clean(?distance) {
-		return new Clipper().CleanPolygon(this, distance);
-	}
-
-	public inline function simplify(?fillType) {
-		return Clipper.SimplifyPolygon(this, fillType);
-	}
-
-	public inline function getOrientation() : Bool {
-		return getArea() >= 0;
-	}
-
-	public inline function removeAt(i:Int) {
-		this.splice(i, 1);
-	}
-
-	@:arrayAccess function arrayGet(i:Int) : Point {
-		return this[i];
-	}
-
-	@:arrayAccess function arraySet(i:Int, v : Point) : Point {
-		return this[i] = v;
-	}
-
-	@:to public function toArray() : Array<Point> {
-		return this;
-	}
-
-	@:from public static function fromArray( pts : Array<Point> ) {
-		return new Polygon(pts);
-	}
-
-}
-
+typedef Polygon = h2d.col.IPolygon;

+ 2 - 2
hxd/earcut/Earcut.hx

@@ -87,7 +87,7 @@ class Earcut {
 		return result;
 	}
 
-	function setLinkedList < T: { x:Float, y:Float } > (points : Array<T>, start : Int, end : Int, clockwise : Bool) {
+	@:generic function setLinkedList < T: { x:Float, y:Float } > (points : Array<T>, start : Int, end : Int, clockwise : Bool) {
 
 		// check polygon winding
 		var sum = 0.;
@@ -121,7 +121,7 @@ class Earcut {
 	}
 
 	// link every hole into the outer loop, producing a single-ring polygon without holes
-	function eliminateHoles < T: { x:Float, y:Float } > (points : Array<T>, holes : Array<Int>, root : EarNode) {
+	@:generic function eliminateHoles < T: { x:Float, y:Float } > (points : Array<T>, holes : Array<Int>, root : EarNode) {
 		var queue = [];
 
 		for(i in 0...holes.length) {

+ 2 - 2
hxd/impl/ArrayIterator.hx

@@ -1,6 +1,6 @@
 package hxd.impl;
 
-class ArrayIterator<T> {
+@:generic class ArrayIterator<T> {
 	var i : Int;
 	var l : Int;
 	var a : Array<T>;
@@ -12,7 +12,7 @@ class ArrayIterator<T> {
 	public inline function hasNext() {
 		return i < l;
 	}
-	public inline function next() {
+	public inline function next() : T {
 		return a[i++];
 	}
 }