2014-10-08 14:39:46 +00:00
|
|
|
{-# OPTIONS_HADDOCK ignore-exports #-}
|
|
|
|
|
2014-10-10 15:40:08 +00:00
|
|
|
module Algorithms.ConvexHull.GrahamScan where
|
2014-10-08 14:39:46 +00:00
|
|
|
|
2014-10-10 15:40:08 +00:00
|
|
|
import Algebra.Vector
|
|
|
|
import Algebra.VectorTypes
|
2014-10-08 14:39:46 +00:00
|
|
|
import Diagrams.TwoD.Types
|
2014-10-10 15:40:08 +00:00
|
|
|
import MyPrelude
|
2014-10-08 14:39:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Get all points on a convex hull by using the graham scan
|
|
|
|
-- algorithm.
|
2014-10-12 16:44:48 +00:00
|
|
|
{--
|
|
|
|
========== FUNCTIONAL PSEUDO CODE ======================
|
|
|
|
input: unsorted list us'
|
|
|
|
output: sorted convex hull list
|
|
|
|
|
|
|
|
variables:
|
|
|
|
(lowerHull, restl) = splitAt3IntoTuple (sort us')
|
|
|
|
(upperHull, restu) = reverse (splitAt3IntoTuple (sort us'))
|
|
|
|
|
|
|
|
main scope:
|
|
|
|
return (scanHalf upperHull restu) ++
|
|
|
|
(stripFirstAndLastElem(scanHalf lowerHull restl))
|
|
|
|
|
|
|
|
=== begin scanHalf function ===
|
|
|
|
scanHalf (min 3 elem => lowerHull) (min 1 elem => rest)
|
2014-10-12 18:37:24 +00:00
|
|
|
| isNotClockWise (last3Elements lowerHull) == True
|
2014-10-12 16:44:48 +00:00
|
|
|
= scanHalf (lowerHull + head rest) (tail rest)
|
|
|
|
| otherwise
|
|
|
|
= scanHalf (deleteSndToLastElem lowerHull + head rest)
|
2014-10-12 17:19:44 +00:00
|
|
|
(rest)
|
2014-10-12 16:44:48 +00:00
|
|
|
|
|
|
|
scanHalf (min 3 elem => lowerHull ) []
|
2014-10-12 18:37:24 +00:00
|
|
|
| isNotClockWise (last3Elements lowerHull) == True
|
2014-10-12 16:44:48 +00:00
|
|
|
= return lowerHull
|
|
|
|
| otherwise
|
|
|
|
= scanHalf (deleteSndToLastElem lowerHull) []
|
|
|
|
|
2014-10-12 17:19:44 +00:00
|
|
|
scanHalf lowerHull (min 1 elem => rest) = scanHalf (lowerHull + head rest)
|
|
|
|
(tail rest)
|
|
|
|
|
|
|
|
|
2014-10-12 16:44:48 +00:00
|
|
|
scanHalf lowerHull _ = lowerHull
|
|
|
|
=== end scanHalf function ===
|
|
|
|
|
|
|
|
|
|
|
|
============= SIMULATION ===================================
|
|
|
|
xs = [(100, 100), (200, 450), (250, 250)]
|
|
|
|
ys = [(300, 400), (400, 200)]
|
|
|
|
|
2014-10-12 18:37:24 +00:00
|
|
|
notcw (100, 100) (200, 450) (250, 250) => false, pop snd2last of xs
|
2014-10-12 16:44:48 +00:00
|
|
|
===
|
|
|
|
move first of ys to end of xs
|
|
|
|
|
|
|
|
xs = [(100, 100), (250, 250), (300, 400)]
|
|
|
|
ys = [(400, 200)]
|
|
|
|
|
2014-10-12 18:37:24 +00:00
|
|
|
notcw (100, 100), (250, 250) (300, 400) => true
|
2014-10-12 16:44:48 +00:00
|
|
|
===
|
|
|
|
move first of ys to end of xs
|
|
|
|
|
|
|
|
xs = [(100, 100), (250, 250), (300, 400), (400, 200)]
|
|
|
|
ys = []
|
|
|
|
|
2014-10-12 18:37:24 +00:00
|
|
|
notcw (250, 250) (300, 400) (400, 200) => false, pop snd2last of xs
|
2014-10-12 16:44:48 +00:00
|
|
|
===
|
|
|
|
xs = [(100, 100), (250, 250), (400, 200)]
|
|
|
|
ys = []
|
|
|
|
|
2014-10-12 18:37:24 +00:00
|
|
|
notcw (100, 100) (250, 250) (400, 200) => false, pop snd2last of xs
|
2014-10-12 16:44:48 +00:00
|
|
|
===
|
|
|
|
xs = [(100, 100), (400, 200)]
|
|
|
|
ys = []
|
|
|
|
===
|
|
|
|
return [(100, 100), (400, 200)]
|
|
|
|
=========================================================
|
|
|
|
--}
|
2014-10-13 17:49:53 +00:00
|
|
|
grahamCH :: [PT] -> [PT]
|
|
|
|
grahamCH vs = grahamUCH vs ++ (tailInit . grahamLCH $ vs)
|
|
|
|
|
|
|
|
|
|
|
|
-- |Get the lower part of the convex hull.
|
|
|
|
grahamLCH :: [PT] -> [PT]
|
|
|
|
grahamLCH vs = scanH lH lHRest
|
2014-10-08 14:39:46 +00:00
|
|
|
where
|
2014-10-12 00:11:47 +00:00
|
|
|
sortedXY = fmap p2 . sortLex . fmap unp2 $ vs
|
2014-10-12 16:44:48 +00:00
|
|
|
(lH, lHRest) = first reverse . splitAt 3 $ sortedXY
|
2014-10-13 17:49:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Get the upper part of the convex hull.
|
|
|
|
grahamUCH :: [PT] -> [PT]
|
|
|
|
grahamUCH vs = scanH uH uHRest
|
|
|
|
where
|
|
|
|
sortedXY = fmap p2 . sortLex . fmap unp2 $ vs
|
2014-10-12 16:44:48 +00:00
|
|
|
(uH, uHRest) = first reverse . splitAt 3 . reverse $ sortedXY
|
2014-10-13 17:49:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |This scans only a half of the convex hull. If it's the upper
|
|
|
|
-- or lower half depends on the input.
|
|
|
|
-- Also, the first list is reversed since we only care about the last
|
|
|
|
-- 3 elements and want to stay efficient.
|
|
|
|
scanH :: [PT] -- ^ the first 3 starting points in reversed order
|
|
|
|
-> [PT] -- ^ the rest of the points
|
|
|
|
-> [PT] -- ^ all convex hull points for the half
|
|
|
|
scanH hs@(x:y:z:xs) (r':rs')
|
|
|
|
| notcw z y x = scanH (r':hs) rs'
|
|
|
|
| otherwise = scanH (x:z:xs) (r':rs')
|
|
|
|
scanH hs@(x:y:z:xs) []
|
|
|
|
| notcw z y x = hs
|
|
|
|
| otherwise = scanH (x:z:xs) []
|
|
|
|
scanH hs (r':rs') = scanH (r':hs) rs'
|
|
|
|
scanH hs _ = hs
|
2014-10-09 01:10:21 +00:00
|
|
|
|
|
|
|
|
2014-10-09 01:27:02 +00:00
|
|
|
-- |Compute all steps of the graham scan algorithm to allow
|
2014-10-09 01:10:21 +00:00
|
|
|
-- visualizing it.
|
2014-10-13 00:58:18 +00:00
|
|
|
-- Whether the upper or lower hull is computed depends on the input.
|
2014-10-13 17:50:02 +00:00
|
|
|
grahamCHSteps :: Int -> [PT] -> [PT] -> [[PT]]
|
|
|
|
grahamCHSteps c xs' ys'
|
|
|
|
| c >= 0 = scanH 0 xs' ys' : grahamCHSteps (c - 1) xs' ys'
|
2014-10-13 00:58:18 +00:00
|
|
|
| otherwise = []
|
2014-10-09 01:10:21 +00:00
|
|
|
where
|
2014-10-13 00:58:18 +00:00
|
|
|
scanH c' hs@(x:y:z:xs) (r':rs')
|
|
|
|
| c' >= c = hs
|
|
|
|
| notcw z y x = scanH (c' + 1) (r':hs) rs'
|
|
|
|
| otherwise = scanH (c' + 1) (x:z:xs) (r':rs')
|
|
|
|
scanH c' hs@(x:y:z:xs) []
|
|
|
|
| c' >= c = hs
|
|
|
|
| notcw z y x = hs
|
|
|
|
| otherwise = scanH (c' + 1) (x:z:xs) []
|
|
|
|
scanH c' hs (r':rs')
|
|
|
|
| c' >= c = hs
|
|
|
|
| otherwise = scanH (c' + 1) (r':hs) rs'
|
|
|
|
scanH _ xs _ = xs
|
|
|
|
|
|
|
|
|
2014-10-13 01:25:22 +00:00
|
|
|
-- |Get all iterations of the upper hull of the graham scan algorithm.
|
2014-10-13 17:50:02 +00:00
|
|
|
grahamUHSteps :: [PT] -> [[PT]]
|
|
|
|
grahamUHSteps vs =
|
2014-10-13 01:25:22 +00:00
|
|
|
(++) [getLastX 2 sortedXY] .
|
|
|
|
rmdups .
|
|
|
|
init .
|
|
|
|
reverse .
|
2014-10-13 17:50:02 +00:00
|
|
|
grahamCHSteps ((* 2) . length $ vs) uH $
|
2014-10-13 00:58:18 +00:00
|
|
|
uHRest
|
|
|
|
where
|
|
|
|
sortedXY = fmap p2 . sortLex . fmap unp2 $ vs
|
2014-10-12 16:44:48 +00:00
|
|
|
(uH, uHRest) = first reverse . splitAt 3 . reverse $ sortedXY
|
2014-10-13 00:58:18 +00:00
|
|
|
|
|
|
|
|
2014-10-13 01:25:22 +00:00
|
|
|
-- |Get all iterations of the lower hull of the graham scan algorithm.
|
2014-10-13 17:50:02 +00:00
|
|
|
grahamLHSteps :: [PT] -> [[PT]]
|
|
|
|
grahamLHSteps vs =
|
2014-10-13 01:25:22 +00:00
|
|
|
(++) [take 2 sortedXY] .
|
|
|
|
rmdups .
|
|
|
|
reverse .
|
2014-10-13 17:50:02 +00:00
|
|
|
grahamCHSteps ((* 2) . length $ vs) lH $
|
2014-10-13 01:25:22 +00:00
|
|
|
lHRest
|
2014-10-13 00:58:18 +00:00
|
|
|
where
|
|
|
|
sortedXY = fmap p2 . sortLex . fmap unp2 $ vs
|
|
|
|
(lH, lHRest) = first reverse . splitAt 3 $ sortedXY
|