OpenSCAD User Manual/Named Objects
Part of the section on Language Reference
Names
Named objects in OpenSCAD include Variables, Functions, and Modules.
To be valid a name:
- must begin with an alphabetic character [a..zA..Z] or an underscore ('_')
- and may continue with any mix of alphabetic characters, numeric digits, and the underscore
- and may not include accented characters, punctuation, nor Unicode characters.
Note: older versions of OpenSCAD allowed names to start with numeric digits but will generate run-time errors in more recent versions
The OpenSCAD convention on multi word names is to use the underscore to delimit them, as this_is_a_name
Variables
Named variables are created when they appear as the Left Hand Side of an assignment statement and will be given the value of the Right Hand Side expression.
result = <calculate something>;
Variables should normally be defined only once in a program as when the program runs the variable will have the last value assigned to it.
The location of a variable definition determines where it can be used, thus it has "scope", a concept discussed fully in the literature, with only a few issues germane to functional programming and OpenSCAD in particular needing to be covered in the following section.
Scope of Variables
Names defined outside of any block created by a brace pair "{}" are in the name space of the "top level" of the program and are considered to be global in scope. They are "visible" throughout the program but may not be given a new value inside a block:
a = 10;
module use_a_1()
echo(a);
module use_a_2() {
echo(a);
a=8; // note (1)
}
echo(a=a); // ECHO: a = 10 global "a"
use_a_1(); // ECHO: 10 global "a" is visible inside the module
use_a_2(); // ECHO: 8 // this is local "a", not global
echo(b=a); // ECHO: b = 10 unchanged by use_a_2()
Note: (1) the assignment of 8 to the local instance of "a" happens at compile time so in the local scope of use_a_2()
that is its run time value.
Anonymous Scopes Do Not Create Local Scopes
Variables defined outside an anonymous block are visible inside, and vice versa, but an assignment of a new value to a global variable is not "converted" to be "local" to it:
a = 10; // global { b=34; echo(a); // global visible here a=12; // this is Line 4 } echo(a,b); // b is really in global space
with these results
WARNING: "a" was assigned on line 1 of "OpenSCAD (Nightly)" but was overwritten in file "OpenSCAD (Nightly)", line 4
...
ECHO: 12
ECHO: 12, 34
The last assignment compiled is its run time value.
Local Variables
Variables for temporary use may be defined inside a flow of control statement using the
or assign()let()
statements, or, in a block "{}" attached to one, by assignment.
This also applies to blocks created by module and function definitions, and by operator modules.
Such variables are called "local" because they are not visible outside the local block. When a block has blocks defined inside it they form an "inner scope" and may be nested to any depth. However blocks at a particular level of scope are completely independent. Locals defined in one inner scope are not visible in any other scope of the same level.
Prior to version 2015.03 there were only two "levels" where variables could be defined by assignment; the global, and the top-level of a module definition.
Temporary variables needed inside a module had to be created using the
statement.
assign()
Variations on Local Scope: Let()
a = 10;
q=5;
if( a==10 ) // using global "a"
echo(q);
else
echo(q+2); // ECHO: 7
The global "q" is used in the local scope of the if()
.
Each part of an FoC statement can have local variables for temporarily holding expression results using the let statement:
a=10; s=5;
if( a==10 )
let( r=3 ) // imagine "3" is an expression
echo( st=s+r); // Echo: st=8
else
let( r=20, u=2 )
echo( sf=s+r+u);
Any number of comma separated variable assignments may be written inside the parentheses of the let, and they really are local:
a=10; s2=5;
if( a==10 )
let( r1=3 )
echo(s2t=s2+r2); // r2 not visible
else
let( r2=6 ) // r2 local in 'else'
echo(s2f=s2+2+r1);
results in these warnings:
WARNING: Ignoring unknown variable "r2" in file scope-issues.scad, line 41
WARNING: undefined operation (number + undefined) in file scope-issues.scad, line 41
ECHO: s2t = undef
Variations on Local Scope: Blocks "{}"
As mentioned above, when an FoC statement is expanded to be a block by braces, "{}", local variables may be defined by assignment:
a=10; // [0:15]
/* [Hidden] */
t=5;
if( a==10 )
{
r=3; // local to 'true' part
echo( tt=t+r);
a = 13; // local "a" in 'true'
}
else
{
r=20; // only in 'else' part
echo( tf=t+2+r);
a = 13; // also local, but in 'else'
}
echo( after=a);
resulting in:
ECHO: tt = 8
ECHO: after = 10
Variations on Local Scope: Functions
As mentioned above, functions and modules also define local scopes.
In this code a ternary "?:" conditional statement is used to satisfy the requirement that a function be a single statement:
a=10; // [0:15]
/* [Hidden] */
function testlocal( condition ) =
let( r=3 )
condition==10 ?
let( r=5, tt=a+r )
tt
:
let( r=20, tf=a+2+r)
tf
;
t=5;
echo( testlocal( a ) ); //ECHO: 15 when a==10, else 32
A module definition shows us how local variables assignment works in blocks:
a=10; // [0:15]
/* [Hidden] */
module testlocal( condition )
{
r=3; // local in 'testlocal'
if( condition==10 )
{
r=5;
echo( tt=a+r );
a = 50;
}
else
{
r=20;
tf=a+2+r;
echo( tf );
a=40;
}
}
t=5;
echo( a ); // ECHO: 10 // unaffected global
testlocal( a );
The value for "a" may be adjusted in the Customizer to see how the calculation result changes to follow the selection of which local scope will be used.
Variations on Local Scope: Operator Modules
As mentioned above, any operator module call may use a block to affect a number of objects:
a = 10; // "a" is global
echo( e1=a,b); //ECHO: 10, undef
translate([5,0,0]) // Open a local scope
{
a= 100;
b= 16; // create b
echo( e2=a,b) //ECHO: 100, 16
color("blue")
{ // level 2 inner scope
echo( e3=a,b)//ECHO: 100, 20
cube();
b=20; // last assign in L2 scope
c="in color";
}
echo( e4=a,b); //ECHO: 100, 16 again
d="in transl";
}
// back at global scope
echo( e5=a,b,c,d); //ECHO: 6, undef, undef, undef
color("red")
{ // a 2nd, different, local scope
cube();
echo( e6=a,b,c,d); //ECHO 6, undef, undef, undef
}
// back at global scope
echo( e7=a,b); //ECHO: 6, undef
Special Variables
Special variables are an alternate means of passing values into modules and functions. Starting a variable name with a dollar sign ('$') makes it special.
These variables operate by dynamic scoping which is nicely illustrated in a StackOverflow post.
Dynamic Scoping: With dynamic scope, a global identifier is a pointer to the top of a stack of instances of that identifier associated with its most recent environment. Said another way, each identifier has a global stack of bindings and the occurrence of an identifier, and thus the value it holds at a particular moment of run-time, is taken from the most recent binding. In simpler terms, in dynamic scoping, the compiler first searches the current block and then successively all the calling functions, not according to their syntactic nesting, but according to the order they were called in as the program executes.
Special variables may be accessed from anywhere in the program, enabling greater flexibility in variable scoping.
Advantages:
- greater flexibility as variables may be accessed from any part of the program.
- easier to write code that is reusable, because variables can be accessed from anywhere.
- make it easier to debug recursive functions or complex control flow.
- allows code to be more easily adapted to changing requirements
Disadvantages:
- Understanding the scope of variables is harder because it is determined at runtime.
- It can lead to slower execution due to the additional look-ups needed at runtime.
- It can be more error-prone because variables can be accessed from unexpected places.
$special=1; normal=1;
module a(){
echo($special, normal);
}
a();
let ($special=2, normal=2) a();
a();
which outputs:
ECHO: 1, 1 ECHO: 2, 1 ECHO: 1, 1
The results show that let($special=2, normal=2) set set $special in a local space in which a call to a() had been compiled. It also defined a new, local variable called "normal" that was not used. That call to s() used the global value for "normal", one,
Several special variables are already defined by OpenSCAD.
Curve Smoothness: $fa, $fs, and $fn
The $fa, $fs values OR the $fn value are used to calculate how many facets to use in sub-dividing a curved line or surface into line segments or polygons respectively. When $fn is greater than zero, $fs and $fa are not used.
Note: while the three specials may be floating point values the calculation of the number of fragments always results in an integer value. There is a complete section on Curvature Smoothness on the Rendering page showing how these three variables may be used.
Animation Using the $t Special Variable
This variable, when animation is active, is applied to all rotate()
and translate()
modules, $t*360 giving complete cycles.
The value of $t will repeat from 0 through (1 - 1/Steps). It never reaches 1 because this would produce a "hitch" in the animation if using it for rotation -- two consecutive frames would be at the same angle.
There is no variable to distinguish between the cases of animation running at the first frame ($t=0) and animation not running, so make $t=0 your rest position for the model.
-
Simple harmonic motion, 20 FPS, 100 steps
-
Animated gears 17T and 31T
Viewport: $vpr, $vpt, $vpf and $vpd
These contain the current viewport rotation and translation and camera distance - at the time of doing the rendering. Moving the viewport does not update them. During an animation they are updated for each frame.
- $vpr
- rotation
- $vpt
- translation (i.e. won't be affected by rotate and zoom)
- $vpf
- the FOV (Field of View) of the view [Note: Requires version 2021.01]
- $vpd
- the camera distance [Note: Requires version 2015.03]
All four variables may be modified but at only at the top-level of the main file. Changing these values by assignment in included files does not work [Note: Requires version 2015.03]
There are menu items that place the value of the selected variable on the clipboard so that it can then be used in the script, typically to animate the camera view.
Use the menu Edit > Copy Viewport *
,
The use of these variables is covered in a bit more depth in the Rendering page.
Rendering Mode: $preview
[Note: Requires version 2019.05]
This variable makes the rendering option visible in a script. In OpenCSG preview (F5) $preview is false, while in render mode (F6) it will be true.
The script can then selectively omit details in the preview that will decorate the finished part in the final, slower rendering.
When OpenSCAD is run from the command line $preview is only true
when generating a PNG image with OpenCSG.
When generating STL, DXF and SVG files with CGAL, or CSG and ECHO files $preview will be false
.
This behavior can be overridden by -D\$preview
on the command line.
The full story of the $preview variable is told on the Rendering Page