diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index 6389b25..845851e 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -413,9 +413,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { headSegs = new ArrayBuffer[Segment] for(i <- 0 until headHole.size-1) - chestSegs += new Segment(headHole(i), headHole(i+1)) - chestSegs += new Segment(headHole.first, headHole.last) + headSegs += new Segment(headHole(i), headHole(i+1)) + headSegs += new Segment(headHole.first, headHole.last) + // Add the holes slCDT.addHole(headHole) slCDT.addHole(chestHole) } diff --git a/src/org/poly2tri/cdt/AFront.scala b/src/org/poly2tri/cdt/AFront.scala index a67e45a..cb8b438 100644 --- a/src/org/poly2tri/cdt/AFront.scala +++ b/src/org/poly2tri/cdt/AFront.scala @@ -99,6 +99,7 @@ class AFront(iTriangle: Triangle) { val point2 = edge.p // Scan the advancing front and update Node triangle pointers + // TODO: Make this more efficient while(node != null && node != eNode.next) { T2.foreach(t => { if(t.contains(node.point, node.next.point)) diff --git a/src/org/poly2tri/cdt/CDT.scala b/src/org/poly2tri/cdt/CDT.scala index de3c42a..01639ce 100644 --- a/src/org/poly2tri/cdt/CDT.scala +++ b/src/org/poly2tri/cdt/CDT.scala @@ -45,6 +45,7 @@ import utils.Util // and triangle traversal respectively. See figure 14(a) from Domiter et al. // Although it may not be necessary for simple polygons.... +// clearPoint is any interior point inside the polygon class CDT(polyLine: Array[Point], clearPoint: Point) { // Triangle list @@ -140,6 +141,7 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { private def sweep { for(i <- 1 until points.size) { + val point = points(i) // Process Point event val node = pointEvent(point) @@ -149,18 +151,21 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { } - // Final step in the sweep-line CDT algo + // Final step in the sweep-line CDT // Clean exterior triangles private def finalization { + var found = false mesh.map.foreach(m => { if(!found) + // Mark the originating clean triangle if(m.pointIn(clearPoint)) { found = true cleanTri = m } m.markNeighborEdges }) + // Collect interior triangles constrained by edges mesh clean cleanTri } @@ -367,7 +372,7 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { do { angle = fill(node1) node1 = node1.next - } while(angle <= PI_2 && node1.next != null) + } while(angle <= PI_2 && angle >= -PI_2 && node1.next != null) } var node2 = n.prev @@ -377,17 +382,19 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { do { angle = fill(node2) node2 = node2.prev - } while(angle <= PI_2 && node2.prev != null) + } while(angle <= PI_2 && angle >= -PI_2 && node2.prev != null) } } // Fill empty space with a triangle private def fill(node: Node): Double = { + val a = (node.prev.point - node.point) val b = (node.next.point - node.point) - val angle = Math.abs(Math.atan2(a cross b, a dot b)) - if(angle <= PI_2) { + val angle = Math.atan2(a cross b, a dot b) + // Is the angle acute? + if(angle <= PI_2 && angle >= -PI_2) { val points = Array(node.prev.point, node.point, node.next.point) val triangle = new Triangle(points) // Update neighbor pointers diff --git a/src/org/poly2tri/cdt/Mesh.scala b/src/org/poly2tri/cdt/Mesh.scala index c9d37a3..fdd2a8a 100644 --- a/src/org/poly2tri/cdt/Mesh.scala +++ b/src/org/poly2tri/cdt/Mesh.scala @@ -30,20 +30,23 @@ */ package org.poly2tri.cdt -import scala.collection.mutable.{HashSet, ArrayBuffer} +import scala.collection.mutable.ArrayBuffer import shapes.{Point, Triangle} class Mesh { // Triangles that constitute the mesh - val map = HashSet.empty[Triangle] + val map = new ArrayBuffer[Triangle] // Debug triangles - val debug = HashSet.empty[Triangle] + val debug = new ArrayBuffer[Triangle] val triangles = new ArrayBuffer[Triangle] - // Recursively collect triangles + // Recursively collect interior triangles and clean the mesh + // Excludes exterior triangles outside constrained edges + // TODO: Investigate depth first search as an alternative def clean(triangle: Triangle) { + if(triangle != null && !triangle.interior) { triangle.interior = true triangles += triangle diff --git a/src/org/poly2tri/shapes/Triangle.scala b/src/org/poly2tri/shapes/Triangle.scala index 4718692..ccbeb49 100644 --- a/src/org/poly2tri/shapes/Triangle.scala +++ b/src/org/poly2tri/shapes/Triangle.scala @@ -30,7 +30,6 @@ */ package org.poly2tri.shapes -import scala.collection.mutable.HashSet import scala.collection.mutable.ArrayBuffer import utils.Util @@ -80,6 +79,7 @@ class Triangle(val points: Array[Point]) { neighbors(2) = t t.markNeighbor(points(0), points(1), this) } + } def clearNeighbors { @@ -87,6 +87,7 @@ class Triangle(val points: Array[Point]) { } def oppositePoint(t: Triangle): Point = { + assert(t != this, "self-pointer error") if(points(0) == t.points(1)) points(1) @@ -123,6 +124,7 @@ class Triangle(val points: Array[Point]) { // Locate first triangle crossed by constrained edge def locateFirst(edge: Segment): Triangle = { + if(contains(edge)) this if(edge.q == points(0)) search(points(1), points(2), edge, neighbors(2)) @@ -171,6 +173,7 @@ class Triangle(val points: Array[Point]) { // The neighbor clockwise to given point def neighborCW(point: Point): Triangle = { + if(point == points(0)) { neighbors(1) }else if(point == points(1)) { @@ -181,6 +184,7 @@ class Triangle(val points: Array[Point]) { // The neighbor counter-clockwise to given point def neighborCCW(oPoint: Point): Triangle = { + if(oPoint == points(0)) { neighbors(2) }else if(oPoint == points(1)) { @@ -191,6 +195,7 @@ class Triangle(val points: Array[Point]) { // The neighbor clockwise to given point def neighborAcross(point: Point): Triangle = { + if(point == points(0)) { neighbors(0) }else if(point == points(1)) { @@ -201,6 +206,7 @@ class Triangle(val points: Array[Point]) { // The point counter-clockwise to given point def pointCCW(point: Point): Point = { + if(point == points(0)) { points(1) } else if(point == points(1)) { @@ -214,6 +220,7 @@ class Triangle(val points: Array[Point]) { // The point counter-clockwise to given point def pointCW(point: Point): Point = { + if(point == points(0)) { points(2) } else if(point == points(1)) { @@ -227,6 +234,7 @@ class Triangle(val points: Array[Point]) { // Legalized triangle by rotating clockwise around point(0) def legalize(oPoint: Point) { + points(1) = points(0) points(0) = points(2) points(2) = oPoint @@ -234,6 +242,7 @@ class Triangle(val points: Array[Point]) { // Legalize triagnle by rotating clockwise around oPoint def legalize(oPoint: Point, nPoint: Point) { + if(oPoint == points(0)) { points(1) = points(0) points(0) = points(2) @@ -257,6 +266,7 @@ class Triangle(val points: Array[Point]) { // Check if legalized triangle will be collinear def collinear(oPoint: Point, nPoint: Point): Boolean = { + if(oPoint == points(0)) { Util.collinear(points(0), points(2), nPoint) } else if (oPoint == points(1)) { @@ -268,6 +278,7 @@ class Triangle(val points: Array[Point]) { // Rotate neighbors clockwise around give point. Share diagnal with triangle def rotateNeighborsCW(oPoint: Point, triangle: Triangle) { + if(oPoint == points(0)) { neighbors(2) = neighbors(1) neighbors(1) = null @@ -289,6 +300,7 @@ class Triangle(val points: Array[Point]) { // Finalize edge marking def markNeighborEdges { + for(i <- 0 to 2) if(edges(i)) i match { @@ -299,6 +311,7 @@ class Triangle(val points: Array[Point]) { } def markEdge(triangle: Triangle) { + for(i <- 0 to 2) if(edges(i)) i match { @@ -309,6 +322,7 @@ class Triangle(val points: Array[Point]) { } def markEdge(T: ArrayBuffer[Triangle]) { + for(t <- T) { for(i <- 0 to 2) if(t.edges(i)) @@ -322,6 +336,7 @@ class Triangle(val points: Array[Point]) { // Mark edge as constrained def markEdge(p: Point, q: Point) { + if((q == points(0) && p == points(1)) || (q == points(1) && p == points(0))) { finalized = true edges(2) = true @@ -335,12 +350,14 @@ class Triangle(val points: Array[Point]) { } def area = { + val b = points(0).x - points(1).x val h = points(2).y - points(1).y (b*h*0.5f) } def centroid: Point = { + val cx = (points(0).x + points(1).x + points(2).x)/3f val cy = (points(0).y + points(1).y + points(2).y)/3f Point(cx, cy) diff --git a/src/org/poly2tri/utils/Util.scala b/src/org/poly2tri/utils/Util.scala index f9aaf19..6f29d65 100644 --- a/src/org/poly2tri/utils/Util.scala +++ b/src/org/poly2tri/utils/Util.scala @@ -14,7 +14,7 @@ object Util { val ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon val iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon - // From "Scala By Example," by Martin Odersky + // Refursive merge sort, from "Scala By Example," by Martin Odersky def msort[A](less: (A, A) => Boolean)(xs: List[A]): List[A] = { def merge(xs1: List[A], xs2: List[A]): List[A] = if (xs1.isEmpty) xs2 @@ -26,6 +26,7 @@ object Util { else merge(msort(less)(xs take n), msort(less)(xs drop n)) } + // Insertion sort - best for lists <= 10 elements def insertSort[A](less: (A, A) => Boolean)(xs: ArrayBuffer[A]): ArrayBuffer[A] = { var j = 1 while(j < xs.size){ @@ -46,7 +47,7 @@ object Util { val d = Math.abs((p2-p1) cross (p1-p3)) - if(Math.abs(d) <= COLLINEAR_SLOP) + if(d <= COLLINEAR_SLOP) true else false @@ -157,7 +158,7 @@ object Util { (center, radius) } - def det(p1: Point, p2: Point, p3: Point): Float = { + private def det(p1: Point, p2: Point, p3: Point): Float = { val a11 = p1.x val a12 = p1.y @@ -171,7 +172,7 @@ object Util { } - def detC(p1: Point, p2: Point, p3: Point): Float = { + private def detC(p1: Point, p2: Point, p3: Point): Float = { val a11 = p1.x*p1.x + p1.y*p1.y val a12 = p1.x