C4D Python Snippet: Navigating the Hierarchy

Navigating the hierarchy with Python in C4D Recently I've been doing some Cinema 4D hierarchy navigation directly in Python, accessing objects that are children of a known start object (maybe set as user data or in an XPresso node). It's been a handy way to get to the properties of lots of similar objects that aren't easily manipulated by Mograph effectors. Read on to find out what I've discovered!

The Basics
Cinema 4D has a bunch of Python functions to help get from one object to another based on their relationship in the object manager hierarchy. These are GetNext(), GetPred(), GetUp(), GetDown() and GetDownLast(). Each of these functions returns another object that is related to the current one, for example:
1parent_object = start_object.GetUp()
This bit of code sets 'parent_object' to be the item in the hierarchy directly above 'start_object' - its parent in the object manager. If 'start_object' has no parent (eg if it isn't inside a null or another object in your project), the function GetUp() will return 'None' - then if you try to do something with 'parent_object' like change its scale you'll get an error. The other functions work the same way - if they can't find anything in the requested position, they'll return 'None'.

More examples
Ok now I'll run through how to get to some of the objects in this hierarchy, imagining that our start object is the cube.
Object manager hierarchy example 1
For these examples to work, your Python script would need to already know what object the variable 'cube' refers to - you could set it as a 'Link' input in an XPresso Python node.
1torus = cube.GetPred() #GetPred() gets the previous item at the same level of the hierarchy
2 sphere = cube.GetDown() #this gets the first child of an object
3 capsule = cube.GetDown().GetNext() #GetNext() gets the next item at the same level of the hierarchy. Since we've already found the sphere, you could also do capsule = sphere.GetNext()
4 disc = cube.GetDown().GetNext().GetDown()
5 cylinder = cube.GetDownLast() #this one grabs the last child of any object, you could also get to this cylinder using cube.GetDown().GetNext().GetNext()
6 pyramid = cube.GetNext()
7 tube = cube.GetNext().GetDown()
8 #and finally, if you wanted to get to the cube from one of its children, you'd use GetUp() again, eg:
9 cube = capsule.GetUp()
It's handy that you can keep attaching more 'Get' functions in sequence to navigate more complex relationships - but this can get unwieldy.

Complex hierarchies
Object manager hierarchy example 2
Imagine trying to get to the sphere in this hierarchy starting from the pyramid using the methods I just described. Your code would look like this:
1sphere = pyramid.GetDown().GetDown().GetDown().GetNext().GetDown().GetDown() #I think!
Where are we? I'm lost in a get trance now. If you make a mistake anywhere along the way with a long sequence like this, you'll get an error something like this: "AttributeError: 'NoneType' object has no attribute 'GetDown'". Without any feedback on where you went wrong this can be a bit tedious to fix. In the example above you could just link to the sphere directly - but if you were trying to loop through tens or hundreds of pyramids this might not be an option.

So, to make things a bit cleaner and easier I made this function (feel free to use this in your own scripts if you think it'll help you out as well - just paste it in somewhere before your 'main' function):
1def getObjectByAddress(startobject, address):
2     if startobject==None:
3         startobject = doc.GetFirstObject() #get the first object in the project if none is given
5     address = address.lower() #makes all characters lower case, so you can use capital letters when calling the function if you'd prefer
6     result = startobject
7     for i in xrange(len(address)): #cycle through all the letters in the address string, testing which relationship to get next
8         if(address[i]=='d'):
9             result = result.GetDown()
10         elif(address[i]=='n'):
11             result = result.GetNext()
12         elif(address[i]=='u'):
13             result = result.GetUp()
14         elif(address[i]=='p'):
15             result = result.GetPred()
16         elif(address[i]=='l'):
17             result = result.GetDownLast()
18         if result==None: #if nothing is found print a message telling us where we got to before returning 'None'
19             print 'Nothing found at position ' + str(i+1) + ' in address "' + address + '"'
20             return None
21     return result #if we have successfully found something, return it
Using this function to find the sphere from the pyramid we'd use:
1sphere = getObjectByAddress(pyramid, 'dddndd')
The first argument is the starting object, and the second argument is a string of letters defining the relationships to search for. Use 'd' for GetDown(), 'n' for GetNext(), 'u' for GetUp(), 'p' for GetPred() and 'l' for GetDownLast(). The string can be as long as you like, and the function will ignore any other characters (so you could add spaces or dashes to organise really long searches).

If something goes wrong along the way it will still return 'None' like the built in get functions, but it will also print to the console the point in your address string where it couldn't find anything. Finally - if you pass 'None' as the startobject, the function will start with the first item in your object manager, so you can use it to find objects by their absolute position within your project (although I've personally found it more useful for finding things relative to lists of other selected objects that I'm looping over).

That's it for now, happy hierarchy navigating!
Posted February 27, 2013