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