Patch #17346: Align bones in edit mode

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!
This commit is contained in:
Joshua Leung 2008-09-26 08:51:05 +00:00
parent 06a5e9b58a
commit 1206061ed4
4 changed files with 146 additions and 2 deletions

@ -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'],

@ -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

@ -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)

@ -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();