Laden...

rekursives Zeichnen mit einer Turtle (kochsche schneeflocke, pythagoras-baum, sierspinski-dreieck)

Erstellt von bluedragon vor 15 Jahren Letzter Beitrag vor 15 Jahren 10.488 Views
B
bluedragon Themenstarter:in
101 Beiträge seit 2008
vor 15 Jahren
rekursives Zeichnen mit einer Turtle (kochsche schneeflocke, pythagoras-baum, sierspinski-dreieck)

Beschreibung:

Also da ich mal vor Jahren in der Schule mit einem Programm arbeitete was "Turtle" hieß und einen Stift simulierte, den man "hoch nehmen und auf das Papier absetzen" , "links drehen", "rechts drehen", "vorwärts -" und "rückwärts gehen", konnte habe ich mal so als Projekt, selbiges gemacht. Falls ihr euch nicht vorstellen könnt wie ich das meine, siehe hier http://wiki.zum.de/Turtle-Grafik .
Hinzu kommen noch 3 Codes, die diese Turtle verwenden. Einmal die Kochsche Schneeflocke mit 3*4n Seiten, einmal den Pytagorasbaum mit 2n Ästen und einmal das Sierpinski-Dreieck mit 3^n immerwieder verschachtelten Dreiecken. Wem das alles nichts sagt, kann gerne bei Wiki nachschauen. (Links sind weiter unten)

Die Turtle ist eine Klasse und die 3 Rekursionen lediglich je eine Funktion.

Die Turtle:


public class CTurtle
    {
        private Graphics g;
        private bool meinstift = false;//Stift oben 
        private Pen p = new Pen(Color.Black);
        private float meinturtlex=0;
        private float meinturtley=0;
        private float meinturtlew=0;
        public float X
        {
            get { return meinturtlex; }
            set { meinturtlex = value; }
        }

        public float Y
        {
            get { return meinturtley; }
            set { meinturtley = value; }
        }

        public float Winkel
        {
            get { return meinturtlew; }
            set { meinturtlew = value; }
        }

        private void mitte()
        {
            meinturtlex = Form1.ActiveForm.Width / 2;
            meinturtley = Form1.ActiveForm.Height / 2;
            meinturtlew = 0;
        }
        public void stiftab()
        {
            meinstift = true;
        }
        public void stiftauf()
        {
            meinstift = false;
        }

        public CTurtle()
        {
            g = Form1.ActiveForm.CreateGraphics();
            mitte();
            stiftab();
        }

        public void Clear()
        {
            g = Form1.ActiveForm.CreateGraphics();
            g.Clear(Color.White);
        }
        public void vor(float länge)
        {
            g = Form1.ActiveForm.CreateGraphics();
            float neuesx = meinturtlex + länge *(float) Math.Cos((meinturtlew - 90) / 180 * Math.PI);
           float neuesy = meinturtley + länge * (float)Math.Sin((meinturtlew - 90) / 180 * Math.PI);
            if (meinstift)//zeichnen, wenn Stift unten
            {
                g.DrawLine(p, meinturtlex, meinturtley, neuesx, neuesy);
            }
            meinturtlex = neuesx;
            meinturtley = neuesy;
        }
        public void dreherechts(float winkel)
        {
            meinturtlew += winkel;
            meinturtlew %= 360;
        }
        public void drehelinks(float winkel)
        {
            meinturtlew -= winkel;
            meinturtlew %= 360;
        }
        public void neustart()
        {
            g = Form1.ActiveForm.CreateGraphics();
            mitte();
            g.Clear(Color.White);
        }
    }

Ich habe es ganz simpel gehalten, sodass es nur 3 Buttons und eine Textbox für die Eingabe von n gibt.

Die Kochsche Schneeflocke(die Funktion an sich ist eigentlich die Kochkurve):



private void button1_Click(object sender, EventArgs e) //Button für die Schneeflocke
{
    float länge = 480; //Startseitenlänge
    länge = länge / (float)(Math.Pow(3, Convert.ToInt32(textBox1.Text))); //Schlusslänge jeder Seite
    CTurtle t2 = new CTurtle(); 
    t2.Clear();      

    if (textBox1.Text == "0") //für n=0 ist ein sonderfall und muss seperat behandelt werden
    {
        t2.dreherechts(30);
        t2.vor(länge);    
        t2.dreherechts(120);
        t2.vor(länge);
        t2.dreherechts(120);
        t2.vor(länge);
    }
    else //von 1 bis unendlich
    {
        //Da sich die Kochsche Schneeflocke aus 3 mal Koch-Kurve zusammensetzt, erfolgt der Aufruf einfach 3 mal mit je einer Drehung

        t2.dreherechts(30);                
        Kurve(Convert.ToInt32(textBox1.Text), ref t2, länge);
        t2.dreherechts(120);
        Kurve(Convert.ToInt32(textBox1.Text), ref t2, länge);
        t2.dreherechts(120);
        Kurve(Convert.ToInt32(textBox1.Text), ref t2, länge);
    }
}

public void Kurve(int n, ref CTurtle t, float schritt)
{             
        if (n > 0) Kurve(n-1, ref t, schritt);
        if(n==1) t.vor(schritt);
        t.drehelinks(60);
        if (n > 0) Kurve (n- 1, ref t, schritt);
        if (n == 1) t.vor(schritt);
        t.dreherechts(120);
        if (n > 0) Kurve- 1, ref t, schritt);
        if (n == 1) t.vor(schritt);
        t.drehelinks(60);
        if (n > 0) Kurve(n - 1, ref t, schritt);
        if (n == 1) t.vor(schritt);
}

Pythagoras - Baum:


private void button2_Click(object sender, EventArgs e) //Button für Pythagorasbaum
{
    int größe = 70; //Startseitenlänge im Anfangs-Quadrat
    CTurtle t = new CTurtle();
    t.Clear();
    Pytagoras(t, größe, Convert.ToInt32(textBox1.Text));
}

private void Quadrat(CTurtle t, int größe) //eigentlich nicht nötig, habe es aber trotzdem so gemacht
{
    for (int i = 0; i < 4; i++)
    {
        t.vor(größe);
        t.drehelinks(90);
    }
}

private void Pytagoras(CTurtle t, int größe2, int n)
{
    CTurtle t2 = new CTurtle();
    int ankathete = (int)(Math.Cos(60 * 2 * Math.PI / 360) * größe2);
    int gegenkathete = (int)(Math.Sin(60 * 2 * Math.PI / 360) * größe2);   

    Quadrat(t, größe2);

    t.vor(größe2);
    t.dreherechts(60);

    t2.Y = t.Y;
    t2.X = t.X;
    t2.Winkel = t.Winkel;

    if (n > 0) Pytagoras(t2, ankathete, n - 1);

    t.drehelinks(90);
    if (n > 0) t.vor(ankathete);

    t2.Y = t.Y;
    t2.X = t.X;
    t2.Winkel = t.Winkel;

    if (n > 0) Pytagoras(t2, gegenkathete, n - 1);
}


Das Sierspinski - Dreieck:


private void button3_Click(object sender, EventArgs e) //Button für das Dreieck
{
    int größe = 512; //Startseitenlänge des Startdreiecks
    CTurtle t = new CTurtle();
    t.Clear();
    t.dreherechts(30); //die Turtle "schaut" beim Start nach Norden, aber das Dreieck fängt im 60° Winkel an, d.h. muss sie erst 30° gedreht werden

    Dreieck(t, größe, Convert.ToInt32(textBox1.Text));
}

private void klDreieck(CTurtle t, int größe) //auch diese Funktion ist nicht nötig, könnte man alles unten einfügen
{
    t.vor(größe);
    t.dreherechts(120);
    t.vor(größe);
    t.dreherechts(120);
    t.vor(größe);
    t.dreherechts(120);
}

private void Dreieck(CTurtle t, int größe2, int n)
{
    CTurtle t2 = new CTurtle();

    t2.Y = t.Y;
    t2.X = t.X;
    t2.Winkel = t.Winkel;

    klDreieck(t, größe2);

    if (n > 0)
    {
        Dreieck(t2, größe2 / 2, n - 1);

        t2.vor(größe2 / 2);

        Dreieck(t2, größe2 / 2, n - 1);

        t2.dreherechts(180);
        t2.vor(größe2 / 2);
        t2.drehelinks(120);
        t2.vor(größe2 / 2);
        t2.drehelinks(60);

        Dreieck(t2, größe2 / 2, n - 1);
    }
}


Schlagwörter: *Zeichenturtle *Turtle *rekursion *reskursives zeichnen *Zeichenstift simulieren *Kochsche Schneeflocke *Pythagoras - Baum *Sierspinski - Dreieck

Links:
Kochsche Schneeflocke
Pythagoras - Baum
Sierpinski - Dreieck

Tja und das wars so weit. Ich bitte darum, weder so etwas zu hören wie "boahr das geht doch besser" oder "wo hast du den Code runtergeladen/kopiert, gib mir auchmal die Seite" o.ä. !! Ich habe Obiges selbst mit meinen eigenen Fingern geschrieben und nirgends, auch nur einen Ansatz von ,Hilfe bekommen oder gar gesucht !!! Lediglich der Gedanke, an vergangene Schulzeit, brachte mich auf die Idee eine Turtle zu programmieren. Daraus entstand von ganz alleine die Frage, was ich nun damit Alles, tolles machen kann.
Beim Pythagoras - Baum hätte man noch die innen-Winkel selber eingeben können, aber das habe ich mal außen vor gelassen und einfach mit 60°/90°/30° gearbeitet.

Wie gesagt, bei Fragen "wieso, weshalb, warum" stehe ich gerne zur Verfügung und konstruktive Kritik ist auch sehr gern gesehen 🙂

MfG
bluedragon

Man muss viel gelernt haben, um nach etwas, worüber man nicht Bescheid weiß, richtig fragen zu können.

Wenn du jemandem vertrauen kannst, erübrigt sich ein Vertrag. Kannst du ihm nicht vertrauen, ist ein Vertrag nutzlos.

Gelöschter Account
vor 15 Jahren

Hallo bluedragon,

interessante sache aber du gibst nicht eine einzige grafische resource wieder frei.
konkret:

  1. create graphics ist ganz ganz böse und nur in seltensten fällen sinnvoll
    mach es lieber wie hier beschrieben: [Tutorial] Zeichnen in Windows-Programmen (Paint/OnPaint, PictureBox)

  2. pen, brush und graphics implementieren IDisposable. das bedeutet das du sie wieder freigeben musst. siehe: Dispose implementieren und verwenden

und wenn wir schon dabei sind, sollte deine klasse cTurtle auch dispose implementieren und die gdi-ressourcen dort freigeben.

  1. "Convert.ToInt32(textBox1.Text)" macht bei flascheingaben booom. hier ist int.pars bzw .tryparse besser

  2. "public void Kurve(int n, ref CTurtle t, float schritt)
    " in dieser methode hast du "-" anstatt "("

  3. "größe2 / 2" (ganz unten) ergibt fehler, da du dort ein integer geteilt einem integer machst. also ist 3/2 = 1 da der nachkomma-anteil abgeschnitten wird. hier musst du vorher gegen z.b. float/double/decimal casten oder durch z.b. float/double/decimal teilen.

ansonsten ist es guter code und recht übersichtlich gehalten. ein wenig mehr kommentare über die schritte wären nett aber ich hab mich auch so recht gut zurechtgefunden.

gruß
Jack

B
bluedragon Themenstarter:in
101 Beiträge seit 2008
vor 15 Jahren

Zu 1. Ich weiß 😉 In anderen Programmen arbeite ich mit soetwas nicht, aber kann man damit sehr einfach realisieren. Nebenbei habe ich noch andere Projekte am laufen wo is ausschließlich mit OnPain und Invalidate arbeite.

Zu 2. Dispose kenne und benutze ich ebenfalls, wie gesagt in andere Projekten ;=)
Aber kann man ja trozdem mal einbauen.

Zu 3. hust da ich bisher nur das Programm in den Fingern hatte, ging ich immer davon aus, das eine gültige Eingabe getätigt wird. Aber man weiß ja nie, danke für den Hinweis, aber ist ja kein berauschender Fehler 🙂

Zu 4. Ich hab den Code nicht 1:1 Kopiert:Eingefügt und wohl beim Variablen umbenennen, etwas zuviel des guten getan. Aber mit richtigen Zeichen ist die Funktionalität vorhanden.

Zu 5. Das es sich dabei im einen Integer handelt war schon beabsichtigt. Da 512 ein vielfaches von 2 ist, war es egal ob man 512 1, 2,3...n mal teilt. Bis man bei 512/n=1 angelangt wäre, würde er ja problemlos zeichnen. Aber ab 512/n=1 wären dann beim nächten Mal 1/2=0 Pixel nichtmehr zeichenbar und wäre, somit immer 0. Also das war schon beabsichtigt, aber trotzdem danke ;=)

ansonsten ist es guter code und recht übersichtlich gehalten. ein wenig mehr kommentare über die schritte wären nett aber ich hab mich auch so recht gut zurechtgefunden.

Danke sehr =)
Kommentierungen mache ich eigentlich nur bei größere Projekten, da es sich hier nur um reine Funktionen handelt, ausser der Turtle, habe ich es mehr oder wengier gelassen. Bei wirklich umfangreichen Dingen, kommen auch so Dinge wie "#region Test", "#endregion", "<summary>" o.ä. ins Spiel.

MfG
bluedragon

Man muss viel gelernt haben, um nach etwas, worüber man nicht Bescheid weiß, richtig fragen zu können.

Wenn du jemandem vertrauen kannst, erübrigt sich ein Vertrag. Kannst du ihm nicht vertrauen, ist ein Vertrag nutzlos.