docs(vtk): spec 加类型抽象层(描述符+渲染策略注册表+扩展契约)

§5 重写为 CategoryDescriptor/categoryCatalog + FilterKind/OpKind/SceneKind +
IDatasetRenderStrategy 字符串键注册表;classify 用谓词+byDdCode/byDsTypeCode 便捷器;
消费方(段/列/控制器)只认抽象不再散判;接入新类型=加描述符(必要时补策略/操作/筛选器);
§3/§6/§7/§8/§9/§11/§12 同步改为描述符+策略驱动并加可扩展性验收。
This commit is contained in:
gaozheng 2026-06-30 20:35:08 +08:00
parent dba1b32a43
commit 3b90b7de6a
1 changed files with 106 additions and 25 deletions

View File

@ -40,11 +40,11 @@
## 3. 架构方案 ## 3. 架构方案
**采用方案 A统一单列 `DatasetColumn`(由 `CategoryAnalysisTab` 升级而来)。** **采用方案 A统一单列 `DatasetColumn` + 类型抽象§5)。**
- 用**一份扩展的段配置**(增 `dimension`)同时驱动 3D 类型段与 2D 类型段;`CategorySection` 扩展「响应式图标工具条 + 维度专属操作」 - 用**一份类目描述符目录 `categoryCatalog()`**§5每类型一份 `CategoryDescriptor`:分类/筛选/操作/渲染策略)同时驱动 3D/2D 段;`CategorySection` 按描述符建筛选器与图标条;`VtkSceneController` 按描述符的 `renderStrategyId` 查可插拔渲染策略§5.4)渲染。**消费方不再 `if dimension/ddCode` 散判**
- 删除 `Column2DDataset``QTabWidget``ColumnDrawer` 承载单个 `DatasetColumn`,保留折叠开关。 - 删除 `Column2DDataset``QTabWidget``ColumnDrawer` 承载单个 `DatasetColumn`,保留折叠开关。
- 取舍:复用已验证的勾选保留/折叠/spinner/结构树逻辑;改动集中在段配置、段头工具条、2D 段操作、渲染落点 - 取舍:复用已验证的勾选保留/折叠/spinner/结构树逻辑;改动集中在描述符目录、段头工具条OpKind 映射)、渲染策略注册表
(备选 B「两 widget 去 tab 竖堆」因 2D/3D 段不一致、重复逻辑被否C「全重写」工作量过大被否。 (备选 B「两 widget 去 tab 竖堆」因 2D/3D 段不一致、重复逻辑被否C「全重写」工作量过大被否。
@ -59,12 +59,87 @@
--- ---
## 5. 统一段配置§2 ## 5. 类型抽象(扩展契约 —— 本 spec 的架构基石
- `CategorySpec` 增字段:`Dimension dim`(枚举 `D3` / `D2`)。 > 目标:**接入一个新 ds 类型 = 实现一份描述符(必要时再补一个渲染策略 / 一个操作 / 一个筛选器)**无论它「按什么规则接入、有什么操作、怎么渲染」UI 层与渲染层都只消费抽象、不再 `if dimension/ddCode` 散判。这是「数据集栏目」的统一规范,所有现存 5 类与未来新类都走它。
- `categoryConfigs()` 末尾加 2D 段:`{"trajectory","轨迹数据", dsTypeCode="", ddCode="dd_trajectory_data", dim=D2, ...}`。
- 段顺序(自上而下):电阻率 / 视电阻率 / 瞬变D3 反演)→ 三维体D3→ 轨迹D2 ### 5.1 类目描述符 `CategoryDescriptor`data 层,纯 C++,无 Qt/VTK
- 分流:`splitByCategory()` 扩展为同时按 D3 规则(现状)与 D2 口径(`dd_trajectory_data`,吸收 `splitByDimension` 的 2D 分支)分入对应段。`Column2DDataset`/`splitByDimension` 退役(保留 `dimOf` 给渲染路由按 ddCode 分派用)。
```cpp
namespace geopro::data {
enum class SceneKind { Volume3D, Curtain3D, Plane2D }; // 渲染语义 / 共存规则
enum class FilterKind { DateRange, ArrayType }; // 筛选器契约(可扩展)
enum class OpKind { GenerateVolume, Filter, PlaneZ, Basemap }; // 段操作契约(可扩展)
struct CategoryDescriptor {
std::string id; // "resistivity"/"apparent"/"transient"/"voxel"/"trajectory" ...
std::string title; // 段标题
SceneKind sceneKind; // 渲染语义
std::function<bool(const DsRow&)> classify; // 轴1 数据来源/分类("无论按什么规则接入"
std::vector<FilterKind> filters; // 轴2 本段筛选器(顺序=显示顺序)
std::vector<OpKind> operations; // 轴3 段头图标操作(顺序=显示顺序)
std::string renderStrategyId; // 轴4 渲染策略键(解析到注册表,见 §5.4
};
// classify 便捷构造器(覆盖现有按 ddCode / dsTypeCode 接入的常见情形;任意复杂规则可直接写 lambda
std::function<bool(const DsRow&)> byDdCode(std::initializer_list<std::string> codes);
std::function<bool(const DsRow&)> byDsTypeCode(std::initializer_list<std::string> codes);
const std::vector<CategoryDescriptor>& categoryCatalog(); // 有序目录,取代 categoryConfigs()
} // namespace geopro::data
```
### 5.2 目录定义catalog取代 `categoryConfigs()`
| id | title | sceneKind | classify | filters | operations | renderStrategyId |
|---|---|---|---|---|---|---|
| resistivity | 电阻率数据 | Curtain3D | `byDsTypeCode({"ERT platform inversion data"})` | DateRange,ArrayType | GenerateVolume,Filter | `"curtain"` |
| apparent | 视电阻率数据 | Curtain3D | `byDsTypeCode({"visual resistivity data"})` | DateRange,ArrayType | GenerateVolume,Filter | `"curtain"` |
| transient | 瞬变电磁数据 | Curtain3D | `byDsTypeCode({"DD TRANSIENT ELECTROMAGNETIC INVERSION"})` | DateRange | GenerateVolume,Filter | `"curtain"` |
| voxel | 三维体 | Volume3D | `byDdCode({"dd_voxel"})`mock 注入,见 §10 | DateRange | Filter | `"volume"` |
| trajectory | 轨迹数据 | Plane2D | `byDdCode({"dd_trajectory_data"})` | DateRange | PlaneZ,Filter,Basemap | `"plane2d"` |
段顺序即表序(电阻率→…→轨迹)。分流:`splitByCategory(rows)` 改为遍历 catalog对每行命中**首个** `classify(row)==true` 的描述符即归入该段(保留原顺序)。`Column2DDataset` / `splitByDimension` 退役;旧 `categoryConfigs()`/`CategorySpec` 由 `categoryCatalog()`/`CategoryDescriptor` 取代。
### 5.3 消费方只认抽象
- `CategorySection(descriptor)`:按 `descriptor.filters` 建筛选器(`FilterKind→UI` 映射一处)、按 `descriptor.operations` 建图标条(`OpKind→按钮+信号` 映射一处,见 §6/§7
- `DatasetColumn`:遍历 `categoryCatalog()` 建段、用 `descriptor.classify` 路由数据(即 `splitByCategory`)。
- `VtkSceneController`:勾选某 ds → 查其所属描述符 → `renderStrategyId` → 渲染策略 `add/remove` + 首勾/全消 `onTypeActivated/Deactivated`(见 §5.4、§8
### 5.4 可插拔渲染策略 `IDatasetRenderStrategy`controller/render 层)
```cpp
namespace geopro::controller {
class IDatasetRenderStrategy {
public:
virtual ~IDatasetRenderStrategy() = default;
virtual void add(const std::string& typeId, const std::string& dsId) = 0; // 异步加载+入场
virtual void remove(const std::string& dsId) = 0;
// 每类型场景资源生命周期(可选):本类型首个 ds 入场 / 全部离场
virtual void onTypeActivated(const std::string& typeId) {}
virtual void onTypeDeactivated(const std::string& typeId) {}
};
// 注册表renderStrategyId(字符串键) → 策略实例。当前 3 实现:
// "volume" VolumeRenderStrategy —— 体素/雷达体(包现 isVolumeDataset 分支)
// "curtain" CurtainRenderStrategy —— 反演帘面(包现 dd_section 等分支)
// "plane2d" Plane2DRenderStrategy —— 2D 折线落类型平面 + 平面底图(封装 §8.2 平面 z + §9.2 底图)
} // namespace geopro::controller
```
**关键**2D 的全部特殊性(按类型平面 z 生命周期 + N 个平面底图)**封死在 `Plane2DRenderStrategy` 一个类内**(内含 `PlaneZRegistry` + 平面底图管理3D 两个策略只是包住现有渲染分支。控制器主流程不再含维度/ddCode 分支,只做「查描述符 → 取策略 → 调用」。
### 5.5 「接入一个新 ds 类型」标准动作(即本规范)
| 场景 | 要做的 |
|---|---|
| 新类型、**沿用**已有筛选/操作/渲染 | **只加一条 `CategoryDescriptor`**(纯数据),完。 |
| 新类型、要**新渲染方式** | 加描述符 + 实现一个 `IDatasetRenderStrategy` 并注册(新 `renderStrategyId`)。 |
| 新类型、要**新操作** | `OpKind` 加一项 + `CategorySection` 加该 kind→UI 的**一处**映射;描述符 `operations` 列上。 |
| 新类型、要**新筛选器** | `FilterKind` 加一项 + `CategorySection` 加**一处**映射;描述符 `filters` 列上。 |
数据驱动优先;只有真出现「新渲染/新操作/新筛选器」才动对应那**一个**扩展点,且改动收敛在单一位置,不扩散。
--- ---
@ -72,32 +147,34 @@
- 段头右侧承载图标工具条:**默认最多显示 3 个图标**;图标总数超 3**或段宽被挤压放不下时****右侧图标依次收进末尾「…」下拉菜单**。`resizeEvent` 动态重算可见数 —— 这是**必须实现并可验证**的行为:即使图标 ≤3当栏位宽度收窄到放不下时右侧图标也要实时折进「…」栏位变宽再弹回。 - 段头右侧承载图标工具条:**默认最多显示 3 个图标**;图标总数超 3**或段宽被挤压放不下时****右侧图标依次收进末尾「…」下拉菜单**。`resizeEvent` 动态重算可见数 —— 这是**必须实现并可验证**的行为:即使图标 ≤3当栏位宽度收窄到放不下时右侧图标也要实时折进「…」栏位变宽再弹回。
- 每个图标 = `QToolButton`autoRaise + glyph + tooltip点击触发对应操作部分弹 popup - 每个图标 = `QToolButton`autoRaise + glyph + tooltip点击触发对应操作部分弹 popup
- 各维度图标集 - **图标集由描述符 `operations``OpKind` 列表)驱动**,不是按维度硬编码。`CategorySection` 内有一处 `OpKind→(图标 glyph + tooltip + 点击/popup)` 映射表新增操作只加一项映射§5.5)。当前 catalog 各段对应
| 段 | 图标(左→右,右侧优先收进…) | | 段 | `operations`(左→右,右侧优先收进…) |
|---|---| |---|---|
| 3D 反演(电阻率/视电阻率/瞬变) | `新增三维体`、`筛选` | | 3D 反演(电阻率/视电阻率/瞬变) | `GenerateVolume`、`Filter` |
| 3D 三维体 | `筛选` | | 3D 三维体 | `Filter` |
| 2D 轨迹 | `z值`、`筛选`、`底图` | | 2D 轨迹 | `PlaneZ`、`Filter`、`Basemap` |
- 注:当前各段图标 ≤3「数量超 3」分支暂不触发但**「宽度挤压」分支必须工作**(窄栏即折叠);两个分支都要实现(后续图标会增加,数量分支随之生效)。 - 注:当前各段图标 ≤3「数量超 3」分支暂不触发但**「宽度挤压」分支必须工作**(窄栏即折叠);两个分支都要实现(后续图标会增加,数量分支随之生效)。
--- ---
## 7. 段内操作行为(§4 ## 7. 段内操作行为(每个 = 一个 `OpKind`
### 7.1 筛选通用D2+D3 > 下列每个操作对应 §5 的一个 `OpKind``CategorySection` 的 `OpKind→UI` 映射表据描述符 `operations` 装配。`PlaneZ`/`Basemap` 的渲染落点封装在 `Plane2DRenderStrategy`§5.4/§8.2/§9.2)。
### 7.1 筛选 `OpKind::Filter`通用D2+D3
- 现「固定显示的筛选行(日期范围 + 装置类型[仅 ERT])」改为**默认折叠**(段体内不占位)。 - 现「固定显示的筛选行(日期范围 + 装置类型[仅 ERT])」改为**默认折叠**(段体内不占位)。
- 点「筛选」图标 → 展开筛选行;再点 → 收起toggle。筛选逻辑`passesFilters`/`rebuildList`)不变。 - 点「筛选」图标 → 展开筛选行;再点 → 收起toggle。筛选逻辑`passesFilters`/`rebuildList`)不变。
### 7.2 新增三维体(仅 3D 反演段) ### 7.2 新增三维体 `OpKind::GenerateVolume`(仅 3D 反演段)
- 保持原功能(发 `generateVolumeRequested`),入口从文字按钮改图标。 - 保持原功能(发 `generateVolumeRequested`),入口从文字按钮改图标。
### 7.3 z值仅 2D 段) ### 7.3 z值 `OpKind::PlaneZ`(仅 2D 段)
- 点图标弹 popup一个滑块整体上下移动**该 2D 类型那块平面**的 z。 - 点图标弹 popup一个滑块整体上下移动**该 2D 类型那块平面**的 z。
- 初值 = 该类型第一个被勾选 ds 的 z见 §8.2);范围按场景高程量级合理取(实现期定)。 - 初值 = 该类型第一个被勾选 ds 的 z见 §8.2);范围按场景高程量级合理取(实现期定)。
### 7.4 底图(仅 2D 段) ### 7.4 底图 `OpKind::Basemap`(仅 2D 段)
- 点图标弹 popup【底图类型矢量平面(默认) / 无】+【透明度滑块(默认 50%)】。 - 点图标弹 popup【底图类型矢量平面(默认) / 无】+【透明度滑块(默认 50%)】。
- 作用于**该 2D 类型自己那块平面底图**§9.2)。 - 作用于**该 2D 类型自己那块平面底图**§9.2,由 `Plane2DRenderStrategy` 持有)。
### 7.5 3D 底图(移到 `VtkViewToolbar`,非段操作) ### 7.5 3D 底图(移到 `VtkViewToolbar`,非段操作)
- 在 `Gear`(坐标轴设置) 正下方新增「地图」图标按钮,点击弹 popup【底图类型天地图(默认) / 无】+【透明度滑块(默认 50%)】。 - 在 `Gear`(坐标轴设置) 正下方新增「地图」图标按钮,点击弹 popup【底图类型天地图(默认) / 无】+【透明度滑块(默认 50%)】。
@ -109,13 +186,15 @@
--- ---
## 8. 渲染模型§5 ## 8. 渲染模型(经渲染策略 §5.4
> 控制器主流程:勾选 diff → 对每个新增/移除 ds 查其描述符 → 取 `renderStrategyId` 对应策略 → `add/remove`;某类型「首勾」「全消」时调 `onTypeActivated/Deactivated`。无维度/ddCode 分支。
### 8.1 删除维度耦合 ### 8.1 删除维度耦合
- 移除 `VtkSceneView::setAnalysisMode2D` 的「相机锁定俯视 + 按维度翻可见标志」与 `VtkViewToolbar::setAnalysisMode2D` 的 6 向禁用。场景恒自由透视2D/3D actor 同时可见(各自由勾选控制显隐)。 - 移除 `VtkSceneView::setAnalysisMode2D` 的「相机锁定俯视 + 按维度翻可见标志」与 `VtkViewToolbar::setAnalysisMode2D` 的 6 向禁用。场景恒自由透视2D/3D actor 同时可见(各自由勾选控制显隐)。
- 移除 `view2DMode` 5 模式`set2DPlacement` 的 mode 维度退化为「按类型平面 z」)。 - 移除 `view2DMode` 5 模式与旧 `set2DPlacement` mode 维度(其职责并入 `Plane2DRenderStrategy`)。
### 8.2 2D 按类型平面(需求 5 ### 8.2 2D 按类型平面(需求 5—— 封装在 `Plane2DRenderStrategy`
- 同一 2D 类型(段)勾选的全部 ds 投影到**一块平面** - 同一 2D 类型(段)勾选的全部 ds 投影到**一块平面**
- 平面 z = 该类型**第一个被勾选 ds** 的 z首个勾选时确定后**固定不变**,仅由 z 滑块整体升降)。 - 平面 z = 该类型**第一个被勾选 ds** 的 z首个勾选时确定后**固定不变**,仅由 z 滑块整体升降)。
- 平面**纯平、不渲染高程**。 - 平面**纯平、不渲染高程**。
@ -139,8 +218,8 @@
| 地面 Z | `kGroundZ=0` 常量 | 构造/setter 传 `groundZ`2D 平面 = 类型平面 z | | 地面 Z | `kGroundZ=0` 常量 | 构造/setter 传 `groundZ`2D 平面 = 类型平面 z |
| 透明度 | `kTerrainOpacity=0.55` 固定 | 参数化,默认 0.5 可调 | | 透明度 | `kTerrainOpacity=0.55` 固定 | 参数化,默认 0.5 可调 |
| 平面/矢量模式 | 由 `Kind` 隐含 | 复用 `Street`(vec_w)+`buildFlat`(已是纯平矢量路径,跳过 DEM/warp | | 平面/矢量模式 | 由 `Kind` 隐含 | 复用 `Street`(vec_w)+`buildFlat`(已是纯平矢量路径,跳过 DEM/warp |
- **最终布局**1 个 `TileBasemap`3DSatellite/带高程Z=0§9.1+ N 个 `TileBasemap`(每 2D 类型一个Street/纯平矢量,`groundZ`=平面 z。共享同一 `Scene` / `GeoLocalFrame` - **最终布局**1 个 `TileBasemap`3DSatellite/带高程Z=0§9.1,挂控制器+ N 个 `TileBasemap`(每 2D 类型一个Street/纯平矢量,`groundZ`=平面 z**由 `Plane2DRenderStrategy` 持有**)。共享同一 `Scene` / `GeoLocalFrame`
- **生命周期管理器**(挂 `VtkSceneController`):按 2D 类型持有 N 个实例 + 各自平面 z该类型首勾 → 建实例§8.2 定 z该类型全消 → 销毁实例(连同折线平面)。 - **生命周期**(在 `Plane2DRenderStrategy` 内,经 `onTypeActivated/Deactivated`):按 2D 类型持有 N 个实例 + 各自平面 z`PlaneZRegistry`;该类型首勾 → 建实例§8.2 定 z该类型全消 → 销毁实例(连同折线平面)。
- 瓦片范围**复用** `dataHorizontalRadius()×10``[2000,30000]m` 规则(各实例共享同一 `dataRadiusProvider`)。 - 瓦片范围**复用** `dataHorizontalRadius()×10``[2000,30000]m` 规则(各实例共享同一 `dataRadiusProvider`)。
- 坐标对齐沿用 `GeoLocalFrame`经纬→局部frame 重锚逻辑不变。 - 坐标对齐沿用 `GeoLocalFrame`经纬→局部frame 重锚逻辑不变。
@ -158,7 +237,8 @@
- **替代 2026-06-26 spec**:该 spec 的「一场景两相机 + 按维度显隐 + 高程拖动分层」中,**相机锁定 + 维度显隐被本 spec 推翻**改单一自由场景共存。其「2D 沿 Z 拖动分离」语义改由「按类型平面 + z 滑块」承担——逐 ds 独立拖动 `nudgeSelectedMapLinesZ` / `mapLineZOffset_`(及拾取拖动浮层读数)**废弃移除**,统一到类型平面 z。 - **替代 2026-06-26 spec**:该 spec 的「一场景两相机 + 按维度显隐 + 高程拖动分层」中,**相机锁定 + 维度显隐被本 spec 推翻**改单一自由场景共存。其「2D 沿 Z 拖动分离」语义改由「按类型平面 + z 滑块」承担——逐 ds 独立拖动 `nudgeSelectedMapLinesZ` / `mapLineZOffset_`(及拾取拖动浮层读数)**废弃移除**,统一到类型平面 z。
- **`dd_raster`** 仍未接2026-06-26 §6 遗留),本 spec 不含;后续作为新的 2D 类型段加入时复用 §8.2/§9.2 平面+底图机制。 - **`dd_raster`** 仍未接2026-06-26 §6 遗留),本 spec 不含;后续作为新的 2D 类型段加入时复用 §8.2/§9.2 平面+底图机制。
- **3D 反演段 vs 三维体段**:均为 D3但 3D 反演段渲染为帘面、三维体段为体素/雷达体——共用 3D 底图与自由场景,无需区分。 - **3D 反演段 vs 三维体段**:均为 D3但 3D 反演段渲染为帘面、三维体段为体素/雷达体——共用 3D 底图与自由场景,无需区分。
- **责任拆分**`SectionIconBar` 响应式工具条、`TileBasemap` 多实例参数化、`DatasetColumn` 动态显隐 三块相互独立,可分别实现与测试。 - **责任拆分**`CategoryDescriptor`/`categoryCatalog`(描述符)、`IDatasetRenderStrategy` 注册表(渲染)、`SectionIconBar`(响应式工具条)、`TileBasemap` 多实例、`DatasetColumn`(动态显隐)相互独立,可分别实现与测试。
- **抽象的 YAGNI 边界**`FilterKind`/`OpKind`/`SceneKind` 只列当前真用到的枚举值;渲染策略只实现 `volume`/`curtain`/`plane2d` 三个。扩展点是「加新值/新策略」的预留口,不预先实现任何未用类型。
--- ---
@ -173,3 +253,4 @@
7. 渲染区工具栏 Gear 下方新增「地图」按钮,控制全局 3D 底图(天地图/无 + 透明度默认 50%)。 7. 渲染区工具栏 Gear 下方新增「地图」按钮,控制全局 3D 底图(天地图/无 + 透明度默认 50%)。
8. 「导入雷达」入口出现在顶部「设备」菜单;三维体段头不再有该按钮。 8. 「导入雷达」入口出现在顶部「设备」菜单;三维体段头不再有该按钮。
9. 「直接挂项目下的 ds」加载时默认勾选进 VTK 不被破坏。 9. 「直接挂项目下的 ds」加载时默认勾选进 VTK 不被破坏。
10. **可扩展性**:现存 5 类全部经 `categoryCatalog()` 描述符 + 渲染策略注册表驱动,控制器/段头无维度/ddCode 散判。新增一个「沿用已有渲染/操作/筛选」的类型,只需在 catalog 加一条描述符即可显示+渲染(以一个验证性 demo 描述符或单元测试佐证分类/路由走通)。