/** * @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 #include #include #include #include #include #include "MeshData/meshKernal.h" #include "PointCloudOperator/PointCloudCommon.h" #include "PointCloudOperator/PointCloudMesh.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 #include #include #include #include #include #include #include #include #include #include #include #include #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)), this, SLOT(selectedComponentsSlot(QList))); 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(); DebugInfo("tc overing %s \n", AbFileName.toStdString().c_str()); QFDialog::accept(); DebugInfo("QFDialog::accept() \n"); } void DialogPCLGPMesh::selectedComponentsSlot(QList 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() { ModuleBase::ThreadTask::threadTaskFinished(); Py::PythonAgent::getInstance()->unLock(); if (_threadRuning) { QString information{}; ModuleBase::Message msg; if (_operation == POINTCLOUD_FILTER || _operation == POINTCLOUD_MESH) { if (_success) { information = QString("Successful resurface Mesh From \"%1\"").arg(_fileName); msg.type = Common::Message::Normal; msg.message = information; qDebug() << "Successful Import Mesh From " << _fileName; QFileInfo info(_fileName); QString name = info.fileName(); QString path = info.filePath(); QString suffix = info.suffix().toLower(); if(info.exists()){ if(suffix.toLower().contains("stl")){ suffix="STL(*.stl)"; emit _mainwindow->importMeshSIGN(_fileName,suffix,-1); }else if(suffix.toLower().contains("vtk")){ suffix="VTK(*.vtk)"; emit _mainwindow->importMeshSIGN(_fileName,suffix,-1); }else if(suffix.toLower().contains("neu")){ suffix="Gambit(*.neu)"; emit _mainwindow->importMeshSIGN(_fileName,suffix,-1); }else{ information = QString("Failed Filter From \"%1\"").arg(_fileName); msg.type = Common::Message::Error; msg.message = information; qDebug() << "Failed Import Mesh From " << _fileName; } } } else { information = QString("Failed resurface From \"%1\"").arg(_fileName); msg.type = Common::Message::Error; msg.message = information; qDebug() << "Failed resurface Mesh From " << _fileName; } } else { } emit showInformation(information); emit _mainwindow->printMessageToMessageWindow(msg); } qDebug()<<"PCLGPMesh::defaultMeshFinished 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; 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 cellToPointFilter = vtkSmartPointer::New(); vtkSmartPointer inpolyData=vtkSmartPointer::New(); vtkSmartPointer polydata2 = vtkSmartPointer::New(); vtkSmartPointer polydata = vtkSmartPointer::New(); for (QString compontId: qCompontIds) { meshSet = meshData->getMeshSetByID(compontId.toInt()); DebugInfo("point count %d : %d \n", compontId.toInt(), meshSet == nullptr); if (!meshSet) continue; QString outfilename = meshSet->getName(); cellToPointFilter->SetInputData(PointCloudOperator::PointCloudCommon::meshSetToVtkDataset(meshSet)); DebugInfo("point count %d \n", compontId.toInt()); } // 执行过滤操作 cellToPointFilter->Update(); DebugInfo("cellToPointFilter \n"); // 获取过滤后的 vtkPolyData inpolyData = cellToPointFilter->GetOutput(); pcl::PointCloud::Ptr cloud_with_rgba(new pcl::PointCloud); pcl::PointCloud::Ptr cloud_filtered( new pcl::PointCloud); pcl::PointCloud::Ptr output( new pcl::PointCloud); // 输出结果 pcl::PointCloud::Ptr cloud_with_normals(new pcl::PointCloud); // 法线向量 pcl::search::KdTree::Ptr tree2(new pcl::search::KdTree); pcl::io::vtkPolyDataToPointCloud(inpolyData, *cloud_with_rgba); qDebug()<<"cloud_with_rgba "<size(); PointCloudOperator::PointCloudCommon::NormalEstimation(cloud_with_rgba, cloud_with_normals); tree2->setInputCloud(cloud_with_normals); // 构建搜索树 std::shared_ptr mesh (new pcl::PolygonMesh); // 非智能指针,需要释放 pcl::GreedyProjectionTriangulation gp3; // 定义三角化对象 gp3.setSearchRadius(_SearchRadius); // 设置连接点之间的最大距离(即三角形的最大边长) gp3.setMu(_Mu); // 设置被样本点搜索其临近点的最远距离,为了适应点云密度的变化 gp3.setMaximumNearestNeighbors(_MaximumNearestNeighbors); // 设置样本点可搜索的邻域个数 gp3.setMaximumSurfaceAngle(_MaximumSurfaceAngle); // 设置某点法线方向偏离样本点法线方向的最大角度 gp3.setMinimumAngle(_MaximumAngle); // 设置三角化后得到三角形内角的最小角度 gp3.setMaximumAngle(_MinimumAngle); // 设置三角化后得到三角形内角的最大角度 gp3.setNormalConsistency(false); // 设置该参数保证法线朝向一致 gp3.setInputCloud(cloud_with_normals); // 设置输入点云为有向点云 gp3.setSearchMethod(tree2); // 设置搜索方式 gp3.reconstruct(*mesh); // 重建提取三角化 -- 这里会导致程序崩溃,可能存在内存泄露, // DebugInfo("remesh over GP \n"); // return false; size_t pointcount = PointCloudOperator::PointCloudCommon::mesh2vtk(*mesh, polydata2); vtkSmartPointer geofilter=vtkSmartPointer::New(); geofilter->SetInputData(polydata2); geofilter->Update(); polydata=geofilter->GetOutput(); qDebug()<<"cellToPointFilter successfully!! wait for writing file dataset : "<GetNumberOfPoints()<<" "<GetNumberOfCells(); // if(PointCloudOperator::PointCloudMeshOperator::GP(inpolyData,polydata,_SearchRadius,_Mu,_MaximumNearestNeighbors,_MaximumSurfaceAngle,_MaximumAngle,_MinimumAngle)){ // // }else{ // return false; // } DebugInfo("PCLGPMeshAlg successfully!! wait for writing file dataset : %d \n",nullptr==polydata); if(nullptr!=polydata){ DebugInfo("PCLGPMeshAlg successfully!! wait for writing file dataset : %d %d\n",polydata->GetNumberOfPoints(),polydata->GetNumberOfCells()); }else{ return false; } QString filepath = _fileName; QFile outfile_presave(_fileName); if (outfile_presave.exists()) { // 如果存在文件,就删除 outfile_presave.remove(); } // 手动释放所有的变量 //return false; { // 写出到文件中 // 加载文件 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 writer = vtkSmartPointer::New(); writer->SetInputData(polydata); 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 writer = vtkSmartPointer::New(); writer->SetInputData(polydata); writer->SetFileTypeToBinary(); writer->SetFileName(ba); writer->Write(); DebugInfo("writing stl !! \n"); } else { } } DebugInfo("dataset %d _fileName %s \n", nullptr==polydata, _fileName.toStdString().c_str()); if (nullptr == polydata) { } else { DebugInfo("GP variable start release !! \n"); //dataset->Delete(); //dataset=nullptr; DebugInfo("GP variable finish release!! \n"); } QFile outfile(_fileName); if (outfile.exists()) { DebugInfo("GP main process sucessfully !! \n"); return true; } else { DebugInfo("GP main process fail !! \n"); return false; } DebugInfo("GP main process fail !! \n"); return false; } } // namespace WBFZ