packages feed

imp-ppl-0.1.0.0: viz/Viz/ConvexHull.hs

-- | Convex hull algorithm for credal set display.
module Viz.ConvexHull
  ( convexHull2D
  ) where

import Data.List (sort, nubBy)

-- | Compute the convex hull of a set of 2D points.
--   Returns the hull vertices in counterclockwise order.
--   Uses Andrew's monotone chain algorithm: O(n log n).
convexHull2D :: [(Double, Double)] -> [(Double, Double)]
convexHull2D pts
  | length unique <= 2 = unique
  | otherwise =
      let sorted = sort unique
          lower = reverse (buildHull sorted)
          upper = reverse (buildHull (reverse sorted))
      in  init lower ++ init upper
  where
    unique = dedup2D pts
    buildHull = foldl' add []
    add stack p = p : dropRightTurns stack
      where
        dropRightTurns (b : a : rest)
          | cross2D a b p <= 0 = dropRightTurns (a : rest)
        dropRightTurns s = s

-- | Cross product of vectors (b - a) and (c - a).
cross2D :: (Double, Double) -> (Double, Double) -> (Double, Double) -> Double
cross2D (ax, ay) (bx, by) (cx, cy) =
  (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)

-- | Remove near-duplicate 2D points.
dedup2D :: [(Double, Double)] -> [(Double, Double)]
dedup2D = nubBy (\(x1, y1) (x2, y2) -> abs (x1 - x2) < epsSamePoint && abs (y1 - y2) < epsSamePoint)
  where epsSamePoint = 1e-10