packages feed

spatial-math 0.1.7 → 0.2.0

raw patch · 7 files changed

+208/−264 lines, 7 filesdep +doctestdep +lineardep −hmatrixdep −random

Dependencies added: doctest, linear

Dependencies removed: hmatrix, random

Files

− Quat.hs
@@ -1,64 +0,0 @@-{-# OPTIONS_GHC -Wall #-}-{-# Language StandaloneDeriving #-}-{-# Language DeriveDataTypeable #-}-{-# Language DeriveFunctor #-}--module Quat ( Quat(..)-            , zipWithQuat-            , inv-            , norm-            , normalize-            , qmult-            , qmult'-            ) where--import Data.Data ( Data )-import Data.Typeable ( Typeable1 )--data Quat a = Quat a a a a deriving (Show, Eq)--deriving instance Typeable1 Quat-deriving instance Data a => Data (Quat a)-deriving instance Functor Quat--zipWithQuat :: (a -> b -> c) -> Quat a -> Quat b -> Quat c-zipWithQuat f (Quat p0 p1 p2 p3) (Quat q0 q1 q2 q3) = Quat (f p0 q0) (f p1 q1) (f p2 q2) (f p3 q3)--instance (Num a, Ord a) => Num (Quat a) where-  (+) = zipWithQuat (+)-  (-) = zipWithQuat (-)-  negate = fmap negate-  (*) = qmult-  abs = fmap abs-  signum = fmap signum-  fromInteger = error "fromInteger undefined for Quat"---- | q_out = q_in^-1-inv :: Num a => Quat a -> Quat a-inv (Quat q0 q1 q2 q3) = Quat q0 (-q1) (-q2) (-q3)---- | return ||q||-norm :: Floating a => Quat a -> a-norm (Quat q0 q1 q2 q3) = sqrt $ q0*q0 + q1*q1 + q2*q2 + q3*q3---- | q /= ||q||-normalize :: Floating a => Quat a -> Quat a-normalize q = fmap (* normInv) q-  where-    normInv = 1/(norm q)---- | quaternion multiply: qa * qb-qmult :: (Num a, Ord a) => Quat a -> Quat a -> Quat a-qmult (Quat p0 p1 p2 p3) (Quat q0 q1 q2 q3)-  | r0 < 0 = negate qOut-  | otherwise = qOut-  where-    qOut = Quat r0 r1 r2 r3-    r0 = p0*q0 - p1*q1 - p2*q2 - p3*q3-    r1 = p0*q1 + p1*q0 + p2*q3 - p3*q2-    r2 = p0*q2 - p1*q3 + p2*q0 + p3*q1-    r3 = p0*q3 + p1*q2 - p2*q1 + p3*q0---- | quaternion multiply then normalize-qmult' :: (Floating a, Ord a) => Quat a -> Quat a -> Quat a-qmult' p q = normalize (qmult q p)
+ README.md view
@@ -0,0 +1,10 @@+spatial-math+===++[![Build Status](https://secure.travis-ci.org/ghorn/spatial-math.png?branch=master)](http://travis-ci.org/ghorn/spatial-math)++3d math including quaternions/euler angles/dcms and utility functions.++This is a port of my 'mathlib' C library: https://github.com/ghorn/mathlib and may someday be merged with it.++
SpatialMath.hs view
@@ -1,13 +1,12 @@ {-# OPTIONS_GHC -Wall #-} {-# Language StandaloneDeriving #-} {-# Language DeriveDataTypeable #-}+{-# Language DeriveGeneric #-} {-# Language DeriveFunctor #-}+{-# Language DeriveFoldable #-}+{-# Language DeriveTraversable #-} -module SpatialMath ( module Xyz-                   , module Quat-                   , Xyz(..)-                   , Quat(..)-                   , Euler(..)+module SpatialMath ( Euler(..)                    , rotateXyzAboutX                    , rotateXyzAboutY                    , rotateXyzAboutZ@@ -25,56 +24,105 @@                    , rotVecByQuatB2A                    , rotVecByEuler                    , rotVecByEulerB2A+                     -- * re-exported from linear+                   , M33+                   , V3(..)+                   , Quaternion(..)                    ) where -import qualified Xyz-import qualified Quat-import Xyz ( Xyz(..) )-import Quat ( Quat(..) )--import Numeric.LinearAlgebra-import Foreign.Storable ( Storable ) import Data.Data ( Data )+import Data.Foldable ( Foldable )+import Data.Traversable ( Traversable ) import Data.Typeable ( Typeable1 )+import GHC.Generics ( Generic, Generic1 )+import Linear -data Euler a = Euler a a a deriving (Eq, Show) -- {yaw, pitch, roll}+normalize' :: Floating a => Quaternion a -> Quaternion a+normalize' q = fmap (* normInv) q+  where+    normInv = 1/(norm q) +--normalize' :: (Floating a, Epsilon a) => Quaternion a -> Quaternion a+--normalize' = normalize++-- | 3-2-1 Euler angle rotation sequence+data Euler a = Euler { eYaw :: a+                     , ePitch :: a+                     , eRoll :: a+                     } deriving (Eq, Show, Generic, Generic1, Functor, Foldable, Traversable, Ord)+ deriving instance Typeable1 Euler deriving instance Data a => Data (Euler a)-deriving instance Functor Euler -rotateXyzAboutX :: Floating a => Xyz a -> a -> Xyz a-rotateXyzAboutX (Xyz ax ay az) rotAngle = Xyz bx by bz+-- | Rotate a vector about the X axis+--+-- >>> rotateXyzAboutX (V3 0 1 0) (pi/2)+-- V3 0.0 6.123233995736766e-17 1.0+--+-- >>> rotateXyzAboutX (V3 0 0 1) (pi/2)+-- V3 0.0 (-1.0) 6.123233995736766e-17+rotateXyzAboutX :: Floating a => V3 a -> a -> V3 a+rotateXyzAboutX (V3 ax ay az) rotAngle = V3 bx by bz   where     cosTheta = cos rotAngle     sinTheta = sin rotAngle      bx =  ax-    by =  ay*cosTheta + az*sinTheta-    bz = -ay*sinTheta + az*cosTheta+    by =  ay*cosTheta - az*sinTheta+    bz =  ay*sinTheta + az*cosTheta -rotateXyzAboutY :: Floating a => Xyz a -> a -> Xyz a-rotateXyzAboutY (Xyz ax ay az) rotAngle = Xyz bx by bz+-- | Rotate a vector about the Y axis+--+-- >>> rotateXyzAboutY (V3 0 0 1) (pi/2)+-- V3 1.0 0.0 6.123233995736766e-17+--+-- >>> rotateXyzAboutY (V3 1 0 0) (pi/2)+-- V3 6.123233995736766e-17 0.0 (-1.0)+rotateXyzAboutY :: Floating a => V3 a -> a -> V3 a+rotateXyzAboutY (V3 ax ay az) rotAngle = V3 bx by bz   where     cosTheta = cos rotAngle     sinTheta = sin rotAngle -    bx =  ax*cosTheta - az*sinTheta+    bx =  ax*cosTheta + az*sinTheta     by =  ay-    bz =  ax*sinTheta + az*cosTheta+    bz = -ax*sinTheta + az*cosTheta -rotateXyzAboutZ :: Floating a => Xyz a -> a -> Xyz a-rotateXyzAboutZ (Xyz ax ay az) rotAngle = Xyz bx by bz+-- | Rotate a vector about the Z axis+--+-- >>> rotateXyzAboutZ (V3 1 0 0) (pi/2)+-- V3 6.123233995736766e-17 1.0 0.0+--+-- >>> rotateXyzAboutZ (V3 0 1 0) (pi/2)+-- V3 (-1.0) 6.123233995736766e-17 0.0+--+rotateXyzAboutZ :: Floating a => V3 a -> a -> V3 a+rotateXyzAboutZ (V3 ax ay az) rotAngle = V3 bx by bz   where     cosTheta = cos rotAngle     sinTheta = sin rotAngle -    bx =  ax*cosTheta + ay*sinTheta-    by = -ax*sinTheta + ay*cosTheta+    bx =  ax*cosTheta - ay*sinTheta+    by =  ax*sinTheta + ay*cosTheta     bz =  az -euler321OfQuat :: RealFloat a => Quat a -> Euler a-euler321OfQuat (Quat q0 q1 q2 q3) = Euler yaw pitch roll++-- | Convert quaternion to Euler angles+--+-- >>> euler321OfQuat (Quaternion 1.0 (V3 0.0 0.0 0.0))+-- Euler {eYaw = 0.0, ePitch = -0.0, eRoll = 0.0}+--+-- >>> euler321OfQuat (Quaternion (sqrt(2)/2) (V3 (sqrt(2)/2) 0.0 0.0))+-- Euler {eYaw = 0.0, ePitch = -0.0, eRoll = 1.5707963267948966}+--+-- >>> euler321OfQuat (Quaternion (sqrt(2)/2) (V3 0.0 (sqrt(2)/2) 0.0))+-- Euler {eYaw = 0.0, ePitch = 1.5707963267948966, eRoll = 0.0}+--+-- >>> euler321OfQuat (Quaternion (sqrt(2)/2) (V3 0.0 0.0 (sqrt(2)/2)))+-- Euler {eYaw = 1.5707963267948966, ePitch = -0.0, eRoll = 0.0}+--+euler321OfQuat :: RealFloat a => Quaternion a -> Euler a+euler321OfQuat (Quaternion q0 (V3 q1 q2 q3)) = Euler yaw pitch roll   where     r11 = q0*q0 + q1*q1 - q2*q2 - q3*q3     r12 = 2.0*(q1*q2 + q0*q3)@@ -90,31 +138,67 @@     pitch = asin mr13     roll  = atan2 r23 r33 -quatOfDcm :: (Storable a, RealFloat a) => Matrix a -> Quat a+-- | convert a DCM to a quaternion+--+-- >>> quatOfDcm $ V3 (V3 1 0 0) (V3 0 1 0) (V3 0 0 1)+-- Quaternion 1.0 (V3 0.0 0.0 0.0)+--+-- >>> quatOfDcm $ V3 (V3 0 1 0) (V3 (-1) 0 0) (V3 0 0 1)+-- Quaternion 0.7071067811865476 (V3 0.0 0.0 0.7071067811865475)+--+-- >>> let s = sqrt(2)/2 in quatOfDcm $ V3 (V3 s s 0) (V3 (-s) s 0) (V3 0 0 1)+-- Quaternion 0.9238795325112867 (V3 0.0 0.0 0.3826834323650898)+--+quatOfDcm :: RealFloat a => M33 a -> Quaternion a quatOfDcm = quatOfEuler321 . euler321OfDcm -quatOfDcmB2A :: (Storable a, RealFloat a) => Matrix a -> Quat a-quatOfDcmB2A = Quat.inv . quatOfDcm+quatOfDcmB2A :: (Conjugate a, RealFloat a) => M33 a -> Quaternion a+quatOfDcmB2A = conjugate . quatOfDcm -euler321OfDcm :: (RealFloat a, Storable a) => Matrix a -> Euler a-euler321OfDcm r = Euler yaw pitch roll+-- | Convert DCM to euler angles+--+-- >>> euler321OfDcm $ V3 (V3 1 0 0) (V3 0 1 0) (V3 0 0 1)+-- Euler {eYaw = 0.0, ePitch = -0.0, eRoll = 0.0}+--+-- >>> euler321OfDcm $ V3 (V3 0 1 0) (V3 (-1) 0 0) (V3 0 0 1)+-- Euler {eYaw = 1.5707963267948966, ePitch = -0.0, eRoll = 0.0}+--+-- >>> let s = sqrt(2)/2 in euler321OfDcm $ V3 (V3 s s 0) (V3 (-s) s 0) (V3 0 0 1)+-- Euler {eYaw = 0.7853981633974483, ePitch = -0.0, eRoll = 0.0}+--+euler321OfDcm :: RealFloat a => M33 a -> Euler a+euler321OfDcm+  (V3+   (V3 r11 r12 r13)+   (V3   _   _ r23)+   (V3   _   _ r33)) = Euler yaw pitch roll   where-    r11 = r @@> (0,0)-    r12 = r @@> (0,1)-    mr13' = -(r @@> (0,2))+    mr13' = -r13     mr13 -- nan protect       | mr13' >  1 =  1       | mr13' < -1 = -1       | otherwise = mr13'-    r23 = r @@> (1,2)-    r33 = r @@> (2,2)-  +     yaw   = atan2 r12 r11     pitch = asin mr13     roll  = atan2 r23 r33 -quatOfEuler321 :: (Floating a, Ord a) => Euler a -> Quat a-quatOfEuler321 (Euler yaw pitch roll) = Quat.normalize q+-- | Convert Euler angles to quaternion+--+-- >>> quatOfEuler321 (Euler 0 0 0)+-- Quaternion 1.0 (V3 0.0 0.0 0.0)+--+-- >>> quatOfEuler321 (Euler (pi/2) 0 0)+-- Quaternion 0.7071067811865476 (V3 0.0 0.0 0.7071067811865475)+--+-- >>> quatOfEuler321 (Euler 0 (pi/2) 0)+-- Quaternion 0.7071067811865476 (V3 0.0 0.7071067811865475 0.0)+--+-- >>> quatOfEuler321 (Euler 0 0 (pi/2))+-- Quaternion 0.7071067811865476 (V3 0.7071067811865475 0.0 0.0)+--+quatOfEuler321 :: (Floating a, Ord a) => Euler a -> Quaternion a+quatOfEuler321 (Euler yaw pitch roll) = normalize' q   where     sr2 = sin $ 0.5*roll     cr2 = cos $ 0.5*roll@@ -127,57 +211,78 @@     q2 = cr2*sp2*cy2 + sr2*cp2*sy2     q3 = cr2*cp2*sy2 - sr2*sp2*cy2 -    q' = Quat q0 q1 q2 q3-    +    q' = Quaternion q0 (V3 q1 q2 q3)+     q-      | q0 < 0 = negate q'+      | q0 < 0 = Quaternion (-q0) (V3 (-q1) (-q2) (-q3))       | otherwise = q' -dcmOfQuat :: (Num a, Element a) => Quat a -> Matrix a-dcmOfQuat (Quat q0 q1 q2 q3) = fromLists [ [r0, r1, r2]-                                         , [r3, r4, r5]-                                         , [r6, r7, r8]-                                         ]+-- | convert a quaternion to a DCM+--+-- >>> dcmOfQuat $ Quaternion 1.0 (V3 0.0 0.0 0.0)+-- V3 (V3 1.0 0.0 0.0) (V3 0.0 1.0 0.0) (V3 0.0 0.0 1.0)+--+-- >>> let s = sqrt(2)/2 in dcmOfQuat $ Quaternion s (V3 0.0 0.0 s)+-- V3 (V3 0.0 1.0000000000000002 0.0) (V3 (-1.0000000000000002) 0.0 0.0) (V3 0.0 0.0 1.0000000000000002)+--+-- >>> dcmOfQuat $ Quaternion 0.9238795325112867 (V3 0.0 0.0 0.3826834323650898)+-- V3 (V3 0.7071067811865475 0.7071067811865476 0.0) (V3 (-0.7071067811865476) 0.7071067811865475 0.0) (V3 0.0 0.0 1.0)+--+dcmOfQuat :: Num a => Quaternion a -> M33 a+dcmOfQuat (Quaternion q0 (V3 q1 q2 q3)) = V3 (V3 r0 r1 r2)+                                             (V3 r3 r4 r5)+                                             (V3 r6 r7 r8)   where     -- 1st column     r0 = q0*q0 + q1*q1 - q2*q2 - q3*q3     r3 = 2*(q1*q2 - q0*q3)     r6 = 2*(q1*q3 + q0*q2)-  +     -- 2nd column     r1 = 2*(q1*q2 + q0*q3)     r4 = q0*q0 - q1*q1 + q2*q2 - q3*q3     r7 = 2*(q2*q3 - q0*q1)-  +     -- 3rd column     r2 = 2*(q1*q3 - q0*q2)     r5 = 2*(q2*q3 + q0*q1)     r8 = q0*q0 - q1*q1 - q2*q2 + q3*q3 -dcmOfEuler321 :: (Floating a, Element a, Ord a) => Euler a -> Matrix a+-- | Convert DCM to euler angles+--+-- >>> dcmOfEuler321 $ Euler {eYaw = 0.0, ePitch = 0, eRoll = 0}+-- V3 (V3 1.0 0.0 0.0) (V3 0.0 1.0 0.0) (V3 0.0 0.0 1.0)+--+-- >>> dcmOfEuler321 $ Euler {eYaw = pi/2, ePitch = 0, eRoll = 0}+-- V3 (V3 2.220446049250313e-16 1.0 0.0) (V3 (-1.0) 2.220446049250313e-16 0.0) (V3 0.0 0.0 1.0)+--+-- >>> dcmOfEuler321 $ Euler {eYaw = pi/4, ePitch = 0, eRoll = 0}+-- V3 (V3 0.7071067811865475 0.7071067811865476 0.0) (V3 (-0.7071067811865476) 0.7071067811865475 0.0) (V3 0.0 0.0 1.0)+--+dcmOfEuler321 :: (Floating a, Ord a) => Euler a -> M33 a dcmOfEuler321 = dcmOfQuat . quatOfEuler321 -dcmOfQuatB2A :: (Num a, Element a) => Quat a -> Matrix a-dcmOfQuatB2A = dcmOfQuat . Quat.inv+dcmOfQuatB2A :: (Conjugate a, RealFloat a) => Quaternion a -> M33 a+dcmOfQuatB2A = dcmOfQuat . conjugate  -- | vec_b = R_a2b * vec_a-rotVecByDcm :: (Num a, Storable a) => Matrix a -> Xyz a -> Xyz a-rotVecByDcm dcm vec = Xyz.mult3x3ByXyz dcm vec+rotVecByDcm :: Num a => M33 a -> V3 a -> V3 a+rotVecByDcm dcm vec = dcm !* vec  -- | vec_a = R_a2b^T * vec_b-rotVecByDcmB2A :: (Num a, Storable a) => Matrix a -> Xyz a -> Xyz a-rotVecByDcmB2A dcm vec = Xyz.mult3x3TransposeByXyz dcm vec+rotVecByDcmB2A :: Num a => M33 a -> V3 a -> V3 a+rotVecByDcmB2A dcm vec = vec *! dcm  -- | vec_b = q_a2b * vec_a * q_a2b^(-1) --   vec_b = R(q_a2b) * vec_a-rotVecByQuat :: (Num a, Element a) => Quat a -> Xyz a -> Xyz a+rotVecByQuat :: Num a => Quaternion a -> V3 a -> V3 a rotVecByQuat q = rotVecByDcm (dcmOfQuat q) -rotVecByQuatB2A :: (Num a, Element a) => Quat a -> Xyz a -> Xyz a+rotVecByQuatB2A :: Num a => Quaternion a -> V3 a -> V3 a rotVecByQuatB2A q = rotVecByDcmB2A (dcmOfQuat q) -rotVecByEuler :: (Floating a, Element a, Ord a) => Euler a -> Xyz a -> Xyz a+rotVecByEuler :: (Floating a, Ord a) => Euler a -> V3 a -> V3 a rotVecByEuler = rotVecByDcm . dcmOfEuler321 -rotVecByEulerB2A :: (Floating a, Element a, Ord a) => Euler a -> Xyz a -> Xyz a+rotVecByEulerB2A :: (Floating a, Ord a) => Euler a -> V3 a -> V3 a rotVecByEulerB2A = rotVecByDcmB2A . dcmOfEuler321
− Xyz.hs
@@ -1,128 +0,0 @@-{-# OPTIONS_GHC -Wall #-}-{-# Language StandaloneDeriving #-}-{-# Language DeriveDataTypeable #-}--module Xyz ( Xyz(..)-           , zipWithXyz-           , cross-           , dot-           , normSquared-           , norm-           , distance-           , scale-           , normalizeTo-           , normalize-           , mult3x3ByXyz-           , mult3x3TransposeByXyz-           ) where--import Numeric.LinearAlgebra ( (@@>), Matrix )-import Foreign.Storable ( Storable )-import Data.Data ( Data )-import Data.Typeable ( Typeable1 )-import System.Random ( Random(..) )--data Xyz a = Xyz a a a deriving (Show, Eq)--deriving instance Typeable1 Xyz-deriving instance Data a => Data (Xyz a)--instance Functor Xyz where-  fmap f (Xyz x y z) = Xyz (f x) (f y) (f z)--instance Random a => Random (Xyz a) where-  random g0 = (Xyz x y z, gz)-    where-      (x,gx) = random g0-      (y,gy) = random gx-      (z,gz) = random gy-  randomR (Xyz x0 y0 z0, Xyz x1 y1 z1) g0 = (Xyz x y z, gz)-    where-      (x,gx) = randomR (x0,x1) g0-      (y,gy) = randomR (y0,y1) gx-      (z,gz) = randomR (z0,z1) gy--zipWithXyz :: (a -> b -> c) -> Xyz a -> Xyz b -> Xyz c-zipWithXyz f (Xyz x0 y0 z0) (Xyz x1 y1 z1) = Xyz (f x0 x1) (f y0 y1) (f z0 z1)--instance (Num a) => Num (Xyz a) where-  (+) = zipWithXyz (+)-  (-) = zipWithXyz (-)-  negate = fmap negate-  (*) = zipWithXyz (*)-  abs = fmap abs-  signum = fmap signum-  fromInteger k = fmap fromInteger (Xyz k k k)--instance (Fractional a) => Fractional (Xyz a) where-  fromRational r = fmap fromRational (Xyz r r r)-  (/) = zipWithXyz (/)--instance (Floating a) => Floating (Xyz a) where-  pi = Xyz pi pi pi-  exp   = fmap exp-  log   = fmap log-  sin   = fmap sin-  cos   = fmap cos-  tan   = fmap tan-  asin  = fmap asin-  acos  = fmap acos-  atan  = fmap atan-  sinh  = fmap sinh-  cosh  = fmap cosh-  tanh  = fmap tanh-  asinh = fmap asinh-  acosh = fmap acosh-  atanh = fmap atanh---- | c = a (cross) b-cross :: Num a => Xyz a -> Xyz a -> Xyz a-cross (Xyz ax ay az) (Xyz bx by bz) = Xyz cx cy cz-  where-    cx =   ay*bz - az*by-    cy = - ax*bz + az*bx-    cz =   ax*by - ay*bx---- | c = a (dot) b-dot :: Num a => Xyz a -> Xyz a -> a-dot (Xyz ax ay az) (Xyz bx by bz) = ax*bx + ay*by + az*bz;---- | c = vec (dot) vec-normSquared :: Num a => Xyz a -> a-normSquared x = dot x x---- | norm(x)-norm :: Floating a => Xyz a -> a-norm x = sqrt $ dot x x---- | norm(a - b)-distance :: Floating a => Xyz a -> Xyz a -> a-distance a b = norm $ a - b---- | vec_out = vec_in*scale_factor-scale :: Num a => a -> Xyz a -> Xyz a-scale k = fmap (k *)---- | vec_out = scale (new_norm/norm(vec_in)) vec_in-normalizeTo :: Floating a => a -> Xyz a -> Xyz a -> Xyz a-normalizeTo newNorm vec = scale (newNorm/(norm(vec) + 1e-12))---- | vec_out = vec_in/norm(vec_in)-normalize :: Floating a => Xyz a -> Xyz a -> Xyz a-normalize = normalizeTo 1---- | v_out = M*v-mult3x3ByXyz :: (Num a, Storable a) => Matrix a -> Xyz a -> Xyz a-mult3x3ByXyz mat (Xyz x y z) = Xyz x' y' z'-  where-    x' = (mat @@> (0,0))*x + (mat @@> (0,1))*y +  (mat @@> (0,2))*z-    y' = (mat @@> (1,0))*x + (mat @@> (1,1))*y +  (mat @@> (1,2))*z-    z' = (mat @@> (2,0))*x + (mat @@> (2,1))*y +  (mat @@> (2,2))*z---- // v_out = M^T*v-mult3x3TransposeByXyz :: (Num a, Storable a) => Matrix a -> Xyz a -> Xyz a-mult3x3TransposeByXyz mat (Xyz x y z) = Xyz x' y' z'-  where-    x' = (mat @@> (0,0))*x + (mat @@> (1,0))*y +  (mat @@> (2,0))*z-    y' = (mat @@> (0,1))*x + (mat @@> (1,1))*y +  (mat @@> (2,1))*z-    z' = (mat @@> (0,2))*x + (mat @@> (1,2))*y +  (mat @@> (2,2))*z
+ changelog.txt view
@@ -0,0 +1,4 @@+0.2.0+- convert to using `linear` V3, M33, Quaternion types+- doctests+- fix long unknown bug: rotateXyzAbout{X,Y,Z} was rotating opposite direction from intended
spatial-math.cabal view
@@ -1,25 +1,34 @@ name:                spatial-math-version:             0.1.7+version:             0.2.0 synopsis:            3d math including quaternions/euler angles/dcms and utility functions-description:         This is a port of my 'mathlib' C library: https://github.com/ghorn/mathlib+description:         This is a port of my 'mathlib' C library: `https://github.com/ghorn/mathlib` license:             BSD3 license-file:        LICENSE author:              Greg Horn maintainer:          gregmainland@gmail.com--- copyright:           +copyright:           Copyright (c) 2012, Greg Horn category:            Math build-type:          Simple-cabal-version:       >=1.8+cabal-version:       >=1.10 +extra-source-files:  README.md+                     changelog.txt+ library-  exposed-modules:     SpatialMath,-                       Quat,-                       Xyz-  -- other-modules:       +  exposed-modules:     SpatialMath   build-depends:       base >= 4 && < 5,-                       hmatrix >= 0.14 && < 0.15,-                       random+                       linear >= 1.3.1+  default-language:    Haskell2010  source-repository head   type:     git   location: git://github.com/ghorn/spatial-math.git++test-suite doctests+  type:                exitcode-stdio-1.0+  main-is:             doctests.hs+  build-depends:       base >= 4 && < 5,+                       doctest >= 0.8+  default-language:    Haskell2010+  ghc-options:         -threaded+  hs-source-dirs:      tests
+ tests/doctests.hs view
@@ -0,0 +1,8 @@+{-# OPTIONS_GHC -Wall #-}++module Main where++import Test.DocTest++main :: IO ()+main = doctest ["SpatialMath.hs"]