matrix_local from head, tail and roll

What is the algorithm for calculating Bone.matrix_local from Editbone.head, tail and roll?

The corresponding world matrix is enough; to get from there I just need to multiply with the parent’s inverse matrix_local. I make the ansatz

M = X(a) Z(c) Y(b)
M = Z(c) X(a) Y(b)

where X, Y, Z are Euler matrices and a,b,c are Euler angles. I think that b is the roll angle. Moreover, let u be a unit vector pointing from head to tail. It should be an easy task to get a and c by solving

u = M e_y,

(e_y = unit vector in y direction) but I don’t manage to get it right.

You might want to look here: http://stackoverflow.com/questions/7132018/how-to-convert-global-to-local-coordinates-in-blender-2-5

Thank you, but this was not really what I asked. Anyway, I figured out the answer myself: in the axis-angle representation, the rotation axis is perpendicular to both the world bone axis and the y-axis (= local bone axis), and the angle is given by cos(phi) = dot product. Here is a code excerpt, using transformations.py.

    def matrixLocalFromBone(self):        
    
        u = self.tail.sub(self.head)
        length = sqrt(u.dot(u))
        if length < 1e-3:
            print("Zero-length bone %s. Removed" % self.name)
            self.matrix_local = tm.identity(4)
            self.matrix_local.matrix[:3,3] = self.head.vector
            return
        u = u.div(length)

        ey = Vector((0,1,0))
        yu = ey.dot(u)        
        if abs(yu) > 0.999:
            axis = ey
            if yu > 0:
                angle = 0
            else:
                angle = pi
        else:        
            axis = ey.cross(u)
            length = sqrt(axis.dot(axis))
            axis = axis.div(length)
            angle = acos(yu)

        mat = tm.rotation_matrix(angle,axis)
        matrix = Matrix(mat)
        if self.parent:
            pmat = self.parent.matrix_local.inverted()
            self.matrix_local = matrix.mult(pmat)
        else:
            self.matrix_local = matrix
        self.matrix_local.matrix[:3,3] = self.head.vector

Actually, this is the answer for roll = 0. For nonzero roll, one must multiply with a extra rotation around the y axis somewhere.