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 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
}
}

View File

@ -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))
}

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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 <code>Random</code> offers a default implementation

View File

@ -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 {