Für Delphi hat Herr Frieder Sprandel einst eine ziemlich luxuriöse Turtle geschrieben und sie in eine eigene Komponente gepackt. Leider erlauben manche der neueren Versionen von Delphi (wie z.B. der "Turbo Delphi Explorer") keine eigenen Komponenten-Installationen mehr. Deshalb müssen wir einen anderen Weg gehen: wir trennen die eigentliche "Zeichenmaschine Turtle" von der Zeichenfläche, für die wir eine Standard-Komponente von Delphi benutzen, nämlich eine PaintBox. Da hinein setzen wir die eigentliche Turtle, die dann alle ihre Ausgaben schön auf die darunterliegende Paintbox zeichnet. Die Implementierung der zugehörigen Klasse "TTurtle" verpacken wir in eine eigene Unit namens "mTurtle2", wobei die nachgestellte "2" eine Versions-Nummer andeuten soll. Der Nachteil dieser Lösung ist, dass wir die Turtle beim Starten unseres Programms erst erzeugen müssen. Das ist aber einfach und zuverlässig möglich, wenn wir es in der "OnCreate"-Methode des Formulars erledigen: procedure TForm1.FormCreate(Sender: TObject); begin Turtle1 := TTurtle.Create(PaintBox1); end;Beachten Sie, dass Sie diese OnCreate-Methode im Objekt-Inspektor auf der Registerkarte "Ereignisse" Ihres Formulars (normalerweise "Form1") erzeugen müssen - nur dann wird die Methode auch intern korrekt "verdrahtet". Zwischen "begin" und "end" dieser Methode tragen Sie dann die obige Zeile ein, mit der die eigentliche Turtle erzeugt wird. Damit Delphi das aber auch so machen kann, müssen wir vor dem nächsten Kompiler-Lauf noch im "private"-Bereich von TForm1 eine Variable "Turtle1" vom Typ "TTurtle" deklarieren, in der dann unser Turtle-Objekt abgespeichert wird. Ein Probelauf des Compilers offenbart allerdings ein weiteres Problem: "Undefinierter Bezeichner TTurtle". Wir müssen noch die Unit "mTurtle2" in die "uses"-Liste der "Unit1" aufnehmen. Diese Liste steht ganz oben im Quelltext von "Unit1" und listet alle Units auf, die von "Unit1" benutzt werden. Zur besseren Übersicht hier nochmals all die einzelnen Schritte, die wir für ein erfolgreiches Initialisieren der Turtle ausführen müssen, gleich in einer etwas sinnvolleren Reihenfolge:
Okay, das klingt nun schon etwas kompliziert, und manches wird Ihnen wohl im Augenblick noch ein wenig mysteriös erscheinen, aber wenn Sie das fünfte Turtle-Grafik-Programm geschrieben hat, dann werden Sie mit diesem Prozedere schon klarkommen, denn: Übung macht den Meister! ;-) Wenn wir unser Programm in diesem Zustand mal versuchsweise starten, dann erscheint die PaintBox, und in ihrer Mitte ein kleines gleichschenkliges Dreieck, das nach oben schaut. Dies ist die (grafische Repräsentation der) Turtle. Um die Turtle nun etwas zeichnen zu lassen, erzeugen wir auf dem Panel einen "Zeichnen"-Knopf, in dessen Klick-Prozedur wir die entsprechenden Turtle-Graphikbefehle eintragen. Das Fenster könnte dann so aussehen: Wenn wir die Turtle nun um 100 Pixel vorwärts gehen lassen wollen, dann schreiben wir in die Klick-Prozedur des "Zeichnen"-Knopfes hinein: procedure TForm1.BtnZeichnenClick(Sender: TObject); begin Turtle1.FD(100); end; Hier ist eine kurze Liste der wichtigsten Turtle-Grafikbefehle:
|
Für Java gibt es zahlreiche Implementierungen der Turtle-Graphik, wie z.B. im "Virtuellen Campus Projekt" der PH Bern, wo eine ganze Online-Lernumgebung mit einer sehr leistungsfähigen Turtle-Graphik zur Verfügung gestellt wird. Wer eine weniger opulente Lösung bevorzugt, kann sich auch mit der spartanischen Turtle zufrieden geben, welche mit dem "JavaEditor" von Herrn Röhner geliefert wird. Diese verzichtet z.B. auf eine graphische Repräsentation der eigentlichen Turtle, also des "Zeichenkopfes", was die Implementierung natürlich deutlich schlanker hält. Trotzdem hat man den Komfort, die Turtle im graphischen Formular-Designer des JavaEditors mit ein paar Mausklicks einfügen zu können. Und alles, was für uns in dieser Unterrichtseinheit über Turtle-Graphik wichtig ist, ist mit dieser schmalen Turtle auch schon abgedeckt. Deshalb entscheiden wir uns hier für die im JavaEditor zur Verfügung gestellte Turtle-Komponente, -- auch wenn man sie trotz allem zuerst noch installieren muss. Es empfiehlt sich, über dem vorgesehenen Zeichenbereich der Turtle zunächst ein JPanel zu platzieren, in welchem man einen oder mehrere Knöpfe zum Auslösen von Zeichenfunktionen der Turtle unterbringen kann. In den meisten Fällen genügen zwei Knöpfe, einer zum "Zeichnen" der gewünschten Figur und einer zum "Löschen" der Zeichnung. Der restliche Bereich des Programmfensters sollte von der Turtle-Komponente ausgefüllt werden. Die im visuellen Design festgelegten Gebietsverteilungen werden zur Laufzeit einfach übernommen: der JavaEditor arbeitet mit "absoluter Positionierung" der Komponenten. Daher ist es klug, auf die Skalierung des Fensters zur Laufzeit zunächst zu verzichten und das JFrame-Attribut "Resizeable" auf "false" stehen zu lassen. Im Formular-Designer könnte das Programmfenster dann so aussehen: Am Anfang steht die (unsichtbare) Turtle in der Mitte des Turtle-Fensters und schaut nach rechts. Um die Turtle etwas zeichnen zu lassen, tragen wir in die Klick-Methode des "Zeichnen"-Knopfs die entsprechenden Turtle-Graphikbefehle ein. Wenn die Turtle z.B. um 100 Pixel vorwärts gehen und dabei einen Strich zeichnen soll, dann schreiben wir: public void button1_ActionPerformed(ActionEvent evt) { turtle1.draw(100); }Hier ist eine kurze Liste der wichtigsten Turtle-Grafikbefehle:
Auf den ersten Blick sieht es so aus, als könnte diese Turtle nur vorwärts gehen. Das ist aber nicht der Fall: wenn Sie den Methoden "draw()" bzw "move()" einen negativen Wert für ds übergeben, dann bewegt sich die Turtle um |ds| entgegengesetzt zu ihrer aktuellen Richtung, kurz: rückwärts! Analog hat ein positiver Wert von angle eine Linksdrehung zur Folge, wenn er an "turn()" übergeben wird, ein negativer hingegen eine Rechtsdrehung. Obwohl die Turtle-Graphik oben als weitgehend koordinatenfrei vorgestellt wurde, verfügt diese Implementierung zusätzlich zu den relativen Zeichenbefehlen ("draw()", "move()" und "turn()") noch über einen vollständigen Satz entsprechender Befehle, die als Argumente absolute Koordinatenwerte erwarten. Das dabei zugrunde gelegte Koordinaten-System ist das Pixelkoorinatensystem des Turtle-Fensters, mit dem Nullpunkt in der linken oberen Ecke!
|
Wenn Sie nun als fortgeschrittener Delphi-Schüler Ihr Nikolaus-Programm gleich schön luxusmäßig gestaltet haben, indem Sie zuerst eine TPanel-Komponente (mit "align = alRight") zur Aufnahme der Steuer-Knöpfe erzeugt haben, und dann die PaintBox1 (mit "align = alClient") auf den ganzen restlichen Platz des Fensters ausgedehnt haben - dann wird Ihnen schmerzlich auffallen, dass sich bei Größenänderungen des Formulars leider der Zeichenbereich der Turtle nicht entsprechend mitverändert: er bleibt genau so groß (oder klein), wie er zu Programmbeginn eingestellt wurde. Aber auch dafür gibt es Abhilfe: erzeugen Sie in Ihrem Formular eine "OnResize"-Methode, und rufen Sie darin "Turtle1.Resize;" auf! Dann wird bei Größenänderungen des Formulars die Größe des Zeichenbereichs der Turtle an die aktuelle Größe der Paintbox angepasst. Allerdings geht dabei die zuvor angezeigte Zeichnung verloren! Das ist bei unseren einfachen Programmen aber leicht zu verschmerzen: ein Klick auf den entsprechenden Knopf behebt das Problem. (Eine allgemeingültige Lösung ist in komplizierteren Programmen übrigens nicht ganz einfach zu implementieren.) |
Wenn Sie nun als fortgeschrittener Java-Schüler Ihr Nikolaus-Programm gleich schön luxusmäßig gestaltet haben, indem Sie das JFrame-Fenster zur Laufzeit skalierbar gemacht haben, dann wird Ihnen aufgefallen sein, dass weder die JPanel-Komponente noch die Turtle-Komponente bei einer Größenänderung adequat reagieren. Um das zu beheben, müssen Sie sowohl für das JPanel als auch für die Turtle-Komponente eine Behandlungsmethode für das (reichlich versteckte) Ereignis "ancestorResized" erzeugen. Erzeugen Sie dann die Ereignismethoden und ergänzen Sie sie folgendermaßen:public void panel1_AncestorResized(HierarchyEvent evt) { panel1.setBounds(0, 0, getWidth(), 50); } public void turtle1_AncestorResized(HierarchyEvent evt) { turtle1.setBounds(0, 50, getWidth(), getHeight() - 50); }Dann wird bei Größenänderungen des Formulars die Größe der beiden Komponenten an die aktuelle Fenstergröße angepasst. |