Module

Data.Quaternion

Package
purescript-quaternions
Repository
hdgarrood/purescript-quaternions

The quaternions are a number system which generalise the complex numbers, in a similar way to how the complex numbers generalise the real numbers. One interesting aspect of the quaternions is that they satisfy all but one of the requirements to be a field; they only fail to be a field because their multiplication is non-commutative.

Just as complex numbers can be thought of as pairs of real numbers, where the first is the "real part" and the second is the "imaginary part", quaternions can be thought of as a pair containing a "real part", which is again simply a real number, and a "vector part", which is a vector in R^3. Addition of quaternions is then easy to define: the sum of two quaternions has a real part equal to the sum of the operands' real parts, and a vector part equal to the sum of the operands' vector parts.

There are four particularly important quaternions, which are known as the "basis elements". The first is 1, which (when considered as a quaternion) has a real part of 1 and a vector part of 0 (i.e. the zero vector). The standard basis vectors in R^3 are commonly written as e_1, e_2, and e_3; the quaternions with a real part of 0 and a vector part of e_1, e_2, and e_3 are called i, j, and k respectively, and form the other three basis elements.

These four elements are called the basis elements because every quaternion can be written as a + bi + cj + dk for a choice of real numbers a, b, c, and d.

Now, we can define multiplication on quaternions just by defining multiplication on the basis elements 1, i, j, and k, for which we use the formula:

i^2 = j^2 = k^2 = ijk = -1

One of the first things we can deduce is that the multiplicative inverse of i is -i (just as with complex numbers), and also that the inverses of j and k are -j and -k respectively.

From here we can calculate products between any two basis elements. For example, to calculate the product jk, we notice that i^2 = ijk and therefore we can cancel i on the left to yield i = jk. As another example, to calculate the product ik, we may start with the equation i = jk and multiply both sides by k, yielding ik = jk^2 = -j. All other products of basis elements may be obtained in a similar fashion.

Knowing the products of basis elements and using the distributive law, we can find the product of any two quaternions:

(a + bi + cj + dk) * (e + fi + gj + hk)
= ae + afi + agj + ahk
  + bei + bf(i^2) + bg(ij) + bh(ik)
  + cej + cf(ji) + cg(j^2) + ch(jk)
  + dek + df(ki) + dg(kj) + dh(k^2)
= ae - bf - cg - dh
  + (af + be + ch - dg) i
  + (ag - bh + ce + df) j
  + (ah + bg - cf + de) k

Note that quaternion multiplication is not commutative; that is, p * q is usually not the same as q * p. For example, ij = k, but ji = -k.

Like the real numbers, however, each quaternion does have a multiplicative inverse, i.e. for each quaternion p there exists a unique quaternion q such that p * q = q * p = 1.

This means that there are two ways of dividing quaternions. If we want to divide a quaternion p by another quaternion q, we can multiply p by the multiplicative inverse of q, which can be written q^-1. Of course, we have two ways of doing this; p * q^-1 and q^-1 * p will not necessarily be the same, so we are really dealing with two different operations here. We call the operation taking p and q to q^-1 * p "left-division", and the alternative operation which yields p * q^-1 "right-division".

The most important application of quaternions in computing is for representing orientations and rotations in 3D space; see the Data.Quaternion.Rotation module for more details of this.

#Quaternion Source

data Quaternion a

A quaternion. The type parameter denotes the underlying type. Note that the underlying type should be a reasonable approximation of the real numbers; if this is not the case, some of the functions may exhibit strange behaviour.

Because multiplication of quaternions is non-commutative, there is no CommutativeRing instance, and consequently no EuclideanRing or Field instance either.

This means, amongst other things, that the (/) operator from Prelude cannot be used with Quaternion values. However, Quaternion does have a DivisionRing instance, so you can use leftDiv and rightDiv from the module Data.DivisionRing from the prelude library instead. These functions are also re-exported from this module for convenience.

Constructors

Instances

#approxEq Source

approxEq :: forall a. Ord a => Ring a => a -> Quaternion a -> Quaternion a -> Boolean

Approximate equality of quaternions, given an epsilon value specifying the maximum amount that any of the four components is allowed to differ by.

#conjugate Source

conjugate :: forall a. Ring a => Quaternion a -> Quaternion a

The conjugate of a quaternion. This operation negates the vector part of the quaternion.

#conjugateBy Source

conjugateBy :: forall a. DivisionRing a => Quaternion a -> Quaternion a -> Quaternion a

The conjugate of a quaternion by another quaternion. Defined as

\p q -> q * p * recip q

#i Source

i :: forall a. Semiring a => Quaternion a

#j Source

j :: forall a. Semiring a => Quaternion a

#k Source

k :: forall a. Semiring a => Quaternion a

#normSquare Source

normSquare :: forall a. Semiring a => Quaternion a -> a

The square of the norm of a quaternion. This is slightly easier to compute than the actual norm, so may be useful in cases where you are worried about performance.

#realPart Source

realPart :: forall a. Quaternion a -> a

The real part of the quaternion, that is, the first component. Defined as

\(Quaternion a _ _ _) -> a

#scalarMul Source

scalarMul :: forall a. Semiring a => a -> Quaternion a -> Quaternion a

Multiplies both the real part and the vector part by the given scalar.

#vectorPart Source

vectorPart :: forall a. Quaternion a -> Vec3 a

The vector part of the quaternion, that is, the second, third, and fourth components, represented as an array with exactly 3 elements. Defined as

\(Quaternion _ x y z) -> vec3 x y z

#versor Source

versor :: Quaternion Number -> Quaternion Number

Scales the given quaternion, returning a quaternion pointing in the same direction of unit norm; multiplying this by the original quaternion's norm will give you back the original quaternion.

Re-exports from Data.DivisionRing

#rightDiv Source

rightDiv :: forall a. DivisionRing a => a -> a -> a

Right division, defined as rightDiv a b = a * recip b. Left and right division are distinct in this module because a DivisionRing is not necessarily commutative.

If the type a is also a EuclideanRing, then this function is equivalent to div from the EuclideanRing class. When working abstractly, div should generally be preferred, unless you know that you need your code to work with noncommutative rings.

#leftDiv Source

leftDiv :: forall a. DivisionRing a => a -> a -> a

Left division, defined as leftDiv a b = recip b * a. Left and right division are distinct in this module because a DivisionRing is not necessarily commutative.

If the type a is also a EuclideanRing, then this function is equivalent to div from the EuclideanRing class. When working abstractly, div should generally be preferred, unless you know that you need your code to work with noncommutative rings.