Recursive hierarchy iteration

by .

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.