Why use clone when copying rotationQuaternion?

Take a look at this Playground

It’s a part of the “Getting started” tutorial. I don’t understand why in lines 86 and 112 they need to clone the rotationQuaternion. For this example, they first define startRotation to describe the character facing toward the right. Once p === 0, they want to reset the rotationQuaternion (that was changed on line 106) back to be equal to startRotation.

It said in the previous section that the reason for cloning it is so that they aren’t linked, but I did some tests, and if you define startRotation = dude.rotationQuaternion (notice there is no clone), and you then change dude.rotationQuaternion, startRotation does NOT change. So they are not linked in that sense. So you should be able to define startRotation = dude.rotationQuaternion without cloning it, and startRotation should NEVER change, despite there being no cloning operation.

Where is the flaw in my logic?

I tried both cases with console.log(startRotation) after line 86, 106:

1. without clone() on line 86

Output:

Object { _isDirty: true, _x: 0, _y: -0.7071067811865476, _z: 0, _w: 0.7071067811865476 }
playground.babylonjs.com:86:67
Object { _isDirty: true, _x: 0, _y: 0.7071067811865476, _z: 0, _w: 0.7071067811865476 }
playground.babylonjs.com:107:25

Result: startRotation is incorrect on 1st rotate()

2. without clone() on line 86 and 112

Output:

Object { _isDirty: true, _x: 0, _y: -0.7071067811865476, _z: 0, _w: 0.7071067811865476 }
playground.babylonjs.com:86:67
Object { _isDirty: true, _x: 0, _y: -0.7071067811865476, _z: 0, _w: 0.7071067811865476 }
playground.babylonjs.com:107:25
Object { _isDirty: true, _x: 0, _y: -0.7071067811865476, _z: 0, _w: 0.7071067811865476 }
playground.babylonjs.com:107:25
Object { _isDirty: true, _x: 0, _y: 0.7071067811865476, _z: 0, _w: 0.7071067811865476 }
playground.babylonjs.com:107:25

Result: startRotation is incorrect on 3rd rotate()

I also tried to test your approach, but still startRotation is linked to rotationQuaternion of the mesh if you don’t use clone():

But you might have a special case (i.e. need of computeWorldMatrix()), can you share a playground where your issue occurs?

If you remove both clones the dude will start to wander off his intended path.
The situation is a bit complex with rotationQuaternions and all the other things going on.

In javascript, all objects and arrays (not numbers, strings and booleans) can be linked (referenced) via multiple variables,
and any change within 1 variable, will be visible by the other variables if they are not made to be their own unique instance (e.g. clone function in babylonjs).

Sometimes it’s easier to show than explain with my poor terminology

// String doesn't link
let a = "abc";
let b = a;
a += "d";
console.log(a, b) // abcd, abc

// Object link
let a = { a: "abc" };
let b = a;
a.a += "d";
console.log(a.a, b.a) // abcd, abcd

// String within object doesn't link
let a = { a: "abc" };
let b = a.a;
a.a += "d";
console.log(a.a, b) // abcd, abc

Finally i made a simple PG to better condense / showoff the concept.
The plane will continously rotate 90 degrees forward and back.
Try to remove the Clone() on line #23 and it will never change direction, startRotation will then be a link to ground.rotation object.

1 Like

Well another deeper aspect of this is in knowing that console.log on a reference object will always display the final state of the object for that frame of execution, if you needed to see the state of some property within the object at some point in your code , you need to console.log the property directly , if you expect to see a log of the object at some intermittent part of code execution you will get caught out.

eg .

let ob = {};
ob.val1 = 10;

console.log(ob)

ob.val1 = 20;

console.log(ob)

both logs will return the object with val1 being 20 , even though your first log was called before this was set to 20.

anyway , for the original question : I believe clone is used to avoid unnecessary allocation ( memory )

:wink:

Thanks for the reply. I ran your tests and it produced the same output. Indeed, startRotation is being linked to dude.rotationQuaternion because clone is not being called. But check out this playground: https://playground.babylonjs.com/#KBS9I5#26064

Scroll down to line 86. I removed clone so that startRotation and dude.rotationQuaternion should be linked. I then console.logged startRotation and dude.RotationQuaternion and they are both the same, as expected. I then changed dude.rotationQuaternion using this:

        dude.rotationQuaternion = new BABYLON.Quaternion.RotationAxis(
            new BABYLON.Vector3(1, 2, 3),
            4
        );

I then console.log both startRotation and dude.rotationQuaternion again and you will see that dude.rotationQuaternion has indeed changed, but startRotation is still what it was initially. This is why I thought that omitting clone doesn’t link them.

EDIT: I think I got it. I’ve got to pay more attention to mutability. When I use new BABYLON.Quaternion.RotationAxis, I am letting dude.rotationQuaternion reference another object, whereas startRotation is STILL referenced to the original object.

Hi @aWeirdo

Thanks for the reply. Please see my comment above this one. It seems that startRotation is being linked to dude.rotationQuaternion when I omit clone, as expected. and calling dude.rotate does change startQuaternion as well as dude.rotationQuaternion (as expected), but if I change dude.rotationQuaternion using new BABYLON.Quaternion.rotationAxis, then the rotationQuaternion changes but startRotation doesn’t change.

Ok, so i’m a bit uncertain how best to explain this.
this is because

startRotation = dude.rotationQuaternion; // Referencing the same quaternion object.

dude.rotationQuaternion = new quaternion; // dude.rotationQuaternion is now referencing a new quaternion object, startRotation is still referencing the old one.

Yup. I had editted my previous reply to @Takemura

I need to pay more attention to mutability. And don’t worry, I think your explanations have been very clear.

1 Like