module DSL.Shallow where
import Matrix  ( Vec, vecX, vecY  -- | :: Vec -> Double|
               , Angle            -- | = Double|
               , Point            -- | = Vec|
               , sub, divide      -- | :: Point  -> Vec    -> Point|
               , rot              -- | :: Angle  -> Point  -> Point|
               )   

empty   :: Shape
disc    :: Shape    -- disc with radius |1| around the origin
square  :: Shape    -- square between |(0,0)| and |(1,1)|    

translate   :: Vec    ->  Shape -> Shape  -- shift the shape along a vector                  
scale       :: Vec    ->  Shape -> Shape  -- magnify the shape by a vector                   
rotate      :: Angle  ->  Shape -> Shape  -- rotate the shape by an angle (around the origin)
union       :: Shape  ->  Shape -> Shape
intersect   :: Shape  ->  Shape -> Shape
difference  :: Shape  ->  Shape -> Shape

inside      :: Point ->  Shape -> Bool    -- run function: is the point inside the shape?

-- Shallow = close to the semantics, far from the syntax = almost the type of the run function
newtype Shape = Shape {runShape :: Point -> Bool}

inside = flip runShape

empty         = Shape (\p ->  False)
disc          = Shape (\p ->  norm p <= 1)
square        = Shape (\p ->  ordered [0, vecX p, 1] && ordered [0, vecY p, 1])
translate   v  s  =  Shape (\p -> inside (sub p v)     s)
scale       v  s  =  Shape (\p -> inside (divide p v)  s)
rotate      a  s  =  Shape (\p -> inside (rot (-a) p)  s)
union       x  y  =  Shape (\p -> inside p x  ||  inside p y)
intersect   x  y  =  Shape (\p -> inside p x  &&  inside p y)
difference  x  y  =  Shape (\p -> inside p x  && not (inside p y))

-- Helper functions:

norm :: Vec -> Double
norm p = sqrt ((vecX p)^2 + (vecY p)^2)

ordered :: Ord a => [a] -> Bool
ordered (x:y:ys) = x <= y && ordered (y:ys)
ordered _        = True