diff options
author | Campbell Barton <ideasman42@gmail.com> | 2020-03-31 06:39:41 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2020-03-31 07:11:11 +0300 |
commit | 0f5c94bbd1709f3942553281460673ba86e715fd (patch) | |
tree | 445b312006c37adfe6d2f8f205073ae531eb2024 /source | |
parent | b555b8dedce058985bfe8c544c8c46ba4821bc1a (diff) |
Armature: add Select Linked (Ctrl-L)
This matches select linked for other modes (curve, mesh)
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/editors/armature/armature_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/armature/armature_ops.c | 1 | ||||
-rw-r--r-- | source/blender/editors/armature/armature_select.c | 198 |
3 files changed, 156 insertions, 44 deletions
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index ba914080d98..37f0c7197a9 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -59,6 +59,7 @@ void ARMATURE_OT_select_mirror(struct wmOperatorType *ot); void ARMATURE_OT_select_more(struct wmOperatorType *ot); void ARMATURE_OT_select_less(struct wmOperatorType *ot); void ARMATURE_OT_select_hierarchy(struct wmOperatorType *ot); +void ARMATURE_OT_select_linked_pick(struct wmOperatorType *ot); void ARMATURE_OT_select_linked(struct wmOperatorType *ot); void ARMATURE_OT_select_similar(struct wmOperatorType *ot); void ARMATURE_OT_shortest_path_pick(struct wmOperatorType *ot); diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index 097d384a609..b304ce92a54 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -56,6 +56,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(ARMATURE_OT_select_less); WM_operatortype_append(ARMATURE_OT_select_hierarchy); WM_operatortype_append(ARMATURE_OT_select_linked); + WM_operatortype_append(ARMATURE_OT_select_linked_pick); WM_operatortype_append(ARMATURE_OT_select_similar); WM_operatortype_append(ARMATURE_OT_shortest_path_pick); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index e927765304f..4eb63df09ab 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -293,25 +293,27 @@ void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel, Base **r_ba return NULL; } -/* **************** EditMode stuff ********************** */ +/* -------------------------------------------------------------------- */ +/** \name Select Linked Implementation + * + * Shared logic for select linked all/pick. + * + * Use #BONE_DONE flag to select linked. + * \{ */ -static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event) +/** + * \param all_forks: Control how chains are stepped over. + * true: select all connected bones traveling up & down forks. + * false: select all parents and all children, but not the children of the root bone. + */ +static bool armature_select_linked_impl(Object *ob, const bool select, const bool all_forks) { - const bool select = !RNA_boolean_get(op->ptr, "deselect"); - /* true: select all connected bones traveling up & down forks. - * false: select all parents and all children, but not the children of the root bone. */ - const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); - - view3d_operator_needs_opengl(C); - BKE_object_update_select_id(CTX_data_main(C)); - - Base *base = NULL; - EditBone *ebone_active = get_nearest_bone(C, event->mval, true, &base); - bArmature *arm = base->object->data; + bool changed = false; + bArmature *arm = ob->data; - if (ebone_active == NULL || !EBONE_SELECTABLE(arm, ebone_active)) { - return OPERATOR_CANCELLED; - } + /* Implementation note, this flood-fills selected bones with the 'TOUCH' flag, + * even though this is a loop-within a loop, walking up the parent chain only touches new bones. + * Bones that have been touched are skipped, so the complexity is OK. */ enum { /* Bone has been walked over, it's LINK value can be read. */ @@ -320,36 +322,44 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv LINK = (1 << 1), }; - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - ebone->temp.i = 0; - } - #define CHECK_PARENT(ebone) \ (((ebone)->flag & BONE_CONNECTED) && \ ((ebone)->parent ? EBONE_SELECTABLE(arm, (ebone)->parent) : false)) + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->temp.i = 0; + } + /* Select parents. */ - for (EditBone *ebone = ebone_active; ebone; ebone = CHECK_PARENT(ebone) ? ebone->parent : NULL) { - if ((ebone->flag & BONE_UNSELECTABLE) == 0) { + for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (ebone_iter->temp.i & TOUCH) { + continue; + } + if ((ebone_iter->flag & BONE_DONE) == 0) { + continue; + } + + ebone_iter->temp.i |= TOUCH | LINK; + + /* We have an un-touched link. */ + for (EditBone *ebone = ebone_iter; ebone; ebone = CHECK_PARENT(ebone) ? ebone->parent : NULL) { ED_armature_ebone_select_set(ebone, select); + changed = true; + if (all_forks) { - ebone->temp.i = (TOUCH | LINK); + ebone->temp.i |= (TOUCH | LINK); } else { - ebone->temp.i = TOUCH; + ebone->temp.i |= TOUCH; + } + /* Don't walk onto links (messes up 'all_forks' logic). */ + if (ebone->parent && ebone->parent->temp.i & LINK) { + break; } } } - if (all_forks == false) { - ebone_active->temp.i = LINK; - } - - /* Select children. - * - * Implementation note, this flood-fills selected bones with the 'TOUCH' flag, - * even though this is a loop-within a loop, walking up the parent chain only touches new bones. - * Bones that have been touched are skipped, so the complexity is OK. */ + /* Select children. */ for (EditBone *ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { /* No need to 'touch' this bone as it won't be walked over when scanning up the chain. */ if (!CHECK_PARENT(ebone_iter)) { @@ -374,6 +384,7 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv if ((ebone->temp.i & LINK) == 0) { ebone->temp.i |= LINK; ED_armature_ebone_select_set(ebone, select); + changed = true; } } } @@ -381,32 +392,129 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv #undef CHECK_PARENT - ED_outliner_select_sync_from_edit_bone_tag(C); + if (changed) { + ED_armature_edit_sync_selection(arm->edbo); + DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, ob); + } - ED_armature_edit_sync_selection(arm->edbo); + return changed; +} - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE); +/** \} */ +/* -------------------------------------------------------------------- */ +/** \name Select Linked Operator + * \{ */ + +static int armature_select_linked_exec(bContext *C, wmOperator *op) +{ + const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); + + bool changed_multi = false; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + bArmature *arm = ob->data; + + bool found = false; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone) && + (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL))) { + ebone->flag |= BONE_DONE; + found = true; + } + else { + ebone->flag &= ~BONE_DONE; + } + } + + if (found) { + if (armature_select_linked_impl(ob, true, all_forks)) { + changed_multi = true; + } + } + } + MEM_freeN(objects); + + if (changed_multi) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } return OPERATOR_FINISHED; } -static bool armature_select_linked_poll(bContext *C) +void ARMATURE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked All"; + ot->idname = "ARMATURE_OT_select_linked"; + ot->description = "Select all bones linked by parent/child connections to the current selection"; + + /* api callbacks */ + ot->exec = armature_select_linked_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Leave disabled by default as this matches pose mode. */ + RNA_def_boolean(ot->srna, "all_forks", 0, "All Forks", "Follow forks in the parents chain"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked (Cursor Pick) Operator + * \{ */ + +static int armature_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const bool select = !RNA_boolean_get(op->ptr, "deselect"); + const bool all_forks = RNA_boolean_get(op->ptr, "all_forks"); + + view3d_operator_needs_opengl(C); + BKE_object_update_select_id(CTX_data_main(C)); + + Base *base = NULL; + EditBone *ebone_active = get_nearest_bone(C, event->mval, true, &base); + bArmature *arm = base->object->data; + + if (ebone_active == NULL || !EBONE_SELECTABLE(arm, ebone_active)) { + return OPERATOR_CANCELLED; + } + + /* Initialize flags. */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~BONE_DONE; + } + ebone_active->flag |= BONE_DONE; + + if (armature_select_linked_impl(base->object, select, all_forks)) { + ED_outliner_select_sync_from_edit_bone_tag(C); + } + + return OPERATOR_FINISHED; +} + +static bool armature_select_linked_pick_poll(bContext *C) { return (ED_operator_view3d_active(C) && ED_operator_editarmature(C)); } -void ARMATURE_OT_select_linked(wmOperatorType *ot) +void ARMATURE_OT_select_linked_pick(wmOperatorType *ot) { /* identifiers */ - ot->name = "Select Connected"; - ot->idname = "ARMATURE_OT_select_linked"; - ot->description = "Select bones related to selected ones by parent/child relationships"; + ot->name = "Select Linked"; + ot->idname = "ARMATURE_OT_select_linked_pick"; + ot->description = "(De)select bones linked by parent/child connections under the mouse cursor"; /* api callbacks */ /* leave 'exec' unset */ - ot->invoke = armature_select_linked_invoke; - ot->poll = armature_select_linked_poll; + ot->invoke = armature_select_linked_pick_invoke; + ot->poll = armature_select_linked_pick_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -416,6 +524,8 @@ void ARMATURE_OT_select_linked(wmOperatorType *ot) RNA_def_boolean(ot->srna, "all_forks", 0, "All Forks", "Follow forks in the parents chain"); } +/** \} */ + /* utility function for get_nearest_editbonepoint */ static int selectbuffer_ret_hits_12(unsigned int *UNUSED(buffer), const int hits12) { |