content: {-# LANGUAGE GADTs #-} -- only for presentation
  -- | Simple library for 2D shapes. Deep embedding.
  module Shape.Deep
    (
    -- * Types
      Shape -- abstract
    -- * Constructor functions
    , empty, disc, square
    -- * Primitive combinators
    , transform, translate
    , union, intersect, invert
    -- * Run functions
    , inside
    ) where
  
  import Matrix
  
  -- | In a deep embedding our objects are represented by a datatype
  --   with constructors for each primitive operation.
  {-
  From the lecture slides:
  
  type Shape
  -- Constructor functions
  empty   :: Shape
  disc    :: Shape
  square  :: Shape
  -- Combinators
  translate :: Vec ->   Shape -> Shape
  scale     :: Vec ->   Shape -> Shape
  rotate    :: Angle -> Shape -> Shape
  union     :: Shape -> Shape -> Shape
  intersect :: Shape -> Shape -> Shape
  difference:: Shape -> Shape -> Shape
  
  Later scale, rotate, difference are derived
  from invert and transform:
  
  invert    :: Shape -> Shape
  transform :: Matrix -> Shape -> Shape
  
  -}
  
  data Shape where
    -- Constructor functions
    Empty   :: Shape
    Disc    :: Shape
    Square  :: Shape
    -- Combinators
    Translate :: Vec ->   Shape -> Shape
    Transform :: Matrix-> Shape -> Shape
    Union     :: Shape -> Shape -> Shape
    Intersect :: Shape -> Shape -> Shape
    Invert    :: Shape -> Shape
  
  {-
  -- Shape without GADT syntax
  data Shape = Empty | Disc | Square
             | Translate Vec Shape
             | Transform Matrix Shape
             | Union Shape Shape
             | Intersect Shape Shape
             | Invert Shape
  -}
  
  -- * Creation functions
  
  empty  :: Shape
  empty  = Empty
  
  disc   :: Shape
  disc   = Disc
  
  square :: Shape
  square = Square
  
  -- * Primitive combinators
  
  transform :: Matrix -> Shape -> Shape
  transform = Transform
  
  translate :: Vec -> Shape -> Shape
  translate = Translate
  
  union :: Shape -> Shape -> Shape
  union = Union
  
  intersect :: Shape -> Shape -> Shape
  intersect = Intersect
  
  invert :: Shape -> Shape
  invert = Invert
  
  -- * Run function
  
  inside :: Point -> Shape -> Bool
  _p `inside` Empty             = False
  p  `inside` Disc              = squaredDistance p <= 1
  p  `inside` Square            = maxNorm  p <= 1
  p  `inside` Translate v sh    = (p `sub` v) `inside` sh
  p  `inside` Transform m sh    = (inv m `mul` p) `inside` sh
  p  `inside` Union sh1 sh2     = p `inside` sh1 || p `inside` sh2
  p  `inside` Intersect sh1 sh2 = p `inside` sh1 && p `inside` sh2
  p  `inside` Invert sh         = not (p `inside` sh)
  
  -- * Helper functions <https://en.wikipedia.org/wiki/Norm_%28mathematics%29#Properties>
  
  -- | Square of the Euclidian norm.
  squaredDistance :: Point -> Double
  squaredDistance p = x*x + y*y -- proper distance would use sqrt
    where
      x = ptX p
      y = ptY p
  
  -- | Manhattan norm (maximum norm).
  maxNorm :: Point -> Double
  maxNorm p = max (abs x) (abs y)
    where
      x = ptX p
      y = ptY p
  