Query DG Evaluation Order??

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Search this Thread Display Modes
Old 02 February 2013   #1
Thumbs up Query DG Evaluation Order??

Any DG, DAG pros out there?

I'm trying to sort a list of objects based on their position in the dependency graph. Ive tried, listConnections, listRelatives, listHistory, Not having much luck so far.

To give a bit more detail, Im working with animation controls (transforms). Some are in hierarchies with each other, some are in groups that are constrained. I'm attempting to modify their positions/rotations, but Id like to do it relative to the DG, top to bottom.

Current problem, say a hand_control is under the hierarchy of the body_control. If I set the position of the hand_control first, then the body_control second, the hand_control will inherit the change in position (and thus, move it from where I had wanted it to be). So to avoid this, I need to figure out what needs to be set first. The mix of constraints, space switches, hierarchies in animation rigs, makes this difficult to sort out.

I was thinking, if there was someway to get a sort order of nodes in the DG, it would be easy to figure this out.

Thanks for any ideas!!
 
Old 02 February 2013   #2
if you use listRelatives with the allDescendents and fullPath flags, you can sort the list however you like.

you will get back something like:

|root
|root|hips
|root|hips|thigh_left
|root|hips|thigh_left|knee_left
|root|hips|thigh_right
|root|hips|thigh_right|knee_right

then you can split the name along "|" to get a list of all nodes, in order, that are higher.
 
Old 02 February 2013   #3
Hi rgkovach123,

That's what I was thinking first as well. Correct me if Im wrong, but listRelatives just deals with basic hierarchies, and doesn't take into account constraints, connected attributes, and space switches (DG stuff).

For example:

Say you have a body control under this hierarchy,
bodyRoot_grp|controls_grp|zero_grp|body_control

but the hand control is in a group that's parent constrained to 'body_control' rather than being in the hierarchy,
otherControls_grp|hand_control

Simply checking the hierarchy, wont give you a clear picture of what is being controlled by what.

The Dependency Graph keeps track of these kinds of relationships, just not sure how to query it.
 
Old 02 February 2013   #4
you will have to gather all the info yourself and work out what is important.
listRelatives will give you the heiracry, listConnections can tell you things like constraints.

there isn't a magic bullet command that gathers all the possible influences on a given attribute.
 
Old 02 February 2013   #5
Hi rgkovach123. I was hoping for a magic bullet! I had a look at dgTimer, as it seemed like it might have potential, but that was a complete mess.

So, I put together this script that does the check. Seems to work for my purposes of checking node interdependencies between animation controls.

Works as follows:
Input your A and B objects
maxIterations sets how many levels it will traverse, if it finds a match early on, it'll quit looking
printInfo=True will let you know how many iterations, and how long the whole thing took to process

Not sure how/if this works with other maya nodes. I'm using it mainly with transform nodes. I'd be curious to hear if it does though!

If anyone has a better idea, I'm all ears!


    def is_objectA_affected_by_objectB(objectA, objectB, maxIterations, printInfo):
    	#setup printInfo timer
    	if printInfo:
    		start = cmds.timerX()
    	
    	#set result to default false
    	result = False
    	
    	#convert objects to long names, in case theres duplicates
    	objectA = cmds.ls(objectA, long=True)
    	objectB = cmds.ls(objectB, long=True)
    
    	#place to store returned objects
    	storageBin = []
    	queryChildren = []
    	queryConnections = []
    	query = objectA
    
    	for i in range(maxIterations):
    		if query == []:
    			break
    		print i, query
    		queryChildren = list(set(cmds.ls(cmds.listRelatives(query, allParents=True, fullPath=True ), long=True)))
    		queryConnections = list(set(cmds.ls(cmds.listConnections(query, d=False, s=True), long=True)))
    		query = list(set(queryChildren + queryConnections))
    		storageBin = list(set(storageBin + query))
    		
    		#check if match is found
    		if objectB[0] in storageBin:
    			if printInfo:
    				print 'search took ' + str(i) + ' iterations'
    				result = True
    			#break loop
    			break
    
    	#print out timer info if needed		
    	if printInfo:
    		totalTime = cmds.timerX(startTime=start)
    		print ('total time to check dependency '), totalTime, ('seconds')
    
    	#return
    	return result
    
     

Last edited by backwheelbates : 02 February 2013 at 12:07 AM.
 
Old 02 February 2013   #6
i dont see how max iterations is factor at all? i must be missing something...

in your loop, "i" is never used? seems like one interation is all you need since every iteration would give you the same result?
 
Old 02 February 2013   #7
Hey rgkovach123,

Yes, the maxIterations are used (indirectly). The results of the listConnections, and listRelatives are fed back into itself each loop and are extended each time.

In the rig Im using, Ive found some controls are linked a few iterations away, and some are 15+ iterations away.

FYI, if you turn on the printInfo, it will display how many iterations were done to find a connection, if there's a connection at all.

edit:
You're partially right, the value 'i' isn't directly used. Its only there to control the loop, and to inform the user how many iterations were done.

Last edited by backwheelbates : 02 February 2013 at 12:14 AM.
 
Old 02 February 2013   #8
hmm, i must be missing something else, because you are not extending the lists each iteration?

shoulnd't it be something like:

conns = []
for i in range(10):
	newConns = cmds.listConnections('blah')
	if newConns:
		newConns = list(set(newConns))	   
		conns.extend(newConns)



anyway, you shouldn't need a max iterations, you could write a recursive function to get the entire list of connections


def getConns(node):
	conns = cmds.listConnections(node, d=False)
	if conns: 
		conns = list(set(conns))
		for c in conns:
			newConns = getConns(c)  
			if newConns:  
				 newConns = list(set(newConns))  
				 conns.extend(newConns)
	return conns


or if you want to search for a specific node in it's list:

def isConnected(node, target):
   	 conns = cmds.listConnections(node, d=False)
   	 if conns: 
       		 conns = list(set(conns))
		 if target in conns:
			 return True
		 else:
       			 for c in conns:
				 if isConnected(c, target):
					 return True   
	return False

Last edited by rgkovach123 : 02 February 2013 at 03:07 PM.
 
Old 02 February 2013   #9
Hi rgkovach123,

Check out this part:


query = list(set(queryChildren + queryConnections))
storageBin = list(set(storageBin + query))


The variable 'query', stores any newly returned nodes. This is what gets fed back into itself.
'storageBin' on the other hand, is where all the returned nodes get stored for later.

The idea is to separate the returned nodes to make looping more efficient. Only the RESULT of listConnections, and listRelatives are fed back into the loop. This is to avoid processing nodes that have already been inspected. Hope that makes sense.

I know, its confusing!
 
Old 02 February 2013   #10
i updated my previous post with some code samples, check em out.

it makes sense now what you are doing, it's a bit confusing though...
 
Old 02 February 2013   #11
Hey rgkovach123,

Thanks for your ideas. You're partially right about not needing a max iterations limit. I got rid of it, and I noticed that in some cases it was stuck in an infinity loop returning the same values over and over. So I added some code that looks for this. But I still have a feeling that in some weird situation where someone might have cyclical connections it could be a problem. So I've added an internal max iterations value and set it pretty high.

Heres the cleaned up code that seems to be working. Its MUCH faster than what I started out with.

Simply, you just feed it a list of objects and it will return the list sorted by whats happening in the dependency graph.



 for n in sortNodesBasedOnDG(cmds.ls(sl=True)):
     print n
 



With a couple of diagnostic flags for checking processing time, etc

 for n in sortNodesBasedOnDG(cmds.ls(sl=True), printInfo=True, returnShortNames=True):
     print n
 


Thanks again for all the ideas!!

*note, looks like the cgsociety.org forum software is adding random spaces into the code. So check for those first before you try to run this.

 ##################################################  ##################################################  ##
 def getClosestUpstreamNodeFromList(objectA, objectList, **kwargs):
 	#kwarg stuff
 	printInfo = False
 	if 'printInfo' in kwargs:
 		printInfo = kwargs.pop('printInfo')
 	
 	#probably not needed, but just in case there are any weird circular dependencies
 	maxIterations = 50
 	
 	#setup printInfo timer
 	if printInfo:
 		start = cmds.timerX()
 	
 	#set result to default false
 	result = []
 	
 	#convert objects to long names, in case theres duplicates
 	objectA = cmds.ls(objectA, long=True)
 	objectList = cmds.ls(objectList, long=True)
 	
 	#just incase objectA happens to be in the object list, get rid of it
 	if objectA[0] in objectList:
 	    objectList.pop(objectList.index(objectA[0]))
 
 	#place to store returned objects
 	storageBin = []
 	queryChildren = []
 	queryConnections = []
 	query = objectA
 	
 	#declare i for later
 	i=0
 
 	for i in range(maxIterations):
 		#break if no new results returned
 		#print query
 		if query == []:
 		    break
 		
 		#gather DAG and DG connections
 		queryChildren = list(set(cmds.ls(cmds.listRelatives(query, allParents=True, fullPath=True ), long=True)))
 		queryConnections = list(set(cmds.ls(cmds.listConnections(query, d=False, s=True), long=True)))
 		
 		#break if the query is returning the exact same thing as last time, aka no change
 		if sorted(list(set(query))) == sorted(list(set(queryChildren + queryConnections))):
 			break
 		#new data for next loop
 		else:
 			query = list(set(queryChildren + queryConnections))
 		
 		#store all data
 		storageBin = list(set(storageBin + query))
 		
 		#check if match is found
 		matchingNodes = list(set(objectList).intersection(set(storageBin))  )
 		if matchingNodes:
 			if printInfo:
 				
 				print 'matching nodes', matchingNodes
 
 			#output results, breaks
 			result = matchingNodes
 			break
 
 	#print out timer info if needed        
 	if printInfo:
 		totalTime = cmds.timerX(startTime=start)
 		print 'search took ' + str(i) + ' iterations'
 		print ('total time to check dependency '), totalTime, ('seconds')
 
 	#return
 	return result
 
 
 ##################################################  ##################################################  ##
 def sortNodesBasedOnDG(objectList, **kwargs):
 	#kwarg stuff
 	returnShortNames = False
 	if 'returnShortNames' in kwargs:
 		returnShortNames = kwargs.pop('returnShortNames')
 
 	printInfo = False
 	if 'printInfo' in kwargs:
 		printInfo = kwargs.pop('printInfo')
 	
 	#setup printInfo timer
 	if printInfo:
 		start = cmds.timerX()
 		
 	#get long names for objects
 	objectList = cmds.ls(objectList, long=True)
 
 	#just dump everyting into the sorted list
 	sortedListItems = []
 	
 	#gather nearest upstream nodes list
 	upstreamNodesDict = {}
 	for object in objectList:
 		upstreamNodesDict[object] = getClosestUpstreamNodeFromList(object,objectList)
 
 	#go through list and sort it out
 	upstreamNodes = []
 	#loop until everything has been added
 
 	for object in objectList:
 		
 		#get upstream nodes list
 		upstreamNodes = upstreamNodesDict[object]
 
 		#check if any of the upstream nodes are already in the sortedListItems
 		#if so get the index number
 		index = []
 		#in the case, there are no upstream nodes
 		if (((upstreamNodes==[]) and (object not in sortedListItems))):
 			#put at top of list
 			#print out timer info if needed        
 			if printInfo:
 				print [object], '[Top Node]'
 
 			sortedListItems.insert(0, object)
 		#if there ARE parents, but neither they nor the object is in the list yet
 		elif (upstreamNodes!=[]) and (object not in sortedListItems) and (list(set(sortedListItems).intersection(set(upstre  amNodes)))==[]):
 			#append to end of list
 			sortedListItems.append(object)
 		#go through the rest of the objects and move them to the proper place
 		else:
 			for sortedListItem in sortedListItems:
 				for upstreamNode in upstreamNodes:
 					if upstreamNode == sortedListItem:
 						#of object is not already in the sortedListItems, then add it
 						if object not in sortedListItems:
 							sortedListItems.insert(sortedListItems.index(sorte  dListItem)+1, object)
 
 	#this returns more userfriendly short names
 	if returnShortNames:
 		for i in range(len(sortedListItems)):
 			sortedListItems[i]=cmds.ls(sortedListItems[i], long=False)
 	
 	#print out timer info if needed        
 	if printInfo:
 		totalTime = cmds.timerX(startTime=start)
 		print ('total time to check dependencies '), totalTime, ('seconds')
 	
 	return sortedListItems
 

Last edited by backwheelbates : 02 February 2013 at 05:29 PM.
 
Old 02 February 2013   #12
This seems like a very complicated way of creating a less efficient version of MItDependencyGraph.

Quote: Current problem, say a hand_control is under the hierarchy of the body_control. If I set the position of the hand_control first, then the body_control second, the hand_control will inherit the change in position (and thus, move it from where I had wanted it to be). So to avoid this, I need to figure out what needs to be set first.


1. Set transform 1.
2. Create parent constraint.
3. Set transform 2
4. delete parent constraint.

Last edited by Robert Bateman : 02 February 2013 at 04:56 AM.
 
Old 02 February 2013   #13
Thanks Robert!!

I completely agree, I always thought there must be a built in way of doing this. I was hoping to avoid using constraints, but your idea of using MItDependencyGraph looks perfect! I read the docs and that seems to do EXACTLY what I was trying to achieve.

I'm not familiar with maya's API or how to use it. Any advice on how to call this from python?
 
Old 02 February 2013   #14
Found a bit of a start here:
http://mayamel.tiddlyspot.com

This returns all upstream connections (aka, list connections), but its not returning parents(aka list relatives) as well. Thanks for any ideas?


import maya.OpenMaya as om

def example_MItDependencyGraph(obj):

	# Convert a node name to a MObject, to pass to our MItDependencyGraph class:
	nodeName = obj
	selList = om.MSelectionList()
	selList.add(nodeName)
	node = om.MObject()
	selList.getDependNode(0, node)
	
	upstreamNodesList = []
	# Iterate through all incoming connections to our node:
	dependGraphIter = om.MItDependencyGraph(node,

											om.MItDependencyGraph.kUpstream,
											om.MItDependencyGraph.kNodeLevel)
	print "%s: All Incoming Connected Nodes"%nodeName
	while not dependGraphIter.isDone():
		currentItem = dependGraphIter.currentItem() # MObject
		currentItemFunc = om.MFnDependencyNode(currentItem)
		name = currentItemFunc.name()
		#print "\t", name
		upstreamNodesList.append(name)
		dependGraphIter.next()
	
	return upstreamNodesList
 
Old 02 February 2013   #15
MItDag is the DAG counterpart to MItDependencyGraph.

I'm still quite confused over what you're trying to achieve. The order of evaluation is dependent on the order in which the attributes are pulled which I don't believe you will have any visibility into as the attributes could be pulled from any number of sources (refresh being the most significant of them).

For your original example problem:

Quote: Current problem, say a hand_control is under the hierarchy of the body_control. If I set the position of the hand_control first, then the body_control second, the hand_control will inherit the change in position (and thus, move it from where I had wanted it to be). So to avoid this, I need to figure out what needs to be set first. The mix of constraints, space switches, hierarchies in animation rigs, makes this difficult to sort out.


Couldn't you just in that case disable the "Inherit Transform" attribute on the hand_control so that it no longer inherits its parent's transform? That seems to be what you're asking for.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 01:04 AM.


Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.