LAMPCAE/src/PluginWBFZExchangePlugin/DialogPCLGPMesh.cpp

449 lines
15 KiB
C++
Raw Normal View History

/**
* @file DialogPCLGPMesh.cpp
* @brief None
* @author (3045316072@qq.com)
* @version 2.5.0
* @date 2024/4/5
* @copyright Copyright (c) Since 2024 All rights reserved.
*/
// You may need to build the project (run Qt uic code generator) to get "ui_DialogPCLGPMesh.h"
// resolved
#include "DialogPCLGPMesh.h"
#include "ui_DialogPCLGPMesh.h"
#include "ModuleBase/ThreadTask.h"
#include "PythonModule/PyAgent.h"
#include "MeshData/meshSingleton.h"
#include "MeshData/meshSet.h"
#include <QMenu>
#include <QDebug>
#include <pcl/filters/statistical_outlier_removal.h>
#include <QtWidgets>
#include <QFileDialog>
#include <QFileInfo>
#include "MeshData/meshKernal.h"
#include "PointCloudOperator/PointCloudCommon.h"
#include "Settings/BusAPI.h"
#include "BaseTool.h"
#include "IO/IOConfig.h"
#include "ModuleBase/ThreadControl.h"
#include "ConfigOptions/ConfigOptions.h"
#include "ConfigOptions/MeshConfig.h"
#include "MeshData/meshSingleton.h"
#include "MeshData/meshKernal.h"
#include <vtkSmartPointer.h>
#include <vtkDataSetReader.h>
#include <vtkDataSet.h>
#include <vtkSTLReader.h>
#include <vtkTecplotReader.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkAppendFilter.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnstructuredGridWriter.h>
#include <vtkAppendFilter.h>
#include <QFileInfo>
#include <QTextCodec>
#include <vtkSTLWriter.h>
#include "Common/DebugLogger.h"
// auto meshData = MeshData::MeshData::getInstance();
namespace MainWidget {
DialogPCLGPMesh::DialogPCLGPMesh(GUI::MainWindow* parent)
: QFDialog(parent)
, _ui(new Ui::DialogPCLGPMesh)
, _mw(parent)
, _selectdlg(new DialogSelectComponents(parent))
{
_ui->setupUi(this);
_ui->geoSelectPoint->setToolTip(tr("Clicked Button Selected Components"));
setWindowTitle(tr("GP Meshing"));
_ui->listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(_ui->geoSelectPoint, &QPushButton::clicked, [=]() {
_selectdlg->clearSelectItems();
_selectdlg->exec();
});
connect(_selectdlg, SIGNAL(selectedComponentsSig(QList<MeshData::MeshSet*>)), this,
SLOT(selectedComponentsSlot(QList<MeshData::MeshSet*>)));
connect(_ui->listWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this,
SLOT(customContextMenuRequestedSlot(const QPoint&)));
}
DialogPCLGPMesh::~DialogPCLGPMesh()
{
delete _ui;
_ui = NULL;
delete _selectdlg;
_selectdlg = NULL;
}
void DialogPCLGPMesh::accept()
{
if(_components.size() == 0)
return;
QString componentIds;
for(auto component : _components)
componentIds.append(QString(",%1").arg(component->getID()));
componentIds.remove(0, 1);
double SearchRadius = _ui->SearchRadius->value();
double Mu = _ui->Mu->value();
int MaximumNearestNeighbors = _ui->MaximumNearestNeighbors->value();
double MaximumSurfaceAngle = _ui->MaximumSurfaceAngle->value();
double MaximumAngle = _ui->MaximumAngle->value();
double MinimumAngle = _ui->MinimumAngle->value();
QString outfilename = "filter";
for(auto component : _components)
outfilename.append(QString("_%1").arg(component->getName()));
// 确定是否保存结果文件
QMessageBox::StandardButton result =
QMessageBox::critical(this, "info", "save as result ?");
QString filepath =
JoinPath(Setting::BusAPI::instance()->getWorkingDir(), outfilename + "_tmep.pcd");
QString AbFileName = filepath;
if(result == QMessageBox::StandardButton::Ok
|| result == QMessageBox::StandardButton::Yes) {
DebugInfo("outfilename ok ok \n");
QStringList suffixlist = IO::IOConfigure::getMeshExporters();
if(suffixlist.isEmpty()) {
QMessageBox::warning(this, tr("Warning"), tr("The MeshPlugin is not installed !"));
return;
}
QStringList meshsuffix = ConfigOption::ConfigOption::getInstance()
->getMeshConfig()
->getExportSuffix(ConfigOption::MeshDataType::vtkMesh)
.split(";");
QStringList list;
for(QString s : meshsuffix) {
for(int i = 0; i < suffixlist.size(); i++) {
QString suffix = suffixlist.at(i);
if(suffix.contains(s))
list.append(suffix);
}
}
std::sort(list.begin(), list.end());
QString suffixes = list.join(";;");
QString workDir = Setting::BusAPI::instance()->getWorkingDir();
QFileDialog dlg(this, tr("Export mesh"), workDir, suffixes);
dlg.setAcceptMode(QFileDialog::AcceptSave);
if(dlg.exec() != QFileDialog::FileName)
return;
QString aSuffix = dlg.selectedNameFilter();
QString aFileName = dlg.selectedFiles().join(",");
if(!(aFileName.isEmpty())) {
filepath = aFileName;
} else {
}
AbFileName = filepath;
} else { // 不保存成点云数据
}
DebugInfo("outfilename %s \n", AbFileName.toStdString().c_str());
// 启动线程
auto pclremesh =
new WBFZ::PCLGPMesh(AbFileName, WBFZ::PointCloudOperation::POINTCLOUD_MESH, _mainWindow,
componentIds, SearchRadius, Mu, MaximumNearestNeighbors,
MaximumSurfaceAngle, MaximumAngle, MinimumAngle);
ModuleBase::ThreadControl* tc = new ModuleBase::ThreadControl(pclremesh);
emit tc->threadStart(); // emit MSHwriter->start();
QFDialog::accept();
}
void DialogPCLGPMesh::selectedComponentsSlot(QList<MeshData::MeshSet*> components)
{
for(MeshData::MeshSet* set : components) {
if(_components.contains(set))
continue;
_components.append(set);
_ui->listWidget->addItem(set->getName());
}
}
void DialogPCLGPMesh::customContextMenuRequestedSlot(const QPoint& point)
{
QListWidgetItem* curItem = _ui->listWidget->itemAt(point);
if(!curItem)
return;
QMenu* menu = new QMenu(this);
QAction* deleteItem = new QAction(tr("delete this item"));
menu->addAction(deleteItem);
connect(menu, &QMenu::triggered, [=]() { removeCurrentItem(curItem); });
menu->exec(QCursor::pos());
}
void DialogPCLGPMesh::removeCurrentItem(QListWidgetItem* curItem)
{
auto meshData = MeshData::MeshData::getInstance();
auto meshSet = meshData->getMeshSetByName(curItem->text());
if(!meshSet)
return;
_components.removeOne(meshSet);
_ui->listWidget->removeItemWidget(curItem);
delete curItem;
}
} // namespace MainWidget
namespace WBFZ {
PCLGPMesh::PCLGPMesh(const QString& fileName, WBFZ::PointCloudOperation operation,
GUI::MainWindow* mw, QString componentIds, double SearchRadius, double Mu,
int MaximumNearestNeighbors, double MaximumSurfaceAngle,
double MaximumAngle, double MinimumAngle)
: ModuleBase::ThreadTask(mw)
, _operation(operation)
, _fileName(fileName)
, _componentIds(componentIds)
, _SearchRadius(SearchRadius)
, _Mu(Mu)
, _MaximumNearestNeighbors(MaximumNearestNeighbors)
, _MaximumSurfaceAngle(MaximumSurfaceAngle)
, _MaximumAngle(MaximumAngle)
, _MinimumAngle(MinimumAngle)
{
}
PCLGPMesh::~PCLGPMesh() {}
void PCLGPMesh::defaultMeshFinished()
{
if(_threadRuning) {
QString information{};
ModuleBase::Message msg;
if(_operation == POINTCLOUD_FILTER || _operation == POINTCLOUD_MESH) {
if(_success) {
emit _mainwindow->updateMeshTreeSig();
emit _mainwindow->updateSetTreeSig();
emit _mainwindow->updateActionStatesSig();
// emit _mainwindow->updateActionsStatesSig();
emit _mainwindow->getSubWindowManager()->openPreWindowSig();
emit _mainwindow->updatePreMeshActorSig();
information = QString("Successful Import Mesh From \"%1\"").arg(_fileName);
msg.type = Common::Message::Normal;
msg.message = information;
auto meshdata = MeshData::MeshData::getInstance();
// meshdata->generateDisplayDataSet();
const int nk = meshdata->getKernalCount();
if(nk <= 0)
return;
auto k = meshdata->getKernalAt(nk - 1);
if(k != nullptr)
k->setPath(_fileName);
} else {
information = QString("Failed Filter From \"%1\"").arg(_fileName);
msg.type = Common::Message::Error;
msg.message = information;
}
} else {
}
}
// emit showInformation(information);
// emit _mainwindow->printMessageToMessageWindow(msg);
ModuleBase::ThreadTask::threadTaskFinished();
Py::PythonAgent::getInstance()->unLock();
}
void PCLGPMesh::setThreadRunState(bool flag)
{
_success = flag;
}
void PCLGPMesh::run()
{
ModuleBase::ThreadTask::run();
bool result = false;
switch(_operation) {
case POINTCLOUD_MESH:
emit showInformation(tr("POINTCLOUD_MESH From \"%1\"").arg(_fileName));
result = remeshtaskProcess();
setThreadRunState(result);
break;
default:
break;
}
DebugInfo("run ok _success %d _threadRuning %d \n", _success, _threadRuning);
defaultMeshFinished();
}
bool PCLGPMesh::remeshtaskProcess()
{
QString componentIds = _componentIds;
double SearchRadius = _SearchRadius;
double Mu = _Mu;
int MaximumNearestNeighbors = _MaximumNearestNeighbors;
double MaximumSurfaceAngle = _MaximumSurfaceAngle;
double MaximumAngle = _MaximumAngle;
double MinimumAngle = _MinimumAngle;
emit _mainwindow->printMessage(Common::Message::Normal, "PCLGPMeshAlg");
// 获取vtdataset
QStringList qCompontIds = QString(componentIds).split(',');
MeshData::MeshSet* meshSet = NULL;
MeshData::MeshKernal* meshKernal = NULL;
MeshData::MeshData* meshData = MeshData::MeshData::getInstance();
QString kernalName, transformedName, setType, ids;
// 筛选其中的点云数据
// 创建 vtkCellDataToPointData 过滤器
vtkSmartPointer<vtkGeometryFilter> cellToPointFilter =
vtkSmartPointer<vtkGeometryFilter>::New();
QString outfilename;
for(QString compontId : qCompontIds) {
meshSet = meshData->getMeshSetByID(compontId.toInt());
DebugInfo("point count %d : %d \n", compontId.toInt(), meshSet == nullptr);
if(!meshSet)
continue;
outfilename = meshSet->getName();
vtkPolyData* temppolyData =
PointCloudOperator::PointCloudCommon::meshSetToVtkDataset(meshSet);
cellToPointFilter->SetInputData(temppolyData);
DebugInfo("point count %d : %d \n", compontId.toInt(), temppolyData == nullptr);
}
// 执行过滤操作
cellToPointFilter->Update();
DebugInfo("cellToPointFilter \n");
// 获取过滤后的 vtkPolyData
vtkPolyData* outpolyData = cellToPointFilter->GetOutput();
DebugInfo("outpolyData ");
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud_with_rgba(
new pcl::PointCloud<pcl::PointXYZRGBA>);
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud_filtered(
new pcl::PointCloud<pcl::PointXYZRGBA>);
pcl::io::vtkPolyDataToPointCloud(outpolyData, *cloud_with_rgba);
// 创建滤波器对象
// 计算点云滤波结果
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr output(
new pcl::PointCloud<pcl::PointXYZRGBA>); // 输出结果
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(
new pcl::PointCloud<pcl::PointNormal>);
pcl::PolygonMesh* mesh = new pcl::PolygonMesh;
// 估计法线
PointCloudOperator::PointCloudCommon::NormalEstimation(cloud_with_rgba, cloud_with_normals);
//------------------定义搜索树对象------------------------
pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
tree2->setInputCloud(cloud_with_normals);
pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3; // 定义三角化对象
gp3.setSearchRadius(SearchRadius); // 设置连接点之间的最大距离(即三角形的最大边长)
gp3.setMu(Mu); // 设置被样本点搜索其临近点的最远距离,为了适应点云密度的变化
gp3.setMaximumNearestNeighbors(MaximumNearestNeighbors); // 设置样本点可搜索的邻域个数
gp3.setMaximumSurfaceAngle(
MaximumSurfaceAngle); // 设置某点法线方向偏离样本点法线方向的最大角度
gp3.setMinimumAngle(MinimumAngle); // 设置三角化后得到三角形内角的最小角度
gp3.setMaximumAngle(MaximumAngle); // 设置三角化后得到三角形内角的最大角度
gp3.setNormalConsistency(false); // 设置该参数保证法线朝向一致
// qDebug() << "[GP] start reconstruct :" << cloud_in->points.size();
// Get result
DebugInfo("setInputCloud point count : %d \n", cloud_with_normals->size());
gp3.setInputCloud(cloud_with_normals); // 设置输入点云为有向点云
gp3.setSearchMethod(tree2); // 设置搜索方式
gp3.reconstruct(*mesh); // 重建提取三角化
emit _mainwindow->printMessageToMessageWindow(
QString("edge count %1 ").arg(QString::number(mesh->polygons.size())));
DebugInfo("remesh over ,conver init %d \n", mesh->polygons.size());
// 转换处理结果
vtkPolyData* polydata = vtkPolyData::New(); // 创建新的指针,智能指针会释放
size_t pointcount = PointCloudOperator::PointCloudCommon::mesh2vtk(*mesh, polydata);
DebugInfo("remesh over ,conver over polydata %d , pointcount %d\n", polydata == nullptr,
pointcount);
vtkDataSet* dataset =
vtkDataSet::SafeDownCast(polydata); // 默认完成 vtkpolydata --> vtkdataset
// outpolyData->Delete(); // 释放指针 ,
// 千万不能释放这个指针,不然程序回调时,会直接内存崩溃
DebugInfo("PCLGPMeshAlg successfully!! wait for writing file \n");
// vtkDataSet*
// dataset=PCLGPMeshAlg(_componentIds,_SearchRadius,_Mu,_MaximumNearestNeighbors,_MaximumSurfaceAngle,_MaximumAngle,_MinimumAngle);
DebugInfo("remesh \n");
QString filepath = _fileName;
// 加载文件
QFileInfo info(filepath);
QString name = info.fileName();
QString path = info.filePath();
QString suffix = info.suffix().toLower();
QTextCodec* codec = QTextCodec::codecForName("GB18030");
QByteArray ba = codec->fromUnicode(filepath);
std::string outFileName = ba.data();
DebugInfo("writing %s suffix %s !! \n", ba.data(), suffix.toStdString().c_str());
if(suffix == "vtk") {
vtkSmartPointer<vtkUnstructuredGridWriter> writer =
vtkSmartPointer<vtkUnstructuredGridWriter>::New();
writer->SetInputData(dataset);
writer->SetFileTypeToBinary();
writer->SetFileName(ba);
writer->Write();
DebugInfo("writing vtk !! \n");
} else if(suffix == "stl") {
QTextCodec* codec = QTextCodec::codecForName("GB18030");
QByteArray ba = codec->fromUnicode(_fileName);
vtkSmartPointer<vtkSTLWriter> writer = vtkSmartPointer<vtkSTLWriter>::New();
writer->SetInputData(dataset);
writer->SetFileTypeToBinary();
writer->SetFileName(ba);
writer->Write();
DebugInfo("writing stl !! \n");
} else {
}
DebugInfo("writing finish !! %d \n", dataset == nullptr);
// 回调函数
DebugInfo("remeshtaskProcess !! %d \n", dataset == nullptr);
// QFileInfo info(_fileName);
// QString name = info.fileName();
// QString path = info.filePath();
// QString suffix = info.suffix().toLower();
DebugInfo("dataset %d _fileName %s \n", dataset == nullptr,
_fileName.toStdString().c_str());
// 保存流程文件
if(dataset != nullptr) {
DebugInfo("load result in meshKernal \n");
auto* k = new MeshData::MeshKernal;
k->setName(name);
k->setPath(path);
auto meshData = MeshData::MeshData::getInstance();
int nKernal = meshData->getKernalCount();
for(int iKernal = 0; iKernal < nKernal; ++iKernal) {
MeshData::MeshKernal* temp = meshData->getKernalAt(iKernal);
if(temp->getPath() == path) { ///< MG same file update
meshData->removeKernalAt(iKernal);
break;
}
}
k->setMeshData(dataset);
meshData->appendMeshKernal(k);
DebugInfo("load result in meshKernal \n");
return true;
} else {
return false;
}
return false;
}
} // namespace WBFZ