From f461aca3dcdf3da8a40d7d9d5b47d2ae1cd0fb75 Mon Sep 17 00:00:00 2001 From: zzzrrr Date: Mon, 13 Jul 2009 19:54:32 -0400 Subject: [PATCH] initial commit --- src/org/poly2tri/MonoToneMountain.scala | 138 +++++++++++++++ src/org/poly2tri/Node.scala | 59 +++++++ src/org/poly2tri/Point.scala | 58 +++++++ src/org/poly2tri/QueryGraph.scala | 97 +++++++++++ src/org/poly2tri/Segment.scala | 56 ++++++ src/org/poly2tri/Sink.scala | 50 ++++++ src/org/poly2tri/Trapezoid.scala | 101 +++++++++++ src/org/poly2tri/TrapezoidalMap.scala | 215 ++++++++++++++++++++++++ src/org/poly2tri/Triangulator.scala | 155 +++++++++++++++++ src/org/poly2tri/Util.scala | 148 ++++++++++++++++ src/org/poly2tri/XNode.scala | 52 ++++++ src/org/poly2tri/YNode.scala | 53 ++++++ 12 files changed, 1182 insertions(+) create mode 100644 src/org/poly2tri/MonoToneMountain.scala create mode 100644 src/org/poly2tri/Node.scala create mode 100644 src/org/poly2tri/Point.scala create mode 100644 src/org/poly2tri/QueryGraph.scala create mode 100644 src/org/poly2tri/Segment.scala create mode 100644 src/org/poly2tri/Sink.scala create mode 100644 src/org/poly2tri/Trapezoid.scala create mode 100644 src/org/poly2tri/TrapezoidalMap.scala create mode 100644 src/org/poly2tri/Triangulator.scala create mode 100644 src/org/poly2tri/Util.scala create mode 100644 src/org/poly2tri/XNode.scala create mode 100644 src/org/poly2tri/YNode.scala diff --git a/src/org/poly2tri/MonoToneMountain.scala b/src/org/poly2tri/MonoToneMountain.scala new file mode 100644 index 0000000..bfffe3c --- /dev/null +++ b/src/org/poly2tri/MonoToneMountain.scala @@ -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) + } +} diff --git a/src/org/poly2tri/Node.scala b/src/org/poly2tri/Node.scala new file mode 100644 index 0000000..f3126ad --- /dev/null +++ b/src/org/poly2tri/Node.scala @@ -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 + } + } +} diff --git a/src/org/poly2tri/Point.scala b/src/org/poly2tri/Point.scala new file mode 100644 index 0000000..e402371 --- /dev/null +++ b/src/org/poly2tri/Point.scala @@ -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) +} diff --git a/src/org/poly2tri/QueryGraph.scala b/src/org/poly2tri/QueryGraph.scala new file mode 100644 index 0000000..8c3b922 --- /dev/null +++ b/src/org/poly2tri/QueryGraph.scala @@ -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) + } + } +} diff --git a/src/org/poly2tri/Segment.scala b/src/org/poly2tri/Segment.scala new file mode 100644 index 0000000..8263b84 --- /dev/null +++ b/src/org/poly2tri/Segment.scala @@ -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) + +} diff --git a/src/org/poly2tri/Sink.scala b/src/org/poly2tri/Sink.scala new file mode 100644 index 0000000..8e7cf79 --- /dev/null +++ b/src/org/poly2tri/Sink.scala @@ -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 + +} diff --git a/src/org/poly2tri/Trapezoid.scala b/src/org/poly2tri/Trapezoid.scala new file mode 100644 index 0000000..0780ab7 --- /dev/null +++ b/src/org/poly2tri/Trapezoid.scala @@ -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 + } + +} diff --git a/src/org/poly2tri/TrapezoidalMap.scala b/src/org/poly2tri/TrapezoidalMap.scala new file mode 100644 index 0000000..255cf8a --- /dev/null +++ b/src/org/poly2tri/TrapezoidalMap.scala @@ -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) + } +} diff --git a/src/org/poly2tri/Triangulator.scala b/src/org/poly2tri/Triangulator.scala new file mode 100644 index 0000000..4fd647f --- /dev/null +++ b/src/org/poly2tri/Triangulator.scala @@ -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) + } +} diff --git a/src/org/poly2tri/Util.scala b/src/org/poly2tri/Util.scala new file mode 100644 index 0000000..a10fde4 --- /dev/null +++ b/src/org/poly2tri/Util.scala @@ -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 Random 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 + } +} diff --git a/src/org/poly2tri/XNode.scala b/src/org/poly2tri/XNode.scala new file mode 100644 index 0000000..e923433 --- /dev/null +++ b/src/org/poly2tri/XNode.scala @@ -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) + } + } + +} diff --git a/src/org/poly2tri/YNode.scala b/src/org/poly2tri/YNode.scala new file mode 100644 index 0000000..ef331e9 --- /dev/null +++ b/src/org/poly2tri/YNode.scala @@ -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) + } + } + } +}