CDT work; removed manual point drawing; added model; fixed bugs

This commit is contained in:
zzzzrrr
2009-07-29 15:07:51 -04:00
parent b31d115774
commit dc70f1ca78
11 changed files with 164 additions and 329 deletions

View File

@@ -39,6 +39,7 @@ import scala.io.Source
import seidel.Triangulator
import shapes.{Segment, Point, Triangle}
import earClip.EarClip
import cdt.CDT
// TODO: Lots of documentation!
@@ -73,6 +74,11 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
val nazcaMonkey = "data/nazca_monkey.dat"
val bird = "data/bird.dat"
val snake = "data/i.snake"
val star = "data/star.dat"
val strange = "data/strange.dat"
val i18 = "data/i.18"
var currentModel = nazcaMonkey
def init(container: GameContainer) {
@@ -171,9 +177,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
if(c == 'm') drawMap = !drawMap
if(c == '1') {currentModel = nazcaMonkey; selectModel}
if(c == '2') {currentModel = bird; selectModel}
if(c == '3') {poly; earClipPoly}
if(c == '4') snake
if(c == '5') star
if(c == '3') {currentModel = strange; selectModel}
if(c == '4') {currentModel = snake; selectModel}
if(c == '5') {currentModel = star; selectModel}
if(c == '6') {currentModel = i18; selectModel}
if(c == 's') drawSegs = !drawSegs
if(c == 'e') {drawEarClip = !drawEarClip; selectModel}
if(c == 'h') {hertelMehlhorn = !hertelMehlhorn; selectModel}
@@ -185,147 +192,26 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500)
case "data/bird.dat" =>
loadModel(bird, 25f, Point(400, 300), 350)
case "data/i.snake" =>
loadModel(snake, 10f, Point(600, 300), 10)
case "data/star.dat" =>
loadModel(star, -1f, Point(0f, 0f), 10)
case "data/strange.dat" =>
loadModel(strange, -1f, Point(0f, 0f), 15)
case "data/i.18" =>
loadModel(i18, 15f, Point(500f, 400f), 20)
case _ =>
assert(false)
}
}
// Test #1
def poly {
val p1 = Point(400,472)
val p2 = Point(500,392)
val p3 = Point(520,272)
val p4 = Point(460,232)
val p5 = Point(580,212)
val p6 = Point(480,152)
val p7 = Point(360,172)
val p8 = Point(360,52)
val p9 = Point(300,112)
val p10 = Point(200,32)
val p11 = Point(120,92)
val p12 = Point(200,72)
val p13 = Point(340,272)
val p14 = Point(208,212)
val p15 = Point(180,352)
val p16 = Point(300,312)
segments = new ArrayBuffer[Segment]
segments += new Segment(p16, p1)
segments += new Segment(p9, p10)
segments += new Segment(p13, p14)
segments += new Segment(p5, p6)
segments += new Segment(p2, p3)
segments += new Segment(p1, p2)
segments += new Segment(p4, p5)
segments += new Segment(p7, p8)
segments += new Segment(p8, p9)
segments += new Segment(p10, p11)
segments += new Segment(p11, p12)
segments += new Segment(p12, p13)
segments += new Segment(p3, p4)
segments += new Segment(p15, p16)
segments += new Segment(p14, p15)
segments += new Segment(p6, p7)
tesselator = new Triangulator(segments)
tesselator.buildTriangles = hertelMehlhorn
val t1 = System.nanoTime
tesselator process
val t2 = System.nanoTime
println
println("**Poly1**")
println("Poly2Tri total (ms) = " + (t2-t1)*1e-6)
}
def star {
val p1 = Point(350,75)
val p2 = Point(379,161)
val p3 = Point(469,161)
val p4 = Point(397,215)
val p5 = Point(423,301)
val p6 = Point(350,250)
val p7 = Point(277,301)
val p8 = Point(303,215)
val p9 = Point(231,161)
val p10 = Point(321,161)
segments = new ArrayBuffer[Segment]
segments += new Segment(p1, p2)
segments += new Segment(p2, p3)
segments += new Segment(p3, p4)
segments += new Segment(p4, p5)
segments += new Segment(p5, p6)
segments += new Segment(p6, p7)
segments += new Segment(p7, p8)
segments += new Segment(p8, p9)
segments += new Segment(p9, p10)
segments += new Segment(p10, p1)
tesselator = new Triangulator(segments)
tesselator.buildTriangles = hertelMehlhorn
val t1 = System.nanoTime
tesselator process
val t2 = System.nanoTime
println
println("**Star**")
println("Poly2Tri total (ms) = " + (t2-t1)*1e-6)
}
// Test #2
def snake {
val scale = 10.0f
val displace = 100
val p1 = Point(10,1)*scale+displace
val p2 = Point(20,10)*scale+displace
val p3 = Point(30,1)*scale+displace
val p4 = Point(40,10)*scale+displace
val p5 = Point(50,1)*scale+displace
val p6 = Point(50,10)*scale+displace
val p7 = Point(40,20)*scale+displace
val p8 = Point(30,10)*scale+displace
val p9 = Point(20,20)*scale+displace
val p10 = Point(10,10)*scale+displace
val p11 = Point(1,20)*scale+displace
val p12 = Point(1,10)*scale+displace
segments = new ArrayBuffer[Segment]
segments += new Segment(p1, p2)
segments += new Segment(p2, p3)
segments += new Segment(p3, p4)
segments += new Segment(p4, p5)
segments += new Segment(p5, p6)
segments += new Segment(p6, p7)
segments += new Segment(p7, p8)
segments += new Segment(p8, p9)
segments += new Segment(p9, p10)
segments += new Segment(p10, p11)
segments += new Segment(p11, p12)
segments += new Segment(p12, p1)
tesselator = new Triangulator(segments)
tesselator.buildTriangles = hertelMehlhorn
val t1 = System.nanoTime
tesselator process
val t2 = System.nanoTime
println
println("**Snake**")
println("Poly2Tri total (ms) = " + (t2-t1)*1e-6)
}
def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int) {
println("*** " + model + " ***")
polyX = new ArrayBuffer[Float]
polyY = new ArrayBuffer[Float]
val points = new ArrayBuffer[Point]
val angle = Math.Pi
for (line <- Source.fromFile(model).getLines) {
@@ -337,24 +223,20 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
// Transform the shape
polyX += (Math.cos(angle)*x - Math.sin(angle)*y).toFloat * scale + center.x
polyY += (Math.sin(angle)*x + Math.cos(angle)*y).toFloat * scale + center.y
points += new Point(polyX.last, polyY.last)
} else {
throw new Exception("Bad input file")
}
}
points.foreach(println)
segments = new ArrayBuffer[Segment]
var i = 0
val numPoints = polyX.size
while(i < polyX.size-2) {
val p1 = new Point(polyX(i), polyY(i))
val p2 = new Point(polyX(i+1), polyY(i+1))
segments += new Segment(p1, p2)
i += 1
}
for(i <- 0 until polyX.size-1)
segments += new Segment(points(i), points(i+1))
// Connect the end points
val p1 = segments(0).p
val p2 = segments(segments.length-1).q
segments += new Segment(p2, p1)
segments += new Segment(points.first, points.last)
val cdt = CDT.init(points)
println(cdt.points.size + "," + cdt.segments.size)
println("Number of points = " + polyX.size)
println
@@ -379,8 +261,14 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles)
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
val xVerts = polyX.toArray.reverse
val yVerts = polyY.toArray.reverse
var xVerts = polyX.toArray
var yVerts = polyY.toArray
if(currentModel != strange) {
xVerts = xVerts.reverse
yVerts = yVerts.reverse
}
val t1 = System.nanoTime
earClip.triangulatePolygon(xVerts, yVerts, xVerts.size, earClipResults)
@@ -391,55 +279,5 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
println("Number of triangles = " + earClip.numTriangles)
}
}
def earClipPoly {
val polyX = Array(400f, 500f, 520f, 460f, 580f, 480f, 360f, 360f, 300f, 200f, 120f, 200f, 340f, 208f, 180f, 300f)
val polyY = Array(472f, 392f, 272f, 232f, 212f, 152f, 172f, 52f, 112f, 32f, 92f, 72f, 272f, 212f, 352f, 312f)
val earClipResults = new Array[poly2tri.earClip.Triangle](14)
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
val t1 = System.nanoTime
earClip.triangulatePolygon(polyX, polyY, polyX.size, earClipResults)
val t2 = System.nanoTime
println("Earclip total (ms) = " + (t2-t1)*1e-6)
}
def earClipSnake {
val polyX = Array(200f, 300f, 400f, 500f, 600f, 600f, 500f, 400f, 300f, 200f, 110f, 110f)
val polyY = Array(110f, 200f, 110f, 200f, 110f, 200f, 300f, 200f, 300f, 200f, 300f, 200f)
val earClipResults = new Array[poly2tri.earClip.Triangle](14)
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
val t1 = System.nanoTime
earClip.triangulatePolygon(polyX, polyY, polyX.size, earClipResults)
val t2 = System.nanoTime
println("Earclip total (ms) = " + (t2-t1)*1e-6)
}
def earClipStar {
val p1 = Point(350,75)
val p2 = Point(379,161)
val p3 = Point(469,161)
val p4 = Point(397,215)
val p5 = Point(423,301)
val p6 = Point(350,250)
val p7 = Point(277,301)
val p8 = Point(303,215)
val p9 = Point(231,161)
val p10 = Point(321,161)
val polyX = Array(350f, 379f, 469f, 397f, 423f, 350f, 277f, 303f, 231f, 321f)
val polyY = Array(75f, 161f, 161f, 215f, 301f, 250f, 301f,215f, 161f, 161f)
val earClipResults = new Array[poly2tri.earClip.Triangle](14)
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
val t1 = System.nanoTime
earClip.triangulatePolygon(polyX, polyY, polyX.size, earClipResults)
val t2 = System.nanoTime
println("Earclip total (ms) = " + (t2-t1)*1e-6)
}
}

View File

@@ -40,69 +40,73 @@ import utils.Util
* See: Domiter, V. and Žalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
* International Journal of Geographical Information Science,22:4,449 — 462
*/
class CDT(segments: ArrayBuffer[Segment]) {
object CDT {
// Inital triangle factor
val ALPHA = 0.3f
def init(points: ArrayBuffer[Point]): CDT = {
var xmax, xmin = 0f
var ymax, ymin = 0f
// Calculate bounds
for(i <- 0 until points.size) {
points(i) = shearTransform(points(i))
val p = points(i)
if(p.x > xmax) xmax = p.x
if(p.x < xmin) xmin = p.x
if(p.y > ymax) ymax = p.x
if(p.y < ymin) ymin = p.x
}
val deltaX = ALPHA * (xmax - xmin)
val deltaY = ALPHA * (ymax - ymin)
val p1 = Point(xmin - deltaX, ymin - deltaY)
val p2 = Point(xmax - deltaX, ymin - deltaY)
val initialTriangle = new Triangle(Array(p2, points(0), p1), null)
val segments = initSegments(points)
val sortedPoints = pointSort(points)
new CDT(sortedPoints, segments, initialTriangle)
}
// Create segments and connect end points
private def initSegments(points: ArrayBuffer[Point]): List[Segment] = {
var segments = List[Segment]()
for(i <- 0 until points.size-1)
segments = new Segment(points(i), points(i+1)) :: segments
segments = new Segment(points.first, points.last) :: segments
segments
}
// 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
else
Util.msort((p1: Point, p2: Point) => p1 > p2)(pts.toList)
}
// Prevents any two distinct endpoints from lying on a common horizontal line, and avoiding
// the degenerate case. See Mark de Berg et al, Chapter 6.3
//val SHEER = 0.0001f
private def shearTransform(point: Point) = Point(point.x, point.y + point.x * 0.0001f)
}
// The initial triangle
var initialTriangle: Triangle = null
// The point list
val points = init
class CDT(val points: List[Point], val segments: List[Segment], initialTriangle: Triangle) {
// The triangle mesh
val mesh = new Mesh(initialTriangle)
// Used to compute inital triangle
private val ALPHA = 0.3f
// Sweep points; build mesh
sweep
// Finalize triangulation
finalization
// Initialize and sort point list
private def init: List[Point] = {
var xmax, xmin = segments(0).p.x
var ymax, ymin = segments(0).p.y
val pts = new ArrayBuffer[Point]
for(i <- 0 until segments.size) {
val p = segments(i).p
val q = segments(i).q
if(p.x > xmax) xmax = p.x
if(q.x > xmax) xmax = q.x
if(p.x < xmin) xmin = p.x
if(q.x < xmin) xmin = q.x
if(p.y > ymax) ymax = p.x
if(q.y > ymax) ymax = q.x
if(p.y < ymin) ymin = p.x
if(q.y < ymin) ymin = q.x
pts += shearTransform(p)
pts += shearTransform(q)
}
var points: List[Point] = null
if(pts.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.
points = Util.insertSort((p1: Point, p2: Point) => p1 > p2)(pts).toList
else
// Merge sort: O(n log n)
points = Util.msort((p1: Point, p2: Point) => p1 > p2)(pts.toList)
val deltaX = ALPHA * (xmax - xmin)
val deltaY = ALPHA * (ymax - ymin)
val p1 = Point(xmin - deltaX, ymin - deltaY)
val p2 = Point(xmax - deltaX, ymin - deltaY)
initialTriangle = new Triangle(Array(p2, points(0), p1), null)
points
}
// Implement sweep-line paradigm
private def sweep {
}
@@ -110,8 +114,5 @@ class CDT(segments: ArrayBuffer[Segment]) {
private def finalization {
}
// Prevents any two distinct endpoints from lying on a common horizontal line, and avoiding
// the degenerate case. See Mark de Berg et al, Chapter 6.3
//val SHEER = 0.0001f
def shearTransform(point: Point) = Point(point.x, point.y + point.x * 0.0001f)
def triangles = mesh.map
}

View File

@@ -32,7 +32,7 @@ package org.poly2tri.cdt
import scala.collection.mutable.HashSet
import shapes.Point
import shapes.{Point, Triangle}
class Mesh(initialTriangle: Triangle) {

View File

@@ -30,18 +30,16 @@
*/
package org.poly2tri.shapes
object Event extends Enumeration {
val point, edge = Value
}
case class Point(val x: Float, val y: Float) {
// Pointers to next and previous points in Monontone Mountain
var next, prev: Point = null
// The setment this point belongs to
var segment: Segment = null
// Point type for CDT
var eventType: Event.Value = _
// Edge event pointer for CDT
var eEvent: Segment = null
// Point event pointer for CDT
var pEvent: Segment = null
@inline def -(p: Point) = Point(x - p.x, y - p.y)
@inline def +(p: Point) = Point(x + p.x, y + p.y)

View File

@@ -34,10 +34,7 @@ import scala.collection.mutable.{ArrayBuffer}
// Represents a simple polygon's edge
class Segment(var p: Point, var q: Point) {
p.segment = this
q.segment = this
// Pointers used for building trapezoidal map
var above, below: Trapezoid = null
// Montone mountain points
@@ -53,27 +50,4 @@ class Segment(var p: Point, var q: Point) {
// Determines if this segment lies below the given point
def < (point: Point) = (Math.floor(point.y) > Math.floor(slope * point.x + b))
// Assign point type for CDT
if(p.y > q.y)
pEdge
else if (p.y < q.y)
pPoint
else
if(p.x < q.x)
pPoint
else if (p.x > q.x)
pEdge
else
throw new Exception("Invalid segment")
private def pPoint {
p.eventType = Event.point
q.eventType = Event.edge
}
private def pEdge {
p.eventType = Event.edge
q.eventType = Event.point
}
}

View File

@@ -30,8 +30,6 @@
*/
package org.poly2tri.shapes
import shapes.Point
// Triangle-based data structures are know to have better performance than quad-edge structures
// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
// "Triangulations in CGAL"