/** * @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 "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(); QFDialog::accept(); } 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() { 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 cellToPointFilter = vtkSmartPointer::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::Ptr cloud_with_rgba( new pcl::PointCloud); pcl::PointCloud::Ptr cloud_filtered( new pcl::PointCloud); pcl::io::vtkPolyDataToPointCloud(outpolyData, *cloud_with_rgba); // 创建滤波器对象 // 计算点云滤波结果 pcl::PointCloud::Ptr output( new pcl::PointCloud); // 输出结果 pcl::PointCloud::Ptr cloud_with_normals( new pcl::PointCloud); pcl::PolygonMesh* mesh = new pcl::PolygonMesh; // 估计法线 PointCloudOperator::PointCloudCommon::NormalEstimation(cloud_with_rgba, cloud_with_normals); //------------------定义搜索树对象------------------------ pcl::search::KdTree::Ptr tree2(new pcl::search::KdTree); tree2->setInputCloud(cloud_with_normals); pcl::GreedyProjectionTriangulation 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 writer = vtkSmartPointer::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 writer = vtkSmartPointer::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