diff --git a/src/org/poly2tri/MonoToneMountain.scala b/src/org/poly2tri/MonoToneMountain.scala index 88700ba..148ccb5 100644 --- a/src/org/poly2tri/MonoToneMountain.scala +++ b/src/org/poly2tri/MonoToneMountain.scala @@ -39,9 +39,9 @@ class MonotoneMountain { var size = 0 val convexPoints = new Queue[Point] - // Monotone mountain points + // Monotone mountain points val monoPoly = new ArrayBuffer[Point] - // Triangles that constitute the mountain + // Triangles that constitute the mountain val triangles = new ArrayBuffer[Array[Point]] // Used to track which side of the line we are on var positive = false @@ -53,14 +53,14 @@ class MonotoneMountain { size match { case 0 => head = point - case 1 => - tail = point - tail.prev = head - head.next = tail - case _ => - tail.next = point - point.prev = tail - tail = point + case 1 => + tail = point + tail.prev = head + head.next = tail + case _ => + tail.next = point + point.prev = tail + tail = point } size += 1 } @@ -79,54 +79,53 @@ class MonotoneMountain { def triangulate { // Establish the proper sign - positive = initAngle + positive = angleSign // create monotone polygon - for dubug purposes genMonoPoly if(size == 3) { lastTriangle } else { - // Initialize internal angles at each nonbase vertex - // Link strictly convex vertices into a list, ignore reflex vertices - var p = head.next - while(p != tail) { - val a = angle(p) - // If the point is almost colinear with it's neighbor, remove it! - if(a >= PI_SLOP || a <= -PI_SLOP) - remove(p) - else - if(convex(p)) convexPoints.enqueue(p) - p = p.next - } + // Initialize internal angles at each nonbase vertex + // Link strictly convex vertices into a list, ignore reflex vertices + var p = head.next + while(p != tail) { + val a = angle(p) + // If the point is almost colinear with it's neighbor, remove it! + if(a >= PI_SLOP || a <= -PI_SLOP) + remove(p) + else + if(convex(p)) convexPoints.enqueue(p) + p = p.next + } - while(!convexPoints.isEmpty) { + while(!convexPoints.isEmpty) { - val ear = convexPoints.dequeue - val a = ear.prev - val b = ear - val c = ear.next - val triangle = Array(a, b, c) + val ear = convexPoints.dequeue + val a = ear.prev + val b = ear + val c = ear.next + val triangle = Array(a, b, c) - triangles += triangle + triangles += triangle - // Remove ear, update angles and convex list - remove(ear) - if(valid(a)) convexPoints.enqueue(a); - if(valid(c)) convexPoints.enqueue(c) - - } - assert(size <= 2, "Triangulation bug") - } - } + // Remove ear, update angles and convex list + remove(ear) + if(valid(a)) convexPoints.enqueue(a) + if(valid(c)) convexPoints.enqueue(c) + } + assert(size <= 3, "Triangulation bug") + } + } def valid(p: Point) = (p.prev != null && p.next != null && convex(p)) // Create the monotone polygon private def genMonoPoly { - var p = head + var p = head while(p != null) { - monoPoly += p - p = p.next + monoPoly += p + p = p.next } } @@ -136,7 +135,7 @@ class MonotoneMountain { Math.atan2(a cross b, a dot b) } - def initAngle = { + def angleSign = { val a = (head.next - head) val b = (tail - head) (Math.atan2(a cross b, a dot b) >= 0) @@ -144,20 +143,18 @@ class MonotoneMountain { // Determines if the inslide angle is convex or reflex private def convex(p: Point) = { - if(positive != (angle(p) >= 0)) - false - else - true - } + if(positive != (angle(p) >= 0)) false + else true + } private def lastTriangle { val triangle = new Array[Point](3) var i = 0 - var p = head + var p = head while(p != null) { - triangle(i) = p - p = p.next - i += 1 + triangle(i) = p + p = p.next + i += 1 } triangles += triangle } diff --git a/src/org/poly2tri/Node.scala b/src/org/poly2tri/Node.scala index f3126ad..bef7dec 100644 --- a/src/org/poly2tri/Node.scala +++ b/src/org/poly2tri/Node.scala @@ -30,7 +30,7 @@ */ package org.poly2tri -import collection.jcl.ArrayList +import scala.collection.mutable.ArrayBuffer // Node for a Directed Acyclic graph (DAG) abstract class Node(var left: Node, var right: Node) { @@ -38,7 +38,7 @@ abstract class Node(var left: Node, var right: Node) { if(left != null) left.parentList += this if(right != null) right.parentList += this - var parentList = new ArrayList[Node] + var parentList = new ArrayBuffer[Node] def locate(s: Segment): Sink @@ -46,13 +46,9 @@ abstract class Node(var left: Node, var right: Node) { // Make sure parent pointers are updated def replace(node: Node) { for(parent <- node.parentList) { - // Select the correct node (left or right child) - if(parent.left == node) { - parent.left = this - } else { - parent.right = this - } - // Decouple the node + // Select the correct node to replace (left or right child) + if(parent.left == node) parent.left = this + else parent.right = this parentList += parent } } diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index f06807b..9c81229 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -64,7 +64,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { var hiLighter = 0 def init(container: GameContainer) { - snake + poly } def update(gc: GameContainer, delta: Int) { @@ -81,7 +81,6 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { if(debug) { val draw = if(drawMap) tesselator.trapezoidMap else tesselator.trapezoids for(t <- draw) { - assert(t.rightPoint != t.leftPoint) val polygon = new Polygon() for(v <- t.vertices) { polygon.addPoint(v.x, v.y) @@ -93,14 +92,13 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { g.setColor(yellow); g.draw(rCirc); g.fill(rCirc) } g.setColor(red) - g.draw(polygon) + g.draw(polygon) } } if(!debug) { var i = 0 for(t <- tesselator.triangles) { - if(t.size < 3) println("wtf") val triangle = new Polygon t.foreach(p => triangle.addPoint(p.x, p.y)) g.setColor(red) @@ -154,7 +152,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { // Test #1 def poly { - + val p1 = Point(400,472) val p2 = Point(500,392) val p3 = Point(520,272) @@ -224,7 +222,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { // Test #2 def snake { - + val scale = 10.0f val displace = 100 val p1 = Point(10,1)*scale+displace diff --git a/src/org/poly2tri/QueryGraph.scala b/src/org/poly2tri/QueryGraph.scala index ffe1b39..002f7e9 100644 --- a/src/org/poly2tri/QueryGraph.scala +++ b/src/org/poly2tri/QueryGraph.scala @@ -44,17 +44,13 @@ class QueryGraph(var head: Node) { val trapezoids = new ArrayBuffer[Trapezoid] trapezoids += locate(s) var j = 0 - try { - while(s.q.x > trapezoids(j).rightPoint.x) { - if(s > trapezoids(j).rightPoint) { - trapezoids += trapezoids(j).upperRight - } else { - trapezoids += trapezoids(j).lowerRight - } - j += 1 - } - } catch { - case e => println("# of Trapezoids = " + j) + while(s.q.x > trapezoids(j).rightPoint.x) { + if(s > trapezoids(j).rightPoint) { + trapezoids += trapezoids(j).upperRight + } else { + trapezoids += trapezoids(j).lowerRight + } + j += 1 } trapezoids } @@ -63,31 +59,32 @@ class QueryGraph(var head: Node) { if(sink.parentList.size == 0) { head = node } else { - node.replace(sink) + node replace sink } } - def case1(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) { + def case1(sink: Sink, s: Segment, tList: Array[Trapezoid]) { val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2))) val qNode = new XNode(s.q, yNode, Sink.init(tList(3))) val pNode = new XNode(s.p, Sink.init(tList(0)), qNode) replace(sink, pNode) } - def case2(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) { + def case2(sink: Sink, s: Segment, tList: Array[Trapezoid]) { val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2))) val pNode = new XNode(s.p, Sink.init(tList(0)), yNode) replace(sink, pNode) } - def case3(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) { + def case3(sink: Sink, s: Segment, tList: Array[Trapezoid]) { val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1))) replace(sink, yNode) } - def case4(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) { + def case4(sink: Sink, s: Segment, tList: Array[Trapezoid]) { val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1))) val qNode = new XNode(s.q, yNode, Sink.init(tList(2))) replace(sink, qNode) } + } diff --git a/src/org/poly2tri/Sink.scala b/src/org/poly2tri/Sink.scala index 805f377..fa6e677 100644 --- a/src/org/poly2tri/Sink.scala +++ b/src/org/poly2tri/Sink.scala @@ -33,11 +33,10 @@ package org.poly2tri object Sink { def init(trapezoid: Trapezoid) = { - if(trapezoid.sink != null) { + if(trapezoid.sink != null) trapezoid.sink - } else { + else new Sink(trapezoid) - } } } diff --git a/src/org/poly2tri/TrapezoidalMap.scala b/src/org/poly2tri/TrapezoidalMap.scala index 0a81b1f..dc8e661 100644 --- a/src/org/poly2tri/TrapezoidalMap.scala +++ b/src/org/poly2tri/TrapezoidalMap.scala @@ -30,7 +30,7 @@ */ package org.poly2tri -import scala.collection.mutable.{Map, HashSet, ArrayBuffer} +import scala.collection.mutable.{HashSet, ArrayBuffer} // See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2 @@ -70,11 +70,11 @@ class TrapezoidalMap { assert(s.p.x != s.q.x) assert(s.p.x < s.q.x) - val trapezoids = new ArrayBuffer[Trapezoid] - trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom) - trapezoids += new Trapezoid(s.p, s.q, t.top, s) - trapezoids += new Trapezoid(s.p, s.q, s, t.bottom) - trapezoids += new Trapezoid(s.q, t.rightPoint, t.top, t.bottom) + val trapezoids = new Array[Trapezoid](4) + trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom) + trapezoids(1) = new Trapezoid(s.p, s.q, t.top, s) + trapezoids(2) = new Trapezoid(s.p, s.q, s, t.bottom) + trapezoids(3) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom) trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2)) trapezoids(1).update(trapezoids(0), null, trapezoids(3), null) @@ -93,10 +93,10 @@ class TrapezoidalMap { assert(s.p.x < s.q.x) - val trapezoids = new ArrayBuffer[Trapezoid] - trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom) - trapezoids += new Trapezoid(s.p, t.rightPoint, t.top, s) - trapezoids += new Trapezoid(s.p, t.rightPoint, s, t.bottom) + val trapezoids = new Array[Trapezoid](3) + trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom) + trapezoids(1) = new Trapezoid(s.p, t.rightPoint, t.top, s) + trapezoids(2) = new Trapezoid(s.p, t.rightPoint, s, t.bottom) trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2)) trapezoids(1).update(trapezoids(0), null, t.upperRight, null) @@ -119,9 +119,9 @@ class TrapezoidalMap { val topCross = (tCross == t.top) val bottomCross = (bCross == t.bottom) - val trapezoids = new ArrayBuffer[Trapezoid] - trapezoids += {if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, t.rightPoint, t.top, s)} - trapezoids += {if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, t.rightPoint, s, t.bottom)} + val trapezoids = new Array[Trapezoid](2) + trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, t.rightPoint, t.top, s) + trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, t.rightPoint, s, t.bottom) if(topCross) { trapezoids(0).upperRight = t.upperRight @@ -156,10 +156,10 @@ class TrapezoidalMap { val topCross = (tCross == t.top) val bottomCross = (bCross == t.bottom) - val trapezoids = new ArrayBuffer[Trapezoid] - trapezoids += {if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, s.q, t.top, s)} - trapezoids += {if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, s.q, s, t.bottom)} - trapezoids += new Trapezoid(s.q, t.rightPoint, t.top, t.bottom) + val trapezoids = new Array[Trapezoid](3) + trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, s.q, t.top, s) + trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, s.q, s, t.bottom) + trapezoids(2) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom) if(topCross) { trapezoids(0).upperRight = trapezoids(2) diff --git a/src/org/poly2tri/Triangulator.scala b/src/org/poly2tri/Triangulator.scala index 5b842e3..6134c4e 100644 --- a/src/org/poly2tri/Triangulator.scala +++ b/src/org/poly2tri/Triangulator.scala @@ -40,17 +40,17 @@ class Triangulator(segments: ArrayBuffer[Segment]) { var triangles = new ArrayBuffer[Array[Point]] // Order and randomize the segments - val segs = orderSegments + val segmentList = orderSegments // Build the trapezoidal map and query graph def process { - for(s <- segs) { - val traps = queryGraph.followSegment(s) + for(s <- segmentList) { + var traps = queryGraph.followSegment(s) // Remove trapezoids from trapezoidal Map traps.foreach(trapezoidalMap.remove) for(t <- traps) { - var tList: ArrayBuffer[Trapezoid] = null + var tList: Array[Trapezoid] = null val containsP = t.contains(s.p) val containsQ = t.contains(s.q) if(containsP && containsQ) { @@ -75,7 +75,7 @@ class Triangulator(segments: ArrayBuffer[Segment]) { } trapezoidalMap reset } - + // Mark outside trapezoids trapezoidalMap.map.foreach(markOutside) @@ -93,13 +93,14 @@ class Triangulator(segments: ArrayBuffer[Segment]) { for(t <- xMonoPoly(i).triangles) triangles += t - println("# triangles = " + triangles.size) + //println("# triangles = " + triangles.size) } // The trapezoidal map def trapezoidMap = trapezoidalMap.map // Trapezoid decomposition list var trapezoids = new ArrayBuffer[Trapezoid] + // Monotone polygons - these are monotone mountains def monoPolies: ArrayBuffer[ArrayBuffer[Point]] = { val polies = new ArrayBuffer[ArrayBuffer[Point]] @@ -116,15 +117,15 @@ class Triangulator(segments: ArrayBuffer[Segment]) { // Build a list of x-monotone mountains private def createMountains { - for(s <- segments) { + for(s <- segmentList) { + if(s.mPoints.size > 0) { val mountain = new MonotoneMountain val k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList) val points = s.p :: k ::: List(s.q) points.foreach(p => mountain += p.clone) - if(mountain.size > 2) { - mountain.triangulate - xMonoPoly += mountain - } + mountain.triangulate + xMonoPoly += mountain + } } } @@ -141,15 +142,14 @@ class Triangulator(segments: ArrayBuffer[Segment]) { for(s <- segments) // Point p must be to the left of point q if(s.p.x > s.q.x) { - val tmp = s.p - s.p = s.q - s.q = tmp - segs += s + segs += new Segment(s.q.clone, s.p.clone) } else if(s.p.x < s.q.x) - segs += s + segs += new Segment(s.p.clone, s.q.clone) // Randomized triangulation improves performance // See Seidel's paper, or O'Rourke's book, p. 57 - // Turn this off for now because of stupid pointer bug somewhere! + // Turn this off for now because of pointer bug somewhere in DAG? + // The solution to this bug may be more apparent with a better Trapezoidal Map + // data structure... Maybe a modified doubly connected edge list? //Random.shuffle(segs) segs } diff --git a/src/org/poly2tri/YNode.scala b/src/org/poly2tri/YNode.scala index ef331e9..fc0266a 100644 --- a/src/org/poly2tri/YNode.scala +++ b/src/org/poly2tri/YNode.scala @@ -40,7 +40,7 @@ class YNode(segment: Segment, lChild: Node, rChild: Node) extends Node(lChild, r // Move up the graph left.locate(s) } else { - // s and segment share the same endpoint + // s and segment share the same endpoint, p if (s.slope < segment.slope) { // Move down the graph return right.locate(s)