Anim: add `bone_collection.bones_recursive` property

Add a `bone_collection.bones_recursive` property that returns the set of
bones assigned to that bone collection or any of its child collections.

This property is implemented in Python, as that made it considerably
simpler to create the set semantics.
This commit is contained in:
Sybren A. Stüvel 2024-01-02 15:57:42 +01:00
parent 99d43c9198
commit 65d25ed813
2 changed files with 32 additions and 5 deletions

View File

@ -538,6 +538,22 @@ class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
self.align_roll(matrix @ z_vec)
class BoneCollection(StructRNA, metaclass=StructMetaPropGroup):
__slots__ = ()
@property
def bones_recursive(self):
"""A set of all bones assigned to this bone collection and its child collections."""
bones = set()
collections = [self]
while collections:
visit = collections.pop()
bones.update(visit.bones)
collections.extend(visit.children)
return bones
def ord_ind(i1, i2):
if i1 < i2:
return i1, i2

View File

@ -116,24 +116,35 @@ class BoneCollectionTest(unittest.TestCase):
bcolls = self.arm.collections
bcoll_root = bcolls.new('root')
bcoll_child1 = bcolls.new('child1', parent=bcoll_root)
bcoll_child2 = bcolls.new('child2', parent=bcoll_root)
bcoll_child2 = bcolls.new('child2', parent=bcoll_child1)
# Add bones to the armature & assign to collections.
bone_dict = self.add_bones(self.arm_ob)
bcoll_root.assign(bone_dict['root'])
bcoll_child1_bone_names = ('child_L', 'child_L_L', 'child_L_R')
bcoll_child1_bone_names = {'child_L', 'child_L_L', 'child_L_R'}
for bone_name in bcoll_child1_bone_names:
bcoll_child1.assign(bone_dict[bone_name])
bcoll_child2_bone_names = ('child_R', 'child_R_L', 'child_R_R')
bcoll_child2_bone_names = {'child_R', 'child_R_L', 'child_R_R'}
for bone_name in bcoll_child2_bone_names:
bcoll_child2.assign(bone_dict[bone_name])
# Check that the `.bones` property returns the expected ones.
self.assertEqual([self.arm.bones['root']], list(bcoll_root.bones))
self.assertEqual(set(bcoll_child1_bone_names), {b.name for b in bcoll_child1.bones})
self.assertEqual(set(bcoll_child2_bone_names), {b.name for b in bcoll_child2.bones})
self.assertEqual(bcoll_child1_bone_names, {b.name for b in bcoll_child1.bones})
self.assertEqual(bcoll_child2_bone_names, {b.name for b in bcoll_child2.bones})
# Check that the `.bones_recursive` property returns the expected bones.
all_bones = set(self.arm.bones)
self.assertEqual(all_bones, set(bcoll_root.bones_recursive),
'All bones should have been assigned to at least one bone collection')
self.assertEqual(bcoll_child1_bone_names | bcoll_child2_bone_names,
{b.name for b in bcoll_child1.bones_recursive},
"All bones of child1 and child2 should be in child1.bones_recursive")
self.assertEqual(bcoll_child2_bone_names, {b.name for b in bcoll_child2.bones_recursive})
def test_bone_collection_armature_join(self):
other_arm_ob, other_arm = self.create_armature()