Post

puzzlecad 机械谜题建模笔记

学习了一下 puzzlecad 进行机械谜题 3d 建模,这货过于冷门,搜索引擎 0 中文结果,下载记录也只有寥寥几千,但十分简单好用,写一篇当作笔记/教程吧。

Why

一直很喜欢机械谜题 (mechanical puzzle),小时候小卖部里,各式各样一两块钱的金属套环 (后来知道是盗版魔金),每一款都能拿到手玩很久。长大以后,也逐渐关注了几个谜题视频主,云了不少。但真想上手来玩,却有诸多困难。如果想支持正版,动辄上百刀的价格不说,shipment 时间也久,而且这些谜题大多是小作坊手工制作,常年缺货。即使是盗版,价格也不便宜,复杂一些的常常大几十 RMB。除了大厂的畅销货有厂家开模,冷门一些的也无从购买。前段时间入手了一台 3d 打印机,找了找网上谜题的模型也不是特别丰富,就想着自己建模实现 puzzle 自由。

弯路

由长方体组成,方方正正的谜题,用一般的 CAD 建模原则上并不难,稍微学了一下就上手复刻了一些简单的谜题。打印效果可以接受,但也发现了谜题复杂以后的一些坑:
1,严丝合缝或拼插型的 (如 Turning Interlocking Cubes),需要预留空间,要提前算好。
2,修改麻烦,尤其是涉及1,且容易出错。
3,一些模型悬空部分过多,使用支撑不经济也难保障平整度。最好的方法是拼插,没有经验手搓很难保证性能。

这么笨的做法一定是不正确的,就找了找现成的工具。找到了 Printable Puzzle Project,以及 OpenSCAD 的 puzzlecad 库。

基本使用

作为开源项目,openSCAD 是 well documented 的,所以其实没啥好讲的,不过还是水一下(

下载安装 OpenSCAD,下载最新版 puzzlecad,解压后打开 half-hour-example.scad

默认的单位是mm。
$burr_scale设定单位正方体的长度 (是考虑嵌入后的整体尺寸)。
$burr_inset设定嵌入的尺寸,嵌入是为零件紧贴滑动或插入时,留出的空间,方便活动。
$burr_bevel设定切边的大小。
$unit_beveled设定是否对每个正方体切边,若否只有外沿切边。

1
2
3
4
5
6
7
8
burr_plate([
    ["xxx|.x.", "...|.x."],
    [".xx|xx.", "...|.x."],
    [".x.|xxx", "...|x.."],
    [".x.|xxx"],
    ["x..|xxx"],
    ["x.|xx", "..|.x"]
]);

burr_plate是所有模型组成的二维数组,每个子数组表示单个模型。单个模型的每个元素表示沿 z 轴每层的排布,由底层到顶层。每层的字符串由 x 表示实,. 表示空,沿 y 轴由近到远表示各行,以 | 分隔。
当然最快的理解方法,是修改各个参数和模型二维数组,按F6重新渲染观察 3d 图像的变化。更详细的 doc 可以查看 puzzlecad-examples.scad
顶部导航选择文件File导出Export stl 就可以切片打印了。

以 Clutch 1 为例建模

Clutch 1 是由 Girish Sharma 设计的 2022 年 International Puzzle Party 获奖作品,可以看成完全由正方体组成,只需要使用 puzzlecad 最基本的特性,十分适合练手。 clutch 1

搜寻信息,上图是官方商店的细节图,基本可以确定 3d 模型。还找到了一些其它的图像视频佐证。

然后是建模,我的经验是先不要考虑可打印性和拼插接口,以方便自己理解的方式码好每个零件,旋转对照确认与实物无误。

接下来是设计最大程度免去支撑的拼插接口 (snap joint),详见puzzlecad-examples.scad。puzzlecad 有一个自动生成拼插的功能,但并不好用,稍微复杂一些的零件建议手动切分。先肉眼观察确定能减少支撑的切分位置,把零件数组拆分成两个子零件数组,注意如果切口不在整层处,需要用空 . 将子零件数组补齐。
然后在子零件接口所在的立方体对应字符之后,插入形如标识串 {connect=(m|f)[xyz](+|-)([xyz](+|-))?}mf表示公头或母头。x+, x-, y+, y-, z+, z-表示该立方体与数轴方向相关的 6 个面。第一项 [xyz](+|-)表示接口所在的立方体的面,第二项([xyz](+|-))?可选表示箭头状接口的指向,若缺省则生成无向的方形接口。
例如 burr_plate([["x..|xxx{connect=mz+y+}"], ["x{connect=fz+y+}|x"]]);生成的接口如图所示。 example

接着就是 routine work,完成后代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
include <puzzlecad.scad>

$burr_scale = 16;
$burr_inset = 0.1;
$burr_bevel = 1.3;
//$unit_beveled = true;

//p1 whole
*burr_piece(["xxxx|...x|...x|xxxx","....|x...|....|x..x","....|x...|....|...x","....|x...|x...|xxxx"]);
//p1 split
*burr_plate([["xxxx|...x|...x|xxxx","....|....|....|x..x{connect=fz+y+}"],["....|x...|....|....","....|x...|....|...x{connect=mz-y+}","....|x...|x...|xxxx"]]);
//p2 whole
*burr_piece(["x...|x...|xx..|xxx.","x...|...x|...x|...x","xxx.|..xx|....|...."]);
//p2 split
*burr_plate([["x{connect=fz+y+}...|x...|xx..|xxx."],["x{connect=mz-y+}...|...x|...x|...x","xxx.|..xx|....|...."]]);
//p3 whole
*burr_piece(["..x.|..xx|..xx|....","....|....|x.x.|....","....|....|xxx.|..x."]);
//p3 split
*burr_plate([["x.|xx|x{connect=fz+y+}x"],["x.x{connect=mz-y+}|...","xxx|..x"]]);

前面的长度参数是经过一些测试和参考后得来的,我的挤出机并未调校过,实际打印 xy 平面会微大一些,实际玩起来偏紧,但不使用任何蛮力就可以完成 puzzle。成品如图:
piece goal
不得不说挺难的,花了三个多小时解出(

puzzlecad 还支持更复杂的几何体建模与斜向的复杂切分,未来用到这些高级特性的时候或许会更新一下。

Tips

填充率至少 20% 并且使用 cubic 填充,snap joints 太紧先用砂纸打磨。不要问我掰断了多少 snap joints (

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.