ColorYourModel 产品需求文档(PRD)
| 项目 | ColorYourModel — 3D 打印白模智能分区上色工具 |
|---|---|
| 版本 | v0.3(基线:v0.1.0 已发布试玩版) |
| 文档日期 | 2026-08-06 |
| 当前分支 | feat_theme_persist_paint_ux |
| 状态 | 需求基线已冻结,Fill 范围缺陷阻塞验收 |
文档来源:本 PRD 由代码现状(32 次提交、18 个 IPC 命令)、30 轮对抗式迭代记录、v0.1.0 发布说明反向提炼而成,用于校正已严重脱节的 README/CHANGELOG。凡与 README 冲突处,以本文档为准。
核对状态:2026-08-06 经独立 agent 逐条对照源码证伪。12 项功能断言全部找到实现证据,无虚构;持久化 8 键与
partialize(appStore.ts:425-434)逐字一致;经验依据类常量(#8a8a8a、non-indexed、54.74° 下界、MANUAL_SEGMENT_OFFSET、0.8/0.95 阈值、18 个 IPC、13 项后端单测)全部命中。仅 FR-PRF-01 措辞过严,已收紧。
一、背景与问题
3D 打印领域大量精美模型(手办、建筑、机械零件)以白模 STL 分发。STL 在数据结构上只是一堆彼此独立的三角形面片,不携带任何"零件"或"逻辑区域"语义,也不支持颜色。
用户希望给白模上色送进切片软件时,面临三个结构性痛点:
- 无法按逻辑区域选区 — 三角面片彼此独立,"盔甲""皮肤""底座"这类概念在文件里不存在。
- 手动上色不可行 — 只能逐面片涂色。以实测 150 万面模型计,人工逐面操作量级不具备可操作性。
- 格式断层 — STL 无颜色通道,必须转为 3MF 才能携带逐面颜色数据进入切片流程。
产品定位:在 STL 与切片软件之间补上"语义分区 + 交互上色 + 彩色导出"这一段,输出符合 3MF Materials & Colors Extension 规范的文件,直接对接 OrcaSlicer / BambuStudio / PrusaSlicer。
二、目标用户与场景
| 用户角色 | 核心诉求 | 典型场景 |
|---|---|---|
| 3D 打印爱好者 | 把下载的白模手办打成多色 | 导入 STL → 智能分区识别出头/身/底座 → 各区填色 → 导出 3MF |
| 多色打印机用户(AMS/AMX) | 色号必须匹配实际料盘 | 使用 AMS 标准色板选色,包含合法纯黑 |
| 模型设计者 | 为自己的模型预设配色方案 | 套索精细划分自定义区域 → 微调 → 交付带色 3MF |
非目标(明确排除):纹理贴图、UV 展开、艺术级绘画、网格编辑与修复、切片本身。
三、业务目标与成功指标
| 目标 | 指标 | 当前实测 |
|---|---|---|
| 大模型可用 | 150 万面模型可导入并交互 | 已达成(导入 ~4 秒;BVH 加速拾取) |
| 分区自动化 | 一次点击产出语义分区 | 已达成(SDF / 二面角双算法) |
| 上色效率 | 单个逻辑区域一次点击完成填色 | 未达成 — Fill 范围溢出缺陷阻塞 |
| 产物兼容 | 导出 3MF 被主流切片软件正确识别 | 未验证 — 尚无端到端真机测试 |
四、用户故事
- US-1 作为打印爱好者,我希望导入 STL 后系统自动识别出模型的语义部件,这样我不用手工划分区域。
- US-2 作为打印爱好者,我希望点击一个区域就能整体填色,这样我不必逐面涂抹。
- US-3 作为模型设计者,我希望在自动分区不理想时用套索手工圈出区域,这样我能获得精确控制。
- US-4 作为多色打印机用户,我希望从 AMS 标准色板取色(含纯黑),这样打印结果与预期一致。
- US-5 作为任何用户,我希望误操作可以撤销,这样我敢于尝试。
- US-6 作为任何用户,我希望关掉软件再打开时我的主题、语言、笔刷参数还在,这样我不用反复设置。
- US-7 作为任何用户,我希望导出的 3MF 能被 OrcaSlicer 直接打开并显示颜色。
五、功能需求(EARS 规范)
EARS 句式:Ubiquitous(恒常)/ Event-driven(事件驱动)/ State-driven(状态驱动)/ Unwanted(异常)/ Optional(可选)
5.1 模型导入
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-IMP-01 | Event-driven | 当用户选择一个 STL 文件时,系统应解析二进制与 ASCII 两种格式,完成顶点去重、面邻接图与 KD-Tree 空间索引构建。 |
| FR-IMP-02 | Ubiquitous | 系统应将所有面的初始颜色设为 #8a8a8a(RGBA [138,138,138,255])。依据:纯白面在浅色画布下对比度仅 1.06:1,不可辨识。 |
| FR-IMP-03 | Event-driven | 当模型加载进行中时,系统应通过 import-progress 事件持续上报进度与阶段。 |
| FR-IMP-04 | Ubiquitous | 系统应在加载时按面数初始化 segment_labels(长度 == 面数,全 0)。依据:曾因空向量导致索引越界 panic 并毒化 Mutex。 |
| FR-IMP-05 | Unwanted | 若 STL 解析失败,则系统应保持前一状态不变并向状态栏输出错误,不得使应用崩溃。 |
5.2 自动分区
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-SEG-01 | Event-driven | 当用户触发「智能分区」时,系统应执行 SDF(Shape Diameter Function)分割:法线一致化 → 内向锥形射线取中值厚度 → 对数归一化 → 一维 k-means 聚类 → 凹度合并。 |
| FR-SEG-02 | Event-driven | 当用户触发「自动分区」时,系统应执行二面角分割:二面角分裂 → 法线一致性合并 → 小分区吸收。 |
| FR-SEG-03 | Ubiquitous | 系统应在任一分区操作后,将分区元数据同时写入顶层 segments 与 meshData.segments。依据:仅写顶层会使 Fill 路由与分区悬停高亮全量失效。 |
| FR-SEG-04 | Optional | 在分区数需要控制的场景,系统应支持自动估计聚类数 k(直方图峰值,clamp 2–12)。 |
5.3 手动分区(套索)
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-LAS-01 | State-driven | 当处于套索模式且光标位于模型上时,系统应实时显示下一次点击将吸附到的顶点标记。 |
| FR-LAS-02 | Event-driven | 当用户点击时,系统应将点吸附到命中面 1-ring 邻域内、法线夹角 ≤ 60° 的最近顶点。 依据:立方体角点夹角 arccos(1/√3)=54.74°,阈值须 >54.74° 才不误杀。前后端阈值必须原子同步。 |
| FR-LAS-03 | Event-driven | 当用户点回起点附近(距离阈值判定,非精确顶点匹配)且已有 ≥3 点时,系统应闭合环并生成新分区。 依据:精确顶点匹配在稠密网格下几乎不可达,会导致闭合永远无法触发。 |
| FR-LAS-04 | Ubiquitous | 系统应用「顶点图 Dijkstra 加密环边 → 面图 BFS 跳过环边 → 取较小连通分量」实现区域切分。 |
| FR-LAS-05 | Ubiquitous | 手动分区标签应自 MANUAL_SEGMENT_OFFSET = 100_000 起始,与自动分区标签空间隔离。 |
| FR-LAS-06 | Event-driven | 当用户按 Esc 时清空进行中套索;按 Backspace 时删除上一个点;按 Ctrl+Z 时——有进行中选点则删点,否则撤销上一个已完成的手动分区(LIFO)。 |
| FR-LAS-07 | Ubiquitous | 系统应对手动区边界执行 1 遍形态学平滑(ERODE + DILATE),且任何情况下不得删除整个区域。 |
5.4 上色工具
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-PNT-01 | Event-driven | 当用户使用填充工具点击某面时,系统应对该面所属分区整体填色。 |
| FR-PNT-02 | Unwanted | 若目标分区面数占比超过阈值(整段 0.8 / 手动区 0.95),则系统应降级为半径填充,不得对全模型染色。 |
| FR-PNT-03 | Ubiquitous | 填充目标应优先取悬停高亮所指示的分区,与用户所见黄色高亮范围保持一致。 依据:所填面集与所示高亮面集不一致是当前 P0 缺陷。 |
| FR-PNT-04 | Event-driven | 当用户拖动笔刷时,系统应按 KD-Tree 半径命中并依 linear/smooth/step 衰减混合颜色。 |
| FR-PNT-05 | Ubiquitous | 智能笔应同时施加同分区约束与法线点积裁剪(dot ≥ 0.5),防止球查询穿透薄壁染到背面。 |
| FR-PNT-06 | Ubiquitous | 系统应提供喷枪、橡皮、吸管工具。 |
| FR-PNT-07 | Ubiquitous | 系统应串行化绘制请求(latest-wins,队列长度 ≤1)。 依据:并发 IPC 乱序返回会用旧结果覆盖新颜色。 |
| FR-PNT-08 | Ubiquitous | 系统应允许选择包含纯黑 [0,0,0] 在内的任意 AMS 色板颜色,不得对合法黑色做提亮钳制。 |
5.5 撤销 / 重做
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-UND-01 | Event-driven | 当用户按 Ctrl+Z / Ctrl+Y 时,系统应全局恢复/重做上一次颜色状态。 |
| FR-UND-02 | Ubiquitous | 颜色快照应经后端 restore_face_colors 回滚,保证前后端状态一致。 |
5.6 视图与交互
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-VIEW-01 | Ubiquitous | 系统应使用正交相机。 依据:透视投影下不同深度物体屏幕位移不同,平移必然产生视差错觉;该问题历经 5 轮(tilt 压低、target 修正、关闭阻尼)均无法根治,属投影方式的数学固有性质。 |
| FR-VIEW-02 | Ubiquitous | 缩放应通过 camera.zoom 实现,范围 [0.1, 500]。 |
| FR-VIEW-03 | State-driven | 在视图模式下,左键应为旋转。 |
| FR-VIEW-04 | State-driven | 在绘制/套索模式下,光标位于模型上时左键应执行工具操作;位于空白处时左键应为旋转。 |
| FR-VIEW-05 | Ubiquitous | 中键应为缩放,右键应为平移,Alt+左键应强制旋转。 |
| FR-VIEW-06 | Ubiquitous | 任何新增工具必须在自身 pointermove 的命中与未命中两个分支同时刷新 overModelRef。依据:漏刷会读到冻结旧值,使左键映射整体失效。 |
| FR-VIEW-07 | Ubiquitous | 面拾取应采用射线法(three-mesh-bvh 加速,firstHitOnly),不得使用 GPU 颜色编码回读。依据:GPU 回读在 rect ≠ size 时整体错位;射线 NDC 由 rect 归一化,与 DPR/分辨率无关。此为全工具共享入口。 |
| FR-VIEW-08 | Ubiquitous | 几何体应为非索引(toNonIndexed)。依据:逐顶点写色按 faceIdx*3+v 寻址,要求每面独占三顶点;索引几何共享顶点会导致"涂 A 显 B"。BVH 构建须用 indirect: true,否则会物理重排 index 复现该缺陷。 |
5.7 主题、国际化与偏好
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-PRF-01 | Ubiquitous | 系统应支持 dark / light 双主题,全部颜色经 CSS 变量下发,不得存在脱离 CSS 变量的裸色值(var(--x, #fallback) 形式的兜底值合规,如 App.tsx:54-55、BrushSettings.tsx:94-122)。 |
| FR-PRF-02 | Event-driven | 当应用启动时,系统应在首帧前应用已保存主题(内联脚本),不得出现 FOUC 闪白。 |
| FR-PRF-03 | Ubiquitous | 系统应持久化 8 项偏好:theme, language, shadingMode, brushRadius, brushStrength, brushFalloff, currentColor, snapEnabled。 |
| FR-PRF-04 | Unwanted | 若读出的偏好越界或损坏,则系统应逐项校验并回退默认值,不得因脏数据启动失败。 |
| FR-PRF-05 | Unwanted | 若 localStorage 不可用,则系统应回退内存存储并正常运行。 |
| FR-PRF-06 | Ubiquitous | 界面应全量支持中英双语。 |
5.8 导出
| ID | 类型 | 需求描述 |
|---|---|---|
| FR-EXP-01 | Event-driven | 当用户导出时,系统应生成符合 3MF Materials & Colors Extension 的 zip 包,携带逐面颜色。 |
| FR-EXP-02 | Ubiquitous | 导出产物应能被 OrcaSlicer / BambuStudio / PrusaSlicer 正确读取并显示颜色。 |
六、验收标准
6.1 功能验收(端到端,需真机 cargo tauri dev)
| # | 验收项 | 通过判据 |
|---|---|---|
| A1 | 大模型导入 | 150 万面 STL 导入 <6 秒,无卡死,默认灰面 |
| A2 | 智能分区 | 双立方体分出 2 段;块体+薄板凹接合处边界合理 |
| A3 | 套索闭合 | 吸附标记跟随流畅;点回起点必定闭合;不出现"点完消失" |
| A4 | Fill 一致性 | 所填面集 == 黄色高亮面集(HUD 显示 N filled / N highlighted,无 MISMATCH) |
| A5 | 笔刷精度 | 光标环所在位置即着色位置,无偏移 |
| A6 | 撤销 | 连续 10 次操作可逐级撤销并重做,颜色完全还原 |
| A7 | 偏好持久化 | 改主题/语言/笔刷后重启,设置全部保留 |
| A8 | 主题无闪白 | 深色主题下启动全程不出现白屏帧 |
| A9 | 3MF 导出 | 产物在 OrcaSlicer 中打开,颜色与软件内所见一致 |
6.2 工程验收(每次提交必跑,须贴输出)
| # | 命令 | 通过判据 |
|---|---|---|
| B1 | npx tsc --noEmit |
exit code 0 |
| B2 | cargo test --lib |
13 项全通过 |
| B3 | npx tauri build --bundles nsis |
产出 setup.exe 与便携 exe |
七、已知约束与风险
| 类别 | 内容 |
|---|---|
| 验证局限 | headless Chromium 无 Tauri runtime,凡依赖模型加载(走 IPC)的功能无法自动化验证,只能人工回归。纯 CSS/UI 层可 E2E。 |
| 调试局限 | Tauri release 包无 devtools(F12 失效),所有诊断探针必须打到 UI 层(DebugHud)。 |
| 测试覆盖 | 后端 13 项单测;前端零单测,仅靠类型检查 + 人工。 |
| 算法固有 | 分区边界呈几何锯齿(形态学平滑只能缓解);模型有洞时射线会 miss。 |
| 构建环境 | 打包须 unset NODE_OPTIONS;须 --bundles nsis 显式限定,否则会尝试需联网的 WiX。 |
| P0 阻塞 | Fill 范围溢出缺陷经 7 版修复仍未确认收敛,阻塞 A4 与整体验收。 |
八、待确认问题
- Fill 在"未分区模型"上的期望语义是什么 —— 半径填充、整体填充,还是禁用并提示?
MANUAL_REGION_MAX_SHARE = 0.95阈值缺乏出处,是否需要产品侧给出明确判据?- 3MF 导出的验收基线以哪个切片软件为准?是否需要覆盖多软件矩阵?
- 偏好项新增/变更时是否需要版本迁移机制(当前改默认值对已持久化用户无效)?
- v0.3 路线图中「分区合并/拆分」优先级是否高于当前 P0 缺陷修复?