packages feed

tinytools-0.1.0.5: src/Potato/Flow/BroadPhase.hs

{-# LANGUAGE RecordWildCards #-}

module Potato.Flow.BroadPhase (
  AABB
  , NeedsUpdateSet
  , BPTree(..)
  , bPTreeFromOwlPFState
  , emptyBPTree
  , broadPhase_cull
  , broadPhase_cull_includeZero

  , BroadPhaseState(..)
  , emptyBroadPhaseState

  -- exposed for testing
  , update_bPTree
) where

import           Relude

import           Potato.Flow.Math
import           Potato.Flow.Methods.Types
import           Potato.Flow.Owl
import           Potato.Flow.OwlItem
import           Potato.Flow.OwlState
import           Potato.Flow.Methods.SEltMethods
import           Potato.Flow.Serialization.Snake
import           Potato.Flow.Types

import qualified Data.IntMap.Strict        as IM

type AABB = LBox

type NeedsUpdateSet = [AABB]

-- TODO actual BroadPhase...
data BPTree = BPTree {
  -- TODO you want something sortable too...
  _bPTree_potato_tree :: REltIdMap AABB
} deriving (Show, Eq)

emptyBPTree :: BPTree
emptyBPTree = BPTree IM.empty

-- TODO
--bPTreeFromPFState :: PFState -> BPTree
--bPTreeFromPFState PFState {..} = r where
--  potato_tree = IM.mapMaybe (getSEltBox_naive . _sEltLabel_sElt) _pFState_directory
--  r = BPTree potato_tree

bPTreeFromOwlPFState :: OwlPFState -> BPTree
bPTreeFromOwlPFState OwlPFState {..} = r where
  potato_tree = IM.mapMaybe (\(_,oelt) -> getSEltBox_naive . owlItem_toSElt_hack $ oelt) (_owlTree_mapping _owlPFState_owlTree)
  r = BPTree potato_tree

data BroadPhaseState = BroadPhaseState {
  _broadPhaseState_bPTree    :: BPTree -- updated BPTree
} deriving (Show, Eq)

emptyBroadPhaseState :: BroadPhaseState
emptyBroadPhaseState = BroadPhaseState emptyBPTree




-- | updates a BPTree and returns list of AABBs that were affected
-- exposed for testing only, do not call this directly
update_bPTree :: (HasOwlTree a) => a -> SuperOwlChanges -> BPTree -> (NeedsUpdateSet, BroadPhaseState)
update_bPTree ot changes BPTree {..} = r where
  -- deletions
  deletefn (aabbs, im) rid = (newaabbs, newim) where
    (moldaabb, newim) = IM.updateLookupWithKey (\_ _ -> Nothing) rid im
    newaabbs = maybe aabbs (\oldaabb -> oldaabb:aabbs) moldaabb

  -- modify/insert
  insmodfn (aabbs, im) (rid, lbox) = (newaabbs, newim) where
    (moldaabb :: Maybe AABB, newim) = IM.insertLookupWithKey (\_ a _ -> a) rid lbox im
    newaabbs' = lbox:aabbs
    newaabbs = maybe newaabbs' (\oldaabb -> oldaabb:newaabbs') moldaabb

  (insmods, deletes) = foldl'
    (\(insmods',deletes') (rid, msowl) -> case msowl of
      Nothing    -> (insmods', rid:deletes')

      -- TODO don't use getSEltBox_naive like this come on -__-
      Just sowl -> case getSEltBox_naive (superOwl_toSElt_hack sowl) of
        Nothing   -> (insmods', rid:deletes')
        
        -- TODO this needs to use expanded wide char box
        Just _ -> ((rid, _sEltDrawer_box (getDrawer (_owlItem_subItem $ _superOwl_elt sowl)) ot):insmods', deletes'))


    ([],[])
    (IM.toList changes)
  (aabbs', nbpt) = foldl' insmodfn (foldl' deletefn ([], _bPTree_potato_tree) deletes) insmods
  r = (aabbs', BroadPhaseState (BPTree nbpt))

-- TODO prob don't need this, DELETE
--update_bPTree' ::  (REltId, Maybe SEltLabel) -> BPTree -> BPTree
--update_bPTree' (rid, ms) BPTree {..} = BPTree $ IM.alter (const (ms >>= getSEltBox_naive . _sEltLabel_sElt)) rid _bPTree_potato_tree

-- | returns list of REltIds that intersect with given AABB
broadPhase_cull :: AABB -> BPTree -> [REltId]
broadPhase_cull box BPTree {..} = r where
  foldfn rid aabb cols = if does_lBox_intersect box aabb then rid:cols else cols
  r = IM.foldrWithKey foldfn [] _bPTree_potato_tree

-- | same as above but also returns zero area elements for selecting
broadPhase_cull_includeZero :: AABB -> BPTree -> [REltId]
broadPhase_cull_includeZero box BPTree {..} = r where
  foldfn rid aabb cols = if does_lBox_intersect_include_zero_area box aabb then rid:cols else cols
  r = IM.foldrWithKey foldfn [] _bPTree_potato_tree