{-# LANGUAGE PatternGuards, RankNTypes #-} -- | Various ray casting algorithms. module Graphics.Gloss.Algorithms.RayCast ( castSegIntoCellularQuadTree , traceSegIntoCellularQuadTree) where import Graphics.Gloss.Data.Picture import Graphics.Gloss.Data.Quad import Graphics.Gloss.Data.Extent import Graphics.Gloss.Data.QuadTree import Data.List import Data.Function -- | 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 ] _ -> []