Last active
August 29, 2015 14:09
-
-
Save ashaindlin/c93f7ea6cab773f97103 to your computer and use it in GitHub Desktop.
Graham scan for convex hull calculation (Real World Haskell Ch. 3 Exercises 10-13)
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
module ConvexHull where | |
import Data.List (sortBy, delete) | |
import Data.Ord (comparing) | |
data Direction = Left | Right | Straight | |
deriving (Show, Eq) | |
data Point = Point { | |
x :: Double, | |
y :: Double | |
} deriving (Show, Eq) | |
-- Calculate the turn made by three 2D points | |
getDirection :: Point -> Point -> Point -> Direction | |
getDirection a b c | |
| turn > 0 = ConvexHull.Left | |
| turn == 0 = ConvexHull.Straight | |
| turn < 0 = ConvexHull.Right | |
where turn = (((x b)-(x a))*((y c)-(y a))) - (((y b)-(y a)))*(((x c)-(x a))) | |
-- Given a list of points, list the directions of turns for them | |
path :: [Point] -> [Direction] | |
path (a:b:c:ps) = (getDirection a b c):(path (b:c:ps)) | |
path _ = [] | |
-- Starts in the +x direction and sweeps out a complete counter-clockwise circle | |
angleFromX :: Point -> Double | |
angleFromX p | |
| angle >= 0 = angle | |
| otherwise = angle + 2*pi | |
where angle = atan2 (y p) (x p) | |
-- Sort points by angle from x-axis and duplicate the first one at end of list | |
preparePoints :: [Point] -> [Point] | |
preparePoints ps = sorted ++ [head sorted] | |
where sorted = sortBy (comparing angleFromX) ps | |
grahamScan :: [Point] -> [Point] | |
grahamScan shape = (head ps):(map snd $ filter turn $ zip (path ps) (trim ps)) | |
where | |
ps = preparePoints shape | |
turn (d, _) = d /= ConvexHull.Right | |
trim xs = init $ drop 1 xs |
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
Prelude> :l ConvexHull.hs | |
[1 of 1] Compiling ConvexHull ( ConvexHull.hs, interpreted ) | |
Ok, modules loaded: ConvexHull. | |
*ConvexHull> let a = [(Point 1 1), (Point 2 1), (Point (-1) 2), (Point 1 (-1)), (Point 1 (-2)), (Point (-1) (-1))] | |
*ConvexHull> grahamScan a | |
[Point {x = 2.0, y = 1.0},Point {x = -1.0, y = 2.0},Point {x = -1.0, y = -1.0},Point {x = 1.0, y = -2.0}] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The definition of
turn
on line 43 allows collinear points to remain in the returned list of hull points. If you wanted to exclude collinear points from the hull, you could useturn (d, _) = d == ConvexHull.Left
.