0


VTK可交互三维坐标轴

因为实习工作需要制作一个如下图所示的可交互的三维坐标轴,制作这个坐标轴,首先需要创建一些三维图形,接着需要熟悉交互模块和鼠标进行交互,最后将它们**封装成一个

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​=k1​x1​+b1​y2​=k2​y1​+b2​z2​=k3​z1​+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) 
   
  
 ⎝⎛​x2​y2​z2​1​⎠⎞​=⎣⎡​k1​000​0k2​00​00k3​0​b1​b2​b3​1​⎦⎤​⎝⎛​x1​y1​z1​1​⎠⎞​

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] 
   
  
 ⎣⎡​1000​0100​0010​0001​⎦⎤​

移动到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] 
   
  
 ⎣⎡​k1​000​0k2​00​00k3​0​b1​b2​b3​1​⎦⎤​

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源码编译

https://www.bilibili.com/video/BV1S5411D7uj?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=c9263c84459a6eabff37b3b1b85328e6

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》

标签: VTK c++

本文转载自: https://blog.csdn.net/Zz_er/article/details/127098449
版权归原作者 虚幻交界 所有, 如有侵权,请联系我们删除。

“VTK可交互三维坐标轴”的评论:

还没有评论