{-# LANGUAGE TypeSynonymInstances, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, RecursiveDo, TypeFamilies, OverloadedStrings, RecordWildCards,UndecidableInstances, PackageImports, TemplateHaskell, RankNTypes #-}

module Graphics.Diagrams.Point where

import Graphics.Diagrams.Core
import Data.Foldable
import Data.List (transpose)
import Prelude hiding (sum,mapM_,mapM,concatMap,maximum,minimum,Num(..),(/))
import Algebra.Classes

infix 4 .=.
----------------
-- Points
-- | A point in 2d space


type Point = Point' Expr

orthonorm :: Point -> Expr
orthonorm (Point x y) = absE x + absE y

{-


-- | Norm of a vector. Don't minimize this: the solver does not like functions
-- with non-continuous derivatives (at zero in this case).
norm :: Point' Expr -> Expr
norm p = sqrtE (sqNorm p)

normalize :: Point' Expr -> Point' Expr
normalize x = (one/norm x) *^ x

-- | Dot product
dotProd :: forall a. (Ring a) => Point' a -> Point' a -> a
dotProd (Point x y) (Point x' y') = x*x' + y*y'

-- | Squared norm of a vector
sqNorm :: forall a. (Ring a) => Point' a -> a
sqNorm p = dotProd p p
-}
-- | Rotate a vector 90 degres in the trigonometric direction.
rotate90 :: forall a. Group a => Point' a -> Point' a
rotate90 (Point x y) = Point (negate y) x

-- | Rotate a vector 180 degres
rotate180 :: forall a. Group a => Point' a -> Point' a
rotate180 = rotate90 . rotate90

xdiff,ydiff :: Point -> Point -> Expr
xdiff p q = xpart (q - p)
ydiff p q = ypart (q - p)

-----------------
-- Point constraints

(.=.),northOf,southOf,westOf,eastOf :: Monad m => Point -> Point -> Diagram lab m ()
Point x1 y1 .=. Point x2 y2 = do
  x1 === x2
  y1 === y2

northOf (Point _ y1) (Point _ y2) = y2 <== y1
southOf = flip northOf
westOf (Point x1 _) (Point x2 _) = x1 <== x2
eastOf = flip westOf

alignHoriz,alignVert :: Monad m => [Point] -> Diagram lab m ()
alignHoriz = align ypart
alignVert = align xpart

align :: Monad m => (a -> Expr) -> [a] -> Diagram lab m ()
align _ [] = return ()
align f (p:ps) = forM_ ps $ \p' -> f p === f p'

alignMatrix :: Monad m => [[Point]] -> Diagram lab m ()
alignMatrix ls = do
  forM_ ls alignHoriz
  forM_ (transpose ls) alignVert