I am definitely not qualified to be discussing the topic of geometric algebra. This post is likely to have mistakes, but it has been incredibly useful for improving my understanding on things that previously felt disconnected, unintuitive and full of exceptions. I am aware that geometric algebra is a Clifford algebra which dates back to the 1800s, but the framing is valuable for me. Throughout this post, I am able to derive and build up a construct which allows me to perform rotations in 3D. Quaternions, on the other hand, are significantly more complicated to understand and derive (from my perspective). This post is not unique, but actually just documents the order in which I was able to understand and derive this. You can jump straight to the references at the bottom of this post which take a number of different approaches. This mostly serves as a reference for myself.

Wait, it’s just multiplication?

Firstly, we begin with a simple definition (we define this, it is not some universal truth1) for any vector, v\vec{v}:

v2=v2\vec{v}^2 = \|\vec{v}\|^2

Simply, this new product of a vector with itself gives us the magnitude of that vector squared. Next, we’re going to multiply two vectors a\vec{a} and b\vec{b}. We’ll use x^\hat{x}, y^\hat{y} and z^\hat{z} as our basis vectors – you may see these represented as i^\hat{i}, j^\hat{j}, and k^\hat{k} or even e1^\hat{e_1}, e2^\hat{e_2}, and e3^\hat{e_3}, elsewhere.

ab= (axx^+ayy^+azz^)(bxx^+byy^+bzz^)= axbxx^x^+axbyx^y^+axbzx^z^ + aybxy^x^+aybyy^y^+aybzy^z^ + azbxz^x^+azbyz^y^+azbzz^z^= axbx+axbyx^y^+axbzx^z^ + aybxy^x^+ayby+aybzy^z^ + azbxz^x^+azbyz^y^+azbz= ab + axbyx^y^+axbzx^z^ + aybxy^x^+aybzy^z^ + azbxz^x^+azbyz^y^\begin{align*} \vec{a}\vec{b} =&\ (a_x\hat{x} + a_y\hat{y} + a_z\hat{z}) (b_x\hat{x} + b_y\hat{y} + b_z\hat{z}) \\ =&\ a_x b_x \hat{x}\hat{x} + a_x b_y \hat{x}\hat{y} + a_x b_z \hat{x}\hat{z}\ + \\ &\ a_y b_x \hat{y}\hat{x} + a_y b_y \hat{y}\hat{y} + a_y b_z \hat{y}\hat{z}\ + \\ &\ a_z b_x \hat{z}\hat{x} + a_z b_y \hat{z}\hat{y} + a_z b_z \hat{z}\hat{z} \\ =&\ a_x b_x + a_x b_y \hat{x}\hat{y} + a_x b_z \hat{x}\hat{z}\ + \\ &\ a_y b_x \hat{y}\hat{x} + a_y b_y + a_y b_z \hat{y}\hat{z}\ + \\ &\ a_z b_x \hat{z}\hat{x} + a_z b_y \hat{z}\hat{y} + a_z b_z \\ =&\ \vec{a}\cdot\vec{b}\ + \\ &\ a_x b_y \hat{x}\hat{y} + a_x b_z \hat{x}\hat{z}\ + \\ &\ a_y b_x \hat{y}\hat{x} + a_y b_z \hat{y}\hat{z}\ + \\ &\ a_z b_x \hat{z}\hat{x} + a_z b_y \hat{z}\hat{y} \\ \end{align*}

We could easily eliminate the basis vectors multiplied by themselves with our definition from the beginning as we know that they have magnitude one (i.e. x^2=x^2=12=1\hat{x}^2 = \|\hat{x}\|^2 = 1^2 = 1). We’ve also managed to pull the dot product out of the result. However, we’re now stuck with these products of two orthogonal bases. Let’s see how far we can get without reducing these values but rather treating them as their own new unique type, we’ll call them “bivectors”. You can think of them as oriented planes. Moving forward, I may choose to annotate x^x^\hat{x}\hat{x} as xx^\widehat{xx}.

We don’t have enough tricks to reduce this just yet, so let’s see what we can do with these bases. Let’s take x^\hat{x} and y^\hat{y}:

x^+y^=12+12=2\begin{align*} \|\hat{x} + \hat{y}\| &= \sqrt{1^2 + 1^2} \\ &= \sqrt{2} \end{align*}

From our definition in the beginning, we have:

(x^+y^)2=x^+y^2xx^+xy^+yx^+yy^=(2)21+xy^+yx^+1=2xy^+yx^=0xy^=yx^\begin{align*} (\hat{x} + \hat{y})^2 &= \|\hat{x} + \hat{y}\|^2 \\ \widehat{xx} + \widehat{xy} + \widehat{yx} + \widehat{yy} &= (\sqrt{2})^2 \\ 1 + \widehat{xy} + \widehat{yx} + 1 &= 2 \\ \widehat{xy} + \widehat{yx} &= 0 \\ \widehat{xy} &= -\widehat{yx} \\ \end{align*}

That’s a useful result. We know that our basis bivectors anti-commute! We’ll use the bivectors yz^\widehat{yz}, zx^\widehat{zx}, and xy^\widehat{xy}. zx^\widehat{zx} is flipped so that the cross product of the vectors that make up the basis bivector points in the positive direction of another basis vector2. You can think of these as planes in the corresponding axes oriented in the direction of the axis that is not involved. So, yz^\widehat{yz} exists in the yzyz-plane and it points in the direction of the positive xx-axis.

So, continuing with the geoemtric product from above:

ab= ab + axbyxy^+axbzxz^ + aybxyx^+aybzyz^ + azbxzx^+azbyzy^= ab + axbyxy^axbzzx^  aybxxy^+aybzyz^ + azbxzx^azbyyz^= ab + (aybzazby)yz^ + (azbxaxbz)zx^ + (axbyaybx)xy^= ab+ab\begin{align*} \vec{a} \vec{b} =&\ \vec{a}\cdot\vec{b}\ + \\ &\ a_x b_y \widehat{xy} + a_x b_z \widehat{xz}\ + \\ &\ a_y b_x \widehat{yx} + a_y b_z \widehat{yz}\ + \\ &\ a_z b_x \widehat{zx} + a_z b_y \widehat{zy} \\ =&\ \vec{a}\cdot\vec{b}\ + \\ &\ a_x b_y \widehat{xy} - a_x b_z \widehat{zx}\ - \\ &\ a_y b_x \widehat{xy} + a_y b_z \widehat{yz}\ + \\ &\ a_z b_x \widehat{zx} - a_z b_y \widehat{yz} \\ =&\ \vec{a}\cdot\vec{b}\ + \\ &\ (a_y b_z - a_z b_y)\widehat{yz}\ + \\ &\ (a_z b_x - a_x b_z)\widehat{zx}\ + \\ &\ (a_x b_y - a_y b_x)\widehat{xy} \\ =&\ \vec{a}\cdot\vec{b} + \vec{a}\wedge\vec{b} \\ \end{align*}

We pulled out a new product called the wedge (\wedge) product, but you may have noticed that its components are the same as the cross product! The wedge product represents a bivector, which corresponds to an oriented plane. The cross product represents the vector which is perpendicular to its inputs. That essentially encodes the same detail, but allows for the wedge product to exist in any number of dimensions. The cross product doesn’t exist in 2D, for example.

The result of this geometric product corresponds to a rotor, which is similar but not identical to a quaternion. A quaternion focus on the rotation axis, or the normal to the bivector plane.

R=rwreal+ryzyz^+rzxzx^+rxyxy^bivector\begin{align*} R = \underbrace{r_w}_{\mathclap{\text{real}}} + \underbrace{r_{yz} \widehat{yz} + r_{zx} \widehat{zx} + r_{xy} \widehat{xy}}_{\mathclap{\text{bivector}}} \end{align*}

This differs from the common interpretation of using the three imaginary components i\bold{i}, j\bold{j}, and k\bold{k} as seen in quaternions.

A Complex Detour

If you apply the geometric product to two 2D vectors, you’ll arrive at the following:

ab=ab+ab=ab+axbxxx^+aybyyy^+axbyxy^+aybxyx^=ab+axbx+ayby+axbyxy^aybxxy^=(ab+axbx+ayby)+(axbyaybx)xy^=r+qxy^=r+qi\begin{align*} \vec{a}\vec{b} &= \vec{a} \cdot \vec{b} + \vec{a} \wedge \vec{b} \\ &= \vec{a} \cdot \vec{b} + a_x b_x \widehat{xx} + a_y b_y \widehat{yy} + a_x b_y \widehat{xy} + a_y b_x \widehat{yx} \\ &= \vec{a} \cdot \vec{b} + a_x b_x + a_y b_y + a_x b_y \widehat{xy} - a_y b_x \widehat{xy} \\ &= (\vec{a} \cdot \vec{b} + a_x b_x + a_y b_y) + (a_x b_y - a_y b_x) \widehat{xy} \\ &= r + q \widehat{xy} \tag{$\bold{i} = \widehat{xy}$} \\ &= r + q \bold{i} \end{align*}

To convince you, let’s multiply this number (as a scalar and bivector) with a bivector that represents a rotation by 90°90\degree. Typically, this just means multiplying by i\bold{i}, but for us it means multiplying by xy^\widehat{xy}.

(r+qxy^)(xy^)=rxy^+qxy^xy^=rxy^qyx^xy^=rxy^qy^(1)y^=rxy^q(1)=q+rxy^\begin{align*} (r + q \widehat{xy})(\widehat{xy}) &= r \widehat{xy} + q \widehat{xy} \widehat{xy} \\ &= r \widehat{xy} - q \widehat{yx} \widehat{xy} \tag{anti-commutative} \\ &= r \widehat{xy} - q \hat{y}(1)\hat{y} \tag{definition} \\ &= r \widehat{xy} - q(1) \tag{definition} \\ &= -q + r \widehat{xy} \\ \end{align*}

Same result, but not a 1\sqrt{-1} in sight! While I’m not opposed to the use of 1\sqrt{-1}, it does feel like an unnecessary reduction. Treating these as a standalone type with the anti-commutative property gives us the same result, without breaking the rules of real numbers.

Back to Rotors

Inverse of a vector

All vectors have an inverse. We can determine this under the geometric product. Given a vector v\vec{v} and a potential inverse, v=vv2\vec{v}' = \frac{\vec{v}}{\|\vec{v}\|^2}, we have:

vv=vv+vv=1v2(vv)+1v2(vv)=1v2v2+1v20=v2v2vv=1\begin{align*} \vec{v}\vec{v}' &= \vec{v} \cdot \vec{v}' + \vec{v} \wedge \vec{v}' \\ &= \frac{1}{\|\vec{v}\|^2} (\vec{v} \cdot \vec{v}) + \frac{1}{\|\vec{v}\|^2} (\vec{v} \wedge \vec{v}) \\ &= \frac{1}{\|\vec{v}\|^2} |\vec{v}|^2 + \frac{1}{\|\vec{v}\|^2} 0 \\ &= \frac{\|\vec{v}\|^2}{\|\vec{v}\|^2} \\ \vec{v}\vec{v}' &= 1 \\ \end{align*}

Similarly, we check the opposite, since the geometric product is anti-commutative.

vv=v.v+vv=1v2(v.v)+1v2(vv)=1v2v2+1v20=v2v2vv=1\begin{align*} v'v &= v'.v + v' \wedge v \\ &= \frac{1}{\|v\|^2} (v.v) + \frac{1}{\|v\|^2} (v \wedge v) \\ &= \frac{1}{\|v\|^2} |v|^2 + \frac{1}{\|v\|^2} 0 \\ &= \frac{\|v\|^2}{\|v\|^2} \\ v'v &= 1 \\ \end{align*}

Therefore, the inverse v1=v=vv2\vec{v}^{-1} = \vec{v}' = \frac{\vec{v}}{\|\vec{v}\|^2}.

Additionally, we can determine the inverse of the geometric product of two vectors, a\vec{a} and b\vec{b} as (ab)1=b1a1(\vec{a}\vec{b})^{-1} = \vec{b}^{-1}\vec{a}^{-1}:

(ab)(b1a1)=a(bb1)a1=aa1=1\begin{align*} (\vec{a}\vec{b})(\vec{b}^{-1}\vec{a}^{-1}) &= \vec{a}(\vec{b}\vec{b}^{-1})\vec{a}^{-1} \\ &= \vec{a}\vec{a}^{-1} \\ &= 1 \\ \end{align*} (b1a1)(ab)=b1(a1a)b=b1b=1\begin{align*} (\vec{b}^{-1}\vec{a}^{-1})(\vec{a}\vec{b}) &= \vec{b}^{-1}(\vec{a}^{-1}\vec{a})\vec{b} \\ &= \vec{b}^{-1}\vec{b} \\ &= 1 \\ \end{align*}

Components of a vector

Now, given the above, we can represent a vector, aa, as the components of another vector, b\vec{b}.

a=abb1=1b2(ab)b=abb2b+abb2b\begin{align*} \vec{a} &= \vec{a}\vec{b}\vec{b}^{-1} \\ &= \frac{1}{\|\vec{b}^2\|}(\vec{a}\vec{b})\vec{b} \\ &= \frac{\vec{a} \cdot \vec{b}}{\|\vec{b}^2\|}\vec{b} + \frac{\vec{a} \wedge \vec{b}}{\|\vec{b}^2\|}\vec{b} \\ \end{align*}

It turns out that this is equivalent to:

a=ab+ab\vec{a} = \vec{a}_{\|b} + \vec{a}_{\perp{b}}

Since the dot product is greatest when the two vectors are parallel, and the wedge product (similar to the cross product) is greatest when the two vectors are perpendicular.

Reflections

In a previous post, we showed that a reflection vv' of a vector vv can be represented as:

v=vv\vec{v'} = \vec{v}_{\perp} - \vec{v}_{\|}

However, that was for a physical representation of a ray reflecting off of a surface represented by its normal. The definition for a reflection about a vector is given as:

v=vv\vec{v'} = \vec{v}_{\|} - \vec{v}_{\perp}

So, for two vectors a\vec{a} and v\vec{v}, we can write the reflection as:

a=avav=vaa2avaa2a=(va)aa2(va)aa2=(va)a1(va)a1=(av)a1+(av)a1=(av+av)a1=(av)a1=ava1\begin{align*} \vec{a} &= \vec{a}_{\|v} - \vec{a}_{\perp{v}} \\ &= \frac{\vec{v} \cdot \vec{a}}{\|\vec{a}^2\|}\vec{a} - \frac{\vec{v} \wedge \vec{a}}{\|\vec{a}^2\|}\vec{a} \\ &= (\vec{v} \cdot \vec{a}) \frac{\vec{a}}{\|\vec{a}^2\|} - (\vec{v} \wedge \vec{a}) \frac{\vec{a}}{\|\vec{a}^2\|} \\ &= (\vec{v} \cdot \vec{a}) \vec{a}^{-1} - (\vec{v} \wedge \vec{a}) \vec{a}^{-1} \\ &= (\vec{a} \cdot \vec{v}) \vec{a}^{-1} + (\vec{a} \wedge \vec{v}) \vec{a}^{-1} \\ &= (\vec{a} \cdot \vec{v} + \vec{a} \wedge \vec{v}) \vec{a}^{-1} \\ &= (\vec{a}\vec{v}) \vec{a}^{-1} \\ &= \vec{a}\vec{v}\vec{a}^{-1} \\ \end{align*}

This is referred to as the sandwich product. We see it outputs a vector, which is a valuable insight as we move towards a derivation for rotation.

Rotations

We can perform a rotation by performing two consecutive reflections, as seen below, from vvv\vec{v} \rightarrow \vec{v'} \rightarrow \vec{v''}. First by a\vec{a} and then by b\vec{b}.

This works in 3D too. You can imagine that a reflection would “invert” an image, so another reflection would remove that inversion. We get to keep the side-effect that is the rotation, however.

Since we defined our reflection in terms of the sandwich product, it means we can define rotations in terms of two applications of the sandwich product! You may have noticed the one deficiency is that the angle between a\vec{a} and b\vec{b} is θ\theta, but the rotation of the original vector, v\vec{v}, is double that. In a practical implementation, you may want to correct the angles in advance, so that when constructing a rotor as the geometric product of two vectors, you pick a vector halfway between them and then multiply them. This gives us an object in which the rotation always represents exactly the angle between the two input vectors.

Since we will often want to hold onto a rotor instead of the two vectors that represent the orientation, we can calculate the sandwich product directly. First, we define the rotor and vector, respectively:

R=rw+ryzyz^+rzxzx^+rxyxy^v=vxx^+vyy^+vzz^\begin{align*} R &= r_w + r_{yz} \widehat{yz} + r_{zx} \widehat{zx} + r_{xy} \widehat{xy} \\ v &= v_x \hat{x} + v_y \hat{y} + v_z \hat{z} \\ \end{align*}

Now, we want to calculate RvR1R\vec{v}R^{-1}. Let’s first start by calculating the left-hand side, which is just a lot of elbow grease.

Rv= (rw+ryzyz^+rzxzx^+rxyxy^)(vxx^+vyy^+vzz^)= rw(vxx^+vyy^+vzz^)+ ryzyz^(vxx^+vyy^+vzz^)+ rzxzx^(vxx^+vyy^+vzz^)+ rxyxy^(vxx^+vyy^+vzz^)= rwvxx^+rwvyy^+rwvzz^+ ryzvxyzx^+ryzvyyzy^+ryzvzyzz^+ rzxvxzxx^+rzxvyzxy^+rzxvzzxz^+ rxyvxxyx^+rxyvyxyy^+rxyvzxyz^= rwvxx^+rwvyy^+rwvzz^+ ryzvxxyz^ryzvyz^+ryzvzy^+ rzxvxz^+rzxvyxyz^rzxvzx^ rxyvxy^+rxyvyx^+rxyvzxyz^= (rwvxrzxvz+rxyvy)x^+ (rwvy+ryzvzrxyvx)y^+ (rwvzryzvy+rzxvx)z^+ (ryzvx+rzxvy+rxyvz)xyz^\begin{align*} R\vec{v} =&\ (r_w + r_{yz} \widehat{yz} + r_{zx} \widehat{zx} + r_{xy} \widehat{xy})(v_x \hat{x} + v_y \hat{y} + v_z \hat{z}) \\ =&\ r_w (v_x \hat{x} + v_y \hat{y} + v_z \hat{z}) + \\ &\ r_{yz} \widehat{yz} (v_x \hat{x} + v_y \hat{y} + v_z \hat{z}) + \\ &\ r_{zx} \widehat{zx} (v_x \hat{x} + v_y \hat{y} + v_z \hat{z}) + \\ &\ r_{xy} \widehat{xy} (v_x \hat{x} + v_y \hat{y} + v_z \hat{z}) \\ =&\ r_w v_x \hat{x} + r_w v_y \hat{y} + r_w v_z \hat{z} + \\ &\ r_{yz} v_x \widehat{yzx} + r_{yz} v_y \widehat{yzy} + r_{yz} v_z \widehat{yzz} + \\ &\ r_{zx} v_x \widehat{zxx} + r_{zx} v_y \widehat{zxy} + r_{zx} v_z \widehat{zxz} + \\ &\ r_{xy} v_x \widehat{xyx} + r_{xy} v_y \widehat{xyy} + r_{xy} v_z \widehat{xyz} \\ =&\ r_w v_x \hat{x} + r_w v_y \hat{y} + r_w v_z \hat{z} + \\ &\ r_{yz} v_x \widehat{xyz} - r_{yz} v_y \hat{z} + r_{yz} v_z \hat{y} + \\ &\ r_{zx} v_x \hat{z} + r_{zx} v_y \widehat{xyz} - r_{zx} v_z \hat{x} - \\ &\ r_{xy} v_x \hat{y} + r_{xy} v_y \hat{x} + r_{xy} v_z \widehat{xyz} \\ =&\ (r_w v_x - r_{zx} v_z + r_{xy} v_y) \hat{x} + \\ &\ (r_w v_y + r_{yz} v_z - r_{xy} v_x) \hat{y} + \\ &\ (r_w v_z - r_{yz} v_y + r_{zx} v_x) \hat{z} + \\ &\ (r_{yz} v_x + r_{zx} v_y + r_{xy} v_z) \widehat{xyz} \\ \end{align*}

We get our first trivector in xyz^\widehat{xyz}, which represents an oriented volume, however we’ll see this is quickly resolved in the rest of the proof. Moving on, we’ll define the following:

Rv=L=lxx^+lyy^+lzz^+lxyzxyz^R\vec{v} = L = l_x \hat{x} + l_y \hat{y} + l_z \hat{z} + l_{xyz} \widehat{xyz}

We know that we’re trying to perform two applications of some vectors, a\vec{a} and b\vec{b}, respectively. So, we are really looking for (ba)v(ba)1(\vec{b}\vec{a})\vec{v}(\vec{b}\vec{a})^{-1}.

From some of the previous definitions, we can show the following:

RvR1= (ba)v(ba)1= (ba)v(a1b1) = 1ab(ba)v(ab)= 1abL(ab)\begin{align*} R\vec{v}R^{-1} =&\ (\vec{b}\vec{a})\vec{v}(\vec{b}\vec{a})^{-1} \\ =&\ (\vec{b}\vec{a})\vec{v}(\vec{a}^{-1}\vec{b}^{-1}) \\ &\ \tag{\text{notice the two sandwich products}} \\ =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|}(\vec{b}\vec{a})\vec{v}(\vec{a}\vec{b}) \\ =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|}L(\vec{a}\vec{b}) \\ \end{align*}

ab\vec{a}\vec{b} is the result of commuting ba\vec{b}\vec{a}, so we could imagine this means that since the dot product commutes and the wedge product anti-commutes that this means the following is true

ab=rwryzyz^rzxzx^rxyxy^\vec{a}\vec{b} = r_w - r_{yz} \widehat{yz} - r_{zx} \widehat{zx} - r_{xy} \widehat{xy}

You can verify this more formally by flipping the vectors in the Multiplying Rotors section below. So, we have:

RvR1= 1abL(ab)= 1ab[   (lxx^+lyy^+lzz^+lxyzxyz^)(rwryzyz^rzxzx^rxyxy^) ]= 1ab[   lxx^(rwryzyz^rzxzx^rxyxy^)+   lyy^(rwryzyz^rzxzx^rxyxy^)+   lzz^(rwryzyz^rzxzx^rxyxy^)+   lxyzxyz^(rwryzyz^rzxzx^rxyxy^) ]= 1ab[   lxrwx^lxryzxyz^lxrzxxzx^lxrxyxxy^+   lyrwy^lyryzyyz^lyrzxyzx^lyrxyyxy^+   lzrwz^lzryzzyz^lzrzxzzx^lzrxyzxy^+   lxyzrwxyz^lxyzryzxyzyz^lxyzrzxxyzzx^lxyzrxyxyzxy^ ]= 1ab[   lxrwx^lxryzxyz^+lxrzxz^lxrxyy^+   lyrwy^lyryzz^lyrzxxyz^+lyrxyx^+   lzrwz^+lzryzy^lzrzxx^lzrxyxyz^+   lxyzrwxyz^+lxyzryzx^+lxyzrzxy^+lxyzrxyz^ ]= 1ab[   (lxyzryz+lxrw+lyrxylzrzx)x^+   (lxyzrzx+lyrwlxrxy+lzryz)y^+   (lxyzrxy+lzrw+lxrzxlyryz)z^+   (lxyzrwlxryzlyrzxlzrxy)xyz^ ]\begin{align*} R\vec{v}R^{-1} =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|}L(\vec{a}\vec{b}) \\ =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|} [ \\ &\ \ \ (l_x \hat{x} + l_y \hat{y} + l_z \hat{z} + l_{xyz} \widehat{xyz})(r_w - r_{yz} \widehat{yz} - r_{zx} \widehat{zx} - r_{xy} \widehat{xy}) \\ &\ ] \\ =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|} [ \\ &\ \ \ l_x \hat{x}(r_w - r_{yz} \widehat{yz} - r_{zx} \widehat{zx} - r_{xy} \widehat{xy}) + \\ &\ \ \ l_y \hat{y}(r_w - r_{yz} \widehat{yz} - r_{zx} \widehat{zx} - r_{xy} \widehat{xy}) + \\ &\ \ \ l_z \hat{z}(r_w - r_{yz} \widehat{yz} - r_{zx} \widehat{zx} - r_{xy} \widehat{xy}) + \\ &\ \ \ l_{xyz} \widehat{xyz}(r_w - r_{yz} \widehat{yz} - r_{zx} \widehat{zx} - r_{xy} \widehat{xy}) \\ &\ ] \\ =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|} [ \\ &\ \ \ l_x r_w \hat{x} - l_x r_{yz} \widehat{xyz} - l_x r_{zx} \widehat{xzx} - l_x r_{xy} \widehat{xxy} + \\ &\ \ \ l_y r_w \hat{y} - l_y r_{yz} \widehat{yyz} - l_y r_{zx} \widehat{yzx} - l_y r_{xy} \widehat{yxy} + \\ &\ \ \ l_z r_w \hat{z} - l_z r_{yz} \widehat{zyz} - l_z r_{zx} \widehat{zzx} - l_z r_{xy} \widehat{zxy} + \\ &\ \ \ l_{xyz} r_w \widehat{xyz} - l_{xyz} r_{yz} \widehat{xyzyz} - l_{xyz} r_{zx} \widehat{xyzzx} - l_{xyz} r_{xy} \widehat{xyzxy} \\ &\ ] \\ =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|} [ \\ &\ \ \ l_x r_w \hat{x} - l_x r_{yz} \widehat{xyz} + l_x r_{zx} \hat{z} - l_x r_{xy} \hat{y} + \\ &\ \ \ l_y r_w \hat{y} - l_y r_{yz} \widehat{z} - l_y r_{zx} \widehat{xyz} + l_y r_{xy} \widehat{x} + \\ &\ \ \ l_z r_w \hat{z} + l_z r_{yz} \widehat{y} - l_z r_{zx} \widehat{x} - l_z r_{xy} \widehat{xyz} + \\ &\ \ \ l_{xyz} r_w \widehat{xyz} + l_{xyz} r_{yz} \widehat{x} + l_{xyz} r_{zx} \widehat{y} + l_{xyz} r_{xy} \widehat{z} \\ &\ ] \\ =&\ \frac{1}{\|\vec{a}\|\|\vec{b}\|} [ \\ &\ \ \ (l_{xyz} r_{yz} + l_x r_w + l_y r_{xy} - l_z r_{zx}) \hat{x} + \\ &\ \ \ (l_{xyz} r_{zx} + l_y r_w - l_x r_{xy} + l_z r_{yz}) \hat{y} + \\ &\ \ \ (l_{xyz} r_{xy} + l_z r_w + l_x r_{zx} - l_y r_{yz}) \hat{z} + \\ &\ \ \ (l_{xyz} r_w - lx r_{yz} - ly r_{zx} - lz r_{xy}) \widehat{xyz} \\ &\ ] \\ \end{align*}

Finally, it can be shown that the trivector is zero by substituting in the components of LL and simplifying. If we do this, and assume that our rotor is made of two vectors of unit length, then we have our final vector, vv', as:

v=RvR1= (lxyzryz+lxrw+lyrxylzrzx)x^+ (lxyzrzx+lyrwlxrxy+lzryz)y^+ (lxyzrxy+lzrw+lxrzxlyryz)z^\begin{align*} \vec{v'} = R\vec{v}R^{-1} =&\ (l_{xyz} r_{yz} + l_x r_w + l_y r_{xy} - l_z r_{zx}) \hat{x} + \\ &\ (l_{xyz} r_{zx} + l_y r_w - l_x r_{xy} + l_z r_{yz}) \hat{y} + \\ &\ (l_{xyz} r_{xy} + l_z r_w + l_x r_{zx} - l_y r_{yz}) \hat{z} \\ \end{align*}

It can be substituted further, but we can stop here, as there is no further simplification of the type. We have produced a vector.

Rotor to matrix

Rotors represent a rotation, not an orientation, but we can build up a matrix which represents a change of basis to feed into some transformation matrix3. So, assuming we have the basis vectors i^\hat{i}, j^\hat{j} and k^\hat{k}, as well as a rotor RR, we can calculate the rotation matrix, M, as:

M=[(Ri^R1)x(Rj^R1)x(Rk^R1)x0(Ri^R1)y(Rj^R1)y(Rk^R1)y0(Ri^R1)z(Rj^R1)z(Rk^R1)z00001]M = \begin{bmatrix} (R\hat{i}R^{-1})_x & (R\hat{j}R^{-1})_x & (R\hat{k}R^{-1})_x & 0 \\ (R\hat{i}R^{-1})_y & (R\hat{j}R^{-1})_y & (R\hat{k}R^{-1})_y & 0 \\ (R\hat{i}R^{-1})_z & (R\hat{j}R^{-1})_z & (R\hat{k}R^{-1})_z & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix}

You can calculate the sandwich product once per basis vector. You can even take it a step further and calculate the zz-axis column as the cross product of the first two to reduce some of the computation involved.

Multiplying rotors

For two rotors, AA and BB, we can derive their product. Note that this is just the same application as for the 2D case at the start, there’s just a lot more algebra. This is useful for the composition of rotors, which may be useful for some applications.

AB= (aw+ayzyz^+azxzx^+axyxy^)(bw+byzyz^+bzxzx^+bxyxy^)= awbw +awbyzyz^+awbzxzx^+awbxyxy^ +bwayzyz^+bwazxzx^+bwaxyxy^ +ayzbyzyz^yz^+ayzbzxyz^zx^+ayzbxyyz^xy^ +azxbyzzx^yz^+azxbzxzx^zx^+azxbxyzx^xy^ +axybyzxy^yz^+axybzxxy^zx^+axybxyxy^xy^= awbw +awbyzyz^+awbzxzx^+awbxyxy^ +bwayzyz^+bwazxzx^+bwaxyxy^ ayzbyzayzbzxxy^+ayzbxyzx^ +azxbyzxy^azxbzxazxbxyyz^ axybyzzx^+axybzxyz^axybxy= awbwaBbB+ (awbyz+bwayzazxbxy+axybzx)yz^ + (awbzx+bwazx+ayzbxyaxybyz)zx^ + (awbxy+bwaxyayzbzx+azxbyz)xy^\begin{align*} AB =&\ (a_w + a_{yz} \widehat{yz} + a_{zx} \widehat{zx} + a_{xy} \widehat{xy}) (b_w + b_{yz} \widehat{yz} + b_{zx} \widehat{zx} + b_{xy} \widehat{xy}) \\ =&\ a_w b_w \\ &\ + a_w b_{yz} \widehat{yz} + a_w b_{zx} \widehat{zx} + a_w b_{xy} \widehat{xy} \\ &\ + b_w a_{yz} \widehat{yz} + b_w a_{zx} \widehat{zx} + b_w a_{xy} \widehat{xy} \\ &\ + a_{yz} b_{yz} \widehat{yz}\widehat{yz} + a_{yz} b_{zx} \widehat{yz}\widehat{zx} + a_{yz} b_{xy} \widehat{yz}\widehat{xy} \\ &\ + a_{zx} b_{yz} \widehat{zx}\widehat{yz} + a_{zx} b_{zx} \widehat{zx}\widehat{zx} + a_{zx} b_{xy} \widehat{zx}\widehat{xy} \\ &\ + a_{xy} b_{yz} \widehat{xy}\widehat{yz} + a_{xy} b_{zx} \widehat{xy}\widehat{zx} + a_{xy} b_{xy} \widehat{xy}\widehat{xy} \\ =&\ a_w b_w \\ &\ + a_w b_{yz} \widehat{yz} + a_w b_{zx} \widehat{zx} + a_w b_{xy} \widehat{xy} \\ &\ + b_w a_{yz} \widehat{yz} + b_w a_{zx} \widehat{zx} + b_w a_{xy} \widehat{xy} \\ &\ - a_{yz} b_{yz} - a_{yz} b_{zx} \widehat{xy} + a_{yz} b_{xy} \widehat{zx} \\ &\ + a_{zx} b_{yz} \widehat{xy} - a_{zx} b_{zx} - a_{zx} b_{xy} \widehat{yz} \\ &\ - a_{xy} b_{yz} \widehat{zx} + a_{xy} b_{zx} \widehat{yz} - a_{xy} b_{xy} \\ \tag{with $\vec{a_B}$ and $\vec{b_B}$ made up of the bivector components of the rotors} \\ =&\ a_w b_w - \vec{a_B} \cdot \vec{b_B} + \\ &\ (a_w b_{yz} + b_w a_{yz} - a_{zx} b_{xy} + a_{xy} b_{zx})\widehat{yz}\ + \\ &\ (a_w b_{zx} + b_w a_{zx} + a_{yz} b_{xy} - a_{xy} b_{yz})\widehat{zx}\ + \\ &\ (a_w b_{xy} + b_w a_{xy} - a_{yz} b_{zx} + a_{zx} b_{yz})\widehat{xy} \\ \end{align*}

This is like the Hamilton product, except the signs of the last two coefficients for each basis is flipped. I assume it’s because they’re isomorphic, but not identical objects.

Rotor from plane-angle

Quaternions represent an axis-angle, but for rotors we consider the bivector plane and an angle, so I’ve personally dubbed this the “plane-angle” representation.

Firstly, we have to recall the following equations from their definition:

ab=abcosθa×b=absinθ\begin{aligned} \vec{a} \cdot \vec{b} &= \|a\|\|b\|\cos{\theta} \\ \|\vec{a} \times \vec{b}\| &= \|a\|\|b\|\sin{\theta} \\ \end{aligned}

And with a bit of a leap of faith, we’ll assume that

aba×b=absinθ\|\vec{a} \wedge \vec{b}\| \approx \|\vec{a} \times \vec{b}\| = \|a\|\|b\|\sin{\theta} \\

Therefore, we can represent a rotor, RR, as:

R=ab=ab+ab=ab+ababab=abcosθ+absinθabab\begin{aligned} R &= ab \\ &= a \cdot b + a \wedge b \\ &= a \cdot b + |a \wedge b| \frac{a \wedge b}{|a \wedge b|} \\ &= \|a\|\|b\|\cos{\theta} + \|a\|\|b\|\sin{\theta} \frac{a \wedge b}{|a \wedge b|} \\ \end{aligned}

You’ll note that the last bivector component represents a unit bivector (or plane), BB, instead of an oriented vector. Additionally, assuming a\vec{a} and b\vec{b} are unit vectors, we have ab=1\|a\|\|b\| = 1. Given this, and the fact that rotors produce a rotation that is twice the angle between the two vectors that make up the rotor, we can summarize this equation as:

R=cosθ2+sinθ2B\begin{aligned} R &= \cos{\frac{\theta}{2}} + \sin{\frac{\theta}{2}} B \\ \end{aligned}

The following links were referenced before and while I made this post. I tried to tackle it in the direction that made most sense to me, but each of these are invaluable resources for the intuition and understanding of geometric algebra.

  1. From Zero to Geo - sudgylacmoe
    • My first exposure to the concept. Blew me away, but it’s quite content dense!
  2. Why can’t you multiply vectors? - Freya Holmer
    • A lovely introduction to all of the concepts without too much algebra. Led me down this rabbit hole.
  3. Let’s remove Quaternions from every 3D Engine
    • Beautiful visualizations; tackles the reflections from the “normal-of-the-plane” approach which ends in the same result, a rotation!
  4. Rotors: A practical introduction for 3D graphics
    • An incredible aid for the approach to the algebra concerning the sandwich product in this post. I was stumped for a few sections, and this helped me through it. There are a bunch more proofs and applications in this post for those interested.

Footnotes

  1. Maybe it is, who knows. 

  2. You can use the right-hand rule in a right-handed coordinate system, or left-hand rule in a left-handed coordinate system. 

  3. I’ll demonstrate this in a WebGPU post in future.