Commit: 156921114ec50760abfaadec3dbc02140f33ef5b
Author: Campbell Barton
Date:   Thu Mar 19 17:57:17 2015 +1100
Branches: master
https://developer.blender.org/rB156921114ec50760abfaadec3dbc02140f33ef5b

Armature Symmetrize tool

D1147 by @julien, with fixes/improvements

Duplicate bones where needed, otherwise use existing.
Keeps parent relations intact, can operate on parts of an armature.

===================================================================

M       release/scripts/startup/bl_ui/space_view3d.py
M       source/blender/editors/armature/armature_add.c
M       source/blender/editors/armature/armature_intern.h
M       source/blender/editors/armature/armature_ops.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_view3d.py 
b/release/scripts/startup/bl_ui/space_view3d.py
index c65cebf..128e24a 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2710,6 +2710,7 @@ class VIEW3D_MT_edit_armature(Menu):
         layout.separator()
 
         layout.operator_context = 'EXEC_AREA'
+        layout.operator("armature.symmetrize")
         layout.operator("armature.autoside_names", text="AutoName 
Left/Right").type = 'XAXIS'
         layout.operator("armature.autoside_names", text="AutoName 
Front/Back").type = 'YAXIS'
         layout.operator("armature.autoside_names", text="AutoName 
Top/Bottom").type = 'ZAXIS'
@@ -2748,6 +2749,7 @@ class VIEW3D_MT_armature_specials(Menu):
         layout.operator("armature.autoside_names", text="AutoName 
Front/Back").type = 'YAXIS'
         layout.operator("armature.autoside_names", text="AutoName 
Top/Bottom").type = 'ZAXIS'
         layout.operator("armature.flip_names", text="Flip Names")
+        layout.operator("armature.symmetrize")
 
 
 class VIEW3D_MT_edit_armature_parent(Menu):
diff --git a/source/blender/editors/armature/armature_add.c 
b/source/blender/editors/armature/armature_add.c
index a4520db..5cc2101 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -43,6 +43,7 @@
 #include "BKE_constraint.h"
 #include "BKE_context.h"
 #include "BKE_idprop.h"
+#include "BKE_deform.h"
 
 #include "RNA_access.h"
 #include "RNA_define.h"
@@ -516,6 +517,212 @@ void ARMATURE_OT_duplicate(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+/**
+ * near duplicate of #armature_duplicate_selected_exec,
+ * except for parenting part (keep in sync)
+ */
+static int armature_symmetrize_exec(bContext *C, wmOperator *op)
+{
+       bArmature *arm;
+       EditBone *ebone_iter;
+       EditBone *ebone_first_dupe = NULL;  /* The beginning of the duplicated 
mirrored bones in the edbo list */
+
+       Object *obedit = CTX_data_edit_object(C);
+       const int direction = RNA_enum_get(op->ptr, "direction");
+       const int axis = 0;
+
+       arm = obedit->data;
+
+       /* cancel if nothing selected */
+       if (CTX_DATA_COUNT(C, selected_bones) == 0)
+               return OPERATOR_CANCELLED;
+
+       ED_armature_sync_selection(arm->edbo); // XXX why is this needed?
+
+       preEditBoneDuplicate(arm->edbo);
+
+       /* Select mirrored bones */
+       for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = 
ebone_iter->next) {
+               if (EBONE_VISIBLE(arm, ebone_iter) &&
+                   (ebone_iter->flag & BONE_SELECTED))
+               {
+                       char name_flip[MAX_VGROUP_NAME];
+
+                       BKE_deform_flip_side_name(name_flip, ebone_iter->name, 
false);
+
+                       if (STREQ(name_flip, ebone_iter->name)) {
+                               /* if the name matches, we don't have the 
potential to be mirrored, just skip */
+                               ebone_iter->flag &= ~(BONE_SELECTED | 
BONE_TIPSEL | BONE_ROOTSEL);
+                       }
+                       else {
+                               EditBone *ebone = 
ED_armature_bone_find_name(arm->edbo, name_flip);
+
+                               if (ebone) {
+                                       if ((ebone->flag & BONE_SELECTED) == 0) 
{
+                                               /* simple case, we're selected, 
the other bone isn't! */
+                                               ebone_iter->temp = ebone;
+                                       }
+                                       else {
+                                               /* complicated - choose which 
direction to copy */
+                                               float axis_delta;
+
+                                               axis_delta = ebone->head[axis] 
- ebone_iter->head[axis];
+                                               if (axis_delta == 0.0f) {
+                                                       axis_delta = 
ebone->tail[axis] - ebone_iter->tail[axis];
+                                               }
+
+                                               if (axis_delta == 0.0f) {
+                                                       /* both mirrored bones 
exist and point to eachother and overlap exactly.
+                                                        *
+                                                        * in this case theres 
no well defined solution, so de-select both and skip.
+                                                        */
+                                                       ebone->flag      &= 
~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+                                                       ebone_iter->flag &= 
~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+                                               }
+                                               else {
+                                                       EditBone *ebone_src, 
*ebone_dst;
+                                                       if (((axis_delta < 
0.0f) ? -1 : 1) == direction) {
+                                                               ebone_src = 
ebone;
+                                                               ebone_dst = 
ebone_iter;
+                                                       }
+                                                       else {
+                                                               ebone_src = 
ebone_iter;
+                                                               ebone_dst = 
ebone;
+                                                       }
+
+                                                       ebone_src->temp = 
ebone_dst;
+                                                       ebone_dst->flag &= 
~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /*      Find the selected bones and duplicate them as needed, with 
mirrored name */
+       for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != 
ebone_first_dupe; ebone_iter = ebone_iter->next) {
+               if (EBONE_VISIBLE(arm, ebone_iter) &&
+                   (ebone_iter->flag & BONE_SELECTED) &&
+                   /* will be set if the mirror bone already exists (no need 
to make a new one) */
+                   (ebone_iter->temp == NULL))
+               {
+                       char name_flip[MAX_VGROUP_NAME];
+
+                       BKE_deform_flip_side_name(name_flip, ebone_iter->name, 
false);
+
+                       /* bones must have a side-suffix */
+                       if (!STREQ(name_flip, ebone_iter->name)) {
+                               EditBone *ebone;
+
+                               ebone = duplicateEditBone(ebone_iter, 
name_flip, arm->edbo, obedit);
+
+                               if (!ebone_first_dupe) {
+                                       ebone_first_dupe = ebone;
+                               }
+                       }
+               }
+       }
+
+       /*      Run though the list and fix the pointers */
+       for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != 
ebone_first_dupe; ebone_iter = ebone_iter->next) {
+               if (ebone_iter->temp) {
+                       /* copy all flags except for ... */
+                       const int flag_copy = ((int)~0) & ~(BONE_SELECTED | 
BONE_ROOTSEL | BONE_TIPSEL);
+
+                       EditBone *ebone = ebone_iter->temp;
+
+                       /* copy flags incase bone is pre-existing data */
+                       ebone->flag = (ebone->flag & ~flag_copy) | 
(ebone_iter->flag & flag_copy);
+
+                       if (ebone_iter->parent == NULL) {
+                               /* If this bone has no parent,
+                                * Set the duplicate->parent to NULL
+                                */
+                               ebone->parent = NULL;
+                       }
+                       else {
+                               /* the parent may have been duplicated, if not 
lookup the mirror parent */
+                               EditBone *ebone_parent =
+                                       (ebone_iter->parent->temp ?
+                                        ebone_iter->parent->temp : 
ED_armature_bone_get_mirrored(arm->edbo, ebone_iter->parent));
+
+                               if (ebone_parent == NULL) {
+                                       /* If the mirror lookup failed, (but 
the current bone has a parent)
+                                        * then we can assume the parent has no 
L/R but is a center bone.
+                                        * So just use the same parent for both.
+                                        */
+                                       ebone_parent = ebone_iter->parent;
+                               }
+
+                               ebone->parent = ebone_parent;
+                       }
+
+                       /* Lets try to fix any constraint subtargets that might
+                        * have been duplicated
+                        */
+                       updateDuplicateSubtarget(ebone, arm->edbo, obedit);
+               }
+       }
+
+       transform_armature_mirror_update(obedit);
+
+       /* Selected bones now have their 'temp' pointer set,
+        * so we don't need this anymore */
+
+       /* Deselect the old bones and select the new ones */
+       for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != 
ebone_first_dupe; ebone_iter = ebone_iter->next) {
+               if (EBONE_VISIBLE(arm, ebone_iter)) {
+                       ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | 
BONE_ROOTSEL);
+               }
+       }
+
+       /* New bones will be selected, but some of the bones may already exist 
*/
+       for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != 
ebone_first_dupe; ebone_iter = ebone_iter->next) {
+               EditBone *ebone = ebone_iter->temp;
+               if (ebone && EBONE_SELECTABLE(arm, ebone)) {
+                       ED_armature_ebone_select_set(ebone, true);
+               }
+       }
+
+       /* correct the active bone */
+       if (arm->act_edbone && arm->act_edbone->temp) {
+               arm->act_edbone = arm->act_edbone->temp;
+       }
+
+       ED_armature_validate_active(arm);
+
+       WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
+
+       return OPERATOR_FINISHED;
+}
+
+/* following conventions from #MESH_OT_symmetrize */
+void ARMATURE_OT_symmetrize(wmOperatorType *ot)
+{
+       /* subset of 'symmetrize_direction_items' */
+       static EnumPropertyItem arm_symmetrize_direction_items[] = {
+               {-1, "NEGATIVE_X", 0, "-X to +X", ""},
+               {+1, "POSITIVE_X", 0, "+X to -X", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       /* identifiers */
+       ot->name = "Symmetrize";
+       ot->idname = "ARMATURE_OT_symmetrize";
+       ot->description = "Enforce symmetry, make copies of the selection or 
use existing";
+
+       /* api callbacks */
+       ot->exec = armature_symmetrize_exec;
+       ot->poll = ED_operator_editarmature;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       ot->prop = RNA_def_enum(
+               ot->srna, "direction", arm_symmetrize_direction_items, -1,
+               "Direction", "Which sides to copy from and to (when both are 
selected)");
+}
+
 /* ------------------------------------------ */
 
 /* previously extrude_armature */
diff --git a/source/blender/editors/armature/armature_intern.h 
b/source/blender/editors/armature/armature_intern.h
index 2c64c9a..0607bc4 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -72,6 +72,7 @@ void ARMATURE_OT_shortest_path_pick(struct wmOperatorType 
*ot);
 
 void ARMATURE_OT_delete(struct wmOperatorType *ot);
 void ARMATURE_OT_duplicate(struct wmOperatorType *ot);
+void ARMATURE_OT_symmetrize(struct wmOperatorType *ot);
 void ARMATURE_OT_extrude(struct wmOperatorType *ot);
 void ARMATURE_OT_hide(struct wmOperatorType *ot);
 void ARMATURE_OT_reveal(struct wmOperatorType *ot);
diff --git a/source/blender/editors/armature/armature_ops.c 
b/source/blender/editors/armature/armature_ops.c
index c80953d..552faa4 100644
--- a/source/blender/editors/armature/armature_ops.c
+++ b/source/blender/editors/armature/armature_ops.c
@@ -66,6 +66,7 @@ void ED_operatortypes_armature(void)
 
        WM_operatortype_append(ARMATURE_OT_delete);
        WM_operatortype_append(ARMATURE_OT_duplicate);
+       WM_operatortype_append(ARMATURE_OT_symmetrize);
        WM_operatortype_append(ARMATURE_OT_extrude);
        WM_operatortype_append(ARMATURE_OT_hide);
        WM_operatortype_append(ARMATURE_OT_reveal);

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to