Тестирование, если объект зависит от другого объекта
Есть ли способ проверить, зависит ли объект через родительские функции, ограничения или соединения с другим объектом? Я хотел бы сделать эту проверку перед созданием родительского объекта, чтобы увидеть, вызовет ли он циклы зависимости или нет.
Я помню, у 3DsMax была команда сделать это точно. Я проверил OpenMaya
но ничего не смог найти. Есть cmds.cycleCheck
, но это работает только тогда, когда в настоящее время есть цикл, который будет слишком поздно для меня, чтобы использовать.
Хитрость заключается в том, что эти 2 объекта могут находиться где угодно в иерархии сцены, поэтому они могут иметь или не иметь прямые родительские отношения.
РЕДАКТИРОВАТЬ
Относительно легко проверить, не вызовет ли иерархия какие-либо проблемы:
children = cmds.listRelatives(obj1, ad = True, f = True)
if obj2 in children:
print "Can't parent to its own children!"
Проверка на наличие ограничений или связей - другая история.
2 ответа
Это не самый элегантный подход, но это быстрый и грязный способ, который пока работает нормально. Идея состоит в том, что если происходит цикл, просто отмените операцию и остановите оставшуюся часть сценария. При тестировании на установке не имеет значения, насколько сложны соединения, она его поймает.
# Class to use to undo operations
class UndoStack():
def __init__(self, inputName = ''):
self.name = inputName
def __enter__(self):
cmds.undoInfo(openChunk = True, chunkName = self.name, length = 300)
def __exit__(self, type, value, traceback):
cmds.undoInfo(closeChunk = True)
# Create a sphere and a box
mySphere = cmds.polySphere()[0]
myBox = cmds.polyCube()[0]
# Parent box to the sphere
myBox = cmds.parent(myBox, mySphere)[0]
# Set constraint from sphere to box (will cause cycle)
with UndoStack("Parent box"):
cmds.parentConstraint(myBox, mySphere)
# If there's a cycle, undo it
hasCycle = cmds.cycleCheck([mySphere, myBox])
if hasCycle:
cmds.undo()
cmds.warning("Can't do this operation, a dependency cycle has occurred!")
В зависимости от того, что вы ищете, cmds.listHistory
или же cmds.listConnections
скажет вам, что входит в данный узел. listHistory
ограничено подмножеством возможных соединений, которые вызывают изменения формы узла, поэтому, если вы заинтересованы в ограничениях, вам нужно пройти через listConnections
для вашего узла и посмотреть, что вверх по течению. Список может быть сколь угодно большим, потому что он может включать в себя множество скрытых узлов, таких как перевод единиц, групповые части и т. Д., О которых вы, вероятно, не хотите заботиться.
Вот простой способ троллинга входящих соединений узла и получения дерева входящих соединений:
def input_tree(root_node):
visited = set() # so we don't get into loops
# recursively extract input connections
def upstream(node, depth = 0):
if node not in visited:
visited.add(node)
children = cmds.listConnections(node, s=True, d=False)
if children:
grandparents = ()
for history_node in children:
grandparents += (tuple(d for d in upstream(history_node, depth + 1)))
yield node, tuple((g for g in grandparents if len(g)))
# unfold the recursive generation of the tree
tree_iter = tuple((i for i in upstream(root_node)))
# return the grandparent array of the first node
return tree_iter[0][-1]
Который должен создавать вложенный список входных соединений, таких как
((u'pCube1_parentConstraint1',
((u'pSphere1',
((u'pSphere1_orientConstraint1', ()),
(u'pSphere1_scaleConstraint1', ()))),)),
(u'pCube1_scaleConstraint1', ()))
в котором каждый уровень содержит список входов. Затем вы можете просмотреть это, чтобы увидеть, включает ли предложенное вами изменение этот элемент.
Однако это не скажет вам, вызовет ли соединение реальный цикл: это зависит от потока данных в разных узлах. После того, как вы определили возможный цикл, вы можете вернуться назад, чтобы увидеть, является ли цикл реальным (например, два элемента, влияющие на перевод друг друга) или безвредным (я влияю на вашу ротацию, а вы - на мой перевод).