Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2020-03-31 06:39:41 +0300
committerCampbell Barton <ideasman42@gmail.com>2020-03-31 07:11:11 +0300
commit0f5c94bbd1709f3942553281460673ba86e715fd (patch)
tree445b312006c37adfe6d2f8f205073ae531eb2024 /source/blender/editors/armature/armature_select.c
parentb555b8dedce058985bfe8c544c8c46ba4821bc1a (diff)
Armature: add Select Linked (Ctrl-L)
This matches select linked for other modes (curve, mesh)
Diffstat (limited to 'source/blender/editors/armature/armature_select.c')
-rw-r--r--source/blender/editors/armature/armature_select.c198
1 files changed, 154 insertions, 44 deletions
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)
{