module Data.Vec.LinAlg
(dot
,normSq
,norm
,normalize
,cross
,homPoint
,homVec
,project
,multvm
,multmv
,multmm
,translate
,column
,row
,Transpose(transpose)
,SetDiagonal(setDiagonal)
,GetDiagonal(getDiagonal)
,scale
,diagonal
,identity
,det
,cramer'sRule
,NearZero(nearZero)
,GaussElim(gaussElim)
,BackSubstitute(backSubstitute)
,BackSubstitute'(backSubstitute')
,invert
,invertAndDet
,solve
,translation
,rotationX
,rotationY
,rotationZ
,rotationVec
,rotationEuler
,rotationQuat
,rotationLookAt
,scaling
,perspective
,orthogonal
) where
import Prelude hiding (map,zipWith,foldl,foldr,reverse,take,drop,
head,tail,sum,length,last)
import qualified Prelude as P
import Data.Vec.Base
import Data.Vec.Nat
import Control.Monad
import Data.Maybe
dot :: (Num a, Fold v a, ZipWith a a a v v v) => v -> v -> a
dot u v = fold (+) (zipWith (*) u v)
normSq :: (Num a, Num v, Fold v a, ZipWith a a a v v v) => v -> a
normSq v = dot v v
norm :: (Num v, Floating a, Fold v a, ZipWith a a a v v v) => v -> a
norm v = sqrt (dot v v)
normalize :: (Floating a, Num v, Fold v a, Map a a v v, ZipWith a a a v v v) => v -> v
normalize v = map (/ norm v) v
cross :: Num a => Vec3 a -> Vec3 a -> Vec3 a
cross (ux:.uy:.uz:._) (vx:.vy:.vz:._) =
(uy*vzuz*vy):.(uz*vxux*vz):.(ux*vyuy*vx):.()
homPoint :: (Snoc v a v', Num a) => v -> v'
homPoint v = snoc v 1
homVec :: (Snoc v a v', Num a) => v -> v'
homVec v = snoc v 0
project ::
( Reverse' () t1 v'
, Fractional t1
, Vec a t t1
, Reverse' () v (t :. t1)
) => v -> v'
project v = case reverse v of (w:.u) -> reverse (u/vec w)
multvm ::
( Transpose m mt
, Map v a mt v'
, Fold v a
, ZipWith a a a v v v
, Num a
, Num v
) => v -> m -> v'
multvm v m = map (dot v) (transpose m)
multmv ::
( Map v a m v'
, Num v
, Fold v a
, ZipWith a a a v v v
, Num a
) => m -> v -> v'
multmv m v = map (dot v) m
multmm ::
(Map v v' m1 m3
,Map v a b v'
,Transpose m2 b
,Fold v a
,ZipWith a a a v v v
,Num v
,Num a
) => m1 -> m2 -> m3
multmm a b = map (\v -> map (dot v) (transpose b)) a
translate ::
(Transpose m mt
,Reverse' () mt (v' :. t)
,Reverse' (v' :. ()) t v'1
,Transpose v'1 m
,Num v'
,Num a
,Snoc v a v'
) => v -> m -> m
translate v m =
case reverse (transpose m) of
(h:.t) -> transpose (reverse ((homVec v + h) :. t))
column :: (Transpose m mt, Access n v mt) => n -> m -> v
column n = get n . transpose
row :: (Access n a v) => n -> v -> a
row = get
class Transpose a b | a -> b, b -> a where
transpose :: a -> b
instance Transpose () () where
transpose = id
instance
(Vec (Succ n) s (s:.ra)
,Vec (Succ m) (s:.ra) ((s:.ra):.a)
,Vec (Succ m) s (s:.rb)
,Vec (Succ n) (s:.rb) ((s:.rb):.b)
,Transpose' ((s:.ra):.a) ((s:.rb):.b)
)
=> Transpose ((s:.ra):.a) ((s:.rb):.b)
where
transpose = transpose'
class Transpose' a b | a->b
where transpose' :: a -> b
instance Transpose' () () where
transpose' = id
instance
(Transpose' vs vs') => Transpose' ( () :. vs ) vs'
where
transpose' (():.vs) = transpose' vs
instance Transpose' ((x:.()):.()) ((x:.()):.()) where
transpose' = id
instance
(Head xss_h xss_hh
,Map xss_h xss_hh (xss_h:.xss_t) xs'
,Tail xss_h xss_ht
,Map xss_h xss_ht (xss_h:.xss_t) xss_
,Transpose' (xs :. xss_) xss'
)
=> Transpose' ((x:.xs):.(xss_h:.xss_t)) ((x:.xs'):.xss')
where
transpose' ((x:.xs):.xss) =
(x :. map head xss) :. transpose' (xs :. map tail xss :: (xs :. xss_))
class SetDiagonal v m where
setDiagonal :: v -> m -> m
instance (Vec n a v, Vec n r m, SetDiagonal' N0 v m) => SetDiagonal v m where
setDiagonal = setDiagonal' (undefined :: N0)
class SetDiagonal' n v m where
setDiagonal' :: n -> v -> m -> m
instance SetDiagonal' n () m where
setDiagonal' _ _ m = m
instance
( SetDiagonal' (Succ n) v m
, Access n a r
) => SetDiagonal' n (a:.v) (r:.m)
where
setDiagonal' _ (a:.v) (r:.m) =
set (undefined :: n) a r :. setDiagonal' (undefined :: Succ n) v m
class GetDiagonal m v | m -> v, v -> m where
getDiagonal :: m -> v
instance (Vec n a v, Vec n v m, GetDiagonal' N0 () m v) => GetDiagonal m v where
getDiagonal = getDiagonal' (undefined :: N0) ()
class GetDiagonal' n p m v where
getDiagonal' :: n -> p -> m -> v
instance
(Access n a r
,Append p (a:.()) (a:.p)
) => GetDiagonal' n p (r:.()) (a:.p)
where
getDiagonal' _ p (r:.()) = append p (get (undefined :: n) r :. ())
instance
(Access n a r
,Append p (a:.()) p'
,GetDiagonal' (Succ n) p' (r:.m) v
)
=> GetDiagonal' n p (r:.r:.m) v
where
getDiagonal' _ p (r:.m) =
getDiagonal' (undefined::Succ n) (append p (get (undefined :: n) r :. ())) m
scale ::
( GetDiagonal' N0 () m r
, Num r
, Vec n a r
, Vec n r m
, SetDiagonal' N0 r m
) => r -> m -> m
scale s m = setDiagonal (s * getDiagonal m) m
diagonal :: (Vec n a v, Vec n v m, SetDiagonal v m, Num m) => v -> m
diagonal v = setDiagonal v 0
identity :: (Vec n a v, Vec n v m, Num v, Num m, SetDiagonal v m) => m
identity = diagonal 1
det :: forall n a r m. (Vec n a r, Vec n r m, Det' m a) => m -> a
det = det'
class Det' m a | m -> a where
det' :: m -> a
instance Det' ((a:.()):.()) a where
det' ((a:._):._) = a
instance
( (a:.a:.v) ~ r
, ((a:.a:.v):.(a:.a:.v):.vs) ~ m
, ((a:.v):.(a:.v):.vs_) ~ m_
, (((a:.v):.vs_):.(x:.y)) ~ mm
, Map (a:.a:.v) (a:.v) m m_
, DropConsec m_ mm
, Det' ((a:.v):.vs_) a
, Map ((a:.v):.vs_) a mm r
, Map r a m r
, NegateOdds r
, Fold r a
, Num r
, Num a
) => Det' ((a:.a:.v):.(a:.a:.v):.vs) a
where
det' m =
sum $ negateOdds (map head m) * map det' (dropConsec $ map tail m)
class DropConsec v vv | v -> vv where
dropConsec :: v -> vv
instance
(Vec n a v
,Pred n n_
,Vec n_ a v_
,Vec n v_ vv
,DropConsec' () v vv
) => DropConsec v vv
where
dropConsec = dropConsec' ()
class DropConsec' p v vv where
dropConsec' :: p -> v -> vv
instance DropConsec' p (a:.()) (p:.()) where
dropConsec' p (a:.()) = p :. ()
instance
(Append p (a:.v) x
,Append p (a:.()) y
,DropConsec' y (a:.v) z
)
=> DropConsec' p (a:.a:.v) (x:.z)
where
dropConsec' p (a:.v) =
append p v :. dropConsec' (append p (a :. ())) v
class NegateOdds v where
negateOdds :: v -> v
class NegateEvens v where
negateEvens :: v -> v
instance NegateOdds () where
negateOdds () = ()
instance NegateEvens () where
negateEvens () = ()
instance (Num a, NegateEvens v) => NegateOdds (a:.v) where
negateOdds (a:.v) = a :. negateEvens v
instance (Num a, NegateOdds v) => NegateEvens (a:.v) where
negateEvens (a:.v) = negate a :. negateOdds v
class ReplConsec a v vv | v->a, v->vv, vv->v, vv->a where
replConsec :: a -> v -> vv
instance
(Vec n a v
,Vec n v vv
,ReplConsec' a () v vv
) => ReplConsec a v vv
where
replConsec a v = replConsec' a () v :: vv
class ReplConsec' a p v vv where
replConsec' :: a -> p -> v -> vv
instance ReplConsec' a p () () where
replConsec' _ _ () = ()
instance
(Append p (a:.v) x
,Append p (a:.()) y
,ReplConsec' a y v z
)
=> ReplConsec' a p (a:.v) (x:.z)
where
replConsec' r p (a:.v) =
append p (r :. v) :. replConsec' r (append p (a :. ())) v
cramer'sRule ::
(Map a a1 b1 v
,Transpose w b1
,ZipWith a2 b vv v m w
,ReplConsec' a2 () b vv
,Vec n b vv
,Vec n a2 b
,Fractional a1
,Det' m a1
,Det' a a1
) => m -> v -> v
cramer'sRule m b =
case map (\m' -> det' m' / det' m)
(transpose (zipWith replConsec b m))
of b' -> b' `asTypeOf` b
mapFst f (a,b) = (f a,b)
class Num a => NearZero a where
nearZero :: a -> Bool
instance NearZero Float where
nearZero x = abs x < 1e-6
instance NearZero Double where
nearZero x = abs x < 1e-14
instance NearZero Rational where
nearZero 0 = True
nearZero _ = False
class Pivot1 a m where
pivot1 :: m -> Maybe (m,a)
instance Pivot1 a () where
pivot1 _ = Nothing
instance
( Fractional a, NearZero a
) => Pivot1 a ((a:.()):.())
where
pivot1 ((p:._):._)
| nearZero p = Nothing
| otherwise = Just (1,p)
instance
( Fractional a, NearZero a
, Map a a (a:.r) (a:.r)
) => Pivot1 a ((a:.(a:.r)):.())
where
pivot1 ((p:.r):._)
| nearZero p = Nothing
| otherwise = Just ((1 :. map (/ p) r):.(), p)
instance
( Fractional a, NearZero a
, Map a a (a:.r) (a:.r)
, ZipWith a a a (a:.r) (a:.r) (a:.r)
, Map (a:.r) (a:.r) ((a:.r):.rs) ((a:.r):.rs)
, Pivot1 a ((a:.r):.rs)
) => Pivot1 a ((a:.r):.(a:.r):.rs)
where
pivot1 (row@(p:._):.rows)
| nearZero p = pivot1 rows >>= \(r:.rs,p)-> Just(r:.row:.rs,p)
| otherwise = Just (first :. map add rows , p)
where first = map (/p) row
add r@(x:._) = zipWith () r . map (*x) $ first
class Pivot a m where
pivot :: m -> Maybe (m,a)
instance Pivot a (():.v) where
pivot m = Nothing
instance
( Fractional a
, NearZero a
, Pivot1 a rs
, Tail (a:.r) r
, Map (a:.r) r ((a:.r):.rs) (r:.rs')
, Map r (a:.r) (r:.rs') ((a:.r):.rs)
, Pivot1 a ((a:.r):.rs)
, Pivot a (r:.rs')
) => Pivot a ((a:.r):.rs)
where
pivot m =
mplus (pivot1 m)
(liftM (mapFst (map (0 :.))) (pivot (map tail m)) )
class GaussElim a m | m -> a where
gaussElim :: m -> (m,a)
instance (Num a, Pivot a ((a :. r):.())) => GaussElim a ((a :. r):.())
where
gaussElim m = fromMaybe (m,1) (pivot m)
instance
( Fractional a
, Map (a:.r) r ((a:.r):.rs) rs_
, Map r (a:.r) rs_ ((a:.r):.rs)
, Pivot a ((a:.r):.(a:.r):.rs)
, GaussElim a rs_
) => GaussElim a ((a:.r):.(a:.r):.rs)
where
gaussElim m =
flip (maybe (m,1)) (pivot m) $ \(row:.rows,p) ->
case gaussElim (map tail rows)
of (rows',p') -> ( row :. map (0 :.) rows' , p*p')
class BackSubstitute m where
backSubstitute :: m -> Maybe m
instance NearZero a => BackSubstitute ((a:.r):.()) where
backSubstitute r@((a:._):._)
| nearZero (1a) = Just r
| otherwise = Nothing
instance
( Map (a:.r) r ((a:.r):.rs) rs_
, Map r (a:.r) rs_ ((a:.r):.rs)
, Fold aas (a,a:.r)
, ZipWith a a a (a:.r) (a:.r) (a:.r)
, Map a a (a:.r) (a:.r)
, ZipWith a (a:.r) (a,a:.r) r ((a:.r):.rs) aas
, Num a, NearZero a
, BackSubstitute rs_
) => BackSubstitute ((a:.r):.(a:.r):.rs)
where
backSubstitute m@(r@(rh:.rt):.rs)
| nearZero (1rh) =
liftM (map (0:.)) (backSubstitute . map tail $ rs) >>= \rs' ->
return . (:. rs') . foldl (\ v (a, w) -> sub v a w) r $ zipWith (,) rt rs'
| otherwise = Nothing
where sub v a = zipWith () v . map (*a)
class BackSubstitute' m where
backSubstitute' :: m -> m
instance BackSubstitute' ((a:.r):.()) where
backSubstitute' = id
instance
( Map (a:.r) r ((a:.r):.rs) rs_
, Map r (a:.r) rs_ ((a:.r):.rs)
, Fold aas (a,a:.r)
, ZipWith a a a (a:.r) (a:.r) (a:.r)
, Map a a (a:.r) (a:.r)
, ZipWith a (a:.r) (a,a:.r) r ((a:.r):.rs) aas
, Num a
, BackSubstitute' rs_
) => BackSubstitute' ((a:.r):.(a:.r):.rs)
where
backSubstitute' (r@(_:.rt):.rs) =
case map (0:.) (backSubstitute' . map tail $ rs)
of rs' -> (:.rs') $ foldl (\ v (a,w) -> sub v a w) r (zipWith (,) rt rs')
where sub v a = zipWith () v . map (*a)
invert :: forall n a r m r' m'.
( Num r, Num m
, Vec n a r
, Vec n r m
, Append r r r'
, ZipWith r r r' m m m'
, Drop n r' r
, Map r' r m' m
, SetDiagonal r m
, GaussElim a m'
, BackSubstitute m'
) => m -> Maybe m
invert m =
liftM (map dropn)
((backSubstitute . fst . gaussElim . zipWith append m) i)
where dropn = drop (undefined::n)
i = identity :: m
invertAndDet :: forall n a r m r' m'.
( Num a, Num r, Num m
, Vec n a r
, Vec n r m
, Append r r r'
, ZipWith r r r' m m m'
, Drop n r' r
, Map r' r m' m
, SetDiagonal r m
, GaussElim a m'
, BackSubstitute m'
) => m -> (m,a)
invertAndDet m =
case backSubstitute rref of
Nothing -> (m,0)
Just m' -> ( map dropn m' , d )
where
(rref,d) = gaussElim . zipWith append m $ i
dropn = drop (undefined::n)
i = identity :: m
solve :: forall n a v r m r' m'.
( Num r, Num m
, Vec n a r
, Vec n r m
, Snoc r a r'
, ZipWith r a r' m r m'
, Drop n r' (a:.())
, Map r' a m' r
, GaussElim a m'
, BackSubstitute m'
) => m -> r -> Maybe r
solve m v =
liftM (map (head . drop (undefined :: n)))
((backSubstitute . fst . gaussElim . zipWith snoc m) v)
translation :: Num a => Vec3 a -> Mat44 a
translation = flip translate identity
rotationX :: Floating a
=> a
-> Mat44 a
rotationX a = matFromList [1, 0, 0, 0,
0, cos a, sin a, 0,
0, sin a, cos a, 0,
0, 0, 0, 1]
rotationY :: Floating a
=> a
-> Mat44 a
rotationY a = matFromList [cos a, 0, sin a, 0,
0, 1, 0, 0,
sin a, 0, cos a, 0,
0, 0, 0, 1]
rotationZ :: Floating a
=> a
-> Mat44 a
rotationZ a = matFromList [cos a, sin a, 0, 0,
sin a, cos a, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1]
rotationVec :: Floating a
=> Vec3 a
-> a
-> Mat44 a
rotationVec (x:.y:.z:.()) a =
matFromList [x^2+(1x^2)*c, x*y*(1c)z*s, x*z*(1c)+y*s, 0,
x*y*(1c)+z*s, y^2+(1y^2)*c, y*z*(1c)x*s, 0,
x*z*(1c)y*s, y*z*(1c)+x*s, z^2+(1z^2)*c, 0,
0, 0, 0, 1]
where c = cos a
s = sin a
rotationEuler :: Floating a
=> Vec3 a
-> Mat44 a
rotationEuler (x:.y:.z:.()) = rotationZ z `multmm` rotationY y `multmm` rotationX x
rotationQuat :: Num a
=> Vec4 a
-> Mat44 a
rotationQuat (x:.y:.z:.w:.()) =
matFromList [12*y^22*z^2, 2*(x*yz*w), 2*(x*z+y*w), 0,
2*(x*y+z*w), 12*x^22*z^2, 2*(y*zx*w), 0,
2*(x*zy*w), 2*(x*w+y*z), 12*x^22*y^2, 0,
0, 0, 0, 1]
rotationLookAt :: Floating a
=> Vec3 a
-> Vec3 a
-> Vec3 a
-> Mat44 a
rotationLookAt up' pos target = transpose $ homVec left :. homVec up :. homVec forward :. homPoint 0 :. ()
where
forward = normalize $ pos target
left = normalize $ up' `cross` forward
up = forward `cross`left
scaling :: Num a => Vec3 a -> Mat44 a
scaling = diagonal . homPoint
perspective :: Floating a
=> a
-> a
-> a
-> a
-> Mat44 a
perspective n f fovy aspect = matFromList [2*n/(rl), 0, (r+l)/(rl), 0,
0, 2*n/(tb), (t+b)/(tb), 0,
0, 0, (f+n)/(fn), 2*f*n/(fn),
0,0,1,0]
where
t = n*tan(fovy/2)
b = t
r = aspect*t
l = r
orthogonal :: Fractional a
=> a
-> a
-> Vec2 a
-> Mat44 a
orthogonal n f (w:.h:.()) = matFromList [2/w, 0, 0, 0,
0, 2/h, 0, 0,
0, 0, 2/(fn), (f+n)/(fn),
0, 0, 0, 1]