mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-12-29 06:03:31 +01:00
CDT work
This commit is contained in:
parent
2cf811d635
commit
81265ea832
@ -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 {
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
@ -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
|
|
||||||
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!")
|
|
||||||
|
|
||||||
}
|
nTri.updateNeighbors(ccwPoint, cwPoint, triangle)
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -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)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user