【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()重新绘制整个场景。

相关推荐
橘子遇见BUG23 分钟前
Unity Shader学习日记 part 3 线性代数--矩阵变换
学习·线性代数·unity·矩阵·图形渲染
wkd_0071 小时前
【开源库 | xlsxio】C/C++读写.xlsx文件,xlsxio 在 Linux(Ubuntu18.04)的编译、交叉编译
c语言·c++·xlsxio·c语言读写xlsx·c++读写xlsx·xlsxio交叉编译
捕鲸叉2 小时前
C++软件设计模式之类型模式和对象型模式
开发语言·c++·设计模式
捕鲸叉2 小时前
C++软件设计模式之代理(Proxy)模式
c++·设计模式
code monkey.3 小时前
【排序算法】—— 计数排序
c++·算法·排序算法
云青山水林3 小时前
2024.12.21 周六
c++·算法·贪心算法
Moweiii3 小时前
SDL3 GPU编程探索
c++·游戏引擎·图形渲染·sdl·vulkan
渝妳学C4 小时前
【C++】类和对象(下)
c++
EleganceJiaBao4 小时前
【C语言】结构体模块化编程
c语言·c++·模块化·static·结构体·struct·耦合
成都渲染101云渲染66664 小时前
云渲染,Enscape、D5、Lumion渲染提速教程
运维·服务器·unity·电脑·图形渲染·blender·houdini