packages feed

aviation-navigation-0.1.0.1: src/Data/Aviation/Navigation/Vector.hs

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}

module Data.Aviation.Navigation.Vector(
  Vector(..)
, Vector'
, vectorDegrees
, HasVector(..)
) where

import Control.Category ( Category(id, (.)) )
import Control.Lens ( view, Lens' )
import Data.Eq ( Eq )
import Data.Functor ( Functor(fmap) )
import Data.Monoid ( Monoid(mempty) )
import Data.Ord ( Ord )
import Data.Radian ( fromRadians )
import Data.Semigroup ( Semigroup((<>)) )
import GHC.Float ( Floating(cos, sqrt, pi, atan, sin), Double )
import GHC.Show(Show)
import Prelude(Num((*), (-), (+)), Fractional((/)))

data Vector a =
  Vector a a
  deriving (Eq, Ord, Show)

type Vector' =
  Vector Double

vectorDegrees ::
  Double
  -> Double
  -> Vector'
vectorDegrees =
  Vector . view fromRadians

instance Floating a => Semigroup (Vector a) where
  Vector aa am <> Vector ba bm =
    let square x = x * x
        t = aa - ba
        mag = sqrt (square am + square bm - 2 * am * bm * cos (pi - t))
        ang = atan (bm * sin t / (am + bm * cos t))
    in  Vector (aa - ang) mag

instance Floating a => Monoid (Vector a) where
  mempty =
    Vector 0 0

class HasVector a c | a -> c where
  vector ::
    Lens' a (Vector c)
  {-# INLINE angle #-}
  angle ::
    Lens' a c
  angle =
    vector . angle
  {-# INLINE magnitude #-}
  magnitude ::
    Lens' a c
  magnitude =
    vector . magnitude

instance HasVector (Vector a) a where
  vector = id
  {-# INLINE angle #-}
  angle f (Vector x y) =
    fmap (`Vector` y) (f x)
  {-# INLINE magnitude #-}
  magnitude f (Vector a y) =
    fmap (Vector a) (f y)