【FastCAE源码阅读9】鼠标框选网格、节点的实现

一、VTK的框选支持类vtkInteractorStyleRubberBandPick

FastCAE的鼠标事件交互类是PropPickerInteractionStyle,它扩展自vtkInteractorStyleRubberBandPick。vtkInteractorStyleRubberBandPick类可以实现鼠标框选物体,默认情况下按下键盘r键开启框选模式,这时拖动鼠标可拾取物体。VTK官网有其例子:HighlightSelection。

二、FastCAE框选产品设计

我们看FastCAE的鼠标拾取产品设计。其只支持框选网格点与网格单元,几何点、线、面都不支持。框选网格单元效果如下:
请添加图片描述

在VTK的给的案例中,按r键是为了打开vtkInteractorStyleRubberBandPick类的框选开关,设置vtkInteractorStyleRubberBandPick字段CurrentMode=1(VTKISRBP_SELECT),表示开启框选模式。VTK中vtkInteractorStyleRubberBandPick.cxx源码如下:

void vtkInteractorStyleRubberBandPick::OnChar()
{
  switch (this->Interactor->GetKeyCode())
  {
    case 'r':
    case 'R':
      // r toggles the rubber band selection mode for mouse button 1
      if (this->CurrentMode == VTKISRBP_ORIENT)
      {
        this->CurrentMode = VTKISRBP_SELECT;
      }
      else
      {
        this->CurrentMode = VTKISRBP_ORIENT;
      }
      break;
    case 'p':
    case 'P':
    {
      vtkRenderWindowInteractor* rwi = this->Interactor;
      int* eventPos = rwi->GetEventPosition();
      this->FindPokedRenderer(eventPos[0], eventPos[1]);
      this->StartPosition[0] = eventPos[0];
      this->StartPosition[1] = eventPos[1];
      this->EndPosition[0] = eventPos[0];
      this->EndPosition[1] = eventPos[1];
      this->Pick();
      break;
    }
    default:
      this->Superclass::OnChar();
  }
}

而在FastCAE框选时,不需要按下r键,其原因在用切换拾取模式时,直接改掉了CurrentMode的值,源码如下(注意看这个函数最后一行):

void PropPickerInteractionStyle::setSelectModel(int m)
{
	_selectModel = (SelectModel)m;
	this->CurrentMode = 0;
	if (_actor != nullptr)
		_actor->GetProperty()->DeepCopy(_property);
	_actor = nullptr;
	_preGeoSeltctActor = nullptr;
	_selectItems.clear();
	emit grabKeyBoard(false);
	switch (_selectModel)
	{
	case ModuleBase::MeshNode:
	case ModuleBase::MeshCell:
	case ModuleBase::GeometryWinPoint:
	case ModuleBase::GeometryWinCurve:
	case ModuleBase::GeometryWinSurface:
	case ModuleBase::GeometryWinBody:
		emit grabKeyBoard(true);
		break;
	case ModuleBase::GeometryPoint:
	case ModuleBase::GeometryCurve:
	case ModuleBase::GeometrySurface:
	case ModuleBase::GeometryBody:
		break;
	case ModuleBase::BoxMeshNode: // 当选择方式是框选时,直接设置开始框选
	case ModuleBase::BoxMeshCell:
	case ModuleBase::DrawSketch:
		this->CurrentMode = 1;
		break;
	}
}

这种设计的好处是不用按键盘进行交互,但造成激活框选按钮之后,视图的角度无法更改。本来按住左键拖拽可以旋转视图的,打开框选之后就失效了。

其框选还有一个比较严重的问题:框选会同时拾取物体的表面与背面单元,效果如下:
请添加图片描述

这种效果惊不惊喜,意不意外!?很多场景下,这种拾取是不满足要求的。进一步分析其框选逻辑就很好理解这种现象了。

三、框选计算逻辑

PropPickerInteractionStyle::OnLeftButtonUp()处理鼠标抬起事件,框选计算哪些物体要被选中的逻辑也在这里被触发。

void PropPickerInteractionStyle::OnLeftButtonUp()
{
	vtkInteractorStyleRubberBandPick::OnLeftButtonUp();
	if (_selectModel == None && !_mouseMoved)
		emit this->clearAllHighLight();
	if ((_selectModel != BoxMeshCell) && (_selectModel != BoxMeshNode) && (_selectModel != DrawSketch))
		return;
	if (this->CurrentMode == 0)
		return;
	//		_selectItemIDs->SetNumberOfValues(0);
	_selectItems.clear();
	int *endPos = this->GetInteractor()->GetEventPosition();
	_endPos[0] = endPos[0];
	_endPos[1] = endPos[1];
	//		qDebug() << "end  " << _endPos[0] << "   " << _endPos[1];
	if (_selectModel != DrawSketch)
	{
		vtkActor *ac = nullptr;
		vtkAreaPicker *areaPicker = dynamic_cast<vtkAreaPicker *>(this->GetInteractor()->GetPicker());
		ac = areaPicker->GetActor();
		if (ac == nullptr)
			return;
	}
	switch (_selectModel)
	{
	case ModuleBase::BoxMeshNode: // 计算哪些节点被选中
		boxSelectMeshNode();
		break;
	case ModuleBase::BoxMeshCell: // 计算哪些单元要被选中
		boxSelectMeshCell();
		break;
	case ModuleBase::DrawSketch:
		_coordinate->SetCoordinateSystemToDisplay();
		_coordinate->SetValue(endPos[0], endPos[1], 0);
		double *d = _coordinate->GetComputedWorldValue(_renderer);
		emit mouseReleasePoint(d);
		break;
	}
	_mouseMoved = false;
	_leftButtonDown = false;
}

boxSelectMeshNode()、boxSelectMeshCell()函数分别哪些节点、单元要被拾取。

void PropPickerInteractionStyle::boxSelectMeshNode()
{
	emit clearAllHighLight(); // 清除掉当前高亮
	_selectItems.clear(); // 清理当前选择项
	// Forward events
	int range[4];
	this->getBoxRange(range);  // 获取框选矩形的坐标
	vtkActorCollection *actors = _renderer->GetActors(); // 获取当前的场景中所有actor
	actors->InitTraversal();
	const int nac = actors->GetNumberOfItems();
	for (int i = 0; i < nac; ++i) // 对Actor进行遍历
	{
		vtkActor *actor = actors->GetNextActor();
		if (actor == nullptr)
			if (!actor->GetVisibility())
				continue;
		if (!actor->GetPickable())
			continue;
		vtkMapper *mapper = actor->GetMapper();
		if (mapper == nullptr)
			continue;
		vtkDataSet *dataset = mapper->GetInputAsDataSet();
		if (dataset == nullptr)
			continue;
		vtkDataArray *IDS = dataset->GetPointData()->GetArray("IDS"); // 提取Actor的点数据
		if (IDS == nullptr)
			continue;
		this->selectMesh(dataset, range);
	}
	emit highLight(&_selectItems);
}

void PropPickerInteractionStyle::selectMesh(vtkDataSet *dataSet, int *range)
{
	vtkRenderer *render = this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer();
	vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
	coordinate->SetCoordinateSystemToWorld();
	coordinate->GetComputedDisplayValue(render);

	if (_selectModel == BoxMeshNode)
	{
		vtkDataArray *ids = dataSet->GetPointData()->GetArray("IDS"); // 获取点集
		const int npoint = dataSet->GetNumberOfPoints();
		for (int i = 0; i < npoint; ++i)
		{
			double coor[3];
			dataSet->GetPoint(i, coor); // 获取点的坐标
			coordinate->SetValue(coor); // 将点的坐标设置给coordinate
			int *va = coordinate->GetComputedDisplayValue(render); // 计算屏幕坐标
			if (isPointInRange(va, range)) // 是否在鼠标框内部
			{
				double *k_id = ids->GetTuple2(i); // 看不懂?
				_selectItems.insert(k_id[0], k_id[1]);
			}
		}
	}
	else if (_selectModel == BoxMeshCell)
	{
		vtkDataArray *ids = dataSet->GetCellData()->GetArray("IDS"); // 获取cell数据
		const int ncell = dataSet->GetNumberOfCells();
		for (int i = 0; i < ncell; ++i) // 遍历cell
		{
			vtkCell *cell = dataSet->GetCell(i); // 当前的cell
			double pcenter[3] = {0};
			cell->GetParametricCenter(pcenter); // 获取当前cell的中心点参数坐标
			int subid;
			double coor[3];
			double w[100];
			cell->EvaluateLocation(subid, pcenter, coor, w); // 根据参数坐标获取中心点世界空间坐标
			coordinate->SetValue(coor);
			int *va = coordinate->GetComputedDisplayValue(render); // 计算屏幕坐标
			if (isPointInRange(va, range)) // 屏幕坐标是否在选择框内
			{
				double *k_id = ids->GetTuple2(i);
				_selectItems.insert(k_id[0], k_id[1]);
			}
		}
	}
}

根据以上代码,框选点时,直接根据点坐标计算其投影到屏幕上的坐标,判断是否在选择框内。单元是判断中心点是否在选择框内部。因为投影之后,丢弃了深度方向的信息,没有考虑物体的遮挡信息,所以框选时表面、背面均可选择。而且其计算框选时遍历所有网格,没有借助一些加速结构,如BVH树等,造成框选效率较低,当网格数量较多时,这种方式很慢。

总结:

FastCAE的框选逻辑过于简单,只是demo阶段,实际的CAE软件的拾取逻辑要远比这复杂。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/145486.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

qt之扫码枪编码自动识别文本

一、前言 使用扫码枪输入扫码后&#xff0c;自动将编码转为文字或识别进入下一功能。 只是简单的实现了一种方式&#xff0c;并不适用于商业用途 二、环境 扫码枪免驱自动扫码编码打印到输入库的环境下 三、正文 本文介绍也是输入一种方式&#xff0c;不限于非得扫码识别…

YOLO-NAS:最高效的目标检测算法之一

YOLO-NAS目标检测 介绍 YOLO&#xff08;You Only Look Once&#xff09;是一种目标检测算法&#xff0c;它使用深度神经网络模型&#xff0c;特别是卷积神经网络&#xff0c;来实时检测和分类对象。该算法首次在2016年的论文《You Only Look Once&#xff1a;统一的实时目标检…

【Proteus仿真】【51单片机】拔河游戏设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用按键、LED、动态数码管模块等。 主要功能&#xff1a; 系统运行后&#xff0c;指示灯处于中间位置&#xff0c;数码管显示得分0&#xff0c;当按下…

c++范围for语句

语法格式 for(declaration:expression)statement 基本使用 遍历输出 vector<int> nums { 1,2,3,4,5}; for (int num : nums) {num;cout << num << " "; } cout << endl; 遍历时修改 vector<int> nums { 1,2,3,4,5}; for (int&…

Android 布局优化,看过来 ~

屏幕刷新机制 基本概念 刷新率&#xff1a;屏幕每秒刷新的次数&#xff0c;单位是 Hz&#xff0c;例如 60Hz&#xff0c;刷新率取决于硬件的固定参数。帧率&#xff1a;GPU 在一秒内绘制操作的帧数&#xff0c;单位是 fps。Android 采用的是 60fps&#xff0c;即每秒 GPU 最多…

[文件读取]cuberite 文件读取 (CVE-2019-15516)

1.1漏洞描述 漏洞编号CVE-2019-15516漏洞类型文件上传漏洞等级⭐⭐⭐漏洞环境VULFOCUS攻击方式 描述: Cuberite是一款使用C语言编写的、轻量级、可扩展的多人游戏服务器。 Cuberite 2019-06-11之前版本中存在路径遍历漏洞。该漏洞源于网络系统或产品未能正确地过滤资源或文件路…

苍穹外卖项目笔记(1)

前言 苍穹外卖项目笔记附代码&#xff0c;贴上 github 链接&#xff0c;持续更新中&#xff1a;GitHub - Echo0701/sky-take-out &#xff08;不知道为啥发不了项目压缩包&#xff0c;那就下次再试试吧........&#xff09; 1 软件开发整体介绍 1.1 软件开发流程 1.2 角色分…

U-boot(一):Uboot命令和tftp

本文主要基于S5PV210探讨uboot。 uboot 部署&#xff1a;uboot(180~400K的裸机程序)在Flash(可上电读取)、OS在FLash(nand) 启动过程&#xff1a;上电后先执行uboot、uboot初始化DDR和Flash,将OS从Flash中读到DDR中启动OS,uboot结束 特点&#xff1a;…

MES系统如何改进生产管理?

伴随机械制造业行业竞争逐渐加剧&#xff0c;越来越多企业意识到MES系统的重要性&#xff0c;慢慢积极主动把握和实施MES系统。可是纵观绝大部分企业或者MES生产商&#xff0c;对MES的掌握依然存在比较大的分歧。 有一些人说MES系统是企业信息化构建的中枢神经&#xff0c;也有…

Oracle(2-3) Basic Oracle Net Server Side Configuration

文章目录 一、基础知识1、The Listener Process监听器进程2、Connection Methods 连接方法3、Spawn and Bequeath Conn4、Direct Hand-Off Connections 直接切换连接5、Redirection Session 重定向会话6、Simple to Complex:N-Tier 简单到复杂&#xff1a;N层7、Service Config…

SQL-LABS

less8 and 11-- 12 发现存在注入点 接下来我们会接着用联合查询 和以往的题目不一样没显错位&#xff0c;也就是没有报错的内容&#xff0c;尝试用盲注 布尔型 length&#xff08;&#xff09;返回长度 substr&#xff08;&#xff09;截取字符串&#xff08;语法substr&a…

【Linux】 ls -l 和 grep

语法:用于显示指定工作目录下之内容 ls [-alrtAFR] [name...]将 /bin 目录以下所有目录及文件详细资料列出: ls -lR /bin将 /usr/local/bin 目录以下所有有关python列出: ls -l /usr/local/bin/ | grep python在使用 ls -l 命令时&#xff0c;第一列的字符表示文件或目录的类…

计算机组成原理——指令系统题库21-40

21、假定指令地址码给出的是操作数的存储地址&#xff0c;则该操作数采用的是什么寻址。 A、 立即    B、 直接     C、 基址     D、 相对 22、寄存器间接寻址方式的操作数存储在什么中 A、 通用寄存器    B、 存储单元     C、 程序计数器     …

【C++】STL的基本用法

目录结构 1. STL概念 1.2 常见容器 1.3 六大组件 2. STL容器之vector 1. vector 2. 基本用法示例 3. STL容器之map 1. map 2. 基本用法示例 1. STL概念 C中的STL是指标准模板库的缩写。STL提供了一组通用的模板类和函数&#xff0c;用于实现常见的数据结构和算法&…

【C/C++底层】内存分配:栈区(Stack)与堆区(Heap)

/*** poject * author jUicE_g2R(qq:3406291309)* file 底层内存分配&#xff1a;栈区(Stack)与堆区(Heap)* * language C/C* EDA Base on MVS2022* editor Obsidian&#xff08;黑曜石笔记软件&#xff09;* * copyright 2023* COPYRIGHT …

图文示例:Python程序的运行原理解读

文章目录 一、编译型语言&#xff08;C语言为例&#xff09;二、动态型语言三、程序是如何运行起来的&#xff1f;四、分析五、dir 函数六、def 指令七、pyc文件1.pyc文件三大作用 八、import 指令总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三…

软件工程分析报告05体系结构说明书——基于Paddle的肝脏CT影像分割

基于Paddle的肝脏CT影像分割系统的体系结构说明书 目录 HIPO图 H图 Ipo图 软件结构图 面向数据流的体系结构设计图 程序流程图 S图 用PDL语言描述的伪代码 HIPO图 H图 Ipo图 软件结构图 面向数据流的体系结构设计图 程序流程图 S图 PAD图 用PDL语言描述的伪代码 (1)…

【Hello Go】初识Go语言

初识Go语言 Go语言介绍Go语言是什么Go语言优势Go语言能用来做什么 Go语言环境安装第一个GO语言程序运行Go语言程序 Go语言介绍 Go语言是什么 go语言是是Google开发的一种静态强类型、编译型、并发型&#xff0c;并具有垃圾回收功能的编程语言. 静态类型&#xff1a;在静态类型…

【计算机网络】UDP协议

UDP的结构 我们学习一个协议最主要的就是理解它的报文格式&#xff0c;对于UDP协议来说 我们看下面的这张图。 16位UDP长度&#xff0c;表示整个数据报&#xff08;UDP首部UDP数据&#xff09;的最大长度。UDP报文长度占两个字节&#xff0c;16位表示的数据范围&#xff08;0-…

Java学习之路 —— Day3(内部类、枚举、泛型、API)

文章目录 1. 内部类2. 枚举3. 泛型 1. 内部类 成员内部类 就是类中的一个普通成员&#xff0c;类似普通的成员方法、成员变量。&#xff08;套娃&#xff09; public class Outer {public class Inner {private String name;public static String school;public String getNa…