```{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE Safe #-}

{- |
Module      :  Physics.Learn.CoordinateSystem
Copyright   :  (c) Scott N. Walck 2012-2018
Maintainer  :  Scott N. Walck <walck@lvc.edu>
Stability   :  experimental

A module for working with coordinate systems.
-}

module Physics.Learn.CoordinateSystem
( CoordinateSystem(..)
, standardCartesian
, standardCylindrical
, standardSpherical
, newCoordinateSystem
)
where

import Physics.Learn.Position
( Position
, cartesian
, cartesianCoordinates
, cylindrical
, cylindricalCoordinates
, spherical
, sphericalCoordinates
)

-- | Specification of a coordinate system requires
--   a map from coordinates into space, and
--   a map from space into coordinates.
data CoordinateSystem
= CoordinateSystem { toPosition   :: (Double,Double,Double) -> Position  -- ^ a map from coordinates into space
, fromPosition :: Position -> (Double,Double,Double)  -- ^ a map from space into coordinates
}

-- | The standard Cartesian coordinate system
standardCartesian :: CoordinateSystem
standardCartesian = CoordinateSystem cartesian cartesianCoordinates

-- | The standard cylindrical coordinate system
standardCylindrical :: CoordinateSystem
standardCylindrical = CoordinateSystem cylindrical cylindricalCoordinates

-- | The standard spherical coordinate system
standardSpherical :: CoordinateSystem
standardSpherical = CoordinateSystem spherical sphericalCoordinates

-- | Define a new coordinate system in terms of an existing one.
--   First parameter is a map from old coordinates to new coordinates.
--   Second parameter is the inverse map from new coordinates to old coordinates.
newCoordinateSystem :: ((Double,Double,Double) -> (Double,Double,Double))  -- ^ (x',y',z') = f(x,y,z)
-> ((Double,Double,Double) -> (Double,Double,Double))  -- ^ (x,y,z) = g(x',y',z')
-> CoordinateSystem  -- ^ old coordinate system
-> CoordinateSystem
newCoordinateSystem f g (CoordinateSystem tp fp)
= CoordinateSystem (tp . g) (f . fp)
```