Skip to content

Instantly share code, notes, and snippets.

@theonejb
Created July 3, 2014 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theonejb/a913c33a5f616f76846f to your computer and use it in GitHub Desktop.
Save theonejb/a913c33a5f616f76846f to your computer and use it in GitHub Desktop.
Grahams Scan in Haskell
-- GrahamScan.hs
-- Implements Graham's Scan algo to find the convex hull of a group of 2D points
-- http://en.wikipedia.org/wiki/Graham_scan
import GHC.Exts
import Data.List
data Point = Point {
x :: Double,
y :: Double
} deriving (Eq, Show)
data Direction = DLeft | DRight | DStraight deriving (Eq, Show)
comparePoints p1 p2 =
case compare (y p1) (y p2) of
GT -> GT
LT -> LT
EQ -> compare (x p1) (x p2)
sortPointsByCoordinates ps = sortBy comparePoints ps
getAngleOfPointWithXAxis p1 p2
| (x p1) == (x p2) = 1.571
| (y p1) == (y p2) = 0.000
| otherwise =
let theta = atan (((y p2) - (y p1)) / ((x p2) - (x p1)))
in
if theta < 0
then pi + theta
else theta
sortPointsByAngleWithXAxis points = basePoint : sortWith getAngleOfPoint (tail ps)
where
ps = sortPointsByCoordinates points
basePoint = head ps
getAngleOfPoint p = getAngleOfPointWithXAxis basePoint p
getDirectionOfLines p1 p2 p3
| zcord == 0 = DStraight
| zcord > 0 = DLeft
| zcord < 0 = DRight
where zcord = (((x p2) - (x p1)) * ((y p3) - (y p1))) - (((y p2) - (y p1)) * ((x p3) - (x p1)))
-- Main Graham Scan function
_grahamScan [] stack = reverse stack -- Reverse the stack back to a list
_grahamScan points stack =
case direction of
DLeft ->
_grahamScan ps (p:stack)
_ ->
_grahamScan points (tail stack)
where
p = head points
ps = tail points
direction = getDirectionOfLines (head (tail stack)) (head stack) p
-- Entry point for the Graham Scan function
grahamScan points =
let
sortedPoints = sortPointsByAngleWithXAxis points
stack = reverse (take 3 sortedPoints) -- Reverse list so that it acts as a stack
ps = drop 3 sortedPoints
in
(_grahamScan ps stack) ++ [head sortedPoints]
pointsList1 =
[
Point 30 30,
Point 50 60,
Point 60 20,
Point 70 45,
Point 86 39,
Point 112 60,
Point 200 113,
Point 250 50,
Point 300 200,
Point 130 240,
Point 76 150,
Point 47 76,
Point 36 40,
Point 33 35,
Point 30 30
]
pointsList2 =
[
Point 50 60,
Point 60 20,
Point 70 45,
Point 100 70,
Point 125 90,
Point 200 113,
Point 250 140,
Point 180 170,
Point 105 140,
Point 79 140,
Point 60 85,
Point 50 60
]
pointsList3 =
[
Point 60 20,
Point 250 140,
Point 180 170,
Point 79 140,
Point 50 60,
Point 60 20
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment