首页
/ CLI-Anything 3MF 网格几何编辑器:圆柱孔检测、径向缩放与切片元数据无损保留实战

CLI-Anything 3MF 网格几何编辑器:圆柱孔检测、径向缩放与切片元数据无损保留实战

2026-09-05 11:07:25作者:仰钰奇

cli-anything-3mf 是 CLI-Anything 项目中面向 3D 打印 3MF 文件的"功能感知"网格几何编辑器:它能通过多平面截面分析自动检测模型中的圆柱孔、测量孔径,并沿径向缩放孔壁顶点实现精确改径,同时保留 BambuStudio / PrusaSlicer 的切片元数据。读完本文,你将掌握该工具的 5 个核心命令(info / inspect / resize / repair / compare)的完整用法与全部可选参数,理解其孔检测置信度计算、径向缩放公式与网格修复流水线的源码级原理,并知道它如何做到重打包后切片软件"零配置丢失"。

1. 工具定位:不止于"三角面汤"

传统 3D 打印 CLI 工具(PrusaSlicer CLI、BambuStudio CLI、admesh)把网格当作不透明的三角面集合处理,而 cli-anything-3mf 提供的是 feature-aware geometry editing(功能感知的几何编辑)——它可以"看懂"网格中的圆柱孔这一特征,测量其直径,并通过命令行修改它。

安装方式(继承自仓库技能文档 SKILL.mdREADME):

pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=3MF/agent-harness

开发模式安装:

cd 3MF/agent-harness
pip install -e ".[dev]"

根据 setup.py,依赖为 click>=8.0.0prompt-toolkit>=3.0.0numpy>=1.24.0scipy>=1.10.0trimesh>=4.0.0,控制台入口注册为 cli-anything-3mf=cli_anything.threemf.threemf_cli:main

2. 3MF 文件格式基础

理解该工具的前提是理解 3MF 容器结构。一个 3MF 文件本质是 ZIP 归档,内部包含(见 3MF.md):

  • [Content_Types].xml — MIME 类型声明;
  • _rels/.rels — 关系定义(OPC 标准);
  • 3D/3dmodel.model — XML 网格数据,核心命名空间为 http://schemas.microsoft.com/3dmanufacturing/core/2015/02;
  • Metadata/ — 切片器设置、缩略图、plate 配置(BambuStudio、PrusaSlicer 等)。

parser.py 的解析策略是"网格数据结构化 + 其余内容字节级保留":parse_3mf() 打开 ZIP 后,除主模型 XML 外的所有成员都被读入 raw_entries 字典({zip_path: bytes}),模型 XML 则解析为不可变的 MeshData(N×3 float64 顶点数组 + M×3 int32 三角形索引数组)与 ThreeMFData(meshes、unit、model_path、metadata、raw_entries)两个 frozen dataclass。这一设计是后面"无损重打包"的基础。

3. 五个核心命令

CLI 入口在 threemf_cli.py,基于 Click 实现,直接运行 cli-anything-3mf(不带子命令)会进入交互式 REPL。

3.1 info:网格统计

cli-anything-3mf info <file.3mf>

输出每个 mesh 对象的顶点数、面数、包围盒(min/max/size)、水密性(watertight)、体积与表面积。源码中 info 命令对每个 mesh 调用 threemf_backend.pycompute_mesh_stats():体积仅在网格水密时给出,否则显示 N/A (not watertight)

3.2 inspect:圆柱孔检测

cli-anything-3mf inspect <file.3mf>

每个检测到的孔输出:直径、中心坐标、轴向范围(axis_min..axis_max)、置信度、孔壁顶点数。

该命令的完整参数集(来自 threemf_cli.py#L154-L158):

参数 短写 默认值 说明
--planes -n 20 垂直于孔轴的截面平面数量
--min-diameter 0.5 最小孔直径(mm),过滤掉微小特征
--min-confidence 0.7 最小检测置信度(0~1)
--axis -a 0 孔轴方向:0=X,1=Y,2=Z(截面垂直于该轴)
--mesh -m 0 多对象 3MF 中要分析的 mesh 索引

注意 --axis 默认指向 X 轴,源码注释解释原因是"大多数 3D 打印件的孔沿 X 方向"。检测参数由 inspector.InspectParams dataclass 承载,上述默认值与 inspector.py#L32-L39 中的定义一致。

3.3 resize:孔径缩放

# 将孔 0 和孔 1 改径到 4.2mm
cli-anything-3mf resize <file.3mf> --hole 0 --hole 1 --diameter 4.2 -o output.3mf

# 一次改多个孔(短写形式)
cli-anything-3mf resize model.3mf -h 0 -h 1 -h 2 -h 3 -d 4.2 -o output.3mf

resize 的参数在 inspect 参数基础上增加:

参数 短写 必填 说明
--hole -h 是(可重复) 要改径的孔 ID,来自 inspect 输出
--diameter -d 目标直径(mm),必须为正数
--output -o 输出文件路径
--overwrite 输出已存在时允许覆盖,否则报 FileExistsError

关键约束:resize 内部会重新执行一次孔检测以把 hole_id 解析回几何,因此 hole_id 编号只对同一次检测有效——你必须传与 inspect完全相同的检测参数(尤其是 --axis),否则 ID 对不上。这一点在 modifier.py#L39-L46 的文档字符串中被明确强调。

resize 完成后源码会自动执行一轮网格修复(见 §5.3),--json 模式下输出的 repairs 字段会报告合并了多少重复顶点、移除了多少个退化面;人类可读模式则打印 Auto-repair: N fixes applied

3.4 repair:网格修复

cli-anything-3mf repair <file.3mf> -o repaired.3mf

修复内容:合并重复顶点、移除退化三角面、移除无引用顶点、修正法线朝向。报告字段为 vertices_merged / degenerate_faces_removed / unreferenced_vertices_removed / 最终顶点面数。

3.5 compare:两文件对比

cli-anything-3mf compare <file1.3mf> <file2.3mf>

输出顶点数、面数、体积(mm3)的 file1/file2/diff 三列对比,以及双方的水密状态。实现是 inspector.pycompare_meshes(),对两个文件各自调用 compute_mesh_stats() 后取差值。

3.6 交互式 REPL

直接运行 cli-anything-3mf 进入 REPL(源码中是隐藏的 repl 子命令)。REPL 提示样式由 repl_skin.py 统一提供;REPL 内输入用 shlex.split 切分后回灌给同一个 Click 命令树,因此支持全部子命令与选项,help 查看可用命令,quit/exit/q 退出。REPL 模式下的错误不会触发进程退出(_repl_mode 全局位控制)。

4. JSON 输出:面向 Agent 的机器可读接口

所有命令支持全局 --json 标志,这是该工具"agent-native"定位的核心:

cli-anything-3mf --json inspect model.3mf

threemf_cli.py#L35-L47output() 函数看,--json 模式下所有数据以 json.dumps(data, indent=2) 输出;错误处理同样双通道:handle_error 装饰器捕获 RuntimeError/FileNotFoundError/ValueError/IndexError 后,JSON 模式输出 {"error": ..., "type": ...},文本模式向 stderr 打印 Error: ... 并以退出码 1 结束。这让 LLM Agent 可以稳定地解析成功/失败两种结果——例如先 --json inspect 拿到 hole_id 列表,再程序化拼装 resize 命令。

5. 核心算法解析(源码级)

5.1 孔检测:多平面截面 + Kasa 圆拟合 + 层次聚类

inspect_mesh()(inspector.py#L54-L190)的完整流程:

第一步,多平面截面。 cross_section_circles()(threemf_backend.py#L243-L351)沿指定轴取 num_planes 个等距截面,但截平面不是从边界开始,而是向内收缩 PLANE_INSET_FRACTION = 0.02(轴跨度的 2%),避免在端面处切出退化片段。每个截面用 trimesh 的 section() 得到 Path3D,再逐条离散轮廓投影到二维平面拟合圆;轮廓点少于 5 个的候选直接跳过。

第二步,Kasa 代数圆拟合。 fit_circle_least_squares() 最小化代数距离:解超定方程组 x² + y² + a·x + b·y + c = 0,中心为 (-a/2, -b/2),半径为 sqrt(a²/4 + b²/4 - c),并返回 RMS 拟合误差。相对拟合误差超过半径 5% 的轮廓判定为"不够圆"而丢弃。

第三步,层次聚类分组。 不同截面上的圆属于同一个孔,当且仅当它们的中心半径都接近。inspector.py#L271-L297_group_circles()(center_x, center_y, radius) 三元特征做 scipy 单链接层次聚类,距离阈值 _GROUP_DISTANCE_MM = 0.5mm。半径参与聚类是为了区分同心特征——例如实体上打穿的孔,截面会同时得到内孔和外轮廓两个圆,若只按中心聚类,二者半径会被平均成一个"假圆"。

第四步,置信度过滤。 置信度公式(inspector.py#L122-L130):

confidence = (detected_planes / total_planes) × (1 − mean_fit_error / mean_radius)

即"检测一致性 × 拟合质量",低于 min_confidence(默认 0.7)或平均直径小于 min_hole_diameter(默认 0.5mm)的候选被丢弃。

第五步,内孔判别。 这是区分"孔"与"外表面圆柱"的关键:孔的圆柱壁面向内(法线指向轴线),而外边界、实心圆柱体、凸台的法线朝外。_is_interior_hole() 采样孔壁面上的法线,计算其径向分量均值,小于 0(指向轴线)才认定为孔;若网格绕向不一致(winding inconsistent)则保守地保留候选。注释说明该判别同时兼容通孔与盲孔,而基于顶点包围的测试会在盲孔底部失效。

此外,轴向范围不直接取截平面层级,而是用 _wall_axial_extent() 从实际孔壁顶点反推(带一个 inset 的外扩搜索窗),以恢复恰好位于零件端面上的 rim 顶点——这正是让后续 resize 能移动孔缘顶点的修复点。

5.2 孔径缩放:径向顶点缩放

resize_single_hole()(modifier.py#L92-L146)对每个目标孔:

  1. 计算每个顶点到孔轴的距离,选出径向距离落在 [radius − 0.06, radius + 0.06]mm 内且轴向坐标落在孔范围内(同样带 0.06mm 容差)的孔壁顶点,容差常量 _WALL_RADIUS_TOLERANCE = 0.06;
  2. 对这些顶点执行径向缩放:
new_pos = center + direction × (new_r / old_r)

即只修改垂直于孔轴的两个坐标,轴向坐标不动,顶点的角向位置(angular position)与网格拓扑(三角形连接关系)完全保留; 3. 返回新的 MeshData(不可变 dataclass 的 replace 副本)与改动报告 {hole_id, old_diameter, new_diameter, vertices_moved}

对未知 hole_id,resize_holes() 会抛出包含已检测 ID 列表的 ValueError,便于 Agent 自纠错。

5.3 网格修复流水线

repair.pyrepair_mesh() 分四步:

  1. 合并重复顶点:merge_duplicate_vertices() 先把坐标四舍五入到 6 位小数,再用 numpy 结构化视图 + np.unique 做快速去重,并同步重映射三角形索引;
  2. 移除退化面:任何两个顶点索引相同的三角形(合并后自然产生)被 _nondegenerate_face_mask() 筛掉,且同步过滤对应的 triangle_attributes;
  3. 移除无引用顶点并压缩索引;
  4. 另有独立的 fix_normals() 通过 trimesh.repair.fix_normals() 修正绕向、使法线一致朝外。

resize 命令自动复用该流水线,因为径向缩放可能让原本不相邻的顶点重合、产生退化面。

6. 切片器兼容性与无损重打包

这是该工具区别于普通网格编辑器的核心承诺:输出文件可以在原切片软件中重新打开且不丢配置。实现机制:

字节级保留非网格内容。 parse_3mf() 把所有非模型 ZIP 成员(缩略图、Bambu 的 project_settings.config/model_settings.config、PrusaSlicer 的 slic3r_pe 配置、层厚曲线等)存入 raw_entries;write_3mf() 重写 ZIP 时原样写回,只替换模型 XML。

XML 层保留非网格元素。 重打包时 _rebuild_model_xml()(parser.py#L298-L360)重新读取源文件 XML,仅把 id 匹配的 <object><mesh> 子树换成修改后的顶点/三角形数组,其余元素(metadata、build item、扩展命名空间、切片器嵌入的属性)原样保留;命名空间前缀通过 ET.register_namespace 注册(核心命名空间与 Bambu 的 b 前缀),避免序列化出 ns0 之类的陌生前缀。

三角形属性保留。 解析时 <triangle> 元素上除 v1/v2/v3 外的所有属性(3MF 材料属性 pidp1p2p3 及未知 vendor 属性)被存入 MeshData.triangle_attributes 并随行保存;写回时逐三角形恢复。这意味着某三角形在编辑中存活,它的材料属性就跟着存活;退化面被修复移除时属性同步过滤。

明确的边界(能力限制)。 纯 component 对象(只有 <components> 没有 <mesh>)和 component/build 的 transform 属性作为未触碰的 XML 保留,但当前几何操作不会把 component transform 解析进网格坐标,也无法直接编辑组件实例的 transform。此外,孔检测假设网格绕向一致(有效 3MF 文件满足此条件);--axis 参数意味着一次检测只覆盖一个轴方向的孔,其他方向的孔需要换 --axis 重新 inspect。

7. 测试体系

tests/test_core.py(约 1260 行)覆盖 parser、backend、inspector、repair、modifier 全部五个模块,全部基于合成 numpy 数据与内存生成的 3MF 夹具,不依赖外部文件。例如三角形属性保留测试用内嵌 XML 构造含 pid/p1/p2/p3vendor:tag 属性的 3MF 夹具,并包含 component-only 对象与 build transform 来验证无损保留;README 提到另有 30+ E2E 测试。

8. 适用前提与使用建议

  • 输入必须是符合 3MF Core Specification 的文件,几何单位以文件内 unit 属性为准(默认 millimeter),命令中的 mm 参数与之对应;
  • 典型工作流:先 inspect 确认 hole_id 与直径 → 用相同检测参数 resizecompare 验证前后差异 → 用切片软件重开确认配置未丢;
  • 从源码结构看,孔检测只识别"圆柱形"孔,锥形/异形孔不会以稳定置信度被识别;--planes 增加可提高对短孔/浅盲孔的检测稳定性,但会增加截面开销;
  • 修改后的文件如需再编辑,请以最近一次输出为输入并重新 inspect——hole_id 不跨文件持久。

本文内容基于仓库 3MF/ 目录下的技能文档、SOP 文档与 cli_anything/threemf 源码整理,引用的默认值、容差常量(0.06mm 壁面容差、0.5mm 聚类阈值、2% 截面内缩、5% 圆拟合误差上限)均可在对应源码文件中逐行核对。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384