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


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

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

    一、拾取对象

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

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

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

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

    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;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

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

    二、从信号到槽函数

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

    三、高亮拾取对象

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

    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

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

  • 相关阅读:
    android gradle 开发与应用(一) : Gradle基础
    灰色预测MATLAB程序
    SRE 的黄昏,平台工程的初晨
    深圳大学计软《程序设计基础》实验三 选择结构
    java学习day8(Java基础)static关键字和继承
    【无标题】
    线程的创建和状态(操作系统和java)
    kubernetes数据持久化StorageClass动态供给(二)
    xxl-job调度的一致性及路由选择策略
    聊一聊 tcp/ip 在.NET故障分析的重要性
  • 原文地址:https://blog.csdn.net/loveoobaby/article/details/134248511