Sternenhimmel

Im World Wide Web kann man Listen mit den Positionen der hellsten Sterne finden. Eine von diesen ist eine CSV-Datei, in der die ersten Zeilen so aussehen:

  1;Sirius;06h 45m;-16.7°
  2;Canopus;06h 24m;-52.7°

Die Funktion csv-to-list zerlegt den Text aus der Datei an den Stellen, an denen ein Semikolon oder ein Zeilenumbruch steht und wandelt die Einzelbestandteile in eine Liste von Listen von Zeichenketten um.
 
(defproc csv-to-list (csv-text separator)
  (let
    ((row nil)
     (rows nil))
    (dolist (token (tokenize-string csv-text (concatenate separator (code-char 10) (code-char 13)) t) (reverse rows))
      (cond
        ((or (= token (code-char 10))
          (= token (code-char 13)))
          (when row
            (push (reverse row) rows)
            (setq row nil)))
        ((not (= token separator))
          (push token row))))))

Im zweiten Schritt braucht es eine Funktion, die die Liste von Listen von Zeichenketten in eine Form bringt, die es ermöglicht, Sterne anhand ihres Namens auszuwählen und auf der Himmelskugel darzustellen. Das erledigt die Funktion strings-to-radians mit ihren Hilfsfunktionen hour-and-minute-to-radian und degrees-to-radian.

(defproc strings-to-radians (rows)
  (map-with
    (lambda (row)
      (list
        (read-from-string (first row))
        (second row)
        (hour-and-minute-to-radian (third row))
        (degrees-to-radian (read-from-string (fourth row)) 1e-4)))
    rows))

Die Hilfsfunktion hour-and-minute-to-radian wandelt Zeichenketten wie "06h 45m" ins Bogenmaß um.

(defproc hour-and-minute-to-radian (string)
  (if
    (and
      (= (substring string 3 1) "h")
      (= (substring string 4 1) " ")
      (= (substring string 7 1) "m"))
    (+ (* 2/24 *pi* (read-from-string (substring string 1 2)))
       (* 2/1440 *pi* (read-from-string (substring string 5 2))))
    (throw (quote error) (concatenate "unexpected: " string))))

Auf dem Resultat von strings-to-radians arbeitet die Funktion get-declination-and-rectascension. Sie sucht aus der Liste den Eintrag für den Stern mit dem angegebenen Namen und liefert dessen Deklination und Rektaszension.

(defproc get-declination-and-rectascension (star-name data-rows)
  (aif (find-if
      (lambda (data-row) (= (second data-row) star-name))
      data-rows)
    (list
      (fourth it)
      (third it))
    (throw (quote error) (concatenate "not a known star: " star-name))))

Diese beiden Angaben rechnet die Funktion spherical-to-cartesian in kartesische Koordinaten um. Dabei werden Cosinus und Sinus benötigt.

(defproc spherical-to-cartesian (d r)
  (let
    ((dxy (get-xy d 1e-4))
     (rxy (get-xy (- r) 1e-4)))
    (list
      (* (first dxy) (second rxy))
      (second dxy)
      (* (first dxy) (first rxy)))))

Die Konstante *star-path* enthält dem Umriss der gezeichneten Sterne.

(setq *star-path*
  (map-with
    (lambda (i)
      (let
        ((xy (get-xy (* 1/5 i *pi*) 1e-4))
         (radius (if (even? i) 0.04 0.02)))
        (list
          (* radius (first xy))
          (* radius (second xy)))))
    (list 0 1 2 3 4 5 6 7 8 9 0)))

(defproc even? (n)
  (integer? (/ n 2)))

Die Funktion make-star liefert ein Polygon für den Stern mit dem angegebenen Namen.

(defproc make-star (star-name data-rows)
  (let
    ((spherical-position (get-declination-and-rectascension star-name data-rows)))
    (map-with
      (lambda (translation)
        (spherical-to-cartesian
          (+ (first spherical-position) (first translation))
          (+ (second spherical-position) (second translation))))
      *star-path*)))

Die Funktion make-heaven liefert Polygone für Längen- und Breitenkreise.

(defproc make-heaven ()
  (list
    (make-circle-of-longitude 0 1 40 1e-4)
    (make-circle-of-longitude 30 1 40 1e-4)
    (make-circle-of-longitude 60 1 40 1e-4)
    (make-circle-of-latitude -60 1 40 1e-4)
    (make-circle-of-latitude -30 1 40 1e-4)
    (make-circle-of-latitude 0 1 40 1e-4)
    (make-circle-of-latitude 30 1 40 1e-4)
    (make-circle-of-latitude 60 1 40 1e-4)))

Schließlich kann die Funktion project-stars eine Zeichnung erstellen, die die Längen- und Breitenkreise und die Sterne, deren Namen in der Liste star-names enthalten sind, darstellt. Hier wird die Funktion draw-projected-polylines verwendet, die eine Zentralprojektion der 3D-Polygone durchführt.
 
(defproc project-stars (star-names data-rows)
  (draw-projected-polylines
    (list 1 0 0)
    (list 0 1 0)
    (list 0 0 0)
    (list -4 2 -20)
    (append
      (make-heaven)
      (map-with
        (lambda (star-name) (make-star star-name data-rows))
        star-names))))


stars.sheet.xml

Anlage

Sterne des großen Wagens