In this article we’ll take a look at how to recursively crawl through a hierarchy of objects in CINEMA 4D. Starting with a very basic and simple function, we will extend the functionality step by step until we have a function that can do anything with a hierarchy, using callback functions.
The easiest way to iterate a whole hierarchy of nested objects is a recursive function.
Basics
Let’s start with just a simple recursive iteration…
C++
The function
// Recurses a hierarchy, starting from op void RecurseHierarchy(BaseObject* op) { while (op) { GePrint(op->GetName()); RecurseHierarchy(op->GetDown()); op = op->GetNext(); } }
Usage
BaseDocument *doc = GetActiveDocument(); if (doc) { // Iterate all objects in the document RecurseHierarchy(doc->GetFirstObject()); }
Python
The function
# Recurses a hierarchy, starting from op def recurse_hierarchy(op): while op: print op.GetName() recurse_hierarchy(op.GetDown()) op = op.GetNext()
Usage
doc = c4d.GetActiveDocument() if doc: # Iterate all objects in the document recurse_hierarchy(doc.GetFirstObject())
Counting the objects
Here, we simply count the objects that are iterated with GetNext(), add the result of the recursion to the counter and return the counter value at the end of the function.
C++
The function
// Recurses a hierarchy, starting from op // Returns number of iterated objects LONG RecurseHierarchy(BaseObject* op) { LONG count = 0; while (op) { GePrint(op->GetName()); count++; count += RecurseHierarchy(op->GetDown()); op = op->GetNext(); } return count; }
Usage
BaseDocument *doc = GetActiveDocument(); if (doc) { // Count all objects in the document LONG count = RecurseHierarchy(doc->GetFirstObject()); GePrint("Iterated " + LongToString(count) + " objects."); }
Python
The function
# Recurses a hierarchy, starting from op # Returns number of iterated objects def recurse_hierarchy(op): count = 0 while op: print op.GetName() count += 1 count += recurse_hierarchy(op.GetDown()) op = op.GetNext() return count
Usage
doc = c4d.GetActiveDocument() if doc: # Count all objects in the document count = recurse_hierarchy(doc.GetFirstObject()) print "Iterated " + str(count) + " objects."
Using a callback function
Currently, our function always prints out the name of each found object. What, if we want to do something different with the objects? Of course, we could write another function that performs the same iteration, and then we just exchange the printing part by something else. But that way, in a bigger project, we would easily end up with several iteration functions that all contain almost the same code. Not very efficient.
Using a callback function, we are able to tell the iteration function what to do with each object. That way, we can have one function for the hierarchy iteration and any number of separate functions for processing the objects.
In addition to that, let’s increase the counter only if the callback function returns True.
C++
The function
// Type definition for callback function typedef Bool (*IterationCallback)(BaseObject *op); // Prints the name of op to the console and returns TRUE if successful Bool PrintObjectName(BaseObject* op) { if (!op) return FALSE; GePrint(op->GetName()); return TRUE; } // Renames op and returns TRUE if successful Bool RenameObject(BaseObject* op) { if (!op) return FALSE; op->SetName(op->GetName() + " RENAMED"); return TRUE; } // Returns TRUE if op has at least one child Bool HasChildren(BaseObject* op) { if (!op) return FALSE; if (op->GetDown()) return TRUE; return FALSE; } // Recurses a hierarchy, starting from op // Returns number successful callbacks LONG RecurseHierarchy(BaseObject* op, IterationCallback callback) { LONG count = 0; while (op) { if (callback(op)) count++; count += RecurseHierarchy(op->GetDown(), callback); op = op->GetNext(); } return count; }
Usage
BaseDocument *doc = GetActiveDocument(); if (doc) { // Iterate all objects in the document LONG count = RecurseHierarchy(doc->GetFirstObject(), PrintObjectName); GePrint("Iterated " + LongToString(count) + " objects."); // Rename all objects in the document count = RecurseHierarchy(doc->GetFirstObject(), RenameObject); GePrint("Renamed " + LongToString(count) + " objects."); // Count all objects in the document that have children count = RecurseHierarchy(doc->GetFirstObject(), HasChildren); GePrint(LongToString(count) + " objects have children."); }
Python
The function
# Prints the name of op to the console and returns True if successful def print_object_name(op): if not op: return False print op.GetName() return True # Renames op and returns True if successful def rename_object(op): if not op: return False op.SetName(op.GetName + " RENAMED") return True # Returns True if op has at least one child def has_children(op): if not op: return False if op.GetDown(): return True return False # Recurses a hierarchy, starting from op # Returns number successful callbacks def recurse_hierarchy(op, callback): count = 0 while op: if callback(op): count += 1 count += recurse_hierarchy(op.GetDown()) op = op.GetNext() return count
Usage
doc = c4d.GetActiveDocument() if doc: # Print the names all objects in the document count = recurse_hierarchy(doc.GetFirstObject(), print_object_name) print "Iterated " + str(count) + " objects."; # Rename all objects in the document count = recurse_hierarchy(doc.GetFirstObject(), rename_object) print "Renamed " + str(count) + " objects."; # Count all objects in the document that have children count = recurse_hierarchy(doc.GetFirstObject(), has_children) print str(count) + " objects have children."
Further notes
Also read the article about non-recursive hierarchy iteration.