diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index a7eaa53..0973ad6 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -170,7 +170,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { }) if(drawCDT) { - val draw = if(drawcdtMesh) slCDT.triangleMesh else slCDT.triangles + //val draw = if(drawcdtMesh) slCDT.triangleMesh else slCDT.triangles + val draw = slCDT.testTri draw.foreach( t => { val triangle = new Polygon triangle.addPoint(t.points(0).x, t.points(0).y) diff --git a/src/org/poly2tri/cdt/AFront.scala b/src/org/poly2tri/cdt/AFront.scala index 4c0eaee..ef9a4af 100644 --- a/src/org/poly2tri/cdt/AFront.scala +++ b/src/org/poly2tri/cdt/AFront.scala @@ -30,6 +30,8 @@ */ package org.poly2tri.cdt +import scala.collection.mutable.ArrayBuffer + import shapes.{Point, Triangle} // Advancing front @@ -87,7 +89,94 @@ class AFront(iTriangle: Triangle) { node } - def constrainedEdge() { + // Update advancing front with constrained edge triangles + def constrainedEdge(sNode: Node, eNode: Node, T1: ArrayBuffer[Triangle], + T2: ArrayBuffer[Triangle]): Triangle = { + + var node = sNode + var t1r, t2r = false + + while(node != eNode) { + + T2.foreach(t => { + if(t.contains(node.point, node.next.point)) { + node.triangle = t + t2r = true + } + }) + + if(!t2r) + T1.foreach(t => { + if(t.contains(node.point, node.next.point)) { + node.triangle = t + t1r = true + } + }) + + node = node.next + } + + if(t1r && !t2r) + T1.first + else if(t2r && !t1r) + T2.first + else + throw new Exception("edge insertion error") + + } + + // Update advancing front with qNode + def constrainedEdge(qNode: Node, tList: ArrayBuffer[Triangle], T1: ArrayBuffer[Triangle], + T2: ArrayBuffer[Triangle]): Triangle = { + + var t1r, t2r = false + + for(t <- tList) { + + if(qNode.triangle == t) { + + val p1 = qNode.point + val p2 = qNode.next.point + + T2.foreach(tri => if(tri.contains(p1, p2)) { + qNode.triangle = tri + t2r = true + }) + + if(!t2r) + T1.foreach(tri => if(tri.contains(p1, p2)) { + qNode.triangle = tri + t1r = true + }) + + } else if(qNode.prev.triangle == t) { + + val p1 = qNode.prev.point + val p2 = qNode.point + + T2.foreach(tri => if(tri.contains(p1, p2)) { + qNode.prev.triangle = tri + t2r = true + }) + + if(!t2r) + T1.foreach(tri => if(tri.contains(p1, p2)) { + qNode.prev.triangle = tri + t1r = true + }) + + } else if(qNode.next.triangle == t) { + throw new Exception("unanticipated edge event!") + } + + } + + if(t1r && !t2r) + T1.first + else if(t2r && !t1r) + T2.first + else + throw new Exception("edge insertion error") } @@ -98,6 +187,12 @@ class AFront(iTriangle: Triangle) { node.triangle = triangle } + def link(node1: Node, node2: Node, t: Triangle) { + node1.next = node2 + node2.prev = node1 + node1.triangle = t + } + def basin(node: Node) { if(node.next != tail) { val p1 = node.point diff --git a/src/org/poly2tri/cdt/CDT.scala b/src/org/poly2tri/cdt/CDT.scala index f1b2206..b3fe723 100644 --- a/src/org/poly2tri/cdt/CDT.scala +++ b/src/org/poly2tri/cdt/CDT.scala @@ -128,6 +128,18 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Finalize triangulation finalization + val testTri = new ArrayBuffer[Triangle] + + val p1 = Point(200, 300) + val p2 = Point(500, 300) + + val p3 = Point(250, 500) + val p4 = Point(350, 400) + val p5 = Point(400, 350) + + val pts = Array(p3, p4, p5) + triangulate(pts, List(p1, p2), testTri) + // Implement sweep-line private def sweep { @@ -136,7 +148,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Process Point event var triangle = pointEvent(point) // Process edge events - //point.edges.foreach(e => triangle = edgeEvent(e, triangle)) + point.edges.foreach(e => triangle = edgeEvent(e, triangle)) if(i == CDT.clearPoint) {cleanTri = triangle; mesh.debug += cleanTri} } } @@ -231,41 +243,17 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Update advancing front - val qNode = aFront.locatePoint(edge.q) - val pNode = aFront.locatePoint(edge.p) + var first: Triangle = null + val sNode = aFront.locatePoint(point1) + val eNode = aFront.locatePoint(point2) - // TODO: Make this more robust.... - - for(t <- tList) { - - if(qNode.triangle == t) { - val p1 = qNode.point - val p2 = qNode.next.point - T1.foreach(tri => if(tri.contains(p1, p2)) qNode.triangle = tri) - T2.foreach(tri => if(tri.contains(p1, p2)) qNode.triangle = tri) - } else if(qNode.prev.triangle == t) { - val p1 = qNode.prev.point - val p2 = qNode.point - T1.foreach(tri => if(tri.contains(p1, p2)) qNode.prev.triangle = tri) - T2.foreach(tri => if(tri.contains(p1, p2)) qNode.prev.triangle = tri) - } - - if(pNode != null) { - if(pNode.triangle == t) { - val p1 = pNode.point - val p2 = pNode.next.point - T1.foreach(tri => if(tri.contains(p1, p2)) pNode.triangle = tri) - T2.foreach(tri => if(tri.contains(p1, p2)) pNode.triangle = tri) - } else if(pNode.prev.triangle == t) { - val p1 = pNode.point - val p2 = pNode.prev.point - T1.foreach(tri => if(tri.contains(p1, p2)) pNode.prev.triangle = tri) - T2.foreach(tri => if(tri.contains(p1, p2)) pNode.prev.triangle = tri) - } - } - + if(sNode != null && eNode != null) { + first = aFront.constrainedEdge(sNode, eNode, T1, T2) + } else { + val qNode = if(sNode == null) eNode else sNode + first = aFront.constrainedEdge(qNode, tList, T1, T2) } - + // Update neighbors edgeNeighbors(nTriangles, T1) edgeNeighbors(nTriangles, T2) @@ -275,7 +263,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian T2.first.mark(point1, point2) // Double check returning T2.first vs T1.first - T2.first + first } else if(firstTriangle == null) { @@ -283,6 +271,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Apply constraint; traverse the AFront, and build triangles val ahead = (edge.p.x > edge.q.x) + val point1 = if(ahead) edge.q else edge.p val point2 = if(ahead) edge.p else edge.q @@ -310,7 +299,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian triangulate(points.toArray, endPoints, T) // Update advancing front - aFront -= (first, node.prev, T.first) + aFront link (first, node, T.first) // Update neighbors edgeNeighbors(nTriangles, T) @@ -318,6 +307,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Mark constrained edge T.first mark(edge.p, edge.q) + // Return original triangle triangle } else { @@ -335,7 +325,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian for(t1 <- nTriangles) for(t2 <- T) t1.markNeighbor(t2) - + for(i <- 0 until T.size) for(j <- i+1 until T.size) T(i).markNeighbor(T(j)) @@ -347,9 +337,9 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian val a = ab.first val b = ab.last + var c = if(!P.isEmpty) P.first else null if(P.size > 1) { - var c = P.first var i = 0 for(j <- 1 until P.size) { if(illegal(a, c, b, P(j))) { @@ -358,13 +348,16 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian } } val PE = P.slice(0, i) - val PD = P.slice(i, P.size-1) + val PD = P.slice(i+1, P.size) triangulate(PE, List(a, c), T) triangulate(PD, List(c, b), T) } if(!P.isEmpty) { - val points = Array(a, P.first, b) + val left = if(Util.orient(a, b, c) > 0) true else false + val p2 = if(left) c else b + val p3 = if(left) b else c + val points = Array(a, p2, p3) T += new Triangle(points) mesh.map += T.last } diff --git a/src/org/poly2tri/shapes/Triangle.scala b/src/org/poly2tri/shapes/Triangle.scala index 998aec5..da0b329 100644 --- a/src/org/poly2tri/shapes/Triangle.scala +++ b/src/org/poly2tri/shapes/Triangle.scala @@ -49,28 +49,14 @@ class Triangle(val points: Array[Point]) { var clean = false // Update neighbor pointers - // Debug version - def markNeighbor(p1: Point, p2: Point, triangle: Triangle, debug: HashSet[Triangle]) { + private def markNeighbor(p1: Point, p2: Point, t: Triangle) { + assert(t != this, "self-pointer error") if((p1 == points(2) && p2 == points(1)) || (p1 == points(1) && p2 == points(2))) - neighbors(0) = triangle + neighbors(0) = t else if((p1 == points(0) && p2 == points(2)) || (p1 == points(2) && p2 == points(0))) - neighbors(1) = triangle + neighbors(1) = t else if((p1 == points(0) && p2 == points(1)) || (p1 == points(1) && p2 == points(0))) - neighbors(2) = triangle - else { - debug += triangle - println("Neighbor pointer ERROR!") - } - } - - // Update neighbor pointers - private def markNeighbor(p1: Point, p2: Point, triangle: Triangle) { - if((p1 == points(2) && p2 == points(1)) || (p1 == points(1) && p2 == points(2))) - neighbors(0) = triangle - else if((p1 == points(0) && p2 == points(2)) || (p1 == points(2) && p2 == points(0))) - neighbors(1) = triangle - else if((p1 == points(0) && p2 == points(1)) || (p1 == points(1) && p2 == points(0))) - neighbors(2) = triangle + neighbors(2) = t else { throw new Exception("Neighbor pointer error, please report!") } @@ -78,6 +64,7 @@ class Triangle(val points: Array[Point]) { /* Exhaustive search to update neighbor pointers */ def markNeighbor(t: Triangle) { + assert(t != this, "self-pointer error") if (t.contains(points(1), points(2))) { neighbors(0) = t t.markNeighbor(points(1), points(2), this) @@ -95,13 +82,16 @@ 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) else if(points(0) == t.points(2)) points(2) - else if((points(2) == t.points(1) && points(1) == t.points(2)) || (points(1) == t.points(1) && points(2) == t.points(2))) + else if(contains(t.points(1), t.points(2))) points(0) else { + t.printDebug + printDebug throw new Exception("Point location error, please report") } diff --git a/src/org/poly2tri/utils/Util.scala b/src/org/poly2tri/utils/Util.scala index f49b3b9..0ba7df8 100644 --- a/src/org/poly2tri/utils/Util.scala +++ b/src/org/poly2tri/utils/Util.scala @@ -49,6 +49,18 @@ object Util { } + // Return: positive if point p is left of ab + // negative if point p is right of ab + // zero if points are colinear + // See: http://www-2.cs.cmu.edu/~quake/robust.html + def orient(b: Point, a: Point, p: Point): Float = { + val acx = a.x - p.x + val bcx = b.x - p.x + val acy = a.y - p.y + val bcy = b.y - p.y + acx * bcy - acy * bcx + } + } /** The object Random offers a default implementation