Python Iterators for your Cinema 4D Scene
One thing you will do over and over again in scripting Cinema 4D is to go through your scene. Usually you go hunting for certain objects, tags or materials.
This is pretty straight forward using the BaseList2D class methods by
GetDown() but can get old pretty fast as you
are doing the same checking and safeguards over and over again.
Python - like many modern programming languages - has a neat iterator pattern that is engrained into the language. So instead of using Cinema 4D’s object model you can do it more comfortably by using Python in a more natural way.
Here is a quick and simple implementation to iterate over objects in your scene. Just pass in a BaseList2D derived object (any object in your scene) and the iterator will go through all it’s siblings and children - depth first.
class ObjectIterator : def __init__(self, baseObject): self.baseObject = baseObject self.currentObject = baseObject self.objectStack =  self.depth = 0 self.nextDepth = 0 def __iter__(self): return self def next(self): if self.currentObject == None : raise StopIteration obj = self.currentObject self.depth = self.nextDepth child = self.currentObject.GetDown() if child : self.nextDepth = self.depth + 1 self.objectStack.append(self.currentObject.GetNext()) self.currentObject = child else : self.currentObject = self.currentObject.GetNext() while( self.currentObject == None and len(self.objectStack) > 0 ) : self.currentObject = self.objectStack.pop() self.nextDepth = self.nextDepth - 1 return obj
next() make this a Python iterator. You can use it
like any other Python iterator in a
for loop like this:
import c4d from c4d import documents as docs if __name__ == '__main__': doc = docs.GetActiveDocument() obj = doc.GetFirstObject() scene = ObjectIterator(obj) for obj in scene: print scene.depth, scene.depth*' ', obj.GetName()
You can pass any object in your scene as argument to the
ObjectIterator constructor. Then
use it as you would use any iterable object in Python in a for loop to go through all its
siblings and children. Additionally, the ObjectIterator object has an attribute called ‘depth’
that allows you to check how deep into the hierarchy you are.
Once you got to the object you were looking for the next step might be to check its tags. The TagIterator provides the same iterable mechanism for tags like the ObjectIterator does for objects.
class TagIterator: def __init__(self, obj): currentTag = None if obj : self.currentTag = obj.GetFirstTag() def __iter__(self): return self def next(self): tag = self.currentTag if tag == None : raise StopIteration self.currentTag = tag.GetNext() return tag
To use the TagIterator just feed the object to the constructor and loop over the objects tags:
tags = TagIterator(obj) for tag in tags: print tag.GetTypeName()
To go through all the materials in your scene you can use the ObjectIterator as materials derived from BaseList2D as well. Materials don’t have child objects you can use a much simpler version of a BaseList2D iterator that simply ignores children and only goes through all its siblings.
class MaterialIterator: def __init__(self, doc): self.doc = doc self.currentMaterial = None if doc == None : return self.currentMaterial = doc.GetFirstMaterial() def __iter__(self): return self def next(self): if self.currentMaterial == None : raise StopIteration mat = self.currentMaterial self.currentMaterial = self.currentMaterial.GetNext() return mat
Just feed the current scene document to the iterator and off you go:
import c4d from c4d import documents as docs if __name__ == '__main__': doc = docs.GetActiveDocument() materials = MaterialIterator(doc) for mat in materials: print mat.GetName, " - ", mat.GetTypeName()
I hope this helps you get through your scene more comfortably and with less code.
Do you have any question? Drop me a note on Twitter @martinweber