```{-# LANGUAGE DeriveFunctor        #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE UndecidableInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.Direction
-- Copyright   :  (c) 2014 diagrams-lib team (see LICENSE)
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  diagrams-discuss@googlegroups.com
--
-- Type for representing directions, polymorphic in vector space
--
-----------------------------------------------------------------------------

module Diagrams.Direction
( Direction
, _Dir
, direction, dir, fromDirection, fromDir
, angleBetweenDirs
, dirBetween
) where

import           Control.Lens   (Iso', iso)

import           Diagrams.Angle
import           Diagrams.Core

import           Linear.Affine
import           Linear.Metric
import           Linear.Vector

--------------------------------------------------------------------------------
-- Direction

-- | A vector is described by a @Direction@ and a magnitude.  So we
-- can think of a @Direction@ as a vector that has forgotten its
-- magnitude.  @Direction@s can be used with 'fromDirection' and the
-- lenses provided by its instances.
newtype Direction v n = Dir (v n)
deriving (Read, Show, Eq, Ord, Functor) -- todo: special instances

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

instance (V (v n) ~ v, N (v n) ~ n, Transformable (v n)) => Transformable (Direction v n) where
transform t (Dir v) = Dir (transform t v)

instance HasTheta v => HasTheta (Direction v) where
_theta = _Dir . _theta

instance HasPhi v => HasPhi (Direction v) where
_phi = _Dir . _phi

-- | _Dir is provided to allow efficient implementations of functions
--   in particular vector-spaces, but should be used with care as it
--   exposes too much information.
_Dir :: Iso' (Direction v n) (v n)
_Dir = iso (\(Dir v) -> v) Dir

-- | @direction v@ is the direction in which @v@ points.  Returns an
--   unspecified value when given the zero vector as input.
direction :: v n -> Direction v n
direction = Dir

-- | Synonym for 'direction'.
dir :: v n -> Direction v n
dir = Dir

-- | @fromDirection d@ is the unit vector in the direction @d@.
fromDirection :: (Metric v, Floating n) => Direction v n -> v n
fromDirection (Dir v) = signorm v

-- | Synonym for 'fromDirection'.
fromDir :: (Metric v, Floating n) => Direction v n -> v n
fromDir (Dir v) = signorm v

-- | compute the positive angle between the two directions in their common plane
angleBetweenDirs :: (Metric v, Floating n, Ord n)
=> Direction v n -> Direction v n -> Angle n
angleBetweenDirs d1 d2 = angleBetween (fromDirection d1) (fromDirection d2)

-- | @dirBetween p q@ returns the direction from @p@ to @q@.
dirBetween :: (Additive v, Num n) => Point v n -> Point v n -> Direction v n
dirBetween p q = dir \$ q .-. p
```