Many thanks @batFINGER that got me heading in the correct direction, I now need to spend a few hours understanding why it works :) I modified the code from the example you linked to.
I've updated with extra comments and modified the code after I did some research to get my head around it, happy to take feed back I've misinterpreted anything
does not seem to need any additional calculations if it's a child of another bone but would be interested I'f I missed something there.
Mark
In my example from my question
- bone y+1 is located at (0,1,0) in pose space but the local location
is (0,0,0) as that is where the bone is in edit mode.
- bone z+2 is located at (0,0,2) in pose space but it's local location
is (0,0,0) as that is where the bone is in edit mode.
- the armature object is at (0,0,0) world space
The translation (where the bone actually is in pose space) is stored in the first 3 rows of the 4th column of the 4x4 matrix reading down X,Y Z the other numbers define rotation and scale which I'm not looking at here.
so for y+1 the default matrix is
(1.0000, 0.0000, 0.0000, 0.0000)
(0.0000, 0.0000, -1.0000, 1.0000)
(0.0000, 1.0000, 0.0000, 0.0000)
(0.0000, 0.0000, 0.0000, 1.0000)
for z+2 the default matrix is
(1.0000, 0.0000, 0.0000, 0.0000)
(0.0000, 0.0000, -1.0000, 0.0000)
(0.0000, 1.0000, 0.0000, 2.0000)
(0.0000, 0.0000, 0.0000, 1.0000)
the armature world matrix is
(1.0000, 0.0000, 0.0000, 0.0000)
(0.0000, 1.0000, 0.0000, 0.0000)
(0.0000, 0.0000, 1.0000, 0.0000)
(0.0000, 0.0000, 0.0000, 1.0000)
The armature world matrix is important if you want to translate locations between world and local space as if it's not (0,0,0) then it needs to be added as an offset (and removed as an offset (inverted) as required.)
import bpy
#assume armature is active and has 2 bones in it called
# "y+1" and "z+2" at different locations
armature = bpy.context.active_object
y1_bone = armature.pose.bones["y+1"]
z2_bone = armature.pose.bones["z+2"]
#y1 is the bone we want to use as the target,
#multiplying 2 matrices is a dot multiplication i.e. each column gets multiplied with each row then added together
#it's not simple scalar with one value with just multiply the whole matrix so the below will produce a matrix
#that will add the armature world location to the y1 location
#if you want to place or align stuff outside the armature in world space then the following two pieces of code will
#take the armature world space offset into account but it's over kill for just moving about in the same local space
#this gets the world space
y1_world_matrix = armature.matrix_world * y1_bone.matrix
#this copies the default and calculates the local space
matrix = armature.matrix_world.inverted() * y1_world_matrix
#the above two would make more sense if say you were trying to place an
#empty in world space at the pose bones location.
z2_bone.matrix.translation = matrix.translation
#if it's just in local space you can remove the armature offset calculations
#above and just assign the translation
#of the target bone to the bone you want to move
#e.g. the below and the local space coordinates will update accordingly.
z2_bone.matrix.translation = y1_bone.matrix.translation
addons/rigify/rig_ui_template.py
Visual Helper functions. $\endgroup$ – batFINGER Jan 28 '18 at 9:36