Calculate a edit_bone's roll using a quaternion
Concept
I believe that the following relationship holds.
$ M=M_{\phi} M_{roll} $
where
- $M$ is the matrix of an edit_bone.matrix.to_3x3(),
- $M_{roll}$ is a roll matrix and,
- $M_{\phi}$ is a matrix to match a vector(0,1,0) with the edit_bone.vector direction.
Therefore
$ M_{roll} = M_{\phi}^{-1} M $
Script
import bpy
from mathutils import Matrix, Vector
from math import *
def calc_roll_from_quaternion(quaternion):
mat = quaternion.to_matrix() # corresponds to EditBone.matrix.to_3x3()
vec_yo = Vector([0,1,0])
vec = mat @ vec_yo # corresponds to EditBone.vector
# Get the rotation matrix to match vec_yo with vec direction
if vec.cross(vec_yo).length == 0 and vec.dot(vec_yo) < 0:
# Correct the rotation matrix when the direction of vec is equal to -vec_yo
rot = Matrix.Diagonal((-1,-1,1),)
else:
diff_angle = vec_yo.rotation_difference(vec)
rot = vec_yo.rotation_difference(vec).to_matrix()
mat_roll = rot.inverted() @ mat
v, angle = mat_roll.to_quaternion().to_axis_angle()
from numpy import sign
angle *= sign(v.y)
return angle
bpy.ops.object.mode_set(mode='EDIT')
obj = bpy.context.object
print(obj.name)
for ebone in obj.data.edit_bones:
quaternion = ebone.matrix.to_3x3().to_quaternion()
calc_roll = calc_roll_from_quaternion(quaternion)
if abs(calc_roll - ebone.roll) > 1e-4:
diff = calc_roll - ebone.roll
print(f"error of {ebone.name}: {degrees(diff):.4f} deg")
Results of testing with Human (Meta-Rig)
metarig.001
error of cheek.B.L: -0.0068 deg
error of cheek.B.R: 0.0068 deg
error of shoulder.L: -0.0229 deg
error of shoulder.R: 0.0229 deg