vtkResliceCursorWidget 系统设计还原
vtkResliceCursorWidget 系统设计还原
基于 VTK 9.5.0 源码
Interaction/Widgets/分析
涉及类:vtkResliceCursorWidget / vtkResliceCursorRepresentation / vtkResliceCursorLineRepresentation /
vtkResliceCursor / vtkResliceCursorActor / vtkResliceCursorPolyDataAlgorithm / vtkResliceCursorPicker
一、整体架构(MVC 分层 + 数据管线)
VTK Widget 框架的标准三层(Widget / Representation / Prop),叠加一条独立的重切片数据管线。
关键设计点
| 角色 | 类 | 职责 |
|---|---|---|
| 控制器 | vtkResliceCursorWidget | 只做事件→动作映射,把坐标交给 Rep,不含几何运算 |
| 视图/交互 | vtkResliceCursorRepresentation | 切片图像管线 + 交互态计算(抽象) |
| 交互实现 | vtkResliceCursorLineRepresentation | 旋转/平移的具体数学 + 十字线 actor |
| 数据模型 | vtkResliceCursor | 中心/三轴/厚度/三平面,唯一真相源(被多视图共享) |
| 几何生成 | vtkResliceCursorPolyDataAlgorithm | 模型 → 线条几何 |
| 十字线渲染 | vtkResliceCursorActor | 3D Prop,画十字线和厚层线 |
| 拾取 | vtkResliceCursorPicker | 屏幕坐标 ↔ 切片平面交点 |
二、切片图像数据管线(View 内部)
ResliceAxes 这个 4×4 矩阵是模型→管线的桥梁:UpdateReslicePlane() 用 vtkResliceCursor
的三轴和中心填充它,再 reslice->SetResliceAxes() 驱动重采样。
三、核心过程时序图
时序 1:初始化与首帧渲染
时序 2:鼠标拖拽旋转/平移 → 重切片 → 多视图同步(核心闭环)
以 Ctrl+左键旋转为例:
多视图同步的核心机制
关键在于:三个视图(轴/冠/矢)共享同一个 vtkResliceCursor 实例。
- 任一视图改动 →
rc->SetXAxis()/SetCenter()→rc的MTime更新 - 该 Widget 通过
rc->InvokeEvent(ResliceAxesChangedEvent)主动广播 - 其它 Widget 在
rc上注册了观察者,回调里调用各自Render() - 各视图
BuildRepresentation()发现rc->GetMTime() > BuildTime,重新UpdateReslicePlane()
拉取新模型 → 重切片
时序 3:厚度调整(右键) / 窗宽窗位(空白区左键)
注意
BuildRepresentation()里有判断:WindowLevelling模式跳过UpdateReslicePlane()——
窗位只改颜色映射,不需要重切片,这是性能优化。
四、设计模式总结
| 模式 | 体现 |
|---|---|
| MVC / 三层 | Widget(控制) — Representation(视图) — ResliceCursor(模型) |
| 状态机 | WidgetState(Start/Active) + ManipulationMode(None/PanAndRotate/RotateBothAxes/TranslateSingleAxis/ResizeThickness/WindowLevelling) |
| 命令/回调映射 | vtkWidgetCallbackMapper 把"事件+修饰键"映射到 static 回调函数 |
| 观察者 | InvokeEvent(ResliceAxesChangedEvent),多视图通过监听共享模型实现同步 |
| 模板方法 | 基类 vtkResliceCursorRepresentation 定义交互接口,子类 LineRepresentation 实现 |
| 管线(Pipeline) | Reslice → ColorMap → Texture/Actor 的 VTK 数据流 |
| 策略 | UseImageActor 切换两种渲染策略;ThickMode 在 vtkImageReslice / vtkImageSlabReslice 间切换 |
设计精髓:Widget 只管"翻译"鼠标事件,所有几何运算下沉到 Representation,而 vtkResliceCursor
作为被多视图共享的单一数据模型,配合 VTK 的事件观察者机制,实现了三正交视图的实时联动重切片。
五、事件→动作映射表(构造函数中注册)
| 触发条件 | WidgetEvent | 回调 | 动作 |
|---|---|---|---|
| 左键按下(无修饰) | Select | SelectAction | 命中→PanAndRotate;空白→窗宽窗位 |
| Ctrl+左键 | Rotate | RotateAction | RotateBothAxes |
| Alt+左键 | Translate | TranslateAction | TranslateSingleAxis |
| 右键按下 | Resize | ResizeThicknessAction | 调整厚度(需 ThickMode) |
| 左/右键释放 | EndSelect/EndResize | EndSelectAction | 结束交互,复位 |
| 鼠标移动 | Move | MoveAction | 悬停判定光标 / 拖拽时更新 |
| 按键 “o” | Reset | ResetResliceCursorAction | 复位 cursor |
六、类继承 UML 图
6.1 继承层次(泛化关系 ▷)
箭头方向为「子类 → 父类」(泛化)。
6.2 关联/聚合关系(类图,简化)
关系图例:
o-->聚合(生命周期独立) ·*-->组合(内部 new/Delete 强拥有) ·<|--泛化(继承) ·..>共享/依赖
6.3 关系性质说明
| 关系 | 类型 | 说明 |
|---|---|---|
| Widget → Representation | 聚合 | WidgetRep 由外部 SetRepresentation 传入,或 CreateDefaultRepresentation 默认创建 |
| LineRep ◆ ResliceCursorActor | 组合 | 子类内部 new/Delete,强拥有 |
| ResliceCursorActor ◆ CursorAlgorithm | 组合 | actor 内部持有几何算法 |
| Rep / Actor / Picker → ResliceCursor | 关联(共享) | 三者都引用同一个 vtkResliceCursor,是多视图联动的关键 |
| Picker → CursorAlgorithm | 关联 | 拾取前 SetResliceCursorAlgorithm() 注入 |
七、vtkResliceCursorPicker 拾取细节
vtkResliceCursorPicker 继承自 vtkPicker,是射线投射拾取器(ray-cast),专门判断鼠标点中了
十字线的哪一部分:轴 1 / 轴 2 / 中心。它由 ComputeInteractionState() 在每次按下或悬停时调用。
7.1 调用入口
// vtkResliceCursorLineRepresentation::ComputeInteractionState()
this->Picker->SetResliceCursorAlgorithm(this->ResliceCursorActor->GetCursorAlgorithm());
int picked = this->Picker->Pick(X, Y, 0, this->Renderer);
bool pickedAxis1 = this->Picker->GetPickedAxis1();
bool pickedAxis2 = this->Picker->GetPickedAxis2();
bool pickedCenter = this->Picker->GetPickedCenter();
if (picked) this->Picker->GetPickPosition(this->StartPickPosition);
// 优先级:Center > Axis1 > Axis2
if (pickedCenter) InteractionState = OnCenter;
else if (pickedAxis1) InteractionState = OnAxis1;
else if (pickedAxis2) InteractionState = OnAxis2;
else InteractionState = Outside;
拾取优先级:中心 > 轴1 > 轴2。命中中心可平移整个 cursor,命中单轴则旋转/平移该轴。
7.2 Pick() 算法五步(vtkResliceCursorPicker::Pick)
① 构造拾取射线
屏幕 (X,Y) ──DisplayToWorld──▶ 世界坐标 PickPosition
以相机位置→该点连线为射线,裁剪到前/后裁剪面 → 线段 [p1World, p2World]
(区分透视投影 / 平行投影两种端点算法)
② 计算世界坐标容差 tol
取视口对角两点的世界距离 × this->Tolerance
→ 把屏幕像素容差换算成世界空间的"粗细半径"
③ 取出待测几何(来自共享的 ResliceCursor)
axis1 = CursorAlgorithm->GetAxis1(); axis2 = GetAxis2();
center = rc->GetCenter();
line1 = rc->GetCenterlineAxisPolyData(axis1); // 轴1 线条
line2 = rc->GetCenterlineAxisPolyData(axis2); // 轴2 线条
④ 三次相交测试
PickedCenter = IntersectPointWithLine(p1,p2, center, tol); // 点 vs 射线
PickedAxis1 = IntersectPolyDataWithLine(p1,p2, line1, tol); // 线段 vs 射线
PickedAxis2 = IntersectPolyDataWithLine(p1,p2, line2, tol);
⑤ 若命中,回算精确拾取点
TransformPlane(); // 应用 TransformMatrix
Plane->IntersectWithLine(p1,p2, t, pickPosT); // 与切片平面求交
InverseTransformPoint(pickPosT, PickPosition); // 逆变换回真实世界坐标
返回 PickedAxis1 + PickedAxis2 + PickedCenter (>0 即命中)
7.3 两个相交测试核心
IntersectPointWithLine(点—射线,判中心命中):
把中心点投影到射线上,参数 t∈[0,1] 且三个分量偏差都 < tol → 命中。
t = dot(ray, X - p1) / dot(ray, ray); // 投影参数
if (t in [0,1] && |X - (p1 + t*ray)| < tol) // 逐分量在容差内
return 1;
IntersectPolyDataWithLine(线段—射线,判轴命中):
遍历 polydata 每个 cell,用 cell->IntersectWithLine(p1,p2,tol,...) 求交,命中即返回。
若设了 TransformMatrix,会先把 cell 顶点变换后再测。
7.4 TransformMatrix 的作用
当切片平面被旋转/有用户矩阵(SetUserMatrix)时,十字线几何在世界空间是"斜放"的。
Picker 用 TransformMatrix 把:
- 测试阶段:几何点变换到与射线一致的空间求交;
- 回算阶段:用
InverseTransformPoint把交点逆变换回真实世界坐标,
保证StartPickPosition在后续WidgetInteraction的平移/旋转计算中正确。
7.5 重载 Pick(displayPos, world, ren) — 平移用
void Pick(double displayPos[2], double world[3], vtkRenderer* ren);
这是另一个重载:给一个屏幕坐标,直接返回它在当前切片平面上的世界交点。
WidgetInteraction 中平移(OnCenter / TranslateAxis)就靠它把鼠标位置映射到平面上:
// 平移中心
this->Picker->Pick(e, intersectionPos, this->Renderer);
newCenter[i] = StartCenterPosition[i] + intersectionPos[i] - StartPickPosition[i];
rc->SetCenter(newCenter);
7.6 拾取时序小结
八、补充图(Mermaid 汇总)
第一/二/三/六/七节的图已全部内嵌为 Mermaid。此节作为汇总附录,提供含 VTK 基类的完整类图与 Widget 状态机。
8.1 完整类继承 / 关联图(含 VTK 基类)
交互时序图见 §三、拾取流程图见 §7.6,均已内嵌为 Mermaid,此处不再重复。
8.2 状态机(Mermaid stateDiagram)
注:本文档统一采用 Mermaid 渲染,已移除 PlantUML 块。在 VS Code 中安装扩展
Markdown Preview Mermaid Support(bierner.markdown-mermaid)后,用Ctrl+Shift+V
打开 Markdown 预览即可本地渲染上述全部图表,无需任何服务器。
更多推荐



所有评论(0)