{- | General functions applicable to all vector types. -} module Data.Vector.Class where -- | The type of vector field values. type Scalar = Double {- | All vector types belong to this class. Aside from 'vpack' and 'vunpack', these methods aren't especially useful to end-users; they're used internally by the vector arithmetic implementations. -} class BasicVector v where -- | Apply a function to all vector fields. vmap :: (Scalar -> Scalar) -> (v -> v) -- | Zip two vectors together field-by-field using the supplied function (in the style of @Data.List.zipWith@). vzip :: (Scalar -> Scalar -> Scalar) -> (v -> v -> v) -- | Reduce a vector down to a single value using the supplied binary operator. The ordering in which this happens isn't guaranteed, so the operator should probably be associative and commutative. vfold :: (Scalar -> Scalar -> Scalar) -> (v -> Scalar) -- | Pack a list of values into a vector. Extra values are ignored, too few values yields @Nothing@. vpack :: [Scalar] -> Maybe v -- | Unpack a vector into a list of values. (Always succeeds.) vunpack :: v -> [Scalar] -- | Convert a 'Scalar' to a vector (with all components the same). vpromote :: Scalar -> v {- | Dummy class that enables you to request a vector in a type signature without needing to explicitly list 'Num' or 'Fractional' as well. -} class (BasicVector v, Num v, Fractional v) => Vector v where {- | Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(*)@ operator. The @(*|)@ and @(|*)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part. -} (*|) :: Vector v => Scalar -> v -> v k *| v = vmap (k*) v {- | Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(*)@ operator. The @(*|)@ and @(|*)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part. -} (|*) :: Vector v => v -> Scalar -> v v |* k = vmap (k*) v {- | Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(/)@ operator. The @(/|)@ and @(|/)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part. -} (|/) :: Vector v => v -> Scalar -> v v |/ k = v |* (1/k) {- | Scale a vector (i.e., change its length but not its direction). This operator has the same precedence as the usual @(/)@ operator. The @(/|)@ and @(|/)@ operators are identical, but with their argument flipped. Just remember that the \'@|@\' denotes the scalar part. -} (/|) :: Vector v => Scalar -> v -> v k /| v = (1/k) *| v infixl 7 *| infixl 7 |* infixl 7 /| infixl 7 |/ -- | Take the /dot product/ of two vectors. This is a scalar equal to the cosine of the angle between the two vectors multiplied by the length of each vectors. vdot :: Vector v => v -> v -> Scalar v1 `vdot` v2 = vfold (+) $ vzip (*) v1 v2 -- | Return the length or /magnitude/ of a vector. (Note that this involves a slow square root operation.) vmag :: Vector v => v -> Scalar vmag v = sqrt (v `vdot` v) -- | Normalise a vector. In order words, return a new vector with the same direction, but a length of exactly one. (If the vector's length is zero or very near to zero, the vector is returned unchanged.) vnormalise :: Vector v => v -> v vnormalise v = let m = vmag v in if m < 1e-16 then v else v |* (1/m) {- | Linearly interpolate between two points in space. * @vlinear 0 a b = a@ * @vlinear 1 a b = b@ * @vlinear 0.5 a b@ would give a point exactly half way between @a@ and @b@ in a straight line. -} vlinear :: (Vector v) => Scalar -> v -> v -> v vlinear t a b = (1-t) *| a + t *| b