{-# LANGUAGE TypeFamilies #-}

{-# OPTIONS_GHC -fno-warn-orphans #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.TwoD.Types
-- Copyright   :  (c) 2011 diagrams-lib team (see LICENSE)
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  diagrams-discuss@googlegroups.com
--
-- Basic types for two-dimensional Euclidean space.
--
-----------------------------------------------------------------------------

module Diagrams.TwoD.Types
       ( -- * 2D Euclidean space
         V2 (..), R1 (..), R2 (..)
       , P2, T2
       , r2, unr2, mkR2, r2Iso
       , p2, mkP2, unp2, p2Iso
       , r2PolarIso
       , HasR (..)
       ) where

import           Control.Lens            (Iso', Lens', iso, _1, _2)

import           Diagrams.Angle
import           Diagrams.Points

import           Diagrams.Core.Transform
import           Diagrams.Core.V
import           Linear.Metric
import           Linear.V2

type P2 = Point V2
type T2 = Transformation V2

type instance V (V2 n) = V2
type instance N (V2 n) = n

-- | Construct a 2D vector from a pair of components.  See also '&'.
r2 :: (n, n) -> V2 n
r2 = uncurry V2

-- | Convert a 2D vector back into a pair of components.  See also 'coords'.
unr2 :: V2 n -> (n, n)
unr2 (V2 x y) = (x, y)

-- | Curried form of `r2`.
mkR2 :: n -> n -> V2 n
mkR2 = V2

r2Iso :: Iso' (V2 n) (n, n)
r2Iso = iso unr2 r2

-- | Construct a 2D point from a pair of coordinates.  See also '^&'.
p2 :: (n, n) -> P2 n
p2 = P . uncurry V2

-- | Convert a 2D point back into a pair of coordinates.  See also 'coords'.
unp2 :: P2 n -> (n,n)
unp2 (P (V2 x y)) = (x,y)

-- | Curried form of `p2`.
mkP2 :: n -> n -> P2 n
mkP2 x y = P (V2 x y)

p2Iso :: Iso' (Point V2 n) (n, n)
p2Iso = iso unp2 p2

instance Transformable (V2 n) where
  transform = apply

r2PolarIso :: RealFloat n => Iso' (V2 n) (n, Angle n)
r2PolarIso = iso (\v@(V2 x y) -> (norm v, atan2A y x))
              (\(r,θ)      -> V2 (r * cosA θ) (r * sinA θ))
{-# INLINE r2PolarIso #-}

-- | A space which has magnitude '_r' that can be calculated numerically.
class HasR t where
  _r :: RealFloat n => Lens' (t n) n

instance HasR v => HasR (Point v) where
  _r = lensP . _r
  {-# INLINE _r #-}

instance HasR V2 where
  _r = r2PolarIso . _1
  {-# INLINE _r #-}

instance HasTheta V2 where
  _theta = r2PolarIso . _2
  {-# INLINE _theta #-}