Difference between revisions of "Graham Scan Implementation"
Jump to navigation
Jump to search
m |
|||
Line 2: | Line 2: | ||
<haskell> |
<haskell> |
||
+ | import Data.Ord (comparing) |
||
--Graham Scan exercise |
--Graham Scan exercise |
||
Line 37: | Line 38: | ||
GT -> LeftTurn |
GT -> LeftTurn |
||
LT -> RightTurn |
LT -> RightTurn |
||
− | where sign = compare ((bx - ax) * (cy - ay) |
+ | where sign = compare ((bx - ax) * (cy - ay)) ((by - ay) * (cx - ax)) |
--Get a list of Directions from a list of Points |
--Get a list of Directions from a list of Points |
||
Line 47: | Line 48: | ||
sortByY :: [Point] -> [Point] |
sortByY :: [Point] -> [Point] |
||
sortByY xs = sortBy lowestY xs |
sortByY xs = sortBy lowestY xs |
||
− | where lowestY (Point(x1,y1)) (Point (x2,y2)) = |
+ | where lowestY (Point(x1,y1)) (Point (x2,y2)) = compare (y1,x1) (y2,x2) |
− | then compare x1 x2 |
||
− | else compare y1 y2 |
||
--get COT of line defined by two points and the x-axis |
--get COT of line defined by two points and the x-axis |
||
pointAngle :: Point -> Point -> Double |
pointAngle :: Point -> Point -> Double |
||
Line 68: | Line 67: | ||
--Compare angles |
--Compare angles |
||
compareAngles :: Point -> Point -> Point -> Ordering |
compareAngles :: Point -> Point -> Point -> Ordering |
||
− | compareAngles |
+ | compareAngles = comparing . pointAngle |
--Graham Scan |
--Graham Scan |
Revision as of 18:52, 21 February 2010
Descriptions of this problem can be found in Real World Haskell, Chapter 3
import Data.Ord (comparing)
--Graham Scan exercise
--Direction type
data Direction = LeftTurn
| RightTurn
| Straight
deriving (Show, Eq)
--Point type
data Point = Point (Double, Double)
deriving (Show)
--some points
p0 = Point (2.1,2.0)
p1 = Point (4.2,2.0)
p2 = Point (0.5,2.5)
p3 = Point (3.2,3.5)
p4 = Point (1.2,4.0)
p5 = Point (0.7,4.7)
p6 = Point (1.0,1.0)
p7 = Point (3.0,5.2)
p8 = Point (4.0,4.0)
p9 = Point (3.5,1.5)
pA = Point (0.5,1.0)
points = [p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA]
-- Actually, I'd leave it as EQ, GT, LT. Then, actually,
-- if you wanted to sort points rotationally around a single point,
-- sortBy (dir x) would actually work. --wasserman.louis@gmail.com
--Get direction of single set of line segments
dir :: Point -> Point -> Point -> Direction
dir (Point (ax, ay)) (Point (bx, by)) (Point (cx, cy)) = case sign of
EQ -> Straight
GT -> LeftTurn
LT -> RightTurn
where sign = compare ((bx - ax) * (cy - ay)) ((by - ay) * (cx - ax))
--Get a list of Directions from a list of Points
dirlist :: [Point] -> [Direction]
dirlist (x:y:z:xs) = dir x y z : dirlist (y:z:xs)
dirlist _ = []
--Compare Y axes
sortByY :: [Point] -> [Point]
sortByY xs = sortBy lowestY xs
where lowestY (Point(x1,y1)) (Point (x2,y2)) = compare (y1,x1) (y2,x2)
--get COT of line defined by two points and the x-axis
pointAngle :: Point -> Point -> Double
pointAngle (Point (x1, y1)) (Point (x2, y2)) = (x2 - x1) / (y2 - y1)
--compare based on point angle
pointOrdering :: Point -> Point -> Ordering
pointOrdering a b = compare (pointAngle a b) 0.0
--Sort by angle
sortByAngle :: [Point] -> [Point]
sortByAngle ps = bottomLeft : sortBy (compareAngles bottomLeft) (tail (sortedPs))
where sortedPs = sortByY ps
bottomLeft = head (sortedPs)
--Compare angles
compareAngles :: Point -> Point -> Point -> Ordering
compareAngles = comparing . pointAngle
--Graham Scan
gscan :: [Point] -> [Point]
gscan ps = scan (sortByAngle ps)
where scan (x:y:z:xs) = if dir x y z == RightTurn
then x: scan (z:xs)
else x: scan (y:z:xs)
scan [x,y] = [x,y] -- there's no shame in a pattern match
-- of this type!
scan _ = []