{-# LANGUAGE TypeFamilies #-} {-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Core.BoundingBox -- Copyright : (c) Stephen Tetley 2009 -- License : BSD-style (see LICENSE) -- Maintainer : stephen.tetley@gmail.com -- Stability : experimental -- Portability : GHC only -- -- Bounding box with no notion of \'empty\'. -- -------------------------------------------------------------------------------- module Wumpus.Core.BoundingBox ( -- * Types BoundingBox(..) , DBoundingBox , CardinalPoint(..) -- * Type class , Boundary(..) -- * Operations , bbox , obbox , union , trace , corners , lowerLeftUpperRight , withinBB , boundaryWidth , boundaryHeight , boundaryBottomLeft , boundaryTopRight , boundaryTopLeft , boundaryBottomRight , boundaryPoint , leftPlane , rightPlane , lowerPlane , upperPlane ) where import Wumpus.Core.AffineTrans import Wumpus.Core.Geometry import Wumpus.Core.Utils ( CMinMax(..), within ) import Data.Semigroup import Text.PrettyPrint.Leijen hiding ( width ) -- | Bounding box of a picture. -- -- We cannot construct empty pictures - so bounding boxes too a -- saved the obligation to be empty. -- data BoundingBox a = BBox { ll_corner :: Point2 a, ur_corner :: Point2 a } deriving (Eq,Show) type DBoundingBox = BoundingBox Double data CardinalPoint = C | N | NE | E | SE | S | SW | W | NW deriving (Eq,Show) -------------------------------------------------------------------------------- -- instances -- BBox is NOT monoidal - it\'s much simpler that way. instance Ord a => Semigroup (BoundingBox a) where append = union instance Pretty a => Pretty (BoundingBox a) where pretty (BBox p0 p1) = text "|_" <+> pretty p0 <+> pretty p1 <+> text "_|" -------------------------------------------------------------------------------- -- type instance DUnit (BoundingBox u) = u instance (Num u, Ord u) => Scale (BoundingBox u) where scale x y bb = trace $ map (scale x y) $ corners bb -------------------------------------------------------------------------------- -- Boundary class class Boundary a where boundary :: a -> BoundingBox (DUnit a) -------------------------------------------------------------------------------- instance Pointwise (BoundingBox a) where type Pt (BoundingBox a) = Point2 a pointwise f (BBox bl tr) = BBox (f bl) (f tr) -------------------------------------------------------------------------------- bbox :: Point2 a -> Point2 a -> BoundingBox a bbox = BBox -- | Create a BoundingBox with bottom left corner at the origin, -- and dimensions @w@ and @h@. obbox :: Num a => a -> a -> BoundingBox a obbox w h = BBox zeroPt (P2 w h) union :: Ord a => BoundingBox a -> BoundingBox a -> BoundingBox a BBox ll ur `union` BBox ll' ur' = BBox (cmin ll ll') (cmax ur ur') -- Trace the point list finding the /extremity/... trace :: (Num a, Ord a) => [Point2 a] -> BoundingBox a trace (p:ps) = uncurry BBox $ foldr (\z (a,b) -> (cmin z a, cmax z b) ) (p,p) ps trace [] = error $ "BoundingBox.trace called in empty list" corners :: BoundingBox a -> [Point2 a] corners (BBox bl@(P2 x0 y0) tr@(P2 x1 y1)) = [bl, br, tr, tl] where br = P2 x1 y0 tl = P2 x0 y1 lowerLeftUpperRight :: (a,a,a,a) -> BoundingBox a -> (a,a,a,a) lowerLeftUpperRight _ (BBox (P2 x0 y0) (P2 x1 y1)) = (x0,y0,x1,y1) withinBB :: Ord a => Point2 a -> BoundingBox a -> Bool withinBB p (BBox ll ur) = within p ll ur boundaryWidth :: Num a => BoundingBox a -> a boundaryWidth (BBox (P2 xmin _) (P2 xmax _)) = xmax - xmin boundaryHeight :: Num a => BoundingBox a -> a boundaryHeight (BBox (P2 _ ymin) (P2 _ ymax)) = ymax - ymin -------------------------------------------------------------------------------- -- Points on the boundary boundaryBottomLeft :: BoundingBox a -> Point2 a boundaryBottomLeft (BBox p0 _ ) = p0 boundaryTopRight :: BoundingBox a -> Point2 a boundaryTopRight (BBox _ p1) = p1 boundaryTopLeft :: BoundingBox a -> Point2 a boundaryTopLeft (BBox (P2 x _) (P2 _ y)) = P2 x y boundaryBottomRight :: BoundingBox a -> Point2 a boundaryBottomRight (BBox (P2 _ y) (P2 x _)) = P2 x y boundaryPoint :: Fractional a => CardinalPoint -> BoundingBox a -> Point2 a boundaryPoint loc (BBox (P2 x0 y0) (P2 x1 y1)) = fn loc where fn C = P2 xMid yMid fn N = P2 xMid y1 fn NE = P2 x1 y1 fn E = P2 x1 yMid fn SE = P2 x1 y0 fn S = P2 xMid y0 fn SW = P2 x0 y0 fn W = P2 x0 yMid fn NW = P2 x0 y1 xMid = x0 + 0.5 * (x1 - x0) yMid = y0 + 0.5 * (y1 - y0) -------------------------------------------------------------------------------- -- /planes/ on the bounding box -- Are these really worthwhile ? ... leftPlane :: BoundingBox a -> a leftPlane (BBox (P2 l _) _) = l rightPlane :: BoundingBox a -> a rightPlane (BBox _ (P2 r _)) = r lowerPlane :: BoundingBox a -> a lowerPlane (BBox (P2 _ l) _) = l upperPlane :: BoundingBox a -> a upperPlane (BBox _ (P2 _ u)) = u