{-# LANGUAGE TypeFamilies #-} module Data.Vec3.Class ( Vec3(..) ) where import Prelude hiding (zipWith) -- | Three-dimensional vector, with an associated matrix type. class Vec3 v where -- | Associated type for 3×3 matrix. data Matrix v -- | Origin point @(0, 0, 0)@. origin :: v origin = fromXYZ (0, 0, 0) {-# INLINE origin #-} -- | Construct a new vector from components. fromXYZ :: (Double, Double, Double) -> v -- | Deconstruct a vector into components. toXYZ :: v -> (Double, Double, Double) -- | Zip two vectors elementwise. zipWith :: (Double -> Double -> Double) -> v -> v -> v zipWith f v1 v2 = fromXYZ (f x1 x2, f y1 y2, f z1 z2) where (x1, y1, z1) = toXYZ v1 (x2, y2, z2) = toXYZ v2 {-# INLINE zipWith #-} -- | Add two vectors. (<+>) :: v -> v -> v (<+>) v1 v2 = zipWith (+) v1 v2 {-# INLINE (<+>) #-} -- | Subtract two vectors. (<->) :: v -> v -> v (<->) v1 v2 = zipWith (-) v1 v2 {-# INLINE (<->) #-} -- | Cross product. (><) :: v -> v -> v (><) v1 v2 = fromXYZ (y1 * z2 - y2 * z1, x2 * z1 - x1 * z2, x1 * y2 - x2 * y1) where (x1, y1, z1) = toXYZ v1 (x2, y2, z2) = toXYZ v2 -- | Scale a vector. (.^) :: v -> Double -> v (.^) v s = fromXYZ (x * s, y * s, z * s) where (x, y, z) = toXYZ v -- | Dot product. (.*) :: v -> v -> Double (.*) v1 v2 = x + y + z where (x, y, z) = toXYZ $ zipWith (*) v1 v2 -- | Euclidean norm of a vector. norm :: v -> Double norm v = sqrt (v .* v) {-# INLINE norm #-} -- | Produce unit vector with the same direction as the original -- one. normalize :: v -> v normalize v = v .^ (1 / norm v) {-# INLINE normalize #-} -- | Distance between two points. distance :: v -> v -> Double distance v1 v2 = norm (v1 <-> v2) {-# INLINE distance #-} -- | Invert the direction of a vector. invert :: v -> v invert v = origin <-> v {-# INLINE invert #-} -- | Construct a new matrix from rows. fromRows :: (v, v, v) -> Matrix v -- | Deconstruct a matrix into rows. toRows :: Matrix v -> (v, v, v) -- | Generic vector dot product. -- -- Multiply the transpose of the first vector by the given matrix, -- then multiply the result by the second vector. -- -- @ -- [ a11 a12 a13 ] [ v2x ] -- [ ] [ ] -- [ v1x v1y v1z ] . [ a21 a22 a23 ] . [ v2y ] = s -- [ ] [ ] -- [ a31 a32 a33 ] [ v2z ] -- @ dotM :: v -> v -> Matrix v -> Double dotM v1 v2 m = v1 .* (m `mxv` v2) {-# INLINE dotM #-} -- | Multiply a matrix and a vector. -- -- @ -- [ a11 a12 a13 ] [ v2x ] [ rx ] -- [ ] [ ] [ ] -- [ a21 a22 a23 ] . [ v2y ] = [ ry ] -- [ ] [ ] [ ] -- [ a31 a32 a33 ] [ v2z ] [ rz ] -- @ mxv :: Matrix v -> v -> v mxv m v = fromXYZ (r1 .* v, r2 .* v, r3 .* v) where (r1, r2, r3) = toRows m {-# INLINE mxv #-} -- | Build a diagonal matrix from a number @d@. -- -- @ -- [ d 0 0 ] -- [ ] -- [ 0 d 0 ] -- [ ] -- [ 0 0 d ] -- @ diag :: Double -> Matrix v diag d = fromRows (fromXYZ (d, 0, 0), fromXYZ (0, d, 0), fromXYZ (0, 0, d)) {-# INLINE diag #-} -- | Transpose a vector and multiply it by another vector, -- producing a matrix. -- -- @ -- [ v1x ] [ r11 r12 r13 ] -- [ ] [ ] -- [ v1y ] . [ v2x v2y v2z ] = [ r21 r22 r23 ] -- [ ] [ ] -- [ v1z ] [ r31 r32 r33 ] -- @ vxv :: v -> v -> Matrix v vxv v1 v2 = fromRows (v2 .^ v11, v2 .^ v12, v2 .^ v13) where (v11, v12, v13) = toXYZ v1 {-# INLINE vxv #-} -- | Add two matrices. addM :: Matrix v -> Matrix v -> Matrix v addM m1 m2 = fromRows (r11 <+> r21, r12 <+> r22, r13 <+> r23) where (r11, r12, r13) = toRows m1 (r21, r22, r23) = toRows m2 {-# INLINE addM #-}