```{-# LANGUAGE FlexibleContexts
, TypeFamilies
#-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.TwoD.Transform
--
-- Transformations specific to two dimensions, with a few generic
-- transformations (uniform scaling, translation) also re-exported for
-- convenience.
--
-----------------------------------------------------------------------------

module Diagrams.TwoD.Transform
(
-- * Rotation
rotation, rotate
, rotationBy, rotateBy

-- * Scaling
, scalingX, scaleX
, scalingY, scaleY
, scaling, scale

-- * Translation
, translationX, translateX
, translationY, translateY
, translation, translate

-- * Reflection
, reflectionX, reflectX
, reflectionY, reflectY
) where

import Graphics.Rendering.Diagrams

import Diagrams.TwoD.Types

import Control.Arrow (first, second)

-- | Create a transformation which performs a rotation by the given
rotation :: Angle -> Transformation R2
rotation theta = fromLinear r (linv r)
where
rot th (x,y) = (cos th * x - sin th * y, sin th * x + cos th * y)
r = rot theta <-> rot (-theta)

-- | Rotate by the given angle in radians.
rotate :: (Transformable t, V t ~ R2) => Angle -> t -> t
rotate = transform . rotation

-- | Create a transformation which performs a rotation by the given
--   fraction of a circle.  For example, @rotationBy (1/4)@ rotates by
--   one quarter of a circle (i.e. 90 degrees, i.e. pi/2 radians).
rotationBy :: Double -> Transformation R2
rotationBy = rotation . (*(2*pi))

-- | Rotate by the given fraction of a circle.
rotateBy :: (Transformable t, V t ~ R2) => Angle -> t -> t
rotateBy = transform . rotationBy

-- | Construct a transformation which scales by the given factor in
--   the x (horizontal) direction.
scalingX :: Double -> Transformation R2
scalingX c = fromLinear s s
where s = first (*c) <-> first (/c)

-- | Scale a diagram by the given factor in the x (horizontal)
--   direction.  To scale uniformly, use
--   'Graphics.Rendering.Diagrams.Transform.scale'.
scaleX :: (Transformable t, V t ~ R2) => Double -> t -> t
scaleX = transform . scalingX

-- | Construct a transformation which scales by the given factor in
--   the y (vertical) direction.
scalingY :: Double -> Transformation R2
scalingY c = fromLinear s s
where s = second (*c) <-> second (/c)

-- | Scale a diagram by the given factor in the y (vertical)
--   direction.  To scale uniformly, use
--   'Graphics.Rendering.Diagrams.Transform.scale'.
scaleY :: (Transformable t, V t ~ R2) => Double -> t -> t
scaleY = transform . scalingY

-- | Construct a transformation which translates by the given distance
--   in the x (horizontal) direction.
translationX :: Double -> Transformation R2
translationX x = translation (x,0)

-- | Translate a diagram by the given distance in the x (horizontal)
--   direction.
translateX :: (Transformable t, V t ~ R2) => Double -> t -> t
translateX = transform . translationX

-- | Construct a transformation which translates by the given distance
--   in the y (vertical) direction.
translationY :: Double -> Transformation R2
translationY y = translation (0,y)

-- | Translate a diagram by the given distance in the y (vertical)
--   direction.
translateY :: (Transformable t, V t ~ R2) => Double -> t -> t
translateY = transform . translationY

-- | Construct a transformation which flips a diagram from left to
--   right, i.e. sends the point (x,y) to (-x,y).
reflectionX :: Transformation R2
reflectionX = scalingX (-1)

-- | Flip a diagram from left to right, i.e. send the point (x,y) to
--   (-x,y).
reflectX :: (Transformable t, V t ~ R2) => t -> t
reflectX = transform reflectionX

-- | Construct a transformation which flips a diagram from top to
--   bottom, i.e. sends the point (x,y) to (x,-y).
reflectionY :: Transformation R2
reflectionY = scalingY (-1)

-- | Flip a diagram from top to bottom, i.e. send the point (x,y) to
--   (x,-y).
reflectY :: (Transformable t, V t ~ R2) => t -> t
reflectY = transform reflectionY

-- XXX todo: add general reflection/reflect operators which reflect
-- around an arbitrary axis (taking a vector as an argument).
```