OpenSCAD 用户手册/其他语言特性
特殊变量提供了一种向模块和函数传递参数的替代方法。所有以 '$' 开头的用户定义或 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 的相应结果应该保持较小,至少在设计最终确定之前,可以增加最终结果的值。不建议将 $fn 设置为 128 以上,或者只在特定情况下使用,建议将其设置为 50 以下以提高性能。
您也可以在预览和渲染时使用两个不同的值
$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 设置为模型的静止位置。
translate ([0, 0, 10*sin($t*360)])
sphere(2);
使球体在 Z 轴上 -10 和 +10 之间振荡。
rotate ([0, 0, $t*360])
square(5);
围绕 Z 轴使正方形绕一个角旋转。要使正方形围绕其中心旋转,请使用
rotate ([0, 0, $t*360])
square(5, center=true);
动画中的所有部分在相同的时间 $t 内完成一个运动周期,跳回零,然后重新开始。但是,可以为周期设置不同的步数,以在同一个动画中呈现不同速度的错觉。这可以用来制作不同尺寸的啮合齿轮的动画。
rotate([0, 0, $t*360/17])
gear(teeth=17);
和
rotate([0, 0, -$t*360/31])
gear(teeth=31);
rotate ([0, 0, $t*360])
translate ([10, 0])
square(5, center=true);
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 为真。当在渲染(F6)中时,$preview 为假。
例如,这可以用来在预览期间减少细节以节省时间,而不会在最终渲染结果中丢失细节
$fn = $preview ? 12 : 72; sphere(r = 1);
请注意,渲染模块不会影响 $preview
render(){ $fn = $preview ? 12 : 72; sphere(r = 1); }
另一个用途可能是让预览显示一个装配视图,而渲染只生成用于打印的打印零件布局。
如果打印零件需要额外的功能,例如悬空孔的支撑,则预览可以省略这些功能,以显示后处理后的完成零件。
当从命令行运行 OpenSCAD 时,$preview 仅在使用 OpenCSG 生成 PNG 图像时为真。它在使用 CGAL 生成 STL、DXF 和 SVG 文件时为假。它在生成 CSG 和 ECHO 文件时也为假。这可能就是你想要的,也可能不是,但你总是可以用 -D 选项在命令行中覆盖它,就像任何其他变量一样。
回显模块
[edit | edit source]echo() 模块将内容打印到编译窗口(也称为控制台)。对于调试代码很有用。另请参见 String 函数 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 用户手册/常见问题解答#为什么模型的某些部分(例如孔)渲染不正确?
用法示例: (需要描述)
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 轴上居中。否则,物体将放置在正象限中。默认为假。
- 反转
- 布尔值。反转导入图像的颜色值如何转换为高度值。在导入文本数据文件时,这没有影响。默认为假:使用此参数产生的几何体将以其顶部位于 z = 0 平面处的位置定位。在高度图下方自动添加一个厚度为一个单位的薄“占位”层。:: [注意: 需要版本 2015.03]
- 凸性
- 整数。凸度参数指定光线与物体相交时可能穿过的正面(背面)的最大数量。此参数仅在 OpenCSG 预览模式下正确显示物体时需要,对最终渲染没有影响。
基于文本的高度图的格式是数字矩阵,表示特定点的海拔高度。行映射在 Y 轴方向,列映射在 X 轴方向,相邻行和列之间单位增量为 1。数字必须用空格或制表符分隔。以 # 字符开头的空行和行将被忽略。
[注意: 需要版本 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 将返回一个列表列表,其中包含 match_value 每个元素最多 num_returns_per_match 个索引值。
- 请参阅下面的示例 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_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"
[注意: 需要版本 2019.05]
另见 断言(软件开发)
Assert 评估一个逻辑表达式。如果表达式评估结果为假,则预览/渲染的生成将停止,并且通过控制台报告错误条件。报告包含表达式的字符串表示形式和在 assert 命令中指定的附加字符串(可选)。
assert(condition); assert(condition, message);
参数
- 条件
- 表达式。要评估的表达式,作为对断言的检查。
- 消息
- 字符串。断言失败时输出的可选消息。
最简单的示例是简单的 assert(false);
,例如在一个名为 assert_example1.scad
的文件中。
cube();
assert(false);
sphere();
// ERROR: Assertion 'false' failed in file assert_example1.scad, line 2
这个示例几乎没有用,但简单的 assert(false);
可以用于应该不可到达的代码部分。
一个有用的示例是检查输入参数的有效性
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
在编写库时,在断言失败的情况下向用户输出更多信息可能很有用。
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
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