mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-30 01:03:30 +01:00
bug hunting
This commit is contained in:
parent
dfcec3e307
commit
7af2a3f5b5
@ -79,7 +79,7 @@ class MonotoneMountain {
|
|||||||
def triangulate {
|
def triangulate {
|
||||||
|
|
||||||
// Establish the proper sign
|
// Establish the proper sign
|
||||||
positive = initAngle
|
positive = angleSign
|
||||||
// create monotone polygon - for dubug purposes
|
// create monotone polygon - for dubug purposes
|
||||||
genMonoPoly
|
genMonoPoly
|
||||||
|
|
||||||
@ -111,11 +111,10 @@ class MonotoneMountain {
|
|||||||
|
|
||||||
// Remove ear, update angles and convex list
|
// Remove ear, update angles and convex list
|
||||||
remove(ear)
|
remove(ear)
|
||||||
if(valid(a)) convexPoints.enqueue(a);
|
if(valid(a)) convexPoints.enqueue(a)
|
||||||
if(valid(c)) convexPoints.enqueue(c)
|
if(valid(c)) convexPoints.enqueue(c)
|
||||||
|
|
||||||
}
|
}
|
||||||
assert(size <= 2, "Triangulation bug")
|
assert(size <= 3, "Triangulation bug")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +135,7 @@ class MonotoneMountain {
|
|||||||
Math.atan2(a cross b, a dot b)
|
Math.atan2(a cross b, a dot b)
|
||||||
}
|
}
|
||||||
|
|
||||||
def initAngle = {
|
def angleSign = {
|
||||||
val a = (head.next - head)
|
val a = (head.next - head)
|
||||||
val b = (tail - head)
|
val b = (tail - head)
|
||||||
(Math.atan2(a cross b, a dot b) >= 0)
|
(Math.atan2(a cross b, a dot b) >= 0)
|
||||||
@ -144,10 +143,8 @@ class MonotoneMountain {
|
|||||||
|
|
||||||
// Determines if the inslide angle is convex or reflex
|
// Determines if the inslide angle is convex or reflex
|
||||||
private def convex(p: Point) = {
|
private def convex(p: Point) = {
|
||||||
if(positive != (angle(p) >= 0))
|
if(positive != (angle(p) >= 0)) false
|
||||||
false
|
else true
|
||||||
else
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def lastTriangle {
|
private def lastTriangle {
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.poly2tri
|
package org.poly2tri
|
||||||
|
|
||||||
import collection.jcl.ArrayList
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
// Node for a Directed Acyclic graph (DAG)
|
// Node for a Directed Acyclic graph (DAG)
|
||||||
abstract class Node(var left: Node, var right: Node) {
|
abstract class Node(var left: Node, var right: Node) {
|
||||||
@ -38,7 +38,7 @@ abstract class Node(var left: Node, var right: Node) {
|
|||||||
if(left != null) left.parentList += this
|
if(left != null) left.parentList += this
|
||||||
if(right != null) right.parentList += this
|
if(right != null) right.parentList += this
|
||||||
|
|
||||||
var parentList = new ArrayList[Node]
|
var parentList = new ArrayBuffer[Node]
|
||||||
|
|
||||||
def locate(s: Segment): Sink
|
def locate(s: Segment): Sink
|
||||||
|
|
||||||
@ -46,13 +46,9 @@ abstract class Node(var left: Node, var right: Node) {
|
|||||||
// Make sure parent pointers are updated
|
// Make sure parent pointers are updated
|
||||||
def replace(node: Node) {
|
def replace(node: Node) {
|
||||||
for(parent <- node.parentList) {
|
for(parent <- node.parentList) {
|
||||||
// Select the correct node (left or right child)
|
// Select the correct node to replace (left or right child)
|
||||||
if(parent.left == node) {
|
if(parent.left == node) parent.left = this
|
||||||
parent.left = this
|
else parent.right = this
|
||||||
} else {
|
|
||||||
parent.right = this
|
|
||||||
}
|
|
||||||
// Decouple the node
|
|
||||||
parentList += parent
|
parentList += parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
var hiLighter = 0
|
var hiLighter = 0
|
||||||
|
|
||||||
def init(container: GameContainer) {
|
def init(container: GameContainer) {
|
||||||
snake
|
poly
|
||||||
}
|
}
|
||||||
|
|
||||||
def update(gc: GameContainer, delta: Int) {
|
def update(gc: GameContainer, delta: Int) {
|
||||||
@ -81,7 +81,6 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
if(debug) {
|
if(debug) {
|
||||||
val draw = if(drawMap) tesselator.trapezoidMap else tesselator.trapezoids
|
val draw = if(drawMap) tesselator.trapezoidMap else tesselator.trapezoids
|
||||||
for(t <- draw) {
|
for(t <- draw) {
|
||||||
assert(t.rightPoint != t.leftPoint)
|
|
||||||
val polygon = new Polygon()
|
val polygon = new Polygon()
|
||||||
for(v <- t.vertices) {
|
for(v <- t.vertices) {
|
||||||
polygon.addPoint(v.x, v.y)
|
polygon.addPoint(v.x, v.y)
|
||||||
@ -100,7 +99,6 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
if(!debug) {
|
if(!debug) {
|
||||||
var i = 0
|
var i = 0
|
||||||
for(t <- tesselator.triangles) {
|
for(t <- tesselator.triangles) {
|
||||||
if(t.size < 3) println("wtf")
|
|
||||||
val triangle = new Polygon
|
val triangle = new Polygon
|
||||||
t.foreach(p => triangle.addPoint(p.x, p.y))
|
t.foreach(p => triangle.addPoint(p.x, p.y))
|
||||||
g.setColor(red)
|
g.setColor(red)
|
||||||
|
@ -44,7 +44,6 @@ class QueryGraph(var head: Node) {
|
|||||||
val trapezoids = new ArrayBuffer[Trapezoid]
|
val trapezoids = new ArrayBuffer[Trapezoid]
|
||||||
trapezoids += locate(s)
|
trapezoids += locate(s)
|
||||||
var j = 0
|
var j = 0
|
||||||
try {
|
|
||||||
while(s.q.x > trapezoids(j).rightPoint.x) {
|
while(s.q.x > trapezoids(j).rightPoint.x) {
|
||||||
if(s > trapezoids(j).rightPoint) {
|
if(s > trapezoids(j).rightPoint) {
|
||||||
trapezoids += trapezoids(j).upperRight
|
trapezoids += trapezoids(j).upperRight
|
||||||
@ -53,9 +52,6 @@ class QueryGraph(var head: Node) {
|
|||||||
}
|
}
|
||||||
j += 1
|
j += 1
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
case e => println("# of Trapezoids = " + j)
|
|
||||||
}
|
|
||||||
trapezoids
|
trapezoids
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,31 +59,32 @@ class QueryGraph(var head: Node) {
|
|||||||
if(sink.parentList.size == 0) {
|
if(sink.parentList.size == 0) {
|
||||||
head = node
|
head = node
|
||||||
} else {
|
} else {
|
||||||
node.replace(sink)
|
node replace sink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def case1(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) {
|
def case1(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
|
||||||
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
|
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
|
||||||
val qNode = new XNode(s.q, yNode, Sink.init(tList(3)))
|
val qNode = new XNode(s.q, yNode, Sink.init(tList(3)))
|
||||||
val pNode = new XNode(s.p, Sink.init(tList(0)), qNode)
|
val pNode = new XNode(s.p, Sink.init(tList(0)), qNode)
|
||||||
replace(sink, pNode)
|
replace(sink, pNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
def case2(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) {
|
def case2(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
|
||||||
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
|
val yNode = new YNode(s, Sink.init(tList(1)), Sink.init(tList(2)))
|
||||||
val pNode = new XNode(s.p, Sink.init(tList(0)), yNode)
|
val pNode = new XNode(s.p, Sink.init(tList(0)), yNode)
|
||||||
replace(sink, pNode)
|
replace(sink, pNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
def case3(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) {
|
def case3(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
|
||||||
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
||||||
replace(sink, yNode)
|
replace(sink, yNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
def case4(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) {
|
def case4(sink: Sink, s: Segment, tList: Array[Trapezoid]) {
|
||||||
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
||||||
val qNode = new XNode(s.q, yNode, Sink.init(tList(2)))
|
val qNode = new XNode(s.q, yNode, Sink.init(tList(2)))
|
||||||
replace(sink, qNode)
|
replace(sink, qNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,12 @@ package org.poly2tri
|
|||||||
object Sink {
|
object Sink {
|
||||||
|
|
||||||
def init(trapezoid: Trapezoid) = {
|
def init(trapezoid: Trapezoid) = {
|
||||||
if(trapezoid.sink != null) {
|
if(trapezoid.sink != null)
|
||||||
trapezoid.sink
|
trapezoid.sink
|
||||||
} else {
|
else
|
||||||
new Sink(trapezoid)
|
new Sink(trapezoid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class Sink(val trapezoid: Trapezoid) extends Node(null, null) {
|
class Sink(val trapezoid: Trapezoid) extends Node(null, null) {
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.poly2tri
|
package org.poly2tri
|
||||||
|
|
||||||
import scala.collection.mutable.{Map, HashSet, ArrayBuffer}
|
import scala.collection.mutable.{HashSet, ArrayBuffer}
|
||||||
|
|
||||||
// See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
|
// See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
|
||||||
|
|
||||||
@ -70,11 +70,11 @@ class TrapezoidalMap {
|
|||||||
assert(s.p.x != s.q.x)
|
assert(s.p.x != s.q.x)
|
||||||
assert(s.p.x < s.q.x)
|
assert(s.p.x < s.q.x)
|
||||||
|
|
||||||
val trapezoids = new ArrayBuffer[Trapezoid]
|
val trapezoids = new Array[Trapezoid](4)
|
||||||
trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
||||||
trapezoids += new Trapezoid(s.p, s.q, t.top, s)
|
trapezoids(1) = new Trapezoid(s.p, s.q, t.top, s)
|
||||||
trapezoids += new Trapezoid(s.p, s.q, s, t.bottom)
|
trapezoids(2) = new Trapezoid(s.p, s.q, s, t.bottom)
|
||||||
trapezoids += new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
trapezoids(3) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
||||||
|
|
||||||
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
||||||
trapezoids(1).update(trapezoids(0), null, trapezoids(3), null)
|
trapezoids(1).update(trapezoids(0), null, trapezoids(3), null)
|
||||||
@ -93,10 +93,10 @@ class TrapezoidalMap {
|
|||||||
|
|
||||||
assert(s.p.x < s.q.x)
|
assert(s.p.x < s.q.x)
|
||||||
|
|
||||||
val trapezoids = new ArrayBuffer[Trapezoid]
|
val trapezoids = new Array[Trapezoid](3)
|
||||||
trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
trapezoids(0) = new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
||||||
trapezoids += new Trapezoid(s.p, t.rightPoint, t.top, s)
|
trapezoids(1) = new Trapezoid(s.p, t.rightPoint, t.top, s)
|
||||||
trapezoids += new Trapezoid(s.p, t.rightPoint, s, t.bottom)
|
trapezoids(2) = new Trapezoid(s.p, t.rightPoint, s, t.bottom)
|
||||||
|
|
||||||
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
||||||
trapezoids(1).update(trapezoids(0), null, t.upperRight, null)
|
trapezoids(1).update(trapezoids(0), null, t.upperRight, null)
|
||||||
@ -119,9 +119,9 @@ class TrapezoidalMap {
|
|||||||
val topCross = (tCross == t.top)
|
val topCross = (tCross == t.top)
|
||||||
val bottomCross = (bCross == t.bottom)
|
val bottomCross = (bCross == t.bottom)
|
||||||
|
|
||||||
val trapezoids = new ArrayBuffer[Trapezoid]
|
val trapezoids = new Array[Trapezoid](2)
|
||||||
trapezoids += {if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, t.rightPoint, t.top, s)}
|
trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, t.rightPoint, t.top, s)
|
||||||
trapezoids += {if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, t.rightPoint, s, t.bottom)}
|
trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, t.rightPoint, s, t.bottom)
|
||||||
|
|
||||||
if(topCross) {
|
if(topCross) {
|
||||||
trapezoids(0).upperRight = t.upperRight
|
trapezoids(0).upperRight = t.upperRight
|
||||||
@ -156,10 +156,10 @@ class TrapezoidalMap {
|
|||||||
val topCross = (tCross == t.top)
|
val topCross = (tCross == t.top)
|
||||||
val bottomCross = (bCross == t.bottom)
|
val bottomCross = (bCross == t.bottom)
|
||||||
|
|
||||||
val trapezoids = new ArrayBuffer[Trapezoid]
|
val trapezoids = new Array[Trapezoid](3)
|
||||||
trapezoids += {if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, s.q, t.top, s)}
|
trapezoids(0) = if(topCross) t.upperLeft else new Trapezoid(t.leftPoint, s.q, t.top, s)
|
||||||
trapezoids += {if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, s.q, s, t.bottom)}
|
trapezoids(1) = if(bottomCross) t.lowerLeft else new Trapezoid(t.leftPoint, s.q, s, t.bottom)
|
||||||
trapezoids += new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
trapezoids(2) = new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
||||||
|
|
||||||
if(topCross) {
|
if(topCross) {
|
||||||
trapezoids(0).upperRight = trapezoids(2)
|
trapezoids(0).upperRight = trapezoids(2)
|
||||||
|
@ -40,17 +40,17 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
|
|||||||
var triangles = new ArrayBuffer[Array[Point]]
|
var triangles = new ArrayBuffer[Array[Point]]
|
||||||
|
|
||||||
// Order and randomize the segments
|
// Order and randomize the segments
|
||||||
val segs = orderSegments
|
val segmentList = orderSegments
|
||||||
|
|
||||||
// Build the trapezoidal map and query graph
|
// Build the trapezoidal map and query graph
|
||||||
def process {
|
def process {
|
||||||
|
|
||||||
for(s <- segs) {
|
for(s <- segmentList) {
|
||||||
val traps = queryGraph.followSegment(s)
|
var traps = queryGraph.followSegment(s)
|
||||||
// Remove trapezoids from trapezoidal Map
|
// Remove trapezoids from trapezoidal Map
|
||||||
traps.foreach(trapezoidalMap.remove)
|
traps.foreach(trapezoidalMap.remove)
|
||||||
for(t <- traps) {
|
for(t <- traps) {
|
||||||
var tList: ArrayBuffer[Trapezoid] = null
|
var tList: Array[Trapezoid] = null
|
||||||
val containsP = t.contains(s.p)
|
val containsP = t.contains(s.p)
|
||||||
val containsQ = t.contains(s.q)
|
val containsQ = t.contains(s.q)
|
||||||
if(containsP && containsQ) {
|
if(containsP && containsQ) {
|
||||||
@ -93,13 +93,14 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
|
|||||||
for(t <- xMonoPoly(i).triangles)
|
for(t <- xMonoPoly(i).triangles)
|
||||||
triangles += t
|
triangles += t
|
||||||
|
|
||||||
println("# triangles = " + triangles.size)
|
//println("# triangles = " + triangles.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The trapezoidal map
|
// The trapezoidal map
|
||||||
def trapezoidMap = trapezoidalMap.map
|
def trapezoidMap = trapezoidalMap.map
|
||||||
// Trapezoid decomposition list
|
// Trapezoid decomposition list
|
||||||
var trapezoids = new ArrayBuffer[Trapezoid]
|
var trapezoids = new ArrayBuffer[Trapezoid]
|
||||||
|
|
||||||
// Monotone polygons - these are monotone mountains
|
// Monotone polygons - these are monotone mountains
|
||||||
def monoPolies: ArrayBuffer[ArrayBuffer[Point]] = {
|
def monoPolies: ArrayBuffer[ArrayBuffer[Point]] = {
|
||||||
val polies = new ArrayBuffer[ArrayBuffer[Point]]
|
val polies = new ArrayBuffer[ArrayBuffer[Point]]
|
||||||
@ -116,12 +117,12 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
|
|||||||
|
|
||||||
// Build a list of x-monotone mountains
|
// Build a list of x-monotone mountains
|
||||||
private def createMountains {
|
private def createMountains {
|
||||||
for(s <- segments) {
|
for(s <- segmentList) {
|
||||||
|
if(s.mPoints.size > 0) {
|
||||||
val mountain = new MonotoneMountain
|
val mountain = new MonotoneMountain
|
||||||
val k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList)
|
val k = Util.msort((p1: Point, p2: Point) => p1 < p2)(s.mPoints.toList)
|
||||||
val points = s.p :: k ::: List(s.q)
|
val points = s.p :: k ::: List(s.q)
|
||||||
points.foreach(p => mountain += p.clone)
|
points.foreach(p => mountain += p.clone)
|
||||||
if(mountain.size > 2) {
|
|
||||||
mountain.triangulate
|
mountain.triangulate
|
||||||
xMonoPoly += mountain
|
xMonoPoly += mountain
|
||||||
}
|
}
|
||||||
@ -141,15 +142,14 @@ class Triangulator(segments: ArrayBuffer[Segment]) {
|
|||||||
for(s <- segments)
|
for(s <- segments)
|
||||||
// Point p must be to the left of point q
|
// Point p must be to the left of point q
|
||||||
if(s.p.x > s.q.x) {
|
if(s.p.x > s.q.x) {
|
||||||
val tmp = s.p
|
segs += new Segment(s.q.clone, s.p.clone)
|
||||||
s.p = s.q
|
|
||||||
s.q = tmp
|
|
||||||
segs += s
|
|
||||||
} else if(s.p.x < s.q.x)
|
} else if(s.p.x < s.q.x)
|
||||||
segs += s
|
segs += new Segment(s.p.clone, s.q.clone)
|
||||||
// Randomized triangulation improves performance
|
// Randomized triangulation improves performance
|
||||||
// See Seidel's paper, or O'Rourke's book, p. 57
|
// See Seidel's paper, or O'Rourke's book, p. 57
|
||||||
// Turn this off for now because of stupid pointer bug somewhere!
|
// Turn this off for now because of pointer bug somewhere in DAG?
|
||||||
|
// The solution to this bug may be more apparent with a better Trapezoidal Map
|
||||||
|
// data structure... Maybe a modified doubly connected edge list?
|
||||||
//Random.shuffle(segs)
|
//Random.shuffle(segs)
|
||||||
segs
|
segs
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class YNode(segment: Segment, lChild: Node, rChild: Node) extends Node(lChild, r
|
|||||||
// Move up the graph
|
// Move up the graph
|
||||||
left.locate(s)
|
left.locate(s)
|
||||||
} else {
|
} else {
|
||||||
// s and segment share the same endpoint
|
// s and segment share the same endpoint, p
|
||||||
if (s.slope < segment.slope) {
|
if (s.slope < segment.slope) {
|
||||||
// Move down the graph
|
// Move down the graph
|
||||||
return right.locate(s)
|
return right.locate(s)
|
||||||
|
Loading…
Reference in New Issue
Block a user