diff --git a/data/dude.dat b/data/dude.dat new file mode 100644 index 0000000..7e43f9c --- /dev/null +++ b/data/dude.dat @@ -0,0 +1,94 @@ +280.35714 648.79075 +286.78571 662.8979 +263.28607 661.17871 +262.31092 671.41548 +250.53571 677.00504 +250.53571 683.43361 +256.42857 685.21933 +297.14286 669.50504 +289.28571 649.50504 +285 631.6479 +285 608.79075 +292.85714 585.21932 +306.42857 563.79075 +323.57143 548.79075 +339.28571 545.21932 +357.85714 547.36218 +375 550.21932 +391.42857 568.07647 +404.28571 588.79075 +413.57143 612.36218 +417.14286 628.07647 +438.57143 619.1479 +438.03572 618.96932 +437.5 609.50504 +426.96429 609.86218 +424.64286 615.57647 +419.82143 615.04075 +420.35714 605.04075 +428.39286 598.43361 +437.85714 599.68361 +443.57143 613.79075 +450.71429 610.21933 +431.42857 575.21932 +405.71429 550.21932 +372.85714 534.50504 +349.28571 531.6479 +346.42857 521.6479 +346.42857 511.6479 +350.71429 496.6479 +367.85714 476.6479 +377.14286 460.93361 +385.71429 445.21932 +388.57143 404.50504 +360 352.36218 +337.14286 325.93361 +330.71429 334.50504 +347.14286 354.50504 +337.85714 370.21932 +333.57143 359.50504 +319.28571 353.07647 +312.85714 366.6479 +350.71429 387.36218 +368.57143 408.07647 +375.71429 431.6479 +372.14286 454.50504 +366.42857 462.36218 +352.85714 462.36218 +336.42857 456.6479 +332.85714 438.79075 +338.57143 423.79075 +338.57143 411.6479 +327.85714 405.93361 +320.71429 407.36218 +315.71429 423.07647 +314.28571 440.21932 +325 447.71932 +324.82143 460.93361 +317.85714 470.57647 +304.28571 483.79075 +287.14286 491.29075 +263.03571 498.61218 +251.60714 503.07647 +251.25 533.61218 +260.71429 533.61218 +272.85714 528.43361 +286.07143 518.61218 +297.32143 508.25504 +297.85714 507.36218 +298.39286 506.46932 +307.14286 496.6479 +312.67857 491.6479 +317.32143 503.07647 +322.5 514.1479 +325.53571 521.11218 +327.14286 525.75504 +326.96429 535.04075 +311.78571 540.04075 +291.07143 552.71932 +274.82143 568.43361 +259.10714 592.8979 +254.28571 604.50504 +251.07143 621.11218 +250.53571 649.1479 +268.1955 654.36208 diff --git a/data/dude.svg b/data/dude.svg new file mode 100644 index 0000000..cdf867f --- /dev/null +++ b/data/dude.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/org/poly2tri/Poly2Tri.scala b/src/org/poly2tri/Poly2Tri.scala index 2ad6bab..7d49dd0 100644 --- a/src/org/poly2tri/Poly2Tri.scala +++ b/src/org/poly2tri/Poly2Tri.scala @@ -58,6 +58,8 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { // Sedidel Triangulator var seidel: Triangulator = null var segments: ArrayBuffer[Segment] = null + var chestSegs: ArrayBuffer[Segment] = null + var headSegs: ArrayBuffer[Segment] = null // EarClip Triangulator val earClip = new EarClip @@ -86,8 +88,9 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val strange = "data/strange.dat" val i18 = "data/i.18" val tank = "data/tank.dat" + val dude = "data/dude.dat" - var currentModel = nazcaHeron + var currentModel = dude var doCDT = true var mouseButton = 0 @@ -112,12 +115,12 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { def render(container: GameContainer, g: Graphics) { - g.drawString("'1-8' to cycle models, mouse to pan & zoom", 10, 520) + g.drawString("'1-9' to cycle models, mouse to pan & zoom", 10, 520) g.drawString("'SPACE' to show Seidel debug info", 10, 532) g.drawString("'m' to show trapezoidal map (Seidel debug mode)", 10, 544) g.drawString("'e' to switch Seidel / EarClip", 10, 556) g.drawString("'d' to switch CDT / Seidel", 10, 568) - g.drawString("'c' to how CDT mesh", 10, 580) + g.drawString("'c' to show CDT mesh, 's' to draw edges", 10, 580) g.scale(scaleFactor, scaleFactor) g.translate(deltaX, deltaY) @@ -177,25 +180,25 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { val draw = if(drawcdtMesh) slCDT.triangleMesh else slCDT.triangles draw.foreach( t => { - - for(i <- 0 to 2) { - val s = t.points(i) - val e = if(i == 2) t.points(0) else t.points(i + 1) - val j = if(i == 0) 2 else if(i == 1) 0 else 1 - if(t.edges(j)) - g.setColor(yellow) - else - g.setColor(red) - g.drawLine(s.x,s.y,e.x,e.y) - } - /* - val triangle = new Polygon - triangle.addPoint(t.points(0).x, t.points(0).y) - triangle.addPoint(t.points(1).x, t.points(1).y) - triangle.addPoint(t.points(2).x, t.points(2).y) - g.setColor(red) - g.draw(triangle) - */ + if(true) { + for(i <- 0 to 2) { + val s = t.points(i) + val e = if(i == 2) t.points(0) else t.points(i + 1) + val j = if(i == 0) 2 else if(i == 1) 0 else 1 + if(t.edges(j)) + g.setColor(yellow) + else + g.setColor(red) + g.drawLine(s.x,s.y,e.x,e.y) + } + } else { + val triangle = new Polygon + triangle.addPoint(t.points(0).x, t.points(0).y) + triangle.addPoint(t.points(1).x, t.points(1).y) + triangle.addPoint(t.points(2).x, t.points(2).y) + g.setColor(red) + g.draw(triangle) + } }) slCDT.debugTriangles.foreach( t => { @@ -217,6 +220,18 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { } } + if(currentModel == "data/dude.dat" && drawSegs) { + g.setColor(green) + for(i <- 0 until chestSegs.size) { + val s = chestSegs(i) + g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) + } + for(i <- 0 until headSegs.size) { + val s = headSegs(i) + g.drawLine(s.p.x,s.p.y,s.q.x,s.q.y) + } + } + } /** @@ -289,6 +304,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { if(c == '6') selectModel(i18) if(c == '7') selectModel(nazcaHeron) if(c == '8') selectModel(tank) + if(c == '9') selectModel(dude) if(c == 's') drawSegs = !drawSegs if(c == 'c') drawcdtMesh = !drawcdtMesh if(c == 'e') {drawEarClip = !drawEarClip; drawCDT = false; selectModel(currentModel)} @@ -297,50 +313,47 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { def selectModel(model: String) { model match { case "data/nazca_monkey.dat" => - CDT.clearPoint = 50 - loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500) + val clearPoint = Point(418, 282) + loadModel(nazcaMonkey, 4.5f, Point(400, 300), 1500, clearPoint) case "data/bird.dat" => - CDT.clearPoint = 80 - loadModel(bird, 25f, Point(400, 300), 350) + val clearPoint = Point(400, 300) + loadModel(bird, 25f, Point(400, 300), 350, clearPoint) case "data/i.snake" => - doCDT = true; drawCDT = true - CDT.clearPoint = 6 - loadModel(snake, 10f, Point(600, 300), 10) + val clearPoint = Point(336f, 196f) + loadModel(snake, 10f, Point(600, 300), 10, clearPoint) case "data/star.dat" => - doCDT = true; drawCDT = true - CDT.clearPoint = 6 - loadModel(star, -1f, Point(0f, 0f), 10) + val clearPoint = Point(400, 204) + loadModel(star, -1f, Point(0f, 0f), 10, clearPoint) case "data/strange.dat" => - doCDT = true; drawCDT = true - CDT.clearPoint = 13 - loadModel(strange, -1f, Point(0f, 0f), 15) + val clearPoint = Point(400, 268) + loadModel(strange, -1f, Point(0f, 0f), 15, clearPoint) case "data/i.18" => - doCDT = true; drawCDT = true - CDT.clearPoint = 7 - loadModel(i18, 20f, Point(600f, 500f), 20) + val clearPoint = Point(510, 385) + loadModel(i18, 20f, Point(600f, 500f), 20, clearPoint) case "data/nazca_heron_old.dat" => - //doCDT = false; drawCDT = false; drawcdtMesh = false - CDT.clearPoint = 100 - loadModel(nazcaHeron, 4.2f, Point(400f, 300f), 1500) + val clearPoint = Point(85, 290) + loadModel(nazcaHeron, 4.2f, Point(400f, 300f), 1500, clearPoint) case "data/tank.dat" => - //doCDT = false; drawCDT = false; drawcdtMesh = false - doCDT = true; drawCDT = true - CDT.clearPoint = 38 - loadModel(tank, -1f, Point(100f, 0f), 10) + val clearPoint = Point(450, 350) + loadModel(tank, -1f, Point(100f, 0f), 10, clearPoint) + case "data/dude.dat" => + val clearPoint = Point(365, 427) + loadModel(dude, -1f, Point(100f, -200f), 10, clearPoint) case _ => assert(false) + } currentModel = model } - def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int) { + def loadModel(model: String, scale: Float, center: Point, maxTriangles: Int, clearPoint: Point) { println println("************** " + model + " **************") polyX = new ArrayBuffer[Float] polyY = new ArrayBuffer[Float] - val points = new ArrayBuffer[Point] + var points = new ArrayBuffer[Point] val angle = Math.Pi for (line <- Source.fromFile(model).getLines) { @@ -359,7 +372,7 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { } segments = new ArrayBuffer[Segment] - for(i <- 0 until polyX.size-1) + for(i <- 0 until points.size-1) segments += new Segment(points(i), points(i+1)) segments += new Segment(points.first, points.last) @@ -367,9 +380,49 @@ class Poly2TriDemo extends BasicGame("Poly2Tri") { println if(doCDT) { + + val pts = points.toArray + val t1 = System.nanoTime - slCDT = CDT.init(points) + slCDT = new CDT(pts, clearPoint) + + // Add some holes.... + if(model == "data/dude.dat") { + + val headHole = Array(Point(325f,437f), Point(320f,423f), Point(329f,413f), Point(332f,423f)) + val chestHole = Array(Point(320.72342f,480f), Point(338.90617f,465.96863f), + Point(347.99754f,480.61584f), Point(329.8148f,510.41534f), + Point(339.91632f,480.11077f), Point(334.86556f,478.09046f)) + + // Tramsform the points + for(i <- 0 until headHole.size) { + val hx = -headHole(i).x*scale + center.x + val hy = -headHole(i).y*scale + center.y + headHole(i) = Point(hx, hy) + } + for(i <- 0 until chestHole.size) { + val cx = -chestHole(i).x*scale + center.x + val cy = -chestHole(i).y*scale + center.y + chestHole(i) = Point(cx, cy) + } + + chestSegs = new ArrayBuffer[Segment] + for(i <- 0 until chestHole.size-1) + chestSegs += new Segment(chestHole(i), chestHole(i+1)) + chestSegs += new Segment(chestHole.first, chestHole.last) + + headSegs = new ArrayBuffer[Segment] + for(i <- 0 until headHole.size-1) + chestSegs += new Segment(headHole(i), headHole(i+1)) + chestSegs += new Segment(headHole.first, headHole.last) + + slCDT.addHole(headHole) + slCDT.addHole(chestHole) + } + + slCDT triangulate val runTime = System.nanoTime - t1 + println("CDT average (ms) = " + runTime*1e-6) println("Number of triangles = " + slCDT.triangles.size) println diff --git a/src/org/poly2tri/cdt/AFront.scala b/src/org/poly2tri/cdt/AFront.scala index 5c1744a..2648db0 100644 --- a/src/org/poly2tri/cdt/AFront.scala +++ b/src/org/poly2tri/cdt/AFront.scala @@ -101,7 +101,7 @@ class AFront(iTriangle: Triangle) { var marked = false // Scan the advancing front and update Node triangle pointers - while(node != null && node != eNode.next) { + while(node != null && node != eNode) { T2.foreach(t => { if(t.contains(node.point, node.next.point)) diff --git a/src/org/poly2tri/cdt/CDT.scala b/src/org/poly2tri/cdt/CDT.scala index de09f94..134c450 100644 --- a/src/org/poly2tri/cdt/CDT.scala +++ b/src/org/poly2tri/cdt/CDT.scala @@ -30,11 +30,10 @@ */ package org.poly2tri.cdt -import scala.collection.mutable.{ArrayBuffer, Set} +import scala.collection.mutable.ArrayBuffer import shapes.{Segment, Point, Triangle} import utils.Util -import seidel.MonotoneMountain /** * Sweep-line, Constrained Delauney Triangulation (CDT) @@ -45,14 +44,25 @@ import seidel.MonotoneMountain // NOTE: May need to implement edge insertion which combines advancing front (AF) // and triangle traversal respectively. See figure 14(a) from Domiter et al. // Although it may not be necessary for simple polygons.... -object CDT { + +class CDT(polyLine: Array[Point], clearPoint: Point) { + + // Triangle list + def triangles = mesh.triangles + def triangleMesh = mesh.map + def debugTriangles = mesh.debug - // Inital triangle factor - val ALPHA = 0.3f - var clearPoint = 0 + // Initialize edges + initEdges(polyLine) - // Triangulate simple polygon - def init(points: ArrayBuffer[Point]): CDT = { + // Add a hole + def addHole(holePolyLine: Array[Point]) { + initEdges(holePolyLine) + points = points ++ holePolyLine.toList + } + + // Triangulate simple polygon with holes + def triangulate { var xmax, xmin = points.first.x var ymax, ymin = points.first.y @@ -71,33 +81,39 @@ object CDT { val p1 = Point(xmin - deltaX, ymin - deltaY) val p2 = Point(xmax + deltaX, p1.y) - val segments = initSegments(points) - val sortedPoints = pointSort(points) + // Sort the points along y-axis + points = pointSort - val tPoints = Array(sortedPoints(0), p1, p2) - val iTriangle = new Triangle(tPoints) - new CDT(sortedPoints, segments, iTriangle) + // Initial triangle + val iTriangle = new Triangle(Array(points(0), p1, p2)) + mesh.map += iTriangle + aFront = new AFront(iTriangle) + + // Sweep points; build mesh + sweep + // Finalize triangulation + finalization + } - // Create segments and connect end points; update edge event pointer - private def initSegments(points: ArrayBuffer[Point]): List[Segment] = { + // Create edges and connect end points; update edge event pointer + private def initEdges(pts: Array[Point]) { - var segments = List[Segment]() - - for(i <- 0 until points.size-1) { - val endPoints = validatePoints(points(i), points(i+1)) - segments = new Segment(endPoints(0), endPoints(1)) :: segments - endPoints(1).edges += segments.first + // Connect pts + for(i <- 0 until pts.size-1) { + val endPoints = validatePoints(pts(i), pts(i+1)) + val edge = new Segment(endPoints(0), endPoints(1)) + endPoints(1).edges += edge } - val endPoints = validatePoints(points.first, points.last) - segments = new Segment(endPoints(0), endPoints(1)) :: segments - endPoints(1).edges += segments.first + // Connect endpoints + val endPoints = validatePoints(pts.first, pts.last) + val edge = new Segment(endPoints(0), endPoints(1)) + endPoints(1).edges += edge - segments } - def validatePoints(p1: Point, p2: Point): List[Point] = { + private def validatePoints(p1: Point, p2: Point): List[Point] = { if(p1.y > p2.y) { // For CDT we want q to be the point with > y @@ -108,6 +124,7 @@ object CDT { if(p1.x > p2.x) { return List(p2, p1) } else if(p1.x == p2.x) { + println(p1 + "," + p2) throw new Exception("Duplicate point") } } @@ -115,50 +132,17 @@ object CDT { List(p1, p2) } - // Insertion sort is one of the fastest algorithms for sorting arrays containing - // fewer than ten elements, or for lists that are already mostly sorted. // Merge sort: O(n log n) - private def pointSort(points: ArrayBuffer[Point]): List[Point] = { - if(points.size < 10) - Util.insertSort((p1: Point, p2: Point) => p1 > p2)(points).toList - else - Util.msort((p1: Point, p2: Point) => p1 > p2)(points.toList) - } - -} - -class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Triangle) { + private def pointSort: List[Point] = + Util.msort((p1: Point, p2: Point) => p1 > p2)(points) - - // Triangle list - def triangles = mesh.triangles - def triangleMesh = mesh.map - def debugTriangles = mesh.debug - - // The triangle mesh - private val mesh = new Mesh(iTriangle) - // Advancing front - private val aFront = new AFront(iTriangle) - - private val PI_2 = Math.Pi/2 - private val PI_34 = Math.Pi*3/4 - - // Triangle used to clean interior - var cleanTri: Triangle = null - - // Sweep points; build mesh - sweep - // Finalize triangulation - finalization - // Implement sweep-line private def sweep { - for(i <- 1 until points.size) { + for(i <- 1 until 36 /*points.size*/) { val point = points(i) // Process Point event val node = pointEvent(point) - if(i == CDT.clearPoint) {cleanTri = node.triangle; mesh.debug += cleanTri} // Process edge events point.edges.foreach(e => edgeEvent(e, node)) } @@ -168,10 +152,16 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Final step in the sweep-line CDT algo // Clean exterior triangles private def finalization { - - mesh.map.foreach(m => m.markNeighborEdges) + var found = false + mesh.map.foreach(m => { + if(!found) + if(m.pointIn(clearPoint)) { + found = true + cleanTri = m + } + m.markNeighborEdges + }) mesh clean cleanTri - } // Point event @@ -201,17 +191,14 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Remove intersected triangles if(firstTriangle != null && !firstTriangle.contains(edge)) { - + // Collect intersected triangles val tList = new ArrayBuffer[Triangle] tList += firstTriangle // Not sure why tList.last is null sometimes.... - while(tList.last != null && !tList.last.contains(edge.p)) - tList += tList.last.findNeighbor(edge.p - edge.q) - - if(tList.last == null) - tList -= tList.last + while(!tList.last.contains(edge.p)) + tList += tList.last.findNeighbor(edge.p) // Neighbor triangles val nTriangles = new ArrayBuffer[Triangle] @@ -221,8 +208,10 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian tList.foreach(t => { t.neighbors.foreach(n => if(n != null && !tList.contains(n)) nTriangles += n) mesh.map -= t + //mesh.debug += t }) - + //nTriangles.foreach(n => mesh.debug += n) + val lPoints = new ArrayBuffer[Point] val rPoints = new ArrayBuffer[Point] @@ -249,20 +238,23 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian triangulate(lPoints.toArray, List(edge.q, edge.p), T1) val T2 = new ArrayBuffer[Triangle] triangulate(rPoints.toArray, List(edge.q, edge.p), T2) - + // Update neighbors edgeNeighbors(nTriangles, T1) edgeNeighbors(nTriangles, T2) T1.last.markNeighbor(T2.last) // Update advancing front - + val ahead = (edge.p.x > edge.q.x) val point1 = if(ahead) edge.q else edge.p val point2 = if(ahead) edge.p else edge.q + + val sNode = if(ahead) node else aFront.locate(point1).prev + val eNode = aFront.locate(point2).next - val sNode = aFront.locate(point1) - val eNode = aFront.locate(point2) + //mesh.debug += sNode.triangle + //mesh.debug += eNode.triangle aFront.constrainedEdge(sNode, eNode, T1, T2, edge) @@ -270,9 +262,15 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian T1.last markEdge(point1, point2) T2.last markEdge(point1, point2) // Copy constraied edges from old triangles - T1.foreach(t => t.markEdge(tList)) - T2.foreach(t => t.markEdge(tList)) + T1.foreach(t => {t.markEdge(tList)/*;mesh.debug += t*/}) + T2.foreach(t => {t.markEdge(tList)/*;mesh.debug += t*/}) + var n = sNode + while(n != eNode) { + mesh.debug += n.triangle + n = n.next + } + } else if(firstTriangle == null) { // No triangles are intersected by the edge; edge must lie outside the mesh @@ -323,11 +321,11 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Update neigbor pointers for edge event // Inneficient, but it works well... - def edgeNeighbors(nTriangles: ArrayBuffer[Triangle], T: ArrayBuffer[Triangle]) { + private def edgeNeighbors(nTriangles: ArrayBuffer[Triangle], T: ArrayBuffer[Triangle]) { for(t1 <- nTriangles) for(t2 <- T) - t1.markNeighbor(t2) + t2.markNeighbor(t1) for(i <- 0 until T.size) for(j <- i+1 until T.size) @@ -411,7 +409,7 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian // Circumcircle test. // Determines if point d lies inside triangle abc's circumcircle - def illegal(a: Point, b: Point, c: Point, d: Point): Boolean = { + private def illegal(a: Point, b: Point, c: Point, d: Point): Boolean = { val ccw = Util.orient2d(a, b, c) > 0 @@ -476,4 +474,17 @@ class CDT(val points: List[Point], val segments: List[Segment], iTriangle: Trian } + // The triangle mesh + private val mesh = new Mesh + // Advancing front + private var aFront: AFront = null + // Sorted point list + private var points = polyLine.toList + // Half Pi + private val PI_2 = Math.Pi/2 + // Inital triangle factor + private val ALPHA = 0.3f + // Triangle used to clean interior + private var cleanTri: Triangle = null + } diff --git a/src/org/poly2tri/cdt/Mesh.scala b/src/org/poly2tri/cdt/Mesh.scala index de1fa5d..c9d37a3 100644 --- a/src/org/poly2tri/cdt/Mesh.scala +++ b/src/org/poly2tri/cdt/Mesh.scala @@ -34,10 +34,10 @@ import scala.collection.mutable.{HashSet, ArrayBuffer} import shapes.{Point, Triangle} -class Mesh(initialTriangle: Triangle) { +class Mesh { // Triangles that constitute the mesh - val map = HashSet(initialTriangle) + val map = HashSet.empty[Triangle] // Debug triangles val debug = HashSet.empty[Triangle] val triangles = new ArrayBuffer[Triangle] diff --git a/src/org/poly2tri/shapes/Triangle.scala b/src/org/poly2tri/shapes/Triangle.scala index 2d134a7..4718692 100644 --- a/src/org/poly2tri/shapes/Triangle.scala +++ b/src/org/poly2tri/shapes/Triangle.scala @@ -155,15 +155,18 @@ class Triangle(val points: Array[Point]) { // Locate next triangle crossed by edge def findNeighbor(e: Point): Triangle = { - if(Util.orient2d(points(0), points(1), e) < 0) + + if(contains(e)) return this + + if(Util.orient2d(points(1), points(0), e) > 0) return neighbors(2) - else if(Util.orient2d(points(1), points(2), e) < 0) + else if(Util.orient2d(points(2), points(1), e) > 0) return neighbors(0) - else if(Util.orient2d(points(2), points(0), e) < 0) + else if(Util.orient2d(points(0), points(2), e) > 0) return neighbors(1) else - // Point must reside inside this triangle - this + throw new Exception("Point not found") + } // The neighbor clockwise to given point @@ -337,4 +340,10 @@ class Triangle(val points: Array[Point]) { (b*h*0.5f) } + def centroid: Point = { + val cx = (points(0).x + points(1).x + points(2).x)/3f + val cy = (points(0).y + points(1).y + points(2).y)/3f + Point(cx, cy) + } + } diff --git a/src/org/poly2tri/utils/Util.scala b/src/org/poly2tri/utils/Util.scala index f4e775c..f9aaf19 100644 --- a/src/org/poly2tri/utils/Util.scala +++ b/src/org/poly2tri/utils/Util.scala @@ -85,7 +85,7 @@ object Util { // negative if point a, b, and c are clockwise // zero if points are collinear // See: http://www-2.cs.cmu.edu/~quake/robust.html - def orient(b: Point, a: Point, p: Point): Float = { + def orient(a: Point, b: Point, p: Point): Float = { val acx = a.x - p.x val bcx = b.x - p.x val acy = a.y - p.y