当前位置: 首页 > news >正文

VTK知识学习(54)- 交互与Widget(五)

1、前言

        选择拾取是人机交互过程的一个重要功能。在玩3D游戏时,场景中可能会存在多个角色,有时需要使用鼠标来选择所要控制的角色,这就需要用到拾取功能。另外,在某些三维图形的编辑软件中,经常需要编辑其中的一个点、一个面片或者一个局部区域,这也需要通过拾取功能来完成。VTK中定义了多个拾取功能的类,下图显示了这些拾取类的继承关系。VTK中的所有拾取类都继承自vkAbstractPicker 类,利用这些类可以实现许多复杂的功能。

2、点拾取

1)概述

        从图上可以看出,完成点拾取功能类是vtkPointPicker。VTK中的消息是通过vtkRenderWindowInteractor 类来处理的,在类 vtkRenderWindowInteractor 中,有如下函数:

public virtual void SetPicker(vtkAbstractPicker arg0);

        该函数用来设置具体的 vkAbstractPicker 对象执行对应的拾取操作,对于点拾取就是设置vtkPointPicker 对象。
        vtkRenderWindowInteractor 内部定义了一个 vtkInteractorStyle 对象。vtkInteractorStyle 类是一个虚基类,其子类定义了多种鼠标和键盘消息的处理方法,在实现拾取操作时,需要定制相应的鼠标消息处理函数。比如拾取某个点时,应该响应鼠标的左键按下消息,并在响应该消息的函数中根据鼠标的当前窗口坐标来完成拾取操作。

2)代码
private void TestPointPicker(){vtkSphereSource sphereSource = vtkSphereSource.New();sphereSource.Update();//create a mapper and actorvtkPolyDataMapper mapper = vtkPolyDataMapper.New();mapper.SetInputConnection(sphereSource.GetOutputPort());vtkActor actor = vtkActor.New();actor.SetMapper(mapper);//creat a renderer,render window,and interactorvtkRenderer renderer = vtkRenderer.New();vtkRenderWindow renderWindow = vtkRenderWindow.New();renderWindow.Render();renderWindow.SetWindowName("PointPicker");renderWindow.AddRenderer(renderer);vtkPointPicker pointPicker = vtkPointPicker.New();vtkRenderWindowInteractor windowInteractor = vtkRenderWindowInteractor.New();windowInteractor.SetPicker(pointPicker);windowInteractor.SetRenderWindow(renderWindow);windowInteractor.LeftButtonPressEvt += WindowInteractor_PickEvt;vtkInteractorStyleTrackballCamera style = new vtkInteractorStyleTrackballCamera();windowInteractor.SetInteractorStyle(style);renderer.AddActor(actor);renderer.SetBackground(1, 1, 1);renderWindow.Render();windowInteractor.Start();}
 private void WindowInteractor_PickEvt(vtkObject sender, vtkObjectEventArgs e){if (sender is vtkRenderWindowInteractor interactor){int[] pos = interactor.GetEventPosition();Console.WriteLine($"Picking pixel:  {pos[0]} {pos[1]}");// 拾取函数 4个参数 前三个为鼠标的当前窗口坐标,第四个参数是vktRenderer对象interactor.GetPicker().Pick(pos[0], pos[1], 0, interactor.GetRenderWindow().GetRenderers().GetFirstRenderer());double[] picked = interactor.GetPicker().GetPickPosition();Console.WriteLine($"Picked value:  {picked[0]} {picked[1]} {picked[2]}");vtkSphereSource sphereSource = vtkSphereSource.New();sphereSource.Update();vtkPolyDataMapper mapper = vtkPolyDataMapper.New();mapper.SetInputConnection(sphereSource.GetOutputPort());vtkActor actor = vtkActor.New();actor.SetMapper(mapper);actor.SetPosition(picked[0], picked[1], picked[2]);actor.SetScale(0.05);actor.GetProperty().SetColor(1, 0, 0);interactor.GetRenderWindow().GetRenderers().GetFirstRenderer().AddActor(actor);}}
3)效果

4)说明

        这里是注册LeftButtonPressEvt事件响应函数。在事件函数中,调用了vtkRenderWindowInteractor 的GetEventPosition(函数输出鼠标点击的屏幕坐标(以像素为单位)。实现拾取的函数是:

public virtual int Pick(double selectionX, double selectionY, double selectionZ, vtkRenderer renderer);

        该函数需要接收四个参数,前三个为(selectionX,selectionY,selectionZ),即鼠标的当前窗口坐标,其中selectionZ通常为0:第四个参数是vtkRenderer对象。
        GetPickPosition()函数输出鼠标当前单击位置的世界坐标系下的坐标值。为了更加直观地显示鼠标左键按下的位置,在鼠标的单击位置生成了一个小红球。 

3、单元拾取

1)概述

        vtkCellPicker 类用于拾取模型中的某个单元。

2)代码
private void TestCellPicker(){vtkSphereSource sphereSource = vtkSphereSource.New();sphereSource.Update();polyData = sphereSource.GetOutput();selectedMapper = vtkDataSetMapper.New();selectedActor = vtkActor.New();//create a mapper and actorvtkPolyDataMapper mapper = vtkPolyDataMapper.New();mapper.SetInputConnection(sphereSource.GetOutputPort());vtkActor actor = vtkActor.New();actor.SetMapper(mapper);actor.GetProperty().SetColor(0, 1, 0);//creat a renderer,render window,and interactorvtkRenderer renderer = vtkRenderer.New();vtkRenderWindow renderWindow = vtkRenderWindow.New();renderWindow.Render();renderWindow.SetWindowName("CellPicker");renderWindow.AddRenderer(renderer);vtkCellPicker cellPicker = vtkCellPicker.New();vtkRenderWindowInteractor windowInteractor = vtkRenderWindowInteractor.New();windowInteractor.SetPicker(cellPicker);windowInteractor.SetRenderWindow(renderWindow);windowInteractor.LeftButtonPressEvt += WindowInteractor_LeftButtonPressEvt;vtkInteractorStyleTrackballCamera style = new vtkInteractorStyleTrackballCamera();windowInteractor.SetInteractorStyle(style);renderer.AddActor(actor);renderer.SetBackground(1, 1, 1);renderWindow.Render();windowInteractor.Initialize();windowInteractor.Start();}
  private void WindowInteractor_LeftButtonPressEvt(vtkObject sender, vtkObjectEventArgs e){if (sender is vtkRenderWindowInteractor interactor){int[] pos = interactor.GetEventPosition();Console.WriteLine($"Picking pixel:  {pos[0]} {pos[1]}");vtkCellPicker pick = interactor.GetPicker() as vtkCellPicker;// 拾取函数 4个参数 前三个为鼠标的当前窗口坐标,第四个参数是vktRenderer对象pick.Pick(pos[0], pos[1], 0, interactor.GetRenderWindow().GetRenderers().GetFirstRenderer());if (pick.GetCellId() != -1){vtkIdTypeArray ids = vtkIdTypeArray.New();ids.SetNumberOfComponents(1);//拾取完毕,通过GetCellId()函数来得到当前拾取的单元索引号。ids.InsertNextTuple1(pick.GetCellId());//vtkPolyData的局部数据提取功能//声明了要提取的数据的类型vtkSelectionNode selectionNode = vtkSelectionNode.New();selectionNode.SetFieldType((int)vtkSelectionNode.SelectionField.CELL); //设置数据的类型为单元selectionNode.SetContentType((int)vtkSelectionNode.SelectionContent.INDICES); //设置数据的内容为索引号selectionNode.SetSelectionList(ids);vtkSelection selection = vtkSelection.New();selection.Union(selectionNode);//实现了数据提取功能vtkExtractSelection extractSelection = vtkExtractSelection.New();extractSelection.SetInputData(0, polyData);extractSelection.SetInputData(1, selection);extractSelection.Update();selectedMapper.SetInputData(extractSelection.GetOutput() as vtkDataSet);selectedActor.SetMapper(selectedMapper);selectedActor.GetProperty().EdgeVisibilityOn();selectedActor.GetProperty().SetEdgeColor(1, 0, 0);selectedActor.GetProperty().SetLineWidth(3);interactor.GetRenderWindow().GetRenderers().GetFirstRenderer().AddActor(selectedActor);}}}
3)效果

4)说明

        通过注册vtkRenderWindowInteractor的LeftButtonPressEvt事件响应函数。polyData 为被拾取的模型数据,需要通过外部设置。在响应鼠标左键消息时,首先定义了vtkCellPicker 对象,使用 Pick()函数实现拾取功能。拾取完毕,即可通过 GetCelld()函数来得到当前拾取的单元索引号。为了更方便地显示拾取的结果,可实现单元边的高亮显示。这里就涉及了vtkPolyData的局部数据提取功能。实现该功能时使用了几个新的类。vtkIdTypeAray对象存储当前选中的单元的索引号,每次只选择一个单元,因此每次该对象仅有一个索引号;vtkSelectionNode 对象与 vtkSelection 对象通常搭配使用,vtkSelection 实际上是一个 vtkSelectionNode 的数组,而vtkSelectionNode 则声明了要提取的数据的类型,这里 SetFieldType()设置数据的类型为单元,SetContentType()设置数据的内容为索引号。vtkExtractSelection 实现了数据提取功能,其第一个输入为被提取的 vtkPolyData 数据,第二个输入为 vkSelection 对象,标记要提取的数据类型。提取完毕,即可将提取的结果保存至一个vtkActor 对象,并添加至当前的 vtkRenderer中显示。

2、Prop拾取

1)概述

        在渲染场景中拾取某一Prop对象时,使用的类是 vtkPropPicker。

2)代码
 private void TestPropPicker(){vtkRenderer renderer = vtkRenderer.New();vtkRenderWindow renderWindow = vtkRenderWindow.New();renderWindow.Render();renderWindow.SetWindowName("PropPicker");renderWindow.AddRenderer(renderer);vtkRenderWindowInteractor windowInteractor = vtkRenderWindowInteractor.New();windowInteractor.SetRenderWindow(renderWindow);lastPickedProperty = vtkProperty.New();windowInteractor.LeftButtonPressEvt += WindowInteractor_PropPickerEvt;for (int i = 0; i < 10; i++){vtkSphereSource source = vtkSphereSource.New();double x, y, z, radius;x = vtkMath.Random(-5, 5);y = vtkMath.Random(-5, 5);z = vtkMath.Random(-5, 5);radius = vtkMath.Random(0.5, 1.0);source.SetRadius(radius);source.SetCenter(x, y, z);source.SetPhiResolution(11);source.SetThetaResolution(21);vtkPolyDataMapper mapper = vtkPolyDataMapper.New();mapper.SetInputConnection(source.GetOutputPort());vtkActor actor = vtkActor.New();actor.SetMapper(mapper);double r, g, b;r = vtkMath.Random(0.4, 1.0);g = vtkMath.Random(0.4, 1.0);b = vtkMath.Random(0.4, 1.0);actor.GetProperty().SetDiffuseColor(r, g, b);actor.GetProperty().SetDiffuse(0.8);actor.GetProperty().SetSpecular(0.5);actor.GetProperty().SetSpecularColor(1, 1, 1);actor.GetProperty().SetSpecularPower(30);renderer.AddActor(actor);}renderer.SetBackground(1, 1, 1);renderWindow.Render();windowInteractor.Initialize();windowInteractor.Start();}
  private void WindowInteractor_PropPickerEvt(vtkObject sender, vtkObjectEventArgs e){/**  首先获取鼠标单击的坐标值,然后实例化一个vtkPropPicker对象,并调用Pick()函数实现Prop的拾取。*  该类的主要功能是当用户单击渲染场景中的某个对象时,对所拾取的对象进行红色高亮显示。*  为了便于恢复Actor原来的属性设置,程序中先储存当前拾取的Actorn属性值到LastPciedProperty中。*/if (sender is vtkRenderWindowInteractor interactor){int[] clickPos = interactor.GetEventPosition();//pick from this location.vtkPropPicker picker = vtkPropPicker.New();picker.Pick(clickPos[0], clickPos[1], 0, interactor.GetRenderWindow().GetRenderers().GetFirstRenderer());double[] pos = picker.GetPickPosition();if (lastPickedActor != null){lastPickedActor.GetProperty().DeepCopy(lastPickedProperty);}lastPickedActor = picker.GetActor();if (lastPickedActor != null){// Save the property of the picked actor so that we can restore it next time// 保存选取的 actor 的属性,以便下次可以恢复它lastPickedProperty.DeepCopy(lastPickedActor.GetProperty());// Highlight the picked actor by changing its properties//通过更改其属性高亮显示选取的角色lastPickedActor.GetProperty().SetColor(1, 0, 0);lastPickedActor.GetProperty().SetDiffuse(1);lastPickedActor.GetProperty().SetSpecular(0);}}}
3)效果

4)说明

        通过注册vtkRenderWindowInteractor的LeftButtonPressEvt事件响应函数。

首先获取鼠标单击的坐标值,然后实例化一个 vtkPropPicker对象,并调用Pick()函数实现Prop的拾取。

该类的主要功能是当用户单击渲染场景中的某个对象时,对所拾取的对象进行红色高亮显示。

为了便于恢复Actor原来的属性设置,程序中先储存当前拾取的Actor属性值到LastPickedProperty中,以便在下次拾取其他 Actor 对象时,将先前所拾取的对象恢复到原来的属性。
PropPickerInteractorStyle 类在 main()函数的使用方法与以上两例相似。
其他的拾取类,如vkAreaPicker可以根据用户提供的矩形框来选择该矩形框范围内的vtkActor/vtkProp 对象;vtkWroldPointPicker 可以实现窗口坐标到世界坐标的转换等,实现方法基本都是一致的。实现的关键在于定义和消息处理。

相关文章:

  • ES 索引加载 vs BulkLoad
  • function ‘as_cholmod_sparse‘ not provided by package ‘Matrix‘
  • FreeCAD创作参数化凹形和水波纹式雨水箅子
  • 意法STM32F103C8T6 单片机ARM Cortex-M3 国民MCU 电机控制到物联网专用
  • Windows系统提示“mfc140u.dll丢失”?详细修复指南,一键恢复程序运行!
  • 智能制造——解读 51页制造业数据治理主数据管理系统建设方案【附全文阅读】
  • 从零Gazebo中实现Cartographer算法建图(新目录)
  • 如何使用 mkimage 工具生成 uImage 文件(RISC-V 环境)
  • 使用 Rust Clippy 的详细方案
  • JS使用~、>>、<<、>>>、|来取整,有啥区别
  • 软件技术专业的出路在哪
  • 【浏览器插件】如何开发一个Chrome浏览器插件
  • Flink基础知识
  • 实战指南:用DataHub管理Hive元数据
  • uni-app总结2-所需知识储备和学习途径
  • Android ViewBinding 简述
  • SQL基础知识,MySQL学习(长期更新)
  • 深入解析线程池与队列系统设计原理
  • 以list为输入条件,查询数据库表,java中的mapper层和mybatis层应该怎么写?
  • C++11多线程内存模型:从入门到精通
  • 建设厅/西安网站建设方案优化
  • 学校网站建设工作内容/济宁做网站的电话
  • 佛山企业网站推广/品牌传播推广方案
  • 梅州市做试块网站/站内seo优化
  • 天河区营销型网站建设/手机百度高级搜索入口
  • wordpress记账/seo标题优化分析范文