znlgis 博客

GIS开发与技术分享

第04章:参数、变量、函数、模块与作用域

1. 参数化设计的核心

OpenSCAD 最有价值的能力是参数化。参数化不是简单地把数字放到变量里,而是建立一套稳定的尺寸关系。一个好的 OpenSCAD 模型应该能回答:哪些参数允许用户修改?哪些尺寸由公式推导?哪些约束必须检查?哪些误差来自制造工艺?

wall = 2.4;
clearance = 0.3;
box = [80, 50, 24];
inner = [box[0] - 2*wall, box[1] - 2*wall, box[2] - wall];

如果模型中到处都是魔法数字,后期修改会非常痛苦。建议在文件顶部集中定义公开参数,在模块内部定义局部派生参数。

2. 模块 module

module 用于生成几何或组织子对象。它类似“可复用几何组件”。

module screw_hole(d = 3.2, h = 10, countersink = false) {
    cylinder(h = h, d = d, center = true);
    if (countersink)
        translate([0, 0, h/2 - 1]) cylinder(h = 2, d1 = 7, d2 = d, center = true);
}

调用时可以使用位置参数或命名参数:

screw_hole(3.2, 8, true);
screw_hole(d = 4.2, h = 12, countersink = true);

复杂项目推荐使用命名参数,尤其是参数数量较多时。

3. 函数 function

function 返回值,不直接生成几何。它适合计算尺寸、点集、角度、列表和条件结果。

function clamp(x, lo, hi) = min(max(x, lo), hi);
function polar(r, a) = [r * cos(a), r * sin(a)];

points = [for (i = [0:5]) polar(20, i * 60)];
polygon(points);

函数应该保持纯粹:同样输入得到同样输出,不依赖隐含状态。这样更容易测试和复用。

4. 作用域

OpenSCAD 有全局作用域、模块作用域、函数作用域和 let 局部作用域。理解作用域可以避免变量名冲突。

thickness = 4;

module plate(size = [40, 20]) {
    local_thickness = thickness;
    cube([size[0], size[1], local_thickness], center = true);
}

建议:

  • 全局参数使用清晰长名,如 base_plate_thickness
  • 模块内部派生变量使用短名但不泄露到外部。
  • 库文件避免使用容易冲突的全局变量。
  • 对外参数尽量通过模块参数传入,而不是依赖全局变量。

5. children() 与高阶模块

children() 允许模块接收调用处的子对象,类似几何过滤器或包装器。

module mirrored_x() {
    children();
    mirror([1, 0, 0]) children();
}

mirrored_x()
    translate([15, 0, 0]) cylinder(h = 5, d = 6);

这类模块适合封装对称、阵列、颜色、调试显示、局部坐标系和重复变换。

6. include 与 use

OpenSCAD 用 includeuse 引入其他文件:

include <config.scad>
use <parts/bracket.scad>

一般理解:

  • include 会引入变量、函数和模块,适合配置文件或共享常量。
  • use 更偏向只使用模块和函数,避免执行被引入文件中的顶层几何。

实际项目中建议把库文件设计为“顶层不生成几何”,这样无论 use 还是 include 都更可控。

7. 参数校验

OpenSCAD 没有强类型系统,但可以通过 assert 或条件输出保护模型。

module box_shell(size = [60, 40, 20], wall = 2) {
    assert(wall > 0, "wall must be positive");
    assert(size[0] > 2*wall && size[1] > 2*wall, "wall too thick for box size");

    difference() {
        cube(size, center = true);
        translate([0, 0, wall])
            cube([size[0]-2*wall, size[1]-2*wall, size[2]], center = true);
    }
}

公开库模块应尽量校验关键参数,例如负半径、零厚度、孔径大于外径、阵列数量小于 1 等。

8. 命名规范

推荐命名风格:

  • 模块名使用小写加下划线:mounting_plate
  • 函数名使用动词或数学含义:bolt_circle_pointsdeg_to_rad
  • 参数名体现单位或语义:hole_dwall_tclearance
  • 布尔参数使用 has_use_show_ 前缀。
  • 避免 abtmp 这类无法说明设计意图的名字。

9. 可配置模型的分层

一个可维护模型通常分三层:

  1. 配置层:公开参数、产品规格、制造补偿。
  2. 零件层:各模块负责单个零件或几何特征。
  3. 装配层:放置零件、显示参考、导出单件或整件。
part = "assembly"; // "base", "cover", "assembly"

if (part == "base") base();
else if (part == "cover") cover();
else assembly();

这种模式便于命令行批量导出不同零件。

10. 设计 API 的原则

当你编写给别人使用的 OpenSCAD 模块时,应像设计软件 API 一样认真:

  • 参数默认值能生成有效模型。
  • 参数顺序从最重要到最少改动。
  • 单位、坐标原点和朝向在文档中明确。
  • 不在模块内部随意改变全局特殊变量,必要时作为参数传入。
  • 不把导出、调试、颜色和真实几何混在一起。