updated comments; optimizations

This commit is contained in:
zzzzrrr 2009-08-21 22:28:49 -04:00
parent 122b2d6f40
commit 5811c2c0d1
6 changed files with 46 additions and 16 deletions

View File

@ -413,9 +413,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
headSegs = new ArrayBuffer[Segment] headSegs = new ArrayBuffer[Segment]
for(i <- 0 until headHole.size-1) for(i <- 0 until headHole.size-1)
chestSegs += new Segment(headHole(i), headHole(i+1)) headSegs += new Segment(headHole(i), headHole(i+1))
chestSegs += new Segment(headHole.first, headHole.last) headSegs += new Segment(headHole.first, headHole.last)
// Add the holes
slCDT.addHole(headHole) slCDT.addHole(headHole)
slCDT.addHole(chestHole) slCDT.addHole(chestHole)
} }

View File

@ -99,6 +99,7 @@ class AFront(iTriangle: Triangle) {
val point2 = edge.p val point2 = edge.p
// Scan the advancing front and update Node triangle pointers // Scan the advancing front and update Node triangle pointers
// TODO: Make this more efficient
while(node != null && node != eNode.next) { while(node != null && node != eNode.next) {
T2.foreach(t => { T2.foreach(t => {
if(t.contains(node.point, node.next.point)) if(t.contains(node.point, node.next.point))

View File

@ -45,6 +45,7 @@ import utils.Util
// and triangle traversal respectively. See figure 14(a) from Domiter et al. // and triangle traversal respectively. See figure 14(a) from Domiter et al.
// Although it may not be necessary for simple polygons.... // Although it may not be necessary for simple polygons....
// clearPoint is any interior point inside the polygon
class CDT(polyLine: Array[Point], clearPoint: Point) { class CDT(polyLine: Array[Point], clearPoint: Point) {
// Triangle list // Triangle list
@ -140,6 +141,7 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
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)
// Process Point event // Process Point event
val node = pointEvent(point) val node = pointEvent(point)
@ -149,18 +151,21 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
} }
// Final step in the sweep-line CDT algo // Final step in the sweep-line CDT
// Clean exterior triangles // Clean exterior triangles
private def finalization { private def finalization {
var found = false var found = false
mesh.map.foreach(m => { mesh.map.foreach(m => {
if(!found) if(!found)
// Mark the originating clean triangle
if(m.pointIn(clearPoint)) { if(m.pointIn(clearPoint)) {
found = true found = true
cleanTri = m cleanTri = m
} }
m.markNeighborEdges m.markNeighborEdges
}) })
// Collect interior triangles constrained by edges
mesh clean cleanTri mesh clean cleanTri
} }
@ -367,7 +372,7 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
do { do {
angle = fill(node1) angle = fill(node1)
node1 = node1.next node1 = node1.next
} while(angle <= PI_2 && node1.next != null) } while(angle <= PI_2 && angle >= -PI_2 && node1.next != null)
} }
var node2 = n.prev var node2 = n.prev
@ -377,17 +382,19 @@ class CDT(polyLine: Array[Point], clearPoint: Point) {
do { do {
angle = fill(node2) angle = fill(node2)
node2 = node2.prev node2 = node2.prev
} while(angle <= PI_2 && node2.prev != null) } while(angle <= PI_2 && angle >= -PI_2 && node2.prev != null)
} }
} }
// Fill empty space with a triangle // Fill empty space with a triangle
private def fill(node: Node): Double = { private def fill(node: Node): Double = {
val a = (node.prev.point - node.point) val a = (node.prev.point - node.point)
val b = (node.next.point - node.point) val b = (node.next.point - node.point)
val angle = Math.abs(Math.atan2(a cross b, a dot b)) val angle = Math.atan2(a cross b, a dot b)
if(angle <= PI_2) { // Is the angle acute?
if(angle <= PI_2 && angle >= -PI_2) {
val points = Array(node.prev.point, node.point, node.next.point) val points = Array(node.prev.point, node.point, node.next.point)
val triangle = new Triangle(points) val triangle = new Triangle(points)
// Update neighbor pointers // Update neighbor pointers

View File

@ -30,20 +30,23 @@
*/ */
package org.poly2tri.cdt package org.poly2tri.cdt
import scala.collection.mutable.{HashSet, ArrayBuffer} import scala.collection.mutable.ArrayBuffer
import shapes.{Point, Triangle} import shapes.{Point, Triangle}
class Mesh { class Mesh {
// Triangles that constitute the mesh // Triangles that constitute the mesh
val map = HashSet.empty[Triangle] val map = new ArrayBuffer[Triangle]
// Debug triangles // Debug triangles
val debug = HashSet.empty[Triangle] val debug = new ArrayBuffer[Triangle]
val triangles = new ArrayBuffer[Triangle] val triangles = new ArrayBuffer[Triangle]
// Recursively collect triangles // Recursively collect interior triangles and clean the mesh
// Excludes exterior triangles outside constrained edges
// TODO: Investigate depth first search as an alternative
def clean(triangle: Triangle) { def clean(triangle: Triangle) {
if(triangle != null && !triangle.interior) { if(triangle != null && !triangle.interior) {
triangle.interior = true triangle.interior = true
triangles += triangle triangles += triangle

View File

@ -30,7 +30,6 @@
*/ */
package org.poly2tri.shapes package org.poly2tri.shapes
import scala.collection.mutable.HashSet
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import utils.Util import utils.Util
@ -80,6 +79,7 @@ class Triangle(val points: Array[Point]) {
neighbors(2) = t neighbors(2) = t
t.markNeighbor(points(0), points(1), this) t.markNeighbor(points(0), points(1), this)
} }
} }
def clearNeighbors { def clearNeighbors {
@ -87,6 +87,7 @@ class Triangle(val points: Array[Point]) {
} }
def oppositePoint(t: Triangle): Point = { def oppositePoint(t: Triangle): Point = {
assert(t != this, "self-pointer error") assert(t != this, "self-pointer error")
if(points(0) == t.points(1)) if(points(0) == t.points(1))
points(1) points(1)
@ -123,6 +124,7 @@ class Triangle(val points: Array[Point]) {
// Locate first triangle crossed by constrained edge // Locate first triangle crossed by constrained edge
def locateFirst(edge: Segment): Triangle = { def locateFirst(edge: Segment): Triangle = {
if(contains(edge)) this if(contains(edge)) this
if(edge.q == points(0)) if(edge.q == points(0))
search(points(1), points(2), edge, neighbors(2)) search(points(1), points(2), edge, neighbors(2))
@ -171,6 +173,7 @@ class Triangle(val points: Array[Point]) {
// The neighbor clockwise to given point // The neighbor clockwise to given point
def neighborCW(point: Point): Triangle = { def neighborCW(point: Point): Triangle = {
if(point == points(0)) { if(point == points(0)) {
neighbors(1) neighbors(1)
}else if(point == points(1)) { }else if(point == points(1)) {
@ -181,6 +184,7 @@ class Triangle(val points: Array[Point]) {
// The neighbor counter-clockwise to given point // The neighbor counter-clockwise to given point
def neighborCCW(oPoint: Point): Triangle = { def neighborCCW(oPoint: Point): Triangle = {
if(oPoint == points(0)) { if(oPoint == points(0)) {
neighbors(2) neighbors(2)
}else if(oPoint == points(1)) { }else if(oPoint == points(1)) {
@ -191,6 +195,7 @@ class Triangle(val points: Array[Point]) {
// The neighbor clockwise to given point // The neighbor clockwise to given point
def neighborAcross(point: Point): Triangle = { def neighborAcross(point: Point): Triangle = {
if(point == points(0)) { if(point == points(0)) {
neighbors(0) neighbors(0)
}else if(point == points(1)) { }else if(point == points(1)) {
@ -201,6 +206,7 @@ class Triangle(val points: Array[Point]) {
// The point counter-clockwise to given point // The point counter-clockwise to given point
def pointCCW(point: Point): Point = { def pointCCW(point: Point): Point = {
if(point == points(0)) { if(point == points(0)) {
points(1) points(1)
} else if(point == points(1)) { } else if(point == points(1)) {
@ -214,6 +220,7 @@ class Triangle(val points: Array[Point]) {
// The point counter-clockwise to given point // The point counter-clockwise to given point
def pointCW(point: Point): Point = { def pointCW(point: Point): Point = {
if(point == points(0)) { if(point == points(0)) {
points(2) points(2)
} else if(point == points(1)) { } else if(point == points(1)) {
@ -227,6 +234,7 @@ class Triangle(val points: Array[Point]) {
// Legalized triangle by rotating clockwise around point(0) // Legalized triangle by rotating clockwise around point(0)
def legalize(oPoint: Point) { def legalize(oPoint: Point) {
points(1) = points(0) points(1) = points(0)
points(0) = points(2) points(0) = points(2)
points(2) = oPoint points(2) = oPoint
@ -234,6 +242,7 @@ class Triangle(val points: Array[Point]) {
// Legalize triagnle by rotating clockwise around oPoint // Legalize triagnle by rotating clockwise around oPoint
def legalize(oPoint: Point, nPoint: Point) { def legalize(oPoint: Point, nPoint: Point) {
if(oPoint == points(0)) { if(oPoint == points(0)) {
points(1) = points(0) points(1) = points(0)
points(0) = points(2) points(0) = points(2)
@ -257,6 +266,7 @@ class Triangle(val points: Array[Point]) {
// Check if legalized triangle will be collinear // Check if legalized triangle will be collinear
def collinear(oPoint: Point, nPoint: Point): Boolean = { def collinear(oPoint: Point, nPoint: Point): Boolean = {
if(oPoint == points(0)) { if(oPoint == points(0)) {
Util.collinear(points(0), points(2), nPoint) Util.collinear(points(0), points(2), nPoint)
} else if (oPoint == points(1)) { } else if (oPoint == points(1)) {
@ -268,6 +278,7 @@ class Triangle(val points: Array[Point]) {
// Rotate neighbors clockwise around give point. Share diagnal with triangle // Rotate neighbors clockwise around give point. Share diagnal with triangle
def rotateNeighborsCW(oPoint: Point, triangle: Triangle) { def rotateNeighborsCW(oPoint: Point, triangle: Triangle) {
if(oPoint == points(0)) { if(oPoint == points(0)) {
neighbors(2) = neighbors(1) neighbors(2) = neighbors(1)
neighbors(1) = null neighbors(1) = null
@ -289,6 +300,7 @@ class Triangle(val points: Array[Point]) {
// Finalize edge marking // Finalize edge marking
def markNeighborEdges { def markNeighborEdges {
for(i <- 0 to 2) for(i <- 0 to 2)
if(edges(i)) if(edges(i))
i match { i match {
@ -299,6 +311,7 @@ class Triangle(val points: Array[Point]) {
} }
def markEdge(triangle: Triangle) { def markEdge(triangle: Triangle) {
for(i <- 0 to 2) for(i <- 0 to 2)
if(edges(i)) if(edges(i))
i match { i match {
@ -309,6 +322,7 @@ class Triangle(val points: Array[Point]) {
} }
def markEdge(T: ArrayBuffer[Triangle]) { def markEdge(T: ArrayBuffer[Triangle]) {
for(t <- T) { for(t <- T) {
for(i <- 0 to 2) for(i <- 0 to 2)
if(t.edges(i)) if(t.edges(i))
@ -322,6 +336,7 @@ class Triangle(val points: Array[Point]) {
// Mark edge as constrained // Mark edge as constrained
def markEdge(p: Point, q: Point) { def markEdge(p: Point, q: Point) {
if((q == points(0) && p == points(1)) || (q == points(1) && p == points(0))) { if((q == points(0) && p == points(1)) || (q == points(1) && p == points(0))) {
finalized = true finalized = true
edges(2) = true edges(2) = true
@ -335,12 +350,14 @@ class Triangle(val points: Array[Point]) {
} }
def area = { def area = {
val b = points(0).x - points(1).x val b = points(0).x - points(1).x
val h = points(2).y - points(1).y val h = points(2).y - points(1).y
(b*h*0.5f) (b*h*0.5f)
} }
def centroid: Point = { def centroid: Point = {
val cx = (points(0).x + points(1).x + points(2).x)/3f val cx = (points(0).x + points(1).x + points(2).x)/3f
val cy = (points(0).y + points(1).y + points(2).y)/3f val cy = (points(0).y + points(1).y + points(2).y)/3f
Point(cx, cy) Point(cx, cy)

View File

@ -14,7 +14,7 @@ object Util {
val ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon val ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon
val iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon val iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon
// From "Scala By Example," by Martin Odersky // Refursive merge sort, from "Scala By Example," by Martin Odersky
def msort[A](less: (A, A) => Boolean)(xs: List[A]): List[A] = { def msort[A](less: (A, A) => Boolean)(xs: List[A]): List[A] = {
def merge(xs1: List[A], xs2: List[A]): List[A] = def merge(xs1: List[A], xs2: List[A]): List[A] =
if (xs1.isEmpty) xs2 if (xs1.isEmpty) xs2
@ -26,6 +26,7 @@ object Util {
else merge(msort(less)(xs take n), msort(less)(xs drop n)) else merge(msort(less)(xs take n), msort(less)(xs drop n))
} }
// Insertion sort - best for lists <= 10 elements
def insertSort[A](less: (A, A) => Boolean)(xs: ArrayBuffer[A]): ArrayBuffer[A] = { def insertSort[A](less: (A, A) => Boolean)(xs: ArrayBuffer[A]): ArrayBuffer[A] = {
var j = 1 var j = 1
while(j < xs.size){ while(j < xs.size){
@ -46,7 +47,7 @@ object Util {
val d = Math.abs((p2-p1) cross (p1-p3)) val d = Math.abs((p2-p1) cross (p1-p3))
if(Math.abs(d) <= COLLINEAR_SLOP) if(d <= COLLINEAR_SLOP)
true true
else else
false false
@ -157,7 +158,7 @@ object Util {
(center, radius) (center, radius)
} }
def det(p1: Point, p2: Point, p3: Point): Float = { private def det(p1: Point, p2: Point, p3: Point): Float = {
val a11 = p1.x val a11 = p1.x
val a12 = p1.y val a12 = p1.y
@ -171,7 +172,7 @@ object Util {
} }
def detC(p1: Point, p2: Point, p3: Point): Float = { private def detC(p1: Point, p2: Point, p3: Point): Float = {
val a11 = p1.x*p1.x + p1.y*p1.y val a11 = p1.x*p1.x + p1.y*p1.y
val a12 = p1.x val a12 = p1.x