mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-12-28 13:43:30 +01:00
optimizations
This commit is contained in:
parent
f5f3fc3b42
commit
bdbef0e93e
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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))
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user