From 4f48b2e42992c2c619de8bf5cc37e7c3770efe11 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 28 Feb 2020 16:11:35 +0100 Subject: [PATCH] Mesh: [skip ci] refactor RemeshGmsh to use it for other purposes --- src/Mod/Mesh/Gui/RemeshGmsh.cpp | 367 +++++++++++++++++++++----------- src/Mod/Mesh/Gui/RemeshGmsh.h | 33 ++- 2 files changed, 267 insertions(+), 133 deletions(-) diff --git a/src/Mod/Mesh/Gui/RemeshGmsh.cpp b/src/Mod/Mesh/Gui/RemeshGmsh.cpp index f1a7a0d990..de705d4af7 100644 --- a/src/Mod/Mesh/Gui/RemeshGmsh.cpp +++ b/src/Mod/Mesh/Gui/RemeshGmsh.cpp @@ -45,11 +45,10 @@ using namespace MeshGui; -class RemeshGmsh::Private { +class GmshWidget::Private { public: - Private(Mesh::Feature* mesh, QWidget* parent) - : mesh(mesh) - , mesher(parent) + Private(QWidget* parent) + : gmsh(parent) { } @@ -68,30 +67,24 @@ public: Ui_RemeshGmsh ui; QPointer label; QPointer syntax; - App::DocumentObjectWeakPtrT mesh; - MeshCore::MeshKernel copy; - QProcess mesher; + QProcess gmsh; QTime time; - std::string stlFile; - std::string geoFile; }; -RemeshGmsh::RemeshGmsh(Mesh::Feature* mesh, QWidget* parent, Qt::WindowFlags fl) +GmshWidget::GmshWidget(QWidget* parent, Qt::WindowFlags fl) : QWidget(parent, fl) - , d(new Private(mesh, parent)) + , d(new Private(parent)) { - connect(&d->mesher, SIGNAL(started()), this, SLOT(started())); - connect(&d->mesher, SIGNAL(finished(int, QProcess::ExitStatus)), + connect(&d->gmsh, SIGNAL(started()), this, SLOT(started())); + connect(&d->gmsh, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); - connect(&d->mesher, SIGNAL(errorOccurred(QProcess::ProcessError)), + connect(&d->gmsh, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(errorOccurred(QProcess::ProcessError))); - connect(&d->mesher, SIGNAL(readyReadStandardError()), + connect(&d->gmsh, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError())); - connect(&d->mesher, SIGNAL(readyReadStandardOutput()), + connect(&d->gmsh, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput())); - // Copy mesh that is used each time when applying gmsh's remeshing function - d->copy = mesh->Mesh.getValue().getKernel(); d->ui.setupUi(this); d->ui.fileChooser->onRestore(); d->syntax = new Gui::DockWnd::ReportHighlighter(d->ui.outputWindow); @@ -107,17 +100,14 @@ RemeshGmsh::RemeshGmsh(Mesh::Feature* mesh, QWidget* parent, Qt::WindowFlags fl) d->ui.method->addItem(QString::fromLatin1("BAMG"), static_cast(5)); d->ui.method->addItem(tr("Frontal Quad"), static_cast(6)); d->ui.method->addItem(tr("Parallelograms"), static_cast(9)); - - d->stlFile = App::Application::getTempFileName() + "mesh.stl"; - d->geoFile = App::Application::getTempFileName() + "mesh.geo"; } -RemeshGmsh::~RemeshGmsh() +GmshWidget::~GmshWidget() { d->ui.fileChooser->onSave(); } -void RemeshGmsh::changeEvent(QEvent *e) +void GmshWidget::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { d->ui.retranslateUi(this); @@ -158,7 +148,202 @@ Mesh 2; Coherence Mesh; // Remove duplicate vertices #endif -void RemeshGmsh::accept() +bool GmshWidget::writeProject(QString& inpFile, QString& outFile) +{ + Q_UNUSED(inpFile) + Q_UNUSED(outFile) + + return false; +} + +bool GmshWidget::loadOutput() +{ + return false; +} + +int GmshWidget::meshingAlgorithm() const +{ + return d->ui.method->itemData(d->ui.method->currentIndex()).toInt(); +} + +double GmshWidget::getAngle() const +{ + return d->ui.angle->value().getValue(); +} + +double GmshWidget::getMaxSize() const +{ + return d->ui.maxSize->value().getValue(); +} + +double GmshWidget::getMinSize() const +{ + return d->ui.minSize->value().getValue(); +} + +void GmshWidget::accept() +{ + QString inpFile; + QString outFile; + if (writeProject(inpFile, outFile)) { + // ./gmsh - -bin -2 /tmp/mesh.geo -o /tmp/best.stl + QString proc = d->ui.fileChooser->fileName(); + QStringList args; + args << QLatin1String("-") + << QLatin1String("-bin") + << QLatin1String("-2") + << inpFile + << QLatin1String("-o") + << outFile; + d->gmsh.start(proc, args); + + d->time.start(); + d->ui.labelTime->setText(tr("Time:")); + } +} + +void GmshWidget::readyReadStandardError() +{ + QByteArray msg = d->gmsh.readAllStandardError(); + if (msg.startsWith("\0[1m\0[31m")) { + msg = msg.mid(9); + } + if (msg.endsWith("\0[0m")) { + msg.chop(5); + } + + QString text = QString::fromUtf8(msg.data()); + d->appendText(text, true); +} + +void GmshWidget::readyReadStandardOutput() +{ + QByteArray msg = d->gmsh.readAllStandardOutput(); + QString text = QString::fromUtf8(msg.data()); + d->appendText(text, false); +} + +void GmshWidget::on_killButton_clicked() +{ + if (d->gmsh.state() == QProcess::Running) { + d->gmsh.kill(); + d->gmsh.waitForFinished(1000); + d->ui.killButton->setDisabled(true); + } +} + +void GmshWidget::on_clearButton_clicked() +{ + d->ui.outputWindow->clear(); +} + +void GmshWidget::started() +{ + d->ui.killButton->setEnabled(true); + if (!d->label) { + d->label = new Gui::StatusWidget(this); + d->label->setAttribute(Qt::WA_DeleteOnClose); + d->label->setStatusText(tr("Running gmsh...")); + d->label->show(); + } +} + +void GmshWidget::finished(int /*exitCode*/, QProcess::ExitStatus exitStatus) +{ + d->ui.killButton->setDisabled(true); + if (d->label) + d->label->close(); + + d->ui.labelTime->setText(QString::fromLatin1("%1 %2 ms").arg(tr("Time:")).arg(d->time.elapsed())); + if (exitStatus == QProcess::NormalExit) { + loadOutput(); + } +} + +void GmshWidget::errorOccurred(QProcess::ProcessError error) +{ + QString msg; + switch (error) { + case QProcess::FailedToStart: + msg = tr("Failed to start"); + break; + default: + break; + } + + if (!msg.isEmpty()) { + QMessageBox::warning(this, tr("Error"), msg); + } +} + +void GmshWidget::reject() +{ + on_killButton_clicked(); +} + +// ------------------------------------------------- + +class RemeshGmsh::Private { +public: + Private(Mesh::Feature* mesh) + : mesh(mesh) + { + } + +public: + App::DocumentObjectWeakPtrT mesh; + MeshCore::MeshKernel copy; + std::string stlFile; + std::string geoFile; +}; + +RemeshGmsh::RemeshGmsh(Mesh::Feature* mesh, QWidget* parent, Qt::WindowFlags fl) + : GmshWidget(parent, fl) + , d(new Private(mesh)) +{ + // Copy mesh that is used each time when applying gmsh's remeshing function + d->copy = mesh->Mesh.getValue().getKernel(); + d->stlFile = App::Application::getTempFileName() + "mesh.stl"; + d->geoFile = App::Application::getTempFileName() + "mesh.geo"; +} + +RemeshGmsh::~RemeshGmsh() +{ +} + +#if 0 // this is for meshing a CAD shape see gmshtools.py write_geo +// geo file for meshing with Gmsh meshing software created by FreeCAD + +// open brep geometry +Merge "/tmp/fcfem_f1enjjfa/Part__Feature_Geometry.brep"; + +// Characteristic Length +// no boundary layer settings for this mesh +// min, max Characteristic Length +Mesh.CharacteristicLengthMax = 1e+22; +Mesh.CharacteristicLengthMin = 0.0; + +// optimize the mesh +Mesh.Optimize = 1; +Mesh.OptimizeNetgen = 0; +Mesh.HighOrderOptimize = 0; // for more HighOrderOptimize parameter check http://gmsh.info/doc/texinfo/gmsh.html + +// mesh order +Mesh.ElementOrder = 2; +Mesh.SecondOrderLinear = 1; // Second order nodes are created by linear interpolation instead by curvilinear + +// mesh algorithm, only a few algorithms are usable with 3D boundary layer generation +// 2D mesh algorithm (1=MeshAdapt, 2=Automatic, 5=Delaunay, 6=Frontal, 7=BAMG, 8=DelQuad) +Mesh.Algorithm = 2; +// 3D mesh algorithm (1=Delaunay, 2=New Delaunay, 4=Frontal, 5=Frontal Delaunay, 6=Frontal Hex, 7=MMG3D, 9=R-tree) +Mesh.Algorithm3D = 1; + +// meshing +Geometry.Tolerance = 1e-06; // set geometrical tolerance (also used for merging nodes) +Mesh 2; +Coherence Mesh; // Remove duplicate vertices +#endif +bool RemeshGmsh::writeProject(QString& inpFile, QString& outFile) { if (!d->mesh.expired()) { Base::FileInfo stl(d->stlFile); @@ -168,12 +353,12 @@ void RemeshGmsh::accept() stlOut.close(); // Parameters - int algorithm = d->ui.method->itemData(d->ui.method->currentIndex()).toInt(); - double maxSize = d->ui.maxSize->value().getValue(); + int algorithm = meshingAlgorithm(); + double maxSize = getMaxSize(); if (maxSize == 0.0) maxSize = 1.0e22; - double minSize = d->ui.minSize->value().getValue(); - double angle = d->ui.angle->value().getValue(); + double minSize = getMinSize(); + double angle = getAngle(); int maxAngle = 120; int minAngle = 20; @@ -212,118 +397,40 @@ void RemeshGmsh::accept() << "Volume(1) = {1};\n"; geoOut.close(); - // ./gmsh - -bin -2 /tmp/mesh.geo -o /tmp/best.stl - QString proc = d->ui.fileChooser->fileName(); - QStringList args; - args << QLatin1String("-") - << QLatin1String("-bin") - << QLatin1String("-2") - << QString::fromUtf8(d->geoFile.c_str()) - << QLatin1String("-o") - << QString::fromUtf8(d->stlFile.c_str()); - d->mesher.start(proc, args); + inpFile = QString::fromUtf8(d->geoFile.c_str()); + outFile = QString::fromUtf8(d->stlFile.c_str()); - d->time.start(); - d->ui.labelTime->setText(tr("Time:")); - } -} - -void RemeshGmsh::readyReadStandardError() -{ - QByteArray msg = d->mesher.readAllStandardError(); - if (msg.startsWith("\0[1m\0[31m")) { - msg = msg.mid(9); - } - if (msg.endsWith("\0[0m")) { - msg.chop(5); + return true; } - QString text = QString::fromUtf8(msg.data()); - d->appendText(text, true); + return false; } -void RemeshGmsh::readyReadStandardOutput() +bool RemeshGmsh::loadOutput() { - QByteArray msg = d->mesher.readAllStandardOutput(); - QString text = QString::fromUtf8(msg.data()); - d->appendText(text, false); -} + if (d->mesh.expired()) + return false; -void RemeshGmsh::on_killButton_clicked() -{ - if (d->mesher.state() == QProcess::Running) { - d->mesher.kill(); - d->mesher.waitForFinished(1000); - d->ui.killButton->setDisabled(true); - } -} + // Now read-in modified mesh + Base::FileInfo stl(d->stlFile); + Base::FileInfo geo(d->geoFile); -void RemeshGmsh::on_clearButton_clicked() -{ - d->ui.outputWindow->clear(); -} + Mesh::MeshObject kernel; + MeshCore::MeshInput input(kernel.getKernel()); + Base::ifstream stlIn(stl, std::ios::in | std::ios::binary); + input.LoadBinarySTL(stlIn); + stlIn.close(); + kernel.harmonizeNormals(); -void RemeshGmsh::started() -{ - d->ui.killButton->setEnabled(true); - if (!d->label) { - d->label = new Gui::StatusWidget(this); - d->label->setAttribute(Qt::WA_DeleteOnClose); - d->label->setStatusText(tr("Running remeshing...")); - d->label->show(); - } -} + Mesh::Feature* fea = d->mesh.get(); + App::Document* doc = fea->getDocument(); + doc->openTransaction("Remesh"); + fea->Mesh.setValue(kernel.getKernel()); + doc->commitTransaction(); + stl.deleteFile(); + geo.deleteFile(); -void RemeshGmsh::finished(int /*exitCode*/, QProcess::ExitStatus exitStatus) -{ - d->ui.killButton->setDisabled(true); - if (d->label) - d->label->close(); - - d->ui.labelTime->setText(QString::fromLatin1("%1 %2 ms").arg(tr("Time:")).arg(d->time.elapsed())); - if (exitStatus == QProcess::NormalExit) { - if (!d->mesh.expired()) { - // Now read-in modified mesh - Base::FileInfo stl(d->stlFile); - Base::FileInfo geo(d->geoFile); - - Mesh::MeshObject kernel; - MeshCore::MeshInput input(kernel.getKernel()); - Base::ifstream stlIn(stl, std::ios::in | std::ios::binary); - input.LoadBinarySTL(stlIn); - stlIn.close(); - kernel.harmonizeNormals(); - - Mesh::Feature* fea = d->mesh.get(); - App::Document* doc = fea->getDocument(); - doc->openTransaction("Remesh"); - fea->Mesh.setValue(kernel.getKernel()); - doc->commitTransaction(); - stl.deleteFile(); - geo.deleteFile(); - } - } -} - -void RemeshGmsh::errorOccurred(QProcess::ProcessError error) -{ - QString msg; - switch (error) { - case QProcess::FailedToStart: - msg = tr("Failed to start"); - break; - default: - break; - } - - if (!msg.isEmpty()) { - QMessageBox::warning(this, tr("Error"), msg); - } -} - -void RemeshGmsh::reject() -{ - on_killButton_clicked(); + return true; } // ------------------------------------------------- diff --git a/src/Mod/Mesh/Gui/RemeshGmsh.h b/src/Mod/Mesh/Gui/RemeshGmsh.h index f90ce8a50e..b2348379c0 100644 --- a/src/Mod/Mesh/Gui/RemeshGmsh.h +++ b/src/Mod/Mesh/Gui/RemeshGmsh.h @@ -47,18 +47,24 @@ namespace MeshGui { * Non-modal dialog to remesh an existing mesh. * @author Werner Mayer */ -class MeshGuiExport RemeshGmsh : public QWidget +class MeshGuiExport GmshWidget : public QWidget { Q_OBJECT public: - RemeshGmsh(Mesh::Feature* mesh, QWidget* parent = 0, Qt::WindowFlags fl = 0); - ~RemeshGmsh(); + GmshWidget(QWidget* parent = 0, Qt::WindowFlags fl = 0); + ~GmshWidget(); void accept(); void reject(); protected: void changeEvent(QEvent *e); + int meshingAlgorithm() const; + double getAngle() const; + double getMaxSize() const; + double getMinSize() const; + virtual bool writeProject(QString& inpFile, QString& outFile); + virtual bool loadOutput(); private Q_SLOTS: void started(); @@ -75,6 +81,27 @@ private: std::unique_ptr d; }; +/** + * Non-modal dialog to remesh an existing mesh. + * @author Werner Mayer + */ +class MeshGuiExport RemeshGmsh : public GmshWidget +{ + Q_OBJECT + +public: + RemeshGmsh(Mesh::Feature* mesh, QWidget* parent = 0, Qt::WindowFlags fl = 0); + ~RemeshGmsh(); + +protected: + virtual bool writeProject(QString& inpFile, QString& outFile); + virtual bool loadOutput(); + +private: + class Private; + std::unique_ptr d; +}; + /** * Embed the panel into a task dialog. */