-- This is a module for constructing bounding volume hierarchies using a kdtree

module KDTree(generateSceneGraphUsingKDTree, makeSplittingPlane, degenerateSplitList, findSplittingPlane) where

import PolymorphicNum
import Vector
import Primitive
import Data.List
import BoundingBox

-- This stuff is object specific 

-- What side of a plane is an object on?
onPositiveSide :: (Vector, Double) -> Object -> Bool
onPositiveSide (planeNormal, planeDist) obj = planeDist + (planeNormal `dot3` objBoxCentre) > 0.01
    where
      Just (boxMin, boxMax) = primitiveBoundingBox (primitive obj) obj
      objBoxCentre = (boxMin <+> boxMax) <*> (0.5 :: Double)

-- This stuff is generic

-- Generate a plane to split the objects along
makeSplittingPlane :: AABB -> Int -> (Vector, Double)
makeSplittingPlane (boxMin, boxMax) buildCycle = case nthLargestAxis (boxMax <-> boxMin) buildCycle of
                                                   0 -> (xaxis, -(vecX midPoint))
                                                   1 -> (yaxis, -(vecY midPoint))
                                                   2 -> (zaxis, -(vecZ midPoint))
                                                   _ -> error "Undefined value"
    where
      midPoint = (boxMin <+> boxMax) <*> (0.5 :: Double)

-- Find a working splitting plane
findSplittingPlane :: AABB -> Int -> [t] -> ((Vector, Double) -> t -> Bool) -> Maybe (Vector, Double) 
findSplittingPlane box buildCycle objs partitionFunc
    | buildCycle > 2 = Nothing
    | otherwise = if length leftObjects > 0 && length rightObjects > 0 
                  then Just candidateSplittingPlane
                  else findSplittingPlane box (buildCycle + 1) objs partitionFunc
                      where
                        candidateSplittingPlane = makeSplittingPlane box buildCycle
                        (leftObjects, rightObjects) = partition (partitionFunc candidateSplittingPlane) objs

-- We use this dysfunctional strategy where all our smarter ideas run out
degenerateSplitList :: (Eq t) => [t] -> ([t], [t])
degenerateSplitList objs = ([x | x <- objs, case x `elemIndex` objs of
                                              Just index -> odd index
                                              Nothing -> False], 
                            [x | x <- objs, case x `elemIndex` objs of
                                              Just index -> even index
                                              Nothing -> False])

-- Make children using a kd tree
generateSceneGraphUsingKDTree :: [Object] -> [[Object]]
generateSceneGraphUsingKDTree objs = [leftObjects, rightObjects]
    where
      objBox = objectListBoundingBox objs
      (leftObjects, rightObjects) = case findSplittingPlane objBox 0 objs onPositiveSide of
                                      Nothing -> degenerateSplitList objs
                                      Just splittingPlane -> partition (onPositiveSide splittingPlane) objs
