diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index 56da3d5..2d1c73c 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -46,7 +46,7 @@ import cdt.CDT object Poly2Tri { def main(args: Array[String]) { - val container = new AppGameContainer(new Poly2TriDemo()) + val container = new AppGameContainer(new Poly2TriDemo()) container.setDisplayMode(800,600,false) container.start() } @@ -54,7 +54,13 @@ object Poly2Tri { } class Poly2TriDemo extends BasicGame("Poly2Tri") { - + + object Algo extends Enumeration { + type Algo = Value + val CDT, Seidel, EarClip = Value + } + import Algo._ + // Sedidel Triangulator var seidel: Triangulator = null var segments: ArrayBuffer[Segment] = null @@ -68,17 +74,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { // Sweep Line Constraied Delauney Triangulator (CDT) var slCDT: CDT = null - var polyX: ArrayBuffer[Float] = null - var polyY: ArrayBuffer[Float] = null - var quit = false - var debug = false var drawMap = false var drawSegs = true - var hiLighter = 0 - var drawEarClip = false - var drawCDT = true - var drawcdtMesh = false + var drawCDTMesh = false val nazcaMonkey = "data/nazca_monkey.dat" val nazcaHeron = "data/nazca_heron_old.dat" @@ -92,6 +91,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { var currentModel = dude var doCDT = true + // The current algorithm + var algo = CDT var mouseButton = 0 var mousePressed = false @@ -115,129 +116,107 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { def render(container: GameContainer, g: Graphics) { - g.drawString("'1-9' to cycle models, mouse to pan & zoom", 10, 520) - g.drawString("'SPACE' to show Seidel debug info", 10, 532) - g.drawString("'m' to show trapezoidal map (Seidel debug mode)", 10, 544) - g.drawString("'e' to switch Seidel / EarClip", 10, 556) - g.drawString("'d' to switch CDT / Seidel", 10, 568) - g.drawString("'c' to show CDT mesh, 's' to draw edges", 10, 580) - + g.drawString("'1-9' to cycle models, mouse to pan & zoom", 10, 522) + g.drawString("'c,s,e' to switch CDT / Seidel / EarClip algos", 10, 537) + g.drawString("'t' to show trapezoidal map (Seidel)", 10, 552) + g.drawString("'m' to show triangle mesh (CDT)", 10, 567) + g.drawString("'d' to draw edges", 10, 582) + g.scale(scaleFactor, scaleFactor) - g.translate(deltaX, deltaY) + g.translate(deltaX, deltaY) val red = new Color(1f, 0f,0.0f) val blue = new Color(0f, 0f, 1f) val green = new Color(0f, 1f, 0f) val yellow = new Color(1f, 1f, 0f) - if(debug) { - val draw = if(drawMap) seidel.trapezoidMap else seidel.trapezoids - for(t <- draw) { - val polygon = new Polygon() - for(v <- t.vertices) { - polygon.addPoint(v.x, v.y) - } - if(!drawMap) { - val lCirc = new Circle(t.leftPoint.x, t.leftPoint.y, 4) - g.setColor(blue); g.draw(lCirc); g.fill(lCirc) - val rCirc = new Circle(t.rightPoint.x+5, t.rightPoint.y, 4) - g.setColor(yellow); g.draw(rCirc); g.fill(rCirc) - } - g.setColor(red) - g.draw(polygon) - } - } - - if(!debug && !drawEarClip && !drawCDT) { - var i = 0 - for(t <- seidel.polygons) { - val poly = new Polygon - t.foreach(p => poly.addPoint(p.x, p.y)) - g.setColor(red) - g.draw(poly) + algo match { + + case Algo.Seidel => { + for(t <- seidel.polygons) { + val poly = new Polygon + t.foreach(p => poly.addPoint(p.x, p.y)) + g.setColor(red) + g.draw(poly) + } + if (drawMap) { + for(t <- seidel.trapezoidMap) { + val polygon = new Polygon() + for(v <- t.vertices) { + polygon.addPoint(v.x, v.y) + } + g.setColor(red) + g.draw(polygon) + }/* + for(mp <- seidel.monoPolies) { + val poly = new Polygon + mp.foreach(p => poly.addPoint(p.x, p.y)) + g.setColor(yellow) + g.draw(poly) + }*/ + } } - } else if (debug && drawMap && !drawEarClip){ - for(mp <- seidel.monoPolies) { - val poly = new Polygon - mp.foreach(p => poly.addPoint(p.x, p.y)) - g.setColor(yellow) - g.draw(poly) + + case Algo.EarClip => { + earClipResults.foreach(t => { + val triangle = new Polygon + triangle.addPoint(t.x(0), t.y(0)) + triangle.addPoint(t.x(1), t.y(1)) + triangle.addPoint(t.x(2), t.y(2)) + g.setColor(red) + g.draw(triangle) + }) + } + + case Algo.CDT => { + val draw = if(drawCDTMesh) slCDT.triangleMesh else slCDT.triangles + draw.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) + }) + + slCDT.debugTriangles.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(blue) + g.draw(triangle) + }) + + for(i <- 0 until slCDT.cList.size) { + val circ = new Circle(slCDT.cList(i).x, slCDT.cList(i).y, 0.5f) + g.setColor(blue); g.draw(circ); g.fill(circ) + } + + } + + case _ => + } + + if(drawSegs) { + g.setColor(green) + for(i <- 0 until segments.size) { + val s = segments(i) + g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) } } - if(drawEarClip) - earClipResults.foreach(t => { - val triangle = new Polygon - triangle.addPoint(t.x(0), t.y(0)) - triangle.addPoint(t.x(1), t.y(1)) - triangle.addPoint(t.x(2), t.y(2)) - g.setColor(red) - g.draw(triangle) - }) - - if(drawCDT) { - - val draw = if(drawcdtMesh) slCDT.triangleMesh else slCDT.triangles - - draw.foreach( t => { - if(false) { - for(i <- 0 to 2) { - val s = t.points(i) - val e = if(i == 2) t.points(0) else t.points(i + 1) - val j = if(i == 0) 2 else if(i == 1) 0 else 1 - if(t.edges(j)) - g.setColor(yellow) - else - g.setColor(red) - g.drawLine(s.x,s.y,e.x,e.y) - } - } else { - 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) - } - }) - - slCDT.debugTriangles.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(blue) - g.draw(triangle) - }) - - //slCDT.cList.foreach(c => { - for(i <- 0 until slCDT.cList.size) { - val circ = new Circle(slCDT.cList(i).x, slCDT.cList(i).y, 0.5f) - g.setColor(blue); g.draw(circ); g.fill(circ) - } - //}) - - } - - if(drawSegs) { - g.setColor(green) - for(i <- 0 until segments.size) { - val s = segments(i) - g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) - } - } - - if(currentModel == "data/dude.dat" && drawSegs) { - g.setColor(green) - for(i <- 0 until chestSegs.size) { - val s = chestSegs(i) - g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) - } - for(i <- 0 until headSegs.size) { - val s = headSegs(i) - g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) - } - } + if(currentModel == "data/dude.dat" && drawSegs) { + g.setColor(green) + for(i <- 0 until chestSegs.size) { + val s = chestSegs(i) + g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) + } + for(i <- 0 until headSegs.size) { + val s = headSegs(i) + g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) + } + } } @@ -246,6 +225,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { * @param p The screen location that the mouse is down at. */ override def mousePressed(b: Int, x: Int, y: Int) { + mouseButton = b mousePressed = true mousePosOld = mousePos @@ -278,7 +258,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { mousePosOld = mousePos mousePos = Point(x,y) if(mousePressed) { - deltaX += mousePos.x - mousePosOld.x + deltaX += mousePos.x - mousePosOld.x deltaY += mousePos.y - mousePosOld.y } } @@ -296,23 +276,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { // ESC if(key == 1) quit = true - // SPACE - if(key == 57) debug = !debug - // UP - if(key == 200) { - hiLighter += 1 - if (hiLighter == seidel.polygons.size) - hiLighter = 0 - } - // DOWN - if(key == 208) { - hiLighter -= 1 - if (hiLighter == -1) - hiLighter = seidel.polygons.size-1 - } - if(c == 'm') drawMap = !drawMap - if(c == 'd') drawCDT = !drawCDT if(c == '1') selectModel(nazcaMonkey) if(c == '2') selectModel(bird) if(c == '3') selectModel(strange) @@ -322,9 +286,16 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { if(c == '7') selectModel(nazcaHeron) if(c == '8') selectModel(tank) if(c == '9') selectModel(dude) - if(c == 's') drawSegs = !drawSegs - if(c == 'c') drawcdtMesh = !drawcdtMesh - if(c == 'e') {drawEarClip = !drawEarClip; drawCDT = false; selectModel(currentModel)} + + if(c == 'd') drawSegs = !drawSegs + if(c == 'm') drawCDTMesh = !drawCDTMesh + if(c == 't') drawMap = !drawMap + + if(c == 's') {algo = Seidel; selectModel(currentModel) } + if(c == 'c') {algo = CDT; selectModel(currentModel) } + if(c == 'e') {algo = EarClip; selectModel(currentModel) } + + // Experimental... if(c == 'r') slCDT.refine } @@ -358,9 +329,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { case "data/dude.dat" => val clearPoint = Point(365, 427) loadModel(dude, -1f, Point(100f, -200f), 10, clearPoint) - case _ => - assert(false) - + case _ => } currentModel = model } @@ -370,8 +339,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { println println("************** " + model + " **************") - polyX = new ArrayBuffer[Float] - polyY = new ArrayBuffer[Float] + val polyX = new ArrayBuffer[Float] + val polyY = new ArrayBuffer[Float] var points = new ArrayBuffer[Point] val angle = Math.Pi @@ -379,12 +348,12 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val s = line.replaceAll("\n", "") val tokens = s.split("[ ]+") if(tokens.size == 2) { - var x = tokens(0).toFloat - var y = tokens(1).toFloat - // Transform the shape - polyX += (Math.cos(angle)*x - Math.sin(angle)*y).toFloat * scale + center.x - polyY += (Math.sin(angle)*x + Math.cos(angle)*y).toFloat * scale + center.y - points += new Point(polyX.last, polyY.last) + var x = tokens(0).toFloat + var y = tokens(1).toFloat + // Transform the shape + polyX += (Math.cos(angle)*x - Math.sin(angle)*y).toFloat * scale + center.x + polyY += (Math.sin(angle)*x + Math.cos(angle)*y).toFloat * scale + center.y + points += new Point(polyX.last, polyY.last) } else { throw new Exception("Bad input file") } @@ -392,91 +361,100 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { segments = new ArrayBuffer[Segment] for(i <- 0 until points.size-1) - segments += new Segment(points(i), points(i+1)) + segments += new Segment(points(i), points(i+1)) segments += new Segment(points.first, points.last) println("Number of points = " + polyX.size) println - - if(doCDT) { - + + algo match { + + case Algo.CDT => { + val pts = points.toArray - val t1 = System.nanoTime - slCDT = new CDT(pts, clearPoint) - + val t1 = System.nanoTime + slCDT = new CDT(pts, clearPoint) + // Add some holes.... if(model == "data/dude.dat") { - + val headHole = Array(Point(325f,437f), Point(320f,423f), Point(329f,413f), Point(332f,423f)) - val chestHole = Array(Point(320.72342f,480f), Point(338.90617f,465.96863f), - Point(347.99754f,480.61584f), Point(329.8148f,510.41534f), + val chestHole = Array(Point(320.72342f,480f), Point(338.90617f,465.96863f), + Point(347.99754f,480.61584f), Point(329.8148f,510.41534f), Point(339.91632f,480.11077f), Point(334.86556f,478.09046f)) - - // Tramsform the points + + // Tramsform the points for(i <- 0 until headHole.size) { val hx = -headHole(i).x*scale + center.x val hy = -headHole(i).y*scale + center.y headHole(i) = Point(hx, hy) - } - for(i <- 0 until chestHole.size) { - val cx = -chestHole(i).x*scale + center.x - val cy = -chestHole(i).y*scale + center.y - chestHole(i) = Point(cx, cy) } - + for(i <- 0 until chestHole.size) { + val cx = -chestHole(i).x*scale + center.x + val cy = -chestHole(i).y*scale + center.y + chestHole(i) = Point(cx, cy) + } + chestSegs = new ArrayBuffer[Segment] - for(i <- 0 until chestHole.size-1) - chestSegs += new Segment(chestHole(i), chestHole(i+1)) - chestSegs += new Segment(chestHole.first, chestHole.last) - + for(i <- 0 until chestHole.size-1) + chestSegs += new Segment(chestHole(i), chestHole(i+1)) + chestSegs += new Segment(chestHole.first, chestHole.last) + headSegs = new ArrayBuffer[Segment] - for(i <- 0 until headHole.size-1) - headSegs += new Segment(headHole(i), headHole(i+1)) - headSegs += new Segment(headHole.first, headHole.last) - + for(i <- 0 until headHole.size-1) + headSegs += new Segment(headHole(i), headHole(i+1)) + headSegs += new Segment(headHole.first, headHole.last) + // Add the holes slCDT.addHole(headHole) slCDT.addHole(chestHole) } - + slCDT triangulate - val runTime = System.nanoTime - t1 - - println("CDT average (ms) = " + runTime*1e-6) - println("Number of triangles = " + slCDT.triangles.size) - println - } - - if(!drawEarClip) { - - // Sediel triangulation - val t1 = System.nanoTime - seidel = new Triangulator(points) val runTime = System.nanoTime - t1 - - println("Poly2Tri average (ms) = " + runTime*1e-6) - println("Number of triangles = " + seidel.polygons.size) - - } else { - - // Earclip - - earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles) - - for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle - - var xVerts = polyX.toArray - var yVerts = polyY.toArray - - val t1 = System.nanoTime - earClip.triangulatePolygon(xVerts, yVerts, xVerts.size, earClipResults) - val runTime = System.nanoTime - t1 - - println - println("Earclip average (ms) = " + runTime*1e-6) - println("Number of triangles = " + earClip.numTriangles) - } + + println("CDT average (ms) = " + runTime*1e-6) + println("Number of triangles = " + slCDT.triangles.size) + println + } + + case Algo.Seidel => { + + // Sediel triangulation + val t1 = System.nanoTime + seidel = new Triangulator(points) + val runTime = System.nanoTime - t1 + + println("Seidel average (ms) = " + runTime*1e-6) + println("Number of triangles = " + seidel.polygons.size) + + } + + case Algo.EarClip => { + + // Earclip + + earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles) + + for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle + + var xVerts = polyX.toArray + var yVerts = polyY.toArray + + val xv = if(currentModel != "data/strange.dat") xVerts.reverse.toArray else xVerts + val yv = if(currentModel != "data/strange.dat") yVerts.reverse.toArray else yVerts + + val t1 = System.nanoTime + earClip.triangulatePolygon(xv, yv, xVerts.size, earClipResults) + val runTime = System.nanoTime - t1 + + println + println("Earclip average (ms) = " + runTime*1e-6) + println("Number of triangles = " + earClip.numTriangles) + } + + case _ => } - -} \ No newline at end of file + } +} diff --git a/src/org/poly2tri/cdt/CDT.scala b/src/org/poly2tri/cdt/CDT.scala index f0f2492..fef50d8 100644 --- a/src/org/poly2tri/cdt/CDT.scala +++ b/src/org/poly2tri/cdt/CDT.scala @@ -109,76 +109,65 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { // Create edges and connect end points; update edge event pointer private def initEdges(pts: Array[Point]) { - // Connect pts for(i <- 0 until pts.size-1) { val endPoints = validatePoints(pts(i), pts(i+1)) val edge = new Segment(endPoints(0), endPoints(1)) endPoints(1).edges += edge } - // Connect endpoints val endPoints = validatePoints(pts.first, pts.last) val edge = new Segment(endPoints(0), endPoints(1)) endPoints(1).edges += edge - } private def validatePoints(p1: Point, p2: Point): List[Point] = { - if(p1.y > p2.y) { // For CDT we want q to be the point with > y - return List(p2, p1) - } else if(p1.y == p2.y) { + return List(p2, p1) + } else if(p1.y == p2.y) { // If y values are equal, make sure point with smaller x value // is to the left if(p1.x > p2.x) { return List(p2, p1) } else if(p1.x == p2.x) { - throw new Exception("Duplicate point") - } + throw new Exception("Duplicate point") + } } - List(p1, p2) } // Merge sort: O(n log n) private def pointSort: List[Point] = - Util.msort((p1: Point, p2: Point) => p1 > p2)(points) + Util.msort((p1: Point, p2: Point) => p1 > p2)(points) // Implement sweep-line private def sweep { - val size = if(refined) 1 else points.size - for(i <- 1 until points.size) { - val point = points(i) // Process Point event val node = pointEvent(point) // Process edge events point.edges.foreach(e => edgeEvent(e, node)) } - } // 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 - } + // Mark the originating clean triangle + if(m.pointIn(clearPoint)) { + found = true + cleanTri = m + } m.markNeighborEdges }) // Collect interior triangles constrained by edges mesh clean cleanTri - } // Delauney Refinement: Refine triangules using Steiner points @@ -191,13 +180,13 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { mesh.triangles.foreach(t => { if(t.thin) { val center = Util.circumcenter(t.points(0), t.points(1), t.points(2)) - cList += center - addPoint(center) + cList += center + addPoint(center) } }) // Retriangulate if(cList.size > 0) - triangulate + triangulate } // Point event @@ -224,63 +213,63 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { // Locate the first intersected triangle val firstTriangle = if(!node.triangle.contains(edge.q)) - node.triangle + node.triangle else - node.triangle.locateFirst(edge) + node.triangle.locateFirst(edge) if(firstTriangle != null && !firstTriangle.contains(edge)) { - // Interior mesh traversal - edge is "burried" in the mesh - // Constrained edge lies below the advancing front. Traverse through intersected triangles, - // form empty pseudo-polygons, and re-triangulate + // Interior mesh traversal - edge is "burried" in the mesh + // Constrained edge lies below the advancing front. Traverse through intersected triangles, + // form empty pseudo-polygons, and re-triangulate - // Collect intersected triangles - val tList = new ArrayBuffer[Triangle] - tList += firstTriangle + // Collect intersected triangles + val tList = new ArrayBuffer[Triangle] + tList += firstTriangle - while(tList.last != null && !tList.last.contains(edge.p)) - tList += tList.last.findNeighbor(edge.p) + while(tList.last != null && !tList.last.contains(edge.p)) + tList += tList.last.findNeighbor(edge.p) - // TODO: Finish implementing edge insertion which combines advancing front (AF) - // and triangle traversal respectively. See figure 14(a) from Domiter et al. - // Should only occur with complex patterns of interior points - // Already added provision for transitioning from AFront traversal to - // interior mesh traversal - may need to add the opposite case - if(tList.last == null) - throw new Exception("Not implemented yet - interior points too complex") + // TODO: Finish implementing edge insertion which combines advancing front (AF) + // and triangle traversal respectively. See figure 14(a) from Domiter et al. + // Should only occur with complex patterns of interior points + // Already added provision for transitioning from AFront traversal to + // interior mesh traversal - may need to add the opposite case + if(tList.last == null) + throw new Exception("Not implemented yet - interior points too complex") - // Neighbor triangles - // HashMap or set may improve performance - val nTriangles = new ArrayBuffer[Triangle] + // Neighbor triangles + // HashMap or set may improve performance + val nTriangles = new ArrayBuffer[Triangle] - // Remove old triangles; collect neighbor triangles - // Keep duplicates out - tList.foreach(t => { - t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n) - mesh.map -= t - }) + // Remove old triangles; collect neighbor triangles + // Keep duplicates out + tList.foreach(t => { + t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n) + mesh.map -= t + }) - // Using a hashMap or set may improve performance - val lPoints = new ArrayBuffer[Point] - val rPoints = new ArrayBuffer[Point] + // Using a hashMap or set may improve performance + val lPoints = new ArrayBuffer[Point] + val rPoints = new ArrayBuffer[Point] - // Collect points left and right of edge - tList.foreach(t => { - t.points.foreach(p => { - if(p != edge.q && p != edge.p) { - if(Util.orient2d(edge.q, edge.p, p) > 0 ) { - // Keep duplicate points out - if(!lPoints.contains(p)) { - lPoints += p - } - } else { - // Keep duplicate points out - if(!rPoints.contains(p)) - rPoints += p + // Collect points left and right of edge + tList.foreach(t => { + t.points.foreach(p => { + if(p != edge.q && p != edge.p) { + if(Util.orient2d(edge.q, edge.p, p) > 0 ) { + // Keep duplicate points out + if(!lPoints.contains(p)) { + lPoints += p } - } - }) - }) + } else { + // Keep duplicate points out + if(!rPoints.contains(p)) + rPoints += p + } + } + }) + }) // Triangulate empty areas. val T1 = new ArrayBuffer[Triangle] @@ -296,8 +285,8 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { // Update advancing front 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 point1 = if(ahead) edge.q else edge.p + val point2 = if(ahead) edge.p else edge.q val sNode = if(ahead) node else aFront.locate(point1) val eNode = aFront.locate(point2) @@ -333,23 +322,23 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { if(ahead) { // Scan right pNode = pNode.next - while(pNode.point != edge.p && !aboveEdge) { - points += pNode.point + while(pNode.point != edge.p && !aboveEdge) { + points += pNode.point nTriangles += pNode.triangle - pNode = pNode.next + pNode = pNode.next aboveEdge = edge < pNode.point - } + } } else { - // Scan left - pNode = pNode.prev - while(pNode.point != edge.p && !aboveEdge) { - points += pNode.point + // Scan left + pNode = pNode.prev + while(pNode.point != edge.p && !aboveEdge) { + points += pNode.point nTriangles += pNode.triangle - pNode = pNode.prev + pNode = pNode.prev aboveEdge = edge < pNode.point - } - nTriangles += pNode.triangle - } + } + nTriangles += pNode.triangle + } val point2 = if(aboveEdge) { val p1 = pNode.point @@ -396,11 +385,11 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { private def edgeNeighbors(nTriangles: ArrayBuffer[Triangle], T: ArrayBuffer[Triangle]) { for(t1 <- nTriangles) - for(t2 <- T) - t2.markNeighbor(t1) + for(t2 <- T) + t2.markNeighbor(t1) for(i <- 0 until T.size) - for(j <- i+1 until T.size) + for(j <- i+1 until T.size) T(i).markNeighbor(T(j)) } @@ -455,7 +444,7 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { if(node2.prev != null) { var angle = 0.0 do { - angle = fill(node2) + angle = fill(node2) node2 = node2.prev } while(angle <= PI_2 && angle >= -PI_2 && node2.prev != null) } @@ -468,31 +457,28 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { val a = (node.prev.point - node.point) val b = (node.next.point - node.point) val angle = Math.atan2(a cross b, a dot b) - // Is the angle acute? + // 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 - node.prev.triangle.markNeighbor(triangle) - node.triangle.markNeighbor(triangle) + // Update neighbor pointers + node.prev.triangle.markNeighbor(triangle) + node.triangle.markNeighbor(triangle) mesh.map += triangle aFront -= (node.prev, node, triangle) } - angle + angle } // Circumcircle test. // Determines if point d lies inside triangle abc's circumcircle private def illegal(a: Point, b: Point, c: Point, d: Point): Boolean = { - val ccw = Util.orient2d(a, b, c) > 0 - // Make sure abc is oriented counter-clockwise if(ccw) - Util.incircle(a, b, c, d) + Util.incircle(a, b, c, d) else Util.incircle(a, c, b, d) - } // Ensure adjacent triangles are legal @@ -509,33 +495,33 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { if(illegal && !t2.finalized) { - // Flip edge and rotate everything clockwise + // Flip edge and rotate everything clockwise - // Legalize points + // Legalize points t1.legalize(oPoint) t2.legalize(oPoint, point) - // Update neighbors + // Update neighbors - // Copy old neighbors + // Copy old neighbors val neighbors = List(t2.neighbors(0), t2.neighbors(1), t2.neighbors(2)) - // Clear old neighbors - t2.clearNeighbors - // Update new neighbors + // Clear old neighbors + t2.clearNeighbors + // Update new neighbors for(n <- neighbors) { if(n != null) { t1.markNeighbor(n) - t2.markNeighbor(n) + t2.markNeighbor(n) } } - t2.markNeighbor(t1) + t2.markNeighbor(t1) - // Don't legalize these triangles again - t2.finalized = true - t1.finalized = true + // Don't legalize these triangles again + t2.finalized = true + t1.finalized = true - // Update advancing front - aFront.insertLegalized(t1.points(1), t1, node) + // Update advancing front + aFront.insertLegalized(t1.points(1), t1, node) } else { @@ -545,7 +531,6 @@ class CDT(polyLine: Array[Point], clearPoint: Point) { aFront.insert(point, t1, node) } - } // The triangle mesh diff --git a/src/org/poly2tri/earClip/EarClip.scala b/src/org/poly2tri/earClip/EarClip.scala index 9243ae4..431ac65 100644 --- a/src/org/poly2tri/earClip/EarClip.scala +++ b/src/org/poly2tri/earClip/EarClip.scala @@ -38,21 +38,10 @@ class EarClip { var numTriangles = 0 - def triangulatePolygon(x: Array[Float], y: Array[Float], vn: Int, results: Array[Triangle]): Int = { - - /* - val p1 = Point(x(0), y(0)) - val p2 = Point(x(1), y(1)) - val p3 = Point(x(2), y(2)) - - val ccw = Util.orient2d(p1, p2, p3) > 0 - */ - - val xv = x.reverse.toArray - val yv = y.reverse.toArray - - if (vn < 3) return 0 - var vNum = vn + def triangulatePolygon(xv: Array[Float], yv: Array[Float], vn: Int, results: Array[Triangle]): Int = { + + if (vn < 3) return 0 + var vNum = vn //Recurse and split on pinch points val pA = new Poly @@ -133,15 +122,18 @@ class EarClip { System.out.println("Couldn't find an ear, dumping remaining poly:\n"); System.out.println("Please submit this dump to ewjordan at Box2d forums\n"); + + assert(false) + for (i <- 0 until bufferSize) { results(i).set(buffer(i)); } if (bufferSize > 0) return bufferSize; - else { - numTriangles = -1 - return numTriangles - } + else { + numTriangles = -1 + return numTriangles + } } // Clip off the ear: