Convex Hull Graham

/**
 * Author: Arnab Ray
 * ConvexHull using Graham Scan
 * Wikipedia: https://en.wikipedia.org/wiki/Graham_scan
 * Given a set of points in the plane. The Convex hull of the set is the smallest
 * convex polygon that contains all the points of it.
 */

function compare(a, b) {
  // Compare Function to Sort the points, a and b are points to compare
  if (a.x < b.x) return -1
  if (a.x === b.x && a.y < b.y) return -1
  return 1
}
function orientation(a, b, c) {
  // Check orientation of Line(a,b) and Line(b,c)
  const alpha = (b.y - a.y) / (b.x - a.x)
  const beta = (c.y - b.y) / (c.x - b.x)

  // Clockwise
  if (alpha > beta) return 1
  // Anticlockwise
  else if (beta > alpha) return -1
  // Colinear
  return 0
}

function convexHull(points) {
  const pointsLen = points.length
  if (pointsLen <= 2) {
    throw new Error('Minimum of 3 points is required to form closed polygon!')
  }

  points.sort(compare)
  const p1 = points[0]
  const p2 = points[pointsLen - 1]

  // Divide Hull in two halves
  const upperPoints = []
  const lowerPoints = []

  upperPoints.push(p1)
  lowerPoints.push(p1)

  for (let i = 1; i < pointsLen; i++) {
    if (i === pointsLen - 1 || orientation(p1, points[i], p2) !== -1) {
      let upLen = upperPoints.length

      while (
        upLen >= 2 &&
        orientation(
          upperPoints[upLen - 2],
          upperPoints[upLen - 1],
          points[i]
        ) === -1
      ) {
        upperPoints.pop()
        upLen = upperPoints.length
      }
      upperPoints.push(points[i])
    }
    if (i === pointsLen - 1 || orientation(p1, points[i], p2) !== 1) {
      let lowLen = lowerPoints.length
      while (
        lowLen >= 2 &&
        orientation(
          lowerPoints[lowLen - 2],
          lowerPoints[lowLen - 1],
          points[i]
        ) === 1
      ) {
        lowerPoints.pop()
        lowLen = lowerPoints.length
      }
      lowerPoints.push(points[i])
    }
  }
  const hull = []
  for (let i = 1; i < upperPoints.length - 1; i++) {
    hull.push(upperPoints[i])
  }
  for (let i = lowerPoints.length - 1; i >= 0; i--) {
    hull.push(lowerPoints[i])
  }

  return hull
}

export { convexHull }

// Example

// const points = [
//   { x: 0, y: 3 },
//   { x: 1, y: 1 },
//   { x: 2, y: 2 },
//   { x: 4, y: 4 },
//   { x: 0, y: 0 },
//   { x: 1, y: 2 },
//   { x: 3, y: 1 },
//   { x: 3, y: 3 }]

// convexHull(points)