At My Fingertips

Schweizer Bahnhofsuhr

Die Schweizer Bahnhofsuhr ist Kult! Eine technische Zeichnung mit der Originalkonstruktion findest Du auf der Webseite des Museums für Gestaltung Zürich (klicke den Pfeil nach rechts um zum zweiten Bild zu gelangen).

Photo of Swiss Railway clock

JuergenG, modified by Rainer Z, CC BY-SA 3.0, via Wikimedia Commons

Lass uns eine Funktion entwickeln, mit der man eine Uhr bauen kann, die eine beliebige Uhrzeit anzeigt:

def uhr(stunden: int, minuten: int, sekunden: int) -> Grafik:
  return ...

Dekomposition der Uhr

Die Uhr könnte zum Beispiel in die folgende Teilgrafiken zerlegt werden:

  • Hintergrund (weiss, mit grauem Rand)
  • Kleinere Striche für Minuten
  • Grössere Striche für Stunden
  • Stundenzeiger (fett und kurz)
  • Minutenzeiger (schlank und lang)
  • Sekundenzeiger (rot)

Die obigen Bilder der einzelnen Teilgrafiken zeigen auch die Bounding Box (roter Rahmen) und die Fixierpositionen (oranges Kreuz) an. Die Bounding Box ist das kleinste horizontale Rechteck das alle Teile, auch möglicherweise transparente Teile, umschliesst. Die Fixierposition ist der Punkt, and dem zwei Grafiken beim Kombinieren ausgerichtet und überlagert werden. Er kann mit fixiere definiert werden, und wird von kombiniere verwendet.

Eine Funktion für jede Komponente

Du könntest für das Erstellen der obigen Teilgrafiken zum Beispiel die folgenden Funktionen entwickeln:

def hintergrund() -> Grafik:
  return ...

def minuten_striche() -> Grafik:
  return ...

def stunden_striche() -> Grafik:
  return ...

def stunden_zeiger() -> Grafik:
  return ...

def minuten_zeiger() -> Grafik:
  return ...

def sekunden_zeiger() -> Grafik:
  return ...

Du kannst somit jede Funktion (jede Teilgrafik) unabhängig entwickeln und anschauen.

Hintergrund

Tipp 1: Den grauen Rand kann man generieren, indem man den weissen Kreis einem etwas grösseren grauen Kreis überlagert.

Tipp 2: Falls Du bereits eine kreis Funktion in Deiner Toolbox hast, kannst Du diese importieren und verwenden (from toolbox import kreis).

Loading...

Erzeugen der Zeiger

Was steht im Zentrum jeder Uhr? Eine Achse! Eine Achse um die sich die Zeiger drehen.

Mehr als bloss ueberlagern

Wenn wir genau hinschauen sehen wir, dass wir viele der obigen Komponenten der Uhr zusammenbauen können, indem wir diese in ihrem Zentrum überlagern. Sowohl der Hintergrund als auch die Kreise mit den Minutenstrichen und den Stundenstrichen sind zentriert.

In einer echten Uhr hätten wir im Zentrum eine Achse. In unserer PyTamaro Grafik ist dies ein guter Punkt für die Fixierpositionen der zu komponierenden Grafiken.

Wenn wir den Hintergrund und die zwei Strich-Kreise zusammenbauen wollen, so können wir dies mit ueberlagere tun. Dabei werden die Grafiken in ihren Zentren überlagert:

ueberlagere(minutenstriche, stundenstriche)

Wir können aber auch die etwas generelleren Funktionen fixiere und kombiniere verwenden: zuerst verwenden wir fixiere um Grafiken mit Fixierungspunkten in ihrem Zentrum zu erhalten. Danach verwenden wir kombiniere um zwei solche Grafiken an ihren Fixierungspunkten übereinanderzulegen:

kombiniere(
  fixiere(mitte, mitte, minutenstriche),
  fixiere(mitte, mitte, stundenstriche)
)

Beide Ansätze führen zum selben Bild, aber der Ansatz mit kombiniere und fixiere bietet mehr Flexibilität: wir können die Fixierpositionen auf andere Orte als auf die Mitte der Grafik legen!

Zeiger zusammenbauen

In einer echten Uhr sind auch die Zeiger an der Achse montiert. Die Achse geht an einem bestimmten Punkt durch den Zeiger durch. Wär's nicht schön, wenn wir dies auch hier so tun könnten? Vielleicht indem wir einen Zeiger mit einer entsprechenden Fixierposition bauen, bevor wir diesen auf die Achse (oder den Rest der Uhr) stecken?

Fixiertes langes Teil:

Fixiertes kurzes Teil:

Zeiger:

Und wir können! Wie? Wir können einen Zeiger als aus zwei Teilen gebaut anschauen, dem langen Teil über der Achse, und dem kurzen Teil unter der Achse.

Wenn wir den Zeiger aus diesen zwei Teilen zusammenbauen, indem wir kombiniere und fixiere verwenden, dann kriegen wir einen Zeiger mit der Fixierposition genau in der Mitte der Naht zwischen den zwei Teilen:

kombiniere(
  fixiere(mitte, unten_mitte, langes_teil),
  fixiere(mitte, oben_,mitte, kurzes_teil)
)

Die obige Abbildung zeigt die zwei fixierten Teile (mit den Fixierpositionen) und das Ergebnis des Kombinierens, den Zeiger (mit seiner Fixierposition). Nun können wir den Zeiger an dieser Fixierposition auf die Achse stecken. Oder ihn vorher noch um diese Fixierposition drehen.

Loading...
Loading...
Loading...

Zeiger drehen

Wenn Du gemäss der obigen Anleitung einen Zeiger baust, zeigt dieser gerade nach oben.

Wie können wir den Zeiger drehen so dass er die gewünschte Zeit anzeigt?

Die Funktion drehe dreht eine Grafik um deren Fixierposition. Sie tut das im Gegenuhrzeigersinn. Wir möchten den Zeiger aber im Uhrzeigersinn drehen. Dazu können wir der Funktion drehe einen negativen Winkel übergeben. Zum Beispiel drehe(-30, g) dreht die Grafik g um 30 Grad im Uhrzeigersinn.

Wenn Du also zum Beispiel einen Minutenzeiger auf "5 Minuten" stellen willst, musst Du ihn um 5 Minuten * 6 Grad/Minute = 30 Grad im Uhrzeigersinn drehen (bei 360 Grad für eine volle Drehung braucht es für jede der 60 Minuten 6 Grad).

Loading...
Loading...
Loading...

Alle Zeiger auf Hintergrund

Lass uns nun die drei Zeiger und den Hintergrund kombinieren, um die Uhrzeit 5:10:55 anzuzeigen:

Loading...

Erzeugen der Striche

Wie können wir einen Stunden-Strich oder einen Minuten-Strich an der richtigen Position an der Uhr anbringen?

Wir könnten einen Strich als eine Art Zeiger ansehen, nur dass der Strich nicht bis ins Zentrum der Uhr geht (die Achse geht nicht durch den Strich).

Wir können allerdings einen Strich (ähnlich wie einen Zeiger) auch als Komposition von zwei Teilen anschauen. Nur ist eines der zwei Teile durchsichtig! Wir haben also ein durchsichtiges Teil vom Zentrum der Uhr bis zum inneren Radius des Strichs, und dann ein schwarzes Teil von dort bis zum äusseren Radius des Strichs.

Fixierter Strich: Fixierter Abstand: Strich mit Abstand: Unten fixiert:

Wir können die zwei Teile (den schwarzen Strich und den transparenten Abstand) auf die gleiche Weise fixieren und zusammenbauen wie die Teile eines Zeigers:

strich_mit_abstand = kombiniere(
  fixiere(mitte, unten_mitte, strich),
  fixiere(mitte, oben_mitte, abstand)
)

Nun müssen wir nur noch den Fixierpunkt auf das untere Ende legen, und dann sind wir bereit, den Strich um einen beliebigen Winkel zu drehen und auf die Achse zu stecken.

fixiere(mitte, unten_mitte, strich_mit_abstand)

Nun brauchen wir nur noch eine genügende Anzahl Striche, jeder mit einem anderen Drehwinkel. Die Stunden-Striche bauen wir auf die gleiche Weise.

Loading...
Loading...

Komposition der Einzelteile in eine Uhr

Sobald Du die obigen Funktionen (unabhänging voneinander) implementiert und getestet hast, kannst Du die von ihnen erzeugten Teilgrafiken zusammenbauen.

Loading...

Refactoring

Obwohl Dein Code nun funktioniert, könnte er wahrscheinlich noch verbessert werden. Hier ein paar Vorschläge:

  • Siehst Du Gemeinsamkeiten beim Erzeugen des Minuten- und des Stunden-Zeigers? Könnte man diese in eine Funktion auslagern?

  • Siehst Du Gemeinsamkeiten bei den Winkel-Berechnungen? Könnte man diese in eine Funktion auslagern?

  • Siehst Du Gemeinsamkeiten bei den Minuten- und den Stunden-Strichen? Könnte man diese in eine Funktion auslagern?

Die Vorschläge beziehen sich alle auf das Finden und Eliminieren ähnlicher Code-Stücke ("Code Clones"). Guter Code enthält praktisch keine Clones. Ähnliche Code-Stücke werden durch eine Funktion ersetzt. Für die möglichen Unterschiede in den ähnlichen Code-Stücken werden Parameter eingeführt.

Versuche möglichst alle Clones in Deinem obigen Code zu elimineren.


This activity has been created by LuCE Research Lab and is licensed under CC BY-SA 4.0.

Schweizer Bahnhofsuhr

Logo of PyTamaro

PyTamaro is a project created by the Lugano Computing Education Research Lab at the Software Institute of USI

Privacy PolicyPlatform Version b744b47 (Tue, 08 Oct 2024 16:30:14 GMT)