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.

### #QuaternionSource

``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

• `Quaternion a a a a`

#### Instances

• `(Eq a) => Eq (Quaternion a)`
• `(Show a) => Show (Quaternion a)`
• `(Ring a) => Semiring (Quaternion a)`
• `(Ring a) => Ring (Quaternion a)`
• `Functor Quaternion`
• `(DivisionRing a) => DivisionRing (Quaternion a)`

### #approxEqSource

``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.

### #conjugateSource

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

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

### #conjugateBySource

``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
``````

### #iSource

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

### #jSource

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

### #kSource

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

### #normSource

``norm :: Quaternion Number -> Number``

### #normSquareSource

``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.

### #realPartSource

``realPart :: forall a. Quaternion a -> a``

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

``````\(Quaternion a _ _ _) -> a
``````

### #scalarMulSource

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

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

### #vectorPartSource

``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
``````

### #versorSource

``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

### #rightDivSource

``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.

### #leftDivSource

``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.