ir is i just a low priority operator like echo and assert?
On 7/28/2025 4:18 PM, vulcan_--- via Discuss wrote:
ir is i just a low priority operator like echo and assert?
Colors is an actual operator module.
There are only three of the "special" expression operators: let, echo,
assert.
They are similar to, but different from, the operator modules let, echo,
and assert.
And those are different from the list comprehension module let.
oi
so when i a statement do i need to be concerned that i dont know if (let echo assert) are operators or modules?
if i write
xxx= ( assert(true) 2 ) + 3 // i would think this assert is an operator in an expression so, as it is inside parens to force precedence right?
but
assert( true ) translate( [1,1,1] ) cube(); // here it is an operator module, yes?
are there any differences in operation between operator and module forms of (let echo assert) ?
are there any syntactic differences in how they are used in a script?
The execution order is different between modules and expressions, because
expressions run first, before all modules.
If you write this
assert(x>0); // assert is a module
y=f(x); // function f requires that x>0
sphere(y);
then your code will fail if x<=0 when f() is evaluated and the assert will
never do anything. You have to rewrite it as:
dummy = assert(x>0); // assert is in an expression
y=f(x);
sphere(y);
in order for the assertion to prevent f() from being invoked incorrectly.
I was totally mystified the first time I encountered this behavior and I
could only fix it by moving the assert() into f(). I rarely put an assert
inside an expression that does something else. It seems less clear and
binds together two things that may not necessarily belong together. Also
it may not be obvious that you can chain them like dummy = assert(cond1)
assert(cond2) assert(cond3);
On Wed, Jul 30, 2025 at 3:38 PM vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
oi
so when i a statement do i need to be concerned that i dont know if (let
echo assert) are operators or modules?
if i write
xxx= ( assert(true) 2 ) + 3 // i would think this assert is an operator in
an expression so, as it is inside parens to force precedence right?
but
assert( true ) translate( [1,1,1] ) cube(); // here it is an operator
module, yes?
are there any differences in operation between operator and module forms
of (let echo assert) ?
are there any syntactic differences in how they are used in a script?
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
I would do:
y = assert(x>0) f(x);
That avoids the dummy variable and makes it clearer why the condition must
hold, i.e. to satisfy f().
On Thu, 31 Jul 2025 at 00:32, Adrian Mariano via Discuss <
discuss@lists.openscad.org> wrote:
The execution order is different between modules and expressions, because
expressions run first, before all modules.
If you write this
assert(x>0); // assert is a module
y=f(x); // function f requires that x>0
sphere(y);
then your code will fail if x<=0 when f() is evaluated and the assert will
never do anything. You have to rewrite it as:
dummy = assert(x>0); // assert is in an expression
y=f(x);
sphere(y);
in order for the assertion to prevent f() from being invoked incorrectly.
I was totally mystified the first time I encountered this behavior and I
could only fix it by moving the assert() into f(). I rarely put an assert
inside an expression that does something else. It seems less clear and
binds together two things that may not necessarily belong together. Also
it may not be obvious that you can chain them like dummy = assert(cond1)
assert(cond2) assert(cond3);
On Wed, Jul 30, 2025 at 3:38 PM vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
oi
so when i a statement do i need to be concerned that i dont know if (let
echo assert) are operators or modules?
if i write
xxx= ( assert(true) 2 ) + 3 // i would think this assert is an operator
in an expression so, as it is inside parens to force precedence right?
but
assert( true ) translate( [1,1,1] ) cube(); // here it is an operator
module, yes?
are there any differences in operation between operator and module forms
of (let echo assert) ?
are there any syntactic differences in how they are used in a script?
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
Real examples are not so simple, and it rarely makes sense, at least to
me, to try to attach the assertion to some specific calculation. There
may be 5 or 10 assertions checking the validity of input to a module. And
then there may be a dozen expressions, possibly with different conditions
based on the parameters. How do I decide which calculation to attach the
assertions to? Determining at which point invalid input causes a problem
may require effort, and I don't think it's going to increase clarity for
someone reading the code. In fact, I would argue that it's not trivial to
determine whether you can get away with the module form of assert. It
requires examining the code to figure out whether anything goes wrong
before the modules run.
Algorithmically the assertions are separate operations, not part of some
computation. Attaching ten assertions to a computation just makes the code
more confusing and harder to read by mixing assertion and calculation
together and putting a huge space between the variable and the calculation
for that variable. And if I were to somehow associate each assertion
with a different computation, that would be pretty confusing as well, since
now I have the same problem repeatedly. Also note that in real code, an
assertion doesn't look like "assert(x>0)", it's 80 characters with some
condition to check and a text error message to display. The calculation
also doesn't look like "f(x)" but may be a long calculation with
conditionals.
On Thu, Jul 31, 2025 at 3:55 AM nop head via Discuss <
discuss@lists.openscad.org> wrote:
I would do:
y = assert(x>0) f(x);
That avoids the dummy variable and makes it clearer why the condition must
hold, i.e. to satisfy f().
On Thu, 31 Jul 2025 at 00:32, Adrian Mariano via Discuss <
discuss@lists.openscad.org> wrote:
The execution order is different between modules and expressions, because
expressions run first, before all modules.
If you write this
assert(x>0); // assert is a module
y=f(x); // function f requires that x>0
sphere(y);
then your code will fail if x<=0 when f() is evaluated and the assert
will never do anything. You have to rewrite it as:
dummy = assert(x>0); // assert is in an expression
y=f(x);
sphere(y);
in order for the assertion to prevent f() from being invoked
incorrectly. I was totally mystified the first time I encountered this
behavior and I could only fix it by moving the assert() into f(). I
rarely put an assert inside an expression that does something else. It
seems less clear and binds together two things that may not necessarily
belong together. Also it may not be obvious that you can chain them like
dummy = assert(cond1) assert(cond2) assert(cond3);
On Wed, Jul 30, 2025 at 3:38 PM vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
oi
so when i a statement do i need to be concerned that i dont know if (let
echo assert) are operators or modules?
if i write
xxx= ( assert(true) 2 ) + 3 // i would think this assert is an operator
in an expression so, as it is inside parens to force precedence right?
but
assert( true ) translate( [1,1,1] ) cube(); // here it is an operator
module, yes?
are there any differences in operation between operator and module forms
of (let echo assert) ?
are there any syntactic differences in how they are used in a script?
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
when assert() is a module, is it an operator module or an object module?
my testing:
RGB=[0,0,1];
color("red") assert( RGB == [0,0,1])
translate([0,3,0])
cube(3);
this works, the assert passes and the cube is drawn.
Or i change the test to [0,0,2 ] and the assert fails
color("red")
translate([0,3,0])
cube(3) assert( RGB == [0,0,01]);
emits this:
[WARNING: module cube() does not support child modules in file ., line 14](14,C:/Program Files/OpenSCAD (Nightly))
and draws the red cube
so .. in a limited sense assert() is an operator module
it can be placed so:
assert( RGB == [0,0,2]);
and then it is being an Object Model as it works even when it has no children, which an operator module cannot do.
and it can be a function returning a value and both types of module.
There aren't any operator modules. Just modules used in statements and
functions used in expressions. Some are both like assert and echo.
On Thu, 31 Jul 2025, 16:08 vulcan_--- via Discuss, <
discuss@lists.openscad.org> wrote:
when assert() is a module, is it an operator module or an object module?
my testing:
RGB=[0,0,1];
color("red") assert( RGB == [0,0,1])
translate([0,3,0])
cube(3);
this works, the assert passes and the cube is drawn.
Or i change the test to [0,0,2 ] and the assert fails
color("red")
translate([0,3,0])
cube(3) assert( RGB == [0,0,01]);
emits this:
WARNING: module cube() does not support child modules in file ., line 14
and draws the red cube
so .. in a limited sense assert() is an operator module
it can be placed so:
assert( RGB == [0,0,2]);
and then it is being an Object Model as it works even when it has no
children, which an operator module cannot do.
and it can be a function returning a value and both types of module.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
I think he is distinguishing between a module like cube which creates
geometry and does not accept children and a module like rotate which
operates on children and doesn’t produce any results itself. It does not
fail without children. It just does not produce a result.
So then cube is an “object” and rotate is an “operator”.
If you want to put the assert module into that framework it’s an operator.
It accepts children like rotate. It does not fail without children.
However the framework is not complete. A module can perfectly well produce
geometry and also process and operate on children. It just happens that no
built in modules do that.
I don’t think there is ever a reason to give children to assert even though
you can.
On Thu, Jul 31, 2025 at 11:12 nop head via Discuss <
discuss@lists.openscad.org> wrote:
There aren't any operator modules. Just modules used in statements and
functions used in expressions. Some are both like assert and echo.
On Thu, 31 Jul 2025, 16:08 vulcan_--- via Discuss, <
discuss@lists.openscad.org> wrote:
when assert() is a module, is it an operator module or an object module?
my testing:
RGB=[0,0,1];
color("red") assert( RGB == [0,0,1])
translate([0,3,0])
cube(3);
this works, the assert passes and the cube is drawn.
Or i change the test to [0,0,2 ] and the assert fails
color("red")
translate([0,3,0])
cube(3) assert( RGB == [0,0,01]);
emits this:
WARNING: module cube() does not support child modules in file ., line 14
and draws the red cube
so .. in a limited sense assert() is an operator module
it can be placed so:
assert( RGB == [0,0,2]);
and then it is being an Object Model as it works even when it has no
children, which an operator module cannot do.
and it can be a function returning a value and both types of module.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
while the underlying implementation may be the same for Operator Modules and Object Modules in practice the differences are critical.
If one defines an Object Module it results in one or more shapes being drawn. An Operator Module must end with a call to children() and cannot itself draw shapes.
An operator at the end of a statement causes no error nor warning, but also does nothing. .. at least in the 2025.07.20 i am using
An object can only be at the end of a statement .. sort of
color("red")
cube(5)
translate([0,3,0])
;
this will run but emits a warning: WARNING: module cube() does not support child modules …
but it does draw the cube
This also draws the cube, with the warning, but does not draw the second object, the sphere()
color("red")
cube(5)
translate([0,3,0])
sphere(r=4);
So in real terms, there are
functions that return a value, can only be used in expressions, and cannot draw geometry
modules that draw geometry, must come last in a statement, and cannot call children()
modules that operate on objects can only call other operator modules and must call children() as their last statement
and i have documented all of this after quite a lot of experimentation to be sure i was writing true things about modules
If you folks can correct my understanding of modules where i might be in error i would appreciate it so i can update the docs
thanks
First a few comments on your language. In three you talk about "calling
children()". Well, "children()" is a module that you can call any place a
module is allowed. Another module can call it many times, and it need not
be at the end of anything. Maybe you meant "children".
Another problem with the language is that modules may "call" other modules,
including children(), but they do that within the module definition, which
is not available to us for built-in modules. When you are using a module
you write the modules name and then you give it a list of children, in
braces if there are more than one. I would not describe this relationship
as "the module calling" the children. It's more like the module "has"
children. They are there for the module to use, which is can do any number
of times. Also if you have an expression like
color("red")
translate([0,3,0])
sphere(r=4);
then color() is the first module. It has one child which is
"translate([0.3,0])sphere(r=4)". The notion that the child (children?) is
somehow last is mistaken. Every module not followed immediately by a ";"
character has children. Furthermore, you don't seem to be clearly
acknowledging that there might be more than 1 child. For example:
color("red") { cube(5); translate([0,3,0]) sphere(r=4);}
Now the color module has 2 children. Again, nothing is "called" they are
simply the children provided to the color module.
The problem is that your "real terms" aren't right because a module can
both draw geometry and call children. Consider:
module both(size)
{
right(size*1.5) children();
cube(size);
}
both(10) sphere(4);
It draws a shape but also it acts on its children. And both() can appear
anywhere in what you're calling a statement. Based on your list it fails
2 because it can call children and it fails 3 because it need not call
children. So it doesn't fit in your classification. If your goal is to
classify ONLY the built-in modules in native OpenSCAD without any broader
applicability (no libraries or user written code) then I think it does
cover the (currently) available modules, but it's not fully general and it
also doesn't cover modules found in libraries or the range of what can be
written. In other words, it's not a fundamental characteristic of modules
but more like a pragmatic division of the native modules.
In the BOSL2 library, for example, the cuboid() module draws geometry and
can be invoked without children, but it also optionally accepts children
and runs them. There are very few modules in BOSL2 that satisfy condition
2 on your list because almost everything that creates geometry also accepts
children, and furthermore, BOSL2 redefines most of the native modules to
match BOSL2's general behavior.
So in BOSL2:
include<BOSL2/std.scad>
cuboid(10) attach(RIGHT,BOT) cyl(r=5,h=10);
produces this:
[image: image.png]
On Thu, Jul 31, 2025 at 7:02 PM vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
while the underlying implementation may be the same for Operator Modules
and Object Modules in practice the differences are critical.
If one defines an Object Module it results in one or more shapes being
drawn. An Operator Module must end with a call to children() and cannot
itself draw shapes.
An operator at the end of a statement causes no error nor warning, but
also does nothing. .. at least in the 2025.07.20 i am using
An object can only be at the end of a statement .. sort of
color("red")
cube(5)
translate([0,3,0])
;
this will run but emits a warning: WARNING: module cube() does not support
child modules …
but it does draw the cube
This also draws the cube, with the warning, but does not draw the second
object, the sphere()
color("red")
cube(5)
translate([0,3,0])
sphere(r=4);
So in real terms, there are
1.
functions that return a value, can only be used in expressions, and
cannot draw geometry
2.
modules that draw geometry, must come last in a statement, and cannot
call children()
3.
modules that operate on objects can only call other operator modules
and must call children() as their last statement
and i have documented all of this after quite a lot of experimentation to
be sure i was writing true things about modules
If you folks can correct my understanding of modules where i might be in
error i would appreciate it so i can update the docs
thanks
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
thanks Adrian, for the feedback and guidance.
i first want to check a few things with you :
from you n other comments my breadth of language is a bit jarring for the reader .. ok
object is now reserved for things made by the object() function .. unless .. is that also a module pretending to be a function ??
i will use shape as the generic term for anything drawn into the model as lines or surfaces, and thus will not use “geometry” except when talking about math shapes that are geometrical concepts
drawn will now be the word for making lines and surfaces appear in the model
rendering will be the word for processing the shapes in the model into a human visible form, be that preview, generating an image or exporting a file
is children() a placeholder for the objects that the operator module will be applied to at run-time? OR is it a funky function? or is it more of an object module ? or is it another of the funky operators let(), assert() etc.?
Now the discussion for thread:
the term “call” or “calling“ is time honored tradition to use when one chunk of code (program, subroutine whatever) makes a reference to another chunk of code .. be called a function, subroutine or method soo .
if there is something really truly NOT correct about using the term calling then i will change my approach
BUT
the appearance of the thing is that an openSCAD script can “call” modules, be they user defined or built-in and can “call” functions in the process of evaluating an expression with exactly the same meaning from when FORTRAN allowed the definition of subroutines .. actually i write an un-truth .. subroutines were available from the time the 8008 processor was built with a “call <address>” microcode that pushed the registers onto the stack, jumped to the <address> to continue processing until the “return” microcode when the stack was popped to reload the registers and the program counter reset to the instruction
so, most / many/ some people reading the scad docs and example code (especially!) will see modules and functions being “called“ so i propose to keep using that language to describe code calling code
That said; your comment raises a really valuable point .. the effect of some modules is to create shapes that get placed into a hierarchy .. thus creating parent-child relationships between shapes that have no connection to their underlying math, nor when in the process of execution they got created.
To return to the syntactical issue for a sec, most people reading code that uses braces will see them as creating lexical scope .. which is also true in scad, right? variables defined inside of a brace pair are local to that scope.
IN ADDITION scad is preserving the state of special variables on the stack to allow them to do .. well, something pretty nifty but that i don’t really have my head around yet.
AND IN FURTHER ADDITION braces are creating another generation of children
first .. is this all true? pretty close? where am i still going wrong?
back to children and parents .. so i am now planning to revise my text on object module versus operator module to say that they are variations on the single underlying module thing in the code. But i still feel strongly that the conceptual separation of operator versus object will help people coming new to the language get their head’s around it.
That a module can BOTH draw shapes AND operate on child objects via a call to children() is almost irrelevant to most users. There was no mention of this in the docs before i got into them .. uhh .. at least not that i saw .. i could have missed it. There have already been a few tiny but important details that did get mention, but in places a bit hidden away.
But even so .. this ability can be covered as an exception to the general usages of modules, specifically user-defined modules, as the vast majority of these will be Either making objects or Operating on objects. A few folks have said stuff about BOSL2 and they way they have extended scad .. which is all to the good .. but it is hard to program anything really complex in scad because the fundamentals of the language are quite difference .. but use the same syntax as C, Java etc … by which i mean the same operators, the concept of stored routines that you call by name, functions that return values to stand in for complex calculations .. sure looks similar ..
i am trying to help two groups of readers
expert scad coders that need a language reference
people coming to scad from other programming traditions n tech
An example of a module that creates geometry and places children is a
washer in NopSCADlib that draws a washer but if it has a child it places it
on top. The child could be a screw or a nut or a second washer.
On Sat, 2 Aug 2025, 23:20 vulcan_--- via Discuss, <
discuss@lists.openscad.org> wrote:
thanks Adrian, for the feedback and guidance.
i first want to check a few things with you :
from you n other comments my breadth of language is a bit jarring for the
reader .. ok
1.
object is now reserved for things made by the object() function ..
unless .. is that also a module pretending to be a function ??
2.
i will use shape as the generic term for anything drawn into the model
as lines or surfaces, and thus will not use “geometry” except when talking
about math shapes that are geometrical concepts
3.
drawn will now be the word for making lines and surfaces appear in the
model
4.
rendering will be the word for processing the shapes in the model into
a human visible form, be that preview, generating an image or exporting a
file
5.
is children() a placeholder for the objects that the operator module
will be applied to at run-time? OR is it a funky function? or is it more of
an object module ? or is it another of the funky operators let(), assert()
etc.?
Now the discussion for thread:
the term “call” or “calling“ is time honored tradition to use when one
chunk of code (program, subroutine whatever) makes a reference to another
chunk of code .. be called a function, subroutine or method soo .
if there is something really truly NOT correct about using the term
calling then i will change my approach
BUT
the appearance of the thing is that an openSCAD script can “call”
modules, be they user defined or built-in and can “call” functions in the
process of evaluating an expression with exactly the same meaning from when
FORTRAN allowed the definition of subroutines .. actually i write an
un-truth .. subroutines were available from the time the 8008 processor was
built with a “call <address>” microcode that pushed the registers onto the
stack, jumped to the <address> to continue processing until the “return”
microcode when the stack was popped to reload the registers and the program
counter reset to the instruction
so, most / many/ some people reading the scad docs and example code
(especially!) will see modules and functions being “called“ so i propose to
keep using that language to describe code calling code
That said; your comment raises a really valuable point .. the effect of
some modules is to create shapes that get placed into a hierarchy .. thus
creating parent-child relationships between shapes that have no connection
to their underlying math, nor when in the process of execution they got
created.
To return to the syntactical issue for a sec, most people reading code
that uses braces will see them as creating lexical scope .. which is also
true in scad, right? variables defined inside of a brace pair are local to
that scope.
IN ADDITION scad is preserving the state of special variables on the stack
to allow them to do .. well, something pretty nifty but that i don’t really
have my head around yet.
AND IN FURTHER ADDITION braces are creating another generation of children
first .. is this all true? pretty close? where am i still going wrong?
back to children and parents .. so i am now planning to revise my text on
object module versus operator module to say that they are variations on the
single underlying module thing in the code. But i still feel strongly that
the conceptual separation of operator versus object will help people coming
new to the language get their head’s around it.
That a module can BOTH draw shapes AND operate on child objects via a call
to children() is almost irrelevant to most users. There was no mention of
this in the docs before i got into them .. uhh .. at least not that i saw
.. i could have missed it. There have already been a few tiny but important
details that did get mention, but in places a bit hidden away.
But even so .. this ability can be covered as an exception to the general
usages of modules, specifically user-defined modules, as the vast majority
of these will be Either making objects or Operating on objects. A few folks
have said stuff about BOSL2 and they way they have extended scad .. which
is all to the good .. but it is hard to program anything really complex in
scad because the fundamentals of the language are quite difference .. but
use the same syntax as C, Java etc … by which i mean the same operators,
the concept of stored routines that you call by name, functions that return
values to stand in for complex calculations .. sure looks similar ..
i am trying to help two groups of readers
1.
expert scad coders that need a language reference
2.
people coming to scad from other programming traditions n tech
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
Vulcan,
Let me note that I'm involved in the BOSL2 project, which consumes a lot of
my time and effort and I don't have time to deeply review your
documentation effort for OpenSCAD. I know OpenSCAD well and almost never
refer to its manual, so I'm not exactly the audience. I'm not sure why
expert coders would need a language reference. I only need to occasionally
look up how search() works or other things like that, mainly because the
way search() works is ridiculous.
I do have a bit of a concern that you seem to be trying to (re)write the
OpenSCAD manual while having a somewhat shallow and incomplete
understanding of OpenSCAD.
I think you do not understand how modules are defined and how children
work. It is absolutely incorrect to describe modules as "calling"
children, in my opinion. The best word would be that children are
"passed".
Users can define modules in their code, they aren't just magical things
provided by the language. A module defined by a user can call other
modules in the normal sense of "calling". That looks like this:
module foo(input)
{
other_module(3*input);
}
In this example, the module foo() has called the module other_module().
You would invoke foo by writing "foo(17);" in your code, perhaps. And we
can see that foo() makes no use of children.
If you have a module that uses children, perhaps bar(), then when you call
it, you might write this:
bar(24) child();
In this case, bar() is being run and given one child. I maintain that bar
does not "call" the child in this construction. The most standard way to
describe the relationship is that the child is PASSED to bar as a
parameter, but it's a different kind of parameter than a variable. It's a
parameter which is geometry, or I guess what you want to call "shape".
(I'm not a great fan of "shape" because geometry can be several
shapes...but I'm also used to calling it geometry. That's what we call it
in the BOSL2 manual.) So the other way to invoke bar() with children is
this:
bar(24)
{
child1();
child2();
child3();
}
Here I have passed three children to bar(). Again, in this code, no
children have been "called". The children are parameters which are
geometry (shape) instead of data and they are being passed into bar which
can use them or ignore them as it chooses.
Now within bar() I may want to use the children. The way to do that is to
call the children() module. This is an ordinary module that invokes all of
the children of the current module(). I guess in your language it "draws
the shape" of the children. The bar() module can call children() never,
or once, or as many times as it likes, just like with any other kind of
parameter. It can also do children(0) which "draws the shape" of the first
child, which I called child1() above.
It MAY be a useful taxonomy for the native modules to split them apart into
shape modules and operator modules. I don't know. But it's important to
be aware that this doesn't apply to ALL modules, only the native ones
currently in existence. But this is kind of like saying that in the C
language functions can either return a computed value or they can print
output. It's an incomplete taxonomy of what functions in C can do.
Another thing to consider here is that a beginning user will be using the
modules provided by native OpenSCAD. But as the user advances just a bit
they will start writing their own modules which may or may not fit into the
above framework. Writing your own modules is not some kind of advanced
thing---it's a basic approach for creating readable code. And if the user
chooses to use libraries like BOSL2 or NopSCADlib they will encounter
modules in those libraries that do not fit into the above framework. As
previously noted, in BOSL2 every module that "draws a shape" also accepts
children---with very few exceptions. It's probably a better dichotomy if
you say that there are modules that REQUIRE children and then modules that
do NOT require children. I suggest that if you present a classification
of module types, make sure it is complete, in the sense that it covers
every possible kind of module and then indicate that the NATIVE modules all
fit into just these two classes, whereas modules defined by libraries or
that you define yourself may fit into this or these other classes.
I have no clue what you mean about preserving the state of special
variables on a stack.
An open brace actually does NOT create a new scope when used in isolation.
You can see this by doing
a=3;
{
a=2;
echo(a);
}
If a new scope was created, this would run without warnings, but it gives a
reassignment warning when a=2 is assigned. There was talk in the past of
making isolated braces invalid, actually.
If the braces are used to pass children to a parent, then they create a new
scope. Hence this works:
a=3;
assert(true)
{
a=2;
echo(a);
}
Now the stuff in braces forms children to the assert() module and it
creates a new scope. When you open a new scope like this all previously
existing variables are preserved, and new variables---which may have the
same name and hence hide the old ones---can be created in the new scope.
The same thing happens with let(). This can make it appear that you can
redefine variables, like above where I "redefined" a, but it only works
because the "redefinition" is in a new scope.
On Sat, Aug 2, 2025 at 6:20 PM vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
thanks Adrian, for the feedback and guidance.
i first want to check a few things with you :
from you n other comments my breadth of language is a bit jarring for the
reader .. ok
1.
object is now reserved for things made by the object() function ..
unless .. is that also a module pretending to be a function ??
2.
i will use shape as the generic term for anything drawn into the model
as lines or surfaces, and thus will not use “geometry” except when talking
about math shapes that are geometrical concepts
3.
drawn will now be the word for making lines and surfaces appear in the
model
4.
rendering will be the word for processing the shapes in the model into
a human visible form, be that preview, generating an image or exporting a
file
5.
is children() a placeholder for the objects that the operator module
will be applied to at run-time? OR is it a funky function? or is it more of
an object module ? or is it another of the funky operators let(), assert()
etc.?
Now the discussion for thread:
the term “call” or “calling“ is time honored tradition to use when one
chunk of code (program, subroutine whatever) makes a reference to another
chunk of code .. be called a function, subroutine or method soo .
if there is something really truly NOT correct about using the term
calling then i will change my approach
BUT
the appearance of the thing is that an openSCAD script can “call”
modules, be they user defined or built-in and can “call” functions in the
process of evaluating an expression with exactly the same meaning from when
FORTRAN allowed the definition of subroutines .. actually i write an
un-truth .. subroutines were available from the time the 8008 processor was
built with a “call <address>” microcode that pushed the registers onto the
stack, jumped to the <address> to continue processing until the “return”
microcode when the stack was popped to reload the registers and the program
counter reset to the instruction
so, most / many/ some people reading the scad docs and example code
(especially!) will see modules and functions being “called“ so i propose to
keep using that language to describe code calling code
That said; your comment raises a really valuable point .. the effect of
some modules is to create shapes that get placed into a hierarchy .. thus
creating parent-child relationships between shapes that have no connection
to their underlying math, nor when in the process of execution they got
created.
To return to the syntactical issue for a sec, most people reading code
that uses braces will see them as creating lexical scope .. which is also
true in scad, right? variables defined inside of a brace pair are local to
that scope.
IN ADDITION scad is preserving the state of special variables on the stack
to allow them to do .. well, something pretty nifty but that i don’t really
have my head around yet.
AND IN FURTHER ADDITION braces are creating another generation of children
first .. is this all true? pretty close? where am i still going wrong?
back to children and parents .. so i am now planning to revise my text on
object module versus operator module to say that they are variations on the
single underlying module thing in the code. But i still feel strongly that
the conceptual separation of operator versus object will help people coming
new to the language get their head’s around it.
That a module can BOTH draw shapes AND operate on child objects via a call
to children() is almost irrelevant to most users. There was no mention of
this in the docs before i got into them .. uhh .. at least not that i saw
.. i could have missed it. There have already been a few tiny but important
details that did get mention, but in places a bit hidden away.
But even so .. this ability can be covered as an exception to the general
usages of modules, specifically user-defined modules, as the vast majority
of these will be Either making objects or Operating on objects. A few folks
have said stuff about BOSL2 and they way they have extended scad .. which
is all to the good .. but it is hard to program anything really complex in
scad because the fundamentals of the language are quite difference .. but
use the same syntax as C, Java etc … by which i mean the same operators,
the concept of stored routines that you call by name, functions that return
values to stand in for complex calculations .. sure looks similar ..
i am trying to help two groups of readers
1.
expert scad coders that need a language reference
2.
people coming to scad from other programming traditions n tech
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
I share this concern. I welcome your helpful participation, but worry
that the result may require a lot of re-work.
On 8/2/2025 8:37 PM, Adrian Mariano via Discuss wrote:
I do have a bit of a concern that you seem to be trying to (re)write
the OpenSCAD manual while having a somewhat shallow and incomplete
understanding of OpenSCAD.
--
This email has been checked for viruses by AVG antivirus software.
www.avg.com
[ Sigh. I hate being away from my desktop environment. Sorry for the
duplicate. ]
On 8/2/2025 3:20 PM, vulcan_--- via Discuss wrote:
object is now reserved for things made by the object() function ..
unless .. is that also a module pretending to be a function ??
Modules are modules and functions are functions. There is no such thing
as one pretending to be the other.
object() is a function, period.
rendering will be the word for processing the shapes in the model into
a human visible form, be that preview, generating an image or
exporting a file
No, absolutely not. The word "render" is well-defined in OpenSCAD to
refer to the process of reducing a model to a mesh. It most specifically
does not refer to the process of previewing a model. Yes, abstractly
that is a form of rendering, but it is not what the word means in OpenSCAD.
is children() a placeholder for the objects that the operator module
will be applied to at run-time? OR is it a funky function? or is it
more of an object module ? or is it another of the funky operators
let(), assert() etc.?
children() is a module that invokes the operations that were passed to
the current module as its children.
As previously discussed, "operator module" and "object module" are a
false dichotomy, perhaps useful as a simple mental distinction but not
useful for formally describing OpenSCAD modules. OpenSCAD modules may
accept zero or more children. They may add shapes of their own, or they
may not. They always yield a shape, though that shape may be empty.
children() does not neatly fall into either the "operator" or "object"
classification, because it does not accept children, and does not
produce shapes of its own; it invokes the children of the current module
instantiation.
There are only three "funky operators" - let, echo, and assert - and
they only apply in expression context, just as operators like + and -
only apply in expression context.
[ "calling" ]
The debate is over what to call the relationship between A and B in
A() B();
It is formally not correct to say that that line calls B. Nor is it
truly correct to say that A calls B. That line calls A, which might or
might not ... invoke ... the following statement that then calls B.
But how to say that in a way that is both technically correct and is
accessible to a newbie is difficult.
actually i write an un-truth .. subroutines were available from the
time the 8008 processor was built with a “call <address>” microcode
Long, long before that. 8008 is 1970s; FORTRAN is 1950s. I don't know
what's before that, but something is.
That said; your comment raises a really valuable point .. the /effect/
of some modules is to create shapes that get placed into a hierarchy
.. thus creating parent-child relationships between shapes that have
no connection to their underlying math, nor when in the process of
execution they got created.
Actually, the resulting CSG tree is pretty closely tied to the execution
process.
To return to the syntactical issue for a sec, most people reading code
that uses braces will see them as creating lexical scope .. which is
also true in scad, right? variables defined inside of a brace pair are
local to that scope.
Not exactly. It's really the parent module invocation that creates the
scope. Braces by themselves do not create scopes (sigh).
It's a little hard to see this, but let() and for() demonstrate it; they
create a scope without needing braces to do it.
IN ADDITION scad is preserving the state of special variables on the
stack to allow them to do .. well, something pretty nifty but that i
don’t really have my head around yet.
$ variables are accessible to the scope that creates them and to all
scopes called by that scope.
AND IN FURTHER ADDITION braces are creating another generation of
children
No. Braces do not themselves create children. Braces group children.
If you have only one child, braces are not required.
A() { B(); }
is exactly equivalent to
A() B();
That a module can BOTH draw shapes AND operate on child objects via a
call to children() is almost irrelevant to most users.
Increasingly untrue. These days, pretty much as soon as somebody asks a
tricky question, the answer is "go look at BOSL2, it solves that
problem"... and for BOSL2, modules that create shapes and operate on
their children are almost universal.
There was no mention of this in the docs before i got into them .. uhh
.. at least not that i saw .. i could have missed it. There have
already been a few tiny but important details that did get mention,
but in places a bit hidden away.
The core set of modules doesn't include any that create shapes and
consume children.
That only arises from the implications of children(), that there is no
reason that you can't create shapes from a module that also calls
children().
but it is hard to program anything really complex in scad because the
fundamentals of the language are quite difference
I suppose that it depends on your definition of "hard", but I think
there are at least hundreds of thousands of lines of OpenSCAD programs
that would disagree. (I mean, I can account for over over 10K in one
project alone, and BOSL2 is 77K...)
Is it different? Sure. And some aspects are really tricky, probably
trickier than other languages. But you can do something moderately
complex without needing to explore the dark corners.
Writing documentation, on the other hand, is hard, because it needs to
both correctly describe those dark corners and be accessible to
beginners. I think the answer to that is what's sometimes called
"progressive disclosure", where you present a simplified view, and then
introduce more advanced concepts as the user progresses.
That's pretty straightforward for tutorial matter, where you control the
intended order that the user reads the documentation. It's harder for
reference material, where you don't know in advance how deep the user
wants to go. But even then you can take care to use language that is
simple but formally correct, and to introduce simpler aspects of a
particular topic before more advanced aspects.
Some documentation I read once - I dimly think it was the TeX manual -
specifically said in its introduction that it would lie to you, that it
would give you an incorrect but simplified and useful view of a topic,
and then would later correct it with more advanced concepts. I think
that's a useful model.
i am trying to help two groups of readers
expert scad coders that need a language reference
people coming to scad from other programming traditions n tech
Mostly, group 1 doesn't look at the documentation because they already
have everything in their head.
But also there's group 3: non-programmers. They form a significant
fraction of the user base.
Many of the concepts in this discussion, especially those around the
"child" relationship, are easier to talk about if you talk about lambda
functions... but those are a fairly advanced programming concept that
you don't really need to get into as a beginner; you can use the
mechanisms without needing to dive into that level of detail.
[ Going dark for a couple of days, en route from Scotland to Iceland. ]
HI Jordan .. i know it will be a while before you see this, but i wanted to reply to one small issue you raised
No, absolutely not. The word "render" is well-defined in OpenSCAD to
refer to the process of reducing a model to a mesh. It most specifically
does not refer to the process of previewing a model. Yes, abstractly
that is a form of rendering, but it is not what the word means in OpenSCAD.
ah .. that scad assigns that specific meaning to “render” was not made clear to my until i read your message just now.
You know that my use of render, to process a model to create an image, is the standard use in the industry, yes?
In my experience the operation of making a polygonal version of the shapes in a model is “tessellation“
I did note that the terms, if you will, of “F5” and “F6” in the documentation seemed to carry more weight than just to indicate which function key to press. But after using them both i concluded that “F6” was a short form for “tessellation“ as its operation is to divide any face with more than 3 edges into triangles.
I have learned that the result of running an scad script is a mesh .. but, at least for primitives, drawn in n-gons .. so the term rendering does not seem to apply, to me. But i bow to the linguistic convention of the group.
er .. i hope that my readers understand that i am not being facetious saying this .. my career was based on my being able to establish good communications between my teams and those around us, and within the teams themselves, by creating and promoting language specific to each project and its corporate environment.
That was what i did in every job, so i am doing it here too
and .. this one
OpenSCAD modules may
accept zero or more children. They may add shapes of their own, or they
may not. They always yield a shape, though that shape may be empty.
First, .. what is an “empty shape“ ?
on the face of it that makes little sense. If there is nothing displayed in the preview there was no mesh made, right?
I do get, now, that a module may create shape(s) itself, or be a parent to children that may create shapes. And that a module might perform only operations like color(), translate(), assert() and the like, thus defining what i have been calling an operator module, does not create any mesh, though its children might
so how can a module always yield a shape?
i hope to get to alignment on terminology with respect to the roles of functions and modules in a scad.
in this statement:
xx = do_something( 12 );
^ this is a call to a function, there are no children
do_operation() make_shape();
^ this and ^ this are both calls to modules
^ this is the parent of ^ this child, by the syntax of it following the call to `do_operation()`
I am using the terms “call” and “calling“ to refer to the execution of a module during the execution of a script, which is industry standard as far as i know.
What was not clear to me was that a sequence of module calls in a single statement is the syntax for defining a nested parent-child shape that will be “rendered” as a mesh as a part of the model the script is drawing. But now it is and i am updating the docs to express my improved understanding
If you intersect two shapes that don't overlap or difference a shape that
completely overlaps the first operand, then you get an empty shape. It is
an empty geometry object.
All modules return a shape, but it may be empty.
On Tue, 5 Aug 2025, 22:51 vulcan_--- via Discuss, <
discuss@lists.openscad.org> wrote:
and .. this one
OpenSCAD modules may
accept zero or more children. They may add shapes of their own, or they
may not. They always yield a shape, though that shape may be empty.
First, .. what is an “empty shape“ ?
on the face of it that makes little sense. If there is nothing displayed
in the preview there was no mesh made, right?
I do get, now, that a module may create shape(s) itself, or be a parent to
children that may create shapes. And that a module might perform only
operations like color(), translate(), assert() and the like, thus defining
what i have been calling an operator module, does not create any mesh,
though its children might
so how can a module always yield a shape?
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
ah .. that leads me to ask, why bother? if there is no geometry created by a module, thus no change to any part of a mesh that might already exist in the model, why retain anything of the operation that effectively did nothing?
An empty geometry object has to exist because some CGS operations may
result in one.
In the case of do_operation() make_shape();
do_operation() is instantiated, but it may or may not instantiate
make_shape();
On Tue, 5 Aug 2025 at 23:10, vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
ah .. that leads me to ask, why bother? if there is no geometry created by
a module, thus no change to any part of a mesh that might already exist in
the model, why retain anything of the operation that effectively did
nothing?
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
thanks for patiently answering my incessant questions .. enlightenment is as hard a road for the teacher as the student.
i am grateful
if() is a module that may or may not invoke its child. If the expressions
is false then the child is never called and may even have errors in it. An
empty geometry object has to be return because it might be part of say a
difference() or a union().
On Tue, 5 Aug 2025 at 23:19, vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
thanks for patiently answering my incessant questions .. enlightenment is
as hard a road for the teacher as the student.
i am grateful
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
ah .. that leads me to ask, why bother? if there is no geometry created by a module, thus no change to any part of a mesh that might already exist in the model, why retain anything of the operation that effectively did nothing?
This is why the invention of the zero was so important. Mostly :-) but only mostly.
Let’s talk about lists.
Suppose that we have a list [a,b]. Now take away the last element; what is the result? You might reasonably say “a”, and I’ve seen environments that did that, but it’s a nuisance because list operations like iteration and concatenations may not work well on a non-list. (Or may do something totally unexpected if “a” itself is a list.) The answer really needs to be [a], a list of one element.
Now take away the first element of that list. What do you have? You might say “nothing” or “null” or “undefined”, and I have seen environments that do that, but again it’s inconvenient because list operations will not work. You want the answer to be [], the empty list. You can hand it around and you can do all of the usual list-y operations to it, and they work sensibly.
Now let’s go back to OpenSCAD shapes.
Suppose that we have this module:
module empty () {
}
What does it produce?
If you say that it produces nothing at all, not even emptiness, that things should work as if it was not even there, then what does this yield?
difference () {
empty ();
cube(10);
}
I would hope that the answer is never “a cube”.
Perhaps more real-world, consider this module:
module maybe_sphere(f) {
if (f) sphere (10);
}
If that didn’t always produce a shape, perhaps an empty shape, it would be very hard to use. There would be lots of cases where the caller would have to somehow know whether it was going to produce something, and treat it specially if not.
It only gets worse when you start introducing other OpenSCAD semantics.
What does this print?
module how_many_children() {
echo ($children);
}
how_many_children() {
maybe_sphere(rands(0,2,1)[0] > 1);
}
If maybe_sphere() should effectively disappear when it generates nothing, then the answer should be either zero or one, but (for very useful reasons) OpenSCAD doesn’t evaluate the children until the module calls children(), so the answer is not knowable.
Net, for the same basic reasons that zero, empty lists, empty sets, and empty strings are valuable and even essential, empty shapes are valuable and even essential.
In my experience the operation of making a polygonal version of the shapes in a model is “tessellation“
This usage of “tessellation” does not seem to appear in https://en.m.wikipedia.org/wiki/Tessellation .
Tessellation is the process of fitting shapes into other shapes - often, covering an infinite plane with a pattern of shapes. It has nothing to do with things like transformations and Boolean operations, which are what the OpenSCAD rendering process does. If the OpenSCAD rendering process does tessellation, it’s an implementation detail subject to change at any time for any reason or no reason.
I agree that “rendering” in the broader industry often means generating an image, though I think it usually means generating a final image - I don’t think that CGI people call it “rendering” when they generate stick-figure visualizations; they mean the compute-intensive process of producing the final image.
But, expect for the “producing the final result” connotation, that’s not what it means here, and for better or worse the local definition is unlikely to change.
I did note that the terms, if you will, of “F5” and “F6” in the documentation seemed to carry more weight than just to indicate which function key to press.
You do know that they are short cuts for the Preview and Render menu items, right?
(I might have the exact words wrong, because I’m not in front of a computer, but it’s something like that.)
But after using them both i concluded that “F6” was a short form for “tessellation“ as its operation is to divide any face with more than 3 edges into triangles.
No, not at all. Again, if that happens it is an implementation detail subject to change at any time.
OpenSCAD rendering is the process of doing all of the operations required to yield a single (perhaps discontiguous) mesh. Concretely, given this script:
difference() {
cube(10);
translate ([5,5,5]) cube(10);
}
It is the process that turns those twenty-four edges and twelve faces into the resulting nine faces and fourteen edges. (If I’ve counted in my head correctly.)
It may, or may not, further divide those faces into triangles.
It may, in fact, do anything it feels like, as long as the result describes the correct closed shape.
(And, less commonly used but still entirely valid, given a 2D model it yields a set of polygons.)
I have learned that the result of running an scad script is a mesh
Maybe, maybe not. Sometimes it’s a mesh, sometimes it’s an image, sometimes it’s polygons, sometimes it’s text. It depends on the script and how it’s run.
If, and only if, you use the Render (F6) operation, or its CLI equivalent, the result is a mesh. (NB: not the render() module; that does not affect whether the final result is a mesh.) (Or, for a 2D model, a set of polygons.)
i hope to get to alignment on terminology with respect to the roles of functions and modules in a scad.
in this statement:
xx = do_something( 12 );
^ this is a call to a function, there are no children
do_operation() make_shape();
^ this and ^ this are both calls to modules
^ this is the parent of ^ this child, by the syntax of it following the call to do_operation()
Yes. Give or take the discuss of “call” below.
I am using the terms “call” and “calling“ to refer to the execution of a module during the execution of a script, which is industry standard as far as i know.
Yes, but there are important questions around what calls what, and when, and how that is described to the user.
For a beginning user, it is probably best to think of
rotate(45) cube(10);
as calling cube() to create a cube, and passing the result to rotate() to rotate it.
And you can use mental model for quite a while, even when you start to write modules that take children of their own.
But it’s false.
What actually happens is that it calls the rotate() module and passes the “cube(10)” statement to it, and rotate() evaluates that statement, rotates the result, and yields the rotated result.
But that’s a way more complex mental model than a beginner, or even an intermediate, really needs.
So the trick is in how to start out with the simpler mental model and eventually introduce the more complex and correct model, without totally confusing the user.
What was not clear to me was that a sequence of module calls in a single statement is the syntax for defining a nested parent-child shape that will be “rendered” as a mesh as a part of the model the script is drawing.
That seems like a pretty serious failure of the tutorial matter, since that’s the most fundamental building block of the entire language. (You did read the tutorial matter before the reference manual, right?)
After following the recent discussions and reading the current version of
the wiki, I have several major concerns here.
While I appreciate Vulcan's hard work, I don't think the current rewrite
works.
Best,
John
On Wed, Aug 6, 2025 at 1:01 PM Jordan Brown via Discuss <
discuss@lists.openscad.org> wrote:
i hope to get to alignment on terminology with respect to the roles of
functions and modules in a scad.
in this statement:
xx = do_something( 12 );
^ this is a call to a function, there are no children
do_operation() make_shape();
^ this and ^ this are both calls to modules
^ this is the parent of ^ this child, by the syntax of it following the
call to do_operation()
Yes. Give or take the discuss of “call” below.
I am using the terms “call” and “calling“ to refer to the execution of a
module during the execution of a script, which is industry standard as far
as i know.
Yes, but there are important questions around what calls what, and when,
and how that is described to the user.
For a beginning user, it is probably best to think of
rotate(45) cube(10);
as calling cube() to create a cube, and passing the result to rotate() to
rotate it.
And you can use mental model for quite a while, even when you start to
write modules that take children of their own.
But it’s false.
What actually happens is that it calls the rotate() module and passes the
“cube(10)” statement to it, and rotate() evaluates that statement, rotates
the result, and yields the rotated result.
But that’s a way more complex mental model than a beginner, or even an
intermediate, really needs.
So the trick is in how to start out with the simpler mental model and
eventually introduce the more complex and correct model, without totally
confusing the user.
What was not clear to me was that a sequence of module calls in a single
statement is the syntax for defining a nested parent-child shape that will
be “rendered” as a mesh as a part of the model the script is drawing.
That seems like a pretty serious failure of the tutorial matter, since
that’s the most fundamental building block of the entire language. (You did
read the tutorial matter before the reference manual, right?)
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
Hi Jordan
you said this:
This usage of “tessellation” does not seem to appear in https://en.m.wikipedia.org/wiki/Tessellation .
correct, my usage of “tessellation“ comes from talking about rendering issues in long sequence animations for the movie industry with the folks developing / maintaining the render engines for Alias|wavefront Power Animator, Maya and the wavefront tool chain .. all of whom gleefully plucked the word from its correct use as a math term and applied it to
mean “Map all the NURBs in a model into their equivalent mesh so that we can make pictures with it”
The wiki page you cite is correct in another context. And it is even possible that the render gods of A|w were simplifying things in their explanations of the many things that could produce unwanted artifacts in a rendered image to me and so i may have misunderstood what they meant by “tessellation“ too.
—-
about “calling” a subroutine, with ref to your kind reply:
rotate(45) cube(10);
as calling cube() to create a cube, and passing the result to rotate() to rotate it.
if we could agree that the term “call a module” to mean: ( using cube(10) as the example)
jump to the chunk of code labeled cube() passing its defined argument list with default values except size=45
execute that code and then return
in your example cube() is not in the parameter list of the call to rotate(), so i do not like to say it is “passed” to rotate()
My new understanding of the module’s syntax is that it includes a following child module, thus the definition of a statement’s syntax when a module is being called is (hacking BNF brutally) :
module <name>( <arg>* ) [ | child_module()* | {<statement>* } ];
meaning that the following child can be omitted, a call to another module, or a block of code within braces (using ‘|’ to separate alternatives). Textually this parallels how a procedural language builds a program’s structure in preparation for execution .. and no, the tutorials do not do enough to help a guy from that sort of background learn the very different approach of 1. a functional language and 2. scad’s use of a sequence of module calls and blocks in a statement to define an element of a model
and finally, no - of course i did not read the tutorials. I need to 3D print some small gears and google found me some FreeCAD extensions, websites, stand-alone programs, and several OpenSCAD libraries .. so i took a look at the ones that seemed to do what i needed .. one of the most accessible was an scad script that had been adapted (upgraded?) from the original version on Thingverse, which was in German. So what does any Real Programmer do? That is right!
One downloads the code to start hacking it to understand it, “improving it” as i go, translating to english and studying up on gear terminology and when the bits i touched broke .. being dissatisfied with the scad docs so that of course i had to write a few dozen text and example scripts to test the details of syntax etc.
Finding BOSL2 too complex for what i was trying to do .. so finding stuff i could used in the relativity library .. but not really understanding some things its code was doing .. more experimenting revealed flaws in the docs .. which as a Wikiverse contributor i could actually fix
well and truly a rabbit hole into which i jumped. I think you know how that happens, yes?
pca006132 wrote:
After following the recent discussions and reading the current version of
the wiki, I have several major concerns here.
you are giving me the feedback i crave PCA, thanks
umm .. then how better to understand it? the docs and tutorials did not do enough so i started to experiment to see how things worked. I have dozens of test and example code snippets to back up what i write about how scad works ..
that said: feedback from you and others has already led me closer to the One True Way of SCAD and i continue to enshrine the revealed wisdom in updates to the docs
you overfit.
i am not at all clear what overfit means .. if you mean i try to understand scad in the context of my own experience but not knowing enough in the context of SCAD i get things wrong .. yep, that is why we write in a wiki, and have this forum - for you folks to educate me. I am happy to do the grunt work of docs updates .. i enjoy getting an explanation correct and helping others along the road to enlightenment.
special variable values are resolved.
Yeah, specials are still a fuzzy thing for me .. even after Jordan’s diligent attempt to educate me on how they work i am not at all clear on what they bring to the language that is any different from having global variables at the top level of scope
I am in the process of fixing these terminology issues .. in a previous post i published the glossary i am now working with
if you take a look at the new pages i am happiest with you will see that i am trying to answer the questions you raised. I too found the old docs imprecise and in a few places contradictory
for instance, i spent time to make this update to the page on 2D primitives
and i had done a complete rewrite of the page on operators in my sandbox to incorporate input i had from others, notably Jordan, and my observations of how they work.
While I appreciate Vulcan's hard work, I don't think the current rewrite
works.
how we move forward is up to the community - if you folks are willing to keep giving me feedback on what is broken / wrong / needs improving then i will keep working to make it all come good.
If not .. it is a wiki .. everyone is free to correct what they find to be wrong .. or even just revert it all back to early April when i started
Best,
John
Thanks John, for your input
drat .. the links i pasted into my reply to john are not visible. I repeat them here
glossary https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox/SCADGlossary
updated page i am happy with ( and answers some of the issues John raised)
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Using_the_2D_Subsystem
and a complete rewrite of the page on Operators ( + - * ?: etc) that is still in my sandbox
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox/Operators_Discussion
rotate(45) cube(10);
as calling cube() to create a cube, and passing the result to rotate() to rotate it.
if we could agree that the term “call a module” to mean: ( using cube(10) as the example)
jump to the chunk of code labeled cube() passing its defined argument list with default values except size=45
execute that code and then return
Sure.
in your example cube() is not in the parameter list of the call to rotate(), so i do not like to say it is “passed” to rotate()
Weeeeellllll…. It kind of is in the parameter list. There are two parameter lists. One is a conventional parameter list. The other is the child list, which in this case has a single entry and so does not need braces, but could have multiple entries surrounded by braces.
I’m happy with saying that cube() probably ends up being called. The questions are what calls it, and when.
The simple mental model says that it’s called by the top level, and the result passed to rotate(). But that’s not correct, or not exactly correct. What’s passed down to rotate() is the list of child statements, rotate() evaluates those statements, and that evaluation leads to cube() being called.
My new understanding of the module’s syntax is that it includes a following child module,
It’s best that when you’re being formal you distinguish between a module instantiation (or perhaps “call” or “invocation”) and a module definition. I believe that you’re referring to a module instantiation here.
thus the definition of a statement’s syntax when a module is being called is (hacking BNF brutally) :
module <name>( <arg>* ) [ | child_module()* | {<statement>* } ];
The keyword “module” indicates a module definition. It is roughly akin to “def” in Python or “function” in JavaScript.
The syntax of the body of a module is very similar to the syntax of a statement. (I think it might be identical, but don’t want to go check the parser right now.).
I think you’re trying to describe a module instantiation, so let’s drop “module”:
<name>( <arg>* ) [ | child_module()* | {<statement>* } ];
I assume that you are using brackets to indicate grouping for the | alternatives, rather than to indicate optionality.
A semicolon is not used after a brace-enclosed list, and the * on child_module() includes the case of zero so you don’t need the empty case.
Also, “<arg>” doesn’t describe argument list syntax correctly, so let’s just push the details down into a separate and not-defined-here level. Net, next draft:
<name>( <args> ) [ child_module() ; | {<statement>* } ]
But that’s not quite right, because the last child module installation can have a brace-enclosed list. I think you could syntactically describe it as
<name>( <args>) child_module()* [ ; | {<statement>* } ]
But that’s not really the best representation, because it does not represent the nesting.
Best is probably a recursive definition, something like:
module_instantiation:
<name>(<args>);
| <name>(<args>) <module_instantiation>
| <name>(<args>) { <statement>* }
But for the formally correct definition, refer to the parser, src/core/parser.y.
The subject is still "is color an actual operator???"
I'm losing focus on this thread :)
Jordan Brown via Discuss schreef op 2025-08-06 13:02:
rotate(45) cube(10);
as calling cube() to create a cube, and passing the result to rotate()
to rotate it.
if we could agree that the term "call a module" to mean: ( using
cube(10) as the example)
jump to the chunk of code labeled cube() passing its defined argument
list with default values except size=45
execute that code and then return
Sure.
in your example cube() is not in the parameter list of the call to
rotate(), so i do not like to say it is "passed" to rotate()
Weeeeellllll.... It kind of is in the parameter list. There are
two parameter lists. One is a conventional parameter list. The other
is the child list, which in this case has a single entry and so does not
need braces, but could have multiple entries surrounded by braces.
I'm happy with saying that cube() probably ends up being called. The
questions are what calls it, and when.
The simple mental model says that it's called by the top level, and the
result passed to rotate(). But that's not correct, or not exactly
correct. What's passed down to rotate() is the list of child statements,
rotate() evaluates those statements, and that evaluation leads to cube()
being called.
My new understanding of the module's syntax is that it includes a
following child module,
It's best that when you're being formal you distinguish between a
module instantiation (or perhaps "call" or "invocation") and a module
definition. I believe that you're referring to a module instantiation
here.
thus the definition of a statement's syntax when a module is being
called is (hacking BNF brutally) :
module <name>( <arg>* ) [ | child_module()* | {<statement>* } ];
The keyword "module" indicates a module definition. It is roughly
akin to "def" in Python or "function" in JavaScript.
The syntax of the body of a module is very similar to the syntax of a
statement. (I think it might be identical, but don't want to go check
the parser right now.).
I think you're trying to describe a module instantiation, so let's drop
"module":
<name>( <arg>* ) [ | child_module()* | {<statement>* } ];
I assume that you are using brackets to indicate grouping for the |
alternatives, rather than to indicate optionality.
A semicolon is not used after a brace-enclosed list, and the * on
child_module() includes the case of zero so you don't need the empty
case.
Also, "<arg>*" doesn't describe argument list syntax correctly, so let's
just push the details down into a separate and not-defined-here level.
Net, next draft:
<name>( <args> ) [ child_module()* ; | {<statement>* } ]
But that's not quite right, because the last child module installation
can have a brace-enclosed list. I think you could syntactically
describe it as
<name>( <args>) child_module()* [ ; | {<statement>* } ]
But that's not really the best representation, because it does not
represent the nesting.
Best is probably a recursive definition, something like:
module_instantiation:
<name>(<args>);
| <name>(<args>) <module_instantiation>
| <name>(<args>) { <statement>* }
But for the formally correct definition, refer to the parser,
src/core/parser.y.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
colour is simply a module. In fact I think all named items that are allowed
in a statement are modules. There is no such thing as operator modules.
They are just modules.
On Wed, 6 Aug 2025, 12:09 Matthieu Hendriks via Discuss, <
discuss@lists.openscad.org> wrote:
The subject is still "is color an actual operator???"
I'm losing focus on this thread :)
Jordan Brown via Discuss schreef op 2025-08-06 13:02:
rotate(45) cube(10);
as calling cube() to create a cube, and passing the result to rotate() to
rotate it.
if we could agree that the term "call a module" to mean: ( using cube(10)
as the example)
jump to the chunk of code labeled cube() passing its defined argument list
with default values except size=45
execute that code and then return
Sure.
in your example cube() is not in the parameter list of the call to
rotate(), so i do not like to say it is "passed" to rotate()
Weeeeellllll.... It kind of is in the parameter list. There are two
parameter lists. One is a conventional parameter list. The other is the
child list, which in this case has a single entry and so does not need
braces, but could have multiple entries surrounded by braces.
I'm happy with saying that cube() probably ends up being called. The
questions are what calls it, and when.
The simple mental model says that it's called by the top level, and the
result passed to rotate(). But that's not correct, or not exactly correct.
What's passed down to rotate() is the list of child statements, rotate()
evaluates those statements, and that evaluation leads to cube() being
called.
My new understanding of the module's syntax is that it includes a
following child module,
It's best that when you're being formal you distinguish between a module
instantiation (or perhaps "call" or "invocation") and a module definition.
I believe that you're referring to a module instantiation here.
thus the definition of a statement's syntax when a module is being called
is (hacking BNF brutally) :
module <name>( <arg>* ) [ | child_module()* | {<statement>* } ];
The keyword "module" indicates a module definition. It is roughly akin
to "def" in Python or "function" in JavaScript.
The syntax of the body of a module is very similar to the syntax of a
statement. (I think it might be identical, but don't want to go check the
parser right now.).
I think you're trying to describe a module instantiation, so let's drop
"module":
<name>( <arg>* ) [ | child_module()* | {<statement>* } ];
I assume that you are using brackets to indicate grouping for the |
alternatives, rather than to indicate optionality.
A semicolon is not used after a brace-enclosed list, and the * on
child_module() includes the case of zero so you don't need the empty case.
Also, "<arg>*" doesn't describe argument list syntax correctly, so let's
just push the details down into a separate and not-defined-here level.
Net, next draft:
<name>( <args> ) [ child_module()* ; | {<statement>* } ]
But that's not quite right, because the last child module installation can
have a brace-enclosed list. I think you could syntactically describe it as
<name>( <args>) child_module()* [ ; | {<statement>* } ]
But that's not really the best representation, because it does not
represent the nesting.
Best is probably a recursive definition, something like:
module_instantiation:
<name>(<args>);
| <name>(<args>) <module_instantiation>
| <name>(<args>) { <statement>* }
But for the formally correct definition, refer to the parser,
src/core/parser.y.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
umm .. then how better to understand it? the docs and tutorials did
not do enough so i started to experiment to see how things worked. I
have dozens of test and example code snippets to back up what i write
about how scad works ..
Yes, you should experiment, but that only tells you about the behavior
of the current implementation, not the API contract. You have to think
about what is the truly important thing, and ignore everything that are
not crucial. If you include things that are not crucial, this is
overspecification. The real specification probably only exists in the
devs' mind, if that exists, so deciding what to include requires a bit
of personal judgement and requires thinking like the devs. Here are some
examples (note that these are just my opinion, I am not the core dev and
I don't have the final say over the spec):
I am in the process of fixing these terminology issues .. in a
previous post i published the glossary i am now working with
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox/SCADGlossary
It is good that you are fixing this. My recommendation is to avoid terms
with similar meaning unless you have a really, really convincing reason
to use both of them frequently and you can clearly show the difference
between them. "Shape" and "geometry" doesn't pass this test for me, at
least I have no idea why you want to have them both. For "drawn", do you
want to use it only when describing GUI or use it in the language
reference manual? I don't think you need this word. And I really want to
get rid of the term "action". Why is "statement" not good enough?
if you take a look at the new pages i am happiest with you will see
that i am trying to answer the questions you raised. I too found the old
docs imprecise and in a few places contradictory
I have quite a few comments about style. IMO a reference manual is not
for beginners, and it should be written in a way that helps people
scanning and finding what they need, i.e. consistent style and concise.
First, people reading the reference manual probably don't care much
about the default behavior, so things like "By default this module draws
a unit circle centered on the origin [0,0] as a pentagon with its
starting point on the X-axis at X=1. Its lines have no thickness but the
shape is drawn as a 1 unit high, filled plane." should not be put at the
top of the documentation for the circle module. The first description
should be about the general behavior, written in a concise manner. The
old one "Creates a circle at the origin." is good enough for me, or
maybe you can say "Creates a regular polygon that approximates a circle
at the origin".
Second, the way you document the parameters is hard to understand. Again
for the circle module example, I wouldn't understand its signature if I
had no experience with openscad. The old documentation is not great
either in this case. I would rather want something like
circle(radius)
circle(r=radius)
circle(d=diameter)
radius: circle radius
diameter: circle diameter
And you talk about $fa, $fs, $fn at the end about quality of the
approximation and where the first point is placed, not as parameters.
You only give examples at the end, not interleave examples like the old
circle module documentation.
Third, we should have a more uniform way of writing
deprecated/experimental things. Instead of natural language like "Object
data structures have been introduced to the language recently. [Note:
Requires version 2025.07.11].", a simple highlight with "Since
2025.07.11:" or "Development snapshot only:" will be good. We should
probably put this in the section heading or at the top of the section if
that section is about something new (or deprecated..).
Fourth, we should have things like a grammar for expression and
statements, instead of natural language description such as "A vector or
list is a sequence of zero or more OpenSCAD values. Vectors are
collections of numeric or boolean values, variables, vectors, strings or
any combination thereof. They can also be expressions that 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 that
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 can contain vectors, etc.". This should be
left in the example. Note that the current documentation is also
imprecise: Vector (as a value) elements cannot be unevaluated
expressions, they must be values. You can have expressions in the
constructor of a vector, i.e., the square bracket form. And for things
like matrix, we should at least say whether it is column- or row-major,
i.e., whether the inner vector is a row or a column. This is never
described explicitly in the documentation.
These are what I meant by "too verbose and not detailed enough". I hope
I made my points clear. I will try to write something when I have time
as an example of the style that I want in the reference manual, but I am
running out of time now.
Best,
John
pca006132 wrote:
yeah .. i am still a bit shaky on rendering .. but i am getting there
i get all that .. i have been using docs updates as a way to clarify my thoughts on rendering ..
it is working slowly .. there will be a few more revisions to the rendering page till it is right.
. "Shape" and "geometry" doesn't pass this test for me, at
least I have no idea why you want to have them both.
this is my new glossary : https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox/SCADGlossary
For "drawn", do you
want to use it only when describing GUI
I need a word for everywhere that a module .. does whatever it is that results in a mesh being created.
Jordan tells me the word in use in the community is “render” .. that is a problem for me as my
background in 3D modelling and animation that word was only used for when images were being
generated from a mesh or model.
i settled on “draw” to mean “make or add to a mesh being created by an scad script running”
I don't think you need this word. And I really want to
get rid of the term "action".
action is waaayy too general a word for “doing something”
Why is "statement" not good enough?
“statement“ has a particular lexical and syntactic meaning in the context of coding
in a programming language .. i use in the way i describe in
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Program_Structure
First, people reading the reference manual probably don't care much
about the default behavior, so things like "By default this module draws
a unit circle centered on the origin [0,0]
i have to disagree with you on this. IMHO a clear, complete, and accurate statement
of a built-in function or modules default behavior is a good starting point for a
newcomer to OpenSCAD.
I start with that because when i was just starting out with it was a nice confirmation
of what i was seeing in my experiments .. and from there i write about the parameters
in a concise list .. and then for the params that have some complexity i add additional
sections to cover that .. complexity
The first description
should be about the general behavior, written in a concise manner. The
old one "Creates a circle at the origin." is good enough for me, or
maybe you can say "Creates a regular polygon that approximates a circle
at the origin".
But that .. is too cringe for me. Circle creates a circle .. not a polygon .. it is drawn
as a polygon, but that is an approximation of its shape. The polygon’s size
is determined by it being inscribed inside the circle of the given diameter.
Second, the way you document the parameters is hard to understand.
i agree .. i have experimented with a few templates for param lists .. no final
answer yet … but my current favorite is in the docs for linear_extrude()
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/3D_Modelling#linear_extrude()_Operator_Module
You only give examples at the end, not interleave examples like the old
circle module documentation.
actually i strongly prefer to interleave examples .. the places you are seeing them
bunched up at the end of a section or page are mostly legacy pages that are still
in progress in being reafactored
Third, we should have a more uniform way of writing
deprecated/experimental things.
to this end i have created templates specific to our needs .. see them on
my sandbox page
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox#templates
Fourth, we should have things like a grammar for expression and
statements,
well this would be a can of worms to open.
I prefer natural language expressions and descriptions over symbolic
syntax specs like BNF .. as long as the wording is clear and concise i feel
words are better.
But i am willing to hear a discussion on this point .. it is an important one.
I do make the warning that in my working career the ONLY time that a
team could agree on a standard for documentation was when i was in the
position of being able to unilaterally dictating it.
These are what I meant by "too verbose and not detailed enough". I hope
I made my points clear.
I would say so John, and thanks again for the feedback
Best,
John
Guten Abend
Jeff
speaking only of the built-in modules .. the modules that draw shapes .. circle, sphere etc. only create shapes in the model and cannot do anything with child modules that follow them in a statement .. if there are any
I have been calling these Object Modules, copying that terminology from one or two places in the docs, but now we have actual objects so my attempt to build a consensus crashes and burns :)
and .. built-ins for modifying or moving those objects .. translate, color etc .. these are modules that ONLY operate on their children, can’t draw shapes and are not useful if they end a statement.
There was as place in the docs that referred to these as operating on the other type of module .. thus object modules and operator modules became a “thing” for me and i wrote them up that way .. it seemed a useful distinction .. for the built-in modules.
Not so much for user-defined modules .. folks have been telling me about the cool and complicated things being done in BOSL2 and NopSCADlib .. modules that both draw shapes and do operations on their children.
if we can come up with a better terminology i am all ears
thanks for your input Noppy
I just read this:
I need a word for everywhere that a module .. does whatever it is that
results in a mesh being created.
Jordan tells me the word in use in the community is “render” .. that is
a problem for me as my
background in 3D modelling and animation that word was only used for
when images were being
generated /from/ a mesh or model.
i settled on “draw” to mean “make or add to a mesh being created by an
scad script running”
I can't speak for anyone else, but I thought that the documentation was
OK (not great, but OK), and now we're entertaining changing how
everything is described, because we are not using the same terms as
elsewhere on the web. This is not a compelling argument, at least to me.
I wish that, before ANY of these changes were even CONTEMPLATED, the
community (not someone inexperienced with OpenSCAD) came up with a list
of issues that needed to be resolved, and then we resolved them. I do
not believe that a wholesale change to the documentation was required or
desired. Pieces? Sure. Dealing with the new term "object"? Sure.
I, for one, find what Vulcan is doing to be terrifying. And I do not
have the patience to read all of the new documentation, over and over
again, trying to figure out what else has been broken.
I don't doubt that Vulcan's intentions are good, but I wonder if there
really is a problem that needs solving. I also wonder whether the
community would be better off with an experienced OpenSCAD programmer
making the changes.
I understand that the documentation should be accessible to an OpenSCAD
newbie, but I am not sure that these changes will produce the best product.
If I'm way off base, I apologize.
Jon
On 8/6/2025 5:37 PM, vulcan_--- via Discuss wrote:
pca006132 wrote:
yeah .. i am still a bit shaky on rendering .. but i am getting there
*
Your confusion over how render works is an example of what I
meant by overfit. The API contract is simple: The output of
the render module is a mesh. Trying to understand why the
implementation chooses quad over triangles in some cases but
not the other is futile because it is complex and can be
changed later. Writing it down in documentation will confuse
people and make future updates harder (people will complain it
is a breaking change).
i get all that .. i have been using docs updates as a way to clarify
my thoughts on rendering ..
it is working slowly .. there will be a few more revisions to the
rendering page till it is right.
*
The starting point of the circle module is an important detail
in the API documentation, because it can be used to draw
regular polygons.
. "Shape" and "geometry" doesn't pass this test for me, at least I
have no idea why you want to have them both.
this is my new glossary :
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox/SCADGlossary
For "drawn", do you want to use it only when describing GUI
I need a word for everywhere that a module .. does whatever it is that
results in a mesh being created.
Jordan tells me the word in use in the community is “render” .. that
is a problem for me as my
background in 3D modelling and animation that word was only used for
when images were being
generated /from/ a mesh or model.
i settled on “draw” to mean “make or add to a mesh being created by an
scad script running”
I don't think you need this word. And I really want to get rid of
the term "action".
action is waaayy too general a word for “doing something”
Why is "statement" not good enough?
“statement“ has a particular lexical and syntactic meaning in the
context of coding
in a programming language .. i use in the way i describe in
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Program_Structure
First, people reading the reference manual probably don't care
much about the default behavior, so things like "By default this
module draws a unit circle centered on the origin [0,0]
i have to disagree with you on this. IMHO a clear, complete, and
accurate statement
of a built-in function or modules default behavior is a good starting
point for a
newcomer to OpenSCAD.
I start with that because when i was just starting out with it was a
nice confirmation
of what i was seeing in my experiments .. and from there i write about
the parameters
in a concise list .. and then for the params that have some complexity
i add additional
sections to cover that .. complexity
The first description should be about the general behavior,
written in a concise manner. The old one "Creates a circle at the
origin." is good enough for me, or maybe you can say "Creates a
regular polygon that approximates a circle at the origin".
But that .. is too cringe for me. Circle creates a circle .. not a
polygon .. it is drawn
as a polygon, but that is an approximation of its /shape/. The
polygon’s size
is determined by it being inscribed inside the circle of the given
diameter.
Second, the way you document the parameters is hard to understand.
i agree .. i have experimented with a few templates for param lists ..
no final
answer yet … but my current favorite is in the docs for linear_extrude()
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/3D_Modelling#linear_extrude()_Operator_Module
You only give examples at the end, not interleave examples like
the old circle module documentation.
actually i strongly prefer to interleave examples .. the places you
are seeing them
bunched up at the end of a section or page are mostly legacy pages
that are still
in progress in being reafactored
Third, we should have a more uniform way of writing
deprecated/experimental things.
to this end i have created templates specific to our needs .. see them on
my sandbox page
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox#templates
Fourth, we should have things like a grammar for expression and
statements,
well this would be a can of worms to open.
I prefer natural language expressions and descriptions over symbolic
syntax specs like BNF .. as long as the wording is clear and concise i
feel
words are better.
But i am willing to hear a discussion on this point .. it is an
important one.
I do make the warning that in my working career the ONLY time that a
team could agree on a standard for documentation was when i was in the
position of being able to unilaterally dictating it.
These are what I meant by "too verbose and not detailed enough". I
hope I made my points clear.
I would say so John, and thanks again for the feedback
Best, John
Guten Abend
Jeff
OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org
--
This email has been checked for viruses by AVG antivirus software.
www.avg.com
vulcan_ wrote:
But that .. is too cringe for me. Circle creates a circle .. not a polygon
.. it is drawn
as a polygon, but that is an approximation of its shape. The polygon’s size
is determined by it being inscribed inside the circle of the given diameter.
It NEVER makes a circle. It isn't just drawn as a polygon. It makes a
polygon with the number of sides determined by the value of special
variables such as $fn or $fs and $fa.
On Wed, Aug 6, 2025 at 4:37 PM vulcan_--- via Discuss <
discuss@lists.openscad.org> wrote:
pca006132 wrote:
yeah .. i am still a bit shaky on rendering .. but i am getting there
-
Your confusion over how render works is an example of what I meant by
overfit. The API contract is simple: The output of the render module is a
mesh. Trying to understand why the implementation chooses quad over
triangles in some cases but not the other is futile because it is complex
and can be changed later. Writing it down in documentation will confuse
people and make future updates harder (people will complain it is a
breaking change).
i get all that .. i have been using docs updates as a way to clarify my
thoughts on rendering ..
it is working slowly .. there will be a few more revisions to the
rendering page till it is right.
-
The starting point of the circle module is an important detail in the
API documentation, because it can be used to draw regular polygons.
. "Shape" and "geometry" doesn't pass this test for me, at least I have no
idea why you want to have them both.
this is my new glossary :
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox/SCADGlossary
For "drawn", do you want to use it only when describing GUI
I need a word for everywhere that a module .. does whatever it is that
results in a mesh being created.
Jordan tells me the word in use in the community is “render” .. that is a
problem for me as my
background in 3D modelling and animation that word was only used for when
images were being
generated from a mesh or model.
i settled on “draw” to mean “make or add to a mesh being created by an
scad script running”
I don't think you need this word. And I really want to get rid of the term
"action".
action is waaayy too general a word for “doing something”
Why is "statement" not good enough?
“statement“ has a particular lexical and syntactic meaning in the context
of coding
in a programming language .. i use in the way i describe in
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Program_Structure
First, people reading the reference manual probably don't care much about
the default behavior, so things like "By default this module draws a unit
circle centered on the origin [0,0]
i have to disagree with you on this. IMHO a clear, complete, and accurate
statement
of a built-in function or modules default behavior is a good starting
point for a
newcomer to OpenSCAD.
I start with that because when i was just starting out with it was a nice
confirmation
of what i was seeing in my experiments .. and from there i write about the
parameters
in a concise list .. and then for the params that have some complexity i
add additional
sections to cover that .. complexity
The first description should be about the general behavior, written in a
concise manner. The old one "Creates a circle at the origin." is good
enough for me, or maybe you can say "Creates a regular polygon that
approximates a circle at the origin".
But that .. is too cringe for me. Circle creates a circle .. not a polygon
.. it is drawn
as a polygon, but that is an approximation of its shape. The polygon’s
size
is determined by it being inscribed inside the circle of the given
diameter.
Second, the way you document the parameters is hard to understand.
i agree .. i have experimented with a few templates for param lists .. no
final
answer yet … but my current favorite is in the docs for linear_extrude()
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/3D_Modelling#linear_extrude()_Operator_Module
You only give examples at the end, not interleave examples like the old
circle module documentation.
actually i strongly prefer to interleave examples .. the places you are
seeing them
bunched up at the end of a section or page are mostly legacy pages that
are still
in progress in being reafactored
Third, we should have a more uniform way of writing
deprecated/experimental things.
to this end i have created templates specific to our needs .. see them on
my sandbox page
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox#templates
Fourth, we should have things like a grammar for expression and
statements,
well this would be a can of worms to open.
I prefer natural language expressions and descriptions over symbolic
syntax specs like BNF .. as long as the wording is clear and concise i feel
words are better.
But i am willing to hear a discussion on this point .. it is an important
one.
I do make the warning that in my working career the ONLY time that a
team could agree on a standard for documentation was when i was in the
position of being able to unilaterally dictating it.
These are what I meant by "too verbose and not detailed enough". I hope I
made my points clear.
I would say so John, and thanks again for the feedback
Best, John
Guten Abend
Jeff
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
. "Shape" and "geometry" doesn't pass this test for me, at least I have no
idea why you want to have them both.
this is my new glossary :
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox/SCADGlossary
This doesn't answer my question. When would you mention the word "geometry"?
For "drawn", do you want to use it only when describing GUI
I need a word for everywhere that a module .. does whatever it is that
results in a mesh being created.
circle() "creates" a regular polygon that approximates a circle. Isn't that
good enough? Why draw? Btw, we never add to a mesh, we only create new
meshes.
Why is "statement" not good enough?
“statement“ has a particular lexical and syntactic meaning in the context
of coding
in a programming language .. i use in the way i describe in
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Program_Structure
Well, I still don't get why "statement" is not good enough and the word
"action" is needed. I know programming languages well, I am doing a PhD
with it, and we define what a statement means all the time.
The first code block about <perform named action>; just confuse me further.
This is not a grammar description, nor an example, and it doesn't tell me
anything. The description about "invokes one or more modules to instantiate
a shape
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Constructing_Solid_Geometry
that appears in the preview panel
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_User_Interface"
is just wrong because we can do it without the GUI. Similarly in the second
code block <Named variable> = <expression>; is also confusing: does that
imply we have unnamed variables?
The sentence "Normally a statement does just one thing, and for assignment
expressions that is enough." is also confusing: Statements doing only one
thing is just a special form, why bother? And assignment is a statement,
not an expression.
I don't want to continue on-and-on, there are too many issues that I can
point out here. I would suggest you to just start with a BNF form and
describe each construct, don't interleave them and try to write a good
prose.
First, people reading the reference manual probably don't care much about
the default behavior, so things like "By default this module draws a unit
circle centered on the origin [0,0]
i have to disagree with you on this. IMHO a clear, complete, and accurate
statement
of a built-in function or modules default behavior is a good starting
point for a
newcomer to OpenSCAD.
I start with that because when i was just starting out with it was a nice
confirmation
of what i was seeing in my experiments .. and from there i write about the
parameters
in a concise list .. and then for the params that have some complexity i
add additional
sections to cover that .. complexity
https://diataxis.fr/reference/ is, in my opinion, a good resource about
writing technical documentation. It says that "Reference material describes
the machinery. It should be austere. One hardly reads reference
material; one consults it.".
And I should probably clarify, by experts I mean people mature enough to
read the docs. A reference manual written in a clear, concise, predictable
style can only help people understand things.
The first description should be about the general behavior, written in a
concise manner. The old one "Creates a circle at the origin." is good
enough for me, or maybe you can say "Creates a regular polygon that
approximates a circle at the origin".
But that .. is too cringe for me. Circle creates a circle .. not a polygon
.. it is drawn
as a polygon, but that is an approximation of its shape. The polygon’s
size
is determined by it being inscribed inside the circle of the given
diameter.
Who cares if the reference manual is stating the obvious? You come to the
reference manual to find things you care, not to read a prose, and
typically not to look for the simple default behaviour. Also, the
description being obvious is a good thing, not a bad thing.
You only give examples at the end, not interleave examples like the old
circle module documentation.
actually i strongly prefer to interleave examples .. the places you are
seeing them
bunched up at the end of a section or page are mostly legacy pages that
are still
in progress in being reafactored
As mentioned above, one consults the reference manual. If you interleave
examples with description of the API, it makes it long, and people will
need more time to find the thing they need.
Third, we should have a more uniform way of writing
deprecated/experimental things.
to this end i have created templates specific to our needs .. see them on
my sandbox page
https://en.wikibooks.org/wiki/User:VulcanWikiEdit/sandbox#templates
Fourth, we should have things like a grammar for expression and
statements,
well this would be a can of worms to open.
I prefer natural language expressions and descriptions over symbolic
syntax specs like BNF .. as long as the wording is clear and concise i feel
words are better.
But i am willing to hear a discussion on this point .. it is an important
one.
I do make the warning that in my working career the ONLY time that a
team could agree on a standard for documentation was when i was in the
position of being able to unilaterally dictating it.
I am not saying we should not have natural language description. Again, it
is from a point of a quick search over the reference manual. If you have
the expertise in reading things like BNF, BNF is just the most concise way
to convey the information. You can supplement it with natural language
later. And as mentioned above, the current way is absolutely not clear, at
least to me.
Best,
John
I can't speak for anyone else, but I thought that the documentation was OK
(not great, but OK), and now we're entertaining changing how everything is
described, because we are not using the same terms as elsewhere on the
web. This is not a compelling argument, at least to me.
Well I don't think that is the motivation for the overall change. That is
only my complaint about the new documentation.
I wish that, before ANY of these changes were even CONTEMPLATED, the
community (not someone inexperienced with OpenSCAD) came up with a list of
issues that needed to be resolved, and then we resolved them. l
I, for one, find what Vulcan is doing to be terrifying. And I do not have
the patience to read all of the new documentation, over and over again,
trying to figure out what else has been broken.
Same here. I don't know what problem the current rewrite it solving. And I
think it makes something worse, i.e., searching for information in the
reference.
Best,
John
On 8/6/2025 4:20 PM, Todd Allen via Discuss wrote:
[ circle() ] NEVER makes a circle. It isn't just drawn as a polygon.
It makes a polygon with the number of sides determined by the value of
special variables such as $fn or $fs and $fa.
That's absolutely true, but we have an active discussion going on about
whether we should ensure that the documentation allows for a future
where (absent $fn) it does create a true circle.
[ Sigh yet again... what e-mail address I use is largely automatically
handled on my desktop, but not on this laptop. ]
Mattheiu is right; this needs a subject change.
On 8/6/2025 12:51 AM, vulcan_--- via Discuss wrote:
1.
One should not attempt to infer the API contract from the
behavior.
umm .. then how better to understand it?
Reread the documentation. Repeat repeat repeat. Then ask and/or
inspect the sources. Behavior is good only for very targeted questions,
and even then should be tempered by the others.
i am not at all clear what overfit means ..
He means that you may assume that the behavior that you see is the only
possible behavior. (For instance, you may assume that some particular
triangularization (or lack thereof) is the promised answer, and it isn't.)
Yeah, specials are still a fuzzy thing for me .. even after Jordan’s
diligent attempt to educate me on how they work i am not at all clear
on what they bring to the language that is any different from having
global variables at the top level of scope
They aren't necessarily at the top level. Try running this...
normal = "top";
$special = "top";
module foo() {
echo(normal=normal, $special=$special);
}
module bar() {
$special = "bar";
normal = "bar";
foo();
}
foo();
foo($special="top per-call");
bar();
echo(normal=normal, $special=$special);
Normal variables are scoped based on the program's static structure, so
"normal" is visible from the top, from foo(), and from bar() until the
definition of the new "normal" on bar's second line. Nothing ever
refers to bar's definition of "normal".
Special variables ($ variables) are scoped based on the call stack. The
initial setting ("top") is visible at the top level, and in anything
called from the top level - which means the top-level call to foo(), and
the call to bar() until bar's first line, where a new $special is
created ("bar"). That new $special is visible to bar() and to
everything that bar() calls - notably, its call to foo(). The second
call to foo() sets $special in the call, and that setting applies to
that call to foo() and everything that it calls (which is nothing).
Another way to put it:
For a normal variable, to find the assignment, start at the reference to
the variable, then look at that scope. If it's defined there, you're
done. Move to the next higher scope (one fewer level of braces). If
it's defined there, you're done. Continue until you reach the top scope.
For a special variable, look at the current scope. If it's defined
there, you're done. Move to the place that called this module; look at
the scope there. If it's defined there, you're done. Continue until
you reach the top scope.
It's actually a little more complicated than that, in in both cases that
a reference in an assignment needs to look at earlier assignments in
that scope.
[ Sigh ]
Prose descriptions are good for when you're trying to understand the
overall structure of the thing, and when you're trying to write and
understand simple cases.
BNF or something similarly concise and specific is essential when you're
trying to actually understand the syntax and how the pieces fit
together. There is no way, for instance, that a prose description is
going to reasonably explain the parsing surrounding the let(), echo(),
and assert() operators.
[ I'll spend the effort to fix up this laptop configuration, again,
sorry for the duplicates. ]
Two Dimensional Modelling
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stages2D Primitives
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives
All 2D primitives can be transformed with 3D transformations.
Really bad place to start. Yes, you can transform them with 3D
transforms, but if you do then the results can be weird. It should be
discouraged; you should almost always work with 2D transforms when
working with a 2D subassembly.
Also, maybe we should talk about the primitives before we talk about
what you can do with them.
They are usually used as part of a 3D extrusion.
Yeah, eventually. But again this doesn't seem appropriate for a "2D
primitives" section. Maybe for an overview section above that.
Although they are infinitely thin, they are rendered with a 1-unit
thickness.
Again, maybe in an overview section.
Note: Trying to subtract with|difference()|from 3D object will lead
to unexpected results in final rendering.
The real rule is "don't mix 2D objects with 3D objects and 3D
operations". It isn't necessary or appropriate to say very much about
what will happen if you do. Some cases will yield errors, while others
will do something weird. We don't want the documentation to nail down
any particular behavior, because there are reasons that we might want to
change the behavior in these cases.
Ref, e.g., OEP 7 "Mixed Dimension Geometry Support"
https://github.com/openscad/openscad/wiki/OEP7%3A-Mixed-Dimension-Geometry-Support.
Square Object Module
By default this module draws a unit square in the first quadrant,
(+X,+Y), starting at the origin [0,0]. Its four lines have no
thickness but the shape is drawn as a 1 unit high, filled plane.
The second sentence should probably just go away:
The module's arguments may be written in the order|<size>,
center=<bool>|without being named, but the names may be used as shown
in the examples:
There needs to be (but probably isn't) enough documentation convention
that this need not be said.
Parameters
size
has two forms:/single value/or/vector/
single - non-negative float, length of all four sides
Should use the word "number" rather than the word "float". OpenSCAD does
not have distinct floating point and integer types; it has only numbers.
center
boolean, default false, to set the shape's position in the X-Y plane
CenterWhen|false|, as it is by default, the shape will be drawn from
its first point at (0,0) in the First Quadrant, (+X,+Y). With center
set to|true|the shape is drawn centered on the origin.
These two paragraphs should be merged.
Circle Object Module
By default this module draws a unit circle centered on the origin
[0,0] as a pentagon with its starting point on the X-axis at X=1. Its
lines have no thickness but the shape is drawn as a 1 unit high,
filled plane.
The part of the first sentence starting "as a pentagon ..." should go
away. It's true, but it really belongs as part of the description of
$fa/$fs.
Again, the second sentence should just go away.
Somewhere it should say "Circles are approximated as regular polygons;
see <reference to $fa/$fs/$fn> for the details of the polygons generated."
The argument|radius|may be given without being named, but
the|r|and|d|arguments must be named.
There is no "radius" argument. There are r and d.
Again, we should have a documentation convention so that we don't have
to repeat positional/named rules, but the behavior here is that r can be
supplied as the first argument, but d must be named.
(Technically, if you say "circle(undef, 10)" the 10 is the second it
creates a 10-unit-diameter circle. I would say that the fact that this
works is a minor bug.)
$fa
Special Variable
$fs
Special Variable
$fn
Special Variable
Theses should be described only to the extent of pointing at the general
description of $fa/$fs/$fn.
The default circle displays as a pentagram as that is the minimum
number of fragments used to approximate a curved shape calculated from
the default values for $fs and $fa. To have it draw as a smooth shape
increase the $fn value, the minimum number of fragments to draw, to 20
or more (best $fn < 128).
This is just bad. First, everything here should be covered in the
description of $fa/$fs/$fn. Second, using $fn to control the resolution
of a circle is generally the wrong answer; you are better off setting
$fa and $fs. Finally, specific advice on $fn values is a bad idea,
because the "looks smooth" value varies dramatically with size. A
20-gon is okay for a medium-small circle; a 72-gon is not good enough
for a 100-unit circle.
An alternative method to draw a very smooth circle scale is to scale
down a very large circle.
scale( 0.001 ) circle(200);
This should just go away; it confuses the issue.
Another way to solve the lack of a built-in module for regular
polygons is to write a custom one:module regular_polygon()
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/example_module_regular_polygon()
I wouldn't include this. Using polygon() is harder than using circle(),
and anybody who's capable of using it should have little trouble
simulating circle().
convexity
Integer, default=1 - complex edge geometry may require a higher
value value to preview correctly.
Should include a link to a general discussion of convexity. Probably
should not even mention the default; that should be covered in the
general discussion.
Points ParameterA list of X-Y coordinates in this form:
[[1, 1], [1, 4], [3, 4], [3, 1], [1, 1]]
which defines four points and makes it explicit that the last one is
the same as the first.
Including the first point twice is not strictly necessary as this:
[[1, 1], [1, 4], [3, 4], [3, 1]]
gives the same result.
This seems like it should be simplified. In the absence of a paths
parameter, the last point always connects to the first, because polygons
are always closed.
Paths Parameter
This optional parameter is a nested vector of paths.
A "path" is a list of index values that reference points in
the|points|vector. It can explicitly describe a closed loop by its
last index being the same as its first, as in:
[1, 2, 3, 4, 1]
but this is equivalent to:
[1, 2, 3, 4]
Again, this seems like unnecessary complexity; the last point always
connects to the first.
Notice that the points vector is simple list,
No, it's a list of lists.
while each path is a separate vector.
Yes... points and paths are the same order. They are both lists of lists.
This means that paths, that are lists of references to points, have
to "know" which points it needs to include.
While it's true that paths need to "know" the indexes they connect, I
don't see how that follows from the previous sentences.
This can be an issue if the polygon is assembled from a number of
shapes at run time as the order of adding shapes affects their point's
index values.
It's true that this is something that you must handle, but I don't think
that a reference manual needs to discuss it.
.Convexity
Formatting error: this title is merged with the previous paragraph.
(But should be deleted, see below.)
Shapes with a lot of detail in their edges may need the convexity
parameter increased to preview correctly. See Convexity
Already discussed, should be deleted.
Example With Multiple Holes
[Note:Requires version2015.03] (for use of|concat()|)
https://en.wikibooks.org/wiki/File:OpenSCAD_romboid_with_holes.jpg
We are using "a" for the point lists and "b" for their paths:
a0 = [[0,0],[100,0],[130,50],[30,50]]; // outer boundary
b0 = [1,0,3,2];
a1 = [[20,20],[40,20],[30,30]]; // hole 1
b1 = [4,5,6];
a2 = [[50,20],[60,20],[40,30]]; // hole 2
b2 = [7,8,9];
a3 = [[65,10],[80,10],[80,40],[65,40]]; // hole 3
b3 = [10,11,12,13];
a4 = [[98,10],[115,40],[85,40],[85,10]]; // hole 4
b4 = [14,15,16,17];
a = concat( a0,a1,a2,a3,a4 ); // merge all points into "a"
b = [b0,b1,b2,b3,b4]; // place all paths into a vector
polygon(a,b);
//alternate
polygon(a,[b0,b1,b2,b3,b4]);
The "alternate" at the end of the example seems unnecessary - of course
you can use either a particular expression or a variable that has been
set to that expression.
2D to 3D by Extrusion
A polygon may be the basis for an extrusion, just as any of the 2D
primitives can. Thisexample script
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/example_module_2D_to_3D_extrusionmay
be used to draw the shape in this image:
Yes, a polygon can be used as the basis for extrusion, just as any of
the 2D primitives can. That means that you do not need a specific
example of that case.
Import a 2D Shape From a DXF
[Deprecated:import_dxf() will be removed in a future release. Use
Useimport() Object Module
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language#importinstead.
instead*]*
As a deprecated feature, this should be pushed to the bottom.
Read a DXF file and create a 2D shape.
Example
linear_extrude(height = 5, center = true)
import_dxf(file = "example009.dxf", layer = "plate");
Example with Import()
linear_extrude(height = 5, center = true)
import(file = "example009.dxf", layer = "plate");
The second should perhaps be titled "Replacement example with
import()". Note also that since OpenSCAD is case sensitive the word
"import" should not be capitalized.
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stagesText
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text
Text is a big enough topic that it should probably have its own page,
with just a brief mention and cross-reference here.
I see that it has its own page and is transcluded here. It should not
be transcluded, because that makes it harder to just read everything.
Text in OpenSCAD
Being able to use text objects as a part of a model is valuable in a
lot of design solutions.
Delete this sentence. This is reference material, not sales material.
The user already knows whether or not it's valuable to them.
The fontsavailable to use in a script
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Fonts_in_OpenSCADare
from the system that OpenSCAD is running in with the addition of those
explicitly added by the script itself.
And OpenSCAD includes several. (And this duplicates a more extensive
discussion below.)
text() Object Module
The|text()|object module draws a single string of text as a 2D
geometric object, using fonts installed on the local system or
provided as separate font file.
provided as +a+ separate font file
The shape starts at the origin and is drawn along the positive X axis.
By default, ...
(because halign and valign change things)
text
String. A single line ofany character allowed
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Characters_Strings#Characters.*Limitation:*non-printable
ASCII characters like newline and tab rendered as placeholders
Delete the second sentence. If it's a string, it's allowed. As for
being a single line and treatment of non-printable characters, need to
phrase that as a current restriction, not as a permanent behavior - it
would be good if we could eventually provide more support there, and we
wouldn't want to be prevented from adding that support by compatibility
concerns. Ref https://github.com/openscad/openscad/issues/5018 for the
desire for multi-line text.
font
aformatted string
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Parameterswith
default font of "Liberation Sans:style=Regular"
"formatted string" is a poor phrase there. Better would be something
like "String. A font specification with ...".
Also I see that this is a link over to a separate Text page. A separate
Text page is good, as discussed above, but it shouldn't be duplicated here.
size
non-negative decimal, default=10. The generated text has a height
above the baseline of approximately this value, varying for
different fonts but typically being slightly smaller.
The "decimal" part should be "number". (It isn't even sensible to talk
about a base.)
I don't feel the need for the "non-negative" part. (It should probably
also be non-zero.) Unless we have a special meaning for a negative
size, we should be able to let people figure out for themselves that if
they make a silly request they will get a silly answer.
Current behavior is ... interesting... though when you think about it
unsurprising: the text is mirrored in X and Y, leading to it being
effectively rotated 180 degrees. Unless we really want to keep that
behavior, we should probably make it be an error instead. Until and
unless we decide that we want to keep that behavior, we should not
document it.
There needs to be a footnote about size. Because of an arithmetic error
in the implementation (issue #4304
https://github.com/openscad/openscad/issues/4304), the "size"
parameter does not correspond to a typical font size specification. It
is a coincidence that the arithmetic error approximately cancels out the
usual ratio between the specified font size and the size of a capital
letter, making "size" approximately specify the size of a capital letter
in a typical Western font. However, since the result is useful, and
the error has been in place since the beginning, we really can't fix
it. Maybe at some point we can introduce an alternative parameter that
specifies a more conventional font size, eg PR#4306
https://github.com/openscad/openscad/pull/4306.
spacing
float, default=1. Multiplicative factor that increases or
decreases spacing between characters.
"float" should be "number".
language
String. The language of the text (e.g., "en", "ar", "ch"). Default
is "en".
script
String, default="latin". The script of the text (e.g. "latin",
"arabic", "hani").
Somebody needs to figure out what these actually do.
$fn
higher values generate smoother curves (refer toSpecial Variables
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#special_variables)
This should refer to $fa, $fs, and $fn... and really you shouldn't be
using $fn here.
Font & Style Parameter
The "name" of a font is a string starting with its|logical font
name|and|variation|,
I don't see variation as a separate part of the specification.
Also, use of the "typewriter" font here is inappropriate; neither of
these is a language keyword or language component. Either use plain
text or perhaps italics.
optionally followed by a colon (":") separated list of font
specifications like a|style|selection, and a set of zero or
more|features|.
We should include a list of the name=value specifications supported, or
refer to external (fontconfig?) documentation.
Again, "features" is not a keyword and should not be in typewriter font.
The common variations in a font family are|sans|and|serif|though many
others will be seen in the list of fonts available. Each font
variation can be drawn with a/style/to support textual emphasis.
I think those are part of the font name, and that there they are usually
capitalized. I'm a bit torn on whether they should be in typewriter font.
The default, upright appearance is usually called "Regular" with
"Bold", "Italic", and "Bold Italic" being the other three styles
commonly included in a font. In general the styles offered by a font
may only be known by using the platform's font configuration tools or
theOpenSCAD font list dialog
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Fonts_in_Openscad.
This should explicitly tie to the "style=" parameter.
The fontfeatures property is appended to the|font name|after the
"fontfeatures" should be in typewriter font because it is a keyword.
"font name" should not be in typewriter font because it is not a keyword.
optional style parameter. Its value is a semi-colon separated list of
feature codes, each prefixed by a plus, "+", to indicate that it is
being added,
Should end with a colon, not a comma.
font = "Linux Libertine G:style=Regular:fontfeatures=+smcp;+onum");
Size Parameter
Text size is normally given in points, and a point is 1/72 of an inch
high. The formula to convert thesizevalue to "points" is|pt =
size/3.937|, so asizeargument of 3.05 is about 12 points.
This is incorrect, because OpenSCAD is unitless. "size" specifies some
dimension of the font, in OpenSCAD units. See the discussion above
about exactly what dimension it measures. (OpenSCAD units are
typically interpreted as millimeters, but that's up to the user and
the consuming program; it is not part of OpenSCAD's definitions.)
There should be no reference to "points" except perhaps to disclaim
that anything is measured in points.
Note: Character size the distance from ascent to descent, not from
ascent to baseline.
Ref the arithmetic error mentioned above and the long discussion in
issue #4304, this is incorrect. "size" should have measured
approximately the font ascent plus descent, but instead measures (even
more approximately) the font ascent.
One of these four names must be given as a string to the|valign|parameter.
Since the valign parameter itself is optional, the word "must" seems
inappropriate. Perhaps "The valign parameter may be set to one of these
four words".
top
The text is aligned so the top of the tallest character in your
text is at the given Y coordinate.
There is no "given Y coordinate". The top of the tallest character in
your text is at the X axis, Y=0.
center
The text is aligned with the center of the bounding box at the
given Y coordinate.
Again, at Y=0.
baseline
The text is aligned with the font baseline at the given Y coordinate.
Again, at Y=0.
bottom
The text is aligned so the bottom of the lowest-reaching character
in your text is at the given Y coordinate.
Again, at Y=0.
Note: only the "baseline" vertical alignment option will ensure
correct alignment of texts that use mix of fonts and sizes.
This overlaps a lot with the last sentence of the definition of
"baseline" and should probably be merged with it.
One of these three names must be given as a string to
the|halign|parameter.
Again, the word "must" seems inappropriate.
left
The text is aligned with the left side of the bounding box at the
given X coordinate.
center
The text is aligned with the center of the bounding box at the
given X coordinate.
right
The text is aligned with the right of the bounding box at the
given X coordinate.
None of these are correct. The alignment is based on spacing, not on
the bounding box. For most letters, "left" will position the ink
slightly to the right of X=0. (For a size=10 M in Liberation Sans, it's
about 1.1 units right of X=0.) I'd need to do more research to figure
out the exactly correct wording.
And for all of them, there is no "given [XY] coordinate". Positioning is
relative to the origin.
Spacing Parameter
Characters in a text element have the size dictated by their glyph in
the font being used. As such their size in X and Y is fixed. Each
glyph also has fixed|advance|values (it is a vector [a,b],
seetextmetrics
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#textmetrics)
for the offset to the origin of the next character. The position of
each following character is the|advance.x|value multiplied by
the|space|value. Obviously letters in the string can be stretched out
when the factor is greater than 1, and can be made to overlap
when|space|is a fraction closer to zero, but interestingly, using a
negative value spaces each letter in the opposite of
the|direction|parameter.
This is more or less correct, but what it doesn't say is that "spacing"
is almost completely useless for a proportionally spaced font, for two
reasons. Ref https://github.com/openscad/openscad/issues/3859 .
The "spacing" parameter should probably be downplayed, and should
probably be deprecated.
Text Examples
Simulating Formatted Text
Needs to define what it means by "formatted".
When text needs to be drawn as if it was formatted it is possible to
use translate() to space lines of text vertically. Fonts that descend
below the baseline need to be spaced apart vertically by
about|1.4size|to not overlap. Some word processing programs use a
more generous spacing of|1.6size|for "single spacing" and double
spacing can use|3.2*size|.
fontmetrics() can supply more correct values for the particular font.
But really this is advice, not reference material.
Fonts in OpenSCAD
The fonts available for use in a script are thosed:
A call to fontmetrics() using only default settings shows the
installation's standard font and settings:
Any reference to fontmetrics() needs a "requires release XXX" note,
which at the moment is still "requires development snapshot". But
really this should be at most a reference to the fontmetrics() section.
{
nominal = {
ascent = 12.5733;
descent = -2.9433;
};
max = {
ascent = 13.6109; descent = -4.2114;
};
interline = 15.9709;
font = {
family = "Liberation Sans";
style = "Regular";
};
}
Wherever this ends up, the indentation needs work. It should match the
indentation style used in the examples.
None of the platforms OpenSCAD is available on include the Liberation
font family so having it as part of the app's installation, and making
it the default font, avoids problems of font availability.
"None" is an awfully broad statement about a moving target. It would be
better to say "To avoid problems of font availability, OpenSCAD includes
the Liberation font family as part of its installation, and has
Liberation Sans as the default font.".
Note: It was previously noted in the docs that fonts may be added to
the installation by drag-and-drop of a font file into the editor
window, but as of version 2025 Snapshot this isnotthe case
That isn't what it said. It said:
You can drag a font in the font list, into the editor window to use
in the text() statement.
I can't readily check a 2025 build at the moment, but as of Oct 2024 the
it does exactly as described: dragging a font from the OpenSCAD font
list into the editor window drops its name in the editor window. If
that is no longer the case, it's a bug.
In general, don't say things like this. If the documentation said X,
and you find that X is not true, then one of the following is true:
Regardless, the right answer is to file an issue to get the actual answer.
In the following sample code a True Type Font, Andika, has been added
to the system fonts using its Font Management service.
We shouldn't talk about adding fonts to the system. That's not our problem.
But also, that's not what the sample does. It adds a font to
OpenSCAD, and has nothing to do with the platform font mechanisms.
Supported font file formats areTrueType
https://en.wikipedia.org/wiki/TrueTypefonts (.ttf) andOpenType
https://en.wikipedia.org/wiki/OpenTypefonts (.otf). Once a file is
registered to the project the details of the fonts in it may be seen
in the font list dialog (see image) so that the logical font names,
variations, and their available styles are available for use in the
project.
This says "see image", but doesn't indicate which image.
And: OpenSCAD doesn't have the notion of "projects" or "registered to
the project".
3D Text by Extrusion
This is true of all 2D objects and so does not need to be mentioned.
Delete.
position
a vector [X,Y], the origin of the first glyph, thus the lower-left
corner of the drawn text.
No, it's not the origin of the first glyph, or at least that's a
confusing phrase to use. A glyph is usually positioned slightly to the
right of the origin, and if it's a descender then it's below the origin,
and some characters (e.g. quotes) are well above the origin. A more
correct statement would be that it's the lower left corner of the
bounding box of the text.
If one is going to talk about the origin of a glyph, it should be the
point on the baseline to at the left edge of the advance... which this
isn't.
size
a vector [a,b], the size of the generated text.
Should be [x,y]. [a,b] doesn't tell you what "a" and "b" mean.
ascent
positive float, the amount that the text extends above the baseline.
Use the word "number" rather than "float".
It's not always positive; for a glyph entirely below the baseline (like
underscore in Liberation Sans) it's negative. (I'm not sure that's
truly the right definition, but it's the current behavior.)
descent
negative float, the amount that the text extends below the baseline.
Not always negative; for a glyph that is entirely above the baseline
(like apostrophe in Liberation Sans) it's positive. Again, I'm not sure
that's the right definition, but it's the current behavior.
offset
a vector default [0, 0], the lower-left corner of the box
containing the text, including inter-glyph spacing before the
first glyph.
There is no default; this is a value that's returned to you.
This is not the correct definition (and it wasn't correct in the
original that I wrote). It's the position of the origin of the text,
after adjusting for halign and valign. For normal LTR text, the X
coordinate is the X coordinate at the left edge of the first glyph's
advance, and the Y component is the Y coordinate of the baseline.
advance
a vector default [153.09, 0], amount of space to leave to any
following text.
There is no default (and certainly not that one!).
The original definition ("the "other end" of the text, the point at
which additional text should be positioned.") wasn't great, but was more
correct. I would say "The point at which additional text should be
positioned, relative to the text's origin as reported by 'offset'.".
This example displays the text metrics for the default font used by
OpenSCAD:
"text metrics for ... font" is a non sequitur. Text metrics measure a
particular string. (And "used by OpenSCAD" is unnecessary; the entire
document is in that context.)
And it's incorrect; the default font is Liberation Sans and this example
uses Liberation Serif.
Better would be:
This example displays the text metrics for "Hello, World!" for
Liberation Serif with size=20:
https://en.wikibooks.org/wiki/File:OpenSCAD_textmetrics.pngUsing
textmetrics() to draw a box around text
s="Hello, World!";
size=20;
font="Liberation Serif";
translate([0,0,1])
text("Hello, World!",size=size,font=font);
Should use "s" instead of repeating the string. (And this is in my
original, sigh.)
displays (formatted for readability):
The original "yields" is better, because it might or might not be displayed.
ECHO:{
position=[0.7936,-4.2752];
size=[149.306,23.552];
ascent=19.2768;
descent=-4.2752;
offset=[0,0];
advance=[153.09,0];
}
The indentation should match the examples, with the close brace at the
left margin.
fontmetrics()
size
Decimal, optional. The size of the font, as described above
for|text()|.
Replace "decimal" with "number".
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stages3D to 2D
Projection
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/3D_to_2D_Projection
Using the|projection()|function, you can create 2d drawings from 3d
models,
So far so good.
and export them to the dxf format.
This part should be deleted. There are any number of things you might
do with a 2D projection of a 3D object. Exporting to DXF is only one.
It works by projecting a 3D model to the (x,y) plane, with z at 0.
If|cut=true|, only points with z=0 are considered (effectively cutting
the object), with|cut=false|(/the default/), points above and below
the plane are considered as well (creating a proper projection).
Example: Consider example002.scad, that comes with OpenSCAD.
https://en.wikibooks.org/wiki/File:Openscad_projection_example_2x.png
Then you can do a 'cut' projection, which gives you the 'slice' of the
x-y plane with z=0.
Doing the non-default case as the first example seems wrong; I would
swap the two examples.
Another Example
You can also use projection to get a 'side view' of an object.
This example seems unnecessary for a reference manual. It's a
straightforward combination of the features described.
Links:
Seems inappropriate for a reference manual. Also, doesn't seem more
complicated at all.
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stages2D to 3D
Extrusion
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_to_3D_Extrusion
Extrusion https://en.wikipedia.org/wiki/Extrusionis the process of
creating an object with a fixed cross-sectional profile. OpenSCAD
provides two commands
"Commands" isn't the right word. "Modules" is more correct, but
"operations" is probably best.
Both extrusion methods work on a (possibly disjointed) 2D shape
normally drawn in the relevant plane (see below).
The old description of the behavior of extrusion for 2D objects that
have been moved off the Z=0 plane is an example of something that should
never have been documented. It's not a particularly useful behavior,
and we might eventually want a different behavior. At most, it should
have said "don't do that".
It should probably say "drawn on the Z=0 plane".
This child object is first projected onto the X-Y plane along the Z
axis to create the starting face of the extrusion.
Delete. We shouldn't document that behavior.
The start face is duplicated at the Z position given by the height
parameter to create the extrusion's end face. The extrusion is then
formed by creating a surface that joins each point along the edges of
the two faces.
That's a seriously incomplete description, because it's only true with
all of the parameters at their defaults.
The 2D shape may be any2D primitive shape
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives,
a2d polygon
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives#Polygon,
animported 2D drawing
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives#Importing_a_2D_Drawing,
or aboolean combination
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/CSG_Modellingof them.
Or, in other words, the 2D shape may be ... a 2D shape.
Delete the whole sentence.
The 2D shape may have a Z value that moves it out of the X-Y plane,
and it may even be rotated out of parallel with it. As stated above,
the extrusion's starting face is the projection of the 2D shape onto
the X-Y plane, which, if it is rotated, will have the effect of
fore-shortening it normal to the axis of the rotation.
Delete.
Using a 3D object as the extrusion's child will cause a compile time
error.
Factually incorrect. It's not a compile-time error; it's a run-time error.
Also, we just said that the child must be a 2D shape. Exact behavior
when that requirement is violated need not be (and probably should not
be) specified.
Delete.
Including a 3D object in a composition of 2D objects (formed using
boolean combinations on them) will be detected, the 3D object(s) will
be deleted from it and the remaining 2D objects will be the basis for
projecting their shape onto the X-Y plane.
We need not (and generally should not) specify the behavior in error
conditions. Delete.
Parameters For Linear Extrusion
There are no required parameters. The default operation is to extrude
the child by 100 units vertically from the X-Y Plane, centered on the
[0,0] origin.
"centered" is at best meaningless (because it's extruded wherever the
child is, without respect to the origin) and at worst incorrect (because
the default is to extrude into +Z, not to center in Z). Delete that last
phrase.
Doesn't have to be an integer.
I don't know how strong a pattern we have for specifying parameters, but
they shouldn't be numbered. (Except maybe if they are usable as
positional parameters - which don't match these numbers.)
I can't say that I truly understand eigenvectors, but I don't think this
is one. The "signed" part is unnecessary, because all numbers are
signed, and the "decimal" part is meaningless because abstract numbers
have no base.
"v" is a vector of three numbers that controls the vector along which
the extrusion is done.
It has an interesting interaction with "height". If both are specified,
height is used as the length of the extrusion, along the direction that
v points, and v's magnitude is ignored. If only v is specified, it is
used to control both the direction and length of the extrusion.
Saying that it's the axis of rotation for twist is sort of right, but
maybe needs more explanation. Normally when you think of an axis of
rotation, you're rotating along the plane perpendicular to that axis.
Here, though, it is perhaps more correct to say that it controls the
origin of the rotation. At each slice, the 2D shape is rotated around
Z, with the origin being the XY position of the extrusion vector.
"at the Z=0 plane" would be a bit more obvious.
Should include a link... which should not be pointing at this page, no
matter which page we're talking about.
a number
180 degrees is a half twist, 360 is all the way around, and so on.
Unnecessary, delete.
a non-negative number
minimum 0.0,
Implied by "non-negative", delete.
that specifies the factor by which the end face should be scaled
up, or down, in size from that of the start face.
All scaling is either up or down. Just "should be scaled".
or : an [x,y] vector that scales the extrusion in the X and Y
directions separately.
Delete the colon.
Needs help.
h
a named parameter, synonym to height
Just list it in the same block as height.
$fn $fs $fa
Special Parameters - given as named parameters.
They have standard special-variable semantics, which means they can be
specified in the context or in the call. They should be mentioned, but
perhaps not as parameters per se. I believe they only affect twisted
extrusions, so maybe they should be mentioned there.
Center
This parameter affects only affects the vertical position or the
extrusion. Its X-Y position is always that of the projection that sets
its starting face.
"or" should be "of".
This is a nice comment, but doesn't say what the parameter does.
"When true, the extrusion is centered vertically around Z=0." seems
adequate to me, without any further comment, but a subsequent comment
about not affecting X and Y would be OK.
Scale
This is multiplicative factor that affects the size of extrusion's end
face. As such 1.0 means no change, a value greater than one expands
the end face, and a value between 0.001 and less than 1 shrinks it.
"As such" is unnecessary.
I don't know where 0.001 came from. I would say "a value less than 1
shrinks it".
A value of 0.0 causes the end face to degenerate to a point, turning
the extrusion into a pyramid, cone, or complex pointy shape according
to what the starting shape is.
I'd say this is unnecessary.
Using the vector form sets the scale factor in the X and Y directions
separately
Twist
Twist is applied, by default, as a rotation about the Z Axis.
As discussed above, twist is always around Z. What v controls is the
origin of that rotation.
When the start face is at the origin a twist creates a spiral out of
any corners in the child shape. If the start face is translated away
from the origin the twist creates a spring shape.
I don't know if it's truly useful to try to describe the various shapes
that can result from twisting.
One thing that might be worth explicitly mentioning is that you can't
practically use linear_extrude to generate threads. You can come
temptingly close, but they won't be shaped right. (It is actually
possible to get right, but requires an unobvious base shape.)
A positive twist rotates clockwise, negative twist the opposite.
Huh. I basically never use twist, so I never noticed that it's
backwards from rotate. That seems very wrong... and it's way too late
to fix it. It might be worth mentioning this difference.
Twist Axis Vector
The second parameter is an [x,y,z] eigen vector that specifies the
axis of rotation of the applied twist.
Suggest referring to it by name instead of by position.
The ratios of the three dimensional values to their respective
coordinate axes specify the tilt away from the default axis, [0,0,1],
the Z-Axis. For instance, v=[cos(45),0,1] tilts the extrusion at 45
degrees to the X axis.
It's actually a skew rather than a tilt.
The start and end faces are always normal to the Z-axis, even when the
twist axis is tilted. The extruded and twisted surfaces are thus
distorted from what might be expected in an extruded shape. The more
expected result may be achieved by applying a rotation to then twisted
extrusion on the Z Axis to tilt it into the desired position.
It's best not to make assumptions about what the user expects. Describe
the operation, and describe it carefully. Do not describe how to do
things that are straightforward combinations of operations.
Note that the documentation does not discuss which happens first: twist
or scale. I don't believe it matters when using the same scaling for X
and Y, but matters a great deal with using different scaling on the two
axes. It twists and then scales, which can mean (for instance) that a
shape that started out rectangular turns into a parallelogram. There's
an argument that this is not the useful behavior, and that
scale-and-then-twist is more useful since it retains the general shape
throughout the extrusion. I've started a discussion a few times about
maybe changing this, but I don't think it ever came to a conclusion. It
might be best not to document this without that conclusion.
$fn, $fa, $fs Special Parameters
The special variables must be given as named parameters and are
applied to the extrusion, overriding the global setting. When the same
special variables are set on the base shape its values override their
use as parameters on the extrusion.
None of this is really accurate.
The special variables have standard special-variable behavior, which
means that you can specify them in the context or in the particular
call, and they apply to that context (including a specific call) and
everything that that is called from that context. There is no "global
setting" that is special.
What matters for the linear_extrude (and in particular for twisted
extrusions) is the setting that it sees.
If the child 2D shape also uses these variables, what matters for it
is what it sees... which, absent an inner setting, will be the same as
what linear_extrude sees.
Thus, either:
linear_extrude(height=10, twist=20, $fn=100) circle(10);
or
$fn=100; linear_extrude(height=10, twist=20) circle(10);
will yield a high-resolution twist of a high-resolution circle.
On the other hand, either
linear_extrude(height=10, twist=20, $fn=100) circle(10, $fn=3);
or
$fn=100; linear_extrude(height=10, twist=20) circle(10, $fn=3);
will yield a high-resolution twist of a low-resolution circle - a triangle.
Extrusion From Imported DXF
Does not need to be discussed. You can linear_extrude any 2D shape, and
an import of a DXF yields a 2D shape.
A Unit Circle with No Twist
I don't think all of these examples are necessary.
Generate an extrusion from a circle 2 units along the X Axis from the
origin,
unit circle
centered vertically on the X-Y plane, with no twist. The extrusion
appears to have a pentagonal cross-section because the extrusion's
child is a 2D circle with the default value for $fn.
It doesn't appear to have a pentagonal cross-section. It does have
a pentagonal cross-section.
The same circle, but made into a spiral by 500 degrees of
counter-clockwise twist.
If you look carefully, this example demonstrates why you can't make
threads. As you twist it more, it becomes thinner and thinner in Z.
The problem is that the cross-section of a thread is a strange shape.
Mesh Refinement
The slices parameter defines the number of intermediate points along
the Z axis of the extrusion.
I am not sure of the exactly right way to describe this, because of
fence post errors.
"slices" controls the number of 3D "chunks" that make up the extrusion.
The total number of instances of the original 2D object is slices+1.
Its default increases with the value of twist.
It's some function of that and $fa/$fs/$fn. I don't know what the
function is or exactly how to describe it.
Additional the segments parameter
Addition -> Additionally
Segments need to be a multiple of the polygon's fragments to have an
effect (6 or 9.. for a circle($fn=3), 8,12.. for a square() ).
I don't know what the actual behavior is, but that's not it. For more
complex shapes (I experimented with a right triangle) intermediate
values do have an effect.
Thespecial variables
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features$fn,
$fs and $fa can also be used to improve the output. If slices is not
defined, its value is taken from the defined $fn value.
Again, I don't know what the behavior is, but that's not it. Increasing
$fn does increase the number of slices, but it isn't simply used as the
number of slices.
$fa/$fs/$fn seem to control both slices and segments.
Using with imported SVG
Does not need to be separately discussed.
rotate_extrude() Operator Module
Rotational extrusion spins a 2D shape around the Z-axis to form a
solid which has rotational symmetry. One way to think of this
operation is to imagine a Potter's wheel placed on the X-Y plane with
its axis of rotation pointing up towards +Z. Then place the to-be-made
object on this virtual Potter's wheel (possibly extending down below
the X-Y plane towards -Z). The to-be-made object is the cross-section
of the object on the X-Y plane (keeping only the right half, X >= 0).
That is the 2D shape that will be fed to rotate_extrude() as the child
in order to generate this solid. Note that the object started on the
X-Y plane but is tilted up (rotated +90 degrees about the X-axis) to
extrude.
I'm not sure that this is the best possible explanation.
Since a 2D shape is rendered by OpenSCAD on the X-Y plane, an
alternative way to think of this operation is as follows: spins a 2D
shape around the Y-axis to form a solid. The resultant solid is placed
so that its axis of rotation lies along the Z-axis.
That's the way that I always think of it, though I mentally rotate it to
vertical before spinning it.
Just like the linear_extrude, the extrusion is always performed on the
projection of the 2D polygon to the XY plane.
Again, we should not document this behavior.
Transformations like rotate, translate, etc. applied to the 2D polygon
before extrusion modify the projection of the 2D polygon to the XY
plane and therefore also modify the appearance of the final 3D object.
This is perhaps good stuff, if the part about projecting is removed.
Don't get confused, as OpenSCAD displays 2D polygons with a certain
height in the Z direction, so the 2D object (with its height) appears
to have a bigger projection to the XY plane. But for the projection to
the XY plane and also for the later extrusion only the base polygon
without height is used.
Once you get rid of the part about projecting this goes away too.
You cannot use rotate_extrude to produce a helix or screw thread.
Doing this properly can be difficult, so it's best to find a thread
library to make them for you.
This kind of comment can be valuable, but I'm not sure it belongs in a
reference manual. If it is in a reference manual, it should be in a
clear and separate section (of the description of the particular
feature) marked "Application Notes" or something like that, to make it
clear that it's not part of the description of the feature.
If the shape spans the X axis a warning appears in the console windows
and the rotate_extrude() is ignored.
Don't talk about what window something appears in, because not everybody
uses the OpenSCAD GUI.
Don't talk about what happens "after" the error.
Just say that it's an error, period.
(And, BTW, this particular one is something that I think should not be
an error; I think that the result should be as if you split the 2D shape
in half, rotationally extruded both, and unioned them. That is, take
the shape, sweep it 360 degrees, and anything it sweeps through (whether
once or twice) is part of the result.)
If the 2D shape touches the Y axis, i.e. at x=0, itmustbe a line
that touches, not a point, as a point results in a zero thickness 3D
object, which is invalid and results in a CGAL error.
This may have been addressed with Manifold.
*convexity* : If the extrusion fails for a non-trival 2D shape,
try setting the convexity parameter (the default is not 10, but 10
is a "good" value to try). See explanation further down.
Just point at the general discussion of convexity. (Which should not be
on this page.)
And the extrusion does not "fail". In fact, the artifacts may be quite
subtle.
*start*[Note:Requires versionDevelopment snapshot] : Defaults to 0
if*angle*is specified, and 180 if not. Specifies the starting
angle of the extrusion, counter-clockwise from the positive X axis.
start was part of an effort to align rotational extrusion behavior with
the behavior of other round things. I don't remember all of the
details, but there are few reasons why it isn't equivalent to rotating
the result.
*$fa* : minimum angle (in degrees) of each fragment.
*$fs* : minimum circumferential length of each fragment.
*$fn* :*fixed*number of fragments in 360 degrees. Values of 3 or
more override $fa and $fs
$fa, $fs and $fn must be named parameters.click here for more
details,
<https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features>.
Do not describe these in detail here. Refer to a general description of
them elsewhere.
Do not ever, ever, say "click here". Any text that would not make sense
when printed is wrong.
Rotate Extrude on Imported DXF
Delete.
Increasing the number of fragments composing the 2D shape improves the
quality of the mesh, but takes longer to render.
Unnecessary.
rotate_extrude(convexity = 10)
translate([2, 0, 0])
circle(r = 1, $fn = 100);
This example is unnecessary; this is a description of rotate_extrude,
not circle()
The number of fragments used by the extrusion can also be increased.
rotate_extrude(convexity = 10, $fn = 100)
translate([2, 0, 0])
circle(r = 1, $fn = 100);
Use $fs and $fa here. In fact, supply them at the top level. And this
case doesn't require specifying convexity (though I'm not sure why
not). Also, for circles this small high resolution is not practical;
make them bigger to make the example more real. Thus:
$fa = 1;
$fs = 1;
rotate_extrude()
translate([20, 0, 0])
circle(r = 10);
That's really the best practice. You almost never want to use $fn if
your intent is to create a circle.
(Minor exception that is itself something of a bug: if you're trying to
force the number of sides to be a multiple of 4. But that shouldn't be
discussed here.)
Using the parameter angle (with OpenSCAD versions 2016.xx), a hook can
be modeled .
https://en.wikibooks.org/wiki/File:Hook.pngOpenSCAD - a hook
eps = 0.01;
translate([eps, 60, 0])
rotate_extrude(angle=270, convexity=10)
translate([40, 0]) circle(10);
rotate_extrude(angle=90, convexity=10)
translate([20, 0]) circle(10);
translate([20, eps, 0])
rotate([90, 0, 0]) cylinder(r=10, h=80+eps);
Delete.
Extruding a Polygon
Delete.
Description of extrude parameters
Why are we repeating these here? Don't, especially because there is
little commonality between linear_extrude and rotate_extrude.
0% developed as of November 17, 2009
<https://en.wikibooks.org/wiki/Help:Development_stages>DXF
Extrusion
<https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/DXF_Extrusion>
Delete.
This should get more content. At the current state of things it can
probably all go on the 2D page, but if it gets much more complex it
might want its own page with a brief summary and reference here.
For the record, several modules in BOSL2 that can create multiple copies of a shape, create $idx (or similar) special variables in their scope, that the child modules can use to change their behavior. For example:
xcopies(10,n=4) circle(d=$idx+1);
will create four circles of increasing diameter, spread every 10 units along the X axis.

On Aug 8, 2025, at 1:06 AM, Jordan Brown via Discuss discuss@lists.openscad.org wrote:
[ Sigh yet again... what e-mail address I use is largely automatically handled on my desktop, but not on this laptop. ]
Mattheiu is right; this needs a subject change.
On 8/6/2025 12:51 AM, vulcan_--- via Discuss wrote:
One should not attempt to infer the API contract from the behavior.
umm .. then how better to understand it?
Reread the documentation. Repeat repeat repeat. Then ask and/or inspect the sources. Behavior is good only for very targeted questions, and even then should be tempered by the others.
i am not at all clear what overfit means ..
He means that you may assume that the behavior that you see is the only possible behavior. (For instance, you may assume that some particular triangularization (or lack thereof) is the promised answer, and it isn't.)
Yeah, specials are still a fuzzy thing for me .. even after Jordan’s diligent attempt to educate me on how they work i am not at all clear on what they bring to the language that is any different from having global variables at the top level of scope
They aren't necessarily at the top level. Try running this...
normal = "top";
$special = "top";
module foo() {
echo(normal=normal, $special=$special);
}
module bar() {
$special = "bar";
normal = "bar";
foo();
}
foo();
foo($special="top per-call");
bar();
echo(normal=normal, $special=$special);
Normal variables are scoped based on the program's static structure, so "normal" is visible from the top, from foo(), and from bar() until the definition of the new "normal" on bar's second line. Nothing ever refers to bar's definition of "normal".
Special variables ($ variables) are scoped based on the call stack. The initial setting ("top") is visible at the top level, and in anything called from the top level - which means the top-level call to foo(), and the call to bar() until bar's first line, where a new $special is created ("bar"). That new $special is visible to bar() and to everything that bar() calls - notably, its call to foo(). The second call to foo() sets $special in the call, and that setting applies to that call to foo() and everything that it calls (which is nothing).
Another way to put it:
For a normal variable, to find the assignment, start at the reference to the variable, then look at that scope. If it's defined there, you're done. Move to the next higher scope (one fewer level of braces). If it's defined there, you're done. Continue until you reach the top scope.
For a special variable, look at the current scope. If it's defined there, you're done. Move to the place that called this module; look at the scope there. If it's defined there, you're done. Continue until you reach the top scope.
It's actually a little more complicated than that, in in both cases that a reference in an assignment needs to look at earlier assignments in that scope.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
On 8/8/2025 1:14 AM, Revar Desmera via Discuss wrote:
For the record, several modules in BOSL2 that can create multiple
copies of a shape, create $idx (or similar) special variables in their
scope, that the child modules can use to change their behavior. For
example:
xcopies(10,n=4) circle(d=$idx+1);
will create four circles of increasing diameter, spread every 10 units
along the X axis.
Yes. This capability - passing special variables from parent to child -
is implied by the "the scope it's defined in and all scopes called from
that scope" rule.
It does point out one subtle aspect of the semantics: the top level
does not "call" circle() in this case. Rather, xcopies() calls it (via
children() and the particular call supplied by the top level. I don't
remember exactly what thread it was, but there's been a significant
discussion about the need for documentation to discuss this behavior,
while at the same time keeping the description of the "rotate() cube()"
case simple.
A note on the fontfeatures option in the font name. The text says it has
to be a plus sign followed by a feature name. But actually it can also be
<featurename>=<value>, such as smcp=1 instead of +smcp, but in the case of
the font I was looking at, frac=1 and frac=2 produce different
results----it's not a boolean setting. And yes, this means it looks like
fontfeatures=frac=2.
On Fri, Aug 8, 2025 at 4:14 AM Jordan Brown via Discuss <
discuss@lists.openscad.org> wrote:
[ I'll spend the effort to fix up this laptop configuration, again, sorry
for the duplicates. ]
Two Dimensional Modelling
[image: 0% developed as of November 17, 2009]
https://en.wikibooks.org/wiki/Help:Development_stages 2D Primitives
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives
All 2D primitives can be transformed with 3D transformations.
Really bad place to start. Yes, you can transform them with 3D
transforms, but if you do then the results can be weird. It should be
discouraged; you should almost always work with 2D transforms when working
with a 2D subassembly.
Also, maybe we should talk about the primitives before we talk about what
you can do with them.
They are usually used as part of a 3D extrusion.
Yeah, eventually. But again this doesn't seem appropriate for a "2D
primitives" section. Maybe for an overview section above that.
Although they are infinitely thin, they are rendered with a 1-unit
thickness.
Again, maybe in an overview section.
Note: Trying to subtract with difference() from 3D object will lead to
unexpected results in final rendering.
The real rule is "don't mix 2D objects with 3D objects and 3D
operations". It isn't necessary or appropriate to say very much about what
will happen if you do. Some cases will yield errors, while others will do
something weird. We don't want the documentation to nail down any
particular behavior, because there are reasons that we might want to change
the behavior in these cases.
Ref, e.g., OEP 7 "Mixed Dimension Geometry Support"
https://github.com/openscad/openscad/wiki/OEP7%3A-Mixed-Dimension-Geometry-Support
.
Square Object Module
By default this module draws a unit square in the first quadrant, (+X,+Y),
starting at the origin [0,0]. Its four lines have no thickness but the
shape is drawn as a 1 unit high, filled plane.
The second sentence should probably just go away:
- The first part "its four lines have no thickness" is both misleading
- the lines have no independent existence - and incorrect; when rendered
they *do* have thickness.
- The second half (drawn as 1 unit high) restates something already
said in above.
The module's arguments may be written in the order <size>, center=<bool> without
being named, but the names may be used as shown in the examples:
There needs to be (but probably isn't) enough documentation convention
that this need not be said.
Parameters
size has two forms: single value or vector single - non-negative
float, length of all four sides
Should use the word "number" rather than the word "float". OpenSCAD does
not have distinct floating point and integer types; it has only numbers.
center boolean, default false, to set the shape's position in the X-Y
plane
Center When false, as it is by default, the shape will be drawn from
its first point at (0,0) in the First Quadrant, (+X,+Y). With center set to
true the shape is drawn centered on the origin.
These two paragraphs should be merged.
Circle Object Module
By default this module draws a unit circle centered on the origin [0,0] as
a pentagon with its starting point on the X-axis at X=1. Its lines have no
thickness but the shape is drawn as a 1 unit high, filled plane.
The part of the first sentence starting "as a pentagon ..." should go
away. It's true, but it really belongs as part of the description of
$fa/$fs.
Again, the second sentence should just go away.
Somewhere it should say "Circles are approximated as regular polygons; see
<reference to $fa/$fs/$fn> for the details of the polygons generated."
The argument radius may be given without being named, but the r and d arguments
must be named.
There is no "radius" argument. There are r and d.
Again, we should have a documentation convention so that we don't have to
repeat positional/named rules, but the behavior here is that r can be
supplied as the first argument, but d must be named.
(Technically, if you say "circle(undef, 10)" the 10 is the second it
creates a 10-unit-diameter circle. I would say that the fact that this
works is a minor bug.)
$fa Special Variable $fs Special Variable $fn Special Variable
Theses should be described only to the extent of pointing at the general
description of $fa/$fs/$fn.
The default circle displays as a pentagram as that is the minimum number
of fragments used to approximate a curved shape calculated from the default
values for $fs and $fa. To have it draw as a smooth shape increase the $fn
value, the minimum number of fragments to draw, to 20 or more (best $fn <
128).
This is just bad. First, everything here should be covered in the
description of $fa/$fs/$fn. Second, using $fn to control the resolution of
a circle is generally the wrong answer; you are better off setting $fa and
$fs. Finally, specific advice on $fn values is a bad idea, because the
"looks smooth" value varies dramatically with size. A 20-gon is okay for a
medium-small circle; a 72-gon is not good enough for a 100-unit circle.
An alternative method to draw a very smooth circle scale is to scale down
a very large circle.
scale( 0.001 ) circle(200);
This should just go away; it confuses the issue.
Another way to solve the lack of a built-in module for regular polygons is
to write a custom one: module regular_polygon()
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/example_module_regular_polygon()
I wouldn't include this. Using polygon() is harder than using circle(),
and anybody who's capable of using it should have little trouble simulating
circle().
convexity Integer, default=1 - complex edge geometry may require a higher
value value to preview correctly.
Should include a link to a general discussion of convexity. Probably
should not even mention the default; that should be covered in the general
discussion.
Points Parameter A list of X-Y coordinates in this form:
[[1, 1], [1, 4], [3, 4], [3, 1], [1, 1]]
which defines four points and makes it explicit that the last one is the
same as the first.
Including the first point twice is not strictly necessary as this:
[[1, 1], [1, 4], [3, 4], [3, 1]]
gives the same result.
This seems like it should be simplified. In the absence of a paths
parameter, the last point always connects to the first, because polygons
are always closed.
Paths Parameter
This optional parameter is a nested vector of paths.
A "path" is a list of index values that reference points in the points vector.
It can explicitly describe a closed loop by its last index being the same
as its first, as in:
[1, 2, 3, 4, 1]
but this is equivalent to:
[1, 2, 3, 4]
Again, this seems like unnecessary complexity; the last point always
connects to the first.
Notice that the points vector is simple list,
No, it's a list of lists.
while each path is a separate vector.
Yes... points and paths are the same order. They are both lists of lists.
This means that paths, that are lists of references to points, have to
"know" which points it needs to include.
While it's true that paths need to "know" the indexes they connect, I
don't see how that follows from the previous sentences.
This can be an issue if the polygon is assembled from a number of shapes
at run time as the order of adding shapes affects their point's index
values.
It's true that this is something that you must handle, but I don't think
that a reference manual needs to discuss it.
. Convexity
Formatting error: this title is merged with the previous paragraph. (But
should be deleted, see below.)
Shapes with a lot of detail in their edges may need the convexity
parameter increased to preview correctly. See Convexity
Already discussed, should be deleted.
Example With Multiple Holes
[Note: Requires version 2015.03] (for use of concat())
https://en.wikibooks.org/wiki/File:OpenSCAD_romboid_with_holes.jpg
We are using "a" for the point lists and "b" for their paths:
a0 = [[0,0],[100,0],[130,50],[30,50]]; // outer boundary
b0 = [1,0,3,2];
a1 = [[20,20],[40,20],[30,30]]; // hole 1
b1 = [4,5,6];
a2 = [[50,20],[60,20],[40,30]]; // hole 2
b2 = [7,8,9];
a3 = [[65,10],[80,10],[80,40],[65,40]]; // hole 3
b3 = [10,11,12,13];
a4 = [[98,10],[115,40],[85,40],[85,10]]; // hole 4
b4 = [14,15,16,17];
a = concat( a0,a1,a2,a3,a4 ); // merge all points into "a"
b = [b0,b1,b2,b3,b4]; // place all paths into a vector
polygon(a,b);
//alternate
polygon(a,[b0,b1,b2,b3,b4]);
The "alternate" at the end of the example seems unnecessary - of course
you can use either a particular expression or a variable that has been set
to that expression.
2D to 3D by Extrusion
A polygon may be the basis for an extrusion, just as any of the 2D
primitives can. This example script
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/example_module_2D_to_3D_extrusion
may be used to draw the shape in this image:
Yes, a polygon can be used as the basis for extrusion, just as any of the
2D primitives can. That means that you do not need a specific example of
that case.
Import a 2D Shape From a DXF
[Deprecated: import_dxf() will be removed in a future release. Use Use import()
Object Module
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language#import
instead. instead*]*
As a deprecated feature, this should be pushed to the bottom.
Read a DXF file and create a 2D shape.
Example
linear_extrude(height = 5, center = true)
import_dxf(file = "example009.dxf", layer = "plate");
Example with Import()
linear_extrude(height = 5, center = true)
import(file = "example009.dxf", layer = "plate");
The second should perhaps be titled "Replacement example with import()".
Note also that since OpenSCAD is case sensitive the word "import" should
not be capitalized.
[image: 0% developed as of November 17, 2009]
https://en.wikibooks.org/wiki/Help:Development_stages Text
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text
Text is a big enough topic that it should probably have its own page, with
just a brief mention and cross-reference here.
I see that it has its own page and is transcluded here. It should not
be transcluded, because that makes it harder to just read everything.
Text in OpenSCAD
Being able to use text objects as a part of a model is valuable in a lot
of design solutions.
Delete this sentence. This is reference material, not sales material.
The user already knows whether or not it's valuable to them.
The fonts available to use in a script
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Fonts_in_OpenSCAD
are from the system that OpenSCAD is running in with the addition of
those explicitly added by the script itself.
And OpenSCAD includes several. (And this duplicates a more extensive
discussion below.)
text() Object Module
The text() object module draws a single string of text as a 2D geometric
object, using fonts installed on the local system or provided as separate
font file.
provided as +a+ separate font file
The shape starts at the origin and is drawn along the positive X axis.
By default, ...
(because halign and valign change things)
text String. A single line of any character allowed
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Characters_Strings#Characters
. Limitation: non-printable ASCII characters like newline and tab
rendered as placeholders
Delete the second sentence. If it's a string, it's allowed. As for being
a single line and treatment of non-printable characters, need to phrase
that as a current restriction, not as a permanent behavior - it would be
good if we could eventually provide more support there, and we wouldn't
want to be prevented from adding that support by compatibility concerns.
Ref https://github.com/openscad/openscad/issues/5018 for the desire for
multi-line text.
font a formatted string
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Parameters with
default font of "Liberation Sans:style=Regular"
"formatted string" is a poor phrase there. Better would be something like
"String. A font specification with ...".
Also I see that this is a link over to a separate Text page. A separate
Text page is good, as discussed above, but it shouldn't be duplicated here.
size non-negative decimal, default=10. The generated text has a height
above the baseline of approximately this value, varying for different fonts
but typically being slightly smaller.
The "decimal" part should be "number". (It isn't even sensible to talk
about a base.)
I don't feel the need for the "non-negative" part. (It should probably
also be non-zero.) Unless we have a special meaning for a negative size,
we should be able to let people figure out for themselves that if they make
a silly request they will get a silly answer.
Current behavior is ... interesting... though when you think about it
unsurprising: the text is mirrored in X and Y, leading to it being
effectively rotated 180 degrees. Unless we really want to keep that
behavior, we should probably make it be an error instead. Until and unless
we decide that we want to keep that behavior, we should not document it.
There needs to be a footnote about size. Because of an arithmetic error
in the implementation (issue #4304
https://github.com/openscad/openscad/issues/4304), the "size" parameter
does not correspond to a typical font size specification. It is a
coincidence that the arithmetic error approximately cancels out the usual
ratio between the specified font size and the size of a capital letter,
making "size" approximately specify the size of a capital letter in a
typical Western font. However, since the result is useful, and the error
has been in place since the beginning, we really can't fix it. Maybe at
some point we can introduce an alternative parameter that specifies a more
conventional font size, eg PR#4306
https://github.com/openscad/openscad/pull/4306.
spacing float, default=1. Multiplicative factor that increases or
decreases spacing between characters.
"float" should be "number".
language String. The language of the text (e.g., "en", "ar", "ch").
Default is "en". script String, default="latin". The script of the text
(e.g. "latin", "arabic", "hani").
Somebody needs to figure out what these actually do.
$fn higher values generate smoother curves (refer to Special Variables
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#special_variables
)
This should refer to $fa, $fs, and $fn... and really you shouldn't be
using $fn here.
Font & Style Parameter
The "name" of a font is a string starting with its logical font name and
variation,
I don't see variation as a separate part of the specification.
Also, use of the "typewriter" font here is inappropriate; neither of these
is a language keyword or language component. Either use plain text or
perhaps italics.
optionally followed by a colon (":") separated list of font
specifications like a style selection, and a set of zero or more features.
We should include a list of the name=value specifications supported, or
refer to external (fontconfig?) documentation.
Again, "features" is not a keyword and should not be in typewriter font.
The common variations in a font family are sans and serif though many
others will be seen in the list of fonts available. Each font variation can
be drawn with a style to support textual emphasis.
I think those are part of the font name, and that there they are usually
capitalized. I'm a bit torn on whether they should be in typewriter font.
The default, upright appearance is usually called "Regular" with "Bold",
"Italic", and "Bold Italic" being the other three styles commonly included
in a font. In general the styles offered by a font may only be known by
using the platform's font configuration tools or the OpenSCAD font list
dialog
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Fonts_in_Openscad
.
This should explicitly tie to the "style=" parameter.
The fontfeatures property is appended to the font name after the
"fontfeatures" should be in typewriter font because it is a keyword.
"font name" should not be in typewriter font because it is not a keyword.
optional style parameter. Its value is a semi-colon separated list of
feature codes, each prefixed by a plus, "+", to indicate that it is being
added,
Should end with a colon, not a comma.
font = "Linux Libertine G:style=Regular:fontfeatures=+smcp;+onum");
Size Parameter
Text size is normally given in points, and a point is 1/72 of an inch
high. The formula to convert the size value to "points" is pt =
size/3.937, so a size argument of 3.05 is about 12 points.
This is incorrect, because OpenSCAD is unitless. "size" specifies some
dimension of the font, in OpenSCAD units. See the discussion above about
exactly what dimension it measures. (OpenSCAD units are typically
interpreted as millimeters, but that's up to the user and the consuming
program; it is not part of OpenSCAD's definitions.)
There should be no reference to "points" except perhaps to disclaim that
anything is measured in points.
Note: Character size the distance from ascent to descent, not from
ascent to baseline.
Ref the arithmetic error mentioned above and the long discussion in issue
#4304, this is incorrect. "size" should have measured approximately the
font ascent plus descent, but instead measures (even more approximately)
the font ascent.
One of these four names must be given as a string to the valign parameter.
Since the valign parameter itself is optional, the word "must" seems
inappropriate. Perhaps "The valign parameter may be set to one of these
four words".
top The text is aligned so the top of the tallest character in your text
is at the given Y coordinate.
There is no "given Y coordinate". The top of the tallest character in
your text is at the X axis, Y=0.
center The text is aligned with the center of the bounding box at the
given Y coordinate.
Again, at Y=0.
baseline The text is aligned with the font baseline at the given Y
coordinate.
Again, at Y=0.
bottom The text is aligned so the bottom of the lowest-reaching character
in your text is at the given Y coordinate.
Again, at Y=0.
Note: only the "baseline" vertical alignment option will ensure correct
alignment of texts that use mix of fonts and sizes.
This overlaps a lot with the last sentence of the definition of "baseline"
and should probably be merged with it.
One of these three names must be given as a string to the halign
parameter.
Again, the word "must" seems inappropriate.
left The text is aligned with the left side of the bounding box at the
given X coordinate. center The text is aligned with the center of the
bounding box at the given X coordinate. right The text is aligned with
the right of the bounding box at the given X coordinate.
None of these are correct. The alignment is based on spacing, not on the
bounding box. For most letters, "left" will position the ink slightly to
the right of X=0. (For a size=10 M in Liberation Sans, it's about 1.1
units right of X=0.) I'd need to do more research to figure out the
exactly correct wording.
And for all of them, there is no "given [XY] coordinate". Positioning is
relative to the origin.
Spacing Parameter
Characters in a text element have the size dictated by their glyph in the
font being used. As such their size in X and Y is fixed. Each glyph also
has fixed advance values (it is a vector [a,b], see textmetrics
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#textmetrics)
for the offset to the origin of the next character. The position of each
following character is the advance.x value multiplied by the space value.
Obviously letters in the string can be stretched out when the factor is
greater than 1, and can be made to overlap when space is a fraction
closer to zero, but interestingly, using a negative value spaces each
letter in the opposite of the direction parameter.
This is more or less correct, but what it doesn't say is that "spacing" is
almost completely useless for a proportionally spaced font, for two
reasons. Ref https://github.com/openscad/openscad/issues/3859 .
- It does not take ligatures into account; it spaces a ligature as a
single glyph, yielding text that looks like "d i ffi c u l t".
- Because it's a multiplier on the advance value, and because the
advance value is larger for a wide glyph than it is for a narrow glyph,
spacing between narrow glyphs and wide glyphs is radically different.
"IIIMMM" demonstrates this problem.
The "spacing" parameter should probably be downplayed, and should probably
be deprecated.
Text Examples
Simulating Formatted Text
Needs to define what it means by "formatted".
When text needs to be drawn as if it was formatted it is possible to use
translate() to space lines of text vertically. Fonts that descend below the
baseline need to be spaced apart vertically by about 1.4size to not
overlap. Some word processing programs use a more generous spacing of
1.6size for "single spacing" and double spacing can use 3.2*size.
fontmetrics() can supply more correct values for the particular font.
But really this is advice, not reference material.
Fonts in OpenSCAD
The fonts available for use in a script are thosed:
- registered in the local system
- included in the OpenSCAD installation
- imported at run-time by a program
A call to fontmetrics() using only default settings shows the
installation's standard font and settings:
Any reference to fontmetrics() needs a "requires release XXX" note, which
at the moment is still "requires development snapshot". But really this
should be at most a reference to the fontmetrics() section.
{
nominal = {
ascent = 12.5733;
descent = -2.9433;
};
max = {
ascent = 13.6109; descent = -4.2114;
};
interline = 15.9709;
font = {
family = "Liberation Sans";
style = "Regular";
};
}
Wherever this ends up, the indentation needs work. It should match the
indentation style used in the examples.
None of the platforms OpenSCAD is available on include the Liberation font
family so having it as part of the app's installation, and making it the
default font, avoids problems of font availability.
"None" is an awfully broad statement about a moving target. It would be
better to say "To avoid problems of font availability, OpenSCAD includes
the Liberation font family as part of its installation, and has Liberation
Sans as the default font.".
Note: It was previously noted in the docs that fonts may be added to
the installation by drag-and-drop of a font file into the editor window,
but as of version 2025 Snapshot this is not the case
That isn't what it said. It said:
You can drag a font in the font list, into the editor window to use in the
text() statement.
I can't readily check a 2025 build at the moment, but as of Oct 2024 the
it does exactly as described: dragging a font from the OpenSCAD font list
into the editor window drops its name in the editor window. If that is no
longer the case, it's a bug.
In general, don't say things like this. If the documentation said X, and
you find that X is not true, then one of the following is true:
- You didn't understand, and X is indeed true. (And maybe the
documentation needs to be clearer.)
- X is false, and it's a bug. (The bug should be fixed, not the
documentation.)
- X is false, and has always been false, and it was always a
documentation error. (And the documentation needs to be fixed.)
- Indeed, X used to be true and is no longer true, and it's an
intentional change, and nobody updated the documentation. This is a very
rare case, because it often means a compatibility problem or feature
regression.
Regardless, the right answer is to file an issue to get the actual answer.
In the following sample code a True Type Font, Andika, has been added to
the system fonts using its Font Management service.
We shouldn't talk about adding fonts to the system. That's not our
problem.
But also, that's not what the sample does. It adds a font to OpenSCAD,
and has nothing to do with the platform font mechanisms.
Supported font file formats are TrueType
https://en.wikipedia.org/wiki/TrueType fonts (.ttf) and OpenType
https://en.wikipedia.org/wiki/OpenType fonts (.otf). Once a file is
registered to the project the details of the fonts in it may be seen in the
font list dialog (see image) so that the logical font names, variations,
and their available styles are available for use in the project.
This says "see image", but doesn't indicate which image.
And: OpenSCAD doesn't have the notion of "projects" or "registered to the
project".
3D Text by Extrusion
This is true of all 2D objects and so does not need to be mentioned.
Delete.
position a vector [X,Y], the origin of the first glyph, thus the
lower-left corner of the drawn text.
No, it's not the origin of the first glyph, or at least that's a confusing
phrase to use. A glyph is usually positioned slightly to the right of the
origin, and if it's a descender then it's below the origin, and some
characters (e.g. quotes) are well above the origin. A more correct
statement would be that it's the lower left corner of the bounding box of
the text.
If one is going to talk about the origin of a glyph, it should be the
point on the baseline to at the left edge of the advance... which this
isn't.
size a vector [a,b], the size of the generated text.
Should be [x,y]. [a,b] doesn't tell you what "a" and "b" mean.
ascent positive float, the amount that the text extends above the
baseline.
Use the word "number" rather than "float".
It's not always positive; for a glyph entirely below the baseline (like
underscore in Liberation Sans) it's negative. (I'm not sure that's truly
the right definition, but it's the current behavior.)
descent negative float, the amount that the text extends below the
baseline.
Not always negative; for a glyph that is entirely above the baseline (like
apostrophe in Liberation Sans) it's positive. Again, I'm not sure that's
the right definition, but it's the current behavior.
offset a vector default [0, 0], the lower-left corner of the box
containing the text, including inter-glyph spacing before the first glyph.
There is no default; this is a value that's returned to you.
This is not the correct definition (and it wasn't correct in the original
that I wrote). It's the position of the origin of the text, after
adjusting for halign and valign. For normal LTR text, the X coordinate is
the X coordinate at the left edge of the first glyph's advance, and the Y
component is the Y coordinate of the baseline.
advance a vector default [153.09, 0], amount of space to leave to any
following text.
There is no default (and certainly not that one!).
The original definition ("the "other end" of the text, the point at which
additional text should be positioned.") wasn't great, but was more
correct. I would say "The point at which additional text should be
positioned, relative to the text's origin as reported by 'offset'.".
This example displays the text metrics for the default font used by
OpenSCAD:
"text metrics for ... font" is a non sequitur. Text metrics measure a
particular string. (And "used by OpenSCAD" is unnecessary; the entire
document is in that context.)
And it's incorrect; the default font is Liberation Sans and this example
uses Liberation Serif.
Better would be:
This example displays the text metrics for "Hello, World!" for Liberation
Serif with size=20:
https://en.wikibooks.org/wiki/File:OpenSCAD_textmetrics.pngUsing
textmetrics() to draw a box around text
s = "Hello, World!";size = 20;font = "Liberation Serif";
translate([0,0,1]) text("Hello, World!", size=size, font=font);
Should use "s" instead of repeating the string. (And this is in my
original, sigh.)
displays (formatted for readability):
The original "yields" is better, because it might or might not be
displayed.
ECHO: { position = [0.7936, -4.2752]; size = [149.306, 23.552]; ascent = 19.2768; descent = -4.2752; offset = [0, 0]; advance = [153.09, 0]; }
The indentation should match the examples, with the close brace at the
left margin.
fontmetrics()
size Decimal, optional. The size of the font, as described above for
text().
Replace "decimal" with "number".
[image: 0% developed as of November 17, 2009]
https://en.wikibooks.org/wiki/Help:Development_stages 3D to 2D
Projection
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/3D_to_2D_Projection
Using the projection() function, you can create 2d drawings from 3d
models,
So far so good.
and export them to the dxf format.
This part should be deleted. There are any number of things you might do
with a 2D projection of a 3D object. Exporting to DXF is only one.
It works by projecting a 3D model to the (x,y) plane, with z at 0. If
cut=true, only points with z=0 are considered (effectively cutting the
object), with cut=false(the default), points above and below the plane
are considered as well (creating a proper projection).
Example: Consider example002.scad, that comes with OpenSCAD.
https://en.wikibooks.org/wiki/File:Openscad_projection_example_2x.png
Then you can do a 'cut' projection, which gives you the 'slice' of the x-y
plane with z=0.
Doing the non-default case as the first example seems wrong; I would swap
the two examples.
Another Example
You can also use projection to get a 'side view' of an object.
This example seems unnecessary for a reference manual. It's a
straightforward combination of the features described.
Links:
- More complicated example
<http://www.gilesbathgate.com/2010/06/extracting-2d-mendel-outlines-using-openscad/>
from Giles Bathgate's blog
Seems inappropriate for a reference manual. Also, doesn't seem more
complicated at all.
[image: 0% developed as of November 17, 2009]
https://en.wikibooks.org/wiki/Help:Development_stages 2D to 3D Extrusion
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_to_3D_Extrusion
Extrusion https://en.wikipedia.org/wiki/Extrusion is the process of
creating an object with a fixed cross-sectional profile. OpenSCAD provides
two commands
"Commands" isn't the right word. "Modules" is more correct, but
"operations" is probably best.
Both extrusion methods work on a (possibly disjointed) 2D shape normally
drawn in the relevant plane (see below).
The old description of the behavior of extrusion for 2D objects that have
been moved off the Z=0 plane is an example of something that should never
have been documented. It's not a particularly useful behavior, and we
might eventually want a different behavior. At most, it should have said
"don't do that".
It should probably say "drawn on the Z=0 plane".
This child object is first projected onto the X-Y plane along the Z axis
to create the starting face of the extrusion.
Delete. We shouldn't document that behavior.
The start face is duplicated at the Z position given by the height
parameter to create the extrusion's end face. The extrusion is then formed
by creating a surface that joins each point along the edges of the two
faces.
That's a seriously incomplete description, because it's only true with all
of the parameters at their defaults.
The 2D shape may be any 2D primitive shape
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives, a 2d
polygon
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives#Polygon,
an imported 2D drawing
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives#Importing_a_2D_Drawing,
or a boolean combination
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/CSG_Modelling of
them.
Or, in other words, the 2D shape may be ... a 2D shape.
Delete the whole sentence.
The 2D shape may have a Z value that moves it out of the X-Y plane, and it
may even be rotated out of parallel with it. As stated above, the
extrusion's starting face is the projection of the 2D shape onto the X-Y
plane, which, if it is rotated, will have the effect of fore-shortening it
normal to the axis of the rotation.
Delete.
Using a 3D object as the extrusion's child will cause a compile time error.
Factually incorrect. It's not a compile-time error; it's a run-time error.
Also, we just said that the child must be a 2D shape. Exact behavior when
that requirement is violated need not be (and probably should not be)
specified.
Delete.
Including a 3D object in a composition of 2D objects (formed using boolean
combinations on them) will be detected, the 3D object(s) will be deleted
from it and the remaining 2D objects will be the basis for projecting their
shape onto the X-Y plane.
We need not (and generally should not) specify the behavior in error
conditions. Delete.
Parameters For Linear Extrusion
There are no required parameters. The default operation is to extrude the
child by 100 units vertically from the X-Y Plane, centered on the [0,0]
origin.
"centered" is at best meaningless (because it's extruded wherever the
child is, without respect to the origin) and at worst incorrect (because
the default is to extrude into +Z, not to center in Z). Delete that last
phrase.
Doesn't have to be an integer.
I don't know how strong a pattern we have for specifying parameters, but
they shouldn't be numbered. (Except maybe if they are usable as positional
parameters - which don't match these numbers.)
I can't say that I truly understand eigenvectors, but I don't think this
is one. The "signed" part is unnecessary, because all numbers are signed,
and the "decimal" part is meaningless because abstract numbers have no base.
"v" is a vector of three numbers that controls the vector along which the
extrusion is done.
It has an interesting interaction with "height". If both are specified,
height is used as the length of the extrusion, along the direction that v
points, and v's magnitude is ignored. If only v is specified, it is used
to control both the direction and length of the extrusion.
Saying that it's the axis of rotation for twist is sort of right, but
maybe needs more explanation. Normally when you think of an axis of
rotation, you're rotating along the plane perpendicular to that axis.
Here, though, it is perhaps more correct to say that it controls the
origin of the rotation. At each slice, the 2D shape is rotated around Z,
with the origin being the XY position of the extrusion vector.
"at the Z=0 plane" would be a bit more obvious.
Should include a link... which should not be pointing at this page, no
matter which page we're talking about.
a number
180 degrees is a half twist, 360 is all the way around, and so on.
Unnecessary, delete.
a non-negative number
minimum 0.0,
Implied by "non-negative", delete.
that specifies the factor by which the end face should be scaled up, or
down, in size from that of the start face.
All scaling is either up or down. Just "should be scaled".
or : an [x,y] vector that scales the extrusion in the X and Y directions
separately.
Delete the colon.
Needs help.
h a named parameter, synonym to height
Just list it in the same block as height.
$fn $fs $fa Special Parameters - given as named parameters.
They have standard special-variable semantics, which means they can be
specified in the context or in the call. They should be mentioned, but
perhaps not as parameters per se. I believe they only affect twisted
extrusions, so maybe they should be mentioned there.
Center
This parameter affects only affects the vertical position or the
extrusion. Its X-Y position is always that of the projection that sets its
starting face.
"or" should be "of".
This is a nice comment, but doesn't say what the parameter does.
"When true, the extrusion is centered vertically around Z=0." seems
adequate to me, without any further comment, but a subsequent comment about
not affecting X and Y would be OK.
Scale
This is multiplicative factor that affects the size of extrusion's end
face. As such 1.0 means no change, a value greater than one expands the end
face, and a value between 0.001 and less than 1 shrinks it.
"As such" is unnecessary.
I don't know where 0.001 came from. I would say "a value less than 1
shrinks it".
A value of 0.0 causes the end face to degenerate to a point, turning the
extrusion into a pyramid, cone, or complex pointy shape according to what
the starting shape is.
I'd say this is unnecessary.
Using the vector form sets the scale factor in the X and Y directions
separately
Twist
Twist is applied, by default, as a rotation about the Z Axis.
As discussed above, twist is always around Z. What v controls is the
origin of that rotation.
When the start face is at the origin a twist creates a spiral out of any
corners in the child shape. If the start face is translated away from the
origin the twist creates a spring shape.
I don't know if it's truly useful to try to describe the various shapes
that can result from twisting.
One thing that might be worth explicitly mentioning is that you can't
practically use linear_extrude to generate threads. You can come
temptingly close, but they won't be shaped right. (It is actually possible
to get right, but requires an unobvious base shape.)
A positive twist rotates clockwise, negative twist the opposite.
Huh. I basically never use twist, so I never noticed that it's backwards
from rotate. That seems very wrong... and it's way too late to fix it. It
might be worth mentioning this difference.
Twist Axis Vector
The second parameter is an [x,y,z] eigen vector that specifies the axis of
rotation of the applied twist.
Suggest referring to it by name instead of by position.
The ratios of the three dimensional values to their respective coordinate
axes specify the tilt away from the default axis, [0,0,1], the Z-Axis. For
instance, v=[cos(45),0,1] tilts the extrusion at 45 degrees to the X axis.
It's actually a skew rather than a tilt.
The start and end faces are always normal to the Z-axis, even when the
twist axis is tilted. The extruded and twisted surfaces are thus distorted
from what might be expected in an extruded shape. The more expected result
may be achieved by applying a rotation to then twisted extrusion on the Z
Axis to tilt it into the desired position.
It's best not to make assumptions about what the user expects. Describe
the operation, and describe it carefully. Do not describe how to do things
that are straightforward combinations of operations.
Note that the documentation does not discuss which happens first: twist
or scale. I don't believe it matters when using the same scaling for X and
Y, but matters a great deal with using different scaling on the two axes.
It twists and then scales, which can mean (for instance) that a shape that
started out rectangular turns into a parallelogram. There's an argument
that this is not the useful behavior, and that scale-and-then-twist is
more useful since it retains the general shape throughout the extrusion.
I've started a discussion a few times about maybe changing this, but I
don't think it ever came to a conclusion. It might be best not to document
this without that conclusion.
$fn, $fa, $fs Special Parameters
The special variables must be given as named parameters and are applied to
the extrusion, overriding the global setting. When the same special
variables are set on the base shape its values override their use as
parameters on the extrusion.
None of this is really accurate.
The special variables have standard special-variable behavior, which means
that you can specify them in the context or in the particular call, and
they apply to that context (including a specific call) and everything that
that is called from that context. There is no "global setting" that is
special.
What matters for the linear_extrude (and in particular for twisted
extrusions) is the setting that it sees.
If the child 2D shape also uses these variables, what matters for it is
what it sees... which, absent an inner setting, will be the same as what
linear_extrude sees.
Thus, either:
linear_extrude(height=10, twist=20, $fn=100) circle(10);
or
$fn=100; linear_extrude(height=10, twist=20) circle(10);
will yield a high-resolution twist of a high-resolution circle.
On the other hand, either
linear_extrude(height=10, twist=20, $fn=100) circle(10, $fn=3);
or
$fn=100; linear_extrude(height=10, twist=20) circle(10, $fn=3);
will yield a high-resolution twist of a low-resolution circle - a triangle.
Extrusion From Imported DXF
Does not need to be discussed. You can linear_extrude any 2D shape, and
an import of a DXF yields a 2D shape.
A Unit Circle with No Twist
I don't think all of these examples are necessary.
Generate an extrusion from a circle 2 units along the X Axis from the
origin,
unit circle
centered vertically on the X-Y plane, with no twist. The extrusion appears
to have a pentagonal cross-section because the extrusion's child is a 2D
circle with the default value for $fn.
It doesn't appear to have a pentagonal cross-section. It does have a
pentagonal cross-section.
The same circle, but made into a spiral by 500 degrees of
counter-clockwise twist.
If you look carefully, this example demonstrates why you can't make
threads. As you twist it more, it becomes thinner and thinner in Z. The
problem is that the cross-section of a thread is a strange shape.
Mesh Refinement
The slices parameter defines the number of intermediate points along the Z
axis of the extrusion.
I am not sure of the exactly right way to describe this, because of fence
post errors.
"slices" controls the number of 3D "chunks" that make up the extrusion.
The total number of instances of the original 2D object is slices+1.
Its default increases with the value of twist.
It's some function of that and $fa/$fs/$fn. I don't know what the
function is or exactly how to describe it.
Additional the segments parameter
Addition -> Additionally
Segments need to be a multiple of the polygon's fragments to have an
effect (6 or 9.. for a circle($fn=3), 8,12.. for a square() ).
I don't know what the actual behavior is, but that's not it. For more
complex shapes (I experimented with a right triangle) intermediate values
do have an effect.
The special variables
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features
$fn, $fs and $fa can also be used to improve the output. If slices is
not defined, its value is taken from the defined $fn value.
Again, I don't know what the behavior is, but that's not it. Increasing
$fn does increase the number of slices, but it isn't simply used as the
number of slices.
$fa/$fs/$fn seem to control both slices and segments.
Using with imported SVG
Does not need to be separately discussed.
rotate_extrude() Operator Module
Rotational extrusion spins a 2D shape around the Z-axis to form a solid
which has rotational symmetry. One way to think of this operation is to
imagine a Potter's wheel placed on the X-Y plane with its axis of rotation
pointing up towards +Z. Then place the to-be-made object on this virtual
Potter's wheel (possibly extending down below the X-Y plane towards -Z).
The to-be-made object is the cross-section of the object on the X-Y plane
(keeping only the right half, X >= 0). That is the 2D shape that will be
fed to rotate_extrude() as the child in order to generate this solid. Note
that the object started on the X-Y plane but is tilted up (rotated +90
degrees about the X-axis) to extrude.
I'm not sure that this is the best possible explanation.
Since a 2D shape is rendered by OpenSCAD on the X-Y plane, an alternative
way to think of this operation is as follows: spins a 2D shape around the
Y-axis to form a solid. The resultant solid is placed so that its axis of
rotation lies along the Z-axis.
That's the way that I always think of it, though I mentally rotate it to
vertical before spinning it.
Just like the linear_extrude, the extrusion is always performed on the
projection of the 2D polygon to the XY plane.
Again, we should not document this behavior.
Transformations like rotate, translate, etc. applied to the 2D polygon
before extrusion modify the projection of the 2D polygon to the XY plane
and therefore also modify the appearance of the final 3D object.
- A translation in Z of the 2D polygon has no effect on the result (as
also the projection is not affected).
- A translation in X increases the diameter of the final object.
- A translation in Y results in a shift of the final object in Z
direction.
- A rotation about the X or Y axis distorts the cross section of the
final object, as also the projection to the XY plane is distorted.
This is perhaps good stuff, if the part about projecting is removed.
Don't get confused, as OpenSCAD displays 2D polygons with a certain height
in the Z direction, so the 2D object (with its height) appears to have a
bigger projection to the XY plane. But for the projection to the XY plane
and also for the later extrusion only the base polygon without height is
used.
Once you get rid of the part about projecting this goes away too.
You cannot use rotate_extrude to produce a helix or screw thread. Doing
this properly can be difficult, so it's best to find a thread library to
make them for you.
This kind of comment can be valuable, but I'm not sure it belongs in a
reference manual. If it is in a reference manual, it should be in a
clear and separate section (of the description of the particular feature)
marked "Application Notes" or something like that, to make it clear that
it's not part of the description of the feature.
If the shape spans the X axis a warning appears in the console windows and
the rotate_extrude() is ignored.
Don't talk about what window something appears in, because not everybody
uses the OpenSCAD GUI.
Don't talk about what happens "after" the error.
Just say that it's an error, period.
(And, BTW, this particular one is something that I think should not be an
error; I think that the result should be as if you split the 2D shape in
half, rotationally extruded both, and unioned them. That is, take the
shape, sweep it 360 degrees, and anything it sweeps through (whether once
or twice) is part of the result.)
If the 2D shape touches the Y axis, i.e. at x=0, it must be a line
that touches, not a point, as a point results in a zero thickness 3D
object, which is invalid and results in a CGAL error.
This may have been addressed with Manifold.
convexity : If the extrusion fails for a non-trival 2D shape, try
setting the convexity parameter (the default is not 10, but 10 is a "good"
value to try). See explanation further down.
Just point at the general discussion of convexity. (Which should not be
on this page.)
And the extrusion does not "fail". In fact, the artifacts may be quite
subtle.
start [Note: Requires version Development snapshot] : Defaults to 0 if
angle is specified, and 180 if not. Specifies the starting angle of the
extrusion, counter-clockwise from the positive X axis.
start was part of an effort to align rotational extrusion behavior with
the behavior of other round things. I don't remember all of the details,
but there are few reasons why it isn't equivalent to rotating the result.
$fa : minimum angle (in degrees) of each fragment. $fs : minimum
circumferential length of each fragment. $fn : fixed number of
fragments in 360 degrees. Values of 3 or more override $fa and $fs $fa,
$fs and $fn must be named parameters. click here for more details,
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features
.
Do not describe these in detail here. Refer to a general description of
them elsewhere.
Do not ever, ever, say "click here". Any text that would not make sense
when printed is wrong.
Rotate Extrude on Imported DXF
Delete.
Increasing the number of fragments composing the 2D shape improves the
quality of the mesh, but takes longer to render.
Unnecessary.
rotate_extrude(convexity = 10)
translate([2, 0, 0])
circle(r = 1, $fn = 100);
This example is unnecessary; this is a description of rotate_extrude, not
circle()
The number of fragments used by the extrusion can also be increased.
rotate_extrude(convexity = 10, $fn = 100)
translate([2, 0, 0])
circle(r = 1, $fn = 100);
Use $fs and $fa here. In fact, supply them at the top level. And this
case doesn't require specifying convexity (though I'm not sure why not).
Also, for circles this small high resolution is not practical; make them
bigger to make the example more real. Thus:
$fa = 1;
$fs = 1;
rotate_extrude()
translate([20, 0, 0])
circle(r = 10);
That's really the best practice. You almost never want to use $fn if your
intent is to create a circle.
(Minor exception that is itself something of a bug: if you're trying to
force the number of sides to be a multiple of 4. But that shouldn't be
discussed here.)
Using the parameter angle (with OpenSCAD versions 2016.xx), a hook can be
modeled .
https://en.wikibooks.org/wiki/File:Hook.pngOpenSCAD - a hook
eps = 0.01;
translate([eps, 60, 0])
rotate_extrude(angle=270, convexity=10)
translate([40, 0]) circle(10);
rotate_extrude(angle=90, convexity=10)
translate([20, 0]) circle(10);
translate([20, eps, 0])
rotate([90, 0, 0]) cylinder(r=10, h=80+eps);
Delete.
Extruding a Polygon
Delete.
Description of extrude parameters
Why are we repeating these here? Don't, especially because there is
little commonality between linear_extrude and rotate_extrude.
[image: 0% developed as of November 17, 2009]
https://en.wikibooks.org/wiki/Help:Development_stages DXF Extrusion
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/DXF_Extrusion
Delete.
Import 2D
Import 2D Shapes
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Import_2D
This should get more content. At the current state of things it can
probably all go on the 2D page, but if it gets much more complex it might
want its own page with a brief summary and reference here.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
What is the community getting for this INCREDIBLE level of effort?
I think we should
revert the documentation back to prior to when Vulcan started
changing things
make a list of problems with the documentation (an explicit,
reviewable requirements list from which we can make controlled changes
to the documentation)
make small, localized changes that can be validated with minimal effort.
perhaps this should be controlled in a manner similar to the
development of the software itself, with a GIT-like system of problem
descriptions and resolutions.
The people who really contribute to OpenSCAD are few, perhaps less than
half a dozen. To force them to spend their time working on a project
THAT DOES NOT NEED TO BE DONE, and is not going well, will slow down
development and bug fixes that DO need to be done.
It is clear to me that having an OpenSCAD novice working on the
documentation is not a great idea. Good intentions but bad results.
Vulcan: if you REALLY want to help this community, use OpenSCAD actively
(every day) for a year, making complex designs, and then come back.
Is there anyone (or any group) that is in control of the documentation
Wiki? If not, we need such an entity. If so, someone needs to take
control of this project.
Again, if I'm wrong, I apologize. But I have been getting private
messages agreeing with me.
Jon
On 8/8/2025 4:08 AM, Jordan Brown via Discuss wrote:
[ I'll spend the effort to fix up this laptop configuration, again,
sorry for the duplicates. ]
Two Dimensional Modelling
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stages2D
Primitives
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives
All 2D primitives can be transformed with 3D transformations.
Really bad place to start. Yes, you can transform them with 3D
transforms, but if you do then the results can be weird. It should be
discouraged; you should almost always work with 2D transforms when
working with a 2D subassembly.
Also, maybe we should talk about the primitives before we talk about
what you can do with them.
They are usually used as part of a 3D extrusion.
Yeah, eventually. But again this doesn't seem appropriate for a "2D
primitives" section. Maybe for an overview section above that.
Although they are infinitely thin, they are rendered with a 1-unit
thickness.
Again, maybe in an overview section.
Note: Trying to subtract with|difference()|from 3D object will lead
to unexpected results in final rendering.
The real rule is "don't mix 2D objects with 3D objects and 3D
operations". It isn't necessary or appropriate to say very much about
what will happen if you do. Some cases will yield errors, while
others will do something weird. We don't want the documentation to
nail down any particular behavior, because there are reasons that we
might want to change the behavior in these cases.
Ref, e.g., OEP 7 "Mixed Dimension Geometry Support"
https://github.com/openscad/openscad/wiki/OEP7%3A-Mixed-Dimension-Geometry-Support.
Square Object Module
By default this module draws a unit square in the first quadrant,
(+X,+Y), starting at the origin [0,0]. Its four lines have no
thickness but the shape is drawn as a 1 unit high, filled plane.
The second sentence should probably just go away:
The module's arguments may be written in the order|<size>,
center=<bool>|without being named, but the names may be used as shown
in the examples:
There needs to be (but probably isn't) enough documentation convention
that this need not be said.
Parameters
size
has two forms:/single value/or/vector/
single - non-negative float, length of all four sides
Should use the word "number" rather than the word "float". OpenSCAD
does not have distinct floating point and integer types; it has only
numbers.
center
boolean, default false, to set the shape's position in the X-Y plane
CenterWhen|false|, as it is by default, the shape will be drawn
from its first point at (0,0) in the First Quadrant, (+X,+Y). With
center set to|true|the shape is drawn centered on the origin.
These two paragraphs should be merged.
Circle Object Module
By default this module draws a unit circle centered on the origin
[0,0] as a pentagon with its starting point on the X-axis at X=1. Its
lines have no thickness but the shape is drawn as a 1 unit high,
filled plane.
The part of the first sentence starting "as a pentagon ..." should go
away. It's true, but it really belongs as part of the description of
$fa/$fs.
Again, the second sentence should just go away.
Somewhere it should say "Circles are approximated as regular polygons;
see <reference to $fa/$fs/$fn> for the details of the polygons generated."
The argument|radius|may be given without being named, but
the|r|and|d|arguments must be named.
There is no "radius" argument. There are r and d.
Again, we should have a documentation convention so that we don't have
to repeat positional/named rules, but the behavior here is that r can
be supplied as the first argument, but d must be named.
(Technically, if you say "circle(undef, 10)" the 10 is the second it
creates a 10-unit-diameter circle. I would say that the fact that
this works is a minor bug.)
$fa
Special Variable
$fs
Special Variable
$fn
Special Variable
Theses should be described only to the extent of pointing at the
general description of $fa/$fs/$fn.
The default circle displays as a pentagram as that is the minimum
number of fragments used to approximate a curved shape calculated
from the default values for $fs and $fa. To have it draw as a smooth
shape increase the $fn value, the minimum number of fragments to
draw, to 20 or more (best $fn < 128).
This is just bad. First, everything here should be covered in the
description of $fa/$fs/$fn. Second, using $fn to control the
resolution of a circle is generally the wrong answer; you are better
off setting $fa and $fs. Finally, specific advice on $fn values is a
bad idea, because the "looks smooth" value varies dramatically with
size. A 20-gon is okay for a medium-small circle; a 72-gon is not
good enough for a 100-unit circle.
An alternative method to draw a very smooth circle scale is to scale
down a very large circle.
scale( 0.001 ) circle(200);
This should just go away; it confuses the issue.
Another way to solve the lack of a built-in module for regular
polygons is to write a custom one:module regular_polygon()
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/example_module_regular_polygon()
I wouldn't include this. Using polygon() is harder than using
circle(), and anybody who's capable of using it should have little
trouble simulating circle().
convexity
Integer, default=1 - complex edge geometry may require a higher
value value to preview correctly.
Should include a link to a general discussion of convexity. Probably
should not even mention the default; that should be covered in the
general discussion.
Points ParameterA list of X-Y coordinates in this form:
[[1, 1], [1, 4], [3, 4], [3, 1], [1, 1]]
which defines four points and makes it explicit that the last one is
the same as the first.
Including the first point twice is not strictly necessary as this:
[[1, 1], [1, 4], [3, 4], [3, 1]]
gives the same result.
This seems like it should be simplified. In the absence of a paths
parameter, the last point always connects to the first, because
polygons are always closed.
Paths Parameter
This optional parameter is a nested vector of paths.
A "path" is a list of index values that reference points in
the|points|vector. It can explicitly describe a closed loop by its
last index being the same as its first, as in:
[1, 2, 3, 4, 1]
but this is equivalent to:
[1, 2, 3, 4]
Again, this seems like unnecessary complexity; the last point always
connects to the first.
Notice that the points vector is simple list,
No, it's a list of lists.
while each path is a separate vector.
Yes... points and paths are the same order. They are both lists of lists.
This means that paths, that are lists of references to points, have
to "know" which points it needs to include.
While it's true that paths need to "know" the indexes they connect, I
don't see how that follows from the previous sentences.
This can be an issue if the polygon is assembled from a number of
shapes at run time as the order of adding shapes affects their
point's index values.
It's true that this is something that you must handle, but I don't
think that a reference manual needs to discuss it.
.Convexity
Formatting error: this title is merged with the previous paragraph.
(But should be deleted, see below.)
Shapes with a lot of detail in their edges may need the convexity
parameter increased to preview correctly. See Convexity
Already discussed, should be deleted.
Example With Multiple Holes
[Note:Requires version2015.03] (for use of|concat()|)
https://en.wikibooks.org/wiki/File:OpenSCAD_romboid_with_holes.jpg
We are using "a" for the point lists and "b" for their paths:
a0 = [[0,0],[100,0],[130,50],[30,50]]; // outer boundary
b0 = [1,0,3,2];
a1 = [[20,20],[40,20],[30,30]]; // hole 1
b1 = [4,5,6];
a2 = [[50,20],[60,20],[40,30]]; // hole 2
b2 = [7,8,9];
a3 = [[65,10],[80,10],[80,40],[65,40]]; // hole 3
b3 = [10,11,12,13];
a4 = [[98,10],[115,40],[85,40],[85,10]]; // hole 4
b4 = [14,15,16,17];
a = concat( a0,a1,a2,a3,a4 ); // merge all points into "a"
b = [b0,b1,b2,b3,b4]; // place all paths into a vector
polygon(a,b);
//alternate
polygon(a,[b0,b1,b2,b3,b4]);
The "alternate" at the end of the example seems unnecessary - of
course you can use either a particular expression or a variable that
has been set to that expression.
2D to 3D by Extrusion
A polygon may be the basis for an extrusion, just as any of the 2D
primitives can. Thisexample script
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/example_module_2D_to_3D_extrusionmay
be used to draw the shape in this image:
Yes, a polygon can be used as the basis for extrusion, just as any of
the 2D primitives can. That means that you do not need a specific
example of that case.
Import a 2D Shape From a DXF
[Deprecated:import_dxf() will be removed in a future release. Use
Useimport() Object Module
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language#importinstead.
instead*]*
As a deprecated feature, this should be pushed to the bottom.
Read a DXF file and create a 2D shape.
Example
linear_extrude(height = 5, center = true)
import_dxf(file = "example009.dxf", layer = "plate");
Example with Import()
linear_extrude(height = 5, center = true)
import(file = "example009.dxf", layer = "plate");
The second should perhaps be titled "Replacement example with
import()". Note also that since OpenSCAD is case sensitive the word
"import" should not be capitalized.
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stagesText
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text
Text is a big enough topic that it should probably have its own page,
with just a brief mention and cross-reference here.
I see that it has its own page and is transcluded here. It should
not be transcluded, because that makes it harder to just read everything.
Text in OpenSCAD
Being able to use text objects as a part of a model is valuable in a
lot of design solutions.
Delete this sentence. This is reference material, not sales
material. The user already knows whether or not it's valuable to them.
The fontsavailable to use in a script
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Fonts_in_OpenSCADare
from the system that OpenSCAD is running in with the addition of
those explicitly added by the script itself.
And OpenSCAD includes several. (And this duplicates a more extensive
discussion below.)
text() Object Module
The|text()|object module draws a single string of text as a 2D
geometric object, using fonts installed on the local system or
provided as separate font file.
provided as +a+ separate font file
The shape starts at the origin and is drawn along the positive X axis.
By default, ...
(because halign and valign change things)
text
String. A single line ofany character allowed
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Characters_Strings#Characters.*Limitation:*non-printable
ASCII characters like newline and tab rendered as placeholders
Delete the second sentence. If it's a string, it's allowed. As for
being a single line and treatment of non-printable characters, need to
phrase that as a current restriction, not as a permanent behavior - it
would be good if we could eventually provide more support there, and
we wouldn't want to be prevented from adding that support by
compatibility concerns. Ref
https://github.com/openscad/openscad/issues/5018 for the desire for
multi-line text.
font
aformatted string
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Parameterswith
default font of "Liberation Sans:style=Regular"
"formatted string" is a poor phrase there. Better would be something
like "String. A font specification with ...".
Also I see that this is a link over to a separate Text page. A
separate Text page is good, as discussed above, but it shouldn't be
duplicated here.
size
non-negative decimal, default=10. The generated text has a height
above the baseline of approximately this value, varying for
different fonts but typically being slightly smaller.
The "decimal" part should be "number". (It isn't even sensible to
talk about a base.)
I don't feel the need for the "non-negative" part. (It should
probably also be non-zero.) Unless we have a special meaning for a
negative size, we should be able to let people figure out for
themselves that if they make a silly request they will get a silly answer.
Current behavior is ... interesting... though when you think about it
unsurprising: the text is mirrored in X and Y, leading to it being
effectively rotated 180 degrees. Unless we really want to keep that
behavior, we should probably make it be an error instead. Until and
unless we decide that we want to keep that behavior, we should not
document it.
There needs to be a footnote about size. Because of an arithmetic
error in the implementation (issue #4304
https://github.com/openscad/openscad/issues/4304), the "size"
parameter does not correspond to a typical font size specification.
It is a coincidence that the arithmetic error approximately cancels
out the usual ratio between the specified font size and the size of a
capital letter, making "size" approximately specify the size of a
capital letter in a typical Western font. However, since the result
is useful, and the error has been in place since the beginning, we
really can't fix it. Maybe at some point we can introduce an
alternative parameter that specifies a more conventional font size, eg
PR#4306 https://github.com/openscad/openscad/pull/4306.
spacing
float, default=1. Multiplicative factor that increases or
decreases spacing between characters.
"float" should be "number".
language
String. The language of the text (e.g., "en", "ar", "ch").
Default is "en".
script
String, default="latin". The script of the text (e.g. "latin",
"arabic", "hani").
Somebody needs to figure out what these actually do.
$fn
higher values generate smoother curves (refer toSpecial Variables
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#special_variables)
This should refer to $fa, $fs, and $fn... and really you shouldn't be
using $fn here.
Font & Style Parameter
The "name" of a font is a string starting with its|logical font
name|and|variation|,
I don't see variation as a separate part of the specification.
Also, use of the "typewriter" font here is inappropriate; neither of
these is a language keyword or language component. Either use plain
text or perhaps italics.
optionally followed by a colon (":") separated list of font
specifications like a|style|selection, and a set of zero or
more|features|.
We should include a list of the name=value specifications supported,
or refer to external (fontconfig?) documentation.
Again, "features" is not a keyword and should not be in typewriter font.
The common variations in a font family are|sans|and|serif|though many
others will be seen in the list of fonts available. Each font
variation can be drawn with a/style/to support textual emphasis.
I think those are part of the font name, and that there they are
usually capitalized. I'm a bit torn on whether they should be in
typewriter font.
The default, upright appearance is usually called "Regular" with
"Bold", "Italic", and "Bold Italic" being the other three styles
commonly included in a font. In general the styles offered by a font
may only be known by using the platform's font configuration tools or
theOpenSCAD font list dialog
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Fonts_in_Openscad.
This should explicitly tie to the "style=" parameter.
The fontfeatures property is appended to the|font name|after the
"fontfeatures" should be in typewriter font because it is a keyword.
"font name" should not be in typewriter font because it is not a
keyword.
optional style parameter. Its value is a semi-colon separated list of
feature codes, each prefixed by a plus, "+", to indicate that it is
being added,
Should end with a colon, not a comma.
font = "Linux Libertine G:style=Regular:fontfeatures=+smcp;+onum");
Size Parameter
Text size is normally given in points, and a point is 1/72 of an inch
high. The formula to convert thesizevalue to "points" is|pt =
size/3.937|, so asizeargument of 3.05 is about 12 points.
This is incorrect, because OpenSCAD is unitless. "size" specifies
some dimension of the font, in OpenSCAD units. See the discussion
above about exactly what dimension it measures. (OpenSCAD units are
typically interpreted as millimeters, but that's up to the user and
the consuming program; it is not part of OpenSCAD's definitions.)
There should be no reference to "points" except perhaps to disclaim
that anything is measured in points.
Note: Character size the distance from ascent to descent, not from
ascent to baseline.
Ref the arithmetic error mentioned above and the long discussion in
issue #4304, this is incorrect. "size" should have measured
approximately the font ascent plus descent, but instead measures (even
more approximately) the font ascent.
One of these four names must be given as a string to
the|valign|parameter.
Since the valign parameter itself is optional, the word "must" seems
inappropriate. Perhaps "The valign parameter may be set to one of
these four words".
top
The text is aligned so the top of the tallest character in your
text is at the given Y coordinate.
There is no "given Y coordinate". The top of the tallest character in
your text is at the X axis, Y=0.
center
The text is aligned with the center of the bounding box at the
given Y coordinate.
Again, at Y=0.
baseline
The text is aligned with the font baseline at the given Y coordinate.
Again, at Y=0.
bottom
The text is aligned so the bottom of the lowest-reaching
character in your text is at the given Y coordinate.
Again, at Y=0.
Note: only the "baseline" vertical alignment option will ensure
correct alignment of texts that use mix of fonts and sizes.
This overlaps a lot with the last sentence of the definition of
"baseline" and should probably be merged with it.
One of these three names must be given as a string to
the|halign|parameter.
Again, the word "must" seems inappropriate.
left
The text is aligned with the left side of the bounding box at the
given X coordinate.
center
The text is aligned with the center of the bounding box at the
given X coordinate.
right
The text is aligned with the right of the bounding box at the
given X coordinate.
None of these are correct. The alignment is based on spacing, not on
the bounding box. For most letters, "left" will position the ink
slightly to the right of X=0. (For a size=10 M in Liberation Sans,
it's about 1.1 units right of X=0.) I'd need to do more research to
figure out the exactly correct wording.
And for all of them, there is no "given [XY] coordinate". Positioning
is relative to the origin.
Spacing Parameter
Characters in a text element have the size dictated by their glyph in
the font being used. As such their size in X and Y is fixed. Each
glyph also has fixed|advance|values (it is a vector [a,b],
seetextmetrics
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#textmetrics)
for the offset to the origin of the next character. The position of
each following character is the|advance.x|value multiplied by
the|space|value. Obviously letters in the string can be stretched out
when the factor is greater than 1, and can be made to overlap
when|space|is a fraction closer to zero, but interestingly, using a
negative value spaces each letter in the opposite of
the|direction|parameter.
This is more or less correct, but what it doesn't say is that
"spacing" is almost completely useless for a proportionally spaced
font, for two reasons. Ref
https://github.com/openscad/openscad/issues/3859 .
The "spacing" parameter should probably be downplayed, and should
probably be deprecated.
Text Examples
Simulating Formatted Text
Needs to define what it means by "formatted".
When text needs to be drawn as if it was formatted it is possible to
use translate() to space lines of text vertically. Fonts that descend
below the baseline need to be spaced apart vertically by
about|1.4size|to not overlap. Some word processing programs use a
more generous spacing of|1.6size|for "single spacing" and double
spacing can use|3.2*size|.
fontmetrics() can supply more correct values for the particular font.
But really this is advice, not reference material.
Fonts in OpenSCAD
The fonts available for use in a script are thosed:
A call to fontmetrics() using only default settings shows the
installation's standard font and settings:
Any reference to fontmetrics() needs a "requires release XXX" note,
which at the moment is still "requires development snapshot". But
really this should be at most a reference to the fontmetrics() section.
{
nominal = {
ascent = 12.5733;
descent = -2.9433;
};
max = {
ascent = 13.6109; descent = -4.2114;
};
interline = 15.9709;
font = {
family = "Liberation Sans";
style = "Regular";
};
}
Wherever this ends up, the indentation needs work. It should match
the indentation style used in the examples.
None of the platforms OpenSCAD is available on include the Liberation
font family so having it as part of the app's installation, and
making it the default font, avoids problems of font availability.
"None" is an awfully broad statement about a moving target. It would
be better to say "To avoid problems of font availability, OpenSCAD
includes the Liberation font family as part of its installation, and
has Liberation Sans as the default font.".
Note: It was previously noted in the docs that fonts may be added
to the installation by drag-and-drop of a font file into the editor
window, but as of version 2025 Snapshot this isnotthe case
That isn't what it said. It said:
You can drag a font in the font list, into the editor window to
use in the text() statement.
I can't readily check a 2025 build at the moment, but as of Oct 2024
the it does exactly as described: dragging a font from the OpenSCAD
font list into the editor window drops its name in the editor window.
If that is no longer the case, it's a bug.
In general, don't say things like this. If the documentation said X,
and you find that X is not true, then one of the following is true:
Regardless, the right answer is to file an issue to get the actual answer.
In the following sample code a True Type Font, Andika, has been added
to the system fonts using its Font Management service.
We shouldn't talk about adding fonts to the system. That's not our
problem.
But also, that's not what the sample does. It adds a font to
OpenSCAD, and has nothing to do with the platform font mechanisms.
Supported font file formats areTrueType
https://en.wikipedia.org/wiki/TrueTypefonts (.ttf) andOpenType
https://en.wikipedia.org/wiki/OpenTypefonts (.otf). Once a file is
registered to the project the details of the fonts in it may be seen
in the font list dialog (see image) so that the logical font names,
variations, and their available styles are available for use in the
project.
This says "see image", but doesn't indicate which image.
And: OpenSCAD doesn't have the notion of "projects" or "registered to
the project".
3D Text by Extrusion
This is true of all 2D objects and so does not need to be mentioned.
Delete.
position
a vector [X,Y], the origin of the first glyph, thus the
lower-left corner of the drawn text.
No, it's not the origin of the first glyph, or at least that's a
confusing phrase to use. A glyph is usually positioned slightly to
the right of the origin, and if it's a descender then it's below the
origin, and some characters (e.g. quotes) are well above the origin.
A more correct statement would be that it's the lower left corner of
the bounding box of the text.
If one is going to talk about the origin of a glyph, it should be the
point on the baseline to at the left edge of the advance... which this
isn't.
size
a vector [a,b], the size of the generated text.
Should be [x,y]. [a,b] doesn't tell you what "a" and "b" mean.
ascent
positive float, the amount that the text extends above the baseline.
Use the word "number" rather than "float".
It's not always positive; for a glyph entirely below the baseline
(like underscore in Liberation Sans) it's negative. (I'm not sure
that's truly the right definition, but it's the current behavior.)
descent
negative float, the amount that the text extends below the baseline.
Not always negative; for a glyph that is entirely above the baseline
(like apostrophe in Liberation Sans) it's positive. Again, I'm not
sure that's the right definition, but it's the current behavior.
offset
a vector default [0, 0], the lower-left corner of the box
containing the text, including inter-glyph spacing before the
first glyph.
There is no default; this is a value that's returned to you.
This is not the correct definition (and it wasn't correct in the
original that I wrote). It's the position of the origin of the text,
after adjusting for halign and valign. For normal LTR text, the X
coordinate is the X coordinate at the left edge of the first glyph's
advance, and the Y component is the Y coordinate of the baseline.
advance
a vector default [153.09, 0], amount of space to leave to any
following text.
There is no default (and certainly not that one!).
The original definition ("the "other end" of the text, the point at
which additional text should be positioned.") wasn't great, but was
more correct. I would say "The point at which additional text should
be positioned, relative to the text's origin as reported by 'offset'.".
This example displays the text metrics for the default font used by
OpenSCAD:
"text metrics for ... font" is a non sequitur. Text metrics measure a
particular string. (And "used by OpenSCAD" is unnecessary; the entire
document is in that context.)
And it's incorrect; the default font is Liberation Sans and this
example uses Liberation Serif.
Better would be:
This example displays the text metrics for "Hello, World!" for
Liberation Serif with size=20:
https://en.wikibooks.org/wiki/File:OpenSCAD_textmetrics.pngUsing
textmetrics() to draw a box around text
s="Hello, World!";
size=20;
font="Liberation Serif";
translate([0,0,1])
text("Hello, World!",size=size,font=font);
Should use "s" instead of repeating the string. (And this is in my
original, sigh.)
displays (formatted for readability):
The original "yields" is better, because it might or might not be
displayed.
ECHO:{
position=[0.7936,-4.2752];
size=[149.306,23.552];
ascent=19.2768;
descent=-4.2752;
offset=[0,0];
advance=[153.09,0];
}
The indentation should match the examples, with the close brace at the
left margin.
fontmetrics()
size
Decimal, optional. The size of the font, as described above
for|text()|.
Replace "decimal" with "number".
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stages3D to 2D
Projection
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/3D_to_2D_Projection
Using the|projection()|function, you can create 2d drawings from 3d
models,
So far so good.
and export them to the dxf format.
This part should be deleted. There are any number of things you might
do with a 2D projection of a 3D object. Exporting to DXF is only one.
It works by projecting a 3D model to the (x,y) plane, with z at 0.
If|cut=true|, only points with z=0 are considered (effectively
cutting the object), with|cut=false|(/the default/), points above and
below the plane are considered as well (creating a proper projection).
Example: Consider example002.scad, that comes with OpenSCAD.
https://en.wikibooks.org/wiki/File:Openscad_projection_example_2x.png
Then you can do a 'cut' projection, which gives you the 'slice' of
the x-y plane with z=0.
Doing the non-default case as the first example seems wrong; I would
swap the two examples.
Another Example
You can also use projection to get a 'side view' of an object.
This example seems unnecessary for a reference manual. It's a
straightforward combination of the features described.
Links:
Seems inappropriate for a reference manual. Also, doesn't seem more
complicated at all.
0% developed as of November 17, 2009
https://en.wikibooks.org/wiki/Help:Development_stages2D to 3D
Extrusion
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_to_3D_Extrusion
Extrusion https://en.wikipedia.org/wiki/Extrusionis the process of
creating an object with a fixed cross-sectional profile. OpenSCAD
provides two commands
"Commands" isn't the right word. "Modules" is more correct, but
"operations" is probably best.
Both extrusion methods work on a (possibly disjointed) 2D shape
normally drawn in the relevant plane (see below).
The old description of the behavior of extrusion for 2D objects that
have been moved off the Z=0 plane is an example of something that
should never have been documented. It's not a particularly useful
behavior, and we might eventually want a different behavior. At most,
it should have said "don't do that".
It should probably say "drawn on the Z=0 plane".
This child object is first projected onto the X-Y plane along the Z
axis to create the starting face of the extrusion.
Delete. We shouldn't document that behavior.
The start face is duplicated at the Z position given by the height
parameter to create the extrusion's end face. The extrusion is then
formed by creating a surface that joins each point along the edges of
the two faces.
That's a seriously incomplete description, because it's only true with
all of the parameters at their defaults.
The 2D shape may be any2D primitive shape
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives,
a2d polygon
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives#Polygon,
animported 2D drawing
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_Primitives#Importing_a_2D_Drawing,
or aboolean combination
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/CSG_Modellingof
them.
Or, in other words, the 2D shape may be ... a 2D shape.
Delete the whole sentence.
The 2D shape may have a Z value that moves it out of the X-Y plane,
and it may even be rotated out of parallel with it. As stated above,
the extrusion's starting face is the projection of the 2D shape onto
the X-Y plane, which, if it is rotated, will have the effect of
fore-shortening it normal to the axis of the rotation.
Delete.
Using a 3D object as the extrusion's child will cause a compile time
error.
Factually incorrect. It's not a compile-time error; it's a run-time
error.
Also, we just said that the child must be a 2D shape. Exact behavior
when that requirement is violated need not be (and probably should not
be) specified.
Delete.
Including a 3D object in a composition of 2D objects (formed using
boolean combinations on them) will be detected, the 3D object(s) will
be deleted from it and the remaining 2D objects will be the basis for
projecting their shape onto the X-Y plane.
We need not (and generally should not) specify the behavior in error
conditions. Delete.
Parameters For Linear Extrusion
There are no required parameters. The default operation is to extrude
the child by 100 units vertically from the X-Y Plane, centered on the
[0,0] origin.
"centered" is at best meaningless (because it's extruded wherever the
child is, without respect to the origin) and at worst incorrect
(because the default is to extrude into +Z, not to center in Z).
Delete that last phrase.
Doesn't have to be an integer.
I don't know how strong a pattern we have for specifying parameters,
but they shouldn't be numbered. (Except maybe if they are usable as
positional parameters - which don't match these numbers.)
I can't say that I truly understand eigenvectors, but I don't think
this is one. The "signed" part is unnecessary, because all numbers
are signed, and the "decimal" part is meaningless because abstract
numbers have no base.
"v" is a vector of three numbers that controls the vector along which
the extrusion is done.
It has an interesting interaction with "height". If both are
specified, height is used as the length of the extrusion, along the
direction that v points, and v's magnitude is ignored. If only v is
specified, it is used to control both the direction and length of the
extrusion.
Saying that it's the axis of rotation for twist is sort of right, but
maybe needs more explanation. Normally when you think of an axis of
rotation, you're rotating along the plane perpendicular to that axis.
Here, though, it is perhaps more correct to say that it controls the
origin of the rotation. At each slice, the 2D shape is rotated
around Z, with the origin being the XY position of the extrusion vector.
"at the Z=0 plane" would be a bit more obvious.
Should include a link... which should not be pointing at this page, no
matter which page we're talking about.
a number
180 degrees is a half twist, 360 is all the way around, and so on.
Unnecessary, delete.
a non-negative number
minimum 0.0,
Implied by "non-negative", delete.
that specifies the factor by which the end face should be scaled
up, or down, in size from that of the start face.
All scaling is either up or down. Just "should be scaled".
or : an [x,y] vector that scales the extrusion in the X and Y
directions separately.
Delete the colon.
Needs help.
h
a named parameter, synonym to height
Just list it in the same block as height.
$fn $fs $fa
Special Parameters - given as named parameters.
They have standard special-variable semantics, which means they can be
specified in the context or in the call. They should be mentioned,
but perhaps not as parameters per se. I believe they only affect
twisted extrusions, so maybe they should be mentioned there.
Center
This parameter affects only affects the vertical position or the
extrusion. Its X-Y position is always that of the projection that
sets its starting face.
"or" should be "of".
This is a nice comment, but doesn't say what the parameter does.
"When true, the extrusion is centered vertically around Z=0." seems
adequate to me, without any further comment, but a subsequent comment
about not affecting X and Y would be OK.
Scale
This is multiplicative factor that affects the size of extrusion's
end face. As such 1.0 means no change, a value greater than one
expands the end face, and a value between 0.001 and less than 1
shrinks it.
"As such" is unnecessary.
I don't know where 0.001 came from. I would say "a value less than 1
shrinks it".
A value of 0.0 causes the end face to degenerate to a point, turning
the extrusion into a pyramid, cone, or complex pointy shape according
to what the starting shape is.
I'd say this is unnecessary.
Using the vector form sets the scale factor in the X and Y directions
separately
Twist
Twist is applied, by default, as a rotation about the Z Axis.
As discussed above, twist is always around Z. What v controls is the
origin of that rotation.
When the start face is at the origin a twist creates a spiral out of
any corners in the child shape. If the start face is translated away
from the origin the twist creates a spring shape.
I don't know if it's truly useful to try to describe the various
shapes that can result from twisting.
One thing that might be worth explicitly mentioning is that you can't
practically use linear_extrude to generate threads. You can come
temptingly close, but they won't be shaped right. (It is actually
possible to get right, but requires an unobvious base shape.)
A positive twist rotates clockwise, negative twist the opposite.
Huh. I basically never use twist, so I never noticed that it's
backwards from rotate. That seems very wrong... and it's way too late
to fix it. It might be worth mentioning this difference.
Twist Axis Vector
The second parameter is an [x,y,z] eigen vector that specifies the
axis of rotation of the applied twist.
Suggest referring to it by name instead of by position.
The ratios of the three dimensional values to their respective
coordinate axes specify the tilt away from the default axis, [0,0,1],
the Z-Axis. For instance, v=[cos(45),0,1] tilts the extrusion at 45
degrees to the X axis.
It's actually a skew rather than a tilt.
The start and end faces are always normal to the Z-axis, even when
the twist axis is tilted. The extruded and twisted surfaces are thus
distorted from what might be expected in an extruded shape. The more
expected result may be achieved by applying a rotation to then
twisted extrusion on the Z Axis to tilt it into the desired position.
It's best not to make assumptions about what the user expects.
Describe the operation, and describe it carefully. Do not describe
how to do things that are straightforward combinations of operations.
Note that the documentation does not discuss which happens first:
twist or scale. I don't believe it matters when using the same
scaling for X and Y, but matters a great deal with using different
scaling on the two axes. It twists and then scales, which can mean
(for instance) that a shape that started out rectangular turns into a
parallelogram. There's an argument that this is not the useful
behavior, and that scale-and-then-twist is more useful since it
retains the general shape throughout the extrusion. I've started a
discussion a few times about maybe changing this, but I don't think it
ever came to a conclusion. It might be best not to document this
without that conclusion.
$fn, $fa, $fs Special Parameters
The special variables must be given as named parameters and are
applied to the extrusion, overriding the global setting. When the
same special variables are set on the base shape its values override
their use as parameters on the extrusion.
None of this is really accurate.
The special variables have standard special-variable behavior, which
means that you can specify them in the context or in the particular
call, and they apply to that context (including a specific call) and
everything that that is called from that context. There is no "global
setting" that is special.
What matters for the linear_extrude (and in particular for twisted
extrusions) is the setting that it sees.
If the child 2D shape also uses these variables, what matters for it
is what it sees... which, absent an inner setting, will be the same
as what linear_extrude sees.
Thus, either:
linear_extrude(height=10, twist=20, $fn=100) circle(10);
or
$fn=100; linear_extrude(height=10, twist=20) circle(10);
will yield a high-resolution twist of a high-resolution circle.
On the other hand, either
linear_extrude(height=10, twist=20, $fn=100) circle(10, $fn=3);
or
$fn=100; linear_extrude(height=10, twist=20) circle(10, $fn=3);
will yield a high-resolution twist of a low-resolution circle - a
triangle.
Extrusion From Imported DXF
Does not need to be discussed. You can linear_extrude any 2D shape,
and an import of a DXF yields a 2D shape.
A Unit Circle with No Twist
I don't think all of these examples are necessary.
Generate an extrusion from a circle 2 units along the X Axis from the
origin,
unit circle
centered vertically on the X-Y plane, with no twist. The extrusion
appears to have a pentagonal cross-section because the extrusion's
child is a 2D circle with the default value for $fn.
It doesn't appear to have a pentagonal cross-section. It does
have a pentagonal cross-section.
The same circle, but made into a spiral by 500 degrees of
counter-clockwise twist.
If you look carefully, this example demonstrates why you can't make
threads. As you twist it more, it becomes thinner and thinner in Z.
The problem is that the cross-section of a thread is a strange shape.
Mesh Refinement
The slices parameter defines the number of intermediate points along
the Z axis of the extrusion.
I am not sure of the exactly right way to describe this, because of
fence post errors.
"slices" controls the number of 3D "chunks" that make up the
extrusion. The total number of instances of the original 2D object is
slices+1.
Its default increases with the value of twist.
It's some function of that and $fa/$fs/$fn. I don't know what the
function is or exactly how to describe it.
Additional the segments parameter
Addition -> Additionally
Segments need to be a multiple of the polygon's fragments to have an
effect (6 or 9.. for a circle($fn=3), 8,12.. for a square() ).
I don't know what the actual behavior is, but that's not it. For more
complex shapes (I experimented with a right triangle) intermediate
values do have an effect.
Thespecial variables
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features$fn,
$fs and $fa can also be used to improve the output. If slices is not
defined, its value is taken from the defined $fn value.
Again, I don't know what the behavior is, but that's not it.
Increasing $fn does increase the number of slices, but it isn't simply
used as the number of slices.
$fa/$fs/$fn seem to control both slices and segments.
Using with imported SVG
Does not need to be separately discussed.
rotate_extrude() Operator Module
Rotational extrusion spins a 2D shape around the Z-axis to form a
solid which has rotational symmetry. One way to think of this
operation is to imagine a Potter's wheel placed on the X-Y plane with
its axis of rotation pointing up towards +Z. Then place the
to-be-made object on this virtual Potter's wheel (possibly extending
down below the X-Y plane towards -Z). The to-be-made object is the
cross-section of the object on the X-Y plane (keeping only the right
half, X >= 0). That is the 2D shape that will be fed to
rotate_extrude() as the child in order to generate this solid. Note
that the object started on the X-Y plane but is tilted up (rotated
+90 degrees about the X-axis) to extrude.
I'm not sure that this is the best possible explanation.
Since a 2D shape is rendered by OpenSCAD on the X-Y plane, an
alternative way to think of this operation is as follows: spins a 2D
shape around the Y-axis to form a solid. The resultant solid is
placed so that its axis of rotation lies along the Z-axis.
That's the way that I always think of it, though I mentally rotate it
to vertical before spinning it.
Just like the linear_extrude, the extrusion is always performed on
the projection of the 2D polygon to the XY plane.
Again, we should not document this behavior.
Transformations like rotate, translate, etc. applied to the 2D
polygon before extrusion modify the projection of the 2D polygon to
the XY plane and therefore also modify the appearance of the final 3D
object.
This is perhaps good stuff, if the part about projecting is removed.
Don't get confused, as OpenSCAD displays 2D polygons with a certain
height in the Z direction, so the 2D object (with its height) appears
to have a bigger projection to the XY plane. But for the projection
to the XY plane and also for the later extrusion only the base
polygon without height is used.
Once you get rid of the part about projecting this goes away too.
You cannot use rotate_extrude to produce a helix or screw thread.
Doing this properly can be difficult, so it's best to find a thread
library to make them for you.
This kind of comment can be valuable, but I'm not sure it belongs in a
reference manual. If it is in a reference manual, it should be in a
clear and separate section (of the description of the particular
feature) marked "Application Notes" or something like that, to make it
clear that it's not part of the description of the feature.
If the shape spans the X axis a warning appears in the console
windows and the rotate_extrude() is ignored.
Don't talk about what window something appears in, because not
everybody uses the OpenSCAD GUI.
Don't talk about what happens "after" the error.
Just say that it's an error, period.
(And, BTW, this particular one is something that I think should not be
an error; I think that the result should be as if you split the 2D
shape in half, rotationally extruded both, and unioned them. That is,
take the shape, sweep it 360 degrees, and anything it sweeps through
(whether once or twice) is part of the result.)
If the 2D shape touches the Y axis, i.e. at x=0, itmustbe a line
that touches, not a point, as a point results in a zero thickness 3D
object, which is invalid and results in a CGAL error.
This may have been addressed with Manifold.
*convexity* : If the extrusion fails for a non-trival 2D shape,
try setting the convexity parameter (the default is not 10, but
10 is a "good" value to try). See explanation further down.
Just point at the general discussion of convexity. (Which should not
be on this page.)
And the extrusion does not "fail". In fact, the artifacts may be
quite subtle.
*start*[Note:Requires versionDevelopment snapshot] : Defaults to
0 if*angle*is specified, and 180 if not. Specifies the starting
angle of the extrusion, counter-clockwise from the positive X axis.
start was part of an effort to align rotational extrusion behavior
with the behavior of other round things. I don't remember all of the
details, but there are few reasons why it isn't equivalent to rotating
the result.
*$fa* : minimum angle (in degrees) of each fragment.
*$fs* : minimum circumferential length of each fragment.
*$fn* :*fixed*number of fragments in 360 degrees. Values of 3 or
more override $fa and $fs
$fa, $fs and $fn must be named parameters.click here for more
details,
<https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features>.
Do not describe these in detail here. Refer to a general description
of them elsewhere.
Do not ever, ever, say "click here". Any text that would not make
sense when printed is wrong.
Rotate Extrude on Imported DXF
Delete.
Increasing the number of fragments composing the 2D shape improves
the quality of the mesh, but takes longer to render.
Unnecessary.
rotate_extrude(convexity = 10)
translate([2, 0, 0])
circle(r = 1, $fn = 100);
This example is unnecessary; this is a description of rotate_extrude,
not circle()
The number of fragments used by the extrusion can also be increased.
rotate_extrude(convexity = 10, $fn = 100)
translate([2, 0, 0])
circle(r = 1, $fn = 100);
Use $fs and $fa here. In fact, supply them at the top level. And this
case doesn't require specifying convexity (though I'm not sure why
not). Also, for circles this small high resolution is not practical;
make them bigger to make the example more real. Thus:
$fa = 1;
$fs = 1;
rotate_extrude()
translate([20, 0, 0])
circle(r = 10);
That's really the best practice. You almost never want to use $fn if
your intent is to create a circle.
(Minor exception that is itself something of a bug: if you're trying
to force the number of sides to be a multiple of 4. But that
shouldn't be discussed here.)
Using the parameter angle (with OpenSCAD versions 2016.xx), a hook
can be modeled .
https://en.wikibooks.org/wiki/File:Hook.pngOpenSCAD - a hook
eps = 0.01;
translate([eps, 60, 0])
rotate_extrude(angle=270, convexity=10)
translate([40, 0]) circle(10);
rotate_extrude(angle=90, convexity=10)
translate([20, 0]) circle(10);
translate([20, eps, 0])
rotate([90, 0, 0]) cylinder(r=10, h=80+eps);
Delete.
Extruding a Polygon
Delete.
Description of extrude parameters
Why are we repeating these here? Don't, especially because there is
little commonality between linear_extrude and rotate_extrude.
0% developed as of November 17, 2009
<https://en.wikibooks.org/wiki/Help:Development_stages>DXF
Extrusion
<https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/DXF_Extrusion>
Delete.
This should get more content. At the current state of things it can
probably all go on the 2D page, but if it gets much more complex it
might want its own page with a brief summary and reference here.
OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org
--
This email has been checked for viruses by AVG antivirus software.
www.avg.com
On 8/8/2025 4:36 AM, Jon Bondy wrote:
What is the community getting for this INCREDIBLE level of effort?
To be fair, a lot of the issues that I commented on were already there.
I didn't try to distinguish, since much of my goal was to give my
opinions on what did and did not make good reference material.
Perhaps, but there's value to the fact that making small corrections is
very light-weight.
Probably the biggest thing is to write something that gives guidelines
for writing the documentation, e.g. in no particular order:
The documentation certainly could use work, but the first requirement
for any change is that it's right.