OpenSCAD is a 2D/3D and solid modeling program which is based on a Functional programming language used to create models that are previewed on the screen, and rendered into 3D mesh which allows the model to be exported in a variety of 2D/3D file formats.
A script in the OpenSCAD language is used to create 2D or 3D models. This script is a free format list of action statements.
object(); variable = value; operator() action(); operator() { action(); action(); } operator() operator() { action(); action(); } operator() { operator() action(); operator() { action(); action(); } }
Objects are the building blocks for models, created by 2D and 3D primitives. Objects end in a semicolon ';'.
Action statements include creating objects using primitives and assigning values to variables. Action statements also end in a semicolon ';'.
Operators, or transformations, modify the location, color and other properties of objects. Operators use braces '{}' when their scope covers more than one action. More than one operator may be used for the same action or group of actions. Multiple operators are processed Right to Left, that is, the operator closest to the action is processed first. Operators do not end in semicolons ';', but the individual actions they contain do.
Examples cube(5); x = 4+y; rotate(40) square(5,10); translate([10,5]) { circle(5); square(4); } rotate(60) color("red") { circle(5); square(4); } color("blue") { translate([5,3,0]) sphere(5); rotate([45,0,45]) { cylinder(10); cube([5,6,7]); } }
Comments are a way of leaving notes within the script, or code, (either to yourself or to future programmers) describing how the code works, or what it does. Comments are not evaluated by the compiler, and should not be used to describe self-evident code.
OpenSCAD uses C++-style comments:
// This is a comment myvar = 10; // The rest of the line is a comment /* Multi-line comments can span multiple lines. */
A value in OpenSCAD is either a Number (like 42), a Boolean (like true), a String (like "foo"), a Range (like [0: 1: 10]), a Vector (like [1,2,3]), or the Undefined value (undef). Values can be stored in variables, passed as function arguments, and returned as function results.
[OpenSCAD is a dynamically typed language with a fixed set of data types. There are no type names, and no user defined types. Functions are not values. In fact, variables and functions occupy disjoint namespaces.]
Numbers are the most important type of value in OpenSCAD, and they are written in the familiar decimal notation used in other languages. Eg, -1, 42, 0.5, 2.99792458e+8. [OpenSCAD does not support octal or hexadecimal notation for numbers.]
In additional to decimal numerals, the following names for special numbers are defined:
OpenSCAD has only a single kind of number, which is a 64 bit IEEE floating point number. [OpenSCAD does not distinguish integers and floating point numbers as two different types, nor does it support complex numbers.] Because OpenSCAD uses the IEEE floating point standard, there are a few deviations from the behaviour of numbers in mathematics:
The constants 'inf' and 'nan' are not supported as numeric constants by OpenSCAD, even though you can compute numbers that are printed this way by 'echo'. You can define variables with these values by using:
inf = 1e200 * 1e200; nan = 0 / 0; echo(inf,nan);
The value 'nan' is the only OpenSCAD value that is not equal to any other value, including itself. Although you can test if a variable 'x' has the undefined value using 'x == undef', you can't use 'x == 0/0' to test if x is Not A Number. Instead, you must use 'x != x' to test if x is nan.
Booleans are truth values. There are two Boolean values, namely true
and false
.
A Boolean is passed as the argument to conditional statement 'if()'. conditional operator '? :',
and logical operators '!' (not), '&&' (and), and '||' (or). In all of these contexts, you can actually
pass any quantity. Most values are converted to 'true' in a Boolean context, the values that count as 'false' are:
Note that "false"
(the string), [0]
(a numeric vector),
[ [] ]
(a vector containing an empty vector), [false]
(a vector containing the Boolean value false) and 0/0 (Not A Number) all count as true.
A string is a sequence of zero or more unicode characters. String values are used to specify file names when importing a file, and to display text for debugging purposes when using echo(). Strings can also be used with the new text() primitive added in 2015.03.
A string literal is written as a sequence of characters enclosed in quotation marks "
, like this: ""
(an empty string), or "this is a string"
.
To include a "
character in a string literal, use \"
. To include a \
character in a string literal, use \\
. The following escape sequences beginning with \
can be used within string literals:
Note: This behavior is new since OpenSCAD-2011.04. You can upgrade old files using the following sed command: sed 's/\\/\\\\/g' non-escaped.scad > escaped.scad
Example: echo("The quick brown fox \tjumps \"over\" the lazy dog.\rThe quick brown fox.\nThe \\lazy\\ dog."); result ECHO: "The quick brown fox jumps "over" the lazy dog. The quick brown fox. The \lazy\ dog." old result ECHO: "The quick brown fox \tjumps \"over\" the lazy dog. The quick brown fox.\nThe \\lazy\\ dog."
Ranges are used by for() loops and children(). They have 2 varieties:
Although enclosed in square brackets [] , they are not vectors. They use colons : for separators rather than commas.
r1 = [0:10]; r2 = [0.5:2.5:20]; echo(r1); // ECHO: [0: 1: 10] echo(r2); // ECHO: [0.5: 2.5: 20]
You should avoid step values that cannot be represented exactly as binary floating point numbers. Integers are okay, as are fractional values whose denominator is a power of two. For example, 0.25 (1/4) and 0.125 (1/8) are safe, but 0.2 (2/10) should be avoided. The problem with these step values is that your range may have too many or too few elements, due to inexact arithmetic.
A missing <increment> defaults to 1. A range in the form [<start>:<end>] with <start> greater than <end> generates a warning and is equivalent to [<end>: 1: <start>]. A range in the form [<start>:1:<end>] with <start> greater than <end> does not generate a warning and is equivalent to []. The <increment> in a range may be negative (for versions after 2014).
The undefined value is a special value written as undef. It's the initial value of a variable that hasn't been assigned a value, and it is often returned as a result by functions or operations that are passed illegal arguments. Finally, undef
can be used as a null value, equivalent to null
or NULL
in other programming languages.
All arithmetic expressions containing undef
values evaluate as undef
. In logical expressions, undef
is equivalent to false
. Relational operator expressions with undef
evaluate as false
except for undef==undef
which is true
.
Note that numeric operations may also return 'nan' (not-a-number) to indicate an illegal argument. For example, 0/false
is undef
, but 0/0
is 'nan'. Relational operators like < and > return false
if passed illegal arguments. Although undef
is a language value, 'nan' is not.
OpenSCAD variables are created by a statement with a name or identifier, assignment via an expression and a semicolon. The role of arrays, found in many imperative languages, is handled in OpenSCAD via vectors.
var = 25; xx = 1.25 * cos(50); y = 2*xx+var; logic = true; MyString = "This is a string"; a_vector = [1,2,3]; rr = a_vector[2]; // member of vector range1 = [-1.5:0.5:3]; // for() loop range xx = [0:5]; // alternate for() loop range
OpenSCAD is a Functional programming language, as such variables are bound to expressions and keep a single value during their entire lifetime due to the requirements of referential transparency. In imperative languages, such as C, the same behavior is seen as constants, which are typically contrasted with normal variables.
In other words OpenSCAD variables are more like constants, but with an important difference. If variables are assigned a value multiple times, only the last assigned value is used in all places in the code. See further discussion at Variables are set at compile-time, not run-time. This behavior is due to the need to supply variable input on the command line, via the use of -D variable=value option. OpenSCAD currently places that assignment at the end of the source code, and thus must allow a variable's value to be changed for this purpose.
Values cannot be modified during run time; all variables are effectively constants that do not change. Each variable retains its last assigned value at compile time, in line with Functional programming languages. Unlike Imperative languages, such as C, OpenSCAD is not an iterative language, and as such the concept of x = x + 1 is not valid. Understanding this concept leads to understanding the beauty of OpenSCAD.
It was not possible to do assignments at any place except the file top-level and module top-level. Inside an if/else or for loop, assign() was needed.
Variables can now be assigned in any scope. Note that assignments are only valid within the scope in which they are defined - you are still not allowed to leak values to an outer scope. See Scope of variables for more details.
a=0; if (a==0) { a=1; // before 2015.03 this line would generate a Compile Error // since 2015.03 no longer an error, but the value a=1 is confined to within the braces {} }
A non assigned variable has the special value undef. It could be tested in conditional expression, and returned by a function.
Example echo("Variable a is ", a); // Variable a is undef if (a==undef) { echo("Variable a is tested undefined"); // Variable a is tested undefined }
When operators such as translate() and color() need to encompass more than one action ( actions end in ;), braces {} are needed to group the actions, creating a new, inner scope. When there is only one semicolon, braces are usually optional.
Each pair of braces creates a new scope inside the scope where they were used. Since 2015.03, new variables can be created within this new scope. New values can be given to variables which were created in an outer scope . These variables and their values are also available to further inner scopes created within this scope, but are not available to any thing outside this scope. Variables still have only the last value assigned within a scope.
// scope 1 a = 6; // create a echo(a,b); // 6, undef translate([5,0,0]){ // scope 1.1 a= 10; b= 16; // create b echo(a,b); // 100, 16 a=10; was overridden by later a=100; color("blue") { // scope 1.1.1 echo(a,b); // 100, 20 cube(); b=20; } // back to 1.1 echo(a,b); // 100, 16 a=100; // override a in 1.1 } // back to 1 echo(a,b); // 6, undef color("red"){ // scope 1.2 cube(); echo(a,b); // 6, undef } // back to 1 echo(a,b); // 6, undef //In this example, scopes 1 and 1.1 are outer scopes to 1.1.1 but 1.2 is not.
{ angle = 45; } rotate(angle) square(10);
For() loops are not an exception to the rule about variables having only one value within a scope. A copy of loop contents is created for each pass. Each pass is given its own scope, allowing any variables to have unique values for that pass. No, you still can't do a=a+1;
Because OpenSCAD calculates its variable values at compile-time, not run-time, the last variable assignment within a scope applies everywhere in that scope or inner scopes thereof. It may be helpful to think of them as override-able constants rather than as variables.
// The value of 'a' reflects only the last set value a = 0; echo(a); // 5 a = 3; echo(a); // 5 a = 5;
While this appears to be counter-intuitive, it allows you to do some interesting things: for instance, if you set up your shared library files to have default values defined as variables at their root level, when you include that file in your own code you can 're-define' or override those constants by simply assigning a new value to them. So changing constant values gives you more flexibility. If constants would never change, of course, you always can be sure having the value you see in any constant definition. Not so here. If you see a constant value definition at any other place its value could be different. This is very flexible.
Special variables provide an alternate means of passing arguments to modules and functions. All variables starting with a '$' are special variables, similar to special variables in lisp. As such they are more dynamic than regular variables. (for more details see Other Language Features)
A vector or list is a sequence of zero or more OpenSCAD values. Vectors are a collection of numeric or boolean values, variables, vectors, strings or any combination thereof. They can also be expressions which evaluate to one of these. Vectors handle the role of arrays found in many imperative languages. The information here also applies to lists and tables which use vectors for their data.
A vector has square brackets, [] enclosing zero or more items (elements or members), separated by commas. A vector can contain vectors, which contain vectors, etc.
[1,2,3] [a,5,b] [] [5.643] ["a","b","string"] [[1,r],[x,y,z,4,5]] [3, 5, [6,7], [[8,9],[10,[11,12],13], c, "string"] [4/3, 6*1.5, cos(60)]
use in OpenSCAD:
cube( [width,depth,height] ); // optional spaces shown for clarity translate( [x,y,z] ) polygon( [ [x0,y0], [x1,y1], [x2,y2] ] );
Vectors are created by writing the list of elements, separated by commas, and enclosed in square brackets. Variables are replaced by their values.
cube([10,15,20]); a1 = [1,2,3]; a2 = [4,5]; a3 = [6,7,8,9]; b = [a1,a2,a3]; // [ [1,2,3], [4,5], [6,7,8,9] ] note increased nesting depth
Elements within vectors are numbered from 0 to n-1 where n is the length returned by len(). Address elements within vectors with the following notation:
e[5] // element no 5 (sixth) at 1st nesting level e[5][2] // element 2 of element 5 2nd nesting level e[5][2][0] // element 0 of 2 of 5 3rd nesting level e[5][2][0][1] // element 1 of 0 of 2 of 5 4th nesting level
e = [ [1], [], [3,4,5], "string", "x", [[10,11],[12,13,14],[[15,16],[17]]] ]; // length 6 address length element e[0] 1 [1] e[1] 0 [] e[5] 3 [ [10,11], [12,13,14], [[15,16],[17]] ] e[5][1] 3 [ 12, 13, 14 ] e[5][2] 2 [ [15,16], [17] ] e[5][2][0] 2 [ 15, 16 ] e[5][2][0][1] undef 16 e[3] 6 "string" e[3 ][2] 1 "r" s = [2,0,5]; a = 2; s[a] undef 5 e[s[a]] 3 [ [10,11], [12,13,14], [[15,16],[17]] ]
The first three elements of a vector can be accessed with an alternate dot notation:
e.x //equivalent to e[0] e.y //equivalent to e[1] e.z //equivalent to e[2]
[Note: Requires version 2015.03]
concat() combines the elements of 2 or more vectors into a single vector. No change in nesting level is made.
vector1 = [1,2,3]; vector2 = [4]; vector3 = [5,6]; new_vector = concat(vector1, vector2, vector3); // [1,2,3,4,5,6] string_vector = concat("abc","def"); // ["abc", "def"] one_string = str(string_vector[0],string_vector[1]); // "abcdef"
len() is a function which returns the length of vectors or strings. Indices of elements are from [0] to [length-1].
a = [1,2,3]; echo(len(a)); // 3
See example elements with lengths
A matrix is a vector of vectors.
Example which defines a 2D rotation matrix mr = [ [cos(angle), -sin(angle)], [sin(angle), cos(angle)] ];
Now we have variables, it would be nice to be able to get input into them instead of setting the values from code. There are a few functions to read data from DXF files, or you can set a variable with the -D switch on the command line.
Getting a point from a drawing
Getting a point is useful for reading an origin point in a 2D view in a technical drawing. The function dxf_cross reads the intersection of two lines on a layer you specify and returns the intersection point. This means that the point must be given with two lines in the DXF file, and not a point entity.
OriginPoint = dxf_cross(file="drawing.dxf", layer="SCAD.Origin", origin=[0, 0], scale=1);
Getting a dimension value
You can read dimensions from a technical drawing. This can be useful to read a rotation angle, an extrusion height, or spacing between parts. In the drawing, create a dimension that does not show the dimension value, but an identifier. To read the value, you specify this identifier from your program:
TotalWidth = dxf_dim(file="drawing.dxf", name="TotalWidth", layer="SCAD.Origin", origin=[0, 0], scale=1);
For a nice example of both functions, see Example009 and the image on the homepage of OpenSCAD.