📱 Erkannter Endgerättyp ⛱️ Tag und Nacht. Verbraucht keinen oder einen 🍪.
guest
Login 🍪 0 Anzahl Ihrer gespeicherten Kekse, führt zur Keksdose

Geometric Algebra

The planes e₁₂ e₂₃ e₃₁ initially chosen here work but in 4D and STA it makes more sense to think about "around X, Y, Z" so ill go with e₂₃ e₃₁ e₁₂

Rotating a vector (v)

How?

Mirror it along another (u)!

how to find this other (u)? later.

if you found it (u)

  1. find u_parallel_to_v (dot) and u_perp_to_v (wedge)

  2. when you subtract those that's the new v' - rotated:

so

v' = u_parallel - u_perp

do it on paper once.

Shitty U V Para Perp

If yours is less shitty that's passed

Multiplying

you get a multivector by multiplying 2 vectors.

The form of this multivector depends on the dimensions of the vectors multiplied. In 2D thats just 2 values you can look at like any other 2D vector. Because x and y components are simply cos and sin of the angle. Its similar in higher dimensions.

v (4,3) u (5,8)

you multiply these to a geometric product like

uv = u · v + u ∧ v

this gets you a 2D Rotor grade 0 scalar + grade 2 bivector

uv  = uₓvₓ(e₁e₁) + uₓvᵧ(e₁e₂) + uᵧvₓ(e₂e₁) + uᵧvᵧ(e₂e₂)
         1            e₁e₂        -e₁e₂       1

    = (uₓvₓ + uᵧvᵧ) + (uₓvᵧ - uᵧvₓ)e₁₂ (dot and wedge) <-- 2 floats so we usually want this one

    = |u| |v| (cos(θ) + e₁₂ sin(θ)) (polar form)

    = |u| |v| e^(e₁₂θ) (euler form)

You can also use a sin(θ) e₁₂ notation instead of e₁₂ sin(θ) as it commutes (scalar)

Polar and euler form - Why is this |u||v| ? its the length of the rotor but where is √ and ² ?!

  • Lenght of a vector is √(x² + y²)

  • factor in |u| |v| --> √( (|u| |v| cos θ)² + (|u| |v| sin θ)² )

  • distribute ² √( (|u|² |v|² cos² θ) + (|u|² |v|² sin² θ) ) - dont be confused about sin² its (sin(θ))²

  • factor out |u|² |v|² --> √( |u|² |v|² (cos²θ + sin²θ) ) - now you may know cos²θ + sin²θ is always 1² as its the unit circle

  • Ultimately √(|u|² |v|²) --> |u| |v|

Thats a rotor2

but its not the half angle yet we want to mirror a vector with so it rotates

We have a cos / grade 0 / x and sin e₁₂ / grade 2 / y

This gives you a triangle with an angle θ. Now to get half that angle in any rotor not just normalized ones you need to build a parallelogram or better: rhombus.

∧
|                
|           
|           Biv (4, 3)         
|       /             
|  /              
+---------------------->
0  1  2  3  4

to do so imagine what happens when you extend x by the length/magnitude of the bivector but keep the height y the same

∧
|                
|                  
|           old (4,3)      Biv (9, 3)   
|       /         -             
|  /     -             
+---------------------->
0  1  2  3  4  5  6  7  8  9

Thats now exactly half that angle. Why? Where is the rhombus hidden?

Y (Bivector)
^
|            (4,3) = uv ---------------- (9,3) = uv + |uv|
|           /  .                        /
|         /      .                    /
|   L=5 /          . (Diagonal)     /  L=5
|     /              .            /
|   /                  .        /
| /                      .   /
+--------------------------+-------------> X (Scalar)
(0,0)                      5

There it is! As you can see no matter the values - this is always the half angle.

Proof:

  • Write the geometric product in polar form

    • R = uv = |u||v| ( cos(θ) + sin(θ) e₁₂ )
  • add the length!

    • R = |u||v| + uv = |u||v| + |u||v|( cos(θ) + sin(θ) e₁₂ )
  • factor length out!

    • R = |u||v| ( (1 + cos(θ)) + sin(θ) e₁₂ )
  • https://en.wikipedia.org/wiki/List_of_trigonometric_identities#Multiple-angle_and_half-angle_formulas gives us

    • cos (2 θ) = 2cos²(θ) - 1 --> 1 + cos (2 θ) = 2cos²(θ) --> 1 + cos (θ) = 2cos²(θ/2)
    • sin (2 θ) = 2 sin(θ) cos (θ) --> sin (θ) = 2 sin(θ/2) cos (θ/2)
  • substitute

    • |u||v| [ (2 cos² (θ/2) + (2 sin (θ/2) cos (θ/2)) e₁₂) ] <-- all share 2 cos (θ/2)
  • factor out 2 cos (θ/2)

    • R = [ |u| |v| 2 cos (θ/2) ] * ( cos (θ/2) + sin(θ/2) e₁₂)
  • done. now assuming your rotor is normalized all |u| |v| 2 cos (θ/2) would be gone leaving cos (θ/2) + sin(θ/2) e₁₂

Using this rotor to rotate

You now have a rotor that can be used to go from v to u - handy!

using a vector on the left of a rotor is counterclockwise

on the right is clockwise

vR - ccw

Rv - cw

kinda logical aint it. Not? Understand that your sin points up. So no matter how the cos part behaves the sin on plane e₁₂ will always go from x to y so UP which is counterclockwise.

But if we put R LEFT of v i mean thats the mirror we flip along - isnt that ccw? Again no but for other reasons. In GA you do not multiply like in "normal" math because it does not commute. 4 * 3 is not the same as 3 * 4 here. When you multiply a plane like e₁₂ it matters.

To make it easier on you vectors will now be indexed by dim and Rotors will have memory index. So [0] is scalar cos (θ) and [1] is the sin (θ) of plane e₁₂

Remember we store the value that cos(θ) and sin(θ) evaluate to. For a normalized 45° degree rotor you can surely figure them out: Its R[(√2)/2 , (√2)/2]

Lets see what happens to our v when we use R right of it:

vR = (vₓ + vᵧ) * (R[0] + R[1])

dear god, how do you multiply this??? Remember binomial theorem and (a + b) * (c + d) ? It's the exact same thing here:

vR = vₓ R[0] + vₓ R[1] + vᵧ R[0] + vᵧ R[1]

so these are 2 2-term and 2 3-term multiplications involved BUT you cannot add R[1] and R[2] together or vₓ and vᵧ and its the same for higher dimensional rotors.

But we know x is e₁ and y is e₂ !

Squaring axis eₙ and planes eₘₙ

eₙ always squares to the signature. you can imagine this easily with a coord systems x axis. if you take that in length 1 (e is a basis vector so always "1" as its not stretched, etc.) and use it two times to form a square - thats again just 1 x 1 saying this area will be 1.

Now in other signatures like spacetime for a certain axis this might be -1 instead. AND YOU CAN MIX!! e₁² = -1 but e₂² = e₃² = e₄² = 1!

Neat, aint it. A plane itself however like e₁₂ will ALWAYS square to -1 and the reason for that is even easier:

Imagine a plane eₓᵧ or cw eᵧₓ - no matter how if you use this 90° plane TWICE and apply it to any scalar this scalar will be flipped 180°. y = 3 will the y = -3. x = 2 will be x = -2

That's why these always square to -1. Now take a break and think about the pain of having to deal with imaginary i instead of e₁₂.

Multiplying axis with planes

How to solve e₁ * e₁₂ ? because these planes are orthonormal (normalized, perpendicular) you can always go from a plane like e₁₂ back to the geometric product of its axis. Because the cos(90°) part is always 0! and the sin(90°) e₁₂ is always 1 e₁₂!

now e₁ e₂ = (e₁ · e₂) + (e₁ ∧ e₂) this leaves only 1 e₁₂

so e₁ e₁₂ is just e₁ e₁ e₂ which we know e₁² = 1, so e₁ e₁ e₂ = e₂

flipping planes

Remember how a plane is ccw when its positive and cw when negative? Thats helpful now because what happens when you flip e₁₂ to e₂₁ ? The rotation flips! But thats not what we want! We may want to keep plane e₁₂ but rotate -90° on it! And thats the same as going e₂₁! And vica versa rotating -90° on a cw e₂₁ plane ... that's just 90° ccw on e₁₂!

So we can know: e₁₂ = -e₂₁

Thats handy because we can now solve stuff like

e₁ e₂₁ thats e₁ -e₁₂ so e₁ -e₁ e₂ = -(1)e₂

back to algebra

Knowing these we can now write the vector * rotor multiplication to axis and planes. I will keep R[0] and R[1] but add R[1]e₁₂ here because here 1 is y in 2D, but in 3D and up its not R[2] is z - but all planes numbered. In 3D a Rotor is [s, e₁₂, e₁₃, e₂₃] and in 4D its [s, e₁₂, e₁₃, e₁₄, e₂₃, e₂₄, e₃₄, e₁₂₃₄] so you can see ... x,y,z,w etc. wont hold up there much.

Use axis for vectors and nothing/scalars or planes for rotors!

vR = (vₓ e₁ * R[0]) + (vₓ e₁ R[1] e₁₂) + (vᵧ e₂ R[0]) + (vᵧ e₂ R[1] e₁₂)

now thats better.

Here are all candidates to work on. Its simple now - but imagine in 4D - that will not be 2 * 2 terms. But ... umm many more i dono...

So first 2D, then 3D (3 * 4)

Distribute:

vR = (vₓ e₁ * R[0]) + (vₓ e₁e₁₂ R[1]) + (vᵧ e₂ R[0]) + (vᵧ e₂e₁₂ R[1])

solve e₁e₁₂ (e₂) and e₂e₁₂ (-e₁)

vR = (vₓ e₁ * R[0]) + (vₓ e₂ R[1]) + (vᵧ e₂ R[0]) + (vᵧ -e₁ R[1])

shuffle a bit so we get the e₁ and e₂ near each other

vR = (vₓ e₁ * R[0]) + (vᵧ -e₁ R[1]) + (vₓ e₂ R[1]) + (vᵧ e₂ R[0])

now factor out the e axes

v' = vR = e₁ (vₓ R[0] - vᵧ R[1]) + e₂ (vₓ R[1] + vᵧ R[0])

now thats just 2 multiplications of your vector and rotor components and thats it - a rotated v!

If you think 'hhhhmmmmm that looks familiar' thats because it is - its the rotation matrix!

cos θ, -sin θ
sin θ, cos θ

Now do it again, but in reverse!

Ok so this gives us the rotation matrix we know is ccw. Wanna do Rv to show this will be cw?

HELL YES!!!

So Rv = (R[0] + R[1]) * (vₓ + vᵧ)

Rv = R[0] vₓ + R[1] vₓ + R[0] vᵧ + R[1] vᵧ

Again we put in planes and axes

Rv = (R[0] * vₓ e₁) + (R[1] e₁₂ * vₓ e₁) + (R[0] * vᵧ e₂) + (R[1] e₁₂ * vᵧ e₂ )

Distribute. Note the e₁₂e₁ and e₁₂e₂

Rv = (R[0] * vₓ e₁) + (R[1] e₁₂e₁ vₓ) + (R[0] vᵧ e₂) + (R[1] e₁₂e₂ vᵧ)

You can already smell it - the anticommutivity of the geometric product will be different!

e₁₂e₁ = e₁ e₂ e₁ = -e₂ and e₁₂e₂ = e₁ e₂ e₂ = e₁ heresy!! its flipped!!!

Rv = (R[0] * vₓ e₁) + (R[1] -e₂ vₓ) + (R[0] vᵧ e₂) + (R[1] e₁ vᵧ)

reorder

Rv = (R[0] * vₓ e₁) + (R[1] e₁ vᵧ) + (R[1] -e₂ vₓ) + (R[0] vᵧ e₂)

factor out both axes

v' = Rv = e₁ (R[0] vₓ + R[1] vᵧ) + e₂ (-R[1] vₓ + R[0] vᵧ)

Tadaaaa

cos(θ), sin(θ)
-sin(θ), cos(θ)

Thats a cw rotation!!

You still have not shown me how to get u so i can rotate

Thats not true, you already have everything you need - you just dont know it yet.

Because multiplying by a rotor will (after doing witchcraft / algebra on that) literally end up as an instruction to do:

- find u_para and u_perp, then do u_para - u_perp

So even though you multiply vR ... that ends up being vector_proj v onto R (u_para) - vector_perp from v onto R.

And thats it!

Build a rotor just from 1, not 2 vectors.

Imagine you want to rotate by a vector. So turn it into a rotor

Building the rotor is just like multiplying two vectors where one is the basis vector e₁ so the x-axis.

And that's just [1,0] for the second vector to multiply with.

Remember we want the second vector (u) AGAIN on the LEFT

uv  = uₓvₓ(e₁e₁) + uₓvᵧ(e₁e₂) + uᵧvₓ(e₂e₁) + uᵧvᵧ(e₂e₂)
         1            e₁e₂        -e₁e₂       1

and when thats [1,0] its simple because 2 terms (uᵧ) are already 0

uv  = vₓ + vᵧ e₁₂

now half the angle again easy - add their length. |u| is ofc 1

R = (|v| + vₓ) + vᵧ e₁₂

Build a rotor from an angle

This one is reaaally easy as

R = cos (α) + sin(α) e₁₂

Now look closely and notice theres no magnitude of a vector up front. Its there, but it's 1!

Because we want the half angle to rotate around we simple put θ/2 as the angle

R = cos (θ/2) + sin(θ/2) e₁₂

Thats all there is to it! This rotor is automatically normalized already, no need to double the length!

Normalizing a rotor

you can normalize a rotor by doing

uv / |uv|

thats possible because both s and e₁₂ are prepended by a scalar. Helpful as when using non-normalized rotors, things get ugly.

That's not the |u| |v| in front when looking at the polar or euler form. You want the magnitude of the rotor.

if you have the cos scalar and sin bivector part it's just them squared and added, then sqroot:

|uv| = |R| = √( (u·v)² + (u∧v)²)

The problem here a technically you omit the e₁₂ part that squares to -1 ! You can do so in code, its the correct result, but technically it's cheated

So how does GA do it without cheating? By reversing the rotor. So it's defined as multiplying the rotor with its own reverse:

|R|² := RR̃

Reversing a rotor and inversing (⁻¹) are not the exact same thing.

R̃ = scalar - bivector e₁₂

instead of

R = scalar + bivector e₁₂

Let's multiply them

|R|² = (s + b e₁₂) (s - b e₁₂)

You damn well know this can be done shorter with (a+b)(a-b) from hs (we can skip s b e₁₂ - s b e₁₂ )

|R|² = s² - (b² (e₁₂)²)

Now e₁₂ will square to -1 and thus turn b to plus:

|R|² = s² + b²

And well that's the exact same algo - but now without cheating.

Did you know not cheating will keep you sheltered in a walled garden that needs no bricks? You might need it! So even when no one is looking, remember - physics don't need to lie, cheat and trick. So i can totally recommend going the extra mile just so you don't need to cheat.

Rotor-Rotor multiplication

R1 R2

this again will multiply on the left so a rotor would rotate another in ccw

R1 = s1 + bv1 e₁₂

R2 = s2 + bv2 e₁₂

hs algebra binomal theorem!

R1 * R2 = 
            s1 * s2
        +   s1 * bv2 e₁₂
        +   bv1 e₁₂ * s2
        +   bv1 e₁₂ * bv2 e₁₂

Regroup

R1 * R2 = 
            s1 * s2
        +   s1 * bv2 e₁₂
        +   bv1 * s2 e₁₂
        +   bv1 * bv2 e₁₂ e₁₂

Now remember our plane e₁₂² will be -1

R1 * R2 = 
            s1 * s2
        +   s1 * bv2 e₁₂
        +   bv1 * s2 e₁₂
        -   bv1 * bv2

Regroup the now-a-scalar bivector² and factor out e₁₂

R1 * R2 = 
            s1 * s2
        -   bv1 * bv2
        +   (s1 * bv2 + bv1 * s2) e₁₂

R1 * R2 = (s1 * s2 - bv1 * bv2) + (s1 * bv2 + bv1 * s2) e₁₂

See, that easy and again we see its a rotation matrix ccw!

3D

Now you are ready to totally slash down your gimbal locked matrix multiplication 3D rotations and still dont have to deal with quaternions.

BUT while you might be now in the super cool GA clique with their not-so-obvious shibboleths and tokens of recogniton - remember talking to normies is a lifeskill.

TotallyAI

So what are quaternions actually? Remember

i² = j² = k² = ijk + -1

?

I had to learn THIS to not have gimbal locks at first:

https://eater.net/quaternions/video/stereo4d

Dude...

Going to 4D to rotate in 3D sounds super stressful to you? It totally is! And its not even 4D.

See how we can already rotate in 2D without having to introduce some weird 3rd axis and just use the 2D plane?

Well that's the same in 3D AND YOU DO NOT END UP IN 4D LIKE THE VIDEO STATES.

No 4D in 3D

Thats not how it works. We have 4 values here but these are not x y z w like in 4D - it's just a visualization. Do not be confused, you need none of these spheres!!!

Lets map these 4 values to what they actually are in GA and then we'll talk about the ⁻¹ inverse and why gimbal locked platos prisoners normies think its 4D while its not.

  • i - the complex plane we already dealt with in 2D - its just e₁₂ so the plane spanning from x to y!

  • j - now this is the second plane x to z - STILL A 2D PLANE - and you guessed it e₁₃ in GA

  • k - the last 2D plane e₂₃

it's super important to understand these are not the 3 3D axes but 3 2D planes - and that's also why these always square to -1. No need to think about signature and STA yet! All AXES still square to 1!

  • i, j, k == e₁₂, e₁₃, e₂₃ - 3 2D |_ PLANES _| squaring to -1

  • x, y, z = e₁, e₂, e₃ - 3 AXES squaring to 1

That's already all you need to go waterfall rotate

So let's just answer what you most likely want to do in a 3D engine: Rotating one or multiple vectors.

Now imagine a very simple 3D vector - a 3D camera.

  • Around X: It can look up/down, but what is the PLANE you rotate in? Its YZ! So e₂₃

  • Around Y: Looking left and right, but ahead so with Z. So thats XZ e₁₃

  • Around Z: Also has a roll (kinda also left and right but with Y!) and thats the plane XY / e₁₂

as you can see we can just define all these angles out of thin air but for the scalar we would need to ... well what?

R₃ = scalar + plane e₁₂ + plane e₁₃ + plane e₂₃

Start with 2 3D vectors to build a rotor like in 2D

To understand the beauty of GA realize that any 2 3D vectors will lie in a plane between them. Thats the diff we got in 2D from uv and its the same here. Sadly there is no subscript z nor w in unicode so we will use ₁₂₃.. not ₓᵧ..

v = v₁e₁ + v₂e₂ + v₃e₃

u = u₁e₁ + u₂e₂ + u₃e₃

We will skip the half angle and normalization at first like in 2D - you dont want 2 or 3 operators in one go. Just 1.

uv = u · v + u ∧ v

u · v = (u₁ * v₁ + u₂ * v₂ + u₃ * v₃)

Dot is easy. There are rules you COULD follow for 3D thats similar to a matrix determinant... but we like the exercise.

u ∧ v = (u₁e₁ + u₂e₂ + u₃e₃) ∧ (v₁e₁ + v₂e₂ + v₃e₃)

Still ... you can kinda see e₁e₁ will be 0 ... so skip those, but ill add them as 0

u ∧ v = 0        + u₁v₂ e₁₂ + u₁v₃ e₁₃
        u₂v₁ e₂₁ + 0        + u₂v₃ e₂₃
        u₃v₁ e₃₁ + u₃v₂ e₃₂ + 0

You will gain a lot by doing thes manually when you flip STA bits later. Whats more cool than defining your own spacetime signature and deriving the algos by hand?

Now lets flip all planes that are reversed like e₁₃ and e₃₂ and e₂₁

👾 BONUS LEVEL 🃏

In WebGPU you have the freedom to set z axis as MINUS when in front (towards viewer) of the projection plane / screen. Usually its PLUS. Why? I have no idea and think thats stupid. If you look ahead that should be Z PLUS and behind you is Z minus.

Another thing to watch out for which will totally f... you up if you just copy paste the solution from galgebra is that in 3D you need all planes to differ on an edge or your meshes are not closed in 3D and the volume of a cube / trivector would not be positive.

e₁₂ ∧ e₃ = e₁ e₂ e₃                        = e₁₂₃
e₂₃ ∧ e₁ = e₂ e₃ e₁ = -e₂ e₁ e₃ = e₁ e₂ e₃ = e₁₂₃ 
e₃₁ ∧ e₂ = e₃ e₁ e₂ = -e₁ e₃ e₂ = e₁ e₂ e₃ = e₁₂₃

But galgebra will give you e₁₃ - and extruding this with e₂ would totally invert your volume now!

e₁₃ ∧ e₂ = e₁ e₃ e₂ = -e₁ e₂ e₃            = -e₁₂₃ !!!!

So imagine a cube - all 6 sides need to have an orientation in ccw. But where that is will totally differ depending on what you defined z plus as. Then, suddenly you start flipping planes just so your camera rotates correctly. Endless fun. So don't do that!

You still have to map what turning your head left or right (ZX, yaw) will mean because

- in XY e₁₂ ccw is rolling left - good

- in YZ e₂₃ ccw is looking DOWN

- in ZX e₃₁ ccw is looking RIGHT - you will need to account for that!!

Oriented cube

Take a piece of paper and draw the orientations of the planes of your cube when x y z will be plus.

If you dont want that use YOUR LEFT HAND

- Thumb to right - X

- Index up - Y

- Middle Finger ahead - Z

Now act like you would try to fit around a cube like in the drawing: Y Index stays up, Thumb goes \ and Middle goes /

Now you can see that for all these to be ccw you need e₁₂, e₂₃ and e₃₁

galgebra will go e₁₃ on you and thus you will later need to flip all planes involving Y. Use e₃₁ !!!!

BONUS LEVEL FINISHED 🎀

Sooo long story short - use Z plus ahead and e₃₁!

u ∧ v = 0        + u₁v₂ e₁₂ + u₁v₃ e₁₃
      + u₂v₁ e₂₁ + 0        + u₂v₃ e₂₃
      + u₃v₁ e₃₁ + u₃v₂ e₃₂ + 0

now let's flip to our choice of e₁₂ e₂₃ e₃₁

u ∧ v = 0        + u₁v₂ e₁₂ - u₁v₃ e₃₁     ( 0  1 -1)
      - u₂v₁ e₁₂ + 0        + u₂v₃ e₂₃     (-1  0  1)
      + u₃v₁ e₃₁ - u₃v₂ e₂₃ + 0            ( 1 -1  0)

now lets order these so we get the planes in order of e₁₂ e₂₃ e₃₁

u ∧ v = u₁v₂ e₁₂ - u₂v₁ e₁₂
      + u₂v₃ e₂₃ - u₃v₂ e₂₃
      + u₃v₁ e₃₁ - u₁v₃ e₃₁

Ain't it beautiful? no remembering cross products needed! You can do it all from knowing eₙ² is 0 and how a plane flips!

And theres even the levi civita permutation tensor (0,1,-1 etc), a term the ai told me while proofreading and a term i will instantly forget again.

u · v + u ∧ u

Let's put it all together now

 uv = u · v + u ∧ v =

     (u₁v₁ + u₂v₂ + u₃v₃)
    + u₁v₂ e₁₂ - u₂v₁ e₁₂
    + u₂v₃ e₂₃ - u₃v₂ e₂₃
    + u₃v₁ e₃₁ - u₁v₃ e₃₁

Sherical coordinates

To understand better what the 3 bivector values actually represent please head over to 780-SO

vR in 3D

vR = (v₁ * e₁ + v₂ * e₂ + v₃ * e₃) * (R[0] + R[1] e₁₂ + R[2] e₂₃ + R[3] e₃₁)

Binom

vR  = 
        v₁e₁ R[0] + v₁e₁ R[1]e₁₂ + v₁e₁ R[2]e₂₃ + v₁e₁ R[3]e₃₁
    +   v₂e₂ R[0] + v₂e₂ R[1]e₁₂ + v₂e₂ R[2]e₂₃ + v₂e₂ R[3]e₃₁
    +   v₃e₃ R[0] + v₃e₃ R[1]e₁₂ + v₃e₃ R[2]e₂₃ + v₃e₃ R[3]e₃₁

Solve all eₘ eₘₙ - remember eₘ² is 1

vR  = 
        v₁e₁ R[0]   + v₁ R[1]e₂       + v₁e₁ R[2]e₂₃  - v₁ R[3]e₃
    +   v₂e₂ R[0]   - v₂ R[1]e₁       + v₂ R[2]e₃     + v₂e₂ R[3]e₃₁
    +   v₃e₃ R[0]   + v₃e₃ R[1]e₁₂    - v₃ R[2]e₂     + v₃ R[3]e₁

Trivectors

vR  = 
        v₁e₁ R[0]   + v₁ R[1]e₂     + v₁ R[2]e₁₂₃   - v₁ R[3]e₃
    +   v₂e₂ R[0]   - v₂ R[1]e₁     + v₂ R[2]e₃     + v₂ R[3]e₂₃₁
    +   v₃e₃ R[0]   + v₃ R[1]e₃₁₂   - v₃ R[2]e₂     + v₃ R[3]e₁

Swap Trivectors

vR  = 
        v₁e₁ R[0]   + v₁ R[1]e₂     + v₁ R[2]e₁₂₃   - v₁ R[3]e₃             (no swap)
    +   v₂e₂ R[0]   - v₂ R[1]e₁     + v₂ R[2]e₃     + v₂ R[3]e₁₂₃           (2 swaps e₂₃₁ to -e₂₁₃ to e₁₂₃)
    +   v₃e₃ R[0]   + v₃ R[1]e₁₂₃   - v₃ R[2]e₂     + v₃ R[3]e₁             (2 swaps e₃₁₂ to -e₁₃₂ to e₁₂₃)

Regroup ... wait what, where are my eₘₙ planes???? Mhh well who cares, lets output grade 1,3

Regroup to e₁, e₂, e₃ and e₁₂₃

vR  = 
        v₁ R[0] e₁   - v₂ R[1] e₁   + v₃ R[3] e₁ 
    +   v₁ R[1] e₂   + v₂ R[0] e₂   - v₃ R[2] e₂
    +  -v₁ R[3] e₃   + v₂ R[2] e₃   + v₃ R[0] e₃

    +   v₁ R[2] e₁₂₃ + v₂ R[3] e₁₂₃ + v₃ R[1] e₁₂₃

As you can see multiplying our rotor with a vector on the left results in a grade 1,3 multivector. We need grade 1 only though!

So how can we do that?

INTRODUCE THE SANDWICH

Now to do that you literally multiply again by the reverse rotor R̃ (just rign flip all planes)

̃ R̃vR = (R[0] - R[1]e₁₂ - R[2]e₂₃ - R[2]e₃₁) * (vR)

    =
        R[0] v₁ R[0] e₁   - R[0] v₂ R[1] e₁   + R[0] v₃ R[3] e₁ 
    +   R[0] v₁ R[1] e₂   + R[0] v₂ R[0] e₂   - R[0] v₃ R[2] e₂
    +  -R[0] v₁ R[3] e₃   + R[0] v₂ R[2] e₃   + R[0] v₃ R[0] e₃
    +   R[0] v₁ R[2] e₁₂₃ + R[0] v₂ R[3] e₁₂₃ + R[0] v₃ R[1] e₁₂₃

    +   -R[1]e₁₂ v₁ R[0] e₁   - -R[1]e₁₂ v₂ R[1] e₁   + -R[1]e₁₂ v₃ R[3] e₁ 
    +   -R[1]e₁₂ v₁ R[1] e₂   + -R[1]e₁₂ v₂ R[0] e₂   - -R[1]e₁₂ v₃ R[2] e₂
    +  --R[1]e₁₂ v₁ R[3] e₃   + -R[1]e₁₂ v₂ R[2] e₃   + -R[1]e₁₂ v₃ R[0] e₃
    +   -R[1]e₁₂ v₁ R[2] e₁₂₃ + -R[1]e₁₂ v₂ R[3] e₁₂₃ + -R[1]e₁₂ v₃ R[1] e₁₂₃

    +   -R[2]e₂₃ v₁ R[0] e₁   - -R[2]e₂₃ v₂ R[1] e₁   + -R[2]e₂₃v₃ R[3] e₁ 
    +   -R[2]e₂₃ v₁ R[1] e₂   + -R[2]e₂₃ v₂ R[0] e₂   - -R[2]e₂₃v₃ R[2] e₂
    +  --R[2]e₂₃ v₁ R[3] e₃   + -R[2]e₂₃ v₂ R[2] e₃   + -R[2]e₂₃ v₃ R[0] e₃
    +   -R[2]e₂₃ v₁ R[2] e₁₂₃ + -R[2]e₂₃ v₂ R[3] e₁₂₃ + -R[2]e₂₃ v₃ R[1] e₁₂₃

    +   -R[3]e₃₁ v₁ R[0] e₁   - -R[3]e₃₁ v₂ R[1] e₁   + -R[3]e₃₁ v₃ R[3] e₁ 
    +   -R[3]e₃₁ v₁ R[1] e₂   + -R[3]e₃₁ v₂ R[0] e₂   - -R[3]e₃₁ v₃ R[2] e₂
    +  --R[3]e₃₁ v₁ R[3] e₃   + -R[3]e₃₁ v₂ R[2] e₃   + -R[3]e₃₁ v₃ R[0] e₃
    +   -R[3]e₃₁ v₁ R[2] e₁₂₃ + -R[3]e₃₁ v₂ R[3] e₁₂₃ + -R[3]e₃₁ v₃ R[1] e₁₂₃

i like the pain!

        R[0] v₁ R[0] e₁   - R[0] v₂ R[1] e₁   + R[0] v₃ R[3] e₁ 
    +   R[0] v₁ R[1] e₂   + R[0] v₂ R[0] e₂   - R[0] v₃ R[2] e₂
    +  -R[0] v₁ R[3] e₃   + R[0] v₂ R[2] e₃   + R[0] v₃ R[0] e₃
    +   R[0] v₁ R[2] e₁₂₃ + R[0] v₂ R[3] e₁₂₃ + R[0] v₃ R[1] e₁₂₃

    +   -R[1]e₁₂ v₁ R[0] e₁   +  R[1]e₁₂ v₂ R[1] e₁   + -R[1]e₁₂ v₃ R[3] e₁ 
    +   -R[1]e₁₂ v₁ R[1] e₂   + -R[1]e₁₂ v₂ R[0] e₂   +  R[1]e₁₂ v₃ R[2] e₂
    +    R[1]e₁₂ v₁ R[3] e₃   + -R[1]e₁₂ v₂ R[2] e₃   + -R[1]e₁₂ v₃ R[0] e₃
    +   -R[1]e₁₂ v₁ R[2] e₁₂₃ + -R[1]e₁₂ v₂ R[3] e₁₂₃ + -R[1]e₁₂ v₃ R[1] e₁₂₃

    +   -R[2]e₂₃ v₁ R[0] e₁   +  R[2]e₂₃ v₂ R[1] e₁   + -R[2]e₂₃ v₃ R[3] e₁ 
    +   -R[2]e₂₃ v₁ R[1] e₂   + -R[2]e₂₃ v₂ R[0] e₂   +  R[2]e₂₃ v₃ R[2] e₂
    +    R[2]e₂₃ v₁ R[3] e₃   + -R[2]e₂₃ v₂ R[2] e₃   + -R[2]e₂₃ v₃ R[0] e₃
    +   -R[2]e₂₃ v₁ R[2] e₁₂₃ + -R[2]e₂₃ v₂ R[3] e₁₂₃ + -R[2]e₂₃ v₃ R[1] e₁₂₃

    +   -R[3]e₃₁ v₁ R[0] e₁   +  R[3]e₃₁ v₂ R[1] e₁   + -R[3]e₃₁ v₃ R[3] e₁ 
    +   -R[3]e₃₁ v₁ R[1] e₂   + -R[3]e₃₁ v₂ R[0] e₂   +  R[3]e₃₁ v₃ R[2] e₂
    +    R[3]e₃₁ v₁ R[3] e₃   + -R[3]e₃₁ v₂ R[2] e₃   + -R[3]e₃₁ v₃ R[0] e₃
    +   -R[3]e₃₁ v₁ R[2] e₁₂₃ + -R[3]e₃₁ v₂ R[3] e₁₂₃ + -R[3]e₃₁ v₃ R[1] e₁₂₃

Solve all eₘ eₘₙ and eₘ e₁₂₃

        R[0] v₁ R[0] e₁   - R[0] v₂ R[1] e₁   + R[0] v₃ R[3] e₁ 
    +   R[0] v₁ R[1] e₂   + R[0] v₂ R[0] e₂   - R[0] v₃ R[2] e₂
    +  -R[0] v₁ R[3] e₃   + R[0] v₂ R[2] e₃   + R[0] v₃ R[0] e₃
    +   R[0] v₁ R[2] e₁₂₃ + R[0] v₂ R[3] e₁₂₃ + R[0] v₃ R[1] e₁₂₃

    +    R[1]e₂ v₁ R[0]   -  R[1]e₂ v₂ R[1]   + R[1]e₂ v₃ R[3] 
    +   -R[1]e₁ v₁ R[1]   + -R[1]e₁ v₂ R[0]   +  R[1]e₁ v₃ R[2]
    +    R[1]e₁₂₃ v₁ R[3]   + -R[1]e₁₂₃ v₂ R[2]   + -R[1]e₁₂₃ v₃ R[0]

    // thats e₁₂ * e₁₂₃ so you go e₁ e₂ e₁ e₂ e₃. e₁₂₁₂₃ swap ONCE -e₁₂₂₁₃ to -e3 !
    +    R[1] v₁ R[2]e₃ + R[1] v₂ R[3] e₃ + R[1] v₃ R[1] e₃

    // 2 swaps no sign change e₂₃ e₁ = e₂₃₁ = -e₂₁₃ = e₁₂₃
    +   -R[2]e₁₂₃ v₁ R[0]   +  R[2]e₁₂₃ v₂ R[1]   + -R[2]e₁₂₃ v₃ R[3] 
    // 1 swap
    +    R[2]e₃ v₁ R[1]   + R[2]e₃ v₂ R[0]   -  R[2]e₃ v₃ R[2]
    // 0 swaps
    +    R[2]e₂ v₁ R[3]   + -R[2]e₂ v₂ R[2]   + -R[2]e₂ v₃ R[0]
    // e₂₃₁₂₃ = -e₂₃₁₃₂ = e₂₃₃₁₂ = -e₂₃₃₂₁ = -e₁
    +    R[2] v₁ R[2]e₁ + R[2] v₂ R[3] e₁ + R[2] v₃ R[1] e₁

    // no swap, 1
    +   -R[3]e₃ v₁ R[0]   +  R[3]e₃ v₂ R[1]   + -R[3]e₃ v₃ R[3] 
    // e₃₁₂ = -e₁₃₂ = e₁₂₃
    +   -R[3]e₁₂₃ v₁ R[1]   + -R[3]e₁₂₃ v₂ R[0]   +  R[3]e₁₂₃ v₃ R[2]
    // 1 swap
    +    -R[3]e₁ v₁ R[3]   + R[3]e₁ v₂ R[2]   + R[3]e₁ v₃ R[0]
    // e₃₁₁₂₃ = e₃₂₃ = -e₃₃₂ = -e2
    +   R[3] v₁ R[2] e₂ + R[3] v₂ R[3] e₂ + R[3] v₃ R[1] e₂

lets find all trivectors first and whether they sum to 0 or we did some serious errors

+r0v1r2 +r0v2r3 +r0v3r1
+r1v1r3 -r1v2r2 -r1v3r0
-r2v1r0 +r2v2r1 -r2v3r3
-r3v1r1 -r3v2r0 +r3v3r2

reorder to rrv and sort r

+r0r2v1 +r0r3v2 +r0r1v3
+r1r3v1 -r1r2v2 -r0r1v3
-r0r2v1 +r1r2v2 -r2r3v3
-r1r3v1 -r0r3v2 +r2r3v3

mark duplicates

1 +r0r2v1 2 +r0r3v2 3 +r0r1v3
4 +r1r3v1 5 -r1r2v2 3 -r0r1v3
1 -r0r2v1 5 +r1r2v2 6 -r2r3v3
4 -r1r3v1 2 -r0r3v2 6 +r2r3v3

all match, no errors! remove all trivectors and clean a little

         R[0] v₁ R[0] e₁  - R[0] v₂ R[1] e₁   +  R[0] v₃ R[3] e₁ 
    +    R[0] v₁ R[1] e₂  + R[0] v₂ R[0] e₂   -  R[0] v₃ R[2] e₂
    +   -R[0] v₁ R[3] e₃  + R[0] v₂ R[2] e₃   +  R[0] v₃ R[0] e₃

    +    R[1]e₂ v₁ R[0]   -  R[1]e₂ v₂ R[1]   + R[1]e₂ v₃ R[3] 
    +   -R[1]e₁ v₁ R[1]   + -R[1]e₁ v₂ R[0]   +  R[1]e₁ v₃ R[2]
    +    R[1] v₁ R[2]e₃   +  R[1] v₂ R[3] e₃  + R[1] v₃ R[1] e₃

    +    R[2]e₃ v₁ R[1]   +  R[2]e₃ v₂ R[0]   + -R[2]e₃ v₃ R[2]
    +    R[2]e₂ v₁ R[3]   + -R[2]e₂ v₂ R[2]   + -R[2]e₂ v₃ R[0]
    +    R[2] v₁ R[2]e₁   +  R[2] v₂ R[3] e₁  +  R[2] v₃ R[1] e₁

    +   -R[3]e₃ v₁ R[0]   +  R[3]e₃ v₂ R[1]   + -R[3]e₃ v₃ R[3] 
    +   -R[3]e₁ v₁ R[3]   +  R[3]e₁ v₂ R[2]   +  R[3]e₁ v₃ R[0]
    +    R[3] v₁ R[2] e₂  +  R[3] v₂ R[3] e₂  +  R[3] v₃ R[1] e₂

reorder to R R v plane and sort R

         R[0] R[0] v₁ e₁  - R[0] R[1] v₂ e₁   +  R[0] R[3] v₃ e₁ 
    +    R[0] R[1] v₁ e₂  + R[0] R[0] v₂ e₂   -  R[0] R[2] v₃ e₂
    +   -R[0] R[3] v₁ e₃  + R[0] R[2] v₂ e₃   +  R[0] R[0] v₃ e₃

    +    R[0] R[1] v₁ e₂  -  R[1] R[1] v₂ e₂  +  R[1] R[3] v₃ e₂
    +   -R[1] R[1] v₁ e₁  + -R[0] R[1] v₂ e₁  +  R[1] R[2] v₃ e₁
    +    R[1] R[2] v₁ e₃  +  R[1] R[3] v₂ e₃  +  R[1] R[1] v₃ e₃

    +    R[1] R[2] v₁ e₃  +  R[0] R[2] v₂ e₃  + -R[2] R[2] v₃ e₃ 
    +    R[2] R[3] v₁ e₂  + -R[2] R[2] v₂ e₂  + -R[0] R[2] v₃ e₂ 
    +    R[2] R[2] v₁ e₁  +  R[2] R[3] v₂ e₁  +  R[1] R[2] v₃ e₁

    +   -R[0] R[3] v₁ e₃  +  R[1] R[3] v₂ e₃  + -R[3] R[3] v₃ e₃ 
    +   -R[3] R[3] v₁ e₁  +  R[2] R[3] v₂ e₁  +  R[0] R[3] v₃ e₁ 
    +    R[2] R[3] v₁ e₂  +  R[3] R[3] v₂ e₂  +  R[1] R[3] v₃ e₂

find matches

      R[0] R[0] v₁ e₁   - R[0] R[1] v₂ e₁   + R[0] R[3] v₃ e₁  ( n  1  2)
    + R[0] R[1] v₁ e₂   + R[0] R[0] v₂ e₂   - R[0] R[2] v₃ e₂  ( 3  n  4)
    - R[0] R[3] v₁ e₃   + R[0] R[2] v₂ e₃   + R[0] R[0] v₃ e₃  ( 5  6  n)

    +  R[0] R[1] v₁ e₂  -  R[1] R[1] v₂ e₂  + R[1] R[3] v₃ e₂  ( 3  n  7)
    -  R[1] R[1] v₁ e₁  -  R[0] R[1] v₂ e₁  + R[1] R[2] v₃ e₁  ( n  1  8)
    +  R[1] R[2] v₁ e₃  +  R[1] R[3] v₂ e₃  + R[1] R[1] v₃ e₃  ( 9 10  n)

    +  R[1] R[2] v₁ e₃  + R[0] R[2] v₂ e₃   - R[2] R[2] v₃ e₃  ( 9  6  n)
    +  R[2] R[3] v₁ e₂  - R[2] R[2] v₂ e₂   - R[0] R[2] v₃ e₂  (11  n  4)
    +  R[2] R[2] v₁ e₁  + R[2] R[3] v₂ e₁   + R[1] R[2] v₃ e₁  ( n 12  8)

    -  R[0] R[3] v₁ e₃  + R[1] R[3] v₂ e₃   - R[3] R[3] v₃ e₃  ( 5 10  n)
    -  R[3] R[3] v₁ e₁  + R[2] R[3] v₂ e₁   + R[0] R[3] v₃ e₁  ( n 12  2)
    +  R[2] R[3] v₁ e₂  + R[3] R[3] v₂ e₂   + R[1] R[3] v₃ e₂  (11  n  7)

there are 12 matches so thats where those 2(rrv)eₘ terms come from!

Notice that all terms with no match have their rotor component squared. And all that match have a 2x term

So to do a looooong stretch lets build it from grade 1s up

R̃vR  = 

    e₁ part = 
            (R[0]² v₁ e₁) - 2(R[0] R[1] v₂ e₁) + 2(R[0] R[3] v₃ e₁) - (R[1]² v₁ e₁) + 2(R[1] R[2] v₃ e₁) + (R[2]² v₁ e₁) + 2(R[2] R[3] v₂ e₁) - (R[3]² v₁ e₁)

    e₂ part = 
            2(R[0] R[1] v₁ e₂) + (R[0]² v₂ e₂) - 2(R[0] R[2] v₃ e₂) - (R[1]² v₂ e₂) + 2(R[2] R[3] v₁ e₂) - (R[2]² v₂ e₂) + 2(R[1] R[3] v₃ e₂) + (R[3]² v₂ e₂)

    e₃ part =
           -2(R[0] R[3] v₁ e₃) + 2(R[0] R[2] v₂ e₃) + (R[0]² v₃ e₃) + 2(R[1] R[2] v₁ e₃) + 2(R[1] R[3] v₂ e₃) + (R[1]² v₃ e₃) - (R[2]² v₃ e₃) - (R[3]² v₃ e₃)

In Heidelberg this is correct, Doran will use RvR̃ however. Depends on how you build your rotor.

Note this is untested!!

lets clean those up so we know which vector part to multipy with easier

        e₁ = v₁ * (R[0]² - R[1]² + R[2]² - R[3]²) + v₂ * 2(R[2] R[3] - R[0] R[1]) + v₃ * 2(R[0] R[3] + R[1] R[2])

        e₂ = v₂ * (R[0]² - R[1]² - R[2]² + R[3]²) + v₁ * 2(R[0] R[1] + R[2] R[3]) + v₃ * 2(R[1] R[3] - R[0] R[2]) 

        e₃ = v₃ * (R[0]² + R[1]² - R[2]² - R[3]²) + v₁ * 2(R[1] R[2] - R[0] R[3]) + v₂ * 2(R[0] R[2] + R[1] R[3]) 

R×R

R2*R1 = (R2[0] + R2[1] e₁₂ + R2[2] e₂₃ + R2[3] e₃₁) * (R1[0] + R1[1] e₁₂ + R1[2] e₂₃ + R1[3] e₃₁) 

    =   
        R2[0]     R1[0]     + R2[0]     R1[1] e₁₂   + R2[0]     R1[2] e₂₃   + R2[0]     R1[3] e₃₁
    +   R2[1] e₁₂ R1[0]     + R2[1] e₁₂ R1[1] e₁₂   + R2[1] e₁₂ R1[2] e₂₃   + R2[1] e₁₂ R1[3] e₃₁
    +   R2[2] e₂₃ R1[0]     + R2[2] e₂₃ R1[1] e₁₂   + R2[2] e₂₃ R1[2] e₂₃   + R2[2] e₂₃ R1[3] e₃₁
    +   R2[3] e₃₁ R1[0]     + R2[3] e₃₁ R1[1] e₁₂   + R2[3] e₃₁ R1[2] e₂₃   + R2[3] e₃₁ R1[3] e₃₁

You can see its not the worst mindbender this time.

Lets clean all eₘₙ² to -1

R2*R1 =   
        R2[0]     R1[0]     + R2[0]     R1[1] e₁₂   + R2[0]     R1[2] e₂₃   + R2[0]     R1[3] e₃₁
    +   R2[1] e₁₂ R1[0]     - (R2[1] R1[1])         + R2[1] e₁₂ R1[2] e₂₃   + R2[1] e₁₂ R1[3] e₃₁
    +   R2[2] e₂₃ R1[0]     + R2[2] e₂₃ R1[1] e₁₂   - (R2[2] R1[2])         + R2[2] e₂₃ R1[3] e₃₁
    +   R2[3] e₃₁ R1[0]     + R2[3] e₃₁ R1[1] e₁₂   + R2[3] e₃₁ R1[2] e₂₃   - (R2[3] R1[3])

Lets swap all plane multiplications

R2*R1 =   
        R2[0]     R1[0]     + R2[0]     R1[1] e₁₂   + R2[0]     R1[2] e₂₃   + R2[0]     R1[3] e₃₁
    //                                        easy  but e₁₃ = -e₃₁            thats e₁₂₃₁ = -e₁₂₁₃ = e₁₁₂₃    
    +   R2[1] e₁₂ R1[0]     - (R2[1] R1[1])         - R2[1]     R1[2] e₃₁   + R2[1]     R1[3] e₂₃
    //                      e₂₃₁₂ = -e₂₃₂₁ = e₃₂₂₁                             easy but swap e₂₁ to -e₁₂
    +   R2[2] e₂₃ R1[0]     + R2[2]     R1[1] e₃₁   - (R2[2] R1[2])         - R2[2]    R1[3] e₁₂
    //                      easy but swap e₃₂ to -e₂₃ ... e₃₁₂₃ = -e₃₁₃₂ = e₃₃₁₂
    +   R2[3] e₃₁ R1[0]     - R2[3]     R1[1] e₂₃   + R2[3]     R1[2] e₁₂   - (R2[3] R1[3])

no notes but clean to r r (e)

R2*R1 =   
        R2[0] R1[0]     + R2[0] R1[1] e₁₂ + R2[0] R1[2] e₂₃   + R2[0] R1[3] e₃₁
    +   R2[1] R1[0] e₁₂ - R2[1] R1[1]     - R2[1] R1[2] e₃₁   + R2[1] R1[3] e₂₃
    +   R2[2] R1[0] e₂₃ + R2[2] R1[1] e₃₁ - R2[2] R1[2]       - R2[2] R1[3] e₁₂
    +   R2[3] R1[0] e₃₁ - R2[3] R1[1] e₂₃ + R2[3] R1[2] e₁₂   - R2[3] R1[3]

as only scalars and planes are left lets check whether any are matches. There seem to be no matches. Thats technically not a surprise as we need all 4x4 information combinations i think?

So i think we can combine to the new rotor

R2*R1 =   
    (s)    R2[0] R1[0] - R2[1] R1[1] - R2[2] R1[2] - R2[3] R1[3]
    + e₁₂ (R2[0] R1[1] + R2[1] R1[0] - R2[2] R1[3] + R2[3] R1[2])
    + e₂₃ (R2[0] R1[2] + R2[1] R1[3] + R2[2] R1[0] - R2[3] R1[1])
    + e₃₁ (R2[0] R1[3] - R2[1] R1[2] + R2[2] R1[1] + R2[3] R1[0])    

Done.

Current PTF

Hintergrund ändern. Verbraucht keinen oder einen 🍪.

Verknüpften Viewport öffnen

🎮 Steuerung
Dokumentation 🕹️
Sie sind leider kein Entwickler :(

Content Nodes Amount

Diligence / PTF Amount

FPS

Vertex-Count