optimizations

This commit is contained in:
zzzzrrr 2009-07-22 10:22:51 -04:00
parent f5f3fc3b42
commit bdbef0e93e
8 changed files with 90 additions and 99 deletions

View File

@ -38,15 +38,15 @@ class MonotoneMountain {
var tail, head: Point = null var tail, head: Point = null
var size = 0 var size = 0
val convexPoints = new ArrayBuffer[Point] private val convexPoints = new ArrayBuffer[Point]
// Monotone mountain points // Monotone mountain points
val monoPoly = new ArrayBuffer[Point] val monoPoly = new ArrayBuffer[Point]
// Triangles that constitute the mountain // Triangles that constitute the mountain
val triangles = new ArrayBuffer[Array[Point]] val triangles = new ArrayBuffer[Array[Point]]
// Used to track which side of the line we are on // Used to track which side of the line we are on
var positive = false private var positive = false
// Almost Pi! // Almost Pi!
val PI_SLOP = 3.1 private val PI_SLOP = 3.1
// Append a point to the list // Append a point to the list
def +=(point: Point) { 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 // Create the monotone polygon
private def genMonoPoly { private def genMonoPoly {
@ -134,13 +134,13 @@ class MonotoneMountain {
} }
} }
def angle(p: Point) = { private def angle(p: Point) = {
val a = (p.next - p) val a = (p.next - p)
val b = (p.prev - p) val b = (p.prev - p)
Math.atan2(a cross b, a dot b) Math.atan2(a cross b, a dot b)
} }
def angleSign = { private def angleSign = {
val a = (head.next - head) val a = (head.next - head)
val b = (tail - head) val b = (tail - head)
(Math.atan2(a cross b, a dot b) >= 0) (Math.atan2(a cross b, a dot b) >= 0)

View File

@ -37,13 +37,8 @@ class Segment(var p: Point, var q: Point) {
// Pointers used for building trapezoidal map // Pointers used for building trapezoidal map
var above, below: Trapezoid = null var above, below: Trapezoid = null
// This can be adjusted accordingly
val MAX_MPOINTS = 20
// Montone mountain points // Montone mountain points
val mPoints = new Array[Point](MAX_MPOINTS) val mPoints = new ArrayBuffer[Point]
// mPoints index counter
var np = 0
// Equation of a line: y = m*x + b // Equation of a line: y = m*x + b
// Slope of the line (m) // Slope of the line (m)
@ -52,8 +47,8 @@ class Segment(var p: Point, var q: Point) {
val b = p.y - (p.x * slope) val b = p.y - (p.x * slope)
// Determines if this segment lies above the given point // 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 // 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))
} }

View File

@ -34,7 +34,6 @@ object Sink {
def init(trapezoid: Trapezoid) = { def init(trapezoid: Trapezoid) = {
if(trapezoid.sink != null) if(trapezoid.sink != null)
// What about adding to the parent list?
trapezoid.sink trapezoid.sink
else else
new Sink(trapezoid) new Sink(trapezoid)

View File

@ -41,14 +41,27 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
var upperRight: Trapezoid = null var upperRight: Trapezoid = null
var lowerRight: 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 upperLeft = ul; if(ul != null) ul.upperRight = this
lowerLeft = ll; if(ll != null) ll.lowerRight = this lowerLeft = ll; if(ll != null) ll.lowerRight = this
upperRight = ur; if(ur != null) ur.upperLeft = this upperRight = ur; if(ur != null) ur.upperLeft = this
lowerRight = lr; if(lr != null) lr.lowerLeft = this lowerRight = lr; if(lr != null) lr.lowerLeft = this
} }
// Recursively trim neightbors // Recursively trim outside neighbors
def trimNeighbors { def trimNeighbors {
if(inside) { if(inside) {
inside = false inside = false
@ -80,10 +93,10 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
// Add points to monotone mountain // Add points to monotone mountain
def addPoints { def addPoints {
if(leftPoint != bottom.p) {bottom.mPoints(bottom.np) = leftPoint.clone; bottom.np += 1} if(leftPoint != bottom.p) {bottom.mPoints += leftPoint.clone}
if(rightPoint != bottom.q) {bottom.mPoints(bottom.np) = rightPoint.clone; bottom.np += 1} if(rightPoint != bottom.q) {bottom.mPoints += rightPoint.clone}
if(leftPoint != top.p) {top.mPoints(top.np) = leftPoint.clone; top.np += 1} if(leftPoint != top.p) {top.mPoints += leftPoint.clone}
if(rightPoint != top.q) {top.mPoints(top.np) = rightPoint.clone; top.np += 1} if(rightPoint != top.q) {top.mPoints += rightPoint.clone}
} }
def debugData { def debugData {

View File

@ -46,19 +46,7 @@ class TrapezoidalMap {
// Top segment that spans multiple trapezoids // Top segment that spans multiple trapezoids
private var tCross: Segment = null private var tCross: Segment = null
// Add a trapezoid to the map def clear {
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 {
bCross = null bCross = null
tCross = null tCross = null
} }
@ -73,10 +61,10 @@ class TrapezoidalMap {
trapezoids(2) = new Trapezoid(s.p, s.q, s, t.bottom) trapezoids(2) = new Trapezoid(s.p, s.q, s, t.bottom)
trapezoids(3) = new Trapezoid(s.q, t.rightPoint, t.top, 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(0).updateLeft(t.upperLeft, t.lowerLeft)
trapezoids(1).update(trapezoids(0), null, trapezoids(3), null) trapezoids(1).updateLeftRight(trapezoids(0), null, trapezoids(3), null)
trapezoids(2).update(null, trapezoids(0), null, trapezoids(3)) trapezoids(2).updateLeftRight(null, trapezoids(0), null, trapezoids(3))
trapezoids(3).update(trapezoids(1), trapezoids(2), t.upperRight, t.lowerRight) trapezoids(3).updateRight(t.upperRight, t.lowerRight)
trapezoids trapezoids
} }
@ -92,9 +80,9 @@ class TrapezoidalMap {
trapezoids(1) = new Trapezoid(s.p, rp, t.top, s) trapezoids(1) = new Trapezoid(s.p, rp, t.top, s)
trapezoids(2) = new Trapezoid(s.p, rp, s, t.bottom) trapezoids(2) = new Trapezoid(s.p, rp, s, t.bottom)
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2)) trapezoids(0).updateLeft(t.upperLeft, t.lowerLeft)
trapezoids(1).update(trapezoids(0), null, t.upperRight, null) trapezoids(1).updateLeftRight(trapezoids(0), null, t.upperRight, null)
trapezoids(2).update(null, trapezoids(0), null, t.lowerRight) trapezoids(2).updateLeftRight(null, trapezoids(0), null, t.lowerRight)
bCross = t.bottom bCross = t.bottom
tCross = t.top tCross = t.top
@ -122,7 +110,7 @@ class TrapezoidalMap {
if(t.upperRight != null) t.upperRight.upperLeft = trapezoids(0) if(t.upperRight != null) t.upperRight.upperLeft = trapezoids(0)
trapezoids(0).rightPoint = rp trapezoids(0).rightPoint = rp
} else { } else {
trapezoids(0).update(t.upperLeft, s.above, t.upperRight, null) trapezoids(0).updateLeftRight(t.upperLeft, s.above, t.upperRight, null)
} }
if(bottomCross) { if(bottomCross) {
@ -130,7 +118,7 @@ class TrapezoidalMap {
if(t.lowerRight != null) t.lowerRight.lowerLeft = trapezoids(1) if(t.lowerRight != null) t.lowerRight.lowerLeft = trapezoids(1)
trapezoids(1).rightPoint = rp trapezoids(1).rightPoint = rp
} else { } else {
trapezoids(1).update(s.below, t.lowerLeft, null, t.lowerRight) trapezoids(1).updateLeftRight(s.below, t.lowerLeft, null, t.lowerRight)
} }
bCross = t.bottom bCross = t.bottom
@ -156,20 +144,18 @@ class TrapezoidalMap {
trapezoids(2) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom) trapezoids(2) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
if(topCross) { if(topCross) {
trapezoids(0).upperRight = trapezoids(2)
trapezoids(0).rightPoint = s.q trapezoids(0).rightPoint = s.q
} else { } else {
trapezoids(0).update(t.upperLeft, s.above, trapezoids(2), null) trapezoids(0).updateLeft(t.upperLeft, s.above)
} }
if(bottomCross) { if(bottomCross) {
trapezoids(1).lowerRight = trapezoids(2)
trapezoids(1).rightPoint = s.q trapezoids(1).rightPoint = s.q
} else { } 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.above = trapezoids(0)
s.below = trapezoids(1) s.below = trapezoids(1)

View File

@ -51,17 +51,18 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
def process { def process {
val t1 = System.nanoTime val t1 = System.nanoTime
var i = 0
var i = 0
while(i < segmentList.size) { while(i < segmentList.size) {
val s = segmentList(i) val s = segmentList(i)
var traps = queryGraph.followSegment(s) var traps = queryGraph.followSegment(s)
// Remove trapezoids from trapezoidal Map // Remove trapezoids from trapezoidal Map
var j = 0 var j = 0
while(j < traps.size) { while(j < traps.size) {
trapezoidalMap.remove(traps(j)) trapezoidalMap.map -= traps(j)
j += 1 j += 1
} }
@ -88,17 +89,19 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
tList = trapezoidalMap.case4(t, s) tList = trapezoidalMap.case4(t, s)
queryGraph.case4(t.sink, s, tList) queryGraph.case4(t.sink, s, tList)
} }
// Add new trapezoids to map // Add new trapezoids to map
var k = 0 var k = 0
while(k < tList.size) { while(k < tList.size) {
trapezoidalMap.add(tList(k)) trapezoidalMap.map += tList(k)
k += 1 k += 1
} }
j += 1 j += 1
} }
trapezoidalMap.reset trapezoidalMap.clear
i += 1 i += 1
} }
coreTime = System.nanoTime - t1 coreTime = System.nanoTime - t1
@ -117,15 +120,6 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
// Generate the triangles // Generate the triangles
createMountains 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) //println("# triangles = " + triangles.size)
} }
@ -150,29 +144,47 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
// Build a list of x-monotone mountains // Build a list of x-monotone mountains
private def createMountains { private def createMountains {
var i = 0 var i = 0
while(i < segmentList.size) { while(i < segmentList.size) {
val s = segmentList(i) val s = segmentList(i)
if(s.np > 0) {
if(s.mPoints.size > 0) {
val mountain = new MonotoneMountain val mountain = new MonotoneMountain
var k: List[Point] = null var k: List[Point] = null
val tmp = new Array[Point](s.np)
for(i <- 0 until s.np) tmp(i) = s.mPoints(i) // Sorting is a perfromance hit. Literature says this can be accomplised in
if(s.np < 10) // 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 // Insertion sort is one of the fastest algorithms for sorting arrays containing
// fewer than ten elements, or for lists that are already mostly sorted. // 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 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) val points = s.p :: k ::: List(s.q)
var j = 0 var j = 0
while(j < points.size) { while(j < points.size) {
mountain += points(j) mountain += points(j)
j += 1 j += 1
} }
// Triangulate monotone mountain
val t1 = System.nanoTime val t1 = System.nanoTime
mountain.triangulate mountain.triangulate
coreTime += System.nanoTime - t1 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 xMonoPoly += mountain
} }
i += 1 i += 1

View File

@ -17,28 +17,19 @@ object Util {
else merge(msort(less)(xs take n), msort(less)(xs drop n)) else merge(msort(less)(xs take n), msort(less)(xs drop n))
} }
def insertionSort(values : List[Point], matcher : (Float, Float) => Boolean) : List[Point] = { def insertSort(list:ArrayBuffer[Point]) = {
def iSort(values : List[Point]) : List[Point] = { var j = 1
val result = values match { while(j < list.size){
case List() => List() val key = list(j)
case value :: valuesTail => insert(value, iSort(valuesTail)) var i = j-1
while(i>=0 && list(i).x > key.x){
list(i+1) = list(i)
i=i-1
} }
result list(i+1)=key
j=j+1
} }
def insert(value : Point, values : List[Point]) : List[Point] = { list
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)
} }
} }

View File

@ -33,20 +33,15 @@ package org.poly2tri
class YNode(segment: Segment, lChild: Node, rChild: Node) extends Node(lChild, rChild) { class YNode(segment: Segment, lChild: Node, rChild: Node) extends Node(lChild, rChild) {
override def locate(s: Segment): Sink = { override def locate(s: Segment): Sink = {
//println(s.p.y)
//println(Math.round(segment.slope * s.p.x + segment.b))
if (segment > s.p) { if (segment > s.p) {
// Move down the graph // Move down the graph
return right.locate(s) return right.locate(s)
} else if (segment < s.p) { } else if (segment < s.p) {
//println("*****")
//println(s.p.y)
//println(Math.round(segment.slope * s.p.x + segment.b))
// Move up the graph // Move up the graph
return left.locate(s) return left.locate(s)
} else { } else {
// s and segment share the same endpoint, p // 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 // Move down the graph
return right.locate(s) return right.locate(s)
} else { } else {