OpenSCAD 用户手册/概述
OpenSCAD 是一个基于 2D/3D 和 实体建模 程序的 函数式编程 语言,用于创建 模型,这些模型会在屏幕上预览,并渲染成 3D 网格,允许模型以各种 2D/3D 文件格式导出。
OpenSCAD 语言中的脚本用于创建 2D 或 3D 模型。此脚本是一个自由格式的动作语句列表。
object(); variable = value; operator() action(); operator() { action(); action(); } operator() operator() { action(); action(); } operator() { operator() action(); operator() { action(); action(); } }
- 对象
- 对象是模型的构建块,由 2D 和 3D 原语创建。对象以分号 ';' 结束。
- 示例包括:cube()、sphere()、polygon()、circle() 等。
- 动作
- 动作语句包括使用原语创建对象和为变量赋值。动作语句也以分号 ';' 结束。
- 示例:a=1; b = a+7;
- 运算符
- 运算符或变换会修改对象的位置、颜色和其他属性。当运算符的作用域涵盖多个动作时,运算符使用大括号 '{}'。同一个动作或动作组可以使用多个运算符。多个运算符从右到左处理,即最靠近动作的运算符先处理。运算符不以分号 ';' 结束,但它们包含的单个动作会结束。
- 示例
cube(5); x = 4+y; rotate(40) square(5,10); translate([10,5]) { circle(5); square(4); } rotate(60) color("red") { circle(5); square(4); } color("blue") { translate([5,3,0]) sphere(5); rotate([45,0,45]) { cylinder(10); cube([5,6,7]); } }
注释是脚本或代码中留下笔记的一种方式(无论是给自己还是未来的程序员),描述代码的工作原理或作用。注释不会被编译器评估,不应该用于描述不言自明的代码。
OpenSCAD 使用 C++ 风格的注释
// This is a comment myvar = 10; // The rest of the line is a comment /* Multi-line comments can span multiple lines. */
OpenSCAD 中的值可以是数字(例如 42)、布尔值(例如 true)、字符串(例如 "foo")、范围(例如 [0: 1: 10])、向量(例如 [1,2,3])或未定义值(undef)。值可以存储在变量中,作为函数参数传递,并作为函数结果返回。
[OpenSCAD 是一种动态类型语言,具有固定的一组数据类型。没有类型名称,也没有用户定义的类型。]
数字是 OpenSCAD 中最重要的值类型,它们以其他语言中熟悉的十进制记数法编写。例如,-1、42、0.5、2.99792458e+8。[OpenSCAD 不支持数字的八进制或十六进制记数法。]
除了十进制数字,还定义了以下特殊数字的名称
PI
OpenSCAD 只有一个数字类型,即 64 位 IEEE 浮点数。OpenSCAD 不会将整数和浮点数区分成两种不同的类型,也不支持复数。由于 OpenSCAD 使用 IEEE 浮点数标准,因此数字在数学中的行为存在一些偏差
- 我们使用二进制浮点数。除非分母是 2 的幂,否则小数不能完全表示。例如,0.2 (2/10) 没有精确的内部表示,但 0.25 (1/4) 和 0.125 (1/8) 是精确表示的。
- 可表示的最大数字约为 1e308。如果数字结果太大,则结果可以是无穷大(通过 echo 打印为 inf)。
- 可表示的最小数字约为 -1e308。如果数字结果太小,则结果可以是负无穷大(通过 echo 打印为 -inf)。
- 如果数字结果无效,则结果可以是 Not A Number(通过 echo 打印为 nan)。
- 如果非零数字结果太接近于零而无法表示,则如果结果为负,则结果为 -0,否则结果为 0。零 (0) 和负零 (-0) 被一些数学运算视为两个不同的数字,并且通过 'echo' 打印时也不同,尽管它们比较为相等。
常量 inf
和 nan
不被 OpenSCAD 作为数字常量支持,即使你可以计算以这种方式由 'echo' 打印的数字。你可以使用以下方法定义具有这些值的变量
inf = 1e200 * 1e200; nan = 0 / 0; echo(inf,nan);
值 nan
是 OpenSCAD 中唯一不等于任何其他值的值,包括它本身。虽然你可以使用 'x == undef' 测试变量 'x' 是否具有未定义值,但你不能使用 'x == 0/0' 来测试 x 是否为 Not A Number。相反,你必须使用 'x != x' 来测试 x 是否为 nan。
布尔值是具有两种状态的变量,通常在 OpenSCAD 中表示为 true 和 false。布尔变量通常由条件测试生成,并由条件语句 'if()' 使用。条件运算符 '? :',并由逻辑运算符 !
(非)、&&
(与) 和 ||
(或) 生成。诸如 if()
之类的语句实际上接受非布尔变量,但大多数值在布尔上下文中被转换为 true
。以下值被视为 false
false
0
和-0
""
[]
undef
请注意,"false"
(字符串)、[0]
(数字向量)、[ [] ]
(包含空向量的向量)、[false]
(包含布尔值 false 的向量)和 0/0
(不是数字)都算作 true。
字符串是零个或多个 Unicode 字符的序列。字符串值用于在导入文件时指定文件名,以及在使用 echo() 时显示文本以进行调试。字符串也可以与 text() 原语 一起使用,该原语是在版本 2015.03 中添加的。
字符串文字被写成用引号 "
括起来的字符序列,例如:""
(空字符串)或 "this is a string"
。
要在字符串文字中包含 "
字符,请使用 \"
。要在字符串文字中包含 \
字符,请使用 \\
。以下以 \
开头的转义序列可以在字符串文字中使用
- \" → "
- \\ → \
- \t → 制表符
- \n → 换行符
- \r → 回车符
- \x21 → ! - 仅在 \x01 到 \x7f 的范围内有效,\x00 生成空格
- \u03a9 → Ω - 4 位 Unicode 代码点,有关 Unicode 字符的更多信息,请参见 text()
- \U01f600 → 😀 - 6 位 Unicode 代码点
此行为自 OpenSCAD-2011.04 以来是新的。你可以使用以下 sed 命令升级旧文件:sed 's/\\/\\\\/g' non-escaped.scad > escaped.scad
示例
echo("The quick brown fox \tjumps \"over\" the lazy dog.\rThe quick brown fox.\nThe \\lazy\\ dog."); result
ECHO: "The quick brown fox jumps "over" the lazy dog. The quick brown fox. The \lazy\ dog." old result ECHO: "The quick brown fox \tjumps \"over\" the lazy dog. The quick brown fox.\nThe \\lazy\\ dog."
范围由 for() 循环 和 children() 使用。它们有 2 种变体
- [<start>:<end>]
- [<start>:<increment>:<end>]
尽管用方括号 [] 括起来,它们不是向量。它们使用冒号 : 作为分隔符,而不是逗号。
r1 = [0:10]; r2 = [0.5:2.5:20]; echo(r1); // ECHO: [0: 1: 10] echo(r2); // ECHO: [0.5: 2.5: 20]
您应该避免使用无法精确表示为二进制浮点数的步长值。整数是可以的,分数值的分母为 2 的幂也可以。例如,0.25 (1/4) 和 0.125 (1/8) 是安全的,但 0.2 (2/10) 应该避免。这些步长值的问题在于,由于算术不精确,您的范围可能会有太多或太少的元素。
缺少的 <increment> 默认值为 1。形式为 [<start>:<end>] 的范围,其中 <start> 大于 <end> 会生成一个警告,并且等效于 [<end>: 1: <start>]。形式为 [<start>:1:<end>] 的范围,其中 <start> 大于 <end> 不会生成警告,并且等效于 []。范围内的 <increment> 可以为负数(对于 2014 年后的版本)。
未定义的值是一个特殊的值,写为 undef
。它是未分配值的变量的初始值,并且通常由传递非法参数的函数或操作返回。最后,undef
可以用作空值,等效于其他编程语言中的 null
或 NULL
。
所有包含 undef
值的算术表达式都计算为 undef
。在逻辑表达式中,undef
等效于 false
。带有 undef
的关系运算符表达式计算为 false
,除了 undef==undef
为 true
。
请注意,数值运算也可能返回 'nan'(非数字)以指示非法参数。例如,0/false
为 undef
,但 0/0
为 'nan'。关系运算符(如 < 和 >)如果传递非法参数则返回 false
。虽然 undef
是一个语言值,但 'nan' 不是。
OpenSCAD 变量通过带有名称或 标识符 的语句创建,通过表达式进行赋值,并以分号结尾。OpenSCAD 中处理数组(在许多命令式语言中找到)的角色由向量处理。目前有效的标识符只能由简单字符和下划线 [a-zA-Z0-9_] 组成,并且不允许高 ASCII 或 Unicode 字符。
var = 25; xx = 1.25 * cos(50); y = 2*xx+var; logic = true; MyString = "This is a string"; a_vector = [1,2,3]; rr = a_vector[2]; // member of vector range1 = [-1.5:0.5:3]; // for() loop range xx = [0:5]; // alternate for() loop range
OpenSCAD 是一种 函数式 编程语言,因此 变量 绑定到表达式,并且由于 引用透明度 的要求,在其整个生命周期内保持单个值。在 命令式语言(如 C)中,相同行为被视为常量,通常与普通变量形成对比。
换句话说,OpenSCAD 变量更像是常量,但有一个重要的区别。如果变量被多次分配值,则只有最后分配的值在代码中的所有地方使用。请参阅 变量在编译时设置,而不是在运行时设置 的进一步讨论。这种行为是由于需要在 命令行 上提供变量输入,通过使用 -D variable=value 选项。OpenSCAD 目前将该赋值放在源代码的末尾,因此必须允许变量的值为此目的而改变。
值在运行时不能修改;所有变量实际上都是不会改变的常量。每个变量在编译时保留其最后分配的值,这与 函数式 编程语言一致。与 命令式 语言(如 C)不同,OpenSCAD 不是一种迭代语言,因此 x = x + 1 的概念是无效的。理解这个概念就能理解 OpenSCAD 的美妙之处。
在 2015.03 版本之前,除了文件顶层和模块顶层之外,无法在任何地方进行赋值。在 if/else 或 for 循环内,需要使用 assign()。
从 2015.03 版本开始,现在可以在任何范围内分配变量。请注意,赋值仅在定义的范围内有效——您仍然不允许将值泄漏到外部范围。有关更多详细信息,请参阅 变量的范围。
a=0; if (a==0) { a=1; // before 2015.03 this line would generate a Compile Error // since 2015.03 no longer an error, but the value a=1 is confined to within the braces {} }
未赋值的变量具有特殊值 undef。它可以在条件表达式中进行测试,并由函数返回。
Example echo("Variable a is ", a); // Variable a is undef if (a==undef) { echo("Variable a is tested undefined"); // Variable a is tested undefined }
当 translate() 和 color() 等运算符需要包含多个操作(操作以 ; 结尾)时,需要使用大括号 {} 来分组操作,从而创建一个新的内部范围。当只有一个分号时,大括号通常是可选的。
每对大括号都在使用它们的大括号范围内创建一个新范围。从 2015.03 版本开始,可以在此新范围内创建新变量。可以为在外部范围内创建的变量赋予新值。这些变量及其值也适用于在此范围内创建的进一步内部范围,但对该范围之外的任何内容不可用。变量在范围内仍然只具有最后分配的值。
// scope 1 a = 6; // create a echo(a,b); // 6, undef translate([5,0,0]){ // scope 1.1 a= 10; b= 16; // create b echo(a,b); // 100, 16 a=10; was overridden by later a=100; color("blue") { // scope 1.1.1 echo(a,b); // 100, 20 cube(); b=20; } // back to 1.1 echo(a,b); // 100, 16 a=100; // override a in 1.1 } // back to 1 echo(a,b); // 6, undef color("red"){ // scope 1.2 cube(); echo(a,b); // 6, undef } // back to 1 echo(a,b); // 6, undef //In this example, scopes 1 and 1.1 are outer scopes to 1.1.1 but 1.2 is not.
- 匿名范围不被视为范围
{ angle = 45; } rotate(angle) square(10);
For() 循环不是关于变量在一个范围内只具有一个值的规则的例外。循环内容的副本将为每次传递创建。每次传递都拥有自己的范围,允许任何变量在该传递中具有唯一的值。不,您仍然不能做 a=a+1;
由于 OpenSCAD 在编译时而不是在运行时计算其变量值,因此一个范围内最后的变量赋值适用于该范围或其内部范围中的所有地方。将它们视为可覆盖的常量而不是变量可能会有所帮助。
// The value of 'a' reflects only the last set value a = 0; echo(a); // 5 a = 3; echo(a); // 5 a = 5;
虽然这似乎违反直觉,但它允许你做一些有趣的事情:例如,如果你将共享库文件设置为在其根级别定义默认值作为变量,当你将该文件包含在自己的代码中时,你可以简单地为它们分配一个新值来“重新定义”或覆盖这些常量。因此,更改常量值可以让你更灵活。如果常量永远不会改变,当然,你可以始终确定拥有你在任何常量定义中看到的那个值。这里并非如此。如果你在其他地方看到一个常量值定义,它的值可能不同。这非常灵活。
前面的描述似乎与 OpenSCAD 在 2022 年 5 月 23 日的行为不同。在那个日期,运行上面的示例会导致以下输出
特殊变量提供了传递参数给模块和函数的另一种方法。所有以 '$' 开头的变量都是特殊变量,类似于 lisp 中的特殊变量。因此,它们比普通变量更动态。(有关更多详细信息,请参阅 其他语言特性)
向量或列表是零个或多个 OpenSCAD 值的序列。向量是数字或布尔值、变量、向量、字符串或它们的任意组合的集合。它们也可以是表达式,这些表达式计算为其中之一。向量处理在许多命令式语言中找到的数组的角色。此处的 信息也适用于使用向量作为其数据的列表和表格。
向量具有方括号 [],其中包含零个或多个项目(元素或成员),用逗号分隔。向量可以包含向量,向量可以包含向量,等等。
示例
[1,2,3] [a,5,b] [] [5.643] ["a","b","string"] [[1,r],[x,y,z,4,5]] [3, 5, [6,7], [[8,9],[10,[11,12],13], c, "string"] [4/3, 6*1.5, cos(60)]
在 OpenSCAD 中使用
cube( [width,depth,height] ); // optional spaces shown for clarity translate( [x,y,z] ) polygon( [ [x0,y0], [x1,y1], [x2,y2] ] );
向量通过用逗号分隔的元素列表创建,并用方括号括起来。变量将被其值替换。
cube([10,15,20]); a1 = [1,2,3]; a2 = [4,5]; a3 = [6,7,8,9]; b = [a1,a2,a3]; // [ [1,2,3], [4,5], [6,7,8,9] ] note increased nesting depth
向量可以使用包含在方括号中的 for 循环进行初始化。
以下示例用长度为 n 的 10 个值为 a 的值初始化向量 result。
n = 10
a = 0;
result = [ for (i=[0:n-1]) a ];
echo(result); //ECHO: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
以下示例展示了一个长度为 n 的 10 个值的向量 result,它使用交替的 a 或 b 值初始化,如果索引位置 i 是偶数或奇数,则分别使用 a 或 b 值。
n = 10
a = 0;
b = 1;
result = [ for (i=[0:n-1]) (i % 2 == 0) ? a : b ];
echo(result); //ECHO: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
向量内的元素从 0 到 n-1 编号,其中 n 是由 len() 返回的长度。使用以下符号访问向量内的元素
e[5] // element no 5 (sixth) at 1st nesting level e[5][2] // element 2 of element 5 2nd nesting level e[5][2][0] // element 0 of 2 of 5 3rd nesting level e[5][2][0][1] // element 1 of 0 of 2 of 5 4th nesting level
e = [ [1], [], [3,4,5], "string", "x", [[10,11],[12,13,14],[[15,16],[17]]] ]; // length 6 address length element e[0] 1 [1] e[1] 0 [] e[5] 3 [ [10,11], [12,13,14], [[15,16],[17]] ] e[5][1] 3 [ 12, 13, 14 ] e[5][2] 2 [ [15,16], [17] ] e[5][2][0] 2 [ 15, 16 ] e[5][2][0][1] undef 16 e[3] 6 "string" e[3 ][2] 1 "r" s = [2,0,5]; a = 2; s[a] undef 5 e[s[a]] 3 [ [10,11], [12,13,14], [[15,16],[17]] ]
可以访问字符串的元素(字符)
"string"[2] //resolves to "r"
可以使用备选的点表示法访问向量的前三个元素
e.x //equivalent to e[0] e.y //equivalent to e[1] e.z //equivalent to e[2]
[注意: 需要版本 2015.03]
concat()
将 2 个或多个向量的元素组合成一个单一向量。不会更改嵌套级别。
vector1 = [1,2,3]; vector2 = [4]; vector3 = [5,6]; new_vector = concat(vector1, vector2, vector3); // [1,2,3,4,5,6] string_vector = concat("abc","def"); // ["abc", "def"] one_string = str(string_vector[0],string_vector[1]); // "abcdef"
len()
是一个返回向量或字符串长度的函数。元素的索引从 [0] 到 [length-1]。
- 向量
- 返回此级别的元素数量。
- 单个值,即不是向量,会引发错误。
- 字符串
- 返回字符串中的字符数量。
a = [1,2,3]; echo(len(a)); // 3
矩阵是向量组成的向量。
Example that defines a 2D rotation matrix mr = [ [cos(angle), -sin(angle)], [sin(angle), cos(angle)] ];
没有从键盘获取变量输入或从任意文件读取的机制。没有提示机制、输入窗口、输入字段,也没有任何方法可以在脚本运行时手动输入数据。
数据只能在脚本开始时设置为常量,并通过访问少数文件格式(如 stl、dxf、png 等)中的数据来设置。
除了 DXF 文件之外,脚本无法访问这些数据,尽管脚本可以在有限程度上将数据作为一个整体进行操作。例如,STL 文件可以在 OpenSCAD 中渲染、平移、裁剪等。但是构成 STL 文件的内部数据是无法访问的。
现在我们有了变量,如果能够将输入放入它们中,而不是从代码中设置值,那就太好了。有一些函数可以从 DXF 文件中读取数据,或者你可以在命令行上使用 -D 开关设置变量。
获取点对于在技术图纸的二维视图中读取原点很有用。函数 dxf_cross 读取你在指定图层上的两条线的交点并返回交点。这意味着该点必须使用 DXF 文件中的两条线给出,而不是点实体。
OriginPoint = dxf_cross(file="drawing.dxf", layer="SCAD.Origin",
origin=[0, 0], scale=1);
你可以从技术图纸中读取尺寸。这对于读取旋转角度、拉伸高度或零件之间的间距很有用。在图纸中,创建一个不显示尺寸值而是显示标识符的尺寸。要读取该值,你需要在程序中指定该标识符
TotalWidth = dxf_dim(file="drawing.dxf", name="TotalWidth",
layer="SCAD.Origin", origin=[0, 0], scale=1);
有关这两个函数的很好示例,请参阅 Example009 以及 OpenSCAD 主页 上的图像。