packages feed

delaunayNd-0.1.0.2: src/Geometry/Delaunay/Delaunay.hs

module Geometry.Delaunay.Delaunay
  ( delaunay
  , vertexNeighborFacets
  , sandwichedFacet
  , facetOf
  , facetFamilies
  , facetCenters
  , facetOf'
  , facetFamilies'
  , facetCenters'
  , getDelaunayTiles
  ) 
  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                  ( fromMaybe )
import           Geometry.Delaunay.CDelaunay ( c_tessellation
                                             , cTessellationToTessellation 
                                             )
import           Geometry.Delaunay.Types     ( Tessellation(_tilefacets, _sites, _tiles)
                                             , Tile (..)
                                             , Simplex(_vertices')
                                             , TileFacet(_facetOf)
                                             , Site(_neighfacetsIds) 
                                             )
import           Foreign.C.Types             ( CDouble, CUInt )
import           Foreign.Marshal.Alloc       ( free, mallocBytes )
import           Foreign.Marshal.Array       ( pokeArray )
import           Foreign.Storable            ( peek, sizeOf )
import           Geometry.Qhull.Types        ( HasCenter(_center) 
                                             , HasFamily(_family)
                                             , Family
                                             , Index 
                                             )

delaunay :: [[Double]]      -- ^ sites (vertex coordinates)
         -> Bool            -- ^ whether to add a point at infinity
         -> Bool            -- ^ whether to include degenerate tiles
         -> Maybe Double    -- ^ volume threshold
         -> IO Tessellation -- ^ Delaunay tessellation
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_tessellation 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 <- cTessellationToTessellation 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 :: Tessellation -> 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 :: Tessellation -> TileFacet -> IntMap Tile
facetOf tess tilefacet = IM.restrictKeys (_tiles tess) (_facetOf tilefacet)

-- | the families of the tiles a facet belongs to
facetFamilies :: Tessellation -> TileFacet -> IntMap Family
facetFamilies tess tilefacet = IM.map _family (facetOf tess tilefacet)

-- | the circumcenters of the tiles a facet belongs to
facetCenters :: Tessellation -> TileFacet -> IntMap [Double]
facetCenters tess tilefacet =
  IM.map _center (facetOf tess tilefacet)

funofFacetToFunofInt :: (Tessellation -> TileFacet -> IntMap a)
                     -> (Tessellation -> 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' :: Tessellation -> Int -> IntMap Tile
facetOf' = funofFacetToFunofInt facetOf

-- | the families of the tiles a facet belongs to, facet given by its id
facetFamilies' :: Tessellation -> Int -> IntMap Family
facetFamilies' = funofFacetToFunofInt facetFamilies

-- | the circumcenters of the tiles a facet belongs to, facet given by its id
facetCenters' :: Tessellation -> Int -> IntMap [Double]
facetCenters' = funofFacetToFunofInt facetCenters

-- | list of the maps of vertices for all tiles
getDelaunayTiles :: Tessellation -> [IntMap [Double]]
getDelaunayTiles tess = IM.elems $ IM.map (_vertices' . _simplex) (_tiles tess)