mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-30 01:03:30 +01:00
fixed angle init bug
This commit is contained in:
parent
5f651233d8
commit
9cdc372690
72
data/15Point.svg
Normal file
72
data/15Point.svg
Normal 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 |
@ -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
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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,23 +129,12 @@ 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 = {
|
||||||
// Ignore vertical segments!
|
// Ignore vertical segments!
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user