packages feed

LambdaHack-0.1.20080413: FOV.hs

module FOV where

import Data.Map as M
import Data.Set as S
import Data.List as L
import Data.Ratio
import Debug.Trace

import Geometry
import Level

type Interval = (Rational, Rational)
type Distance = Int
type Progress = Int

-- The current state of a scan is kept in a variable of Maybe Rational.
-- If Just something, we're in a visible interval. If Nothing, we're in
-- a shadowed interval.
scan :: ((Distance,Progress) -> Loc) -> LMap -> Distance -> Interval -> Set Loc
scan tr l d (s,e) = 
    let ps = downBias (s * fromIntegral d)   -- minimal progress to check
        pe = upBias (e * fromIntegral d)     -- maximal progress to check
        st = if open (l `at` tr (d,ps)) then (Just s) -- start in light
                                        else Nothing  -- start in shadow
    in 
        -- trace (show (d,s,e,ps,pe)) $
        S.union (S.fromList [tr (d,p) | p <- [ps..pe]]) (scan' st ps pe)
  where
    scan' :: Maybe Rational -> Progress -> Progress -> Set Loc
    -- scan' st ps pe
    --   | trace (show (st,ps,pe)) False = undefined
    scan' (Just s) ps pe
      | s  >= e  = S.empty               -- empty interval
      | ps > pe  = scan tr l (d+1) (s,e) -- reached end, scan next
      | closed (l `at` tr (d,ps)) =
                   let ne = (fromIntegral ps - (1%2)) / (fromIntegral d + (1%2))
                   in  scan tr l (d+1) (s,ne) `S.union` scan' Nothing (ps+1) pe
                                      -- entering shadow
      | otherwise = scan' (Just s) (ps+1) pe
                                      -- continue in light
    scan' Nothing ps pe
      | ps > pe  = S.empty            -- reached end while in shadow
      | open (l `at` tr (d,ps)) = 
                   let ns = (fromIntegral ps - (1%2)) / (fromIntegral d - (1%2))
                   in  scan' (Just ns) (ps+1) pe
                                      -- moving out of shadow
      | otherwise = scan' Nothing (ps+1) pe
                                      -- continue in shadow

tr0 (oy,ox) (d,p) = (oy+d,ox+p)
tr1 (oy,ox) (d,p) = (oy+d,ox-p)
tr2 (oy,ox) (d,p) = (oy-d,ox+p)
tr3 (oy,ox) (d,p) = (oy-d,ox-p)
tr4 (oy,ox) (d,p) = (oy+p,ox+d)
tr5 (oy,ox) (d,p) = (oy+p,ox-d)
tr6 (oy,ox) (d,p) = (oy-p,ox+d)
tr7 (oy,ox) (d,p) = (oy-p,ox-d)

fullscan loc lvl = 
  S.unions $
  L.map (\ tr -> scan (tr loc) lvl 0 (0,1)) [tr0,tr1,tr2,tr3,tr4,tr5,tr6,tr7]


downBias, upBias :: (Integral a, Integral b) => Ratio a -> b
downBias x = round (x - 1 % (denominator x * 3))
upBias   x = round (x + 1 % (denominator x * 3))