Turtle Grafik

 

 

Dieses Kapitel sollten Sie bearbeiten, falls der bisherige Kurs für Sie zu schnell gegangen ist. Mit Hilfe der Turtle Grafik sollen Sie Ablaufstrukturen und OOP trainieren. Aber: Turtle Grafik macht auch Spass! Es gibt sie für fast alle Programmiersprachen als sehr effizientes Trainings - Tool.

Die Idee der Trutle Grafik ist sehr einfach: Eine Turtle (Schildkröte) bewegt sich programmgesteuert auf dem Bildschirm. Sie kann einfache Anweisungen ausführen: vorwärts, nach rechts drehen, etc. Und das Schöne ist: Sie kann eine Spur hinterlassen, so dass ihr Weg zu einer Grafik wird.

Damit wir das Ganze nicht selber programmieren müssen, übernehmen wir eine Klassenbibliothek von Ägidius Plüss, welche sein wunderschönes Buch "Java - exemplarisch" begleitet und welche auf der Website www.aplu.ch zu finden ist. Sie sehen so auch gleich, wie man Klassenbibliotheken verwendet, welche nicht zum Standard SDK gehören:

  • Am besten kopieren Sie die Bibliothek (aplu.jar) auf Ihr P: in Ihren Java Ordner.

  • Erstellen Sie jetzt ein neues Projekt z.B. mit dem Namen TurtleTest vom Typ "Empty Project".

  • Ein solches Projekt enthält noch gar keine Datei. Klicken Sie im "File View" Fenster mit der rechten Taste auf Ihr Projekt und wählen Sie "Add...", "New File ...", geben Sie der Datei den Namen TurtleTest.java und speichern Sie sie hinter "Location" in Ihrem Projektordner.

  • Die Quelldatei ist noch ganz leer, hier brauchen wir die Template Hilfe von JCreator nicht, unser Programm ist nämlich sehr einfach:

    import ch.aplu.turtle.*;
    class TurtleTest {
      public static void main(String[] args) {
        Turtle fritz = new Turtle();
      }
    }
  • Wenn Sie das übersetzen, gibt's haufenweise Fehlermeldungen, denn das Package ch.aplu.turtle wird nicht gefunden. Dem helfen Sie ab, indem Sie JCreator sagen, wo er die Bibliothek findet: Menü "Project", "Project Properties...", "Required Libraries", "New...", "Classes", "Add", "Add Archive". Jetzt suchen Sie Ihr aplu.jar und wählen es aus. Geben Sie im Fenster "Set Library" der Bibliothek noch einen Namen, z.B. Turtle und setzen Sie im Fenster "Project Properties" den Haken vor Turtle. Leider müssen Sie diese Einstellung in der Schule nach jedem Computerneustart machen - der HDDSheriff lässt grüssen!.

 

 
  • Übersetzen Sie - das sollte jetzt klappen - und führen Sie aus: Die Turtle sitzt mitten in ihrem "Playground" und wartet auf Befehle!

Haben Sie bemerkt, wie schön objektorientiert alles läuft? Es gibt eine Klasse Turtle, von der Sie eine Instanz mit Namen fritz gemacht haben. Bei der Instanzierung von fritz ist für uns unsichtbar im Hintergrund sehr viel passiert: fritz hat sich nämlich einen Playground, also ein grafisches Fenster, gemacht. Selbstverständlich hat die Klasse Turtle Methoden, welche fritz bekannt sind, z.B. forward(), right() und left().

  • Ergänzen Sie Ihr Programm:

    Turtle fritz = new Turtle();
    fritz.forward(20);
    fritz.right(90);
    fritz.forward(50);

    und lassen Sie es laufen. Ich denke, dass Sie begriffen haben wie's geht.

  • Schaffen Sie eine Treppe mit 5 Schritten? Und eine solche mit 100 Schritten, so dass eine for - Schlaufe nötig ist? 

 

 
  • Oder wie wär's mit einer patriotischen Turtle? Siehe Bild rechts!

  • Fritz sollte doch nicht ganz allein sein - wie wär's wenn sich ida dazu gesellen würde: eine zweite Instanz von Turtle!

  • Hoppla, ida hat sich ihren eigenen Playground gemacht. Turtle hat aber noch weitere Konstruktoren:

    Turtle ida = new Turtle(fritz);

    Als Parameter für den Konstrukor von ida geben wir an, in wessen Playground sie spielen soll!

  • ida sollte nicht gleich aussehen wie Fritz, also verwenden wir einen weiteren Konstruktor von Turtle:

    Turtle ida = new Turtle(fritz,java.awt.Color.yellow);

    Selbstverständlich können Sie auch java.awt.* importieren. Das lohnt sich, falls es in Zukunft noch farbiger werden soll.

Falls Sie wissen möchten, was eine Turtle sonst noch kann, schauen Sie in der Dokumentation zur Bibliothek nach und suchen Sie die Klasse Turtle.

Zurück zum Schweizerkreuz und zum Thema OOP! Sie haben wohl die vier Arme des Kreuzes schön einzeln ausprogrammiert. Das geht bei vier Armen, wird aufwändig bei 100 und ist überhaupt unschön. Unsere Turtle sollte doch einfach die Eigenschaft haben, dass sie einen Arm zeichnen kann. Eine Eigenschaft eines Objektes ist in Java ja eine Methode. Schreiben wir also eine Methode, welche einen Arm eines Kreuzes zeichnet und rufen wir diese Methode viermal auf. Wenn Sie das versuchen, dann stolpern Sie über eine der häufigsten Fehlermeldungen in Java:

"non-static method arm() cannot be referenced from a static context"

heisst es dann bei der Übersetzung, falls Ihre Methode arm() heisst. Die Methode main() ist static, das heisst, sie wird in der Klasse deklariert, sie ist aber in keiner Instanz der Klasse vorhanden. Die Methode arm() ist aber nicht-statisch und damit haben wir das Problem. Es gibt verschiedene Lösungen zu diesem Problem. Wir wählen diejenige, welche auch JCreator wählt. Erinnern Sie sich: Bei einem Projekt vom Typ "Basic Java Application" wurden jeweils zwei Quelldateien mit zwei Klassen erstellt: Die Eine enthielt die Methode main(), in welcher eine Instanz der andern Klasse erzeugt wurde. Das wollen wir jetzt auch machen, nur noch etwas knapper. Schauen Sie sich den folgenden Quelltext an und die nachfolgenden Erklärungen:

import ch.aplu.turtle.*;

class Koch {


   Koch() {
    Turtle fritz = new Turtle();
    for (int i=1; i<=4; i++) arm(fritz);
  }

   void arm(Turtle t) {
    t.forward(50);
    t.right(90);
    t.forward(30);
    t.right(90);
    t.forward(50);
    t.left(90);
  }

   public static void main(String[] args) {
     new Koch();
  }
}

  1. Meine Klasse heisst Koch, weil wir später mit ihr eine sogenannte Koch - Kurve zeichnen wollen.
  2. Im Konstruktor der Klasse wird die Instanz fritz der Klasse Turtle erstellt und es wird in der for - Schlaufe viermal die Methode arm() aufgerufen.
  3. Die Methode arm() erwartet als Parameter eine Turtle Instanz, mit welcher sie arbeitet. Beachten Sie die letzte Anweisung: Die Turtle richtet sich so aus, dass der nächste Arm gezeichnet werden kann.
  4. Die Methode main() ist hier in der selben Klasse (bei JCreator eine separate Klasse in einer separaten Datei) und erzeugt eine Instanz der Klasse. Dieser Instanz wird nicht einmal ein Name gegeben, weil dieser ja auch nicht weiter verwendet wird.

Merken Sie sich diesen Trick, wie man das Problem mit dem "static context" umgehen kann! Und jetzt zur Anwendung:

  • Erstellen Sie ein neues "Empty Project" namens Koch, fügen Sie dem Projekt eine Quelldatei Koch.java zu, fügen Sie den obigen Quelltext in die Datei ein (kopieren, warum auch nicht?), übersetzen und testen Sie - Sie sollten wieder ein Schweizerkreuz haben!

  • Da jetzt unser Kreuz auf OOP Art programmiert ist, stehen uns viele Möglichkeiten offen, die wir etwas ausloten wollen: Drehen Sie am Schluss die Turtle um 170 anstatt 90 Grad!

  • Wenn die Arme so fast nebeneinander liegen, dann sollte das "Kreuz" eben 36 Arme haben! Bevor Sie die for - Schlaufe verändern, sollten Sie die Arme kleiner machen, sonst hat's nicht Platz im Fenster.

 

 

 

 
  • Der Arm muss ja nicht unbedingt rechteckig sein - schaffen Sie das Bild nebenan oder was ähnliches?
  • Nun sind natürlich den Spielereien Tür und Tor offen. Zwei oder mehr Turtles, die je einen Stern zeichnen z.B.?

  • Ich möchte Sie aber noch in einen weiteren Trick einführen: Das Thema heisst Rekursion: Man könnte das auch einen "Schwanzbeisser" nennen, denn eine Methode soll sich selber aufrufen! Das geht in Java und ist ein genialer Trick. Dazu habe ich die Klasse Koch etwas umgebaut:

import ch.aplu.turtle.*;

class Koch {

  Turtle t;

  Koch() {
    t = new Turtle(); 
    t.hideTurtle();
    t.setX(-150);
    t.right(90);
    linie(100);
  }

  void linie(int laenge) {
    t.forward(laenge);
    t.left(60);
    t.forward(laenge);
    t.right(120);
    t.forward(laenge);
    t.left(60);
    t.forward(laenge);
  }

  public static void main(String[] args) {
    new Koch();
  }
}

 
  1. Meine Methode heisst linie, sie zeichnet eine Verbindung von A (linker Rand) nach B (rechter Rand). Diese Verbindung ist aber nicht eine einfache Strecke, die Strecke wird in drei Teile der Länge laenge unterteilt. Der mittlere Teil besteht aus zwie Strecken der Länge laenge, die zwei Seiten eines gleichseitigen Dreiecks bilden.
  2. Da ich nur eine Turtle brauche, wird sie nicht der Methode übergeben. Damit sie sowohl im Konstruktor als auch in der Methode bekannt ist, wird sie in der Klasse deklariert.
  3. Mit setX() startet die Turtle am linken Rand und mit hideTurtle() wird schneller gezeichnet.
  • Ändern Sie Ihre Quelldatei entsprechend und testen Sie!

Die Verbindung von A nach B besteht also selber wieder aus vier Verbindungen und diese müssten nicht unbedingt eine Strecke sein, sondern wir könnten aus jede wieder die Vorschriften der Methode linie anwenden. Irgendmal braucht's aber ein Ende für dieses "sich selber wieder aufrufen": Wenn die Länge klein genug geworden ist, zeichnen wir die Strecke. Das sieht dann etwa so aus:

void linie(int laenge) {
  laenge = laenge / 3;
  if (laenge > limit) linie(laenge);
  else t.forward(laenge);
  t.left(60);
  if (laenge > limit) linie(laenge);
  else t.forward(laenge);
  t.right(120);
  if (laenge > limit) linie(laenge);
  else t.forward(laenge);
  t.left(60);
  if (laenge > limit) linie(laenge);
  else t.forward(laenge);
}

Die Variable limit bestimmt bei welcher Streckenlänge die Rekursion abbricht.

  • Ändern Sie wieder und testen Sie für verschiedene Werte von limit.

  • Die Kurve sieht aus wie ein Teil einer Schneeflocke. Mit drei Grunsstrecken, die ein gleichseitiges Dreieck bilden, erhalten Sie die vollständige Schneeflocke.

Falls Sie weiter an der Theorie zu dieser Kurve interessiert sind, finden Sie z.B. hier oder hier weitere Angaben.Eine Kurve, welche sich selbst wieder enthält, heisst auch Fraktal.Fraktale sind von grosser Bedeutung in der Chaostheorie. Viel Vergnügen bei weiteren Experimenten!