Skip to content

Instantly share code, notes, and snippets.

@maddenpj
Created July 12, 2013 02:47
Show Gist options
  • Save maddenpj/5981047 to your computer and use it in GitHub Desktop.
Save maddenpj/5981047 to your computer and use it in GitHub Desktop.
Graham Scan algorithm for finding convex hull
import Data.List
data Point = Point Float Float deriving (Show, Eq)
data Direction = LeftDirection
| RightDirection
| StraightDirection deriving (Show)
dot :: Point -> Point -> Float
dot (Point x1 y1) (Point x2 y2) = x1*x2 + y1*y2
plength :: Point -> Float
plength (Point x y) = sqrt (x*x + y*y)
angle :: Point -> Point -> Float
angle a b = acos ((dot a b)/(plength a * plength b))
add :: Point -> Point -> Point
add (Point x2 y2) (Point x1 y1) = Point (x2+x1) (y2+y1)
diff :: Point -> Point -> Point
diff (Point x2 y2) (Point x1 y1) = Point (x2-x1) (y2-y1)
ccw :: Point -> Point -> Point -> Float
ccw (Point x1 y1) (Point x2 y2) (Point x3 y3) = (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1)
drive :: Point -> Point -> Point -> Direction
drive a b c
| x < 0 = RightDirection
| x > 0 = LeftDirection
| x == 0 = StraightDirection
where x = ccw a b c
driveList :: [Point] -> [Direction]
driveList (a:b:c:xs) = drive a b c :driveList (b:c:xs)
driveList _ = []
sortFindLowest :: Point -> Point -> Ordering
sortFindLowest (Point x1 y1) (Point x2 y2)
| y1 > y2 = GT
| y1 < y2 = LT
| y1 == y2 = if x1 > x2 then GT
else if x1 < x2 then LT
else EQ
sortWithLowest :: Point -> Point -> Point -> Ordering
sortWithLowest p a b
| angleA > angleB = GT
| angleA < angleB = LT
| angleA == angleB = EQ
where angleA = angle px a
angleB = angle px b
px = (Point 1 0) `diff` p
-- Takes unordered points, returns ordered list in shape of convex hull
grahamScan :: [Point] -> [Point]
grahamScan points = p:(foldl cullRight [] possible)
where possible = zip angularSort $ driveList (p:angularSort ++ [p])
angularSort = sortBy (sortWithLowest p) $ tail geographicSort
p = head geographicSort
geographicSort = sortBy sortFindLowest points
cullRight :: [Point] -> (Point, Direction) -> [Point]
cullRight xs (_, RightDirection) = xs
cullRight xs (p, _) = xs ++ [p]
a = Point 0 0
b = Point 1 0
list = [a, b, (Point 1 1), (Point 2 1), (Point 2 2), (Point 1 3), (Point 0 2)]
grahamScan list
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment