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