updated demo framework

This commit is contained in:
Mason 2009-09-18 22:34:34 -04:00
parent 1f418be366
commit 6054e161da
3 changed files with 302 additions and 347 deletions

View File

@ -46,7 +46,7 @@ import cdt.CDT
object Poly2Tri {
def main(args: Array[String]) {
val container = new AppGameContainer(new Poly2TriDemo())
val container = new AppGameContainer(new Poly2TriDemo())
container.setDisplayMode(800,600,false)
container.start()
}
@ -54,7 +54,13 @@ object Poly2Tri {
}
class Poly2TriDemo extends BasicGame("Poly2Tri") {
object Algo extends Enumeration {
type Algo = Value
val CDT, Seidel, EarClip = Value
}
import Algo._
// Sedidel Triangulator
var seidel: Triangulator = null
var segments: ArrayBuffer[Segment] = null
@ -68,17 +74,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
// Sweep Line Constraied Delauney Triangulator (CDT)
var slCDT: CDT = null
var polyX: ArrayBuffer[Float] = null
var polyY: ArrayBuffer[Float] = null
var quit = false
var debug = false
var drawMap = false
var drawSegs = true
var hiLighter = 0
var drawEarClip = false
var drawCDT = true
var drawcdtMesh = false
var drawCDTMesh = false
val nazcaMonkey = "data/nazca_monkey.dat"
val nazcaHeron = "data/nazca_heron_old.dat"
@ -92,6 +91,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
var currentModel = dude
var doCDT = true
// The current algorithm
var algo = CDT
var mouseButton = 0
var mousePressed = false
@ -115,129 +116,107 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
def render(container: GameContainer, g: Graphics) {
g.drawString("'1-9' to cycle models, mouse to pan & zoom", 10, 520)
g.drawString("'SPACE' to show Seidel debug info", 10, 532)
g.drawString("'m' to show trapezoidal map (Seidel debug mode)", 10, 544)
g.drawString("'e' to switch Seidel / EarClip", 10, 556)
g.drawString("'d' to switch CDT / Seidel", 10, 568)
g.drawString("'c' to show CDT mesh, 's' to draw edges", 10, 580)
g.drawString("'1-9' to cycle models, mouse to pan & zoom", 10, 522)
g.drawString("'c,s,e' to switch CDT / Seidel / EarClip algos", 10, 537)
g.drawString("'t' to show trapezoidal map (Seidel)", 10, 552)
g.drawString("'m' to show triangle mesh (CDT)", 10, 567)
g.drawString("'d' to draw edges", 10, 582)
g.scale(scaleFactor, scaleFactor)
g.translate(deltaX, deltaY)
g.translate(deltaX, deltaY)
val red = new Color(1f, 0f,0.0f)
val blue = new Color(0f, 0f, 1f)
val green = new Color(0f, 1f, 0f)
val yellow = new Color(1f, 1f, 0f)
if(debug) {
val draw = if(drawMap) seidel.trapezoidMap else seidel.trapezoids
for(t <- draw) {
val polygon = new Polygon()
for(v <- t.vertices) {
polygon.addPoint(v.x, v.y)
}
if(!drawMap) {
val lCirc = new Circle(t.leftPoint.x, t.leftPoint.y, 4)
g.setColor(blue); g.draw(lCirc); g.fill(lCirc)
val rCirc = new Circle(t.rightPoint.x+5, t.rightPoint.y, 4)
g.setColor(yellow); g.draw(rCirc); g.fill(rCirc)
}
g.setColor(red)
g.draw(polygon)
}
}
if(!debug && !drawEarClip && !drawCDT) {
var i = 0
for(t <- seidel.polygons) {
val poly = new Polygon
t.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(red)
g.draw(poly)
algo match {
case Algo.Seidel => {
for(t <- seidel.polygons) {
val poly = new Polygon
t.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(red)
g.draw(poly)
}
if (drawMap) {
for(t <- seidel.trapezoidMap) {
val polygon = new Polygon()
for(v <- t.vertices) {
polygon.addPoint(v.x, v.y)
}
g.setColor(red)
g.draw(polygon)
}/*
for(mp <- seidel.monoPolies) {
val poly = new Polygon
mp.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(yellow)
g.draw(poly)
}*/
}
}
} else if (debug && drawMap && !drawEarClip){
for(mp <- seidel.monoPolies) {
val poly = new Polygon
mp.foreach(p => poly.addPoint(p.x, p.y))
g.setColor(yellow)
g.draw(poly)
case Algo.EarClip => {
earClipResults.foreach(t => {
val triangle = new Polygon
triangle.addPoint(t.x(0), t.y(0))
triangle.addPoint(t.x(1), t.y(1))
triangle.addPoint(t.x(2), t.y(2))
g.setColor(red)
g.draw(triangle)
})
}
case Algo.CDT => {
val draw = if(drawCDTMesh) slCDT.triangleMesh else slCDT.triangles
draw.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)
})
slCDT.debugTriangles.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(blue)
g.draw(triangle)
})
for(i <- 0 until slCDT.cList.size) {
val circ = new Circle(slCDT.cList(i).x, slCDT.cList(i).y, 0.5f)
g.setColor(blue); g.draw(circ); g.fill(circ)
}
}
case _ =>
}
if(drawSegs) {
g.setColor(green)
for(i <- 0 until segments.size) {
val s = segments(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
}
if(drawEarClip)
earClipResults.foreach(t => {
val triangle = new Polygon
triangle.addPoint(t.x(0), t.y(0))
triangle.addPoint(t.x(1), t.y(1))
triangle.addPoint(t.x(2), t.y(2))
g.setColor(red)
g.draw(triangle)
})
if(drawCDT) {
val draw = if(drawcdtMesh) slCDT.triangleMesh else slCDT.triangles
draw.foreach( t => {
if(false) {
for(i <- 0 to 2) {
val s = t.points(i)
val e = if(i == 2) t.points(0) else t.points(i + 1)
val j = if(i == 0) 2 else if(i == 1) 0 else 1
if(t.edges(j))
g.setColor(yellow)
else
g.setColor(red)
g.drawLine(s.x,s.y,e.x,e.y)
}
} else {
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)
}
})
slCDT.debugTriangles.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(blue)
g.draw(triangle)
})
//slCDT.cList.foreach(c => {
for(i <- 0 until slCDT.cList.size) {
val circ = new Circle(slCDT.cList(i).x, slCDT.cList(i).y, 0.5f)
g.setColor(blue); g.draw(circ); g.fill(circ)
}
//})
}
if(drawSegs) {
g.setColor(green)
for(i <- 0 until segments.size) {
val s = segments(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
}
if(currentModel == "data/dude.dat" && drawSegs) {
g.setColor(green)
for(i <- 0 until chestSegs.size) {
val s = chestSegs(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
for(i <- 0 until headSegs.size) {
val s = headSegs(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
}
if(currentModel == "data/dude.dat" && drawSegs) {
g.setColor(green)
for(i <- 0 until chestSegs.size) {
val s = chestSegs(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
for(i <- 0 until headSegs.size) {
val s = headSegs(i)
g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y)
}
}
}
@ -246,6 +225,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
* @param p The screen location that the mouse is down at.
*/
override def mousePressed(b: Int, x: Int, y: Int) {
mouseButton = b
mousePressed = true
mousePosOld = mousePos
@ -278,7 +258,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
mousePosOld = mousePos
mousePos = Point(x,y)
if(mousePressed) {
deltaX += mousePos.x - mousePosOld.x
deltaX += mousePos.x - mousePosOld.x
deltaY += mousePos.y - mousePosOld.y
}
}
@ -296,23 +276,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
// ESC
if(key == 1) quit = true
// SPACE
if(key == 57) debug = !debug
// UP
if(key == 200) {
hiLighter += 1
if (hiLighter == seidel.polygons.size)
hiLighter = 0
}
// DOWN
if(key == 208) {
hiLighter -= 1
if (hiLighter == -1)
hiLighter = seidel.polygons.size-1
}
if(c == 'm') drawMap = !drawMap
if(c == 'd') drawCDT = !drawCDT
if(c == '1') selectModel(nazcaMonkey)
if(c == '2') selectModel(bird)
if(c == '3') selectModel(strange)
@ -322,9 +286,16 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
if(c == '7') selectModel(nazcaHeron)
if(c == '8') selectModel(tank)
if(c == '9') selectModel(dude)
if(c == 's') drawSegs = !drawSegs
if(c == 'c') drawcdtMesh = !drawcdtMesh
if(c == 'e') {drawEarClip = !drawEarClip; drawCDT = false; selectModel(currentModel)}
if(c == 'd') drawSegs = !drawSegs
if(c == 'm') drawCDTMesh = !drawCDTMesh
if(c == 't') drawMap = !drawMap
if(c == 's') {algo = Seidel; selectModel(currentModel) }
if(c == 'c') {algo = CDT; selectModel(currentModel) }
if(c == 'e') {algo = EarClip; selectModel(currentModel) }
// Experimental...
if(c == 'r') slCDT.refine
}
@ -358,9 +329,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
case "data/dude.dat" =>
val clearPoint = Point(365, 427)
loadModel(dude, -1f, Point(100f, -200f), 10, clearPoint)
case _ =>
assert(false)
case _ =>
}
currentModel = model
}
@ -370,8 +339,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
println
println("************** " + model + " **************")
polyX = new ArrayBuffer[Float]
polyY = new ArrayBuffer[Float]
val polyX = new ArrayBuffer[Float]
val polyY = new ArrayBuffer[Float]
var points = new ArrayBuffer[Point]
val angle = Math.Pi
@ -379,12 +348,12 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
val s = line.replaceAll("\n", "")
val tokens = s.split("[ ]+")
if(tokens.size == 2) {
var x = tokens(0).toFloat
var y = tokens(1).toFloat
// 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)
var x = tokens(0).toFloat
var y = tokens(1).toFloat
// 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")
}
@ -392,91 +361,100 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
segments = new ArrayBuffer[Segment]
for(i <- 0 until points.size-1)
segments += new Segment(points(i), points(i+1))
segments += new Segment(points(i), points(i+1))
segments += new Segment(points.first, points.last)
println("Number of points = " + polyX.size)
println
if(doCDT) {
algo match {
case Algo.CDT => {
val pts = points.toArray
val t1 = System.nanoTime
slCDT = new CDT(pts, clearPoint)
val t1 = System.nanoTime
slCDT = new CDT(pts, clearPoint)
// Add some holes....
if(model == "data/dude.dat") {
val headHole = Array(Point(325f,437f), Point(320f,423f), Point(329f,413f), Point(332f,423f))
val chestHole = Array(Point(320.72342f,480f), Point(338.90617f,465.96863f),
Point(347.99754f,480.61584f), Point(329.8148f,510.41534f),
val chestHole = Array(Point(320.72342f,480f), Point(338.90617f,465.96863f),
Point(347.99754f,480.61584f), Point(329.8148f,510.41534f),
Point(339.91632f,480.11077f), Point(334.86556f,478.09046f))
// Tramsform the points
// Tramsform the points
for(i <- 0 until headHole.size) {
val hx = -headHole(i).x*scale + center.x
val hy = -headHole(i).y*scale + center.y
headHole(i) = Point(hx, hy)
}
for(i <- 0 until chestHole.size) {
val cx = -chestHole(i).x*scale + center.x
val cy = -chestHole(i).y*scale + center.y
chestHole(i) = Point(cx, cy)
}
for(i <- 0 until chestHole.size) {
val cx = -chestHole(i).x*scale + center.x
val cy = -chestHole(i).y*scale + center.y
chestHole(i) = Point(cx, cy)
}
chestSegs = new ArrayBuffer[Segment]
for(i <- 0 until chestHole.size-1)
chestSegs += new Segment(chestHole(i), chestHole(i+1))
chestSegs += new Segment(chestHole.first, chestHole.last)
for(i <- 0 until chestHole.size-1)
chestSegs += new Segment(chestHole(i), chestHole(i+1))
chestSegs += new Segment(chestHole.first, chestHole.last)
headSegs = new ArrayBuffer[Segment]
for(i <- 0 until headHole.size-1)
headSegs += new Segment(headHole(i), headHole(i+1))
headSegs += new Segment(headHole.first, headHole.last)
for(i <- 0 until headHole.size-1)
headSegs += new Segment(headHole(i), headHole(i+1))
headSegs += new Segment(headHole.first, headHole.last)
// Add the holes
slCDT.addHole(headHole)
slCDT.addHole(chestHole)
}
slCDT triangulate
val runTime = System.nanoTime - t1
println("CDT average (ms) = " + runTime*1e-6)
println("Number of triangles = " + slCDT.triangles.size)
println
}
if(!drawEarClip) {
// Sediel triangulation
val t1 = System.nanoTime
seidel = new Triangulator(points)
val runTime = System.nanoTime - t1
println("Poly2Tri average (ms) = " + runTime*1e-6)
println("Number of triangles = " + seidel.polygons.size)
} else {
// Earclip
earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles)
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
var xVerts = polyX.toArray
var yVerts = polyY.toArray
val t1 = System.nanoTime
earClip.triangulatePolygon(xVerts, yVerts, xVerts.size, earClipResults)
val runTime = System.nanoTime - t1
println
println("Earclip average (ms) = " + runTime*1e-6)
println("Number of triangles = " + earClip.numTriangles)
}
println("CDT average (ms) = " + runTime*1e-6)
println("Number of triangles = " + slCDT.triangles.size)
println
}
case Algo.Seidel => {
// Sediel triangulation
val t1 = System.nanoTime
seidel = new Triangulator(points)
val runTime = System.nanoTime - t1
println("Seidel average (ms) = " + runTime*1e-6)
println("Number of triangles = " + seidel.polygons.size)
}
case Algo.EarClip => {
// Earclip
earClipResults = new Array[poly2tri.earClip.Triangle](maxTriangles)
for(i <- 0 until earClipResults.size) earClipResults(i) = new poly2tri.earClip.Triangle
var xVerts = polyX.toArray
var yVerts = polyY.toArray
val xv = if(currentModel != "data/strange.dat") xVerts.reverse.toArray else xVerts
val yv = if(currentModel != "data/strange.dat") yVerts.reverse.toArray else yVerts
val t1 = System.nanoTime
earClip.triangulatePolygon(xv, yv, xVerts.size, earClipResults)
val runTime = System.nanoTime - t1
println
println("Earclip average (ms) = " + runTime*1e-6)
println("Number of triangles = " + earClip.numTriangles)
}
case _ =>
}
}
}
}

View File

@ -109,76 +109,65 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
// Create edges and connect end points; update edge event pointer
private def initEdges(pts: Array[Point]) {
// Connect pts
for(i <- 0 until pts.size-1) {
val endPoints = validatePoints(pts(i), pts(i+1))
val edge = new Segment(endPoints(0), endPoints(1))
endPoints(1).edges += edge
}
// Connect endpoints
val endPoints = validatePoints(pts.first, pts.last)
val edge = new Segment(endPoints(0), endPoints(1))
endPoints(1).edges += edge
}
private def validatePoints(p1: Point, p2: Point): List[Point] = {
if(p1.y > p2.y) {
// For CDT we want q to be the point with > y
return List(p2, p1)
} else if(p1.y == p2.y) {
return List(p2, p1)
} else if(p1.y == p2.y) {
// If y values are equal, make sure point with smaller x value
// is to the left
if(p1.x > p2.x) {
return List(p2, p1)
} else if(p1.x == p2.x) {
throw new Exception("Duplicate point")
}
throw new Exception("Duplicate point")
}
}
List(p1, p2)
}
// Merge sort: O(n log n)
private def pointSort: List[Point] =
Util.msort((p1: Point, p2: Point) => p1 > p2)(points)
Util.msort((p1: Point, p2: Point) => p1 > p2)(points)
// Implement sweep-line
private def sweep {
val size = if(refined) 1 else points.size
for(i <- 1 until points.size) {
val point = points(i)
// Process Point event
val node = pointEvent(point)
// Process edge events
point.edges.foreach(e => edgeEvent(e, node))
}
}
// Final step in the sweep-line CDT
// Clean exterior triangles
private def finalization {
var found = false
mesh.map.foreach(m => {
if(!found)
// Mark the originating clean triangle
if(m.pointIn(clearPoint)) {
found = true
cleanTri = m
}
// Mark the originating clean triangle
if(m.pointIn(clearPoint)) {
found = true
cleanTri = m
}
m.markNeighborEdges
})
// Collect interior triangles constrained by edges
mesh clean cleanTri
}
// Delauney Refinement: Refine triangules using Steiner points
@ -191,13 +180,13 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
mesh.triangles.foreach(t => {
if(t.thin) {
val center = Util.circumcenter(t.points(0), t.points(1), t.points(2))
cList += center
addPoint(center)
cList += center
addPoint(center)
}
})
// Retriangulate
if(cList.size > 0)
triangulate
triangulate
}
// Point event
@ -224,63 +213,63 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
// Locate the first intersected triangle
val firstTriangle = if(!node.triangle.contains(edge.q))
node.triangle
node.triangle
else
node.triangle.locateFirst(edge)
node.triangle.locateFirst(edge)
if(firstTriangle != null && !firstTriangle.contains(edge)) {
// Interior mesh traversal - edge is "burried" in the mesh
// Constrained edge lies below the advancing front. Traverse through intersected triangles,
// form empty pseudo-polygons, and re-triangulate
// Interior mesh traversal - edge is "burried" in the mesh
// Constrained edge lies below the advancing front. Traverse through intersected triangles,
// form empty pseudo-polygons, and re-triangulate
// Collect intersected triangles
val tList = new ArrayBuffer[Triangle]
tList += firstTriangle
// Collect intersected triangles
val tList = new ArrayBuffer[Triangle]
tList += firstTriangle
while(tList.last != null && !tList.last.contains(edge.p))
tList += tList.last.findNeighbor(edge.p)
while(tList.last != null && !tList.last.contains(edge.p))
tList += tList.last.findNeighbor(edge.p)
// TODO: Finish implementing edge insertion which combines advancing front (AF)
// and triangle traversal respectively. See figure 14(a) from Domiter et al.
// Should only occur with complex patterns of interior points
// Already added provision for transitioning from AFront traversal to
// interior mesh traversal - may need to add the opposite case
if(tList.last == null)
throw new Exception("Not implemented yet - interior points too complex")
// TODO: Finish implementing edge insertion which combines advancing front (AF)
// and triangle traversal respectively. See figure 14(a) from Domiter et al.
// Should only occur with complex patterns of interior points
// Already added provision for transitioning from AFront traversal to
// interior mesh traversal - may need to add the opposite case
if(tList.last == null)
throw new Exception("Not implemented yet - interior points too complex")
// Neighbor triangles
// HashMap or set may improve performance
val nTriangles = new ArrayBuffer[Triangle]
// Neighbor triangles
// HashMap or set may improve performance
val nTriangles = new ArrayBuffer[Triangle]
// Remove old triangles; collect neighbor triangles
// Keep duplicates out
tList.foreach(t => {
t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n)
mesh.map -= t
})
// Remove old triangles; collect neighbor triangles
// Keep duplicates out
tList.foreach(t => {
t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n)
mesh.map -= t
})
// Using a hashMap or set may improve performance
val lPoints = new ArrayBuffer[Point]
val rPoints = new ArrayBuffer[Point]
// Using a hashMap or set may improve performance
val lPoints = new ArrayBuffer[Point]
val rPoints = new ArrayBuffer[Point]
// Collect points left and right of edge
tList.foreach(t => {
t.points.foreach(p => {
if(p != edge.q && p != edge.p) {
if(Util.orient2d(edge.q, edge.p, p) > 0 ) {
// Keep duplicate points out
if(!lPoints.contains(p)) {
lPoints += p
}
} else {
// Keep duplicate points out
if(!rPoints.contains(p))
rPoints += p
// Collect points left and right of edge
tList.foreach(t => {
t.points.foreach(p => {
if(p != edge.q && p != edge.p) {
if(Util.orient2d(edge.q, edge.p, p) > 0 ) {
// Keep duplicate points out
if(!lPoints.contains(p)) {
lPoints += p
}
}
})
})
} else {
// Keep duplicate points out
if(!rPoints.contains(p))
rPoints += p
}
}
})
})
// Triangulate empty areas.
val T1 = new ArrayBuffer[Triangle]
@ -296,8 +285,8 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
// Update advancing front
val ahead = (edge.p.x > edge.q.x)
val point1 = if(ahead) edge.q else edge.p
val point2 = if(ahead) edge.p else edge.q
val point1 = if(ahead) edge.q else edge.p
val point2 = if(ahead) edge.p else edge.q
val sNode = if(ahead) node else aFront.locate(point1)
val eNode = aFront.locate(point2)
@ -333,23 +322,23 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
if(ahead) {
// Scan right
pNode = pNode.next
while(pNode.point != edge.p && !aboveEdge) {
points += pNode.point
while(pNode.point != edge.p && !aboveEdge) {
points += pNode.point
nTriangles += pNode.triangle
pNode = pNode.next
pNode = pNode.next
aboveEdge = edge < pNode.point
}
}
} else {
// Scan left
pNode = pNode.prev
while(pNode.point != edge.p && !aboveEdge) {
points += pNode.point
// Scan left
pNode = pNode.prev
while(pNode.point != edge.p && !aboveEdge) {
points += pNode.point
nTriangles += pNode.triangle
pNode = pNode.prev
pNode = pNode.prev
aboveEdge = edge < pNode.point
}
nTriangles += pNode.triangle
}
}
nTriangles += pNode.triangle
}
val point2 = if(aboveEdge) {
val p1 = pNode.point
@ -396,11 +385,11 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
private def edgeNeighbors(nTriangles: ArrayBuffer[Triangle], T: ArrayBuffer[Triangle]) {
for(t1 <- nTriangles)
for(t2 <- T)
t2.markNeighbor(t1)
for(t2 <- T)
t2.markNeighbor(t1)
for(i <- 0 until T.size)
for(j <- i+1 until T.size)
for(j <- i+1 until T.size)
T(i).markNeighbor(T(j))
}
@ -455,7 +444,7 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
if(node2.prev != null) {
var angle = 0.0
do {
angle = fill(node2)
angle = fill(node2)
node2 = node2.prev
} while(angle <= PI_2 && angle >= -PI_2 && node2.prev != null)
}
@ -468,31 +457,28 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
val a = (node.prev.point - node.point)
val b = (node.next.point - node.point)
val angle = Math.atan2(a cross b, a dot b)
// Is the angle acute?
// Is the angle acute?
if(angle <= PI_2 && angle >= -PI_2) {
val points = Array(node.prev.point, node.point, node.next.point)
val triangle = new Triangle(points)
// Update neighbor pointers
node.prev.triangle.markNeighbor(triangle)
node.triangle.markNeighbor(triangle)
// Update neighbor pointers
node.prev.triangle.markNeighbor(triangle)
node.triangle.markNeighbor(triangle)
mesh.map += triangle
aFront -= (node.prev, node, triangle)
}
angle
angle
}
// Circumcircle test.
// Determines if point d lies inside triangle abc's circumcircle
private def illegal(a: Point, b: Point, c: Point, d: Point): Boolean = {
val ccw = Util.orient2d(a, b, c) > 0
// Make sure abc is oriented counter-clockwise
if(ccw)
Util.incircle(a, b, c, d)
Util.incircle(a, b, c, d)
else
Util.incircle(a, c, b, d)
}
// Ensure adjacent triangles are legal
@ -509,33 +495,33 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
if(illegal && !t2.finalized) {
// Flip edge and rotate everything clockwise
// Flip edge and rotate everything clockwise
// Legalize points
// Legalize points
t1.legalize(oPoint)
t2.legalize(oPoint, point)
// Update neighbors
// Update neighbors
// Copy old neighbors
// Copy old neighbors
val neighbors = List(t2.neighbors(0), t2.neighbors(1), t2.neighbors(2))
// Clear old neighbors
t2.clearNeighbors
// Update new neighbors
// Clear old neighbors
t2.clearNeighbors
// Update new neighbors
for(n <- neighbors) {
if(n != null) {
t1.markNeighbor(n)
t2.markNeighbor(n)
t2.markNeighbor(n)
}
}
t2.markNeighbor(t1)
t2.markNeighbor(t1)
// Don't legalize these triangles again
t2.finalized = true
t1.finalized = true
// Don't legalize these triangles again
t2.finalized = true
t1.finalized = true
// Update advancing front
aFront.insertLegalized(t1.points(1), t1, node)
// Update advancing front
aFront.insertLegalized(t1.points(1), t1, node)
} else {
@ -545,7 +531,6 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
aFront.insert(point, t1, node)
}
}
// The triangle mesh

View File

@ -38,21 +38,10 @@ class EarClip {
var numTriangles = 0
def triangulatePolygon(x: Array[Float], y: Array[Float], vn: Int, results: Array[Triangle]): Int = {
/*
val p1 = Point(x(0), y(0))
val p2 = Point(x(1), y(1))
val p3 = Point(x(2), y(2))
val ccw = Util.orient2d(p1, p2, p3) > 0
*/
val xv = x.reverse.toArray
val yv = y.reverse.toArray
if (vn < 3) return 0
var vNum = vn
def triangulatePolygon(xv: Array[Float], yv: Array[Float], vn: Int, results: Array[Triangle]): Int = {
if (vn < 3) return 0
var vNum = vn
//Recurse and split on pinch points
val pA = new Poly
@ -133,15 +122,18 @@ class EarClip {
System.out.println("Couldn't find an ear, dumping remaining poly:\n");
System.out.println("Please submit this dump to ewjordan at Box2d forums\n");
assert(false)
for (i <- 0 until bufferSize) {
results(i).set(buffer(i));
}
if (bufferSize > 0) return bufferSize;
else {
numTriangles = -1
return numTriangles
}
else {
numTriangles = -1
return numTriangles
}
}
// Clip off the ear: