# How to get global rotation of a bone with respect to global planes of axis via python?

I'm simply trying to find the global rotation of a bone in pose mode using python

i.e. what angle a bone makes with the global axis? The closest I've come is:

for pb in context.selected_pose_bones_from_active_object:

pb_world_matrix=armature.matrix_world @ pb.matrix
loc, rot, scale = pb_world_matrix.decompose()
rot= rot.to_euler();

print(pb.name,"x=",math.degrees(rot.x),"y=",math.degrees(rot.y),"z=",math.degrees(rot.z))


Edit:
let me explain what I mean with an example: As you can see in the gif above, initially the angles are:

x_glob= 0deg
y_glob= 0deg
z_glob= 0deg


then I first rotate it by 38deg on global y orientation then I rotate by 41.3deg on global x orientation so the final output should be:

x_glob= 41.3deg
y_glob= 38deg
z_glob= 0deg


I think a better way of looking at the problem is finding the angle made with the planes of global axis (I couldn't find the wording to phrase it till now)

• In your example you don't show any parenting, so bone's local rotation is also global rotation... Except Y and Z are swapped. Jun 20 at 13:56
• @MarkusvonBroady I didn't want to make the question too complex & confusing so I didn't add that part, but when I meant globally I meant the rotation regardless of parenting Jun 20 at 14:40

I had a little look into it but I'm not an expert. Discovered you can get the matrix world of the selected bone using pb_mw = pb.matrix. Then using pb_mw.to_euler() you can obtain the euler rotation from the matrix. The only catch seemed to be that a bone at rest position in the interface (x,y,z rotation at 0 degrees in the UI) is actually at 90 degrees on the x axis in the world matrix. Happy for further input on this.

import bpy
from math import degrees

context = bpy.context

for pb in context.selected_pose_bones_from_active_object:

pb_mw = pb.matrix
pb_rot = pb_mw.to_euler()

print(f"pb matrix world: {pb_mw}")
print(f"x rotation: {degrees(pb_rot.x) - 90}")
print(f"y rotation: {degrees(pb_rot.y)}")
print(f"z rotation: {degrees(pb_rot.z)}")

# Test on empty
# empty = bpy.data.objects["Empty"]
# empty.matrix_world = pb_mw


Edit: related answer here... Get bone rotation in pose mode using Python

• I don't see how this gives you the global rotation? Jun 20 at 11:33
• The pb.matrix is the matrix world of the bone which has the global rotation in it... you simply use .to_euler() to extract just the rotation part of it.
– Dan
Jun 20 at 11:34
• that does not give me the global rotation i.e. what angle a bone makes with the global axis? Jun 20 at 12:04
• @cakelover you might need give some more details about what angle you are trying to calculate exactly, because the matrix world does give the global rotation as far as I can see?
– Dan
Jun 20 at 12:21
• I've added an example, does that clear it up? Jun 20 at 12:57

You can't do this.

Why? It is because you have converted rotation from quaternions to Euler angles. That's basically means, that you have dozens of variants to have the same rotation using Eulers. Let me show the example:

Here are 2 figures rotated same way, but, first one has rotation 90,90,0: second 180, 90, 90 As you see, you can't get initial transforms of the object.

Using rot.to_euler(); gives you one of the variants. But you may get something different, that you have expected.

Of course, you have some initial rotation values, but it's hard to convert them to global without using a transform matrix. And when you put values in the matrix, there is no way to return them back unchanged.

• I had a feeling that might be the case, that's why I modified the question and added planes, finding the rotation with respect to planes would achieve what I'm looking for, sorry for phrasing the question poorly initially Jun 20 at 13:48
• It's not really hard, you need to iterate over parents and rotate by negated Euler rotation components in reversed order. Jun 20 at 13:55
• @MarkusvonBroady I'm not sure, you have missed possible scaling. Jun 20 at 14:13
• @Crantisz scaling won't affect rotation, unless of course I don't understand the OP. Jun 20 at 14:14
• Uniform maybe yes, but not uniform affects Jun 20 at 14:36

I think I've found a solution

import bpy
from bpy import context
import math
from mathutils import Matrix,Vector

unit_x=Vector((1.0,0.0,0.0))
unit_y=Vector((0.0,1.0,0.0))
unit_z=Vector((0.0,0.0,1.0))

for pb in context.selected_pose_bones_from_active_object:

axis, roll = pb.bone.AxisRollFromMatrix(pb.matrix.to_3x3(), axis=pb.y_axis)

x_angle=Vector((pb.vector.x,0,pb.vector.z)).angle(unit_x) #angle made with x plane
y_angle=Vector((0,pb.vector.y,pb.vector.z)).angle(unit_y) #angle made with y plane
z_angle=Vector((pb.vector.x,0,pb.vector.z)).angle(unit_z) #angle made with z plane

print("angle with plane x:",math.degrees(x_angle))
print("angle with plane y:",math.degrees(y_angle))
print("angle with plane z:",math.degrees(z_angle))
print("self roll:",math.degrees(roll))


added a little roll bonus in there