{-# LANGUAGE RankNTypes   #-}
{-# LANGUAGE TypeFamilies #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.Core.Points
-- Copyright   :  (c) 2011 diagrams-core team (see LICENSE)
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  diagrams-discuss@googlegroups.com
--
-- A type for /points/ (as distinct from vectors).
--
-----------------------------------------------------------------------------

module Diagrams.Core.Points
       ( -- * Points

         Point(..), origin, (*.), relative, _Point

       , reflectThrough, mirror, relative2, relative3
       ) where

import           Control.Lens    (over)

import           Linear.Affine
import           Linear.Vector

import           Diagrams.Core.V

type instance V (Point v n) = v
type instance N (Point v n) = n

-- | Reflect a point across the origin.
mirror :: (Additive v, Num n) => Point v n -> Point v n
mirror :: forall (v :: * -> *) n.
(Additive v, Num n) =>
Point v n -> Point v n
mirror = forall (v :: * -> *) n.
(Additive v, Num n) =>
Point v n -> Point v n -> Point v n
reflectThrough forall (f :: * -> *) a. (Additive f, Num a) => Point f a
origin

-- | Scale a point by a scalar. Specialized version of '(*^)'.
(*.) :: (Functor v, Num n) => n -> Point v n -> Point v n
*. :: forall (v :: * -> *) n.
(Functor v, Num n) =>
n -> Point v n -> Point v n
(*.) = forall (f :: * -> *) a. (Functor f, Num a) => a -> f a -> f a
(*^)

-- | Apply a transformation relative to the given point.
relative2 :: (Additive v, Num n)
  => Point v n -> (v n -> v n -> v n)
  -> Point v n -> Point v n -> Point v n
relative2 :: forall (v :: * -> *) n.
(Additive v, Num n) =>
Point v n
-> (v n -> v n -> v n) -> Point v n -> Point v n -> Point v n
relative2 Point v n
p v n -> v n -> v n
f Point v n
x Point v n
y = (Point v n
p forall (p :: * -> *) a. (Affine p, Num a) => p a -> Diff p a -> p a
.+^) forall a b. (a -> b) -> a -> b
$ v n -> v n -> v n
f (Point v n -> Diff (Point v) n
inj Point v n
x) (Point v n -> Diff (Point v) n
inj Point v n
y) where inj :: Point v n -> Diff (Point v) n
inj = (forall (p :: * -> *) a. (Affine p, Num a) => p a -> p a -> Diff p a
.-. Point v n
p)

-- | Apply a transformation relative to the given point.
relative3 :: (Additive v, Num n)
  => Point v n -> (v n -> v n -> v n -> v n)
  -> Point v n -> Point v n -> Point v n -> Point v n
relative3 :: forall (v :: * -> *) n.
(Additive v, Num n) =>
Point v n
-> (v n -> v n -> v n -> v n)
-> Point v n
-> Point v n
-> Point v n
-> Point v n
relative3 Point v n
p v n -> v n -> v n -> v n
f Point v n
x Point v n
y Point v n
z = (Point v n
p forall (p :: * -> *) a. (Affine p, Num a) => p a -> Diff p a -> p a
.+^) forall a b. (a -> b) -> a -> b
$ v n -> v n -> v n -> v n
f (Point v n -> Diff (Point v) n
inj Point v n
x) (Point v n -> Diff (Point v) n
inj Point v n
y) (Point v n -> Diff (Point v) n
inj Point v n
z) where inj :: Point v n -> Diff (Point v) n
inj = (forall (p :: * -> *) a. (Affine p, Num a) => p a -> p a -> Diff p a
.-. Point v n
p)

-- | Mirror a point through a given point.
reflectThrough :: (Additive v, Num n) => Point v n -> Point v n -> Point v n
reflectThrough :: forall (v :: * -> *) n.
(Additive v, Num n) =>
Point v n -> Point v n -> Point v n
reflectThrough Point v n
o = forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
over (forall (f :: * -> *) a.
(Additive f, Num a) =>
Point f a -> Iso' (Point f a) (f a)
relative Point v n
o) forall (f :: * -> *) a. (Functor f, Num a) => f a -> f a
negated