packages feed

mini-1.6.4.0: src/Mini/Linear/Vector.hs

{-# LANGUAGE ImpredicativeTypes #-}

-- | Vector operations in orthonormal Euclidean spaces
module Mini.Linear.Vector (
  -- * Class
  Vector (
    axes
  ),

  -- * Construction
  basis,
  e,
  zero,

  -- * Operations
  (^+^),
  (^-^),
  (~*^),
  cross,
  dot,
  hat,
  norm,
  onto,

  -- * Interpolation
  lerp,
) where

import Control.Applicative (
  liftA2,
 )
import Mini.Linear.Space (
  V0 (V0),
  V1 (V1),
  V2 (V2),
  V3 (V3),
  V4 (V4),
  w,
  x,
  y,
  yzx,
  z,
  zxy,
 )
import Mini.Optics.Lens (
  Lens,
  set,
  view,
 )
import Prelude (
  Applicative,
  Floating,
  Foldable,
  Fractional,
  Num,
  fmap,
  pure,
  recip,
  sqrt,
  sum,
  ($),
  (*),
  (+),
  (-),
  (/),
 )

-- Class

-- | The class of vectors
class (Applicative v, Foldable v) => Vector v where
  -- | Basis vector lenses
  axes :: v (Lens (v a) (v a) a a)

instance Vector V0 where
  axes = V0

instance Vector V1 where
  axes = V1 x

instance Vector V2 where
  axes = V2 x y

instance Vector V3 where
  axes = V3 x y z

instance Vector V4 where
  axes = V4 x y z w

-- Construction

-- | Standard basis
basis :: (Vector v, Num a) => v (v a)
basis = fmap e axes

-- | Make a basis vector from a basis vector lens
e :: (Vector v, Num a) => Lens (v a) (v a) a a -> v a
e o = set o 1 zero

-- | Additive identity vector
zero :: (Vector v, Num a) => v a
zero = pure 0

-- Operations

infixl 6 ^+^

-- | Vector-vector addition
(^+^) :: (Vector v, Num a) => v a -> v a -> v a
(^+^) = liftA2 (+)

infixl 6 ^-^

-- | Vector-vector subtraction
(^-^) :: (Vector v, Num a) => v a -> v a -> v a
(^-^) = liftA2 (-)

infixl 7 ~*^

-- | Scalar-vector multiplication
(~*^) :: (Vector v, Num a) => a -> v a -> v a
s ~*^ v = fmap (s *) v

-- | Cross product
cross :: (Num a) => V3 a -> V3 a -> V3 a
cross u v =
  (^-^)
    (liftA2 (*) (view yzx u) (view zxy v))
    (liftA2 (*) (view zxy u) (view yzx v))

-- | Dot product
dot :: (Vector v, Num a) => v a -> v a -> a
dot u v = sum $ liftA2 (*) u v

-- | Adapt a vector /v/ to have unit norm (assumes @not $ v ~= zero@)
hat :: (Vector v, Floating a) => v a -> v a
hat u = recip (norm u) ~*^ u

-- | Norm of a vector
norm :: (Vector v, Floating a) => v a -> a
norm u = sqrt $ u `dot` u

-- | Project a vector onto a vector /v/ (assumes @not $ v ~= zero@)
onto :: (Vector v, Fractional a) => v a -> v a -> v a
onto u v = (u `dot` v) / (v `dot` v) ~*^ v

-- Interpolation

-- | Linear interpolation s.t. @lerp u v 0 == u && lerp u v 1 == v@
lerp :: (Vector v, Num a) => v a -> v a -> a -> v a
lerp u v t = (1 - t) ~*^ u ^+^ t ~*^ v