# Quaternion rotates but then reverses

4.3.dev

### Question

Trying to turn a node3d to the left in discrete steps (here 45 degs) so that it just keeps going around. However, it gets to a certain angle (usually just over 180 deg) and then starts to come backwards! Can anyone enlighten me, I am going quite mad.

(I am using Quaternions on advice and my final use-case is RigidBody3D, but until I can get predictable angles and behaviour of them, I best keep it simple.)

Image to show the scene and code. Code also below. It’s on the `%pointy` node. `%aim` is what I lookat at, which is always 45 degs.

The numbers output are pasted below.

``````extends Node3D

func _physics_process(delta: float) -> void:
var frames := Engine.get_physics_frames()
if frames % 60 == 0:
turn(delta)

func turn(delta):
var currQ := basis.get_rotation_quaternion()

var target : Vector3 = transform * %aim.position

var look_basis : Basis = transform.looking_at(target, Vector3.UP).basis
var lookQ := look_basis.get_rotation_quaternion()

var QQ := lookQ.normalized() * currQ.inverse()

var angle:= QQ.get_angle()

print(" going to rot by:", rad_to_deg( angle))
%pointy.rotate_y(angle)
``````

Output:

``````curr angle:0
look angle:44.9999910069553
going to rot by:44.9999910069553
curr angle:44.9999910069553
look angle:89.999995674289
going to rot by:45.0000080824283 <-- OK
curr angle:90.0000025044782
look angle:135.000000341623
going to rot by:44.9999910069553 <-- OK
curr angle:135.000000341623
look angle:179.999991348578
going to rot by:44.9999910069553 <-- OK
curr angle:179.9999776882
look angle:224.999982355533
going to rot by:44.9999910069553 <-- OK
curr angle:224.999968695155
look angle:90.0000298252348
going to rot by:314.999991690201 <-- WTF?
``````

Have simplified the code to:

``````extends Node3D

func _physics_process(delta: float) -> void:
turn(delta)

func turn(delta):
var currQ := basis.get_rotation_quaternion()
var curr_angle := currQ.get_angle()
# Let's keep adding some angle to current's angle
var toQ := Quaternion(Vector3.UP, currQ.get_angle() + PI/8)
# find the difference - I hope. Who knows?
var diffQ := toQ.normalized() * currQ.inverse()
var diff_angle:= diffQ.get_angle()
print(" SUM:", rad_to_deg(curr_angle + diff_angle ))
print(" TO angle:", rad_to_deg( toQ.get_angle()), " SHOULD EQUAL SUM")
print()
%pointy.rotate_y(diff_angle)
assert(is_equal_approx(diff_angle, PI/8),"STOP")
``````

Let it run and it will stop when the numbers get funky. Then go look at the output:

`````` diffQ's ANGLE:22.4999476921535
SUM:22.4999476921535
TO angle:22.5000177015925 SHOULD EQUAL SUM

curr's angle:22.4999476921535
diffQ's ANGLE:22.4999476921535
SUM:44.9998953843069
TO angle:44.9999534409149 SHOULD EQUAL SUM

curr's angle:44.9999022144961
diffQ's ANGLE:22.5000177015925
SUM:67.4999199160886
TO angle:67.499904548163 SHOULD EQUAL SUM

curr's angle:67.4999182085413
diffQ's ANGLE:22.5000177015925
SUM:89.9999359101338
TO angle:89.9999273723973 SHOULD EQUAL SUM

curr's angle:89.9999342025865
diffQ's ANGLE:22.5000177015925
SUM:112.499951904179
TO angle:112.499943366442 SHOULD EQUAL SUM

curr's angle:112.499957026821
diffQ's ANGLE:22.5000177015925
SUM:134.999974728413
TO angle:134.999959360488 SHOULD EQUAL SUM

curr's angle:134.999973020866
diffQ's ANGLE:22.4999818430993
SUM:157.499954863965
TO angle:157.499968524344 SHOULD EQUAL SUM

curr's angle:157.499954863965
diffQ's ANGLE:22.5000177015925
SUM:179.999972565558
TO angle:179.999950367443 SHOULD EQUAL SUM

curr's angle:179.999964027821
diffQ's ANGLE:22.4999818430993
SUM:202.499945870921
TO angle:202.499959531299 SHOULD EQUAL SUM

curr's angle:202.499945870921
diffQ's ANGLE:22.4999818430993
SUM:224.99992771402
TO angle:224.999941374398 SHOULD EQUAL SUM

curr's angle:224.99992771402
diffQ's ANGLE:22.4999476921535
SUM:247.499875406173
TO angle:247.499936877876 SHOULD EQUAL SUM

(Here we are at angle 247 degrees, but ...
suddenly it reads as 112 degrees?
Which is maybe why the movement goes wrong.)

curr's angle:112.500120951361
diffQ's ANGLE:247.500237406199
SUM:360.00035835756
TO angle:135.000123285028 SHOULD EQUAL SUM

--- Debugging process stopped ---
``````

A small comment. I have been hacking-away slowly. Here’s some code that seems to work much better. At least the fish does not stop and bounce backwards:

``````func follow(rotation_force, delta) -> Vector3:
var g := get_gravity()
var negg: = -g.normalized()
var up := negg

var target : Vector3 = transform * %aim.position
var to_basis: Basis
to_basis = transform.looking_at(target, up).basis.orthonormalized()

# !! Doing this multiply mambo with basis works better than with quaternions!
var Q := (to_basis * basis.inverse()).get_rotation_quaternion()
# !!

var axis := Q.get_axis()
var angle := Q.get_angle()
var T = axis * angle * rotation_force * delta
return T
``````