# 设计:三维体/切片 数据详情(只读属性对话框) > 日期 2026-06-18。分支 `feat/vtk-3d-view`。收尾/打磨项 #6(见 `docs/superpowers/HANDOFF-vtk-3d.md` §4 末「下一步候选」)。 > 异常详情已用对话框做掉(`AnomalyPropertiesDialog`),本设计为**三维体 / 切片**补同类只读详情。 ## 1. 目标与范围 三维分析栏右键「数据详情」时,弹出只读属性对话框展示该三维体 / 切片的元数据与统计。 - **形态**:只读 `QDialog`(仿 `AnomalyPropertiesDialog`),非停靠面板页签。 - 取舍理由:现成 `DatasetDetailController/Panel` 绑定 2D 的 `IAsyncDatasetRepository` + chartRegistry,而体/切片数据在 `Api3dRepository`(独立 3D 仓储),硬接需跨仓储桥接 + 新策略/视图,代价大、动共享设施风险高。对话框与刚落地的异常详情 UX 一致、零侵入 2D 管线。 - **内容范围**:参数/位姿随时可取;三维体统计(值域/测点数/范围)体被生成(loadVolume 缓存)后才显示,未生成显「—(生成/渲染后可见)」。 ## 2. 架构与新增文件 仿 `src/app/AnomalyPropertiesDialog.{hpp,cpp}`,`QFormLayout` + `QLabel` 只读表: | 文件 | 职责 | |------|------| | `src/app/VolumePropertiesDialog.{hpp,cpp}` | 三维体属性(参数 + 统计) | | `src/app/SlicePropertiesDialog.{hpp,cpp}` | 切片属性(位姿 + 参数) | 两个对话框各自独立、构造即填充、`exec()` 模态,无网络、无加载态。 ## 3. 数据获取 只改具体类 `src/data/api/Api3dRepository.{hpp,cpp}`;**接口 `I3dSceneRepository` 与 `LocalSample3dRepository` 不动**(`main.cpp` 持有具体 `scene3dRepo`,见 main.cpp:266,全程直接用)。 ### 3.1 三维体 getter(新增) ```cpp // Api3dRepository.hpp 内嵌结构 + 方法 struct VolumeInfo { VolumeBuildParams params; std::string name; bool loaded = false; // cachedGrid 是否已就绪(= loadVolume 跑过) // 以下仅 loaded 时有效: double vmin = 0.0, vmax = 0.0; // 来自 cachedGrid int nx = 0, ny = 0, nz = 0; // 网格维度 double dx = 0, dy = 0, dz = 0; // 单元间距(来自 cachedGrid.spacing) std::size_t pointCount = 0; // 聚合后参与插值的散点数 }; bool volumeInfo(const std::string& dsId, VolumeInfo& out) const; // 非体返回 false ``` - `loaded` 取 `StoredVolume::cachedGrid.has_value()`;统计字段从 `cachedGrid`(vmin/vmax、`vol.nx()/ny()/nz()`、`spacing`)填。 - **测点数持久化**:`StoredVolume` 增 `std::optional pointCount`,在 `finalizeVolume`(散点聚合完成处)写入 `pts.v.size()`。`volumeInfo` 透出。 ### 3.2 切片数据 复用已有 `bool sliceSpec(const std::string& dsId, SliceSpec& out) const`(main.cpp 已在用)取位姿;名称用 `detailRequested` 信号已携带的 `name`,不新增 getter。 ## 4. 触发与接线(`main.cpp`) `detailRequested` 仅来自三维分析栏(`Column3DAnalysis`,项非体即切片;右键菜单「数据详情」已接,无需改 Column3DAnalysis),现连接 `detailCtrl.openDataset`(对 3D dsId 会降级失败)。改为按 ddCode 分派: ```cpp QObject::connect(ca, &Column3DAnalysis::detailRequested, &window, [&window, scene3dRepo](const QString& dsId, const QString& ddCode, const QString& name) { if (ddCode == QStringLiteral("dd_slice")) { I3dSceneRepository::SliceSpec sp; if (scene3dRepo->sliceSpec(dsId.toStdString(), sp)) { SlicePropertiesDialog dlg(name, sp, &window); dlg.exec(); } } else { // dd_voxel Api3dRepository::VolumeInfo info; if (scene3dRepo->volumeInfo(dsId.toStdString(), info)) { VolumePropertiesDialog dlg(name, info, &window); dlg.exec(); } } }); ``` `src/app/CMakeLists.txt` 加两个新 `.cpp`。 ## 5. 内容字段 ### 三维体(`VolumePropertiesDialog`) - 名称 - 源数据集(`sourceDatasetIds`,逗号连接) - 插值模型(IDW / Kriging)+ 幂指数(IDW 时显 `power`) - 网格间距(`XY=cellXY m Z=cellZ m`) - 超距(`maxDist m`) - 色阶来源(`colorScaleId`,空显「首个源数据集」) - **统计**(loaded 才有,否则全显「—(生成/渲染后可见)」): - 值域(`vmin ~ vmax`) - 网格(`nx × ny × nz`) - 测点数(`pointCount`) - 范围(`nx·dx × ny·dy × nz·dz` 米) ### 切片(`SlicePropertiesDialog`) - 名称 - 所属三维体(`volumeDsId`) - 轴向(0 上下 / 1 前后 / 2 左右 / 3 任意) - 平面三点 Origin / Point1 / Point2(各 `(x, y, z)` 米,2 位小数) - 色阶来源(`colorScaleId`,空显「首个源数据集」) > 切片**不含统计项**:采样分辨率/值域来自渲染时的切面网格,仓储层不持久化(`StoredSlice` 仅存 `spec`+`name`)。回写渲染产物属额外 plumbing,守 YAGNI 不做。位姿/参数已完整。 ## 6. 错误处理 - `volumeInfo` / `sliceSpec` 取不到(非体/非切片)→ 返回 false,不弹空对话框(理论不发生,触发来自该行)。 - 统计未就绪 → 占位「—(生成/渲染后可见)」,不报错。 ## 7. 测试 - 新增 gtest(`tests/` 内 Api3dRepository 测套,若无则新建)覆盖 `volumeInfo`: - `createVolume` 后、`loadVolume` 前:`volumeInfo` 返回 true、`params`/`name` 正确、`loaded=false`、`pointCount=0`。 - `loadVolume` 成功后:`loaded=true`、`vmin0`、`pointCount>0`。 - 非体 dsId:返回 false。 - 对话框为纯只读 UI(无逻辑分支),不做单测,靠 GUI 实测(Claude 无法 GUI 验证,交用户)。 ## 8. 影响面 / 不变量 - 接口 `I3dSceneRepository` 与 `LocalSample3dRepository` 零改动 → 真实后端就绪后切换不受影响。 - `finalizeVolume` 仅多写一个 `pointCount`,不改插值/渲染行为。 - 不与 VTK 三维视图交互(详情只读查阅,职责清晰)。