studyklar
GdI 2 · Übungsblatt 4 · Aufgabe 4

Ineinander geschachtelte Dreiecke: mit Schleife und rekursiv

Fünf gleichseitige Dreiecke liegen ineinander, alle mit demselben Mittelpunkt. Jedes ist um denselben Betrag kleiner als das vorige. Das Programm zeichnet sie zweimal: einmal mit einer Schleife, einmal mit Rekursion, also einer Methode, die sich selbst aufruft. Rechts steht das Programm, die Zeilen zum aktuellen Schritt sind hervorgehoben. Mit Weiter geht es Schritt für Schritt voran.

Die Aufgabe

Aufgabe 4

Die Klasse „DreieckApp“ enthält eine Methode „zeichneGleichseitigesDreieck“. Diese Methode soll verwendet werden, um mehrere, sich ineinander befindende Dreiecke zu zeichnen. Die Dreiecke haben alle denselben Mittelpunkt. Bei jedem Dreieck ist die Seitenlänge gegenüber dem nächst größeren Dreieck um denselben Betrag reduziert.

a) Schreiben Sie eine Methode „zeichneGeschachtelteDreiecke“, die derartige ineinander geschachtelte Dreiecke zeichnet. Parameter sind die Koordinaten des Mittelpunkts, die maximale Seitenlänge des äußersten Dreiecks sowie die Kürzung (der Betrag, um den die Seitenlänge jeweils reduziert wird). Diese Methode soll eine geeignete Schleife verwenden.

b) Schreiben Sie eine zweite Methode „zeichneGeschachtelteDreieckeRekursiv“, die dasselbe tut wie die Methode unter a) – allerdings soll keine Schleife verwendet werden, sondern die Methode soll sich rekursiv selbst wieder aufrufen, um die jeweils nächstinneren Dreiecke zu zeichnen.

Die Klasse DreieckApp mit der Methode zeichneGleichseitigesDreieck ist vorgegeben, liegt uns aber nicht vor. Zum Ausprobieren haben wir sie selbst geschrieben (Zeilen 31 bis 52). Die Zeichenfläche ist wie in Aufgabe 3 durch eine eigene ersetzt, die die Pixel sammelt.

Unser Beispiel: Mittelpunkt (250, 400), größte Seitenlänge 400, Kürzung 80. Das ergibt die Seitenlängen 400, 320, 240, 160 und 80. Bei 0 hören wir auf, denn ein Dreieck mit Seitenlänge 0 ist nur ein Punkt. Die Musterlösung zeichnet diesen Punkt noch mit.

Merke: Eine rekursive Methode braucht zwei Teile: einen kleinen Schritt, den sie selbst erledigt, und eine Abbruchbedingung, die das Weiterrufen beendet.
  1. Die vorgegebene Methode für ein Dreieck verstehen.
  2. a) Die Seitenlängen mit einer Schleife durchlaufen.
  3. b) Dasselbe ohne Schleife: ein Dreieck zeichnen, den Rest an sich selbst abgeben.
  4. Beide Bilder vergleichen.

Die vorgegebene Methode für ein Dreieck

    // vorgegeben: ein Dreieck mit Mittelpunkt (xMitte, yMitte), Spitze oben    public static void zeichneGleichseitigesDreieck(int xMitte, int yMitte, int seitenlaenge) {        double hoehe = seitenlaenge * Math.sqrt(3) / 2;        int xLinks = (int) Math.round(xMitte - seitenlaenge / 2.0);        int xRechts = (int) Math.round(xMitte + seitenlaenge / 2.0);        int yUnten = (int) Math.round(yMitte - hoehe / 3);        int yOben = (int) Math.round(yMitte + hoehe * 2 / 3);        zeichneLinie(xLinks, yUnten, xRechts, yUnten);        zeichneLinie(xRechts, yUnten, xMitte, yOben);        zeichneLinie(xMitte, yOben, xLinks, yUnten);    }

zeichneGleichseitigesDreieck bekommt einen Mittelpunkt und eine Seitenlänge. Sie rechnet die drei Ecken aus und verbindet sie mit Linien. Die Spitze zeigt nach oben. Wie sie das genau rechnet, müssen wir nicht wissen: Wir rufen sie nur auf.

Für Neugierige: Die Höhe ist Seitenlänge · √3 / 2, bei 400 also etwa 346. Der Mittelpunkt liegt ein Drittel der Höhe über der unteren Seite. So ergeben sich für Seitenlänge 400 um (250, 400) die Ecken (50, 285), (450, 285) und (250, 631).

Aus Blatt 4, Aufgabe 3 („Linien, Rechteck und Quadrat Pixel für Pixel zeichnen“): Die Zeichenfläche ist 1000 × 800 Pixel groß. Der Punkt (0, 0) liegt links unten, x zählt nach rechts, y nach oben.

Welche Seitenlängen kommen vor?

    public static void zeichneGeschachtelteDreiecke(int xMitte, int yMitte,            int maxSeitenlaenge, int kuerzung) {
DreieckSeitenlängegrößer als 0?
1400ja, zeichnen
2400 − 80 = 320ja, zeichnen
3320 − 80 = 240ja, zeichnen
4240 − 80 = 160ja, zeichnen
5160 − 80 = 80ja, zeichnen
–80 − 80 = 0nein, fertig

Die neue Methode bekommt vier Parameter: den Mittelpunkt (xMitte, yMitte), die Seitenlänge des äußersten Dreiecks und die Kürzung. Mit 400 und 80 beginnen wir bei 400 und ziehen jedes Mal 80 ab.

Nach 80 käme 0. Ein Dreieck mit Seitenlänge 0 hat keine Fläche, es wäre nur ein Pixel im Mittelpunkt. Darum zeichnen wir nur, solange die Seitenlänge größer als 0 ist. Die Musterlösung schreibt seitenlaenge >= 0 und färbt dieses Pixel noch.

Noch 6 Schritte bis zum Ergebnis
Den ganzen Lösungsweg sehen Studierende und Lehrende der HS KL. Registriere dich mit deiner Adresse @stud.hs-kl.de oder @hs-kl.de, dann geht es hier direkt weiter.

Die Klasse DreieckApp

neu: Zeile 5 bis 29

Abbildung 1Die Klasse DreieckApp

neu: Zeile 5 bis 29

import grafik.Zeichenflaeche; public class DreieckApp {     public static void main(String[] args) {        zeichneGeschachtelteDreiecke(250, 400, 400, 80);        zeichneGeschachtelteDreieckeRekursiv(750, 400, 400, 80);    }     // a) mit einer Schleife    public static void zeichneGeschachtelteDreiecke(int xMitte, int yMitte,            int maxSeitenlaenge, int kuerzung) {        for (int seitenlaenge = maxSeitenlaenge;                seitenlaenge > 0;                seitenlaenge -= kuerzung) {            zeichneGleichseitigesDreieck(xMitte, yMitte, seitenlaenge);        }    }     // b) rekursiv, ohne Schleife    public static void zeichneGeschachtelteDreieckeRekursiv(int xMitte, int yMitte,            int maxSeitenlaenge, int kuerzung) {        if (maxSeitenlaenge <= 0) {            return;        }        zeichneGleichseitigesDreieck(xMitte, yMitte, maxSeitenlaenge);        zeichneGeschachtelteDreieckeRekursiv(xMitte, yMitte,                maxSeitenlaenge - kuerzung, kuerzung);    }     // vorgegeben: ein Dreieck mit Mittelpunkt (xMitte, yMitte), Spitze oben    public static void zeichneGleichseitigesDreieck(int xMitte, int yMitte, int seitenlaenge) {        double hoehe = seitenlaenge * Math.sqrt(3) / 2;        int xLinks = (int) Math.round(xMitte - seitenlaenge / 2.0);        int xRechts = (int) Math.round(xMitte + seitenlaenge / 2.0);        int yUnten = (int) Math.round(yMitte - hoehe / 3);        int yOben = (int) Math.round(yMitte + hoehe * 2 / 3);        zeichneLinie(xLinks, yUnten, xRechts, yUnten);        zeichneLinie(xRechts, yUnten, xMitte, yOben);        zeichneLinie(xMitte, yOben, xLinks, yUnten);    }     // vorgegeben: eine gerade Linie Pixel für Pixel    private static void zeichneLinie(int x1, int y1, int x2, int y2) {        int schritte = Math.max(Math.abs(x2 - x1), Math.abs(y2 - y1));        for (int i = 0; i <= schritte; i++) {            double t = (schritte == 0) ? 0 : (double) i / schritte;            int x = (int) Math.round(x1 + t * (x2 - x1));            int y = (int) Math.round(y1 + t * (y2 - y1));            Zeichenflaeche.zeichnePixel(x, y);        }    }}
LegendeDas ganze Programm.