module Algorithms.Geometry.ConvexHull.DivideAndConquer( convexHull
, upperHull
, lowerHull
) where
import Algorithms.DivideAndConquer
import Control.Arrow ((&&&))
import Control.Lens ((^.), to)
import Data.Ext
import Data.Geometry.Point
import Data.Geometry.Polygon
import Data.Geometry.Polygon.Convex
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Util
convexHull :: (Ord r, Num r) => NonEmpty (Point 2 r :+ p) -> ConvexPolygon p r
convexHull (p :| []) = ConvexPolygon . fromPoints $ [p]
convexHull pts = combine . (upperHull' &&& lowerHull') . NonEmpty.sortBy incXdecY $ pts
where
combine (l:|uh,_:|lh) = ConvexPolygon . fromPoints $ l : uh <> reverse (init lh)
lowerHull :: (Ord r, Num r)
=> NonEmpty (Point 2 r :+ p) -> NonEmpty (Point 2 r :+ p)
lowerHull = lowerHull' . NonEmpty.sortBy incXdecY
lowerHull' :: (Ord r, Num r) => NonEmpty (Point 2 r :+ p) -> NonEmpty (Point 2 r :+ p)
lowerHull' = unLH . divideAndConquer1 (LH . (:|[]))
newtype LH r p = LH { unLH :: NonEmpty (Point 2 r :+ p) } deriving (Eq,Show)
instance (Num r, Ord r) => Semigroup (LH r p) where
(LH lh) <> (LH rh) = LH $ hull lowerTangent' lh rh
upperHull :: (Ord r, Num r) => NonEmpty (Point 2 r :+ p) -> NonEmpty (Point 2 r :+ p)
upperHull = upperHull' . NonEmpty.sortBy incXdecY
upperHull' :: (Ord r, Num r) => NonEmpty (Point 2 r :+ p) -> NonEmpty (Point 2 r :+ p)
upperHull' = unUH . divideAndConquer1 (UH . (:|[]))
newtype UH r p = UH { unUH :: NonEmpty (Point 2 r :+ p) }
instance (Num r, Ord r) => Semigroup (UH r p) where
(UH lh) <> (UH rh) = UH $ hull upperTangent' lh rh
hull :: (NonEmpty p -> NonEmpty p -> Two (p :+ [p]))
-> NonEmpty p -> NonEmpty p -> NonEmpty p
hull tangent lh rh = let Two (l :+ lh') (r :+ rh') = tangent (NonEmpty.reverse lh) rh
in NonEmpty.fromList $ (reverse lh') <> [l,r] <> rh'
incXdecY :: Ord r => (Point 2 r) :+ p -> (Point 2 r) :+ q -> Ordering
incXdecY (Point2 px py :+ _) (Point2 qx qy :+ _) =
compare px qx <> compare qy py