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(); } }
- 对象
对象(object)是模型的基本组成单元,由2D与3D图元(primitive)创建而成。对象以分号';'结尾。
- 动作
动作(action)语句包括利用图元创建对象以及为变量赋值。动作语句同样以分号';'作为结尾。
- 运算符
运算符(operator)或称变换(transformation),用于修改对象的位置、颜色以及其他属性。当运算符的工作域中存在一个以上的动作,就要用大括号'{}'将其圈定起来。多个运算符可执行相同的动作,也可构成一组不同的动作。多个运算符按由右至左的顺序处理,即,离动作最近的运算符率先执行。运算符结尾不加分号';',但是不同的动作间要加分号。
示例 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++-风格的注释
// 这是一行注释 myvar = 10; // 本行的后续部分是一条注释 /* 多行注释 可跨多行 */
数值与数据类型
编辑OpenSCAD中,一个数值的类型可能是一个数Number (如42), 一个布尔值Boolean (如true), 一个字符串String (如"foo"), 一个范围Range (如[0: 1: 10]), 一个向量Vector (如[1,2,3]), 或未定义值Undefined value (undef)。值可存于变量之中,随之作为参数传入函数,再以函数计算结果返回。
[OpenSCAD是一种有着固定数据类型集的动态类型语言。不存在类型名,也没有用户定义类型。Functions are not values. 事实上,变量与函数各占据着无交集的命名空间。]
数
编辑数(number)是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。如果计算的数值结果过大,那么将视之为正无穷(打印的反馈结果为inf)。
- 可表示的最小数约为-1e308。如果计算的数值结果过小,那么将视之为负无穷(打印的反馈结果为-inf)。
- 如果数值的计算结果无效,那么其结果可能是非数值(打印的反馈结果为nan)
- 如果一个非零的计算数值结果过于接近0而无法表示,那么当结果为负值时其值为-0,否则其值为0。在一些数学计算中,零(0)与负零(-0)是两种截然不同的数。而且尽管两者比较起来相等,但是打印的反馈结果却不相同。
请注意,尽管能够通过计算数值从打印的反馈结果中看到'inf'与'nan',但是,在OpenSCAD中并不支持这两种数值常量。您可以按以下方式,用这些数值来定义变量:
inf = 1e200 * 1e200; nan = 0 / 0; echo(inf,nan);
请注意,'nan'是OpenSCAD中唯一一种不等于其他任意值的值(包括它自己在内)。尽管您可以通过'x == undef'来检测变量'x'是否为未定义值,但是却不能利用'x == 0/0'来检测x是否并非一个数。对此,您必须要通过'x != x'来检测x是否为一个nan。
布林值
编辑布林值皆为真值。OpenSCAD中支持两种布林值,分别名为true
与false
。
一个布林值能以参数的形式传入条件语句'if()'、条件运算符'? :'以及逻辑运算符'!'(非)、'&&'(与)、'||'(或)。事实上,在上述所有的结构中,您都可以传入任意量值。大多数数值都会转换为布林值下的'true',而将值视作'false'的情况有:
- false
- 0 and -0
- ""
- []
- undef
请注意,"false"
(此字符串), [0]
(一个数值向量),
[ [] ]
(内含一个空向量的向量), [false]
(内含布林值false的向量)与0/0 (并非一个数)都计作true。
字符串
编辑一个字符串为0个或多个unicode字符所构成的序列。字符串值常用于在导入文件时确定文件名,以及为调试目的而在使用echo()时显示文本。字符串亦可与2015.03版加入的新式text()图元配合使用。
通过双引号"
围起字符序列来编写一个字符串字面值, 就像这样: ""
(一个空字符串), 或者"this is a string"
.
为了将"
字符包括在字符串字面值里,要这样写\"
。为了将\
字符包括在字符串字面值里, 要这样写\\
。下列以\
开头的转义序列即可用在字符串字面值里:
- \" → "
- \\ → \
- \t → 水平制表符(tab)
- \n → 换行(newline)
- \r → 回车(carriage return)
- \u03a9 → Ω - 关于unicode字符的进一步信息请参见text()
请注意: 这是自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."); 结果
ECHO: "The quick brown fox jumps "over" the lazy dog. The quick brown fox. The \lazy\ dog." 舊结果 ECHO: "The quick brown fox \tjumps \"over\" the lazy dog. The quick brown fox.\nThe \\lazy\\ dog."
范围
编辑范围(Range)常用于for()循环与children()。它们有两种变体形式:
- [<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]
您应当避免采用不能被表示为精确二进制浮点数的步长值(step)。使用整数没有问题,因为其分数中分母部分都为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>值,这将生成一个警告,而实际逻辑则等价于[]。对于OpenSCAD 2014之后的版本而言,范围中的<increment>可取负值。
未定义值
编辑未定义值是一种记作undef的特殊值。它是未被赋值的变量的初始值,常用作传有非法参数的函数或运算所返回的结果。最后要提到的是,可将undef
用作一个空(null)值,这等价于其他编程语言中的null
或NULL
。
一切含有undef
值的算数表达式的运算结果皆为undef
。在逻辑表达式中,undef
等价于false
。除了undef==undef
的计算结果为true
以外,其他有undef
参与的关系运算符表达的计算结果皆为false
。
请注意,数值计算也可能会返回'nan' (并非一个数值) 来表示存在非法参数。例如,0/false
为undef
, 而0/0
为'nan'。如果向< 与 >等关系运算符传入非法参数,将返回false
。尽管undef
是OpenSCAD语言中定义的值,但'nan'却不是。
变量
编辑利用带有变量名或标识符的语句来创建OpenSCAD变量, 通过以分号为结尾的表达式来为之赋值。在许多命令式语言中数组所担当的角色,在OpenSCAD中由向量来扮演。
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]; // 向量中的成员 range1 = [-1.5:0.5:3]; // for()循环范围 xx = [0:5]; // 替代for()循环范围
OpenSCAD是一种函数式编程语言,因此变量都与表达式相绑定,且在整个生命周期中保持为一个恒定的值,这是因为它需要满足引用透明性这项需求。在类似于C这样的命令式编程语言中,上述变量行为就如同常量一般,与其中的普通变量形成鲜明对比。
换言之,OpenSCAD中的变量更像是常量,但仍有一点重要的不同之处。如果多次为变量赋值,则在整个代码中此变量仅存有最后一次所赋的值。这一点可参见进一步的讨论设置变量发生在编译时,而非运行时。此行为的原因是需要通过使用-D variable=value选项,才能在命令行中输入变量。OpenSCAD当前将赋值过程置于源代码的结尾处,这样,一定可使变量的值以上述方式发生改变。
在编译期变量保留其最后一次赋值结果,这与函数式编程语言的行为相符。与C这样的命令式编程语言不同,OpenSCAD并不是一种支持迭代的语言,像x = x + 1这样的语句是不合法的,如果理解了这个概念,您将领略OpenSCAD之美。
- 2015.03之前的版本
除文件顶层(file top-level)与模块顶层(module top-level)之外,不能在任意作用域内进行赋值。在if/else语句或for循环语句中,需要使用assign()。
- 自2015.03版开始
可在任何作用域内进行赋值。请注意,只有在定义变量的作用域内对其进行赋值才是有效的——您仍然不能将数值“泄露”到外层的作用域中。参见变量的作用域来获得更多详情。
a=0; if (a==0) { a=1; // 在2015.03版之前,本行将造成一条编译错误 // 自2015.03版开始,将不再出现错误信息,但是值a=1却被限制在括号{}之内 }
未定义变量
编辑未赋值变量具有一种特殊值undef。 此值可作为条件语句的测试对象,且可由函数作为返回值。
示例 echo("Variable a is ", a); // Variable a is undef (变量a为undef) if (a==undef) { echo("Variable a is tested undefined"); // Variable a is tested undefined (变量a是被测试的未定义变量) }
变量的作用域
编辑当如translate()与color()这样的运算符需要囊括一个以上的动作时(动作以;结尾), 就要用大括号{}来将动作打包成组,创建一个新的内部作用域。 如果组中仅有一个分号,那么大括号可有可无。
每对大括号都在其所在的作用域中创建了一个新的作用域。自2015.03版开始, 可将新变量创建在此新作用域之内。另外,还可以为外部域中创建的变量指定新值。 这些变量及其值可存在于创建它们的作用域的内部作用域中,但是却不存在于创建它们的作用域的外部域中。变量仍然仅保留作用域中最后一次所赋的值。
// 作用域1 a = 6; // 创建变量a echo(a,b); // 6, undef translate([5,0,0]){ // 作用域1.1 a= 10; b= 16; // 创建变量b echo(a,b); // 100, 16 a=10;在后面被a=100;覆盖 color("blue") { // 作用域1.1.1 echo(a,b); // 100, 20 cube(); b=20; } // 返回作用域1,1 echo(a,b); // 100, 16 a=100; // 在作用域1.1中覆盖a } // 返回作用域1 echo(a,b); // 6, undef color("red"){ // 作用域1.2 cube(); echo(a,b); // 6, undef } // 返回作用域1 echo(a,b); // 6, undef // 在本例中,作用域1与作用域1.1都为作用域1.1.1的外部域,而作用域1.2却不是。
- 不将匿名作用域看作是一种作用域:
{ angle = 45; } rotate(angle) square(10);
对于上述变量规则而言,For()循环也不例外,其中的变量各仅有一个值。OpenSCAD会为每趟循环中的内容创建一个副本。因而每趟循环中都有其自己的作用域,以此令对应循环过程中的变量有其唯一值。所以,您仍然不能使用a=a+1;语句。
设置变量发生在编译时,而非运行时
编辑由于OpenSCAD在编译时计算其变量值,而非运行时,因此,只有最后一次变量赋值才会在其所在作用域或其内部域中生效。这样想也许会更有助于理解:即OpenSCAD中的变量是一种可重写(override-able)的常量而非传统意义上的“变量”。
// 'a'的值反映了仅最后一次的赋值才会生效 a = 0; echo(a); // 5 a = 3; echo(a); // 5 a = 5;
上述示例表现出了一种“反直觉”的执行效果,这使您可以做一些有趣的事情:例如,如果您建立了自己的共享库文件,并在其顶层定义了具有默认值的变量,当您将此文件用于自己的项目中时,就可以简单地通过为之赋予新值的方法来“重新定义”或覆盖(override)那些常量。
特殊变量
编辑特殊变量提供了另一种向模块或函数传递参数的方式。 所有以一个'$'开头的变量均为特殊变量,这与lisp中的特殊变量很相似。 特殊变量较普通变量而言更为灵活(dynamic动态)。 (对于更多细节可参考其他语言特性)
向量
编辑向量是由0或更多的OpenSCAD值按序构成的。因此,向量实为一种由数字、布尔值、变量、向量、字符串或它们之间任意组合而成的集合(或称列表或表)。They can also be expressions which evaluate to one of these. 向量在OpenSCAD中扮演的角色就相当于大多命令式语言中的数组。 The information here also applies to lists and tables which use vectors for their data.
向量的表示是由中括号(方括号)[]围起的0或多个项(或称元素或成员),其间由逗号隔开。向量也能以向量作为元素,而向量元素中还可以包含更多的向量,诸如此类。
- 示例
[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] ); // 空格可选,这里为方便阅读而设 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] ] 请注意,这里增加了嵌套的深度(层数)
- 向量中的元素
向量中的元素从0至n-1进行编号,其中的n是len()函数返回的向量长度值。 通过下列方式来定位向量中的元素:
e[5] // 元素5 (第6个元素) 第1层嵌套 e[5][2] // 元素5中的第2个元素 第2层嵌套 e[5][2][0] // 元素5中的第2个元素中的第0个元素 第3层嵌套 e[5][2][0][1] // 元素5中的第2个元素中的第0个元素的第1个元素 第4层嵌套
e = [ [1], [], [3,4,5], "string", "x", [[10,11],[12,13,14],[[15,16],[17]]] ]; // e的长度为6 地址 长度 元素 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]] ]
- 另一种点表示法
向量中的前三个元素可用另一种点表示法进行访问:
e.x // 等价于e[0] e.y // 等价于e[1] e.z // 等价于e[2]
向量操作符
编辑concat
编辑[请注意: 需要使用版本 2015.03]
concat()将两个或两个以上向量中的元素合并至单个向量。同时并不改变原向量的嵌套层次。
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
编辑len()是一个返回向量或字符串长度的函数。 元素的索引值范围为由[0]至[length-1]。
- 向量
- 返回此层级的元素数量。
- 单值并非向量, 此时将返回undef.
- 字符串
- 返回字符串中的字符数量。
a = [1,2,3]; echo(len(a)); // 3
矩阵
编辑向量中的元素为向量是谓矩阵。
定义了一个2D旋转矩阵的示例 mr = [ [cos(angle), -sin(angle)], [sin(angle), cos(angle)] ];
获取输入
编辑我们现在已掌握了变量,可以用它方便地获取用户的输入,而不仅从代码中对其进行设置。OpenSCAD中提供了一些函数用于从DXF文件中国读取数据,或者您可以在命令行中通过-D开关来设置变量。
从图纸中获取一点
获取点功能便于您从技术制图中的2D视图内获取一原始点数据。dxf_cross函数将读取并返回某层中您指定的两条线的交点。这意味着必须指定DXF文件中的两条线才能获取相应交点,而并非指定对应的点实体。
OriginPoint = dxf_cross(file="drawing.dxf", layer="SCAD.Origin",
origin=[0, 0], scale=1);
获取标注值
您可以从技术制图中读取其标注。这对于读取旋转角、挤压(extrusion)高度或零件间的空间是很有用处的。在图纸中,所创建的标注并不能反映其标注值,而仅是一种标识符。为读取此值,您要在自己的程序中指定此标识符:
TotalWidth = dxf_dim(file="drawing.dxf", name="TotalWidth",
layer="SCAD.Origin", origin=[0, 0], scale=1);
对于这两个函数的更佳示例,可参考Example009,其效果图位于OpenSCAD主页。