【FastCAE源码阅读5】使用VTK实现鼠标拾取对象并高亮

鼠标拾取对象是很多软件的基本功能。FastCAE的拾取比较简单,是通过VTK实现的。

对几何而言,拾取类型切换在工具栏上,单击后再来单击视图区对象进行拾取,拾取后的对象会高亮显示。效果如下图:

一、拾取对象

拾取对象是在PropPickerInteractionStyle类实现的,该类是vtkInteractorStyleRubberBandPick的子类,重写原来的鼠标处理函数。当鼠标左键按下,会触发以下代码:

cpp 复制代码
void PropPickerInteractionStyle::clickSelectGeometry(bool preSelect)
{
	int *clickPos = this->GetInteractor()->GetEventPosition(); // 获取鼠标点击位置
	int success = -1, index = -1;
	vtkActor *actor = nullptr;
	if (_selectModel == GeometryPoint || _selectModel == GeometryWinPoint) // 几何点
	{
		vtkSmartPointer<vtkPointPicker> picker = vtkSmartPointer<vtkPointPicker>::New();
		success = picker->Pick(clickPos[0], clickPos[1], 0, this->GetDefaultRenderer());
		if (0 != success)
		{
			index = picker->GetPointId(); // 点的id
			actor = picker->GetActor(); // 点对应的Actor
		}
	}
	else
	{
		vtkSmartPointer<vtkCellPicker> picker = vtkSmartPointer<vtkCellPicker>::New();
		if (_selectModel == GeometryCurve || _selectModel == GeometryWinCurve)
			picker->SetTolerance(0.0025);
		success = picker->Pick(clickPos[0], clickPos[1], 0, this->GetDefaultRenderer());
		if (0 != success)
		{
			index = picker->GetCellId(); // cell的id
			actor = picker->GetActor(); // 对应的actor
		}
	}
	emit selectGeometry(preSelect, actor, index);
}

这段代码就是鼠标拾取对象的核心逻辑。其实现是借助了VTK提供的vtkPointPicker、vtkCellPicker。其中当拾取模式是点的时候,使用vtkPointPicker,如果拾取成功返回点的id及对应的Actor(一个Actor可能包含多个点)。当拾取模式是线、面、体的时候,使用vtkCellPicker,成功则返回对应cell id及Actor(一个Actor可能包含多个cell)。注意当拾取线的时候,需要提高拾取Tolerance,不然线很难拾取到。

另外要注意的是,当拾取线的时候,面Actor是拾取不到的,同样当拾取面的时候,线Actor也是拾取不到。这是通过在用户切换选择模式的时候,设置面或边Actor是否可以拾取实现的。核心代码在GeometryViewProvider类中,如下:

cpp 复制代码
void GeometryViewProvider::setGeoSelectMode(int m)
{
	_viewData->updateGraphOption();
	ModuleBase::SelectModel selectType = (ModuleBase::SelectModel)m;
	QList<GeoViewObj> viewObjs = _geoViewHash.values();
	vtkActor *actor = nullptr;
	for (GeoViewObj vobj : viewObjs)
	{
		actor = vobj._faceObj.first;
		if (actor != nullptr)
			actor->SetPickable(false);
		actor = vobj._edgeObj.first;
		if (actor != nullptr)
			actor->SetPickable(false);
		actor = vobj._pointObj.first;
		if (actor != nullptr)
			actor->SetPickable(false);
		switch (selectType)
		{
		case ModuleBase::GeometryBody:
		case ModuleBase::GeometrySurface:
		case ModuleBase::GeometryWinBody:
		case ModuleBase::GeometryWinSurface:
			actor = vobj._faceObj.first;
			if (actor != nullptr)
				actor->SetPickable(true);
			break;
		case ModuleBase::GeometryWinCurve:
		case ModuleBase::GeometryCurve:
			actor = vobj._edgeObj.first;
			if (actor != nullptr)
				actor->SetPickable(true);
			break;
		case ModuleBase::GeometryWinPoint:
		case ModuleBase::GeometryPoint:
			actor = vobj._pointObj.first;
			if (actor != nullptr)
				actor->SetPickable(true);
			break;
		default:
			break;
		}
	}
}

这个代码相对比较简单,不再分析。

二、从信号到槽函数

拾取对象之后,会发出emit selectGeometry信号,参数中带有Actor及cell或点的id。这个信号是QT框架提供的编程机制。其对应的槽函数在如下图中能找到:

三、高亮拾取对象

高亮拾取对象处理过程代码如下:

cpp 复制代码
void GeometryViewProvider::selectGeometry(bool pre, vtkActor *ac, int index)
{
	if (ac == nullptr || index < 0) // 无效拾取
	{
		_viewData->preHighLight(nullptr);
		_preWindow->reRender();
		return;
	}
	ModuleBase::SelectModel selectMod = _preWindow->getSelectModel(); // 选择模式
	vtkDataSet *dataSet = ac->GetMapper()->GetInputAsDataSet(); // 获取Actor的多边形数据集vtkPolyData
	vtkPolyData *poly = vtkPolyData::SafeDownCast(dataSet);
	if (poly == nullptr)
		return;
	GeometryViewObject *vobj = nullptr;
	switch (selectMod)
	{
	case ModuleBase::GeometryWinBody:
	case ModuleBase::GeometryBody:
		vobj = _viewData->getSolidViewObj(poly, index);
		break;
	case ModuleBase::GeometryWinSurface:
	case ModuleBase::GeometrySurface:
		vobj = _viewData->getFaceViewObj(poly, index); // 输入多边形数据+cell id
		break;
	case ModuleBase::GeometryWinCurve:
	case ModuleBase::GeometryCurve:
		vobj = _viewData->getEdgeViewObj(poly, index);
		break;
	case ModuleBase::GeometryWinPoint:
	case ModuleBase::GeometryPoint:
		vobj = _viewData->getPointViewObj(poly, index);
		break;
	default:
		break;
	}
	if (vobj == nullptr)
		return;
	if (pre)
		_viewData->preHighLight(vobj); // 设置预高亮
	else
	{
		vobj->highLight(); // 高亮对象
		_viewData->preHighLight(nullptr);
		emit geoShapeSelected(vobj->getGeometySet(), vobj->getIndex());
	}
	_preWindow->reRender();
}

同样,这里也只分析面的高亮,其他的高亮逻辑类似。高亮处理的时候,直接先根据Actor获取了其vtkPolyData数据集,找到哪个面需要高亮,最后更改其颜色。如何根据cell id找到这个面,需要理解其数据结构才能看懂其逻辑。对于面的数据结构,这篇文章【FastCAE源码阅读3】几何模型显示:从OCC对象到VTK对象有详细描述。其核心原理是查找cell id落在哪个面包含的cell区间上,确定高亮哪个面。最后调用_preWindow->reRender()重新绘制整个场景。

相关推荐
霁月风37 分钟前
设计模式——适配器模式
c++·适配器模式
jrrz08281 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
咖啡里的茶i1 小时前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1071 小时前
Webserver(4.9)本地套接字的通信
c++
@小博的博客1 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
爱吃喵的鲤鱼2 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
7年老菜鸡3 小时前
策略模式(C++)三分钟读懂
c++·qt·策略模式
Ni-Guvara3 小时前
函数对象笔记
c++·算法
似霰3 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
芊寻(嵌入式)3 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习