MeshPart: add gmsh as another option to create mesh from shape
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "RemeshGmsh.h"
|
||||
#include "ui_RemeshGmsh.h"
|
||||
#include <Base/Console.h>
|
||||
#include <Base/FileInfo.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <App/Application.h>
|
||||
@@ -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()) {
|
||||
|
||||
@@ -36,11 +36,14 @@
|
||||
#include <App/Document.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/Control.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/Selection.h>
|
||||
#include <Gui/ViewProvider.h>
|
||||
#include <Gui/WaitCursor.h>
|
||||
#include <Mod/Mesh/App/Mesh.h>
|
||||
#include <Mod/Mesh/App/MeshFeature.h>
|
||||
#include <Mod/Part/App/PartFeature.h>
|
||||
#include <Mod/Mesh/Gui/ViewProvider.h>
|
||||
#include <Mod/Part/Gui/ViewProvider.h>
|
||||
@@ -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<App::SubObjectT> 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<App::SubObjectT> 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<App::SubObjectT>& 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<Mesh::Feature*>(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,42 @@
|
||||
#include <Gui/TaskView/TaskView.h>
|
||||
#include <Gui/Selection.h>
|
||||
#include <App/DocumentObserver.h>
|
||||
#include <Mod/Mesh/Gui/RemeshGmsh.h>
|
||||
#include <memory>
|
||||
#include <QPointer>
|
||||
|
||||
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<App::SubObjectT>&);
|
||||
|
||||
Q_SIGNALS:
|
||||
void processed();
|
||||
|
||||
protected:
|
||||
virtual bool writeProject(QString& inpFile, QString& outFile);
|
||||
virtual bool loadOutput();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
std::unique_ptr<Private> 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<Mesh2ShapeGmsh> gmsh;
|
||||
std::unique_ptr<Ui_Tessellation> ui;
|
||||
};
|
||||
|
||||
|
||||
@@ -416,6 +416,13 @@ A value in the range of 0.2-10.</string>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxDontQuit">
|
||||
<property name="text">
|
||||
<string>Leave panel open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
Reference in New Issue
Block a user