The Same Quaternion But .angleTo Self Is Not Zero

When I use the Quaternion.rotateTowards() to animate camera, like this:

    function moving(dt) {
      timer += dt
      q1.rotateTowards(q2, dt * turnRate * speed)
      scope.camera.position.set(0, 0, 1).applyQuaternion(q1)
      const currentAngle = q1.angleTo(q2)

      if (q1.angleTo(q2) === 0) {
        scope._movingCamera = null
      }
    }

But the q1.angleTo(q2) === 0 is always false.
The result of q1.angleTo(q2) is a very very small number, so I think it because of the number’s precision.

I write an unit test about this.

	QUnit.test('angleTo', (assert) => {
		const q = new Quaternion();
		q._x = -0.3011968357996212
		q._y = -0.20601814257707293
		q._z = -0.06682031825320903
		q._w = 0.9286398850479817

		assert.ok( q.angleTo( q ) === 0, 'Passed!' )
	})

It is an issue with precision. The calculation of angleTo does 2*acos(x*x + y*y + z*z + w*w).

For the specific values of the quaternion, JavaScript gives:

so, (x*x+...+w*w) is 0.999…9, although it should be 1.

Generally, to safeguard against similar problems, avoid using == with floating point numbers that are results of calculations. Instead, check whether the numbers are close enough.

2 Likes

It was discussed at GitHub to add an additional epsilon parameter to certain math methods (e.g. equals()) but the main issue is agreeing on an appropriate default value. A typical candidate is Number.EPSILON however it does not work in all use cases since it might be too small. A higher value is also hard to pinpoint since you don’t want to pick a too large one. Epsilon values are mainly app specific so try to solve the issue like shown below:

if (q1.angleTo(q2) < Number.EPSILON ) {

If Number.EPSILON doesn’t work, pick whatever epsilon works for you.

2 Likes

Thank you for your prompt reply.
Yeah, finally, I use q1.equals(q2) to fix it. I just want to know whether q1 equals to q2.