# How can a bone's tail be changed by a script?

I want to calculate a rotation matrix from two vectors, in order to check the steps are correct I have setup 3 armatures.

1. A reference bone (arm_org),
2. One that is directly rotated by applying a rotation matrix (arm_rot) and
3. Another (arm_mat) that should be rotated by a matrix that has been calculated from the rotational difference of the 1st and 2nd.

Why are the bones tails not updated? Unfortunatly ArmatureEditBones isn't well documented. I also tried to set the active bone. But the changes are not reflected in the 3D view. Only the currently selected bone can manipulated.

def setBone( name, v ):
arm = bpy.data.armatures[ name ]
print("set arm=" + name + " v=" + str( v ))
for bone in arm.edit_bones:
print( bone.tail )
bone.tail= v

def getBone( name ):
#bpy.context.scene.objects.active = bpy.data.objects[ name ]
arm = bpy.data.armatures[ name ]
print("get arm=" + name + " arm=" + str( arm ))
v = None
for bone in arm.edit_bones:
print( bone.tail )
v = bone.tail
return v

def vec():
m = mx * my * mz
print( "m : " + str (m ))

# get and duplicate original tail pos of reference bone
vo = getBone("arm_org")
if vo == None:
return
setBone("arm_mat", vo.copy() )
setBone("arm_rot", vo.copy() )
vr = vo * m
setBone("arm_rot", vr )

# recalc and apply matrix from rotational difference
q = vo.rotation_difference( vr )
mr = q.to_matrix()
mr.resize_4x4()
vm = vo * mr
setBone( "arm_mat" , vm )
print("mr: " + str(mr))
print( "-" * 20 )

vec()


Blend file (works only in edit mode with arm_org selected)

To change bones in an Armature, you need to use the EditBones, and the Armature has to be in editmode. As only a single object can be in editmode at a time in Blender, you need to change mode if you wanna access different Armature's EditBones.

Your script doesn't change mode and the .edit_bones property will be None if the owning Armature is in Object or Pose Mode. Thus for bone in arm.edit_bones will end immediately (nothing to loop over) and v remains None (the initial value).

I tried to make your script working, and it will have a visual effect now, not sure if it's what you want though:

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

def dump2(obj, level=0):
for attr in dir(obj):
print( "obj.%s = %s" % (attr, getattr(obj, attr)))

def setBone(name, v):
arm = bpy.data.objects.get(name)
if armature_prepare(arm):
print("set arm=" + name + " v=" + str(v))
for bone in arm.data.edit_bones:
print( bone.tail )
bone.tail= v

def armature_prepare(arm):
if arm is not None and bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = arm
arm.select = True
bpy.ops.object.mode_set(mode='EDIT')
return True
return False

def getBone(name):
v = None
arm = bpy.data.objects.get(name)

if armature_prepare(arm):
print("get arm=" + name + " arm=" + str(arm))
for bone in arm.data.edit_bones:
print(bone.tail)
v = bone.tail
return v

def vec():
m = mx * my * mz
print( "m : " + str (m ))

# get and duplicate original tail pos of reference bone
vo = getBone("arm_org")
if vo == None:
return
setBone("arm_mat", vo.copy())
setBone("arm_rot", vo.copy())
vr = vo * m
setBone("arm_rot", vr )

# recalc and apply matrix from rotational difference
q = vo.rotation_difference( vr )
mr = q.to_matrix()
mr.resize_4x4()
vm = vo * mr
setBone("arm_mat" , vm)
print("mr: " + str(mr))
print("-" * 20)

vec()

• Thank so much, do you have an idea why q = vo.rotation_difference( vr ) and therefore the matrix mr shows no difference in rotation (identity matrix) ? Oct 27 '13 at 21:47

Based on CoDEmanX answer I added a few minor changes to make it fully work:

• copy() of vectors in order to not share references

• reverse order of vectors in rotational_difference() because the resulting quaternion was mirrored.

-

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

def setBone( name, v ):
arm = bpy.data.objects.get(name)
if armature_prepare(arm):
print("set arm=" + name + " v=" + str( v ))
for bone in arm.data.edit_bones:
print( bone.tail )
bone.tail= v.copy()

def armature_prepare(arm):
if arm is not None and bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = arm
arm.select = True
bpy.ops.object.mode_set(mode='EDIT')
return True
return False

def getBone(name):
v = None
arm = bpy.data.objects.get(name)
if armature_prepare(arm):
print("get arm=" + name + " arm=" + str( arm ))
for bone in arm.data.edit_bones:
print( bone.tail )
v = bone.tail.copy()
return v

def vec():
#m = mx * my * mz
m = mz * my * mx
print( "m : " + str (m ))

# get and duplicate original tail pos of reference bone
vo = getBone("arm_org")
vr = vo * m
setBone("arm_rot", vr )

# recalc and apply matrix from rotational difference
print("." * 20 )
print( "vo=" + str( vo ) + repr( vo ) )
print( "vr=" + str( vr ) + repr( vr ))
#q = vo.rotation_difference( vr )
q = vr.rotation_difference( vo )
print("q " + str(q))
mr = q.to_matrix()
mr.resize_4x4()
vm = vo * mr
setBone( "arm_mat" , vm )
print("mr: " + str(mr))
print( "-" * 20 )

vec()