Hello! I have a list of joint controls that I want to sort by hierarchy.
My current list prints as:
[u’wrist’, u’shoulder’, u’forearm’, u’elbow’]
I would like to sort my list by hierarchy…
[u’shoulder’, u’elbow’, u’forearm’, u’wrist’]
Is there a way to do this?
chalk
August 26, 2019, 3:08am
2
Hi @hanaharu ,
Easiest way - you could get the hierarchy then reference into it:
hierarchy = cmds.listRelatives(root, allDescendents=True, type="joint")
filtered_list = filter(lambda n: n in my_list, hierarchy)
If not you’ll need to re-arrange the list by parent/child relatives.
1 Like
In most cases the following should pull you through:
def get_node_depth(node):
"""
Returns scene depth of a node.
"""
depth = 0
if node in (None, ''):
return depth
while True:
depth += 1
node = cmds.listRelatives(node, parent=True)
if node is None:
break
return depth
sorted_nodes = sorted(nodes, key=get_node_depth)
2 Likes
The classic cheat is
hierarchy = sorted(cmds.ls(my_list_of_things, long=True))
because the long
flag will prepend the whole path to each item. Then sorting the list alphabetically gives you the proper hierarchy. It’s good practice to use long names by default anyway, since it gets you out of trouble if you ever have leaf nodes with the same name.
6 Likes
chalk
August 26, 2019, 6:14pm
5
If you need to do arbitrary sorting, i.e. you’re dictating the order yourself -1 being no parent:
import operator
names = ["chest", "hips", "spine", "rightArm", "leftShoulder", "rightShoulder", "leftArm", "pelvis"]
indices = [2, -1, 1, 5, 0, 0, 4, -1] # The order that names are parented in -1 being no parent.
counts = []
for i in indices:
idx = i
count = 0
while idx != -1:
count += 1
idx = indices[idx]
counts.append(count)
count_pairs = dict(zip(names, counts))
sorted_count_pairs = sorted(
count_pairs.items(), key=operator.itemgetter(1))
order = []
for item in sorted_count_pairs:
if item[1] != 0:
parent = names[indices[names.index(item[0])]]
order.insert(order.index(parent)+1, item[0])
else:
order.append(item[0])
print order
# >> ['pelvis', 'hips', 'spine', 'chest', 'rightShoulder', 'rightArm', 'leftShoulder', 'leftArm']
Thanks to @Klaudikus for the while loop idea.
2 Likes
You can also sort the list by counting how many ‘|’ there are in your strings, as this is the hierarchy sign in long names. Pretty usefull if your list already exists or if you want to reverse the order:
list0 = [u'|group6',
u'|group6|group4',
u'|group6|group4|group3|group2|group1|null1',
u'|group6|group4|group3',
u'|group6|group4|group3|group2',
u'|group6|group4|group3|group2|group1',
u'|group6|group5',
u'|group6|group5|null2',
u'|group6|null3']
list0 = sorted(list0, key=lambda x: x.count('|'), reverse=1)
result = [u'|group6|group4|group3|group2|group1|null1',
u'|group6|group4|group3|group2|group1',
u'|group6|group4|group3|group2',
u'|group6|group4|group3',
u'|group6|group5|null2',
u'|group6|group4',
u'|group6|group5',
u'|group6|null3',
u'|group6']
1 Like
chalk:
names = [“chest”, “hips”, “spine”, “rightArm”, “leftShoulder”, “rightShoulder”, “leftArm”, “pelvis”] indices = [2, -1, 1, 5, 0, 0, 4, -1] # The order that names are parented in -1 being no parent.
# >> ['pelvis', 'hips', 'spine', 'chest', 'rightShoulder', 'rightArm', 'leftShoulder', 'leftArm']
You can write this as
[v for k, v in sorted(zip(indices, names))]
However I like @BenWall ’s idea of using long names and sorting on pipes which takes the possiblity of index errors out of the picture.
1 Like