comments
This commit is contained in:
@ -1,12 +1,27 @@
|
||||
import inputHelper
|
||||
import turtle
|
||||
import drawer
|
||||
import sys
|
||||
|
||||
def quit():
|
||||
print() # Letzte Zeile wird abgeschlossen
|
||||
sys.exit()
|
||||
|
||||
# Dummy-Turtle, die erzeugt wird damit sich das Fenster öffnet und getestet werden kann,
|
||||
# ob der Nutzer eine korrekte Farbeingabe gemacht hat. Allerdings wird mit dieser Turtle nichts gezeichnet.
|
||||
turtleObject = drawer.newTurtle()
|
||||
screen = turtleObject.screen
|
||||
screen.colormode(255)
|
||||
screen.colormode(255) # ändert den Farbmodus auf RGB mit 24 Bits
|
||||
# Programm wird geschlossen, wenn das Fenster geschlossen wird
|
||||
# http://ostack.cn/?qa=1086384/how-to-detect-x-close-button-in-python-turtle-graphics
|
||||
screen.getcanvas().winfo_toplevel().protocol("WM_DELETE_WINDOW", quit)
|
||||
try:
|
||||
inputHelper.takeInput(turtleObject)
|
||||
# Die Python integrierte "input"-Funktion wirft diesen Fehler, wenn der Nutzer die Tastenkombi Strg-D
|
||||
# auf einem Unix-System verwendet. Es ist üblich, dass das Programm daraufhin beendet wird.
|
||||
# Wir folgen dieser Konvention und beenden das Programm wobei die Fehlermeldung abgefangen wird,
|
||||
# damit der Nutzer nicht von einer Fehlermeldung verwirrt wird, wenn dieser das Programm absichtlich
|
||||
# beendet. EOF-Fehler von anderen Ein- oder Augabeoperationen werden bereits voher abgefangen.
|
||||
except EOFError:
|
||||
quit()
|
||||
screen.mainloop()
|
||||
screen.mainloop() # führt den Tk-mainloop aus
|
||||
|
||||
@ -2,11 +2,13 @@ import turtle
|
||||
from dataclasses import dataclass
|
||||
import lSystems
|
||||
|
||||
# um die Rotation und Position einer turtle zu speichern
|
||||
@dataclass
|
||||
class State:
|
||||
heading: float
|
||||
position: turtle.Vec2D
|
||||
|
||||
# enthält alle Information, die notwendig sind, um ein Lindenmayer-System zu zeichnen
|
||||
@dataclass
|
||||
class DrawingInfo:
|
||||
lSystem: lSystems.LSytem
|
||||
@ -16,78 +18,100 @@ class DrawingInfo:
|
||||
scale: float
|
||||
recursionDepth: int
|
||||
|
||||
# enthält zusätzlich zu DrawingInfo die turtle mit der die Zeichnung gemacht wurde
|
||||
@dataclass
|
||||
class Drawing:
|
||||
info: DrawingInfo
|
||||
turtle: turtle.Turtle
|
||||
|
||||
drawings = []
|
||||
drawings = [] # speichert alle Zeichnungen (Objekte der Klasse Drawing), die anzeigt werden
|
||||
|
||||
class Drawer():
|
||||
def __init__(self, drawing):
|
||||
# verschiedensten Variablen werden Werte zugewiesen
|
||||
self.startWord = drawing.info.lSystem.startWord
|
||||
self.recursionDepth = drawing.info.recursionDepth
|
||||
self.productionRules = drawing.info.lSystem.productionRules
|
||||
self.angel = drawing.info.lSystem.angel
|
||||
self.forwardDistance = drawing.info.scale
|
||||
self.turtle = drawing.turtle
|
||||
|
||||
# bewegt die turtle zur Sartposition und rotiert die turtle
|
||||
# Zuvor wird das Zeichnen temporär deaktiviert, damit die turtle beim Bewegen
|
||||
# zur neuen Position keine Linie zeichnet.
|
||||
self.turtle.penup()
|
||||
self.turtle.setposition(drawing.info.position)
|
||||
self.turtle.setheading(drawing.info.rotation)
|
||||
self.pendown()
|
||||
self.turtle.pencolor(drawing.info.color)
|
||||
|
||||
self.turtle.pencolor(drawing.info.color) # ändert die Farbe
|
||||
|
||||
# Diese Methode wird nur bei der Klasse DrawerSimulation gebraucht, um die Eckpunkte zu speichern.
|
||||
def storeEdges(self):
|
||||
pass
|
||||
|
||||
# wrappt die pendown Methode der turtle, damit das zeichnen bei überschreiben der Methode
|
||||
# deaktiviert werden kann.
|
||||
def pendown(self):
|
||||
self.turtle.pendown()
|
||||
|
||||
# Die Methode zeichnet das Lindenmayer-System.
|
||||
# n gibt die (verbleibende) Rekursiontiefe an.
|
||||
# Beim ersten Aufrufen wird die Methode ohne Paramenter aufgerufen.
|
||||
def draw(self, word = None, n = None):
|
||||
# weist die Standartwerte zu
|
||||
if n == None:
|
||||
n = self.recursionDepth
|
||||
if word == None:
|
||||
word = self.startWord
|
||||
|
||||
turtleStates = []
|
||||
if n == 0:
|
||||
self.turtle.screen.update()
|
||||
if n == 0: # zeichnen beendet
|
||||
self.turtle.screen.update() # updated den Bildschirm, sodass das Lindenmayer-System anzeigt wird
|
||||
return
|
||||
for character in word:
|
||||
for character in word: # itteriert über das Wort
|
||||
match character:
|
||||
case "F":
|
||||
self.turtle.forward(self.forwardDistance)
|
||||
self.storeEdges()
|
||||
self.turtle.forward(self.forwardDistance) # bewegt die turtle nach vorne
|
||||
self.storeEdges() # speichert die Eckpunkte (für die Klasse DrawerSimulation)
|
||||
case "+":
|
||||
self.turtle.left(self.angel)
|
||||
self.turtle.left(self.angel) # dreht die turtle nach links
|
||||
case "-":
|
||||
self.turtle.right(self.angel)
|
||||
self.turtle.right(self.angel) # dreht die turtle nach rechts
|
||||
case "[":
|
||||
# speichert Position und Rotation der turtle
|
||||
turtleStates.append(State(self.turtle.heading(), self.turtle.position()))
|
||||
case "]":
|
||||
self.turtle.penup()
|
||||
# Die letzte abgespeicherte Position und Rotation wird wiederhergestellt
|
||||
self.turtle.penup() # um einen Strich zu der neuen Position zu vermeiden
|
||||
state = turtleStates.pop()
|
||||
self.turtle.setposition(state.position)
|
||||
self.turtle.setheading(state.heading)
|
||||
self.pendown()
|
||||
# Bei einem Nicht-Terminal werden die jeweiligen Produktionsregeln ausgeführt
|
||||
if character in self.productionRules:
|
||||
self.draw(self.productionRules[character], n - 1)
|
||||
|
||||
# Klasse für das Speichern der Eckpunkte
|
||||
class Edges:
|
||||
def __init__(self):
|
||||
# Die Klasse DrawerSimulation wird immer mit der Startpostion (0, 0) aufgerufen.
|
||||
# Deshalb sind die minimalen und maximalen Werte zu anfangs auch alle 0.
|
||||
self.min = [0, 0]
|
||||
self.max = [0, 0]
|
||||
|
||||
# Methoden, die die beiden Eckpunkte als Vektor zurückgeben
|
||||
def minVec(self):
|
||||
return turtle.Vec2D(self.min[0], self.min[1])
|
||||
|
||||
def maxVec(self):
|
||||
return turtle.Vec2D(self.max[0], self.max[1])
|
||||
|
||||
class DrawerSimulation(Drawer):
|
||||
def __init__(self, drawing):
|
||||
super(DrawerSimulation, self).__init__(drawing)
|
||||
super(DrawerSimulation, self).__init__(drawing) # Konstruktor der Superklasse
|
||||
self.edges = Edges()
|
||||
|
||||
# ändert die Koordinaten der Eckpunkte, wenn die turtle über die bisherigen hinaus geht.
|
||||
def storeEdges(self):
|
||||
for i, koord in enumerate(self.turtle.position()):
|
||||
if koord < self.edges.min[i]:
|
||||
@ -95,36 +119,55 @@ class DrawerSimulation(Drawer):
|
||||
elif koord > self.edges.max[i]:
|
||||
self.edges.max[i] = koord
|
||||
|
||||
# deaktiviert das Zeichnen
|
||||
def pendown(self):
|
||||
pass
|
||||
|
||||
# Die Funktion simuliert erst das Lindenmayer-System, um die Größe zu erhalten.
|
||||
# Danach wird das Lindenmayer-System gezeichnet und auf die agegebene Größe skalliert
|
||||
def draw(lSystem, recursionDepth, middle, rotation, size, color):
|
||||
# Informationen zum Zeichnen des Lindenmayer-System
|
||||
drawingInfo = DrawingInfo(lSystem, turtle.Vec2D(0, 0), rotation, color, 1, recursionDepth)
|
||||
turtleObject = newTurtle()
|
||||
turtleObject = newTurtle() # erstellt eine neue turtle
|
||||
drawing = Drawing(drawingInfo, turtleObject)
|
||||
simulatedDraw = DrawerSimulation(drawing)
|
||||
simulatedDraw.draw()
|
||||
simulatedDraw.draw() # simuliert das Lindenmayer-System
|
||||
|
||||
# die beiden diagonalen Eckpunkte der Fläche in der die Zeichnung liegen würde
|
||||
maxVec = simulatedDraw.edges.maxVec()
|
||||
minVec = simulatedDraw.edges.minVec()
|
||||
|
||||
distance = maxVec - minVec
|
||||
xScale = size[0] / distance[0]
|
||||
yScale = size[1] / distance[1]
|
||||
xScale = size[0] / distance[0] # Skallierungswert für die Höhe
|
||||
yScale = size[1] / distance[1] # Skallierungswert für die Breite
|
||||
|
||||
# Der kleinere Skallierungswert wird benutzt damit die Zeichnung noch innerhalb des Bereichs ist.
|
||||
scale = yScale if xScale > yScale else xScale
|
||||
|
||||
# Die Sartposition für die Zeichnung wird berechnet indem die Startpostion berechnet wird,
|
||||
# wenn der Mittelpunkt bei (0, 0) liegen soll. Dieser Punkt wird dann um den gewählten Mittelpunkt verschoben.
|
||||
pos = middle + (-minVec - distance * (1/2)) * scale
|
||||
|
||||
# zeichnet das Lindenmayer-System mit den berechneten Werten
|
||||
drawing.info.position = pos
|
||||
drawing.info.scale = scale
|
||||
drawScaled(drawing)
|
||||
|
||||
# erstellt eine neue turtle und nimmt einige Einstellungen vor.
|
||||
def newTurtle():
|
||||
turtleObject = turtle.Turtle()
|
||||
turtleObject.hideturtle()
|
||||
turtleObject.hideturtle() # turtle wird nicht angezeigt
|
||||
|
||||
# macht das Zeichnen so schnell wie möglich und für das Sehen der Änderungen ist ein Update des Bildschirms erforderlich
|
||||
turtleObject._tracer(0, 0)
|
||||
return turtleObject
|
||||
|
||||
# löscht eine Zeichnung
|
||||
def delete(i):
|
||||
drawings[i].turtle.clear()
|
||||
del drawings[i]
|
||||
|
||||
# zeichnet ein Lindenmayer-System und es der Liste hinzu
|
||||
def drawScaled(drawing):
|
||||
actualDrawer = Drawer(drawing)
|
||||
actualDrawer.draw()
|
||||
|
||||
@ -5,34 +5,42 @@ import pickle
|
||||
from dataclasses import dataclass
|
||||
from PIL import Image
|
||||
import io
|
||||
import sys
|
||||
|
||||
# Klasse mit allen nötigen Informationen, um den Zustand des Programms abzuspeichern.
|
||||
@dataclass
|
||||
class Save:
|
||||
backgroundColor: any
|
||||
drawingInfos: [drawer.DrawingInfo]
|
||||
|
||||
# Pfad der geladenen Datei, um eine Option anzubieten schnell unter der gleichen Datei zu speichern.
|
||||
loadedFilepath = None
|
||||
backgroundColor = "white"
|
||||
backgroundColor = "white" # aktuelle Hintergrundfarbe des Fensters
|
||||
|
||||
# Funktion, um eine Information über den Standartwert einer Benutzereingabe anzugeben
|
||||
def defaultValueMsg(defaultValue):
|
||||
if defaultValue == None:
|
||||
return ""
|
||||
return f" (Standartwert: {defaultValue})"
|
||||
|
||||
# liest eine Nutzereingabe mit der entsprechenden Beschreibung ein und infomiert den
|
||||
# Nutzer über den Standartwert
|
||||
def inputWithDefault(description, defaultValue):
|
||||
return input(f"{description}{defaultValueMsg(defaultValue)}: ")
|
||||
|
||||
def inputNum(inputType, description, rangeErrorMsg, minRange, maxRange, defaultValue = None):
|
||||
while True:
|
||||
inputValue = inputWithDefault(description, defaultValue)
|
||||
# Hilfsfunktion für die Nutzereingabe einer Zahl in einem bestimmten Bereich
|
||||
def inputNum(numberType, description, rangeErrorMsg, minRange, maxRange, defaultValue = None):
|
||||
while True: # Die Eingabeauffordung wird wiederholt, wenn die Eingabe ungültig ist.
|
||||
inputValue = inputWithDefault(description, defaultValue) # Eingabe wird eingelesen
|
||||
try:
|
||||
number = inputType(inputValue)
|
||||
inputError = number < minRange or number > maxRange
|
||||
if inputError:
|
||||
print(rangeErrorMsg)
|
||||
else:
|
||||
return number
|
||||
# Der Eingabewert Wert wird zu einer Zahl konvertiert (float oder int).
|
||||
# Wurde keine Zahl eingegeben, wird ein ValueError ausgelöst.
|
||||
number = numberType(inputValue)
|
||||
if number >= minRange and number <= maxRange: # liegt die Zahl im entsprechenden Bereich,
|
||||
return number # wird diese zurückgegeben.
|
||||
print(rangeErrorMsg) # Ansonsten wird eine Fehlermeldung ausgegeben.
|
||||
except ValueError:
|
||||
# Wurde nichts eingegeben und es gibt einen Standartwert, wird dieser zurückgegeben.
|
||||
if inputValue == "" and defaultValue != None:
|
||||
return defaultValue
|
||||
print("Fehler: keine gültige Zahl")
|
||||
@ -40,38 +48,49 @@ def inputNum(inputType, description, rangeErrorMsg, minRange, maxRange, defaultV
|
||||
def inputColorError():
|
||||
print("Fehler: ungültige Farbeingabe")
|
||||
|
||||
# fragt eine TK-Farbe ab
|
||||
def inputColor(turtleObject, question, defaultValue = None):
|
||||
while True:
|
||||
while True: # Die Eingabeauffordung wird wiederholt, wenn die Eingabe ungültig ist.
|
||||
inputValue = input(f"{question} RGB-Wert eingeben, wobei die Farben mit Leerzeichen getrennt werden, oder einen Tk-Farbnamen eingeben [0 0 0 - 255 255 255 oder Farben auf https://www.tcl.tk/man/tcl8.4/TkCmd/colors.html]{defaultValueMsg(defaultValue)}: ")
|
||||
if inputValue == "":
|
||||
if inputValue == "": # Wenn keine Farbe eingeben wurde,
|
||||
if defaultValue != None:
|
||||
return defaultValue
|
||||
inputColorError()
|
||||
return defaultValue # wird der Standartwert verwendet
|
||||
inputColorError() # bzw. ein Fehler ausgegeben und die Abfrage wiederholt,
|
||||
# wenn es keinen Standartwert gibt
|
||||
else:
|
||||
try:
|
||||
try:
|
||||
# Die Eingabe wird in eine RGB-Tuple umgewandelt.
|
||||
color = tuple([int(color) for color in inputValue.split()])
|
||||
turtleObject.pencolor(color)
|
||||
turtleObject.pencolor(color) # der RGB-Wert wird getestet
|
||||
return color
|
||||
# hat der Benutzer keinen gültigen RGB-Wert eingeben, wird ein Fehler geworfen und es
|
||||
# wird probiert ob ein Tk-Farbname verwendet wurde.
|
||||
except:
|
||||
try:
|
||||
turtleObject.pencolor(inputValue)
|
||||
return inputValue
|
||||
except:
|
||||
# Wurde kein Tk-Farbname verwendet, ist die Eingabe ungültig und
|
||||
# der Nutzer wird erneut nach einer Farbe gefragt
|
||||
inputColorError()
|
||||
|
||||
# fordert den Nutzer auf einen String einzugeben
|
||||
def inputString(question, defaultValue = None):
|
||||
while True:
|
||||
while True: # Die Eingabeauffordung wird wiederholt, wenn die Eingabe ungültig ist.
|
||||
inputValue = inputWithDefault(question, defaultValue)
|
||||
if inputValue != "":
|
||||
return inputValue
|
||||
if defaultValue != None:
|
||||
if defaultValue != None: # wurde nichts eingegeben, wird der Standartwert verwendet
|
||||
return defaultValue
|
||||
|
||||
# Die Funktion führt einen Befehl aus und ruft sich daraufhin rekursiv auf.
|
||||
def takeInput(turtleObject):
|
||||
global backgroundColor
|
||||
global loadedFilepath
|
||||
inputValue = input("Bitte einen Befehl eingeben. h für Hilfe: ")
|
||||
match inputValue:
|
||||
case "h":
|
||||
# Hilfeseite
|
||||
print("""h: Hilfe anzeigen
|
||||
d: ein neues Lindenmayer-System zeichnen
|
||||
l: ein Lindenmayer-System löschen
|
||||
@ -81,88 +100,121 @@ s: Lindenmayer-Systeme speichern
|
||||
r: zuvor gespeicherte Lindenmayer-Systeme wiederherstellen
|
||||
e: als Bilddatei exportieren""")
|
||||
case "d":
|
||||
# gibt vorhandene Lindenmayer-Systeme aus
|
||||
for i, lSystem in enumerate(lSystems.LSystems):
|
||||
print(f"{i}: {lSystem.name}")
|
||||
lSystemIndex = inputNum(int, "Bitte ein Lindenmayer-System auswählen und die entsprechende Nummer eingeben: ", "Es gibt kein L-System mit dieser Nummer.", 0, len(lSystems.LSystems) - 1)
|
||||
|
||||
# verschiedene Nutzereingaben
|
||||
lSystemIndex = inputNum(int, "Bitte ein Lindenmayer-System auswählen und die entsprechende Nummer eingeben", "Es gibt kein L-System mit dieser Nummer.", 0, len(lSystems.LSystems) - 1)
|
||||
lSystem = lSystems.LSystems[lSystemIndex]
|
||||
recursionDepth = inputNum(int, "Rekursiontiefe des Lindenmayer-Systems eingeben [1-50]", "Rekursionstiefe nicht im vorgegebenen Bereich.", 1, 50, lSystem.recursionDepth)
|
||||
rotation = inputNum(float, "Bitte die Rotation in Grad gegen den Uhrzeigersinn angeben, wobei 0° rechts ist", "nur Gradzahlen von 0 bis 360 werden akzeptiert.", 0, 360, 90)
|
||||
inputError = True
|
||||
color = inputColor(turtleObject, "Welche Farbe soll das Lindenmayer-System haben?", "black")
|
||||
|
||||
inputError = True
|
||||
while inputError:
|
||||
match input("Möchtest du das das Lindenmayer-System das ganze Fenster ausfüllt? [J/n]: ").lower():
|
||||
case "j"|"":
|
||||
match input("Möchtest du, dass das Lindenmayer-System das ganze Fenster ausfüllt? [J/n]: ").lower():
|
||||
case "j"|"": # Ja ist die Standartantwort.
|
||||
inputError = False
|
||||
|
||||
# zeichnet das Lindenmayer-System
|
||||
# Von der Größe des Fensters werden drei Pixel abgezogen, da die Linien auch
|
||||
# eine Breite haben und sonst die Linien am Rand nicht zu sehen sind.
|
||||
drawer.draw(lSystem, recursionDepth, turtle.Vec2D(0, 0), rotation, turtle.Vec2D(turtleObject.screen.window_width(), turtleObject.screen.window_height()) - turtle.Vec2D(3, 3), color)
|
||||
takeInput(turtleObject)
|
||||
takeInput(turtleObject) # ruft sich rekursiv auf, um den nächsten Befehl entgegen zu nehmen
|
||||
case "n":
|
||||
inputError = False
|
||||
pos = []
|
||||
print("Klicke bitte die beiden diagonalen Eckpunkte der Fläche an in der das Lindenmayer-System gezeichnet werden soll.")
|
||||
pos = [] # Liste, in der die angeklickte Punkte gespeichert werden
|
||||
|
||||
# Die Funktion wird nach einem Klick mit einem Ortsvektor des angeklickten Punktes aufgerufen.
|
||||
def afterClick(vec):
|
||||
pos.append(vec)
|
||||
if len(pos) == 2:
|
||||
if len(pos) == 2: # Wenn zwei Punkte angeklickt wurden
|
||||
# Nach einem Klick wird die Funktion nicht mehr aufgerufen
|
||||
turtleObject.screen.onclick(None)
|
||||
size = pos[1] - pos[0]
|
||||
middle = pos[0] + (1/2) * size
|
||||
size = turtle.Vec2D(abs(size[0]), abs(size[1]))
|
||||
drawer.draw(lSystem, recursionDepth, middle, rotation, size, color)
|
||||
takeInput(turtleObject)
|
||||
from0to1 = pos[1] - pos[0] # Vektor von Punkt Index 0 zu Punkt Index 1
|
||||
middle = pos[0] + (1/2) * from0to1 # Mittelpunkt der beiden angeklickten Punkte
|
||||
size = turtle.Vec2D(abs(from0to1[0]), abs(from0to1[1]))
|
||||
drawer.draw(lSystem, recursionDepth, middle, rotation, size, color) # zeichnet das Lindenmayer-System
|
||||
takeInput(turtleObject) # ruft sich rekursiv auf, um den nächsten Befehl entgegen zu nehmen
|
||||
|
||||
# registriert die afterClick-Funktion als event
|
||||
turtleObject.screen.onclick(lambda x, y: afterClick(turtle.Vec2D(x, y)))
|
||||
case _:
|
||||
print("Bitte j oder n eingeben")
|
||||
case "b":
|
||||
# fragt nach einer Hintergrundfarbe und ändert diese entsprechend.
|
||||
backgroundColor = inputColor(turtleObject, "Hintergrundfarbe eingeben")
|
||||
turtleObject.screen.bgcolor(backgroundColor)
|
||||
case "q":
|
||||
quit()
|
||||
sys.exit() # beendet das Programm
|
||||
case "l":
|
||||
if len(drawer.drawings) == 0:
|
||||
print("Es gibt nichts, was man löschen könnte.")
|
||||
else:
|
||||
# gibt die angezeigten Lindenmayer-Systeme aus
|
||||
for i, drawing in enumerate(drawer.drawings):
|
||||
print(f"{i}: {drawing.info.lSystem.name} Position: {drawing.info.position}")
|
||||
iDelete = inputNum(int, "Nummer des zu löschenden Zeichnung eingeben: ", "Es gibt keine Zeichnung mit dieser Nummer", 0, len(drawer.drawings) - 1)
|
||||
drawer.delete(iDelete)
|
||||
iDelete = inputNum(int, "Nummer des zu löschenden Zeichnung eingeben", "Es gibt keine Zeichnung mit dieser Nummer", 0, len(drawer.drawings) - 1)
|
||||
drawer.delete(iDelete) # löscht das Lindenmayer-System
|
||||
case "s":
|
||||
# fragt nach dem Dateipfad. Wenn bereits eine Datei geladen oder gespeichert wurde,
|
||||
# wird der dazugehörige Pfad als Standartwert verwendet.
|
||||
filepath = inputString("Dateipfad zum Speichern eingeben", loadedFilepath)
|
||||
drawingInfo = [drawing.info for drawing in drawer.drawings]
|
||||
save = Save(backgroundColor, drawingInfo)
|
||||
loadedFilepath = filepath
|
||||
|
||||
# Beim Speichern der angezeigten Lindenmayer-Systeme werden die turtles nicht benötigt.
|
||||
drawingInfos = [drawing.info for drawing in drawer.drawings]
|
||||
save = Save(backgroundColor, drawingInfos) # Objekt mit allen Daten, die gespeichert werden sollen.
|
||||
try:
|
||||
file = open(filepath, "wb")
|
||||
pickle.dump(save, file)
|
||||
pickle.dump(save, file) # schreibt die Daten in die Datei
|
||||
file.close()
|
||||
|
||||
# infomiert den Nutzer, dass seine Daten gespeichert wurden
|
||||
print("erfolgreich gespeichert")
|
||||
except Exception as err:
|
||||
print(err)
|
||||
print(err) # gibt mögliche IO-Fehler aus
|
||||
case "r":
|
||||
filepath = inputString("Datei, die geladen werden soll eingeben")
|
||||
loadedFilepath = filepath
|
||||
try:
|
||||
file = open(filepath, "rb")
|
||||
save = pickle.load(file)
|
||||
save = pickle.load(file) # liest die Datei
|
||||
file.close()
|
||||
turtleObject.screen.clear()
|
||||
turtleObject.screen.clear() # leert das Fenster
|
||||
|
||||
# ändert die Hintergrundfarbe
|
||||
backgroundColor = save.backgroundColor
|
||||
turtleObject.screen.bgcolor(save.backgroundColor)
|
||||
|
||||
# zeichnet die Lindenmayer-Systeme
|
||||
for drawingInfo in save.drawingInfos:
|
||||
drawing = drawer.Drawing(drawingInfo, drawer.newTurtle())
|
||||
drawer.drawScaled(drawing)
|
||||
except Exception as err:
|
||||
print(err)
|
||||
print(err) # gibt mögliche IO-Fehler aus
|
||||
case "e":
|
||||
# speichert das Bild. für genauere Informationen siehe:
|
||||
# https://stackoverflow.com/questions/34777676/how-to-convert-a-python-tkinter-canvas-postscript-file-to-an-image-file-readable
|
||||
canvas = turtleObject.screen.getcanvas()
|
||||
postscript = canvas.postscript(colormode = "color")
|
||||
image = Image.open(io.BytesIO(postscript.encode("utf-8")))
|
||||
postscript = canvas.postscript(colormode = "color") # exportiert das Bild im postscript Format
|
||||
image = Image.open(io.BytesIO(postscript.encode("utf-8"))) # lädt das postscript mit PIL
|
||||
|
||||
# fragt den Nutzer nach einem Dateipfad
|
||||
filepath = inputString("Dateipfad für das zu exportierende Bild eingeben. Das Bildformat wird über die Dateiendung bestimmt")
|
||||
try:
|
||||
image.save(filepath)
|
||||
image.save(filepath) # speichert das Bild
|
||||
print("Bild gespeichert")
|
||||
except ValueError:
|
||||
except ValueError: # PIL löst einen ValueError aus, wenn die Dateiendung unbekannt ist
|
||||
print("Fehler: unbekannte Dateiendung")
|
||||
except Exception as err:
|
||||
print(err)
|
||||
print(err) # gibt andere Fehler aus
|
||||
case _:
|
||||
print("unbekannter Befehl")
|
||||
|
||||
# Bei dem d(raw)-Befehl wurde die Funktion bereits rekursiv aufgerufen.
|
||||
# Ansonsten wird sie noch aufgerufen und der nächste Befehl kann ausgeführt werden.
|
||||
if inputValue != "d":
|
||||
takeInput(turtleObject)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
import typing
|
||||
|
||||
# beschreibt ein Lindenmayer-System
|
||||
@dataclass
|
||||
class LSytem:
|
||||
name: str
|
||||
@ -9,12 +10,17 @@ class LSytem:
|
||||
angel: float
|
||||
recursionDepth: int
|
||||
|
||||
# dem Programm bekannte Lindenmayer-Systeme
|
||||
LSystems = [
|
||||
# Lindenmayer-Systeme vom AB
|
||||
LSytem("toter Busch", "F", {"F": "F[+F]F[-F]F"}, 25.7, 5),
|
||||
LSytem("Gretenbaum", "F", {"F": "F[+F]F[-F][F]"}, 20.0, 5),
|
||||
LSytem("Laubbaum", "F", {"F": "FF-[-F+F+F]+[+F-F-F]"}, 22.5, 4),
|
||||
LSytem("AB d", "X", {"X": "F[+X]F[-X]+X", "F": "FF"}, 20.0, 7),
|
||||
LSytem("AB e", "X", {"X": "F[+X][-X]FX", "F": "FF"}, 25.7, 7),
|
||||
LSytem("AB f", "X", {"X": "F-[[X]+X]+F[+FX]-X", "F": "FF"}, 22.5, 5),
|
||||
LSytem("dürrer Strauch", "X", {"X": "F[+X]F[-X]+X", "F": "FF"}, 20.0, 7),
|
||||
LSytem("sysmetrisches Pflänzchen", "X", {"X": "F[+X][-X]FX", "F": "FF"}, 25.7, 7),
|
||||
LSytem("schiefer Strauch", "X", {"X": "F-[[X]+X]+F[+FX]-X", "F": "FF"}, 22.5, 5),
|
||||
|
||||
# bekannte Lindenmayer-Systeme
|
||||
LSytem("Drachenkurve", "FX", {"X": "X+YF+", "Y": "-FX-Y"}, 90.0, 15),
|
||||
LSytem("Kochsche Schneeflocke", "F--F--F", {"F": "F+F--F+F"}, 60.0, 5),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user