From 1cb4625bfdacea2895edc056c752829e41ff1ea2 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 28 Feb 2020 21:15:17 +0100 Subject: [PATCH] MeshPart: add gmsh as another option to create mesh from shape --- src/Mod/Mesh/Gui/RemeshGmsh.cpp | 71 +---------- src/Mod/MeshPart/Gui/Tessellation.cpp | 164 +++++++++++++++++++++++++- src/Mod/MeshPart/Gui/Tessellation.h | 37 +++++- src/Mod/MeshPart/Gui/Tessellation.ui | 7 ++ 4 files changed, 210 insertions(+), 69 deletions(-) diff --git a/src/Mod/Mesh/Gui/RemeshGmsh.cpp b/src/Mod/Mesh/Gui/RemeshGmsh.cpp index de705d4af7..9651aac101 100644 --- a/src/Mod/Mesh/Gui/RemeshGmsh.cpp +++ b/src/Mod/Mesh/Gui/RemeshGmsh.cpp @@ -32,6 +32,7 @@ #include "RemeshGmsh.h" #include "ui_RemeshGmsh.h" +#include #include #include #include @@ -115,39 +116,6 @@ void GmshWidget::changeEvent(QEvent *e) QWidget::changeEvent(e); } -#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 GmshWidget::writeProject(QString& inpFile, QString& outFile) { Q_UNUSED(inpFile) @@ -183,6 +151,11 @@ double GmshWidget::getMinSize() const void GmshWidget::accept() { + if (d->gmsh.state() == QProcess::Running) { + Base::Console().Warning("Cannot start gmsh because it's already running\n"); + return; + } + QString inpFile; QString outFile; if (writeProject(inpFile, outFile)) { @@ -311,38 +284,6 @@ 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()) { diff --git a/src/Mod/MeshPart/Gui/Tessellation.cpp b/src/Mod/MeshPart/Gui/Tessellation.cpp index 4e3fa0fc53..5722f964da 100644 --- a/src/Mod/MeshPart/Gui/Tessellation.cpp +++ b/src/Mod/MeshPart/Gui/Tessellation.cpp @@ -36,11 +36,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -53,6 +56,10 @@ Tessellation::Tessellation(QWidget* parent) : QWidget(parent), ui(new Ui_Tessellation) { ui->setupUi(this); + gmsh = new Mesh2ShapeGmsh(this); + connect(gmsh, SIGNAL(processed()), this, SLOT(gmshProcessed())); + + ui->stackedWidget->addTab(gmsh, tr("gmsh")); ParameterGrp::handle handle = App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/Mod/Mesh/Meshing/Standard"); @@ -85,7 +92,8 @@ Tessellation::Tessellation(QWidget* parent) Gui::Command::doCommand(Gui::Command::Doc, "import MeshPart"); } catch (...) { - ui->stackedWidget->setDisabled(true); + ui->stackedWidget->setTabEnabled(Mefisto, false); + ui->stackedWidget->setTabEnabled(Netgen, false); } } @@ -154,6 +162,13 @@ void Tessellation::on_checkQuadDominated_toggled(bool on) ui->checkSecondOrder->setChecked(false); } +void Tessellation::gmshProcessed() +{ + bool doClose = !ui->checkBoxDontQuit->isChecked(); + if (doClose) + Gui::Control().reject(); +} + void Tessellation::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { @@ -233,12 +248,24 @@ bool Tessellation::accept() return false; } + bool doClose = !ui->checkBoxDontQuit->isChecked(); + int method = ui->stackedWidget->currentIndex(); + + // For gmsh the workflow is very different because it uses an executable + // and therefore things are asynchronous + if (method == Gmsh) { + std::list obj; + for (const auto &info : shapeObjects) { + obj.emplace_back(info.obj, info.subname.c_str()); + } + gmsh->process(activeDoc, obj); + return false; + } + try { QString objname, label, subname; Gui::WaitCursor wc; - int method = ui->stackedWidget->currentIndex(); - // Save parameters if (method == Standard) { ParameterGrp::handle handle = App::GetApplication().GetParameterGroupByPath @@ -380,6 +407,137 @@ bool Tessellation::accept() Base::Console().Error(e.what()); } + return doClose; +} + +// --------------------------------------- + +class Mesh2ShapeGmsh::Private { +public: + std::string label; + std::list shapes; + App::DocumentT doc; + std::string cadFile; + std::string stlFile; + std::string geoFile; +}; + +Mesh2ShapeGmsh::Mesh2ShapeGmsh(QWidget* parent, Qt::WindowFlags fl) + : GmshWidget(parent, fl) + , d(new Private()) +{ + d->cadFile = App::Application::getTempFileName() + "mesh.brep"; + d->stlFile = App::Application::getTempFileName() + "mesh.stl"; + d->geoFile = App::Application::getTempFileName() + "mesh.geo"; +} + +Mesh2ShapeGmsh::~Mesh2ShapeGmsh() +{ +} + +void Mesh2ShapeGmsh::process(App::Document* doc, const std::list& objs) +{ + d->doc = doc; + d->shapes = objs; + + doc->openTransaction("Meshing"); + accept(); +} + +bool Mesh2ShapeGmsh::writeProject(QString& inpFile, QString& outFile) +{ + if (!d->shapes.empty()) { + App::SubObjectT sub = d->shapes.front(); + d->shapes.pop_front(); + + App::DocumentObject* part = sub.getObject(); + if (part) { + Part::TopoShape shape = Part::Feature::getTopoShape(part, sub.getSubName().c_str()); + shape.exportBrep(d->cadFile.c_str()); + d->label = part->Label.getStrValue() + " (Meshed)"; + + // Parameters + int algorithm = meshingAlgorithm(); + double maxSize = getMaxSize(); + if (maxSize == 0.0) + maxSize = 1.0e22; + double minSize = getMinSize(); + + // gmsh geo file + Base::FileInfo geo(d->geoFile); + Base::ofstream geoOut(geo, std::ios::out); + geoOut << "// geo file for meshing with Gmsh meshing software created by FreeCAD\n" + << "// open brep geometry\n" + << "Merge \"" << d->cadFile << "\";\n\n" + << "// Characteristic Length\n" + << "// no boundary layer settings for this mesh\n" + << "// min, max Characteristic Length\n" + << "Mesh.CharacteristicLengthMax = " << maxSize << ";\n" + << "Mesh.CharacteristicLengthMin = " << minSize << ";\n\n" + << "// optimize the mesh\n" + << "Mesh.Optimize = 1;\n" + << "Mesh.OptimizeNetgen = 0;\n" + << "// for more HighOrderOptimize parameter check http://gmsh.info/doc/texinfo/gmsh.html\n" + << "Mesh.HighOrderOptimize = 0;\n\n" + << "// mesh order\n" + << "Mesh.ElementOrder = 2;\n" + << "// Second order nodes are created by linear interpolation instead by curvilinear\n" + << "Mesh.SecondOrderLinear = 1;\n\n" + << "// mesh algorithm, only a few algorithms are usable with 3D boundary layer generation\n" + << "// 2D mesh algorithm (1=MeshAdapt, 2=Automatic, 5=Delaunay, 6=Frontal, 7=BAMG, 8=DelQuad)\n" + << "Mesh.Algorithm = " << algorithm << ";\n" + << "// 3D mesh algorithm (1=Delaunay, 2=New Delaunay, 4=Frontal, 5=Frontal Delaunay, 6=Frontal Hex, 7=MMG3D, 9=R-tree)\n" + << "Mesh.Algorithm3D = 1;\n\n" + << "// meshing\n" + << "// set geometrical tolerance (also used for merging nodes)\n" + << "Geometry.Tolerance = 1e-06;\n" + << "Mesh 2;\n" + << "Coherence Mesh; // Remove duplicate vertices\n"; + geoOut.close(); + + inpFile = QString::fromUtf8(d->geoFile.c_str()); + outFile = QString::fromUtf8(d->stlFile.c_str()); + + return true; + } + } + else { + App::Document* doc = d->doc.getDocument(); + if (doc) + doc->commitTransaction(); + + Q_EMIT processed(); + } + + return false; +} + +bool Mesh2ShapeGmsh::loadOutput() +{ + App::Document* doc = d->doc.getDocument(); + if (!doc) + return false; + + // Now read-in the 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 = static_cast(doc->addObject("Mesh::Feature", "Mesh")); + fea->Label.setValue(d->label); + fea->Mesh.setValue(kernel.getKernel()); + stl.deleteFile(); + geo.deleteFile(); + + // process next object + accept(); + return true; } diff --git a/src/Mod/MeshPart/Gui/Tessellation.h b/src/Mod/MeshPart/Gui/Tessellation.h index 17c07b59d5..ea55ea2fb7 100644 --- a/src/Mod/MeshPart/Gui/Tessellation.h +++ b/src/Mod/MeshPart/Gui/Tessellation.h @@ -28,10 +28,42 @@ #include #include #include +#include #include +#include +namespace App { +class Document; +class SubObjectT; +} namespace MeshPartGui { +/** + * Non-modal dialog to mesh a shape. + * @author Werner Mayer + */ +class MeshGuiExport Mesh2ShapeGmsh : public MeshGui::GmshWidget +{ + Q_OBJECT + +public: + Mesh2ShapeGmsh(QWidget* parent = 0, Qt::WindowFlags fl = 0); + ~Mesh2ShapeGmsh(); + + void process(App::Document* doc, const std::list&); + +Q_SIGNALS: + void processed(); + +protected: + virtual bool writeProject(QString& inpFile, QString& outFile); + virtual bool loadOutput(); + +private: + class Private; + std::unique_ptr d; +}; + class Ui_Tessellation; class Tessellation : public QWidget { @@ -40,7 +72,8 @@ class Tessellation : public QWidget enum { Standard, Mefisto, - Netgen + Netgen, + Gmsh }; public: @@ -57,9 +90,11 @@ private Q_SLOTS: void on_comboFineness_currentIndexChanged(int); void on_checkSecondOrder_toggled(bool); void on_checkQuadDominated_toggled(bool); + void gmshProcessed(); private: QString document; + QPointer gmsh; std::unique_ptr ui; }; diff --git a/src/Mod/MeshPart/Gui/Tessellation.ui b/src/Mod/MeshPart/Gui/Tessellation.ui index 4971bc48e8..84a781bc0b 100644 --- a/src/Mod/MeshPart/Gui/Tessellation.ui +++ b/src/Mod/MeshPart/Gui/Tessellation.ui @@ -416,6 +416,13 @@ A value in the range of 0.2-10. + + + + Leave panel open + + +