Penrose-Parkettierung

Penrose-Parkettierungen bedecken die Ebene mit wenigen verschiedenen Kacheln so, dass keine Translationssymmetrie entsteht. Das bedeutet, dass sich das Muster der Kacheln nicht periodisch wiederholt.

Besonders einfach ist die Parkettierung P3. Diese verwendet als Kacheln zwei verschiedene Rauten, eine mit einem Winkel von 36 Grad (dünne Raute) und eine mit einem Winkel von 108 Grad (dicke Raute). Diese Parkettierung lässt sich durch das Verfahren der Aufteilung berechnen. Dazu werden die beiden Kacheln in je zwei kongruente Dreiecke halbiert und diese Dreiecke werden rekursiv in weitere aufgeteilt.

Es wird später eine Methode benötigt, die den goldenen Schnitt zwischen zwei Punkten in der Ebene bestimmt. Punkte werden hier durch komplexe Zahlen repräsentiert. Der für den goldenen Schnitt nötige Wert der Quadratwurzel von 5 wird auf 30 Stellen genau berechnet (siehe Quadratwurzeln mit dem Halley-Verfahren berechnen).

(setq *golden-ratio* (/ (+ 1 (square-root 5 1e-30)) 2))

(defmethod golden-cut (r s)
  (and (number? r) (number? s))
  (+ r (/ (- s r) *golden-ratio*)))

(defmethod golden-cut (c d)
  (or (instance-of? c complex) (instance-of? d complex))
  (new complex
    (golden-cut (real c) (real d))
    (golden-cut (imaginary c) (imaginary d))))

Die Instanzen der Klasse triangle repräsentieren Dreiecke. Die Klasse hat zwei Konstruktoren. Der eine erwartet drei Punkte als Argumente. Der andere erzeugt gleichschenklige Dreiecke und verwendet die Seitenlänge, den Winkel zwischen den Schenkeln, einen Startpunkt und eine Startrichtung, um die Punkte des Dreiecks zu berechnen. Für die Berechnung wird die Schildkröten-Grafik verwendet.

(defclass triangle ())

(defmethod initialize ((this triangle) a b c)
  t
  (progn
    (.= this a a)
    (.= this b b)
    (.= this c c)
    (freeze this)))

(defmethod initialize ((this triangle) size angle x y heading)
  t
  (let
    ((that (new turtle)))
    (progn
      (move-to that x y)
      (set-heading that heading)
      (pen-down that)
      (forward that size)
      (right that (- 180 angle))
      (forward that size)
      (pen-up that)
      (let
        ((polyline (first (get-polylines that))))
        (initialize this
          (second polyline)
          (first polyline)
          (third polyline))))))

Die Methode mirror erzeugt aus einem Dreieck ein anderes, bei dem die beiden Schenkel vertauscht sind.

(defmethod mirror ((this triangle))
  t
  (new (class-of this) (. this a) (. this c) (. this b)))

Von der Klasse triangle werden zwei Subklassen abgeleitet. Instanzen von diesen Klassen repräsentieren die Dreiecke, die durch die Halbierung der dünnen bzw. dicken Kachel entstehen.

(defclass thin-triangle (triangle))

(defclass thick-triangle (triangle))

(defmethod initialize ((this thin-triangle) size x y heading)
  t
  (initialize this size 36 x y heading))

(defmethod initialize ((this thick-triangle) size x y heading)
  t
  (initialize this size 108 x y heading))

Die beiden Arten von Dreiecken werden von der rekursiven Aufteilung unterschiedlich behandelt: Aus einem dünnen Dreieck entstehen ein kleineres dünnes und ein kleineres dickes und aus einem dicken Dreieck entstehen zwei kleinere dicke und ein kleineres dünnes. In beiden Fällen spielt der goldene Schnitt eine Rolle.

(defmethod subdivide ((this thin-triangle))
  t
  (let
    ((p (golden-cut (. this a) (. this b))))
    (list
      (new thin-triangle (. this c) p (. this b))
      (new thick-triangle p (. this c) (. this a)))))

(defmethod subdivide ((this thick-triangle))
  t
  (let
    ((q (golden-cut (. this b) (. this a)))
     (r (golden-cut (. this b) (. this c))))
    (list
      (new thick-triangle r (. this c) (. this a))
      (new thick-triangle q r (. this b))
      (new thin-triangle r q (. this a)))))

Die Kernidee des Verfahrens besteht darin, die Dreiecke wiederholt aufzuteilen. Das macht die Funktion repeated-subdivide für die in einer Liste übergebenen Dreiecke.

(defproc repeated-subdivide (triangles count)
  (dotimes (i count triangles)
    (setq triangles
      (apply
        append
        (map-with subdivide triangles)))))

Schließlich werden die Dreiecke aus der resultierenden Liste mit der Schildkröten-Grafik gezeichnet.

(defproc draw-all (triangles)
  (let
    ((this (new turtle)))
    (dolist (triangle triangles (get-picture this))
      (move-to this (round (. triangle c)))
      (pen-down this)
      (move-to this (round (. triangle a)))
      (move-to this (round (. triangle b)))
      (pen-up this))))

Wenn man mit den beiden Dreiecken @a2

(new thin-triangle 1000 0 0 0)

und @b2

(mirror (new thin-triangle @a1 (real (. @a2 c)) (imaginary (. @a2 c)) 180))

startet, dann ergibt

(draw-all (repeated-subdivide (list @a2 @b2) 7))

die unten abgebildete Parkettierung.

Die verlinkte Datei penrose.sheet.xml enthält die hier beschriebenen Klassen, Methoden und Funktionen und kann mit dem Programm Calc ausgeführt werden.

Links
https://de.wikipedia.org/wiki/Penrose-Parkettierung
http://preshing.com/20110831/penrose-tiling-explained/


penrose.sheet.xml

Anlage

Penrose Parkettierung P3