packages feed

scenegraph-0.2.0.0: src/Graphics/SceneGraph/BoundingBox.hs

module Graphics.SceneGraph.BoundingBox where

import           Control.Applicative       (liftA2)
import           Control.Lens              ((^.))
import           Data.Graph.Inductive      (Node)
import qualified Data.Graph.Inductive      as G
import           Data.Maybe                (fromMaybe)
import           Graphics.SceneGraph.Types
import           Linear                    (R3 (..), V3 (..), (!*))
import qualified Linear                    as L


-- | A box. Used for calculating bounds
type Box a = (V3 a, V3 a)

-- | Return the diagonal vector across the box corners.
boxSize :: Box Float -> V3 Float
boxSize (a, b) = b - a

-- | Bounds suitable for starting off with
smallBox :: Box Float
smallBox = (V3 (-0.1) (-0.1) (-0.1), V3 0.1 0.1 0.1)

-- | Create union of two boxes
union :: Box Float -> Box Float -> Box Float
union (v1,v2) (w1,w2) = (liftA2 min v1 w1, liftA2 max v2 w2)

bounds :: Scene g -> Box Float
bounds (Scene gr nde) =
  let sn = llab gr nde in
  boundsSceneNode gr sn

-- | Determine bounds of a @SceneNode@
boundsSceneNode :: SceneGraph g -> SceneNode g -> Box Float
boundsSceneNode gr (SceneNode nde _ (MatrixTransform mt)) =
  let (v1, v2) = boundsOfChildren gr nde in
  ((mt !* L.point v1) ^. _xyz, (mt !* L.point v2) ^. _xyz)

boundsSceneNode gr (SceneNode nde _ (Switch i)) =
  let nde' = G.suc gr nde !! i in
  bounds (Scene gr nde')

boundsSceneNode _ (SceneNode _ _ (Geode _ _)) = smallBox

boundsSceneNode gr (SceneNode nde _ _) = boundsOfChildren gr nde

boundsOfChildren :: SceneGraph g -> Node -> Box Float
boundsOfChildren gr =
  fromMaybe smallBox . foldr f Nothing . G.suc gr
  where
    f nde Nothing  = Just $ bounds (Scene gr nde)
    f nde (Just b) = Just $ b `union` bounds (Scene gr nde)