From 660f5d752af9fdc67a83e6abc8d39c3ad07e97eb Mon Sep 17 00:00:00 2001 From: zzzzrrr Date: Tue, 4 Aug 2009 16:23:07 -0400 Subject: [PATCH] bug hunting --- src/org/poly2tri/Poly2Tri.scala | 14 ++- src/org/poly2tri/cdt/AFront.scala | 10 +- src/org/poly2tri/cdt/CDT.scala | 137 +++++++++++++++---------- src/org/poly2tri/shapes/Triangle.scala | 56 +++++++++- 4 files changed, 147 insertions(+), 70 deletions(-) diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index 2981aa2..2c3f281 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -232,7 +232,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int) { - println("*** " + model + " ***") + println + println("************** " + model + " **************") polyX = new ArrayBuffer[Float] polyY = new ArrayBuffer[Float] @@ -259,11 +260,16 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { segments += new Segment(points(i), points(i+1)) segments += new Segment(points.first, points.last) - slCDT = CDT.init(points) - println("Number of points = " + polyX.size) println - + + val t1 = System.nanoTime + slCDT = CDT.init(points) + val runTime = System.nanoTime - t1 + println("CDT average (ms) = " + runTime*1e-6) + println("Number of triangles = " + slCDT.triangles.size) + println + if(!drawEarClip) { // Sediel triangulation diff --git a/src/org/poly2tri/cdt/AFront.scala b/src/org/poly2tri/cdt/AFront.scala index 9f8ea67..680ade1 100644 --- a/src/org/poly2tri/cdt/AFront.scala +++ b/src/org/poly2tri/cdt/AFront.scala @@ -36,7 +36,6 @@ import shapes.{Point, Triangle} class AFront(iTriangle: Triangle) { // Doubly linked list - // TODO: Replace with a Red-Black Tree for better performance var head = new Node(iTriangle.points(1), iTriangle) head.next = new Node(iTriangle.points(0), iTriangle) var tail = new Node(iTriangle.points(2), null) @@ -44,6 +43,7 @@ class AFront(iTriangle: Triangle) { head.next.next = tail tail.prev = head.next + // TODO: Usea Red-Black Tree or interval tree for better search performance! def locate(point: Point): Node = { var node = head while(node != tail) { @@ -76,14 +76,8 @@ class AFront(iTriangle: Triangle) { } +// Advancing front node class Node(val point: Point, var triangle: Triangle) { - var next: Node = null var prev: Node = null - - def printDebug { - println("Point: " + point) - if(triangle != null) - println("Triangle points: " + triangle.points) - } } \ No newline at end of file diff --git a/src/org/poly2tri/cdt/CDT.scala b/src/org/poly2tri/cdt/CDT.scala index e38c94d..b2bc488 100644 --- a/src/org/poly2tri/cdt/CDT.scala +++ b/src/org/poly2tri/cdt/CDT.scala @@ -36,7 +36,7 @@ import shapes.{Segment, Point, Triangle} import utils.Util /** - * Sweep-line, Constrained Delauney Triangulation + * Sweep-line, Constrained Delauney Triangulation (CDT) * See: Domiter, V. and Žalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', * International Journal of Geographical Information Science,22:4,449 — 462 */ @@ -124,7 +124,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Implement sweep-line private def sweep { - for(i <- 1 until 11 /*points.size*/) { + for(i <- 1 until points.size) { val point = points(i) // Process Point event val triangle = pointEvent(point) @@ -137,9 +137,8 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian private def pointEvent(point: Point): Triangle = { val node = aFront.locate(point) - var newNode: Node = null - - if(point.x == node.point.x) { + + if(point.x == node.point.x && node != aFront.head) { // Projected point coincides with existing point; create two triangles val rPts = Array(point, node.point, node.next.point) @@ -163,11 +162,15 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian node.prev.triangle.updateNeighbors(node.point, node.prev.point, rTriangle) // Update advancing front - newNode = aFront.insert(point, rTriangle, node) + val newNode = aFront.insert(point, rTriangle, node) aFront -= (node.prev, node, rTriangle) - + // Fill in adjacent triangles if required + scanAFront(newNode) + + newNode.triangle + } else { - + // Projected point hits advancing front; create new triangle val cwPoint = node.next.point val ccwPoint = node.point @@ -181,16 +184,14 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Legalize legalization(triangle, nTri) // Update neighbor pointers - nTri.updateNeighbors(ccwPoint, cwPoint, triangle) + nTri.updateNeighbors(triangle.points(1), triangle.points(2), triangle) // Update advancing front - newNode = aFront.insert(point, triangle, node) - + val newNode = aFront.insert(point, triangle, node) + // Fill in adjacent triangles if required + scanAFront(newNode) + + newNode.triangle } - - // Fill in adjacent triangles if required - scan(newNode) - newNode.triangle - } // EdgeEvent @@ -198,49 +199,58 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // STEP 1: Locate the first intersected triangle val firstTriangle = triangle.locateFirst(edge) + // STEP 2: Remove intersected triangles + // STEP 3: Triangulate empty areas. if(firstTriangle != null && !firstTriangle.contains(edge)) { + // Collect intersected triangles val triangles = new ArrayBuffer[Triangle] triangles += firstTriangle val e = edge.p - edge.q while(!triangles.last.contains(edge.p)) triangles += triangles.last.findNeighbor(e) - // Remove from the mesh + + // TODO: Implement this section //triangles.foreach(t => mesh.map -= t) + } else if(firstTriangle == null) { - // Traverse the AFront, and build triangles - var node = aFront.locate(edge.q) - node = node.next - val points = new ArrayBuffer[Point] + // No triangles are intersected by the edge; edge must lie outside the mesh + // Apply constraint; traverse the AFront, and build triangles - if(edge.p.x > edge.q.x) { - // Search right - while(node != null && node.point != edge.p) { - // Collect points - points += node.point - node = node.next - } - } else { - // Search left - while(node != null && node.point != edge.p) { - // Collect points - points += node.point - node = node.prev - } - } + 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 + + val points = new ArrayBuffer[Point] + var node = aFront.locate(point1) + val node1 = node + + node = node.next + + while(node.point != point2) { + points += node.point + node = node.next + } + + val node2 = node val T = new ArrayBuffer[Triangle] - angladaTPD(points.toArray, List(edge.p, edge.q), T) - T.foreach(t => mesh.debug += t) + angladaTPD(points.toArray, List(point1, point2), T) + + node1.triangle = T.first + node1.next = node2 + node2.prev = node1 + + T.foreach(t => mesh.map += t) + } - // STEP 3: Triangulate empty areas. - } // Marc Vigo Anglada's triangulatePseudopolygonDelaunay algo + // TODO: Bugy fix - works for a single triangle def angladaTPD(P: Array[Point], ab: List[Point], T: ArrayBuffer[Triangle]) { val a = ab.first @@ -263,13 +273,14 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian if(!P.isEmpty) { c = P.first val points = Array(a, b, c) + // TODO: Correctly update neighbor pointers val neighbors = new Array[Triangle](3) T += new Triangle(points, neighbors) } } // Scan left and right along AFront to fill holes - def scan(n: Node) { + def scanAFront(n: Node) { var node = n.next // Update right @@ -294,6 +305,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Fill empty space with a triangle 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)) @@ -312,6 +324,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Do edges need to be swapped? private def illegal(p1: Point, p2: Point, p3: Point, p4:Point): Boolean = { + val v1 = p3 - p2 val v2 = p1 - p2 val v3 = p1 - p4 @@ -322,28 +335,42 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian false } - // Ensure new adjacent triangles are legal + // Ensure adjacent triangles are legal + // If illegal, flip edges and update triangle's pointers private def legalization(t1: Triangle, t2: Triangle) { + val oPoint = t2 oppositePoint t1 + if(illegal(t1.points(1), oPoint, t1.points(2), t1.points(0))) { + // Flip edges and rotate everything clockwise - val point = t1.points(0) - t1.points(1) = t1.points(0) - t1.points(0) = t1.points(2) - t1.points(2) = oPoint - val tmp = t2.points(1) - t2.points(1) = point - t2.points(0) = t2.points(2) - t2.points(2) = tmp - - t1.updatePoints - t2.updatePoints - // TODO: Rotate neighbors - println("legalize") + val point = t1.points(0) + t1.legalize(oPoint) + t2.legalize(oPoint, point) + + // TODO: Make sure this is correct + val cwNeighbor = t2.neighborCW(oPoint) + val ccwNeighbor = t2.neighborCCW(oPoint) + + t1.neighbors(0) = t2 + t1.neighbors(1) = cwNeighbor + t1.neighbors(2) = null + + if(t2.points(0) == oPoint) { + t2.neighbors(0) = null + t2.neighbors(1) = ccwNeighbor + t2.neighbors(2) = t1 + } else { + t2.neighbors(0) = ccwNeighbor + t2.neighbors(1) = t1 + t2.neighbors(2) = null + } } } + // Final step in the sweep-line CDT algo private def finalization { + } } diff --git a/src/org/poly2tri/shapes/Triangle.scala b/src/org/poly2tri/shapes/Triangle.scala index 52e5923..ecc5614 100644 --- a/src/org/poly2tri/shapes/Triangle.scala +++ b/src/org/poly2tri/shapes/Triangle.scala @@ -38,12 +38,14 @@ import scala.collection.mutable.ArrayBuffer class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { var ik, ij , jk, ji, kj, ki: Point = null - updatePoints + updateEdges // Flags to determine if an edge is the final Delauney edge val edges = new Array[Boolean](3) - def updatePoints { + // Update the edges that consitite this triangle + // May change during legalization + def updateEdges { ik = points(2) - points(0) ij = points(1) - points(0) jk = points(2) - points(1) @@ -89,9 +91,10 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { true } + // Locate first triangle crossed by constrained edge def locateFirst(edge: Segment): Triangle = { val p = edge.p - if(contains(p) || contains(edge)) return this + if(contains(p)) return this val q = edge.q val e = p - q if(q == points(0)) { @@ -113,6 +116,7 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { null } + // Locate next triangle crossed by constraied edge def findNeighbor(e: Point): Triangle = { var sameSign = Math.signum(ik cross e) == Math.signum(ij cross e) if(!sameSign) return neighbors(0) @@ -123,6 +127,52 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { this } + // The neighbor CW to given point + def neighborCW(point: Point): Triangle = { + if(point == points(0)) { + neighbors(2) + }else if(point == points(1)) { + neighbors(0) + } else + neighbors(1) + } + + // The neighbor CW to given point + def neighborCCW(point: Point): Triangle = { + if(point == points(0)) { + neighbors(1) + }else if(point == points(1)) { + neighbors(2) + } else + neighbors(0) + } + + // Legalized triangle by rotating clockwise around point(0) + def legalize(oPoint: Point) { + points(1) = points(0) + points(0) = points(2) + points(2) = oPoint + updateEdges + } + + // 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) + points(2) = nPoint + } else if (oPoint == points(1)) { + points(2) = points(1) + points(1) = points(0) + points(0) = nPoint + } else { + points(0) = points(2) + points(2) = points(1) + points(1) = nPoint + } + updateEdges + } + def printDebug { println("**************") println(points(0) + "," + points(1) + "," + points(2))