module Data.Geometry.Point.Class where

import           Control.Lens
import           Data.Geometry.Point.Internal (Point)
import qualified Data.Geometry.Point.Internal as Internal
import           Data.Geometry.Vector
import           GHC.TypeNats

--------------------------------------------------------------------------------

-- $setup
-- >>> import Data.Geometry.Point.Internal (pattern Point2, pattern Point3, origin)

class ToAPoint point d r where
  toPoint   :: Prism' (point d r) (Point d r)

class AsAPoint p where
  asAPoint :: Lens (p d r) (p d' r') (Point d r) (Point d' r')

-- | Lens to access the vector corresponding to this point.
--
-- >>> (Point3 1 2 3) ^. vector'
-- Vector3 1 2 3
-- >>> origin & vector' .~ Vector3 1 2 3
-- Point3 1 2 3
vector' :: AsAPoint p => Lens (p d r) (p d r') (Vector d r) (Vector d r')
vector' :: Lens (p d r) (p d r') (Vector d r) (Vector d r')
vector' = (Point d r -> f (Point d r')) -> p d r -> f (p d r')
forall (p :: Nat -> * -> *) (d :: Nat) r (d' :: Nat) r'.
AsAPoint p =>
Lens (p d r) (p d' r') (Point d r) (Point d' r')
asAPoint ((Point d r -> f (Point d r')) -> p d r -> f (p d r'))
-> ((Vector d r -> f (Vector d r')) -> Point d r -> f (Point d r'))
-> (Vector d r -> f (Vector d r'))
-> p d r
-> f (p d r')
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Point d r -> Vector d r)
-> (Point d r -> Vector d r' -> Point d r')
-> Lens (Point d r) (Point d r') (Vector d r) (Vector d r')
forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens Point d r -> Vector d r
forall (d :: Nat) r. Point d r -> Vector d r
Internal.toVec ((Vector d r' -> Point d r')
-> Point d r -> Vector d r' -> Point d r'
forall a b. a -> b -> a
const Vector d r' -> Point d r'
forall (d :: Nat) r. Vector d r -> Point d r
Internal.Point)

-- | Get the coordinate in a given dimension
--
-- >>> Point3 1 2 3 ^. coord (C :: C 2)
-- 2
-- >>> Point3 1 2 3 & coord (C :: C 1) .~ 10
-- Point3 10 2 3
-- >>> Point3 1 2 3 & coord (C :: C 3) %~ (+1)
-- Point3 1 2 4
coord   :: (1 <= i, i <= d, KnownNat i, Arity d, AsAPoint p) => proxy i -> Lens' (p d r) r
coord :: proxy i -> Lens' (p d r) r
coord proxy i
i = (Point d r -> f (Point d r)) -> p d r -> f (p d r)
forall (p :: Nat -> * -> *) (d :: Nat) r (d' :: Nat) r'.
AsAPoint p =>
Lens (p d r) (p d' r') (Point d r) (Point d' r')
asAPoint((Point d r -> f (Point d r)) -> p d r -> f (p d r))
-> ((r -> f r) -> Point d r -> f (Point d r))
-> (r -> f r)
-> p d r
-> f (p d r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.proxy i -> Lens' (Point d r) r
forall (proxy :: Nat -> *) (i :: Nat) (d :: Nat) r.
(1 <= i, i <= d, Arity d, KnownNat i) =>
proxy i -> Lens' (Point d r) r
Internal.coord proxy i
i

-- | Get the coordinate in a given dimension. This operation is unsafe in the
-- sense that no bounds are checked. Consider using `coord` instead.
--
--
-- >>> Point3 1 2 3 ^. unsafeCoord 2
-- 2
unsafeCoord   :: (Arity d, AsAPoint p) => Int -> Lens' (p d r) r
unsafeCoord :: Int -> Lens' (p d r) r
unsafeCoord Int
i = (Point d r -> f (Point d r)) -> p d r -> f (p d r)
forall (p :: Nat -> * -> *) (d :: Nat) r (d' :: Nat) r'.
AsAPoint p =>
Lens (p d r) (p d' r') (Point d r) (Point d' r')
asAPoint((Point d r -> f (Point d r)) -> p d r -> f (p d r))
-> ((r -> f r) -> Point d r -> f (Point d r))
-> (r -> f r)
-> p d r
-> f (p d r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Int -> Lens' (Point d r) r
forall (d :: Nat) r. Arity d => Int -> Lens' (Point d r) r
Internal.unsafeCoord Int
i

instance ToAPoint Point d r where
  toPoint :: p (Point d r) (f (Point d r)) -> p (Point d r) (f (Point d r))
toPoint = (Point d r -> Point d r)
-> (Point d r -> Maybe (Point d r))
-> Prism' (Point d r) (Point d r)
forall b s a. (b -> s) -> (s -> Maybe a) -> Prism s s a b
prism' Point d r -> Point d r
forall a. a -> a
id Point d r -> Maybe (Point d r)
forall a. a -> Maybe a
Just

instance AsAPoint Point where
  asAPoint :: (Point d r -> f (Point d' r')) -> Point d r -> f (Point d' r')
asAPoint = (Point d r -> f (Point d' r')) -> Point d r -> f (Point d' r')
forall a. a -> a
id


-- | Shorthand to access the first coordinate C 1
--
-- >>> Point3 1 2 3 ^. xCoord
-- 1
-- >>> Point2 1 2 & xCoord .~ 10
-- Point2 10 2
xCoord :: (1 <= d, Arity d, AsAPoint point) => Lens' (point d r) r
xCoord :: Lens' (point d r) r
xCoord = C 1 -> Lens' (point d r) r
forall (i :: Nat) (d :: Nat) (p :: Nat -> * -> *)
       (proxy :: Nat -> *) r.
(1 <= i, i <= d, KnownNat i, Arity d, AsAPoint p) =>
proxy i -> Lens' (p d r) r
coord (C 1
forall (n :: Nat). C n
C :: C 1)
{-# INLINABLE xCoord #-}

-- | Shorthand to access the second coordinate C 2
--
-- >>> Point2 1 2 ^. yCoord
-- 2
-- >>> Point3 1 2 3 & yCoord %~ (+1)
-- Point3 1 3 3
yCoord :: (2 <= d, Arity d, AsAPoint point) => Lens' (point d r) r
yCoord :: Lens' (point d r) r
yCoord = C 2 -> Lens' (point d r) r
forall (i :: Nat) (d :: Nat) (p :: Nat -> * -> *)
       (proxy :: Nat -> *) r.
(1 <= i, i <= d, KnownNat i, Arity d, AsAPoint p) =>
proxy i -> Lens' (p d r) r
coord (C 2
forall (n :: Nat). C n
C :: C 2)
{-# INLINABLE yCoord #-}

-- | Shorthand to access the third coordinate C 3
--
-- >>> Point3 1 2 3 ^. zCoord
-- 3
-- >>> Point3 1 2 3 & zCoord %~ (+1)
-- Point3 1 2 4
zCoord :: (3 <= d, Arity d, AsAPoint point) => Lens' (point d r) r
zCoord :: Lens' (point d r) r
zCoord = C 3 -> Lens' (point d r) r
forall (i :: Nat) (d :: Nat) (p :: Nat -> * -> *)
       (proxy :: Nat -> *) r.
(1 <= i, i <= d, KnownNat i, Arity d, AsAPoint p) =>
proxy i -> Lens' (p d r) r
coord (C 3
forall (n :: Nat). C n
C :: C 3)
{-# INLINABLE zCoord #-}