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.


turtle.sheet.xml

Anlage

Beispiele für Schildkröten-Grafik mit Calc