content: {-# LANGUAGE ConstraintKinds #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE RankNTypes            #-}
  {-# LANGUAGE TypeFamilies          #-}
  
  {-# OPTIONS_GHC -fno-warn-unused-imports #-}
    -- for Data.Semigroup
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  Diagrams.Transform
  -- Copyright   :  (c) 2011-15 diagrams-lib team (see LICENSE)
  -- License     :  BSD-style (see LICENSE)
  -- Maintainer  :  diagrams-discuss@googlegroups.com
  --
  -- Affine transformations, parameterized by any vector space.  For
  -- transformations on particular vector spaces, see /e.g./
  -- "Diagrams.TwoD.Transform".
  --
  -----------------------------------------------------------------------------
  
  
  module Diagrams.Transform
      ( -- * Transformations
        Transformation, inv, transl, apply, papply
  
        -- * The Transformable class
      , Transformable(..)
  
        -- * Some specific transformations
      , translation, translate, moveTo, place, scaling, scale
  
        -- * Miscellaneous transformation-related utilities
      , conjugate, underT, transformed, translated, movedTo, movedFrom
  
        -- * The HasOrigin class
  
      , HasOrigin(..), moveOriginBy
  
      ) where
  
  import           Control.Lens   hiding (transform)
  import           Data.Semigroup
  import           Diagrams.Core
  
  import           Linear.Vector
  
  -- | Conjugate one transformation by another. @conjugate t1 t2@ is the
  --   transformation which performs first @t1@, then @t2@, then the
  --   inverse of @t1@.
  conjugate :: (Additive v, Num n)
            => Transformation v n -> Transformation v n -> Transformation v n
  conjugate t1 t2 = inv t1 <> t2 <> t1
  
  -- | Carry out some transformation \"under\" another one: @f ``underT``
  --   t@ first applies @t@, then @f@, then the inverse of @t@.  For
  --   example, @'scaleX' 2 ``underT`` 'rotation' (-1/8 \@\@ Turn)@
  --   is the transformation which scales by a factor of 2 along the
  --   diagonal line y = x.
  --
  --   Note that
  --
  -- @
  -- (transform t2) `underT` t1 == transform (conjugate t1 t2)
  -- @
  --
  --   for all transformations @t1@ and @t2@.
  --
  --   See also the isomorphisms like 'transformed', 'movedTo',
  --   'movedFrom', and 'translated'.
  underT :: (InSpace v n a, SameSpace a b, Transformable a, Transformable b)
        => (a -> b) -> Transformation v n -> a -> b
  f `underT` t = transform (inv t) . f . transform t
  
  -- | Use a 'Transformation' to make an 'Iso' between an object
  --   transformed and untransformed. This is useful for carrying out
  --   functions 'under' another transform:
  --
  -- @
  -- under (transformed t) f               == transform (inv t) . f . transform t
  -- under (transformed t1) (transform t2) == transform (conjugate t1 t2)
  -- transformed t ## a                    == transform t a
  -- a ^. transformed t                    == transform (inv t) a
  -- @
  transformed :: (InSpace v n a, SameSpace a b, Transformable a, Transformable b)
              => Transformation v n -> Iso a b a b
  transformed t = iso (transform $ inv t) (transform t)
  
  -- | Use a 'Point' to make an 'Iso' between an object
  --   moved to and from that point:
  --
  -- @
  -- under (movedTo p) f == moveTo (-p) . f . moveTo p
  -- over (movedTo p) f  == moveTo p . f . moveTo (-p)
  -- movedTo p           == from (movedFrom p)
  -- movedTo p ## a      == moveTo p a
  -- a ^. movedTo p      == moveOriginTo p a
  -- @
  movedTo :: (InSpace v n a, SameSpace a b, HasOrigin a, HasOrigin b)
          => Point v n -> Iso a b a b
  movedTo p = iso (moveTo (negated p)) (moveTo p)
  
  -- | Use a 'Transformation' to make an 'Iso' between an object
  --   transformed and untransformed. We have
  --
  -- @
  -- under (movedFrom p) f == moveTo p . f . moveTo (-p)
  -- movedFrom p           == from (movedTo p)
  -- movedFrom p ## a      == moveOriginTo p a
  -- a ^. movedFrom p      == moveTo p a
  -- over (movedFrom p) f  == moveTo (-p) . f . moveTo p
  -- @
  movedFrom :: (InSpace v n a, SameSpace a b, HasOrigin a, HasOrigin b)
            => Point v n -> Iso a b a b
  movedFrom p = iso (moveOriginTo (negated p)) (moveOriginTo p)
  
  -- | Use a vector to make an 'Iso' between an object translated and
  --   untranslated.
  --
  -- @
  -- under (translated v) f == translate (-v) . f . translate v
  -- translated v ## a      == translate v a
  -- a ^. translated v      == translate (-v) a
  -- over (translated v) f  == translate v . f . translate (-v)
  -- @
  translated :: (InSpace v n a, SameSpace a b, Transformable a, Transformable b)
             => v n -> Iso a b a b
  translated v = transformed $ translation v
  