diff --git a/data/bird.dat b/data/bird.dat index 67b424c..0adb5e2 100644 --- a/data/bird.dat +++ b/data/bird.dat @@ -42,8 +42,8 @@ -7.61308 2.03218 -7.18844 1.45589 -6.79414 1.12225 --6.64248.788605 --6.36951.242648 +-6.64248 0.788605 +-6.36951 0.242648 -6.24818 -0.212317 -6.00553 -0.515627 -5.73255 -0.818936 diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index 1e86295..6582b9e 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -57,17 +57,21 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { var tesselator: Triangulator = null var segments: ArrayBuffer[Segment] = null - val earClip = new EarClip + val earClip = new EarClip + var earClipResults: Array[Triangle] = 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 def init(container: GameContainer) { - poly - earClipPoly bird } @@ -100,7 +104,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { } } - if(!debug) { + if(!debug && !drawEarClip) { var i = 0 for(t <- tesselator.triangles) { val triangle = new Polygon @@ -108,7 +112,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { g.setColor(red) g.draw(triangle) } - } else if (debug && drawMap){ + } else if (debug && drawMap && !drawEarClip){ for(mp <- tesselator.monoPolies) { val poly = new Polygon mp.foreach(p => poly.addPoint(p.x, p.y)) @@ -117,22 +121,24 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { } } + 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(drawSegs) { g.setColor(green) - for(s <- segments) - g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) + for(i <- 0 until segments.size) { + val s = segments(i) + g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) + } } - /* - 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) - })*/ - } override def keyPressed(key:Int, c:Char) { @@ -153,11 +159,12 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { hiLighter = tesselator.triangles.size-1 } if(c == 'm') drawMap = !drawMap - if(c == '1') {poly; earClipPoly; hiLighter = 0} - if(c == '2') {snake; hiLighter = 0} - if(c == '3') {star; hiLighter = 0} + if(c == '1') bird + if(c == '2') {poly; earClipPoly} + if(c == '3') snake + if(c == '4') star if(c == 's') drawSegs = !drawSegs - + if(c == 'e') drawEarClip = !drawEarClip } // Test #1 @@ -204,7 +211,6 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val t2 = System.nanoTime println println("**Poly1**") - println("Poly2Tri core (ms) = " + tesselator.coreTime*1e-6) println("Poly2Tri total (ms) = " + (t2-t1)*1e-6) } @@ -240,7 +246,6 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val t2 = System.nanoTime println println("**Star**") - println("Poly2Tri core (ms) = " + tesselator.coreTime*1e-6) println("Poly2Tri total (ms) = " + (t2-t1)*1e-6) } @@ -282,15 +287,82 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val t2 = System.nanoTime println println("**Snake**") - println("Poly2Tri core (ms) = " + tesselator.coreTime*1e-6) println("Poly2Tri total (ms) = " + (t2-t1)*1e-6) } def bird { - for (line <- Source.fromFile("data/bird.dat").getLines) - print(line) + println("*** Bird ***") + + polyX = new ArrayBuffer[Float] + polyY = new ArrayBuffer[Float] + + val scale = 25.0f + val center = Point(400, 300) + val angle = Math.Pi + for (line <- Source.fromFile("data/bird.dat").getLines) { + 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 + } + } + + segments = new ArrayBuffer[Segment] + + var i = 0 + val numPoints = polyX.size + while(i < polyX.size-2) { + val p1 = new Point(polyX(i), polyY(i)) + val p2 = new Point(polyX(i+1), polyY(i+1)) + segments += new Segment(p1, p2) + i += 1 + } + + // Connect the end points + val p1 = segments(0).p + val p2 = segments(segments.length-1).q + segments += new Segment(p2, p1) + + var t1: Float = 0f + var t2: Float = 0f + var runTime: Float = 0 + val iterations = 100 + + println("Iteration count = " + iterations) + println("Seidel triangulation:") + + for(i <- 0 until iterations) { + // Run benchmarks + tesselator = new Triangulator(segments) + t1 = System.nanoTime + tesselator.process + runTime += System.nanoTime - t1 + } + println("Poly2Tri average (ms) = " + runTime*1e-6/iterations) + println("Number of triangles = " + tesselator.triangles.size) + + println("Earclip triangulation:") + // Earclip + earClipResults = new Array[Triangle](500) + for(i <- 0 until earClipResults.size) earClipResults(i) = new Triangle + val xVerts = polyX.toArray.reverse + val yVerts = polyY.toArray.reverse + + runTime = 0 + for(i <- 0 until iterations) { + t1 = System.nanoTime + earClip.triangulatePolygon(xVerts, yVerts, xVerts.size, earClipResults) + runTime += System.nanoTime - t1 + } + println("Earclip average (ms) = " + runTime*1e-6/iterations) + val numTriangles = earClip.triangulatePolygon(xVerts, yVerts, xVerts.size, earClipResults) + println("Number of triangles = " + numTriangles) } def earClipPoly { diff --git a/src/org/poly2tri/Segment.scala b/src/org/poly2tri/Segment.scala index 303b695..07289cd 100644 --- a/src/org/poly2tri/Segment.scala +++ b/src/org/poly2tri/Segment.scala @@ -47,8 +47,8 @@ class Segment(var p: Point, var q: Point) { val b = p.y - (p.x * slope) // Determines if this segment lies above the given point - def > (point: Point) = (point.y < Math.round(slope * point.x + b)) + def > (point: Point) = (Math.floor(point.y) < Math.floor(slope * point.x + b)) // Determines if this segment lies below the given point - def < (point: Point) = (point.y > Math.round(slope * point.x + b)) + def < (point: Point) = (Math.floor(point.y) > Math.floor(slope * point.x + b)) } diff --git a/src/org/poly2tri/Triangulator.scala b/src/org/poly2tri/Triangulator.scala index d4b610b..0247648 100644 --- a/src/org/poly2tri/Triangulator.scala +++ b/src/org/poly2tri/Triangulator.scala @@ -36,11 +36,6 @@ import scala.collection.mutable.ArrayBuffer // algorithm for computing trapezoidal decompositions and for triangulating polygons" class Triangulator(segments: ArrayBuffer[Segment]) { - var sortTime = 0.0 - var coreTime = 0.0 - var trapezoidTime = 0.0 - var markTime = 0.0 - // Triangle decomposition list var triangles = new ArrayBuffer[Array[Point]] @@ -49,14 +44,11 @@ class Triangulator(segments: ArrayBuffer[Segment]) { // Build the trapezoidal map and query graph def process { - - val t1 = System.nanoTime - + var i = 0 while(i < segmentList.size) { val s = segmentList(i) - var traps = queryGraph.followSegment(s) // Remove trapezoids from trapezoidal Map @@ -104,8 +96,6 @@ class Triangulator(segments: ArrayBuffer[Segment]) { } - coreTime = System.nanoTime - t1 - // Mark outside trapezoids for(t <- trapezoidalMap.map) markOutside(t) @@ -174,9 +164,7 @@ class Triangulator(segments: ArrayBuffer[Segment]) { } // Triangulate monotone mountain - val t1 = System.nanoTime mountain.triangulate - coreTime += System.nanoTime - t1 // Extract the triangles into a single list j = 0 diff --git a/src/org/poly2tri/XNode.scala b/src/org/poly2tri/XNode.scala index fde5b9a..1664469 100644 --- a/src/org/poly2tri/XNode.scala +++ b/src/org/poly2tri/XNode.scala @@ -33,6 +33,7 @@ package org.poly2tri class XNode(point: Point, lChild: Node, rChild: Node) extends Node(lChild, rChild) { override def locate(s: Segment): Sink = { + if(s.p.x >= point.x) { // Move to the right in the graph return right.locate(s)