From bdbef0e93e4da1aef8aede0c242a26a29da3d603 Mon Sep 17 00:00:00 2001 From: zzzzrrr Date: Wed, 22 Jul 2009 10:22:51 -0400 Subject: [PATCH] optimizations --- src/org/poly2tri/MonoToneMountain.scala | 14 +++---- src/org/poly2tri/Segment.scala | 13 ++----- src/org/poly2tri/Sink.scala | 1 - src/org/poly2tri/Trapezoid.scala | 25 +++++++++--- src/org/poly2tri/TrapezoidalMap.scala | 40 +++++++------------ src/org/poly2tri/Triangulator.scala | 52 +++++++++++++++---------- src/org/poly2tri/Util.scala | 37 +++++++----------- src/org/poly2tri/YNode.scala | 7 +--- 8 files changed, 90 insertions(+), 99 deletions(-) diff --git a/src/org/poly2tri/MonoToneMountain.scala b/src/org/poly2tri/MonoToneMountain.scala index f81669d..4e02f77 100644 --- a/src/org/poly2tri/MonoToneMountain.scala +++ b/src/org/poly2tri/MonoToneMountain.scala @@ -38,15 +38,15 @@ class MonotoneMountain { var tail, head: Point = null var size = 0 - val convexPoints = new ArrayBuffer[Point] + private val convexPoints = new ArrayBuffer[Point] // Monotone mountain points val monoPoly = new ArrayBuffer[Point] // 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 + private var positive = false // Almost Pi! - val PI_SLOP = 3.1 + private val PI_SLOP = 3.1 // Append a point to the list def +=(point: Point) { @@ -123,7 +123,7 @@ class MonotoneMountain { } - def valid(p: Point) = (p != head && p != tail && convex(p)) + private def valid(p: Point) = (p != head && p != tail && convex(p)) // Create the monotone polygon private def genMonoPoly { @@ -134,13 +134,13 @@ class MonotoneMountain { } } - def angle(p: Point) = { + private def angle(p: Point) = { val a = (p.next - p) val b = (p.prev - p) Math.atan2(a cross b, a dot b) } - def angleSign = { + private def angleSign = { val a = (head.next - head) val b = (tail - head) (Math.atan2(a cross b, a dot b) >= 0) @@ -149,7 +149,7 @@ class MonotoneMountain { // Determines if the inslide angle is convex or reflex private def convex(p: Point) = { if(positive != (angle(p) >= 0)) false - else true + else true } } diff --git a/src/org/poly2tri/Segment.scala b/src/org/poly2tri/Segment.scala index abe6088..303b695 100644 --- a/src/org/poly2tri/Segment.scala +++ b/src/org/poly2tri/Segment.scala @@ -37,14 +37,9 @@ class Segment(var p: Point, var q: Point) { // Pointers used for building trapezoidal map var above, below: Trapezoid = null - - // This can be adjusted accordingly - val MAX_MPOINTS = 20 // Montone mountain points - val mPoints = new Array[Point](MAX_MPOINTS) - // mPoints index counter - var np = 0 - + val mPoints = new ArrayBuffer[Point] + // Equation of a line: y = m*x + b // Slope of the line (m) val slope = (q.y - p.y)/(q.x - p.x) @@ -52,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 - @inline def > (point: Point) = (point.y < Math.round(slope * point.x + b)) + def > (point: Point) = (point.y < Math.round(slope * point.x + b)) // Determines if this segment lies below the given point - @inline def < (point: Point) = (point.y > Math.round(slope * point.x + b)) + def < (point: Point) = (point.y > Math.round(slope * point.x + b)) } diff --git a/src/org/poly2tri/Sink.scala b/src/org/poly2tri/Sink.scala index a2c921f..fa6e677 100644 --- a/src/org/poly2tri/Sink.scala +++ b/src/org/poly2tri/Sink.scala @@ -34,7 +34,6 @@ object Sink { def init(trapezoid: Trapezoid) = { if(trapezoid.sink != null) - // What about adding to the parent list? trapezoid.sink else new Sink(trapezoid) diff --git a/src/org/poly2tri/Trapezoid.scala b/src/org/poly2tri/Trapezoid.scala index 3eb8e59..31ba580 100644 --- a/src/org/poly2tri/Trapezoid.scala +++ b/src/org/poly2tri/Trapezoid.scala @@ -41,14 +41,27 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v var upperRight: Trapezoid = null var lowerRight: Trapezoid = null - def update(ul: Trapezoid, ll: Trapezoid, ur: Trapezoid, lr: Trapezoid) { + // Update neighbors to the left + def updateLeft(ul: Trapezoid, ll: Trapezoid) { + upperLeft = ul; if(ul != null) ul.upperRight = this + lowerLeft = ll; if(ll != null) ll.lowerRight = this + } + + // Update neighbors to the right + def updateRight(ur: Trapezoid, lr: Trapezoid) { + upperRight = ur; if(ur != null) ur.upperLeft = this + lowerRight = lr; if(lr != null) lr.lowerLeft = this + } + + // Update neighbors on both sides + def updateLeftRight(ul: Trapezoid, ll: Trapezoid, ur: Trapezoid, lr: Trapezoid) { upperLeft = ul; if(ul != null) ul.upperRight = this lowerLeft = ll; if(ll != null) ll.lowerRight = this upperRight = ur; if(ur != null) ur.upperLeft = this lowerRight = lr; if(lr != null) lr.lowerLeft = this } - // Recursively trim neightbors + // Recursively trim outside neighbors def trimNeighbors { if(inside) { inside = false @@ -80,10 +93,10 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v // Add points to monotone mountain def addPoints { - if(leftPoint != bottom.p) {bottom.mPoints(bottom.np) = leftPoint.clone; bottom.np += 1} - if(rightPoint != bottom.q) {bottom.mPoints(bottom.np) = rightPoint.clone; bottom.np += 1} - if(leftPoint != top.p) {top.mPoints(top.np) = leftPoint.clone; top.np += 1} - if(rightPoint != top.q) {top.mPoints(top.np) = rightPoint.clone; top.np += 1} + if(leftPoint != bottom.p) {bottom.mPoints += leftPoint.clone} + if(rightPoint != bottom.q) {bottom.mPoints += rightPoint.clone} + if(leftPoint != top.p) {top.mPoints += leftPoint.clone} + if(rightPoint != top.q) {top.mPoints += rightPoint.clone} } def debugData { diff --git a/src/org/poly2tri/TrapezoidalMap.scala b/src/org/poly2tri/TrapezoidalMap.scala index 34cd8a9..d3b251b 100644 --- a/src/org/poly2tri/TrapezoidalMap.scala +++ b/src/org/poly2tri/TrapezoidalMap.scala @@ -46,19 +46,7 @@ class TrapezoidalMap { // Top segment that spans multiple trapezoids private var tCross: Segment = null - // Add a trapezoid to the map - def add(t: Trapezoid) { - assert(t != null, "Bad value") - map += t - } - - // Remove a trapezoid from the map - def remove(t: Trapezoid) { - assert(t != null, "Bad value") - map -=t - } - - def reset { + def clear { bCross = null tCross = null } @@ -73,10 +61,10 @@ class TrapezoidalMap { 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) - trapezoids(2).update(null, trapezoids(0), null, trapezoids(3)) - trapezoids(3).update(trapezoids(1), trapezoids(2), t.upperRight, t.lowerRight) + trapezoids(0).updateLeft(t.upperLeft, t.lowerLeft) + trapezoids(1).updateLeftRight(trapezoids(0), null, trapezoids(3), null) + trapezoids(2).updateLeftRight(null, trapezoids(0), null, trapezoids(3)) + trapezoids(3).updateRight(t.upperRight, t.lowerRight) trapezoids } @@ -92,9 +80,9 @@ class TrapezoidalMap { trapezoids(1) = new Trapezoid(s.p, rp, t.top, s) trapezoids(2) = new Trapezoid(s.p, rp, s, t.bottom) - trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2)) - trapezoids(1).update(trapezoids(0), null, t.upperRight, null) - trapezoids(2).update(null, trapezoids(0), null, t.lowerRight) + trapezoids(0).updateLeft(t.upperLeft, t.lowerLeft) + trapezoids(1).updateLeftRight(trapezoids(0), null, t.upperRight, null) + trapezoids(2).updateLeftRight(null, trapezoids(0), null, t.lowerRight) bCross = t.bottom tCross = t.top @@ -122,7 +110,7 @@ class TrapezoidalMap { if(t.upperRight != null) t.upperRight.upperLeft = trapezoids(0) trapezoids(0).rightPoint = rp } else { - trapezoids(0).update(t.upperLeft, s.above, t.upperRight, null) + trapezoids(0).updateLeftRight(t.upperLeft, s.above, t.upperRight, null) } if(bottomCross) { @@ -130,7 +118,7 @@ class TrapezoidalMap { if(t.lowerRight != null) t.lowerRight.lowerLeft = trapezoids(1) trapezoids(1).rightPoint = rp } else { - trapezoids(1).update(s.below, t.lowerLeft, null, t.lowerRight) + trapezoids(1).updateLeftRight(s.below, t.lowerLeft, null, t.lowerRight) } bCross = t.bottom @@ -156,20 +144,18 @@ class TrapezoidalMap { trapezoids(2) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom) if(topCross) { - trapezoids(0).upperRight = trapezoids(2) trapezoids(0).rightPoint = s.q } else { - trapezoids(0).update(t.upperLeft, s.above, trapezoids(2), null) + trapezoids(0).updateLeft(t.upperLeft, s.above) } if(bottomCross) { - trapezoids(1).lowerRight = trapezoids(2) trapezoids(1).rightPoint = s.q } else { - trapezoids(1).update(s.below, t.lowerLeft, null, trapezoids(2)) + trapezoids(1).updateLeft(s.below, t.lowerLeft) } - trapezoids(2).update(trapezoids(0), trapezoids(1), t.upperRight, t.lowerRight) + trapezoids(2).updateLeftRight(trapezoids(0), trapezoids(1), t.upperRight, t.lowerRight) s.above = trapezoids(0) s.below = trapezoids(1) diff --git a/src/org/poly2tri/Triangulator.scala b/src/org/poly2tri/Triangulator.scala index 5508f0d..d4b610b 100644 --- a/src/org/poly2tri/Triangulator.scala +++ b/src/org/poly2tri/Triangulator.scala @@ -51,20 +51,21 @@ class Triangulator(segments: ArrayBuffer[Segment]) { def process { val t1 = System.nanoTime - var i = 0 + var i = 0 while(i < segmentList.size) { val s = segmentList(i) + var traps = queryGraph.followSegment(s) // Remove trapezoids from trapezoidal Map var j = 0 while(j < traps.size) { - trapezoidalMap.remove(traps(j)) + trapezoidalMap.map -= traps(j) j += 1 } - + j = 0 while(j < traps.size) { val t = traps(j) @@ -88,17 +89,19 @@ class Triangulator(segments: ArrayBuffer[Segment]) { tList = trapezoidalMap.case4(t, s) queryGraph.case4(t.sink, s, tList) } + // Add new trapezoids to map var k = 0 while(k < tList.size) { - trapezoidalMap.add(tList(k)) + trapezoidalMap.map += tList(k) k += 1 } j += 1 } - trapezoidalMap.reset + trapezoidalMap.clear i += 1 + } coreTime = System.nanoTime - t1 @@ -117,15 +120,6 @@ class Triangulator(segments: ArrayBuffer[Segment]) { // Generate the triangles createMountains - // Extract all the triangles into a single list - for(i <- 0 until xMonoPoly.size) { - var j = 0 - while(j < xMonoPoly(i).triangles.size) { - triangles += xMonoPoly(i).triangles(j) - j += 1 - } - } - //println("# triangles = " + triangles.size) } @@ -150,29 +144,47 @@ class Triangulator(segments: ArrayBuffer[Segment]) { // Build a list of x-monotone mountains private def createMountains { + var i = 0 while(i < segmentList.size) { + val s = segmentList(i) - if(s.np > 0) { + + if(s.mPoints.size > 0) { + val mountain = new MonotoneMountain var k: List[Point] = null - val tmp = new Array[Point](s.np) - for(i <- 0 until s.np) tmp(i) = s.mPoints(i) - if(s.np < 10) + + // Sorting is a perfromance hit. Literature says this can be accomplised in + // linear time, although I don't see a way around using traditional methods + // when using a randomized incremental algorithm + if(s.mPoints.size < 10) // Insertion sort is one of the fastest algorithms for sorting arrays containing // fewer than ten elements, or for lists that are already mostly sorted. - k = Util.insertionSort(tmp.toList, {(x1, x2) => x1 <= x2} ) + k = Util.insertSort(s.mPoints).toList else - k = Util.msort((p1: Point, p2: Point) => p1 < p2)(tmp.toList) + k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList) + val points = s.p :: k ::: List(s.q) + var j = 0 while(j < points.size) { mountain += points(j) j += 1 } + + // Triangulate monotone mountain val t1 = System.nanoTime mountain.triangulate coreTime += System.nanoTime - t1 + + // Extract the triangles into a single list + j = 0 + while(j < mountain.triangles.size) { + triangles += mountain.triangles(j) + j += 1 + } + xMonoPoly += mountain } i += 1 diff --git a/src/org/poly2tri/Util.scala b/src/org/poly2tri/Util.scala index 7650aa0..f246f92 100644 --- a/src/org/poly2tri/Util.scala +++ b/src/org/poly2tri/Util.scala @@ -17,29 +17,20 @@ object Util { else merge(msort(less)(xs take n), msort(less)(xs drop n)) } - def insertionSort(values : List[Point], matcher : (Float, Float) => Boolean) : List[Point] = { - def iSort(values : List[Point]) : List[Point] = { - val result = values match { - case List() => List() - case value :: valuesTail => insert(value, iSort(valuesTail)) - } - result - } - def insert(value : Point, values : List[Point]) : List[Point] = { - val result = values match { - // if list is empty return new list with single element in it - case List() => List(value) - // otherwise insert into list in order, recursively - case x :: xTail => - if (matcher(value.x, x.x)) - value :: values - else - x :: insert(value, xTail) - } - result - } - iSort(values) - } + def insertSort(list:ArrayBuffer[Point]) = { + var j = 1 + while(j < list.size){ + val key = list(j) + var i = j-1 + while(i>=0 && list(i).x > key.x){ + list(i+1) = list(i) + i=i-1 + } + list(i+1)=key + j=j+1 + } + list + } } /** The object Random offers a default implementation diff --git a/src/org/poly2tri/YNode.scala b/src/org/poly2tri/YNode.scala index d548966..5e8f45a 100644 --- a/src/org/poly2tri/YNode.scala +++ b/src/org/poly2tri/YNode.scala @@ -33,20 +33,15 @@ package org.poly2tri class YNode(segment: Segment, lChild: Node, rChild: Node) extends Node(lChild, rChild) { override def locate(s: Segment): Sink = { - //println(s.p.y) - //println(Math.round(segment.slope * s.p.x + segment.b)) if (segment > s.p) { // Move down the graph return right.locate(s) } else if (segment < s.p) { - //println("*****") - //println(s.p.y) - //println(Math.round(segment.slope * s.p.x + segment.b)) // Move up the graph return left.locate(s) } else { // s and segment share the same endpoint, p - if (Math.round(s.slope) < Math.round(segment.slope)) { + if (s.slope < segment.slope) { // Move down the graph return right.locate(s) } else {