OpenSCAD Benutzerhandbuch/Benutzerdefiniert
Benutzerdefinierte Funktionen und Module
[Bearbeiten]Du kannst die OpenSCAD-Sprache erweitern, indem du eigene Funktionen und Module definierst. Das ermöglicht es dir, Code-Abschnitte zu gruppieren und mit unterschiedlichen Werten wiederverwendbar zu machen. Gut gewählte Namen helfen außerdem, deinen Code verständlich zu halten.
- Funktionen berechnen und geben Werte zurück.
- Module führen Aktionen aus (z. B. Geometrie erzeugen), geben aber nichts zurück.
OpenSCAD berechnet Variablenwerte zur Kompilierzeit, nicht zur Laufzeit. Innerhalb eines Gültigkeitsbereichs gilt stets die letzte Zuweisung – auch in tieferen Ebenen. Denk besser an „überschreibbare Konstanten“ statt an klassische Variablen.
Für jeden Aufruf von Funktionen oder Modulen erstellt OpenSCAD eine eigene Kopie mit eigenem Gültigkeitsbereich – alle Werte sind darin fest und unabhängig.
Namen sind Groß-/Kleinschreibung-sensitiv: "test()" und "TEST()" sind zwei verschiedene Module/Funktionen.
Gültigkeitsbereich (Scope)
[Bearbeiten]Module und Funktionen können innerhalb eines Moduls definiert werden – dann sind sie nur dort sichtbar.
Beispiel: Eine Parabel wird lokal definiert und geplottet:

function parabola(f,x) = (1/(4*f)) * x*x;
module plotParabola(f, wide, steps=1) {
function y(x) = parabola(f,x); // nur hier sichtbar
module plot(x,y) { // nur hier sichtbar
translate([x,y]) circle(1,$fn=12);
}
xAxis = [-wide/2 : steps : wide/2];
for (x = xAxis) plot(x, y(x));
}
color("red") plotParabola(10, 100, 5);
color("blue") plotParabola(4, 60, 2);
Die Funktion "y()" und das Modul "plot()" können nicht außerhalb von "plotParabola" aufgerufen werden.
Funktionen
[Bearbeiten]Funktionen berechnen aus Eingabewerten einen neuen Wert (auch Vektoren!).
Syntax:
function name(parameter = default) = ausdruck;
- Name: Nur Buchstaben, Ziffern, Unterstrich ("[a-zA-Z0-9_]")
- Parameter: Optional mit Standardwert
- Ausdruck: Beliebiger berechenbarer Wert
Nutzung
[Bearbeiten]Funktionen werden wie Werte behandelt – kein Semikolon am Ende!
Beispiel 1:
function func0() = 5;
function func1(x=3) = 2*x+1;
function func2() = [1,2,3,4];
function func3(y=7) = (y==7) ? 5 : 2;
function func4(p0,p1,p2,p3) = [p0,p1,p2,p3];
echo(func0()); // 5
a = func1(); // 7
b = func1(5); // 11
echo(func2()); // [1,2,3,4]
echo(func3(2), func3()); // 2, 5
z = func4(func0(), func1(), func2(), func3());
// → [5, 7, [1,2,3,4], 5]
translate([0, -4*func0(), 0])
cube([func0(), 2*func0(), func0()]);
// → translate([0,-20,0]) cube([5,10,5]);
Beispiel 2: Dynamischer Bereich für "for"-Schleifen
function steps(start, no_steps, end) =
[start : (end-start)/(no_steps-1) : end];
echo(steps(10, 3, 5)); // [10, 7.5, 5]
echo(steps(0, 5, 5)); // [0, 1.25, 2.5, 3.75, 5]
Beispiel 3: Rhomboid als Funktion

function rhomboid(x=1, y=1, angle=90) =
[[0,0], [x,0],
[x + x*cos(angle)/sin(angle), y],
[x*cos(angle)/sin(angle), y]];
v1 = rhomboid(10,10,35);
polygon(v1);
Im Vergleich dazu als Modul:
module parallelogram(x=1,y=1,angle=90) {
polygon([[0,0],[x,0],
[x+x*cos(angle)/sin(angle),y],
[x*cos(angle)/sin(angle),y]]);
}
parallelogram(10,10,35);
"let" in Funktionen
[Bearbeiten]Seit 2015.03 kannst du mit "let" Zwischenwerte in Funktionen speichern:
function get_square_triangle_perimeter(p1, p2) =
let(hypotenuse = sqrt(p1*p1 + p2*p2))
p1 + p2 + hypotenuse;
Rekursive Funktionen
[Bearbeiten]Rekursion ist möglich – aber mit Begrenzung (einige Tausend Aufrufe). Nutze den Bedingungsoperator ("? :") zum Abbruch.
Einfache Rekursion (kein Tail-Call):
function add_up_to(n) = (n == 0) ? 0 : n + add_up_to(n-1);
Tail-rekursiv (effizienter, bis zu 1 Mio. Aufrufe):
function add_up_to(n, sum=0) =
(n == 0) ? sum : add_up_to(n-1, sum+n);
echo(sum = add_up_to(100000)); // → 5.00005e+009
Funktionsliterale (Lambdas)
[Bearbeiten]Du kannst anonyme Funktionen definieren und wie Werte behandeln:
func = function(x) x * x;
echo(func(5)); // → 25
// Funktion, die eine Funktion zurückgibt:
a = 1;
selector = function(which)
which == "add" ? function(x) x + x + a
: function(x) x * x + a;
echo(selector("add")(5)); // → 11
echo(selector("mul")(5)); // → 26
Überschreiben eingebauter Funktionen
[Bearbeiten]Möglich – aber Vorsicht! Die Definition wird vor der Auswertung verarbeitet:
echo(sin(1)); // → true
function sin(x) = true;
echo(sin(1)); // → true
Module
[Bearbeiten]Module definieren entweder Objekte oder Operatoren (mit "children()"). Sobald definiert, verhalten sie sich wie Sprachbefehle.
Syntax:
module name(parameter = default) { ... }
- Name: Nur "[a-zA-Z0-9_]"
- Parameter: Optional mit Standardwert
- Aktionen: Fast alles erlaubt – auch verschachtelte Module/Funktionen (nur lokal sichtbar)
Variablen existieren nur innerhalb des Modulaufrufs – Rückgabewerte gibt es nicht.
Objekt-Module
[Bearbeiten]Erzeugen Geometrie. Beim Aufruf enden sie mit Semikolon.
Beispiel 1: Farbbalken

ColorBreak = [[0,""], [20,"lime"], [40,"greenyellow"], [60,"yellow"], [75,"LightCoral"], [200,"red"]];
Expense = [16,20,25,85,52,63,45];
module ColorBar(value, period, range) {
RangeHi = ColorBreak[range][0];
RangeLo = ColorBreak[range-1][0];
color(ColorBreak[range][1])
translate([10*period, 0, RangeLo])
if (value > RangeHi) cube([5,2,RangeHi-RangeLo]);
else if (value > RangeLo) cube([5,2,value-RangeLo]);
}
module ShowColorBars(values) {
for (month = [0:len(values)-1], range = [1:len(ColorBreak)-1])
ColorBar(values[month], month, range);
}
translate([-30,-20,0]) ShowColorBars(Expense);
Beispiel 2: Haus mit verschiedenen Dächern

module house(roof="flat", paint=[1,0,0]) {
color(paint)
if (roof=="flat") {
translate([0,-1,0]) cube();
} else if (roof=="pitched") {
rotate([90,0,0]) linear_extrude(height=1)
polygon(points=[[0,0],[0,1],[0.5,1.5],[1,1],[1,0]]);
} else if (roof=="domical") {
translate([0,-1,0]) {
translate([0.5,0.5,1]) sphere(r=0.5,$fn=20);
cube();
}
}
}
house();
translate([2,0,0]) house("pitched");
translate([4,0,0]) house("domical",[0,1,0]);
// ... weitere Varianten
Operator-Module
[Bearbeiten]Nutzen "children()", um Transformationen auf übergebene Objekte anzuwenden. Kein Semikolon beim Aufruf!
Grundlagen:
- "children()" → alle Kindobjekte
- "children(i)" → ein bestimmtes Kind (Index 0 bis "$children-1")
- "children([start:end])" → Bereich auswählen
Beispiel: Eigenes Transformationsmodul
module move(x=0,y=0,z=0,rx=0,ry=0,rz=0) {
translate([x,y,z]) rotate([rx,ry,rz]) children();
}
move(10) cube(10,true);
move(z=7.07, ry=45) cube(10,true);
Beispiel: Nur erstes Kind mehrfach nutzen
module lineup(num, space) {
for (i = [0:num-1])
translate([space*i, 0, 0]) children(0);
}
lineup(5, 65) { sphere(30); cube(35); } // Nur die Kugel wird 5× platziert
Beispiel: Jedes Kind separat bearbeiten

module SeparateChildren(space) {
for (i = [0:$children-1])
translate([i*space, 0, 0]) { children(i); text(str(i)); }
}
SeparateChildren(-20) {
cube(5); // 0
sphere(5); // 1
translate([0,20,0]) { // 2 (als ein Kind!)
cube(5); sphere(5);
}
cylinder(15); // 3
cube(8,true); // 4
}
Veralteter Befehl: Bis 2013.06 hieß es "child()" – heute durch "children()" ersetzt:
- "child()" → "children(0)"
- "child(x)" → "children(x)"
Weitere Beispiele
[Bearbeiten]Objekte:
module arrow(){
cylinder(10);
cube([4,.5,3],true);
cube([.5,4,3],true);
translate([0,0,10]) cylinder(4,2,0,true);
}
module cannon(){
difference(){
union(){ sphere(10); cylinder(40,10,8); }
cylinder(41,4,4);
}
}
Operatoren:

module aim(elevation, azimuth=0) {
rotate([0,0,azimuth])
rotate([0,90-elevation,0]) children(0);
}
module RotaryCluster(radius=30, number=8) {
for (azimuth = [0:360/number:359])
rotate([0,0,azimuth])
translate([radius,0,0]) {
children();
translate([40,0,30]) text(str(azimuth));
}
}
RotaryCluster(200,7) color("lightgreen") aim(15){cannon();base();}
Rekursive Module
[Bearbeiten]Module können sich selbst aufrufen – aber ohne Tail-Call-Optimierung! Halte die Rekursionstiefe niedrig (< 7), sonst wird’s langsam.

module simple_tree(size, dna, n) {
if (n > 0) {
// Stamm
cylinder(r1=size/10, r2=size/12, h=size, $fn=24);
// Äste
translate([0,0,size])
for(bd = dna) {
angx = bd[0]; angz = bd[1]; scal = bd[2];
rotate([angx,0,angz])
simple_tree(scal*size, dna, n-1);
}
} else {
// Blätter
color("green")
scale([1,1,3])
translate([0,0,size/6])
rotate([90,0,0])
cylinder(r=size/6, h=size/10);
}
}
dna = [ [12, 80, 0.85], [55, 0, 0.6],
[62,125, 0.6], [57,-125,0.6] ];
simple_tree(50, dna, 5);
- Überschreiben eingebauter Module ###
Möglich – z. B. um Standardparameter zu ändern:
module sphere() { square(); } // Jetzt erzeugt sphere() ein Quadrat!
sphere();
Sinnvoller Einsatz: Ersetze 3D-Primitive durch extrudierte 2D-Formen mit zusätzlichen Optionen.