Ein fraktaler Baum mit Pythons Turtle
In diesem Beitrag möchte ich wieder Pythons Turtle bemühen, um damit fraktale, selbstähnliche Bäume zu erzeugen. Und auch wenn ich große Sympathien für die Turtle-Implementierung in TigerJython habe, ihr Nachteil ist, daß sie große Unterschiede zu der Turtle-Implementierung in Standard-Python besitzt. Daher habe ich mich bei diesem Beispiel entschieden, Trinkets-Turtle wieder hervorzukramen, da ihre Implementierung sich – bis auf wenige Ausnahmen – streng an CPythons Turtle orientiert. In der Regel können die Programme Eins-zu-Eins übernommen werden.
Also habe ich erst einmal einen perfekt selbstähnlichen Baum konstruiert. Es ist ein rekursiver Aufruf und bei der Implementierung habe ich mich an Al Sweigarts wunderbarem Buch1 orientiert, allerdings konnte ich mit Hilfe von Trinket auch ohne JavaScript ein im Browser lauffähiges Programm erzeugen.
import turtle
from random import randint
WIDTH, HEIGHT = 640, 400
LEFT_ANGLE = randint(10, 30)
LEFT_DECREASE = randint(8, 15)
RIGHT_ANGLE = randint(10, 30)
RIGHT_DECREASE = randint(8, 15)
wn = turtle.Screen()
wn.setup(width = WIDTH, height = HEIGHT)
wn.setworldcoordinates(0, 0, WIDTH, HEIGHT)
# wn.title("Fractal Tree")
# wn.colormode(255)
wn.bgcolor(210, 219, 142)
wn.tracer(0)
alice = turtle.Turtle()
alice.speed(0)
alice.hideturtle()
def draw_branch(start_position, direction, branch_length):
if branch_length < 2:
return
# Startposition und initiale Richtung
alice.penup()
alice.goto(start_position)
alice.setheading(direction)
# Zeichne einen Zweig
alice.pendown()
# Die Dicke des Zweiges beträgt 1/7 der Zweiglänge, aber mindestens 1
alice.pensize(max(branch_length/7.0, 1))
# Farben in Abängigkeit von der Dicke des Stammes
if branch_length >= 70:
alice.pencolor(139, 69, 19)
elif branch_length >= 65:
alice.pencolor(139, 115, 85)
elif branch_length >= 60:
alice.pencolor(139, 134, 78)
elif branch_length >= 55:
alice.pencolor(189, 183, 110)
elif branch_length >= 40:
alice.pencolor(85, 107, 47)
elif branch_length >= 25:
alice.pencolor(107, 142, 35)
else:
alice.pencolor(0, 100, 0)
alice.forward(branch_length)
# Speichere die Position am Ende des Zweiges
end_position = alice.position()
left_direction = direction + LEFT_ANGLE
left_branch_length = branch_length - LEFT_DECREASE
right_direction = direction - RIGHT_ANGLE
right_branch_length = branch_length - RIGHT_DECREASE
# Rekursion
draw_branch(end_position, left_direction, left_branch_length)
draw_branch(end_position, right_direction, right_branch_length)
# (Pseudo-) Zufallswert für die Startlänge des Stamms
start_length = randint(60, 90)
# Zeichne den Baum
draw_branch((WIDTH//2, 10), 90, start_length)
wn.update()
print("I did it, Babe!")
wn.mainloop()
Das Programm ist fast ohne Änderung auch in CPython lauffähig (siehe Screenshot im Bannerbild oben), lediglich die beiden oben auskommentierten Zeilen
müssen in dem CPython-Skript aktiviert werden2.
Jeder Durchlauf erzeugt einen neuen Baum (einfach auf den »Run«-Button klicken). Die Paramenter zu Beginn des Programms (LEFT_ANGLE
, LEFT_DECREASE
, RIGHT_ANGLE
und RIGHT_DECREASE
) sind zwar zufällig, aber durch wildes Experimentieren so ausgewählt, daß die meisten Bäume in das Fenster passen.
Die Bäume sehen ja schon sehr nett, aber nicht sehr natürlich aus, da jede Verzweigung eine exakte, wenn auch verkleinerte Kopie der ursprünglichen Verzweigung ist. Das macht den Baum zwar perfekt selbstähnlich, aber solche perfekte Selbstähnlichkeit kommt in der Natur selten vor.
Um die erzeugten Bäume natürlicher aussehen zu lassen, habe ich in einer zweiten Version des Programms die Deklaration für LEFT_ANGLE
, LEFT_DECREASE
, RIGHT_ANGLE
und RIGHT_DECREASE
gelöscht und dafür den Abschnitt
# Speichere die Position am Ende des Zweiges
end_position = alice.position()
left_direction = direction + randint(10, 30)
left_branch_length = branch_length - randint(8, 15)
right_direction = direction - randint(10, 30)
right_branch_length = branch_length - randint(8, 15)
wie oben geändert. Dadurch wird jede Verzweigung zufällig erzeugt und der Baum sieht schon viel natürlicher aus.
Verwendete und weiterführende Literatur
- Karl-Heinz Becker, Michael Dörfler: Fraktale und Dynamische Systeme. Computergrafische Experimente mit Processing, Bremen (Kindle Direct Publishing) 2025
- David Peak, Michael Frame: Komplexität – das gezähmte Chaos, Basel (Birkhäuser Verlag) 1995
- Heinz-Otto Peitgen, Hartmut Jürgens und Dietmar Saupe: Bausteine des Chaos – Fraktale, Berlin, Heidelberg (Springer) und Stuttgart (Klett-Cotta) 1992
- Heinz-Otto Peitgen, Hartmut Jürgens und Dietmar Saupe: Chaos – Bausteine der Ordnung, Berlin, Heidelberg (Springer) und Stuttgart (Klett-Cotta) 1994
- Al Sweigart: The Recursive Book of Recursion. Ace the Coding Interview with Python and JavaScript, San Francisco CA (no starch press) 2022
Den Quellcode für die Trinket-Versionen Recursive Tree 1 und Recursive Tree 2 könnt ihr in meinen Trinkets finden, die Quellcodes für CPython (fractatree1.py und fractaltree2.py) findet Ihr in meinem GitHub-Repositorium.