All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ietze.vectory_sjs0.6_2.10.0.1.0.source-code.ConvexHull2D.scala Maven / Gradle / Ivy

The newest version!
package vectory

import collection.breakOut

//===================================================================
// chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm
//     Input:  P[] = an array of 2D points
//                   presorted by increasing x- and y-coordinates
//             n = the number of points in P[]
//     Output: H[] = an array of the convex hull vertices (max is n)
//     Return: the number of points in H[]

object ConvexHull2D {
  def apply(in: Iterable[Vec2]): Seq[Vec2] = {
    val verts: IndexedSeq[Vec2] = in.toIndexedSeq sortWith compare
    val n = verts.size

    val minmin = 0
    val xmin = verts(minmin).x
    var minmax = 0
    while (minmax < n - 1 && verts(minmax + 1).x == xmin)
      minmax += 1

    val maxmax = n - 1
    val xmax = verts(maxmax).x
    var maxmin = n - 1
    while (maxmin > 1 && verts(maxmin - 1).x == xmax) {
      maxmin -= 1
    }

    var stack: List[Vec2] = Nil

    if (minmax == n - 1) { // degenerate case: all x-coords == xmin
      stack ::= verts(minmin)
      if (verts(minmin).y != verts(minmax).y)
        stack ::= verts(minmax) // a nontrivial segment
      stack
    } else {
      // Compute the lower hull on the stack H
      stack ::= verts(minmin)

      for (i ← minmax + 1 to maxmin) {
        // the lower line joins P[minmin] with P[maxmin]
        // ignore P[i] above or on the lower line
        if (isLeft(verts(minmin), verts(maxmin), verts(i)) < 0 || i == maxmin) {
          while (stack.size > 1 && isLeft(stack(1), stack(0), verts(i)) <= 0)
            stack = stack.tail
          stack ::= verts(i)
        }
      }

      if (maxmax != maxmin)
        stack ::= verts(maxmax)

      val bottom = stack.size

      // Next, compute the upper hull on the stack H above the bottom hull
      for (i ← (maxmin - 1).to(minmax, -1)) {
        // the upper line joins P[maxmax] with P[minmax]
        if (isLeft(verts(maxmax), verts(minmax), verts(i)) < 0 || i == minmax) {
          // ignore P[i] below or on the upper line
          while (stack.size > bottom && isLeft(stack(1), stack(0), verts(i)) <= 0)
            stack = stack.tail
          stack ::= verts(i)
        }
      }

      // push joining endpoint onto stack
      if (minmax == minmin)
        stack = stack.tail

      stack
    }
  }

  @inline def isLeft(p0: Vec2, p1: Vec2, p2: Vec2) = (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y)

  val compare = (v1: Vec2, v2: Vec2) => if (v1.x == v2.x) (v1.y < v2.y) else (v1.x < v2.x)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy