0


VTK交互-vtkBoxWidget2

VTK交互Widget

widget包含两个重要的组成部分:Interaction和Representation.
Interaction是一些名叫vtkWidget的类(比如vtkBoxWidget2)。它包含了交互的所有选项和事件处理。
Representation是显示并与之交互的一类对象,以名叫vtk
Representation.

在窗口中实现自己的小部件,如果有交互,就要写自己的Representation和Widget。可以参考vtk已有的widget。
vtk已经实现的Widget如下图
在这里插入图片描述

在这里插入图片描述

由上图可知Widget基类是vtkAbstracWidget,它定义小部件/小部件表示的API。vtkAbstractWidget定义了一个API并实现了所有使用交互/表示设计的widget所共有的方法。在这个设计中,术语交互是指小部件中执行事件处理的部分,而表示则对应于用来表示小部件的vtkProp(或子类vtkWidgetRepresentation)。vtkAbstractWidget还实现了一些所有子类共有的方法。

vtkAbstractWidget 提供对 vtkWidgetEventTranslator 的访问。 此类负责将 VTK 事件(在 vtkCommand.h 中定义)转换为小部件事件(在 vtkWidgetEvent.h 中定义)。 可以操作此类,以便将不同的 VTK 事件映射到小部件事件,从而允许修改事件绑定。 vtkAbstractWidget 的每个子类都定义了它响应的事件。

vtkAbstractWidget 派生自 vtkInteractorObserver。vtkInteractorObserver是一个抽象的超类,用于观察由vtkRenderWindowInteractor调用的事件的子类。这些子类通常是像3D部件这样的东西;与场景中的演员交互的对象,或交互地探测场景的信息。

vtkInteractorObserver定义了SetInteractor()方法,并启用和禁用了vtkInteractorObserver对事件的处理。使用EnabledOn()或SetEnabled(1)方法来打开交互器观察器,使用EnabledOff()或SetEnabled(0)方法来关闭交互器。初始值为0。

为了支持对对象的交互式操作,这个类(和子类)调用了StartInteractionEvent、InteractionEvent和EndInteractionEvent等事件。当vtkInteractorObserver进入一个需要快速反应的状态时,这些事件被调用:鼠标运动等。例如,这些事件可以用来设置所需的更新帧率(StartInteractionEvent),操作数据或更新管道(InteractionEvent),并将所需的帧率设置为正常值(EndInteractionEvent)。另外两个事件,EnableEvent和DisableEvent,在交互器观察者被启用或禁用时被调用。

所以窗口小部件Widget类本质就是一个观察者监听来自Interactor的事件
在这里插入图片描述

由上图可知

vtkWidgetRepresentation

的基类是

vtkProp

,我们知道

vtkActor2D

vtkActor

的基类(间接基类)是

vtkProp

。vtkWidgetRepresentation是抽象类定义了小部件和小部件表示类之间的接口用于定义 API 并部分实现不同类型的小部件的表示。小部件表示(即 vtkWidgetRepresentation 的子类)是一种 vtkProp这意味着它们可以像任何其他 vtkActor 一样与嵌入场景中的 vtkRenderer 端相关联。 但是,vtkWidgetRepresentation 还定义了一个 API,使其能够与子类 vtkAbstractWidget 配对,这意味着它可以由小部件驱动,在小部件响应注册事件时用于表示小部件。

此处定义的 API 应被视为实现小部件和小部件表示的指南。 小部件行为很复杂,表示响应已注册小部件事件的方式也很复杂,因此 API 可能因小部件而异,以反映这种复杂性。

创建交互的步骤

虽然每个Widget都提供了不同的功能以及不同的API,但是,Widget的创建以及使用基本都是类似的。创建Widget的一般步骤如下:

  1. 实例化Widget;
  2. 指定渲染窗口交互器。Widget可以通过它来监听用户事件。
  3. 必要时使用观察者/命令模式创建回调函数。与widget交互时,它会调用一些通用的VTK事件(94个事件列表),如StartInteractionEvent、InteractionEvent、EndInteractionEvent。用户通过监听这些事件并作出响应,从而可以更新数据、可视化参数或者应用程序的用户图形界面。
  4. 创建合适的几何表达实体。并用SetRepresentation()函数把他与Widget关联起来,或者使用Widget默认的几何表达实体。
  5. 最后,必须激活Widget,使其在渲染场景中显示。默认情况下,按键用于激活Widget,使其可以再场景中可见。 正如之前我们讨论的那样,如果对Widget默认的事件绑定不满意,需要根据自己习惯定义的事件绑定,可以使用VTKWidgetEventTranslator类。同样,也可以使用该类的RemoveTranslation()函数取消已经绑定的事件,具体操作如下:translator->RemoveTranslation(vtkCommand::LeftButtonPressEvent);translator->RemoveTranslation(vtkCommand::LeftButtonReleaseEvent);

如何使用vtkBoxWidget

设置Widget位置

voidPlaceWidget(double bounds[6]) override;

获取Box的polyData

voidGetPolyData(vtkPolyData* pd);

polydata由6个四边形面和15个点组成。前八个点定义了八个角点顶点;接下来的六个定义了-x、+x、-y、+y、-z、+z面点;最后一点(15个点中的第15个点)定义了六面体的中心(参考上面我标注的图)。当调用InteractionEvent或EndInteractionEvent事件时,这些点值保证是最新的。用户提供vtkPolyData并向其中添加点和单元格。

Box的vtkTransform

virtual voidGetTransform(vtkTransform* t);  
virtual voidSetTransform(vtkTransform* t);

SetTransform设置获取表示长方体变换的线性变换矩阵信息。注意:转换与最初调用PlaceWidget的位置有关。此方法修改提供的变换。变换可用于控制vtkProp3D的位置,以及其他变换操作(例如vtkTransformorMPolyData)

获取Box的plane

voidGetPlanes(vtkPlanes* planes);

GetPlanes方法可以获取描述由box小部件定义的隐式函数的平面。用户必须提供类vtkPlanes的实例。需要注意的是:vtkPlanes是vtkImplicitFunction的一个子类,这意味着各种过滤器都可以使用它来执行数据的剪裁、剪切和选择(可以反转平面法线的方向以启用InsideOut标志)

句柄属性

  • HandleProperty是句柄属性(小球就是句柄),可以设置选定和正常时控制柄的属性。
  • SelectedHandleProperty是当前被选中的句柄属性。
  • FaceProperty是面属性(长方体的面),可以设置选定面和法线时面的特性。
  • SelectedFaceProperty是当前被选中的面属性。
  • OutlineProperty是轮廓属性(外边框的轮廓),可以设置被选中的和正常时轮廓的属性。
  • SelectedOutlineProperty是当前被选中的轮廓属性。
vtkGetObjectMacro(HandleProperty, vtkProperty);vtkGetObjectMacro(SelectedHandleProperty, vtkProperty);vtkGetObjectMacro(FaceProperty, vtkProperty);vtkGetObjectMacro(SelectedFaceProperty, vtkProperty);vtkGetObjectMacro(OutlineProperty, vtkProperty);vtkGetObjectMacro(SelectedOutlineProperty, vtkProperty);

控制句柄(表面上的小球体)的显示

voidHandlesOn();voidHandlesOff();

控制功能变量

TranslationEnabled/ScalingEnabled/RotationEnabled 用来控制小部件的行为,可以启用和禁用平移、旋转和缩放的行为。

vtkSetMacro(TranslationEnabled, vtkTypeBool);vtkGetMacro(TranslationEnabled, vtkTypeBool);vtkBooleanMacro(TranslationEnabled, vtkTypeBool);vtkSetMacro(ScalingEnabled, vtkTypeBool);vtkGetMacro(ScalingEnabled, vtkTypeBool);vtkBooleanMacro(ScalingEnabled, vtkTypeBool);vtkSetMacro(RotationEnabled, vtkTypeBool);vtkGetMacro(RotationEnabled, vtkTypeBool);vtkBooleanMacro(RotationEnabled, vtkTypeBool);

vtkBoxWidget2 相关源码分析

vtkBoxWidget2构造函数

构造函数主要定义了widget的事件,并将VTK事件翻译成Widget事件,从下面的代码可以看出使用

SetCallbackMethod

将VTK消息与实际的操作函数联系起来

SetCallbackMethod

调用了

vtkWidgetEventTranslator::SetTranslation()

方法将VTK事件翻译成Widget事件。这种机制有点类似Qt里面的信号槽连接。

// Define widget events
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent, vtkEvent::NoModifier,0,0, nullptr, vtkWidgetEvent::Select, this, vtkBoxWidget2::SelectAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonReleaseEvent, vtkEvent::NoModifier,0,0, nullptr, vtkWidgetEvent::EndSelect, this, vtkBoxWidget2::EndSelectAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::MiddleButtonPressEvent,
    vtkWidgetEvent::Translate, this, vtkBoxWidget2::TranslateAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::MiddleButtonReleaseEvent,
    vtkWidgetEvent::EndTranslate, this, vtkBoxWidget2::EndSelectAction);
  this->CallbackMapper->SetCallbackMethod(vtkCommand::LeftButtonPressEvent,
    vtkEvent::ControlModifier,0,0, nullptr, vtkWidgetEvent::Translate, this,
    vtkBoxWidget2::TranslateAction);

事件处理函数

// These methods handle eventsstaticvoidSelectAction(vtkAbstractWidget*);staticvoidEndSelectAction(vtkAbstractWidget*);staticvoidTranslateAction(vtkAbstractWidget*);staticvoidScaleAction(vtkAbstractWidget*);staticvoidMoveAction(vtkAbstractWidget*);staticvoidSelectAction3D(vtkAbstractWidget*);staticvoidEndSelectAction3D(vtkAbstractWidget*);staticvoidMoveAction3D(vtkAbstractWidget*);staticvoidStepAction3D(vtkAbstractWidget*);

vtkBoxRepresentation 相关代码分析

BoxWidget中15个点的顺序如下图所示,便于后面代码分析。
在这里插入图片描述

vtkBoxRepresentation的构造函数中主要初始化连接渲染Box的管道。如上图可知Box中有6个面,15条线段,15个点。

vtkBoxRepresentation中相关变量介绍如下

//交互box的状态控制状态定义enum{
    Outside =0,
    MoveF0,
    MoveF1,
    MoveF2,
    MoveF3,
    MoveF4,
    MoveF5,
    Translating,
    Rotating,
    Scaling
  };// 构建6个面变量
vtkActor* HexActor;
vtkPolyDataMapper* HexMapper;
vtkPolyData* HexPolyData;
vtkPoints* Points;// 8 corners; 6 faces; 1 centerdouble N[6][3];// 6个面的方向。存储顺序是(-x,+x,-y,+y,-z,+z)//被选中的六面体其中的一个面的相关变量
vtkActor* HexFace;
vtkPolyDataMapper* HexFaceMapper;
vtkPolyData* HexFacePolyData;// Handle 15个点的相关变量
vtkActor** Handle;
vtkPolyDataMapper** HandleMapper;
vtkSphereSource** HandleGeometry;// 单元拾取相关,也是交互重要环节之一。
vtkCellPicker* HandlePicker;
vtkCellPicker* HexPicker;
vtkActor* CurrentHandle;int CurrentHexFace;
vtkCellPicker* LastPicker;

vtkBoxRepresentation中相关API

//获取包围盒double*GetBounds()VTK_SIZEHINT(6) override;//窗口交互voidWidgetInteraction(double e[2]) override;//复杂的交互voidComplexInteraction(vtkRenderWindowInteractor* iren, vtkAbstractWidget* widget,unsignedlong event,void* calldata) override;//计算交互状态intComputeComplexInteractionState(vtkRenderWindowInteractor* iren, vtkAbstractWidget* widget,unsignedlong event,void* calldata,int modify =0) override;//结束交互状态voidEndComplexInteraction(vtkRenderWindowInteractor* iren, vtkAbstractWidget* widget,unsignedlong event,void* calldata) override;// 帮助函数,也就是具体实现box平移,旋转,缩放的具体函数实现
virtual voidTranslate(constdouble* p1,constdouble* p2);
virtual voidScale(constdouble* p1,constdouble* p2,int X,int Y);
virtual voidRotate(int X,int Y,constdouble* p1,constdouble* p2,constdouble* vpn);voidMovePlusXFace(constdouble* p1,constdouble* p2);voidMoveMinusXFace(constdouble* p1,constdouble* p2);voidMovePlusYFace(constdouble* p1,constdouble* p2);voidMoveMinusYFace(constdouble* p1,constdouble* p2);voidMovePlusZFace(constdouble* p1,constdouble* p2);voidMoveMinusZFace(constdouble* p1,constdouble* p2);voidUpdatePose(constdouble* p1,constdouble* d1,constdouble* p2,constdouble* d2);voidMoveFace(constdouble* p1,constdouble* p2,constdouble* dir,double* x1,double* x2,double* x3,double* x4,double* x5);

Rotate 实现分析

void vtkBoxRepresentation::Rotate(int X,int Y,constdouble* p1,constdouble* p2,constdouble* vpn){double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);double* center = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(3*14);double v[3];// vector of motiondouble axis[3];// axis of rotationdouble theta;// rotation angleint i;

  v[0]= p2[0]- p1[0];
  v[1]= p2[1]- p1[1];
  v[2]= p2[2]- p1[2];// Create axis of rotation and angle of rotation
  vtkMath::Cross(vpn, v, axis);if(vtkMath::Normalize(axis)==0.0){return;}//根据移动的L2距离和屏幕的尺寸L2比例来确定旋转的角度constint* size = this->Renderer->GetSize();double l2 =(X - this->LastEventPosition[0])*(X - this->LastEventPosition[0])+(Y - this->LastEventPosition[1])*(Y - this->LastEventPosition[1]);
  theta =360.0*sqrt(l2 /(size[0]* size[0]+ size[1]* size[1]));// 旋转变换,VTK Transform的标准操作
  this->Transform->Identity();
  this->Transform->Translate(center[0], center[1], center[2]);
  this->Transform->RotateWXYZ(theta, axis);
  this->Transform->Translate(-center[0],-center[1],-center[2]);// 设置拐角点// 首先把Points中的点进行transform//得到新的点,并跟新
  vtkPoints* newPts = vtkPoints::New(VTK_DOUBLE);
  this->Transform->TransformPoints(this->Points, newPts);for(i =0; i <8; i++, pts +=3){
    this->Points->SetPoint(i, newPts->GetPoint(i));}
  newPts->Delete();//根据这8个点去计算其他要设置的点,并更新
  this->PositionHandles();}

vtkBoxWidget 例子

  • BoxWidget2.cxx
#include<vtkActor.h>#include<vtkBoxRepresentation.h>#include<vtkBoxWidget2.h>#include<vtkCommand.h>#include<vtkConeSource.h>#include<vtkInteractorStyleTrackballCamera.h>#include<vtkNamedColors.h>#include<vtkNew.h>#include<vtkPolyDataMapper.h>#include<vtkProperty.h>#include<vtkRenderWindow.h>#include<vtkRenderWindowInteractor.h>#include<vtkRenderer.h>#include<vtkTransform.h>#include<vtkOrientationMarkerWidget.h>#include<vtkAxesActor.h>#include<vtkPlanes.h>

namespace {
class vtkBoxCallback : public vtkCommand
{
public:static vtkBoxCallback*New(){return new vtkBoxCallback;}

  vtkSmartPointer<vtkActor> m_actor;voidSetActor(vtkSmartPointer<vtkActor> actor){
    m_actor = actor;}

  virtual voidExecute(vtkObject* caller,unsignedlong,void*){
    vtkSmartPointer<vtkBoxWidget2> boxWidget =
        dynamic_cast<vtkBoxWidget2*>(caller);

    vtkNew<vtkTransform> t;

    dynamic_cast<vtkBoxRepresentation*>(boxWidget->GetRepresentation())->GetTransform(t);
    this->m_actor->SetUserTransform(t);}vtkBoxCallback(){}};}// namespaceintmain(intvtkNotUsed(argc),char*vtkNotUsed(argv)[]){
  vtkNew<vtkNamedColors> colors;// 显示坐标系的vtk组件
  vtkSmartPointer<vtkAxesActor> axes_actor = vtkSmartPointer<vtkAxesActor>::New();
  axes_actor->SetPosition(0,0,0);
  axes_actor->SetTotalLength(2,2,2);
  axes_actor->SetShaftType(0);
  axes_actor->SetCylinderRadius(0.02);

  vtkNew<vtkConeSource> coneSource;
  coneSource->SetHeight(1.5);

  vtkNew<vtkPolyDataMapper> mapper;
  mapper->SetInputConnection(coneSource->GetOutputPort());

  vtkNew<vtkActor> actor;
  actor->SetMapper(mapper);
  actor->GetProperty()->SetColor(colors->GetColor3d("BurlyWood").GetData());

  vtkNew<vtkRenderer> renderer;
  renderer->AddActor(actor);//renderer->SetBackground(colors->GetColor3d("Blue").GetData());
  renderer->SetBackground(colors->GetColor3d("Black").GetData());
  renderer->ResetCamera();// Reposition camera so the whole scene is visible

  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->AddRenderer(renderer);
  renderWindow->SetWindowName("BoxWidget2");

  vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  renderWindowInteractor->SetRenderWindow(renderWindow);// Use the "trackball camera" interactor style, rather than the default// "joystick camera"
  vtkNew<vtkInteractorStyleTrackballCamera> style;
  renderWindowInteractor->SetInteractorStyle(style);

  vtkNew<vtkBoxWidget2> boxWidget;
  boxWidget->SetInteractor(renderWindowInteractor);
  boxWidget->GetRepresentation()->SetPlaceFactor(1);// Default is 0.5
  boxWidget->GetRepresentation()->PlaceWidget(actor->GetBounds());
  vtkNew<vtkPlanes> planes;
  vtkBoxRepresentation::SafeDownCast(boxWidget->GetRepresentation())->GetPlanes(planes);// Set up a callback for the interactor to call so we can manipulate the actor
  vtkNew<vtkBoxCallback> boxCallback;
  boxCallback->SetActor(actor);
  boxWidget->AddObserver(vtkCommand::InteractionEvent, boxCallback);

  boxWidget->On();// 控制坐标系,使之随视角共同变化 
  vtkSmartPointer<vtkOrientationMarkerWidget> widget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
  widget->SetOrientationMarker(axes_actor);
  widget->SetInteractor(renderWindowInteractor);
  widget->On();

  renderWindow->Render();
  renderWindowInteractor->Start();return EXIT_SUCCESS;}
  • CMakeLists.txt
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)project(BoxWidget2)set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../../../bin)set(VTK_DIR S:/VTK-build)find_package(ITK REQUIRED)IF(ITK_FOUND)INCLUDE(${ITK_USE_FILE})ELSE(ITK_FOUND)MESSAGE(FATAL_ERROR 
            "ITK not found. Please set ITK_DIR.")ENDIF(ITK_FOUND)#message("ITK_USE_FILE BoxWidget2: ${ITK_USE_FILE}")find_package(VTK REQUIRED)if(NOT VTK_FOUND)message("Skipping BoxWidget2: ${VTK_NOT_FOUND_MESSAGE}")return()endif()message(STATUS "VTK_VERSION: ${VTK_VERSION}")if(VTK_VERSION VERSION_LESS "8.90.0")#oldsysteminclude(${VTK_USE_FILE})add_executable(BoxWidget2 MACOSX_BUNDLE BoxWidget2.cxx )target_link_libraries(BoxWidget2 PRIVATE ${VTK_LIBRARIES})else()#Prevent a "command line is too long"failure in Windows.set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")add_executable(BoxWidget2 MACOSX_BUNDLE BoxWidget2.cxx )target_link_libraries(BoxWidget2 PRIVATE ${VTK_LIBRARIES} ${ITK_LIBRARIES})#vtk_module_autoinit is neededvtk_module_autoinit(
    TARGETS BoxWidget2
    MODULES ${VTK_LIBRARIES})endif()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BEuKXMgz-1683711832364)(https://note.youdao.com/yws/res/32503/WEBRESOURCE32c330512a8e819ec3ebb13e00ab260a)]

标签: 交互 VTK BoxWidget

本文转载自: https://blog.csdn.net/BXD1314/article/details/130606380
版权归原作者 @左左@右右 所有, 如有侵权,请联系我们删除。

“VTK交互-vtkBoxWidget2”的评论:

还没有评论