# VTK 三维:三维体 / 切片 / 异常 —— 数据模型与客户端交互设计 - 日期:2026-06-17 - 范围:桌面客户端 VTK 三维视图里的「三维体模型(体素) / 切片 / 异常」三类数据及其交互。是补充需求页"三维分析"栏的落地设计。 - 依据:①《Geopro3.0 需求表.xlsx》「补充需求」页(行号见引用);② 与产品方就 6 个设计问题的确认;③ 现有代码。 - 原则:缺后端端点的**先本地 mock**(保证功能可见可用),端点就绪后切真实;能纯客户端做的先做。 > **⚠ 更正(2026-06-18,本文档以下异常部分已被修订,以此为准)**——实现计划见 `plans/2026-06-18-vtk-3d-anomaly.md`,结构铁律见记忆 `vtk-3d-persistence-structure`: > 1. **异常挂「三维体」**(`remarkSourceId` = 三维体 ds id),**不挂切片**(§1.3 的 `parentSliceId` 作废)——切片是临时圈定载体,业务语义上异常属于三维体(需求 R61)。 > 2. **`remarkSourceType` = 标注形态**(1点/2线/3面/4文字),**不是**"来源实体类型"(§3 原表述更正,实证 `commercial-admin/contourPage.vue:386`)。接口不限定挂载实体类型,`remarkSourceId` 放谁 id 挂谁。 > 3. 异常请求体**无截图字段**;补充需求 **R88「增加截图属性」**证实截图是待新增属性 → 现 mock 本地存。 > 4. **摆放**:3D 异常的"场景控制"(树+显示过滤 R86-87+VTK 选中联动 R84+显隐+删除)放**三维分析区**;右侧「对象异常」面板 = 异常全集 master(暂连后端 2D,整链就绪后并入 3D)。 > 5. 异常**独立显隐**靠 R86-87 过滤(全部显示/随GS/随数据集/全部隐藏),**不被三维体勾选绑死**。 --- ## 1. 核心数据模型 三者都是**可持久化的数据集**,层级关系:`三维体 → 切片 → 异常`。 ### 1.1 三维体(VolumeModel) ddCode:`dd_voxel` / `dd_Structual3D` / `dd_Property3D`(需求行 245/290/291)。 - `id` / `name` - **`sourceDatasetIds[]`(源数据)**:一组输入数据集,用于插值生成本体(需求行 401)。 - **`interpModel`**:插值模型,克里金 / IDW(需求行 404)。 - **`interpParams`**:插值参数(cellXY/cellZ/power/maxDist 等,需求行 405)。 - `colorScale`(色阶参数,行 406)。 - `grid`:体素网格 = `ScalarVolume` + `origin/spacing/vmin/vmax`(= 现有 `VolumeGrid`)。 - `measure`:测量数据,点数/体积(行 407,派生量)。 - 派生:`slices[]`、`anomalies[]`(汇总该体下所有切片圈定的异常,行 403)。 **来源(行业经验)**:① 多条 2D 反演剖面/散点/钻孔点 → IDW/克里金插成 3D 体(最常见);② 直接 3D 采集(三维高密度等,本身即 3D,单数据集)。`sourceDatasetIds` 为 1 个或多个。 ### 1.2 切片(Slice) ddCode:`dd_slice`。 - `id` / `name` - **`parentVolumeId`**:所属三维体(行 389:切片对象=所属三维体模型)。 - **`plane`**:切面 = `SliceSpec`(轴向/原点/法向/偏移)。 - `colorScale`。 - 生命周期:交互式"切片工具"是**临时**的(在体上实时切);**保存后才成为持久化的 `dd_slice` 数据集**,进入三维分析栏列表(行 390)。 ### 1.3 异常(Anomaly) - `id` / `name` - **`parentSliceId`**:异常画在某切片平面上、附着于该切片(行 392 创建异常在切片右键)。 - `polygon`:圈定多边形(切片平面内的坐标)。 - `screenshot` + 元信息(截图大小、异常坐标,行 393)。 - 向上归属:三维体详情的"异常"= 汇总该体下所有切片圈定的异常(行 403)。 --- ## 2. 客户端交互流(需求"三维分析"栏 + VTK 视图右键) ### 2.1 三维分析栏(列表) - 按「**对象 / 三维体模型 / 切片**」三级树显示(行 366)。 - 勾选一个或多个三维体或切片 → 显示在三维视图(行 367)。 - **三维体右键**(行 368–375):切片▸(上下/前后/左右/任意)、色阶、显示/隐藏、数据详情。 - **切片右键**(行 376–383):保存、保存为、导出、删除、色阶、显示/隐藏、数据详情。 ### 2.2 切片工具(行 369–372, 387–389) - 上下/前后/左右切片:在三维体中心或光标位置打开**水平切片工具,角度不可调**。 - 任意切片:初始角度 = 当前视图 45°,可任意调整。 - 选中切片滚轮 → 沿法向往内/外推进;切片对象 = 所属三维体(行 389)。 - 双击切片 → 视角调正为正视该切片(行 386/388)。 ### 2.3 VTK 视图里对切片的右键(行 391–399) - **创建异常**:弹异常工具,以光标拾取点为起点圈定;结束保存时弹对话框(截图大小、异常坐标),参考 Geopro1.0。 - **保存**:保存为数据集(切片数据集)。 - **导出为图片** / **导出到 dat**。 - **正视图** / **视图翻转(水平 180°)** / **关闭**。 ### 2.4 创建三维体(**已定:客户端创建**) 产品确认创建三维体在**客户端**侧。 - 「三维数据集」栏多选若干 3D 数据集(反演剖面)→ 菜单「生成三维体」→ 选插值模型/参数 → 客户端 `IdwInterpolator` 插值 → 三维体。 - 持久化:后端**无三维体端点**(见 §3)→ 先本地 mock(内存/本地文件),端点就绪后切真实。 --- ## 3. 后端依赖 vs 本地 mock(已核对 web 源码 `commercial-admin/src/apis`) | 能力 | 现有端点 | 方案 | |---|---|---| | 展示已有三维体(取网格) | ❌ 无(`/model/*` 只有 ResIPy 反演/雷达,无三维体网格) | **mock**:以源数据集散点 IDW 出体(`IdwInterpolator`) | | 创建三维体(插值) | ❌ 无(无三维空间插值端点) | **客户端做**(IdwInterpolator);持久化 mock | | 三维体/切片**持久化** | ❌ 无写端点 | **mock**:内存/本地存;端点就绪后切真实 | | 切片创建/交互 | — 纯客户端 | ✅ 已有(`SliceTool`/`InteractionManager`) | | 切片保存/另存/导出/删除 | ❌ 无切片端点 | 保存/删除 → mock(内存);导出图片/dat → **客户端做**(截图/写文件) | | 异常**读取** | ✅ `POST /exception/queryExceptionTree`、`queryException`/`queryExceptionByTmObjectId` | **接真实** | | 异常**新增** | ✅ `POST /business/exception` | **接真实** | | 异常**更新** | ✅ `PUT /business/exception` | **接真实** | | 异常**删除** | ✅ `DELETE /business/exception/{exceptionId}` | **接真实** | | 异常类型 | ✅ `GET /exceptionType/queryExceptionTypeByProjectIdAndType/{projectId}/{remarkSourceType}`、`/exceptionType/getDetail/{id}` | **接真实** | | 异常体(exceptionConsortium) | ✅ `POST/PUT /exceptionConsortium`、`/page`、`/export`、`/getDetail/{id}` | **接真实** | | 任务记录/可用任务 | ❌(`/model/task/page` 是反演任务,非三维分析任务) | mock | ### 异常新增请求体(web 实证 `datasetInfo/index.js:212`) `POST /business/exception`: ``` { exceptionName, exceptionTypeId, location:{...圈定坐标...}, projectId, remarkSourceId, remarkSourceType, remark } ``` - `remarkSourceId` / `remarkSourceType`:异常所附着的对象(本场景=切片数据集 id + 其类型)。 - `location`:圈定多边形坐标。截图:保存对话框含截图(行 393),上传方式参考 `exceptionInfos/modalNewException.vue`。 - 更新:`PUT /business/exception` `{ id, exceptionName, remark }`;删除:`DELETE /business/exception/{exceptionId}`;详情:`GET /business/exception/getDetail/{exceptionId}`。 --- ## 4. 代码现状(落点) - 维度归类:`LocalSample3dRepository::dimensionOf`(dd_voxel/Structual3D/Property3D/section/inversion → 3D;dd_slice → Analysis)。 - 体素:`core::IdwInterpolator`(IDW 现成)、`render::buildVoxel`/`VoxelActor`(体绘制)、`VoxelFromScatters`(散点→体)。 - 切片交互:`render::interact::SliceTool` + `InteractionManager`(已可在体素 image 上切,含上下/前后/左右/任意)。 - 仓储接口:`I3dSceneRepository` 已定义 `loadVolume / createSlice / saveSlice / deleteSlice / loadAnomalyTree / saveAnomaly / deleteAnomaly / deleteAnomalyGroup / loadTaskRecords / loadUsableTasks`。 - `LocalSample3dRepository`:以上多为**内存 mock**(slices_/anomalies_ map)。 - `Api3dRepository`:以上多为 **stub(`kNotReady`)** — 真实数据未接。 - UI:`Column3DAnalysis` 已定义信号(sliceRequested/colorScaleRequested/visibilityToggled/detailRequested/sliceSave|SaveAs|Export|Delete);`main.cpp` **仅接了 sliceRequested + detailRequested**,其余未连。 --- ## 5. 待产品确认(更新 2026-06-17) 1. **创建三维体在客户端还是平台?** → **已定:客户端**(§2.4)。 2. **后端三维体/切片/任务端点时间表?** → 暂未知。当前 web 源码**无**三维体网格、三维插值、切片、三维分析任务端点 → 这些**先 mock**(保持 `I3dSceneRepository` 接口不变,端点就绪后仅换实现)。 3. **异常写端点?** → **已找到**(§3):`POST/PUT/DELETE /business/exception` + 类型/异常体一整套 → 异常**接真实**,不 mock。 剩余待确认(次要): - 异常保存对话框的**截图上传**方式/字段(参考 `exceptionInfos/modalNewException.vue`,落地时细化)。 - 三维体/切片本地 mock 的持久化形态(纯内存 vs 本地文件,影响重启后是否还在)。 --- ## 6. 实现拆解(对应"剩余工作" #2–#6,缺端点先 mock) 按依赖与价值排序: 1. **三维体(mock 渲染)**:把当前勾选/源数据反演剖面散点 → `IdwInterpolator` → `VolumeGrid` → `VoxelActor`。`Api3dRepository::loadVolume` 由 stub 改为"取源数据散点 + IDW"(mock)。→ 解锁三维体显示 + 切片有可切对象。 2. **切片交互接通三维体**:体素就位后,三维体右键"切片▸"已能创建切片(现有 SliceTool)。补:选中切片滚轮推进、双击正视。 3. **切片保存/另存/导出/删除**:保存/删除 → Api3dRepository 改内存 mock(同 LocalSample);导出图片(截图)/dat(写文件) → 客户端实现。VTK 视图切片右键菜单接线。 4. **异常**:切片右键"创建异常"→ 圈定工具 + 保存对话框(截图/坐标)。**全套接真实端点**:新增 `POST /business/exception`、更新 `PUT /business/exception`、删除 `DELETE /business/exception/{id}`、读 `queryException*`/`queryExceptionTree`、类型 `exceptionType/*`、异常体 `exceptionConsortium/*`。`remarkSourceId/Type` 填切片数据集。 5. **分析栏右键菜单接线**:色阶、显示/隐藏(纯客户端)、切片保存/另存/导出/删除(接 #3)。 6. **三维体/切片/异常详情**:数据详情栏展示源数据/插值参数/色阶/测量(点数·体积)/异常列表。 > 每步:客户端能做的先做、缺端点的内存 mock 留出可替换缝(保持 `I3dSceneRepository` 接口不变,仅换实现)。 --- ## 7. 三维体持久化策略与存储结构(2026-06-17 定) ### 7.1 策略:参数为准 + 明细可选缓存 + 缺则惰性重算 - **保存**: - **必存**:插值参数(源数据引用 + 插值模型/参数 + 色阶)+ **网格规格 `GridSpec`**(origin/spacing/dims)。 - **可选存**:网格明细值(体素标量,nx·ny·nz)。 - **加载**: - **有明细** → 直接渲染明细。 - **无明细** → 按参数把值**重算填入已存的 `GridSpec`**(坐标系固定),再渲染。 - **理由**:参数小且可复现(详情面板要展示);明细贵、但稳定一致;`GridSpec` 必存以**锚定切片/异常坐标**(它们定义在体网格坐标系上,重算必须落在同一规格里,否则错位)。 ### 7.2 何时建议存明细 | 体的情形 | 建议 | |---|---| | 带切片/异常 | **存明细**(与源数据生命周期解耦,重算变形风险归零) | | 网格大 / 插值慢 | **存明细**(避免每次加载实时重算的卡顿) | | 纯展示、源数据稳定、网格小 | 可只存参数(省存储),加载时重算 | ### 7.3 重算路径的两个前提(UI/逻辑需兜住) 1. **源数据仍在且可取**:源数据集被删/改 → 重算失败或结果变 → 应回退提示 / 阻止。 2. **算法确定性**:同参数同输入 → 同结果(IDW 确定);算法升级时旧体优先用已存明细,避免变形。 ### 7.4 存储结构(字段定义,驱动实现) **三维体元数据(必存,进数据集元数据)`VolumeBuildParams`**: - `sourceDatasetIds[]`:源数据集 id 列表。 - `interpModel`:枚举 IDW / Kriging。 - `interpParams`:`{ cellXY, cellZ, power, maxDist, ... }`(按模型)。 - `colorScaleId` 或内联色阶。 - `gridSpec`:`{ ox, oy, oz, dx, dy, dz, nx, ny, nz }`(**必存**,锚定坐标)。 - `vmin, vmax`、`measure{ points, volume }`(派生,可缓存)。 **三维体明细(可选,进数据集数据文件)**: - `values`:`ScalarVolume`(nx·ny·nz 标量,maxDist 外为 NaN=空)。 **与现有代码的关系**: - 现 `data::VolumeGrid = { ScalarVolume vol; origin[3]; spacing[3]; vmin; vmax }` ≈ "明细 + 规格(部分)"。 - 落地改造:拆出 `VolumeBuildParams`(含 `GridSpec`)为必存元数据;`vol`(values)为可选。`I3dSceneRepository::loadVolume` 返回时:若有 values 直接给 `VolumeGrid`;若无,则用 `VolumeBuildParams` 现场 `IdwInterpolator` 重算出 `vol` 再给(坐标用存好的 `gridSpec`)。 - mock 阶段:客户端 IDW 同时产出 params(含 spec) 与 values,两者本地存;接口签名不变,后端就绪后元数据走数据集属性、values 走数据文件。