mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-12-28 13:43:30 +01:00
CDT work
This commit is contained in:
parent
2cf811d635
commit
81265ea832
@ -55,12 +55,17 @@ object Poly2Tri {
|
||||
|
||||
class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
|
||||
var tesselator: Triangulator = null
|
||||
// Sedidel Triangulator
|
||||
var seidel: Triangulator = null
|
||||
var segments: ArrayBuffer[Segment] = null
|
||||
|
||||
// EarClip Triangulator
|
||||
val earClip = new EarClip
|
||||
var earClipResults: Array[poly2tri.earClip.Triangle] = null
|
||||
|
||||
// Sweep Line Constraied Delauney Triangulator (CDT)
|
||||
var slCDT: CDT = null
|
||||
|
||||
var polyX: ArrayBuffer[Float] = null
|
||||
var polyY: ArrayBuffer[Float] = null
|
||||
|
||||
@ -71,6 +76,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
var hiLighter = 0
|
||||
var drawEarClip = false
|
||||
var hertelMehlhorn = false
|
||||
var drawCDT = false
|
||||
|
||||
val nazcaMonkey = "data/nazca_monkey.dat"
|
||||
val bird = "data/bird.dat"
|
||||
@ -79,7 +85,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
val strange = "data/strange.dat"
|
||||
val i18 = "data/i.18"
|
||||
|
||||
var currentModel = nazcaMonkey
|
||||
var currentModel = star
|
||||
|
||||
def init(container: GameContainer) {
|
||||
selectModel(currentModel)
|
||||
@ -102,7 +108,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
val yellow = new Color(1f, 1f, 0f)
|
||||
|
||||
if(debug) {
|
||||
val draw = if(drawMap) tesselator.trapezoidMap else tesselator.trapezoids
|
||||
val draw = if(drawMap) seidel.trapezoidMap else seidel.trapezoids
|
||||
for(t <- draw) {
|
||||
val polygon = new Polygon()
|
||||
for(v <- t.vertices) {
|
||||
@ -121,14 +127,14 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
|
||||
if(!debug && !drawEarClip) {
|
||||
var i = 0
|
||||
for(t <- tesselator.polygons) {
|
||||
for(t <- seidel.polygons) {
|
||||
val poly = new Polygon
|
||||
t.foreach(p => poly.addPoint(p.x, p.y))
|
||||
g.setColor(red)
|
||||
g.draw(poly)
|
||||
}
|
||||
} else if (debug && drawMap && !drawEarClip){
|
||||
for(mp <- tesselator.monoPolies) {
|
||||
for(mp <- seidel.monoPolies) {
|
||||
val poly = new Polygon
|
||||
mp.foreach(p => poly.addPoint(p.x, p.y))
|
||||
g.setColor(yellow)
|
||||
@ -146,6 +152,17 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
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) {
|
||||
g.setColor(green)
|
||||
for(i <- 0 until segments.size) {
|
||||
@ -165,16 +182,17 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
// UP
|
||||
if(key == 200) {
|
||||
hiLighter += 1
|
||||
if (hiLighter == tesselator.polygons.size)
|
||||
if (hiLighter == seidel.polygons.size)
|
||||
hiLighter = 0
|
||||
}
|
||||
// DOWN
|
||||
if(key == 208) {
|
||||
hiLighter -= 1
|
||||
if (hiLighter == -1)
|
||||
hiLighter = tesselator.polygons.size-1
|
||||
hiLighter = seidel.polygons.size-1
|
||||
}
|
||||
if(c == 'm') drawMap = !drawMap
|
||||
//if(c == 'c') drawCDT = !drawCDT
|
||||
if(c == '1') selectModel(nazcaMonkey)
|
||||
if(c == '2') selectModel(bird)
|
||||
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.first, points.last)
|
||||
|
||||
//val cdt = CDT.init(points)
|
||||
//slCDT = CDT.init(points)
|
||||
|
||||
println("Number of points = " + polyX.size)
|
||||
println
|
||||
@ -243,15 +261,15 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
if(!drawEarClip) {
|
||||
|
||||
// Sediel triangulation
|
||||
tesselator = new Triangulator(segments)
|
||||
tesselator.buildTriangles = hertelMehlhorn
|
||||
seidel = new Triangulator(segments)
|
||||
seidel.buildTriangles = hertelMehlhorn
|
||||
|
||||
val t1 = System.nanoTime
|
||||
tesselator.process
|
||||
seidel.process
|
||||
val runTime = System.nanoTime - t1
|
||||
|
||||
println("Poly2Tri average (ms) = " + runTime*1e-6)
|
||||
println("Number of triangles = " + tesselator.polygons.size)
|
||||
println("Number of triangles = " + seidel.polygons.size)
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -36,28 +36,61 @@ import shapes.{Point, Triangle}
|
||||
class AFront(iTriangle: Triangle) {
|
||||
|
||||
// Doubly linked list
|
||||
// TODO: Replace with a Red-Black Tree for better performance
|
||||
var head = new Node(iTriangle.points(1), iTriangle)
|
||||
var tail = new Node(iTriangle.points(2), iTriangle)
|
||||
|
||||
head.next = new Node(iTriangle.points(0), iTriangle)
|
||||
var tail = new Node(iTriangle.points(2), null)
|
||||
|
||||
head.next.next = tail
|
||||
tail.prev = head.next
|
||||
|
||||
def locate(point: Point): Tuple2[List[Point], Triangle] = {
|
||||
def locate(point: Point): Node = {
|
||||
var node = head
|
||||
while(node != tail) {
|
||||
if(point.x > node.point.x && point.x < node.next.point.x)
|
||||
return (List(node.point, node.next.point), node.triangle)
|
||||
if(point.x >= node.point.x && point.x <= node.next.point.x)
|
||||
return node
|
||||
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 prev: Node = null
|
||||
|
||||
def printDebug {
|
||||
println("Point: " + point)
|
||||
if(triangle != null)
|
||||
println("Triangle points: " + triangle.points)
|
||||
}
|
||||
}
|
@ -68,8 +68,11 @@ object CDT {
|
||||
|
||||
val segments = initSegments(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
|
||||
@ -89,11 +92,11 @@ object CDT {
|
||||
// Insertion sort is one of the fastest algorithms for sorting arrays containing
|
||||
// fewer than ten elements, or for lists that are already mostly sorted.
|
||||
// Merge sort: O(n log n)
|
||||
private def pointSort(pts: ArrayBuffer[Point]): List[Point] = {
|
||||
if(pts.size < 10)
|
||||
Util.insertSort((p1: Point, p2: Point) => p1 > p2)(pts).toList
|
||||
private def pointSort(points: ArrayBuffer[Point]): List[Point] = {
|
||||
if(points.size < 10)
|
||||
Util.insertSort((p1: Point, p2: Point) => p1 > p2)(points).toList
|
||||
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
|
||||
@ -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
|
||||
val mesh = new Mesh(initialTriangle)
|
||||
val mesh = new Mesh(iTriangle)
|
||||
// Advancing front
|
||||
val aFront = new AFront(initialTriangle)
|
||||
val aFront = new AFront(iTriangle)
|
||||
|
||||
// Sweep points; build mesh
|
||||
sweep
|
||||
// Finalize triangulation
|
||||
finalization
|
||||
|
||||
// Implement sweep-line paradigm
|
||||
// Implement sweep-line
|
||||
private def sweep {
|
||||
|
||||
for(i <- 1 until points.size) {
|
||||
val point = points(i)
|
||||
pointEvent(point)
|
||||
legalization
|
||||
edgeEvent(point)
|
||||
val triangle = pointEvent(point)
|
||||
edgeEvent(point, triangle)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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)
|
||||
val (nPts, nTri) = aFront.locate(point)
|
||||
val pts = Array(point, nPts(0), nPts(1))
|
||||
val cwPoint = node.next.point
|
||||
val ccwPoint = node.point
|
||||
val nTri = node.triangle
|
||||
|
||||
val pts = Array(point, ccwPoint, cwPoint)
|
||||
val neighbors = Array(nTri, null, null)
|
||||
val triangle = new Triangle(pts, neighbors)
|
||||
mesh.map += triangle
|
||||
|
||||
// Update neighbor's pointers
|
||||
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!")
|
||||
triangle.legalize
|
||||
|
||||
}
|
||||
|
||||
private def legalization {
|
||||
|
||||
}
|
||||
|
||||
private def legalizeEdge {
|
||||
nTri.updateNeighbors(ccwPoint, cwPoint, triangle)
|
||||
|
||||
// Update advancing front
|
||||
march(aFront += (point, triangle, node))
|
||||
triangle
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -39,4 +39,11 @@ class Mesh(initialTriangle: Triangle) {
|
||||
// Triangles that constitute the mesh
|
||||
val map = HashSet(initialTriangle)
|
||||
|
||||
def addEdge(point:Point, triangle: Triangle) {
|
||||
val p = point.eEvent.p
|
||||
if(!triangle.contains(p)) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user