因为实习工作需要制作一个如下图所示的可交互的三维坐标轴,制作这个坐标轴,首先需要创建一些三维图形,接着需要熟悉交互模块和鼠标进行交互,最后将它们**封装成一个
vtkWidget
类**
VTK中一些基础类介绍
下面是VTK中经常会使用到的类的描述。
vtkProp
渲染场景中数据的可视表达(Visible Depictions)是由vtkProp的子类负责。三维空间中渲染对象最常用的vtkProp子类是vtkActor和vtkVolume,其中vtkActor用于表示场景中的几何数据(Geometry Data),vtkVolume表示场景中的体数据(Volumetric Data)。vtkActor2D常用来表示二维空间中的数据。vtkProp的子类负责确定场景中对象的位置、大小和方向信息。控制Prop位置信息的参数依赖于对象是否在渲染场景中,比如一个三维物体或者二维注释,它们的位置信息控制方式是有所区别的。三维的Prop如vtkActor和vtkVolume(vtkActor和vtkVolume都是vtkProp3D的子类,而vtkProp3D继承自vtkProp),既可以直接控制对象的位置、方向和放缩信息,也可以通过一个4×4的变换矩阵来实现。而对于二维注释功能的Props如vtkScalarBarActor,其大小和位置有许多的定义方式,其中包括指定相对于视口的位置、宽度和高度。Prop除了提供对象的位置信息控制之外,Prop内部通常还有两个对象,一个是Mapper对象,负责存放数据和渲染信息,另一个是Property(属性)对象,负责控制颜色、不透明度等参数。
VTK中定义了大量的功能细化的Prop(超过50个),如vtkImageActor(负责图像显示)和vtkPieChartActor(用于创建数组数据的饼图可视表示)。其中的有些Props内部直接包括了控制显示的参数和待渲染数据的索引,因此并不需要额外的Property和Mapper对象。vtkActor的子类vtkFollower可以自动的更新方向信息保持自身始终面向一个特定的相机,这样无论如何旋转渲染场景中的对象,vtkFellower对象都是可见的,适用于三维场景中的广告板(Billboards)或者是文本。vtkActor的子类vtkLodActor可以自动改变自身的几何表示来实现所要求的交互帧率,vtkProp3D的子类vtkLODProp3D则是通过从许多Mapper中进行选择来实现不同的交互性(可以是Volumetric Mapper和GeometricMapper的集合)。vtkAssembly建立了Actor的等级结构以便在整个结构平移、旋转或者放缩时能够更合理的控制变换。
vtkAbstractMapper
许多Props如vtkActor和vtkVolume利用vtkAbstractMapper的子类来保存输入数据的引用以及提供真正的渲染功能。vtkPolyDataMapper是渲染多边形几何数据主要的Mapper类。而对于体数据,VTK提供了多种渲染技术。例如,vtkFixedPointVolumeRayCastMapper用来渲染vtkImageData类型的数据,vtkProjectedTetrahedraMapper则是用来渲染vtkUnstructuredGrid类型的数据。
vtkProperty和vtkVolumeProperty
某些Props采用单独的属性对象来存储控制数据外观显示的参数,这样不同的对象可以轻松的实现外观参数的共享。vtkActor利用vtkProperty对象存储外观(属性)参数,如颜色、不透明度、材质的环境光(Ambient)系数、散射光(Diffuse)系数和反射光(Specular)系数等。而vtkVolume则是采用vtkVolumeProperty对象来获取体对象的绘制参数,如将标量值映射为颜色和不透明度的传输函数(Transfer Function)【译者:也有译成“传递函数”】。另外,一些vtkMapper提供相应的函数设置裁剪面以便显示对象的内部结构。
vtkCamera
vtkCamera存储了场景中的摄像机参数,换言之,如何来“看”渲染场景里的对象,主要参数是摄像机的位置、焦点、和场景中的上方向向量。其他参数可以控制视图变换,如平行投影或者透视投影,图像的尺度或者视角,以及视景体的远近裁剪平面等。
vtkRenderer
组成场景的对象包括Prop,Camara和Light都被集中在一个vtkRenderer对象中。vtkRenderer负责管理场景的渲染过程。一个vtkRenderWindow中可以有多个vtkRenderer对象,而这些vtkRenderer可以渲染在窗口中不同的矩形区域中(视口),甚至可以是覆盖的区域。
vtkRendererWindow
vtkRendererWindow将操作系统与VTK渲染引擎连接到一起。不同平台下的vtkRendererWindow子类负责本地计算机系统中窗口创建和渲染过程的管理。当使用VTK开发应用程序时,只需要使用平台无关的vtkRendererWindow类,程序运行时,系统会自动替换为平台相关的vtkRendererWindow子类。vtkRendererWindow中包含了vtkRenderer的集合,以及控制渲染的参数,如立体显示(Stereo)、反走样、运动模糊(Motion Blur)和焦点深度(FocalDepth)
vtkRenderWindowInteractor
vtkRenderWindowInteractor负责监听鼠标、键盘和时钟消息,并通过VTK中的Command/Observer设计模式进行相应的处理。vtkInteractorStyle监听这些消息并进行处理以完成旋转、拉伸和放缩等运动控制。vtkRenderWindowInteractor自动建立一个默认的3D场景交互器样式(InteractorStyle),当然你也可以选择一个二维图像浏览的交互器样式,或者是创建自定义的交互器样式。
vtkTransform
场景中的许多对象,如Prop、光源Light、照相机Camera等都需要在场景中合理的放置,它们通过vtkTransform参数可以方便的控制对象的位置和方向。vtkTransform能够描述三维空间中的线性坐标变换,其内部表示为一个4×4的齐次变换矩阵。vtkTransform对象初始化为一个单位矩阵,你可以通过管线连接的方式将变换进行组合来完成复杂的变换。管线方式能够确保当其中任一个变换被修改时,其后续的变换都会相应的进行更新
更多基础类的内容可以参考
VTK图像图像开发进阶 第二章的内容
第03章-VTK系统概述(1)_DolingStudio的博客-CSDN博客
VTKUsersGuide 第三章的内容
可视化管线
第03章-VTK系统概述(2)_DolingStudio的博客-CSDN博客
使用预定义的Source对象创建三维图形的数据(或通过Reader对象从文件中读取数据),一个或多个数据对象传入Filter后,经过处理产生新的数据对象,使用Mapper接收数据,并将其装换为可被渲染引擎绘制的可视化表达,再将Mapper绑定到Actor对象上。
一个例子
#include"vtkSmartPointer.h"#include"vtkSphereSource.h"#include"vtkPolyDataMapper.h"#include"vtkActor.h"#include"vtkRenderer.h"#include"vtkRenderWindow.h"#include"vtkRenderWindowInteractor.h"#include"vtkAutoInit.h"VTK_MODULE_INIT(vtkRenderingOpenGL2);VTK_MODULE_INIT(vtkInteractionStyle);VTK_MODULE_INIT(vtkRenderingFreeType);VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);intmain(){// 实例化一个球体的数据源对象
vtkSmartPointer<vtkSphereSource> sphereSource =vtkSmartPointer<vtkSphereSource>::New();// 实例化一个Mapper
vtkSmartPointer<vtkPolyDataMapper> sphereMapper =vtkSmartPointer<vtkPolyDataMapper>::New();// 实例化一个Actor
vtkSmartPointer<vtkActor> sphereActor =vtkSmartPointer<vtkActor>::New();
sphereSource->SetRadius(0.2);// 设置球体的Source图像源半径
sphereMapper->SetInputConnection(sphereSource->GetOutputPort());// 关联Source的输出与Mapper的输入口
sphereActor->SetMapper(sphereMapper);// 将Mapper绑定到Actor上// 实例化一个Renderer对象
vtkSmartPointer<vtkRenderer> renderer =vtkSmartPointer<vtkRenderer>::New();// 实例化一个窗口对象
vtkSmartPointer<vtkRenderWindow> renWin =vtkSmartPointer<vtkRenderWindow>::New();// 实例化一个窗口的交互器对象
vtkSmartPointer<vtkRenderWindowInteractor> iren =vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderer->AddActor(sphereActor);// 一个Renderer包含多个Actor
renWin->AddRenderer(renderer);// 一个RenderWindow包含多个Renderer,可以为不同Renderer设置视口
iren->SetRenderWindow(renWin);// 关联窗口和交互器
renWin->Render();// RenderWindow 开始渲染
iren->Initialize();// vtkRenderWindowInteractor 初始化事件监听
iren->Start();// vtkRenderWindowInteractor开启事件监听循环return0;}
创建坐标轴三维图形
创建XYZ坐标轴
本文封装了一个Arrow类通过指定起点和终点来创建一个箭头,并可以设置箭头上椎体的数量,设置颜色,线宽参数
#include"vtkSmartPointer.h"#include"vtkActor.h"#include"vtkLineSource.h"#include"vtkConeSource.h"namespace myvtk {classArrow{public:Arrow():m_line(nullptr),m_cone1(nullptr),m_cone2(nullptr){}Arrow(double origin[3],double target[3],int coneCount =1,double color[3]=nullptr,float lineWidth = AXISWIDTH){CreateArrow(origin, target, coneCount, color, lineWidth);}/*函数名: CreateArrow
* @param origin[3] 起点坐标
* @param target[3] 终点坐标
* @param coneCount 椎体数量
* @param color 颜色
* @param lineWidth 线宽
*/voidCreateArrow(double origin[3],double target[3],int coneCount =1,double color[3]=nullptr,float lineWidth = AXISWIDTH){{// 直线部分
m_lineSource =vtkSmartPointer<vtkLineSource>::New();
vtkNew<vtkPolyDataMapper> lineMapper;
m_line =vtkSmartPointer<vtkActor>::New();
lineMapper->SetInputConnection(m_lineSource->GetOutputPort());
m_line->SetMapper(lineMapper);
m_lineSource->SetPoint1(origin);
m_lineSource->SetPoint2(target);
m_line->GetProperty()->SetLineWidth(lineWidth);if(color !=nullptr) m_line->GetProperty()->SetColor(color);// 箭头部分double directVec[3]={0};
vtkMath::Subtract(target, origin, directVec);// 箭头1
m_coneCount = coneCount;if(coneCount >=1){
m_coneSource1 =vtkSmartPointer<vtkConeSource>::New();
vtkNew<vtkPolyDataMapper> coneMapper1;
m_cone1 =vtkSmartPointer<vtkActor>::New();
m_coneSource1->SetCenter(target);
m_coneSource1->SetHeight(0.5);
m_coneSource1->SetRadius(0.08);
m_coneSource1->SetResolution(10);
m_coneSource1->SetDirection(directVec);
coneMapper1->SetInputConnection(m_coneSource1->GetOutputPort());
m_cone1->SetMapper(coneMapper1);if(color !=nullptr) m_cone1->GetProperty()->SetColor(color);}// 箭头2if(coneCount >=2){
m_coneSource2 =vtkSmartPointer<vtkConeSource>::New();
vtkNew<vtkPolyDataMapper> coneMapper2;
m_cone2 =vtkSmartPointer<vtkActor>::New();
vtkMath::Subtract(origin, target, directVec);
m_coneSource2->SetCenter(origin);
m_coneSource2->SetHeight(0.5);
m_coneSource2->SetRadius(0.08);
m_coneSource2->SetResolution(10);
m_coneSource2->SetDirection(directVec);
coneMapper2->SetInputConnection(m_coneSource2->GetOutputPort());
m_cone2->SetMapper(coneMapper2);if(color !=nullptr) m_cone2->GetProperty()->SetColor(color);}}}int m_coneCount =0;
vtkSmartPointer<vtkActor> m_line =nullptr;
vtkSmartPointer<vtkActor> m_cone1 =nullptr;
vtkSmartPointer<vtkActor> m_cone2 =nullptr;
vtkSmartPointer<vtkLineSource> m_lineSource =nullptr;
vtkSmartPointer<vtkConeSource> m_coneSource1 =nullptr;
vtkSmartPointer<vtkConeSource> m_coneSource2 =nullptr;};}
创建弯曲箭头
本文封装了一个CurveArrow类通过指定弯曲箭头需要经过的一系列的点来绘制曲线,并可以设置箭头上椎体的数量,设置颜色,线宽参数
其中绘制曲线涉及到了
vtkParametricSpline
及
vtkParametricFunctionSource
类
#include"vtkSmartPointer.h"#include"vtkPoints.h"#include"vtkParametricFunctionSource.h"#include"vtkParametricSpline.h"#include"vtkPolyDataMapper.h"#include"vtkActor.h"#include"vtkProperty.h"#include"vtkConeSource.h"#defineAXISWIDTH3namespace myvtk {classCurveArrow{public:CurveArrow():m_curve(nullptr),m_cone1(nullptr),m_cone2(nullptr){}CurveArrow(vtkSmartPointer<vtkPoints> points,int coneCount =1,double color[3]=nullptr,float lineWidth = AXISWIDTH){CreateArrow(points, coneCount, color, lineWidth);}/*函数名: CreateArrow
* @param points 曲线经过的一组点
* @param coneCount 椎体数量
* @param color 颜色
* @param lineWidth 线宽
*/voidCreateArrow(vtkSmartPointer<vtkPoints> points,int coneCount =1,double color[3]=nullptr,float lineWidth = AXISWIDTH){if(points->GetNumberOfPoints()<2)throw"input points must have at least two points!";// 曲线部分
m_curve =vtkSmartPointer<vtkActor>::New();
m_spline =vtkSmartPointer<vtkParametricSpline>::New();
m_functionSource =vtkSmartPointer<vtkParametricFunctionSource>::New();
m_spline->SetPoints(points);
m_functionSource->SetParametricFunction(m_spline);
m_functionSource->Update();
vtkNew<vtkPolyDataMapper> lineMapper;
lineMapper->SetInputConnection(m_functionSource->GetOutputPort());
m_curve->SetMapper(lineMapper);
m_curve->GetProperty()->SetLineWidth(lineWidth);if(color !=nullptr) m_curve->GetProperty()->SetColor(color);// 箭头部分double directVec[3]={0};int pointsNum = points->GetNumberOfPoints();//double* target = points->GetPoint(pointsNum - 1);double target[3]{0}, origin[3]{0};
points->GetPoint(pointsNum -1, target);
points->GetPoint(pointsNum -2, origin);//double* origin = points->GetPoint(pointsNum - 2);
vtkMath::Subtract(target, origin, directVec);// 箭头1
m_coneCount = coneCount;if(coneCount >=1){
m_coneSource1 =vtkSmartPointer<vtkConeSource>::New();
vtkNew<vtkPolyDataMapper> coneMapper1;
m_cone1 =vtkSmartPointer<vtkActor>::New();
m_coneSource1->SetCenter(target);
m_coneSource1->SetHeight(0.5);
m_coneSource1->SetRadius(0.08);
m_coneSource1->SetResolution(10);
m_coneSource1->SetDirection(directVec);
coneMapper1->SetInputConnection(m_coneSource1->GetOutputPort());
m_cone1->SetMapper(coneMapper1);if(color !=nullptr) m_cone1->GetProperty()->SetColor(color);}// 箭头2if(coneCount >=2){
points->GetPoint(0, target);
points->GetPoint(0, origin);
m_coneSource2 =vtkSmartPointer<vtkConeSource>::New();
vtkNew<vtkPolyDataMapper> coneMapper2;
m_cone2 =vtkSmartPointer<vtkActor>::New();
vtkMath::Subtract(target, origin, directVec);
m_coneSource2->SetCenter(origin);
m_coneSource2->SetHeight(0.5);
m_coneSource2->SetRadius(0.08);
m_coneSource2->SetResolution(10);
m_coneSource2->SetDirection(directVec);
coneMapper2->SetInputConnection(m_coneSource2->GetOutputPort());
m_cone2->SetMapper(coneMapper2);if(color !=nullptr) m_cone2->GetProperty()->SetColor(color);}}int m_coneCount =0;
vtkSmartPointer<vtkActor> m_curve =nullptr;
vtkSmartPointer<vtkActor> m_cone1 =nullptr;
vtkSmartPointer<vtkActor> m_cone2 =nullptr;
vtkSmartPointer<vtkParametricSpline> m_spline =nullptr;
vtkSmartPointer<vtkParametricFunctionSource> m_functionSource =nullptr;
vtkSmartPointer<vtkConeSource> m_coneSource1 =nullptr;
vtkSmartPointer<vtkConeSource> m_coneSource2 =nullptr;};};
创建有厚度的平面
VTK笔记-图形相关-平面-vtkPlaneSource_黑山老妖的博客的博客-CSDN博客_vtk平面图
(4)建立一个标准尺寸的平面,并对其进行着色贴图、拉伸一定的厚度_rexinx的博客-CSDN博客
本文封装了一个Plane,首先通过 vtkPlaneSource 数据源获取一个平面的点及拓扑数据,通过设置 origin, point1, point2 设置平面图形的大小
接着使用
vtkLinearExtrusionFilter
通过SetVector()及SetScaleFactor()方法设置平面的厚度
#include"vtkSmartPointer.h"#include"vtkActor.h"#include"vtkPlaneSource.h"#include"vtkLinearExtrusionFilter.h"#include"vtkTriangleFilter.h"#include"vtkPolyDataMapper.h"#include"vtkProperty.h"namespace myvtk {classPlane{public:Plane():m_plane(nullptr),m_planeSource(nullptr),m_planeLinearExtrusionFilter(nullptr),m_planeTriangleFilter(nullptr){}Plane(double origin[3],double point1[3],double point2[3],double width,double color[3]){CreatePlane(origin, point1, point2, width, color);};/*函数名: CreatePlane
* @param origin 平面矩形的一个顶点
* @param point1 平面矩形的一个与origin相邻的顶点
* @param point2 平面矩形的一个与origin相邻的顶点
* @param width 平面厚度
* @param color 颜色
*/voidCreatePlane(double origin[3],double point1[3],double point2[3],double width,double color[3]){
m_planeSource =vtkSmartPointer<vtkPlaneSource>::New();
vtkNew<vtkPolyDataMapper> PlaneMapper;
m_plane =vtkSmartPointer<vtkActor>::New();
m_planeSource->SetOrigin(origin);
m_planeSource->SetPoint1(point1);
m_planeSource->SetPoint2(point2);
m_planeSource->Update();// Apply linear extrusion
m_planeLinearExtrusionFilter =vtkSmartPointer<vtkLinearExtrusionFilter>::New();
m_planeLinearExtrusionFilter->SetInputConnection(m_planeSource->GetOutputPort());
m_planeLinearExtrusionFilter->SetExtrusionTypeToNormalExtrusion();double vec[3]{1,0,0};
m_planeLinearExtrusionFilter->SetVector(vec);
m_planeLinearExtrusionFilter->SetScaleFactor(width);
m_planeTriangleFilter =vtkSmartPointer<vtkTriangleFilter>::New();
m_planeTriangleFilter->SetInputConnection(m_planeLinearExtrusionFilter->GetOutputPort());
PlaneMapper->SetInputConnection(m_planeTriangleFilter->GetOutputPort());
m_plane->SetMapper(PlaneMapper);
m_plane->GetProperty()->SetColor(color);}public:
vtkSmartPointer<vtkActor> m_plane;
vtkSmartPointer<vtkPlaneSource> m_planeSource;
vtkSmartPointer<vtkLinearExtrusionFilter> m_planeLinearExtrusionFilter;
vtkSmartPointer<vtkTriangleFilter> m_planeTriangleFilter;};};
创建始终跟随镜头的坐标轴文本
第04章-VTK基础(5)_DolingStudio的博客-CSDN博客
使用 vtkVectorText 作为数据源,vtkFollower是vtkActor的子类,通过SetCamera()方法设置后,可以始终朝向指定的镜头
auto textsSource =vtkSmartPointer<vtkVectorText>::New();auto textMapper =vtkSmartPointer<vtkPolyDataMapper>::New();auto texts =vtkSmartPointer<vtkFollower>::New();
textsSource->SetText("x");
textMapper->SetInputConnection(textsSource->GetOutputPort());
texts->SetMapper(textMapper);
texts->SetScale(0.1,0.1,0.1);
vtkMath::MultiplyScalar(point2,1.05);
texts->SetPosition(point2);
texts->GetProperty()->SetColor(color);
texts->SetCamera(renderer->GetActiveCamera());
创建中心圆球
// 绘制中心圆球
m_centerSphereSource =vtkSmartPointer<vtkSphereSource>::New();
vtkNew<vtkPolyDataMapper> sphereMapper;
m_centerSphere =vtkSmartPointer<vtkActor>::New();
m_centerSphereSource->SetRadius(0.2);
m_centerSphereSource->SetCenter(0,0,0);
sphereMapper->SetInputConnection(m_centerSphereSource->GetOutputPort());
m_centerSphere->SetMapper(sphereMapper);
m_centerSphere->GetProperty()->SetColor(0.8,0.8,0.8);// 初始设置较暗的颜色
m_centerSphere->GetProperty()->SetDiffuse(1.0);
m_centerSphere->SetPosition(0,0,0);
Picker通过鼠标拾取三维图形
第04章-VTK基础(4)_DolingStudio的博客-CSDN博客
拾取操作是可视化应用程序中常见的一种功能。拾取主要是用于选择数据和Actor或者获取底层的数据值。在显示位置(以像素为坐标值)中拾取时,就会调用vtkAbstractPicker的Pick()方法。
本文中使用的是
vtkPropPicker
初始化Picker
// 初始化 vtkPropPickerthis->m_picker = vtkPropPicker::New();// 将所有的三维坐标系中的Actor使用 AddPickList() 方法添加到 picker 的列表中TravelAllActors([this](vtkActor* actor){this->m_picker->AddPickList(actor);});// 限定picker只拾取列表中的Actorthis->m_picker->PickFromListOn();
使用Picker获取鼠标位置的Actor
vtkActor*GetPickedActor(double x,double y){
m_picker->Pick(x, y,0,this->Renderer);return m_picker->GetActor();}
其中 x, y 对应的是鼠标在屏幕上的位置,当鼠标触发事件时,可以通过
RenderWindowInteractor
的
GetEventPosition()
方法获取鼠标当前的位置
double X = self->Interactor->GetEventPosition()[0];double Y = self->Interactor->GetEventPosition()[1];
现在我们已经完成了拾取鼠标位置的Actor操作,但由于三维坐标系由许多的 Actor 组合而成,为了知道我们拾取的Actor是什么,当我们按下鼠标左键开始拾取的时候是点击了X轴,还是Y轴,还是Z轴,或者是点击了平面,又或者是点击了拖动旋转的箭头,我们需要对拾取的Actor在对象组中进行识别。
intGetSelectedState(vtkActor* actor){int offset =0;// X Y Z 轴for(int i =0; i < m_axesArrow.size(); i++){if((unsignedlong)(m_axesArrow[i]->m_line.GetPointer())==(unsignedlong)actor ||(m_axesArrow[i]->m_cone1 !=nullptr&&(unsignedlong)(m_axesArrow[i]->m_cone1.GetPointer())==(unsignedlong)actor)){returnSelAxis(i);}}
offset += m_axesArrow.size();// 中心圆球if((unsignedlong)(m_centerSphere.GetPointer())==(unsignedlong)actor){returnSelAxis(offset);}
offset +=1;// planefor(int i =0; i < m_planes.size(); i++){if((unsignedlong)(m_planes[i]->m_plane.GetPointer())==(unsignedlong)actor){returnSelAxis(i + offset);}}
offset += m_planes.size();// rotatefor(int i =0; i < m_rotateArrow.size(); i++){if((unsignedlong)(m_rotateArrow[i]->m_curve.GetPointer())==(unsignedlong)actor ||(m_rotateArrow[i]->m_cone1 !=nullptr&&(unsignedlong)(m_rotateArrow[i]->m_cone1.GetPointer())==(unsignedlong)actor)||(m_rotateArrow[i]->m_cone2 !=nullptr&&(unsignedlong)(m_rotateArrow[i]->m_cone2.GetPointer())==(unsignedlong)actor)){returnSelAxis(i + offset);}}return noAxis;}
依次将拾取的 Actor 地址与
X Y Z 轴
、
中心圆球
、
平面
、
旋转箭头
对象组中各个成员进行比较,确定当前选中了哪个对象组,使用一个
int
类型的变量InteractionState配合
SelAxis
枚举类型进行指示。
坐标变换
齐次坐标
当知道了拾取的对象组之后就可以配合相关变量对操作进行响应
对坐标轴的平移,旋转,缩放操作,都可以通过操作Actor对象的齐次变换矩阵(UserMatrix)来完成,假设当前的坐标点 A(x1, y1, z1) 与移动后的坐标B(x2, y2, z2)
{
x
2
=
k
1
x
1
+
b
1
y
2
=
k
2
y
1
+
b
2
z
2
=
k
3
z
1
+
b
3
\begin{cases} x_2 = k_{1}x_1 + b_{1} \\ y_2 = k_{2}y_1 + b_{2} \\ z_2 = k_{3}z_1 + b_{3} \end{cases}
⎩⎨⎧x2=k1x1+b1y2=k2y1+b2z2=k3z1+b3
转换为矩阵形式
(
x
2
y
2
z
2
1
)
=
[
k
1
0
0
b
1
0
k
2
0
b
2
0
0
k
3
b
3
0
0
0
1
]
(
x
1
y
1
z
1
1
)
\left( \begin{matrix} x_2 \\ y_2 \\ z_2 \\ 1 \end{matrix} \right) = \left[ \begin{matrix} k_{1}&0&0&b_{1} \\ 0&k_{2}&0&b_{2} \\ 0&0&k_{3}&b_{3} \\ 0&0&0&1 \end{matrix} \right] \left( \begin{matrix} x_1 \\ y_1 \\ z_1 \\ 1 \end{matrix} \right)
⎝⎛x2y2z21⎠⎞=⎣⎡k10000k20000k30b1b2b31⎦⎤⎝⎛x1y1z11⎠⎞
A点对应的齐次变换矩阵为
[
1
0
0
0
0
1
0
0
0
0
1
0
0
0
0
1
]
\left[ \begin{matrix} 1&0&0&0 \\ 0&1&0&0 \\ 0&0&1&0 \\ 0&0&0&1 \end{matrix} \right]
⎣⎡1000010000100001⎦⎤
移动到B点对应的齐次变换矩阵为
[
k
1
0
0
b
1
0
k
2
0
b
2
0
0
k
3
b
3
0
0
0
1
]
\left[ \begin{matrix} k_{1}&0&0&b_{1} \\ 0&k_{2}&0&b_{2} \\ 0&0&k_{3}&b_{3} \\ 0&0&0&1 \end{matrix} \right]
⎣⎡k10000k20000k30b1b2b31⎦⎤
vtk中对矩阵的变换可以通过
vtkTransform
类方便的进行
【VTK学习】空间几何变换_JinSu_的博客-CSDN博客_vtk 矩阵乘法
vtkNew<vtkTransform> transform;
transform->PostMultiply();//M=A*M
transform->RotateZ(30);// 绕Z轴旋转30度
transform->Translate(1,0,0);// 平移一个 (1,0,0) 向量
transform->GetMatrix();// 获取一系列操作合成出的齐次变换矩阵
沿指定轴(X,Y,Z)移动
motion_vector
为坐标轴移动到鼠标位置的向量,
m_moveByVec
为指定移动的轴方向,m_userMatrix4x4为预定义的
vtkMatrix4x4
矩阵
double length = vtkMath::Dot(motion_vector, m_moveByVec);double vec[3]={0};
vec[InteractionState]= length;
vtkNew<vtkTransform> transform;
transform->SetMatrix(m_userMatrix4x4);
transform->Translate(vec);
transform->GetMatrix(m_userMatrix4x4);
actor->GetUserMatrix()->DeepCopy(m_userMatrix4x4);
沿xyz所有方向移动
double vec[3]={0};
vec[0]= motion_vector[0];
vec[1]= motion_vector[1];
vec[2]= motion_vector[2];printf("move vec (%f, %f, %f)\n", vec[0], vec[1], vec[2]);
vtkNew<vtkTransform> transform;
transform->SetMatrix(m_userMatrix4x4);
transform->Translate(vec);
transform->GetMatrix(m_userMatrix4x4);
actor->GetUserMatrix()->DeepCopy(m_userMatrix4x4);
沿指定平面移动
double vec[3];
vec[0]= m_moveByVec[0]* motion_vector[0];
vec[1]= m_moveByVec[1]* motion_vector[1];
vec[2]= m_moveByVec[2]* motion_vector[2];printf("move vec (%f, %f, %f)\n", vec[0], vec[1], vec[2]);
vtkNew<vtkTransform> transform;
transform->SetMatrix(m_userMatrix4x4);
transform->Translate(vec);
transform->GetMatrix(m_userMatrix4x4);
actor->GetUserMatrix()->DeepCopy(m_userMatrix4x4);
绕指定轴旋转
oldPoint
为鼠标上次触发事件时的位置,
newPoint
为鼠标当前触发事件时的位置
// 将鼠标位置移动到自身坐标系下,求两次鼠标位置向量在投影平面的夹角
vtkNew<vtkTransform> trans;
trans->SetMatrix(m_userMatrix4x4);double pos_t1[4]{ oldPoint[0], oldPoint[1], oldPoint[2],1};double pos_t2[4]{ newPoint[0], newPoint[1], newPoint[2],1};
vtkNew<vtkMatrix4x4> posture_inv;
vtkMatrix4x4::Invert(m_userMatrix4x4, posture_inv);auto pos_t = posture_inv->MultiplyDoublePoint(pos_t1);double v1[3]={ pos_t[0], pos_t[1], pos_t[2]};
pos_t = posture_inv->MultiplyDoublePoint(pos_t2);double v2[3]={ pos_t[0], pos_t[1], pos_t[2]};double projection1[3], projection2[3];GetPlaneProjection(m_moveByVec, v1, projection1);GetPlaneProjection(m_moveByVec, v2, projection2);
vtkMath::Normalize(projection1);
vtkMath::Normalize(projection2);double axis[3];
vtkMath::Cross(projection1, projection2, axis);double radians =acos(vtkMath::Dot(projection1, projection2));double degrees = vtkMath::DegreesFromRadians(radians);
trans->RotateWXYZ(degrees, axis);
trans->Update();
m_userMatrix4x4->DeepCopy(trans->GetMatrix());
vtkWidget类
参考VTK图形图像开发进阶 8.3 VTK Widget
vtkInteractorObserver.h
vtkInteractorObserver.cxx
vtkAbstractWidget.h
vtkAbstractWidget.cxx
vtkWidgetRepresentation.h
vtkWidgetRepresentation.cxx
VTK中Widget的设计是从VTK 5.0版本开始引入的,最初的Widget是从vtk3DWidget派生出来的,从VTK5.1版本开始,VTK Widget从新进行设计,主要的设计理念是将Widget的消息处理与几何表达实体分离,但还是保留了 vtk3DWidget及其子类。vtkAbstractWidget作为基类,只定义一些公共的API以及实现了“交互/表达实体”分离的设计机制,其中,把从vtkRenderWindowInteractor路由过来的消息(事件)交给vtkAbstractWidget的“交互”部分处理,而Widget的“表达实体”则对应一个vtkProp对象(或者是vtkWidgetRepresentation的子类)。这样做的好处是:事件的处理与Widget的表达实体互不干扰,而且可以实现同类Widget使用不同的表达形式,每个VTKAbstractWidget子类内部包含一个vtkWidgetEventTranslate对象和一个vtkWidgetCallbackMapper对象,vtkWidgetEventTranslate的作用是将外部的VTK事件映射为Widget事件(定义于 vtkWidgetEvenet.h文件中), vtkWidgetCallbackMapper将相应的Widget事件与各个受保护的静态操作函数关联起来。
可以使用vtkWidgetCallbackMapper的SetCallbackMethod函数设置 VTK Event, Widget Event, Method Invocation之间的关联
void vtkWidgetCallbackMapper::SetCallbackMethod(unsignedlong VTKEvent,unsignedlong widgetEvent, vtkAbstractWidget* w, CallbackType f){this->EventTranslator->SetTranslation(VTKEvent, widgetEvent);this->SetCallbackMethod(widgetEvent, w, f);}
示例:
this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent,
vtkWidgetEvent::EndSelect,this, vtkDistanceWidget::EndSelectAction);
本文将 可拖动的三维坐标轴封装为 vtkAbstractWidget 的子类,作为交互与表达实体相分离的部件,还需要一个继承vtkWidgetRepresentation的子类对象,
继承vtkAbstractWidget类
继承vtkAbstractWidget类只需实现 vtkAbstractWidget 的纯虚函数
virtual void CreateDefaultRepresentation() = 0;
即可
void vtkMoveableAxesWidget::CreateDefaultRepresentation(){if(!this->WidgetRep){auto rep = vtkMoveableAxesRepresentation::New();this->SetWidgetRepresentation(rep);}}
此外 vtkWidget 类还应该实现事件到方法的映射,通过在构造函数执行时调用 vtkWidgetCallbackMapper 对象的 SetCallbackMethod 方法关联 VTK Event, Widget Event, Method Invocation
vtkMoveableAxesWidget::vtkMoveableAxesWidget():m_cursorActor(nullptr),WidgetState(vtkMoveableAxesWidget::Start){this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent, vtkWidgetEvent::Select,this, vtkMoveableAxesWidget::SelectAction);this->CallbackMapper->SetCallbackMethod(vtkCommand::MouseMoveEvent, vtkWidgetEvent::Move,this, vtkMoveableAxesWidget::MoveAction);this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent, vtkWidgetEvent::EndSelect,this, vtkMoveableAxesWidget::EndSelectAction);}
接着定义响应事件的回调函数 SelectAction, MoveAction, EndSelectAction 这些回调函数必须定义为静态成员,并且设置一个形参vtkAbstractWidget *w
void vtkMoveableAxesWidget::SelectAction(vtkAbstractWidget *w){
vtkMoveableAxesWidget* self =reinterpret_cast<vtkMoveableAxesWidget*>(w);// Get the event positionint X = self->Interactor->GetEventPosition()[0];int Y = self->Interactor->GetEventPosition()[1];if(!self->CurrentRenderer ||!self->CurrentRenderer->IsInViewport(X, Y)){
self->WidgetState = vtkMoveableAxesWidget::Start;return;}double e[2];
e[0]=static_cast<double>(X);
e[1]=static_cast<double>(Y);// 调用 vtkWidgetRepresentation 子类中定义的交互接口函数
self->WidgetRep->StartWidgetInteraction(e);int interactionState = self->WidgetRep->GetInteractionState();if(interactionState <0){return;}// 更新鼠标指针
self->UpdateCursorShape(interactionState);// 设置组件状态为 激活
self->WidgetState = vtkMoveableAxesWidget::Active;// 阻断事件传递,发送StartInteractionEvent事件,渲染
self->GrabFocus(self->EventCallbackCommand);
self->StartInteraction();
self->EventCallbackCommand->SetAbortFlag(1);
self->InvokeEvent(vtkCommand::StartInteractionEvent,nullptr);
self->Render();}void vtkMoveableAxesWidget::MoveAction(vtkAbstractWidget* w){
vtkMoveableAxesWidget* self =reinterpret_cast<vtkMoveableAxesWidget*>(w);double X = self->Interactor->GetEventPosition()[0];double Y = self->Interactor->GetEventPosition()[1];// 鼠标移动时高亮功能
self->MoveHighLight(X, Y);// 判断组件是否激活if(self->WidgetState == vtkMoveableAxesWidget::Start){return;}// 开始交互,调用 vtkWidgetRepresentation 子类中定义的交互接口函数double e[2];
e[0]=static_cast<double>(X);
e[1]=static_cast<double>(Y);
self->WidgetRep->WidgetInteraction(e);// 阻断事件传递,发送InteractionEvent事件,渲染
self->EventCallbackCommand->SetAbortFlag(1);
self->InvokeEvent(vtkCommand::InteractionEvent,nullptr);
self->Render();}void vtkMoveableAxesWidget::EndSelectAction(vtkAbstractWidget* w){
vtkMoveableAxesWidget* self =reinterpret_cast<vtkMoveableAxesWidget*>(w);
self->WidgetState = vtkMoveableAxesWidget::Start;// 调用 vtkWidgetRepresentation 子类中定义的交互接口函数
self->WidgetRep->EndWidgetInteraction(nullptr);// 更新鼠标指针
self->UpdateCursorShape(-1);// 阻断事件传递,发送InteractionEvent事件,渲染
self->ReleaseFocus();
self->EventCallbackCommand->SetAbortFlag(1);
self->EndInteraction();
self->InvokeEvent(vtkCommand::EndInteractionEvent,nullptr);
self->Render();}
继承vtkWidgetRepresentation类
继承
vtkWidgetRepresentation
首先要实现它的纯虚函数
virtual void BuildRepresentation() = 0;
void vtkMoveableAxesRepresentation::BuildRepresentation(){// 刷新Repif(!this->Renderer ||!this->Renderer->GetRenderWindow()){return;}// actor、source重新计算if(this->GetMTime()>this->BuildTime
||this->Renderer->GetRenderWindow()->GetMTime()>this->BuildTime){}// 重建和renderwindow更改时调整控制柄的大小if(this->GetMTime()>this->BuildTime
||this->Renderer->GetRenderWindow()->GetMTime()>this->BuildTime){this->SizeHandles();this->BuildTime.Modified();}}
根据
vtkWidgetRepresentation.h
中所述
为了让 vtkWidgetRepresentation 子类表现得像个 vtkProp对象子类需要实现以下方法
/**
* Methods to make this class behave as a vtkProp. They are repeated here (from the
* vtkProp superclass) as a reminder to the widget implementor. Failure to implement
* these methods properly may result in the representation not appearing in the scene
* (i.e., not implementing the Render() methods properly) or leaking graphics resources
* (i.e., not implementing ReleaseGraphicsResources() properly).
*/double*GetBounds()override{returnnullptr;}voidShallowCopy(vtkProp *prop)override;voidGetActors(vtkPropCollection *)override{}voidGetActors2D(vtkPropCollection *)override{}voidGetVolumes(vtkPropCollection *)override{}voidReleaseGraphicsResources(vtkWindow *)override{}intRenderOverlay(vtkViewport *vtkNotUsed(viewport))override{return0;}intRenderOpaqueGeometry(vtkViewport *vtkNotUsed(viewport))override{return0;}intRenderTranslucentPolygonalGeometry(vtkViewport *vtkNotUsed(viewport))override{return0;}intRenderVolumetricGeometry(vtkViewport *vtkNotUsed(viewport))override{return0;}intHasTranslucentPolygonalGeometry()override{return0;}
本文由于只使用到了Actor对象,根据需要实现了如下方法
double*GetBounds()VTK_SIZEHINT(6)override;voidGetActors(vtkPropCollection* pc)override;voidReleaseGraphicsResources(vtkWindow*)override;intRenderOpaqueGeometry(vtkViewport*)override;intRenderTranslucentPolygonalGeometry(vtkViewport*)override;
vtkTypeBool HasTranslucentPolygonalGeometry()override;
具体实现可以查阅VTK提供的完整的源码,部分示例如下
void vtkMoveableAxesRepresentation::GetActors(vtkPropCollection* pc){
pc = vtkPropCollection::New();TravelAllActors([&pc](vtkActor* actor){
pc->AddItem(actor);});}
可以使用TravelAllActors传入一个匿名函数的方式遍历所有Actor对象
#include<functional>classvtkActor;using travelActorsCallback = std::function<void(vtkActor*)>;voidTravelAllActors(travelActorsCallback);void vtkMoveableAxesRepresentation::TravelAllActors(travelActorsCallback callback){
vtkActor* actor;// x,y,z轴for(int i =0; i < m_axesArrow.size(); i++){
actor = m_axesArrow[i]->m_line.GetPointer();callback(actor);
actor = m_axesArrow[i]->m_cone1.GetPointer();callback(actor);}// 坐标轴文本for(int i =0; i <this->m_texts.size(); i++){
actor =this->m_texts[i];callback(actor);}// 拓展部分
m_extendActors->InitTraversal();while((actor = m_extendActors->GetNextActor())!=nullptr){callback(actor);}}
以下是推荐的vtkWidgetRepresentation 子类与 vtkWidget交互的接口函数
/**
* The following is a suggested API for widget representations. These methods
* define the communication between the widget and its representation. These
* methods are only suggestions because widgets take on so many different
* forms that a universal API is not deemed practical. However, these methods
* should be implemented when possible to insure that the VTK widget hierarchy
* remains self-consistent.
* <pre>
* PlaceWidget() - given a bounding box (xmin,xmax,ymin,ymax,zmin,zmax), place
* the widget inside of it. The current orientation of the widget
* is preserved, only scaling and translation is performed.
* StartWidgetInteraction() - generally corresponds to a initial event (e.g.,
* mouse down) that starts the interaction process
* with the widget.
* WidgetInteraction() - invoked when an event causes the widget to change
* appearance.
* EndWidgetInteraction() - generally corresponds to a final event (e.g., mouse up)
* and completes the interaction sequence.
* ComputeInteractionState() - given (X,Y) display coordinates in a renderer, with a
* possible flag that modifies the computation,
* what is the state of the widget?
* GetInteractionState() - return the current state of the widget. Note that the
* value of "0" typically refers to "outside". The
* interaction state is strictly a function of the
* representation, and the widget/represent must agree
* on what they mean.
* Highlight() - turn on or off any highlights associated with the widget.
* Highlights are generally turned on when the widget is selected.
* </pre>
* Note that subclasses may ignore some of these methods and implement their own
* depending on the specifics of the widget.
*/virtualvoidPlaceWidget(double*vtkNotUsed(bounds[6])){}virtualvoidStartWidgetInteraction(double eventPos[2]){(void)eventPos;}virtualvoidWidgetInteraction(double newEventPos[2]){(void)newEventPos;}virtualvoidEndWidgetInteraction(double newEventPos[2]){(void)newEventPos;}virtualintComputeInteractionState(int X,int Y,int modify=0);virtualintGetInteractionState(){returnthis->InteractionState;}virtualvoidHighlight(intvtkNotUsed(highlightOn)){}
与picker相关的接口函数
/**
* Register internal Pickers in the Picking Manager.
* Must be reimplemented by concrete widget representations to register
* their pickers.
*/virtualvoidRegisterPickers();/**
* Unregister internal pickers from the Picking Manager.
*/virtualvoidUnRegisterPickers();/**
* Update the pickers registered in the Picking Manager when pickers are
* modified.
*/virtualvoidPickersModified();
明确了满足 vtkAbstractWidget 与 vtkWidgetRepresentation 类需要实现的接口,以及它们之间交互需要的接口,完整代码见源码。
使用
#include"vtkRenderer.h"#include"vtkRenderWindow.h"#include"vtkRenderWindowInteractor.h"#include"vtkAutoInit.h"VTK_MODULE_INIT(vtkRenderingOpenGL2);VTK_MODULE_INIT(vtkInteractionStyle);VTK_MODULE_INIT(vtkRenderingFreeType);VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);#include"vtkMoveableAxesWidget.h"intmain(){
vtkNew<vtkRenderer> renderer;
vtkNew<vtkRenderWindow> renWin;
vtkNew<vtkRenderWindowInteractor> renWinI;
renderer->GetActiveCamera()->SetPosition(8,8,8);
renWin->AddRenderer(renderer);
renWinI->SetRenderWindow(renWin);
renWinI->Initialize();// Widget的创建需要在 renWinI->SetRenderWindow(renWin) 和 renWinI->Initialize() 之后// 创建可移动坐标轴
vtkMoveableAxesWidget* axes = vtkMoveableAxesWidget::New();
axes->SetInteractor(renWinI);
axes->On();
renWin->Render();
renWinI->Start();return EXIT_SUCCESS;}
其他
Qt VS Tools插件安装
https://blog.51cto.com/u_15707179/5447267
VS配置Qt开发环境
https://www.cnblogs.com/szitcast/p/15733691.html
解决:Qt项目中出现红色波浪线错误提示
解决:vs打开.ui文件出现Qt Designer后闪退
VTK源码编译
Visual Studio C++项目使用第三方库
https://blog.csdn.net/u012611644/article/details/81105539
https://blog.csdn.net/yangfchn/article/details/85162934
控制台显示调试信息
工程属性->链接器->系统->子系统 设置为 控制台
参考
VTK官方网址
VTK官方文档
VTK源码
VTK可移动三维坐标轴 vtkMovableAxesWidget
【VTK】可拖动的坐标轴MovableAxesWidget
第03章-VTK系统概述(1)_DolingStudio的博客-CSDN博客
VTK笔记-图形相关-平面-vtkPlaneSource_黑山老妖的博客的博客-CSDN博客_vtk平面图
(4)建立一个标准尺寸的平面,并对其进行着色贴图、拉伸一定的厚度_rexinx的博客-CSDN博客
第04章-VTK基础(4)_DolingStudio的博客-CSDN博客
【VTK学习】空间几何变换_JinSu_的博客-CSDN博客_vtk 矩阵乘法
第04章-VTK基础(5)_DolingStudio的博客-CSDN博客
《VTK图形图像开发进阶》
《VTKUsersGuide》
版权归原作者 虚幻交界 所有, 如有侵权,请联系我们删除。