diff options
author | Campbell Barton <ideasman42@gmail.com> | 2018-12-13 11:05:11 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2018-12-13 13:11:21 +0300 |
commit | cef2a25518dd41beb5335e73e5b765926b1eb387 (patch) | |
tree | a7838346ba5e2618dd35ffd43f15976838bfc565 /source/blender/editors/armature/armature_select.c | |
parent | e79d7747d21f3d3079c759abe0f20a7904aded80 (diff) |
Armature Edit Mode: improve box/lasso select
Mostly rewrite logic which now matches (de)select picking,
share between both operators.
- Support all selection operations (eSelectOp), fixes T59255.
- Add function that selects using 'BONESEL_*' flags & eSelectOp.
This avoids lasso & box select having to handle selection flushing.
- Fix strange behavior with lasso where selecting a bone in a chain
would only select the tip (from 2.7x).
Diffstat (limited to 'source/blender/editors/armature/armature_select.c')
-rw-r--r-- | source/blender/editors/armature/armature_select.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index d817fbf5229..830798fa737 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -732,6 +732,189 @@ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, b return false; } +/* -------------------------------------------------------------------- */ +/** \name Select Op From Tagged + * + * Implements #ED_armature_edit_select_op_from_tagged + * \{ */ + +static bool armature_edit_select_op_apply( + bArmature *arm, EditBone *ebone, const eSelectOp sel_op, int is_ignore_flag, int is_inside_flag) +{ + BLI_assert(!(is_ignore_flag & ~(BONESEL_ROOT | BONESEL_TIP))); + BLI_assert(!(is_inside_flag & ~(BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE))); + BLI_assert(EBONE_VISIBLE(arm, ebone)); + bool changed = false; + bool is_point_done = false; + int points_proj_tot = 0; + BLI_assert(ebone->flag == ebone->temp.i); + const int ebone_flag_prev = ebone->flag; + + if ((is_ignore_flag & BONE_ROOTSEL) == 0) { + points_proj_tot++; + const bool is_select = ebone->flag & BONE_ROOTSEL; + const bool is_inside = is_inside_flag & BONESEL_ROOT; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_ROOTSEL); + } + } + is_point_done |= is_inside; + } + + if ((is_ignore_flag & BONE_TIPSEL) == 0) { + points_proj_tot++; + const bool is_select = ebone->flag & BONE_TIPSEL; + const bool is_inside = is_inside_flag & BONESEL_TIP; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_TIPSEL); + } + } + is_point_done |= is_inside; + } + + /* if one of points selected, we skip the bone itself */ + if ((is_point_done == false) && (points_proj_tot == 2)) { + const bool is_select = ebone->flag & BONE_SELECTED; + { + const bool is_inside = is_inside_flag & BONESEL_BONE; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) { + SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + } + } + } + + changed = true; + } + changed |= is_point_done; + + if (ebone_flag_prev != ebone->flag) { + ebone->temp.i = ebone->flag; + ebone->flag = ebone_flag_prev; + ebone->flag = ebone_flag_prev | BONE_DONE; + changed = true; + } + + return changed; +} + +/** + * Perform a selection operation on elements which have been 'touched', use for lasso & border select + * but can be used elsewhere too. + * + * Tagging is done via #EditBone.temp.i using: #BONESEL_ROOT, #BONESEL_TIP, #BONESEL_BONE + * And optionally ignoring end-points using the #BONESEL_ROOT, #BONESEL_TIP right shifted 16 bits. + * (used when the values are clipped outside the view). + * + * \param sel_op: #eSelectOp type. + * + * \note Visibility checks must be done by the caller. + */ +bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op) +{ + bool changed = false; + + /* Initialize flags. */ + { + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + + /* Flush the parent flag to this bone + * so we don't need to check the parent when adjusting the selection. */ + if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { + if (ebone->parent->flag & BONE_TIPSEL) { + ebone->flag |= BONE_ROOTSEL; + } + else { + ebone->flag &= ~BONE_ROOTSEL; + } + + /* Flush the 'temp.i' flag. */ + if (ebone->parent->temp.i & BONESEL_TIP) { + ebone->temp.i |= BONESEL_ROOT; + } + } + ebone->flag &= ~BONE_DONE; + } + } + + /* Apply selection from bone selection flags. */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->temp.i != 0) { + int is_ignore_flag = ((ebone->temp.i << 16) & (BONESEL_ROOT | BONESEL_TIP)); + int is_inside_flag = (ebone->temp.i & (BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE)); + + /* Use as previous bone flag from now on. */ + ebone->temp.i = ebone->flag; + + /* When there is a partial selection without both endpoints, only select an endpoint. */ + if ((is_inside_flag & BONESEL_BONE) && + (is_inside_flag & (BONESEL_ROOT | BONESEL_TIP)) && + ((is_inside_flag & (BONESEL_ROOT | BONESEL_TIP)) != (BONESEL_ROOT | BONESEL_TIP))) + { + is_inside_flag &= ~BONESEL_BONE; + } + + changed |= armature_edit_select_op_apply(arm, ebone, sel_op, is_ignore_flag, is_inside_flag); + } + } + + if (changed) { + /* Cleanup flags. */ + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->flag & BONE_DONE) { + SWAP(int, ebone->temp.i, ebone->flag); + ebone->flag |= BONE_DONE; + if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { + if ((ebone->parent->flag & BONE_DONE) == 0) { + /* Checked below. */ + ebone->parent->temp.i = ebone->parent->flag; + } + } + } + } + + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->flag & BONE_DONE) { + if ((ebone->flag & BONE_CONNECTED) && ebone->parent) { + bool is_parent_tip_changed = (ebone->parent->flag & BONE_TIPSEL) != (ebone->parent->temp.i & BONE_TIPSEL); + if ((ebone->temp.i & BONE_ROOTSEL) == 0) { + if ((ebone->flag & BONE_ROOTSEL) != 0) { + ebone->parent->flag |= BONE_TIPSEL; + } + } + else { + if ((ebone->flag & BONE_ROOTSEL) == 0) { + ebone->parent->flag &= ~BONE_TIPSEL; + + } + } + + + if (is_parent_tip_changed == false) { + /* Keep tip selected if the parent remains selected. */ + if (ebone->parent->flag & BONE_SELECTED) { + ebone->parent->flag |= BONE_TIPSEL; + } + } + + } + ebone->flag &= ~BONE_DONE; + } + } + + ED_armature_edit_sync_selection(arm->edbo); + ED_armature_edit_validate_active(arm); + } + + return changed; +} + +/** \} */ /* **************** Selections ******************/ |