content: {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE Safe #-}
  {-# LANGUAGE TypeSynonymInstances #-}
  {-# OPTIONS_GHC -Wall #-}
  
  -- |
  -- Module      :  Physics.Learn.Position
  -- Copyright   :  (c) Scott N. Walck 2012-2018
  -- License     :  BSD3 (see LICENSE)
  -- Maintainer  :  Scott N. Walck <walck@lvc.edu>
  -- Stability   :  experimental
  --
  -- A module for working with the idea of position and coordinate systems.
  module Physics.Learn.Position
    ( Position
    , Displacement
    , ScalarField
    , VectorField
    , Field
    , CoordinateSystem
    , cartesian
    , cylindrical
    , spherical
    , cart
    , cyl
    , sph
    , cartesianCoordinates
    , cylindricalCoordinates
    , sphericalCoordinates
    , displacement
    , shiftPosition
    , shiftObject
    , shiftField
    , addFields
    , rHat
    , thetaHat
    , phiHat
    , sHat
    , xHat
    , yHat
    , zHat
    )
  where
  
  import Data.VectorSpace
    ( AdditiveGroup
    )
  import Physics.Learn.CarrotVec
    ( Vec
    , iHat
    , jHat
    , kHat
    , magnitude
    , sumV
    , vec
    , xComp
    , yComp
    , zComp
    , (^/)
    )
  
  -- | A type for position.
  --   Position is not a vector because it makes no sense to add positions.
  data Position = Cart Double Double Double
    deriving (Show)
  
  -- | A displacement is a vector.
  type Displacement = Vec
  
  -- | A scalar field associates a number with each position in space.
  type ScalarField = Position -> Double
  
  {-
  -- | Scalar fields can be added, subtracted, multiplied, and negated,
  --   just like scalars themselves.
  instance Num ScalarField where
      (f + g) x = f x + g x
      (f * g) x = f x * g x
      (f - g) x = f x - g x
      negate f x = negate (f x)
      abs f x = abs (f x)
      signum f x = signum (f x)
      fromInteger n = const (fromInteger n)
  
  -- | Scalar fields can be divided, just like scalars themselves.
  instance Fractional ScalarField where
      (f / g) x = f x / g x
      recip f x = recip (f x)
      fromRational rat = const (fromRational rat)
  
  -- | Cosine of a scalar field, etc.
  instance Floating ScalarField where
      pi = const pi
      exp f x = exp (f x)
      sqrt f x = sqrt (f x)
      log f x = log (f x)
      (f ** g) x = f x ** g x
      logBase f g x = logBase (f x) (g x)
      sin f x = sin (f x)
      cos f x = cos (f x)
      tan f x = tan (f x)
      asin f x = asin (f x)
      acos f x = acos (f x)
      atan f x = atan (f x)
      sinh f x = sinh (f x)
      cosh f x = cosh (f x)
      tanh f x = tanh (f x)
      asinh f x = asinh (f x)
      acosh f x = acosh (f x)
      atanh f x = atanh (f x)
  -}
  
  -- | A vector field associates a vector with each position in space.
  type VectorField = Field Vec
  
  -- | Sometimes we want to be able to talk about a field without saying
  --   whether it is a scalar field or a vector field.
  type Field v = Position -> v
  
  -- | A coordinate system is a function from three parameters to space.
  type CoordinateSystem = (Double, Double, Double) -> Position
  
  -- | Add two scalar fields or two vector fields.
  addFields :: AdditiveGroup v => [Field v] -> Field v
  addFields flds r = sumV [fld r | fld <- flds]
  
  -- | The Cartesian coordinate system.  Coordinates are (x,y,z).
  cartesian :: CoordinateSystem
  cartesian (x, y, z) = Cart x y z
  
  -- | The cylindrical coordinate system.  Coordinates are (s,phi,z),
  --   where s is the distance from the z axis and phi is the angle
  --   with the x axis.
  cylindrical :: CoordinateSystem
  cylindrical (s, phi, z) = Cart (s * cos phi) (s * sin phi) z
  
  -- | The spherical coordinate system.  Coordinates are (r,theta,phi),
  --   where r is the distance from the origin, theta is the angle with
  --   the z axis, and phi is the azimuthal angle.
  spherical :: CoordinateSystem
  spherical (r, th, phi) = Cart (r * sin th * cos phi) (r * sin th * sin phi) (r * cos th)
  
  -- | A helping function to take three numbers x, y, and z and form the
  --   appropriate position using Cartesian coordinates.
  cart
    :: Double
    -- ^ x coordinate
    -> Double
    -- ^ y coordinate
    -> Double
    -- ^ z coordinate
    -> Position
  cart = Cart
  
  -- | A helping function to take three numbers s, phi, and z and form the
  --   appropriate position using cylindrical coordinates.
  cyl
    :: Double
    -- ^ s coordinate
    -> Double
    -- ^ phi coordinate
    -> Double
    -- ^ z coordinate
    -> Position
  cyl s phi z = Cart (s * cos phi) (s * sin phi) z
  
  -- | A helping function to take three numbers r, theta, and phi and form the
  --   appropriate position using spherical coordinates.
  sph
    :: Double
    -- ^ r coordinate
    -> Double
    -- ^ theta coordinate
    -> Double
    -- ^ phi coordinate
    -> Position
  sph r theta phi = Cart (r * sin theta * cos phi) (r * sin theta * sin phi) (r * cos theta)
  
  -- | Returns the three Cartesian coordinates as a triple from a position.
  cartesianCoordinates :: Position -> (Double, Double, Double)
  cartesianCoordinates (Cart x y z) = (x, y, z)
  
  -- | Returns the three cylindrical coordinates as a triple from a position.
  cylindricalCoordinates :: Position -> (Double, Double, Double)
  cylindricalCoordinates (Cart x y z) = (s, phi, z)
    where
      s = sqrt (x ** 2 + y ** 2)
      phi = atan2 y x
  
  -- | Returns the three spherical coordinates as a triple from a position.
  sphericalCoordinates :: Position -> (Double, Double, Double)
  sphericalCoordinates (Cart x y z) = (r, theta, phi)
    where
      r = sqrt (x ** 2 + y ** 2 + z ** 2)
      theta = atan2 s z
      s = sqrt (x ** 2 + y ** 2)
      phi = atan2 y x
  
  -- | Displacement from source position to target position.
  displacement
    :: Position
    -- ^ source position
    -> Position
    -- ^ target position
    -> Displacement
  displacement (Cart x' y' z') (Cart x y z) = vec (x - x') (y - y') (z - z')
  
  -- | Shift a position by a displacement.
  shiftPosition :: Displacement -> Position -> Position
  shiftPosition v (Cart x y z) = Cart (x + xComp v) (y + yComp v) (z + zComp v)
  
  -- | An object is a map into 'Position'.
  shiftObject :: Displacement -> (a -> Position) -> (a -> Position)
  shiftObject d f = shiftPosition d . f
  
  -- | A field is a map from 'Position'.
  shiftField :: Displacement -> (Position -> v) -> (Position -> v)
  shiftField d f = f . shiftPosition d
  
  -- | The vector field in which each point in space is associated
  --   with a unit vector in the direction of increasing spherical coordinate
  --   r, while spherical coordinates theta and phi
  --   are held constant.
  --   Defined everywhere except at the origin.
  --   The unit vector 'rHat' points in different directions at different points
  --   in space.  It is therefore better interpreted as a vector field, rather
  --   than a vector.
  rHat :: Field Vec
  rHat rv = d ^/ magnitude d
    where
      d = displacement (cart 0 0 0) rv
  
  -- | The vector field in which each point in space is associated
  --   with a unit vector in the direction of increasing spherical coordinate
  --   theta, while spherical coordinates r and phi are held constant.
  --   Defined everywhere except on the z axis.
  thetaHat :: Field Vec
  thetaHat r = vec (cos theta * cos phi) (cos theta * sin phi) (-sin theta)
    where
      (_, theta, phi) = sphericalCoordinates r
  
  -- | The vector field in which each point in space is associated
  --   with a unit vector in the direction of increasing (cylindrical or spherical) coordinate
  --   phi, while cylindrical coordinates s and z
  --   (or spherical coordinates r and theta) are held constant.
  --   Defined everywhere except on the z axis.
  phiHat :: Field Vec
  phiHat r = vec (-sin phi) (cos phi) 0
    where
      (_, phi, _) = cylindricalCoordinates r
  
  -- | The vector field in which each point in space is associated
  --   with a unit vector in the direction of increasing cylindrical coordinate
  --   s, while cylindrical coordinates phi and z
  --   are held constant.
  --   Defined everywhere except on the z axis.
  sHat :: Field Vec
  sHat r = vec (cos phi) (sin phi) 0
    where
      (_, phi, _) = cylindricalCoordinates r
  
  -- | The vector field in which each point in space is associated
  --   with a unit vector in the direction of increasing Cartesian coordinate
  --   x, while Cartesian coordinates y and z
  --   are held constant.
  --   Defined everywhere.
  xHat :: Field Vec
  xHat = const iHat
  
  -- | The vector field in which each point in space is associated
  --   with a unit vector in the direction of increasing Cartesian coordinate
  --   y, while Cartesian coordinates x and z
  --   are held constant.
  --   Defined everywhere.
  yHat :: Field Vec
  yHat = const jHat
  
  -- | The vector field in which each point in space is associated
  --   with a unit vector in the direction of increasing Cartesian coordinate
  --   z, while Cartesian coordinates x and y
  --   are held constant.
  --   Defined everywhere.
  zHat :: Field Vec
  zHat = const kHat
  