Blender V4.5
armature_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include "DNA_armature_types.h"
11#include "DNA_object_types.h"
12#include "DNA_scene_types.h"
13
14#include "BLI_listbase.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_vector.h"
17#include "BLI_rect.h"
18#include "BLI_string_utils.hh"
19
20#include "BKE_action.hh"
21#include "BKE_armature.hh"
22#include "BKE_context.hh"
23#include "BKE_layer.hh"
24#include "BKE_object.hh"
25#include "BKE_object_types.hh"
26#include "BKE_report.hh"
27
28#include "RNA_access.hh"
29#include "RNA_define.hh"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34#include "ED_armature.hh"
35#include "ED_object.hh"
36#include "ED_outliner.hh"
37#include "ED_screen.hh"
38#include "ED_select_utils.hh"
39#include "ED_view3d.hh"
40
41#include "DEG_depsgraph.hh"
42
43#include "GPU_select.hh"
44
45#include "ANIM_armature.hh"
47#include "ANIM_bonecolor.hh"
48
49#include "armature_intern.hh"
50
51using blender::Span;
52using blender::Vector;
53
54/* utility macros for storing a temp int in the bone (selection flag) */
55#define EBONE_PREV_FLAG_GET(ebone) ((void)0, (ebone)->temp.i)
56#define EBONE_PREV_FLAG_SET(ebone, val) ((ebone)->temp.i = val)
57
58/* -------------------------------------------------------------------- */
61
63 const uint select_id,
64 EditBone **r_ebone)
65{
66 const uint hit_object = select_id & 0xFFFF;
67 Base *base = nullptr;
68 EditBone *ebone = nullptr;
69 /* TODO(@ideasman42): optimize, eg: sort & binary search. */
70 for (Base *base_iter : bases) {
71 if (base_iter->object->runtime->select_id == hit_object) {
72 base = base_iter;
73 break;
74 }
75 }
76 if (base != nullptr) {
77 const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
78 bArmature *arm = static_cast<bArmature *>(base->object->data);
79 ebone = static_cast<EditBone *>(BLI_findlink(arm->edbo, hit_bone));
80 }
81 *r_ebone = ebone;
82 return base;
83}
84
86 const uint select_id,
87 EditBone **r_ebone)
88{
89 const uint hit_object = select_id & 0xFFFF;
90 Object *ob = nullptr;
91 EditBone *ebone = nullptr;
92 /* TODO(@ideasman42): optimize, eg: sort & binary search. */
93 for (Object *object_iter : objects) {
94 if (object_iter->runtime->select_id == hit_object) {
95 ob = object_iter;
96 break;
97 }
98 }
99 if (ob != nullptr) {
100 const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
101 bArmature *arm = static_cast<bArmature *>(ob->data);
102 ebone = static_cast<EditBone *>(BLI_findlink(arm->edbo, hit_bone));
103 }
104 *r_ebone = ebone;
105 return ob;
106}
107
109 const uint select_id,
110 bPoseChannel **r_pchan)
111{
112 const uint hit_object = select_id & 0xFFFF;
113 Base *base = nullptr;
114 bPoseChannel *pchan = nullptr;
115 /* TODO(@ideasman42): optimize, eg: sort & binary search. */
116 for (Base *base_iter : bases) {
117 if (base_iter->object->runtime->select_id == hit_object) {
118 base = base_iter;
119 break;
120 }
121 }
122 if (base != nullptr) {
123 if (base->object->pose != nullptr) {
124 const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
125 /* pchan may be nullptr. */
126 pchan = static_cast<bPoseChannel *>(BLI_findlink(&base->object->pose->chanbase, hit_bone));
127 }
128 }
129 *r_pchan = pchan;
130 return base;
131}
132
134 const uint select_id,
135 Bone **r_bone)
136{
137 bPoseChannel *pchan = nullptr;
138 Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, select_id, &pchan);
139 *r_bone = pchan ? pchan->bone : nullptr;
140 return base;
141}
142
144
145/* -------------------------------------------------------------------- */
153
154/* See if there are any selected bones in this buffer */
155/* only bones from base are checked on */
156static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode,
157 const Span<Base *> bases,
158 const Span<GPUSelectResult> hit_results,
159 bool findunsel,
160 bool do_nearest,
161 Base **r_base)
162{
163 bPoseChannel *pchan;
164 EditBone *ebone;
165 void *firstunSel = nullptr, *firstSel = nullptr, *data;
166 Base *firstunSel_base = nullptr, *firstSel_base = nullptr;
167 bool takeNext = false;
168 int minsel = 0xffffffff, minunsel = 0xffffffff;
169
170 for (const GPUSelectResult &hit_result : hit_results) {
171 uint hit_id = hit_result.id;
172
173 if (hit_id & BONESEL_ANY) { /* to avoid including objects in selection */
174 Base *base = nullptr;
175 bool sel;
176
177 hit_id &= ~BONESEL_ANY;
178 /* Determine what the current bone is */
179 if (is_editmode == false) {
180 base = ED_armature_base_and_pchan_from_select_buffer(bases, hit_id, &pchan);
181 if (pchan != nullptr) {
182 if (pchan->bone->flag & BONE_UNSELECTABLE) {
183 continue;
184 }
185 if (findunsel) {
186 sel = (pchan->bone->flag & BONE_SELECTED);
187 }
188 else {
189 sel = !(pchan->bone->flag & BONE_SELECTED);
190 }
191
192 data = pchan;
193 }
194 else {
195 data = nullptr;
196 sel = false;
197 }
198 }
199 else {
200 base = ED_armature_base_and_ebone_from_select_buffer(bases, hit_id, &ebone);
201 if (ebone->flag & BONE_UNSELECTABLE) {
202 continue;
203 }
204 if (findunsel) {
205 sel = (ebone->flag & BONE_SELECTED);
206 }
207 else {
208 sel = !(ebone->flag & BONE_SELECTED);
209 }
210
211 data = ebone;
212 }
213
214 if (data) {
215 if (sel) {
216 if (do_nearest) {
217 if (minsel > hit_result.depth) {
218 firstSel = data;
219 firstSel_base = base;
220 minsel = hit_result.depth;
221 }
222 }
223 else {
224 if (!firstSel) {
225 firstSel = data;
226 firstSel_base = base;
227 }
228 takeNext = true;
229 }
230 }
231 else {
232 if (do_nearest) {
233 if (minunsel > hit_result.depth) {
234 firstunSel = data;
235 firstunSel_base = base;
236 minunsel = hit_result.depth;
237 }
238 }
239 else {
240 if (!firstunSel) {
241 firstunSel = data;
242 firstunSel_base = base;
243 }
244 if (takeNext) {
245 *r_base = base;
246 return data;
247 }
248 }
249 }
250 }
251 }
252 }
253
254 if (firstunSel) {
255 *r_base = firstunSel_base;
256 return firstunSel;
257 }
258 *r_base = firstSel_base;
259 return firstSel;
260}
261
263 const GPUSelectResult *hit_results,
264 const int hits,
265 bool findunsel,
266 bool do_nearest,
267 Base **r_base)
268{
269 const bool is_editmode = true;
271 is_editmode, bases, {hit_results, hits}, findunsel, do_nearest, r_base));
272}
273
275 const GPUSelectResult *hit_results,
276 const int hits,
277 bool findunsel,
278 bool do_nearest,
279 Base **r_base)
280{
281 const bool is_editmode = false;
283 is_editmode, bases, {hit_results, hits}, findunsel, do_nearest, r_base));
284}
285
287 const GPUSelectResult *hit_results,
288 const int hits,
289 bool findunsel,
290 bool do_nearest,
291 Base **r_base)
292{
294 bases, hit_results, hits, findunsel, do_nearest, r_base);
295 return pchan ? pchan->bone : nullptr;
296}
297
299
300/* -------------------------------------------------------------------- */
308
315 const bool is_editmode, bContext *C, const int xy[2], bool findunsel, Base **r_base)
316{
318 rcti rect;
319 GPUSelectBuffer buffer;
320 int hits;
321
323 BLI_assert((vc.obedit != nullptr) == is_editmode);
324
325 BLI_rcti_init_pt_radius(&rect, xy, 0);
326
327 /* Don't use hits with this ID, (armature drawing uses this). */
328 const int select_id_ignore = -1;
329
331 &vc, &buffer, &rect, VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP, select_id_ignore);
332
333 *r_base = nullptr;
334
335 if (hits > 0) {
336 Vector<Base *> bases;
337
338 if (vc.obedit != nullptr) {
340 }
341 else {
343 }
344
346 is_editmode, bases, buffer.storage.as_span().take_front(hits), findunsel, true, r_base);
347
348 return bone;
349 }
350 return nullptr;
351}
352
353EditBone *ED_armature_pick_ebone(bContext *C, const int xy[2], bool findunsel, Base **r_base)
354{
355 const bool is_editmode = true;
356 return static_cast<EditBone *>(
357 ed_armature_pick_bone_impl(is_editmode, C, xy, findunsel, r_base));
358}
359
360bPoseChannel *ED_armature_pick_pchan(bContext *C, const int xy[2], bool findunsel, Base **r_base)
361{
362 const bool is_editmode = false;
363 return static_cast<bPoseChannel *>(
364 ed_armature_pick_bone_impl(is_editmode, C, xy, findunsel, r_base));
365}
366
367Bone *ED_armature_pick_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base)
368{
369 bPoseChannel *pchan = ED_armature_pick_pchan(C, xy, findunsel, r_base);
370 return pchan ? pchan->bone : nullptr;
371}
372
374
375/* -------------------------------------------------------------------- */
382
388static bool armature_select_linked_impl(Object *ob, const bool select, const bool all_forks)
389{
390 bool changed = false;
391 bArmature *arm = static_cast<bArmature *>(ob->data);
392
393 /* Implementation note, this flood-fills selected bones with the 'TOUCH' flag,
394 * even though this is a loop-within a loop, walking up the parent chain only touches new bones.
395 * Bones that have been touched are skipped, so the complexity is OK. */
396
397 enum {
398 /* Bone has been walked over, its LINK value can be read. */
399 TOUCH = (1 << 0),
400 /* When TOUCH has been set, this flag can be checked to see if the bone is connected. */
401 LINK = (1 << 1),
402 };
403
404#define CHECK_PARENT(ebone) \
405\
406 (((ebone)->flag & BONE_CONNECTED) && \
407 ((ebone)->parent ? EBONE_SELECTABLE(arm, (ebone)->parent) : false))
408
409 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
410 ebone->temp.i = 0;
411 }
412
413 /* Select parents. */
414 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
415 if (ebone_iter->temp.i & TOUCH) {
416 continue;
417 }
418 if ((ebone_iter->flag & BONE_DONE) == 0) {
419 continue;
420 }
421
422 ebone_iter->temp.i |= TOUCH | LINK;
423
424 /* We have an un-touched link. */
425 for (EditBone *ebone = ebone_iter; ebone;
426 ebone = CHECK_PARENT(ebone) ? ebone->parent : nullptr)
427 {
429 changed = true;
430
431 if (all_forks) {
432 ebone->temp.i |= (TOUCH | LINK);
433 }
434 else {
435 ebone->temp.i |= TOUCH;
436 }
437 /* Don't walk onto links (messes up 'all_forks' logic). */
438 if (ebone->parent && ebone->parent->temp.i & LINK) {
439 break;
440 }
441 }
442 }
443
444 /* Select children. */
445 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
446 /* No need to 'touch' this bone as it won't be walked over when scanning up the chain. */
447 if (!CHECK_PARENT(ebone_iter)) {
448 continue;
449 }
450 if (ebone_iter->temp.i & TOUCH) {
451 continue;
452 }
453
454 /* First check if we're marked. */
455 EditBone *ebone_touched_parent = nullptr;
456 for (EditBone *ebone = ebone_iter; ebone;
457 ebone = CHECK_PARENT(ebone) ? ebone->parent : nullptr)
458 {
459 if (ebone->temp.i & TOUCH) {
460 ebone_touched_parent = ebone;
461 break;
462 }
463 ebone->temp.i |= TOUCH;
464 }
465
466 if ((ebone_touched_parent != nullptr) && (ebone_touched_parent->temp.i & LINK)) {
467 for (EditBone *ebone = ebone_iter; ebone != ebone_touched_parent; ebone = ebone->parent) {
468 if ((ebone->temp.i & LINK) == 0) {
469 ebone->temp.i |= LINK;
471 changed = true;
472 }
473 }
474 }
475 }
476
477#undef CHECK_PARENT
478
479 if (changed) {
483 }
484
485 return changed;
486}
487
489
490/* -------------------------------------------------------------------- */
493
495{
496 const bool all_forks = RNA_boolean_get(op->ptr, "all_forks");
497
498 bool changed_multi = false;
499 const Scene *scene = CTX_data_scene(C);
500 ViewLayer *view_layer = CTX_data_view_layer(C);
502 scene, view_layer, CTX_wm_view3d(C));
503 for (Object *ob : objects) {
504 bArmature *arm = static_cast<bArmature *>(ob->data);
505
506 bool found = false;
507 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
509 (ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)))
510 {
511 ebone->flag |= BONE_DONE;
512 found = true;
513 }
514 else {
515 ebone->flag &= ~BONE_DONE;
516 }
517 }
518
519 if (found) {
520 if (armature_select_linked_impl(ob, true, all_forks)) {
521 changed_multi = true;
522 }
523 }
524 }
525
526 if (changed_multi) {
528 }
529 return OPERATOR_FINISHED;
530}
531
533{
534 /* identifiers */
535 ot->name = "Select Linked All";
536 ot->idname = "ARMATURE_OT_select_linked";
537 ot->description = "Select all bones linked by parent/child connections to the current selection";
538
539 /* API callbacks. */
542
543 /* flags */
545
546 /* Leave disabled by default as this matches pose mode. */
547 RNA_def_boolean(ot->srna, "all_forks", false, "All Forks", "Follow forks in the parents chain");
548}
549
551
552/* -------------------------------------------------------------------- */
555
557 wmOperator *op,
558 const wmEvent *event)
559{
560 const bool select = !RNA_boolean_get(op->ptr, "deselect");
561 const bool all_forks = RNA_boolean_get(op->ptr, "all_forks");
562
565
566 Base *base = nullptr;
567 EditBone *ebone_active = ED_armature_pick_ebone(C, event->mval, true, &base);
568
569 if (ebone_active == nullptr) {
570 return OPERATOR_CANCELLED;
571 }
572
573 bArmature *arm = static_cast<bArmature *>(base->object->data);
574 if (!EBONE_SELECTABLE(arm, ebone_active)) {
575 return OPERATOR_CANCELLED;
576 }
577
578 /* Initialize flags. */
579 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
580 ebone->flag &= ~BONE_DONE;
581 }
582 ebone_active->flag |= BONE_DONE;
583
584 if (armature_select_linked_impl(base->object, select, all_forks)) {
586 }
587
588 return OPERATOR_FINISHED;
589}
590
595
597{
598 /* identifiers */
599 ot->name = "Select Linked";
600 ot->idname = "ARMATURE_OT_select_linked_pick";
601 ot->description = "(De)select bones linked by parent/child connections under the mouse cursor";
602
603 /* API callbacks. */
604 /* leave 'exec' unset */
607
608 /* flags */
610
611 RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
612 /* Leave disabled by default as this matches pose mode. */
613 RNA_def_boolean(ot->srna, "all_forks", false, "All Forks", "Follow forks in the parents chain");
614}
615
617
618/* -------------------------------------------------------------------- */
621
622/* utility function for get_nearest_editbonepoint */
624 const int hits12)
625{
626 return hits12;
627}
628
630 const int hits12,
631 const int hits5)
632{
633 const int ofs = hits12;
634 /* Shift results to beginning. */
635 hit_results.slice(0, hits5).copy_from(hit_results.slice(ofs, hits5));
636 return hits5;
637}
638
639/* does bones and points */
640/* note that BONE ROOT only gets drawn for root bones (or without IK) */
642 ViewContext *vc, bool findunsel, bool use_cycle, Base **r_base, int *r_selmask)
643{
644 GPUSelectBuffer buffer;
645 struct Result {
646 uint select_id;
647 Base *base;
648 EditBone *ebone;
649 };
650 Result *result = nullptr;
651 Result result_cycle{};
652 result_cycle.select_id = -1;
653 result_cycle.base = nullptr;
654 result_cycle.ebone = nullptr;
655 Result result_bias{};
656 result_bias.select_id = -1;
657 result_bias.base = nullptr;
658 result_bias.ebone = nullptr;
659
660 /* Find the bone after the current (selected) active bone, so as to bump up its chances in
661 * selection. this way overlapping bones will cycle selection state as with objects. */
662 Object *obedit_orig = vc->obedit;
663 EditBone *ebone_active_orig = static_cast<bArmature *>(obedit_orig->data)->act_edbone;
664 if (ebone_active_orig &&
665 (ebone_active_orig->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) == 0)
666 {
667 ebone_active_orig = nullptr;
668 }
669
670 if (ebone_active_orig == nullptr) {
671 use_cycle = false;
672 }
673
674 if (use_cycle) {
675 use_cycle = !WM_cursor_test_motion_and_update(vc->mval);
676 }
677
678 const bool do_nearest = !(XRAY_ACTIVE(vc->v3d) || use_cycle);
679
680 /* matching logic from 'mixed_bones_object_selectbuffer' */
681 int hits = 0;
682 /* Don't use hits with this ID, (armature drawing uses this). */
683 const int select_id_ignore = -1;
684
685 /* we _must_ end cache before return, use 'goto cache_end' */
687
688 {
690
691 GPUSelectStorage &storage = buffer.storage;
692 rcti rect;
693 BLI_rcti_init_pt_radius(&rect, vc->mval, 12);
694 /* VIEW3D_SELECT_PICK_ALL needs to be used or unselectable bones can block selectability of
695 * bones further back. See #123963. */
696 const int hits12 = view3d_gpu_select_with_id_filter(
697 vc, &buffer, &rect, VIEW3D_SELECT_PICK_ALL, select_filter, select_id_ignore);
698
699 if (hits12 == 1) {
700 hits = selectbuffer_ret_hits_12(storage.as_mutable_span(), hits12);
701 goto cache_end;
702 }
703 else if (hits12 > 0) {
704 BLI_rcti_init_pt_radius(&rect, vc->mval, 5);
705 const int hits5 = view3d_gpu_select_with_id_filter(
706 vc, &buffer, &rect, VIEW3D_SELECT_PICK_ALL, select_filter, select_id_ignore);
707
708 if (hits5 == 1) {
709 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits12, hits5);
710 goto cache_end;
711 }
712
713 if (hits5 > 0) {
714 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits12, hits5);
715 goto cache_end;
716 }
717 else {
718 hits = selectbuffer_ret_hits_12(storage.as_mutable_span(), hits12);
719 goto cache_end;
720 }
721 }
722 }
723
724cache_end:
726
728 vc->scene, vc->view_layer, vc->v3d);
729
730 /* See if there are any selected bones in this group */
731 if (hits > 0) {
732 if (hits == 1) {
733 result_bias.select_id = buffer.storage[0].id;
735 bases, result_bias.select_id, &result_bias.ebone);
736 }
737 else {
738 int bias_max = INT_MIN;
739
740 /* Track Cycle Variables
741 * - Offset is always set to the active bone.
742 * - The object & bone indices subtracted by the 'offset.as_u32' value.
743 * Unsigned subtraction wrapping means we always select the next bone in the cycle.
744 */
745 struct {
746 union {
747 uint32_t as_u32;
748 struct {
749#ifdef __BIG_ENDIAN__
750 uint16_t ob;
751 uint16_t bone;
752#else
753 uint16_t bone;
754 uint16_t ob;
755#endif
756 };
757 } offset, test, best;
758 } cycle_order;
759
760 if (use_cycle) {
761 bArmature *arm = static_cast<bArmature *>(obedit_orig->data);
762 int ob_index = obedit_orig->runtime->select_id & 0xFFFF;
763 int bone_index = BLI_findindex(arm->edbo, ebone_active_orig);
764 /* Offset from the current active bone, so we cycle onto the next. */
765 cycle_order.offset.ob = ob_index;
766 cycle_order.offset.bone = bone_index;
767 /* The value of the active bone (with offset subtracted, a signal to always overwrite). */
768 cycle_order.best.as_u32 = 0;
769 }
770
771 int min_depth = INT_MAX;
772 for (int i = 0; i < hits; i++) {
773 const GPUSelectResult &hit_result = buffer.storage[i];
774 const uint select_id = hit_result.id;
775
776 Base *base = nullptr;
777 EditBone *ebone;
778 base = ED_armature_base_and_ebone_from_select_buffer(bases, select_id, &ebone);
779 /* If this fails, selection code is setting the selection ID's incorrectly. */
780 BLI_assert(base && ebone);
781
782 if (ebone->flag & BONE_UNSELECTABLE) {
783 continue;
784 }
785
786 /* Prioritized selection. */
787 {
788 int bias;
789 /* clicks on bone points get advantage */
790 if (select_id & (BONESEL_ROOT | BONESEL_TIP)) {
791 /* but also the unselected one */
792 if (findunsel) {
793 if ((select_id & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) {
794 bias = 4;
795 }
796 else if ((select_id & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) {
797 bias = 4;
798 }
799 else {
800 bias = 3;
801 }
802 }
803 else {
804 bias = 4;
805 }
806 }
807 else {
808 /* bone found */
809 if (findunsel) {
810 if ((ebone->flag & BONE_SELECTED) == 0) {
811 bias = 2;
812 }
813 else {
814 bias = 1;
815 }
816 }
817 else {
818 bias = 2;
819 }
820 }
821
822 if (bias > bias_max) {
823 bias_max = bias;
824
825 min_depth = hit_result.depth;
826 result_bias.select_id = select_id;
827 result_bias.base = base;
828 result_bias.ebone = ebone;
829 }
830 else if (bias == bias_max && do_nearest) {
831 if (min_depth > hit_result.depth) {
832 min_depth = hit_result.depth;
833 result_bias.select_id = select_id;
834 result_bias.base = base;
835 result_bias.ebone = ebone;
836 }
837 }
838 }
839
840 /* Cycle selected items (objects & bones). */
841 if (use_cycle) {
842 cycle_order.test.ob = select_id & 0xFFFF;
843 cycle_order.test.bone = (select_id & ~BONESEL_ANY) >> 16;
844 if (ebone == ebone_active_orig) {
845 BLI_assert(cycle_order.test.ob == cycle_order.offset.ob);
846 BLI_assert(cycle_order.test.bone == cycle_order.offset.bone);
847 }
848 /* Subtraction as a single value is needed to support cycling through bones
849 * from multiple objects. So once the last bone is selected,
850 * the bits for the bone index wrap into the object,
851 * causing the next object to be stepped onto. */
852 cycle_order.test.as_u32 -= cycle_order.offset.as_u32;
853
854 /* Even though this logic avoids stepping onto the active bone,
855 * always set the 'best' value for the first time.
856 * Otherwise ensure the value is the smallest it can be,
857 * relative to the active bone, as long as it's not the active bone. */
858 if ((cycle_order.best.as_u32 == 0) ||
859 (cycle_order.test.as_u32 && (cycle_order.test.as_u32 < cycle_order.best.as_u32)))
860 {
861 cycle_order.best = cycle_order.test;
862 result_cycle.select_id = select_id;
863 result_cycle.base = base;
864 result_cycle.ebone = ebone;
865 }
866 }
867 }
868 }
869
870 result = (use_cycle && result_cycle.ebone) ? &result_cycle : &result_bias;
871
872 if (result->select_id != -1) {
873 *r_base = result->base;
874
875 *r_selmask = 0;
876 if (result->select_id & BONESEL_ROOT) {
877 *r_selmask |= BONE_ROOTSEL;
878 }
879 if (result->select_id & BONESEL_TIP) {
880 *r_selmask |= BONE_TIPSEL;
881 }
882 if (result->select_id & BONESEL_BONE) {
883 *r_selmask |= BONE_SELECTED;
884 }
885 return result->ebone;
886 }
887 }
888 *r_selmask = 0;
889 *r_base = nullptr;
890 return nullptr;
891}
892
894
895/* -------------------------------------------------------------------- */
898
900{
901 bArmature *arm = static_cast<bArmature *>(obedit->data);
902 bool changed = false;
903 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
904 if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
905 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
906 changed = true;
907 }
908 }
909 return changed;
910}
911
913{
914 bArmature *arm = static_cast<bArmature *>(obedit->data);
915 bool changed = false;
916 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
917 /* first and foremost, bone must be visible and selected */
919 if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
920 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
921 changed = true;
922 }
923 }
924 }
925
926 if (changed) {
928 }
929 return changed;
930}
931
933{
934 bool changed_multi = false;
935 for (Base *base : bases) {
936 Object *obedit = base->object;
937 changed_multi |= ED_armature_edit_deselect_all(obedit);
938 }
939 return changed_multi;
940}
941
943{
944 bool changed_multi = false;
945 for (Base *base : bases) {
946 Object *obedit = base->object;
947 changed_multi |= ED_armature_edit_deselect_all_visible(obedit);
948 }
949 return changed_multi;
950}
951
960
962
963/* -------------------------------------------------------------------- */
966
968 bContext *C, Base *basact, EditBone *ebone, const int selmask, const SelectPick_Params &params)
969{
970 const Scene *scene = CTX_data_scene(C);
971 ViewLayer *view_layer = CTX_data_view_layer(C);
972 View3D *v3d = CTX_wm_view3d(C);
973 bool changed = false;
974 bool found = false;
975
976 if (ebone) {
977 bArmature *arm = static_cast<bArmature *>(basact->object->data);
978 if (EBONE_SELECTABLE(arm, ebone)) {
979 found = true;
980 }
981 }
982
983 if (params.sel_op == SEL_OP_SET) {
984 if ((found && params.select_passthrough) &&
985 (ED_armature_ebone_selectflag_get(ebone) & selmask))
986 {
987 found = false;
988 }
989 else if (found || params.deselect_all) {
990 /* Deselect everything. */
992 scene, view_layer, v3d);
994 changed = true;
995 }
996 }
997
998 if (found) {
1000 bArmature *arm = static_cast<bArmature *>(basact->object->data);
1001
1002 /* By definition the non-root connected bones have no root point drawn,
1003 * so a root selection needs to be delivered to the parent tip. */
1004
1005 if (selmask & BONE_SELECTED) {
1006 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1007
1008 /* Bone is in a chain. */
1009 switch (params.sel_op) {
1010 case SEL_OP_ADD: {
1011 /* Select this bone. */
1012 ebone->flag |= BONE_TIPSEL;
1013 ebone->parent->flag |= BONE_TIPSEL;
1014 break;
1015 }
1016 case SEL_OP_SUB: {
1017 /* Deselect this bone. */
1018 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
1019 /* Only deselect parent tip if it is not selected. */
1020 if (!(ebone->parent->flag & BONE_SELECTED)) {
1021 ebone->parent->flag &= ~BONE_TIPSEL;
1022 }
1023 break;
1024 }
1025 case SEL_OP_XOR: {
1026 /* Toggle inverts this bone's selection. */
1027 if (ebone->flag & BONE_SELECTED) {
1028 /* Deselect this bone. */
1029 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
1030 /* Only deselect parent tip if it is not selected. */
1031 if (!(ebone->parent->flag & BONE_SELECTED)) {
1032 ebone->parent->flag &= ~BONE_TIPSEL;
1033 }
1034 }
1035 else {
1036 /* Select this bone. */
1037 ebone->flag |= BONE_TIPSEL;
1038 ebone->parent->flag |= BONE_TIPSEL;
1039 }
1040 break;
1041 }
1042 case SEL_OP_SET: {
1043 /* Select this bone. */
1044 ebone->flag |= BONE_TIPSEL;
1045 ebone->parent->flag |= BONE_TIPSEL;
1046 break;
1047 }
1048 case SEL_OP_AND: {
1049 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
1050 break;
1051 }
1052 }
1053 }
1054 else {
1055 switch (params.sel_op) {
1056 case SEL_OP_ADD: {
1057 ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
1058 break;
1059 }
1060 case SEL_OP_SUB: {
1061 ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
1062 break;
1063 }
1064 case SEL_OP_XOR: {
1065 /* Toggle inverts this bone's selection. */
1066 if (ebone->flag & BONE_SELECTED) {
1067 ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
1068 }
1069 else {
1070 ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
1071 }
1072 break;
1073 }
1074 case SEL_OP_SET: {
1075 ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
1076 break;
1077 }
1078 case SEL_OP_AND: {
1079 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
1080 break;
1081 }
1082 }
1083 }
1084 }
1085 else {
1086 switch (params.sel_op) {
1087 case SEL_OP_ADD: {
1088 ebone->flag |= selmask;
1089 break;
1090 }
1091 case SEL_OP_SUB: {
1092 ebone->flag &= ~selmask;
1093 break;
1094 }
1095 case SEL_OP_XOR: {
1096 if (ebone->flag & selmask) {
1097 ebone->flag &= ~selmask;
1098 }
1099 else {
1100 ebone->flag |= selmask;
1101 }
1102 break;
1103 }
1104 case SEL_OP_SET: {
1105 ebone->flag |= selmask;
1106 break;
1107 }
1108 case SEL_OP_AND: {
1109 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
1110 break;
1111 }
1112 }
1113 }
1114
1116
1117 /* Then now check for active status. */
1119 arm->act_edbone = ebone;
1120 }
1121
1122 BKE_view_layer_synced_ensure(scene, view_layer);
1123 if (BKE_view_layer_active_base_get(view_layer) != basact) {
1125 }
1126
1129 changed = true;
1130 }
1131
1132 if (changed) {
1134 }
1135
1136 return changed || found;
1137}
1138
1140
1141{
1143 EditBone *nearBone = nullptr;
1144 int selmask;
1145 Base *basact = nullptr;
1146
1148 vc.mval[0] = mval[0];
1149 vc.mval[1] = mval[1];
1150
1151 nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
1152 return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, params);
1153}
1154
1156
1157/* -------------------------------------------------------------------- */
1162
1164 EditBone *ebone,
1165 const eSelectOp sel_op,
1166 int is_ignore_flag,
1167 int is_inside_flag)
1168{
1169 BLI_assert(!(is_ignore_flag & ~(BONESEL_ROOT | BONESEL_TIP)));
1170 BLI_assert(!(is_inside_flag & ~(BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE)));
1172 bool changed = false;
1173 bool is_point_done = false;
1174 int points_proj_tot = 0;
1175 BLI_assert(ebone->flag == ebone->temp.i);
1176 const int ebone_flag_prev = ebone->flag;
1177
1178 if ((is_ignore_flag & BONE_ROOTSEL) == 0) {
1179 points_proj_tot++;
1180 const bool is_select = ebone->flag & BONE_ROOTSEL;
1181 const bool is_inside = is_inside_flag & BONESEL_ROOT;
1182 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1183 if (sel_op_result != -1) {
1184 if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) {
1185 SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_ROOTSEL);
1186 }
1187 }
1188 is_point_done |= is_inside;
1189 }
1190
1191 if ((is_ignore_flag & BONE_TIPSEL) == 0) {
1192 points_proj_tot++;
1193 const bool is_select = ebone->flag & BONE_TIPSEL;
1194 const bool is_inside = is_inside_flag & BONESEL_TIP;
1195 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1196 if (sel_op_result != -1) {
1197 if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) {
1198 SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_TIPSEL);
1199 }
1200 }
1201 is_point_done |= is_inside;
1202 }
1203
1204 /* if one of points selected, we skip the bone itself */
1205 if ((is_point_done == false) && (points_proj_tot == 2)) {
1206 const bool is_select = ebone->flag & BONE_SELECTED;
1207 {
1208 const bool is_inside = is_inside_flag & BONESEL_BONE;
1209 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1210 if (sel_op_result != -1) {
1211 if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) {
1213 ebone->flag, sel_op_result, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
1214 }
1215 }
1216 }
1217
1218 changed = true;
1219 }
1220 changed |= is_point_done;
1221
1222 if (ebone_flag_prev != ebone->flag) {
1223 ebone->temp.i = ebone->flag;
1224 ebone->flag = ebone_flag_prev;
1225 ebone->flag = ebone_flag_prev | BONE_DONE;
1226 changed = true;
1227 }
1228
1229 return changed;
1230}
1231
1233{
1234 bool changed = false;
1235
1236 /* Initialize flags. */
1237 {
1238 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1239
1240 /* Flush the parent flag to this bone
1241 * so we don't need to check the parent when adjusting the selection. */
1242 if ((ebone->flag & BONE_CONNECTED) && ebone->parent) {
1243 if (ebone->parent->flag & BONE_TIPSEL) {
1244 ebone->flag |= BONE_ROOTSEL;
1245 }
1246 else {
1247 ebone->flag &= ~BONE_ROOTSEL;
1248 }
1249
1250 /* Flush the 'temp.i' flag. */
1251 if (ebone->parent->temp.i & BONESEL_TIP) {
1252 ebone->temp.i |= BONESEL_ROOT;
1253 }
1254 }
1255 ebone->flag &= ~BONE_DONE;
1256 }
1257 }
1258
1259 /* Apply selection from bone selection flags. */
1260 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1261 if (ebone->temp.i != 0) {
1262 int is_ignore_flag = ((ebone->temp.i << 16) & (BONESEL_ROOT | BONESEL_TIP));
1263 int is_inside_flag = (ebone->temp.i & (BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE));
1264
1265 /* Use as previous bone flag from now on. */
1266 ebone->temp.i = ebone->flag;
1267
1268 /* When there is a partial selection without both endpoints, only select an endpoint. */
1269 if ((is_inside_flag & BONESEL_BONE) &&
1270 ELEM(is_inside_flag & (BONESEL_ROOT | BONESEL_TIP), BONESEL_ROOT, BONESEL_TIP))
1271 {
1272 is_inside_flag &= ~BONESEL_BONE;
1273 }
1274
1276 arm, ebone, eSelectOp(sel_op), is_ignore_flag, is_inside_flag);
1277 }
1278 }
1279
1280 if (changed) {
1281 /* Cleanup flags. */
1282 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1283 if (ebone->flag & BONE_DONE) {
1284 std::swap(ebone->temp.i, ebone->flag);
1285 ebone->flag |= BONE_DONE;
1286 if ((ebone->flag & BONE_CONNECTED) && ebone->parent) {
1287 if ((ebone->parent->flag & BONE_DONE) == 0) {
1288 /* Checked below. */
1289 ebone->parent->temp.i = ebone->parent->flag;
1290 }
1291 }
1292 }
1293 }
1294
1295 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1296 if (ebone->flag & BONE_DONE) {
1297 if ((ebone->flag & BONE_CONNECTED) && ebone->parent) {
1298 bool is_parent_tip_changed = (ebone->parent->flag & BONE_TIPSEL) !=
1299 (ebone->parent->temp.i & BONE_TIPSEL);
1300 if ((ebone->temp.i & BONE_ROOTSEL) == 0) {
1301 if ((ebone->flag & BONE_ROOTSEL) != 0) {
1302 ebone->parent->flag |= BONE_TIPSEL;
1303 }
1304 }
1305 else {
1306 if ((ebone->flag & BONE_ROOTSEL) == 0) {
1307 ebone->parent->flag &= ~BONE_TIPSEL;
1308 }
1309 }
1310
1311 if (is_parent_tip_changed == false) {
1312 /* Keep tip selected if the parent remains selected. */
1313 if (ebone->parent->flag & BONE_SELECTED) {
1314 ebone->parent->flag |= BONE_TIPSEL;
1315 }
1316 }
1317 }
1318 ebone->flag &= ~BONE_DONE;
1319 }
1320 }
1321
1323 }
1324
1325 return changed;
1326}
1327
1329
1330/* -------------------------------------------------------------------- */
1333
1335{
1336 int action = RNA_enum_get(op->ptr, "action");
1337
1338 if (action == SEL_TOGGLE) {
1339 /* Determine if there are any selected bones
1340 * And therefore whether we are selecting or deselecting */
1341 action = SEL_SELECT;
1342 CTX_DATA_BEGIN (C, EditBone *, ebone, visible_bones) {
1343 if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
1344 action = SEL_DESELECT;
1345 break;
1346 }
1347 }
1349 }
1350
1351 /* Set the flags. */
1352 CTX_DATA_BEGIN (C, EditBone *, ebone, visible_bones) {
1353 /* ignore bone if selection can't change */
1354 switch (action) {
1355 case SEL_SELECT:
1356 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
1357 ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1358 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1359 ebone->parent->flag |= BONE_TIPSEL;
1360 }
1361 }
1362 break;
1363 case SEL_DESELECT:
1364 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1365 break;
1366 case SEL_INVERT:
1367 if (ebone->flag & BONE_SELECTED) {
1368 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1369 }
1370 else {
1371 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
1372 ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1373 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1374 ebone->parent->flag |= BONE_TIPSEL;
1375 }
1376 }
1377 }
1378 break;
1379 }
1380 }
1382
1384
1386
1387 /* Tagging only one object to refresh drawing. */
1388 Object *obedit = CTX_data_edit_object(C);
1390
1391 return OPERATOR_FINISHED;
1392}
1393
1395{
1396 /* identifiers */
1397 ot->name = "(De)select All";
1398 ot->idname = "ARMATURE_OT_select_all";
1399 ot->description = "Toggle selection status of all bones";
1400
1401 /* API callbacks. */
1404
1405 /* flags */
1406 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1407
1409}
1410
1412
1413/* -------------------------------------------------------------------- */
1416
1417static void armature_select_more(bArmature *arm, EditBone *ebone)
1418{
1419 if ((EBONE_PREV_FLAG_GET(ebone) & (BONE_ROOTSEL | BONE_TIPSEL)) != 0) {
1420 if (EBONE_SELECTABLE(arm, ebone)) {
1421 ED_armature_ebone_select_set(ebone, true);
1422 }
1423 }
1424
1425 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1426 /* to parent */
1427 if ((EBONE_PREV_FLAG_GET(ebone) & BONE_ROOTSEL) != 0) {
1428 if (EBONE_SELECTABLE(arm, ebone->parent)) {
1431 }
1432 }
1433
1434 /* from parent (difference from select less) */
1435 if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_TIPSEL) != 0) {
1436 if (EBONE_SELECTABLE(arm, ebone)) {
1438 }
1439 }
1440 }
1441}
1442
1443static void armature_select_less(bArmature * /*arm*/, EditBone *ebone)
1444{
1446 {
1447 ED_armature_ebone_select_set(ebone, false);
1448 }
1449
1450 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1451 /* to parent */
1452 if ((EBONE_PREV_FLAG_GET(ebone) & BONE_SELECTED) == 0) {
1454 }
1455
1456 /* from parent (difference from select more) */
1457 if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_SELECTED) == 0) {
1459 }
1460 }
1461}
1462
1463static void armature_select_more_less(Object *ob, bool more)
1464{
1465 bArmature *arm = static_cast<bArmature *>(ob->data);
1466
1467 /* XXX(@ideasman42): eventually we shouldn't need this. */
1469
1470 /* count bones & store selection state */
1471 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1473 }
1474
1475 /* do selection */
1476 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1478 if (more) {
1479 armature_select_more(arm, ebone);
1480 }
1481 else {
1482 armature_select_less(arm, ebone);
1483 }
1484 }
1485 }
1486
1487 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1489 if (more == false) {
1490 if (ebone->flag & BONE_SELECTED) {
1491 ED_armature_ebone_select_set(ebone, true);
1492 }
1493 }
1494 }
1495 ebone->temp.p = nullptr;
1496 }
1497
1499}
1500
1502
1503/* -------------------------------------------------------------------- */
1506
1522
1524{
1525 /* identifiers */
1526 ot->name = "Select More";
1527 ot->idname = "ARMATURE_OT_select_more";
1528 ot->description = "Select those bones connected to the initial selection";
1529
1530 /* API callbacks. */
1533
1534 /* flags */
1535 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1536}
1537
1539
1540/* -------------------------------------------------------------------- */
1543
1559
1561{
1562 /* identifiers */
1563 ot->name = "Select Less";
1564 ot->idname = "ARMATURE_OT_select_less";
1565 ot->description = "Deselect those bones at the boundary of each selection region";
1566
1567 /* API callbacks. */
1570
1571 /* flags */
1572 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1573}
1574
1576
1577/* -------------------------------------------------------------------- */
1580
1581enum {
1592};
1593
1595 {SIMEDBONE_CHILDREN, "CHILDREN", 0, "Children", ""},
1596 {SIMEDBONE_CHILDREN_IMMEDIATE, "CHILDREN_IMMEDIATE", 0, "Immediate Children", ""},
1597 {SIMEDBONE_SIBLINGS, "SIBLINGS", 0, "Siblings", ""},
1598 {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""},
1599 {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y Axis)", ""},
1600 {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""},
1601 {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""},
1602 {SIMEDBONE_COLLECTION, "BONE_COLLECTION", 0, "Bone Collection", ""},
1603 {SIMEDBONE_COLOR, "COLOR", 0, "Color", ""},
1604 {SIMEDBONE_SHAPE, "SHAPE", 0, "Shape", ""},
1605 {0, nullptr, 0, nullptr, nullptr},
1606};
1607
1609{
1610 float v1[3], v2[3];
1611 mul_v3_mat3_m4v3(v1, ob->object_to_world().ptr(), ebone->head);
1612 mul_v3_mat3_m4v3(v2, ob->object_to_world().ptr(), ebone->tail);
1613 return len_squared_v3v3(v1, v2);
1614}
1615
1616static void select_similar_length(bContext *C, const float thresh)
1617{
1618 const Scene *scene = CTX_data_scene(C);
1619 ViewLayer *view_layer = CTX_data_view_layer(C);
1620 Object *ob_act = CTX_data_edit_object(C);
1621 EditBone *ebone_act = CTX_data_active_bone(C);
1622
1623 /* Thresh is always relative to current length. */
1624 const float len = bone_length_squared_worldspace_get(ob_act, ebone_act);
1625 const float len_min = len / (1.0f + (thresh - FLT_EPSILON));
1626 const float len_max = len * (1.0f + (thresh + FLT_EPSILON));
1627
1629 scene, view_layer, CTX_wm_view3d(C));
1630 for (Object *ob : objects) {
1631 bArmature *arm = static_cast<bArmature *>(ob->data);
1632 bool changed = false;
1633
1634 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1635 if (EBONE_SELECTABLE(arm, ebone)) {
1636 const float len_iter = bone_length_squared_worldspace_get(ob, ebone);
1637 if ((len_iter > len_min) && (len_iter < len_max)) {
1638 ED_armature_ebone_select_set(ebone, true);
1639 changed = true;
1640 }
1641 }
1642 }
1643
1644 if (changed) {
1647 }
1648 }
1649}
1650
1651static void bone_direction_worldspace_get(Object *ob, EditBone *ebone, float *r_dir)
1652{
1653 float v1[3], v2[3];
1654 copy_v3_v3(v1, ebone->head);
1655 copy_v3_v3(v2, ebone->tail);
1656
1657 mul_m4_v3(ob->object_to_world().ptr(), v1);
1658 mul_m4_v3(ob->object_to_world().ptr(), v2);
1659
1660 sub_v3_v3v3(r_dir, v1, v2);
1661 normalize_v3(r_dir);
1662}
1663
1664static void select_similar_direction(bContext *C, const float thresh)
1665{
1666 const Scene *scene = CTX_data_scene(C);
1667 ViewLayer *view_layer = CTX_data_view_layer(C);
1668 Object *ob_act = CTX_data_edit_object(C);
1669 EditBone *ebone_act = CTX_data_active_bone(C);
1670
1671 float dir_act[3];
1672 bone_direction_worldspace_get(ob_act, ebone_act, dir_act);
1673
1675 scene, view_layer, CTX_wm_view3d(C));
1676 for (Object *ob : objects) {
1677 bArmature *arm = static_cast<bArmature *>(ob->data);
1678 bool changed = false;
1679
1680 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1681 if (EBONE_SELECTABLE(arm, ebone)) {
1682 float dir[3];
1683 bone_direction_worldspace_get(ob, ebone, dir);
1684
1685 if (angle_v3v3(dir_act, dir) / float(M_PI) < (thresh + FLT_EPSILON)) {
1686 ED_armature_ebone_select_set(ebone, true);
1687 changed = true;
1688 }
1689 }
1690 }
1691
1692 if (changed) {
1696 }
1697 }
1698}
1699
1701{
1702 const Scene *scene = CTX_data_scene(C);
1703 ViewLayer *view_layer = CTX_data_view_layer(C);
1704 EditBone *ebone_act = CTX_data_active_bone(C);
1705
1706 /* Build a set of bone collection names, to allow cross-Armature selection. */
1707 blender::Set<std::string> collection_names;
1708 LISTBASE_FOREACH (BoneCollectionReference *, bcoll_ref, &ebone_act->bone_collections) {
1709 collection_names.add(bcoll_ref->bcoll->name);
1710 }
1711
1713 scene, view_layer, CTX_wm_view3d(C));
1714 for (Object *ob : objects) {
1715 bArmature *arm = static_cast<bArmature *>(ob->data);
1716 bool changed = false;
1717
1718 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1719 if (!EBONE_SELECTABLE(arm, ebone)) {
1720 continue;
1721 }
1722
1723 LISTBASE_FOREACH (BoneCollectionReference *, bcoll_ref, &ebone->bone_collections) {
1724 if (!collection_names.contains(bcoll_ref->bcoll->name)) {
1725 continue;
1726 }
1727
1728 ED_armature_ebone_select_set(ebone, true);
1729 changed = true;
1730 break;
1731 }
1732 }
1733
1734 if (changed) {
1737 }
1738 }
1739}
1741{
1742 const Scene *scene = CTX_data_scene(C);
1743 ViewLayer *view_layer = CTX_data_view_layer(C);
1744 EditBone *ebone_act = CTX_data_active_bone(C);
1745
1746 const blender::animrig::BoneColor &active_bone_color = ebone_act->color.wrap();
1747
1749 scene, view_layer, CTX_wm_view3d(C));
1750 for (Object *ob : objects) {
1751 bArmature *arm = static_cast<bArmature *>(ob->data);
1752 bool changed = false;
1753
1754 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1755 if (!EBONE_SELECTABLE(arm, ebone)) {
1756 continue;
1757 }
1758
1759 const blender::animrig::BoneColor &bone_color = ebone->color.wrap();
1760 if (bone_color != active_bone_color) {
1761 continue;
1762 }
1763
1764 ED_armature_ebone_select_set(ebone, true);
1765 changed = true;
1766 }
1767
1768 if (changed) {
1771 }
1772 }
1773}
1774
1776{
1777 const Scene *scene = CTX_data_scene(C);
1778 ViewLayer *view_layer = CTX_data_view_layer(C);
1779 EditBone *ebone_act = CTX_data_active_bone(C);
1780
1781 char body_tmp[MAXBONENAME];
1782 char prefix_act[MAXBONENAME];
1783
1784 BLI_string_split_prefix(ebone_act->name, sizeof(ebone_act->name), prefix_act, body_tmp);
1785
1786 if (prefix_act[0] == '\0') {
1787 return;
1788 }
1789
1791 scene, view_layer, CTX_wm_view3d(C));
1792 for (Object *ob : objects) {
1793 bArmature *arm = static_cast<bArmature *>(ob->data);
1794 bool changed = false;
1795
1796 /* Find matches */
1797 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1798 if (EBONE_SELECTABLE(arm, ebone)) {
1799 char prefix_other[MAXBONENAME];
1800 BLI_string_split_prefix(ebone->name, sizeof(ebone->name), prefix_other, body_tmp);
1801 if (STREQ(prefix_act, prefix_other)) {
1802 ED_armature_ebone_select_set(ebone, true);
1803 changed = true;
1804 }
1805 }
1806 }
1807
1808 if (changed) {
1811 }
1812 }
1813}
1814
1816{
1817 const Scene *scene = CTX_data_scene(C);
1818 ViewLayer *view_layer = CTX_data_view_layer(C);
1819 EditBone *ebone_act = CTX_data_active_bone(C);
1820
1821 char body_tmp[MAXBONENAME];
1822 char suffix_act[MAXBONENAME];
1823
1824 BLI_string_split_suffix(ebone_act->name, sizeof(ebone_act->name), body_tmp, suffix_act);
1825
1826 if (suffix_act[0] == '\0') {
1827 return;
1828 }
1829
1831 scene, view_layer, CTX_wm_view3d(C));
1832 for (Object *ob : objects) {
1833 bArmature *arm = static_cast<bArmature *>(ob->data);
1834 bool changed = false;
1835
1836 /* Find matches */
1837 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1838 if (EBONE_SELECTABLE(arm, ebone)) {
1839 char suffix_other[MAXBONENAME];
1840 BLI_string_split_suffix(ebone->name, sizeof(ebone->name), body_tmp, suffix_other);
1841 if (STREQ(suffix_act, suffix_other)) {
1842 ED_armature_ebone_select_set(ebone, true);
1843 changed = true;
1844 }
1845 }
1846 }
1847
1848 if (changed) {
1851 }
1852 }
1853}
1854
1856static void select_similar_data_pchan(bContext *C, const size_t bytes_size, const int offset)
1857{
1858 Object *obedit = CTX_data_edit_object(C);
1859 bArmature *arm = static_cast<bArmature *>(obedit->data);
1860 EditBone *ebone_act = CTX_data_active_bone(C);
1861
1862 const bPoseChannel *pchan_active = BKE_pose_channel_find_name(obedit->pose, ebone_act->name);
1863
1864 /* This will mostly happen for corner cases where the user tried to access this
1865 * before having any valid pose data for the armature. */
1866 if (pchan_active == nullptr) {
1867 return;
1868 }
1869
1870 const char *data_active = (const char *)POINTER_OFFSET(pchan_active, offset);
1871 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1872 if (EBONE_SELECTABLE(arm, ebone)) {
1873 const bPoseChannel *pchan = BKE_pose_channel_find_name(obedit->pose, ebone->name);
1874 if (pchan) {
1875 const char *data_test = (const char *)POINTER_OFFSET(pchan, offset);
1876 if (memcmp(data_active, data_test, bytes_size) == 0) {
1877 ED_armature_ebone_select_set(ebone, true);
1878 }
1879 }
1880 }
1881 }
1882
1885}
1886
1887static void is_ancestor(EditBone *bone, EditBone *ancestor)
1888{
1889 if (ELEM(bone->temp.ebone, ancestor, nullptr)) {
1890 return;
1891 }
1892
1893 if (!ELEM(bone->temp.ebone->temp.ebone, nullptr, ancestor)) {
1894 is_ancestor(bone->temp.ebone, ancestor);
1895 }
1896
1897 bone->temp.ebone = bone->temp.ebone->temp.ebone;
1898}
1899
1901{
1902 Object *obedit = CTX_data_edit_object(C);
1903 bArmature *arm = static_cast<bArmature *>(obedit->data);
1904 EditBone *ebone_act = CTX_data_active_bone(C);
1905
1906 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1907 ebone_iter->temp.ebone = ebone_iter->parent;
1908 }
1909
1910 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1911 is_ancestor(ebone_iter, ebone_act);
1912
1913 if (ebone_iter->temp.ebone == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) {
1914 ED_armature_ebone_select_set(ebone_iter, true);
1915 }
1916 }
1917
1920}
1921
1923{
1924 Object *obedit = CTX_data_edit_object(C);
1925 bArmature *arm = static_cast<bArmature *>(obedit->data);
1926 EditBone *ebone_act = CTX_data_active_bone(C);
1927
1928 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1929 if (ebone_iter->parent == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) {
1930 ED_armature_ebone_select_set(ebone_iter, true);
1931 }
1932 }
1933
1936}
1937
1939{
1940 Object *obedit = CTX_data_edit_object(C);
1941 bArmature *arm = static_cast<bArmature *>(obedit->data);
1942 EditBone *ebone_act = CTX_data_active_bone(C);
1943
1944 if (ebone_act->parent == nullptr) {
1945 return;
1946 }
1947
1948 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1949 if (ebone_iter->parent == ebone_act->parent && EBONE_SELECTABLE(arm, ebone_iter)) {
1950 ED_armature_ebone_select_set(ebone_iter, true);
1951 }
1952 }
1953
1956}
1957
1959{
1960 /* Get props */
1961 int type = RNA_enum_get(op->ptr, "type");
1962 float thresh = RNA_float_get(op->ptr, "threshold");
1963
1964 /* Check for active bone */
1965 if (CTX_data_active_bone(C) == nullptr) {
1966 BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
1967 return OPERATOR_CANCELLED;
1968 }
1969
1970#define STRUCT_SIZE_AND_OFFSET(_struct, _member) \
1971 sizeof(_struct::_member), offsetof(_struct, _member)
1972
1973 switch (type) {
1974 case SIMEDBONE_CHILDREN:
1976 break;
1979 break;
1980 case SIMEDBONE_SIBLINGS:
1982 break;
1983 case SIMEDBONE_LENGTH:
1984 select_similar_length(C, thresh);
1985 break;
1987 select_similar_direction(C, thresh);
1988 break;
1989 case SIMEDBONE_PREFIX:
1991 break;
1992 case SIMEDBONE_SUFFIX:
1994 break;
1997 break;
1998 case SIMEDBONE_COLOR:
2000 break;
2001 case SIMEDBONE_SHAPE:
2003 break;
2004 }
2005
2006#undef STRUCT_SIZE_AND_OFFSET
2007
2009
2010 return OPERATOR_FINISHED;
2011}
2012
2014{
2015 /* identifiers */
2016 ot->name = "Select Similar";
2017 ot->idname = "ARMATURE_OT_select_similar";
2018
2019 /* callback functions */
2020 ot->invoke = WM_menu_invoke;
2023 ot->description = "Select similar bones by property types";
2024
2025 /* flags */
2026 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2027
2028 /* properties */
2029 ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMEDBONE_LENGTH, "Type", "");
2030 RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
2031}
2032
2034
2035/* -------------------------------------------------------------------- */
2038
2039/* No need to convert to multi-objects. Just like we keep the non-active bones
2040 * selected we then keep the non-active objects untouched (selected/unselected). */
2042{
2044 EditBone *ebone_active;
2045 int direction = RNA_enum_get(op->ptr, "direction");
2046 const bool add_to_sel = RNA_boolean_get(op->ptr, "extend");
2047 bool changed = false;
2048 bArmature *arm = static_cast<bArmature *>(ob->data);
2049
2050 ebone_active = arm->act_edbone;
2051 if (ebone_active == nullptr) {
2052 return OPERATOR_CANCELLED;
2053 }
2054
2055 if (direction == BONE_SELECT_PARENT) {
2056 if (ebone_active->parent) {
2057 EditBone *ebone_parent;
2058
2059 ebone_parent = ebone_active->parent;
2060
2061 if (EBONE_SELECTABLE(arm, ebone_parent)) {
2062 arm->act_edbone = ebone_parent;
2063
2064 if (!add_to_sel) {
2065 ED_armature_ebone_select_set(ebone_active, false);
2066 }
2067 ED_armature_ebone_select_set(ebone_parent, true);
2068
2069 changed = true;
2070 }
2071 }
2072 }
2073 else { /* BONE_SELECT_CHILD */
2074 EditBone *ebone_child = nullptr;
2075 int pass;
2076
2077 /* first pass, only connected bones (the logical direct child) */
2078 for (pass = 0; pass < 2 && (ebone_child == nullptr); pass++) {
2079 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
2080 /* possible we have multiple children, some invisible */
2081 if (EBONE_SELECTABLE(arm, ebone_iter)) {
2082 if (ebone_iter->parent == ebone_active) {
2083 if ((pass == 1) || (ebone_iter->flag & BONE_CONNECTED)) {
2084 ebone_child = ebone_iter;
2085 break;
2086 }
2087 }
2088 }
2089 }
2090 }
2091
2092 if (ebone_child) {
2093 arm->act_edbone = ebone_child;
2094
2095 if (!add_to_sel) {
2096 ED_armature_ebone_select_set(ebone_active, false);
2097 }
2098 ED_armature_ebone_select_set(ebone_child, true);
2099
2100 changed = true;
2101 }
2102 }
2103
2104 if (changed == false) {
2105 return OPERATOR_CANCELLED;
2106 }
2107
2109
2111
2114
2115 return OPERATOR_FINISHED;
2116}
2117
2119{
2120 static const EnumPropertyItem direction_items[] = {
2121 {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""},
2122 {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""},
2123 {0, nullptr, 0, nullptr, nullptr},
2124 };
2125
2126 /* identifiers */
2127 ot->name = "Select Hierarchy";
2128 ot->idname = "ARMATURE_OT_select_hierarchy";
2129 ot->description = "Select immediate parent/children of selected bones";
2130
2131 /* API callbacks. */
2134
2135 /* flags */
2136 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2137
2138 /* props */
2139 RNA_def_enum(ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", "");
2140 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
2141}
2142
2144
2145/* -------------------------------------------------------------------- */
2148
2153{
2154 const Scene *scene = CTX_data_scene(C);
2155 ViewLayer *view_layer = CTX_data_view_layer(C);
2156 const bool active_only = RNA_boolean_get(op->ptr, "only_active");
2157 const bool extend = RNA_boolean_get(op->ptr, "extend");
2158
2160 scene, view_layer, CTX_wm_view3d(C));
2161 for (Object *ob : objects) {
2162 bArmature *arm = static_cast<bArmature *>(ob->data);
2163
2164 EditBone *ebone_mirror_act = nullptr;
2165
2166 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
2167 const int flag = ED_armature_ebone_selectflag_get(ebone);
2168 EBONE_PREV_FLAG_SET(ebone, flag);
2169 }
2170
2171 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
2172 if (EBONE_SELECTABLE(arm, ebone)) {
2173 EditBone *ebone_mirror;
2174 int flag_new = extend ? EBONE_PREV_FLAG_GET(ebone) : 0;
2175
2176 if ((ebone_mirror = ED_armature_ebone_get_mirrored(arm->edbo, ebone)) &&
2178 {
2179 const int flag_mirror = EBONE_PREV_FLAG_GET(ebone_mirror);
2180 flag_new |= flag_mirror;
2181
2182 if (ebone == arm->act_edbone) {
2183 ebone_mirror_act = ebone_mirror;
2184 }
2185
2186 /* skip all but the active or its mirror */
2187 if (active_only && !ELEM(arm->act_edbone, ebone, ebone_mirror)) {
2188 continue;
2189 }
2190 }
2191
2192 ED_armature_ebone_selectflag_set(ebone, flag_new);
2193 }
2194 }
2195
2196 if (ebone_mirror_act) {
2197 arm->act_edbone = ebone_mirror_act;
2198 }
2199
2201
2203
2206 }
2207
2208 return OPERATOR_FINISHED;
2209}
2210
2212{
2213 /* identifiers */
2214 ot->name = "Select Mirror";
2215 ot->idname = "ARMATURE_OT_select_mirror";
2216 ot->description = "Mirror the bone selection";
2217
2218 /* API callbacks. */
2221
2222 /* flags */
2223 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2224
2225 /* properties */
2227 ot->srna, "only_active", false, "Active Only", "Only operate on the active bone");
2228 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
2229}
2230
2232
2233/* -------------------------------------------------------------------- */
2236
2238 bArmature *arm, EditBone *ebone_parent, EditBone *ebone_child, bool use_parent, bool is_test)
2239{
2240 do {
2241
2242 if (!use_parent && (ebone_child == ebone_parent)) {
2243 break;
2244 }
2245
2246 if (is_test) {
2247 if (!EBONE_SELECTABLE(arm, ebone_child)) {
2248 return false;
2249 }
2250 }
2251 else {
2253 }
2254
2255 if (ebone_child == ebone_parent) {
2256 break;
2257 }
2258
2259 ebone_child = ebone_child->parent;
2260 } while (true);
2261
2262 return true;
2263}
2264
2266 wmOperator *op,
2267 const wmEvent *event)
2268{
2269 Object *obedit = CTX_data_edit_object(C);
2270 bArmature *arm = static_cast<bArmature *>(obedit->data);
2271 EditBone *ebone_src, *ebone_dst;
2272 EditBone *ebone_isect_parent = nullptr;
2273 EditBone *ebone_isect_child[2];
2274 bool changed;
2275 Base *base_dst = nullptr;
2276
2279
2280 ebone_src = arm->act_edbone;
2281 ebone_dst = ED_armature_pick_ebone(C, event->mval, false, &base_dst);
2282
2283 /* fall back to object selection */
2284 if (ELEM(nullptr, ebone_src, ebone_dst) || (ebone_src == ebone_dst)) {
2285 return OPERATOR_PASS_THROUGH;
2286 }
2287
2288 if (base_dst && base_dst->object != obedit) {
2289 /* Disconnected, ignore. */
2290 return OPERATOR_CANCELLED;
2291 }
2292
2293 ebone_isect_child[0] = ebone_src;
2294 ebone_isect_child[1] = ebone_dst;
2295
2296 /* ensure 'ebone_src' is the parent of 'ebone_dst', or set 'ebone_isect_parent' */
2297 if (ED_armature_ebone_is_child_recursive(ebone_src, ebone_dst)) {
2298 /* pass */
2299 }
2300 else if (ED_armature_ebone_is_child_recursive(ebone_dst, ebone_src)) {
2301 std::swap(ebone_src, ebone_dst);
2302 }
2303 else if ((ebone_isect_parent = ED_armature_ebone_find_shared_parent(ebone_isect_child, 2))) {
2304 /* pass */
2305 }
2306 else {
2307 /* disconnected bones */
2308 return OPERATOR_CANCELLED;
2309 }
2310
2311 if (ebone_isect_parent) {
2312 if (armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, true) &&
2313 armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, true))
2314 {
2315 armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, false);
2316 armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, false);
2317 changed = true;
2318 }
2319 else {
2320 /* unselectable */
2321 changed = false;
2322 }
2323 }
2324 else {
2325 if (armature_shortest_path_select(arm, ebone_src, ebone_dst, true, true)) {
2326 armature_shortest_path_select(arm, ebone_src, ebone_dst, true, false);
2327 changed = true;
2328 }
2329 else {
2330 /* unselectable */
2331 changed = false;
2332 }
2333 }
2334
2335 if (changed) {
2336 arm->act_edbone = ebone_dst;
2341
2342 return OPERATOR_FINISHED;
2343 }
2344
2345 BKE_report(op->reports, RPT_WARNING, "Unselectable bone in chain");
2346 return OPERATOR_CANCELLED;
2347}
2348
2350{
2351 /* identifiers */
2352 ot->name = "Pick Shortest Path";
2353 ot->idname = "ARMATURE_OT_shortest_path_pick";
2354 ot->description = "Select shortest path between two bones";
2355
2356 /* API callbacks. */
2359
2360 /* flags */
2361 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2362}
2363
Functions to deal with Armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
C++ part of the BoneColor DNA struct.
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
EditBone * CTX_data_active_bone(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
void BKE_object_update_select_id(Main *bmain)
blender::Vector< Base * > BKE_object_pose_base_array_get(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
#define M_PI
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_mat3_m4v3(float r[3], const float mat[4][4], const float vec[3])
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
void BLI_rcti_init_pt_radius(struct rcti *rect, const int xy[2], int size)
Definition rct.cc:466
void void BLI_string_split_prefix(const char *string, size_t string_maxlen, char *r_pre, char *r_body) ATTR_NONNULL(1
void BLI_string_split_suffix(const char *string, size_t string_maxlen, char *r_body, char *r_suf) ATTR_NONNULL(1
unsigned int uint
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1009
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
#define MAXBONENAME
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_UNSELECTABLE
@ BONE_DONE
@ BONE_TIPSEL
@ BONE_CONNECTED
Object is a sort of wrapper for general info.
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
#define BONESEL_ANY
#define BONESEL_ROOT
#define BONE_SELECT_CHILD
#define BONESEL_TIP
#define BONE_SELECT_PARENT
#define BONESEL_BONE
#define EBONE_SELECTABLE(arm, ebone)
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
bool ED_operator_editarmature(bContext *C)
bool ED_operator_view3d_active(bContext *C)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
int ED_select_op_action_deselected(eSelectOp sel_op, bool is_select, bool is_inside)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
void view3d_gpu_select_cache_end()
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
#define XRAY_ACTIVE(v3d)
int view3d_gpu_select_with_id_filter(const ViewContext *vc, GPUSelectBuffer *buffer, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter, uint select_id)
void view3d_gpu_select_cache_begin()
@ VIEW3D_SELECT_PICK_ALL
Definition ED_view3d.hh:981
@ VIEW3D_SELECT_PICK_NEAREST
Definition ED_view3d.hh:983
void view3d_operator_needs_gpu(const bContext *C)
eV3DSelectObjectFilter
Definition ED_view3d.hh:986
@ VIEW3D_SELECT_FILTER_NOP
Definition ED_view3d.hh:988
blender::Vector< GPUSelectResult, 2500 > GPUSelectStorage
Definition GPU_select.hh:45
#define C
Definition RandGen.cpp:29
#define ND_DATA
Definition WM_types.hh:506
#define NA_EDITED
Definition WM_types.hh:581
#define NC_GPENCIL
Definition WM_types.hh:396
#define ND_BONE_SELECT
Definition WM_types.hh:457
#define NC_OBJECT
Definition WM_types.hh:376
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define CHECK_PARENT(ebone)
Bone * ED_armature_pick_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base)
void ARMATURE_OT_select_similar(wmOperatorType *ot)
void ARMATURE_OT_select_all(wmOperatorType *ot)
void ARMATURE_OT_select_hierarchy(wmOperatorType *ot)
bool ED_armature_edit_deselect_all_visible_multi_ex(const Span< Base * > bases)
EditBone * ED_armature_pick_ebone_from_selectbuffer(const Span< Base * > bases, const GPUSelectResult *hit_results, const int hits, bool findunsel, bool do_nearest, Base **r_base)
bool ED_armature_edit_deselect_all_visible(Object *obedit)
#define EBONE_PREV_FLAG_GET(ebone)
static bool armature_select_linked_pick_poll(bContext *C)
static wmOperatorStatus armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool armature_edit_select_op_apply(bArmature *arm, EditBone *ebone, const eSelectOp sel_op, int is_ignore_flag, int is_inside_flag)
static void select_similar_length(bContext *C, const float thresh)
Base * ED_armature_base_and_bone_from_select_buffer(const Span< Base * > bases, const uint select_id, Bone **r_bone)
Bone * ED_armature_pick_bone_from_selectbuffer(const Span< Base * > bases, const GPUSelectResult *hit_results, const int hits, bool findunsel, bool do_nearest, Base **r_base)
void ARMATURE_OT_select_mirror(wmOperatorType *ot)
@ SIMEDBONE_CHILDREN
@ SIMEDBONE_COLLECTION
@ SIMEDBONE_SUFFIX
@ SIMEDBONE_PREFIX
@ SIMEDBONE_DIRECTION
@ SIMEDBONE_LENGTH
@ SIMEDBONE_SIBLINGS
@ SIMEDBONE_SHAPE
@ SIMEDBONE_CHILDREN_IMMEDIATE
@ SIMEDBONE_COLOR
static wmOperatorStatus armature_select_similar_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_similar_types[]
static void select_similar_suffix(bContext *C)
bPoseChannel * ED_armature_pick_pchan_from_selectbuffer(const Span< Base * > bases, const GPUSelectResult *hit_results, const int hits, bool findunsel, bool do_nearest, Base **r_base)
Base * ED_armature_base_and_pchan_from_select_buffer(const Span< Base * > bases, const uint select_id, bPoseChannel **r_pchan)
static void bone_direction_worldspace_get(Object *ob, EditBone *ebone, float *r_dir)
static EditBone * get_nearest_editbonepoint(ViewContext *vc, bool findunsel, bool use_cycle, Base **r_base, int *r_selmask)
bool ED_armature_edit_deselect_all(Object *obedit)
EditBone * ED_armature_pick_ebone(bContext *C, const int xy[2], bool findunsel, Base **r_base)
static bool armature_select_linked_impl(Object *ob, const bool select, const bool all_forks)
void ARMATURE_OT_select_less(wmOperatorType *ot)
bPoseChannel * ED_armature_pick_pchan(bContext *C, const int xy[2], bool findunsel, Base **r_base)
Base * ED_armature_base_and_ebone_from_select_buffer(const Span< Base * > bases, const uint select_id, EditBone **r_ebone)
static void select_similar_prefix(bContext *C)
static void is_ancestor(EditBone *bone, EditBone *ancestor)
static int selectbuffer_ret_hits_5(blender::MutableSpan< GPUSelectResult > hit_results, const int hits12, const int hits5)
static wmOperatorStatus armature_select_hierarchy_exec(bContext *C, wmOperator *op)
static wmOperatorStatus armature_de_select_all_exec(bContext *C, wmOperator *op)
static void * ed_armature_pick_bone_impl(const bool is_editmode, bContext *C, const int xy[2], bool findunsel, Base **r_base)
static wmOperatorStatus armature_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void select_similar_siblings(bContext *C)
bool ED_armature_edit_deselect_all_visible_multi(bContext *C)
static void select_similar_children_immediate(bContext *C)
static wmOperatorStatus armature_select_linked_exec(bContext *C, wmOperator *op)
static int selectbuffer_ret_hits_12(blender::MutableSpan< GPUSelectResult >, const int hits12)
static void select_similar_children(bContext *C)
static float bone_length_squared_worldspace_get(Object *ob, EditBone *ebone)
Object * ED_armature_object_and_ebone_from_select_buffer(const Span< Object * > objects, const uint select_id, EditBone **r_ebone)
static void armature_select_more_less(Object *ob, bool more)
static void * ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode, const Span< Base * > bases, const Span< GPUSelectResult > hit_results, bool findunsel, bool do_nearest, Base **r_base)
#define EBONE_PREV_FLAG_SET(ebone, val)
#define STRUCT_SIZE_AND_OFFSET(_struct, _member)
static wmOperatorStatus armature_de_select_less_exec(bContext *C, wmOperator *)
static void select_similar_data_pchan(bContext *C, const size_t bytes_size, const int offset)
bool ED_armature_edit_deselect_all_multi_ex(const Span< Base * > bases)
bool ED_armature_edit_select_pick_bone(bContext *C, Base *basact, EditBone *ebone, const int selmask, const SelectPick_Params &params)
void ARMATURE_OT_select_linked(wmOperatorType *ot)
static bool armature_shortest_path_select(bArmature *arm, EditBone *ebone_parent, EditBone *ebone_child, bool use_parent, bool is_test)
static void armature_select_more(bArmature *arm, EditBone *ebone)
static void armature_select_less(bArmature *, EditBone *ebone)
bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op)
static void select_similar_direction(bContext *C, const float thresh)
void ARMATURE_OT_select_linked_pick(wmOperatorType *ot)
bool ED_armature_edit_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
static wmOperatorStatus armature_de_select_more_exec(bContext *C, wmOperator *)
static void select_similar_bone_color(bContext *C)
static wmOperatorStatus armature_select_mirror_exec(bContext *C, wmOperator *op)
void ARMATURE_OT_select_more(wmOperatorType *ot)
static void select_similar_bone_collection(bContext *C)
void ARMATURE_OT_shortest_path_pick(wmOperatorType *ot)
bool ED_armature_ebone_is_child_recursive(EditBone *ebone_parent, EditBone *ebone_child)
void ED_armature_ebone_selectflag_disable(EditBone *ebone, int flag)
void ED_armature_edit_sync_selection(ListBase *edbo)
int ED_armature_ebone_selectflag_get(const EditBone *ebone)
void ED_armature_ebone_selectflag_set(EditBone *ebone, int flag)
void ED_armature_ebone_select_set(EditBone *ebone, bool select)
EditBone * ED_armature_ebone_find_shared_parent(EditBone *ebone_child[], const uint ebone_child_tot)
void ED_armature_ebone_selectflag_enable(EditBone *ebone, int flag)
EditBone * ED_armature_ebone_get_mirrored(const ListBase *edbo, EditBone *ebo)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
BPy_StructRNA * depsgraph
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
MutableSpan< T > as_mutable_span()
Span< T > as_span() const
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:772
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bool bone_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
void base_activate(bContext *C, Base *base)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
struct Object * object
char name[64]
::BoneColor color
float tail[3]
EditBone * parent
ListBase bone_collections
union EditBone::@313026223344046157307162027212134045115206003172 temp
EditBone * ebone
float head[3]
GPUSelectStorage storage
Definition GPU_select.hh:47
unsigned int id
Definition GPU_select.hh:34
unsigned int depth
Definition GPU_select.hh:42
struct bPose * pose
ObjectRuntimeHandle * runtime
int mval[2]
Definition ED_view3d.hh:82
Scene * scene
Definition ED_view3d.hh:73
ViewLayer * view_layer
Definition ED_view3d.hh:74
View3D * v3d
Definition ED_view3d.hh:78
Object * obedit
Definition ED_view3d.hh:76
struct EditBone * act_edbone
ListBase * edbo
struct Bone * bone
ListBase chanbase
int mval[2]
Definition WM_types.hh:760
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
uint len
int xy[2]
Definition wm_draw.cc:174
bool WM_cursor_test_motion_and_update(const int mval[2])
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_operator_properties_select_all(wmOperatorType *ot)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:139