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

news2024/10/6 19:12:45

一、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软件的拾取逻辑要远比这复杂。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程序员月入过万的秘密,赶快收藏史上最靠谱接单攻略!!!

近几年经济十分不景气&#xff0c;无论是哪一行总是面临着生活的不容易。不少人都选择多干几份工作来养家糊口&#xff0c;保证家人的生活。那么咱们程序员该如何是好呢&#xff1f;相信不少人已经有了答案&#xff0c;那就是网上接单&#xff01;那么本期就让小编带你一起来看…

如何设计开发一对一交友App吸引更多活跃用户

在当今社交媒体时代&#xff0c;一对一交友App开发正日渐成为发展热点。如何吸引更多活跃用户成为开发者们的首要任务。通过本文&#xff0c;我们将探讨一系列方法&#xff0c;助您设计开发一对一交友App&#xff0c;吸引更多用户的关注和参与&#xff0c;提升App的活跃度。 了…

【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;第一列的字符表示文件或目录的类…

基于单片机的塑料厂房气体检测系统设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、系统硬件设计三、软件设计实物 四、结论五、 文章目录 概要 本文首先分析了基于单片机的可燃…

树的概念及结构|树的三种表示方法

前言 以前我们学的线性结构是一对一的线性关系&#xff0c;但现实中&#xff0c;还有一对多的情况要处理&#xff0c;那就是树形结构。今天我们将学习树的概念及结构、和树的三种常见表示方法。 一、树的概念及结构 1、树的概念 树是一种非线性的数据结构&#xff0c;它是由n…

【NI-DAQmx入门】多通道数据采集

1.通道扩展解释 通道扩展是扩展数据采集设备的通道以包含另一个设备的通道的过程&#xff0c;从而有效地创建具有更多通道的任务。当使用通道扩展时&#xff0c;DAQmx 自动在 DAQmx 驱动程序级别路由触发器和时钟&#xff0c;以便多个设备同步。为了使设备作为一个整体运行&…

软件工程分析报告07测试计划书——基于Paddle的肝脏CT影像分割

目录 测试计划书 1. 引言 2. 测试目标 3. 测试方法 3.1 黑盒测试 (1)等价类划分&#xff1a; (2)边界值分析&#xff1a; (3)因果图&#xff1a; ​编辑&#xff08;4&#xff09;错误推测法 3.2 白盒测试 测试用例&#xff01;&#xff01; 4. 测试环境 5. 测试计划 6…

YOLO目标检测——苹果缺陷检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;苹果质量检测和自动化分拣系统数据集说明&#xff1a;苹果缺陷检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含有缺陷图片和没缺陷图片。标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量…

JS基础 查漏补缺

学习视频&#xff1a;黑马程序员 第五天——对象 方法和调用 数据行为性的信息称为方法&#xff0c;如跑步、唱歌等&#xff0c;一般是动词性的&#xff0c;其本质是函数。 方法是依附在对象上的函数 方法是由方法名和函数两部分构成&#xff0c;它们之间使用 : 分隔 方法是…

【广州华锐互动】VR居家防火逃生模拟演练增强训练的真实性

VR软件开发公司广州华锐互动在消防培训领域已开发了多款VR产品&#xff0c;今天为大家介绍VR居家防火逃生模拟演练系统&#xff0c;这是一种基于虚拟现实技术的消防教育训练设备&#xff0c;通过模拟真实的火灾场景&#xff0c;让使用者身临其境地体验火灾逃生过程&#xff0c;…

破解tomcat密码并上传webshell

tomcat基础认证爆破 暴力破解 进入vulnhub的tomcat8目录&#xff0c;启动环境 由于tomcat密码默认最大尝试错误次数为5次&#xff0c;需要修改server.xml&#xff0c;修改下面字段 failureCount"10000000000" lockOutTime"0"tomcat默认界面&#xff0c;…

一篇博客读懂队列——Queue

目录 一、队列的概念和结构 ​二、队列的实现 2.1队列的初始化QueueInit 2.2队列的摧毁QueueDestroy 2.3插入结点QueuePush 2.4删除结点QueuePop 2.5返回队头QueueFront 2.6返回队尾QueueBack 2.7判断队列为空QueueEmpty 2.8统计队列数目QueueSize 一、队列的概念和…

打印字符(C++)

系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…

清理mac苹果电脑磁盘软件有哪些免费实用的?

苹果电脑是一款非常流行的操作系统设备&#xff0c;其稳定性和性能一直备受用户的喜爱。然而&#xff0c;随着时间的推移&#xff0c;我们使用电脑的过程中可能会发现磁盘上存储的数据越来越多&#xff0c;这不仅占用了宝贵的硬盘空间&#xff0c;还可能导致电脑运行变慢。因此…

Python入门教程:12个常用基础语法详解

文章目录 前言1.多个字符串组合为一个字符串2. 字符串拆分为子字符串列表3. 统计列表中元素的次数4.使用try-except-else-block模块5. 使用枚举函数得到key/value对6. 检查对象的内存使用情况7. 合并字典8. 计算执行一段代码所花费的时间9. 列表展开10. 列表采样11. 数字化12. …

开启学历新征程,电大搜题助您轻松获取知识

作为一名电大学者&#xff0c;有肩负着传递真实信息、宣传正面价值的使命&#xff0c;而今天我要向您介绍的是一款非常实用的学习工具——电大搜题微信公众号。通过该平台&#xff0c;您可以获得更多关于浙江开放大学和广播电视大学的学习资源&#xff0c;助您在学习和工作上取…

Linux操作系统使用及C高级编程-D4Linux shell命令(文件搜索、文件处理、压缩解压)

查看文件相关命令 cat cat file&#xff1a;将文件内容输出 cat&#xff1a;等待输入&#xff0c;在屏幕上输入什么&#xff0c;就输出什么 tac cat file&#xff1a;与cat相反&#xff0c;将文件内容从后往前输出 more more file&#xff1a;在屏幕上显示一页的文件内容&am…

C语言从文件 D://test.txt 读取字符串,将字符串中所有的大写字符改为小写字母并写回到源文件中

完整代码&#xff1a; /*从文件 D://test.txt 读取字符串&#xff0c;将字符串中所有的大写字母改为小写字母并写回 到源文件中*/ #include<stdio.h>//将字符串中所有的大写字母改为小写字母 void func(char *buff){while (*buff!\0){if (*buff>A&&*buff<…

客户下单时如何自动匹配到最近的门店

有些商家有多个门店&#xff0c;当客户下单时&#xff0c;希望能够将客户下的订单分配给最近的门店。下面就具体介绍一下在采云小程中是如何实现的。 首先&#xff0c;为了简便起见&#xff0c;请确定门店高级设置保持着默认设定。因为单独的商品管理模式以及独享的商品信息模…

使用GPT-4训练数据微调GPT-3.5 RAG管道

原文&#xff1a;使用GPT-4训练数据微调GPT-3.5 RAG管道 - 知乎 OpenAI在2023年8月22日宣布&#xff0c;现在可以对GPT-3.5 Turbo进行微调了。也就是说&#xff0c;我们可以自定义自己的模型了。然后LlamaIndex就发布了0.8.7版本&#xff0c;集成了微调OpenAI gpt-3.5 turbo的…