Patch #17346: Align bones in edit mode
authorJoshua Leung <aligorith@gmail.com>
Fri, 26 Sep 2008 08:51:05 +0000 (08:51 +0000)
committerJoshua Leung <aligorith@gmail.com>
Fri, 26 Sep 2008 08:51:05 +0000 (08:51 +0000)
Submitted by: Lorenzo Pierfederici (lento)

This patch adds the CTRL-ALT-A hotkey to align bones in armature edit mode.

It works the same way as parenting: selected bones will be aligned with active bone, if only one bone is selected it
will be aligned with its parent (if any)

Thanks!

release/scripts/hotkeys.py
source/blender/include/BIF_editarmature.h
source/blender/src/editarmature.c
source/blender/src/space.c

index 929132933ecec3efb2b9da0baebb2881d06d6a01..cb65eef84b2b94802f0aa99cee7deadc0292f688 100644 (file)
@@ -264,6 +264,7 @@ hotkeys={
 ['Alt-A', 'Play animation in current window'],
 ['Ctrl-A', 'Apply objects size/rotation to object data'],
 ['Ctrl-A', 'Text Editor: Select all'],
+['Ctrl-ALT-A', '3D-View: Armature Edit mode, align selected bones to active bone'],
 ['Shift-A', 'Sequencer: Add menu'],
 ['Shift-A', '3D-View: Add menu'],
 ['Shift-ALT-A', 'Play animation in all windows'],
index fdd00a674659f075721752681a0b664a0dad75e3..d390b96f61f5bccc4dd103ff9ae9db3b274a9480 100644 (file)
@@ -139,6 +139,8 @@ void        hide_selected_armature_bones(void);
 void   hide_unselected_armature_bones(void);
 void   show_all_armature_bones(void);
 
+void   align_selected_bones(void);
+
 #define        BONESEL_ROOT    0x10000000
 #define        BONESEL_TIP             0x20000000
 #define        BONESEL_BONE    0x40000000
@@ -157,3 +159,4 @@ void        show_all_armature_bones(void);
 #endif
 
 
+
index 5e50c8117cc860ce2abcde7084539f00401ea50f..d5e5b5a1c4a2ad846cc460a1ab41f3f816810e9e 100644 (file)
@@ -3310,6 +3310,143 @@ void switch_direction_armature (void)
        BIF_undo_push("Switch Direction");
 }
 
+/* editbone alignment */
+
+/* helper to fix a ebone position if its parent has moved due to alignment*/
+static void fix_connected_bone(EditBone *ebone)
+{
+       float diff[3];
+       
+       if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || VecEqual(ebone->parent->tail, ebone->head))
+               return;
+       
+       /* if the parent has moved we translate child's head and tail accordingly*/
+       VecSubf(diff, ebone->parent->tail, ebone->head);
+       VecAddf(ebone->head, ebone->head, diff);
+       VecAddf(ebone->tail, ebone->tail, diff);
+       return;
+}
+
+/* helper to recursively find chains of connected bones starting at ebone and fix their position */
+static void fix_editbone_connected_children(EditBone *ebone)
+{
+       EditBone *selbone;
+       
+       for (selbone = G.edbo.first; selbone; selbone=selbone->next) {
+               if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) {
+                       fix_connected_bone(selbone);
+                       fix_editbone_connected_children(selbone);
+               }
+       }
+       return;
+}                      
+
+static void bone_align_to_bone(EditBone *selbone, EditBone *actbone)
+{
+       float selboneaxis[3], actboneaxis[3], length;
+
+       VecSubf(actboneaxis, actbone->tail, actbone->head);
+       Normalize(actboneaxis);
+
+       VecSubf(selboneaxis, selbone->tail, selbone->head);
+       length =  VecLength(selboneaxis);
+
+       VecMulf(actboneaxis, length);
+       VecAddf(selbone->tail, selbone->head, actboneaxis);
+       selbone->roll = actbone->roll;
+       
+       /* if the bone being aligned has connected descendants they must be moved
+       according to their parent new position, otherwise they would be left
+       in an unconsistent state: connected but away from the parent*/
+       fix_editbone_connected_children(selbone);
+       return;
+}
+
+void align_selected_bones(void)
+{
+       bArmature *arm= G.obedit->data;
+       EditBone *actbone, *ebone, *selbone;
+       EditBone *flipbone, *flippar;
+       short allchildbones= 0, foundselbone= 0;
+       
+       /* find active bone to align to */
+       for (actbone = G.edbo.first; actbone; actbone=actbone->next) {
+               if (arm->layer & actbone->layer) {
+                       if (actbone->flag & BONE_ACTIVE)
+                               break;
+               }
+       }
+       if (actbone == NULL) {
+               error("Needs an active bone");
+               return; 
+       }
+
+       /* find selected bones */
+       for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
+               if (arm->layer & ebone->layer) {
+                       if ((ebone->flag & BONE_SELECTED) && (ebone != actbone)) {
+                               foundselbone++;
+                               if (ebone->parent != actbone) allchildbones= 1; 
+                       }       
+               }
+       }
+       /* abort if no selected bones, and active bone doesn't have a parent to work with instead */
+       if (foundselbone==0 && actbone->parent==NULL) {
+               error("Need selected bone(s)");
+               return;
+       }
+       
+       if (foundselbone==0 && actbone->parent) {
+               /* When only the active bone is selected, and it has a parent,
+                * align it to the parent, as that is the only possible outcome. 
+                */
+               bone_align_to_bone(actbone, actbone->parent);
+               
+               if (arm->flag & ARM_MIRROR_EDIT) {
+                       flipbone = armature_bone_get_mirrored(actbone);
+                       if (flipbone)
+                               bone_align_to_bone(flipbone, flipbone->parent);
+               }
+       }
+       else {
+               /* loop through all editbones, aligning all selected bones to the active bone */
+               for (selbone = G.edbo.first; selbone; selbone=selbone->next) {
+                       if (arm->layer & selbone->layer) {
+                               if ((selbone->flag & BONE_SELECTED) && (selbone!=actbone)) {
+                                       /* align selbone to actbone */
+                                       bone_align_to_bone(selbone, actbone);
+                                       
+                                       if (arm->flag & ARM_MIRROR_EDIT) {
+                                               /* - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone 
+                                                *      (i.e.  selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
+                                                *      This is useful for arm-chains, for example parenting lower arm to upper arm
+                                                * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
+                                                *      then just use actbone. Useful when doing upper arm to spine.
+                                                */
+                                               flipbone = armature_bone_get_mirrored(selbone);
+                                               flippar = armature_bone_get_mirrored(actbone);
+                                               
+                                               if (flipbone) {
+                                                       if (flippar)
+                                                               bone_align_to_bone(flipbone, flippar);
+                                                       else
+                                                               bone_align_to_bone(flipbone, actbone);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       countall(); /* checks selection */
+       allqueue(REDRAWVIEW3D, 0);
+       allqueue(REDRAWBUTSEDIT, 0);
+       allqueue(REDRAWOOPS, 0);
+       BIF_undo_push("Align bones");
+
+       return;
+}
+
 /* ***************** Pose tools ********************* */
 
 void clear_armature(Object *ob, char mode)
index d4460e55a3eb2773d5567a28c6cda07977325f5f..92b5580775ed9601e5c92f13d7a928c662469255 100644 (file)
@@ -1864,8 +1864,11 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                break;
                                
                        case AKEY:
-                               if (G.obedit == 0 && G.qual == (LR_CTRLKEY|LR_ALTKEY)) {
-                                       alignmenu();
+                               if(G.qual == (LR_CTRLKEY|LR_ALTKEY)) {
+                                       if(G.obedit == 0)
+                                               alignmenu();
+                                       else if(G.obedit->type==OB_ARMATURE)
+                                               align_selected_bones();
                                }
                                else if(G.qual & LR_CTRLKEY) { /* also with shift! */
                                        apply_object();