Schildkröten-Grafik
Bei der Schildkröten-Grafik geht es darum, eine Zeichnung aus Linien anzufertigen, indem man einer Schildkröte - diese hat einen Malstift und sitzt auf einem Blatt Papier - Befehle wie "vorwärts" oder "drehe dich nach links" gibt.
Die Schildkröte wird als Instanz der Klasse turtle repräsentiert.
(defclass turtle ())
Eine neue Schildkröte
- befindet sich im Ursprung des Koordinatensystems (position = 0 als komplexe Zahl),
- schaut in Richtung der x-Achse (heading = 0) und
- benutzt ihren Stift zunächst nicht (pen-state = up).
Für die Umrechnung von Winkeln wird der Wert von Pi benötigt. Der Wert wird auf 30 Stellen genau bestimmt (siehe Pi berechnen oder Polarkoordinaten) und in der Instanzvariable *pi* abgelegt.
(defmethod initialize ((this turtle))
t
(progn
(.= this position 0)
(.= this heading 0)
(.= this pen-state (quote up))
(.= this current-polyline nil)
(.= this polylines nil)
(.= this *pi* (pi 1e-30))
this))
Mit den Methoden pen-up und pen-down gibt man der Schildkröte Befehle zum Heben und Senken des Malstifts. Mit is-pen-down? kann man ermitteln, wie die Schildkröte den Stift hält.
(defmethod pen-up ((this turtle))
t
(progn
(when (. this current-polyline)
(.= this polylines
(cons
(reverse (. this current-polyline))
(. this polylines)))
(.= this current-polyline nil))
(.= this pen-state (quote up))))
(defmethod pen-down ((this turtle))
t
(.= this pen-state (quote down)))
(defmethod is-pen-down? ((this turtle))
t
(= (. this pen-state) (quote down)))
Die Methoden right und left sagen der Schildkröte, dass sie sich um den angegebenen Winkel im oder gegen den Uhrzeigersinn drehen soll.
(defmethod right ((this turtle) degrees)
t
(set-heading this
(- (. this heading) degrees)))
(defmethod left ((this turtle) degrees)
t
(set-heading this
(+ degrees (. this heading))))
Die Methode set-heading wird benutzt, wenn sich die Schildkröte in eine Richtung gemessen nach der x-Achse drehen soll.
(defmethod set-heading ((this turtle) heading)
t
(cond
((< heading 0)
(set-heading this (+ 360 heading)))
((< heading 360)
(.= this heading heading))
(t
(set-heading this (- heading 360)))))
Die Methode forward bewegt die Schildkröte in der aktuellen Richtung um die angegebene Distanz. Dafür werden einige Hilfmethoden und -funktionen benötigt.
(defmethod forward ((this turtle) distance)
t
(move-to this
(round
(+ (. this position)
(get-movement this distance)))))
Die Methode get-radiant rechnet die aktuelle Richtung von Grad (Vollkreis 360°) in Rad (Vollkreis 2 Pi) um.
(defmethod get-radiant ((this turtle))
t
(* 2/360 (. this *pi*) (. this heading)))
Die Methode get-movement benutzt den Cosinus und den Sinus der aktuellen Richtung und die Distanz, um einen Verschiebevektor für die Schildkröte zu berechnen. Der Verschiebevektor ist eine komplexe Zahl.
(defmethod get-movement ((this turtle) distance)
t
(* distance (cosine-sine (get-radiant this) 1e-30)))
Die Methoden round werden verwendet, um die Zielkoordinaten auf ganze Zahlen zu runden.
(defmethod round (x)
(number? x)
(floor (+ x 1/2)))
(defmethod round ((c complex))
t
(new complex
(round (. c real))
(round (. c imaginary))))
Die Methoden move-to bewegen die Schildkröte an die angegebenen Koordinaten. Die Koordinaten müssen ganze Zahlen sein.
(defmethod move-to ((this turtle) (next complex))
(and
(integer? (. next real))
(integer? (. next imaginary)))
(progn
(when (is-pen-down? this)
(.= this current-polyline
(aif
(. this current-polyline)
(cons next it)
(list next (. this position)))))
(.= this position next)))
(defmethod move-to ((this turtle) next-x next-y)
t
(move-to this (new complex next-x next-y)))
(defmethod move-to ((this turtle) next-x)
(number? next-x)
(move-to this next-x 0))
Die Methode get-picture wandelt den Weg der Schildkröte in ein darstellbares Bild um. Dabei liefert die Hilfmethode get-polylines alle Linienzüge und die Methode get-bounds bestimmt die Abmessungen des sich ergebenden Bildes. Die Funktion grid liefert anhand einer Liste von Linienzügen ein Objekt, das das Programm Calc als Bild darstellen kann.
(defmethod get-picture ((this turtle))
t
(let
((bounds (get-bounds this)))
(grid
(third bounds)
(fourth bounds)
(map-with
(lambda (polyline)
(map-with
(lambda (point)
(list (- (real point) (first bounds))
(- (imaginary point) (second bounds))))
polyline))
(get-polylines this)))))
(defmethod get-polylines ((this turtle))
t
(with-slots-read-only (current-polyline polylines) this
(if
current-polyline
(cons current-polyline polylines)
polylines)))
(defmethod get-bounds ((this turtle))
t
(let
((min-x nil) (min-y nil) (max-x nil) (max-y nil))
(dolist (polyline (get-polylines this) (list min-x min-y (- max-x min-x) (- max-y min-y)))
(dolist (point polyline)
(when (or (null? min-x) (< (real point) min-x))
(setq min-x (real point)))
(when (or (null? min-y) (< (imaginary point) min-y))
(setq min-y (imaginary point)))
(when (or (null? max-x) (> (real point) max-x))
(setq max-x (real point)))
(when (or (null? max-y) (> (imaginary point) max-y))
(setq max-y (imaginary point)))))))
Die Schildkröten-Grafik kann zusammen mit einem Lindenmayer-System (siehe Lindenmayer-Systeme) verwendet werden, um zum Beispiel eine Drachenkurve darzustellen.
Die Schildkröte wird als Instanz der Klasse turtle repräsentiert.
(defclass turtle ())
Eine neue Schildkröte
- befindet sich im Ursprung des Koordinatensystems (position = 0 als komplexe Zahl),
- schaut in Richtung der x-Achse (heading = 0) und
- benutzt ihren Stift zunächst nicht (pen-state = up).
Für die Umrechnung von Winkeln wird der Wert von Pi benötigt. Der Wert wird auf 30 Stellen genau bestimmt (siehe Pi berechnen oder Polarkoordinaten) und in der Instanzvariable *pi* abgelegt.
(defmethod initialize ((this turtle))
t
(progn
(.= this position 0)
(.= this heading 0)
(.= this pen-state (quote up))
(.= this current-polyline nil)
(.= this polylines nil)
(.= this *pi* (pi 1e-30))
this))
Mit den Methoden pen-up und pen-down gibt man der Schildkröte Befehle zum Heben und Senken des Malstifts. Mit is-pen-down? kann man ermitteln, wie die Schildkröte den Stift hält.
(defmethod pen-up ((this turtle))
t
(progn
(when (. this current-polyline)
(.= this polylines
(cons
(reverse (. this current-polyline))
(. this polylines)))
(.= this current-polyline nil))
(.= this pen-state (quote up))))
(defmethod pen-down ((this turtle))
t
(.= this pen-state (quote down)))
(defmethod is-pen-down? ((this turtle))
t
(= (. this pen-state) (quote down)))
Die Methoden right und left sagen der Schildkröte, dass sie sich um den angegebenen Winkel im oder gegen den Uhrzeigersinn drehen soll.
(defmethod right ((this turtle) degrees)
t
(set-heading this
(- (. this heading) degrees)))
(defmethod left ((this turtle) degrees)
t
(set-heading this
(+ degrees (. this heading))))
Die Methode set-heading wird benutzt, wenn sich die Schildkröte in eine Richtung gemessen nach der x-Achse drehen soll.
(defmethod set-heading ((this turtle) heading)
t
(cond
((< heading 0)
(set-heading this (+ 360 heading)))
((< heading 360)
(.= this heading heading))
(t
(set-heading this (- heading 360)))))
Die Methode forward bewegt die Schildkröte in der aktuellen Richtung um die angegebene Distanz. Dafür werden einige Hilfmethoden und -funktionen benötigt.
(defmethod forward ((this turtle) distance)
t
(move-to this
(round
(+ (. this position)
(get-movement this distance)))))
Die Methode get-radiant rechnet die aktuelle Richtung von Grad (Vollkreis 360°) in Rad (Vollkreis 2 Pi) um.
(defmethod get-radiant ((this turtle))
t
(* 2/360 (. this *pi*) (. this heading)))
Die Methode get-movement benutzt den Cosinus und den Sinus der aktuellen Richtung und die Distanz, um einen Verschiebevektor für die Schildkröte zu berechnen. Der Verschiebevektor ist eine komplexe Zahl.
(defmethod get-movement ((this turtle) distance)
t
(* distance (cosine-sine (get-radiant this) 1e-30)))
Die Methoden round werden verwendet, um die Zielkoordinaten auf ganze Zahlen zu runden.
(defmethod round (x)
(number? x)
(floor (+ x 1/2)))
(defmethod round ((c complex))
t
(new complex
(round (. c real))
(round (. c imaginary))))
Die Methoden move-to bewegen die Schildkröte an die angegebenen Koordinaten. Die Koordinaten müssen ganze Zahlen sein.
(defmethod move-to ((this turtle) (next complex))
(and
(integer? (. next real))
(integer? (. next imaginary)))
(progn
(when (is-pen-down? this)
(.= this current-polyline
(aif
(. this current-polyline)
(cons next it)
(list next (. this position)))))
(.= this position next)))
(defmethod move-to ((this turtle) next-x next-y)
t
(move-to this (new complex next-x next-y)))
(defmethod move-to ((this turtle) next-x)
(number? next-x)
(move-to this next-x 0))
Die Methode get-picture wandelt den Weg der Schildkröte in ein darstellbares Bild um. Dabei liefert die Hilfmethode get-polylines alle Linienzüge und die Methode get-bounds bestimmt die Abmessungen des sich ergebenden Bildes. Die Funktion grid liefert anhand einer Liste von Linienzügen ein Objekt, das das Programm Calc als Bild darstellen kann.
(defmethod get-picture ((this turtle))
t
(let
((bounds (get-bounds this)))
(grid
(third bounds)
(fourth bounds)
(map-with
(lambda (polyline)
(map-with
(lambda (point)
(list (- (real point) (first bounds))
(- (imaginary point) (second bounds))))
polyline))
(get-polylines this)))))
(defmethod get-polylines ((this turtle))
t
(with-slots-read-only (current-polyline polylines) this
(if
current-polyline
(cons current-polyline polylines)
polylines)))
(defmethod get-bounds ((this turtle))
t
(let
((min-x nil) (min-y nil) (max-x nil) (max-y nil))
(dolist (polyline (get-polylines this) (list min-x min-y (- max-x min-x) (- max-y min-y)))
(dolist (point polyline)
(when (or (null? min-x) (< (real point) min-x))
(setq min-x (real point)))
(when (or (null? min-y) (< (imaginary point) min-y))
(setq min-y (imaginary point)))
(when (or (null? max-x) (> (real point) max-x))
(setq max-x (real point)))
(when (or (null? max-y) (> (imaginary point) max-y))
(setq max-y (imaginary point)))))))
Die Schildkröten-Grafik kann zusammen mit einem Lindenmayer-System (siehe Lindenmayer-Systeme) verwendet werden, um zum Beispiel eine Drachenkurve darzustellen.