From 81265ea8320b4c7b7391491862549ce21a66f4e3 Mon Sep 17 00:00:00 2001 From: zzzzrrr Date: Thu, 30 Jul 2009 18:01:59 -0400 Subject: [PATCH] CDT work --- src/org/poly2tri/Poly2Tri.scala | 42 +++++++--- src/org/poly2tri/cdt/AFront.scala | 47 +++++++++-- src/org/poly2tri/cdt/CDT.scala | 106 +++++++++++++++++-------- src/org/poly2tri/cdt/Mesh.scala | 7 ++ src/org/poly2tri/shapes/Triangle.scala | 40 ++++++++++ 5 files changed, 191 insertions(+), 51 deletions(-) diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index 41c6fe9..b07f11a 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -55,12 +55,17 @@ object Poly2Tri { class Poly2TriDemo extends BasicGame("Poly2Tri") { - var tesselator: Triangulator = null + // Sedidel Triangulator + var seidel: Triangulator = null var segments: ArrayBuffer[Segment] = null + // EarClip Triangulator val earClip = new EarClip var earClipResults: Array[poly2tri.earClip.Triangle] = null + // Sweep Line Constraied Delauney Triangulator (CDT) + var slCDT: CDT = null + var polyX: ArrayBuffer[Float] = null var polyY: ArrayBuffer[Float] = null @@ -71,6 +76,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { var hiLighter = 0 var drawEarClip = false var hertelMehlhorn = false + var drawCDT = false val nazcaMonkey = "data/nazca_monkey.dat" val bird = "data/bird.dat" @@ -79,7 +85,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val strange = "data/strange.dat" val i18 = "data/i.18" - var currentModel = nazcaMonkey + var currentModel = star def init(container: GameContainer) { selectModel(currentModel) @@ -102,7 +108,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val yellow = new Color(1f, 1f, 0f) if(debug) { - val draw = if(drawMap) tesselator.trapezoidMap else tesselator.trapezoids + val draw = if(drawMap) seidel.trapezoidMap else seidel.trapezoids for(t <- draw) { val polygon = new Polygon() for(v <- t.vertices) { @@ -121,14 +127,14 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { if(!debug && !drawEarClip) { var i = 0 - for(t <- tesselator.polygons) { + for(t <- seidel.polygons) { val poly = new Polygon t.foreach(p => poly.addPoint(p.x, p.y)) g.setColor(red) g.draw(poly) } } else if (debug && drawMap && !drawEarClip){ - for(mp <- tesselator.monoPolies) { + for(mp <- seidel.monoPolies) { val poly = new Polygon mp.foreach(p => poly.addPoint(p.x, p.y)) g.setColor(yellow) @@ -146,6 +152,17 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { g.draw(triangle) }) + if(drawCDT) { + slCDT.mesh.map.foreach( t => { + val triangle = new Polygon + triangle.addPoint(t.points(0).x, t.points(0).y) + triangle.addPoint(t.points(1).x, t.points(1).y) + triangle.addPoint(t.points(2).x, t.points(2).y) + g.setColor(red) + g.draw(triangle) + }) + } + if(drawSegs) { g.setColor(green) for(i <- 0 until segments.size) { @@ -165,16 +182,17 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { // UP if(key == 200) { hiLighter += 1 - if (hiLighter == tesselator.polygons.size) + if (hiLighter == seidel.polygons.size) hiLighter = 0 } // DOWN if(key == 208) { hiLighter -= 1 if (hiLighter == -1) - hiLighter = tesselator.polygons.size-1 + hiLighter = seidel.polygons.size-1 } if(c == 'm') drawMap = !drawMap + //if(c == 'c') drawCDT = !drawCDT if(c == '1') selectModel(nazcaMonkey) if(c == '2') selectModel(bird) if(c == '3') selectModel(strange) @@ -235,7 +253,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { segments += new Segment(points(i), points(i+1)) segments += new Segment(points.first, points.last) - //val cdt = CDT.init(points) + //slCDT = CDT.init(points) println("Number of points = " + polyX.size) println @@ -243,15 +261,15 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { if(!drawEarClip) { // Sediel triangulation - tesselator = new Triangulator(segments) - tesselator.buildTriangles = hertelMehlhorn + seidel = new Triangulator(segments) + seidel.buildTriangles = hertelMehlhorn val t1 = System.nanoTime - tesselator.process + seidel.process val runTime = System.nanoTime - t1 println("Poly2Tri average (ms) = " + runTime*1e-6) - println("Number of triangles = " + tesselator.polygons.size) + println("Number of triangles = " + seidel.polygons.size) } else { diff --git a/src/org/poly2tri/cdt/AFront.scala b/src/org/poly2tri/cdt/AFront.scala index b976490..0ab88a2 100644 --- a/src/org/poly2tri/cdt/AFront.scala +++ b/src/org/poly2tri/cdt/AFront.scala @@ -36,28 +36,61 @@ 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) - var tail = new Node(iTriangle.points(2), iTriangle) - head.next = new Node(iTriangle.points(0), iTriangle) + var tail = new Node(iTriangle.points(2), null) + head.next.next = tail tail.prev = head.next - def locate(point: Point): Tuple2[List[Point], Triangle] = { + def locate(point: Point): Node = { var node = head while(node != tail) { - if(point.x > node.point.x && point.x < node.next.point.x) - return (List(node.point, node.next.point), node.triangle) + if(point.x >= node.point.x && point.x <= node.next.point.x) + return node node = node.next } - null + throw new Exception("Advancing front error") } + def +=(tuple: Tuple3[Point, Triangle, Node]) = { + val (point, triangle, nNode) = tuple + val node = new Node(point, triangle) + // Update pointer + nNode.triangle = triangle + // Insert new node into advancing front + nNode.next.prev = node + node.next = nNode.next + node.prev = nNode + nNode.next = node + node + } + + def -=(tuple: Tuple3[Node, Node, Triangle]) { + val (node, kNode, triangle) = tuple + kNode.next.prev = node + node.next = kNode.next + node.triangle = triangle + } + + def -==(tuple: Tuple3[Node, Node, Triangle]) { + val (node, kNode, triangle) = tuple + kNode.prev.next = node + node.prev = kNode.prev + node.prev.triangle = triangle + } } -class Node(val point: Point, val triangle: Triangle) { +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 0dfdc5a..d7b9dce 100644 --- a/src/org/poly2tri/cdt/CDT.scala +++ b/src/org/poly2tri/cdt/CDT.scala @@ -68,8 +68,11 @@ object CDT { val segments = initSegments(points) val sortedPoints = pointSort(points) - val initialTriangle = new Triangle(Array(sortedPoints(0), p1, p2), null) - new CDT(sortedPoints, segments, initialTriangle) + + val noNeighbors = new Array[Triangle](3) + val tPoints = Array(sortedPoints(0), p1, p2) + val iTriangle = new Triangle(tPoints, noNeighbors) + new CDT(sortedPoints, segments, iTriangle) } // Create segments and connect end points; update edge event pointer @@ -89,11 +92,11 @@ object CDT { // Insertion sort is one of the fastest algorithms for sorting arrays containing // fewer than ten elements, or for lists that are already mostly sorted. // Merge sort: O(n log n) - private def pointSort(pts: ArrayBuffer[Point]): List[Point] = { - if(pts.size < 10) - Util.insertSort((p1: Point, p2: Point) => p1 > p2)(pts).toList + private def pointSort(points: ArrayBuffer[Point]): List[Point] = { + if(points.size < 10) + Util.insertSort((p1: Point, p2: Point) => p1 > p2)(points).toList else - Util.msort((p1: Point, p2: Point) => p1 > p2)(pts.toList) + Util.msort((p1: Point, p2: Point) => p1 > p2)(points.toList) } // Prevents any two distinct endpoints from lying on a common horizontal line, and avoiding @@ -103,60 +106,99 @@ object CDT { } -class CDT(val points: List[Point], val segments: List[Segment], initialTriangle: Triangle) { +class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Triangle) { // The triangle mesh - val mesh = new Mesh(initialTriangle) + val mesh = new Mesh(iTriangle) // Advancing front - val aFront = new AFront(initialTriangle) + val aFront = new AFront(iTriangle) // Sweep points; build mesh sweep // Finalize triangulation finalization - // Implement sweep-line paradigm + // Implement sweep-line private def sweep { for(i <- 1 until points.size) { val point = points(i) - pointEvent(point) - legalization - edgeEvent(point) + val triangle = pointEvent(point) + edgeEvent(point, triangle) } + } // Point event - private def pointEvent(point: Point) { + private def pointEvent(point: Point): Triangle = { + val node = aFront.locate(point) // Neightbor points (ccw & cw) and triangle(i) - val (nPts, nTri) = aFront.locate(point) - val pts = Array(point, nPts(0), nPts(1)) + val cwPoint = node.next.point + val ccwPoint = node.point + val nTri = node.triangle + + val pts = Array(point, ccwPoint, cwPoint) val neighbors = Array(nTri, null, null) val triangle = new Triangle(pts, neighbors) mesh.map += triangle - - // Update neighbor's pointers - if(nPts(0) == nTri.points(1) && nPts(1) == nTri.points(2)) - nTri.neighbors(2) = triangle - else if(nPts(0) == nTri.points(2) && nPts(1) == nTri.points(1)) - nTri.neighbors(1) = triangle - else - throw new Exception("CDT Error!") + triangle.legalize - } - - private def legalization { - - } - - private def legalizeEdge { + nTri.updateNeighbors(ccwPoint, cwPoint, triangle) + // Update advancing front + march(aFront += (point, triangle, node)) + triangle } // EdgeEvent - private def edgeEvent(point: Point) { + private def edgeEvent(point: Point, triangle: Triangle) { + mesh.addEdge(point, triangle) + } + + def march(n: Node) { + var node = n + /* + // Update right + if(node.next != aFront.tail) { + var a = (node.point - node.next.point) + var b = (node.next.next.point - node.next.point) + var angle = Math.atan2(a cross b, a dot b) + while(node != aFront.tail && angle > -Math.Pi*0.5f) { + if(angle >= -Math.Pi*0.5f) { + val points = Array(node.next.point, node.next.next.point, node.point) + val neighbors = Array(null, node.triangle, node.next.triangle) + val triangle = new Triangle(points, neighbors) + mesh.map += triangle + aFront -= (node, node.next, triangle) + } + node = node.next + a = (node.point - node.next.point) + b = (node.next.next.point - node.next.point) + angle = Math.atan2(a cross b, a dot b) + } + } + */ + node = n + + // Update left + if(node.prev != aFront.head) { + var angle = 0.0 + while(node != aFront.head.next && angle < Math.Pi*0.5f) { + val a = (node.point - node.prev.point) + val b = (node.prev.prev.point - node.prev.point) + angle = Math.abs(Math.atan2(a cross b, a dot b)) + if(angle <= Math.Pi*0.5f) { + val points = Array(node.prev.point, node.prev.prev.point, node.point) + val neighbors = Array(null, node.triangle, node.prev.triangle) + val triangle = new Triangle(points, neighbors) + mesh.map += triangle + aFront -== (node, node.prev, triangle) + } + node = node.prev + } + } } private def finalization { diff --git a/src/org/poly2tri/cdt/Mesh.scala b/src/org/poly2tri/cdt/Mesh.scala index 6a65aa3..e901b4f 100644 --- a/src/org/poly2tri/cdt/Mesh.scala +++ b/src/org/poly2tri/cdt/Mesh.scala @@ -39,4 +39,11 @@ class Mesh(initialTriangle: Triangle) { // Triangles that constitute the mesh val map = HashSet(initialTriangle) + def addEdge(point:Point, triangle: Triangle) { + val p = point.eEvent.p + if(!triangle.contains(p)) { + + } + + } } diff --git a/src/org/poly2tri/shapes/Triangle.scala b/src/org/poly2tri/shapes/Triangle.scala index 0708367..a134b0f 100644 --- a/src/org/poly2tri/shapes/Triangle.scala +++ b/src/org/poly2tri/shapes/Triangle.scala @@ -38,4 +38,44 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) { // Flags to determine if an edge is the final Delauney edge val edges = new Array[Boolean](3) + def contains(point: Point) = { + if(point == points(0) || point == points(1) || point == points(2)) + true + else + false + } + + def legalize { + + } + + // Update neighbor pointers + def updateNeighbors(ccwPoint: Point, cwPoint: Point, triangle: Triangle) { + if(ccwPoint == points(2) && cwPoint == points(1)) + neighbors(0) = triangle + else if(ccwPoint == points(0) && cwPoint == points(2)) + neighbors(1) = triangle + else + neighbors(2) = triangle + } + + // Fast point in triangle test + def pointIn(point: Point): Boolean = { + + val ab = points(1) - points(0) + val bc = points(2) - points(1) + val ca = points(0) - points(2) + + val pab = (point - points(0)).cross(ab) + val pbc = (point - points(1)).cross(bc) + var sameSign = Math.signum(pab) == Math.signum(pbc) + if (!sameSign) return false + + val pca = (point - points(2)).cross(ca) + sameSign = Math.signum(pab) == Math.signum(pca) + if (!sameSign) return false + + true +} + }