packages feed

brillo-algorithms-1.13.3: Brillo/Algorithms/RayCast.hs

{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RankNTypes #-}

-- | Various ray casting algorithms.
module Brillo.Algorithms.RayCast (
  castSegIntoCellularQuadTree,
  traceSegIntoCellularQuadTree,
)
where

import Brillo.Data.Extent
import Brillo.Data.Picture
import Brillo.Data.Quad
import Brillo.Data.QuadTree
import Data.Function
import Data.List


{-| The quadtree contains cells of unit extent (NetHack style).
  Given a line segement (P1-P2) through the tree, get the cell
  closest to P1 that intersects the segment, if any.
-}

---
--   TODO: This currently uses a naive algorithm. It just calls
--         `traceSegIntoCellularQuadTree` and sorts the results
--         to get the one closest to P1. It'd be better to do a
--         proper walk over the tree in the direction of the ray.
--
castSegIntoCellularQuadTree
  :: forall a
   . Point
  -- ^ (P1) Starting point of seg.
  -> Point
  -- ^ (P2) Final point of seg.
  -> Extent
  -- ^ Extent convering the whole tree.
  -> QuadTree a
  -- ^ The tree.
  -> Maybe (Point, Extent, a)
  -- ^ Intersection point, extent of cell, value of cell (if any).
castSegIntoCellularQuadTree p1 p2 extent tree
  | cells@(_ : _) <- traceSegIntoCellularQuadTree p1 p2 extent tree
  , c : _ <- sortBy ((compareDistanceTo p1) `on` (\(a, _, _) -> a)) cells =
      Just c
  | otherwise =
      Nothing


compareDistanceTo :: Point -> Point -> Point -> Ordering
compareDistanceTo p0 p1 p2 =
  let d1 = distance p0 p1
      d2 = distance p0 p2
  in  compare d1 d2


distance :: Point -> Point -> Float
distance (x1, y1) (x2, y2) =
  let xd = x2 - x1
      yd = y2 - y1
  in  sqrt (xd * xd + yd * yd)


{-| The quadtree contains cells of unit extent (NetHack style).
  Given a line segment (P1-P2) through the tree, return the list of cells
  that intersect the segment.
-}
traceSegIntoCellularQuadTree
  :: forall a
   . Point
  -- ^ (P1) Starting point of seg.
  -> Point
  -- ^ (P2) Final point of seg.
  -> Extent
  -- ^ Extent covering the whole tree.
  -> QuadTree a
  -- ^ The tree.
  -> [(Point, Extent, a)]
  -- ^ Intersection point, extent of cell, value of cell.
traceSegIntoCellularQuadTree p1 p2 extent tree =
  case tree of
    TNil -> []
    TLeaf a ->
      case intersectSegExtent p1 p2 extent of
        Just pos -> [(pos, extent, a)]
        Nothing -> []
    TNode nw ne sw se
      | touchesSegExtent p1 p2 extent ->
          concat
            [ traceSegIntoCellularQuadTree p1 p2 (cutQuadOfExtent NW extent) nw
            , traceSegIntoCellularQuadTree p1 p2 (cutQuadOfExtent NE extent) ne
            , traceSegIntoCellularQuadTree p1 p2 (cutQuadOfExtent SW extent) sw
            , traceSegIntoCellularQuadTree p1 p2 (cutQuadOfExtent SE extent) se
            ]
    _ -> []