geopro/docs/superpowers/plans/2026-06-18-vtk-3d-anomaly.md

94 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 实现计划VTK 三维异常(#4全量含异常体/列表/过滤)
- 日期2026-06-18
- 分支:`feat/vtk-3d-view`
- 上位设计:`docs/superpowers/specs/2026-06-17-vtk-3d-volume-slice-anomaly-design.md`;补充需求 R49-56(切片右键创建异常) + R58-65(三维体详情·异常) + R69-88(异常/异常体列表/属性/过滤)。
- 关键决策(用户 2026-06-18 定):
- **异常挂「三维体」**`remarkSourceId`=三维体 ds id不挂切片(切片是临时圈定载体)、不挂源 ds。见记忆 `vtk-3d-persistence-structure`
- **全做**:圈定 + 保存(含截图) + 3D 渲染 + 异常/异常体列表(对象→异常体→异常) + 选中联动 + 显示过滤 + 删除/删除分组。
- **不参考 Geopro1.0**:按需求 + 行业最佳实践(标准多边形圈定)。
- **持久化 mock**:三维体/切片/异常端点后端均未就绪 → 全 mock(内存)走 `I3dSceneRepository`,整链端点就绪再切真实。截图先存本地(R88 截图属性待后端新增)。
## 0. 现状(可复用 vs 新建,实证见探查)
| 资产 | 现状 |
|---|---|
| `core::Anomaly`(name/typeName/markType点线面/localPts Vec2/线样式) | ✅ 有,但**2D**(localPts=x距离·y深度),需补 3D 几何 |
| `I3dSceneRepository` 异常接口(loadAnomalyTree/saveAnomaly/deleteAnomaly/deleteAnomalyGroup + AnomalyTree/AnomalyBody) | ✅ 接口齐,**实现是 stub**(Api 回 onErr/空树) |
| `ObjectExceptionPanel`(对象→异常体→异常 树) | ✅ 只读树完整,**无勾选/选中/删除/过滤交互** |
| `render::buildAnomalies`(点/线/面 vtkActor) | ✅ 有,但坐标=2D(x,depth,0),需 3D(世界点) |
| 异常 DTO(parseExceptions/groupByConsortium) + 真实读取(loadExceptionsByTmAsync) | ✅ 真实读取链路通(后端就绪后用) |
| `I3dSceneView` 异常方法 / VtkSceneController 异常逻辑 / 3D 圈定工具 / 选中联动(3D) / 过滤 | ❌ 全无,需新建 |
## 1. 数据模型core::Anomaly 补 3D 几何
`src/core/model/Anomaly.hpp` 增(保留现有 2D 字段,新增 3D
- `struct Vec3 { double x,y,z; };`
- `std::vector<Vec3> worldPts;`:异常多边形/折线/点的**世界 3D 坐标**(落在所在切片平面上)。
- `Vec3 planeNormal{0,0,1}, planeOrigin{};`:所在切片平面(法向+一点)——供重定位/正视,及与切片解耦后仍能定位。
- 持久化补充字段(不入 core入仓储存储或 Anomaly 扩展)`id`、`volumeDsId`(=remarkSourceId)、`exceptionTypeId`/`typeName`、`remark`、`screenshotPath`、`consortiumId`(异常体分组,空=未分组)。
> core::Anomaly 保持渲染/几何纯数据id/归属/截图等持久化元数据放仓储的 StoredAnomaly 包装(同 StoredVolume/StoredSlice)。
## 2. 渲染3D 异常 actor + I3dSceneView 接口
- `render::buildAnomalies3D(const std::vector<core::Anomaly>&)`(新增或改造 AnomalyActor`worldPts` 直接建点/折线/闭合多边形 actor世界坐标不再 ×1 深度);样式复用(lineColor/width/dashed);选中高亮(加粗/变色)。
- `I3dSceneView` 新增:
- `addAnomaly(const core::Anomaly&)` / `removeAnomaly(id)` / `clearAnomalies()`
- `setAnomalyVisible(id, bool)` / `setAnomalySelected(id, bool)`(选中联动)
- `pickedAnomalyId()` 或经回调 `onAnomalyPicked(id)`VTK 点选异常→列表)
- `VtkSceneView``map<id, actor>`,实现上述。
## 3. 圈定工具(切片平面上画多边形)
`src/render/interact/AnomalyDrawTool.{hpp,cpp}`(新):
- 输入:当前选中切片的平面(origin/normal) + interactor + renderer。
- 交互(行业标准):左键逐点加顶点(投影到切片平面);右键/双击/回车闭合Esc 取消;实时预览折线。点类型=单击一点;面=多边形闭合;(线/文字按 markType)。
- 产物:`worldPts`(平面上的世界点) + planeNormal/origin → 回调上层。
- 入口VTK 视图切片右键「创建异常」(已占位) → 启动本工具(以光标拾取点为起点R49)。
## 4. 保存对话框 + 截图
`src/app/AnomalySaveDialog.{hpp,cpp}`(新,参考 VolumeParamsDialog 风格):
- 字段:异常名称、异常类型(下拉,**mock 几个类型**;真实类型端点 `exceptionType/*` 只读、后续可接)、备注。
- 截图(R50):圈定结束截当前 VTK 视图(或异常包络区) → 存本地文件 → 路径+大小入异常记录(`SliceExport` 同款 PNG 写)。
- accept → 组装 `core::Anomaly`(markType/worldPts/plane/样式) + 元数据(name/typeId/remark/screenshot) → `saveAnomaly`
## 5. 持久化 mockApi3dRepository挂三维体
- `StoredAnomaly { core::Anomaly geom; id; volumeDsId; exceptionTypeId/typeName; remark; screenshotPath; consortiumId; }``map<id, StoredAnomaly> anomalies_`。
- `saveAnomaly(a, screenshotPath, onOk(id), onErr)`:生成 `anomaly-N`,存,回 id。接口已含 screenshotPath 参数)
- `loadAnomalyTree(objectId, onOk(tree), onErr)`:按 objectId 下所有三维体聚合异常 → 组 `AnomalyTree`(bodies=异常体分组 + loose=未分组)。mock 阶段:以 volumeDsId 关联,未分组进 loose。
- `deleteAnomaly(id)` / `deleteAnomalyGroup(bodyId)`:删/删组。
- 异常体(consortium)分组mock 内存(`map<bodyId, {name,typeName,memberIds}>`);真实端点 `exceptionConsortium/*` 后续接。
- 接口签名不变;后端整链就绪仅换实现。
## 6. 列表面板R69-88+ 选中联动 + 过滤
扩展 `ObjectExceptionPanel`(或在三维分析视图侧新建异常面板,复用其树构建):
- 树:对象 → 异常体 → 异常 + 未分组异常(R71-77)。
- 勾选(显隐)、单选(选中) → 信号;选中 ↔ VTK 视图异常高亮**双向联动**(R84)。
- 操作(R79):删除异常、删除分组(deleteAnomaly/deleteAnomalyGroup)。
- 异常属性(R83):选中异常 → 详情(名称/类型/坐标/截图/备注)。
- 显示过滤(R86-87):全部显示 / 随GS / 随数据集 / 全部隐藏 → 控制 VTK 异常可见性集合。
- 异常属性·截图(R88):展示截图缩略 + "确定截图大小"。
## 7. main.cpp 编排
- 切片右键「创建异常」→ 启动 `AnomalyDrawTool`(用当前选中切片平面) → 圈定完成 → `AnomalySaveDialog``scene3dRepo->saveAnomaly` → 渲染(view addAnomaly) + 刷新异常面板。
- 当前对象/三维体变化 → `loadAnomalyTree` → 填异常面板 + 渲染已存异常。
- 面板选中/勾选/过滤/删除 → 驱动 view 的 setAnomalyVisible/Selected + 仓储删VTK 点选异常 → 面板选中(联动)。
## 8. 阶段(每阶段编译绿 + 用户实测)
- **4a 基础**§1 模型 + §2 渲染/接口 + §5 mock 持久化(saveAnomaly/loadAnomalyTree/delete) + main 加载已存异常渲染。可注入一两个测试异常验证 3D 渲染。无圈定/对话框。
- **4b 圈定+保存**§3 圈定工具 + §4 保存对话框(含截图) + 切片右键「创建异常」接通 → 闭环:画→存→显示→删。
- **4c 列表/异常体/联动/过滤**§6 面板交互(选中联动/过滤/删除分组) + 异常体分组 + 异常属性/截图展示。
## 9. 风险/待确认
- **core::Anomaly 改动影响 2D 路径**:补字段不动现有 2D 字段2D 渲染(ContourPlotItem/buildAnomalies)不受影响3D 走新 worldPts 路径。
- **异常体(consortium)创建入口**:需求 R71 有异常体,但"如何把异常归入异常体"的 UI 入口需求未细化 → 4c 落地时按最佳实践补(多选异常→成组),或先只做 loose + 展示分组。
- **截图属性后端缺**(R88 待新增):先本地存,后端加字段再上传。
- **真实类型/异常体端点只读可接**mock 阶段先 mock降耦合可选接真实只读。