Green Cell
Green Cell

Reputation: 4777

Testing if an object is dependent to another object

Is there a way to check if an object is dependent via parenting, constraints, or connections to another object? I would like to do this check prior to parenting an object to see if it would cause dependency cycles or not.

I remember 3DsMax had a command to do this exactly. I checked OpenMaya but couldn't find anything. There is cmds.cycleCheck, but this only works when there currently is a cycle, which would be too late for me to use.

The tricky thing is that these 2 objects could be anywhere in the scene hierarchy, so they may or may not have direct parenting relationships.


EDIT

It's relatively easy to check if the hierarchy will cause any issues:

children = cmds.listRelatives(obj1, ad = True, f = True)
if obj2 in children:
    print "Can't parent to its own children!"

Checking for constraints or connections is another story though.

Upvotes: 3

Views: 3717

Answers (2)

Green Cell
Green Cell

Reputation: 4777

This is not the most elegant approach, but it's a quick and dirty way that seems to be working ok so far. The idea is that if a cycle happens, then just undo the operation and stop the rest of the script. Testing with a rig, it doesn't matter how complex the connections are, it will catch it.

# 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!")

Upvotes: 1

theodox
theodox

Reputation: 12208

depending on what you're looking for, cmds.listHistory or cmds.listConnections will tell you what's coming in to a given node. listHistory is limited to a subset of possible connections that drive shape node changes, so if you're interested in constraints you'll need to traverse the listConnections for your node and see what's upstream. The list can be arbitrarily large because it may include lots of hidden nodes like like unit translations, group parts and so on that you probably don't want to care about.

Here's simple way to troll the incoming connections of a node and get a tree of incoming connections:

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]

Which should produce a nested list of input connections like

 ((u'pCube1_parentConstraint1',
   ((u'pSphere1',
     ((u'pSphere1_orientConstraint1', ()),
     (u'pSphere1_scaleConstraint1', ()))),)),
   (u'pCube1_scaleConstraint1', ()))

in which each level contains a list of inputs. You can then troll through that to see if your proposed change includes that item.

This won't tell you if the connection will cause a real cycle, however: that's dependent on the data flow within the different nodes. Once you identify the possible cycle you can work your way back to see if the cycle is real (two items affecting each other's translation, for example) or harmless (I affect your rotation and you affect my translation).

Upvotes: 2

Related Questions