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
2dcfa82af2
commit
2752b9b021
@ -32,6 +32,7 @@ package org.poly2tri
|
||||
|
||||
import scala.collection.mutable.{ArrayBuffer, Queue}
|
||||
|
||||
// Doubly linked list
|
||||
class MonotoneMountain {
|
||||
|
||||
var tail, head: Point = null
|
||||
@ -40,7 +41,7 @@ class MonotoneMountain {
|
||||
val convexPoints = new Queue[Point]
|
||||
val triangles = new ArrayBuffer[Array[Point]]
|
||||
|
||||
// Append
|
||||
// Append a point to the list
|
||||
def +=(point: Point) {
|
||||
size match {
|
||||
case 0 =>
|
||||
@ -57,8 +58,8 @@ class MonotoneMountain {
|
||||
size += 1
|
||||
}
|
||||
|
||||
// Remove
|
||||
private def remove(point: Point) {
|
||||
// Remove a point from the list
|
||||
def remove(point: Point) {
|
||||
val next = point.next
|
||||
val prev = point.prev
|
||||
point.prev.next = next
|
||||
@ -66,16 +67,66 @@ class MonotoneMountain {
|
||||
size -= 1
|
||||
}
|
||||
|
||||
// Determines if the inslide angle between edge v2-v3 and edge v2-v1 is convex (< PI)
|
||||
private def angle(v1: Point, v2: Point, v3: Point) = {
|
||||
val a = (v2 - v1)
|
||||
val b = (v2 - v3)
|
||||
val angle = Math.atan2(b.y,b.x).toFloat - Math.atan2(a.y,a.x).toFloat
|
||||
angle
|
||||
// Partition a x-monotone mountain into triangles O(n)
|
||||
// See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
|
||||
def triangulate {
|
||||
|
||||
if(size == 3) {
|
||||
lastTriangle
|
||||
} else {
|
||||
// Initialize internal angles at each nonbase vertex
|
||||
var p = head.next
|
||||
while(p != tail) {
|
||||
// Link strictly convex vertices into a list
|
||||
if(convex(p)) convexPoints.enqueue(p)
|
||||
p = p.next
|
||||
}
|
||||
|
||||
def lastTriangle = {
|
||||
assert(size == 3, "Number of points = " + size)
|
||||
while(!convexPoints.isEmpty) {
|
||||
|
||||
val ear = convexPoints.dequeue
|
||||
val a = ear.prev.clone
|
||||
val b = ear
|
||||
val c = ear.next.clone
|
||||
val triangle = Array(a, b, c)
|
||||
triangles += triangle
|
||||
|
||||
// Remove ear, update angles and convex list
|
||||
remove(ear)
|
||||
if(a.prev != null && convex(a)) convexPoints.enqueue(a);
|
||||
if(c.prev != null && convex(c)) convexPoints.enqueue(c)
|
||||
|
||||
}
|
||||
if(size == 3)lastTriangle
|
||||
}
|
||||
}
|
||||
|
||||
// Return the monotone polygon
|
||||
def monoPoly: Array[Point] = {
|
||||
val poly = new Array[Point](size)
|
||||
var i = 0
|
||||
var p = head
|
||||
while(p != null) {
|
||||
poly(i) = p
|
||||
p = p.next
|
||||
i += 1
|
||||
}
|
||||
poly
|
||||
}
|
||||
|
||||
// Determines if the inslide angle between edge v2-v3 and edge v2-v1 is convex
|
||||
private def convex(p: Point) = {
|
||||
val a = (p.next - p)
|
||||
val b = (p.prev - p)
|
||||
var angle = Math.atan2(b.y,b.x).toFloat - Math.atan2(a.y,a.x).toFloat
|
||||
if(p.y >= head.y) {
|
||||
(angle < 0)
|
||||
} else {
|
||||
!(angle < 0)
|
||||
}
|
||||
}
|
||||
|
||||
private def lastTriangle {
|
||||
val triangle = new Array[Point](3)
|
||||
var i = 0
|
||||
var p = head
|
||||
@ -86,53 +137,4 @@ class MonotoneMountain {
|
||||
}
|
||||
triangles += triangle
|
||||
}
|
||||
|
||||
// Partition a x-monotone mountain into triangles o(n)
|
||||
// See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
|
||||
def triangulate {
|
||||
if(size == 3) {
|
||||
lastTriangle
|
||||
} else {
|
||||
// Initialize internal angles at each nonbase vertex
|
||||
var p = head.next
|
||||
while(p != tail) {
|
||||
p.angle = Math.abs(angle(p.prev, p, p.next))
|
||||
println("angle = " + p.angle)
|
||||
// Link strictly convex vertices into a list
|
||||
if(p.angle >= 0 && p.angle <= Math.Pi) convexPoints.enqueue(p)
|
||||
p = p.next
|
||||
}
|
||||
|
||||
while(!convexPoints.isEmpty) {
|
||||
val ear = convexPoints.dequeue
|
||||
val a = ear.prev.clone
|
||||
val b = ear
|
||||
val c = ear.next.clone
|
||||
val triangle = Array(a, b, c)
|
||||
triangles += triangle
|
||||
|
||||
// Remove ear, update angles and convex list
|
||||
remove(ear)
|
||||
a.angle -= ear.angle
|
||||
if(a.angle > 0 && a != head && a != tail) convexPoints.enqueue(a)
|
||||
c.angle -= ear.angle
|
||||
if(c.angle > 0 && c != head && c != tail) convexPoints.enqueue(c)
|
||||
}
|
||||
|
||||
if(size > 2)lastTriangle
|
||||
}
|
||||
}
|
||||
|
||||
def monoPoly {
|
||||
val triangle = new Array[Point](size)
|
||||
var i = 0
|
||||
var p = head
|
||||
while(p != null) {
|
||||
triangle(i) = p
|
||||
p = p.next
|
||||
i += 1
|
||||
}
|
||||
triangles += triangle
|
||||
println(size)
|
||||
}
|
||||
}
|
||||
|
@ -61,8 +61,10 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
var drawMap = false
|
||||
|
||||
def init(container: GameContainer) {
|
||||
testTesselator
|
||||
//snake
|
||||
// TODO: Add text file point loader
|
||||
//poly
|
||||
//poly2
|
||||
snake
|
||||
}
|
||||
|
||||
def update(gc: GameContainer, delta: Int) {
|
||||
@ -92,14 +94,19 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
}
|
||||
}
|
||||
|
||||
var i = 0
|
||||
for(x <- tesselator.xMonoPoly) {
|
||||
var t = x.triangles
|
||||
var j = 0
|
||||
for(t <- x.triangles) {
|
||||
val triangle = new Polygon()
|
||||
t.foreach(p => triangle.addPoint(p.x, p.y))
|
||||
g.setColor(green)
|
||||
val color = if(i == 0 && j == 3) blue else green
|
||||
g.setColor(color)
|
||||
g.draw(triangle)
|
||||
j += 1
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +116,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
if(c == 'm') drawMap = !drawMap
|
||||
}
|
||||
|
||||
def testTesselator {
|
||||
// Test #1
|
||||
def poly {
|
||||
|
||||
val scale = 1.0f
|
||||
val p1 = new Point(100,300)*scale
|
||||
@ -131,6 +139,37 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
||||
tesselator.process
|
||||
}
|
||||
|
||||
def poly2 {
|
||||
|
||||
val scale = 1.0f
|
||||
val displace = 0f
|
||||
val p1 = new Point(350,75)*scale+displace
|
||||
val p2 = new Point(379,161)*scale+displace
|
||||
val p3 = new Point(469,161)*scale+displace
|
||||
val p4 = new Point(397,215)*scale+displace
|
||||
val p5 = new Point(423,301)*scale+displace
|
||||
val p6 = new Point(350,250)*scale+displace
|
||||
val p7 = new Point(277,301)*scale+displace
|
||||
val p8 = new Point(303,215)*scale+displace
|
||||
val p9 = new Point(231,161)*scale+displace
|
||||
val p10 = new Point(321,161)*scale+displace
|
||||
|
||||
val segments = new ArrayList[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.process
|
||||
}
|
||||
|
||||
// Test #2
|
||||
def snake {
|
||||
|
||||
val scale = 10.0f
|
||||
|
@ -40,7 +40,8 @@ class Segment(var p: Point, var q: Point) {
|
||||
var above, below, left: Trapezoid = null
|
||||
|
||||
// Montone mountain points
|
||||
val mPoints = HashSet(p, q)
|
||||
// Use a HashSet to avoid repeats
|
||||
val mPoints = HashSet.empty[Point]
|
||||
|
||||
// Equation of a line: y = m*x + b
|
||||
// Slope of the line (m)
|
||||
|
@ -39,7 +39,6 @@ object Sink {
|
||||
new Sink(trapezoid)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Sink(val trapezoid: Trapezoid) extends Node(null, null) {
|
||||
|
@ -82,11 +82,10 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
|
||||
}
|
||||
|
||||
// Add points to monotone mountain
|
||||
// Use HashSet to aboid repeats
|
||||
def mark {
|
||||
bottom.mPoints += leftPoint
|
||||
bottom.mPoints += rightPoint
|
||||
top.mPoints += leftPoint
|
||||
top.mPoints += rightPoint
|
||||
if(leftPoint != bottom.p) bottom.mPoints += leftPoint
|
||||
if(rightPoint != bottom.q) bottom.mPoints += rightPoint
|
||||
if(leftPoint != top.p) top.mPoints += leftPoint
|
||||
if(rightPoint != top.q) top.mPoints += rightPoint
|
||||
}
|
||||
}
|
||||
|
@ -93,15 +93,11 @@ class Triangulator(var segments: ArrayList[Segment]) {
|
||||
// Build a list of x-monotone mountains
|
||||
private def createMountains {
|
||||
for(s <- segments) {
|
||||
println(s.mPoints.size)
|
||||
if(s.mPoints.size > 2) {
|
||||
if(s.mPoints.size > 0) {
|
||||
val mountain = new MonotoneMountain
|
||||
// TODO: Optomize sort? The number of points should be
|
||||
// fairly small => insertion or merge?
|
||||
val points = s.mPoints.toList
|
||||
for(p <- points.sort((e1,e2) => e1 < e2)) {
|
||||
mountain += p.clone
|
||||
}
|
||||
val k = Util.msort((x: Point, y: Point) => x < y)(s.mPoints.toList)
|
||||
val points = s.p :: k ::: List(s.q)
|
||||
points.foreach(p => mountain += p.clone)
|
||||
if(mountain.size > 2) {
|
||||
mountain.triangulate
|
||||
//mountain.monoPoly
|
||||
|
@ -3,6 +3,21 @@ package org.poly2tri
|
||||
|
||||
import collection.jcl.ArrayList
|
||||
|
||||
object Util {
|
||||
|
||||
// From "Scala By Example," by Martin Odersky
|
||||
def msort[A](less: (A, A) => Boolean)(xs: List[A]): List[A] = {
|
||||
def merge(xs1: List[A], xs2: List[A]): List[A] =
|
||||
if (xs1.isEmpty) xs2
|
||||
else if (xs2.isEmpty) xs1
|
||||
else if (less(xs1.head, xs2.head)) xs1.head :: merge(xs1.tail, xs2)
|
||||
else xs2.head :: merge(xs1, xs2.tail)
|
||||
val n = xs.length/2
|
||||
if (n == 0) xs
|
||||
else merge(msort(less)(xs take n), msort(less)(xs drop n))
|
||||
}
|
||||
}
|
||||
|
||||
/** The object <code>Random</code> offers a default implementation
|
||||
* of scala.util.Random and random-related convenience methods.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user