# dsh-cad — CAD Plugin for DeepSeek Harness

![dsh-cad banner](docs/img/banner.svg)

[![homepage](https://img.shields.io/badge/homepage-dsh--cad-4D6BFE)](https://lau-mars.github.io/dsh-cad/)
[![npm](https://img.shields.io/npm/v/dsh-cad)](https://www.npmjs.com/package/dsh-cad)
[![dsh plugin](https://img.shields.io/badge/dsh-plugin-4D6BFE)](https://github.com/deepseek-ai/deepseek-harness)
[![Node](https://img.shields.io/badge/node-%3E%3D%2022-4D6BFE)](https://nodejs.org/)
[![OCCT](https://img.shields.io/badge/kernel-OCCT-4D6BFE)](https://github.com/donalffons/opencascade.js)
[![License: MIT](https://img.shields.io/badge/license-MIT-4D6BFE)](./LICENSE)

[English](./README.md) | 简体中文

[DeepSeek Harness (dsh)](https://github.com/deepseek-ai/deepseek-harness) 的 CAD 插件：
在 Web UI 中提供**内嵌 3D/2D CAD 查看器**与**原生参数化建模工具族**（OCCT 内核），
让 agent 能够"边建边看"地完成 CAD 工作。

## 预览

启动即见的 CAD 编辑器：示例 L 型支架由内置的 `demo-bracket.brep` 经 OCCT
解析渲染——面 + 边显示模式、悬停即测（拾取面 4,800 mm²）、角落 ViewCube
导航块、支架 / 法兰 / 轴三种示例件一键切换：

![dsh-cad CAD 编辑器](docs/img/bracket-preview.png)

## 功能总览

| 能力 | 说明 |
| --- | --- |
| ⚙️ 单内核架构 | **occt.ts 为主建模内核**（真 BSpline、抽壳/拔模原生、质心），opencascade.js 仅作为加载失败时的降级后端；v0.8 的「抽壳后不可编辑」接缝消失——抽壳/拔模后可继续圆角/倒角/布尔 |
| 🔍 CAD 查看 | STL / OBJ / STEP / IGES / BREP / DCPRT（3D），DXF / SVG（2D），对话内嵌交互卡片（轨道旋转 / 缩放 / 线框 / 平移） |
| 🧭 CAD 编辑器交互 | Onshape 风格 ViewCube（26 区域点击定向）、悬停/点选面与边实时测量（面积 mm² / 长度 mm）、面+边 / 面 / 线框三种渲染模式、支架 / 法兰 / 轴 BRep 示例件一键切换 |
| 🏗️ 参数化建模 | 基本体、轮廓拉伸、**放样**、**扫掠**、**旋转体**、布尔、全边圆角/倒角、**抽壳**、**拔模**、**阵列（线性/圆周）**、变换；轮廓支持**曲线段**（圆弧/圆为精确 BRep，B 样条为采样近似）—— OCCT 精确 BRep，非网格近似 |
| 🗂️ Codex 式文档页签 | 右侧显示区页签栏 + 「+」菜单：**零件**（Part Studio，默认）/ **装配体**（实例插入/移动/移除）/ **工程图**（真实隐藏线图纸），页签可关闭、常驻不丢状态 |
| 📁 多文档 file 空间 | 工作区命名的建模文档（`.dsh-cad/docs/`），每个会话绑定自己的活动文档——新会话从空文档开始，不再继承历史遗留零件；面板上的文件夹按钮列出全部文档（预览 / 删除），对话中用 `cad_doc_new` / `cad_doc_open` 切换建模目标 |
| 📐 工程图 | GB 第一角布局：主视图 / 俯视图 / 左视图 + 轴测图，**occt.ts** 内核（npm 依赖，OCCT 7.9）真实隐藏线消除（虚线）；图框、标题栏、总尺寸标注、标准比例系列；导出 SVG / DXF |
| 🔗 约束求解与运动 | 集成 **Ansatz** 几何约束求解器（**`ansatz-wasm` 一条命令即可**：`npm install ansatz-wasm`，零 Rust 工具链 / 零原生二进制 / 零依赖）：实体/约束建模（装配实例 ↔ rigid3 位姿自动映射）、求解回写、DOF/残差/冗余/建议全量诊断（面向 LLM 的中文报告）、参数扫描运动学（`cad_constraint` / `cad_solve` / `cad_motion`） |
| 📐 几何测量 | 精确体积（mm³）、包围盒、三角统计、DXF 图层 |
| 📤 按需导出 | STEP（参数化）/ STL（网格），仅在用户要求时写文件 |
| 🖥️ 常驻 CAD 显示区 | 会话页右侧常驻面板：Codex 式页签（零件 / 装配体 / 工程图），建模时实时跟踪最新模型 |
| ⚡ 直通渲染管道 | worker 网格 → 内存二进制 → three.js typed-array，零 base64 / 零中间文件 / 零每步落盘 |
| 💾 建模文档持久化 | 操作日志（JSON）+ 防抖磁盘镜像，进程重启后自动重放恢复 |
| 🖼️ 图片 → 轮廓 | PNG 草图/截图 → Otsu 二值化 → 轮廓追踪 → 可直接拉伸的多边形（`cad_image_profile`） |
| 🔌 FreeCAD 执行器 | 在外部 FreeCAD 控制台运行同一 op 族（STEP 输入/输出闭环）；需本地安装 |

## 安装

插件已发布到 npm，一行命令即可：

```sh
dsh plugin --profile web add dsh-cad
```

安装器会通过 `dsh.bundle` 清单自动应用包内自带的 `cordis.patch.yml`，无需手工改任何配置。

### 版本要求

- **Node.js** ≥ 22
- **dsh CLI**（`@deepseek-ai/dsh`）：本项目开发基于 **0.1.0-rc.7**
  （最低支持版本，已在 `package.json` 的 `engines` 字段声明）；
  建议 **≥ 0.1.1-rc.2**（2026-08-31 实测通过）

### 从源码安装（开发模式）

```sh
git clone https://github.com/LAU-MARS/dsh-cad.git
cd dsh-cad
npm install && npm run build && npm test   # 依赖含 occt.ts（工程图真实消隐内核，~20MB wasm）

npm install -g @deepseek-ai/dsh@^0.1.1-rc.2 pnpm   # 需要 Node ≥ 22
dsh web                                  # 首次启动初始化 profile 后 Ctrl-C

dsh plugin --profile web add /path/to/dsh-cad

dsh web
```

patch 内容以包根目录的 `cordis.patch.yml` 随包分发，安装器经同一条 `dsh.bundle` 清单自动应用。

设置 `DEEPSEEK_API_KEY` 后对话即可使用，例如：

- “打开 bracket.stl 看看” → `cad_view`
- “画一个 100×60×5 的板，中间打 ⌀20 孔，四角 R2 圆角，加 ⌀16 高 20 凸台，导出 plate.step”
  → `cad_create_prim` + `cad_boolean` + `cad_fillet` + `cad_export`，每步 3D 页签实时更新
- “堆一个雪人” → 球体 + 圆锥鼻子 + 圆柱帽子（`at`/`axis` 精确定位）
- “再放两个 b1，一个转到 90 度” → `cad_assembly_insert` + `cad_assembly_move`，装配体页签实时更新
- “给 b1 出一张 A3 工程图，导出 dxf” → `cad_drawing`（三视图 + 轴测 + 虚线隐藏线 + 尺寸标注）→ `cad_export` `.dxf`

## 模型工具族

| 工具 | 说明 |
| --- | --- |
| `cad_view` | 打开 CAD 文件，渲染交互式查看器卡片 |
| `cad_info` | 只读几何元信息（格式/数量/包围盒/单位/图层） |
| `cad_create_prim` | 基本体（mm，Z-up），`at` 定位、`axis` 定向（精确轴角旋转） |
| `cad_extrude_profile` | XY 平面闭合多边形沿 +Z 拉伸成实体 |
| `cad_loft` | 放样：多个闭合截面（各自平面内的 [x,y,z…] 环）蒙皮成体，截面形状/点数可不同，`ruled` 直纹 |
| `cad_revolve` | 旋转体：闭合轮廓绕轴回转（profile 坐标 = (半径, 高度)，支持曲线段——圆角轮缘精确），`angle` 角度可选 |
| `cad_chamfer` | 倒角：全锐边等距离斜切（mm） |
| `cad_pattern` | 阵列：线性（delta 间距）或圆周（主轴 + at 轴点 + 总角度），生成副本体，可再 fuse 合并 |
| `cad_shell` | 抽壳：壁厚向内生长、外表面保留；`open` 为开口面的外法向列表（空 = 封闭内腔）。经 occt.ts 内核执行，结果为**托管体** |
| `cad_draft` | 拔模：壁面绕中性面倾斜 `angle` 度（脱模斜度），`direction` 为拔模方向，默认自动选平行于该方向的壁面。经 occt.ts 内核执行，结果为**托管体** |
| `cad_sweep` | 扫掠：闭合 2D 轮廓沿 3D 路径（[x,y,z…]）扫出实体；轮廓自动置于路径起点垂面，无需手工定向 |
| `cad_boolean` | fuse / cut / common（经典打孔：plate cut cylinder） |
| `cad_fillet` | 全锐边等半径圆角 |
| `cad_transform` | 平移 / 欧拉旋转 / 镜像 |
| `cad_volume` | 精确 BRep 体积（mm³） |
| `cad_drawing` | 工程图：主/俯/左 + 轴测四视图，隐藏线（虚线）、图框、标题栏、总尺寸、标准比例，A4/A3 |
| `cad_assembly_insert` | 将零件以实例插入装配体（`at` 定位、`rotate` 定向） |
| `cad_assembly_move` | 设置实例绝对位置/姿态 |
| `cad_assembly_remove` | 从装配体移除实例（零件保留） |
| `cad_constraint` | 声明约束模型：实体（含装配实例绑定）+ 约束（distance/angle/mate/coaxial…），持久化于操作日志 |
| `cad_solve` | Ansatz 求解并回写装配位姿；返回 DOF 剩余/残差/冗余/建议全量诊断 |
| `cad_motion` | 运动学扫描：驱动一个约束值从 from 到 to 逐帧求解，应用末帧位姿并返回运动表 |
| `cad_export` | 导出 STEP / STL / DCPRT（原生可重放零件文档）到工作区路径；`target: "assembly"` 导出装配体 STEP，`target: "drawing"` 导出工程图 SVG / DXF |
| `cad_delete` | 删除 body |
| `cad_docs` | 列出工作区建模文档（id / 名称 / 体数 / 更新时间，标记当前活动文档） |
| `cad_doc_new` | 新建命名文档并设为会话建模目标（多零件项目从这里开始） |
| `cad_doc_open` | 打开已有文档（按 id 或名称）作为会话建模目标——body 精确重放 |
| `cad_doc_rename` | 重命名文档 |
| `cad_doc_delete` | 永久删除文档（需 `confirm: true`） |
| `cad_freecad` | 在外部 FreeCAD 执行器上运行 op 程序（可选 STEP 输入/导出） |
| `cad_fusion` | 在外部 Fusion 360 执行器上运行 op 程序（GUI 桥；可选导出） |
| `cad_image_profile` | PNG → 轮廓 → 可直接拉伸的多边形点集 |

每步建模后：**同一查看器卡片原地刷新**（稳定 viewId + 版本化 URL），
"3D" 页签实时跟踪最新模型。

## 连接器（规划中）

建模主要由 **occt.ts** 内核承担（opencascade.js 为降级后端）（浏览器内的 OCCT 内核，零安装）；
下表连接器指未来以**外部 CAD 引擎作为执行器**驱动同一工具族：

| 连接器 | 套件 | 平台 | 状态 |
| --- | --- | --- | --- |
| **内置内核** | 基于 OCCT + WebGL 的 CAD 建模内核，浏览器内运行——零安装 | 全平台（WebGL 渲染） | ✅ 内置 |
| FreeCAD | 开源参数化套件——可经其 Python API 作为本地执行器（控制台 + GUI 窗口双模式） | Windows / macOS / Linux | ✅ 可用（需本地安装） |
| Fusion 360 | Autodesk CAD/CAM——常驻 Add-In + spool 桥（无 headless，Fusion 窗口即查看器） | Windows / macOS | 🧪 实验性（`cad_fusion`） |
| SolidWorks | 达索系统的主流 3D CAD，COM/.NET 自动化 | 仅 Windows | 🚧 Windows demo 脚手架（`scripts/solidworks-bridge/`） |
| Onshape | 云原生 SaaS CAD，完全在浏览器中 | 全平台（浏览器） | 🚧 规划中 |
| 中望3D（ZW3D） | 中望软件的一体化 CAD/CAM | Windows / Linux | 🚧 规划中 |
| 浩辰3D | 浩辰软件的 3D CAD | Windows | 🚧 规划中 |

所有外部引擎实现同一 **GeometryExecutor 契约**（`available()` / `run(op程序) → 网格`），
WebGL 显示层因此永不改变——更换后端只影响生成几何的质量。

## 架构

```
cad_view(path)                        建模工具（cad_create_prim 等）
  → 导入 worker（occt-import-js）       → 建模 worker（opencascade.js WASM）
  → CadScene JSON（base64-f32）         → BRep 精确几何 + 网格化
  → GET /dsh-cad/scene/<id>            → 内存二进制场景（f32/u32 打包）
                                        → GET /dsh-cad/bin/<docId>
            ↓ 会话 presentationMeta（viewId + 版本化 URL）↓
        浏览器卡片 + 常驻 "3D" 页签（three.js / SVG，Z-up，XYZ 轴）
```

- **两个 worker**：导入（occt-import-js，只读 STEP/IGES/BREP）与建模（opencascade.js 1.1.1，
  完整 OCCT）分离，均惰性启动；embind 重载构造器的 `_N` 后缀约定封装在
  `src/modeling/occt-adapter.cjs`（全部经运行时实证）
- **直通管道**：建模场景零 base64 / 零 JSON 大数组 / 零每步落盘（磁盘镜像 1.5s 防抖，
  仅服务重启回放）；`cad_export` 是唯一的显式文件导出
- **建模文档**：`<workspace>/.dsh-cad/model.json` 操作日志，重启后重放恢复全部 body
- **工程图消隐内核**：隐藏线由 **occt.ts**（npm 依赖，`npm i occt.ts`）执行
  真实 OCCT 消隐，无替代引擎、缺失即报错。相对 opencascade.js 的新增 API：
  `hiddenLines()` 真实消隐线、STEP/BRep 字节级 `readStep`/`readBrep`/`writeStep`/
  `writeBrep`（无需 MEMFS）、自带特征边提取的网格化、`hasError()`/`lastError()`
  错误契约。几何以 STEP 字节跨内核交换，投影线段重映射进图纸坐标系。内核 dist
  解析顺序：`DSH_OCCTJS_DIST` 环境变量 → `node_modules/occt.ts/dist`（npm，
  默认）→ `<repo>/../opencascade-ts/dist`（兄弟检出）→ `vendor/` →
  `node_modules/opencascade-ts`
- **约束求解（Ansatz）**：求解器是 npm 依赖 **`ansatz-wasm`**（wasm-bindgen
  构建，单包 ~538KB、零依赖）——安装 dsh-cad 即自动获得，也可单独
  `npm install ansatz-wasm` 升级；**无需 Rust 工具链、无需原生二进制、跨平台**。
  解析顺序：npm 包（node_modules）→ `DSH_ANSATZ_WASM` 目录 → 兄弟 Ansatz 检出的
  pkg-node。求解器以 JSON 信封契约通信，工具层错误（`unsupported_constraint` 等）
  原样透出给 LLM。装配实例位姿（平移 + XYZ 欧拉度）与求解器 rigid3（平移 + 指数映射
  旋转，弧度）双向映射。求解器能力按阶段推进（当前支持点到原点距离），管道契约
  已就绪、无需随求解器成长改动
- **单内核架构（v0.9）**：建模主内核切换为 **occt.ts ≥0.5.0**——prims/曲线段轮廓
  （真 BSpline 插值）/放样/扫掠/旋转/布尔/圆角/倒角/抽壳/拔模/镜像（scale(-1)+
  旋转 π 组合）/质心全在一个会话内，工程图 HLR 直接吃 shape（零 STEP 中转），
  抽壳后继续编辑无缝（v0.8 的托管接缝删除）。opencascade.js 保留为**降级后端**
  （occt.ts 加载失败时自动启用）。装配体 STEP 导出因无 compound 绑定降级为
  fuse 单体（实例分离保留在装配场景/文档中）。面选择按 `describe()` 平面法向
  匹配（±法向取外侧投影最大者）
- **客户端**：esbuild 单文件 CJS 工厂（three.js 内联 ~560KB，react 由宿主模块表提供），
  Z-up CAD 惯例，带 XYZ 轴标签与地面网格的空场景常驻显示

## 测试

```sh
npm test                             # 41 项：转换器 / 建模 worker（体积精确断言）/ DCPRT 往返 / FreeCAD 执行器 / 图片轮廓 / 二进制管道 / 文档持久化
node test/m0-kernel-check.cjs        # OCCT 内核 API 冒烟
node test/route-check.mjs            # JSON 场景路由层
node test/visual/serve.mjs           # 浏览器卡片/页签视觉验证页（http://127.0.0.1:3987）
```

覆盖的代表性断言：布尔打孔体积精确等于解析值（28429.20 mm³）、L 型轮廓拉伸
3000 mm³、球/锥/环带 `at`/`axis` 定位的体积与包围盒翻转、二进制打包 8 字节对齐、
STL 导出往返（导出 → 一期解析器读回），以及 DCPRT 文档往返
（序列化 → OCCT worker 重放 → 精确包围盒）。

## 已知限制

- DWG（闭源）不支持；DXF bulge 弧以弦线近似；glTF/3MF 查看未实现（结构已预留）
- `cad_fillet` 为全边等半径（embind 下按边选择不稳定）；chamfer 未实现
- 草图拉伸仅支持多边形轮廓（圆弧轮廓用布尔组合圆柱/圆环构造）
- dsh 框架限制：已挂载的 single 槽（右侧 details 面板本体）不响应后注册组件，
  故常驻显示区以 "3D" 视图页签提供（list 槽，官方组合方式）
- 宿主读取 CAD 文件使用 node:fs（平台 fs 服务仅支持 UTF-8 文本，无法承载二进制）

## 贡献者

由提交历史自动生成，感谢每一位贡献者！

[![Contributors](https://contrib.rocks/image?repo=LAU-MARS/dsh-cad)](https://github.com/LAU-MARS/dsh-cad/graphs/contributors)

## License

MIT
