13. Module - module() - Programmblöcke
In den letzten Kapiteln haben wir zwei Schachfiguren und das Schachbrett programmiert. Wie wäre es, alles in einem Bild zusammenzuführen – ein Schachbrett mit allen Figuren?
Ein Schachspiel besteht aus 32 Figuren (16 weiße und 16 schwarze) sowie dem Brett. Bisher haben wir den Bauern, den Turm und das Brett programmiert. Obwohl noch einiges fehlt, ergibt das Zusammenfügen dieser Codeteile bereits einen unübersichtlichen „Bandwurm“.
Um den Code modular und wiederverwendbar zu gestalten, kennt OpenSCAD das module-Konstrukt. Dabei handelt es sich um wiederverwendbare Code-Blöcke, die bei Bedarf aufgerufen werden können.
13.1 module xxx()
Dem Schlüsselwort module folgt der gewünschte Name für das Modul, anschließend eine leere Klammer (). Der gesamte Modulbereich wird – wie bei anderen Befehlen – mit geschweiften Klammern {} abgegrenzt.
Im Beispiel erstellen wir drei Module: schachbrett(), bauer() und turm(). Jedes Modul wird mit {} abgegrenzt.
module schachbrett() {
//... hier folgt der Code
}
module bauer() {
//... hier folgt der Code
}
module turm() {
//... hier folgt der Code
}
Nach einem F5 geschieht... gar nichts.

Ja, klar – in den Modulen steht bisher noch kein Code. Kopieren wir also den Code des Schachbretts in das entsprechende Modul. Dabei übernehmen wir auch die drei Kopfzeilen.
$fn=60;
feld=20;
dick=2;
module schachbrett() {
//... hier den Code einfügen !!
}
Nach einem F5 geschieht... immer noch nichts. Da fehlt noch etwas!

13.2 Aufruf eines Moduls
Das erste Modul ist zwar mit allen Daten vorbereitet, wird aber noch nicht aufgerufen. Um es zu verwenden, genügt es, außerhalb des Moduls einfach seinen Namen mit anschließendem () zu schreiben.
schachbrett();
Nach einem F5... erscheint wieder unser Schachbrett.

Der Modul-Aufruf kann vor oder nach dem eigentlichen Modul-Code stehen. Bei vielen Programmiersprachen muss der Modul-Code zunächst definiert werden, bevor das Modul aufgerufen werden kann.
13.3 Modul Bauer
Holen wir uns den Code des Bauern und kopieren ihn in das entsprechende Modul. Da der Bauer-Code auf der Konstanten skale basiert, muss auch diese Zeile mitkopiert werden. Wie beim Schachbrett benötigen wir auch hier den eigentlichen Modul-Aufruf – also bauer();.
skale=1;
bauer();
module bauer() {
//... hier den Code einfügen !!
}
Die Zeile skale = 1; steht im Beispiel direkt vor dem Modul bauer(). Besser wäre es, diese Zeile ebenfalls im Kopfbereich des Skripts zu definieren.

Oh, der Bauer steht am falschen Ort und hat die falsche Farbe. Wir brauchen ja weisse und schwarze Bauern, die an der Startposition des Spiels stehen sollten.
13.4 Modul mit Übergabewerten
Module können mit Übergabewerten versehen werden – wir sprechen hier von Parametern. Im Beispiel benötigen wir x, y, z sowie die Farbe. Ergänzen wir daher das Modul mit folgendem Code:
module bauer(x,y,z,farbe) {
//... hier den Code einfügen !!
}
Natürlich müssen diese Parameter im Modul auch angewendet werden. Als Erstes ändern wir einfach green in farbe.
module bauer(x,y,z,farbe) {
color(farbe) {
union(){
//... hier folgt der Code
}
Nun ist der Bauer gelb!?

Beim Aufruf des Moduls müssen wir natürlich noch die Parameter einfügen. Dabei schreiben wir gleich auch die x,y,z- Werte.
bauer(10,20,5,"white");
Wichtig: Werden die Parameter nur als Werte übergeben, müssen sie in derselben Reihenfolge wie bei der Modul-Definition angegeben werden.

Die x,y,z Werte werden im Modul noch nicht berücksichtigt. Ergänzen wir den Code mit einem translate([x, y, z]). Dabei müssen wir die geschweiften Klammern {} beachten: Jede öffnende Klammer muss eine Gegenklammer haben, um Fehlermeldungen zu vermeiden.
module bauer(x,y,z,farbe) {
translate([x,y,z]) {
...
}
}
Wenn das Beispiel im Display gedreht wird, sehen wir, dass der z-Wert mit 5 nicht korrekt ist – hier sollte stattdessen die Konstante dick verwendet werden.

13.5 Schleife für alle weissen Bauern
Der Bauer steht aber immer noch ziemlich „im Schilf“. Wir haben ja noch die Konstante feld die die genaue Position eines bestimmten Feldes festlegt. Warum nicht mit feld und einer for()-Schlaufe gleich alle acht Bauern erstellen?
for (i = [0:1:7]) {
bauer(i*feld,1*feld,dick,"white");
}
Knapp daneben ist auch daneben: Die Bauern stehen noch um ein halbes Feld verschoben auf dem Brett.

Es reicht nicht, nur i feld zu verwenden – wir müssen noch ein halbes Feld dazuaddieren. Da "Punkt vor Strich" gilt, müssen wir Klammern um die Multiplikation und Division setzen: (i feld) + (feld / 2).
for (i = [0:1:7]) {
bauer((i*feld)+(feld/2),(1*feld)+(feld/2),dick,"white");
}
Sehr schön!!

Gleich noch die schwarzen Bauern: Sie stehen ja auf der siebten Reihe von vorne. Da die erste Reihe die Reihe 0 ist ergibt die siebte Reihe 6feld.
for (i = [0:1:7]) {
bauer((i*feld)+(feld/2),(6*feld)+(feld/2),dick,"black");
}
Das Schwarz der Felder und das Schwarz der Figuren können leicht unterschiedlich sein. Wenn beide 100 % schwarz sind, lassen sich die Figuren schlecht vom Brett abheben.
Imn Kapitel 2 haben wir ja bereits die Farben behandelt. Als Variante könnte zum Beispiel dimgray verwendet werden.

13.6 Der Turm
Von den bereits erstellten Figuren fehlt noch der Turm.
Beim Turm wurden eine ganze Reihe von Konstanten definiert. Wir müssen diese daher auch mit dem eigentlichen Code kopieren. Im folgenden Codebeispiel habe ich nur den Aufruf des Turm-Moduls mit translate() und der geänderten color(farbe) geschrieben. Der gesamte Rest kann 1:1 übernommen werden.
turm(0,0,0,"white");
module turm(x,y,z,farbe) {
translate([x,y,z]) {
color(farbe) {
...
}
}
}
Der ganze Code ist im Anhang hinterlegt.

Auch die Position des Turms müsste genauer berechnet werden.Statt die genaue Position beim Aufruf zu berechnen, könnten wir stattdessen einfach die Feldnummer übergeben – die Berechnung erfolgt dann im Turm-Modul.
Der erste weiße Turm steht auf Feld 1,1, der zweite auf 8,1.Der erste schwarze Turm steht auf 1,8, der zweite auf 8,8.
Sicher habt ihr bemerkt: Die Felder im Beispiel beginnen nicht bei 0, sondern bei 1. Wir könnten natürlich auch (0,0) verwenden – aber hier soll das erste Feld wirklich das erste Feld sein.
Bei der Modul-Definition wird z entfernt – es wird stattdessen direkt im translate() mit der Konstanten dick festgelegt.
Die Formel für die x und y verschiebung wird nun etwas komplizierter: Da x und y bei 1 beginnen, müssen wir zunächst 1 abziehen und mit feld multiplizieren. Wegen "Punkt vor Strich" setzen wir das in Klammern:
(x-1)*feld
Nun muss noch ein halbes Feld dazugerechnet werden. Da Punkt vor Strich gerechnet wird, braucht es keine weiteren Klammern.
(x-1)*feld+feld/2
Wir können aber auch folgendes schreiben:
(x-1)*feld+(feld/2)
oder
((x-1)*feld)+(feld/2)
Das Resultat ist immer dasselbe.
turm(1,1,"white");
module turm(x,y,farbe) {
translate([(x-1)*feld+feld/2,(y-1)*feld+feld/2,dick]) {
...
Macht sich schon recht gut!

Die weiteren Türme sind nun ein Kinderspiel.
turm(1,1,"white");
turm(8,1,"white");
turm(1,8,"dimgray");
turm(8,8,"dimgray");
Wie ein richtiges Schachspiel – nur fehlen nur noch die weiteren Figuren.Das Beispiel zeigt aber, wie Programmblöcke in Module zusammengefasst und anschließend wiederverwendbar eingesetzt werden.
