geopro/docs/superpowers/specs/2026-07-01-vtk-view-navigat...

7.4 KiB
Raw Blame History

VTK 视图导航与坐标轴改进 — Spec2026-07-01

分支 feat/vtk-merged-dataset-column(合并数据集单栏重构之后的增量)。职责:渲染区坐标轴、方向标、相机导航、列表双击联动。全部决策已与用户逐条确认。

0. 背景与动机

合并单栏 + 单一自由场景后2D 平面与 3D 体/帘面共存于一个世界坐标系。现状渲染坐标轴是全场景合并包围盒的一套 cube axesAxesActor/rebuildAxes)。用户痛点:多个渲染物相距很远/尺度悬殊时,全场景轴只贴近某一个物体,离轴远的物体读不出真实尺寸。业界通行解法不是「每物体一套常驻轴」,而是「全场景一套参考 + 选中物体单独出其贴合轴 + 可点击方向标 + 按需测量」。本 spec 落地导航侧改进。

1. 已确认决策(逐条)

  1. 坐标轴(空间)全场景一套是对的——坐标轴量的是空间位置(一个世界 CS物理量差异由逐数据集色阶承担尺度悬殊用统一 VE不拆轴。
  2. 选中物体 → 隐全景轴、只显该物体贴合轴Q1
  3. 贴合轴按层级子树归一到一个包围盒:选中三维体(或其下切片、切片下异常)→ 包围盒覆盖「该三维体 + 其切片 + 其异常」整棵子树,合成一个盒;选谁都归到该子树同一个盒。
  4. 角落三向标gnomon常驻 + 可点击Q2点击某方向轴 → 相机绕支点转到该轴、保留当前缩放距离ViewCube 手感选项1
  5. 绕轴支点规则:有选中 → 选中物体子树包围盒中心;无选中 → 全场景合并包围盒中心。两者都保留当前缩放。
  6. 列表双击 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。实现取当前 |camfocal| 距离 dfocal 设为 pivot按 dir 设 cam=pivot+dir_offset*d、view-up 按预设;ResetCameraClippingRange

3.2 贴合轴 + 隐全景轴(决策 2/3

  • VtkSceneView 维护两态:rebuildAxes()(全场景,现状)与新 showFittedAxes(const double b[6])/showSceneAxes()
  • 选中某 dsdatasetSelected 到达)→ 上层算该 ds 子树的 dsIds → datasetBoundsshowFittedAxes(box)(把 cube axes 的 bounds 设为子树盒)+ 隐全景轴。
  • 取消选中(空选中)→ showSceneAxes()(恢复全场景轴)。
  • 实现可复用 AxesActor:新增「按外部 bounds 显示」的模式(不再固定用全场景 bounds选中/取消在两种 bounds 间切换。

3.3 可点击方向标 gnomon决策 4/5

  • 新增角落三向标:vtkOrientationMarkerWidget + vtkAxesActorXYZ 三向 + 轴标签),常驻右下(或右上,避开现有工具栏)。
  • 可点击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)。扩展双击行为:并发触发
    1. 适配:算该 dsId 子树 dsIds → datasetBoundsfitToBounds(相机适配到该 DS 空间范围,保持朝向)。
    2. 详情联动:沿用现有 detailRequested → DatasetDetailController 链打开中下方 DatasetDetailPanel
  • 容错静默:对详情页未实现的类型(三维体 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 解析」。
  • T3gnomon:常驻可点击三向标 → orbitToAxis(pivot 规则)。
  • T4双击:双击→适配到子树盒 + 详情联动 gate无详情页静默
  • 顺序T1 先T2/T3/T4 都依赖 T1 的基元T2/T4 依赖「子树 dsIds 解析」T2 引入T4 复用)。

6. 验收

  1. 无选中:显示全场景总览轴 + 右下角三向标。
  2. 选中某体/切片/异常:全景轴隐去,出现覆盖「该体+其切片+异常」子树的一个贴合包围盒轴,能读该子树真实尺寸;取消选中恢复全景轴。
  3. 点击三向标某方向轴:相机绕支点(选中子树中心/无选中则全场景中心)转到该轴、缩放不变、数据仍居中。
  4. 双击列表某 DS视图适配到该 DS子树空间范围有详情页的类型联动打开中下方详情页三维体等无详情页类型静默(只适配、不报错)。
  5. VE 对全部渲染物一致;坐标轴仍是空间位置度量、逐数据集色阶不变。