fixed angle init bug

This commit is contained in:
zzzzrrr 2009-07-16 14:14:09 -04:00
parent 5f651233d8
commit 9cdc372690
9 changed files with 216 additions and 110 deletions

72
data/15Point.svg Normal file
View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="15Point.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="375"
inkscape:cy="829.15262"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="795"
inkscape:window-height="711"
inkscape:window-x="284"
inkscape:window-y="20">
<inkscape:grid
type="xygrid"
id="grid2383" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 400,472.36218 L 500,392.36218 L 520,272.36218 L 460,232.36218 L 580,212.36218 L 480,152.36218 L 360,172.36218 L 360,52.362183 L 300,112.36218 L 200,32.362183 L 120,92.362183 L 200,72.362183 L 340,272.36218 L 200,212.36218 L 180,352.36218 L 300,312.36218 L 400,472.36218 z"
id="path2385" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -46,7 +46,7 @@ class MonotoneMountain {
// Used to track which side of the line we are on // Used to track which side of the line we are on
var positive = false var positive = false
// Almost Pi! // Almost Pi!
val SLOP = 3.1 val PI_SLOP = 3.1
// Append a point to the list // Append a point to the list
def +=(point: Point) { def +=(point: Point) {
@ -78,6 +78,8 @@ class MonotoneMountain {
// See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52 // See "Computational Geometry in C", 2nd edition, by Joseph O'Rourke, page 52
def triangulate { def triangulate {
// Establish the proper sign
positive = initAngle
// create monotone polygon - for dubug purposes // create monotone polygon - for dubug purposes
genMonoPoly genMonoPoly
@ -90,7 +92,7 @@ class MonotoneMountain {
while(p != tail) { while(p != tail) {
val a = angle(p) val a = angle(p)
// If the point is almost colinear with it's neighbor, remove it! // If the point is almost colinear with it's neighbor, remove it!
if(a >= SLOP || a <= -SLOP) if(a >= PI_SLOP || a <= -PI_SLOP)
remove(p) remove(p)
else else
if(convex(p)) convexPoints.enqueue(p) if(convex(p)) convexPoints.enqueue(p)
@ -100,17 +102,17 @@ class MonotoneMountain {
while(!convexPoints.isEmpty) { while(!convexPoints.isEmpty) {
val ear = convexPoints.dequeue val ear = convexPoints.dequeue
val a = ear.prev.clone val a = ear.prev
val b = ear.clone val b = ear
val c = ear.next.clone val c = ear.next
val triangle = Array(a, b, c) val triangle = Array(a, b, c)
triangles += triangle triangles += triangle
// Remove ear, update angles and convex list // Remove ear, update angles and convex list
remove(ear) remove(ear)
if(a.prev != null && convex(a)) convexPoints.enqueue(a); if(valid(a)) convexPoints.enqueue(a);
if(c.prev != null && convex(c)) convexPoints.enqueue(c) if(valid(c)) convexPoints.enqueue(c)
} }
assert(size <= 3, "Triangulation bug") assert(size <= 3, "Triangulation bug")
@ -118,6 +120,8 @@ class MonotoneMountain {
} }
} }
def valid(p: Point) = (p.prev != null && p.next != null && convex(p))
// Create the monotone polygon // Create the monotone polygon
private def genMonoPoly { private def genMonoPoly {
var p = head var p = head
@ -133,12 +137,15 @@ class MonotoneMountain {
Math.atan2(a cross b, a dot b) Math.atan2(a cross b, a dot b)
} }
def initAngle = {
val a = (head.next - head)
val b = (tail - head)
(Math.atan2(a cross b, a dot b) >= 0)
}
// 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) = {
val cvx = (angle(p) >= 0) if(positive != (angle(p) >= 0))
if(p.prev == head)
positive = cvx
if(positive != cvx)
false false
else else
true true

View File

@ -30,21 +30,17 @@
*/ */
package org.poly2tri package org.poly2tri
class Point(val x: Float, val y: Float, var segment: Segment) { case class Point(val x: Float, val y: Float) {
def this(x: Float, y: Float) = this(x, y, null)
// Pointers to next and previous points in Monontone Mountain // Pointers to next and previous points in Monontone Mountain
var next, prev: Point = null var next, prev: Point = null
/* Internal angle */ def -(p: Point) = Point(x - p.x, y - p.y)
var angle = 0f def +(p: Point) = Point(x + p.x, y + p.y)
def +(f: Float) = Point(x + f, y + f)
def -(p: Point) = new Point(x - p.x, y - p.y) def -(f: Float) = Point(x - f, y - f)
def +(p: Point) = new Point(x + p.x, y + p.y) def *(f: Float) = Point(x * f, y * f)
def +(f: Float) = new Point(x + f, y + f) def /(a: Float) = Point(x / a, y / a)
def *(f: Float) = new Point(x * f, y * f)
def /(a: Float) = new Point(x / a, y / a)
def cross(p: Point) = x * p.y - y * p.x def cross(p: Point) = x * p.y - y * p.x
def dot(p: Point) = x * p.x + y * p.y def dot(p: Point) = x * p.x + y * p.y
def length = Math.sqrt(x * x + y * y).toFloat def length = Math.sqrt(x * x + y * y).toFloat
@ -52,12 +48,11 @@ class Point(val x: Float, val y: Float, var segment: Segment) {
def <(p: Point) = { def <(p: Point) = {
if(p.x == x) if(p.x == x)
if(y > p.y) true if(y <= p.y) true
else false else false
else else
(x < p.x) (x < p.x)
} }
override def clone = Point(x, y)
override def clone = new Point(x, y)
} }

View File

@ -86,7 +86,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
val lCirc = new Circle(t.leftPoint.x, t.leftPoint.y, 4) val lCirc = new Circle(t.leftPoint.x, t.leftPoint.y, 4)
g.setColor(blue); g.draw(lCirc); g.fill(lCirc) g.setColor(blue); g.draw(lCirc); g.fill(lCirc)
val rCirc = new Circle(t.rightPoint.x, t.rightPoint.y, 4) val rCirc = new Circle(t.rightPoint.x, t.rightPoint.y, 4)
g.setColor(yellow); g.draw(rCirc); g.fill(rCirc) //g.setColor(yellow); g.draw(rCirc); g.fill(rCirc)
g.setColor(red) g.setColor(red)
g.draw(polygon) g.draw(polygon)
} }
@ -141,21 +141,40 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
// Test #1 // Test #1
def poly { def poly {
val scale = 1.0f val p1 = Point(400,472)
val p1 = new Point(100,300)*scale val p2 = Point(500,392)
val p2 = new Point(400,500)*scale val p3 = Point(520,272)
val p3 = new Point(260,200)*scale val p4 = Point(460,232)
val p4 = new Point(600,175)*scale val p5 = Point(580,212)
val p5 = new Point(400,300)*scale val p6 = Point(480,152)
val p6 = new Point(650,250)*scale val p7 = Point(360,172)
val p8 = Point(360,52)
val p9 = Point(300,112)
val p10 = Point(200,32)
val p11 = Point(120,92)
val p12 = Point(200,72)
val p13 = Point(340,272)
val p14 = Point(208,212)
val p15 = Point(180,352)
val p16 = Point(300,312)
val segments = new ArrayBuffer[Segment] val segments = new ArrayBuffer[Segment]
segments += new Segment(p1, p2) segments += new Segment(p1, p2)
segments += new Segment(p2, p3)
segments += new Segment(p3, p4) segments += new Segment(p3, p4)
segments += new Segment(p1, p3) segments += new Segment(p4, p5)
segments += new Segment(p5, p2)
segments += new Segment(p5, p6) segments += new Segment(p5, p6)
segments += new Segment(p4, 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, p11)
segments += new Segment(p11, p12)
segments += new Segment(p12, p13)
segments += new Segment(p13, p14)
segments += new Segment(p14, p15)
segments += new Segment(p15, p16)
segments += new Segment(p16, p1)
tesselator = new Triangulator(segments) tesselator = new Triangulator(segments)
tesselator.process tesselator.process
@ -163,16 +182,16 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
def star { def star {
val p1 = new Point(350,75) val p1 = Point(350,75)
val p2 = new Point(379,161) val p2 = Point(379,161)
val p3 = new Point(469,161) val p3 = Point(469,161)
val p4 = new Point(397,215) val p4 = Point(397,215)
val p5 = new Point(423,301) val p5 = Point(423,301)
val p6 = new Point(350,250) val p6 = Point(350,250)
val p7 = new Point(277,301) val p7 = Point(277,301)
val p8 = new Point(303,215) val p8 = Point(303,215)
val p9 = new Point(231,161) val p9 = Point(231,161)
val p10 = new Point(321,161) val p10 = Point(321,161)
val segments = new ArrayBuffer[Segment] val segments = new ArrayBuffer[Segment]
segments += new Segment(p1, p2) segments += new Segment(p1, p2)
@ -194,18 +213,18 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") {
val scale = 10.0f val scale = 10.0f
val displace = 100 val displace = 100
val p1 = new Point(10,1)*scale+displace val p1 = Point(10,1)*scale+displace
val p2 = new Point(20,10)*scale+displace val p2 = Point(20,10)*scale+displace
val p3 = new Point(30,1)*scale+displace val p3 = Point(30,1)*scale+displace
val p4 = new Point(40,10)*scale+displace val p4 = Point(40,10)*scale+displace
val p5 = new Point(50,1)*scale+displace val p5 = Point(50,1)*scale+displace
val p6 = new Point(50,10)*scale+displace val p6 = Point(50,10)*scale+displace
val p7 = new Point(40,20)*scale+displace val p7 = Point(40,20)*scale+displace
val p8 = new Point(30,10)*scale+displace val p8 = Point(30,10)*scale+displace
val p9 = new Point(20,20)*scale+displace val p9 = Point(20,20)*scale+displace
val p10 = new Point(10,10)*scale+displace val p10 = Point(10,10)*scale+displace
val p11 = new Point(1,20)*scale+displace val p11 = Point(1,20)*scale+displace
val p12 = new Point(1,10)*scale+displace val p12 = Point(1,10)*scale+displace
val segments = new ArrayBuffer[Segment] val segments = new ArrayBuffer[Segment]
segments += new Segment(p1, p2) segments += new Segment(p1, p2)

View File

@ -40,6 +40,7 @@ class QueryGraph(var head: Node) {
def locate(s: Segment) = head.locate(s).trapezoid def locate(s: Segment) = head.locate(s).trapezoid
def followSegment(s: Segment) = { def followSegment(s: Segment) = {
assert(s.p.x < s.q.x)
val trapezoids = new ArrayBuffer[Trapezoid] val trapezoids = new ArrayBuffer[Trapezoid]
trapezoids += locate(s) trapezoids += locate(s)
var j = 0 var j = 0
@ -68,14 +69,14 @@ class QueryGraph(var head: Node) {
def case1(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) { def case1(sink: Sink, s: Segment, tList: ArrayBuffer[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(new Point(s.q.x, s.q.y, s), yNode, Sink.init(tList(3))) val qNode = new XNode(s.q, yNode, Sink.init(tList(3)))
val pNode = new XNode(new Point(s.p.x, s.p.y, s), 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: ArrayBuffer[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(new Point(s.p.x, s.p.y, s), Sink.init(tList(0)), yNode) val pNode = new XNode(s.p, Sink.init(tList(0)), yNode)
replace(sink, pNode) replace(sink, pNode)
} }
@ -86,7 +87,7 @@ class QueryGraph(var head: Node) {
def case4(sink: Sink, s: Segment, tList: ArrayBuffer[Trapezoid]) { def case4(sink: Sink, s: Segment, tList: ArrayBuffer[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(new Point(s.q.x, s.q.y, s), yNode, Sink.init(tList(2))) val qNode = new XNode(s.q, yNode, Sink.init(tList(2)))
replace(sink, qNode) replace(sink, qNode)
} }
} }

View File

@ -30,14 +30,13 @@
*/ */
package org.poly2tri package org.poly2tri
import collection.jcl.ArrayList
import scala.collection.mutable.HashSet import scala.collection.mutable.HashSet
// Represents a simple polygon's edge // Represents a simple polygon's edge
class Segment(var p: Point, var q: Point) { class Segment(var p: Point, var q: Point) {
// Pointer used for building trapezoidal map // Pointers used for building trapezoidal map
var above, below, left: Trapezoid = null var above, below: Trapezoid = null
// Montone mountain points // Montone mountain points
// Use a HashSet to avoid repeats // Use a HashSet to avoid repeats

View File

@ -33,7 +33,7 @@ package org.poly2tri
class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, val bottom: Segment) { class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, val bottom: Segment) {
var sink: Sink = null var sink: Sink = null
var outside = false var inside = true
// Neighbor pointers // Neighbor pointers
var upperLeft: Trapezoid = null var upperLeft: Trapezoid = null
@ -58,11 +58,15 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
lowerRight = lr; if(lr != null) lr.lowerLeft = this lowerRight = lr; if(lr != null) lr.lowerLeft = this
} }
def markNeighbors { // Recursively trim neightbors
if(upperLeft != null) upperLeft.outside = true def trimNeighbors {
if(lowerLeft != null) lowerLeft.outside = true if(inside) {
if(upperRight != null) upperRight.outside = true inside = false
if(lowerRight != null) lowerRight.outside = true if(upperLeft != null) {upperLeft.trimNeighbors}
if(lowerLeft != null) {lowerLeft.trimNeighbors}
if(upperRight != null) {upperRight.trimNeighbors}
if(lowerRight != null) {lowerRight.trimNeighbors}
}
} }
// Determines if this point lies inside the trapezoid // Determines if this point lies inside the trapezoid
@ -85,7 +89,7 @@ class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, v
} }
// Add points to monotone mountain // Add points to monotone mountain
def mark { def addPoints {
if(leftPoint != bottom.p) bottom.mPoints += leftPoint if(leftPoint != bottom.p) bottom.mPoints += leftPoint
if(rightPoint != bottom.q) bottom.mPoints += rightPoint if(rightPoint != bottom.q) bottom.mPoints += rightPoint
if(leftPoint != top.p) top.mPoints += leftPoint if(leftPoint != top.p) top.mPoints += leftPoint

View File

@ -39,7 +39,7 @@ class TrapezoidalMap {
// Trapezoid associated array // Trapezoid associated array
val map = HashSet.empty[Trapezoid] val map = HashSet.empty[Trapezoid]
// AABB margin // AABB margin
var margin = 20f var margin = 50f
// Bottom segment that spans multiple trapezoids // Bottom segment that spans multiple trapezoids
private var bCross: Segment = null private var bCross: Segment = null
@ -48,11 +48,13 @@ class TrapezoidalMap {
// Add a trapezoid to the map // Add a trapezoid to the map
def add(t: Trapezoid) { def add(t: Trapezoid) {
assert(t != null)
map += t map += t
} }
// Remove a trapezoid from the map // Remove a trapezoid from the map
def remove(t: Trapezoid) { def remove(t: Trapezoid) {
assert(t != null)
map -=t map -=t
} }
@ -66,6 +68,7 @@ class TrapezoidalMap {
def case1(t: Trapezoid, s: Segment) = { def case1(t: Trapezoid, s: Segment) = {
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 ArrayBuffer[Trapezoid]
trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom) trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
@ -88,6 +91,8 @@ class TrapezoidalMap {
// break trapezoid into 3 smaller trapezoids // break trapezoid into 3 smaller trapezoids
def case2(t: Trapezoid, s: Segment) = { def case2(t: Trapezoid, s: Segment) = {
assert(s.p.x < s.q.x)
val rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint val rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint
val trapezoids = new ArrayBuffer[Trapezoid] val trapezoids = new ArrayBuffer[Trapezoid]
@ -111,6 +116,7 @@ class TrapezoidalMap {
def case3(t: Trapezoid, s: Segment) = { def case3(t: Trapezoid, s: Segment) = {
assert(s.p.x != s.q.x) assert(s.p.x != s.q.x)
assert(s.p.x < s.q.x)
val lp = if(s.p.x == t.leftPoint.x) s.p else t.leftPoint val lp = if(s.p.x == t.leftPoint.x) s.p else t.leftPoint
val rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint val rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint
@ -150,6 +156,8 @@ class TrapezoidalMap {
// break trapezoid into 3 smaller trapezoids // break trapezoid into 3 smaller trapezoids
def case4(t: Trapezoid, s: Segment) = { def case4(t: Trapezoid, s: Segment) = {
assert(s.p.x < s.q.x)
val lp = if(s.p.x == t.leftPoint.x) s.p else t.leftPoint val lp = if(s.p.x == t.leftPoint.x) s.p else t.leftPoint
val topCross = (tCross == t.top) val topCross = (tCross == t.top)
@ -176,6 +184,9 @@ class TrapezoidalMap {
trapezoids(2).update(trapezoids(0), trapezoids(1), t.upperRight, t.lowerRight) trapezoids(2).update(trapezoids(0), trapezoids(1), t.upperRight, t.lowerRight)
s.above = trapezoids(0)
s.below = trapezoids(1)
trapezoids trapezoids
} }
@ -183,21 +194,21 @@ class TrapezoidalMap {
def boundingBox(segments: ArrayBuffer[Segment]): Trapezoid = { def boundingBox(segments: ArrayBuffer[Segment]): Trapezoid = {
var max = segments(0).p + margin var max = segments(0).p + margin
var min = segments(0).q + margin var min = segments(0).q - margin
for(s <- segments) { for(s <- segments) {
if(s.p.x > max.x) max = new Point(s.p.x + margin, max.y) if(s.p.x > max.x) max = Point(s.p.x + margin, max.y)
if(s.p.y > max.y) max = new Point(max.x, s.p.y + margin) if(s.p.y > max.y) max = Point(max.x, s.p.y + margin)
if(s.q.x > max.x) max = new Point(s.q.x+margin, max.y) if(s.q.x > max.x) max = Point(s.q.x+margin, max.y)
if(s.q.y > max.y) max = new Point(max.x, s.q.y+margin) if(s.q.y > max.y) max = Point(max.x, s.q.y+margin)
if(s.p.x < min.x) min = new Point(s.p.x-margin, min.y) if(s.p.x < min.x) min = Point(s.p.x-margin, min.y)
if(s.p.y < min.y) min = new Point(min.x, s.p.y-margin) if(s.p.y < min.y) min = Point(min.x, s.p.y-margin)
if(s.q.x < min.x) min = new Point(s.q.x-margin, min.y) if(s.q.x < min.x) min = Point(s.q.x-margin, min.y)
if(s.q.y < min.y) min = new Point(min.x, s.q.y-margin) if(s.q.y < min.y) min = Point(min.x, s.q.y-margin)
} }
val top = new Segment(new Point(min.x, max.y), new Point(max.x, max.y)) val top = new Segment(Point(min.x, max.y), Point(max.x, max.y))
val bottom = new Segment(new Point(min.x, min.y), new Point(max.x, min.y)) val bottom = new Segment(Point(min.x, min.y), Point(max.x, min.y))
val left = bottom.p val left = bottom.p
val right = top.q val right = top.q

View File

@ -70,12 +70,22 @@ class Triangulator(var segments: ArrayBuffer[Segment]) {
tList = trapezoidalMap.case4(t, s) tList = trapezoidalMap.case4(t, s)
queryGraph.case4(t.sink, s, tList) queryGraph.case4(t.sink, s, tList)
} }
// Add new trapezoids to the trapezoidal map // Add new trapezoids to map
tList.foreach(trapezoidalMap.add) tList.foreach(trapezoidalMap.add)
} }
trapezoidalMap reset trapezoidalMap reset
} }
trapezoids = trim
// Mark outside trapezoids
trapezoidalMap.map.foreach(markOutside)
// Collect interior trapezoids
for(t <- trapezoidalMap.map)
if(t.inside) {
trapezoids += t
t addPoints
}
createMountains createMountains
// Extract all the triangles into a single list // Extract all the triangles into a single list
@ -89,7 +99,7 @@ class Triangulator(var segments: ArrayBuffer[Segment]) {
// The trapezoidal map // The trapezoidal map
def trapezoidMap = trapezoidalMap.map def trapezoidMap = trapezoidalMap.map
// Trapezoid decomposition list // Trapezoid decomposition list
var trapezoids : ArrayBuffer[Trapezoid] = null 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]]
@ -101,14 +111,15 @@ class Triangulator(var segments: ArrayBuffer[Segment]) {
// Initialize trapezoidal map and query structure // Initialize trapezoidal map and query structure
private val trapezoidalMap = new TrapezoidalMap private val trapezoidalMap = new TrapezoidalMap
private val boundingBox = trapezoidalMap.boundingBox(segments) private val boundingBox = trapezoidalMap.boundingBox(segments)
private val queryGraph = new QueryGraph(new Sink(boundingBox)) trapezoidalMap add boundingBox
private val queryGraph = new QueryGraph(Sink.init(boundingBox))
private val xMonoPoly = new ArrayBuffer[MonotoneMountain] private val xMonoPoly = new ArrayBuffer[MonotoneMountain]
// 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) {
val mountain = new MonotoneMountain val mountain = new MonotoneMountain
val k = Util.msort((x: Point, y: Point) => x < y)(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) { if(mountain.size > 2) {
@ -118,22 +129,11 @@ class Triangulator(var segments: ArrayBuffer[Segment]) {
} }
} }
// Trim off the extraneous trapezoids surrounding the polygon // Mark the outside trapezoids surrounding the polygon
private def trim = { private def markOutside(t: Trapezoid) {
val traps = new ArrayBuffer[Trapezoid]
// Mark outside trapezoids
for(t <- trapezoidalMap.map) {
if(t.top == boundingBox.top || t.bottom == boundingBox.bottom) { if(t.top == boundingBox.top || t.bottom == boundingBox.bottom) {
t.outside = true t trimNeighbors
t.markNeighbors
} }
}
// Collect interior trapezoids
for(t <- trapezoidalMap.map) if(!t.outside) {
traps += t
t.mark
}
traps
} }
private def orderSegments = { private def orderSegments = {
@ -142,14 +142,12 @@ class Triangulator(var 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)
} }
// This is actually important: See Seidel's paper // This is actually important: See Seidel's paper
Random.shuffle(segs) //Random.shuffle(segs)
segs
} }
} }