Blender V5.0
animrig/intern/bone_collections.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_listbase.h"
10#include "BLI_map.hh"
11#include "BLI_string.h"
12#include "BLI_string_utf8.h"
13#include "BLI_string_utils.hh"
14#include "BLI_utildefines.h"
15
16#include "BLT_translation.hh"
17
18#include "DNA_armature_types.h"
19
20#include "MEM_guardedalloc.h"
21
22#include "BKE_animsys.h"
23#include "BKE_idprop.hh"
24#include "BKE_lib_id.hh"
25#include "BKE_lib_override.hh"
26
27#include "ANIM_armature_iter.hh"
29#include "WM_api.hh"
30
32
33#include <cstring>
34
35using std::strcmp;
36
37using namespace blender::animrig;
38
39namespace {
40
42constexpr eBoneCollection_Flag default_flags = BONE_COLLECTION_VISIBLE |
45constexpr auto bonecoll_default_name = "Bones";
46
47} // namespace
48
49static void ancestors_visible_update(bArmature *armature,
50 const BoneCollection *parent_bcoll,
51 BoneCollection *bcoll);
52
54{
55 if (name == nullptr || name[0] == '\0') {
56 /* Use a default name if no name was given. */
57 name = DATA_(bonecoll_default_name);
58 }
59
60 /* NOTE: the collection name may change after the collection is added to an
61 * armature, to ensure it is unique within the armature. */
63
64 STRNCPY_UTF8(bcoll->name, name);
65 bcoll->flags = default_flags;
66
67 return bcoll;
68}
69
70void ANIM_bonecoll_free(BoneCollection *bcoll, const bool do_id_user_count)
71{
73 "bone collection still has bones assigned to it, will cause dangling pointers in "
74 "bone runtime data");
75 if (bcoll->prop) {
76 IDP_FreeProperty_ex(bcoll->prop, do_id_user_count);
77 }
78 if (bcoll->system_properties) {
79 IDP_FreeProperty_ex(bcoll->system_properties, do_id_user_count);
80 }
81 MEM_delete(bcoll);
82}
83
90{
91 LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
93 ref->bcoll = bcoll;
94 BLI_addtail(&member->bone->runtime.collections, ref);
95 }
96}
97
99{
102
103 /* Make sure the BONE_COLLECTION_ANCESTORS_VISIBLE flags are set correctly. */
104 for (BoneCollection *bcoll : armature->collections_roots()) {
105 ancestors_visible_update(armature, nullptr, bcoll);
106 }
107
108 /* Construct the bone-to-collections mapping. */
109 for (BoneCollection *bcoll : armature->collections_span()) {
111 }
112}
113
115{
116 /* Free the bone-to-its-collections mapping. */
118 [&](Bone *bone) { BLI_freelistN(&bone->runtime.collections); });
119}
120
127{
128 /* Cannot capture armature & bcoll by reference in the lambda, as that would change its signature
129 * and no longer be compatible with BLI_uniquename_cb(). */
130 auto bonecoll_name_is_duplicate = [&](const blender::StringRef name) -> bool {
131 for (BoneCollection *bcoll_iter : armature->collections_span()) {
132 if (bcoll_iter != bcoll && bcoll_iter->name == name) {
133 return true;
134 }
135 }
136 return false;
137 };
138
139 BLI_uniquename_cb(bonecoll_name_is_duplicate,
140 DATA_(bonecoll_default_name),
141 '.',
142 bcoll->name,
143 sizeof(bcoll->name));
144}
145
153static void bonecoll_insert_at_index(bArmature *armature, BoneCollection *bcoll, const int index)
154{
155 BLI_assert(index <= armature->collection_array_num);
156
157 armature->collection_array = reinterpret_cast<BoneCollection **>(
159 sizeof(BoneCollection *) * (armature->collection_array_num + 1),
160 __func__));
161
162 /* To keep the memory consistent, insert the new element at the end of the
163 * now-grown array, then rotate it into place. */
164 armature->collection_array[armature->collection_array_num] = bcoll;
165 armature->collection_array_num++;
166
167 const int rotate_count = armature->collection_array_num - index - 1;
168 internal::bonecolls_rotate_block(armature, index, rotate_count, +1);
169
170 if (armature->runtime.active_collection_index >= index) {
172 armature->runtime.active_collection_index + 1);
173 }
174}
175
176static void bonecoll_insert_as_root(bArmature *armature, BoneCollection *bcoll, int at_index)
177{
178 BLI_assert(at_index >= -1);
179 BLI_assert(at_index <= armature->collection_root_count);
180 if (at_index < 0) {
181 at_index = armature->collection_root_count;
182 }
183
184 bonecoll_insert_at_index(armature, bcoll, at_index);
185 armature->collection_root_count++;
186
187 ancestors_visible_update(armature, nullptr, bcoll);
188}
189
191 BoneCollection *bcoll,
192 const int parent_index)
193{
194 BLI_assert_msg(parent_index >= 0, "Armature bone collection index should be 0 or larger");
195 BLI_assert_msg(parent_index < armature->collection_array_num,
196 "Parent bone collection index should not point beyond the end of the array");
197
198 BoneCollection *parent = armature->collection_array[parent_index];
199 if (parent->child_index == 0) {
200 /* This parent doesn't have any children yet, so place them at the end of the array. */
201 parent->child_index = armature->collection_array_num;
202 }
203 const int insert_at_index = parent->child_index + parent->child_count;
204 bonecoll_insert_at_index(armature, bcoll, insert_at_index);
205 parent->child_count++;
206
207 ancestors_visible_update(armature, parent, bcoll);
208
209 return insert_at_index;
210}
211
213 const char *name,
214 const int parent_index)
215{
217
218 if (!ID_IS_LINKED(&armature->id) && ID_IS_OVERRIDE_LIBRARY(&armature->id)) {
219 /* Mark this bone collection as local override, so that certain operations can be allowed. */
221 }
222
223 bonecoll_ensure_name_unique(armature, bcoll);
224
225 if (parent_index < 0) {
226 bonecoll_insert_as_root(armature, bcoll, -1);
227 }
228 else {
229 bonecoll_insert_as_child(armature, bcoll, parent_index);
230 }
231
232 /* Restore the active bone collection pointer, as its index might have changed. */
234
235 return bcoll;
236}
237
249 const BoneCollection *bcoll_to_copy)
250{
251 BoneCollection *bcoll = static_cast<BoneCollection *>(MEM_dupallocN(bcoll_to_copy));
252
253 /* Reset the child_index and child_count properties. These are unreliable when
254 * coming from an override, as the original array might have been completely
255 * reshuffled. Children will have to be copied separately. */
256 bcoll->child_index = 0;
257 bcoll->child_count = 0;
258
259 if (bcoll->prop) {
260 bcoll->prop = IDP_CopyProperty_ex(bcoll_to_copy->prop,
261 0 /*do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT*/);
262 }
263 if (bcoll->system_properties) {
265 bcoll_to_copy->system_properties, 0 /*do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT*/);
266 }
267
268 /* Remap the bone pointers to the given armature, as `bcoll_to_copy` is
269 * assumed to be owned by another armature. */
270 BLI_duplicatelist(&bcoll->bones, &bcoll->bones);
271 BLI_assert_msg(armature_dst->bonehash, "Expected armature bone hash to be there");
272 LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
273 member->bone = BKE_armature_find_bone_name(const_cast<bArmature *>(armature_dst),
274 member->bone->name);
275 }
276
277 /* Now that the collection points to the right bones, these bones can be
278 * updated to point to this collection. */
280
281 return bcoll;
282}
283
290 const bArmature *armature_src,
291 const int parent_bcoll_dst_index,
292 const BoneCollection *parent_bcoll_src)
293{
294 BLI_assert_msg(parent_bcoll_dst_index >= 0,
295 "this function can only add children to another collection, it cannot add roots");
296
297 /* Iterate over the children in `armature_src`, and clone them one by one into `armature_dst`.
298 *
299 * This uses two loops. The first one adds all the children, the second loop iterates over those
300 * children for the recursion step. As this performs a "breadth-first insertion", it requires
301 * considerably less shuffling of the array as when the recursion was done immediately after
302 * inserting a child. */
303 BoneCollection *parent_bcoll_dst = armature_dst->collection_array[parent_bcoll_dst_index];
304
305 /* Big Fat Assumption: because this code runs as part of the library override system, it is
306 * assumed that the parent is either a newly added root, or another child that was also added by
307 * the liboverride system. Because this would never add a child to an original "sequence of
308 * siblings", insertions of children always happen at the end of the array. This means that
309 * `parent_bcoll_dst_index` remains constant during this entire function. */
310
311 /* Copy & insert all the children. */
312 for (int bcoll_src_index = parent_bcoll_src->child_index;
313 bcoll_src_index < parent_bcoll_src->child_index + parent_bcoll_src->child_count;
314 bcoll_src_index++)
315 {
316 const BoneCollection *bcoll_src = armature_src->collection_array[bcoll_src_index];
317 BoneCollection *bcoll_dst = copy_and_update_ownership(armature_dst, bcoll_src);
318
319 const int bcoll_index_dst = bonecoll_insert_as_child(
320 armature_dst, bcoll_dst, parent_bcoll_dst_index);
321
322#ifndef NDEBUG
323 /* Check that the above Big Fat Assumption holds. */
324 BLI_assert_msg(bcoll_index_dst > parent_bcoll_dst_index,
325 "expecting children to be added to the array AFTER their parent");
326#else
327 (void)bcoll_index_dst;
328#endif
329
330 bonecoll_ensure_name_unique(armature_dst, bcoll_dst);
331 }
332
333 /* Double-check that the above Big Fat Assumption holds. */
334#ifndef NDEBUG
335 const int new_parent_bcoll_dst_index = armature_bonecoll_find_index(armature_dst,
336 parent_bcoll_dst);
337 BLI_assert_msg(new_parent_bcoll_dst_index == parent_bcoll_dst_index,
338 "did not expect parent_bcoll_dst_index to change");
339#endif
340
341 /* Recurse into the children to copy grandchildren. */
342 BLI_assert_msg(parent_bcoll_dst->child_count == parent_bcoll_src->child_count,
343 "all children should have been copied");
344 for (int child_num = 0; child_num < parent_bcoll_dst->child_count; child_num++) {
345 const int bcoll_src_index = parent_bcoll_src->child_index + child_num;
346 const int bcoll_dst_index = parent_bcoll_dst->child_index + child_num;
347
348 const BoneCollection *bcoll_src = armature_src->collection_array[bcoll_src_index];
349 liboverride_recursively_add_children(armature_dst, armature_src, bcoll_dst_index, bcoll_src);
350 }
351}
352
354 const bArmature *armature_src,
355 const BoneCollection *anchor_in_dst,
356 const BoneCollection *bcoll_to_copy)
357{
358#ifndef NDEBUG
359 /* Check that this bone collection is really a root, as this is assumed by the
360 * rest of this function. This is an O(n) check, though, so that's why it's
361 * only running in debug builds. */
362 const int bcoll_index_src = armature_bonecoll_find_index(armature_src, bcoll_to_copy);
363 if (!armature_bonecoll_is_root(armature_src, bcoll_index_src)) {
364 printf(
365 "Armature \"%s\" has library override operation that adds non-root bone collection "
366 "\"%s\". This is unexpected, please file a bug report.\n",
367 armature_src->id.name + 2,
368 bcoll_to_copy->name);
369 }
370#endif
371
372 BoneCollection *bcoll = copy_and_update_ownership(armature_dst, bcoll_to_copy);
373
374 const int anchor_index = armature_bonecoll_find_index(armature_dst, anchor_in_dst);
375 const int bcoll_index = anchor_index + 1;
377 bcoll_index <= armature_dst->collection_root_count,
378 "did not expect library override to add a child bone collection, only roots are expected");
379 bonecoll_insert_as_root(armature_dst, bcoll, bcoll_index);
380 bonecoll_ensure_name_unique(armature_dst, bcoll);
381
382 /* Library override operations are only constructed for the root bones. This means that handling
383 * this operation should also include copying the children. */
384 liboverride_recursively_add_children(armature_dst, armature_src, bcoll_index, bcoll_to_copy);
385
387 return bcoll;
388}
389
391{
392 armature->runtime.active_collection_index = -1;
393 armature->runtime.active_collection = nullptr;
394 armature->active_collection_name[0] = '\0';
395}
396
398{
399 if (bcoll == nullptr) {
401 return;
402 }
403
404 const int index = armature_bonecoll_find_index(armature, bcoll);
405 if (index == -1) {
406 /* TODO: print warning? Or just ignore this case? */
408 return;
409 }
410
411 STRNCPY(armature->active_collection_name, bcoll->name);
412 armature->runtime.active_collection_index = index;
413 armature->runtime.active_collection = bcoll;
414}
415
416void ANIM_armature_bonecoll_active_index_set(bArmature *armature, const int bone_collection_index)
417{
418 if (bone_collection_index < 0 || bone_collection_index >= armature->collection_array_num) {
420 return;
421 }
422
423 BoneCollection *bcoll = armature->collection_array[bone_collection_index];
424
425 STRNCPY(armature->active_collection_name, bcoll->name);
426 armature->runtime.active_collection_index = bone_collection_index;
427 armature->runtime.active_collection = bcoll;
428}
429
435
437{
438 const std::string_view active_name = armature->active_collection_name;
439 if (active_name.empty()) {
441 return;
442 }
443
444 int index = 0;
445 for (BoneCollection *bcoll : armature->collections_span()) {
446 if (bcoll->name == active_name) {
447 armature->runtime.active_collection_index = index;
448 armature->runtime.active_collection = bcoll;
449 return;
450 }
451 index++;
452 }
453
454 /* No bone collection with the name was found, so better to clear everything. */
456}
457
459{
460 const bool is_override = ID_IS_OVERRIDE_LIBRARY(armature);
461 if (ID_IS_LINKED(armature) && !is_override) {
462 return false;
463 }
464
465 if (is_override && BKE_lib_override_library_is_system_defined(nullptr, &armature->id)) {
466 /* A system override is still not editable. */
467 return false;
468 }
469
470 if (is_override && (bcoll->flags & BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL) == 0) {
471 /* This particular collection was not added in the local override, so not editable. */
472 return false;
473 }
474 return true;
475}
476
478 const int from_index,
479 const int to_index)
480{
481 if (from_index >= armature->collection_array_num || to_index >= armature->collection_array_num ||
482 from_index == to_index)
483 {
484 return false;
485 }
486
487 /* Only allow moving within the same parent. This is written a bit awkwardly to avoid two calls
488 * to `armature_bonecoll_find_parent_index()` as that is O(n) in the number of bone collections.
489 */
490 const int parent_index = armature_bonecoll_find_parent_index(armature, from_index);
491 if (!armature_bonecoll_is_child_of(armature, parent_index, to_index)) {
492 return false;
493 }
494
495 if (parent_index < 0) {
496 /* Roots can just be moved around, as there is no `child_index` to update in this case. */
497 internal::bonecolls_move_to_index(armature, from_index, to_index);
498 return true;
499 }
500
501 /* Store the parent's child_index, as that might move if to_index is the first child
502 * (bonecolls_move_to_index() will keep it pointing at that first child). */
503 BoneCollection *parent_bcoll = armature->collection_array[parent_index];
504 const int old_parent_child_index = parent_bcoll->child_index;
505
506 internal::bonecolls_move_to_index(armature, from_index, to_index);
507
508 parent_bcoll->child_index = old_parent_child_index;
509
510 return true;
511}
512
513static int bonecoll_child_number(const bArmature *armature,
514 const int parent_bcoll_index,
515 const int bcoll_index)
516{
517 if (parent_bcoll_index < 0) {
518 /* Root bone collections are always at the start of the array, and thus their index is the
519 * 'child number'. */
520 return bcoll_index;
521 }
522
523 const BoneCollection *parent_bcoll = armature->collection_array[parent_bcoll_index];
524 return bcoll_index - parent_bcoll->child_index;
525}
526
528 const int from_index,
529 int to_index,
530 const MoveLocation before_after)
531{
532 const int from_parent_index = armature_bonecoll_find_parent_index(armature, from_index);
533 const int to_parent_index = armature_bonecoll_find_parent_index(armature, to_index);
534
535 if (from_parent_index != to_parent_index) {
536 /* Moving between parents. */
537 int to_child_num = bonecoll_child_number(armature, to_parent_index, to_index);
538 if (before_after == MoveLocation::After) {
539 to_child_num++;
540 }
541
543 armature, from_index, to_child_num, from_parent_index, to_parent_index);
544 }
545
546 /* Moving between siblings. */
547 switch (before_after) {
549 if (to_index > from_index) {
550 /* Moving to the right, but needs to go before that one, so needs a decrement. */
551 to_index--;
552 }
553 break;
554
556 if (to_index < from_index) {
557 /* Moving to the left, but needs to go after that one, so needs a decrement. */
558 to_index++;
559 }
560 break;
561 }
562
563 if (!ANIM_armature_bonecoll_move_to_index(armature, from_index, to_index)) {
564 return -1;
565 }
566 return to_index;
567}
568
570{
571 if (bcoll == nullptr) {
572 return false;
573 }
574
575 const int bcoll_index = armature_bonecoll_find_index(armature, bcoll);
576 const int to_index = bcoll_index + step;
577 if (bcoll_index < 0 || to_index < 0 || to_index >= armature->collection_array_num) {
578 return false;
579 }
580
581 ANIM_armature_bonecoll_move_to_index(armature, bcoll_index, to_index);
582
583 return true;
584}
585
587{
588 char old_name[sizeof(bcoll->name)];
589
590 STRNCPY(old_name, bcoll->name);
591
592 if (name[0] == '\0') {
593 /* Refuse to have nameless collections. The name of the active collection is stored in DNA, and
594 * an empty string means 'no active collection'. */
595 STRNCPY_UTF8(bcoll->name, DATA_(bonecoll_default_name));
596 }
597 else {
598 STRNCPY_UTF8(bcoll->name, name);
599 }
600
601 bonecoll_ensure_name_unique(armature, bcoll);
602
603 /* Bone collections can be reached via .collections (4.0+) and .collections_all (4.1+).
604 * Animation data from 4.0 should have been versioned to only use `.collections_all`. */
605 BKE_animdata_fix_paths_rename_all(&armature->id, "collections", old_name, bcoll->name);
606 BKE_animdata_fix_paths_rename_all(&armature->id, "collections_all", old_name, bcoll->name);
607}
608
610{
611 BLI_assert(0 <= index && index < armature->collection_array_num);
612
613 BoneCollection *bcoll = armature->collection_array[index];
614
615 /* Get the active bone collection index before the armature is manipulated. */
616 const int active_collection_index = armature->runtime.active_collection_index;
617
618 /* The parent needs updating, so better to find it before this bone collection is removed. */
619 int parent_bcoll_index = armature_bonecoll_find_parent_index(armature, index);
620 BoneCollection *parent_bcoll = parent_bcoll_index >= 0 ?
621 armature->collection_array[parent_bcoll_index] :
622 nullptr;
623
624 /* Move all the children of the to-be-removed bone collection to their grandparent. */
625 int move_to_child_num = bonecoll_child_number(armature, parent_bcoll_index, index);
626 while (bcoll->child_count > 0) {
627 /* Move the child to its grandparent, at the same spot as the to-be-removed
628 * bone collection. The latter thus (potentially) shifts by 1 in the array.
629 * After removal, this effectively makes it appear like the removed bone
630 * collection is replaced by all its children. */
632 bcoll->child_index, /* Move from index... */
633 move_to_child_num, /* to this child number. */
634 index, /* From this parent... */
635 parent_bcoll_index /* to that parent. */
636 );
637
638 /* Both 'index' and 'parent_bcoll_index' can change each iteration. */
639 index = internal::bonecolls_find_index_near(armature, bcoll, index);
640 BLI_assert_msg(index >= 0, "could not find bone collection after moving things around");
641
642 if (parent_bcoll_index >= 0) { /* If there is no parent, its index should stay -1. */
643 parent_bcoll_index = internal::bonecolls_find_index_near(
644 armature, parent_bcoll, parent_bcoll_index);
645 BLI_assert_msg(parent_bcoll_index >= 0,
646 "could not find bone collection parent after moving things around");
647 }
648
649 move_to_child_num++;
650 }
651
652 /* Adjust the parent for the removal of its child. */
653 if (parent_bcoll_index < 0) {
654 /* Removing a root, so the armature itself needs to be updated. */
655 armature->collection_root_count--;
656 BLI_assert_msg(armature->collection_root_count >= 0, "armature root count cannot be negative");
657 }
658 else {
659 parent_bcoll->child_count--;
660 if (parent_bcoll->child_count == 0) {
661 parent_bcoll->child_index = 0;
662 }
663 }
664
665 /* Rotate the to-be-removed collection to the last array element. */
666 internal::bonecolls_move_to_index(armature, index, armature->collection_array_num - 1);
667
668 /* NOTE: we don't bother to shrink the allocation. It's okay if the
669 * capacity has extra space, because the number of valid items is tracked. */
670 armature->collection_array_num--;
671 armature->collection_array[armature->collection_array_num] = nullptr;
672
673 /* Update the active BoneCollection. */
674 if (active_collection_index >= 0) {
675 /* Default: select the next sibling.
676 * If there is none: select the previous sibling.
677 * If there is none: select the parent.
678 */
679 if (armature_bonecoll_is_child_of(armature, parent_bcoll_index, active_collection_index)) {
680 /* active_collection_index still points to a sibling of the removed collection. */
681 ANIM_armature_bonecoll_active_index_set(armature, active_collection_index);
682 }
683 else if (active_collection_index > 0 &&
685 armature, parent_bcoll_index, active_collection_index - 1))
686 {
687 /* The child preceding active_collection_index is a sibling of the removed collection. */
688 ANIM_armature_bonecoll_active_index_set(armature, active_collection_index - 1);
689 }
690 else {
691 /* Select the parent, or nothing if this was a root collection. In that case, if there are no
692 * siblings either, this just means all bone collections have been removed. */
693 ANIM_armature_bonecoll_active_index_set(armature, parent_bcoll_index);
694 }
695 }
696
697 const bool is_solo = bcoll->is_solo();
699 if (is_solo) {
700 /* This might have been the last solo'ed bone collection, so check whether
701 * solo'ing should still be active on the armature. */
703 }
705}
706
712
713template<typename MaybeConstBoneCollection>
714static MaybeConstBoneCollection *bonecolls_get_by_name(
715 blender::Span<MaybeConstBoneCollection *> bonecolls, const char *name)
716{
717 for (MaybeConstBoneCollection *bcoll : bonecolls) {
718 if (STREQ(bcoll->name, name)) {
719 return bcoll;
720 }
721 }
722 return nullptr;
723}
724
726{
727 return bonecolls_get_by_name(armature->collections_span(), name);
728}
729
731{
732 for (int index = 0; index < armature->collection_array_num; index++) {
733 const BoneCollection *bcoll = armature->collection_array[index];
734 if (STREQ(bcoll->name, name)) {
735 return index;
736 }
737 }
738 return -1;
739}
740
743{
744 for (BoneCollection *bcoll : armature->collection_children(parent_bcoll)) {
747 }
748}
749
752{
753 if (!parent_bcoll->is_visible_with_ancestors()) {
754 /* If this bone collection is not visible itself, or any of its ancestors are
755 * invisible, all descendants have an invisible ancestor. */
756 ancestors_visible_descendants_clear(armature, parent_bcoll);
757 return;
758 }
759
760 /* parent_bcoll is visible, and so are its ancestors. This means that all direct children have
761 * visible ancestors. The grandchildren depend on the children's visibility as well, hence the
762 * recursion. */
763 for (BoneCollection *bcoll : armature->collection_children(parent_bcoll)) {
764 bcoll->flags |= BONE_COLLECTION_ANCESTORS_VISIBLE;
766 }
767}
768
771 const BoneCollection *parent_bcoll,
772 BoneCollection *bcoll)
773{
774 if (parent_bcoll == nullptr || parent_bcoll->is_visible_with_ancestors()) {
776 }
777 else {
779 }
781}
782
784{
787}
788
790{
793}
794
796 BoneCollection *bcoll,
797 const bool is_visible)
798{
799 if (is_visible) {
800 ANIM_bonecoll_show(armature, bcoll);
801 }
802 else {
803 ANIM_bonecoll_hide(armature, bcoll);
804 }
805}
806
808 BoneCollection *bcoll,
809 const bool is_solo)
810{
811 if (is_solo) {
812 /* Enabling solo is simple. */
813 bcoll->flags |= BONE_COLLECTION_SOLO;
814 armature->flag |= ARM_BCOLL_SOLO_ACTIVE;
815 return;
816 }
817
818 /* Disabling is harder, as the armature flag can only be disabled when there
819 * are no more bone collections with the SOLO flag set. */
822}
823
825{
826 bool any_bcoll_solo = false;
827 for (const BoneCollection *bcoll : armature->collections_span()) {
828 if (bcoll->flags & BONE_COLLECTION_SOLO) {
829 any_bcoll_solo = true;
830 break;
831 }
832 }
833
834 if (any_bcoll_solo) {
835 armature->flag |= ARM_BCOLL_SOLO_ACTIVE;
836 }
837 else {
838 armature->flag &= ~ARM_BCOLL_SOLO_ACTIVE;
839 }
840}
841
843 const BoneCollection *bcoll)
844{
845 const bool is_solo_active = armature->flag & ARM_BCOLL_SOLO_ACTIVE;
846
847 if (is_solo_active) {
848 /* If soloing is active, nothing in the hierarchy matters except the solo flag. */
849 return bcoll->is_solo();
850 }
851
852 return bcoll->is_visible_with_ancestors();
853}
854
856{
857 if (is_expanded) {
859 }
860 else {
862 }
863}
864
865/* Store the bone's membership on the collection. */
866static void add_membership(BoneCollection *bcoll, Bone *bone)
867{
869 member->bone = bone;
870 BLI_addtail(&bcoll->bones, member);
871}
872/* Store reverse membership on the bone. */
873static void add_reference(Bone *bone, BoneCollection *bcoll)
874{
876 ref->bcoll = bcoll;
877 BLI_addtail(&bone->runtime.collections, ref);
878}
879
881{
882 /* Precondition check: bail out if already a member. */
883 LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
884 if (member->bone == bone) {
885 return false;
886 }
887 }
888
889 add_membership(bcoll, bone);
890 add_reference(bone, bcoll);
891
892 return true;
893}
894
896{
897 /* Precondition check: bail out if already a member. */
899 if (ref->bcoll == bcoll) {
900 return false;
901 }
902 }
903
904 /* Store membership on the edit bone. Bones will be rebuilt when the armature
905 * goes out of edit mode, and by then the newly created bones will be added to
906 * the actual collection on the Armature. */
908 ref->bcoll = bcoll;
909 BLI_addtail(&ebone->bone_collections, ref);
910
911 return true;
912}
913
919
925
927{
928 bool was_found = false;
929
930 /* Remove membership from collection. */
932 if (member->bone == bone) {
933 BLI_freelinkN(&bcoll->bones, member);
934 was_found = true;
935 break;
936 }
937 }
938
939 /* Remove reverse membership from the bone.
940 * For data consistency sake, this is always done, regardless of whether the
941 * above loop found the membership. */
943 if (ref->bcoll == bcoll) {
944 BLI_freelinkN(&bone->runtime.collections, ref);
945 break;
946 }
947 }
948
949 return was_found;
950}
951
953{
955 /* TODO: include Armature as parameter, and check that the bone collection to unassign from is
956 * actually editable. */
957 ANIM_armature_bonecoll_unassign(ref->bcoll, bone);
958 }
959}
960
967
969{
970 bool was_found = false;
971
972 /* Edit bone membership is only stored on the edit bone itself. */
974 if (ref->bcoll == bcoll) {
975 BLI_freelinkN(&ebone->bone_collections, ref);
976 was_found = true;
977 break;
978 }
979 }
980 return was_found;
981}
982
984{
985 /* Remove all the old collection memberships. */
986 for (BoneCollection *bcoll : armature->collections_span()) {
987 BLI_freelistN(&bcoll->bones);
988 }
989
990 /* For all bones, restore their collection memberships. */
991 ANIM_armature_foreach_bone(&armature->bonebase, [&](Bone *bone) {
992 LISTBASE_FOREACH (BoneCollectionReference *, ref, &bone->runtime.collections) {
993 add_membership(ref->bcoll, bone);
994 }
995 });
996}
997
998static bool any_bone_collection_visible(const bArmature *armature,
999 const ListBase /*BoneCollectionRef*/ *collection_refs)
1000{
1001 /* Special case: Hide bone when solo is active and it doesn't belong to any collection, see:
1002 * #137090. */
1003 if (BLI_listbase_is_empty(collection_refs) && !(armature->flag & ARM_BCOLL_SOLO_ACTIVE)) {
1004 return true;
1005 }
1006
1007 LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, collection_refs) {
1008 const BoneCollection *bcoll = bcoll_ref->bcoll;
1009 if (ANIM_armature_bonecoll_is_visible_effectively(armature, bcoll)) {
1010 return true;
1011 }
1012 }
1013 return false;
1014}
1015
1016bool ANIM_bone_in_visible_collection(const bArmature *armature, const Bone *bone)
1017{
1018 return any_bone_collection_visible(armature, &bone->runtime.collections);
1019}
1020bool ANIM_bonecoll_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
1021{
1022 return any_bone_collection_visible(armature, &ebone->bone_collections);
1023}
1024
1026{
1027 for (BoneCollection *bcoll : armature->collections_span()) {
1028 ANIM_bonecoll_show(armature, bcoll);
1029 }
1030}
1031
1033{
1034 for (BoneCollection *bcoll : armature->collections_span()) {
1035 ANIM_bonecoll_hide(armature, bcoll);
1036 }
1037}
1038
1039/* ********************************* */
1040/* Armature Layers transitional API. */
1041
1043{
1044 if (armature->runtime.active_collection == nullptr) {
1045 /* No active collection, do not assign to any. */
1046 return;
1047 }
1048
1050}
1051
1052static bool bcoll_list_contains(const ListBase /*BoneCollectionRef*/ *collection_refs,
1053 const BoneCollection *bcoll)
1054{
1055 LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, collection_refs) {
1056 if (bcoll == bcoll_ref->bcoll) {
1057 return true;
1058 }
1059 }
1060 return false;
1061}
1062
1064 const BoneCollection *bcoll)
1065{
1066 if (armature->edbo) {
1067 if (!armature->act_edbone) {
1068 return false;
1069 }
1070 return bcoll_list_contains(&armature->act_edbone->bone_collections, bcoll);
1071 }
1072
1073 if (!armature->act_bone) {
1074 return false;
1075 }
1076 return bcoll_list_contains(&armature->act_bone->runtime.collections, bcoll);
1077}
1078
1080{
1081 if (ANIM_bone_in_visible_collection(armature, bone)) {
1082 return;
1083 }
1084
1085 /* Making the first collection visible is enough to make the bone visible.
1086 *
1087 * Since bones without collection are considered visible,
1088 * bone->runtime.collections.first is certainly a valid pointer. */
1090 bone->runtime.collections.first);
1092}
1093
1095{
1096 if (ANIM_bonecoll_is_visible_editbone(armature, ebone)) {
1097 return;
1098 }
1099
1100 /* Making the first collection visible is enough to make the bone visible.
1101 *
1102 * Since bones without collection are considered visible,
1103 * ebone->bone_collections.first is certainly a valid pointer. */
1105 ebone->bone_collections.first);
1107}
1108
1110{
1112}
1113
1114/* ********* */
1115/* C++ only. */
1116namespace blender::animrig {
1117
1119{
1120 int index = 0;
1121 for (const BoneCollection *arm_bcoll : armature->collections_span()) {
1122 if (arm_bcoll == bcoll) {
1123 return index;
1124 }
1125 index++;
1126 }
1127
1128 return -1;
1129}
1130
1131int armature_bonecoll_find_parent_index(const bArmature *armature, const int bcoll_index)
1132{
1133 if (bcoll_index < armature->collection_root_count) {
1134 /* Don't bother iterating all collections when it's known to be a root. */
1135 return -1;
1136 }
1137
1138 int index = 0;
1139 for (const BoneCollection *potential_parent : armature->collections_span()) {
1140 if (potential_parent->child_index <= bcoll_index &&
1141 bcoll_index < potential_parent->child_index + potential_parent->child_count)
1142 {
1143 return index;
1144 }
1145
1146 index++;
1147 }
1148
1149 return -1;
1150}
1151
1152int armature_bonecoll_child_number_find(const bArmature *armature, const ::BoneCollection *bcoll)
1153{
1154 const int bcoll_index = armature_bonecoll_find_index(armature, bcoll);
1155 const int parent_index = armature_bonecoll_find_parent_index(armature, bcoll_index);
1156 return bonecoll_child_number(armature, parent_index, bcoll_index);
1157}
1158
1160 ::BoneCollection *bcoll,
1161 int new_child_number)
1162{
1163 const int bcoll_index = armature_bonecoll_find_index(armature, bcoll);
1164 const int parent_index = armature_bonecoll_find_parent_index(armature, bcoll_index);
1165
1166 BoneCollection fake_armature_parent = {};
1167 fake_armature_parent.child_count = armature->collection_root_count;
1168
1169 BoneCollection *parent_bcoll;
1170 if (parent_index < 0) {
1171 parent_bcoll = &fake_armature_parent;
1172 }
1173 else {
1174 parent_bcoll = armature->collection_array[parent_index];
1175 }
1176
1177 /* Bounds checks. */
1178 if (new_child_number >= parent_bcoll->child_count) {
1179 return -1;
1180 }
1181 if (new_child_number < 0) {
1182 new_child_number = parent_bcoll->child_count - 1;
1183 }
1184
1185 /* Store the parent's child_index, as that might move if to_index is the first child
1186 * (bonecolls_move_to_index() will keep it pointing at that first child). */
1187 const int old_parent_child_index = parent_bcoll->child_index;
1188 const int to_index = parent_bcoll->child_index + new_child_number;
1189 internal::bonecolls_move_to_index(armature, bcoll_index, to_index);
1190
1191 parent_bcoll->child_index = old_parent_child_index;
1192
1193 /* Make sure that if this was the active bone collection, its index also changes. */
1194 if (armature->runtime.active_collection_index == bcoll_index) {
1195 ANIM_armature_bonecoll_active_index_set(armature, to_index);
1196 }
1197
1198 return to_index;
1199}
1200
1201bool armature_bonecoll_is_root(const bArmature *armature, const int bcoll_index)
1202{
1203 BLI_assert(bcoll_index >= 0);
1204 return bcoll_index < armature->collection_root_count;
1205}
1206
1208 const int potential_parent_index,
1209 const int potential_child_index)
1210{
1211 /* Check for roots, before we try and access collection_array[-1]. */
1212 if (armature_bonecoll_is_root(armature, potential_child_index)) {
1213 return potential_parent_index == -1;
1214 }
1215 if (potential_parent_index < 0) {
1216 return false;
1217 }
1218
1219 const BoneCollection *potential_parent = armature->collection_array[potential_parent_index];
1220 const int upper_bound = potential_parent->child_index + potential_parent->child_count;
1221
1222 return potential_parent->child_index <= potential_child_index &&
1223 potential_child_index < upper_bound;
1224}
1225
1227 const int potential_parent_index,
1228 const int potential_descendant_index)
1229{
1230 BLI_assert_msg(potential_descendant_index >= 0,
1231 "Potential descendant has to exist for this function call to make sense.");
1232
1233 if (armature_bonecoll_is_child_of(armature, potential_parent_index, potential_descendant_index))
1234 {
1235 /* Found a direct child. */
1236 return true;
1237 }
1238
1239 const BoneCollection *potential_parent = armature->collection_array[potential_parent_index];
1240 const int upper_bound = potential_parent->child_index + potential_parent->child_count;
1241
1242 for (int visit_index = potential_parent->child_index; visit_index < upper_bound; visit_index++) {
1243 if (armature_bonecoll_is_descendant_of(armature, visit_index, potential_descendant_index)) {
1244 return true;
1245 }
1246 }
1247
1248 return false;
1249}
1250
1252{
1253 return bcoll->child_count > 0;
1254}
1255
1257 Span<const BoneCollection *> bcolls_source)
1258{
1259 /* Try to preserve the bone collection expanded/collapsed states. These are UI
1260 * changes that shouldn't impact undo steps. Care has to be taken to match the
1261 * old and the new bone collections, though, as they may have been reordered
1262 * or renamed.
1263 *
1264 * Reordering is handled by looking up collections by name.
1265 * Renames are handled by skipping those that cannot be found by name. */
1266
1267 auto find_old = [bcolls_source](const char *name, const int index) -> const BoneCollection * {
1268 /* Only check index when it's valid in the old armature. */
1269 if (index < bcolls_source.size()) {
1270 const BoneCollection *bcoll = bcolls_source[index];
1271 if (STREQ(bcoll->name, name)) {
1272 /* Index and name matches, let's use */
1273 return bcoll;
1274 }
1275 }
1276
1277 /* Try to find by name as a last resort. This function only works with
1278 * non-const pointers, hence the const_cast. */
1279 const BoneCollection *bcoll = bonecolls_get_by_name(bcolls_source, name);
1280 return bcoll;
1281 };
1282
1283 for (int i = 0; i < bcolls_dest.size(); i++) {
1284 BoneCollection *bcoll_new = bcolls_dest[i];
1285
1286 const BoneCollection *bcoll_old = find_old(bcoll_new->name, i);
1287 if (!bcoll_old) {
1288 continue;
1289 }
1290
1291 ANIM_armature_bonecoll_is_expanded_set(bcoll_new, bcoll_old->is_expanded());
1292 }
1293}
1294
1296 const int from_bcoll_index,
1297 int to_child_num,
1298 const int from_parent_index,
1299 const int to_parent_index)
1300{
1301 BLI_assert(0 <= from_bcoll_index && from_bcoll_index < armature->collection_array_num);
1302 BLI_assert(-1 <= from_parent_index && from_parent_index < armature->collection_array_num);
1303 BLI_assert(-1 <= to_parent_index && to_parent_index < armature->collection_array_num);
1304
1305 if (from_parent_index == to_parent_index) {
1306 /* TODO: use `to_child_num` to still move the child to the desired position. */
1307 return from_bcoll_index;
1308 }
1309
1310 /* The Armature itself acts like some sort of 'parent' for the root collections. By having this
1311 * as a 'fake' BoneCollection, all the code below can just be blissfully unaware of the special
1312 * 'all root collections should be at the start of the array' rule. */
1313 BoneCollection armature_root;
1314 armature_root.child_count = armature->collection_root_count;
1315 armature_root.child_index = 0;
1316 armature_root.flags = default_flags;
1317
1318 BoneCollection *from_parent = from_parent_index >= 0 ?
1319 armature->collection_array[from_parent_index] :
1320 &armature_root;
1321 BoneCollection *to_parent = to_parent_index >= 0 ? armature->collection_array[to_parent_index] :
1322 &armature_root;
1323
1324 BLI_assert_msg(-1 <= to_child_num && to_child_num <= to_parent->child_count,
1325 "to_child_num must point to an index of a child of the new parent, or the index "
1326 "of the last child + 1, or be -1 to indicate 'after last child'");
1327 if (to_child_num < 0) {
1328 to_child_num = to_parent->child_count;
1329 }
1330
1331 /* The new parent might not have children yet. */
1332 int to_bcoll_index;
1333 if (to_parent->child_count == 0) {
1334 /* New parents always get their children at the end of the array. */
1335 to_bcoll_index = armature->collection_array_num - 1;
1336 }
1337 else {
1338 to_bcoll_index = to_parent->child_index + to_child_num;
1339
1340 /* Check whether the new parent's children are to the left or right of bcoll_index.
1341 * This determines which direction the collections have to shift, and thus which index to
1342 * move the bcoll to. */
1343 if (to_bcoll_index > from_bcoll_index) {
1344 to_bcoll_index--;
1345 }
1346 }
1347
1348 /* In certain cases the 'from_parent' gets its first child removed, and needs to have its
1349 * child_index incremented. This needs to be done by comparing these fields before the actual
1350 * move happens (as that could also change the child_index). */
1351 const bool needs_post_move_child_index_bump = from_parent->child_index == from_bcoll_index &&
1352 to_bcoll_index <= from_bcoll_index;
1353 /* bonecolls_move_to_index() will try and keep the hierarchy correct, and thus change
1354 * to_parent->child_index to keep pointing to its current-first child. */
1355 const bool becomes_new_first_child = to_child_num == 0 || to_parent->child_count == 0;
1356 internal::bonecolls_move_to_index(armature, from_bcoll_index, to_bcoll_index);
1357
1358 /* Update child index & count of the old parent. */
1359 from_parent->child_count--;
1360 if (from_parent->child_count == 0) {
1361 /* Clean up the child index when the parent has no more children. */
1362 from_parent->child_index = 0;
1363 }
1364 else if (needs_post_move_child_index_bump) {
1365 /* The start of the block of children of the old parent has moved, because
1366 * we took out the first child. This only needs to be compensated for when
1367 * moving it to the left (or staying put), as then its old siblings stay in
1368 * place.
1369 *
1370 * This only needs to be done if there are any children left, though. */
1371 from_parent->child_index++;
1372 }
1373
1374 /* Update child index & count of the new parent. */
1375 if (becomes_new_first_child) {
1376 to_parent->child_index = to_bcoll_index;
1377 }
1378 to_parent->child_count++;
1379
1380 /* Copy the information from the 'fake' BoneCollection back to the armature. */
1381 armature->collection_root_count = armature_root.child_count;
1382 BLI_assert(armature_root.child_index == 0);
1383
1384 /* Since the parent changed, the effective visibility might change too. */
1385 ancestors_visible_update(armature, to_parent, armature->collection_array[to_bcoll_index]);
1386
1387 return to_bcoll_index;
1388}
1389
1390/* Utility functions for Armature edit-mode undo. */
1391
1393 BoneCollection ***bcoll_array_dst,
1394 int *bcoll_array_dst_num,
1395 BoneCollection **bcoll_array_src,
1396 const int bcoll_array_src_num,
1397 const bool do_id_user)
1398{
1399 BLI_assert(*bcoll_array_dst == nullptr);
1400 BLI_assert(*bcoll_array_dst_num == 0);
1401
1402 *bcoll_array_dst = MEM_malloc_arrayN<BoneCollection *>(bcoll_array_src_num, __func__);
1403 *bcoll_array_dst_num = bcoll_array_src_num;
1404
1406 for (int i = 0; i < bcoll_array_src_num; i++) {
1407 BoneCollection *bcoll_src = bcoll_array_src[i];
1408 BoneCollection *bcoll_dst = static_cast<BoneCollection *>(MEM_dupallocN(bcoll_src));
1409
1410 /* This will be rebuilt from the edit bones, so we don't need to copy it. */
1411 BLI_listbase_clear(&bcoll_dst->bones);
1412
1413 if (bcoll_src->prop) {
1414 bcoll_dst->prop = IDP_CopyProperty_ex(bcoll_src->prop,
1415 do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT);
1416 }
1417 if (bcoll_src->system_properties) {
1419 bcoll_src->system_properties, do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT);
1420 }
1421
1422 (*bcoll_array_dst)[i] = bcoll_dst;
1423
1424 bcoll_map.add(bcoll_src, bcoll_dst);
1425 }
1426
1427 return bcoll_map;
1428}
1429
1431 int *bcoll_array_num,
1432 const bool do_id_user)
1433{
1434 for (int i = 0; i < *bcoll_array_num; i++) {
1435 BoneCollection *bcoll = (*bcoll_array)[i];
1436
1437 if (bcoll->prop) {
1438 IDP_FreeProperty_ex(bcoll->prop, do_id_user);
1439 }
1440 if (bcoll->system_properties) {
1441 IDP_FreeProperty_ex(bcoll->system_properties, do_id_user);
1442 }
1443
1444 /* This will usually already be empty, because the passed BoneCollection
1445 * list is usually from ANIM_bonecoll_listbase_copy_no_membership().
1446 * However, during undo this is also used to free the BoneCollection
1447 * list on the Armature itself before copying over the undo BoneCollection
1448 * list, in which case this of Bone pointers may not be empty. */
1449 BLI_freelistN(&bcoll->bones);
1450
1451 MEM_freeN(bcoll);
1452 }
1453 MEM_SAFE_FREE(*bcoll_array);
1454
1455 *bcoll_array_num = 0;
1456}
1457
1459namespace internal {
1460
1462 const int start_index,
1463 const int count,
1464 const int direction)
1465{
1466 BLI_assert_msg(direction == 1 || direction == -1, "`direction` must be either -1 or +1");
1467
1468 if (count == 0) {
1469 return;
1470 }
1471
1472 /* When the block [start_index:start_index+count] is moved, it causes a duplication of one
1473 * element and overwrites another element. For example: given an array [0, 1, 2, 3, 4], moving
1474 * indices [1, 2] by +1 would result in one double element (1) and one missing element (3): [0,
1475 * 1, 1, 2, 4].
1476 *
1477 * This is resolved by moving that element to the other side of the block, so the result will be
1478 * [0, 3, 1, 2, 4]. This breaks the hierarchical information, so it's up to the caller to update
1479 * this one moved element.
1480 */
1481
1482 const int move_from_index = (direction > 0 ? start_index + count : start_index - 1);
1483 const int move_to_index = (direction > 0 ? start_index : start_index + count - 1);
1484 BoneCollection *bcoll_to_move = armature->collection_array[move_from_index];
1485
1486 BoneCollection **start = armature->collection_array + start_index;
1487 memmove((void *)(start + direction), (void *)start, count * sizeof(BoneCollection *));
1488
1489 armature->collection_array[move_to_index] = bcoll_to_move;
1490
1491 /* Update all child indices that reference something in the moved block. */
1492 for (BoneCollection *bcoll : armature->collections_span()) {
1493 /* Having both child_index and child_count zeroed out just means "no children"; these shouldn't
1494 * be updated at all, as here child_index is not really referencing the element at index 0. */
1495 if (bcoll->child_index == 0 && bcoll->child_count == 0) {
1496 continue;
1497 }
1498
1499 /* Compare to the original start & end of the block (i.e. pre-move). If a
1500 * child_index is within this range, it'll need updating. */
1501 if (start_index <= bcoll->child_index && bcoll->child_index < start_index + count) {
1502 bcoll->child_index += direction;
1503 }
1504 }
1505
1506 /* Make sure the active bone collection index is moved as well. */
1507 const int active_index = armature->runtime.active_collection_index;
1508 if (active_index == move_from_index) {
1509 armature->runtime.active_collection_index = move_to_index;
1510 }
1511 else if (start_index <= active_index && active_index < start_index + count) {
1512 armature->runtime.active_collection_index += direction;
1513 }
1514}
1515
1516void bonecolls_move_to_index(bArmature *armature, const int from_index, const int to_index)
1517{
1518 if (from_index == to_index) {
1519 return;
1520 }
1521
1522 BLI_assert(0 <= from_index);
1523 BLI_assert(from_index < armature->collection_array_num);
1524 BLI_assert(0 <= to_index);
1525 BLI_assert(to_index < armature->collection_array_num);
1526
1527 if (from_index < to_index) {
1528 const int block_start_index = from_index + 1;
1529 const int block_count = to_index - from_index;
1530 bonecolls_rotate_block(armature, block_start_index, block_count, -1);
1531 }
1532 else {
1533 const int block_start_index = to_index;
1534 const int block_count = from_index - to_index;
1535 bonecolls_rotate_block(armature, block_start_index, block_count, +1);
1536 }
1537}
1538
1539int bonecolls_find_index_near(bArmature *armature, BoneCollection *bcoll, const int index)
1540{
1541 BoneCollection **collections = armature->collection_array;
1542
1543 if (collections[index] == bcoll) {
1544 return index;
1545 }
1546 if (index > 0 && collections[index - 1] == bcoll) {
1547 return index - 1;
1548 }
1549 if (index < armature->collection_array_num - 1 && collections[index + 1] == bcoll) {
1550 return index + 1;
1551 }
1552 return -1;
1553}
1554
1555void bonecolls_debug_list(const bArmature *armature)
1556{
1557 printf("\033[38;5;214mBone collections of armature \"%s\":\033[0m\n", armature->id.name + 2);
1558 constexpr int root_ansi_color = 95;
1559 printf(
1560 " - \033[%dmroot\033[0m count: %d\n", root_ansi_color, armature->collection_root_count);
1561 for (int i = 0; i < armature->collection_array_num; ++i) {
1562 const BoneCollection *bcoll = armature->collection_array[i];
1563 printf(" - \033[%dmcolls[%d] = %24s\033[0m ",
1564 i < armature->collection_root_count ? root_ansi_color : 0,
1565 i,
1566 bcoll->name);
1567 if (bcoll->child_index == 0 && bcoll->child_count == 0) {
1568 printf("(leaf)");
1569 }
1570 else {
1571 printf("(child index: %d, count: %d)", bcoll->child_index, bcoll->child_count);
1572 }
1573 printf("\n");
1574 }
1575}
1576
1578{
1579 /* Remove bone membership. */
1581 ANIM_armature_bonecoll_unassign(bcoll, member->bone);
1582 }
1583 if (armature->edbo) {
1584 LISTBASE_FOREACH (EditBone *, ebone, armature->edbo) {
1586 }
1587 }
1588
1589 ANIM_bonecoll_free(bcoll);
1590}
1591
1592} // namespace internal
1593
1594} // namespace blender::animrig
Iterators for armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
void ANIM_armature_bonecoll_is_expanded_set(BoneCollection *bcoll, bool is_expanded)
bool ANIM_armature_bonecoll_unassign_editbone(BoneCollection *bcoll, EditBone *ebone)
void ANIM_bonecoll_free(BoneCollection *bcoll, bool do_id_user_count=true)
bool ANIM_armature_bonecoll_unassign(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_active_index_set(bArmature *armature, int bone_collection_index)
void BKE_animdata_fix_paths_rename_all(struct ID *ref_id, const char *prefix, const char *oldName, const char *newName)
Bone * BKE_armature_find_bone_name(bArmature *arm, const char *name)
IDProperty * IDP_CopyProperty_ex(const IDProperty *prop, int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:845
void IDP_FreeProperty_ex(IDProperty *prop, bool do_id_user)
Definition idprop.cc:1245
@ LIB_ID_CREATE_NO_USER_REFCOUNT
bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void void void void void BLI_duplicatelist(ListBase *dst, const ListBase *src) ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define STRNCPY_UTF8(dst, src)
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
#define STREQ(a, b)
#define DATA_(msgid)
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
eBoneCollection_Flag
@ BONE_COLLECTION_VISIBLE
@ BONE_COLLECTION_SELECTABLE
@ BONE_COLLECTION_ANCESTORS_VISIBLE
@ BONE_COLLECTION_SOLO
@ BONE_COLLECTION_EXPANDED
@ BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL
@ ARM_BCOLL_SOLO_ACTIVE
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define ND_BONE_COLLECTION
Definition WM_types.hh:474
#define NC_OBJECT
Definition WM_types.hh:379
void ANIM_armature_bonecoll_unassign_all_editbone(EditBone *ebone)
void ANIM_armature_refresh_solo_active(bArmature *armature)
static void add_membership(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_runtime_free(bArmature *armature)
void ANIM_armature_bonecoll_remove_from_index(bArmature *armature, int index)
bool ANIM_armature_bonecoll_assign_and_move(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_active_set(bArmature *armature, BoneCollection *bcoll)
static void bonecoll_insert_at_index(bArmature *armature, BoneCollection *bcoll, const int index)
static void bonecoll_insert_as_root(bArmature *armature, BoneCollection *bcoll, int at_index)
void ANIM_armature_bonecoll_is_visible_set(bArmature *armature, BoneCollection *bcoll, const bool is_visible)
void ANIM_armature_bonecoll_hide_all(bArmature *armature)
bool ANIM_armature_bonecoll_is_editable(const bArmature *armature, const BoneCollection *bcoll)
static void ancestors_visible_update(bArmature *armature, const BoneCollection *parent_bcoll, BoneCollection *bcoll)
BoneCollection * ANIM_armature_bonecoll_insert_copy_after(bArmature *armature_dst, const bArmature *armature_src, const BoneCollection *anchor_in_dst, const BoneCollection *bcoll_to_copy)
bool ANIM_armature_bonecoll_assign_and_move_editbone(BoneCollection *bcoll, EditBone *ebone)
void ANIM_armature_bonecoll_is_expanded_set(BoneCollection *bcoll, bool is_expanded)
void ANIM_armature_bonecoll_active_index_set(bArmature *armature, const int bone_collection_index)
void ANIM_bonecoll_hide(bArmature *armature, BoneCollection *bcoll)
bool ANIM_armature_bonecoll_move(bArmature *armature, BoneCollection *bcoll, const int step)
static bool any_bone_collection_visible(const bArmature *armature, const ListBase *collection_refs)
bool ANIM_armature_bonecoll_move_to_index(bArmature *armature, const int from_index, const int to_index)
void ANIM_armature_runtime_refresh(bArmature *armature)
void ANIM_armature_bonecoll_active_name_set(bArmature *armature, const char *name)
void ANIM_armature_bonecoll_show_from_ebone(bArmature *armature, const EditBone *ebone)
static MaybeConstBoneCollection * bonecolls_get_by_name(blender::Span< MaybeConstBoneCollection * > bonecolls, const char *name)
void ANIM_armature_bonecoll_show_from_bone(bArmature *armature, const Bone *bone)
static void ancestors_visible_descendants_clear(bArmature *armature, BoneCollection *parent_bcoll)
BoneCollection * ANIM_armature_bonecoll_get_by_name(bArmature *armature, const char *name)
static int bonecoll_insert_as_child(bArmature *armature, BoneCollection *bcoll, const int parent_index)
bool ANIM_bonecoll_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
static void liboverride_recursively_add_children(bArmature *armature_dst, const bArmature *armature_src, const int parent_bcoll_dst_index, const BoneCollection *parent_bcoll_src)
int ANIM_armature_bonecoll_move_before_after_index(bArmature *armature, const int from_index, int to_index, const MoveLocation before_after)
bool ANIM_armature_bonecoll_unassign_editbone(BoneCollection *bcoll, EditBone *ebone)
static void bonecoll_ensure_name_unique(bArmature *armature, BoneCollection *bcoll)
void ANIM_armature_bonecoll_assign_active(const bArmature *armature, EditBone *ebone)
bool ANIM_armature_bonecoll_assign(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_remove(bArmature *armature, BoneCollection *bcoll)
bool ANIM_armature_bonecoll_unassign(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_unassign_all(Bone *bone)
void ANIM_bonecoll_show(bArmature *armature, BoneCollection *bcoll)
void ANIM_armature_bonecoll_show_from_pchan(bArmature *armature, const bPoseChannel *pchan)
void ANIM_armature_bonecoll_active_runtime_refresh(bArmature *armature)
static int bonecoll_child_number(const bArmature *armature, const int parent_bcoll_index, const int bcoll_index)
static void ancestors_visible_descendants_update(bArmature *armature, BoneCollection *parent_bcoll)
static BoneCollection * copy_and_update_ownership(const bArmature *armature_dst, const BoneCollection *bcoll_to_copy)
BoneCollection * ANIM_bonecoll_new(const char *name)
bool ANIM_armature_bonecoll_is_visible_effectively(const bArmature *armature, const BoneCollection *bcoll)
void ANIM_armature_bonecoll_name_set(bArmature *armature, BoneCollection *bcoll, const char *name)
static void add_reference(Bone *bone, BoneCollection *bcoll)
static bool bcoll_list_contains(const ListBase *collection_refs, const BoneCollection *bcoll)
void ANIM_armature_bonecoll_show_all(bArmature *armature)
void ANIM_armature_bonecoll_solo_set(bArmature *armature, BoneCollection *bcoll, const bool is_solo)
void ANIM_bonecoll_free(BoneCollection *bcoll, const bool do_id_user_count)
void ANIM_armature_bonecoll_reconstruct(bArmature *armature)
bool ANIM_armature_bonecoll_contains_active_bone(const bArmature *armature, const BoneCollection *bcoll)
bool ANIM_bone_in_visible_collection(const bArmature *armature, const Bone *bone)
int ANIM_armature_bonecoll_get_index_by_name(bArmature *armature, const char *name)
static void add_reverse_pointers(BoneCollection *bcoll)
BoneCollection * ANIM_armature_bonecoll_new(bArmature *armature, const char *name, const int parent_index)
static void armature_bonecoll_active_clear(bArmature *armature)
bool ANIM_armature_bonecoll_assign_editbone(BoneCollection *bcoll, EditBone *ebone)
Internal C++ functions to deal with bone collections. These are mostly here for internal use in bone_...
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
constexpr int64_t size() const
Definition BLI_span.hh:252
#define printf(...)
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
int count
void *(* MEM_reallocN_id)(void *vmemh, size_t len, const char *str)
Definition mallocn.cc:40
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void bonecolls_rotate_block(bArmature *armature, const int start_index, const int count, const int direction)
void bonecolls_debug_list(const bArmature *armature)
void bonecoll_unassign_and_free(bArmature *armature, BoneCollection *bcoll)
int bonecolls_find_index_near(bArmature *armature, BoneCollection *bcoll, const int index)
void bonecolls_move_to_index(bArmature *armature, const int from_index, const int to_index)
blender::Map< BoneCollection *, BoneCollection * > ANIM_bonecoll_array_copy_no_membership(BoneCollection ***bcoll_array_dst, int *bcoll_array_dst_num, BoneCollection **bcoll_array_src, int bcoll_array_src_num, bool do_id_user)
bool armature_bonecoll_is_root(const bArmature *armature, int bcoll_index)
static void ANIM_armature_foreach_bone(ListBase *bones, CB callback)
void bonecolls_copy_expanded_flag(Span< BoneCollection * > bcolls_dest, Span< const BoneCollection * > bcolls_source)
int armature_bonecoll_child_number_find(const bArmature *armature, const ::BoneCollection *bcoll)
bool bonecoll_has_children(const BoneCollection *bcoll)
int armature_bonecoll_find_index(const bArmature *armature, const ::BoneCollection *bcoll)
bool armature_bonecoll_is_descendant_of(const bArmature *armature, int potential_parent_index, int potential_descendant_index)
int armature_bonecoll_find_parent_index(const bArmature *armature, int bcoll_index)
int armature_bonecoll_child_number_set(bArmature *armature, ::BoneCollection *bcoll, int new_child_number)
void ANIM_bonecoll_array_free(BoneCollection ***bcoll_array, int *bcoll_array_num, bool do_id_user)
int armature_bonecoll_move_to_parent(bArmature *armature, int from_bcoll_index, int to_child_num, int from_parent_index, int to_parent_index)
bool armature_bonecoll_is_child_of(const bArmature *armature, int potential_parent_index, int potential_child_index)
const char * name
struct BoneCollection * bcoll
struct IDProperty * system_properties
struct IDProperty * prop
Bone_Runtime runtime
ListBase bone_collections
char name[258]
Definition DNA_ID.h:432
void * first
struct BoneCollection * active_collection
struct BoneCollection ** collection_array
char active_collection_name[64]
struct GHash * bonehash
struct EditBone * act_edbone
ListBase * edbo
struct bArmature_Runtime runtime
struct Bone * bone
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)