{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Graphics.Gloss.Accelerate.Data.Point ( -- ** Point data type Point, -- ** Point creation makePoint, xyOfPoint, pointOfIndex, -- ** Testing points pointInBox, ) where import Prelude as P import Data.Typeable import Data.Array.Accelerate as A import Data.Array.Accelerate.Smart import Data.Array.Accelerate.Tuple ( Tuple(..), TupleIdx(..), IsTuple(..), ) import Data.Array.Accelerate.Array.Sugar ( Elt(..), EltRepr, EltRepr' ) -- | An abstract point value on the xy-plane. -- type Point = XY Float -- | A parameterised point in the xy-plane. This is so that the type can be both -- Exp (Point' a) and Point' (Exp a). -- data XY a = XY a a deriving (Show, Eq, Typeable) -- | Pretend a point is a number. -- -- Vectors aren't real numbers according to Haskell, because they don't support -- the multiply and divide field operators. We can pretend they are though, and -- use the (+) and (-) operators as component-wise addition and subtraction. -- instance Num a => Num (XY a) where (+) (XY x1 y1) (XY x2 y2) = XY (x1 + x2) (y1 + y2) (-) (XY x1 y1) (XY x2 y2) = XY (x1 - x2) (y1 - y2) (*) (XY x1 y1) (XY x2 y2) = XY (x1 * x2) (y1 * y2) signum (XY x y) = XY (signum x) (signum y) abs (XY x y) = XY (abs x) (abs y) negate (XY x y) = XY (negate x) (negate y) fromInteger i = let f = fromInteger i in XY f f -- Represent points in Accelerate as a tuple -- type instance EltRepr (XY a) = EltRepr (a, a) type instance EltRepr' (XY a) = EltRepr (a, a) instance Elt a => Elt (XY a) where eltType (_ :: XY a) = eltType (undefined :: (a,a)) toElt p = let (x,y) = toElt p in XY x y fromElt (XY x y) = fromElt (x,y) eltType' (_ :: XY a) = eltType' (undefined :: (a,a)) toElt' p = let (x,y) = toElt' p in XY x y fromElt' (XY x y) = fromElt' (x,y) instance IsTuple (XY a) where type TupleRepr (XY a) = (((),a), a) fromTuple (XY x y) = (((), x), y) toTuple (((),x),y) = XY x y instance (Lift Exp a, Elt (Plain a)) => Lift Exp (XY a) where type Plain (XY a) = XY (Plain a) lift (XY x y) = Exp . Tuple $ NilTup `SnocTup` lift x `SnocTup` lift y instance Elt a => Unlift Exp (XY (Exp a)) where unlift p = let x = Exp $ SuccTupIdx ZeroTupIdx `Prj` p y = Exp $ ZeroTupIdx `Prj` p in XY x y -- | Make a custom point -- makePoint :: Exp Float -- ^ x-coordinate -> Exp Float -- ^ y-coordinate -> Exp Point makePoint x y = lift (XY x y) -- | Take the components of a point -- xyOfPoint :: Exp Point -> (Exp Float, Exp Float) xyOfPoint p = let XY x y = unlift p in (x, y) -- | Convert a two-dimensional index into a point centered in a plane of the -- given width and height. -- pointOfIndex :: Int -- ^ width -> Int -- ^ height -> Exp DIM2 -> Exp Point pointOfIndex sizeX sizeY ix = let -- Size of the raw plane fsizeX, fsizeY :: Float fsizeX = P.fromIntegral sizeX fsizeY = P.fromIntegral sizeY fsizeX2, fsizeY2 :: Exp Float fsizeX2 = constant $ fsizeX / 2 fsizeY2 = constant $ fsizeY / 2 -- Midpoint of plane midX, midY :: Exp Int midX = constant $ sizeX `div` 2 midY = constant $ sizeY `div` 2 -- Centre coordinate in the plane Z :. y :. x = unlift ix x' = A.fromIntegral (x - midX) / fsizeX2 y' = A.fromIntegral (y - midY) / fsizeY2 in makePoint x' y' -- | Test whether a point lies within a rectangular box that is oriented -- on the x-y plane. The points P1-P2 are opposing points of the box, -- but need not be in a particular order. -- -- @ -- P2 +-------+ -- | | -- | + P0 | -- | | -- +-------+ P1 -- @ -- pointInBox :: Exp Point -- ^ point to test -> Exp Point -- ^ corner of box -> Exp Point -- ^ opposite corner of box -> Exp Bool pointInBox p0 p1 p2 = let XY x0 y0 = unlift p0 XY x1 y1 = unlift p1 XY x2 y2 = unlift p2 in x0 >=* min x1 x2 &&* x0 <=* max x1 x2 &&* y0 >=* min y1 y2 &&* y0 <=* max y1 y2