\section{RSAGL.Matrix} \begin{code} module RSAGL.Math.Matrix (Matrix, matrix, transformHomogenous, rowMajorForm, colMajorForm, rowAt, matrixAt, identity_matrix, translationMatrix, rotationMatrix, scaleMatrix, xyzMatrix, matrixAdd, matrixMultiply, matrixTranspose, matrixInverse, determinant, matrixInversePrim, matrixTransposePrim, matrixInverseTransposePrim, determinantPrim) where import Data.List as List import RSAGL.Math.Angle import RSAGL.Math.Vector import Data.Vec as Vec import RSAGL.Types \end{code} A 4-by-4 matrix with cached inverse, transpose, inverse transpose, and determinant. \begin{code} data Matrix = Matrix { matrix_data :: !(Mat44 RSdouble), matrix_inverse :: Matrix, matrix_transpose :: Matrix, matrix_determinant :: RSdouble } instance Eq Matrix where x == y = matrix_data x == matrix_data y instance Show Matrix where show m = show $ rowMajorForm m rowMajorForm :: Matrix -> [[RSdouble]] rowMajorForm = matToLists . matrix_data colMajorForm :: Matrix -> [[RSdouble]] colMajorForm = List.transpose . rowMajorForm \end{code} rowAt answers the nth row of a matrix. \begin{code} rowAt :: Matrix -> Int -> [RSdouble] rowAt m n = (rowMajorForm m) !! n \end{code} matrixAt answers the (i'th,j'th) element of a matrix. \begin{code} matrixAt :: Matrix -> (Int,Int) -> RSdouble matrixAt m (i,j) = rowAt m i !! j \end{code} \subsection{Constructing matrices} matrix constructs a matrix from row major list form. (Such a list form can be formatted correctly in monospaced font and haskell syntax, so that it looks like a matrix as it would be normally written.) \begin{code} matrix :: [[RSdouble]] -> Matrix matrix = uncheckedMatrix . matFromLists -- | Generate a column matrix of length 4, perform an affine transformation on it, and produce the resulting value. {-# INLINE transformHomogenous #-} transformHomogenous :: RSdouble -> RSdouble -> RSdouble -> RSdouble -> (RSdouble -> RSdouble -> RSdouble -> a) -> Matrix -> a transformHomogenous x y z w f m = f x' y' z' where (x':.y':.z':._) = multmv (matrix_data m) (x:.y:.z:.w:.()) uncheckedMatrix :: Mat44 RSdouble -> Matrix uncheckedMatrix dats = m where m_inverse = (matrixInversePrim m) { matrix_inverse = m, matrix_transpose = m_inverse_transpose, matrix_determinant = recip m_det } m_transpose = (matrixTransposePrim m) { matrix_inverse = m_inverse_transpose, matrix_transpose = m, matrix_determinant = m_det } m_inverse_transpose = (matrixTransposePrim m_inverse) { matrix_inverse = m_transpose, matrix_transpose = m_inverse, matrix_determinant = recip m_det } m_det = determinantPrim m m = Matrix { matrix_data=dats, matrix_inverse = m_inverse, matrix_transpose = m_transpose, matrix_determinant = m_det } \end{code} identityMatrix constructs the n by n identity matrix \begin{code} identity_matrix :: Matrix identity_matrix = Matrix { matrix_data = identity, matrix_inverse = identity_matrix, matrix_transpose = identity_matrix, matrix_determinant = 1 } \end{code} \begin{code} translationMatrix :: Vector3D -> Matrix translationMatrix (Vector3D x y z) = uncheckedMatrix $ (1:.0:.0:.x:.()) :. (0:.1:.0:.y:.()) :. (0:.0:.1:.z:.()) :. (0:.0:.0:.1:.()) :. () \end{code} \begin{code} rotationMatrix :: Vector3D -> Angle -> Matrix rotationMatrix vector angle = let s = sine angle c = cosine angle c' = 1 - c (Vector3D x y z) = vectorNormalize vector in uncheckedMatrix $ ((c+c'*x*x) :. (c'*y*x - s*z) :. (c'*z*x + s*y) :. 0 :. ()) :. ((c'*x*y+s*z) :. (c+c'*y*y) :. (c'*z*y - s*x) :. 0 :. ()) :. ((c'*x*z-s*y) :. (c'*y*z+s*x) :. (c+c'*z*z) :. 0 :. ()) :. (0 :. 0 :. 0 :. 1 :. ()) :. () \end{code} \begin{code} scaleMatrix :: Vector3D -> Matrix scaleMatrix (Vector3D x y z) = uncheckedMatrix $ (x :. 0 :. 0 :. 0) :. (0 :. y :. 0 :. 0) :. (0 :. 0 :. z :. 0) :. (0 :. 0 :. 0 :. 1) :. () \end{code} \texttt{xyzMatrix} constructs the matrix in which the x y and z axis are transformed to point in the direction of the specified vectors. \begin{code} xyzMatrix :: Vector3D -> Vector3D -> Vector3D -> Matrix xyzMatrix (Vector3D x1 y1 z1) (Vector3D x2 y2 z2) (Vector3D x3 y3 z3) = uncheckedMatrix $ (x1 :. x2 :. x3 :. 0) :. (y1 :. y2 :. y3 :. 0) :. (z1 :. z2 :. z3 :. 0) :. (0 :. 0 :. 0 :. 1.0) :. () \end{code} \subsection{Matrix arithmetic} \begin{code} matrixAdd :: Matrix -> Matrix -> Matrix matrixAdd m n = uncheckedMatrix $ Vec.zipWith (+) (matrix_data m) (matrix_data n) \end{code} \label{howtoUseMatrixMultiplyImpl} \begin{code} matrixMultiply :: Matrix -> Matrix -> Matrix matrixMultiply m n = uncheckedMatrix $ multmm (matrix_data m) (matrix_data n) matrixTranspose :: Matrix -> Matrix matrixTranspose = matrix_transpose \end{code} \subsection{Inverse and determinant of a matrix} \begin{code} matrixInverse :: Matrix -> Matrix matrixInverse = matrix_inverse \end{code} \begin{code} determinant :: Matrix -> RSdouble determinant = matrix_determinant \end{code} \begin{code} matrixInverseTransposePrim :: Matrix -> Matrix matrixInverseTransposePrim = uncheckedMatrix . Vec.transpose . fst . invertAndDet . matrix_data matrixInversePrim :: Matrix -> Matrix matrixInversePrim = uncheckedMatrix . fst . invertAndDet . matrix_data determinantPrim :: Matrix -> RSdouble determinantPrim = det . matrix_data matrixTransposePrim :: Matrix -> Matrix matrixTransposePrim = uncheckedMatrix . Vec.transpose . matrix_data \end{code}