OpenSCAD 用户手册/OpenSCAD 语言
OpenSCAD 用户手册/OpenSCAD 语言
OpenSCAD 是一款基于 函数式编程 语言 的 二维/三维 以及 实体建模 程序,用于创建可在屏幕上预览并渲染为三维 网格 的 模型,从而允许模型以各种二维/三维文件格式导出。
OpenSCAD 语言中的脚本用于创建二维或三维模型。此脚本是一个自由格式的行动语句列表。
object(); variable = value; operator() action(); operator() { action(); action(); } operator() operator() { action(); action(); } operator() { operator() action(); operator() { action(); action(); } }
- 对象
- 对象是模型的构建块,由二维和三维基本体创建。对象以分号 ';' 结尾。
- 示例: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' 是否是 NaN。相反,必须使用 '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;
虽然这看起来违反直觉,但它允许你做一些有趣的事情:例如,如果你将共享库文件设置为在根级别定义变量作为默认值,当你将该文件包含在自己的代码中时,你可以通过简单地为它们赋值来“重新定义”或覆盖这些常量。因此,改变常量值可以让你更加灵活。如果常量永远不会改变,当然,你总是可以确定拥有你在任何常量定义中看到的那个值。这里不是这样。如果你在任何其他地方看到一个常量值的定义,它的值可能是不同的。这是非常灵活的。
前面的描述似乎与截至 2022 年 5 月 23 日的 OpenSCAD 行为有所不同。在那个日期,运行上面的例子会导致以下输出
警告:a 在 “Untitled” 文件的第 1 行被赋值,但在 “Untitled” 文件的第 3 行被覆盖。执行已中止
特殊变量提供了一种将参数传递给模块和函数的替代方法。所有以“$”开头的变量都是特殊变量,类似于 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 个值将 result 向量初始化为 a 的值。
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 是偶数或奇数。
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);
有关这两个函数的良好示例,请参阅示例 009 和OpenSCAD 主页上的图像。
OpenSCAD 用户手册/OpenSCAD 语言
在第一个卦限中创建立方体或长方体(即“盒子”)。 当center为true时,立方体以原点为中心。 如果按此处显示的顺序给出,参数名称是可选的。
cube(size = [x,y,z], center = true/false); cube(size = x , center = true/false);
- 参数:
- 大小
- 单个值,所有边都为该长度的立方体
- 3个值的数组[x,y,z],尺寸分别为x、y和z的长方体。
- 中心
- false(默认值),第一个(正)卦限,一个角在(0,0,0)处
- true,立方体以(0,0,0)为中心
- 大小
default values: cube(); yields: cube(size = [1, 1, 1], center = false);
- 示例:
equivalent scripts for this example cube(size = 18); cube(18); cube([18,18,18]); . cube(18,false); cube([18,18,18],false); cube([18,18,18],center=false); cube(size = [18,18,18], center = false); cube(center = false,size = [18,18,18] );
equivalent scripts for this example cube([18,28,8],true); box=[18,28,8];cube(box,true);
在坐标系的原点创建一个球体。 r参数名称是可选的。 要使用d而不是r,则必须命名d。
参数
- r
- 半径。 这是球体的半径。 球体的分辨率基于球体的大小以及$fa、$fs和$fn变量。 有关这些特殊变量的更多信息,请参阅:OpenSCAD_User_Manual/其他语言功能
- d
- 直径。 这是球体的直径。
- $fa
- 片段角度(以度为单位)
- $fs
- 片段大小(以毫米为单位)
- $fn
- 分辨率
default values: sphere(); yields: sphere($fn = 0, $fa = 12, $fs = 2, r = 1);
使用示例
sphere(r = 1); sphere(r = 5); sphere(r = 10); sphere(d = 2); sphere(d = 10); sphere(d = 20);
// this creates a high resolution sphere with a 2mm radius sphere(2, $fn=100);
// also creates a 2mm high resolution sphere but this one // does not have as many small triangles on the poles of the sphere sphere(2, $fa=5, $fs=0.1);
围绕z轴创建一个圆柱体或圆锥体。 当center为true时,它也沿着z轴垂直居中。
如果按此处显示的顺序给出,参数名称是可选的。 如果命名了一个参数,则所有后续参数也必须命名。
cylinder(h = height, r1 = BottomRadius, r2 = TopRadius, center = true/false);
说明
第二个和第三个位置参数是r1和r2,如果使用r、d、d1或d2,则必须命名它们。
使用r1和r2或d1和d2,其中任一值为零将形成圆锥形,非零非相等的值将生成圆锥的一部分(圆锥台)。 r1和d1定义底部的宽度,位于[0,0,0]处,r2和d2定义顶部的宽度。
- 参数
- h : 圆柱体或圆锥体的高度
- r : 圆柱体的半径。 r1 = r2 = r。
- r1 : 半径,圆锥体的底部。
- r2 : 半径,圆锥体的顶部。
- d : 圆柱体的直径。 r1 = r2 = d / 2. [注意: 需要版本 2014.03]
- d1 : 直径,圆锥体的底部。 r1 = d1 / 2. [注意: 需要版本 2014.03]
- d2 : 直径,圆锥体的顶部。 r2 = d2 / 2. [注意: 需要版本 2014.03]
- 中心
- false(默认值),z的范围从0到h
- true,z的范围从-h/2到+h/2
- $fa : 每个片段的最小角度(以度为单位)。
- $fs : 每个片段的最小周长长度。
- $fn : 固定360度内的片段数量。 大于或等于3的值将覆盖$fa和$fs
- $fa、$fs和$fn必须是命名参数。 单击此处了解更多详细信息,。
defaults: cylinder(); yields: cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 1, r2 = 1, center = false);
equivalent scripts cylinder(h=15, r1=9.5, r2=19.5, center=false); cylinder( 15, 9.5, 19.5, false); cylinder( 15, 9.5, 19.5); cylinder( 15, 9.5, d2=39 ); cylinder( 15, d1=19, d2=39 ); cylinder( 15, d1=19, r2=19.5);
equivalent scripts cylinder(h=15, r1=10, r2=0, center=true); cylinder( 15, 10, 0, true); cylinder(h=15, d1=20, d2=0, center=true);
-
center = false
-
center = true
equivalent scripts cylinder(h=20, r=10, center=true); cylinder( 20, 10, 10,true); cylinder( 20, d=20, center=true); cylinder( 20,r1=10, d2=20, center=true); cylinder( 20,r1=10, d2=2*10, center=true);
- 使用$fn
较大的$fn值会在更长的渲染时间内创建更平滑、更圆的表面。 有些人在开发过程中使用中等值以实现更快的渲染速度,然后在最终的F6渲染中更改为更大的值。
但是,使用较小的值可能会产生一些有趣的非圆形物体。 这里展示了一些示例
scripts for these examples cylinder(20,20,20,$fn=3); cylinder(20,20,00,$fn=4); cylinder(20,20,10,$fn=4);
- 尺寸过小的孔
使用圆柱体() 与差() 在物体中放置孔会创建尺寸过小的孔。 这是因为圆形路径用内接在圆内的多边形近似。 多边形的点位于圆上,但点之间的直线在圆内。 要使所有孔都比真正的圆更大,多边形必须完全位于圆之外(外接)。 用于外接孔的模块
script for this example poly_n = 6; color("blue") translate([0, 0, 0.02]) linear_extrude(0.1) circle(10, $fn=poly_n); color("green") translate([0, 0, 0.01]) linear_extrude(0.1) circle(10, $fn=360); color("purple") linear_extrude(0.1) circle(10/cos(180/poly_n), $fn=poly_n);
一般来说,半径为 的多边形的任一边中点的半径为 。 如果只知道中点半径 (例如,要将六角扳手放入六角形孔中),则多边形半径为 。
多面体是最通用的 3D 基本实体。 它可以用来创建任何规则或不规则形状,包括那些具有凹面和凸面特征的形状。 曲面由一系列平面近似。
polyhedron( points = [ [X0, Y0, Z0], [X1, Y1, Z1], ... ], triangles = [ [P0, P1, P2], ... ], convexity = N); // before 2014.03 polyhedron( points = [ [X0, Y0, Z0], [X1, Y1, Z1], ... ], faces = [ [P0, P1, P2, P3, ...], ... ], convexity = N); // 2014.03 & later
- 参数
- 点
- 3d 点或顶点的向量。 每个点又是一个向量 [x,y,z],表示它的坐标。
- 点可以按任何顺序定义。 N 个点按定义的顺序被引用为 0 到 N-1。
- 点
- 三角形 [已弃用: 三角形将在将来的版本中删除。 请改用 面 参数]
- 面的向量,它们共同包围了实体。 每个面都是一个向量,包含来自点向量中的 3 个点的索引(从 0 开始)。
- 三角形 [已弃用: 三角形将在将来的版本中删除。 请改用 面 参数]
- 面 [注意: 需要版本 2014.03]
- 面的向量,它们共同包围了实体。 每个面都是一个向量,包含来自点向量中的 3 个或更多点的索引(从 0 开始)。
- 面可以按任何顺序定义,但每个面的点必须按正确的顺序排列(见下文)。 定义足够的面来完全包围实体,并且没有重叠。
- 如果描述单个面的点不在同一个平面上,则面将根据需要自动拆分为三角形。
- 面 [注意: 需要版本 2014.03]
- 凸性
- 整数。 凸性参数指定与物体相交的射线可能穿透的最大面数。 此参数仅用于在 OpenCSG 预览模式中正确显示物体。 它对多面体渲染没有影响。 对于显示问题,将它设置为 10 在大多数情况下应该可以正常工作。
- 凸性
default values: polyhedron(); yields: polyhedron(points = undef, faces = undef, convexity = 1);
在面的列表中,对于每个面,您可以从哪个点开始是任意的,但是面的点(由点列表中的索引引用)必须在从外部向内查看每个面时按顺时针方向排列。 后面从后面查看,底部从底部查看,等等。 记住此排序要求的另一种方法是使用右手定则。 使用你的右手,将你的拇指向上伸出,并像竖起大拇指一样卷起你的手指,将你的拇指指向面,并按你手指卷起的方向排列点。 在下面的示例中尝试一下。
- 示例 1 使用多面体生成 cube( [ 10, 7, 5 ] );
CubePoints = [ [ 0, 0, 0 ], //0 [ 10, 0, 0 ], //1 [ 10, 7, 0 ], //2 [ 0, 7, 0 ], //3 [ 0, 0, 5 ], //4 [ 10, 0, 5 ], //5 [ 10, 7, 5 ], //6 [ 0, 7, 5 ]]; //7 CubeFaces = [ [0,1,2,3], // bottom [4,5,1,0], // front [7,6,5,4], // top [5,6,2,1], // right [6,7,3,2], // back [7,4,0,3]]; // left polyhedron( CubePoints, CubeFaces );
equivalent descriptions of the bottom face [0,1,2,3], [0,1,2,3,0], [1,2,3,0], [2,3,0,1], [3,0,1,2], [0,1,2],[2,3,0], // 2 triangles with no overlap [1,2,3],[3,0,1], [1,2,3],[0,1,3],
- 示例 2 一个正方形底座的金字塔
polyhedron( points=[ [10,10,0],[10,-10,0],[-10,-10,0],[-10,10,0], // the four points at base [0,0,10] ], // the apex point faces=[ [0,1,4],[1,2,4],[2,3,4],[3,0,4], // each triangle side [1,0,3],[2,1,3] ] // two triangles for square base );
- 示例 3 一个三角棱柱
注意:本示例中存在错误,一位目光锐利的 CAD 工程师注意到展开的三角形不正确,斜边应为 1,5 & 0,4。正确的展开方式是将它们与矩形 A 相邻,沿着 1,2 & 0,3 边。代码已修正,希望修正后的图像会尽快出现。
module prism(l, w, h){ polyhedron(//pt 0 1 2 3 4 5 points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]], faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]] ); // preview unfolded (do not include in your function z = 0.08; separation = 2; border = .2; translate([0,w+separation,0]) cube([l,w,z]); translate([0,w+separation+w+border,0]) cube([l,h,z]); translate([0,w+separation+w+border+h+border,0]) cube([l,sqrt(w*w+h*h),z]); translate([l+border,w+separation,0]) polyhedron(//pt 0 1 2 3 4 5 points=[[0,0,0],[h,w,0],[0,w,0], [0,0,z],[h,w,z],[0,w,z]], faces=[[0,1,2], [3,5,4], [0,3,4,1], [1,4,5,2], [2,5,3,0]] ); translate([0-border,w+separation,0]) polyhedron(//pt 0 1 2 3 4 5 points=[[0,0,0],[0-h,w,0],[0,w,0], [0,0,z],[0-h,w,z],[0,w,z]], faces=[[1,0,2],[5,3,4],[0,1,4,3],[1,2,5,4],[2,0,3,5]] ); } prism(10, 5, 3);
定义多面体时常见的错误包括:所有面没有按顺时针顺序排列(从外部观察 - 底部需要从下面观察),面重叠,以及缺少面或面的一部分。一般来说,多面体面还应满足流形条件。
- 任何多面体边上应恰好有两个面相交。
- 如果两个面共用一个顶点,它们应该在围绕该顶点的同一循环面-边中。
第一条规则消除了像两个共用一条边的立方体,以及不具有水密性的模型这样的多面体;第二条规则消除了像两个共用一个顶点的立方体这样的多面体。
从外部观察时,描述每个面的点必须按相同的顺时针顺序排列,并提供了一种检测逆时针顺序的机制。当使用 F12 与 F5 结合的“随机排列”视图时,逆时针面将以粉色显示。重新排列不正确面的点。旋转物体以查看所有面。可以使用 F10 关闭粉色视图。
OpenSCAD 允许临时注释掉部分面描述,以便只显示剩余的面。使用 // 注释掉其余部分。使用 /* 和 */ 开始和结束注释块。这可以是行的一部分,也可以扩展到多行。仅查看部分面有助于确定单个面的正确点。注意,不会显示实体,只会显示面。如果使用 F12,所有面都会有一侧是粉色的。注释掉一些面也有助于显示任何内部面。
CubeFaces = [ /* [0,1,2,3], // bottom [4,5,1,0], // front */ [7,6,5,4], // top /* [5,6,2,1], // right [6,7,3,2], // back */ [7,4,0,3]]; // left
定义多面体后,其预览可能看起来是正确的。多面体本身甚至可能渲染得很好。但是,为了确保它是一个有效的流形,并且可以生成一个有效的 STL 文件,请将其与任何立方体合并并渲染它(F6)。如果多面体消失了,则意味着它不正确。修改所有面的绕组顺序,以及上面提到的两条规则。
- 示例 4 具有面顺序错误的更复杂的多面体
从视图菜单中选择“随机排列”,然后编译(预览 F5)设计(不是编译和渲染!),预览将显示突出显示的面向错误的多边形。不幸的是,这种突出显示在 OpenCSG 预览模式中不可用,因为它会干扰 OpenCSG 预览模式的实现方式。)
下面您可以看到代码和这样一个有问题的多面体的图片,错误的多边形(面或面的组合)以粉色显示。
// Bad polyhedron
polyhedron
(points = [
[0, -10, 60], [0, 10, 60], [0, 10, 0], [0, -10, 0], [60, -10, 60], [60, 10, 60],
[10, -10, 50], [10, 10, 50], [10, 10, 30], [10, -10, 30], [30, -10, 50], [30, 10, 50]
],
faces = [
[0,2,3], [0,1,2], [0,4,5], [0,5,1], [5,4,2], [2,4,3],
[6,8,9], [6,7,8], [6,10,11], [6,11,7], [10,8,11],
[10,9,8], [0,3,9], [9,0,6], [10,6, 0], [0,4,10],
[3,9,10], [3,10,4], [1,7,11], [1,11,5], [1,7,8],
[1,8,2], [2,8,11], [2,11,5]
]
);
正确的多面体如下所示
polyhedron
(points = [
[0, -10, 60], [0, 10, 60], [0, 10, 0], [0, -10, 0], [60, -10, 60], [60, 10, 60],
[10, -10, 50], [10, 10, 50], [10, 10, 30], [10, -10, 30], [30, -10, 50], [30, 10, 50]
],
faces = [
[0,3,2], [0,2,1], [4,0,5], [5,0,1], [5,2,4], [4,2,3],
[6,8,9], [6,7,8], [6,10,11],[6,11,7], [10,8,11],
[10,9,8], [3,0,9], [9,0,6], [10,6, 0],[0,4,10],
[3,9,10], [3,10,4], [1,7,11], [1,11,5], [1,8,7],
[2,8,1], [8,2,11], [5,11,2]
]
);
- 初学者小贴士
如果您不理解“方向”,请尝试识别面向错误的粉色面,然后反转对点向量的引用序列,直到得到正确的结果。例如,在上面的例子中,第三个三角形([0,4,5])是错误的,我们将其修正为 [4,0,5]。请记住,面列表是一个循环列表。此外,您可以从“视图菜单”中选择“显示边”,打印屏幕截图,并对点和面进行编号。在我们的示例中,点用黑色标注,面用蓝色标注。如果需要,将物体翻转过来,并从背面制作第二个副本。这样您就可以跟踪。
- 顺时针技术
方向由顺时针循环索引确定。这意味着,如果您从外部观察三角形(在本例中为 [4,0,5]),您会看到路径围绕面的中心是顺时针方向。绕组顺序 [4,0,5] 是顺时针方向,因此是正确的。绕组顺序 [0,4,5] 是逆时针方向,因此是错误的。同样,[4,0,5] 的任何其他顺时针顺序都有效:[5,4,0] & [0,5,4] 也是正确的。如果您使用顺时针技术,您的面将始终位于外部(OpenSCAD 的外部,其他程序将逆时针方向用作外部)。
将其视为“左手规则”
如果您将左手放在面上,手指弯曲的方向与点的顺序一致,您的拇指应该指向外侧。如果您的拇指指向内侧,您需要反转绕组顺序。
“多面体”的简洁描述
- 点定义了形状中的所有点/顶点。
- 面是连接点/顶点的多边形列表。
点列表中的每个点都用一个 3 元组 x,y,z 位置规范定义。点列表中的点自动从零开始编号,以便在面列表中使用(0,1,2,3,... 等)。
面列表中的每个面都通过从点列表中选择 3 个或更多个点(使用点顺序号)来定义。
例如,faces=[ [0,1,2] ] 定义了一个从第一个点(点从零开始编号)到第二个点,然后到第三点的三角形。
从外部观察任何面时,面必须按顺时针顺序列出所有点。
多面体定义的点列表可能包含重复项。当两个或多个点具有相同的坐标时,它们被视为相同的多面体顶点。因此,以下多面体
points = [[ 0, 0, 0], [10, 0, 0], [ 0,10, 0],
[ 0, 0, 0], [10, 0, 0], [ 0,10, 0],
[ 0,10, 0], [10, 0, 0], [ 0, 0,10],
[ 0, 0, 0], [ 0, 0,10], [10, 0, 0],
[ 0, 0, 0], [ 0,10, 0], [ 0, 0,10]];
polyhedron(points, [[0,1,2], [3,4,5], [6,7,8], [9,10,11], [12,13,14]]);
定义与以下多面体相同的四面体
points = [[0,0,0], [0,10,0], [10,0,0], [0,0,10]];
polyhedron(points, [[0,2,1], [0,1,3], [1,2,3], [0,3,2]]);
使用 projection()
函数,您可以从 3D 模型创建 2D 图纸,并将其导出为 dxf 格式。它的工作原理是将 3D 模型投影到 (x,y) 平面,z 为 0。如果 cut=true
,则只考虑 z=0 的点(有效地切割物体),如果 cut=false
(默认值),则也考虑平面以上和以下的点(创建适当的投影)。
示例:考虑 OpenSCAD 附带的 example002.scad。
然后您可以执行“切割”投影,这将为您提供 z=0 时 x-y 平面的“切片”。
projection(cut = true) example002();
您还可以执行“普通”投影,这将为您提供物体在 xy 平面上的“阴影”。
projection(cut = false) example002();
另一个示例
您还可以使用投影获取物体的“侧视图”。让我们以 example002 为例,将其向上移动到 X-Y 平面之外,并旋转它
translate([0,0,25]) rotate([90,0,0]) example002();
现在我们可以使用 projection() 获取侧视图
projection() translate([0,0,25]) rotate([90,0,0]) example002();
链接
- 更复杂的示例 来自 Giles Bathgate 的博客
OpenSCAD 用户手册/OpenSCAD 语言
所有 2D 图元都可以用 3D 变换进行变换。它们通常用作 3D 挤出的部分。虽然它们无限薄,但它们以 1 个单位的厚度渲染。
注意:尝试使用 difference()
从 3D 物体中减去会导致最终渲染结果出乎意料。
在第一象限中创建一个正方形或矩形。当 center
为真时,正方形将以原点为中心。如果按此处显示的顺序给出,则参数名称是可选的。
square(size = [x, y], center = true/false); square(size = x , center = true/false);
- 参数:
- 大小
- 单个值,两边都为该长度的正方形
- 2 值数组 [x,y],尺寸为 x 和 y 的矩形
- 中心
- false(默认),第一(正)象限,一个角在 (0,0)
- true,正方形以 (0,0) 为中心
- 大小
default values: square(); yields: square(size = [1, 1], center = false);
- 示例:
equivalent scripts for this example square(size = 10); square(10); square([10,10]); . square(10,false); square([10,10],false); square([10,10],center=false); square(size = [10, 10], center = false); square(center = false,size = [10, 10] );
equivalent scripts for this example square([20,10],true); a=[20,10];square(a,true);
在原点创建一个圆形。除 r 外,所有参数必须命名。
circle(r=radius | d=diameter);
- 参数
- r : 圆形半径。r 名是圆形中唯一可选的名称。
- 圆形分辨率基于大小,使用 $fa 或 $fs。
- r : 圆形半径。r 名是圆形中唯一可选的名称。
- 对于一个小而高分辨率的圆形,您可以创建一个大圆形,然后将其缩小,或者您可以设置 $fn 或其他特殊变量。注意:这些示例超出了 3D 打印机以及显示屏的分辨率。
scale([1/100, 1/100, 1/100]) circle(200); // create a high resolution circle with a radius of 2. circle(2, $fn=50); // Another way.
- d : 圆形直径(仅在 2014.03 之后版本可用)。
- $fa : 每个片段的最小角度(以度为单位)。
- $fs : 每个片段的最小周长长度。
- $fn : 360 度中固定的分段数。大于或等于 3 的值会覆盖 $fa 和 $fs。
- 如果使用它们,$fa、$fs 和 $fn 必须是命名参数。单击此处了解详细信息。
defaults: circle(); yields: circle($fn = 0, $fa = 12, $fs = 2, r = 1);
此示例的等效脚本
circle(10); circle(r=10); circle(d=20); circle(d=2+9*2);
可以通过使用 scale()
或 resize()
使 x 和 y 维度不等来从圆形创建椭圆。请参阅OpenSCAD 用户手册/变换
equivalent scripts for this example resize([30,10])circle(d=20); scale([1.5,.5])circle(d=20);
可以通过使用 circle()
将 $fn 设置为边数来创建 3 个或更多个边的正多边形。以下两段代码等效。
circle(r=1, $fn=4);
module regular_polygon(order = 4, r=1){ angles=[ for (i = [0:order-1]) i*(360/order) ]; coords=[ for (th=angles) [r*cos(th), r*sin(th)] ]; polygon(coords); } regular_polygon();
它们将产生以下形状,其中多边形内接于圆形,所有边(和角)相等。一个角指向正 x 方向。对于不规则形状,请参阅下面的多边形图元。
script for these examples translate([-42, 0]){circle(20,$fn=3);%circle(20,$fn=90);} translate([ 0, 0]) circle(20,$fn=4); translate([ 42, 0]) circle(20,$fn=5); translate([-42,-42]) circle(20,$fn=6); translate([ 0,-42]) circle(20,$fn=8); translate([ 42,-42]) circle(20,$fn=12); color("black"){ translate([-42, 0,1])text("3",7,,center); translate([ 0, 0,1])text("4",7,,center); translate([ 42, 0,1])text("5",7,,center); translate([-42,-42,1])text("6",7,,center); translate([ 0,-42,1])text("8",7,,center); translate([ 42,-42,1])text("12",7,,center); }
函数 polygon() 使用 x,y 坐标列表创建一个多边形形状。多边形是最强大的 2D 对象,它可以创建圆形和正方形可以创建的任何东西,以及更多。这包括具有凹边和凸边的不规则形状。此外,它可以在该形状内放置孔。
polygon(points = [ [x, y], ... ], paths = [ [p1, p2, p3..], ...], convexity = N);
- 参数
- 点
- 多边形的 x,y 点列表:2 个元素向量的向量。
- 注意:点从 0 到 n-1 索引。
- 路径
- 默认
- 如果没有指定路径,所有点将按列表中的顺序使用。
- 单个向量
- 遍历点的顺序。使用从 0 到 n-1 的索引。可以按不同的顺序使用,可以使用列表中所有点或部分点。
- 多个向量
- 创建主形状和次要形状。次要形状从主形状中减去(类似于
difference()
)。次要形状可以完全或部分位于主形状内。
- 创建主形状和次要形状。次要形状从主形状中减去(类似于
- 默认
- 通过从指定的最后一个点返回到第一个点来创建一个封闭形状。
- 凸性
- "向内" 曲线的整数数量,即通过多边形的任意线的预期路径交叉点。见下文。
defaults: polygon(); yields: polygon(points = undef, paths = undef, convexity = 1);
equivalent scripts for this example polygon(points=[[0,0],[100,0],[130,50],[30,50]]); polygon([[0,0],[100,0],[130,50],[30,50]], paths=[[0,1,2,3]]); polygon([[0,0],[100,0],[130,50],[30,50]],[[3,2,1,0]]); polygon([[0,0],[100,0],[130,50],[30,50]],[[1,0,3,2]]); a=[[0,0],[100,0],[130,50],[30,50]]; b=[[3,0,1,2]]; polygon(a); polygon(a,b); polygon(a,[[2,3,0,1,2]]);
equivalent scripts for this example polygon(points=[[0,0],[100,0],[0,100],[10,10],[80,10],[10,80]], paths=[[0,1,2],[3,4,5]],convexity=10); triangle_points =[[0,0],[100,0],[0,100],[10,10],[80,10],[10,80]]; triangle_paths =[[0,1,2],[3,4,5]]; polygon(triangle_points,triangle_paths,10);
第一个路径向量 [0,1,2] 选择点 [0,0],[100,0],[0,100] 用于主形状。第二个路径向量 [3,4,5] 选择点 [10,10],[80,10],[10,80] 用于次要形状。次要形状从主形状中减去(考虑 difference()
)。由于次要形状完全位于主形状内,因此它会留下一个带有孔的形状。
[注意: 需要版本 2015.03] (用于使用 concat()
)
//example polygon with multiple holes a0 = [[0,0],[100,0],[130,50],[30,50]]; // main 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); b = [b0,b1,b2,b3,b4]; polygon(a,b); //alternate polygon(a,[b0,b1,b2,b3,b4]);
translate([0,-20,10]) { rotate([90,180,90]) { linear_extrude(50) { polygon( points = [ //x,y /* O . */ [-2.8,0], /* O__X . */ [-7.8,0], /* O \ X__X . */ [-15.3633,10.30], /* X_______._____O \ X__X . */ [15.3633,10.30], /* X_______._______X \ / X__X . O */ [7.8,0], /* X_______._______X \ / X__X . O__X */ [2.8,0], /* X__________.__________X \ / \ O / \ / / \ / / X__X . X__X */ [5.48858,5.3], /* X__________.__________X \ / \ O__________X / \ / / \ / / X__X . X__X */ [-5.48858,5.3], ] ); } } }
凸度参数指定与对象相交的光线可能穿透的最大正面数量(背面数量)。此参数仅在 OpenCSG 预览模式下正确显示对象时需要,对多面体渲染没有影响。
此图像显示一个凸度为 2 的 2D 形状,因为以红色指示的光线以最大 2 次穿透 2D 形状的外部⇒内部(或内部⇒外部)。3D 形状的凸度将以类似的方式确定。将其设置为 10 对于大多数情况应该可以正常工作。
[已弃用: import_dxf() 将在将来的版本中删除。请改用 import()。]
读取 DXF 文件并创建一个 2D 形状。
示例
linear_extrude(height = 5, center = true, convexity = 10) import_dxf(file = "example009.dxf", layer = "plate");
text
模块使用本地系统上安装的字体或作为单独的字体文件提供的字体,将文本创建为 2D 几何对象。
[注意: 需要版本 2015.03]
参数
- 文本
- 字符串。要生成的文本。
- 大小
- 十进制。生成的文本的升高(基线以上的高度)约为给定值。默认值为 10。不同的字体可能略有不同,并且可能不会完全填充指定的大小,通常它们会渲染得略小。在公制系统中,大小为 25.4(1 英寸)将对应于 100pt ⇒ 12pt 字号将是 12×0.254 用于公制转换或 0.12 英寸。
- 字体
- 字符串。要使用的字体的名称。这不是字体文件的名称,而是逻辑字体名称(由 fontconfig 库内部处理)。这也可以包括样式参数,见下文。可以使用字体列表对话框(帮助 -> 字体列表)获取已安装字体和样式的列表。
- halign
- 字符串。文本的水平对齐方式。可能的值为“left”、“center”和“right”。默认值为“left”。
- valign
- 字符串。文本的垂直对齐方式。可能的值为“top”、“center”、“baseline”和“bottom”。默认值为“baseline”。
- 间距
- 十进制。增加/减少字符间距的因子。默认值 1 导致字体正常间距,给出大于 1 的值会导致字母间距更大。
- 方向
- 字符串。文本流的方向。可能的值为“ltr”(从左到右)、“rtl”(从右到左)、“ttb”(从上到下)和“btt”(从下到上)。默认值为“ltr”。
- 语言
- 字符串。文本的语言(例如,“en”、“ar”、“ch”。默认值为“en”。
- 脚本
- 字符串。文本的脚本(例如,“latin”、“arabic”、“hani”。默认值为“latin”。
- $fn
- 用于细分 freetype 提供的曲线路径段
示例
text("OpenSCAD");
- 笔记
为了允许指定特定的 Unicode 字符,可以在字符串中使用以下转义代码指定它们;
- \x03 - 十六进制字符值(仅支持从01到7f的十六进制值)
- \u0123 - 使用 4 个十六进制数字的 Unicode 字符(注意:小写\u)
- \U012345- 使用 6 个十六进制数字的 Unicode 字符(注意:大写\U)
空字符 (NUL) 映射为空格字符 (SP)。
assert(version() == [2019, 5, 0]); assert(ord(" ") == 32); assert(ord("\x00") == 32); assert(ord("\u0000") == 32); assert(ord("\U000000") == 32);
示例
t="\u20AC10 \u263A"; // 10 euro and a smilie
字体由其逻辑字体名称指定;此外,可以添加样式参数以选择特定的字体样式,如“粗体”或“斜体”,例如
font="Liberation Sans:style=Bold Italic"
字体列表对话框(位于帮助>字体列表下)显示每个可用字体的字体名称和字体样式。为了参考,对话框还显示字体文件的路径。您可以将字体列表中的字体拖放到编辑器窗口中以在 text() 语句中使用。
OpenSCAD 包含字体 Liberation Mono、Liberation Sans 和 Liberation Serif。因此,由于字体通常因平台类型而异,使用这些包含的字体很可能在平台之间可移植。
出于这个原因,建议在常用/休闲文本使用中指定这些字体之一。Liberation Sans 是默认字体,以鼓励这一点。
除了已安装的字体(对于 Windows,仅以管理员身份为所有用户安装的字体)之外,还可以添加项目特定的字体文件。支持的字体文件格式为 TrueType 字体 (*.ttf) 和 OpenType 字体 (*.otf)。这些文件需要使用 use<> 进行注册。
use <ttf/paratype-serif/PTF55F.ttf>
注册后,字体将列在字体列表对话框中,因此如果未知字体的逻辑名称,可以查找它,因为它已注册。
OpenSCAD 使用 fontconfig 来查找和管理字体,因此可以使用命令行上的 fontconfig 工具以类似于 GUI 对话框的格式列出系统配置的字体。
$ fc-list -f "%-60{{%{family[0]}%{:style[0]=}}}%{file}\n" | sort
...
Liberation Mono:style=Bold Italic /usr/share/fonts/truetype/liberation2/LiberationMono-BoldItalic.ttf
Liberation Mono:style=Bold /usr/share/fonts/truetype/liberation2/LiberationMono-Bold.ttf
Liberation Mono:style=Italic /usr/share/fonts/truetype/liberation2/LiberationMono-Italic.ttf
Liberation Mono:style=Regular /usr/share/fonts/truetype/liberation2/LiberationMono-Regular.ttf
...
在 Windows 下,字体存储在 Windows 注册表中。要获取包含字体文件名的文件,请使用以下命令
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /s > List_Fonts_Windows.txt
示例
square(10); translate([15, 15]) { text("OpenSCAD", font = "Liberation Sans"); } translate([15, 0]) { text("OpenSCAD", font = "Liberation Sans:style=Bold Italic"); }
- 顶部
- 文本与给定 Y 坐标处的边界框的顶部对齐。
- 中心
- 文本与给定 Y 坐标处的边界框的中心对齐。
- 基线
- 文本与给定 Y 坐标处的字体基线对齐。这是默认值。
- 底部
- 文本与给定 Y 坐标处的边界框的底部对齐。
text = "Align"; font = "Liberation Sans"; valign = [ [ 0, "top"], [ 40, "center"], [ 75, "baseline"], [110, "bottom"] ]; for (a = valign) { translate([10, 120 - a[0], 0]) { color("red") cube([135, 1, 0.1]); color("blue") cube([1, 20, 0.1]); linear_extrude(height = 0.5) { text(text = str(text,"_",a[1]), font = font, size = 20, valign = a[1]); } } }
多行文本不支持 text()
,但将每行大小 × .72 平移将导致 1 em 的行间距(em = 字体的正文高度或点)。可以添加 20%(× 1.2)。
- 左
- 文本与给定 X 坐标处的边界框左侧对齐。这是默认设置。
- 中心
- 文本与给定 X 坐标处的边界框中心对齐。
- 右
- 文本与给定 X 坐标处的边界框右侧对齐。
text = "Align"; font = "Liberation Sans"; halign = [ [10, "left"], [50, "center"], [90, "right"] ]; for (a = halign) { translate([140, a[0], 0]) { color("red") cube([115, 2,0.1]); color("blue") cube([2, 20,0.1]); linear_extrude(height = 0.5) { text(text = str(text,"_",a[1]), font = font, size = 20, halign = a[1]); } } }
可以使用 linear_extrude 函数将文本从二维对象更改为三维对象。
//3d Text Example linear_extrude(4) text("Text");
挤出 是使用固定横截面轮廓创建物体的过程。OpenSCAD 提供两个命令来从二维形状创建三维实体:linear_extrude() 和 rotate_extrude()。线性挤出类似于将橡皮泥通过带有特定形状模具的压机。
两种挤出方法都适用于位于 X-Y 平面的(可能是分离的)二维形状。虽然在二维形状和三维实体上进行的变换可以将形状移出 X-Y 平面,但在执行挤出时,最终结果并不直观。实际上,任何三维坐标信息(Z 坐标)都会被任何二维形状忽略,这个过程相当于在执行挤出之前对任何二维形状执行隐式 projection()。建议对严格位于 X-Y 平面的形状执行挤出。
线性挤出是一个将二维对象作为输入并生成三维对象作为结果的操作。
挤出遵循 V 向量,该向量默认为 Z 轴,为了指定自定义值,需要使用大于 2021.01 的版本。
在 OpenSCAD 中,挤出始终对二维对象的 xy 平面的投影(阴影)执行;因此,如果你在挤出之前对二维对象进行旋转或应用其他变换,其阴影形状将被挤出。
虽然挤出沿着 V 向量是线性的,但有一个扭曲参数可用,它会导致物体在向上挤出时绕 V 向量旋转。这可以用来绕其中心旋转物体,就像螺旋柱一样,或者在 V 向量周围产生螺旋挤出,就像猪尾巴一样。
还包括一个比例参数,以便可以扩展或收缩物体在挤出的范围内,从而使挤出向内或向外扩散。
linear_extrude(height = 5, v = [0, 0, 1], center = true, convexity = 10, twist = -fanrot, slices = 20, scale = 1.0, $fn = 16) {...}
由于向后兼容性问题,你必须使用参数名称。
height
必须为正数。
v
是一个三维向量,必须指向正 Z 方向 [注意: 需要版本 开发快照]
$fn
是可选的,它指定线性挤出的分辨率(数字越大,“平滑度”越高,但需要更多计算时间)。
如果对非平凡二维形状执行挤出失败,请尝试设置凸度参数(默认值不是 10,但 10 是一个“好”的值,可以尝试一下)。见下面的解释。
扭曲是指形状挤出的角度数。将参数 twist 设置为 360 会挤出整整一圈。扭曲方向遵循左手定则。
0° 的扭曲
linear_extrude(height = 10, center = true, convexity = 10, twist = 0) translate([2, 0, 0]) circle(r = 1);
-100° 的扭曲
linear_extrude(height = 10, center = true, convexity = 10, twist = -100) translate([2, 0, 0]) circle(r = 1);
100° 的扭曲
linear_extrude(height = 10, center = true, convexity = 10, twist = 100) translate([2, 0, 0]) circle(r = 1);
-500° 的扭曲
linear_extrude(height = 10, center = true, convexity = 10, twist = -500) translate([2, 0, 0]) circle(r = 1);
它类似于圆柱体的参数 center。如果 center
为 false,则线性挤出 Z 范围为 0 到 height;如果为 true,则范围为 -height/2 到 height/2。
center = true
linear_extrude(height = 10, center = true, convexity = 10, twist = -500) translate([2, 0, 0]) circle(r = 1);
center = false
linear_extrude(height = 10, center = false, convexity = 10, twist = -500) translate([2, 0, 0]) circle(r = 1);
slices 参数定义挤出 Z 轴上的中间点数。其默认值随扭曲值的增加而增加。显式设置 slices 可能会提高输出细化。此外,segments 参数会向挤出多边形添加顶点(点),从而产生更平滑的扭曲几何体。segments 需要是多边形片段的倍数才能产生效果(对于 circle($fn=3),为 6 或 9..;对于 square(),为 8、12..)。
linear_extrude(height = 10, center = false, convexity = 10, twist = 360, slices = 100) translate([2, 0, 0]) circle(r = 1);
特殊变量 $fn、$fs 和 $fa 也可以用来提高输出。如果未定义 slices,则其值将从定义的 $fn 值中获取。
linear_extrude(height = 10, center = false, convexity = 10, twist = 360, $fn = 100) translate([2, 0, 0]) circle(r = 1);
在挤出高度上按此值缩放二维形状。比例可以是标量或向量。
linear_extrude(height = 10, center = true, convexity = 10, scale=3) translate([2, 0, 0]) circle(r = 1);
linear_extrude(height = 10, center = true, convexity = 10, scale=[1,5], $fn=100) translate([2, 0, 0]) circle(r = 1);
注意,如果比例是向量,则生成的侧壁可能是非平面的。使用 twist=0
和 slices
参数来避免 不对称。
linear_extrude(height=10, scale=[1,0.1], slices=20, twist=0) polygon(points=[[0,0],[20,10],[20,-10]]);
此函数的常见用法是导入二维 svg
linear_extrude(height = 10, center = true) import("knight.svg");
旋转挤出将二维形状绕 Z 轴旋转,形成具有旋转对称性的实体。理解此操作的一种方式是想象一个放置在 X-Y 平面上的陶轮,其旋转轴指向 +Z。然后将要制作的物体放在这个虚拟的陶轮上(可能延伸到 X-Y 平面以下,指向 -Z)。要制作的物体是在 X-Y 平面上的物体的横截面(只保留右侧,X >= 0)。这就是将作为子项提供给 rotate_extrude() 以生成该实体的二维形状。请注意,物体最初位于 X-Y 平面,但倾斜向上(绕 X 轴旋转 +90 度)以挤出。
由于二维形状由 OpenSCAD 在 X-Y 平面上渲染,理解此操作的另一种方式如下:将二维形状绕 Y 轴旋转形成实体。生成的实体放置使其旋转轴位于 Z 轴上。
与 linear_extrude 一样,挤出始终对二维多边形到 XY 平面的投影执行。应用于二维多边形在挤出之前的变换(如 rotate、translate 等)会修改二维多边形到 XY 平面的投影,因此也会修改最终三维对象的外观。
- 二维多边形在 Z 方向上的平移对结果没有影响(因为投影也不受影响)。
- 在 X 方向上的平移会增加最终物体的直径。
- 在 Y 方向上的平移会导致最终物体在 Z 方向上发生偏移。
- 绕 X 轴或 Y 轴的旋转会扭曲最终物体的横截面,因为对 XY 平面的投影也会扭曲。
不要混淆,因为 OpenSCAD 在 Z 方向上渲染带有特定高度的二维多边形,所以二维物体(及其高度)似乎在 XY 平面上有更大的投影。但对于投影到 XY 平面的情况,以及之后进行挤出操作,只使用没有高度的基多边形。
它不能用于生成螺旋线或螺纹。 (这些可以通过使用 twist 参数的 linear_extrude() 来完成。)
二维形状**必须**完全位于 Y 轴的右侧(推荐)或左侧。 更准确地说,形状的**每个**顶点必须满足 x >= 0 或 x <= 0。如果形状跨越 X 轴,控制台窗口会显示警告,并且 ignore rotate_extrude()。如果二维形状与 Y 轴相交,即 x = 0,则**必须**为一条与 Y 轴相交的直线,而不是一个点,因为点会导致厚度为零的三维物体,这是无效的,并导致 CGAL 错误。对于 2016.xxxx 之前的 OpenSCAD 版本,如果形状位于负轴上,则生成的面的方向将是内向的,这可能会导致不希望有的效果。
用法
[edit | edit source]rotate_extrude(angle = 360, convexity = 2) {...}
由于向后兼容性问题,你必须使用参数名称。
- 凸性 : 如果对于非平凡的二维形状,挤出失败,请尝试设置凸性参数(默认值不是 10,但 10 是一个值得尝试的“好”值)。 参见下面的解释。
- 角度 [注意: 需要版本 2019.05] : 默认值为 360。指定从正 X 轴开始的扫描角度。扫描方向遵循右手定则,因此负角度将顺时针扫描。
- $fa : 每个片段的最小角度(以度为单位)。
- $fs : 每个片段的最小周长长度。
- $fn : 固定360度内的片段数量。 大于或等于3的值将覆盖$fa和$fs
- $fa、$fs和$fn必须是命名参数。 单击此处了解更多详细信息,。
示例
[edit | edit source]可以使用旋转挤出构造一个简单的环面。
rotate_extrude(convexity = 10) translate([2, 0, 0]) circle(r = 1);
网格细化
[edit | edit source]增加构成二维形状的碎片数量可以提高网格质量,但渲染时间更长。
rotate_extrude(convexity = 10) translate([2, 0, 0]) circle(r = 1, $fn = 100);
挤出使用的碎片数量也可以增加。
rotate_extrude(convexity = 10, $fn = 100) translate([2, 0, 0]) circle(r = 1, $fn = 100);
使用 angle 参数(使用 OpenSCAD 版本 2016.xx),可以建模一个钩子。
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);
挤出多边形
[edit | edit source]也可以对用户选择的点构成的多边形执行挤出操作。
这是一个简单的多边形及其 200 步旋转挤出。(请注意,它已旋转 90 度以显示旋转外观;rotate_extrude()
需要它平坦)。
rotate([90,0,0]) polygon( points=[[0,0],[2,1],[1,2],[1,3],[3,4],[0,5]] );
rotate_extrude($fn=200) polygon( points=[[0,0],[2,1],[1,2],[1,3],[3,4],[0,5]] );
有关多边形的更多信息,请参见:二维基本图形:多边形。
挤出参数的描述
[edit | edit source]所有挤出模式的挤出参数
[edit | edit source]凸性 | 整数。凸性参数指定与物体相交的光线可能穿过的正面(或背面)的最大数量。此参数仅在使用标准 Goldfeather 算法时在 OpenCSG 预览模式中正确显示物体时需要,对多面体渲染(网格生成)没有影响。
|
此图像显示了一个凸性为 2 的二维形状,因为用红色指示的光线与二维形状最多相交 4 次(2 个正面和 2 个背面)。 三维形状的凸性将以类似的方式确定。将其设置为 10 应该适用于大多数情况。 一般来说,仅设置较大的数字会导致预览渲染速度变慢。
仅用于线性挤出的挤出参数
[edit | edit source]高度 | 挤出高度 |
中心 | 如果为真,则实体在挤出后居中 |
扭曲 | 挤出扭曲角度 |
缩放 | 在挤出高度上按此值缩放二维形状。 |
切片 | 类似于特殊变量 $fn,但不会传递给子二维形状。 |
段 | 类似于切片,但在多边形的段上添加点,而不会改变多边形的形状。 |
第 4 章 - 变换
[edit | edit source]OpenSCAD 用户手册/OpenSCAD 语言
基本概念
[edit | edit source]变换影响其子节点,顾名思义,它们以各种方式变换子节点,例如移动、旋转或缩放子节点。变换写在它们影响的物体之前,例如
translate([10,20,30])
cube(10);
请注意,变换命令后面没有分号。可以通过使用 '{' 和 '}' 将子树括起来,将变换应用于一组子节点,例如
translate([0,0,-5])
{
cube(10);
cylinder(r=5,h=10);
}
级联变换用于对最终子节点应用各种变换。级联是通过嵌套语句实现的,例如
rotate([45,45,45])
translate([10,20,30])
cube(10);
组合变换是一个顺序过程,从右到左进行。考虑以下两个变换
color("red") translate([0,10,0]) rotate([45,0,0]) cube(5);
color("green") rotate([45,0,0]) translate([0,10,0]) cube(5);
虽然它们包含相同的操作,但第一个绕原点旋转一个立方体,然后根据 translate 指定的偏移量移动它,最后将其颜色设置为红色。相反,第二个序列首先移动一个立方体,然后绕原点旋转它,最后将其颜色设置为绿色。在这种情况下,旋转会导致立方体沿以原点为中心的弧线移动。此弧线的半径是从原点的距离,由前面的平移设置。旋转和平移变换的不同排序会导致立方体最终位于不同的位置。
高级概念
[edit | edit source]由于 OpenSCAD 使用不同的库来实现功能,这可能会导致变换的 F5 预览行为出现一些不一致。传统的变换(translate、rotate、scale、mirror 和 multimatrix)在预览中使用 OpenGL 执行,而其他更高级的变换,例如 resize,则执行 CGAL 操作,就像 CSG 操作一样影响底层物体,而不仅仅是变换它。特别地,这可能会影响修饰符字符的显示,特别是“#”和“%”,其中高亮可能不会直观地显示,例如高亮预调整大小的物体,但高亮后缩放的物体。
缩放
[edit | edit source]使用指定的向量缩放其子元素。参数名称是可选的。
Usage Example: scale(v = [x, y, z]) { ... }
cube(10);
translate([15,0,0]) scale([0.5,1,2]) cube(10);
调整大小
[edit | edit source]修改子物体的尺寸以匹配给定的 x、y 和 z。
resize() 是一个 CGAL 操作,与其他操作(如 render())一样,使用完整几何体,因此即使在预览中,这也会花费时间处理。
使用示例
// resize the sphere to extend 30 in x, 60 in y, and 10 in the z directions.
resize(newsize=[30,60,10]) sphere(r=10);
如果 x、y 或 z 为 0,则该维度保持原样。
// resize the 1x1x1 cube to 2x2x1
resize([2,2,0]) cube();
如果 'auto' 参数设置为 true,则会自动缩放任何 0 维以匹配。 例如。
// resize the 1x2x0.5 cube to 7x14x3.5
resize([7,0,0], auto=true) cube([1,2,0.5]);
'auto' 参数也可以在您只希望自动缩放单个维度,并将其他维度保持原样时使用。
// resize to 10x8x1. Note that the z dimension is left alone.
resize([10,0,0], auto=[true,true,false]) cube([5,4,1]);
围绕坐标系的轴或任意轴,将子元素旋转 'a' 度。如果参数按照指定顺序给出,则参数名称是可选的。
//Usage:
rotate(a = deg_a, v = [x, y, z]) { ... }
// or
rotate(deg_a, [x, y, z]) { ... }
rotate(a = [deg_x, deg_y, deg_z]) { ... }
rotate([deg_x, deg_y, deg_z]) { ... }
'a' 参数 (deg_a) 可以是数组,如上面后面用法的表达;当 deg_a 是数组时,'v' 参数会被忽略。当 'a' 指定了 多个轴 时,旋转会按照以下顺序应用:先 x 轴,再 y 轴,最后 z 轴。这意味着以下代码
rotate(a=[ax,ay,az]) {...}
等效于
rotate(a=[0,0,az]) rotate(a=[0,ay,0]) rotate(a=[ax,0,0]) {...}
例如,要将物体翻转过来,可以围绕 'y' 轴旋转 180 度。
rotate(a=[0,180,0]) { ... }
这通常简化为
rotate([0,180,0]) { ... }
可选参数 'v' 是一个向量,它决定物体围绕哪个任意轴旋转。
当指定单个轴时,'v' 参数允许指定哪个轴是旋转的基础。例如,与上面等效,只围绕 y 轴旋转
rotate(a=180, v=[0,1,0]) { ... }
当指定单个轴时,'v' 是一个 向量,定义一个任意轴用于旋转;这与上面的 多个轴 不同。例如,围绕由向量 [1,1,0] 定义的轴旋转物体 45 度,
rotate(a=45, v=[1,1,0]) { ... }
用 单个标量参数 旋转围绕 Z 轴旋转。这在二维环境中很有用,因为 Z 轴是唯一的旋转轴。例如
rotate(45) square(10);
对于
rotate([a, b, c]) { ... };
"a" 是围绕 X 轴的旋转,从 +Y 轴,朝向 +Z 轴旋转。
"b" 是围绕 Y 轴的旋转,从 +Z 轴,朝向 +X 轴旋转。
"c" 是围绕 Z 轴的旋转,从 +X 轴,朝向 +Y 轴旋转。
这些都是 右手定则 的情况。将你的右手拇指指向正轴,你的手指显示旋转的方向。
因此,如果 "a" 固定为零,并且 "b" 和 "c" 被适当操纵,那么这就是 球坐标系。
因此,要从原点构造一个到某个其他点 (x,y,z) 的圆柱体
x= 10; y = 10; z = 10; // point coordinates of end of cylinder
length = norm([x,y,z]); // radial distance
b = acos(z/length); // inclination angle
c = atan2(y,x); // azimuthal angle
rotate([0, b, c])
cylinder(h=length, r=0.5);
%cube([x,y,z]); // corner of cube should coincide with end of cylinder
沿着指定向量平移(移动)子元素。参数名称是可选的。
Example: translate(v = [x, y, z]) { ... }
cube(2,center = true);
translate([5,0,0])
sphere(1,center = true);
将子元素转换为原始元素的镜像,就好像它是穿过与原点相交的平面的镜面图像一样。mirror() 的参数是与原点相交的镜面平面法向量,这意味着垂直于平面向外延伸的向量。原始对象的每个坐标都会改变,使其在这个平面的另一侧与平面上最近的点等距。例如,mirror([1,0,0]),对应于指向 x 轴方向的法向量,会产生一个物体,使得所有正 x 坐标都变成负 x 坐标,所有负 x 坐标都变成正 x 坐标。
mirror(v= [x, y, z] ) { ... }
原始图像在右侧。注意,镜像不会创建副本。与旋转和缩放一样,它会改变物体。
-
hand(); // 原始
mirror([1,0,0]) hand();
-
hand(); // 原始
mirror([1,1,0]) hand();
-
hand(); // 原始
mirror([1,1,1]) hand();
rotate([0,0,10]) cube([3,2,1]); mirror([1,0,0]) translate([1,0,0]) rotate([0,0,10]) cube([3,2,1]);
将所有子元素的几何图形与给定的 仿射变换 矩阵相乘,其中矩阵为 4×3 - 一个包含 3 个行向量,每个向量有 4 个元素的向量,或者是一个 4×4 矩阵,其第 4 行始终为 [0,0,0,1]。
用法:multmatrix(m = [...]) { ... }
以下是对矩阵中独立元素(前三行)可以执行的操作的细分
缩放 X | 沿 Y 轴对 X 进行剪切 | 沿 Z 轴对 X 进行剪切 | 平移 X |
沿 X 轴对 Y 进行剪切 | 缩放 Y | 沿 Z 轴对 Y 进行剪切 | 平移 Y |
沿 X 轴对 Z 进行剪切 | 沿 Y 轴对 Z 进行剪切 | 缩放 Z | 平移 Z |
第 4 行被强制为 [0,0,0,1] 并且可以省略,除非你是在将矩阵传递给 multmatrix 之前组合矩阵,因为它不会在 OpenSCAD 中处理。每个矩阵都对给定几何图形的点进行操作,就好像每个顶点都是一个包含 4 个元素的向量,包含一个 3D 向量,其第 4 个元素隐含为 1,例如 v=[x, y, z, 1]。m 中隐含的第 4 行的作用是保留向量第 4 个元素中的隐含 1,允许平移工作。因此,multmatrix 操作对每个顶点 v 执行 m*v。m 中未指定的所有元素(第 4 行除外)都被视为零。
此示例在 XY 平面中旋转 45 度并平移 [10,20,30],即与translate([10,20,30]) rotate([0,0,45])相同。
angle=45;
multmatrix(m = [ [cos(angle), -sin(angle), 0, 10],
[sin(angle), cos(angle), 0, 20],
[ 0, 0, 1, 30],
[ 0, 0, 0, 1]
]) union() {
cylinder(r=10.0,h=10,center=false);
cube(size=[10,10,10],center=false);
}
以下示例演示了通过矩阵乘法组合仿射变换矩阵,在最终版本中产生一个等效于 rotate([0, -35, 0]) translate([40, 0, 0]) Obj(); 的变换。请注意,sin 函数上的符号与上面的示例不同,因为正符号必须按 x 到 y、y 到 z、z 到 x 的顺序排列,以便旋转角度对应于右手坐标系中围绕另一个轴的旋转。
module Obj() {
cylinder(r=10.0,h=10,center=false);
cube(size=[10,10,10],center=false);
}
// This itterates into the future 6 times and demonstrates how multimatrix is moving the object around the center point
for(time = [0 : 15 : 90]){
y_ang=-time;
mrot_y = [ [ cos(y_ang), 0, sin(y_ang), 0],
[ 0, 1, 0, 0],
[-sin(y_ang), 0, cos(y_ang), 0],
[ 0, 0, 0, 1]
];
mtrans_x = [ [1, 0, 0, 40],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
echo(mrot_y*mtrans_x);
// This is the object at [0,0,0]
Obj();
// This is the starting object at the [40,0,0] coordinate
multmatrix(mtrans_x) Obj();
// This is the one rotating and appears 6 times
multmatrix(mrot_y * mtrans_x) Obj();
};
此示例倾斜模型,这是其他变换无法实现的。
M = [ [ 1 , 0 , 0 , 0 ],
[ 0 , 1 , 0.7, 0 ], // The "0.7" is the skew value; pushed along the y axis as z changes.
[ 0 , 0 , 1 , 0 ],
[ 0 , 0 , 0 , 1 ] ] ;
multmatrix(M) { union() {
cylinder(r=10.0,h=10,center=false);
cube(size=[10,10,10],center=false);
} }
此示例显示了如何使用 multmatrix 向量变换向量,例如,可以依次变换点数组(多边形)中的所有点。向量 (v) 使用旋转矩阵 (m) 变换,得到一个新的向量 (vtrans),该向量现在被旋转并且沿着以 z 轴为中心、半径为 v 的圆形路径移动,而不会旋转立方体。
angle=45;
m=[
[cos(angle), -sin(angle), 0, 0],
[sin(angle), cos(angle), 0, 0],
[ 0, 0, 1, 0]
];
v=[10,0,0];
vm=concat(v,[1]); // need to add [1]
vtrans=m*vm;
echo(vtrans);
translate(vtrans)cube();
在此了解更多信息
使用指定的 RGB 颜色 + alpha 值显示子元素。这仅用于 F5 预览,因为 CGAL 和 STL (F6) 目前不支持颜色。alpha 值默认为 1.0(不透明),如果未指定。
color( c = [r, g, b, a] ) { ... } color( c = [r, g, b], alpha = 1.0 ) { ... } color( "#hexvalue" ) { ... } color( "colorname", 1.0 ) { ... }
请注意,r, g, b, a
值限制为范围 [0,1] 内的浮点数,而不是更传统的整数 { 0 ... 255 }。但是,没有什么可以阻止你使用 {0 ... 255} 中的 R, G, B
值,并进行适当的缩放:color([ R/255, G/255, B/255 ]) { ... }
[注意: 需要版本 2011.12] 颜色也可以用名称定义(不区分大小写)。例如,要创建红色球体,你可以写 color("red") sphere(5);
。Alpha 作为命名颜色的附加参数指定:color("Blue",0.5) cube(5);
[注意: 需要版本 2019.05] 十六进制值可以用 4 种格式给出,#rgb
、#rgba
、#rrggbb
和 #rrggbbaa
。如果在十六进制值和单独的 alpha 参数中都给出了 alpha 值,则 alpha 参数优先。
警告: alpha 处理(透明度)对顺序敏感。透明物体必须列在不透明物体之后才能正确显示它们。某些涉及多个透明物体的组合无法正确处理。参见问题 #1390。
可用的颜色名称来自万维网联盟的 SVG 颜色列表。颜色名称的图表如下,
(请注意,灰色/灰色的两种拼写,包括石板灰色/石板灰色等都是有效的):
|
|
|
|
|
这是一个绘制波浪形多色物体的代码片段
for(i=[0:36]) {
for(j=[0:36]) {
color( [0.5+sin(10*i)/2, 0.5+sin(10*j)/2, 0.5+sin(10*(i+j))/2] )
translate( [i, j, 0] )
cube( size = [1, 1, 11+10*cos(10*i)*sin(10*j)] );
}
}
↗ 由于 -1<=sin(x)<=1,则 0<=(1/2 + sin(x)/2)<=1,允许分配给颜色的 RGB 成分保持在 [0,1] 区间内。
在您想根据参数可选地设置颜色时,可以使用以下技巧
module myModule(withColors=false) {
c=withColors?"red":undef;
color(c) circle(r=10);
}
将颜色名称设置为 undef 会保留默认颜色。
[注意: 需要版本 2015.03]
Offset 从现有轮廓生成一个新的 2d 内部或外部轮廓。有两种操作模式:径向和增量。
- 径向方法创建新的轮廓,就像某个半径的圆围绕原始轮廓的外部(r > 0)或内部(r < 0)旋转一样。
- 增量方法创建一个新的轮廓,其边与原始轮廓的固定距离向外(delta > 0)或向内(delta < 0)。
构造方法生成一个轮廓,该轮廓在原始轮廓的内部或外部。对于使用增量的轮廓,当轮廓绕过拐角时,可以给它提供一个可选的倒角。
Offset 通过从原始轮廓中减去负偏移构造,或从正偏移构造中减去原始轮廓来创建薄壁,非常有用。
Offset 可用于模拟一些常见的实体建模操作
- 圆角:
offset(r=-3) offset(delta=+3)
会将所有内部(凹)拐角圆角化,并保持平壁不变。但是,直径小于 2*r 的孔会消失。 - 圆形:
offset(r=+3) offset(delta=-3)
会将所有外部(凸)拐角圆角化,并保持平壁不变。但是,厚度小于 2*r 的壁会消失。
- 参数
第一个参数可以不带名称传递,在这种情况下,它被视为下面的 r 参数。如果使用其他参数,则必须命名它们。
r 或 delta
- 数字。要偏移多边形的量。为负值时,多边形向内偏移。
- r(如果未命名,则为默认参数)指定围绕轮廓旋转的圆的半径,无论是在内部还是外部。此模式会生成圆角。名称可以省略;即,
offset(c)
等效于offset(r=c)
。 - delta 指定新轮廓与原始轮廓的距离,因此会复制角形拐角。在轮廓会与自身交叉的地方,不会生成任何向内的周长。
- r(如果未命名,则为默认参数)指定围绕轮廓旋转的圆的半径,无论是在内部还是外部。此模式会生成圆角。名称可以省略;即,
- 倒角
- 布尔值。(默认值为 false)当使用 delta 参数时,此标志定义边是倒角(用直线切断)还是不倒角(扩展到它们的交点)。此参数对径向偏移没有影响。
$fa、$fs 和 $fn
- 圆形分辨率 特殊变量可用于控制径向偏移生成的曲线的平滑度或面大小。它们对增量偏移没有影响。
示例
// Example 1
linear_extrude(height = 60, twist = 90, slices = 60) {
difference() {
offset(r = 10) {
square(20, center = true);
}
offset(r = 8) {
square(20, center = true);
}
}
}
// Example 2
module fillet(r) {
offset(r = -r) {
offset(delta = r) {
children();
}
}
}
[注意: 需要版本 开发快照]
Fill 会移除多边形的孔,而不会更改轮廓。对于凸多边形,结果与 hull() 相同。
示例
// Example 1
t = "OpenSCAD";
linear_extrude(15) {
text(t, 50);
}
color("darkslategray") {
linear_extrude(2) {
offset(4) {
fill() {
text(t, 50);
}
}
}
}
显示子节点的闵可夫斯基和。
用法示例
假设您有一个扁平的盒子,并且想要一个圆角边缘。有多种方法可以做到这一点(例如,请参阅下面的 hull),但闵可夫斯基很优雅。取您的盒子和一个圆柱体
$fn=50;
cube([10,10,1]);
cylinder(r=2,h=1);
然后,对它们进行闵可夫斯基和(注意,盒子的外尺寸现在为 10+2+2 = 14 单位乘以 14 单位乘以 2 单位高,因为对象的 高度已相加)
$fn=50;
minkowski()
{
cube([10,10,1]);
cylinder(r=2,h=1);
}
注意:原点第二个对象的点用于加法。以下闵可夫斯基和是不同的:第一个从圆柱体中扩展原始立方体 +1 在 -x、+x、-y、+y 中,从圆柱体中扩展 0.5 单位在 -z、+z 中。第二个从圆柱体中扩展它 +1 在 -x、+x、-y、+y 和 +z 中,但在 -z 中扩展 0 单位从圆柱体中扩展。
minkowski() {
cube([10, 10, 1]);
cylinder(1, center=true);
}
minkowski() {
cube([10, 10, 1]);
cylinder(1);
}
警告:对于 $fn 的高值,闵可夫斯基和最终可能会消耗大量的 CPU 和内存,因为它必须将每个元素的每个子节点与每个其他元素的所有节点结合起来。所以,例如,如果 $fn=100 并且您组合了两个圆柱体,那么它不仅执行 200 次操作(就像两个独立的圆柱体一样),而是执行 100*100 = 10000 次操作。
警告:如果其中一个输入是复合的,例如
{
translate([0, 0, collar])
sphere(ball);
cylinder(collar, ball, ball);
}
它可能被视为两个独立的输入,导致输出过大,并且表面之间具有应相对于彼此保持不变的特征。如果是这样,请使用 union()。
显示子节点的凸包。
用法示例
hull() {
translate([15,10,0]) circle(10);
circle(10);
}
2D 对象的船体使用它们在 xy 平面上的投影(阴影),并在 xy 平面生成结果。它们的 Z 高度不会在操作中使用。
参考两个圆柱体的凸包的图示,在两个 2D 圆上使用 hull()
并在 linear_extrude 上计算效率更高。将生成的 2D 形状挤出到 3D 形状,而不是在两个圆柱体上使用 hull()
,即使生成的对象看起来相同。涉及 hull()
的复杂几何体可以通过在 2D 中开始,如果可能的话,以更快的速度渲染。
OpenSCAD 用户手册/OpenSCAD 语言
-
并集(或)
圆 + 正方形 -
差集(与非)
正方形 - 圆形 -
差集(与非)
圆形 - 正方形 -
交集(与)
圆形 - (圆形 - 正方形)
union() {square(10);circle(10);} // square or circle
difference() {square(10);circle(10);} // square and not circle
difference() {circle(10);square(10);} // circle and not square
intersection(){square(10);circle(10);} // square and circle
-
并集(或)
球体 + 立方体 -
差集(与非)
立方体 - 球体 -
差集(与非)
球体 - 立方体 -
交集(与)
球体 - (球体 - 立方体)
union() {cube(12, center=true); sphere(8);} // cube or sphere
difference() {cube(12, center=true); sphere(8);} // cube and not sphere
difference() {sphere(8); cube(12, center=true);} // sphere and not cube
intersection(){cube(12, center=true); sphere(8);} // cube and sphere
创建所有子节点的并集。这是所有子节点的总和(逻辑或)。
可与 2D 或 3D 对象一起使用,但不要混合使用。
//Usage example:
union() {
cylinder (h = 4, r=1, center = true, $fn=100);
rotate ([90,0,0]) cylinder (h = 4, r=0.9, center = true, $fn=100);
}
说明:union 在未使用时是隐式的。但在 difference 中,必须使用 union 来将第一个子节点分组为一个,例如。
注意:对于所有并集,无论是显式还是隐式,合并的外部面都必须不重合。不遵守此规则会导致设计行为未定义,并可能导致生成的模型非流形(包含零体积部分或反向部分),这通常会导致警告,有时还会导致部分设计从渲染输出中移除。(这也会导致预览过程中出现闪烁效果。)此要求不是错误,而是浮点比较的内在属性以及无法准确表示如大多数旋转产生的无理数的根本原因。例如,以下是一个无效的 OpenSCAD 程序,至少会在大多数平台上导致警告。
// Invalid!
size = 10;
rotation = 17;
union() {
rotate([rotation, 0, 0])
cube(size);
rotate([rotation, 0, 0])
translate([0, 0, size])
cube([2, 3, 4]);
}
解决方案是在合并相邻面时始终使用一个称为 epsilon 的小值来保证重叠。请注意,在两个位置使用了 0.01 的 eps 值,以便外部结果等效于预期结果。
// Correct!
size = 10;
rotation = 17;
eps = 0.01;
union() {
rotate([rotation, 0, 0])
cube(size);
rotate([rotation, 0, 0])
translate([0, 0, size-eps])
cube([2, 3, 4+eps]);
}
从第一个子节点中减去第二个(以及所有后续)子节点(逻辑非)。
可与 2D 或 3D 对象一起使用,但不要混合使用。
Usage example:
difference() {
cylinder (h = 4, r=1, center = true, $fn=100);
rotate ([90,0,0]) cylinder (h = 4, r=0.9, center = true, $fn=100);
}
注意:差集操作中要移除的表面必须重叠,并且要移除的负面部分必须完全延伸到它要移除其表面的体积之外。不遵守此规则会导致预览伪像,并可能导致非流形渲染警告或渲染输出中部分内容被移除。请参阅上面并集中的描述,了解为什么需要这样做,以及一个使用小的 epsilon 值来执行此操作的示例。
注意,在第二个实例中,结果是添加了第一个和第二个子节点的并集。
// Usage example for difference of multiple children:
$fn=90;
difference(){
cylinder(r=5,h=20,center=true);
rotate([00,140,-45]) color("LightBlue") cylinder(r=2,h=25,center=true);
rotate([00,40,-50]) cylinder(r=2,h=30,center=true);
translate([0,0,-10])rotate([00,40,-50]) cylinder(r=1.4,h=30,center=true);
}
// second instance with added union
translate([10,10,0]){
difference(){
union(){ // combine 1st and 2nd children
cylinder(r=5,h=20,center=true);
rotate([00,140,-45]) color("LightBlue") cylinder(r=2,h=25,center=true);
}
rotate([00,40,-50]) cylinder(r=2,h=30,center=true);
translate([0,0,-10])rotate([00,40,-50]) cylinder(r=1.4,h=30,center=true);
}
}
创建所有子节点的交集。这保留了重叠部分(逻辑与)。
仅保留所有子节点共有的或共享的区域。
可与 2D 或 3D 对象一起使用,但不要混合使用。
//Usage example:
intersection() {
cylinder (h = 4, r=1, center = true, $fn=100);
rotate ([90,0,0]) cylinder (h = 4, r=0.9, center = true, $fn=100);
}
警告:使用渲染时,始终计算此树的 CSG 模型(即使在 OpenCSG 预览模式下)。这会使预览非常缓慢,并且 OpenSCAD 似乎会挂起/冻结。
Usage example:
render(convexity = 1) { ... }
凸性 | 整数。凸性参数指定与物体相交的光线可能穿透的前后面数量的最多数量。此参数仅在 OpenCSG 预览模式下正确显示物体时才需要,对多面体渲染没有影响。 |
此图像显示了一个凸性为 4 的二维形状,因为红色指示的光线与二维形状最多相交 4 次。三维形状的凸性将以类似方式确定。将其设置为 10 对于大多数情况应该可以正常工作。
OpenSCAD 用户手册/OpenSCAD 语言
评估范围或向量中的每个值,将其应用于以下操作。
for(variable = [start : increment : end]) for(variable = [start : end]) for(variable = [vector])
参数
- 作为范围[ 开始 : <增量 : > 结束 ](请参阅有关范围的部分)。
- 注意:对于范围,值由冒号分隔,而不是向量中使用的逗号。
- 开始 - 初始值
- 增量或步长 - 增加值的量,可选,默认值 = 1
- 结束 - 当下一个值超过结束值时停止。
- 示例:
for (a =[3:5])echo(a); // 3 4 5 for (a =[3:0]){echo(a);} // 0 1 2 3 start > end is invalid, deprecated by 2015.3 for (a =[3:0.5:5])echo(a); // 3 3.5 4 4.5 5 for (a =[0:2:5])echo(a); // 0 2 4 a never equals end for (a =[3:-2:-1])echo(a); // 3 1 -1 negative increment requires 2015.3 be sure end < start
- 作为向量
- 针对向量的每个元素评估操作。
for (a =[3,4,1,5])echo(a); // 3 4 1 5 for (a =[0.3,PI,1,99]){echo(a);} // 0.3 3.14159 1 99 x1=2; x2=8; x3=5.5; for (a =[x1,x2,x3]){echo(a);} // 2 8 5.5 for (a =[[1,2],6,"s",[[3,4],[5,6]]])echo(a); // [1,2] 6 "s" [[3,4],[5,6]]
- 向量可以在其他地方描述,例如其他语言中的“for each”。
animals = ["elephants", "snakes", "tigers", "giraffes"]; for(animal = animals) echo(str("I've been to the zoo and saw ", animal)); // "I've been to the zoo and saw elephants", for each animal
for() 是一个运算符。如果运算符的范围内有多个操作,则运算符需要使用大括号 {}。操作以分号结尾,运算符没有。
for() 不是关于变量在一个范围内只能有一个值的规则的例外。每个评估都有自己的范围,允许任何变量具有唯一的值。不,你仍然不能执行 a=a+1;
请记住,这不是一种迭代语言,for() 不会以编程意义上的循环方式执行,它会为范围/向量中的每个项目构建一个对象树,在每个分支中,“变量”都是一个特定的、独立的实例化或范围。
因此
for (i=[0:3]) translate([i*10,0,0]) cube(i+1);
生成:[请参阅设计/显示-CSG-树菜单]
group() { group() { multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { cube(size = [1, 1, 1], center = false); } multmatrix([[1, 0, 0, 10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { cube(size = [2, 2, 2], center = false); } multmatrix([[1, 0, 0, 20], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { cube(size = [3, 3, 3], center = false); } multmatrix([[1, 0, 0, 30], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { cube(size = [4, 4, 4], center = false); } } }
虽然 group() 是按顺序构建的,但 for() 的所有实例都作为独立实体存在,它们不会按顺序迭代一段代码。
- 嵌套的 for()
虽然嵌套多个 for() 语句(例如)是合理的
for(z=[-180:45:+180]) for(x=[10:5:50]) rotate([0,0,z]) translate([x,0,0]) cube(1);
但是,所有范围/向量都可以包含在同一个 for() 运算符中。
for ( variable1 = <range or vector> , variable2 = <range or vector> ) <do something using both variables>
example for() nested 3 deep color_vec = ["black","red","blue","green","pink","purple"]; for (x = [-20:10:20] ) for (y = [0:4] )color(color_vec[y]) for (z = [0,4,10] ) {translate([x,y*5-10,z])cube();} shorthand nesting for same result color_vec = ["black","red","blue","green","pink","purple"]; for (x = [-20:10:20], y = [0:4], z = [0,4,10] ) translate([x,y*5-10,z]){color(color_vec[y])cube();}
- 使用向量向量的示例
example 1 - iteration over a vector of vectors (rotation) for(i = [ [ 0, 0, 0], [ 10, 20, 300], [200, 40, 57], [ 20, 88, 57] ]) { rotate(i) cube([100, 20, 20], center = true); }
example 2 - iteration over a vector of vectors (translation) for(i = [ [ 0, 0, 0], [10, 12, 10], [20, 24, 20], [30, 36, 30], [20, 48, 40], [10, 60, 50] ]) { translate(i) cube([50, 15, 10], center = true); }
example 3 - iteration over a vector of vectors for(i = [ [[ 0, 0, 0], 20], [[10, 12, 10], 50], [[20, 24, 20], 70], [[30, 36, 30], 10], [[20, 48, 40], 30], [[10, 60, 50], 40] ]) { translate([i[0][0], 2*i[0][1], 0]) cube([10, 15, i[1]]); }
遍历范围或向量中的值,并创建由每次传递创建的对象的交集。
除了为每次传递创建独立的实例之外,标准for()还会对所有这些实例进行分组,从而创建一个隐式的并集。intersection_for()是一个解决方法,因为隐式的并集会阻止使用标准for()和intersection()语句组合获得预期结果。
intersection_for()使用相同的参数,并与for 循环的工作方式相同,只是消除了隐式的并集。
示例 1 - 循环遍历范围 | ||
intersection_for(n = [1 : 6])
{
rotate([0, 0, n * 60])
{
translate([5,0,0])
sphere(r=12);
}
}
|
示例 2 - 旋转: | ||
intersection_for(i = [ [ 0, 0, 0],
[ 10, 20, 300],
[200, 40, 57],
[ 20, 88, 57] ])
{
rotate(i)
cube([100, 20, 20], center = true);
}
|
执行测试以确定子范围内的操作是否应执行。
非常重要。你无法更改变量的值。如果你在括号内更新变量的值,新值将在你退出该范围时丢失。
if (test) scope1 if (test){scope1} if (test) scope1 else scope2 if (test){scope1} else {scope2}
- 参数
- 测试:通常是一个布尔表达式,但可以是任何值或变量。
- 请参阅此处了解值的真或假状态。
- 请参阅此处了解布尔和逻辑运算符。
- 不要将赋值运算符 '=' 与相等运算符 '==' 混淆。
- 范围 1:测试为真时要执行的一个或多个操作。
- 范围 2:测试为假时要执行的一个或多个操作。
- 测试:通常是一个布尔表达式,但可以是任何值或变量。
if (b==a) cube(4); if (b<a) {cube(4); cylinder(6);} if (b&&a) {cube(4); cylinder(6);} if (b!=a) cube(4); else cylinder(3); if (b) {cube(4); cylinder(6);} else {cylinder(10,5,5);} if (!true){cube(4); cylinder(6);} else cylinder(10,5,5); if (x>y) cube(1, center=false); else {cube(size = 2, center = true);} if (a==4) {} else echo("a is not 4"); if ((b<5)&&(a>8)) {cube(4);} else {cylinder(3);} if (b<5&&a>8) cube(4); else cylinder(3);
自 2015.03 起,现在可以在任何范围内分配变量。请注意,分配仅在其定义的范围内有效 - 你仍然不允许将值泄露到外部范围。请参阅变量范围了解更多详细信息。
- 嵌套的 if
if()部分和else部分的范围都可能反过来包含if()语句。此嵌套可以达到多个深度。
if (test1) { scope1 if (test2) {scope2.1} else {scope2.2} } else { scope2 if (test3) {scope3.1} else {scope3.2} }
当范围 1 和范围 2 仅包含 if() 语句时,可以删除外部的一组大括号。
if (test1) if (test2) {scope2.1} else {scope2.2} else if (test3) {scope3.1} else {scope3.2}
一种演变是
if(test1) {scope1} else if(test2) {scope2} else if(test3) {scope3} else if(test4) {scope4} else {scope5}
请注意,else 和 if 是两个单独的单词。当向下处理测试链时,第一个真值使用其范围。所有后续测试都将跳过。
- 示例
if((k<8)&&(m>1)) cube(10); else if(y==6) {sphere(6);cube(10);} else if(y==7) color("blue")sphere(5); else if(k+m!=8) {cylinder(15,5,0);sphere(8);} else color("green"){cylinder(12,5,0);sphere(8);}
一个使用测试来确定要返回哪个值的函数。
a = test ? TrueValue : FalseValue ; echo( test ? TrueValue : FalseValue );
- 参数
- 测试:通常是一个布尔表达式,但可以是任何值或变量。
- 请参阅此处了解值的真或假状态。
- 请参阅此处了解布尔和逻辑运算符。
- 不要将赋值 '=' 与相等 '==' 混淆。
- TrueValue:测试为真时要返回的值。
- FalseValue:测试为假时要返回的值。
- OpenSCAD 中的值可以是数字(如 42)、布尔值(如 true)、字符串(如 "foo")、向量(如 [1,2,3])或未定义值(undef)。值可以存储在变量中,作为函数参数传递,并作为函数结果返回。
- 测试:通常是一个布尔表达式,但可以是任何值或变量。
这就像?:来自 C 类编程语言系列的运算符。
- 示例
a=1; b=2; c= a==b ? 4 : 5 ; // 5 a=1; b=2; c= a==b ? "a==b" : "a!=b" ; // "a!=b" TrueValue = true; FalseValue = false; a=5; test = a==1; echo( test ? TrueValue : FalseValue ); // false L = 75; R = 2; test = (L/R)>25; TrueValue = [test,L,R,L/R,cos(30)]; FalseValue = [test,L,R,sin(15)]; a1 = test ? TrueValue : FalseValue ; // [true, 75, 2, 37.5, 0.866025]
Some forms of tail-recursion elimination are supported.
支持递归函数调用。使用条件 "... ? ... : ..." 可以确保递归的终止。注意:存在内置的递归限制以防止应用程序崩溃。如果达到限制,则函数返回 undef。
- 示例
// recursion - find the sum of the values in a vector (array) by calling itself // from the start (or s'th element) to the i'th element - remember elements are zero based function sumv(v, i, s = 0) = (i == s ? v[i] : v[i] + sumv(v, i-1, s)); vec=[ 10, 20, 30, 40 ]; echo("sum vec=", sumv(vec, 2, 1)); // calculates 20+30=50
多个嵌套条件可能难以理解。将它们格式化为多行缩进的“if/else”语句更清晰。
// find the maximum value in a vector function maxv(v, m=-999999999999, i=0) = (i == len(v) ) ? m : (m > v[i]) ? maxv(v, m, i+1) : maxv(v, v[i], i+1); v=[7,3,9,3,5,6]; echo("max",maxv(v)); // ECHO: "max", 9
[已弃用: assign() 将在未来版本中删除。现在可以在任何地方赋值变量。如果您更喜欢这种设置值的方式,可以使用新的 Let 语句 代替。]
为子树设置变量的新值。
- 参数
- 应该(重新)赋值的变量
- 示例
for (i = [10:50])
{
assign (angle = i*360/20, distance = i*10, r = i*2)
{
rotate(angle, [1, 0, 0])
translate([0, distance, 0])
sphere(r = r);
}
}
for (i = [10:50])
{
angle = i*360/20;
distance = i*10;
r = i*2;
rotate(angle, [1, 0, 0])
translate([0, distance, 0])
sphere(r = r);
}
[注意: 需要 2019.05 版本]
为子树设置变量的新值。参数按顺序计算,可能相互依赖(与已弃用的 assign() 语句相反)。
- 参数
- 应该设置的变量
- 示例
for (i = [10:50])
{
let (angle = i*360/20, r= i*2, distance = r*5)
{
rotate(angle, [1, 0, 0])
translate([0, distance, 0])
sphere(r = r);
}
}
标量算术运算符以数字作为操作数,并生成一个新数字。
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 模运算 |
^ | 指数 [注意: 需要 2021.01 版本] |
-
也可以用作前缀运算符来否定一个数字。
在 2021.01 版本之前,使用内置数学函数 pow()
而不是 ^
指数运算符。
? | 条件运算符 |
使用示例 |
a=[for(i=[0:10])i%2];
echo(a);//ECHO: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
for(i=[0:10]) translate([i,i%2?0:5])cube(1); // move every even up
余数为 0 或 1 偶数/奇数 |
关系运算符从两个操作数生成布尔结果。
< | 小于 |
<= | 小于或等于 |
== | 等于 |
!= | 不等于 |
>= | 大于或等于 |
> | 大于 |
如果两个操作数都是简单数字,则含义不言而喻。
如果两个操作数都是字符串,则字母排序决定相等性和顺序。例如,"ab" > "aa" > "a"。
如果两个操作数都是布尔值,则 true > false。在布尔值和数字之间的不等式比较中,true 被视为 1,false 被视为 0。涉及布尔值的其它不等式测试返回 false。
如果两个操作数都是向量,则当向量相同时,相等性测试返回 true,否则返回 false。涉及一个或两个向量的不等式测试总是返回 false,因此例如 [1] < [2] 为 false。
不同类型总是使用 '==' 和 '!=' 测试为不相等。不同类型之间的不等式比较(除了上面提到的布尔值和数字外)总是导致 false。请注意,[1] 和 1 是不同类型,因此 [1] == 1 为 false。
undef
不等于任何东西,除了 undef。涉及 undef 的不等式比较导致 false。
nan
不等于任何东西(甚至不等于自身),并且所有不等式测试都产生 false。参见 数字。
所有逻辑运算符都以布尔值作为操作数,并产生一个布尔值。非布尔值在运算符计算之前被转换为布尔值。
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑一元非 |
由于 [false]
为 true
,因此 false || [false]
也为 true
。
逻辑运算符对向量的处理方式不同于关系运算符
[1, 1] > [0, 2]
为 false
,但
[false, false] && [false, false]
为 true
。
该?:运算符可以用于有条件地计算一个或另一个表达式。它的工作方式与?:来自 C 类编程语言系列的运算符。
? | 条件运算符 |
使用示例 |
a=1;
b=2;
c= a==b ? 4 : 5;
如果 a 等于 b,则 c 设置为 4,否则 c 设置为 5。 |
向量-数字运算符以向量和数字作为操作数,并生成一个新向量。
* | 将所有向量元素乘以数字 |
/ | 将所有向量元素除以数字 |
- 示例
L = [1, [2, [3, "a"] ] ]; echo(5*L); // ECHO: [5, [10, [15, undef]]]
向量运算符以向量作为操作数,并生成一个新向量。
+ | 逐元素加法 |
- | 逐元素减法 |
-
也可以用作前缀运算符来逐元素否定一个向量。
- 示例
L1 = [1, [2, [3, "a"] ] ]; L2 = [1, [2, 3] ]; echo(L1+L1); // ECHO: [2, [4, [6, undef]]] echo(L1+L2); // ECHO: [2, [4, undef]]
使用 + 或 - 和大小不同的向量操作数会生成一个结果向量,该结果向量的尺寸与较小向量相同。
如果乘法的两个操作数都是简单向量,则结果将根据 点积 的线性代数规则生成一个数字。c = u*v;
导致 。如果操作数的大小不匹配,则结果为 undef
。
如果乘法的操作数之一或两者都是矩阵,则结果将根据 矩阵积 的线性代数规则生成一个简单向量或矩阵。在下文中,A, B, C... 是矩阵,u, v, w... 是向量。下标 i, j 表示元素索引。
对于大小为 n × m 的矩阵 A 和大小为 m × p 的矩阵 B,它们的积 C = A*B;
是一个大小为 n × p 的矩阵,其元素为
.
C = B*A;
导致 undef
,除非 n = p。
对于大小为 n × m 的矩阵 A 和大小为 m 的向量 v,它们的乘积 u = A*v;
是一个大小为 n 的向量,其元素为
.
在线性代数中,这是 矩阵和列向量的乘积。
对于大小为 n 的向量 v 和大小为 n × m 的矩阵 A,它们的乘积 u = v*A;
是一个大小为 m 的向量,其元素为
.
在线性代数中,这是行向量和矩阵的乘积。
矩阵乘法不满足交换律: , .
数学函数
[edit | edit source]三角函数
[edit | edit source]三角函数使用 C 语言的数学函数,这些函数又基于二进制浮点数学,在计算过程中使用实数的近似值。OpenSCAD 的数学函数使用 C++ 的 'double' 类型,在 Value.h/Value.cc 内部,
有关 C 库数学函数的具体细节,例如有效输入/输出范围,可以在 Open Group 网站找到 math.h & acos
cos
[edit | edit source]数学 余弦 函数,以度为单位。参见 余弦
参数
- <degrees>
- 十进制数。以度为单位的角度。
用法示例 | |
for(i=[0:36])
translate([i*10,0,0])
cylinder(r=5,h=cos(i*10)*50+60);
|
sin
[edit | edit source]数学 正弦 函数。参见 正弦
参数
- <degrees>
- 十进制数。以度为单位的角度。
使用示例 1 | |
for (i = [0:5]) {
echo(360*i/6, sin(360*i/6)*80, cos(360*i/6)*80);
translate([sin(360*i/6)*80, cos(360*i/6)*80, 0 ])
cylinder(h = 200, r=10);
}
|
使用示例 2 | |
for(i=[0:36])
translate([i*10,0,0])
cylinder(r=5,h=sin(i*10)*50+60);
|
tan
[edit | edit source]数学 正切 函数。参见 正切
参数
- <degrees>
- 十进制数。以度为单位的角度。
用法示例 | |
for (i = [0:5]) {
echo(360*i/6, tan(360*i/6)*80);
translate([tan(360*i/6)*80, 0, 0 ])
cylinder(h = 200, r=10);
}
|
acos
[edit | edit source]数学 反余弦,或 余弦的逆函数,以度为单位表示。参见:反三角函数
asin
[edit | edit source]数学 反正弦,或 正弦的逆函数,以度为单位表示。参见:反三角函数
atan
[edit | edit source]数学 反正切,或 正切的逆函数,函数。返回 x 的反正切函数的主值,以度为单位表示。atan
无法区分 y/x 和 -y/-x,返回 -90 到 +90 之间的角度。参见:atan2 以及 反三角函数
atan2
[edit | edit source]数学 二参数反正切 函数 atan2(y,x),范围涵盖完整的 360 度。此函数以度为单位返回 x 轴和向量 (x,y) 之间的完整角度 (0-360)。
使用示例
atan2(5.0,-5.0); //result: 135 degrees. atan() would give -45 atan2(y,x); //angle between (1,0) and (x,y) = angle around z-axis
其他数学函数
[edit | edit source]abs
[edit | edit source]数学 绝对值 函数。返回带符号十进制数的正值。
使用示例
abs(-5.0); returns 5.0 abs(0); returns 0.0 abs(8.0); returns 8.0
ceil
[edit | edit source]数学 向上取整 函数。
返回下一个最高的整数,如果需要则向上舍入值。
参见:向上取整函数
echo(ceil(4.4),ceil(-4.4)); // produces ECHO: 5, -4
concat
[edit | edit source][注意: 需要版本 2015.03]
返回一个新向量,该向量是将提供的向量的元素追加后的结果。
如果参数是一个向量,则向量的元素将被逐个附加到结果向量中。在这种情况下,字符串与向量不同。
使用示例
echo(concat("a","b","c","d","e","f")); // produces ECHO: ["a", "b", "c", "d", "e", "f"] echo(concat(["a","b","c"],["d","e","f"])); // produces ECHO: ["a", "b", "c", "d", "e", "f"] echo(concat(1,2,3,4,5,6)); // produces ECHO: [1, 2, 3, 4, 5, 6]
向量组
echo(concat([ [1],[2] ], [ [3] ])); // produces ECHO: [[1], [2], [3]]
注意:传递给函数的所有向量都会降低一个嵌套级别。当添加类似单个元素 [x, y, z] 元组(也是向量)的东西时,元组需要用向量括起来(即额外的括号)才能进行拼接。在下面的例子中,第四个点被添加到多边形路径中,该路径以前类似于一个三角形,现在它变成了一个正方形。
polygon(concat([[0,0],[0,5],[5,5]], [[5,0]]));
与字符串对比
echo(concat([1,2,3],[4,5,6])); // produces ECHO: [1, 2, 3, 4, 5, 6] echo(concat("abc","def")); // produces ECHO: ["abc", "def"] echo(str("abc","def")); // produces ECHO: "abcdef"
计算两个向量在 3D 或 2D 空间中的叉积。如果两个向量都在 3D 空间中,结果将是一个垂直于这两个输入向量的向量。如果两个向量都在 2D 空间中,它们的叉积的形式为 [0,0,z],叉积函数只返回叉积的 z 值。
cross([x,y], [u,v]) = x*v - y*u
请注意,这是 2x2 矩阵 [[x,y],[u,v]] 的行列式。使用任何其他类型、长度不同于 2 或 3 的向量,或者长度不一致的向量会导致 'undef'。
使用示例
echo(cross([2, 3, 4], [5, 6, 7])); // produces ECHO: [-3, 6, -3] echo(cross([2, 1, -3], [0, 4, 5])); // produces ECHO: [17, -10, 8] echo(cross([2, 1], [0, 4])); // produces ECHO: 8 echo(cross([1, -3], [4, 5])); // produces ECHO: 17 echo(cross([2, 1, -3], [4, 5])); // produces ECHO: undef echo(cross([2, 3, 4], "5")); // produces ECHO: undef
对于 2D 或 3D 中的任意两个向量 a 和 b,以下成立:
cross(a,b) == -cross(b,a)
数学 exp 函数。返回 x 的以 e 为底的指数函数,即 e 的 x 次方。参见:指数
echo(exp(1),exp(ln(3)*4)); // produces ECHO: 2.71828, 81
数学 floor 函数。floor(x) = 不大于 x 的最大整数。
参见:向下取整函数
echo(floor(4.4),floor(-4.4)); // produces ECHO: 4, -5
数学 自然对数。参见:自然对数
数学 长度 函数。返回数组、向量或字符串参数的长度。
使用示例
str1="abcdef"; len_str1=len(str1); echo(str1,len_str1); a=6; len_a=len(a); echo(a,len_a); array1=[1,2,3,4,5,6,7,8]; len_array1=len(array1); echo(array1,len_array1); array2=[[0,0],[0,1],[1,0],[1,1]]; len_array2=len(array2); echo(array2,len_array2); len_array2_2=len(array2[2]); echo(array2[2],len_array2_2);
结果
WARNING: len() parameter could not be converted in file , line 4 ECHO: "abcdef", 6 ECHO: 6, undef ECHO: [1, 2, 3, 4, 5, 6, 7, 8], 8 ECHO: [[0, 0], [0, 1], [1, 0], [1, 1]], 4 ECHO: [1, 0], 2
此函数允许(例如)解析数组、向量或字符串。
使用示例
str2="4711"; for (i=[0:len(str2)-1]) echo(str("digit ",i+1," : ",str2[i]));
结果
ECHO: "digit 1 : 4" ECHO: "digit 2 : 7" ECHO: "digit 3 : 1" ECHO: "digit 4 : 1"
请注意,当简单变量作为参数传递时,len() 函数未定义并会引发警告。
这在处理模块参数时很有用,类似于形状可以定义为单个数字,也可以定义为 [x,y,z] 向量;例如 cube(5) 或 cube([5,5,5])。
例如
module doIt(size) { if (len(size) == undef) { // size is a number, use it for x,y & z. (or could be undef) do([size,size,size]); } else { // size is a vector, (could be a string but that would be stupid) do(size); } } doIt(5); // equivalent to [5,5,5] doIt([5,5,5]); // similar to cube(5) v's cube([5,5,5])
[注意: 需要版本 2015.03]
在表达式中对变量进行顺序赋值。以下表达式在 let 赋值的上下文中进行计算,并且可以使用这些变量。这主要用于通过将中间结果赋值给变量来使复杂的表达式更易读。
参数
let (var1 = value1, var2 = f(var1), var3 = g(var1, var2)) expression
用法示例
echo(let(a = 135, s = sin(a), c = cos(a)) [ s, c ]); // ECHO: [0.707107, -0.707107]
Let 也可以用来在 函数 中创建变量。 (另请参见: "Let 语句")
数学 对数,以 10 为底。例如:log(1000) = 3。参见:对数
在表中查找值,如果不存在完全匹配的值,则进行线性插值。第一个参数是要查找的值。第二个是查找表——一个键值对向量。
参数
- 键
- 查找键
- <key,value> 数组
- 键和值
存在一个错误,即超出范围的键将返回列表中的第一个值。更新版本的 Openscad 应该使用表的顶端或底端,而不是使用第一个值。
用法示例: 创建一个由不同高度的圆柱体组成的 3D 图表。
function get_cylinder_h(p) = lookup(p, [
[ -200, 5 ],
[ -50, 20 ],
[ -20, 18 ],
[ +80, 25 ],
[ +150, 2 ]
]);
for (i = [-100:5:+100]) {
// echo(i, get_cylinder_h(i));
translate([ i, 0, -30 ]) cylinder(r1 = 6, r2 = 2, h = get_cylinder_h(i)*3);
}
|
返回参数中的最大值。如果给定单个向量作为参数,则返回该向量的最大元素。
参数
max(n,n{,n}...) max(vector)
- <n>
- 两个或多个小数
- <vector>
- 单个小数向量 [注意: 需要版本 2014.06].
用法示例
max(3.0,5.0) max(8.0,3.0,4.0,5.0) max([8,3,4,5])
结果
5 8 8
返回参数中的最小值。如果给定单个向量作为参数,则返回该向量的最小元素。
参数
min(n,n{,n}...) min(vector)
- <n>
- 两个或多个小数
- <vector>
- 单个小数向量 [注意: 需要版本 2014.06].
用法示例
min(3.0,5.0) min(8.0,3.0,4.0,5.0) min([8,3,4,5])
结果
3 3 3
仅出于清晰度而包含在此文档中。OpenSCAD 中的 '取模' 操作以运算符 %
存在,而不是以函数形式存在。参见 取模运算符 (%)
返回向量的 欧几里得范数。请注意,这将返回实际的数字长度,而 len 将返回向量或数组中的元素数量。
使用示例
a=[1,2,3,4]; b="abcd"; c=[]; d=""; e=[[1,2,3,4],[1,2,3],[1,2],[1]]; echo(norm(a)); //5.47723 echo(norm(b)); //undef echo(norm(c)); //0 echo(norm(d)); //undef echo(norm(e[0])); //5.47723 echo(norm(e[1])); //3.74166 echo(norm(e[2])); //2.23607 echo(norm(e[3])); //1
结果
ECHO: 5.47723 ECHO: undef ECHO: 0 ECHO: undef ECHO: 5.47723 ECHO: 3.74166 ECHO: 2.23607 ECHO: 1
数学 幂 函数。
从版本 2021.01 开始,可以使用 指数运算符 ^
替代。
参数
- <base>
- 小数。底数。
- <exponent>
- 小数。指数。
使用示例
for (i = [0:5]) { translate([i*25,0,0]) { cylinder(h = pow(2,i)*5, r=10); echo (i, pow(2,i)); } }
echo(pow(10,2)); // means 10^2 or 10*10 // result: ECHO: 100 echo(pow(10,3)); // means 10^3 or 10*10*10 // result: ECHO: 1000 echo(pow(125,1/3)); // means 125^(0.333...), which calculates the cube root of 125 // result: ECHO: 5
随机数生成器。生成一个伪随机数的常量向量,非常类似于数组。这些数字是双精度浮点数,而不是整数。当只生成一个数字时,仍然使用 variable[0] 调用它。
参数
- min_value
- 随机数范围的最小值
- max_value
- 随机数范围的最大值
- value_count
- 要作为向量返回的随机数数量
- seed_value (可选)
- 随机数生成器的种子值,用于生成可重复的结果。在 2015 年后期之前的版本中,seed_value 会被四舍五入到最接近的整数。
使用示例
// get a single number single_rand = rands(0,10,1)[0]; echo(single_rand);
// get a vector of 4 numbers seed=42; random_vect=rands(5,15,4,seed); echo( "Random Vector: ",random_vect); sphere(r=5); for(i=[0:3]) { rotate(360*i/4) { translate([10+random_vect[i],0,0]) sphere(r=random_vect[i]/2); } } // ECHO: "Random Vector: ", [8.7454, 12.9654, 14.5071, 6.83435]
"round" 运算符分别返回最大或最小整数部分,具体取决于数值输入是正数还是负数。
使用示例
round(5.4); round(5.5); round(5.6); round(-5.4); round(-5.5); round(-5.6);
结果
5 6 6 -5 -6 -6
数学 符号 函数。返回一个单位值,该值提取值的符号,参见:符号函数
参数
- <x>
- 小数。要查找符号的值。
使用示例
sign(-5.0); sign(0); sign(8.0);
结果
-1.0 0.0 1.0
平方根函数
[edit | edit source]数学上的平方根函数。
- 用法示例
translate([sqrt(100),0,0])sphere(100);
无穷大和 NaN
[edit | edit source]OpenSCAD 如何处理像 (1/0) 这样的输入?基本上,它的行为继承自 OpenSCAD 所用语言 C++ 语言及其浮点数类型和相关的 C 数学库。此系统允许通过特殊值“Inf”或“-Inf”来表示正无穷大和负无穷大。它还允许将像 sqrt(-1) 或 0/0 这样的值表示为“NaN”,它是“非数字”的缩写。可以在网上找到解释,例如Open Group 关于 math.h 的网站或维基百科关于 IEEE 754 数字格式的页面。但是,OpenSCAD 是它自己的语言,因此它可能并不完全匹配 C 中发生的所有事情。例如,OpenSCAD 使用度而不是弧度来表示三角函数。另一个例子是 sin() 在输入为 1/0 时不会抛出“域错误”,尽管它确实返回 NaN。
以下是一些 OpenSCAD 数学函数的无限输入示例及其在 2015 年底 OpenSCAD 回归测试系统中获得的结果。
0/0: nan | sin(1/0): nan | asin(1/0): nan | ln(1/0): inf | round(1/0): inf |
-0/0: nan | cos(1/0): nan | acos(1/0): nan | ln(-1/0): nan | round(-1/0): -inf |
0/-0: nan | tan(1/0): nan | atan(1/0): 90 | log(1/0): inf | sign(1/0): 1 |
1/0: inf | ceil(-1/0): -inf | atan(-1/0): -90 | log(-1/0): nan | sign(-1/0): -1 |
1/-0: -inf | ceil(1/0): inf | atan2(1/0, -1/0): 135 | max(-1/0, 1/0): inf | sqrt(1/0): inf |
-1/0: -inf | floor(-1/0): -inf | exp(1/0): inf | min(-1/0, 1/0): -inf | sqrt(-1/0): nan |
-1/-0: inf | floor(1/0): inf | exp(-1/0): 0 | pow(2, 1/0): inf | pow(2, -1/0): 0 |
字符串函数
[edit | edit source]str
[edit | edit source]将所有参数转换为字符串并连接起来。
使用示例
number=2; echo ("This is ",number,3," and that's it."); echo (str("This is ",number,3," and that's it."));
结果
ECHO: "This is ", 2, 3, " and that's it." ECHO: "This is 23 and that's it."
这可以用于将数字简单地转换为字符串。
s = str(n);
chr
[edit | edit source][注意: 需要版本 2015.03]
将数字转换为包含对应代码的字符的字符串。OpenSCAD 使用 Unicode,因此该数字被解释为 Unicode 代码点。超出有效代码点范围的数字将生成空字符串。
参数
- chr(数字)
- 如果代码点有效,则将一个代码点转换为长度为 1 的字符串(字节数取决于 UTF-8 编码)。
- chr(向量)
- 将参数向量中给出的所有代码点转换为字符串。
- chr(范围)
- 将范围参数产生的所有代码点转换为字符串。
示例
echo(chr(65), chr(97)); // ECHO: "A", "a"
echo(chr(65, 97)); // ECHO: "Aa"
echo(chr([66, 98])); // ECHO: "Bb"
echo(chr([97 : 2 : 102])); // ECHO: "ace"
echo(chr(-3)); // ECHO: ""
echo(chr(9786), chr(9788)); // ECHO: "☺", "☼"
echo(len(chr(9788))); // ECHO: 1
注意:当与 echo() 一起使用时,控制台输出的字符代码大于 127 时,取决于平台。
ord
[edit | edit source][注意: 需要 2019.05 版本]
将字符转换为表示Unicode代码点的数字。如果参数不是字符串,则 ord()
返回 undef
。
参数
- ord(字符串)
- 将给定字符串的第一个字符转换为 Unicode 代码点。
示例
echo(ord("a"));
// ECHO: 97
echo(ord("BCD"));
// ECHO: 66
echo([for (c = "Hello! 🙂") ord(c)]);
// ECHO: [72, 101, 108, 108, 111, 33, 32, 128578]
txt="1";
echo(ord(txt)-48,txt);
// ECHO: 1,"1" // only converts 1 character
len
[edit | edit source]返回文本中的字符数。
echo(len("Hello world")); // 11
另见 search()
[edit | edit source]search() 用于文本搜索。
is_string(value)
[edit | edit source]如果 value 是字符串,则函数 is_string(value) 返回 true,否则返回 false。
echo(is_string("alpha")); //true
echo(is_string(22)); //false
用户定义函数
[edit | edit source]为了补充原生函数,您可以定义自己的函数,以下是一些建议。
//-- Lower case all chars of a string -- does not work with accented characters
function strtolower (string) =
chr([for(s=string) let(c=ord(s)) c<91 && c>64 ?c+32:c]);
//-- Replace char(not string) in a string
function char_replace (s,old=" ",new="_") =
chr([for(i=[0:len(s)-1]) s[i]==old?ord(new):ord(s[i])]);
//-- Replace last chars of a string (can be used for file extension replacement of same length)
function str_rep_last (s,new=".txt") =
str(chr([for(i=[0 :len(s)-len(new)-1])ord(s[i])]),new);
//-- integer value from string ----------
//Parameters ret and i are for function internal use (recursion)
function strtoint (s, ret=0, i=0) =
i >= len(s)
? ret
: strtoint(s, ret*10 + ord(s[i]) - ord("0"), i+1);
请注意这里使用 chr() 从由其 ASCII 代码定义的未知数量的字符重新组合字符串。这避免了在列表管理出现之前需要使用递归模块。
列表推导
[edit | edit source][注意: 需要版本 2015.03]
基本语法
[edit | edit source]列表推导提供了一种灵活的方法来使用通用语法生成列表。
[ list-definition expression ]
以下元素支持用于构建列表定义。
- for (i = sequence)
- 遍历范围或现有列表。
- for (init;condition;next)
- 以 C 风格的 for 表示的简单递归调用。
- each
- 以序列值作为参数,并将每个元素添加到正在构建的列表中。each x 等效于 `for (i = x) i`。
- if (condition)
- 选择标准,当为真时计算表达式并将其添加到结果列表中。
- let (x = value)
- 局部变量赋值。
多个生成器表达式
[edit | edit source][注意: 需要 2019.05 版本]
列表推导语法已推广以允许使用多个表达式。这允许轻松地从由不同列表推导表达式生成的多个子列表构建列表,从而避免使用 concat。
steps = 50;
points = [
// first expression generating the points in the positive Y quadrant
for (a = [0 : steps]) [ a, 10 * sin(a * 360 / steps) + 10 ],
// second expression generating the points in the negative Y quadrant
for (a = [steps : -1 : 0]) [ a, 10 * cos(a * 360 / steps) - 20 ],
// additional list of fixed points
[ 10, -3 ], [ 3, 0 ], [ 10, 3 ]
];
polygon(points);
for
[edit | edit source]for 元素定义了列表生成的输入值。语法与 for 迭代器使用的语法相同。等号右侧的序列可以是任何列表。for 元素遍历列表的所有成员。等号左侧的变量依次采用序列中每个成员的值。然后可以在 for 元素的子级中处理此值,并且每个结果都成为所产生的最终列表的成员。
如果序列具有多个维度,for 将仅遍历第一个维度。可以通过嵌套 for 元素访问更深的维度。
此处介绍了一些常见的用法模式。
- [ for (i = [start : step : end]) i ]
- 根据范围定义生成输出,此版本主要用于计算列表值或使用范围值作为索引来访问现有列表。
示例
// generate a list with all values defined by a range
list1 = [ for (i = [0 : 2 : 10]) i ];
echo(list1); // ECHO: [0, 2, 4, 6, 8, 10]
// extract every second character of a string
str = "SomeText";
list2 = [ for (i = [0 : 2 : len(str) - 1]) str[i] ];
echo(list2); // ECHO: ["S", "m", "T", "x"]
// indexed list access, using function to map input values to output values
function func(x) = x < 1 ? 0 : x + func(x - 1);
input = [1, 3, 5, 8];
output = [for (a = [ 0 : len(input) - 1 ]) func(input[a]) ];
echo(output); // ECHO: [1, 6, 15, 36]
- [ for (i = [a, b, c, ...]) i ]
- 使用列表参数作为输入,此版本可以用来将输入值映射到计算后的输出值。
示例
// iterate over an existing list
friends = ["John", "Mary", "Alice", "Bob"];
list = [ for (i = friends) len(i)];
echo(list); // ECHO: [4, 4, 5, 3]
// map input list to output list
list = [ for (i = [2, 3, 5, 7, 11]) i * i ];
echo(list); // ECHO: [4, 9, 25, 49, 121]
// calculate Fibonacci numbers
function func(x) = x < 3 ? 1 : func(x - 1) + func(x - 2);
input = [7, 10, 12];
output = [for (a = input) func(a) ];
echo(output); // ECHO: [13, 55, 144]
- [ for (c = "String") c ]
- 根据字符串生成输出,这将遍历字符串的每个字符。
[注意: 需要 2019.05 版本]
示例
echo([ for (c = "String") c ]);
// ECHO: ["S", "t", "r", "i", "n", "g"]
- [ for (a = inita, b = initb, ...;condition;a = nexta, b = nextb, ...) expr ]
- 用于以 c 风格的 for 循环表达简单递归调用的生成器。
[注意: 需要 2019.05 版本]
此生成器的递归等效项为
function f(a, b, ...) = condition ? concat([expr], f(nexta, nextb, ...)) : []; f(inita, initb, ...)
示例
echo( [for (a = 0, b = 1;a < 5;a = a + 1, b = b + 2) [ a, b * b ] ] );
// ECHO: [[0, 1], [1, 9], [2, 25], [3, 49], [4, 81]]
// Generate fibonacci sequence
echo([for (a = 0, b = 1;a < 1000;x = a + b, a = b, b = x) a]);
// ECHO: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
// Cumulative sum of values in v
function cumsum(v) = [for (a = v[0]-v[0], i = 0; i < len(v); a = a+v[i], i = i+1) a+v[i]];
echo(cumsum([1, 2, 3, 4]));
// ECHO: [1, 3, 6, 10]
echo(cumsum([[1, 1], [2, 2], [3, 3]]));
// ECHO: [[1, 1], [3, 3], [6, 6]]
each
[edit | edit source][注意: 需要 2019.05 版本]
each 直接嵌入作为参数提供的列表的值,有效地解开了参数列表。
// Without using "each", a nested list is generated
echo([ for (a = [1 : 4]) [a, a * a] ]);
// ECHO: [[1, 1], [2, 4], [3, 9], [4, 16]]
// Adding "each" unwraps the inner list, producing a flat list as result
echo([ for (a = [1 : 4]) each [a, a * a] ]);
// ECHO: [1, 1, 2, 4, 3, 9, 4, 16]
each 解开范围,并在与多个生成器表达式结合使用时有助于构建更通用的 for 列表。
A = [-2, each [1:2:5], each [6:-2:0], -1];
echo(A);
// ECHO: [-2, 1, 3, 5, 6, 4, 2, 0, -1]
echo([ for (a = A) 2 * a ]);
// ECHO: [-4, 2, 6, 10, 12, 8, 4, 0, -2]
if 元素允许在表达式是否应该被分配并添加到结果列表中进行选择。在最简单的情况下,这允许对列表进行过滤。
- [ for (i = list) if (condition(i)) i ]
- 当条件的评估结果为真时,表达式 i 会被添加到结果列表中。
示例
list = [ for (a = [ 1 : 8 ]) if (a % 2 == 0) a ];
echo(list); // ECHO: [2, 4, 6, 8]
请注意,if 元素不能位于表达式内部,它应该位于最顶层。
示例
// from the input list include all positive odd numbers
// and also all even number divided by 2
list = [-10:5];
echo([for(n=list) if(n%2==0 || n>=0) n%2==0 ? n/2 : n ]);
// ECHO: [-5, -4, -3, -2, -1, 0, 1, 1, 3, 2, 5]
// echo([for(n=list) n%2==0 ? n/2 : if(n>=0) n ]); // this would generate a syntactical error
[注意: 需要 2019.05 版本]
if-else 结构等效于条件表达式 ?:,除了它可以与 filter if 结合使用。
- [ for (i = list) if (condition(i)) x else y ]
- 当条件的评估结果为真时,表达式 x 会被添加到结果列表中,否则表达式 y 会被添加到结果列表中。
// even numbers are halved, positive odd numbers are preserved, negative odd numbers are eliminated
echo([for (a = [-3:5]) if (a % 2 == 0) [a, a/2] else if (a > 0) [a, a] ]);
// ECHO: [[-2, -1], [0, 0], [1, 1], [2, 1], [3, 3], [4, 2], [5, 5]];
请注意,在上面的表达式中,条件运算符不能替代 if-else。可以使用条件运算符来表达相同的过滤器,但逻辑更加隐晦。
// even numbers are halved, positive odd numbers are preserved, negative odd numbers are eliminated
echo([for (a = [-3:5]) if (a % 2 == 0 || (a % 2 != 0 && a > 0)) a % 2 == 0 ? [a, a / 2] : [a, a] ]);
// ECHO: [[-2, -1], [0, 0], [1, 1], [2, 1], [3, 3], [4, 2], [5, 5]];
要将 else 表达式绑定到特定的 if,可以使用括号。
// even numbers are dropped, multiples of 4 are substituted by -1
echo([for(i=[0:10]) if(i%2==0) (if(i%4==0) -1 ) else i]);
// ECHO: [-1, 1, 3, -1, 5, 7, -1, 9]
// odd numbers are dropped, multiples of 4 are substituted by -1
echo([for(i=[0:10]) if(i%2==0) if(i%4==0) -1 else i]);
// ECHO: [-1, 2, -1, 6, -1, 10]
let 元素允许在列表理解定义中对变量进行顺序赋值。
- [ for (i = list) let (assignments) a ]
示例
list = [ for (a = [ 1 : 4 ]) let (b = a*a, c = 2 * b) [ a, b, c ] ];
echo(list); // ECHO: [[1, 1, 2], [2, 4, 8], [3, 9, 18], [4, 16, 32]]
有不同的方法来定义嵌套循环。在一个 for 元素中定义多个循环变量,以及多个 for 元素,都会产生扁平的结果列表。为了生成嵌套结果列表,需要额外的 [ ] 标记。
// nested loop using multiple variables
flat_result1 = [ for (a = [ 0 : 2 ], b = [ 0 : 2 ]) a == b ? 1 : 0 ];
echo(flat_result1); // ECHO: [1, 0, 0, 0, 1, 0, 0, 0, 1]
// nested loop using multiple for elements
flat_result2 = [ for (a = [ 0 : 2 ]) for (b = [0 : 2]) a == b ? 1 : 0 ];
echo(flat_result2); // ECHO: [1, 0, 0, 0, 1, 0, 0, 0, 1]
// nested loop to generate a bi-dimensional matrix
identity_matrix = [ for (a = [ 0 : 2 ]) [ for (b = [ 0 : 2 ]) a == b ? 1 : 0 ] ];
echo(identity_matrix); // ECHO: [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
本章列出了一些高级示例、有用的习惯用法和列表理解语法的用例。
使用列表理解,可以计算参数方程在许多点上的值来近似许多曲线,例如以下椭圆的示例(使用 polygon())
sma = 20; // semi-minor axis
smb = 30; // semi-major axis
polygon(
[ for (a = [0 : 5 : 359]) [ sma * sin(a), smb * cos(a) ] ]
);
列表理解可以在用户定义的函数中使用,以对向量执行操作或任务。以下是一个扁平化嵌套向量的用户定义函数。
// input : nested list
// output : list with the outer level nesting removed
function flatten(l) = [ for (a = l) for (b = a) b ] ;
nested_list = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ];
echo(flatten(nested_list)); // ECHO: [1, 2, 3, 4, 5, 6]
即使是复杂的算法 快速排序 也可以通过 for()、if()、let() 和 递归 实现。
// input : list of numbers
// output : sorted list of numbers
function quicksort(arr) = !(len(arr)>0) ? [] : let(
pivot = arr[floor(len(arr)/2)],
lesser = [ for (y = arr) if (y < pivot) y ],
equal = [ for (y = arr) if (y == pivot) y ],
greater = [ for (y = arr) if (y > pivot) y ]
) concat(
quicksort(lesser), equal, quicksort(greater)
);
// use seed in rands() to get reproducible results
unsorted = [for (a = rands(0, 10, 6, 3)) ceil(a)];
echo(unsorted); // ECHO: [6, 1, 8, 9, 3, 2]
echo(quicksort(unsorted)); // ECHO: [1, 2, 3, 6, 8, 9]
select() 执行元素的选择和重新排序,生成一个新的向量。
function select(vector, indices) = [ for (index = indices) vector[index] ];
vector1 = [[0,0],[1,1],[2,2],[3,3],[4,4]];
selector1 = [4,0,3];
vector2 = select(vector1,selector1); // [[4, 4], [0, 0], [3, 3]]
vector3 = select(vector1,[0,2,4,4,2,0]);// [[0, 0], [2, 2], [4, 4],[4, 4], [2, 2], [0, 0]]
// range also works as indices
vector4 = select(vector1, [4:-1:0]); // [[4, 4], [3, 3], [2, 2], [1, 1], [0, 0]]
使用索引
function cat(L1, L2) = [for (i=[0:len(L1)+len(L2)-1])
i < len(L1)? L1[i] : L2[i-len(L1)]] ;
echo(cat([1,2,3],[4,5])); //concatenates two OpenSCAD lists [1,2,3] and [4,5], giving [1, 2, 3, 4, 5]
不使用索引
function cat(L1, L2) = [for(L=[L1, L2], a=L) a];
echo(cat([1,2,3],[4,5])); //concatenates two OpenSCAD lists [1,2,3] and [4,5], giving [1, 2, 3, 4, 5]
特殊变量提供了一种将参数传递给模块和函数的备用方法。所有以 '$' 开头的用户或 OpenSCAD 定义的变量都是特殊变量,类似于 Lisp 中的特殊变量。模块和函数可以查看所有外部变量,以及作为参数传递的变量或内部定义的变量。
目前有效的特殊变量名称只能由 $ 后跟简单字符和下划线 [a-zA-Z0-9_] 组成,不允许使用高 ASCII 或 Unicode 字符。
常规变量的值在编译时分配,因此对于所有调用来说都是静态的。
特殊变量会将它们的值从调用模块或函数的范围 (参见变量范围) 传递过来。这意味着特殊变量在每次调用模块或函数时可能具有不同的值。
regular = "regular global"; $special = "special global"; module show() echo(" in show ", regular," ", $special ); echo (" outside ", regular," ", $special ); // ECHO: " outside ", "regular global", " ", "special global" for ( regular = [0:1] ){ echo("in regular loop ", regular," ", $special ); show();} // ECHO: "in regular loop ", 0, " ", "special global" // ECHO: " in show ", "regular global", " ", "special global" // ECHO: "in regular loop ", 1, " ", "special global" // ECHO: " in show ", "regular global", " ", "special global" for ( $special = [5:6] ){ echo("in special loop ", regular," ", $special ); show();} // ECHO: "in special loop ", "regular global", " ", 5 // ECHO: " in show ", "regular global", " ", 5 // ECHO: "in special loop ", "regular global", " ", 6 // ECHO: " in show ", "regular global", " ", 6 show(); // ECHO: " in show ", "regular global", " ", "special global"
这在需要将多个参数通过多层模块调用传递时非常有用。
OpenSCAD 已经定义了几个特殊变量。
特殊变量 $fa、$fs 和 $fn 控制用于生成弧线的片段数量。
$fa 是片段的最小角度。即使是巨大的圆形,其片段数量也不会超过 360 除以该数字。默认值为 12(即完整的圆形有 30 个片段)。允许的最小值为 0.01。尝试设置更低的值会导致警告。
$fs 是片段的最小尺寸。默认值为 2,因此非常小的圆形片段数量少于使用 $fa 指定的数量。允许的最小值为 0.01。尝试设置更低的值会导致警告。
$fn 是片段数量,通常默认值为 0。当该变量的值大于零时,另外两个变量会被忽略,并且使用该片段数量渲染完整的圆形。
片段数量越高,消耗的内存和 CPU 越多;较大的值会导致许多系统崩溃。根据设计,$fn 值以及 $fa 和 $fs 的相应结果应该保持较小,至少在设计最终确定之前应该保持较小,然后可以增加最终结果的值。**不建议使用超过 128 的 $fn**,或者仅在特定情况下使用,建议使用低于 50 的 $fn 以提高性能。
您也可以使用两个不同的值来进行预览和渲染。
$fn = $preview ? 32 : 64;
**提示:**如果您要创建一个具有轴对齐整数边界框的圆形/圆柱体/球体(即具有整数尺寸和整数位置的边界框),请使用 4 的倍数的 $fn 值。圆形形状将显示为内接在提供的半径或直径内的多边形。
当使用 $fa 和 $fs 来确定圆形的片段数量时,OpenSCAD 永远不会使用少于 5 个片段。
这是计算圆形片段数量的 C 代码
int get_fragments_from_r(double r, double fn, double fs, double fa) { if (r < GRID_FINE) return 3; if (fn > 0.0) return (int)(fn >= 3 ? fn : 3); return (int)ceil(fmax(fmin(360.0 / fa, r*2*M_PI / fs), 5)); }
或者您可以在代码中嵌入这个 OpenSCAD 版本来了解发生了什么,您需要将 r= 设置为您的大小
echo(n=($fn>0?($fn>=3?$fn:3):ceil(max(min(360/$fa,r*2*PI/$fs),5))),a_based=360/$fa,s_based=r*2*PI/$fs);
球体首先被切成与渲染球体半径的圆形的片段数量相同的切片,然后每个切片都渲染成切片半径所需的片段数量。您可能已经注意到,球体的极点通常是五边形。这是为什么。
圆柱体的片段数量使用两个半径中较大的一个来确定。
该方法也用于从 DXF 文件渲染圆形和弧线。导入 STL 文件时,这些变量无效。
您可以通过在实例化模块中重置 $fX 值来生成高分辨率球体
$fs = 0.01; sphere(2);
或者只需将特殊变量作为参数传递
sphere(2, $fs = 0.01);
您甚至可以缩放特殊变量而不是重置它
sphere(2, $fs = $fs * 0.01);
$t 变量在 "rotate" 和 "translate" 中用于动画,$t*360 给出完整的循环。要开始动画,请选择 **窗口\动画** 并在 "FPS" 和 "步长" 中输入值。 "时间" 字段将显示 $t 的当前值,以小数形式表示。
$t 的值将从 0 重复到(1 - 1/步长)。它永远不会达到 1,因为这会导致动画在使用它进行旋转时出现 "卡顿"——两个连续的帧将处于相同的角度。
没有变量来区分动画运行在第一帧($t=0)和动画未运行的情况,因此请将 $t=0 设置为模型的静止位置。
简谐运动
[edit | edit source]translate ([0, 0, 10*sin($t*360)])
sphere(2);
在 Z 轴上,球体在 -10 到 +10 之间振荡。
旋转
[edit | edit source]rotate ([0, 0, $t*360])
square(5);
在 Z 轴上,方块绕着一个角旋转。要绕方块的中心旋转,请使用
rotate ([0, 0, $t*360])
square(5, center=true);
部分旋转
[edit | edit source]在动画中,所有部分都在相同时间 $t 内完成一个运动周期,跳回零点,然后重新开始。但是,可以为每个周期指定不同的步数,以在同一动画中产生不同速度的错觉。这可以用来动画化不同尺寸的啮合齿轮。
rotate([0, 0, $t*360/17])
gear(teeth=17);
和
rotate([0, 0, -$t*360/31])
gear(teeth=31);
圆形轨道
[edit | edit source]rotate ([0, 0, $t*360])
translate ([10, 0])
square(5, center=true);
无旋转的圆形轨道
[edit | edit source]rotate ([0, 0, $t*360])
translate ([9, 0])
rotate ([0, 0, -$t*360])
square(5, center=true);
椭圆轨道
[edit | edit source]translate([10*sin($t*360), 20*cos($t*360)])
square(2, center=true);
请注意,使用 "translate",对象不会旋转。
椭圆运动
[edit | edit source]e=10;
rotate([0, 0, $t*360])
translate([e, 0])
rotate([0, 0, -$t*720])
square([2*e, 2], center=true);
如果选中了 "Dump Pictures",则会在与 .scad 文件相同的目录中创建图像。导出的 PNG 文件可以通过命令行转换为 gif
convert -delay 10 -loop 0 *.png myimage.gif
其中 -delay 10
是每帧的持续时间(以毫秒为单位),-loop 0
指定循环次数 (0 = 无限循环)。
Linux 的 convert
命令是 ImageMagick 的一部分,它也可以安装在 macOS 和 Windows 上。可以添加其他参数来进行裁剪和缩放。
视窗:$vpr、$vpt、$vpf 和 $vpd
[edit | edit source]这些包含当前视窗旋转和平移以及相机距离 - 在渲染时。移动视窗不会更新它们。在动画过程中,它们会为每一帧更新。
- $vpr 显示旋转
- $vpt 显示平移 (即不会受旋转和缩放的影响)
- $vpf 显示视图的 FOV (视野) [注意: 需要版本 2021.01]
- $vpd 显示相机距离 [注意: 需要版本 2015.03]
示例
cube([10, 10, $vpr[0] / 10]);
如果动画循环处于活动状态(不需要使用 $t 变量),这会根据视角更改立方体的大小
还可以使复杂模型的某些部分在更改视角时消失。
所有四个变量都是可写的,但只有主文件顶层处的赋值会对视窗产生影响。 [注意: 需要版本 2015.03]
示例
$vpr = [0, 0, $t * 360];
这允许在动画模式下绕 Z 轴进行简单的 360 度旋转。
菜单命令 Edit - Paste Viewport Rotation/Translation 会复制视窗的当前值,但不会复制当前的 $vpr 或 $vpt。
执行模式:$preview
[edit | edit source][注意: 需要 2019.05 版本]
在 OpenCSG 预览 (F5) 中,$preview 为 true。在渲染 (F6) 中,$preview 为 false。
例如,这可以用来在预览期间减少细节以节省时间,而不会在最终渲染结果中丢失细节。
$fn = $preview ? 12 : 72; sphere(r = 1);
请注意,渲染模块不会影响 $preview。
render(){ $fn = $preview ? 12 : 72; sphere(r = 1); }
另一个用途可能是使预览显示装配视图,而渲染只生成为打印而设计的打印部件。
如果打印部件需要在打印后移除的额外功能,例如悬空孔的支撑,则预览可以省略这些功能以显示后处理后的成品。
当从命令行运行 OpenSCAD 时,$preview 只有在使用 OpenCSG 生成 PNG 图像时才为 true。当使用 CGAL 生成 STL、DXF 和 SVG 文件时,它为 false。它在生成 CSG 和 ECHO 文件时也为 false。这可能符合或不符合您的预期,但您始终可以使用 -D 选项在命令行上覆盖它,就像任何其他变量一样。
Echo 模块
[edit | edit source]echo() 模块将内容打印到编译窗口 (也称为控制台)。这对调试代码很有用。还可以查看字符串函数 str()。
数值四舍五入到 5 位有效数字。
使用 'variable=variable' 作为表达式来轻松标记变量非常方便,请查看下面的示例。
使用示例
my_h=50;
my_r=100;
echo("This is a cylinder with h=", my_h, " and r=", my_r);
echo(my_h=my_h,my_r=my_r); // shortcut
cylinder(h=my_h, r=my_r);
在控制台中显示为
ECHO: "This is a cylinder with h=", 50, " and r=", 100 ECHO: my_h = 50, my_r = 100
请注意,如果使用 str() 转换为字符串,则输出将不会包含额外的双引号和逗号。
舍入示例
[edit | edit source]舍入示例
a=1.0;
b=1.000002;
echo(a);
echo(b);
if(a==b){ //while echoed the same, the values are still distinct
echo ("a==b");
}else if(a>b){
echo ("a>b");
}else if(a<b){
echo ("a<b");
}else{
echo ("???");
}
在控制台中显示为
ECHO: 1 ECHO: 1 ECHO: "a<b"
小数和大数
[edit | edit source]c=1000002;
d=0.000002;
echo(c); //1e+06
echo(d); //2e-06
HTML
[edit | edit source]HTML 输出不受官方支持,但根据 OpenSCAD 版本,某些 HTML 标签会在控制台窗口中渲染。
Echo 函数
[edit | edit source][注意: 需要 2019.05 版本]
Echo 可在表达式上下文中使用,用于在函数/表达式求值时打印信息。输出在表达式求值之前生成,以允许调试递归函数。
示例
a = 3; b = 5; // echo() prints values before evaluating the expression r1 = echo(a, b) a * b; // ECHO: 3, 5 // using let it's still easy to output the result r2 = let(r = 2 * a * b) echo(r) r; // ECHO: 30 // use echo statement for showing results echo(r1, r2); // ECHO: 15, 30
一个更复杂的示例展示了如何在递归函数的下降和上升路径中使用 echo()。result() 辅助函数是一种在求值后输出表达式值的一种简单方法。
打印递归 sum() 的输入值和结果的示例
v = [4, 7, 9, 12]; function result(x) = echo(result = x) x; function sum(x, i = 0) = echo(str("x[", i, "]=", x[i])) result(len(x) > i ? x[i] + sum(x, i + 1) : 0); echo("sum(v) = ", sum(v)); // ECHO: "x[0]=4" // ECHO: "x[1]=7" // ECHO: "x[2]=9" // ECHO: "x[3]=12" // ECHO: "x[4]=undef" // ECHO: result = 0 // ECHO: result = 12 // ECHO: result = 21 // ECHO: result = 28 // ECHO: result = 32 // ECHO: "sum(v) = ", 32
渲染
[edit | edit source]强制生成网格,即使是在预览模式下也是如此。这在某些情况下很有用,例如当布尔运算变得太慢而无法跟踪时。
Render 也可用于 (通常与凸性一起) 避免/解决预览伪影。[1] 另请参见 OpenSCAD User Manual/FAQ#Why are some parts (e.g. holes) of the model not rendered correctly?
使用示例: (需要描述)
render(convexity = 2) difference() { cube([20, 20, 150], center = true); translate([-10, -10, 0]) cylinder(h = 80, r = 10, center = true); translate([-10, -10, +40]) sphere(r = 10); translate([-10, -10, -40]) sphere(r = 10); }
表面
[edit | edit source]Surface 从文本或图像文件读取 高度图 信息。
参数
- 文件
- 字符串。包含高度图数据的文件的路径。
- 中心
- 布尔值。这决定了生成的物体的定位。如果为真,则物体在 X 轴和 Y 轴上居中。否则,物体将放置在正象限中。默认值为 false。
- 反转
- 布尔值。反转导入图像的颜色值如何转换为高度值。这在导入文本数据文件时没有影响。默认值为 false.: 使用此参数产生的几何体以其顶部在 z = 0 平面上的方式定位。一个厚度为一个单位的薄“足迹”层会自动添加到高度图下方。:: [注意: 需要 2015.03 版本]
- 凸性
- 整数。凸度参数指定与物体相交的光线可能穿透的最大正面数(背面数)。此参数仅用于在 OpenCSG 预览模式下正确显示物体,对最终渲染没有影响。
基于文本的高度图的格式是数字矩阵,代表特定点的海拔高度。行在 Y 轴方向上映射,列在 X 轴方向上映射,相邻行和列之间有一个单位的增量。数字必须用空格或制表符分隔。空行和以 # 字符开头的行将被忽略。
[注意: 需要版本 2015.03]
目前仅支持 PNG 图像。图像的 alpha 通道信息将被忽略,像素的高度通过将颜色值转换为 灰度 来确定,使用 sRGB 颜色空间的线性亮度(Y = 0.2126R + 0.7152G + 0.0722B)。灰度值被缩放至 0 到 100 的范围。
一个厚度为一个单位的薄“足迹”层会自动添加到高度图下方。
示例 1
//surface.scad surface(file = "surface.dat", center = true, convexity = 5); %translate([0,0,5])cube([10,10,10], center = true);
#surface.dat 10 9 8 7 6 5 5 5 5 5 9 8 7 6 6 4 3 2 1 0 8 7 6 6 4 3 2 1 0 0 7 6 6 4 3 2 1 0 0 0 6 6 4 3 2 1 1 0 0 0 6 6 3 2 1 1 1 0 0 0 6 6 2 1 1 1 1 0 0 0 6 6 1 0 0 0 0 0 0 0 3 1 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0
结果
示例 2
// example010.dat generated using octave or matlab: d = (sin(1:0.2:10)' * cos(1:0.2:10)) * 10; save("-ascii", "example010.dat", "d");
//original surface surface(file = "example010.dat", center = true, convexity = 5); //rotated surface translate(v = [70, 0, 0]) rotate(45, [0, 0, 1]) surface(file = "example010.dat", center = true, convexity = 5); //intersection translate(v = [35, 60, 0]) intersection() { surface(file = "example010.dat", center = true, convexity = 5); rotate(45, [0, 0, 1]) surface(file = "example010.dat", center = true, convexity = 5); }
示例 3
[注意: 需要版本 2015.03]
// Example 3a scale([1, 1, 0.1]) surface(file = "smiley.png", center = true);
// Example 3b scale([1, 1, 0.1]) surface(file = "smiley.png", center = true, invert = true);
示例 4
[注意: 需要版本 2015.03]
// Example 4 surface(file = "BRGY-Grey.png", center = true, invert = false);
-
PNG 测试文件
-
3D 表面
search() 函数是一个通用函数,用于在向量、字符串或更复杂的列表列表结构中查找一个或多个(或所有)值或值列表的出现。
- search( match_value , string_or_vector [, num_returns_per_match [, index_col_num ] ] );
- match_value
- 可以是单个字符串值。搜索循环遍历字符串中的字符,并在第二个参数中搜索每个字符。第二个参数必须是字符串或列表列表(不建议使用第二种情况)。search 函数不搜索子字符串。
- 可以是单个数值。
- 可以是值列表。search 函数搜索列表中的每个项目。
- 要搜索列表或完整字符串,请将列表或字符串作为单个元素列表提供,例如 ["abc"] 用于搜索字符串 "abc"(参见示例 9)或 [[6,7,8]] 用于搜索列表 [6,7,8]。如果没有额外的括号,search 会分别搜索列表中的每个项目。
- 如果 match_value 是布尔值,则 search 返回 undef。
- string_or_vector
- 要搜索匹配项的字符串或向量。
- 如果match_value是字符串,则这应该是一个字符串,并且会搜索字符串中与match_value中的字符匹配的单个字符匹配项。
- 如果这是一个列表列表,v=[[a0,a1,a2...],[b0,b1,b2,...],[c0,c1,c2,...],...] 那么 search 仅查看子列表的一个索引位置。默认情况下这是位置 0,因此 search 仅查看 a0、b0、c0 等。index_col_num 参数更改搜索的索引。
- 如果match_value是字符串,并且此参数是列表列表,那么字符串的字符将针对列表列表中相应的索引条目进行测试。但是,如果任何字符未能找到匹配项,则会打印警告消息,并且该返回值将从输出中排除(如果num_returns_per_match为 1)。这意味着输出的长度是不可预测的。
- num_returns_per_match(默认值:1)
- 默认情况下,search 仅查找每个 match_value 元素的一个匹配项以返回作为索引列表。
- 如果 num_returns_per_match > 1,则 search 返回一个列表列表,每个列表列表最多包含 num_returns_per_match 个索引值,用于 match_value 的每个元素。
- 参见下面的示例 8。
- 如果 num_returns_per_match = 0,则 search 返回一个列表列表,其中包含每个 match_value 元素的所有匹配索引值。
- 参见下面的示例 6。
- index_col_num(默认值:0)
- 如上所述,在搜索列表列表时,search 仅查看每个子列表的一个索引位置。该索引位置由index_col_num指定。
- 参见下面的示例 5,了解简单的用法示例。
- 请参见 OpenSCAD 附带的example023.scad,了解可渲染的示例。
示例 | 代码 | 结果 |
---|---|---|
1 |
|
[0] |
2 |
|
[] |
3 |
|
[[0,4]] |
4 |
|
[[0,4]](另请参见下面的示例 6) |
示例 5
data= [ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",3] ]; echo(search(3, data)); // Searches index 0, so it doesn't find anything echo(search(3, data, num_returns_per_match=0, index_col_num=1));
输出
ECHO: [] ECHO: [2, 8]
示例 6: 返回每个搜索向量元素的所有匹配项。
data= [ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ]; search("abc", data, num_returns_per_match=0);
返回
[[0,4],[1,5],[2,6]]
示例 7: 返回每个搜索向量元素的第一个匹配项;特殊情况返回向量。
data= [ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ]; search("abc", data, num_returns_per_match=1);
返回
[0,1,2]
示例 8: 返回每个搜索向量元素的前两个匹配项;向量向量。
data= [ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ]; search("abce", data, num_returns_per_match=2);
返回
[[0,4],[1,5],[2,6],[8]]
示例 9
lTable2=[ ["cat",1],["b",2],["c",3],["dog",4],["a",5],["b",6],["c",7],["d",8],["e",9],["apple",10],["a",11] ]; lSearch2=["b","zzz","a","c","apple","dog"]; l2=search(lSearch2,lTable2); echo(str("Default list string search (",lSearch2,"): ",l2));
返回
ECHO: "Default list string search (["b", "zzz", "a", "c", "apple", "dog"]): [1, [], 4, 2, 9, 3]"
// workout which vectors get the results v=[ ["O",2],["p",3],["e",9],["n",4],["S",5],["C",6],["A",7],["D",8] ]; // echo(v[0]); // -> ["O",2] echo(v[1]); // -> ["p",3] echo(v[1][0],v[1][1]); // -> "p",3 echo(search("p",v)); // find "p" -> [1] echo(search("p",v)[0]); // -> 1 echo(search(9,v,0,1)); // find 9 -> [2] echo(v[search(9,v,0,1)[0]]); // -> ["e",9] echo(v[search(9,v,0,1)[0]][0]); // -> "e" echo(v[search(9,v,0,1)[0]][1]); // -> 9 echo(v[search("p",v,1,0)[0]][1]); // -> 3 echo(v[search("p",v,1,0)[0]][0]); // -> "p" echo(v[search("d",v,1,0)[0]][0]); // "d" not found -> undef echo(v[search("D",v,1,0)[0]][1]); // -> 8
version() 和 version_num() 返回 OpenSCAD 版本号。
- version() 函数以三个数字的向量形式返回 OpenSCAD 版本,例如 [2011, 9, 23]
- version_num() 函数以数字形式返回 OpenSCAD 版本,例如 20110923
parent_module(n) 和 $parent_modules
[edit | edit source]$parent_modules 包含实例化堆栈中的模块数量。parent_module(i) 返回实例化堆栈中当前模块上方 i 层的模块名称。堆栈独立于模块定义的位置。重要的是它们被实例化的位置。例如,这可以用来构建 BOM(物料清单)。
示例
module top() { children(); } module middle() { children(); } top() middle() echo(parent_module(0)); // prints "middle" top() middle() echo(parent_module(1)); // prints "top"
断言
[edit | edit source][注意: 需要 2019.05 版本]
另见 断言(软件开发)
assert 评估逻辑表达式。如果表达式评估为 false,则预览/渲染的生成将停止,并且通过控制台报告错误条件。报告包含表达式的字符串表示形式和 assert 命令中指定的附加字符串(可选)。
assert(condition); assert(condition, message);
参数
- 条件
- 表达式。要评估的表达式作为断言的检查。
- 消息
- 字符串。断言失败时输出的可选消息。
示例
[edit | edit source]最简单的示例是简单的 assert(false);
,例如,在一个名为 assert_example1.scad
的文件中。
cube();
assert(false);
sphere();
// ERROR: Assertion 'false' failed in file assert_example1.scad, line 2
此示例几乎没有用处,但简单的 assert(false);
可用于应该无法访问的代码部分。
检查参数
[edit | edit source]一个有用的例子是检查输入参数的有效性
module row(cnt = 3){
// Count has to be a positive integer greater 0
assert(cnt > 0);
for (i = [1 : cnt]) {
translate([i * 2, 0, 0]) sphere();
}
}
row(0);
// ERROR: Assertion '(cnt > 0)' failed in file assert_example2.scad, line 3
添加消息
[edit | edit source]在编写库时,在断言失败时向用户输出更多信息可能很有用。
module row(cnt = 3){
assert(cnt > 0, "Count has to be a positive integer greater 0");
for(i = [1 : cnt]) {
translate([i * 2, 0, 0]) sphere();
}
}
row(0);
// ERROR: Assertion '(cnt > 0)': "Count has to be a positive integer greater 0" failed in file assert_example3.scad, line 2
在函数中使用断言
[edit | edit source]assert 返回其子级,因此在函数中使用它时,您可以编写
function f(a, b) =
assert(a < 0, "wrong a") // assert input
assert(b > 0, "wrong b") // assert input
let (c = a + b) // derive a new value from input
assert(c != 0, "wrong c") // assert derived value
a * b; // calculate
第 7 章 -- 用户定义的函数和模块
[edit | edit source]OpenSCAD 用户手册/OpenSCAD 语言用户可以通过定义自己的 函数 和 模块 来扩展语言。这允许将脚本部分分组以方便地重复使用不同的值。选择良好的名称也有助于记录您的脚本。
函数 返回值。
模块 执行操作,但不会返回值。
OpenSCAD 在编译时计算变量的值,而不是在运行时计算。作用域内的最后一个变量赋值适用于该作用域内的所有位置。它也适用于任何内部作用域或其子级。有关更多详细信息,请参见 变量的作用域。将它们视为可覆盖的常量而不是变量可能会有所帮助。
对于函数和模块,OpenSCAD 为每个使用情况复制脚本的相关部分。每个副本都有自己的作用域,其中包含特定于该实例的变量和表达式的固定值。
函数和模块的名称区分大小写,因此 test() 和 TEST() 指的是不同的函数/模块。
范围
[edit | edit source]模块和函数可以在模块定义中定义,在那里它们仅在该模块的作用域内可见。
例如
function parabola(f,x) = ( 1/(4*f) ) * x*x;
module plotParabola(f,wide,steps=1) {
function y(x) = parabola(f,x);
module plot(x,y) {
translate([x,y])
circle(1,$fn=12);
}
xAxis=[-wide/2:steps:wide/2];
for (x=xAxis)
plot(x, y(x));
}
color("red") plotParabola(10, 100, 5);
color("blue") plotParabola(4, 60, 2);
函数 y() 和模块 plot() 无法在全局作用域中调用。
函数
[edit | edit source]函数对值进行操作以计算并返回新值。
- 函数定义
function name ( parameters ) = value ;
- 名称
- 您为该函数起的名称。一个有意义的名称在以后会有所帮助。目前有效的名称只能由简单字符和下划线 [a-zA-Z0-9_] 组成,并且不允许使用高 ASCII 或 Unicode 字符。
- 参数
- 零个或多个参数。可以为参数分配默认值,以便在调用时省略参数时使用。参数名称是本地的,不与同名外部变量冲突。
- 值
- 计算值的表达式。此值可以是向量。
函数使用
[edit | edit source]使用时,函数被视为值,并且本身不以分号 ;
结尾。
// example 1
function func0() = 5;
function func1(x=3) = 2*x+1;
function func2() = [1,2,3,4];
function func3(y=7) = (y==7) ? 5 : 2 ;
function func4(p0,p1,p2,p3) = [p0,p1,p2,p3];
echo(func0()); // 5
a = func1(); // 7
b = func1(5); // 11
echo(func2()); // [1, 2, 3, 4]
echo(func3(2), func3()); // 2, 5
z = func4(func0(), func1(), func2(), func3());
// [5, 7, [1, 2, 3, 4], 5]
translate([0, -4*func0(), 0])
cube([func0(), 2*func0(), func0()]);
// same as translate([0,-20,0]) cube([5,10,5]);
// example 2 creates for() range to give desired no of steps to cover range
function steps(start, no_steps, end) =
[start : (end-start)/(no_steps-1) : end];
echo(steps(10, 3, 5)); // [10 : -2.5 : 5]
for (i = steps(10, 3, 5)) echo(i); // 10 7.5 5
echo(steps(10, 3, 15)); // [10 : 2.5 : 15]
for (i = steps(10, 3, 15)) echo(i); // 10 12.5 15
echo(steps(0, 5, 5)); // [0 : 1.25 : 5]
for (i = steps(0, 5, 5)) echo(i); // 0 1.25 2.5 3.75 5
// example 3 rectangle with top pushed over, keeping same y
function rhomboid(x=1, y=1, angle=90)
= [[0,0],[x,0],
[x+x*cos(angle)/sin(angle),y],
[x*cos(angle)/sin(angle),y]];
echo (v1); v1 = rhomboid(10,10,35); // [[0, 0],
// [10, 0],
// [24.2815, 10],
// [14.2815, 10]]
polygon(v1);
polygon(rhomboid(10,10,35)); // alternate
//performing the same action with a module
module parallelogram(x=1,y=1,angle=90)
{polygon([[0,0],[x,0],
[x+x*cos(angle)/sin(angle),y],
[x*cos(angle)/sin(angle),y]]);};
parallelogram(10,10,35);
您还可以使用 let 语句 在函数中创建变量
function get_square_triangle_perimeter(p1, p2) =
let (hypotenuse = sqrt(p1*p1+p2*p2))
p1 + p2 + hypotenuse;
它可以用于在递归函数中存储值。有关一般概念的更多信息,请参见 维基百科页面。
递归函数
[edit | edit source]递归 函数调用受支持。使用条件运算符 "... ? ... : ... ",可以确保递归终止。
// recursion example: add all integers up to n
function add_up_to(n) = ( n==0 ? 0 : n + add_up_to(n-1) );
存在一个内置的递归限制,以防止应用程序崩溃(几千个)。如果达到限制,您会收到类似以下的错误:ERROR: Recursion detected calling function ... 。
对于所有 尾递归 函数调用自身,OpenSCAD 能够在内部消除递归,将其转换为迭代循环。前面的示例代码不是尾调用,因为“add”操作需要在调用后计算。但以下有资格进行尾递归消除
// tail-recursion elimination example: add all integers up to n
function add_up_to(n, sum=0) =
n==0 ?
sum :
add_up_to(n-1, sum+n);
echo(sum=add_up_to(100000));
// ECHO: sum = 5.00005e+009
尾递归消除允许更高的递归限制(高达 1000000)。
函数文字
[edit | edit source][注意: 需要版本 2021.01]
函数文字 是定义函数的表达式,其他名称是 lambda 或闭包。
- 函数文字
function (x) x + x
函数文字可以分配给变量,并像任何值一样传递。调用函数使用带括号的正常函数调用语法。
func = function (x) x * x; echo(func(5)); // ECHO: 25
可以定义返回函数的函数。未绑定的变量由词法作用域捕获。
a = 1; selector = function (which) which == "add" ? function (x) x + x + a : function (x) x * x + a; echo(selector("add")); // ECHO: function(x) ((x + x) + a) echo(selector("add")(5)); // ECHO: 11 echo(selector("mul")); // ECHO: function(x) ((x * x) + a) echo(selector("mul")(5)); // ECHO: 26
覆盖内置函数
[edit | edit source]可以覆盖内置函数。请注意,定义首先处理,因此评估确实为 echo()
两个调用返回 true
,因为这些调用在后面的处理步骤中进行评估。
源代码 | 控制台输出 |
---|---|
echo (sin(1));
function sin(x) = true;
echo (sin(1));
|
Compiling design (CSG Tree generation)...
ECHO: true
ECHO: true
Compiling design (CSG Products generation)...
|
模块
[edit | edit source]模块可用于定义对象,或者使用 children()
定义运算符。一旦定义,模块就会暂时添加到语言中。
- 模块定义
module name ( parameters ) { actions }
- 名称
- 您为该模块起的名称。尝试选择一个有意义的名称。目前有效的名称只能由简单字符和下划线 [a-zA-Z0-9_] 组成,并且不允许使用高 ASCII 或 Unicode 字符。
- 参数
- 零个或多个参数。可以为参数分配默认值,以便在调用时省略参数时使用。参数名称是本地的,不与同名外部变量冲突。
- 操作
- 模块外部几乎所有有效的语句都可以包含在模块中。这包括函数和其他模块的定义。这些函数和模块只能从封闭模块中调用。
可以分配变量,但它们的作用域仅限于模块的每次单独使用。OpenSCAD 中没有机制让模块将值返回到外部。有关更多详细信息,请参见 变量的作用域。
对象模块
[edit | edit source]对象模块使用一个或多个基元以及相关的运算符来定义新对象。
在使用中,对象模块是使用分号“;”结尾的操作。
name ( parameter values );
//example 1
translate([-30,-20,0])
ShowColorBars(Expense);
ColorBreak=[[0,""],
[20,"lime"], // upper limit of color range
[40,"greenyellow"],
[60,"yellow"],
[75,"LightCoral"],
[200,"red"]];
Expense=[16,20,25,85,52,63,45];
module ColorBar(value,period,range){ // 1 color on 1 bar
RangeHi = ColorBreak[range][0];
RangeLo = ColorBreak[range-1][0];
color( ColorBreak[range][1] )
translate([10*period,0,RangeLo])
if (value > RangeHi) cube([5,2,RangeHi-RangeLo]);
else if (value > RangeLo) cube([5,2,value-RangeLo]);
}
module ShowColorBars(values){
for (month = [0:len(values)-1], range = [1:len(ColorBreak)-1])
ColorBar(values[month],month,range);
}
//example 2
module house(roof="flat",paint=[1,0,0]) {
color(paint)
if(roof=="flat") { translate([0,-1,0]) cube(); }
else if(roof=="pitched") {
rotate([90,0,0]) linear_extrude(height=1)
polygon(points=[[0,0],[0,1],[0.5,1.5],[1,1],[1,0]]); }
else if(roof=="domical") {
translate([0,-1,0]){
translate([0.5,0.5,1]) sphere(r=0.5,$fn=20); cube(); }
} }
house();
translate([2,0,0]) house("pitched");
translate([4,0,0]) house("domical",[0,1,0]);
translate([6,0,0]) house(roof="pitched",paint=[0,0,1]);
translate([0,3,0]) house(paint=[0,0,0],roof="pitched");
translate([2,3,0]) house(roof="domical");
translate([4,3,0]) house(paint=[0,0.5,0.5]);
//example 3
element_data = [[0,"","",0], // must be in order
[1,"Hydrogen","H",1.008], // indexed via atomic number
[2,"Helium", "He",4.003] // redundant atomic number to preserve your sanity later
];
Hydrogen = 1;
Helium = 2;
module coaster(atomic_number){
element = element_data[atomic_number][1];
symbol = element_data[atomic_number][2];
atomic_mass = element_data[atomic_number][3];
//rest of script
}
运算符模块
[edit | edit source]子项
[edit | edit source]使用 children()
允许模块充当作用于此模块实例化中任何或所有对象的运算符。在使用中,运算符模块不以分号结尾。
name ( parameter values ){scope of operator}
基本上 children()
命令用于对由范围聚焦的对象应用修改
module myModification() { rotate([0,45,0]) children(); } myModification() // The modification { // Begin focus cylinder(10,4,4); // First child cube([20,2,2], true); // Second child } // End focus
对象通过从 0 到 $children-1 的整数索引。OpenSCAD 将 $children 设置为范围内的对象总数。分组到子范围内的对象被视为一个子项。 参见下面单独子项的示例 和 变量范围。请注意,children()
、echo()
和空块语句(包括 if
s)被计为 $children
对象,即使没有几何图形存在(截至 v2017.12.23)。
children(); all children children(index); value or variable to select one child children([start : step : end]); select from start to end incremented by step children([start : end]); step defaults to 1 or -1 children([vector]); selection of several children
已弃用的 child()
模块
在 2013.06 版本之前,使用的是现在已弃用的 child()
模块。这可以根据表格转换为新的 children()
截至 2013.06 | 2014.03 及更高版本 |
---|---|
child() |
children(0) |
child(x) |
children(x) |
for (a = [0:$children-1]) child(a) |
children([0:$children-1]) |
示例
//Use all children
module move(x=0,y=0,z=0,rx=0,ry=0,rz=0)
{ translate([x,y,z])rotate([rx,ry,rz]) children(); }
move(10) cube(10,true);
move(-10) cube(10,true);
move(z=7.07, ry=45)cube(10,true);
move(z=-7.07,ry=45)cube(10,true);
//Use only the first child, multiple times
module lineup(num, space) {
for (i = [0 : num-1])
translate([ space*i, 0, 0 ]) children(0);
}
lineup(5, 65){ sphere(30);cube(35);}
//Separate action for each child
module SeparateChildren(space){
for ( i= [0:1:$children-1]) // step needed in case $children < 2
translate([i*space,0,0]) {children(i);text(str(i));}
}
SeparateChildren(-20){
cube(5); // 0
sphere(5); // 1
translate([0,20,0]){ // 2
cube(5);
sphere(5);
}
cylinder(15); // 3
cube(8,true); // 4
}
translate([0,40,0])color("lightblue")
SeparateChildren(20){cube(3,true);}
//Multiple ranges
module MultiRange(){
color("lightblue") children([0:1]);
color("lightgreen")children([2:$children-2]);
color("lightpink") children($children-1);
}
MultiRange()
{
cube(5); // 0
sphere(5); // 1
translate([0,20,0]){ // 2
cube(5);
sphere(5);
}
cylinder(15); // 3
cube(8,true); // 4
}
更多模块示例
[edit | edit source]- 对象
module arrow(){
cylinder(10);
cube([4,.5,3],true);
cube([.5,4,3],true);
translate([0,0,10]) cylinder(4,2,0,true);
}
module cannon(){
difference(){union()
{sphere(10);cylinder(40,10,8);} cylinder(41,4,4);
} }
module base(){
difference(){
cube([40,30,20],true);
translate([0,0,5]) cube([50,20,15],true);
} }
- 运算符
module aim(elevation,azimuth=0)
{ rotate([0,0,azimuth])
{ rotate([0,90-elevation,0]) children(0);
children([1:1:$children-1]); // step needed in case $children < 2
} }
aim(30,20)arrow();
aim(35,270)cannon();
aim(15){cannon();base();}
module RotaryCluster(radius=30,number=8)
for (azimuth =[0:360/number:359])
rotate([0,0,azimuth])
translate([radius,0,0]) { children();
translate([40,0,30]) text(str(azimuth)); }
RotaryCluster(200,7) color("lightgreen") aim(15){cannon();base();}
rotate([0,0,110]) RotaryCluster(100,4.5) aim(35)cannon();
color("LightBlue")aim(55,30){cannon();base();}
递归模块
[edit | edit source]与函数一样,模块可能包含递归调用。但是,递归模块没有尾递归消除。
下面的代码生成了一个粗糙的树模型。每个树枝本身是树的修改版本,由递归产生。注意将递归深度(分支)n 保持在 7 以下,因为基元的数量和预览时间呈指数增长。
module simple_tree(size, dna, n) {
if (n > 0) {
// trunk
cylinder(r1=size/10, r2=size/12, h=size, $fn=24);
// branches
translate([0,0,size])
for(bd = dna) {
angx = bd[0];
angz = bd[1];
scal = bd[2];
rotate([angx,0,angz])
simple_tree(scal*size, dna, n-1);
}
}
else { // leaves
color("green")
scale([1,1,3])
translate([0,0,size/6])
rotate([90,0,0])
cylinder(r=size/6,h=size/10);
}
}
// dna is a list of branching data bd of the tree:
// bd[0] - inclination of the branch
// bd[1] - Z rotation angle of the branch
// bd[2] - relative scale of the branch
dna = [ [12, 80, 0.85], [55, 0, 0.6],
[62, 125, 0.6], [57, -125, 0.6] ];
simple_tree(50, dna, 5);
另一个递归模块的示例可以在 技巧和窍门 中找到
覆盖内置模块
[edit | edit source]可以覆盖内置模块。
一个简单但无用的例子是
module sphere(){
square();
}
sphere();
注意,覆盖后无法调用内置的 sphere
模块。
使用这种语言特性的更明智的方法是使用挤压的二维基元覆盖三维基元。这允许在默认参数中添加附加内容并添加附加参数。
第 8 章 - 调试辅助
[edit | edit source]OpenSCAD 用户手册/OpenSCAD 语言
修改符用于更改子节点的外观或行为。它们在调试中特别有用,在那里可以用来突出显示特定对象,或包含或排除它们进行渲染。
高级概念
[edit | edit source]OpenSCAD 使用不同的库来实现功能,这可能会给 F5 预览行为带来一些不一致性。传统的变换(平移、旋转、缩放、镜像和多矩阵)在预览中使用 OpenGL 执行,而其他更高级的变换(如调整大小)执行 CGAL 操作,表现得像影响基础对象的 CSG 操作,而不仅仅是变换它。特别是这可能会影响修改符,特别是“#”和“%”,其中高亮可能不会直观地显示,例如高亮预调整大小的对象,但高亮后缩放的对象。
注意:修改符触发的颜色变化仅在“编译”模式下出现,而在“编译和渲染(CGAL)”模式下不出现。(参见颜色部分。)
背景修改符
[edit | edit source]忽略此子树的正常渲染过程,并以透明灰色绘制它(所有变换仍应用于此树中的节点)。
由于标记的子树被完全忽略,因此在使用它时可能会产生意想不到的效果,例如,在 difference()
中使用第一个对象。在这种情况下,此对象将以透明灰色渲染,但不会用作 difference()
的基础!
用法
% { ... }
示例
difference() {
cylinder (h = 12, r=5, center = true, $fn=100);
// first object to be subtracted
rotate ([90,0,0]) cylinder (h = 15, r=1, center = true, $fn=100);
// second object to be subtracted
%rotate ([0,90,0]) cylinder (h = 15, r=3, center = true, $fn=100);
}
示例输出
调试修改符
[edit | edit source]照常使用此子树进行渲染过程,但也以透明粉色绘制它。
用法
# { ... }
示例
difference() {
// start objects
cylinder (h = 12, r=5, center = true, $fn=100);
// first object to be subtracted
#rotate ([90,0,0]) cylinder (h = 15, r=1, center = true, $fn=100);
// second object to be subtracted
#rotate ([0,90,0]) cylinder (h = 15, r=3, center = true, $fn=100);
}
示例输出
根修改符
[edit | edit source]忽略设计其余部分,并使用此子树作为设计根。
用法
! { ... }
示例
difference() {
cube(10, center = true);
translate([0, 0, 5]) {
!rotate([90, 0, 0]) {
#cylinder(r = 2, h = 20, center = true, $fn = 40);
}
}
}
示例输出
如带有活动根修改符的示例输出所示,rotate()
被执行,因为它属于用根修改符标记的子树的一部分,但 translate()
没有效果。
禁用修改符
[edit | edit source]简单地忽略整个子树。
用法
* { ... }
示例
difference() {
cube(10, center = true);
translate([0, 0, 5]) {
rotate([0, 90, 0]) {
cylinder(r = 2, h = 20, center = true, $fn = 40);
}
*rotate([90, 0, 0]) {
#cylinder(r = 2, h = 20, center = true, $fn = 40);
}
}
}
示例输出
禁用修改符允许您注释掉一个或多个子树。与使用通常的行注释或多行注释相比,它了解层次结构,这使得即使无需搜索子树的结尾也能更容易地禁用更大的树。
回显语句
[edit | edit source]此函数将内容打印到编译窗口(又称控制台)。对调试代码很有用。另请参阅字符串函数 str()。
数值四舍五入到 5 位有效数字。
使用 'variable=variable' 作为表达式来轻松标记变量非常方便,请查看下面的示例。
使用示例
my_h=50; my_r=100; echo("This is a cylinder with h=", my_h, " and r=", my_r); echo(my_h=my_h,my_r=my_r); // shortcut cylinder(h=my_h, r=my_r);
在控制台中显示为
ECHO: "This is a cylinder with h=", 50, " and r=", 100 ECHO: my_h = 50, my_r = 100
第 9 章 - 外部库和代码文件
[edit | edit source]OpenSCAD 用户手册/OpenSCAD 语言
使用和包含
[edit | edit source]为了在 OpenSCAD 中包含来自外部文件代码,可以使用两个命令
include <文件名>
的作用与在包含文件中的位置写入包含文件的内容相同,并且use <filename>
导入模块和函数,但不执行任何命令,除了定义。
库文件在以下位置搜索:设计打开的文件夹、OpenSCAD 安装的库文件夹以及 OPENSCADPATH 环境变量指定的文件夹。你可以使用相对路径指定这两个位置中的任何一个。如果它们位于其他位置,则必须提供完整路径。较新的版本具有预定义的用户库,请参阅 OpenSCAD_User_Manual/Libraries 页面,该页面还记录了 OpenSCAD 中包含的许多库文件。
通配符(*,例如 include <MCAD/*.scad>)不能用于包含多个文件。
Windows 和 Linux/Mac 使用不同的目录分隔符。Windows 使用 \,例如 directory\file.ext,而其他平台使用 /,例如 directory/file.ext。这可能会导致跨平台问题。但是,Windows 上的 OpenSCAD 正确处理了 / 的使用,因此在所有 include 或 use 语句中使用 / 在所有平台上都能正常工作。
要访问父目录,可以在 Linux 下使用 ../。
使用 include <filename>
允许在库中指定默认变量。这些默认值可以在主代码中被覆盖。OpenSCAD 变量在程序生命周期中只有一个值。当有多个赋值时,它将采用最后一个值,但在变量首次创建时赋值。当在库中赋值时,这会产生影响,因为你后来用于更改默认值的任何变量必须在 include 语句之前赋值。请参阅下面的第二个示例。
可以覆盖 include
中的默认变量,例如
lib.scad
i=1; k=3; module x() { echo("hello world"); echo("i=",i,"j=",j,"k=",k); }
hello.scad
j=4; include <lib.scad> x(); i=5; x(); k=j; x();
产生以下内容
ECHO: "hello world" ECHO: "i=", 5, "j=", 4, "k=", 4 ECHO: "hello world" ECHO: "i=", 5, "j=", 4, "k=", 4 ECHO: "hello world" ECHO: "i=", 5, "j=", 4, "k=", 4
但是,将 j=4;
放在 include
之后将失败,产生
ECHO: "hello world" ECHO: "i=", 5, "j=", 4, "k=", undef ECHO: "hello world" ECHO: "i=", 5, "j=", 4, "k=", undef ECHO: "hello world" ECHO: "i=", 5, "j=", 4, "k=", undef
用于生成环形的库文件可能如下所示(定义函数并提供示例)
ring.scad
module ring(r1, r2, h) { difference() { cylinder(r = r1, h = h); translate([ 0, 0, -1 ]) cylinder(r = r2, h = h+2); } } ring(5, 4, 10);
使用以下方法包含库
include <ring.scad> rotate([90, 0, 0]) ring(10, 1, 1);
将导致显示示例环形,以及旋转后的环形,但是
use <ring.scad> rotate([90, 0, 0]) ring(10, 1, 1);
仅显示旋转后的环形。
如果使用 use 函数,请确保将 use 语句放在文件开头,或者至少不要放在模块中!
这可以正常工作
// a.scad use <ring.scad> module a() { ring(); }
但这会导致语法错误
//a.scad module a() { use <ring.scad> ring(); }
OpenSCAD 执行对 include 和 use 的嵌套调用。这里有一个注意事项,即 use 仅将函数和模块引入本地文件上下文。因此,对 use 的嵌套调用不会对基文件的环境产生任何影响;子 use 调用在父 use 上下文中工作,但这样导入的模块和函数在基上下文看到它们之前就超出了上下文。
导入通过 import() 命令实现。
[注意: 需要版本 2015.03-2] 文件 >> 打开 命令可用于插入此命令。打开文件对话框的文件类型过滤器可能仅显示 OpenSCAD 文件,但文件名可以替换为通配符(例如 *.stl)以浏览其他文件类型。
导入文件以供在当前 OpenSCAD 模型中使用。文件扩展名用于确定类型。
- 3D 几何图形格式
- STL(ASCII 和二进制)
- OFF
- OBJ
- AMF(已弃用)
- 3MF
- 2D 几何图形格式
- DXF
- SVG
- 数据格式
- JSON [注意: 需要版本 开发快照]
- 其他
- CSG 可以使用 include<> 导入,也可以像 SCAD 文件一样加载
- PNG 可以使用 surface() 导入
- <file>
- 包含文件路径的字符串:如果给定的路径不是绝对路径,则将其解析为相对于导入脚本的路径。请注意,当使用
include<>
与使用import()
的脚本一起使用时,它是相对于执行include<>
的脚本的路径。 - <convexity>
- 整数。convexity 参数指定与对象相交的光线可能穿透的最大正面数量(或背面数量)。此参数仅在 OpenCSG 预览模式下正确显示对象时需要,对多面体渲染没有影响。可选。
- <id>
- 字符串。仅限于 SVG 导入,要导入的元素或组的 ID。可选。 [注意: 需要版本 开发快照]
- <layer>
- 仅限于 DXF 和 SVG 导入,指定要导入的特定图层。可选。
- $fn
- 双精度浮点数。将圆形、弧线和曲线转换为多边形时要使用的多边形段数。 [注意: 需要版本 开发快照]
- $fa
- 双精度浮点数。将圆形和弧线转换为多边形时要使用的最小角度步长。 [注意: 需要版本 开发快照]
- $fs
- 双精度浮点数。将圆形和弧线转换为多边形时要使用的最小线段长度。 [注意: 需要版本 开发快照]
import("example012.stl", convexity=3);
import("D:/Documents and Settings/User/My Documents/Gear.stl", convexity=3); (Windows users must "escape" the backslashes by writing them doubled, or replace the backslashes with forward slashes.)
data = import("data.json"); // for data formats the file content is assigned to a variable
读取 2D DXF 文件的图层并创建 3D 形状。
linear_extrude(height = 5, center = true, convexity = 10) import_dxf(file = "example009.dxf", layer = "plate");
此图像显示了一个凸度为 2 的 2D 形状,因为用红色指示的光线最多与 2D 形状相交两次。3D 形状的凸度将以类似的方式确定。将其设置为 10 对于大多数情况应该可以正常工作。
在最新版本的 OpenSCAD 中,import() 现在用于导入 2D(用于挤出的 DXF)和 3D(STL)文件。
如果你想稍后渲染导入的 STL 文件,则必须确保 STL 文件是“干净”的。这意味着网格必须是流形,并且不应包含孔或自相交。如果 STL 不干净,它可能最初导入并预览正常,但只要你尝试通过将其与其他内容结合渲染来对它执行计算几何,你可能会收到有关它不是流形的警告,你导入的 stl 可能会完全从输出中消失,或者你可能会收到类似的错误
CGAL error in CGAL_Build_PolySet: CGAL ERROR: assertion violation! Expr: check_protocoll == 0 File: /home/don/openscad_deps/mxe/usr/i686-pc-mingw32/include/CGAL/Polyhedron_incremental_builder_3.h Line: 199
或
CGAL error in CGAL_Nef_polyhedron3(): CGAL ERROR: assertion violation! Expr: pe_prev->is_border() || !internal::Plane_constructor<Plane>::get_plane(pe_prev->facet(),pe_prev->facet()->plane()).is_degenerate() File: /home/don/openscad_deps/mxe/usr/i686-pc-mingw32/include/CGAL/Nef_3/polyhedron_3_to_nef_3.h Line: 253
为了清理 STL 文件,你有以下选择
- 使用 http://wiki.netfabb.com/Semi-Automatic_Repair_Options 修复孔,但不修复自相交。
- 使用 netfabb basic。此免费软件没有关闭孔的选项,也无法修复自相交。
- 使用 MeshLab。此免费软件可以修复所有问题。
使用 MeshLab,你可以执行以下操作
- 渲染 - 显示非流形边
- 渲染 - 显示非流形顶点
- 如果发现,请使用过滤器 - 选择 - 选择非流形边或选择非流形顶点 - 应用 - 关闭。然后单击按钮“删除当前选定的顶点集...”或查看 http://www.youtube.com/watch?v=oDx0Tgy0UHo 获取说明视频。屏幕应显示“0 个非流形边”、“0 个非流形顶点”。
接下来,你可以单击“填充孔”图标,选择所有孔,然后单击“填充”,然后单击“接受”。你可能需要重复此操作几次。
使用文件 - 导出网格保存 STL。
如果 Meshlab 无法填充最后一个孔,则 Blender 可能会有所帮助
- 启动 Blender
- `X, 1` 用于移除默认对象
- 文件, 导入, Stl
- `Tab` 用于编辑网格
- `A` 用于取消选择所有顶点
- `Alt+Ctrl+Shift+M` 用于选择所有非流形顶点
- `MMB` 用于旋转, `Shift+MMB` 用于平移, `滚轮` 用于缩放
- `C` 用于“圆形”选择, `Esc` 用于结束
- `Alt+M, 1` 用于合并, 或者使用 `空格` 并搜索 "merge" 作为替代方案
- 合并顶点是填充孔洞的一种有用方法, 在这种情况下, 顶点非常紧密地堆积在一起, 以至于几何形状的微小变化与典型 3D 打印机的精度相比微不足道
这需要在开发版本中启用 import-function 功能。如果您导入一个后缀为 "json" 或 "csv" 的文件, 导入将返回一个 JSON 对象数据类型, 该类型无法以字面值表示 - 它只能被导入。
注意: 后缀为 ".csv" 的文件也会被视为 JSON 文件, 尽管这些格式并不相同 - 从电子表格程序保存的 CSV 文件不能在这里使用。
/* input file contains:
{"people":[{"name":"Helen", "age":19}, {"name":"Chris", "age":32}]}
*/
t = import("people.json");
echo(t);
people = t.people;
for(i=[0:len(people)-1]) {
person = people[i];
echo(str(person.name, ": ", person.age));
}
这将导致以下输出
ECHO: { people = [{ age = 19; name = "Helen"; }, { age = 32; name = "Chris"; }]; }
ECHO: "Helen: 19"
ECHO: "Chris: 32"
[已弃用: import_dxf() 将在未来的版本中移除。请使用 import() 代替。]
读取 DXF 文件并创建一个 3D 形状。
linear_extrude(height = 5, center = true, convexity = 10) import_dxf(file = "example009.dxf", layer = "plate");
[已弃用: import_stl() 将在未来的版本中移除。请使用 import() 代替。见上文。]
导入 STL 文件以供在当前 OpenSCAD 模型中使用
import_stl("body.stl", convexity = 5);
surface()
从文本或图像文件读取 高度图 信息。它可以读取 PNG 文件。
- 文件
- 字符串。包含高度图数据的文件的路径。
- 中心
- 布尔值。这决定了生成的物体的定位。如果为真,则物体在 X 轴和 Y 轴上居中。否则,物体将放置在正象限中。默认值为 false。
- 反转
- 布尔值。反转导入图像的颜色值如何转换为高度值。这在导入文本数据文件时没有影响。默认值为 false。 [注意: 需要版本 2015.03]
- 凸性
- 整数。凸度参数指定与对象相交的射线可能穿透的最大正面(背面)数量。此参数仅在 OpenCSG 预览模式下正确显示对象时需要,对最终渲染没有影响。
基于文本的高度图的格式是表示特定点的高度值的数字矩阵。行映射到 Y 轴,列映射到 X 轴。数字必须用空格或制表符分隔。空行和以 # 字符开头的行将被忽略。
[注意: 需要版本 2015.03]
目前仅支持 PNG 图像。图像的 alpha 通道信息将被忽略,像素的高度通过将颜色值转换为 灰度 来确定,使用 sRGB 颜色空间的线性亮度(Y = 0.2126R + 0.7152G + 0.0722B)。灰度值被缩放至 0 到 100 的范围。
示例 1
//surface.scad surface(file = "surface.dat", center = true, convexity = 5); %translate([0,0,5])cube([10,10,10], center =true);
#surface.dat 10 9 8 7 6 5 5 5 5 5 9 8 7 6 6 4 3 2 1 0 8 7 6 6 4 3 2 1 0 0 7 6 6 4 3 2 1 0 0 0 6 6 4 3 2 1 1 0 0 0 6 6 3 2 1 1 1 0 0 0 6 6 2 1 1 1 1 0 0 0 6 6 1 0 0 0 0 0 0 0 3 1 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0
结果
示例 2
// example010.dat generated using octave: // d = (sin(1:0.2:10)' * cos(1:0.2:10)) * 10; // save("example010.dat", "d"); intersection() { surface(file = "example010.dat", center = true, convexity = 5); rotate(45, [0, 0, 1]) surface(file = "example010.dat", center = true, convexity = 5); }
示例 3
[注意: 需要版本 2015.03]
// Example 3a scale([1, 1, 0.1]) surface(file = "smiley.png", center = true);
// Example 3b scale([1, 1, 0.1]) surface(file = "smiley.png", center = true, invert = true);