mirror of
				https://github.com/jhasse/poly2tri.git
				synced 2025-10-31 20:37:10 +01:00 
			
		
		
		
	bug hunting
This commit is contained in:
		| @@ -232,7 +232,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { | |||||||
|     |     | ||||||
|   def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int) { |   def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int) { | ||||||
|      |      | ||||||
|     println("*** " + model + " ***") |     println | ||||||
|  |     println("************** " + model + " **************") | ||||||
|      |      | ||||||
|     polyX = new ArrayBuffer[Float] |     polyX = new ArrayBuffer[Float] | ||||||
|     polyY = 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(i), points(i+1)) | ||||||
|     segments += new Segment(points.first, points.last) |     segments += new Segment(points.first, points.last) | ||||||
|      |      | ||||||
|     slCDT = CDT.init(points) |  | ||||||
|      |  | ||||||
|     println("Number of points = " + polyX.size) |     println("Number of points = " + polyX.size) | ||||||
|     println |     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) {   |     if(!drawEarClip) {   | ||||||
|        |        | ||||||
| 	    // Sediel triangulation | 	    // Sediel triangulation | ||||||
|   | |||||||
| @@ -36,7 +36,6 @@ import shapes.{Point, Triangle} | |||||||
| class AFront(iTriangle: Triangle) { | class AFront(iTriangle: Triangle) { | ||||||
|  |  | ||||||
|   // Doubly linked list |   // Doubly linked list | ||||||
|   // TODO: Replace with a Red-Black Tree for better performance |  | ||||||
|   var head = new Node(iTriangle.points(1), iTriangle) |   var head = new Node(iTriangle.points(1), iTriangle) | ||||||
|   head.next = new Node(iTriangle.points(0), iTriangle) |   head.next = new Node(iTriangle.points(0), iTriangle) | ||||||
|   var tail = new Node(iTriangle.points(2), null) |   var tail = new Node(iTriangle.points(2), null) | ||||||
| @@ -44,6 +43,7 @@ class AFront(iTriangle: Triangle) { | |||||||
|   head.next.next = tail |   head.next.next = tail | ||||||
|   tail.prev = head.next |   tail.prev = head.next | ||||||
|    |    | ||||||
|  |   // TODO: Usea Red-Black Tree or interval tree for better search performance! | ||||||
|   def locate(point: Point): Node = { |   def locate(point: Point): Node = { | ||||||
|     var node = head |     var node = head | ||||||
|     while(node != tail) { |     while(node != tail) { | ||||||
| @@ -76,14 +76,8 @@ class AFront(iTriangle: Triangle) { | |||||||
|    |    | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Advancing front node | ||||||
| class Node(val point: Point, var triangle: Triangle) { | class Node(val point: Point, var triangle: Triangle) { | ||||||
|  |  | ||||||
|   var next: Node = null |   var next: Node = null | ||||||
|   var prev: Node = null |   var prev: Node = null | ||||||
|    |  | ||||||
|   def printDebug { |  | ||||||
|     println("Point: " + point) |  | ||||||
|     if(triangle != null) |  | ||||||
|       println("Triangle points: " + triangle.points) |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| @@ -36,7 +36,7 @@ import shapes.{Segment, Point, Triangle} | |||||||
| import utils.Util | 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', |  * See: Domiter, V. and Žalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', | ||||||
|  *      International Journal of Geographical Information Science,22:4,449 — 462 |  *      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  |   // Implement sweep-line  | ||||||
|   private def sweep { |   private def sweep { | ||||||
|     for(i <- 1 until 11 /*points.size*/) { |     for(i <- 1 until points.size) { | ||||||
|       val point = points(i) |       val point = points(i) | ||||||
|       // Process Point event |       // Process Point event | ||||||
|       val triangle = pointEvent(point) |       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 = { |   private def pointEvent(point: Point): Triangle = { | ||||||
|      |      | ||||||
|     val node = aFront.locate(point) |     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 |         // Projected point coincides with existing point; create two triangles | ||||||
|     	val rPts = Array(point, node.point, node.next.point) |     	val rPts = Array(point, node.point, node.next.point) | ||||||
| @@ -163,8 +162,12 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian | |||||||
|         node.prev.triangle.updateNeighbors(node.point, node.prev.point, rTriangle) |         node.prev.triangle.updateNeighbors(node.point, node.prev.point, rTriangle) | ||||||
|          |          | ||||||
|         // Update advancing front |         // Update advancing front | ||||||
|         newNode = aFront.insert(point, rTriangle, node) |         val newNode = aFront.insert(point, rTriangle, node) | ||||||
|         aFront -= (node.prev, node, rTriangle) |         aFront -= (node.prev, node, rTriangle) | ||||||
|  |         // Fill in adjacent triangles if required | ||||||
|  | 	    scanAFront(newNode) | ||||||
|  |       | ||||||
|  | 	    newNode.triangle | ||||||
|       |       | ||||||
|     } else {  |     } else {  | ||||||
|       |       | ||||||
| @@ -181,16 +184,14 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian | |||||||
|         // Legalize |         // Legalize | ||||||
| 	    legalization(triangle, nTri) | 	    legalization(triangle, nTri) | ||||||
|         // Update neighbor pointers |         // Update neighbor pointers | ||||||
| 	    nTri.updateNeighbors(ccwPoint, cwPoint, triangle) | 	    nTri.updateNeighbors(triangle.points(1), triangle.points(2), triangle) | ||||||
|         // Update advancing front |         // Update advancing front | ||||||
|         newNode = aFront.insert(point, triangle, node) |         val newNode = aFront.insert(point, triangle, node) | ||||||
|          |  | ||||||
| 	} |  | ||||||
|      |  | ||||||
|         // Fill in adjacent triangles if required |         // Fill in adjacent triangles if required | ||||||
|     scan(newNode) | 	    scanAFront(newNode) | ||||||
|     newNode.triangle |  | ||||||
|       |       | ||||||
|  | 	    newNode.triangle | ||||||
|  | 	} | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   // EdgeEvent |   // EdgeEvent | ||||||
| @@ -198,49 +199,58 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian | |||||||
|      |      | ||||||
|     // STEP 1: Locate the first intersected triangle |     // STEP 1: Locate the first intersected triangle | ||||||
|     val firstTriangle = triangle.locateFirst(edge) |     val firstTriangle = triangle.locateFirst(edge) | ||||||
|  |      | ||||||
|     // STEP 2: Remove intersected triangles |     // STEP 2: Remove intersected triangles | ||||||
|  |     // STEP 3: Triangulate empty areas. | ||||||
|     if(firstTriangle != null && !firstTriangle.contains(edge)) { |     if(firstTriangle != null && !firstTriangle.contains(edge)) { | ||||||
|  |        | ||||||
|        // Collect intersected triangles |        // Collect intersected triangles | ||||||
|        val triangles = new ArrayBuffer[Triangle] |        val triangles = new ArrayBuffer[Triangle] | ||||||
|        triangles += firstTriangle |        triangles += firstTriangle | ||||||
|        val e = edge.p - edge.q |        val e = edge.p - edge.q | ||||||
|        while(!triangles.last.contains(edge.p))  |        while(!triangles.last.contains(edge.p))  | ||||||
|          triangles += triangles.last.findNeighbor(e) |          triangles += triangles.last.findNeighbor(e) | ||||||
|        // Remove from the mesh |         | ||||||
|  |        // TODO: Implement this section | ||||||
|        //triangles.foreach(t => mesh.map -= t) |        //triangles.foreach(t => mesh.map -= t) | ||||||
|  |        | ||||||
|     } else if(firstTriangle == null) { |     } else if(firstTriangle == null) { | ||||||
|        |        | ||||||
|       // Traverse the AFront, and build triangles |       // No triangles are intersected by the edge; edge must lie outside the mesh | ||||||
|       var node = aFront.locate(edge.q) |       // Apply constraint; traverse the AFront, and build triangles | ||||||
|       node = node.next |  | ||||||
|       val points = new ArrayBuffer[Point] |  | ||||||
|        |        | ||||||
|       if(edge.p.x > edge.q.x) { |       val ahead = (edge.p.x > edge.q.x) | ||||||
|         // Search right |       val point1 = if(ahead) edge.q else edge.p | ||||||
|         while(node != null && node.point != edge.p) { |       val point2 = if(ahead) edge.p else edge.q | ||||||
|           // Collect points |        | ||||||
|  |       val points = new ArrayBuffer[Point] | ||||||
|  |       var node = aFront.locate(point1) | ||||||
|  |       val node1 = node | ||||||
|  |        | ||||||
|  |       node = node.next | ||||||
|  |        | ||||||
|  | 	  while(node.point != point2) { | ||||||
| 		points += node.point | 		points += node.point | ||||||
| 		node = node.next | 		node = node.next | ||||||
| 	  } | 	  } | ||||||
|       } else { |        | ||||||
|         // Search left |       val node2 = node | ||||||
|         while(node != null && node.point != edge.p) { |  | ||||||
|           // Collect points |  | ||||||
|           points += node.point |  | ||||||
|           node = node.prev |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|        |        | ||||||
|       val T = new ArrayBuffer[Triangle] |       val T = new ArrayBuffer[Triangle] | ||||||
|       angladaTPD(points.toArray, List(edge.p, edge.q), T) |       angladaTPD(points.toArray, List(point1, point2), T) | ||||||
|       T.foreach(t => mesh.debug += t) |  | ||||||
|     } |  | ||||||
|        |        | ||||||
|     // STEP 3: Triangulate empty areas. |       node1.triangle = T.first | ||||||
|  |       node1.next = node2 | ||||||
|  |       node2.prev = node1 | ||||||
|  |  | ||||||
|  |       T.foreach(t => mesh.map += t) | ||||||
|  |                  | ||||||
|  |     } | ||||||
|      |      | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   // Marc Vigo Anglada's triangulatePseudopolygonDelaunay algo  |   // 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]) { |   def angladaTPD(P: Array[Point], ab: List[Point], T: ArrayBuffer[Triangle]) { | ||||||
|      |      | ||||||
|     val a = ab.first |     val a = ab.first | ||||||
| @@ -263,13 +273,14 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian | |||||||
|     if(!P.isEmpty) { |     if(!P.isEmpty) { | ||||||
|       c = P.first |       c = P.first | ||||||
|       val points = Array(a, b, c) |       val points = Array(a, b, c) | ||||||
|  |       // TODO: Correctly update neighbor pointers | ||||||
|       val neighbors = new Array[Triangle](3) |       val neighbors = new Array[Triangle](3) | ||||||
|       T += new Triangle(points, neighbors) |       T += new Triangle(points, neighbors) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Scan left and right along AFront to fill holes |   // Scan left and right along AFront to fill holes | ||||||
|   def scan(n: Node) { |   def scanAFront(n: Node) { | ||||||
|      |      | ||||||
|     var node = n.next |     var node = n.next | ||||||
|     // Update right |     // Update right | ||||||
| @@ -294,6 +305,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian | |||||||
|    |    | ||||||
|   // Fill empty space with a triangle |   // Fill empty space with a triangle | ||||||
|   def fill(node: Node): Double = { |   def fill(node: Node): Double = { | ||||||
|  |      | ||||||
| 	  val a = (node.prev.point - node.point) | 	  val a = (node.prev.point - node.point) | ||||||
| 	  val b = (node.next.point - node.point) | 	  val b = (node.next.point - node.point) | ||||||
| 	  val angle = Math.abs(Math.atan2(a cross b, a dot b)) | 	  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? |   // Do edges need to be swapped? | ||||||
|   private def illegal(p1: Point, p2: Point, p3: Point, p4:Point): Boolean = { |   private def illegal(p1: Point, p2: Point, p3: Point, p4:Point): Boolean = { | ||||||
|  |      | ||||||
| 	  val v1 = p3 - p2 | 	  val v1 = p3 - p2 | ||||||
|       val v2 = p1 - p2 |       val v2 = p1 - p2 | ||||||
|       val v3 = p1 - p4 |       val v3 = p1 - p4 | ||||||
| @@ -322,28 +335,42 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian | |||||||
|         false |         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) { |   private def legalization(t1: Triangle, t2: Triangle) { | ||||||
|  |      | ||||||
|     val oPoint = t2 oppositePoint t1 |     val oPoint = t2 oppositePoint t1 | ||||||
|  |      | ||||||
|     if(illegal(t1.points(1), oPoint, t1.points(2), t1.points(0))) { |     if(illegal(t1.points(1), oPoint, t1.points(2), t1.points(0))) { | ||||||
|  |          | ||||||
| 	    // Flip edges and rotate everything clockwise | 	    // Flip edges and rotate everything clockwise | ||||||
|         val point = t1.points(0) |         val point = t1.points(0) | ||||||
| 	    t1.points(1) = t1.points(0) | 	    t1.legalize(oPoint)  | ||||||
| 	    t1.points(0) = t1.points(2) | 	    t2.legalize(oPoint, point) | ||||||
| 	    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 | 	    // TODO: Make sure this is correct | ||||||
| 	    t2.updatePoints |         val cwNeighbor = t2.neighborCW(oPoint) | ||||||
| 	    // TODO: Rotate neighbors |         val ccwNeighbor = t2.neighborCCW(oPoint) | ||||||
| 	    println("legalize") |          | ||||||
|  |         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 { |   private def finalization { | ||||||
|  |      | ||||||
|   } |   } | ||||||
|    |    | ||||||
| } | } | ||||||
|   | |||||||
| @@ -38,12 +38,14 @@ import scala.collection.mutable.ArrayBuffer | |||||||
| class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { | class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { | ||||||
|  |  | ||||||
|   var ik, ij , jk, ji, kj, ki: Point = null |   var ik, ij , jk, ji, kj, ki: Point = null | ||||||
|   updatePoints |   updateEdges | ||||||
|    |    | ||||||
|   // Flags to determine if an edge is the final Delauney edge |   // Flags to determine if an edge is the final Delauney edge | ||||||
|   val edges = new Array[Boolean](3) |   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) |     ik = points(2) - points(0) | ||||||
|     ij = points(1) - points(0) |     ij = points(1) - points(0) | ||||||
|     jk = points(2) - points(1) |     jk = points(2) - points(1) | ||||||
| @@ -89,9 +91,10 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { | |||||||
|     true |     true | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Locate first triangle crossed by constrained edge | ||||||
|   def locateFirst(edge: Segment): Triangle = { |   def locateFirst(edge: Segment): Triangle = { | ||||||
|     val p = edge.p |     val p = edge.p | ||||||
|     if(contains(p) || contains(edge)) return this |     if(contains(p)) return this | ||||||
|     val q = edge.q |     val q = edge.q | ||||||
|     val e = p - q |     val e = p - q | ||||||
|     if(q == points(0)) { |     if(q == points(0)) { | ||||||
| @@ -113,6 +116,7 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { | |||||||
|     null |     null | ||||||
|   } |   } | ||||||
|    |    | ||||||
|  |   // Locate next triangle crossed by constraied edge | ||||||
|   def findNeighbor(e: Point): Triangle = { |   def findNeighbor(e: Point): Triangle = { | ||||||
|     var sameSign = Math.signum(ik cross e) == Math.signum(ij cross e) |     var sameSign = Math.signum(ik cross e) == Math.signum(ij cross e) | ||||||
|     if(!sameSign) return neighbors(0) |     if(!sameSign) return neighbors(0) | ||||||
| @@ -123,6 +127,52 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { | |||||||
|     this |     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 { |   def printDebug { | ||||||
|     println("**************") |     println("**************") | ||||||
|     println(points(0) + "," + points(1) + "," + points(2)) |     println(points(0) + "," + points(1) + "," + points(2)) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 zzzzrrr
					zzzzrrr