Iteration über Objekte

Aus CINEMA 4D Wiki

Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

Einführung

Es kommt häufig vor, dass man über alle Objekte in einer Szene iterieren muss, oder über alle Tags eines Objekts. Dieser Artikel zeigt ein Beispiel, wie eine solche Schleife zu realisieren wäre.

Iteration über Objekte

Hier wird über sämtliche Objekte der obersten Hierarchie-Ebene in der Szene iteriert. Das Name jedes Objekts wird in der Konsole ausgegeben.

Beispielcode C++:

void IterateObjects(BaseObject *op)
{
  // Schleife durchlaufen, solange op ein gültiges Objekt ist
  while (op)
  {
    // Als Beispiel den Namen des Objekts in der Konsole ausgeben
    GePrint(op->GetName());

    // Nächstes Objekt
    op = op->GetNext();
  }
}

Aufgerufen wird die Funktion z.B. so:

BaseDocument *doc = GetActiveDocument();
BaseObject *op = doc->GetFirstObject();

IterateObjects(op);

Rekursive Iteration über Objekte

In diesem Beispiel ruft die Funktion sich selbst erneut auf, um Unterobjekte (und deren Unterobjekte) ebenfalls zu iterieren. Als Ergebnis erscheinen die Namen sämtlicher Objekte in der Szene in der Konsole.

Beispielcode C++:

void IterateObjects(BaseObject *op)
{
  // Schleife durchlaufen, solange op ein gültiges Objekt ist
  while (op)
  {
    // Als Beispiel den Namen des Objekts in der Konsole ausgeben
    GePrint(op->GetName());

    // Wenn mindestens ein Unterobjekt existiert, Rekursion starten
    if (op->GetDown()) IterateObjects(op->GetDown());

    // Nächstes Objekt
    op = op->GetNext();
  }
}

Rekursive Iteration über Objekte und deren Tags

Hier wird die Funktion noch erweitert, um auch die Tags sämtlicher Objekte zu iterieren, und deren Namen in der Konsole auszugeben.

Da Tags keine Unter-Tags besitzen können, ist für die Iteration über die Tags eines Objekts keine Rekursion erforderlich.

Beispielcode C++:

void IterateObjectsAndTags(BaseObject *op)
{
  BaseTag *tag = NULL;

  // Schleife durchlaufen, solange op ein gültiges Objekt ist
  while (op)
  {
    // Als Beispiel den Namen des Objekts in der Konsole ausgeben
    GePrint(op->GetName());

    // Erstes Tag des Objekts ermitteln
    tag = op->GetFirstTag();

    // Schleife durchlaufen, solange tag ein gültiges Tag ist
    while (tag)
    {
      // Namen des Tags in der Konsole ausgeben
      GePrint("-> " + tag->GetName());

      // Nächstes Tag
      tag = tag->GetNext();
    }

    // Wenn mindestens ein Unterobjekt existiert, Rekursion starten
    if (op->GetDown()) IterateObjects(op->GetDown());

    // Nächstes Objekt
    op = op->GetNext();
  }
}

Objekte und Tags zählen

Möchte man nun wissen, wieviele Objekte und Tags im Zuge der Rekursion insgesamt durchlaufen wurden, steht man als unerfahrener Entwickler zunächst vor der Frage: Wie zähle ich sowas? Eine Variable innerhalb der Funktion macht keinen Sinn, denn die würde für jede Rekursions-Ebene neu initialisiert.

Die Lösung ist eine Referenz zu einer Zähler-Variable außerhalb der Rekursion. In diesem Beispiel sogar zwei Variablen, da sowohl Objekte als auch Tags gezählt werden sollen.

Beispielcode C++:

void IterateObjectsAndTags(BaseObject *op, LONG &ObjCounter, LONG &TagCounter)
{
  BaseTag *tag = NULL;

  // Schleife durchlaufen, solange op ein gültiges Objekt ist
  while (op)
  {
    // Als Beispiel den Namen des Objekts in der Konsole ausgeben
    GePrint(op->GetName());

    // Erstes Tag des Objekts ermitteln
    tag = op->GetFirstTag();

    // Schleife durchlaufen, solange tag ein gültiges Tag ist
    while (tag)
    {
      // Namen des Tags in der Konsole ausgeben
      GePrint("-> " + tag->GetName());

      // Nächstes Tag
      tag = tag->GetNext();

      // Tagzähler erhöhen
      TagCounter++;
    }

    // Wenn mindestens ein Unterobjekt existiert, Rekursion starten
    // Dabei wird die Referenz auf die Zähler-Variablen mit übergeben
    if (op->GetDown()) IterateObjects(op->GetDown(), ObjCounter, TagCounter);

    // Nächstes Objekt
    op = op->GetNext();

    // Objektzähler erhöhen
    ObjCounter++;
  }
}

Aufruf der Funktion:

BaseDocument *doc = GetActiveDocument();
BaseObject *op = doc->GetFirstObject();
LONG ObjCounter = 0;
LONG TagCounter = 0;

IterateObjects(op, ObjCounter, TagCounter);
GePrint("Es gibt insgesamt " + LongToString(ObjCounter) + " Objekte und " + LongToString(TagCounter) + " Tags in der Szene.");

Jetzt werden in der Konsole zuerst alle Objektnamen und die Namen der zugehörigen Tags ausgegeben, und am Ende die Gesamtanzahl der Objekte und Tags.

Objekte bestimmer Art iterieren

Manchmal möchte man vielleicht gar nicht alle Objekte in der Szene bearbeiten, sondern nur Objekte bestimmter Art, z.B. Null-Objekte. In dem Fall muss einfach der Typ jedes Objekts ermittelt und mit dem gesuchten Typ verglichen werden. Der folgende Code iteriert wieder sämtliche Objekte in der Szene, kümmert sich jedoch nur um Null-Objekte. Wird ein Null-Objekt gefunden, erscheint dessen Name in der Konsole, und der Objektzähler wird erhöht.

Beispielcode C++:

void IterateObjects(BaseObject *op, LONG ObjType, LONG &ObjCounter)
{
  // Schleife durchlaufen, solange op ein gültiges Objekt ist
  while (op)
  {
    if (op->GetType() == ObjType)
    {
      // Als Beispiel den Namen des Objekts in der Konsole ausgeben
      GePrint(op->GetName());

      // Objektzähler erhöhen
      ObjCounter++;
    }

    // Wenn mindestens ein Unterobjekt existiert, Rekursion starten
    if (op->GetDown()) IterateObjects(op->GetDown(), ObjTyp, ObjCounter);

    // Nächstes Objekt
    op = op->GetNext();
  }
}

Der Aufruf könnte folgendermaßen aussehen:

BaseDocument *doc = GetActiveDocument();
BaseObject *op = doc->GetFirstObject();
LONG ObjCounter = 0;

IterateObjects(op, Onull, ObjCounter);  // Anstelle von "Onull" kann auch die ID jedes beliebigen Objekts verwendet werden
GePrint("Es gibt insgesamt " + LongToString(ObjCounter) + " Objekte vom Typ " + LongToString(ObjType) + " in der Szene.");
Persönliche Werkzeuge