diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index 4a657be..6fe3476 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -30,9 +30,93 @@ */ package org.poly2tri +// Based on Raimund Seidel's paper "A simple and fast incremental randomized +// algorithm for computing trapezoidal decompositions and for triangulating polygons" +// See also: "Computational Geometry", 3rd edition, by Mark de Berg et al, Chapter 6.2 +// "Computational Geometry in C", 2nd edition, by Joseph O'Rourke + +import org.newdawn.slick.{BasicGame, GameContainer, Graphics, Color, AppGameContainer} +import org.newdawn.slick.geom.Polygon + +import collection.jcl.ArrayList + +// TODO: Lots of documentation! + object Poly2Tri { def main(args: Array[String]) { - + val container = new AppGameContainer(new Poly2TriDemo()) + container.setDisplayMode(800,600,false) + container.start() } + } + +class Poly2TriDemo extends BasicGame("Poly2Tri") { + + var tesselator: Triangulator = null + var quit = false + + def init(container: GameContainer) { + testTesselator + } + + def update(gc: GameContainer, delta: Int) { + if(quit) gc exit + } + + def render(container: GameContainer, g: Graphics) { + + val red = new Color(1f,0.0f,0.0f) + val blue = new Color(0f, 0f, 1f) + val green = new Color(0f, 1f, 0f) + + //for(t <- tesselator.allTrapezoids) { + for(t <- tesselator.trapezoids) { + val polygon = new Polygon() + for(v <- t.vertices) { + polygon.addPoint(v.x, v.y) + } + //g.setColor(red) + //g.draw(polygon) + } + + for(x <- tesselator.xMonoPoly) { + var t = x.triangles + for(t <- x.triangles) { + val triangle = new Polygon() + t.foreach(p => triangle.addPoint(p.x, p.y)) + g.setColor(green) + g.draw(triangle) + } + } + } + + override def keyPressed(key:Int, c:Char) { + if(key == 1) quit = true + } + + def testTesselator { + + val scale = 1.0f + val p1 = new Point(100,300)*scale + val p2 = new Point(400,500)*scale + val p3 = new Point(260,200)*scale + val p4 = new Point(600,175)*scale + val p5 = new Point(400,300)*scale + val p6 = new Point(650,250)*scale + + val segments = new ArrayList[Segment] + segments += new Segment(p1, p2) + segments += new Segment(p3, p4) + segments += new Segment(p1, p3) + segments += new Segment(p5, p2) + segments += new Segment(p5, p6) + segments += new Segment(p4, p6) + + tesselator = new Triangulator(segments) + tesselator.process + } + + +} \ No newline at end of file diff --git a/src/org/poly2tri/TrapezoidalMap.scala b/src/org/poly2tri/TrapezoidalMap.scala index 255cf8a..43d5ec4 100644 --- a/src/org/poly2tri/TrapezoidalMap.scala +++ b/src/org/poly2tri/TrapezoidalMap.scala @@ -40,7 +40,7 @@ class TrapezoidalMap { // Trapezoid associated array val map = HashSet.empty[Trapezoid] // AABB margin - var margin = 2f + var margin = 20f // Bottom segment that spans multiple trapezoids private var bCross: Segment = null diff --git a/src/org/poly2tri/Triangulator.scala b/src/org/poly2tri/Triangulator.scala index ab42dda..24ee134 100644 --- a/src/org/poly2tri/Triangulator.scala +++ b/src/org/poly2tri/Triangulator.scala @@ -33,8 +33,6 @@ 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]) { @@ -144,6 +142,7 @@ class Triangulator(var segments: ArrayList[Segment]) { segs += s } } + // This is actually important: See Seidel's paper Random.shuffle(segs) } } diff --git a/src/org/poly2tri/Util.scala b/src/org/poly2tri/Util.scala index a10fde4..68794fd 100644 --- a/src/org/poly2tri/Util.scala +++ b/src/org/poly2tri/Util.scala @@ -1,124 +1,13 @@ -/* 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 + * From Scala 2.8 standard library */ object Random extends scala.util.Random {