Created
July 3, 2014 14:22
-
-
Save theonejb/a913c33a5f616f76846f to your computer and use it in GitHub Desktop.
Grahams Scan in Haskell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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