VTK框选模型表面拾取面片——仅选中前表面

VTK框选表面拾取面片——仅选中前表面


接上一篇 VTK框选表面拾取三角面片——通过观察者命令模式

上一篇最后遗留一个问题,框选表面后,会把模型背面的面片也一起选中。所以这篇内容是解决该问题的。


效果预览

在这里插入图片描述
在这里插入图片描述


功能说明

通过鼠标框选模型表面的面片,然后利用红色边框显示被框选的三角面片,实现仅选中前表面部分的面片,不会把背面的面片也选中


方法介绍

  • 利用vtkInteractorStyleRubberBandPick交互方式实现框选。(通过R键切换交互模式和框选模式)

  • 利用vtkAreaPicker收集框选的信息。VTK还提供了vtkCellPicker,但是CellPicker只能选中某个对象,不能框选一个集合。

  • 使用vtkCellLocator中的IntersectWithLine函数,用光线投射法寻找靠近摄像头一侧的面片cell,然后利用vtkPolyDataConnectivityFilter找出与最近面片cell相连的表面,该表面即要显示的前表面。

  • 源代码中已经写好详细注释。


源代码

完整代码可以直接编译运行

#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleRubberBandPick.h>
#include <vtkAreaPicker.h>
#include <vtkCommand.h>
#include <vtkCallbackCommand.h>
#include <vtkIdFilter.h>
#include <vtkExtractPolyDataGeometry.h>
#include <vtkSphereSource.h>
#include <vtkPlanes.h>
#include <vtkDataSetMapper.h>
#include <vtkProperty.h>
#include <vtkCellLocator.h>
#include <vtkCamera.h>
#include <vtkPolyDataConnectivityFilter.h>

/*
* 定义命令:
* 使用vtkInteractorStyleRubberBandPick交互方式时,会自动触发vtkAreaPicker
* 交互时框选的信息会存储在vtkAreaPicker中
* 将vtkAreaPicker的结束拾取事件绑定vtkPickerCallback
* 类似QT中信号与槽的机制
* vtkAreaPicker触发结束拾取信号,然后执行vtkPickerCallback中的Execute函数
*/
class vtkPickerCallback : public vtkCommand
{
public:
	//@brief 定义New函数(固定格式)
	static vtkPickerCallback* New() {return new vtkPickerCallback;}
	//@brief 定义Execute函数(vtkCommand中的Execute为纯虚函数,必须要实现)
	virtual void Execute(vtkObject* caller, unsigned long, void*);
	void SetPolyData(vtkPolyData* input);
	void SetRenderer(vtkRenderer* input);
	
private:
	vtkPolyData* polyData;				//需要处理的几何数据
	vtkRenderer* renderer;				//需要调用的渲染器

	vtkDataSetMapper* mapper;			//用于显示框选面片的mapper
	vtkActor* actor;					//用于显示框选面片的actor
};

int main()
{
	//****************创建球体*****************
	vtkSphereSource* sphere = vtkSphereSource::New();
	sphere->SetThetaResolution(18);
	sphere->SetPhiResolution(18);
	sphere->SetRadius(10);
	sphere->SetCenter(0, 0, 0);
	sphere->Update();

	//****************创建Mapper***************
	vtkPolyDataMapper* mapper = vtkPolyDataMapper::New();
	mapper->SetInputConnection(sphere->GetOutputPort());
	//换成此语句效果一致:mapper->SetInputData(sphere->GetOutput());
	
	//****************创建Actor****************
	vtkActor* actor = vtkActor::New();
	actor->SetMapper(mapper);

	//****************创建渲染器***************
	vtkRenderer* renderer = vtkRenderer::New();
	renderer->AddActor(actor);

	//****************创建渲染窗口*************
	vtkRenderWindow* renderWindow = vtkRenderWindow::New();
	renderWindow->AddRenderer(renderer);
	
	//****************创建交互器***************
	vtkRenderWindowInteractor* renderWindowInteractor = vtkRenderWindowInteractor::New();

	//****************创建交互方式*************
	vtkInteractorStyleRubberBandPick* interactorStyle = vtkInteractorStyleRubberBandPick::New();

	//****************创建拾取回调函数*********
	vtkPickerCallback* callback = vtkPickerCallback::New();
	callback->SetPolyData(sphere->GetOutput());
	callback->SetRenderer(renderer);

	//****************创建区域拾取器***********
	vtkAreaPicker* areaPicker = vtkAreaPicker::New();
	areaPicker->AddObserver(vtkCommand::EndPickEvent, callback);		//为“结束拾取”事件绑定“拾取回调函数”
	
	renderWindowInteractor->SetRenderWindow(renderWindow);				//为交互器设置渲染窗口
	renderWindowInteractor->SetInteractorStyle(interactorStyle);		//为交互器设置交互方式
	renderWindowInteractor->SetPicker(areaPicker);						//为交互器设置拾取器

	renderWindowInteractor->Start();

	return 0;
}

void vtkPickerCallback::Execute(vtkObject* caller, unsigned long, void*)
{
	//通过反射获取调用者
	vtkAreaPicker* areaPicker = static_cast<vtkAreaPicker*>(caller);
	//获取框选的视锥体(由六个面组成)
	vtkPlanes* frustum = areaPicker->GetFrustum();
	//提前标记几何数据的CellId(目前没有,用于后面实现删除面片的)
	vtkIdFilter* idFilter = vtkIdFilter::New();
	idFilter->SetInputData(polyData);
	idFilter->SetCellIdsArrayName("OriginalCellId");
	idFilter->Update();
	//提取视锥体内的模型
	vtkExtractPolyDataGeometry* extract = vtkExtractPolyDataGeometry::New();
	extract->SetInputConnection(idFilter->GetOutputPort());
	extract->SetImplicitFunction(frustum);
	extract->Update();
	if (!extract->GetOutput()->GetPolys())
	{
		return;
	}
	//创建面片定位器
	vtkCellLocator* locator = vtkCellLocator::New();
	locator->SetDataSet(extract->GetOutput());
	locator->BuildLocator();
	//----------利用光线投射的方法寻找更靠近摄像机的面片------------
	double rayStart[3];				//光线起点坐标:设置为摄像机位置
	double rayDirection[3];			//光线方向向量:设置为框选数据包围盒的中心
	renderer->GetActiveCamera()->GetPosition(rayStart);
	extract->GetOutput()->GetCenter(rayDirection);
	double xyz[3];
	double t;
	double pcoords[3];
	int subId;
	vtkIdType cellId = -1;			//记录光线击中的面片Id号
	locator->IntersectWithLine(rayStart, rayDirection, 0.0001, t, xyz, pcoords, subId, cellId);
	//-----------利用找到的面片获取相连的面
	vtkPolyDataConnectivityFilter* connectivity = vtkPolyDataConnectivityFilter::New();
	connectivity->SetInputConnection(extract->GetOutputPort());
	connectivity->SetExtractionModeToCellSeededRegions();
	connectivity->InitializeSeedList();
	connectivity->AddSeed(cellId);
	connectivity->Update();
	//设置actor
	mapper->SetInputConnection(connectivity->GetOutputPort());
	mapper->ScalarVisibilityOff();
	actor->GetProperty()->SetColor(1, 0, 0);
	actor->GetProperty()->SetPointSize(5);
	actor->GetProperty()->SetRepresentationToWireframe();
	//渲染器加入显示框选的actor
	renderer->AddActor(actor);

}

void vtkPickerCallback::SetPolyData(vtkPolyData* input)
{
	polyData = input;
}

void vtkPickerCallback::SetRenderer(vtkRenderer* input)
{
	renderer = input;
	//初始化用于显示框选面片的mapper和actor
	mapper = vtkDataSetMapper::New();
	actor = vtkActor::New();
	actor->SetMapper(mapper);
}


存在问题

源代码为了说明算法,所以算法的是实现以精简为目标,因此没有去释放内存。

而且算法会存在小Bug(框选无面片的时候和边缘的时候可能会报警告),通过调整rayStart和rayDirection两个向量来改善。

需要用到该算法的话,别忘了自行Debug完善代码

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值