{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE Safe #-}

{- | 
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
    , vec
    , xComp
    , yComp
    , zComp
    , iHat
    , jHat
    , kHat
    , sumV
    , magnitude
    , (^/)
    )

-- | A type for position.
--   Position is not a vector because it makes no sense to add positions.
data Position = Cart Double Double Double
                deriving (Int -> Position -> ShowS
[Position] -> ShowS
Position -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Position] -> ShowS
$cshowList :: [Position] -> ShowS
show :: Position -> String
$cshow :: Position -> String
showsPrec :: Int -> Position -> ShowS
$cshowsPrec :: Int -> Position -> ShowS
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 = Position -> 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 :: forall v. AdditiveGroup v => [Field v] -> Field v
addFields [Field v]
flds Position
r = forall (f :: * -> *) v. (Foldable f, AdditiveGroup v) => f v -> v
sumV [Field v
fld Position
r | Field v
fld <- [Field v]
flds]

-- | The Cartesian coordinate system.  Coordinates are (x,y,z).
cartesian :: CoordinateSystem
cartesian :: CoordinateSystem
cartesian (Double
x,Double
y,Double
z) = Double -> Double -> Double -> Position
Cart Double
x Double
y Double
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 :: CoordinateSystem
cylindrical (Double
s,Double
phi,Double
z) = Double -> Double -> Double -> Position
Cart (Double
s forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos Double
phi) (Double
s forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
phi) Double
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 :: CoordinateSystem
spherical (Double
r,Double
th,Double
phi) = Double -> Double -> Double -> Position
Cart (Double
r forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
th forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos Double
phi) (Double
r forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
th forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
phi) (Double
r forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos Double
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 :: Double -> Double -> Double -> Position
cart = Double -> Double -> Double -> Position
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 :: Double -> Double -> Double -> Position
cyl Double
s Double
phi Double
z = Double -> Double -> Double -> Position
Cart (Double
s forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos Double
phi) (Double
s forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
phi) Double
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 :: Double -> Double -> Double -> Position
sph Double
r Double
theta Double
phi = Double -> Double -> Double -> Position
Cart (Double
r forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
theta forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos Double
phi) (Double
r forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
theta forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
phi) (Double
r forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos Double
theta)

-- | Returns the three Cartesian coordinates as a triple from a position.
cartesianCoordinates :: Position -> (Double,Double,Double)
cartesianCoordinates :: Position -> (Double, Double, Double)
cartesianCoordinates (Cart Double
x Double
y Double
z) = (Double
x,Double
y,Double
z)

-- | Returns the three cylindrical coordinates as a triple from a position.
cylindricalCoordinates :: Position -> (Double,Double,Double)
cylindricalCoordinates :: Position -> (Double, Double, Double)
cylindricalCoordinates (Cart Double
x Double
y Double
z) = (Double
s,Double
phi,Double
z)
    where
      s :: Double
s = forall a. Floating a => a -> a
sqrt(Double
xforall a. Floating a => a -> a -> a
**Double
2 forall a. Num a => a -> a -> a
+ Double
yforall a. Floating a => a -> a -> a
**Double
2)
      phi :: Double
phi = forall a. RealFloat a => a -> a -> a
atan2 Double
y Double
x

-- | Returns the three spherical coordinates as a triple from a position.
sphericalCoordinates :: Position -> (Double,Double,Double)
sphericalCoordinates :: Position -> (Double, Double, Double)
sphericalCoordinates (Cart Double
x Double
y Double
z) = (Double
r,Double
theta,Double
phi)
    where
      r :: Double
r = forall a. Floating a => a -> a
sqrt(Double
xforall a. Floating a => a -> a -> a
**Double
2 forall a. Num a => a -> a -> a
+ Double
yforall a. Floating a => a -> a -> a
**Double
2 forall a. Num a => a -> a -> a
+ Double
zforall a. Floating a => a -> a -> a
**Double
2)
      theta :: Double
theta = forall a. RealFloat a => a -> a -> a
atan2 Double
s Double
z
      s :: Double
s = forall a. Floating a => a -> a
sqrt(Double
xforall a. Floating a => a -> a -> a
**Double
2 forall a. Num a => a -> a -> a
+ Double
yforall a. Floating a => a -> a -> a
**Double
2)
      phi :: Double
phi = forall a. RealFloat a => a -> a -> a
atan2 Double
y Double
x

-- | Displacement from source position to target position.
displacement :: Position  -- ^ source position
             -> Position  -- ^ target position
             -> Displacement
displacement :: Position -> Position -> Vec
displacement (Cart Double
x' Double
y' Double
z') (Cart Double
x Double
y Double
z) = Double -> Double -> Double -> Vec
vec (Double
xforall a. Num a => a -> a -> a
-Double
x') (Double
yforall a. Num a => a -> a -> a
-Double
y') (Double
zforall a. Num a => a -> a -> a
-Double
z')

-- | Shift a position by a displacement.
shiftPosition :: Displacement -> Position -> Position
shiftPosition :: Vec -> Position -> Position
shiftPosition Vec
v (Cart Double
x Double
y Double
z) = Double -> Double -> Double -> Position
Cart (Double
x forall a. Num a => a -> a -> a
+ Vec -> Double
xComp Vec
v) (Double
y forall a. Num a => a -> a -> a
+ Vec -> Double
yComp Vec
v) (Double
z forall a. Num a => a -> a -> a
+ Vec -> Double
zComp Vec
v)

-- | An object is a map into 'Position'.
shiftObject :: Displacement -> (a -> Position) -> (a -> Position)
shiftObject :: forall a. Vec -> (a -> Position) -> a -> Position
shiftObject Vec
d a -> Position
f = Vec -> Position -> Position
shiftPosition Vec
d forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Position
f

-- | A field is a map from 'Position'.
shiftField :: Displacement -> (Position -> v) -> (Position -> v)
shiftField :: forall v. Vec -> (Position -> v) -> Position -> v
shiftField Vec
d Position -> v
f = Position -> v
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vec -> Position -> Position
shiftPosition Vec
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 :: VectorField
rHat :: Position -> Vec
rHat Position
rv = Vec
d forall v s.
(VectorSpace v, s ~ Scalar v, Fractional s) =>
v -> s -> v
^/ forall v s. (InnerSpace v, s ~ Scalar v, Floating s) => v -> s
magnitude Vec
d
    where
      d :: Vec
d = Position -> Position -> Vec
displacement (Double -> Double -> Double -> Position
cart Double
0 Double
0 Double
0) Position
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 :: VectorField
thetaHat :: Position -> Vec
thetaHat Position
r = Double -> Double -> Double -> Vec
vec (forall a. Floating a => a -> a
cos Double
theta forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos Double
phi) (forall a. Floating a => a -> a
cos Double
theta forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin Double
phi) (-forall a. Floating a => a -> a
sin Double
theta)
    where
      (Double
_,Double
theta,Double
phi) = Position -> (Double, Double, Double)
sphericalCoordinates Position
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 :: VectorField
phiHat :: Position -> Vec
phiHat Position
r = Double -> Double -> Double -> Vec
vec (-forall a. Floating a => a -> a
sin Double
phi) (forall a. Floating a => a -> a
cos Double
phi) Double
0
    where
      (Double
_,Double
phi,Double
_) = Position -> (Double, Double, Double)
cylindricalCoordinates Position
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 :: VectorField
sHat :: Position -> Vec
sHat Position
r = Double -> Double -> Double -> Vec
vec (forall a. Floating a => a -> a
cos Double
phi) (forall a. Floating a => a -> a
sin Double
phi) Double
0
    where
      (Double
_,Double
phi,Double
_) = Position -> (Double, Double, Double)
cylindricalCoordinates Position
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 :: VectorField
xHat :: Position -> Vec
xHat = forall a b. a -> b -> a
const Vec
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 :: VectorField
yHat :: Position -> Vec
yHat = forall a b. a -> b -> a
const Vec
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 :: VectorField
zHat :: Position -> Vec
zHat = forall a b. a -> b -> a
const Vec
kHat