This commit is contained in:
zzzzrrr 2009-07-30 18:01:59 -04:00
parent 2cf811d635
commit 81265ea832
5 changed files with 191 additions and 51 deletions

View File

@ -55,12 +55,17 @@ object Poly2Tri {
class Poly2TriDemo extends BasicGame("Poly2Tri") { class Poly2TriDemo extends BasicGame("Poly2Tri") {
var tesselator: Triangulator = null // Sedidel Triangulator
var seidel: Triangulator = null
var segments: ArrayBuffer[Segment] = null var segments: ArrayBuffer[Segment] = null
// EarClip Triangulator
val earClip = new EarClip val earClip = new EarClip
var earClipResults: Array[poly2tri.earClip.Triangle] = null var earClipResults: Array[poly2tri.earClip.Triangle] = null
// Sweep Line Constraied Delauney Triangulator (CDT)
var slCDT: CDT = null
var polyX: ArrayBuffer[Float] = null var polyX: ArrayBuffer[Float] = null
var polyY: ArrayBuffer[Float] = null var polyY: ArrayBuffer[Float] = null
@ -71,6 +76,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
var hiLighter = 0 var hiLighter = 0
var drawEarClip = false var drawEarClip = false
var hertelMehlhorn = false var hertelMehlhorn = false
var drawCDT = false
val nazcaMonkey = "data/nazca_monkey.dat" val nazcaMonkey = "data/nazca_monkey.dat"
val bird = "data/bird.dat" val bird = "data/bird.dat"
@ -79,7 +85,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
val strange = "data/strange.dat" val strange = "data/strange.dat"
val i18 = "data/i.18" val i18 = "data/i.18"
var currentModel = nazcaMonkey var currentModel = star
def init(container: GameContainer) { def init(container: GameContainer) {
selectModel(currentModel) selectModel(currentModel)
@ -102,7 +108,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
val yellow = new Color(1f, 1f, 0f) val yellow = new Color(1f, 1f, 0f)
if(debug) { if(debug) {
val draw = if(drawMap) tesselator.trapezoidMap else tesselator.trapezoids val draw = if(drawMap) seidel.trapezoidMap else seidel.trapezoids
for(t <- draw) { for(t <- draw) {
val polygon = new Polygon() val polygon = new Polygon()
for(v <- t.vertices) { for(v <- t.vertices) {
@ -121,14 +127,14 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
if(!debug && !drawEarClip) { if(!debug && !drawEarClip) {
var i = 0 var i = 0
for(t <- tesselator.polygons) { for(t <- seidel.polygons) {
val poly = new Polygon val poly = new Polygon
t.foreach(p => poly.addPoint(p.x, p.y)) t.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(red) g.setColor(red)
g.draw(poly) g.draw(poly)
} }
} else if (debug && drawMap && !drawEarClip){ } else if (debug && drawMap && !drawEarClip){
for(mp <- tesselator.monoPolies) { for(mp <- seidel.monoPolies) {
val poly = new Polygon val poly = new Polygon
mp.foreach(p => poly.addPoint(p.x, p.y)) mp.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(yellow) g.setColor(yellow)
@ -146,6 +152,17 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
g.draw(triangle) g.draw(triangle)
}) })
if(drawCDT) {
slCDT.mesh.map.foreach( t => {
val triangle = new Polygon
triangle.addPoint(t.points(0).x, t.points(0).y)
triangle.addPoint(t.points(1).x, t.points(1).y)
triangle.addPoint(t.points(2).x, t.points(2).y)
g.setColor(red)
g.draw(triangle)
})
}
if(drawSegs) { if(drawSegs) {
g.setColor(green) g.setColor(green)
for(i <- 0 until segments.size) { for(i <- 0 until segments.size) {
@ -165,16 +182,17 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
// UP // UP
if(key == 200) { if(key == 200) {
hiLighter += 1 hiLighter += 1
if (hiLighter == tesselator.polygons.size) if (hiLighter == seidel.polygons.size)
hiLighter = 0 hiLighter = 0
} }
// DOWN // DOWN
if(key == 208) { if(key == 208) {
hiLighter -= 1 hiLighter -= 1
if (hiLighter == -1) if (hiLighter == -1)
hiLighter = tesselator.polygons.size-1 hiLighter = seidel.polygons.size-1
} }
if(c == 'm') drawMap = !drawMap if(c == 'm') drawMap = !drawMap
//if(c == 'c') drawCDT = !drawCDT
if(c == '1') selectModel(nazcaMonkey) if(c == '1') selectModel(nazcaMonkey)
if(c == '2') selectModel(bird) if(c == '2') selectModel(bird)
if(c == '3') selectModel(strange) if(c == '3') selectModel(strange)
@ -235,7 +253,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
segments += new Segment(points(i), points(i+1)) segments += new Segment(points(i), points(i+1))
segments += new Segment(points.first, points.last) segments += new Segment(points.first, points.last)
//val cdt = CDT.init(points) //slCDT = CDT.init(points)
println("Number of points = " + polyX.size) println("Number of points = " + polyX.size)
println println
@ -243,15 +261,15 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
if(!drawEarClip) { if(!drawEarClip) {
// Sediel triangulation // Sediel triangulation
tesselator = new Triangulator(segments) seidel = new Triangulator(segments)
tesselator.buildTriangles = hertelMehlhorn seidel.buildTriangles = hertelMehlhorn
val t1 = System.nanoTime val t1 = System.nanoTime
tesselator.process seidel.process
val runTime = System.nanoTime - t1 val runTime = System.nanoTime - t1
println("Poly2Tri average (ms) = " + runTime*1e-6) println("Poly2Tri average (ms) = " + runTime*1e-6)
println("Number of triangles = " + tesselator.polygons.size) println("Number of triangles = " + seidel.polygons.size)
} else { } else {

View File

@ -36,28 +36,61 @@ import shapes.{Point, Triangle}
class AFront(iTriangle: Triangle) { class AFront(iTriangle: Triangle) {
// Doubly linked list // Doubly linked list
// TODO: Replace with a Red-Black Tree for better performance
var head = new Node(iTriangle.points(1), iTriangle) var head = new Node(iTriangle.points(1), iTriangle)
var tail = new Node(iTriangle.points(2), iTriangle)
head.next = new Node(iTriangle.points(0), iTriangle) head.next = new Node(iTriangle.points(0), iTriangle)
var tail = new Node(iTriangle.points(2), null)
head.next.next = tail head.next.next = tail
tail.prev = head.next tail.prev = head.next
def locate(point: Point): Tuple2[List[Point], Triangle] = { def locate(point: Point): Node = {
var node = head var node = head
while(node != tail) { while(node != tail) {
if(point.x > node.point.x && point.x < node.next.point.x) if(point.x >= node.point.x && point.x <= node.next.point.x)
return (List(node.point, node.next.point), node.triangle) return node
node = node.next node = node.next
} }
null throw new Exception("Advancing front error")
} }
def +=(tuple: Tuple3[Point, Triangle, Node]) = {
val (point, triangle, nNode) = tuple
val node = new Node(point, triangle)
// Update pointer
nNode.triangle = triangle
// Insert new node into advancing front
nNode.next.prev = node
node.next = nNode.next
node.prev = nNode
nNode.next = node
node
}
def -=(tuple: Tuple3[Node, Node, Triangle]) {
val (node, kNode, triangle) = tuple
kNode.next.prev = node
node.next = kNode.next
node.triangle = triangle
}
def -==(tuple: Tuple3[Node, Node, Triangle]) {
val (node, kNode, triangle) = tuple
kNode.prev.next = node
node.prev = kNode.prev
node.prev.triangle = triangle
}
} }
class Node(val point: Point, val triangle: Triangle) { class Node(val point: Point, var triangle: Triangle) {
var next: Node = null var next: Node = null
var prev: Node = null var prev: Node = null
def printDebug {
println("Point: " + point)
if(triangle != null)
println("Triangle points: " + triangle.points)
}
} }

View File

@ -68,8 +68,11 @@ object CDT {
val segments = initSegments(points) val segments = initSegments(points)
val sortedPoints = pointSort(points) val sortedPoints = pointSort(points)
val initialTriangle = new Triangle(Array(sortedPoints(0), p1, p2), null)
new CDT(sortedPoints, segments, initialTriangle) val noNeighbors = new Array[Triangle](3)
val tPoints = Array(sortedPoints(0), p1, p2)
val iTriangle = new Triangle(tPoints, noNeighbors)
new CDT(sortedPoints, segments, iTriangle)
} }
// Create segments and connect end points; update edge event pointer // Create segments and connect end points; update edge event pointer
@ -89,11 +92,11 @@ object CDT {
// 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.
// Merge sort: O(n log n) // Merge sort: O(n log n)
private def pointSort(pts: ArrayBuffer[Point]): List[Point] = { private def pointSort(points: ArrayBuffer[Point]): List[Point] = {
if(pts.size < 10) if(points.size < 10)
Util.insertSort((p1: Point, p2: Point) => p1 > p2)(pts).toList Util.insertSort((p1: Point, p2: Point) => p1 > p2)(points).toList
else else
Util.msort((p1: Point, p2: Point) => p1 > p2)(pts.toList) Util.msort((p1: Point, p2: Point) => p1 > p2)(points.toList)
} }
// Prevents any two distinct endpoints from lying on a common horizontal line, and avoiding // Prevents any two distinct endpoints from lying on a common horizontal line, and avoiding
@ -103,60 +106,99 @@ object CDT {
} }
class CDT(val points: List[Point], val segments: List[Segment], initialTriangle: Triangle) { class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Triangle) {
// The triangle mesh // The triangle mesh
val mesh = new Mesh(initialTriangle) val mesh = new Mesh(iTriangle)
// Advancing front // Advancing front
val aFront = new AFront(initialTriangle) val aFront = new AFront(iTriangle)
// Sweep points; build mesh // Sweep points; build mesh
sweep sweep
// Finalize triangulation // Finalize triangulation
finalization finalization
// Implement sweep-line paradigm // Implement sweep-line
private def sweep { private def sweep {
for(i <- 1 until points.size) { for(i <- 1 until points.size) {
val point = points(i) val point = points(i)
pointEvent(point) val triangle = pointEvent(point)
legalization edgeEvent(point, triangle)
edgeEvent(point)
} }
} }
// Point event // Point event
private def pointEvent(point: Point) { private def pointEvent(point: Point): Triangle = {
val node = aFront.locate(point)
// Neightbor points (ccw & cw) and triangle(i) // Neightbor points (ccw & cw) and triangle(i)
val (nPts, nTri) = aFront.locate(point) val cwPoint = node.next.point
val pts = Array(point, nPts(0), nPts(1)) val ccwPoint = node.point
val nTri = node.triangle
val pts = Array(point, ccwPoint, cwPoint)
val neighbors = Array(nTri, null, null) val neighbors = Array(nTri, null, null)
val triangle = new Triangle(pts, neighbors) val triangle = new Triangle(pts, neighbors)
mesh.map += triangle mesh.map += triangle
triangle.legalize
// Update neighbor's pointers nTri.updateNeighbors(ccwPoint, cwPoint, triangle)
if(nPts(0) == nTri.points(1) && nPts(1) == nTri.points(2))
nTri.neighbors(2) = triangle
else if(nPts(0) == nTri.points(2) && nPts(1) == nTri.points(1))
nTri.neighbors(1) = triangle
else
throw new Exception("CDT Error!")
}
private def legalization {
}
private def legalizeEdge {
// Update advancing front
march(aFront += (point, triangle, node))
triangle
} }
// EdgeEvent // EdgeEvent
private def edgeEvent(point: Point) { private def edgeEvent(point: Point, triangle: Triangle) {
mesh.addEdge(point, triangle)
}
def march(n: Node) {
var node = n
/*
// Update right
if(node.next != aFront.tail) {
var a = (node.point - node.next.point)
var b = (node.next.next.point - node.next.point)
var angle = Math.atan2(a cross b, a dot b)
while(node != aFront.tail && angle > -Math.Pi*0.5f) {
if(angle >= -Math.Pi*0.5f) {
val points = Array(node.next.point, node.next.next.point, node.point)
val neighbors = Array(null, node.triangle, node.next.triangle)
val triangle = new Triangle(points, neighbors)
mesh.map += triangle
aFront -= (node, node.next, triangle)
}
node = node.next
a = (node.point - node.next.point)
b = (node.next.next.point - node.next.point)
angle = Math.atan2(a cross b, a dot b)
}
}
*/
node = n
// Update left
if(node.prev != aFront.head) {
var angle = 0.0
while(node != aFront.head.next && angle < Math.Pi*0.5f) {
val a = (node.point - node.prev.point)
val b = (node.prev.prev.point - node.prev.point)
angle = Math.abs(Math.atan2(a cross b, a dot b))
if(angle <= Math.Pi*0.5f) {
val points = Array(node.prev.point, node.prev.prev.point, node.point)
val neighbors = Array(null, node.triangle, node.prev.triangle)
val triangle = new Triangle(points, neighbors)
mesh.map += triangle
aFront -== (node, node.prev, triangle)
}
node = node.prev
}
}
} }
private def finalization { private def finalization {

View File

@ -39,4 +39,11 @@ class Mesh(initialTriangle: Triangle) {
// Triangles that constitute the mesh // Triangles that constitute the mesh
val map = HashSet(initialTriangle) val map = HashSet(initialTriangle)
def addEdge(point:Point, triangle: Triangle) {
val p = point.eEvent.p
if(!triangle.contains(p)) {
}
}
} }

View File

@ -38,4 +38,44 @@ class Triangle(val points: Array[Point], val neighbors: Array[Triangle]) {
// Flags to determine if an edge is the final Delauney edge // Flags to determine if an edge is the final Delauney edge
val edges = new Array[Boolean](3) val edges = new Array[Boolean](3)
def contains(point: Point) = {
if(point == points(0) || point == points(1) || point == points(2))
true
else
false
}
def legalize {
}
// Update neighbor pointers
def updateNeighbors(ccwPoint: Point, cwPoint: Point, triangle: Triangle) {
if(ccwPoint == points(2) && cwPoint == points(1))
neighbors(0) = triangle
else if(ccwPoint == points(0) && cwPoint == points(2))
neighbors(1) = triangle
else
neighbors(2) = triangle
}
// Fast point in triangle test
def pointIn(point: Point): Boolean = {
val ab = points(1) - points(0)
val bc = points(2) - points(1)
val ca = points(0) - points(2)
val pab = (point - points(0)).cross(ab)
val pbc = (point - points(1)).cross(bc)
var sameSign = Math.signum(pab) == Math.signum(pbc)
if (!sameSign) return false
val pca = (point - points(2)).cross(ca)
sameSign = Math.signum(pab) == Math.signum(pca)
if (!sameSign) return false
true
}
} }