packages feed

Allure-0.4.2: src/FOV/Common.hs

module FOV.Common
  ( Distance, Progress
  , Bump(..)
  , Line, ConvexHull, Edge, EdgeInterval
  , isClear, divUp, maximal, steeper, addHull
  ) where

import qualified Data.List as L

import Geometry
import Loc
import qualified Tile
import Level

type Distance = Int
type Progress = Int

-- | Rotated and translated coordinates of 2D points, so that the points fit
-- in a single quadrant area (quadrant I for Permissive FOV, hence both
-- coordinates positive, and adjacent diagonal halves of quadrant I and II
-- for Digital FOV, hence y positive).
-- The coordinates are written using the standard mathematical coordinate setup,
-- where quadrant I, with x and y positive, is on the upper right.
newtype Bump = B (X, Y)
  deriving (Show)

type Line         = (Bump, Bump)
type ConvexHull   = [Bump]
type Edge         = (Line, ConvexHull)
type EdgeInterval = (Edge, Edge)

isClear :: Level -> (Bump -> Loc) -> Bump -> Bool
isClear l tr = Tile.isClear . (l `at`) . tr

-- | Integer division, rounding up.
divUp :: Int -> Int -> Int
divUp n k = (n + k - 1) `div` k

-- | Maximal element of a non-empty list. Prefers elements from the rear,
-- which is essential for PFOV, to avoid ill-defined lines.
maximal :: (a -> a -> Bool) -> [a] -> a
maximal gte = L.foldl1' (\ acc e -> if gte e acc then e else acc)

-- | Check if the line from the second point to the first is more steep
-- than the line from the third point to the first. This is related
-- to the formal notion of gradient (or angle), but hacked wrt signs
-- to work in this particular setup. Returns True for ill-defined lines.
steeper :: Bump ->  Bump -> Bump -> Bool
steeper (B(xf, yf)) (B(x1, y1)) (B(x2, y2)) =
  (yf - y1)*(xf - x2) >= (yf - y2)*(xf - x1)

-- | Adds a bump to the convex hull of bumps represented as a list.
addHull :: (Bump -> Bump -> Bool) -> Bump -> ConvexHull -> ConvexHull
addHull gte d l =
  case l of
    a:b:cs ->
      if gte a b
      then addHull gte d (b:cs)
      else d : l
    _ -> d : l