[2.4] bone stuff

I need to modify an armature so each parent bone which has only one child bone will connect to it (it’s tip would move to the child bones base and merge). How would you do that? I failed after hours of trying.
PS: using 2.4.x API.

what I have so far:


for i in skeleton.bones.keys():
    if len(skeleton.bones[i].getAllChildren()) < 2:
        
        # children not available for editBone for whatever reason
        child = skeleton.bones[i].children[0]
        
        skeleton.makeEditable()
        skeleton.bones[i].tail  = skeleton.bones[child.name].head
        skeleton.bones[i].options = [Blender.Armature.CONNECTED]
        skeleton.update()

oh come on…

i dont think this is a very difficult question

Is a difficult question because I do not exactly understand what you want. But you can do the following:
Read out all the bones Matricies in Armaturespace and write them to a dictionary.
Append a childlist as well. Then rebuild the Skeleton by unparenting all the bones, setting the ArmSpaceMat.translationPart() as the head Position
and applying the childs ArmSpaceMat.translationPart() (while len(childlist) == 1) as the tail position of your bone. Then redo the parenting. Hope it helps.

its simple:

I have an armature of unconnected bones.
But the parent-child info is there.

I want to have a script which will check if a parent has only one child and if so, connect it to the child, skip otherwise.

Do you understand?

Yes I do.

If you have the childlist the solution mightbe:


for bone in skeleton.bones:
    if len(yourchildlist) == 1:
       skeleton.makeEditable()
       skeleton.bones[yourchildlist[0]].parent = skeleton.bones[bone]
       skeleton.update() 


if you know the parent:



for bone in skeleton.bones:
    parent = yourparentbonename # where ever you get it from
    skeleton.makeEditable()
    skeleton.bones[bone].parent = skeleton.bones[parent]
    skeleton.update()

If you work with bones you have to connect the child to its parent and not as it seems logic the parent to its child.

I need to connect parent to child, which means “connect parent tip to the child’s head”. Its the usual case. If you extrude one bone from another, thats what happens, and you usually build your armature like that.
Is that what you meant?

You have to connect the childbone to the parentbone. That does not mean “connect” parent tip to childs head. It only means connect one bone to another where it doesn’t matter how the bone is oriented. If you have your skeleton done, you can set the parents tail position to the first childs head position to make your armature look fine. Thats what I told you in the first reply. Apply the first childs headposition to the parent bone’s tail position before you connect your bones. You can get the headposition with


childsheadpos = skeleton.bones[childname].matrix['ARMATURESPACE'].translationPart()
skeleton.bones[parent].tail = childsheadpos

what?
No, I don’t want to connect the childbone to parentbone, I need to connect the parentbone to the childbone (as in what I explained above). Open Blender, make an armature with 2 bones, click one bone and then the other, press Ctrl + P, choose “Connected”. The childbone will move to the paren bone’s position. And thats obviously not what I want.

And I don’t connect a parent to its first child, if parent has more than 1 child, it should not connect to anything.

Havent tried your code yet

Thats not how it works if you use python. In python you have to choose the bone that its child and have to tell em witch parent that it hase. There is no other method. There some differences between the way you build up your armature manualy and the way it is done with python. If you work with ordinary “Objects” the method “Hey! I’m the parent of these other Objects” is right. But if you work with armatures you have to tell a bone who is its parent. Telling it who is its child is not implemented in python.

Well yeah, I know that you can’t access a child of EditBone, unlike (object) Bone, for whatever reason. Thats why Im in object mode in my example snippet when getting a handle to the child bone and then switch to edit mode.

Did you try out my snippet?

If you would, youd probably get a “list index out of range”, even though the index is 0. Thats my issue.

The second line in your snippet is:


if (len(skeleton.bone[i].getAllChildren())  <  2:

There you are wrong. It means the length of your childlist could be 1 or 0 cause both are < 2.
And if childlist == 0 the statement


child=skeleton.bones[i].children[0]

will raise an error cause element 0 (first element in list) does not exist (index out of range).

Write


if (len(skeleton.bone[i].getAllChildren()) == 1:

so you are sure that your list has exactly 1 member.

very nice.

However, the bones don’t connect as expected. I dont know how to explain this,could you please try it with an example scene? I can only guess that it might have something to do with the order in which the bones are processed.

the line in your code


skeleton.bones[i].options = [Blender.Armature.CONNECTED]

does not mean that your bone became a parent or child.
These option is an IK related thing.

As far as i can see your snippet means that you already have a parent children relation in your armature and the bones are already “connected”. All you have to do now is set the parents bone tail to the first or only childs head position.

Hint 1:
A parent child relation does not mean that the parents bone tail automaticaly points to its child.

Hint 2:
If you can see doted lines in your armature while in edit mode your bones already have its parents.

I know that, the parent-child info is already set before running the script.

You should check out what CONNECTED does. Ill try to explain it with words: basically if the parent tip and child head are in the same x,y,z position, then CONNECTED makes the head and tip become “one”, it is rendered is 1 sphere instead of two and by moving it, you move them both.

The issue Im encountering is that some bones dont connect, even though they should and some seem to connect to the child’s child istead.


for i in skeleton.bones.keys():
       if len(skeleton.bones[i].getAllChildren()) &lt; 2:
                      # children not available for editBone for whatever reason
                      child = skeleton.bones[i].children[0]
                      skeleton.makeEditable()
                      skeleton.bones[i].tail  = skeleton.bones[child.name].head
                      skeleton.bones[i].options = [Blender.Armature.CONNECTED]
                      skeleton.update()

will become


for bone in skeleton.bones.keys():
     if len(skeleton.bones[bone].children) == 1:
                  # children not available for editBone for whatever reason
                  child = skeleton.bones[bone].children[0]
                  skeleton.makeEditable()
                  #no need for the next line cause parents tip and childs head
                  #are already at the same postion or will jump automaticaly there
                  #skeleton.bones[i].tail  = skeleton.bones[child.name].head
                  editbone = skeleton.bones[<b>child</b>.<b>name</b>] #most importend thing here
                  editbone.options = [Blender.Armature.CONNECTED]
                  skeleton.update()

Its because the option CONNECTED is related to the parentbone not to the childs.

like I already said, it does.

It seems that it only works too in the way you need it if parent tip and childs head are at the same position.
So you first need to solve this.

solve what?
Doesnt this line do that?


skeleton.bones[i].tail  = skeleton.bones[child.name].head

Watch the post above. I did an edit.

your code connects/ moves the child bones head to the parent bones tip, not the opposite.

Ok. To do that you have to rewrite the line


skeleton.bones[bone].tail = child.head['ARMATURESPACE']

Now your bones tip should jump to the childs head. Leave the rest as I have told you.
In Armatures all bone relationships are relative to its parents. You can only set the CONNECTED
statement to the bones parent and not to the bones child. The key ‘ARMATURESPACE’ is needed cause the Armature.bones values came in BONESPACE by default if they have a parent child relationship as far as I know.