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