7.4 KiB
7.4 KiB
VTK 视图导航与坐标轴改进 — Spec(2026-07-01)
分支
feat/vtk-merged-dataset-column(合并数据集单栏重构之后的增量)。职责:渲染区坐标轴、方向标、相机导航、列表双击联动。全部决策已与用户逐条确认。
0. 背景与动机
合并单栏 + 单一自由场景后,2D 平面与 3D 体/帘面共存于一个世界坐标系。现状渲染坐标轴是全场景合并包围盒的一套 cube axes(AxesActor/rebuildAxes)。用户痛点:多个渲染物相距很远/尺度悬殊时,全场景轴只贴近某一个物体,离轴远的物体读不出真实尺寸。业界通行解法不是「每物体一套常驻轴」,而是「全场景一套参考 + 选中物体单独出其贴合轴 + 可点击方向标 + 按需测量」。本 spec 落地导航侧改进。
1. 已确认决策(逐条)
- 坐标轴(空间)全场景一套是对的——坐标轴量的是空间位置(一个世界 CS),物理量差异由逐数据集色阶承担,尺度悬殊用统一 VE,不拆轴。
- 选中物体 → 隐全景轴、只显该物体贴合轴(Q1)。
- 贴合轴按层级子树归一到一个包围盒:选中三维体(或其下切片、切片下异常)→ 包围盒覆盖「该三维体 + 其切片 + 其异常」整棵子树,合成一个盒;选谁都归到该子树同一个盒。
- 角落三向标(gnomon)常驻 + 可点击(Q2);点击某方向轴 → 相机绕支点转到该轴、保留当前缩放距离(ViewCube 手感,选项1)。
- 绕轴支点规则:有选中 → 选中物体子树包围盒中心;无选中 → 全场景合并包围盒中心。两者都保留当前缩放。
- 列表双击 DS →(a)视图适配到该 DS 的空间范围 +(b)联动打开中下方数据集详情页;三维体等详情页未做的类型要容错静默(找不到对应详情页不报错、不弹窗,仅完成适配)。
2. 术语:子树包围盒
- 一个 ds 的「子树」= 该 ds 自身 + 其所有后代 ds(三维体 → 其切片 → 切片的异常),仅计入当前已渲染的成员。
- 子树包围盒 = 子树内所有已渲染 ds 的 actor 包围盒并集(
dsProps_按 dsId 取 actor bounds)。 - 后代关系来源:数据模型的父子(切片 parentId=体、异常 parentId=切片/体)——由控制器/仓储或面板树提供 dsId→后代集。渲染侧只需拿到「要并集的 dsId 列表」。
3. 设计
3.1 相机/包围盒基元(VtkSceneView + CameraPreset)
bool datasetBounds(const std::vector<std::string>& dsIds, double outB[6]) const:并集给定 dsIds 的已渲染 actor 包围盒;无有效则返回 false。void fitToBounds(const double b[6]):把相机适配到指定包围盒(保持朝向,ResetCamera(b))。用于双击适配、贴合。void orbitToAxis(ViewDir dir, const double pivot[3]):相机绕 pivot 转到沿 dir 轴看向 pivot,保留当前 focal-to-camera 距离(即当前缩放)。区别于applyCameraView(正视重置)与 6 视图按钮(重置+fit)。实现:取当前 |cam−focal| 距离 d;focal 设为 pivot;按 dir 设 cam=pivot+dir_offset*d、view-up 按预设;ResetCameraClippingRange。
3.2 贴合轴 + 隐全景轴(决策 2/3)
VtkSceneView维护两态:rebuildAxes()(全场景,现状)与新showFittedAxes(const double b[6])/showSceneAxes()。- 选中某 ds(
datasetSelected到达)→ 上层算该 ds 子树的 dsIds →datasetBounds→showFittedAxes(box)(把 cube axes 的 bounds 设为子树盒)+ 隐全景轴。 - 取消选中(空选中)→
showSceneAxes()(恢复全场景轴)。 - 实现可复用
AxesActor:新增「按外部 bounds 显示」的模式(不再固定用全场景 bounds),选中/取消在两种 bounds 间切换。
3.3 可点击方向标 gnomon(决策 4/5)
- 新增角落三向标:
vtkOrientationMarkerWidget+vtkAxesActor(XYZ 三向 + 轴标签),常驻右下(或右上,避开现有工具栏)。 - 可点击:
vtkOrientationMarkerWidget默认不透传点击到轴。需在其上做拾取——用一个vtkPropPicker/自定义,把点击落到 +X/−X/+Y/−Y/+Z/−Z 六向之一 → 调orbitToAxis(dir, pivot)。pivot 按决策 5(选中子树中心 / 全场景中心)。 - 六向 →
ViewDir映射:+Z=Top、−Z=Bottom、+Y=Back(北望反)/−Y=Front… 与CameraPreset现有 ViewDir 语义对齐(复用,不新造方向定义)。
3.4 双击 DS → 适配 + 详情(决策 6)
- 列表双击现已发
detailRequested(dsId, ddCode, name)(CategorySection::itemDoubleClicked)。扩展双击行为:并发触发- 适配:算该 dsId 子树 dsIds →
datasetBounds→fitToBounds(相机适配到该 DS 空间范围,保持朝向)。 - 详情联动:沿用现有
detailRequested → DatasetDetailController链打开中下方DatasetDetailPanel。
- 适配:算该 dsId 子树 dsIds →
- 容错静默:对详情页未实现的类型(三维体
dd_voxel/dd_radar_3d等),详情链找不到对应页时静默——不弹错、不打断,适配照常完成。实现:详情控制器/面板在无匹配详情页时走「无操作」分支(或双击前按 ddCode 判定「有详情页才发 detailRequested」)。优先在联动入口按「该 ddCode 是否有详情页」gate,无则只做适配。
4. 影响文件(预估)
src/app/VtkSceneView.{hpp,cpp}:datasetBounds/fitToBounds/orbitToAxis/showFittedAxes/showSceneAxes+ gnomon widget。src/render/CameraPreset.{hpp,cpp}或src/render/actors/AxesActor.*:orbit-to-axis 相机数学、按外部 bounds 的轴。src/controller/VtkSceneController.*+I3dSceneView:转发新相机/轴/适配接口;子树 dsIds 解析(或由 main 用面板树/仓储父子算)。src/app/main.cpp:选中→贴合轴、双击→适配+详情 gate、gnomon 点击接线。src/app/panels/columns/CategorySection.*/CategoryAnalysisTab.*:双击透传(已有 detailRequested;可能加「fitRequested(dsId)」或复用 datasetSelected)。
5. 分期/任务
- T1(基元):
datasetBounds/fitToBounds/orbitToAxis(相机+包围盒基元,纯渲染,可单测 orbit 数学)。 - T2(贴合轴):选中→子树盒→贴合轴+隐全景轴;取消→恢复全景轴。含「子树 dsIds 解析」。
- T3(gnomon):常驻可点击三向标 → orbitToAxis(pivot 规则)。
- T4(双击):双击→适配到子树盒 + 详情联动 gate(无详情页静默)。
- 顺序:T1 先(T2/T3/T4 都依赖 T1 的基元);T2/T4 依赖「子树 dsIds 解析」(T2 引入,T4 复用)。
6. 验收
- 无选中:显示全场景总览轴 + 右下角三向标。
- 选中某体/切片/异常:全景轴隐去,出现覆盖「该体+其切片+异常」子树的一个贴合包围盒轴,能读该子树真实尺寸;取消选中恢复全景轴。
- 点击三向标某方向轴:相机绕支点(选中子树中心/无选中则全场景中心)转到该轴、缩放不变、数据仍居中。
- 双击列表某 DS:视图适配到该 DS(子树)空间范围;有详情页的类型联动打开中下方详情页;三维体等无详情页类型静默(只适配、不报错)。
- VE 对全部渲染物一致;坐标轴仍是空间位置度量、逐数据集色阶不变。