Camera animation with quaternion travels undesired path

I have a cube, of which four of the sides can be clicked on. Moving the camera to the position that matches with the side. To achieve the movement of this camera I am using a second camera that moves to the final location of the active camera, and then I tween between the quarternion & position of these two objects. This worked well for the majority of scenario’s, but in a few cases it doesn’t work as desirable. See video below for context. The last animation is how it should be the, the first two are not as desired.

I have been writing the from/to states of the animations on to paper, and tried to discover a pattern that causes this undesired behaviour. But since I lack the basic understanding of quaternions, it makes it hard to understand where to look at precisely (learning curve seems quite steep as well; Quaternion - Wikipedia).

How I understand the problem is as followed;
Assume that you want to draw a dot on a circle, from 0 to 90 degrees. Animating this dot with a tween will move the dot from the top of the circle, to the right of the circle. Viscerally, you would got the same end result when you would change 90 degrees with 450 degrees (360 + 90). But the animation wouldn’t go as intended.

While I feel like this is what is causing the problem I am experiencing, I do not actually know if such looping limits exists with this concept of quaternions. With the resources I have found I am able to make the desired animation, but I can’t find any handles for debugging this problem. So my question is basically; How do I create the “right” quaternion for this camera animation?

The code I am using to execture this specific animation can be found here below, at the “moveTo” function.

Resources found so far:

if you are just going to tween quaternion components you probably want to supply onUpdate handler or equivalent that will call quaternion.normalize(). but, it comes with the built-in method to do what you want: three.js docs

…on a second thought, you probably will still have sideways motion with that since you are tweening a position at the same time. so the best way would be to just call lookAt() continuously along the way

I don’t know if tween.js has any concept of spherical linear interpolation (slerp). If it’s doing linear interpolation (lerp) on the components of a quaternion instead, that isn’t going to work very well. three.js quaternions include a slerp method, though I don’t know how to use that with tween.js.

For some context. Below I have written out the starting state and the ending state of the two quaternion object(s) that show the “problematic” animation. Would it be possible to modify the starting state with mathematically different values, but that will look viscerally the same?

// Start 
Object {
    _x: 0.016542505879011883,
    _y: 0.9828047132846788, 
    _z: 0.1107168573658213,
    _w: -0.1468435171864922

// End 
Object {
    _x: -0.06967661786618205,
    _y: -0.7036655234717202,
    _z: -0.06967661786618205,
    _w: 0.7036655234717202

@makc3d For a moment I thought that the normalize() method would achieve the above described (desired?) behaviour, but that ain’t it.

@donmccurdy, Tween.js has an onUpdate function that uses a second attribute that holds a value from 0 (start) to 1 (finish). Changing the code into the following, changes the behavior of the animation. But it doesn’t resolve the problem. The starting and end position remain correct, but the in between phases remains bad.

new TWEEN.Tween(   
                .to( tmpCamera.position, 1000)
                .easing( TWEEN.Easing.Cubic.InOut )
                .onUpdate((object,t) => {
          , t)
.onUpdate((object,t) => {, t)

this is super-bad take, since it is slerping from i-1 position every time, not from starting position.

but again, using slerp assumes your object moves in an arc, which it does not - you just move it in a straight line. so, instead of interpolating quaternions, just lookAt the origin in onUpdate

@makc3d, thank you for pointing out that I could just put the lookAt method within the tween.onUpdate method. This works splendidly. Below I’ve put a snippet of the working code. Just for future references.

const center = new THREE.Vector3(0,0,0);
const destination = new THREE.Vector3(10,10,10);
new TWEEN.Tween(   
    .to( destination, 1000 )
    .easing( TWEEN.Easing.Sinusoidal.In )
    .onUpdate(() => { center.x, center.y, center.z)