apecs-0.10.0: src/Apecs/Experimental/Util.hs
{-|
Stability : experimental
This module is experimental, and its API might change between point releases. Use at your own risk.
-
-}
module Apecs.Experimental.Util
( -- * Spatial hashing
-- $hash
quantize
, flatten
, inbounds
, region
, flatten'
) where
{- $hash
The following are helper functions for spatial hashing.
Your spatial hash is defined by two vectors;
- The cell size vector contains real components and dictates
how large each cell in your table is in world space units.
It is used by @quantize@ to translate a world space coordinate into a table space index vector
- The table size vector contains integral components and dictates how
many cells your field consists of in each direction.
It is used by @flatten@ to translate a table-space index vector into a flat integer
-}
{- | Quantize turns a world-space coordinate into a table-space coordinate by dividing
by the given cell size and rounding towards negative infinity.
-}
{-# INLINE quantize #-}
quantize
:: (Fractional (v a), Integral b, RealFrac a, Functor v)
=> v a
-- ^ Quantization cell size
-> v a
-- ^ Vector to be quantized
-> v b
quantize cell vec = floor <$> vec / cell
{- | Turns a table-space vector into an integral index, given some table size vector.
Yields Nothing for out-of-bounds queries
-}
{-# INLINE flatten #-}
flatten
:: (Applicative v, Integral a, Foldable v)
=> v a -- Field size vector
-> v a
-> Maybe a
flatten size vec = if inbounds size vec then Just (flatten' size vec) else Nothing
-- | Tests whether a vector is in the region given by 0 and the size vector (inclusive)
{-# INLINE inbounds #-}
inbounds
:: (Num a, Ord a, Applicative v, Foldable v)
=> v a -- Field size vector
-> v a
-> Bool
inbounds size vec = and ((\v s -> v >= 0 && v <= s) <$> vec <*> size)
{- | For two table-space vectors indicating a region's bounds, gives a list of the vectors contained between them.
This is useful for querying a spatial hash.
-}
{-# INLINE region #-}
region
:: (Enum a, Applicative v, Traversable v)
=> v a
-- ^ Lower bound for the region
-> v a
-- ^ Higher bound for the region
-> [v a]
region a b = sequence $ enumFromTo <$> a <*> b
-- | flatten, but yields garbage for out-of-bounds vectors.
{-# INLINE flatten' #-}
flatten'
:: (Applicative v, Integral a, Foldable v)
=> v a -- Field size vector
-> v a
-> a
flatten' size vec = foldr (\(n, x) acc -> n * acc + x) 0 $ (,) <$> size <*> vec