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}
|
import scala.collection.mutable.{ArrayBuffer, Queue}
|
||||||
|
|
||||||
|
// Doubly linked list
|
||||||
class MonotoneMountain {
|
class MonotoneMountain {
|
||||||
|
|
||||||
var tail, head: Point = null
|
var tail, head: Point = null
|
||||||
@ -40,7 +41,7 @@ class MonotoneMountain {
|
|||||||
val convexPoints = new Queue[Point]
|
val convexPoints = new Queue[Point]
|
||||||
val triangles = new ArrayBuffer[Array[Point]]
|
val triangles = new ArrayBuffer[Array[Point]]
|
||||||
|
|
||||||
// Append
|
// Append a point to the list
|
||||||
def +=(point: Point) {
|
def +=(point: Point) {
|
||||||
size match {
|
size match {
|
||||||
case 0 =>
|
case 0 =>
|
||||||
@ -57,8 +58,8 @@ class MonotoneMountain {
|
|||||||
size += 1
|
size += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove
|
// Remove a point from the list
|
||||||
private def remove(point: Point) {
|
def remove(point: Point) {
|
||||||
val next = point.next
|
val next = point.next
|
||||||
val prev = point.prev
|
val prev = point.prev
|
||||||
point.prev.next = next
|
point.prev.next = next
|
||||||
@ -66,16 +67,66 @@ class MonotoneMountain {
|
|||||||
size -= 1
|
size -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines if the inslide angle between edge v2-v3 and edge v2-v1 is convex (< PI)
|
// Partition a x-monotone mountain into triangles O(n)
|
||||||
private def angle(v1: Point, v2: Point, v3: Point) = {
|
// See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
|
||||||
val a = (v2 - v1)
|
def triangulate {
|
||||||
val b = (v2 - v3)
|
|
||||||
val angle = Math.atan2(b.y,b.x).toFloat - Math.atan2(a.y,a.x).toFloat
|
if(size == 3) {
|
||||||
angle
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def lastTriangle = {
|
private def lastTriangle {
|
||||||
assert(size == 3, "Number of points = " + size)
|
|
||||||
val triangle = new Array[Point](3)
|
val triangle = new Array[Point](3)
|
||||||
var i = 0
|
var i = 0
|
||||||
var p = head
|
var p = head
|
||||||
@ -86,53 +137,4 @@ class MonotoneMountain {
|
|||||||
}
|
}
|
||||||
triangles += triangle
|
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
|
var drawMap = false
|
||||||
|
|
||||||
def init(container: GameContainer) {
|
def init(container: GameContainer) {
|
||||||
testTesselator
|
// TODO: Add text file point loader
|
||||||
//snake
|
//poly
|
||||||
|
//poly2
|
||||||
|
snake
|
||||||
}
|
}
|
||||||
|
|
||||||
def update(gc: GameContainer, delta: Int) {
|
def update(gc: GameContainer, delta: Int) {
|
||||||
@ -92,14 +94,19 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
for(x <- tesselator.xMonoPoly) {
|
for(x <- tesselator.xMonoPoly) {
|
||||||
var t = x.triangles
|
var t = x.triangles
|
||||||
|
var j = 0
|
||||||
for(t <- x.triangles) {
|
for(t <- x.triangles) {
|
||||||
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(green)
|
val color = if(i == 0 && j == 3) blue else green
|
||||||
|
g.setColor(color)
|
||||||
g.draw(triangle)
|
g.draw(triangle)
|
||||||
|
j += 1
|
||||||
}
|
}
|
||||||
|
i += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +116,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
if(c == 'm') drawMap = !drawMap
|
if(c == 'm') drawMap = !drawMap
|
||||||
}
|
}
|
||||||
|
|
||||||
def testTesselator {
|
// Test #1
|
||||||
|
def poly {
|
||||||
|
|
||||||
val scale = 1.0f
|
val scale = 1.0f
|
||||||
val p1 = new Point(100,300)*scale
|
val p1 = new Point(100,300)*scale
|
||||||
@ -131,6 +139,37 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
|
|||||||
tesselator.process
|
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 {
|
def snake {
|
||||||
|
|
||||||
val scale = 10.0f
|
val scale = 10.0f
|
||||||
|
@ -40,7 +40,8 @@ class Segment(var p: Point, var q: Point) {
|
|||||||
var above, below, left: Trapezoid = null
|
var above, below, left: Trapezoid = null
|
||||||
|
|
||||||
// Montone mountain points
|
// 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
|
// Equation of a line: y = m*x + b
|
||||||
// Slope of the line (m)
|
// Slope of the line (m)
|
||||||
|
@ -39,7 +39,6 @@ object Sink {
|
|||||||
new Sink(trapezoid)
|
new Sink(trapezoid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Sink(val trapezoid: Trapezoid) extends Node(null, null) {
|
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
|
// Add points to monotone mountain
|
||||||
// Use HashSet to aboid repeats
|
|
||||||
def mark {
|
def mark {
|
||||||
bottom.mPoints += leftPoint
|
if(leftPoint != bottom.p) bottom.mPoints += leftPoint
|
||||||
bottom.mPoints += rightPoint
|
if(rightPoint != bottom.q) bottom.mPoints += rightPoint
|
||||||
top.mPoints += leftPoint
|
if(leftPoint != top.p) top.mPoints += leftPoint
|
||||||
top.mPoints += rightPoint
|
if(rightPoint != top.q) top.mPoints += rightPoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,15 +93,11 @@ class Triangulator(var segments: ArrayList[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 <- segments) {
|
||||||
println(s.mPoints.size)
|
if(s.mPoints.size > 0) {
|
||||||
if(s.mPoints.size > 2) {
|
|
||||||
val mountain = new MonotoneMountain
|
val mountain = new MonotoneMountain
|
||||||
// TODO: Optomize sort? The number of points should be
|
val k = Util.msort((x: Point, y: Point) => x < y)(s.mPoints.toList)
|
||||||
// fairly small => insertion or merge?
|
val points = s.p :: k ::: List(s.q)
|
||||||
val points = s.mPoints.toList
|
points.foreach(p => mountain += p.clone)
|
||||||
for(p <- points.sort((e1,e2) => e1 < e2)) {
|
|
||||||
mountain += p.clone
|
|
||||||
}
|
|
||||||
if(mountain.size > 2) {
|
if(mountain.size > 2) {
|
||||||
mountain.triangulate
|
mountain.triangulate
|
||||||
//mountain.monoPoly
|
//mountain.monoPoly
|
||||||
|
@ -3,6 +3,21 @@ package org.poly2tri
|
|||||||
|
|
||||||
import collection.jcl.ArrayList
|
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
|
/** The object <code>Random</code> offers a default implementation
|
||||||
* of scala.util.Random and random-related convenience methods.
|
* of scala.util.Random and random-related convenience methods.
|
||||||
*
|
*
|
||||||
@ -33,5 +48,5 @@ object Random extends scala.util.Random {
|
|||||||
swap(n - 1, k)
|
swap(n - 1, k)
|
||||||
}
|
}
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user