I’m trying to get better results for quaternion to euler conversions than I seem to get from the native Blender conversions. It’s turning out to be tricky. Apparently eulers are wily beasts.

I have had the most success with the following code, but it fails significantly with the gimbal lock zones.

```
def quat_to_euler2(q1):
#adapted from Maths - Conversion Quaternion to Euler - Martin Baker
#http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm
sqw = q1.w * q1.w
sqx = q1.x * q1.x
sqy = q1.y * q1.y
sqz = q1.z * q1.z
unit = sqx + sqy + sqz + sqw # if normalised is one, otherwise is correction factor
test = q1.x * q1.y + q1.z * q1.w
if (test > 0.499999*unit) and (test < 0.500001*unit): # singularity at north pole
heading = 2 * math.atan2(q1.x,q1.w)
attitude = math.pi/2
bank = 0
elif (test < -0.499999*unit) and (test > -0.500001*unit): # singularity at south pole
heading = -2 * math.atan2(q1.x,q1.w)
attitude = -math.pi/2
bank = 0
else:
heading = math.atan2(2 * q1.y * q1.w-2 * q1.x * q1.z , sqx - sqy - sqz + sqw)
attitude = math.asin(2 * test/unit)
bank = math.atan2(2 * q1.x * q1.w-2 * q1.y * q1.z , -sqx + sqy - sqz + sqw)
return r2d(bank),r2d(heading),r2d(attitude)
```

At the suggestion of someone on another message board, I adapted the following from another source. It returns somewhat worse results with the gimbal zones.

```
#This produces too many gimbal errors
def matToEuler_v2(mat): #mat can be 3X3 or 4X4
#adapted from The Matrix and Quaternions FAQ
#www.j3d.org/matrix_faq/matrfaq_latest.html
angle_y = math.asin(mat[0][2]) #Calculate Y-axis angle
C = math.cos(angle_y)
angle_y = r2d(angle_y)
if math.fabs(C) > 0.005: #Gimball lock?
x1 = mat[2][2]/C #No, so get X-axis angle
y1 = -mat[1][2]/C
angle_x = r2d(math.atan2(y1,x1))
x1 = mat[0][0]/C #Get Z-axis angle
y1 = -mat[0][1]/C
angle_z = r2d(math.atan2(y1,x1))
else: #Gimball lock has occurred
angle_x = 0 #Set X-axis angle to zero
x1 = mat[1][1] #And calculate Z-axis angle
y1 = mat[1][0]
angle_z = r2d(math.atan2(y1,x1))
return angle_x, angle_y, angle_z
#Radians conversion borrowed from xsi export, by "Elira"
def r2d(r): #radians to degrees
return round(r*180.0/math.pi,4) #Approx. 57.295828 before rounding
#This works
def quatToMat3(quat):
w,x,y,z = quat
m00 = 1 - 2*y**2 - 2*z**2
m01 = 2*(x*y) - 2*(w*z)
m02 = 2*(x*z) + 2*(w*y)
m10 = 2*(x*y) + 2*(w*z)
m11 = 1 - 2*x**2 - 2*z**2
m12 = 2*(y*z) - 2*(w*x)
m20 = 2*(x*z) - 2*(w*y)
m21 = 2*(y*z) + 2*(w*x)
m22 = 1 - 2*x**2 - 2*y**2
mat = [[m00,m01,m02],[m10,m11,m12],[m20,m21,m22]]
return mat
```

When I asked at this other board about tightening up the conditions for the gimbal/singularity handling, I received the following response:

The solution is probably dependant on the behaviour of asin and cos in the problem region.

To get some idea of the evaluation shortcuts and argument reduction that takes place inside these functions you can refer to the routines from fdlibm hosted at netlib.

I’d suggest confirming that your are getting values that produce what your expecting when passed to other the functions.

It may be that you’ll need to add more special case processing to deal with some NaNs.

I’ve looked into Nans, and Python’s built-in method for handling them seems to be the new Decimal module in Python 2.4. But this isn’t available with Blender’s normal installation. So I thought I should ask if there is any known alternate way to guarantee good results with very small numbers and such. Or if the Decimal module could be considered for inclusion as part of the Blender distribution, perhaps. It seems like it might be useful.

Does anyone have any thoughts or suggestions on any of this?