module Algorithms.Geometry.SmallestEnclosingBall.Naive( smallestEnclosingDisk
, enclosesAll
) where
import Control.Lens
import Data.Ext
import Algorithms.Geometry.SmallestEnclosingBall.Types
import Data.Geometry.Ball
import Data.Geometry.Point
import Data.List (minimumBy)
import Data.Function (on)
import Data.Maybe (fromMaybe)
import Data.Util(uniquePairs, uniqueTriplets)
import qualified Data.Util as Util
smallestEnclosingDisk :: (Ord r, Fractional r)
=> [Point 2 r :+ p]
-> DiskResult p r
smallestEnclosingDisk pts@(_:_:_) = smallestEnclosingDisk' pts $
pairs pts ++ triplets pts
smallestEnclosingDisk _ = error "smallestEnclosingDisk: Too few points"
pairs :: Fractional r => [Point 2 r :+ p] -> [DiskResult p r]
pairs pts = [ DiskResult (fromDiameter (a^.core) (b^.core)) (Two a b)
| Util.Two a b <- uniquePairs pts]
triplets :: (Ord r, Fractional r) => [Point 2 r :+ p] -> [DiskResult p r]
triplets pts = [DiskResult (disk' a b c) (Three a b c)
| Util.Three a b c <- uniqueTriplets pts]
disk' :: (Ord r, Fractional r)
=> Point 2 r :+ p -> Point 2 r :+ p -> Point 2 r :+ p -> Disk () r
disk' a b c = fromMaybe degen $ disk (a^.core) (b^.core) (c^.core)
where
degen = (smallestEnclosingDisk' [a,b,c] $ pairs [a,b,c])^.enclosingDisk
smallestEnclosingDisk' :: (Ord r, Num r)
=> [Point 2 r :+ p] -> [DiskResult p r] -> DiskResult p r
smallestEnclosingDisk' pts = minimumBy (compare `on` (^.enclosingDisk.squaredRadius))
. filter (flip enclosesAll pts)
enclosesAll :: (Num r, Ord r) => DiskResult p r -> [Point 2 r :+ q] -> Bool
enclosesAll d = all (\(p :+ _) -> p `inClosedBall` (d^.enclosingDisk))