qhull-0.1.0.1: src/Delaunay/Delaunay.hs
module Delaunay.Delaunay
where
import Control.Monad (unless, when)
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.IntSet as IS
import Data.List.Unique (allUnique)
import Data.Maybe
import Delaunay.CDelaunay
import Delaunay.Types
import Foreign.C.Types
import Foreign.Marshal.Alloc (free, mallocBytes)
import Foreign.Marshal.Array (pokeArray)
import Foreign.Storable (peek, sizeOf)
import Qhull.Types
delaunay :: [[Double]] -- sites (vertices)
-> Bool -- add a point at infinity
-> Bool -- include degenerate tiles
-> Maybe Double -- volume threshold
-> IO Tesselation
delaunay sites atinfinity degenerate vthreshold = do
let n = length sites
dim = length (head sites)
when (dim < 2) $
error "dimension must be at least 2"
when (n <= dim+1) $
error "insufficient number of points"
unless (all (== dim) (map length (tail sites))) $
error "the points must have the same dimension"
unless (allUnique sites) $
error "some points are duplicated"
let vthreshold' = fromMaybe 0 vthreshold
sitesPtr <- mallocBytes (n * dim * sizeOf (undefined :: CDouble))
pokeArray sitesPtr (concatMap (map realToFrac) sites)
exitcodePtr <- mallocBytes (sizeOf (undefined :: CUInt))
resultPtr <- c_tesselation sitesPtr
(fromIntegral dim) (fromIntegral n)
(fromIntegral $ fromEnum atinfinity)
(fromIntegral $ fromEnum degenerate)
(realToFrac vthreshold') exitcodePtr
exitcode <- peek exitcodePtr
free exitcodePtr
free sitesPtr
if exitcode /= 0
then do
free resultPtr
error $ "qhull returned an error (code " ++ show exitcode ++ ")"
else do
result <- peek resultPtr
out <- cTesselationToTesselation sites result
free resultPtr
return out
-- | tile facets a vertex belongs to, vertex given by its index;
-- the output is the empty map if the index is not valid
vertexNeighborFacets :: Tesselation -> Index -> IntMap TileFacet
vertexNeighborFacets tess i = IM.restrictKeys (_tilefacets tess) ids
where
ids = maybe IS.empty _neighfacetsIds (IM.lookup i (_sites tess))
-- | whether a tile facet is sandwiched between two tiles
sandwichedFacet :: TileFacet -> Bool
sandwichedFacet tilefacet = IS.size (_facetOf tilefacet) == 2
-- | the tiles a facet belongs to
facetOf :: Tesselation -> TileFacet -> IntMap Tile
facetOf tess tilefacet = IM.restrictKeys (_tiles tess) (_facetOf tilefacet)
-- | the families of the tiles a facet belongs to
facetFamilies :: Tesselation -> TileFacet -> IntMap Family
facetFamilies tess tilefacet = IM.map _family (facetOf tess tilefacet)
-- | the circumcenters of the tiles a facet belongs to
facetCenters :: Tesselation -> TileFacet -> IntMap [Double]
facetCenters tess tilefacet =
IM.map _center (facetOf tess tilefacet)
funofFacetToFunofInt :: (Tesselation -> TileFacet -> IntMap a)
-> (Tesselation -> Int -> IntMap a)
funofFacetToFunofInt f tess i =
maybe IM.empty (f tess) (IM.lookup i (_tilefacets tess))
-- | the tiles a facet belongs to, facet given by its id
facetOf' :: Tesselation -> Int -> IntMap Tile
facetOf' = funofFacetToFunofInt facetOf
-- | the families of the tiles a facet belongs to, facet given by its id
facetFamilies' :: Tesselation -> Int -> IntMap Family
facetFamilies' = funofFacetToFunofInt facetFamilies
-- | the circumcenters of the tiles a facet belongs to, facet given by its id
facetCenters' :: Tesselation -> Int -> IntMap [Double]
facetCenters' = funofFacetToFunofInt facetCenters