Thank you, Jordan
Jon
On 8/8/2025 8:30 AM, Jordan Brown wrote:
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.
By the way, it's entirely appropriate to use github issues to report
documentation issues, even though we don't use github to manage the
documentation.
Perhaps I can frame this differently.
Why would we want to exercise [rigid?] control over the software source code, and exercise no [explicit] control over the documentation?
What is the appropriate level of documentation control, and how should it be managed?
Jon
On 8/8/2025 8:30 AM, Jordan Brown wrote:
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.
Why would we want to exercise [rigid?] control over the software
source code, and exercise no [explicit] control over the documentation?
Because the blast radius is larger when mistakes are made modifying the
source code.
If you make a typo in the documentation, it's ... a typo. If you make a
typo in the source, the program doesn't build, or crashes, or quietly
misbehaves.
If you add a feature to the program that we later regret, it's hard to
back it out because people may have come to depend on it. It isn't
possible to depend on an erroneous comment in documentation.
What is the appropriate level of documentation control, and how should
it be managed?
That is a very good question.
I suspect that one aspect of the answer is that our source gatekeepers
(basically, Torsten and Marius; there are a few other authorized people
but they are rarely active) are fully loaded, and I don't think they
want to take over as documentation gatekeepers too. We'd need several
documentation gatekeepers.
Another question that I don't know the answer to is what level of
control Wikibooks lets us have.
If it ain't broke, work on until it is.😉
The attraction of openscad is it's relatively small set of functions.
I've done everything I need with raw openscad (with the help from a few
folk on here). Bosl2 is there for those who want an easier answer. It
seems that some folk are wanting it to do things that were never
designed into openscad functionality. For those folk, learn fusion,
blender, whatever.
The terminology in parts is a tad incorrect, cubes are more than
squares, cylinders are more than cylinders, and you will never see a
true circle on any digital display. Also, the language is initially
awkward, and the solids can often have unwanted holes, but if you spend
the time and effort you get used to it.
My comment is related to openscad as a whole, not specific to 2d, but as
nothing in the real world is 2d...
Best wishes,
Ray
On 08/08/2025 12:36, Jon Bondy via Discuss wrote:
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
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
OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org
On 8/8/2025 1:08 AM, Jordan Brown via Discuss wrote:
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.
Confirmed working in a build today on Windows. If it doesn't work, it's
a bug.
Jon Bondy wrote:
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.
Terrifying? Really? it is a wiki with version control. How can anything i might do be terrifying?
I might misunderstand a thing and write something misleading .. which someone would eventually notice and fix .. that is the absolute worst situation that i could create.
Repeating … it is a wiki with version control .. just revert my work out and your docs are back under your control, ‘cause i will never contribute again.
and given the communities difficulty with a newcomer trying to contribute maybe that is what i should do
I don't doubt that Vulcan's intentions are good, but I wonder if there
really is a problem that needs solving.
There were problems .. lots of them. When i started exploring scad i found that pretty much every module and function I wanted to use was under documented, even confusing in places .. I am a good tech writer and i like to contribute where i can .. so once my examples and test scripts showed me how to improve things in the docs i thought to contribute my insights back to the community
I also wonder whether the
community would be better off with an experienced OpenSCAD programmer
making the changes.
that would be the community’s choice to make .. but my impression is that the experienced OpenSCAD programmers are quite busy with new features and maintenance so documentation is well down the priority list. But okay, if newcomers are not welcome then lock down the permissions so only the Chosen Few can update the docs and again, the issue is solved.
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.
As stated .. all i need is to be able to READ the docs .. i don’t need to contribute, and i don’t need to be on the wrong side of a turf war
There are other tool sets i can do my 3D printing with
Jon Bondy wrote:
What is the community getting for this INCREDIBLE level of effort?
I think we should
good .. you do that .. i am out
Jordan, thanks for your patient help and feedback .. i have learned a lot about functional language programming .. a sort of programming that i knew nothing about before coming to OpenSCAD
—
Jeff Hayes
I did not find the documentation to be confusing.
Experienced OpenSCAD programmers are spending their time responding and
reacting to the content you are creating. I am not convinced that this
is the best use of their time.
This is not a turf war. This is about how to get quality documentation
with the minimum of effort from all involved.
Perhaps others should comment. I have had my say. If I am all alone,
then I will not comment further.
Jon
On 8/8/2025 9:02 PM, vulcan_--- via Discuss wrote:
Jon Bondy wrote:
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.
Terrifying? Really? it is a wiki with version control. How can
anything i might do be terrifying?
I might misunderstand a thing and write something misleading .. which
someone would eventually notice and fix .. that is the absolute worst
situation that i could create.
Repeating … it /is a wiki with version control/ .. just revert my work
out and your docs are back under your control, ‘cause i will never
contribute again.
and given the communities difficulty with a newcomer trying to
contribute maybe that is what i should do
I don't doubt that Vulcan's intentions are good, but I wonder if
there really is a problem that needs solving.
There were problems .. lots of them. When i started exploring scad i
found that pretty much every module and function I wanted to use was
under documented, even confusing in places .. I am a good tech writer
and i like to contribute where i can .. so once my examples and test
scripts showed me how to improve things in the docs i thought to
contribute my insights back to the community
I also wonder whether the community would be better off with an
experienced OpenSCAD programmer making the changes.
that would be the community’s choice to make .. but my impression is
that the experienced OpenSCAD programmers are quite busy with new
features and maintenance so documentation is well down the priority
list. But okay, if newcomers are not welcome then lock down the
permissions so only the Chosen Few can update the docs and again, the
issue is solved.
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.
As stated .. all i /need/ is to be able to READ the docs .. i don’t
/need/ to contribute, and i don’t /need/ to be on the wrong side of a
turf war
There are other tool sets i can do my 3D printing with
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