1

I'm trying to understand quaternions and am having a lot of trouble converting it into something actually useful. I've watched some 3blue1brown videos and read some tutorials, but I can't seem to translate what I see into something simple which means I don't really understand it at all.

Starting with the simplest example I can think of, I want to start with a point at xyz (1, 0, 0) and rotate 90 degrees around the Z axis. This should give me the point (0, 1, 0). Using trigonometry, doing the rotation is simple (doSimple()).

As I said I am lacking in understanding of the fundamentals of quaternions, but from what I understand a point in 3d space can be represented using x, y, z and 0 for the w. And gl-matrix has a 'quat.rotateZ' method that seems like it should rotate around that axis by an angle. My results seem to indicate that it only rotates by 1/2 the angle and in the wrong direction (repl.it):

const { quat } = require('gl-matrix');

const doSimple = () => {
  // simple using trig:
  const x = 1, y = 0;
  const angle = Math.PI / 2; // 15 degrees
  const newX = x * Math.cos(angle) - y * Math.sin(angle);
  const newY = x * Math.sin(angle) + y * Math.cos(angle);
  console.log({ angle, x, y, newX, newY });
}

const doQuat = () => {
  let [x, y, z, w] = [1, 0, 0, 0];
  const a = quat.fromValues(x, y, z, w);
  const ninetyDegrees = Math.PI / 2;
  const b = quat.create();
  quat.rotateZ(b, a, ninetyDegrees);
  const results = { a, b }
  console.log(results)
}

const doAngle = () => {
  let [x1, y1, z1, w1] = [1, 0, 0, 0];
  let [x2, y2, z2, w2] = [0, 1, 0, 0];
  const q1 = quat.fromValues(x1, y1, z1, w1);
  const q2 = quat.fromValues(x2, y2, z2, w2);
  const angle = quat.getAngle(q1, q2);
  console.log({ angle });
}

doSimple();
doQuat();
doAngle();

doSimple() looks good to me:

{
  angle: 1.5707963267948966,
  x: 1,
  y: 0,
  newX: 6.123233995736766e-17,
  newY: 1
}

doQuat() uses quat.rotateZ() and seems to only rotate 45 degrees and in the wrong direction:

{
  a: Float32Array(4) [ 1, 0, 0, 0 ],
  b: Float32Array(4) [ 0.7071067690849304, -0.7071067690849304, 0, 0 ]
}

doAngle() seems to report that there are 180 degrees between (1,0,0) and (0,1,0):

{ angle: 3.141592653589793 }

So I know I'm totally misunderstanding something, but I don't know what it is...

---Edit---

I think a lot of my confusion stems from different terminology in examples. gl-matrix uses x,y,z,w. 3blue1brown uses <blank>, i,j,k. This paper uses q0,q1,q2,q3. This calculator from the paper doesn't seem to make sense in that Yaw rotates around the Z axis, Pitch around the Y axis, and Roll around the X axis?

If I'm thinking in 3d space the normal way I think of things would be Z facing out of the screen and X and Y representing a normal 2d graph on the screen (X points 'right', Y points 'up'). Yaw should rotate around the Y axis and pitch should rotate around the X axis.

Am I thinking wrong? Are there a set (or sets) of standard ways of looking at the coordinate system, rotations, and quaternions that I should use when trying to learn? For example I think that since gl-matrix uses x,y,z,w and the paper uses q0,q1,q2,q3 and 3b1b uses blank,i,j,k, I think the conversions are as follows:

  • q0 -> blank -> w
  • q1 -> i -> x
  • q2 -> j -> y
  • q3 -> k -> z
1

1 Answer 1

1

I've found out a few things and think I have a good basis now for learning at least. Looking at 3b1b's interactive videos, aligning the axis how I wanted to view them, x and y like a 2d graph and z pointing at me through the screen, understanding that f(i) means a point at x=1, that i represents the x axis, and playing around helped me.

  • (blank,i,j,k) or (q0,q1,q2,q3) are indeed equivilent to (w,x,y,z)
  • Rotating a point involves two multiplications: f(p) = q * p * q-1 so it is a two step process
  • glMatrix's rotateZ function doesn't seem to do what I thought, I don't really know what it does

So this seems to work:

// fromEuler gives quat from X,Y,Z in DEGREES
const euler = quat.fromEuler(quat.create(), 0, 0, 90);

// p is my point, at (1,0,0) with w=0
let [x, y, z, w] = [1, 0, 0, 0];
const p = quat.fromValues(x, y, z, w);

// so I need q (rotation which is euler 90 degrees about Z axis)
// and inverse of q.  Formula:
// f(p) = q * p * qInverse
const q = quat.clone(euler);
const qInverse = quat.invert(quat.create(), q);

// first q * p, then result * qInverse
let pPrime = quat.multiply(quat.create(), q, p);
pPrime = quat.multiply(quat.create(), pPrime, qInverse);

That gives what I was looking for:

{
  euler: Float32Array(4) [ 0, 0, 0.7071067690849304, 0.7071067690849304 ],
  p: Float32Array(4) [ 1, 0, 0, 0 ],
  q: Float32Array(4) [ 0, 0, 0.7071067690849304, 0.7071067690849304 ],
  qInverse: Float32Array(4) [ -0, -0, -0.7071067690849304, 0.7071067690849304 ],
  pPrime: Float32Array(4) [ 0, 0.9999999403953552, 0, 0 ]
}
Ok, from point (1, 0, 0) gives (0, 1, 0)
q (euler rotation 90 degrees about z axis):
  xyzw: (x = 0,y = 0,z = 0.71,w = 0.71)
  ijk: (0.71, i = 0, j = 0, k = 0.71)
qInverse:
  xyzw: (x = 0,y = 0,z = -0.71,w = 0.71)
  ijk: (0.71, i = 0, j = 0, k = -0.71)

Quaternions use angle/2 for sin and cos, why I was getting something like 45 degrees. Now that I can

Sign up to request clarification or add additional context in comments.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.