Zum Inhalt springen

OpenSCAD Benutzerhandbuch/Schleifen und Bedingungen

Aus Wikibooks

For-Schleife

[Bearbeiten]

Wertet jeden Wert in einem Bereich oder Vektor (bzw. jeden Namen in einem Objekt) aus und wendet ihn auf die folgende Aktion an.

for(variable = [start : increment : end])
for(variable = [start : end])
for(variable = [vector])
for(variable = object)

Für jeden Wert in einem Bereich

[Bearbeiten]
for (variable = [ start : increment : end ])
for (variable = [ start : end ])

Hinweis: In Bereichen werden Werte durch Doppelpunkte getrennt – nicht durch Kommas wie in Vektoren!

Die Aktion wird für jeden Wert im Bereich ausgeführt.

start – Startwert
increment (optional, Standard = 1) – Schrittweite
end – Ende (wird nicht überschritten)
Beispiele:
for (a = [3:5]) echo(a);        // 3 4 5
for (a = [3:0]) echo(a);         // ungültig (start > end), ab 2015.3 veraltet
for (a = [3:0.5:5]) echo(a);    // 3 3.5 4 4.5 5
for (a = [0:2:5]) echo(a);      // 0 2 4   (5 wird nicht erreicht!)
for (a = [3:-2:-1]) echo(a);    // 3 1 -1  (negative Schritte erst ab 2015.3)

Für jedes Element eines Vektors

[Bearbeiten]

Die Aktion wird für jedes Element des Vektors ausgeführt.

for (a = [3,4,1,5]) echo(a);               // 3 4 1 5
for (a = [0.3,PI,1,99]) echo(a);           // 0.3 3.14159 1 99
x1=2; x2=8; x3=5.5;
for (a = [x1,x2,x3]) echo(a);              // 2 8 5.5 
for (a = [[1,2],6,"s",[[3,4],[5,6]]]) echo(a); // [1,2] 6 "s" [[3,4],[5,6]]

Der Vektor kann auch extern definiert werden – wie eine „for each“-Schleife in anderen Sprachen:

animals = ["elephants", "snakes", "tigers", "giraffes"];
for(animal = animals)
  echo(str("I've been to the zoo and saw ", animal));
// → Ein Satz pro Tier

Für jedes Element eines Objekts

[Bearbeiten]

Vorlage:Requires

Die Aktion wird für jeden Schlüsselnamen des Objekts ausgeführt (Reihenfolge nicht garantiert):

tm = textmetrics("Hello, World!");
for (name = tm) echo(name, tm[name]);

Wichtige Hinweise

[Bearbeiten]

"for()" ist ein Operator – bei mehreren Aktionen brauchst du "{}". Aktionen enden mit ";", Operatoren nicht.

Obwohl OpenSCAD keine echte Schleifensprache ist, bekommt jede Iteration ihren eigenen Gültigkeitsbereich – so kannst du innerhalb der Schleife Variablen frei nutzen. Aber: Du kannst nicht "a = a + 1" schreiben – das geht nicht!

Intern baut "for()" einen Baum von Objekten, nicht eine Schleife im klassischen Sinn:

for (i=[0:3])
  translate([i*10,0,0])
    cube(i+1);

Erzeugt vier unabhängige Zweige im CSG-Baum – nicht nacheinander ausgeführten Code!

Verschachtelte for()-Schleifen

[Bearbeiten]

Du kannst mehrere "for()"-Schleifen verschachteln:

for(z=[-180:45:+180])
  for(x=[10:5:50])
    rotate([0,0,z]) translate([x,0,0]) cube(1);

Oder noch kürzer: Alle Bereiche in einer "for()"-Anweisung:

// Langversion:
color_vec = ["black","red","blue","green","pink","purple"];
for (x = [-20:10:20] )
for (y = [0:4] ) color(color_vec[y])
for (z = [0,4,10] )
    {translate([x,y*5-10,z]) cube();}

// Kurzversion:
color_vec = ["black","red","blue","green","pink","purple"];
for (x = [-20:10:20],
     y = [0:4],
     z = [0,4,10] )
    translate([x,y*5-10,z]) { color(color_vec[y]) cube(); }
Dreifach verschachtelte for()-Schleife
Beispiele mit Vektor von Vektoren
Beispiel 1: Rotation aus Vektor von Vektoren
// Beispiel 1 – Rotation
for(i = [ [  0,  0,   0],
          [ 10, 20, 300],
          [200, 40,  57],
          [ 20, 88,  57] ])
{
  rotate(i)
  cube([100, 20, 20], center = true);
}
Beispiel 2: Translation aus Vektor von Vektoren
// Beispiel 2 – Translation
for(i = [ [ 0,  0,  0],
          [10, 12, 10],
          [20, 24, 20],
          [30, 36, 30],
          [20, 48, 40],
          [10, 60, 50] ])
{
  translate(i)
  cube([50, 15, 10], center = true);
}
Beispiel 3: Komplexer Vektor von Vektoren
// Beispiel 3
for(i = [ [[ 0,  0,  0], 20],
          [[10, 12, 10], 50],
          [[20, 24, 20], 70],
          [[30, 36, 30], 10],
          [[20, 48, 40], 30],
          [[10, 60, 50], 40] ])
{
  translate([i[0][0], 2*i[0][1], 0])
  cube([10, 15, i[1]]);
}

Intersection For Loop

[Bearbeiten]

Iteriert über einen Bereich oder Vektor und erzeugt den Durchschnitt (Schnittmenge) aller Objekte, die in den einzelnen Durchläufen entstehen.

Während die normale "for()"-Schleife intern eine implizite Vereinigung ("union") aller Instanzen erzeugt, ersetzt "intersection_for()" diese durch eine Schnittmenge ("intersection"). Das ist nötig, weil "intersection() for() ..." nicht das gewünschte Ergebnis liefert.

"intersection_for()" verwendet dieselben Parameter wie "for()" – nur das Ergebnis ist anders.

Beispiel 1 – Bereich:
intersection_for(n = [1 : 6])
{
    rotate([0, 0, n * 60])
    {
        translate([5,0,0])
        sphere(r=12);
    }
}
intersection_for()
Falsch: intersection() for()
Beispiel 2 – Rotation:
intersection_for(i = [ [  0,  0,   0],
                       [ 10, 20, 300],
                       [200, 40,  57],
                       [ 20, 88,  57] ])
{
    rotate(i)
    cube([100, 20, 20], center = true);
}
intersection_for()
Falsch: intersection() for()

If-Anweisung

[Bearbeiten]

Prüft eine Bedingung und führt Aktionen nur aus, wenn sie wahr ist.

WICHTIG: Du kannst keine bestehenden Variablen ändern! Eine Zuweisung innerhalb von "{}" erzeugt eine neue lokale Variable, die nach dem Block wieder verschwindet.

if (test) scope1
if (test){scope1}
if (test) scope1 else scope2
if (test){scope1} else {scope2}
Parameter
test: Meist ein boolescher Ausdruck (aber jeder Wert ist erlaubt).
Wann ist ein Wert „wahr“?
Vergleichsoperatoren
Verwechsle Zuweisung "=" nicht mit Gleichheit "=="!
scope1: Aktion(en), wenn "test" wahr ist.
scope2: Aktion(en), wenn "test" falsch ist.

Beispiele:

if (b==a) cube(4);
if (b<a) {cube(4); cylinder(6);}
if (b&&a) {cube(4); cylinder(6);}
if (b!=a) cube(4); else cylinder(3);
if (b) {cube(4); cylinder(6);} else {cylinder(10,5,5);} 
if (!true){cube(4); cylinder(6);} else cylinder(10,5,5); 
if (x>y) cube(1, center=false); else {cube(size = 2, center = true);}
if (a==4) {} else echo("a is not 4");
if ((b<5)&&(a>8)) {cube(4);} else {cylinder(3);}
if (b<5&&a>8) cube(4); else cylinder(3);

Ab Version 2015.03 darfst du Variablen in jedem Block zuweisen – aber sie bleiben lokal (siehe Gültigkeitsbereich).

Verschachteltes if

Du kannst "if"-Anweisungen beliebig tief verschachteln:

if (test1) 
{
  scope1 
  if (test2) {scope2.1}
  else {scope2.2}
}
else
{
  scope2 
  if (test3) {scope3.1}
  else {scope3.2}
}

Wenn ein Block nur aus einem "if" besteht, kannst du die äußeren Klammern weglassen:

if (test1)
  if (test2) {scope2.1}
  else {scope2.2}
else
  if (test3) {scope3.1}
  else {scope3.2}

else if

[Bearbeiten]

So kannst du mehrere Bedingungen prüfen – die erste wahre wird ausgeführt, alle anderen übersprungen:

if(test1) {scope1}
else if(test2) {scope2}
else if(test3) {scope3}
else if(test4) {scope4}
else {scope5}

Beachte: "else" und "if" sind zwei separate Wörter.

Beispiel
if((k<8)&&(m>1)) cube(10);
else if(y==6) {sphere(6);cube(10);}
else if(y==7) color("blue") sphere(5);
else if(k+m!=8) {cylinder(15,5,0);sphere(8);}
else color("green") {cylinder(12,5,0);sphere(8);}

Bedingungsoperator ? :

[Bearbeiten]

Ein ternärer Operator, der basierend auf einer Bedingung einen von zwei Werten zurückgibt.

a = test ? TrueValue : FalseValue;
echo(test ? TrueValue : FalseValue);
Parameter
test: Beliebiger Ausdruck („falsy“ oder „truthy“).
TrueValue: Wert, wenn "test" wahr ist.
FalseValue: Wert, wenn "test" falsch ist.

Das funktioniert wie der "?:"-Operator in C, Java etc.

Beispiele
a=1; b=2; c= a==b ? 4 : 5;                    // c = 5
a=1; b=2; c= a==b ? "a==b" : "a!=b";          // c = "a!=b"
   
TrueValue = true; FalseValue = false;
a=5; test = a==1;
echo(test ? TrueValue : FalseValue);            // false
   
L = 75; R = 2; test = (L/R)>25;
TrueValue = [test,L,R,L/R,cos(30)];
FalseValue = [test,L,R,sin(15)];
a1 = test ? TrueValue : FalseValue;             // [true, 75, 2, 37.5, 0.866025]

Rekursive Funktionsaufrufe

[Bearbeiten]

Rekursion wird unterstützt – aber es gibt ein internes Limit, um Abstürze zu vermeiden. Wird es erreicht, liefert die Funktion "undef".

Beispiel: Summe eines Vektors berechnen:

function sumv(v, i, s = 0) = (i == s ? v[i] : v[i] + sumv(v, i-1, s));
vec=[10,20,30,40];
echo("sum vec=", sumv(vec, 2, 1)); // → 20+30 = 50

Komplexe Ausdrücke übersichtlich formatieren

[Bearbeiten]

Verschachtelte Bedingungen lassen sich wie "if/else"-Blöcke einrücken:

// Maximum eines Vektors finden
function maxv(v, m=-999999999999, i=0) = 
    (i == len(v)) 
    ? m 
    : (m > v[i]) 
      ? maxv(v, m, i+1) 
      : maxv(v, v[i], i+1);

v=[7,3,9,3,5,6];
echo("max",maxv(v));   // ECHO: "max", 9

Assign-Anweisung

[Bearbeiten]

Vorlage:OpenSCAD User Manual/Deprecated

Weist Variablen für einen Unterbaum neue Werte zu.

Beispiel:
// Alte Schreibweise (veraltet):
for (i = [10:50])
{
    assign (angle = i*360/20, distance = i*10, r = i*2)
    {
        rotate(angle, [1, 0, 0])
        translate([0, distance, 0])
        sphere(r = r);
    }
}

// Moderne Schreibweise (ohne assign):
for (i = [10:50])
{
    angle = i*360/20;
    distance = i*10;
    r = i*2;
    rotate(angle, [1, 0, 0])
    translate([0, distance, 0])
    sphere(r = r);
}

Let-Anweisung

[Bearbeiten]

Vorlage:Requires

Weist Variablen für einen Unterbaum neue Werte zu – nacheinander, sodass spätere Zuweisungen auf frühere zurückgreifen können (im Gegensatz zu "assign").

Beispiel:
for (i = [10:50])
{
    let (angle = i*360/20, r = i*2, distance = r*5)
    {
        rotate(angle, [1, 0, 0])
        translate([0, distance, 0])
        sphere(r = r);
    }
}