mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-30 01:03:30 +01:00
initial commit
This commit is contained in:
commit
f461aca3dc
138
src/org/poly2tri/MonoToneMountain.scala
Normal file
138
src/org/poly2tri/MonoToneMountain.scala
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
import scala.collection.mutable.{ArrayBuffer, Queue}
|
||||||
|
|
||||||
|
class MonotoneMountain {
|
||||||
|
|
||||||
|
var tail, head: Point = null
|
||||||
|
var size = 0
|
||||||
|
|
||||||
|
val convexPoints = new Queue[Point]
|
||||||
|
val triangles = new ArrayBuffer[Array[Point]]
|
||||||
|
|
||||||
|
// Append
|
||||||
|
def +=(point: Point) {
|
||||||
|
size match {
|
||||||
|
case 0 =>
|
||||||
|
head = point
|
||||||
|
case 1 =>
|
||||||
|
tail = point
|
||||||
|
tail.prev = head
|
||||||
|
head.next = tail
|
||||||
|
case _ =>
|
||||||
|
tail.next = point
|
||||||
|
point.prev = tail
|
||||||
|
tail = point
|
||||||
|
}
|
||||||
|
size += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove
|
||||||
|
private def remove(point: Point) {
|
||||||
|
val next = point.next
|
||||||
|
val prev = point.prev
|
||||||
|
point.prev.next = next
|
||||||
|
point.next.prev = prev
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
def lastTriangle = {
|
||||||
|
assert(size == 3, "Number of points = " + size)
|
||||||
|
val triangle = new Array[Point](3)
|
||||||
|
var i = 0
|
||||||
|
var p = head
|
||||||
|
while(p != null) {
|
||||||
|
triangle(i) = p
|
||||||
|
p = p.next
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
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) convexPoints.enqueue(a)
|
||||||
|
c.angle -= ear.angle
|
||||||
|
if(c.angle > 0) convexPoints.enqueue(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(size == 3) 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)
|
||||||
|
}
|
||||||
|
}
|
59
src/org/poly2tri/Node.scala
Normal file
59
src/org/poly2tri/Node.scala
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
import collection.jcl.ArrayList
|
||||||
|
|
||||||
|
// Node for a Directed Acyclic graph (DAG)
|
||||||
|
abstract class Node(var left: Node, var right: Node) {
|
||||||
|
|
||||||
|
if(left != null) left.parentList += this
|
||||||
|
if(right != null) right.parentList += this
|
||||||
|
|
||||||
|
var parentList = new ArrayList[Node]
|
||||||
|
|
||||||
|
def locate(s: Segment): Sink
|
||||||
|
|
||||||
|
// Replace a node in the graph with this node
|
||||||
|
// Make sure parent pointers are updated
|
||||||
|
def replace(node: Node) {
|
||||||
|
for(parent <- node.parentList) {
|
||||||
|
// Select the correct node (left or right child)
|
||||||
|
if(parent.left == node) {
|
||||||
|
parent.left = this
|
||||||
|
} else {
|
||||||
|
parent.right = this
|
||||||
|
}
|
||||||
|
// Decouple the node
|
||||||
|
parentList += parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
src/org/poly2tri/Point.scala
Normal file
58
src/org/poly2tri/Point.scala
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
class Point(val x: Float, val y: Float, var segment: Segment) {
|
||||||
|
|
||||||
|
def this(x: Float, y: Float) = this(x, y, null)
|
||||||
|
|
||||||
|
// Pointers to next and previous points in Monontone Mountain
|
||||||
|
var next, prev: Point = null
|
||||||
|
|
||||||
|
/* Internal angle */
|
||||||
|
var angle = 0f
|
||||||
|
|
||||||
|
def -(p: Point) = new Point(x - p.x, y - p.y)
|
||||||
|
def +(p: Point) = new Point(x + p.x, y + p.y)
|
||||||
|
def +(f: Float) = new Point(x + f, y + f)
|
||||||
|
def *(f: Float) = new Point(x * f, y * f)
|
||||||
|
|
||||||
|
def <(p: Point) = {
|
||||||
|
if(p.x == x)
|
||||||
|
if(y > p.y) true
|
||||||
|
else false
|
||||||
|
else
|
||||||
|
(x < p.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override def clone = new Point(x, y)
|
||||||
|
}
|
97
src/org/poly2tri/QueryGraph.scala
Normal file
97
src/org/poly2tri/QueryGraph.scala
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
import collection.jcl.ArrayList
|
||||||
|
|
||||||
|
// Directed Acyclic graph (DAG)
|
||||||
|
// See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
|
||||||
|
|
||||||
|
class QueryGraph(var head: Node) {
|
||||||
|
|
||||||
|
def locate(s: Segment): Trapezoid = {
|
||||||
|
val sink = head.locate(s)
|
||||||
|
return sink.trapezoid
|
||||||
|
}
|
||||||
|
|
||||||
|
def followSegment(s: Segment) = {
|
||||||
|
val trapezoids = new ArrayList[Trapezoid]
|
||||||
|
trapezoids += locate(s)
|
||||||
|
var j = 0
|
||||||
|
while(s.q.x > trapezoids(j).rightPoint.x) {
|
||||||
|
if(s > trapezoids(j).rightPoint) {
|
||||||
|
trapezoids += trapezoids(j).upperRight
|
||||||
|
} else {
|
||||||
|
trapezoids += trapezoids(j).lowerRight
|
||||||
|
}
|
||||||
|
j += 1
|
||||||
|
}
|
||||||
|
trapezoids
|
||||||
|
}
|
||||||
|
|
||||||
|
def replace(sink: Sink, node: Node) {
|
||||||
|
if(sink.parentList.size == 0) {
|
||||||
|
head = node
|
||||||
|
} else {
|
||||||
|
node.replace(sink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def case1(sink: Sink, s: Segment, tList: ArrayList[Trapezoid]) {
|
||||||
|
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 pNode = new XNode(new Point(s.p.x, s.p.y, s), Sink.init(tList(0)), qNode)
|
||||||
|
replace(sink, pNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
def case2(sink: Sink, s: Segment, tList: ArrayList[Trapezoid]) {
|
||||||
|
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)
|
||||||
|
replace(sink, pNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
def case3(sink: Sink, s: Segment, tList: ArrayList[Trapezoid]) {
|
||||||
|
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
||||||
|
replace(sink, yNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
def case4(sink: Sink, s: Segment, tList: ArrayList[Trapezoid]) {
|
||||||
|
val yNode = new YNode(s, Sink.init(tList(0)), Sink.init(tList(1)))
|
||||||
|
if(s.left != null) {
|
||||||
|
val pNode = new XNode(new Point(s.p.x, s.p.y, s), Sink.init(s.left), yNode)
|
||||||
|
val qNode = new XNode(new Point(s.q.x, s.q.y, s), pNode, Sink.init(tList(2)))
|
||||||
|
replace(sink, qNode)
|
||||||
|
} else {
|
||||||
|
val qNode = new XNode(new Point(s.q.x, s.q.y, s), yNode, Sink.init(tList(2)))
|
||||||
|
replace(sink, qNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/org/poly2tri/Segment.scala
Normal file
56
src/org/poly2tri/Segment.scala
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
import collection.jcl.ArrayList
|
||||||
|
import scala.collection.mutable.HashSet
|
||||||
|
|
||||||
|
// Represents a simple polygon's edge
|
||||||
|
class Segment(var p: Point, var q: Point) {
|
||||||
|
|
||||||
|
// Pointer used for building trapezoidal map
|
||||||
|
var above, below, left: Trapezoid = null
|
||||||
|
|
||||||
|
// Montone mountain points
|
||||||
|
val mPoints = HashSet(p, q)
|
||||||
|
|
||||||
|
// Equation of a line: y = m*x + b
|
||||||
|
// Slope of the line (m)
|
||||||
|
val slope = (q.y - p.y)/(q.x - p.x)
|
||||||
|
// Y intercept
|
||||||
|
val b = p.y - (p.x * slope)
|
||||||
|
|
||||||
|
// Determines if this segment lies above the given point
|
||||||
|
def > (point: Point) = (point.y < slope * point.x + b)
|
||||||
|
// Determines if this segment lies below the given point
|
||||||
|
def < (point: Point) = (point.y > slope * point.x + b)
|
||||||
|
|
||||||
|
}
|
50
src/org/poly2tri/Sink.scala
Normal file
50
src/org/poly2tri/Sink.scala
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
object Sink {
|
||||||
|
|
||||||
|
def init(trapezoid: Trapezoid) = {
|
||||||
|
if(trapezoid.sink != null) {
|
||||||
|
trapezoid.sink
|
||||||
|
} else {
|
||||||
|
new Sink(trapezoid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Sink(val trapezoid: Trapezoid) extends Node(null, null) {
|
||||||
|
|
||||||
|
trapezoid.sink = this
|
||||||
|
override def locate(s: Segment): Sink = this
|
||||||
|
|
||||||
|
}
|
101
src/org/poly2tri/Trapezoid.scala
Normal file
101
src/org/poly2tri/Trapezoid.scala
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
class Trapezoid(val leftPoint: Point, var rightPoint: Point, val top: Segment, val bottom: Segment) {
|
||||||
|
|
||||||
|
var sink: Sink = null
|
||||||
|
var outside = false
|
||||||
|
|
||||||
|
// Neighbor pointers
|
||||||
|
var upperLeft: Trapezoid = null
|
||||||
|
var lowerLeft: Trapezoid = null
|
||||||
|
var upperRight: Trapezoid = null
|
||||||
|
var lowerRight: Trapezoid = null
|
||||||
|
|
||||||
|
def updateNeighbors(ul: Trapezoid, ll: Trapezoid, ur: Trapezoid, lr: Trapezoid) {
|
||||||
|
if(upperLeft != null && upperLeft.top == top) upperLeft.upperRight = ul
|
||||||
|
if(lowerLeft != null && lowerLeft.bottom == bottom) lowerLeft.lowerRight = ll
|
||||||
|
if(upperRight != null && upperRight.top == top) upperRight.upperLeft = ur
|
||||||
|
if(lowerRight != null && lowerRight.bottom == bottom) lowerRight.lowerLeft = lr
|
||||||
|
}
|
||||||
|
|
||||||
|
def update(ul: Trapezoid, ll: Trapezoid, ur: Trapezoid, lr: Trapezoid) {
|
||||||
|
upperLeft = ul
|
||||||
|
lowerLeft = ll
|
||||||
|
upperRight = ur
|
||||||
|
lowerRight = lr
|
||||||
|
}
|
||||||
|
|
||||||
|
def markNeighbors {
|
||||||
|
if(upperLeft != null) upperLeft.outside = true
|
||||||
|
if(lowerLeft != null) lowerLeft.outside = true
|
||||||
|
if(upperRight != null) upperRight.outside = true
|
||||||
|
if(lowerRight != null) lowerRight.outside = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if this point lies inside the trapezoid
|
||||||
|
def contains(point: Point) = {
|
||||||
|
(point.x > leftPoint.x && point.x < rightPoint.x && top > point && bottom < point)
|
||||||
|
}
|
||||||
|
|
||||||
|
def vertices: Array[Point] = {
|
||||||
|
val verts = new Array[Point](4)
|
||||||
|
verts(0) = lineIntersect(top, leftPoint.x)
|
||||||
|
verts(1) = lineIntersect(bottom, leftPoint.x)
|
||||||
|
verts(2) = lineIntersect(bottom, rightPoint.x)
|
||||||
|
verts(3) = lineIntersect(top, rightPoint.x)
|
||||||
|
return verts
|
||||||
|
}
|
||||||
|
|
||||||
|
def lineIntersect(s: Segment, x: Float) = {
|
||||||
|
val y = s.slope * x + s.b
|
||||||
|
new Point(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add points to monotone mountain
|
||||||
|
// Use HashSet to aboid repeats
|
||||||
|
def mark {
|
||||||
|
bottom.mPoints += leftPoint
|
||||||
|
bottom.mPoints += rightPoint
|
||||||
|
top.mPoints += leftPoint
|
||||||
|
top.mPoints += rightPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear points when dividing this trapezoid
|
||||||
|
def clear {
|
||||||
|
bottom.mPoints -= leftPoint
|
||||||
|
bottom.mPoints -= rightPoint
|
||||||
|
top.mPoints -= leftPoint
|
||||||
|
top.mPoints -= rightPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
215
src/org/poly2tri/TrapezoidalMap.scala
Normal file
215
src/org/poly2tri/TrapezoidalMap.scala
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
import collection.jcl.ArrayList
|
||||||
|
import scala.collection.mutable.{Map, HashSet}
|
||||||
|
|
||||||
|
// See "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2
|
||||||
|
|
||||||
|
class TrapezoidalMap {
|
||||||
|
|
||||||
|
// Trapezoid associated array
|
||||||
|
val map = HashSet.empty[Trapezoid]
|
||||||
|
// AABB margin
|
||||||
|
var margin = 2f
|
||||||
|
|
||||||
|
// Bottom segment that spans multiple trapezoids
|
||||||
|
private var bCross: Segment = null
|
||||||
|
// Top segment that spans multiple trapezoids
|
||||||
|
private var tCross: Segment = null
|
||||||
|
|
||||||
|
// Add a trapezoid to the map
|
||||||
|
def add(t: Trapezoid) {
|
||||||
|
map += t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a trapezoid from the map
|
||||||
|
def remove(t: Trapezoid) {
|
||||||
|
map -=t
|
||||||
|
}
|
||||||
|
|
||||||
|
def reset {
|
||||||
|
bCross = null
|
||||||
|
tCross = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 1: segment completely enclosed by trapezoid
|
||||||
|
// break trapezoid into 4 smaller trapezoids
|
||||||
|
def case1(t: Trapezoid, s: Segment) = {
|
||||||
|
|
||||||
|
assert(s.p.x != s.q.x)
|
||||||
|
|
||||||
|
val trapezoids = new ArrayList[Trapezoid]
|
||||||
|
trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
||||||
|
trapezoids += new Trapezoid(s.p, s.q, t.top, s)
|
||||||
|
trapezoids += new Trapezoid(s.p, s.q, s, t.bottom)
|
||||||
|
trapezoids += new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
||||||
|
|
||||||
|
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
||||||
|
trapezoids(1).update(trapezoids(0), null, trapezoids(3), null)
|
||||||
|
trapezoids(2).update(null, trapezoids(0), null, trapezoids(3))
|
||||||
|
trapezoids(3).update(trapezoids(1), trapezoids(2), t.upperRight, t.lowerRight)
|
||||||
|
|
||||||
|
s.above = trapezoids(1)
|
||||||
|
s.below = trapezoids(2)
|
||||||
|
|
||||||
|
t.updateNeighbors(trapezoids(0), trapezoids(0), trapezoids(3), trapezoids(3))
|
||||||
|
trapezoids
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2: Trapezoid contains point p, q lies outside
|
||||||
|
// break trapezoid into 3 smaller trapezoids
|
||||||
|
def case2(t: Trapezoid, s: Segment) = {
|
||||||
|
|
||||||
|
val rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint
|
||||||
|
|
||||||
|
val trapezoids = new ArrayList[Trapezoid]
|
||||||
|
trapezoids += new Trapezoid(t.leftPoint, s.p, t.top, t.bottom)
|
||||||
|
trapezoids += new Trapezoid(s.p, rp, t.top, s)
|
||||||
|
trapezoids += new Trapezoid(s.p, rp, s, t.bottom)
|
||||||
|
|
||||||
|
trapezoids(0).update(t.upperLeft, t.lowerLeft, trapezoids(1), trapezoids(2))
|
||||||
|
trapezoids(1).update(trapezoids(0), null, t.upperRight, null)
|
||||||
|
trapezoids(2).update(null, trapezoids(0), null, t.lowerRight)
|
||||||
|
|
||||||
|
bCross = t.bottom
|
||||||
|
tCross = t.top
|
||||||
|
s.above = trapezoids(1)
|
||||||
|
s.below = trapezoids(2)
|
||||||
|
|
||||||
|
t.updateNeighbors(trapezoids(0), trapezoids(0), trapezoids(1), trapezoids(2))
|
||||||
|
trapezoids
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 3: Trapezoid is bisected
|
||||||
|
def case3(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 rp = if(s.q.x == t.rightPoint.x) s.q else t.rightPoint
|
||||||
|
|
||||||
|
val topCross = (tCross == t.top)
|
||||||
|
val bottomCross = (bCross == t.bottom)
|
||||||
|
|
||||||
|
val trapezoids = new ArrayList[Trapezoid]
|
||||||
|
trapezoids += {if(topCross) t.upperLeft else new Trapezoid(lp, rp, t.top, s)}
|
||||||
|
trapezoids += {if(bottomCross) t.lowerLeft else new Trapezoid(lp, rp, s, t.bottom)}
|
||||||
|
|
||||||
|
if(topCross) {
|
||||||
|
trapezoids(0).upperRight = t.upperRight
|
||||||
|
trapezoids(0).rightPoint = t.rightPoint
|
||||||
|
} else {
|
||||||
|
trapezoids(0).update(t.upperLeft, s.above, t.upperRight, null)
|
||||||
|
if(s.above != null) s.above.lowerRight = trapezoids(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bottomCross) {
|
||||||
|
trapezoids(1).lowerRight = t.lowerRight
|
||||||
|
trapezoids(1).rightPoint = t.rightPoint
|
||||||
|
} else {
|
||||||
|
trapezoids(1).update(s.below, t.lowerLeft, null, t.lowerRight)
|
||||||
|
if(s.below != null) s.below.upperRight = trapezoids(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
bCross = t.bottom
|
||||||
|
tCross = t.top
|
||||||
|
s.above = trapezoids(0)
|
||||||
|
s.below = trapezoids(1)
|
||||||
|
|
||||||
|
t.updateNeighbors(trapezoids(0), trapezoids(1), trapezoids(0), trapezoids(1))
|
||||||
|
trapezoids
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 4: Trapezoid contains point q, p lies outside
|
||||||
|
// break trapezoid into 3 smaller trapezoids
|
||||||
|
def case4(t: Trapezoid, s: Segment) = {
|
||||||
|
|
||||||
|
val lp = if(s.p.x == t.leftPoint.x) s.p else t.leftPoint
|
||||||
|
|
||||||
|
val topCross = (tCross == t.top)
|
||||||
|
val bottomCross = (bCross == t.bottom)
|
||||||
|
|
||||||
|
val trapezoids = new ArrayList[Trapezoid]
|
||||||
|
trapezoids += {if(topCross) t.upperLeft else new Trapezoid(lp, s.q, t.top, s)}
|
||||||
|
trapezoids += {if(bottomCross) t.lowerLeft else new Trapezoid(lp, s.q, s, t.bottom)}
|
||||||
|
trapezoids += new Trapezoid(s.q, t.rightPoint, t.top, t.bottom)
|
||||||
|
|
||||||
|
if(topCross) {
|
||||||
|
trapezoids(0).upperRight = trapezoids(2)
|
||||||
|
trapezoids(0).rightPoint = s.q
|
||||||
|
} else {
|
||||||
|
trapezoids(0).update(t.upperLeft, s.above, trapezoids(2), null)
|
||||||
|
if(s.above != null) s.above.lowerRight = trapezoids(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bottomCross) {
|
||||||
|
trapezoids(1).lowerRight = trapezoids(2)
|
||||||
|
trapezoids(1).rightPoint = s.q
|
||||||
|
} else {
|
||||||
|
trapezoids(1).update(s.below, t.lowerLeft, null, trapezoids(2))
|
||||||
|
if(s.below != null) s.below.upperRight = trapezoids(1)
|
||||||
|
}
|
||||||
|
trapezoids(2).update(trapezoids(0), trapezoids(1), t.upperRight, t.lowerRight)
|
||||||
|
|
||||||
|
s.above = trapezoids(0)
|
||||||
|
s.below = trapezoids(1)
|
||||||
|
|
||||||
|
t.updateNeighbors(trapezoids(0), trapezoids(1), trapezoids(2), trapezoids(2))
|
||||||
|
trapezoids
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an AABB around segments
|
||||||
|
def boundingBox(segments: ArrayList[Segment]): Trapezoid = {
|
||||||
|
|
||||||
|
var max = segments(0).p + margin
|
||||||
|
var min = segments(0).q + margin
|
||||||
|
|
||||||
|
for(s <- segments) {
|
||||||
|
if(s.p.x > max.x) max = new Point(s.p.x + margin, max.y)
|
||||||
|
if(s.p.y > max.y) max = new 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.y > max.y) max = new 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.y < min.y) min = new 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.y < min.y) min = new Point(min.x, s.q.y-margin)
|
||||||
|
}
|
||||||
|
|
||||||
|
val top = new Segment(new Point(min.x, max.y), new Point(max.x, max.y))
|
||||||
|
val bottom = new Segment(new Point(min.x, min.y), new Point(max.x, min.y))
|
||||||
|
val left = bottom.p
|
||||||
|
val right = top.q
|
||||||
|
|
||||||
|
return new Trapezoid(left, right, top, bottom)
|
||||||
|
}
|
||||||
|
}
|
155
src/org/poly2tri/Triangulator.scala
Normal file
155
src/org/poly2tri/Triangulator.scala
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
import collection.jcl.ArrayList
|
||||||
|
import scala.collection.mutable.{HashSet, Map, Stack, ListBuffer}
|
||||||
|
|
||||||
|
import utils.Random
|
||||||
|
|
||||||
|
// Based on Raimund Seidel's paper "A simple and fast incremental randomized
|
||||||
|
// algorithm for computing trapezoidal decompositions and for triangulating polygons"
|
||||||
|
class Triangulator(var segments: ArrayList[Segment]) {
|
||||||
|
|
||||||
|
// Trapezoid decomposition list
|
||||||
|
var trapezoids : ArrayList[Trapezoid] = null
|
||||||
|
// Triangle decomposition list
|
||||||
|
// var triangles: ArrayList[Triangle] = null
|
||||||
|
|
||||||
|
// Build the trapezoidal map and query graph
|
||||||
|
def process {
|
||||||
|
|
||||||
|
val foo = new Point(0f, 0f)
|
||||||
|
val foo2 = new Point(0f, 3f)
|
||||||
|
val foo3 = new Point(10f, 3f)
|
||||||
|
var bar = List(foo, foo3, foo2)
|
||||||
|
bar -= foo
|
||||||
|
|
||||||
|
for(s <- segments) {
|
||||||
|
val traps = queryGraph.followSegment(s)
|
||||||
|
// Remove trapezoids from trapezoidal Map
|
||||||
|
traps.foreach(t => {trapezoidalMap.remove(t); t.clear})
|
||||||
|
for(t <- traps) {
|
||||||
|
var tList: ArrayList[Trapezoid] = null
|
||||||
|
val containsP = t.contains(s.p)
|
||||||
|
val containsQ = t.contains(s.q)
|
||||||
|
if(containsP && containsQ) {
|
||||||
|
// Case 1
|
||||||
|
tList = trapezoidalMap.case1(t,s)
|
||||||
|
queryGraph.case1(t.sink, s, tList)
|
||||||
|
} else if(containsP && !containsQ) {
|
||||||
|
// Case 2
|
||||||
|
tList = trapezoidalMap.case2(t,s)
|
||||||
|
queryGraph.case2(t.sink, s, tList)
|
||||||
|
} else if(!containsP && !containsQ) {
|
||||||
|
// Case 3
|
||||||
|
tList = trapezoidalMap.case3(t, s)
|
||||||
|
queryGraph.case3(t.sink, s, tList)
|
||||||
|
} else {
|
||||||
|
// Case 4
|
||||||
|
tList = trapezoidalMap.case4(t, s)
|
||||||
|
queryGraph.case4(t.sink, s, tList)
|
||||||
|
}
|
||||||
|
// Add new trapezoids to the trapezoidal map
|
||||||
|
tList.foreach(trapezoidalMap.add)
|
||||||
|
}
|
||||||
|
trapezoidalMap reset
|
||||||
|
}
|
||||||
|
trapezoids = trim
|
||||||
|
createMountains
|
||||||
|
}
|
||||||
|
|
||||||
|
def allTrapezoids = trapezoidalMap.map
|
||||||
|
|
||||||
|
// Initialize trapezoidal map and query structure
|
||||||
|
private val trapezoidalMap = new TrapezoidalMap
|
||||||
|
private val boundingBox = trapezoidalMap.boundingBox(segments)
|
||||||
|
private val queryGraph = new QueryGraph(new Sink(boundingBox))
|
||||||
|
|
||||||
|
val xMonoPoly = new ArrayList[MonotoneMountain]
|
||||||
|
|
||||||
|
segments = orderSegments
|
||||||
|
|
||||||
|
// Build a list of x-monotone mountains
|
||||||
|
private def createMountains {
|
||||||
|
for(s <- segments) {
|
||||||
|
if(s.mPoints.size > 2) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if(mountain.size > 2) {
|
||||||
|
mountain.triangulate
|
||||||
|
//mountain.monoPoly
|
||||||
|
xMonoPoly += mountain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim off the extraneous trapezoids surrounding the polygon
|
||||||
|
private def trim = {
|
||||||
|
val traps = new ArrayList[Trapezoid]
|
||||||
|
// Mark outside trapezoids
|
||||||
|
for(t <- trapezoidalMap.map) {
|
||||||
|
if(t.top == boundingBox.top || t.bottom == boundingBox.bottom) {
|
||||||
|
t.outside = true
|
||||||
|
t.markNeighbors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Collect interior trapezoids
|
||||||
|
for(t <- trapezoidalMap.map) if(!t.outside) {
|
||||||
|
traps += t
|
||||||
|
t.mark
|
||||||
|
}
|
||||||
|
traps
|
||||||
|
}
|
||||||
|
|
||||||
|
private def orderSegments = {
|
||||||
|
// Ignore vertical segments!
|
||||||
|
val segs = new ArrayList[Segment]
|
||||||
|
for(s <- segments) {
|
||||||
|
// Point p must be to the left of point q
|
||||||
|
if(s.p.x > s.q.x) {
|
||||||
|
val tmp = s.p
|
||||||
|
s.p = s.q
|
||||||
|
s.q = tmp
|
||||||
|
segs += s
|
||||||
|
} else if(s.p.x < s.q.x) {
|
||||||
|
segs += s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Random.shuffle(segs)
|
||||||
|
}
|
||||||
|
}
|
148
src/org/poly2tri/Util.scala
Normal file
148
src/org/poly2tri/Util.scala
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
import collection.jcl.ArrayList
|
||||||
|
|
||||||
|
import org.villane.vecmath.Vector2
|
||||||
|
|
||||||
|
object Util {
|
||||||
|
|
||||||
|
final def rotateLeft90(v:Vector2) = new Vector2( -v.y, v.x )
|
||||||
|
|
||||||
|
final def rotateRight90(v:Vector2) = new Vector2(v.y, -v.x )
|
||||||
|
|
||||||
|
final def rotate(v:Vector2, angle:Float) = {
|
||||||
|
val cos = Math.cos(angle).asInstanceOf[Float]
|
||||||
|
val sin = Math.sin(angle).asInstanceOf[Float]
|
||||||
|
val u = new Vector2((cos * v.x) - (sin * v.y), (cos * v.y) + (sin * v.x))
|
||||||
|
u
|
||||||
|
}
|
||||||
|
|
||||||
|
final def clamp(a: Float, low: Float, high: Float) =
|
||||||
|
if (a < low) low
|
||||||
|
else if (a > high) high
|
||||||
|
else a
|
||||||
|
|
||||||
|
final def left(a: Vector2, b: Vector2, c: Vector2) =
|
||||||
|
((b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y) > 0)
|
||||||
|
|
||||||
|
/** Melkman's Algorithm
|
||||||
|
* www.ams.sunysb.edu/~jsbm/courses/345/melkman.pdf
|
||||||
|
* Return a convex hull in ccw order
|
||||||
|
*/
|
||||||
|
def hull(V: Array[Vector2]) = {
|
||||||
|
|
||||||
|
val n = V.length
|
||||||
|
val D = new Array[Vector2](2 * n + 1)
|
||||||
|
var bot = n - 2
|
||||||
|
var top = bot + 3
|
||||||
|
|
||||||
|
D(bot) = V(2)
|
||||||
|
D(top) = V(2)
|
||||||
|
|
||||||
|
if (left(V(0), V(1), V(2))) {
|
||||||
|
D(bot+1) = V(0)
|
||||||
|
D(bot+2) = V(1)
|
||||||
|
} else {
|
||||||
|
D(bot+1) = V(1)
|
||||||
|
D(bot+2) = V(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 3
|
||||||
|
while(i < n) {
|
||||||
|
while (left(D(bot), D(bot+1), V(i)) && left(D(top-1), D(top), V(i))) {
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
while (!left(D(top-1), D(top), V(i))) top -= 1
|
||||||
|
top += 1; D(top) = V(i)
|
||||||
|
while (!left(D(bot), D(bot+1), V(i))) bot += 1
|
||||||
|
bot -= 1; D(bot) = V(i)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
val H = new Array[Vector2](top - bot)
|
||||||
|
var h = 0
|
||||||
|
while(h < (top - bot)) {
|
||||||
|
H(h) = D(bot + h)
|
||||||
|
h += 1
|
||||||
|
}
|
||||||
|
H
|
||||||
|
}
|
||||||
|
|
||||||
|
def svgToWorld(points: Array[Float], scale: Float) = {
|
||||||
|
val verts = new Array[Vector2](points.length/2)
|
||||||
|
var i = 0
|
||||||
|
while(i < verts.length) {
|
||||||
|
verts(i) = worldPoint(points(i*2), points(i*2+1), scale)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
verts
|
||||||
|
}
|
||||||
|
|
||||||
|
def worldPoint(x: Float, y: Float, scale: Float) = {
|
||||||
|
val p = Vector2(x*scale, y*scale)
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The object <code>Random</code> offers a default implementation
|
||||||
|
* of scala.util.Random and random-related convenience methods.
|
||||||
|
*
|
||||||
|
* @since 2.8
|
||||||
|
*/
|
||||||
|
object Random extends scala.util.Random {
|
||||||
|
|
||||||
|
/** Returns a new sequence in random order.
|
||||||
|
* @param seq the sequence to shuffle
|
||||||
|
* @return the shuffled sequence
|
||||||
|
*/
|
||||||
|
def shuffle[T](buf: ArrayList[T]): ArrayList[T] = {
|
||||||
|
// It would be better if this preserved the shape of its container, but I have
|
||||||
|
// again been defeated by the lack of higher-kinded type inference. I can
|
||||||
|
// only make it work that way if it's called like
|
||||||
|
// shuffle[Int,List](List.range(0,100))
|
||||||
|
// which nicely defeats the "convenience" portion of "convenience method".
|
||||||
|
|
||||||
|
def swap(i1: Int, i2: Int) {
|
||||||
|
val tmp = buf(i1)
|
||||||
|
buf(i1) = buf(i2)
|
||||||
|
buf(i2) = tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n <- buf.length to 2 by -1) {
|
||||||
|
val k = nextInt(n)
|
||||||
|
swap(n - 1, k)
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
52
src/org/poly2tri/XNode.scala
Normal file
52
src/org/poly2tri/XNode.scala
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
class XNode(point: Point, lChild: Node, rChild: Node) extends Node(lChild, rChild) {
|
||||||
|
|
||||||
|
override def locate(s: Segment): Sink = {
|
||||||
|
if(s.p.x > point.x) {
|
||||||
|
// Move to the right in the graph
|
||||||
|
return right.locate(s)
|
||||||
|
} else if(s.p.x == point.x) {
|
||||||
|
if(point.y > s.p.y) {
|
||||||
|
s.left = point.segment.below
|
||||||
|
} else {
|
||||||
|
s.left = point.segment.above
|
||||||
|
}
|
||||||
|
return right.locate(s)
|
||||||
|
} else {
|
||||||
|
// Move to the left in the graph
|
||||||
|
return left.locate(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
src/org/poly2tri/YNode.scala
Normal file
53
src/org/poly2tri/YNode.scala
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* Poly2Tri
|
||||||
|
* Copyright (c) 2009, Mason Green
|
||||||
|
* http://code.google.com/p/poly2tri/
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||||
|
* used to endorse or promote products derived from this software without specific
|
||||||
|
* prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package org.poly2tri
|
||||||
|
|
||||||
|
class YNode(segment: Segment, lChild: Node, rChild: Node) extends Node(lChild, rChild) {
|
||||||
|
|
||||||
|
override def locate(s: Segment): Sink = {
|
||||||
|
if (segment > s.p) {
|
||||||
|
// Move down the graph
|
||||||
|
right.locate(s)
|
||||||
|
} else if (segment < s.p){
|
||||||
|
// Move up the graph
|
||||||
|
left.locate(s)
|
||||||
|
} else {
|
||||||
|
// s and segment share the same endpoint
|
||||||
|
if (s.slope < segment.slope) {
|
||||||
|
// Move down the graph
|
||||||
|
return right.locate(s)
|
||||||
|
} else {
|
||||||
|
// Move up the graph
|
||||||
|
return left.locate(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user