mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-16 18:52:25 +01:00
CDT work; removed manual point drawing; added model; fixed bugs
This commit is contained in:
parent
b31d115774
commit
dc70f1ca78
@ -236,9 +236,9 @@
|
|||||||
7.85981 -0.78467
|
7.85981 -0.78467
|
||||||
7.79516 -0.409667
|
7.79516 -0.409667
|
||||||
7.49774 -0.151043
|
7.49774 -0.151043
|
||||||
7.84688.042924
|
7.84688 0.042924
|
||||||
8.23481.314479
|
8.23481 0.314479
|
||||||
8.64861.702414
|
8.64861 0.702414
|
||||||
8.70034 1.09035
|
8.70034 1.09035
|
||||||
8.41585 1.42656
|
8.41585 1.42656
|
||||||
8.11843 1.62053
|
8.11843 1.62053
|
||||||
|
36
data/i.18
36
data/i.18
@ -1,18 +1,18 @@
|
|||||||
0 0
|
0 0
|
||||||
10 7
|
10 7
|
||||||
12 3
|
12 3
|
||||||
20 8
|
20 8
|
||||||
13 17
|
13 17
|
||||||
10 12
|
10 12
|
||||||
12 14
|
12 14
|
||||||
14 9
|
14 9
|
||||||
8 10
|
8 10
|
||||||
6 14
|
6 14
|
||||||
10 15
|
10 15
|
||||||
7 18
|
7 18
|
||||||
0 16
|
0 16
|
||||||
1 13
|
1 13
|
||||||
3 15
|
3 15
|
||||||
5 8
|
5 8
|
||||||
-2 9
|
-2 9
|
||||||
5 5
|
5 5
|
||||||
|
24
data/i.snake
24
data/i.snake
@ -1,12 +1,12 @@
|
|||||||
10 0
|
10 0
|
||||||
20 10
|
20 10
|
||||||
30 0
|
30 0
|
||||||
40 10
|
40 10
|
||||||
50 0
|
50 0
|
||||||
50 10
|
50 10
|
||||||
40 20
|
40 20
|
||||||
30 10
|
30 10
|
||||||
20 20
|
20 20
|
||||||
10 10
|
10 10
|
||||||
0 20
|
0 20
|
||||||
0 10
|
0 10
|
||||||
|
10
data/star.dat
Normal file
10
data/star.dat
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
350 75
|
||||||
|
379 161
|
||||||
|
469 161
|
||||||
|
397 215
|
||||||
|
423 301
|
||||||
|
350 250
|
||||||
|
277 301
|
||||||
|
303 215
|
||||||
|
231 161
|
||||||
|
321 161
|
16
data/strange.dat
Normal file
16
data/strange.dat
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
400 472
|
||||||
|
500 392
|
||||||
|
520 272
|
||||||
|
460 232
|
||||||
|
580 212
|
||||||
|
480 152
|
||||||
|
360 172
|
||||||
|
360 52
|
||||||
|
300 112
|
||||||
|
200 32
|
||||||
|
120 92
|
||||||
|
200 72
|
||||||
|
340 272
|
||||||
|
208 212
|
||||||
|
180 352
|
||||||
|
300 312
|
@ -39,6 +39,7 @@ import scala.io.Source
|
|||||||
import seidel.Triangulator
|
import seidel.Triangulator
|
||||||
import shapes.{Segment, Point, Triangle}
|
import shapes.{Segment, Point, Triangle}
|
||||||
import earClip.EarClip
|
import earClip.EarClip
|
||||||
|
import cdt.CDT
|
||||||
|
|
||||||
// TODO: Lots of documentation!
|
// TODO: Lots of documentation!
|
||||||
|
|
||||||
@ -73,6 +74,11 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
|
|
||||||
val nazcaMonkey = "data/nazca_monkey.dat"
|
val nazcaMonkey = "data/nazca_monkey.dat"
|
||||||
val bird = "data/bird.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
|
var currentModel = nazcaMonkey
|
||||||
|
|
||||||
def init(container: GameContainer) {
|
def init(container: GameContainer) {
|
||||||
@ -171,9 +177,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
if(c == 'm') drawMap = !drawMap
|
if(c == 'm') drawMap = !drawMap
|
||||||
if(c == '1') {currentModel = nazcaMonkey; selectModel}
|
if(c == '1') {currentModel = nazcaMonkey; selectModel}
|
||||||
if(c == '2') {currentModel = bird; selectModel}
|
if(c == '2') {currentModel = bird; selectModel}
|
||||||
if(c == '3') {poly; earClipPoly}
|
if(c == '3') {currentModel = strange; selectModel}
|
||||||
if(c == '4') snake
|
if(c == '4') {currentModel = snake; selectModel}
|
||||||
if(c == '5') star
|
if(c == '5') {currentModel = star; selectModel}
|
||||||
|
if(c == '6') {currentModel = i18; selectModel}
|
||||||
if(c == 's') drawSegs = !drawSegs
|
if(c == 's') drawSegs = !drawSegs
|
||||||
if(c == 'e') {drawEarClip = !drawEarClip; selectModel}
|
if(c == 'e') {drawEarClip = !drawEarClip; selectModel}
|
||||||
if(c == 'h') {hertelMehlhorn = !hertelMehlhorn; selectModel}
|
if(c == 'h') {hertelMehlhorn = !hertelMehlhorn; selectModel}
|
||||||
@ -185,147 +192,26 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500)
|
loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500)
|
||||||
case "data/bird.dat" =>
|
case "data/bird.dat" =>
|
||||||
loadModel(bird, 25f, Point(400, 300), 350)
|
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 _ =>
|
case _ =>
|
||||||
assert(false)
|
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) {
|
def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int) {
|
||||||
|
|
||||||
println("*** " + model + " ***")
|
println("*** " + model + " ***")
|
||||||
|
|
||||||
polyX = new ArrayBuffer[Float]
|
polyX = new ArrayBuffer[Float]
|
||||||
polyY = new ArrayBuffer[Float]
|
polyY = new ArrayBuffer[Float]
|
||||||
|
val points = new ArrayBuffer[Point]
|
||||||
|
|
||||||
val angle = Math.Pi
|
val angle = Math.Pi
|
||||||
for (line <- Source.fromFile(model).getLines) {
|
for (line <- Source.fromFile(model).getLines) {
|
||||||
@ -337,24 +223,20 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
// Transform the shape
|
// Transform the shape
|
||||||
polyX += (Math.cos(angle)*x - Math.sin(angle)*y).toFloat * scale + center.x
|
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
|
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]
|
segments = new ArrayBuffer[Segment]
|
||||||
|
for(i <- 0 until polyX.size-1)
|
||||||
var i = 0
|
segments += new Segment(points(i), points(i+1))
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect the end points
|
// Connect the end points
|
||||||
val p1 = segments(0).p
|
segments += new Segment(points.first, points.last)
|
||||||
val p2 = segments(segments.length-1).q
|
|
||||||
segments += new Segment(p2, p1)
|
val cdt = CDT.init(points)
|
||||||
|
println(cdt.points.size + "," + cdt.segments.size)
|
||||||
|
|
||||||
println("Number of points = " + polyX.size)
|
println("Number of points = " + polyX.size)
|
||||||
println
|
println
|
||||||
@ -379,8 +261,14 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles)
|
earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles)
|
||||||
|
|
||||||
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
|
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
|
val t1 = System.nanoTime
|
||||||
earClip.triangulatePolygon(xVerts, yVerts, xVerts.size, earClipResults)
|
earClip.triangulatePolygon(xVerts, yVerts, xVerts.size, earClipResults)
|
||||||
@ -391,55 +279,5 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
println("Number of triangles = " + earClip.numTriangles)
|
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -40,69 +40,73 @@ import utils.Util
|
|||||||
* See: Domiter, V. and Žalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
|
* See: Domiter, V. and Žalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
|
||||||
* International Journal of Geographical Information Science,22:4,449 — 462
|
* 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
|
class CDT(val points: List[Point], val segments: List[Segment], initialTriangle: Triangle) {
|
||||||
var initialTriangle: Triangle = null
|
|
||||||
// The point list
|
|
||||||
val points = init
|
|
||||||
// The triangle mesh
|
// The triangle mesh
|
||||||
val mesh = new Mesh(initialTriangle)
|
val mesh = new Mesh(initialTriangle)
|
||||||
|
|
||||||
// Used to compute inital triangle
|
|
||||||
private val ALPHA = 0.3f
|
|
||||||
|
|
||||||
// Sweep points; build mesh
|
// Sweep points; build mesh
|
||||||
sweep
|
sweep
|
||||||
// Finalize triangulation
|
// Finalize triangulation
|
||||||
finalization
|
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
|
// Implement sweep-line paradigm
|
||||||
private def sweep {
|
private def sweep {
|
||||||
}
|
}
|
||||||
@ -110,8 +114,5 @@ class CDT(segments: ArrayBuffer[Segment]) {
|
|||||||
private def finalization {
|
private def finalization {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevents any two distinct endpoints from lying on a common horizontal line, and avoiding
|
def triangles = mesh.map
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ package org.poly2tri.cdt
|
|||||||
|
|
||||||
import scala.collection.mutable.HashSet
|
import scala.collection.mutable.HashSet
|
||||||
|
|
||||||
import shapes.Point
|
import shapes.{Point, Triangle}
|
||||||
|
|
||||||
class Mesh(initialTriangle: Triangle) {
|
class Mesh(initialTriangle: Triangle) {
|
||||||
|
|
||||||
|
@ -30,18 +30,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.poly2tri.shapes
|
package org.poly2tri.shapes
|
||||||
|
|
||||||
object Event extends Enumeration {
|
|
||||||
val point, edge = Value
|
|
||||||
}
|
|
||||||
|
|
||||||
case class Point(val x: Float, val y: Float) {
|
case class Point(val x: Float, val y: Float) {
|
||||||
|
|
||||||
// Pointers to next and previous points in Monontone Mountain
|
// Pointers to next and previous points in Monontone Mountain
|
||||||
var next, prev: Point = null
|
var next, prev: Point = null
|
||||||
// The setment this point belongs to
|
// The setment this point belongs to
|
||||||
var segment: Segment = null
|
var segment: Segment = null
|
||||||
// Point type for CDT
|
// Edge event pointer for CDT
|
||||||
var eventType: Event.Value = _
|
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)
|
||||||
@inline def +(p: Point) = Point(x + p.x, y + p.y)
|
@inline def +(p: Point) = Point(x + p.x, y + p.y)
|
||||||
|
@ -34,10 +34,7 @@ import scala.collection.mutable.{ArrayBuffer}
|
|||||||
|
|
||||||
// Represents a simple polygon's edge
|
// Represents a simple polygon's edge
|
||||||
class Segment(var p: Point, var q: Point) {
|
class Segment(var p: Point, var q: Point) {
|
||||||
|
|
||||||
p.segment = this
|
|
||||||
q.segment = this
|
|
||||||
|
|
||||||
// Pointers used for building trapezoidal map
|
// Pointers used for building trapezoidal map
|
||||||
var above, below: Trapezoid = null
|
var above, below: Trapezoid = null
|
||||||
// Montone mountain points
|
// Montone mountain points
|
||||||
@ -53,27 +50,4 @@ class Segment(var p: Point, var q: Point) {
|
|||||||
// Determines if this segment lies below the given point
|
// Determines if this segment lies below the given point
|
||||||
def < (point: Point) = (Math.floor(point.y) > Math.floor(slope * point.x + b))
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.poly2tri.shapes
|
package org.poly2tri.shapes
|
||||||
|
|
||||||
import shapes.Point
|
|
||||||
|
|
||||||
// Triangle-based data structures are know to have better performance than quad-edge structures
|
// 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"
|
// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
|
||||||
// "Triangulations in CGAL"
|
// "Triangulations in CGAL"
|
||||||
|
Loading…
Reference in New Issue
Block a user