Blender V5.0
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->flag & POSE_SELECTED);
187 }
188 else {
189 sel = !(pchan->flag & POSE_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) {
508 if (blender::animrig::bone_is_visible(arm, ebone) &&
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 /* NOTE: this is endianness-sensitive.
750 * In Big Endian the order of these two variable would have to be inverted. */
751 uint16_t bone;
752 uint16_t ob;
753 };
754 } offset, test, best;
755 } cycle_order;
756
757 if (use_cycle) {
758 bArmature *arm = static_cast<bArmature *>(obedit_orig->data);
759 int ob_index = obedit_orig->runtime->select_id & 0xFFFF;
760 int bone_index = BLI_findindex(arm->edbo, ebone_active_orig);
761 /* Offset from the current active bone, so we cycle onto the next. */
762 cycle_order.offset.ob = ob_index;
763 cycle_order.offset.bone = bone_index;
764 /* The value of the active bone (with offset subtracted, a signal to always overwrite). */
765 cycle_order.best.as_u32 = 0;
766 }
767
768 int min_depth = INT_MAX;
769 for (int i = 0; i < hits; i++) {
770 const GPUSelectResult &hit_result = buffer.storage[i];
771 const uint select_id = hit_result.id;
772
773 Base *base = nullptr;
774 EditBone *ebone;
775 base = ED_armature_base_and_ebone_from_select_buffer(bases, select_id, &ebone);
776 /* If this fails, selection code is setting the selection ID's incorrectly. */
777 BLI_assert(base && ebone);
778
779 if (ebone->flag & BONE_UNSELECTABLE) {
780 continue;
781 }
782
783 /* Prioritized selection. */
784 {
785 int bias;
786 /* clicks on bone points get advantage */
787 if (select_id & (BONESEL_ROOT | BONESEL_TIP)) {
788 /* but also the unselected one */
789 if (findunsel) {
790 if ((select_id & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) {
791 bias = 4;
792 }
793 else if ((select_id & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) {
794 bias = 4;
795 }
796 else {
797 bias = 3;
798 }
799 }
800 else {
801 bias = 4;
802 }
803 }
804 else {
805 /* bone found */
806 if (findunsel) {
807 if ((ebone->flag & BONE_SELECTED) == 0) {
808 bias = 2;
809 }
810 else {
811 bias = 1;
812 }
813 }
814 else {
815 bias = 2;
816 }
817 }
818
819 if (bias > bias_max) {
820 bias_max = bias;
821
822 min_depth = hit_result.depth;
823 result_bias.select_id = select_id;
824 result_bias.base = base;
825 result_bias.ebone = ebone;
826 }
827 else if (bias == bias_max && do_nearest) {
828 if (min_depth > hit_result.depth) {
829 min_depth = hit_result.depth;
830 result_bias.select_id = select_id;
831 result_bias.base = base;
832 result_bias.ebone = ebone;
833 }
834 }
835 }
836
837 /* Cycle selected items (objects & bones). */
838 if (use_cycle) {
839 cycle_order.test.ob = select_id & 0xFFFF;
840 cycle_order.test.bone = (select_id & ~BONESEL_ANY) >> 16;
841 if (ebone == ebone_active_orig) {
842 BLI_assert(cycle_order.test.ob == cycle_order.offset.ob);
843 BLI_assert(cycle_order.test.bone == cycle_order.offset.bone);
844 }
845 /* Subtraction as a single value is needed to support cycling through bones
846 * from multiple objects. So once the last bone is selected,
847 * the bits for the bone index wrap into the object,
848 * causing the next object to be stepped onto. */
849 cycle_order.test.as_u32 -= cycle_order.offset.as_u32;
850
851 /* Even though this logic avoids stepping onto the active bone,
852 * always set the 'best' value for the first time.
853 * Otherwise ensure the value is the smallest it can be,
854 * relative to the active bone, as long as it's not the active bone. */
855 if ((cycle_order.best.as_u32 == 0) ||
856 (cycle_order.test.as_u32 && (cycle_order.test.as_u32 < cycle_order.best.as_u32)))
857 {
858 cycle_order.best = cycle_order.test;
859 result_cycle.select_id = select_id;
860 result_cycle.base = base;
861 result_cycle.ebone = ebone;
862 }
863 }
864 }
865 }
866
867 result = (use_cycle && result_cycle.ebone) ? &result_cycle : &result_bias;
868
869 if (result->select_id != -1) {
870 *r_base = result->base;
871
872 *r_selmask = 0;
873 if (result->select_id & BONESEL_ROOT) {
874 *r_selmask |= BONE_ROOTSEL;
875 }
876 if (result->select_id & BONESEL_TIP) {
877 *r_selmask |= BONE_TIPSEL;
878 }
879 if (result->select_id & BONESEL_BONE) {
880 *r_selmask |= BONE_SELECTED;
881 }
882 return result->ebone;
883 }
884 }
885 *r_selmask = 0;
886 *r_base = nullptr;
887 return nullptr;
888}
889
891
892/* -------------------------------------------------------------------- */
895
897{
898 bArmature *arm = static_cast<bArmature *>(obedit->data);
899 bool changed = false;
900 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
901 if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
902 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
903 changed = true;
904 }
905 }
906 return changed;
907}
908
910{
911 bArmature *arm = static_cast<bArmature *>(obedit->data);
912 bool changed = false;
913 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
914 /* first and foremost, bone must be visible and selected */
915 if (blender::animrig::bone_is_visible(arm, ebone)) {
916 if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
917 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
918 changed = true;
919 }
920 }
921 }
922
923 if (changed) {
925 }
926 return changed;
927}
928
930{
931 bool changed_multi = false;
932 for (Base *base : bases) {
933 Object *obedit = base->object;
934 changed_multi |= ED_armature_edit_deselect_all(obedit);
935 }
936 return changed_multi;
937}
938
940{
941 bool changed_multi = false;
942 for (Base *base : bases) {
943 Object *obedit = base->object;
944 changed_multi |= ED_armature_edit_deselect_all_visible(obedit);
945 }
946 return changed_multi;
947}
948
957
959
960/* -------------------------------------------------------------------- */
963
965 bContext *C, Base *basact, EditBone *ebone, const int selmask, const SelectPick_Params &params)
966{
967 const Scene *scene = CTX_data_scene(C);
968 ViewLayer *view_layer = CTX_data_view_layer(C);
969 View3D *v3d = CTX_wm_view3d(C);
970 bool changed = false;
971 bool found = false;
972
973 if (ebone) {
974 bArmature *arm = static_cast<bArmature *>(basact->object->data);
975 if (EBONE_SELECTABLE(arm, ebone)) {
976 found = true;
977 }
978 }
979
980 if (params.sel_op == SEL_OP_SET) {
981 if ((found && params.select_passthrough) &&
982 (ED_armature_ebone_selectflag_get(ebone) & selmask))
983 {
984 found = false;
985 }
986 else if (found || params.deselect_all) {
987 /* Deselect everything. */
989 scene, view_layer, v3d);
991 changed = true;
992 }
993 }
994
995 if (found) {
997 bArmature *arm = static_cast<bArmature *>(basact->object->data);
998
999 /* By definition the non-root connected bones have no root point drawn,
1000 * so a root selection needs to be delivered to the parent tip. */
1001
1002 if (selmask & BONE_SELECTED) {
1003 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1004
1005 /* Bone is in a chain. */
1006 switch (params.sel_op) {
1007 case SEL_OP_ADD: {
1008 /* Select this bone. */
1009 ebone->flag |= BONE_TIPSEL;
1010 ebone->parent->flag |= BONE_TIPSEL;
1011 break;
1012 }
1013 case SEL_OP_SUB: {
1014 /* Deselect this bone. */
1015 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
1016 /* Only deselect parent tip if it is not selected. */
1017 if (!(ebone->parent->flag & BONE_SELECTED)) {
1018 ebone->parent->flag &= ~BONE_TIPSEL;
1019 }
1020 break;
1021 }
1022 case SEL_OP_XOR: {
1023 /* Toggle inverts this bone's selection. */
1024 if (ebone->flag & BONE_SELECTED) {
1025 /* Deselect this bone. */
1026 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
1027 /* Only deselect parent tip if it is not selected. */
1028 if (!(ebone->parent->flag & BONE_SELECTED)) {
1029 ebone->parent->flag &= ~BONE_TIPSEL;
1030 }
1031 }
1032 else {
1033 /* Select this bone. */
1034 ebone->flag |= BONE_TIPSEL;
1035 ebone->parent->flag |= BONE_TIPSEL;
1036 }
1037 break;
1038 }
1039 case SEL_OP_SET: {
1040 /* Select this bone. */
1041 ebone->flag |= BONE_TIPSEL;
1042 ebone->parent->flag |= BONE_TIPSEL;
1043 break;
1044 }
1045 case SEL_OP_AND: {
1046 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
1047 break;
1048 }
1049 }
1050 }
1051 else {
1052 switch (params.sel_op) {
1053 case SEL_OP_ADD: {
1054 ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
1055 break;
1056 }
1057 case SEL_OP_SUB: {
1058 ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
1059 break;
1060 }
1061 case SEL_OP_XOR: {
1062 /* Toggle inverts this bone's selection. */
1063 if (ebone->flag & BONE_SELECTED) {
1064 ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
1065 }
1066 else {
1067 ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
1068 }
1069 break;
1070 }
1071 case SEL_OP_SET: {
1072 ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
1073 break;
1074 }
1075 case SEL_OP_AND: {
1076 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
1077 break;
1078 }
1079 }
1080 }
1081 }
1082 else {
1083 switch (params.sel_op) {
1084 case SEL_OP_ADD: {
1085 ebone->flag |= selmask;
1086 break;
1087 }
1088 case SEL_OP_SUB: {
1089 ebone->flag &= ~selmask;
1090 break;
1091 }
1092 case SEL_OP_XOR: {
1093 if (ebone->flag & selmask) {
1094 ebone->flag &= ~selmask;
1095 }
1096 else {
1097 ebone->flag |= selmask;
1098 }
1099 break;
1100 }
1101 case SEL_OP_SET: {
1102 ebone->flag |= selmask;
1103 break;
1104 }
1105 case SEL_OP_AND: {
1106 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
1107 break;
1108 }
1109 }
1110 }
1111
1113
1114 /* Now check for active status. */
1116 arm->act_edbone = ebone;
1117 }
1118
1119 BKE_view_layer_synced_ensure(scene, view_layer);
1120 if (BKE_view_layer_active_base_get(view_layer) != basact) {
1122 }
1123
1126 changed = true;
1127 }
1128
1129 if (changed) {
1131 }
1132
1133 return changed || found;
1134}
1135
1137
1138{
1140 EditBone *nearBone = nullptr;
1141 int selmask;
1142 Base *basact = nullptr;
1143
1145 vc.mval[0] = mval[0];
1146 vc.mval[1] = mval[1];
1147
1148 nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
1149 return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, params);
1150}
1151
1153
1154/* -------------------------------------------------------------------- */
1159
1161 EditBone *ebone,
1162 const eSelectOp sel_op,
1163 int is_ignore_flag,
1164 int is_inside_flag)
1165{
1166 BLI_assert(!(is_ignore_flag & ~(BONESEL_ROOT | BONESEL_TIP)));
1167 BLI_assert(!(is_inside_flag & ~(BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE)));
1169 bool changed = false;
1170 bool is_point_done = false;
1171 int points_proj_tot = 0;
1172 BLI_assert(ebone->flag == ebone->temp.i);
1173 const int ebone_flag_prev = ebone->flag;
1174
1175 if ((is_ignore_flag & BONE_ROOTSEL) == 0) {
1176 points_proj_tot++;
1177 const bool is_select = ebone->flag & BONE_ROOTSEL;
1178 const bool is_inside = is_inside_flag & BONESEL_ROOT;
1179 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1180 if (sel_op_result != -1) {
1181 if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) {
1182 SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_ROOTSEL);
1183 }
1184 }
1185 is_point_done |= is_inside;
1186 }
1187
1188 if ((is_ignore_flag & BONE_TIPSEL) == 0) {
1189 points_proj_tot++;
1190 const bool is_select = ebone->flag & BONE_TIPSEL;
1191 const bool is_inside = is_inside_flag & BONESEL_TIP;
1192 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1193 if (sel_op_result != -1) {
1194 if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) {
1195 SET_FLAG_FROM_TEST(ebone->flag, sel_op_result, BONE_TIPSEL);
1196 }
1197 }
1198 is_point_done |= is_inside;
1199 }
1200
1201 /* if one of points selected, we skip the bone itself */
1202 if ((is_point_done == false) && (points_proj_tot == 2)) {
1203 const bool is_select = ebone->flag & BONE_SELECTED;
1204 {
1205 const bool is_inside = is_inside_flag & BONESEL_BONE;
1206 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1207 if (sel_op_result != -1) {
1208 if (sel_op_result == 0 || EBONE_SELECTABLE(arm, ebone)) {
1210 ebone->flag, sel_op_result, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
1211 }
1212 }
1213 }
1214
1215 changed = true;
1216 }
1217 changed |= is_point_done;
1218
1219 if (ebone_flag_prev != ebone->flag) {
1220 ebone->temp.i = ebone->flag;
1221 ebone->flag = ebone_flag_prev;
1222 ebone->flag = ebone_flag_prev | BONE_DONE;
1223 changed = true;
1224 }
1225
1226 return changed;
1227}
1228
1230{
1231 bool changed = false;
1232
1233 /* Initialize flags. */
1234 {
1235 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1236
1237 /* Flush the parent flag to this bone
1238 * so we don't need to check the parent when adjusting the selection. */
1239 if ((ebone->flag & BONE_CONNECTED) && ebone->parent) {
1240 if (ebone->parent->flag & BONE_TIPSEL) {
1241 ebone->flag |= BONE_ROOTSEL;
1242 }
1243 else {
1244 ebone->flag &= ~BONE_ROOTSEL;
1245 }
1246
1247 /* Flush the 'temp.i' flag. */
1248 if (ebone->parent->temp.i & BONESEL_TIP) {
1249 ebone->temp.i |= BONESEL_ROOT;
1250 }
1251 }
1252 ebone->flag &= ~BONE_DONE;
1253 }
1254 }
1255
1256 /* Apply selection from bone selection flags. */
1257 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1258 if (ebone->temp.i != 0) {
1259 int is_ignore_flag = ((ebone->temp.i << 16) & (BONESEL_ROOT | BONESEL_TIP));
1260 int is_inside_flag = (ebone->temp.i & (BONESEL_ROOT | BONESEL_TIP | BONESEL_BONE));
1261
1262 /* Use as previous bone flag from now on. */
1263 ebone->temp.i = ebone->flag;
1264
1265 /* When there is a partial selection without both endpoints, only select an endpoint. */
1266 if ((is_inside_flag & BONESEL_BONE) &&
1267 ELEM(is_inside_flag & (BONESEL_ROOT | BONESEL_TIP), BONESEL_ROOT, BONESEL_TIP))
1268 {
1269 is_inside_flag &= ~BONESEL_BONE;
1270 }
1271
1273 arm, ebone, eSelectOp(sel_op), is_ignore_flag, is_inside_flag);
1274 }
1275 }
1276
1277 if (changed) {
1278 /* Cleanup flags. */
1279 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1280 if (ebone->flag & BONE_DONE) {
1281 std::swap(ebone->temp.i, ebone->flag);
1282 ebone->flag |= BONE_DONE;
1283 if ((ebone->flag & BONE_CONNECTED) && ebone->parent) {
1284 if ((ebone->parent->flag & BONE_DONE) == 0) {
1285 /* Checked below. */
1286 ebone->parent->temp.i = ebone->parent->flag;
1287 }
1288 }
1289 }
1290 }
1291
1292 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1293 if (ebone->flag & BONE_DONE) {
1294 if ((ebone->flag & BONE_CONNECTED) && ebone->parent) {
1295 bool is_parent_tip_changed = (ebone->parent->flag & BONE_TIPSEL) !=
1296 (ebone->parent->temp.i & BONE_TIPSEL);
1297 if ((ebone->temp.i & BONE_ROOTSEL) == 0) {
1298 if ((ebone->flag & BONE_ROOTSEL) != 0) {
1299 ebone->parent->flag |= BONE_TIPSEL;
1300 }
1301 }
1302 else {
1303 if ((ebone->flag & BONE_ROOTSEL) == 0) {
1304 ebone->parent->flag &= ~BONE_TIPSEL;
1305 }
1306 }
1307
1308 if (is_parent_tip_changed == false) {
1309 /* Keep tip selected if the parent remains selected. */
1310 if (ebone->parent->flag & BONE_SELECTED) {
1311 ebone->parent->flag |= BONE_TIPSEL;
1312 }
1313 }
1314 }
1315 ebone->flag &= ~BONE_DONE;
1316 }
1317 }
1318
1320 }
1321
1322 return changed;
1323}
1324
1326
1327/* -------------------------------------------------------------------- */
1330
1332{
1333 int action = RNA_enum_get(op->ptr, "action");
1334
1335 if (action == SEL_TOGGLE) {
1336 /* Determine if there are any selected bones
1337 * And therefore whether we are selecting or deselecting */
1338 action = SEL_SELECT;
1339 CTX_DATA_BEGIN (C, EditBone *, ebone, visible_bones) {
1340 if (ebone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) {
1341 action = SEL_DESELECT;
1342 break;
1343 }
1344 }
1346 }
1347
1348 /* Set the flags. */
1349 CTX_DATA_BEGIN (C, EditBone *, ebone, visible_bones) {
1350 /* ignore bone if selection can't change */
1351 switch (action) {
1352 case SEL_SELECT:
1353 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
1354 ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1355 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1356 ebone->parent->flag |= BONE_TIPSEL;
1357 }
1358 }
1359 break;
1360 case SEL_DESELECT:
1361 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1362 break;
1363 case SEL_INVERT:
1364 if (ebone->flag & BONE_SELECTED) {
1365 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1366 }
1367 else {
1368 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
1369 ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1370 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1371 ebone->parent->flag |= BONE_TIPSEL;
1372 }
1373 }
1374 }
1375 break;
1376 }
1377 }
1379
1381
1383
1384 /* Tagging only one object to refresh drawing. */
1385 Object *obedit = CTX_data_edit_object(C);
1387
1388 return OPERATOR_FINISHED;
1389}
1390
1392{
1393 /* identifiers */
1394 ot->name = "(De)select All";
1395 ot->idname = "ARMATURE_OT_select_all";
1396 ot->description = "Toggle selection status of all bones";
1397
1398 /* API callbacks. */
1401
1402 /* flags */
1403 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1404
1406}
1407
1409
1410/* -------------------------------------------------------------------- */
1413
1414static void armature_select_more(bArmature *arm, EditBone *ebone)
1415{
1416 if ((EBONE_PREV_FLAG_GET(ebone) & (BONE_ROOTSEL | BONE_TIPSEL)) != 0) {
1417 if (EBONE_SELECTABLE(arm, ebone)) {
1418 ED_armature_ebone_select_set(ebone, true);
1419 }
1420 }
1421
1422 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1423 /* to parent */
1424 if ((EBONE_PREV_FLAG_GET(ebone) & BONE_ROOTSEL) != 0) {
1425 if (EBONE_SELECTABLE(arm, ebone->parent)) {
1428 }
1429 }
1430
1431 /* from parent (difference from select less) */
1432 if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_TIPSEL) != 0) {
1433 if (EBONE_SELECTABLE(arm, ebone)) {
1435 }
1436 }
1437 }
1438}
1439
1440static void armature_select_less(bArmature * /*arm*/, EditBone *ebone)
1441{
1443 {
1444 ED_armature_ebone_select_set(ebone, false);
1445 }
1446
1447 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1448 /* to parent */
1449 if ((EBONE_PREV_FLAG_GET(ebone) & BONE_SELECTED) == 0) {
1451 }
1452
1453 /* from parent (difference from select more) */
1454 if ((EBONE_PREV_FLAG_GET(ebone->parent) & BONE_SELECTED) == 0) {
1456 }
1457 }
1458}
1459
1460static void armature_select_more_less(Object *ob, bool more)
1461{
1462 bArmature *arm = static_cast<bArmature *>(ob->data);
1463
1464 /* XXX(@ideasman42): eventually we shouldn't need this. */
1466
1467 /* count bones & store selection state */
1468 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1470 }
1471
1472 /* do selection */
1473 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1474 if (blender::animrig::bone_is_visible(arm, ebone)) {
1475 if (more) {
1476 armature_select_more(arm, ebone);
1477 }
1478 else {
1479 armature_select_less(arm, ebone);
1480 }
1481 }
1482 }
1483
1484 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1485 if (blender::animrig::bone_is_visible(arm, ebone)) {
1486 if (more == false) {
1487 if (ebone->flag & BONE_SELECTED) {
1488 ED_armature_ebone_select_set(ebone, true);
1489 }
1490 }
1491 }
1492 ebone->temp.p = nullptr;
1493 }
1494
1496}
1497
1499
1500/* -------------------------------------------------------------------- */
1503
1519
1521{
1522 /* identifiers */
1523 ot->name = "Select More";
1524 ot->idname = "ARMATURE_OT_select_more";
1525 ot->description = "Select those bones connected to the initial selection";
1526
1527 /* API callbacks. */
1530
1531 /* flags */
1532 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1533}
1534
1536
1537/* -------------------------------------------------------------------- */
1540
1556
1558{
1559 /* identifiers */
1560 ot->name = "Select Less";
1561 ot->idname = "ARMATURE_OT_select_less";
1562 ot->description = "Deselect those bones at the boundary of each selection region";
1563
1564 /* API callbacks. */
1567
1568 /* flags */
1569 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1570}
1571
1573
1574/* -------------------------------------------------------------------- */
1577
1578enum {
1589};
1590
1592 {SIMEDBONE_CHILDREN, "CHILDREN", 0, "Children", ""},
1593 {SIMEDBONE_CHILDREN_IMMEDIATE, "CHILDREN_IMMEDIATE", 0, "Immediate Children", ""},
1594 {SIMEDBONE_SIBLINGS, "SIBLINGS", 0, "Siblings", ""},
1595 {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""},
1596 {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y Axis)", ""},
1597 {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""},
1598 {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""},
1599 {SIMEDBONE_COLLECTION, "BONE_COLLECTION", 0, "Bone Collection", ""},
1600 {SIMEDBONE_COLOR, "COLOR", 0, "Color", ""},
1601 {SIMEDBONE_SHAPE, "SHAPE", 0, "Shape", ""},
1602 {0, nullptr, 0, nullptr, nullptr},
1603};
1604
1606{
1607 float v1[3], v2[3];
1608 mul_v3_mat3_m4v3(v1, ob->object_to_world().ptr(), ebone->head);
1609 mul_v3_mat3_m4v3(v2, ob->object_to_world().ptr(), ebone->tail);
1610 return len_squared_v3v3(v1, v2);
1611}
1612
1613static void select_similar_length(bContext *C, const float thresh)
1614{
1615 const Scene *scene = CTX_data_scene(C);
1616 ViewLayer *view_layer = CTX_data_view_layer(C);
1617 Object *ob_act = CTX_data_edit_object(C);
1618 EditBone *ebone_act = CTX_data_active_bone(C);
1619
1620 /* Thresh is always relative to current length. */
1621 const float len = bone_length_squared_worldspace_get(ob_act, ebone_act);
1622 const float len_min = len / (1.0f + (thresh - FLT_EPSILON));
1623 const float len_max = len * (1.0f + (thresh + FLT_EPSILON));
1624
1626 scene, view_layer, CTX_wm_view3d(C));
1627 for (Object *ob : objects) {
1628 bArmature *arm = static_cast<bArmature *>(ob->data);
1629 bool changed = false;
1630
1631 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1632 if (EBONE_SELECTABLE(arm, ebone)) {
1633 const float len_iter = bone_length_squared_worldspace_get(ob, ebone);
1634 if ((len_iter > len_min) && (len_iter < len_max)) {
1635 ED_armature_ebone_select_set(ebone, true);
1636 changed = true;
1637 }
1638 }
1639 }
1640
1641 if (changed) {
1644 }
1645 }
1646}
1647
1648static void bone_direction_worldspace_get(Object *ob, EditBone *ebone, float *r_dir)
1649{
1650 float v1[3], v2[3];
1651 copy_v3_v3(v1, ebone->head);
1652 copy_v3_v3(v2, ebone->tail);
1653
1654 mul_m4_v3(ob->object_to_world().ptr(), v1);
1655 mul_m4_v3(ob->object_to_world().ptr(), v2);
1656
1657 sub_v3_v3v3(r_dir, v1, v2);
1658 normalize_v3(r_dir);
1659}
1660
1661static void select_similar_direction(bContext *C, const float thresh)
1662{
1663 const Scene *scene = CTX_data_scene(C);
1664 ViewLayer *view_layer = CTX_data_view_layer(C);
1665 Object *ob_act = CTX_data_edit_object(C);
1666 EditBone *ebone_act = CTX_data_active_bone(C);
1667
1668 float dir_act[3];
1669 bone_direction_worldspace_get(ob_act, ebone_act, dir_act);
1670
1672 scene, view_layer, CTX_wm_view3d(C));
1673 for (Object *ob : objects) {
1674 bArmature *arm = static_cast<bArmature *>(ob->data);
1675 bool changed = false;
1676
1677 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1678 if (EBONE_SELECTABLE(arm, ebone)) {
1679 float dir[3];
1680 bone_direction_worldspace_get(ob, ebone, dir);
1681
1682 if (angle_v3v3(dir_act, dir) / float(M_PI) < (thresh + FLT_EPSILON)) {
1683 ED_armature_ebone_select_set(ebone, true);
1684 changed = true;
1685 }
1686 }
1687 }
1688
1689 if (changed) {
1693 }
1694 }
1695}
1696
1698{
1699 const Scene *scene = CTX_data_scene(C);
1700 ViewLayer *view_layer = CTX_data_view_layer(C);
1701 EditBone *ebone_act = CTX_data_active_bone(C);
1702
1703 /* Build a set of bone collection names, to allow cross-Armature selection. */
1704 blender::Set<std::string> collection_names;
1705 LISTBASE_FOREACH (BoneCollectionReference *, bcoll_ref, &ebone_act->bone_collections) {
1706 collection_names.add(bcoll_ref->bcoll->name);
1707 }
1708
1710 scene, view_layer, CTX_wm_view3d(C));
1711 for (Object *ob : objects) {
1712 bArmature *arm = static_cast<bArmature *>(ob->data);
1713 bool changed = false;
1714
1715 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1716 if (!EBONE_SELECTABLE(arm, ebone)) {
1717 continue;
1718 }
1719
1720 LISTBASE_FOREACH (BoneCollectionReference *, bcoll_ref, &ebone->bone_collections) {
1721 if (!collection_names.contains(bcoll_ref->bcoll->name)) {
1722 continue;
1723 }
1724
1725 ED_armature_ebone_select_set(ebone, true);
1726 changed = true;
1727 break;
1728 }
1729 }
1730
1731 if (changed) {
1734 }
1735 }
1736}
1738{
1739 const Scene *scene = CTX_data_scene(C);
1740 ViewLayer *view_layer = CTX_data_view_layer(C);
1741 EditBone *ebone_act = CTX_data_active_bone(C);
1742
1743 const blender::animrig::BoneColor &active_bone_color = ebone_act->color.wrap();
1744
1746 scene, view_layer, CTX_wm_view3d(C));
1747 for (Object *ob : objects) {
1748 bArmature *arm = static_cast<bArmature *>(ob->data);
1749 bool changed = false;
1750
1751 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1752 if (!EBONE_SELECTABLE(arm, ebone)) {
1753 continue;
1754 }
1755
1756 const blender::animrig::BoneColor &bone_color = ebone->color.wrap();
1757 if (bone_color != active_bone_color) {
1758 continue;
1759 }
1760
1761 ED_armature_ebone_select_set(ebone, true);
1762 changed = true;
1763 }
1764
1765 if (changed) {
1768 }
1769 }
1770}
1771
1773{
1774 const Scene *scene = CTX_data_scene(C);
1775 ViewLayer *view_layer = CTX_data_view_layer(C);
1776 EditBone *ebone_act = CTX_data_active_bone(C);
1777
1778 char body_tmp[MAXBONENAME];
1779 char prefix_act[MAXBONENAME];
1780
1781 BLI_string_split_prefix(ebone_act->name, sizeof(ebone_act->name), prefix_act, body_tmp);
1782
1783 if (prefix_act[0] == '\0') {
1784 return;
1785 }
1786
1788 scene, view_layer, CTX_wm_view3d(C));
1789 for (Object *ob : objects) {
1790 bArmature *arm = static_cast<bArmature *>(ob->data);
1791 bool changed = false;
1792
1793 /* Find matches */
1794 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1795 if (EBONE_SELECTABLE(arm, ebone)) {
1796 char prefix_other[MAXBONENAME];
1797 BLI_string_split_prefix(ebone->name, sizeof(ebone->name), prefix_other, body_tmp);
1798 if (STREQ(prefix_act, prefix_other)) {
1799 ED_armature_ebone_select_set(ebone, true);
1800 changed = true;
1801 }
1802 }
1803 }
1804
1805 if (changed) {
1808 }
1809 }
1810}
1811
1813{
1814 const Scene *scene = CTX_data_scene(C);
1815 ViewLayer *view_layer = CTX_data_view_layer(C);
1816 EditBone *ebone_act = CTX_data_active_bone(C);
1817
1818 char body_tmp[MAXBONENAME];
1819 char suffix_act[MAXBONENAME];
1820
1821 BLI_string_split_suffix(ebone_act->name, sizeof(ebone_act->name), body_tmp, suffix_act);
1822
1823 if (suffix_act[0] == '\0') {
1824 return;
1825 }
1826
1828 scene, view_layer, CTX_wm_view3d(C));
1829 for (Object *ob : objects) {
1830 bArmature *arm = static_cast<bArmature *>(ob->data);
1831 bool changed = false;
1832
1833 /* Find matches */
1834 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1835 if (EBONE_SELECTABLE(arm, ebone)) {
1836 char suffix_other[MAXBONENAME];
1837 BLI_string_split_suffix(ebone->name, sizeof(ebone->name), body_tmp, suffix_other);
1838 if (STREQ(suffix_act, suffix_other)) {
1839 ED_armature_ebone_select_set(ebone, true);
1840 changed = true;
1841 }
1842 }
1843 }
1844
1845 if (changed) {
1848 }
1849 }
1850}
1851
1853static void select_similar_data_pchan(bContext *C, const size_t bytes_size, const int offset)
1854{
1855 Object *obedit = CTX_data_edit_object(C);
1856 bArmature *arm = static_cast<bArmature *>(obedit->data);
1857 EditBone *ebone_act = CTX_data_active_bone(C);
1858
1859 const bPoseChannel *pchan_active = BKE_pose_channel_find_name(obedit->pose, ebone_act->name);
1860
1861 /* This will mostly happen for corner cases where the user tried to access this
1862 * before having any valid pose data for the armature. */
1863 if (pchan_active == nullptr) {
1864 return;
1865 }
1866
1867 const char *data_active = (const char *)POINTER_OFFSET(pchan_active, offset);
1868 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1869 if (EBONE_SELECTABLE(arm, ebone)) {
1870 const bPoseChannel *pchan = BKE_pose_channel_find_name(obedit->pose, ebone->name);
1871 if (pchan) {
1872 const char *data_test = (const char *)POINTER_OFFSET(pchan, offset);
1873 if (memcmp(data_active, data_test, bytes_size) == 0) {
1874 ED_armature_ebone_select_set(ebone, true);
1875 }
1876 }
1877 }
1878 }
1879
1882}
1883
1884static void is_ancestor(EditBone *bone, EditBone *ancestor)
1885{
1886 if (ELEM(bone->temp.ebone, ancestor, nullptr)) {
1887 return;
1888 }
1889
1890 if (!ELEM(bone->temp.ebone->temp.ebone, nullptr, ancestor)) {
1891 is_ancestor(bone->temp.ebone, ancestor);
1892 }
1893
1894 bone->temp.ebone = bone->temp.ebone->temp.ebone;
1895}
1896
1898{
1899 Object *obedit = CTX_data_edit_object(C);
1900 bArmature *arm = static_cast<bArmature *>(obedit->data);
1901 EditBone *ebone_act = CTX_data_active_bone(C);
1902
1903 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1904 ebone_iter->temp.ebone = ebone_iter->parent;
1905 }
1906
1907 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1908 is_ancestor(ebone_iter, ebone_act);
1909
1910 if (ebone_iter->temp.ebone == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) {
1911 ED_armature_ebone_select_set(ebone_iter, true);
1912 }
1913 }
1914
1917}
1918
1920{
1921 Object *obedit = CTX_data_edit_object(C);
1922 bArmature *arm = static_cast<bArmature *>(obedit->data);
1923 EditBone *ebone_act = CTX_data_active_bone(C);
1924
1925 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1926 if (ebone_iter->parent == ebone_act && EBONE_SELECTABLE(arm, ebone_iter)) {
1927 ED_armature_ebone_select_set(ebone_iter, true);
1928 }
1929 }
1930
1933}
1934
1936{
1937 Object *obedit = CTX_data_edit_object(C);
1938 bArmature *arm = static_cast<bArmature *>(obedit->data);
1939 EditBone *ebone_act = CTX_data_active_bone(C);
1940
1941 if (ebone_act->parent == nullptr) {
1942 return;
1943 }
1944
1945 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1946 if (ebone_iter->parent == ebone_act->parent && EBONE_SELECTABLE(arm, ebone_iter)) {
1947 ED_armature_ebone_select_set(ebone_iter, true);
1948 }
1949 }
1950
1953}
1954
1956{
1957 /* Get props */
1958 int type = RNA_enum_get(op->ptr, "type");
1959 float thresh = RNA_float_get(op->ptr, "threshold");
1960
1961 /* Check for active bone */
1962 if (CTX_data_active_bone(C) == nullptr) {
1963 BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
1964 return OPERATOR_CANCELLED;
1965 }
1966
1967#define STRUCT_SIZE_AND_OFFSET(_struct, _member) \
1968 sizeof(_struct::_member), offsetof(_struct, _member)
1969
1970 switch (type) {
1971 case SIMEDBONE_CHILDREN:
1973 break;
1976 break;
1977 case SIMEDBONE_SIBLINGS:
1979 break;
1980 case SIMEDBONE_LENGTH:
1981 select_similar_length(C, thresh);
1982 break;
1984 select_similar_direction(C, thresh);
1985 break;
1986 case SIMEDBONE_PREFIX:
1988 break;
1989 case SIMEDBONE_SUFFIX:
1991 break;
1994 break;
1995 case SIMEDBONE_COLOR:
1997 break;
1998 case SIMEDBONE_SHAPE:
2000 break;
2001 }
2002
2003#undef STRUCT_SIZE_AND_OFFSET
2004
2006
2007 return OPERATOR_FINISHED;
2008}
2009
2011{
2012 /* identifiers */
2013 ot->name = "Select Similar";
2014 ot->idname = "ARMATURE_OT_select_similar";
2015
2016 /* callback functions */
2017 ot->invoke = WM_menu_invoke;
2020 ot->description = "Select similar bones by property types";
2021
2022 /* flags */
2023 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2024
2025 /* properties */
2026 ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMEDBONE_LENGTH, "Type", "");
2027 RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
2028}
2029
2031
2032/* -------------------------------------------------------------------- */
2035
2036/* No need to convert to multi-objects. Just like we keep the non-active bones
2037 * selected we then keep the non-active objects untouched (selected/unselected). */
2039{
2041 EditBone *ebone_active;
2042 int direction = RNA_enum_get(op->ptr, "direction");
2043 const bool add_to_sel = RNA_boolean_get(op->ptr, "extend");
2044 bool changed = false;
2045 bArmature *arm = static_cast<bArmature *>(ob->data);
2046
2047 ebone_active = arm->act_edbone;
2048 if (ebone_active == nullptr) {
2049 return OPERATOR_CANCELLED;
2050 }
2051
2052 if (direction == BONE_SELECT_PARENT) {
2053 if (ebone_active->parent) {
2054 EditBone *ebone_parent;
2055
2056 ebone_parent = ebone_active->parent;
2057
2058 if (EBONE_SELECTABLE(arm, ebone_parent)) {
2059 arm->act_edbone = ebone_parent;
2060
2061 if (!add_to_sel) {
2062 ED_armature_ebone_select_set(ebone_active, false);
2063 }
2064 ED_armature_ebone_select_set(ebone_parent, true);
2065
2066 changed = true;
2067 }
2068 }
2069 }
2070 else { /* BONE_SELECT_CHILD */
2071 EditBone *ebone_child = nullptr;
2072 int pass;
2073
2074 /* first pass, only connected bones (the logical direct child) */
2075 for (pass = 0; pass < 2 && (ebone_child == nullptr); pass++) {
2076 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
2077 /* possible we have multiple children, some invisible */
2078 if (EBONE_SELECTABLE(arm, ebone_iter)) {
2079 if (ebone_iter->parent == ebone_active) {
2080 if ((pass == 1) || (ebone_iter->flag & BONE_CONNECTED)) {
2081 ebone_child = ebone_iter;
2082 break;
2083 }
2084 }
2085 }
2086 }
2087 }
2088
2089 if (ebone_child) {
2090 arm->act_edbone = ebone_child;
2091
2092 if (!add_to_sel) {
2093 ED_armature_ebone_select_set(ebone_active, false);
2094 }
2095 ED_armature_ebone_select_set(ebone_child, true);
2096
2097 changed = true;
2098 }
2099 }
2100
2101 if (changed == false) {
2102 return OPERATOR_CANCELLED;
2103 }
2104
2106
2108
2111
2112 return OPERATOR_FINISHED;
2113}
2114
2116{
2117 static const EnumPropertyItem direction_items[] = {
2118 {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""},
2119 {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""},
2120 {0, nullptr, 0, nullptr, nullptr},
2121 };
2122
2123 /* identifiers */
2124 ot->name = "Select Hierarchy";
2125 ot->idname = "ARMATURE_OT_select_hierarchy";
2126 ot->description = "Select immediate parent/children of selected bones";
2127
2128 /* API callbacks. */
2131
2132 /* flags */
2133 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2134
2135 /* props */
2136 RNA_def_enum(ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", "");
2137 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
2138}
2139
2141
2142/* -------------------------------------------------------------------- */
2145
2150{
2151 const Scene *scene = CTX_data_scene(C);
2152 ViewLayer *view_layer = CTX_data_view_layer(C);
2153 const bool active_only = RNA_boolean_get(op->ptr, "only_active");
2154 const bool extend = RNA_boolean_get(op->ptr, "extend");
2155
2157 scene, view_layer, CTX_wm_view3d(C));
2158 for (Object *ob : objects) {
2159 bArmature *arm = static_cast<bArmature *>(ob->data);
2160
2161 EditBone *ebone_mirror_act = nullptr;
2162
2163 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
2164 const int flag = ED_armature_ebone_selectflag_get(ebone);
2165 EBONE_PREV_FLAG_SET(ebone, flag);
2166 }
2167
2168 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
2169 if (EBONE_SELECTABLE(arm, ebone)) {
2170 EditBone *ebone_mirror;
2171 int flag_new = extend ? EBONE_PREV_FLAG_GET(ebone) : 0;
2172
2173 if ((ebone_mirror = ED_armature_ebone_get_mirrored(arm->edbo, ebone)) &&
2174 blender::animrig::bone_is_visible(arm, ebone_mirror))
2175 {
2176 const int flag_mirror = EBONE_PREV_FLAG_GET(ebone_mirror);
2177 flag_new |= flag_mirror;
2178
2179 if (ebone == arm->act_edbone) {
2180 ebone_mirror_act = ebone_mirror;
2181 }
2182
2183 /* skip all but the active or its mirror */
2184 if (active_only && !ELEM(arm->act_edbone, ebone, ebone_mirror)) {
2185 continue;
2186 }
2187 }
2188
2189 ED_armature_ebone_selectflag_set(ebone, flag_new);
2190 }
2191 }
2192
2193 if (ebone_mirror_act) {
2194 arm->act_edbone = ebone_mirror_act;
2195 }
2196
2198
2200
2203 }
2204
2205 return OPERATOR_FINISHED;
2206}
2207
2209{
2210 /* identifiers */
2211 ot->name = "Select Mirror";
2212 ot->idname = "ARMATURE_OT_select_mirror";
2213 ot->description = "Mirror the bone selection";
2214
2215 /* API callbacks. */
2218
2219 /* flags */
2220 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2221
2222 /* properties */
2224 ot->srna, "only_active", false, "Active Only", "Only operate on the active bone");
2225 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
2226}
2227
2229
2230/* -------------------------------------------------------------------- */
2233
2235 bArmature *arm, EditBone *ebone_parent, EditBone *ebone_child, bool use_parent, bool is_test)
2236{
2237 do {
2238
2239 if (!use_parent && (ebone_child == ebone_parent)) {
2240 break;
2241 }
2242
2243 if (is_test) {
2244 if (!EBONE_SELECTABLE(arm, ebone_child)) {
2245 return false;
2246 }
2247 }
2248 else {
2250 }
2251
2252 if (ebone_child == ebone_parent) {
2253 break;
2254 }
2255
2256 ebone_child = ebone_child->parent;
2257 } while (true);
2258
2259 return true;
2260}
2261
2263 wmOperator *op,
2264 const wmEvent *event)
2265{
2266 Object *obedit = CTX_data_edit_object(C);
2267 bArmature *arm = static_cast<bArmature *>(obedit->data);
2268 EditBone *ebone_src, *ebone_dst;
2269 EditBone *ebone_isect_parent = nullptr;
2270 EditBone *ebone_isect_child[2];
2271 bool changed;
2272 Base *base_dst = nullptr;
2273
2276
2277 ebone_src = arm->act_edbone;
2278 ebone_dst = ED_armature_pick_ebone(C, event->mval, false, &base_dst);
2279
2280 /* fall back to object selection */
2281 if (ELEM(nullptr, ebone_src, ebone_dst) || (ebone_src == ebone_dst)) {
2282 return OPERATOR_PASS_THROUGH;
2283 }
2284
2285 if (base_dst && base_dst->object != obedit) {
2286 /* Disconnected, ignore. */
2287 return OPERATOR_CANCELLED;
2288 }
2289
2290 ebone_isect_child[0] = ebone_src;
2291 ebone_isect_child[1] = ebone_dst;
2292
2293 /* ensure 'ebone_src' is the parent of 'ebone_dst', or set 'ebone_isect_parent' */
2294 if (ED_armature_ebone_is_child_recursive(ebone_src, ebone_dst)) {
2295 /* pass */
2296 }
2297 else if (ED_armature_ebone_is_child_recursive(ebone_dst, ebone_src)) {
2298 std::swap(ebone_src, ebone_dst);
2299 }
2300 else if ((ebone_isect_parent = ED_armature_ebone_find_shared_parent(ebone_isect_child, 2))) {
2301 /* pass */
2302 }
2303 else {
2304 /* disconnected bones */
2305 return OPERATOR_CANCELLED;
2306 }
2307
2308 if (ebone_isect_parent) {
2309 if (armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, true) &&
2310 armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, true))
2311 {
2312 armature_shortest_path_select(arm, ebone_isect_parent, ebone_src, false, false);
2313 armature_shortest_path_select(arm, ebone_isect_parent, ebone_dst, false, false);
2314 changed = true;
2315 }
2316 else {
2317 /* unselectable */
2318 changed = false;
2319 }
2320 }
2321 else {
2322 if (armature_shortest_path_select(arm, ebone_src, ebone_dst, true, true)) {
2323 armature_shortest_path_select(arm, ebone_src, ebone_dst, true, false);
2324 changed = true;
2325 }
2326 else {
2327 /* unselectable */
2328 changed = false;
2329 }
2330 }
2331
2332 if (changed) {
2333 arm->act_edbone = ebone_dst;
2338
2339 return OPERATOR_FINISHED;
2340 }
2341
2342 BKE_report(op->reports, RPT_WARNING, "Unselectable bone in chain");
2343 return OPERATOR_CANCELLED;
2344}
2345
2347{
2348 /* identifiers */
2349 ot->name = "Pick Shortest Path";
2350 ot->idname = "ARMATURE_OT_shortest_path_pick";
2351 ot->description = "Select shortest path between two bones";
2352
2353 /* API callbacks. */
2356
2357 /* flags */
2358 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2359}
2360
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)
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#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:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ POSE_SELECTED
#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:983
@ VIEW3D_SELECT_PICK_NEAREST
Definition ED_view3d.hh:985
void view3d_operator_needs_gpu(const bContext *C)
eV3DSelectObjectFilter
Definition ED_view3d.hh:988
@ VIEW3D_SELECT_FILTER_NOP
Definition ED_view3d.hh:990
blender::Vector< GPUSelectResult, 2500 > GPUSelectStorage
Definition GPU_select.hh:45
#define C
Definition RandGen.cpp:29
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_GPENCIL
Definition WM_types.hh:399
#define ND_BONE_SELECT
Definition WM_types.hh:460
#define NC_OBJECT
Definition WM_types.hh:379
#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)
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)
@ SIMEDBONE_CHILDREN
@ SIMEDBONE_COLLECTION
@ SIMEDBONE_SUFFIX
@ SIMEDBONE_PREFIX
@ SIMEDBONE_DIRECTION
@ SIMEDBONE_LENGTH
@ SIMEDBONE_SIBLINGS
@ SIMEDBONE_SHAPE
@ SIMEDBONE_CHILDREN_IMMEDIATE
@ SIMEDBONE_COLOR
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:764
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bool bone_is_visible(const bArmature *armature, const Bone *bone)
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]
union EditBone::@275371335250266324235150226366250166246037204077 temp
EditBone * parent
ListBase bone_collections
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:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
uint len
int xy[2]
Definition wm_draw.cc:178
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:4237
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:145