Merge pull request #16433 from marioalexis84/fem-mesh_netgen
Fem: New implementation of FemMesh Netgen object
This commit is contained in:
@@ -44,11 +44,21 @@
|
||||
<UserDocu>Add an edge by setting two node indices.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addEdgeList">
|
||||
<Documentation>
|
||||
<UserDocu>Add list of edges by list of node indices and list of nodes per edge.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addFace">
|
||||
<Documentation>
|
||||
<UserDocu>Add a face by setting three node indices.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addFaceList">
|
||||
<Documentation>
|
||||
<UserDocu>Add list of faces by list of node indices and list of nodes per face.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addQuad">
|
||||
<Documentation>
|
||||
<UserDocu>Add a quad by setting four node indices.</UserDocu>
|
||||
@@ -59,6 +69,11 @@
|
||||
<UserDocu>Add a volume by setting an arbitrary number of node indices.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="addVolumeList">
|
||||
<Documentation>
|
||||
<UserDocu>Add list of volumes by list of node indices and list of nodes per volume.</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
<Methode Name="read">
|
||||
<Documentation>
|
||||
<UserDocu>Read in a various FEM mesh file formats.
|
||||
|
||||
@@ -809,6 +809,275 @@ PyObject* FemMeshPy::addVolume(PyObject* args)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* FemMeshPy::addEdgeList(PyObject* args)
|
||||
{
|
||||
PyObject* nodesObj = nullptr;
|
||||
PyObject* npObj = nullptr;
|
||||
;
|
||||
if (!PyArg_ParseTuple(args, "O!O!", &PyList_Type, &nodesObj, &PyList_Type, &npObj)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py::List nodesList(nodesObj);
|
||||
Py::List npList(npObj);
|
||||
SMESHDS_Mesh* meshDS = getFemMeshPtr()->getSMesh()->GetMeshDS();
|
||||
|
||||
std::vector<const SMDS_MeshNode*> nodes;
|
||||
for (Py::List::iterator it = nodesList.begin(); it != nodesList.end(); ++it) {
|
||||
Py::Long n(*it);
|
||||
const SMDS_MeshNode* node = meshDS->FindNode(static_cast<int>(n));
|
||||
if (!node) {
|
||||
throw std::runtime_error("Failed to get node of the given indices");
|
||||
}
|
||||
nodes.push_back(node);
|
||||
}
|
||||
|
||||
std::vector<const SMDS_MeshNode*>::iterator nodeIt = nodes.begin();
|
||||
SMDS_MeshEdge* edge = nullptr;
|
||||
Py::List result;
|
||||
int np = 0;
|
||||
for (Py::List::iterator it = npList.begin(); it != npList.end(); ++it, nodeIt += np) {
|
||||
np = Py::Long(*it);
|
||||
std::vector<const SMDS_MeshNode*> nodesElem(nodeIt, nodeIt + np);
|
||||
switch (np) {
|
||||
case 2:
|
||||
edge = meshDS->AddEdge(nodesElem[0], nodesElem[1]);
|
||||
break;
|
||||
case 3:
|
||||
edge = meshDS->AddEdge(nodesElem[0], nodesElem[1], nodesElem[2]);
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError, "Unknown node count, [2|3] are allowed");
|
||||
return nullptr;
|
||||
}
|
||||
if (edge) {
|
||||
result.append(Py::Long(edge->GetID()));
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError, "Failed to add edge");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return Py::new_reference_to(result);
|
||||
}
|
||||
|
||||
|
||||
PyObject* FemMeshPy::addFaceList(PyObject* args)
|
||||
{
|
||||
PyObject* nodesObj = nullptr;
|
||||
PyObject* npObj = nullptr;
|
||||
;
|
||||
if (!PyArg_ParseTuple(args, "O!O!", &PyList_Type, &nodesObj, &PyList_Type, &npObj)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py::List nodesList(nodesObj);
|
||||
Py::List npList(npObj);
|
||||
SMESHDS_Mesh* meshDS = getFemMeshPtr()->getSMesh()->GetMeshDS();
|
||||
|
||||
std::vector<const SMDS_MeshNode*> nodes;
|
||||
for (Py::List::iterator it = nodesList.begin(); it != nodesList.end(); ++it) {
|
||||
Py::Long n(*it);
|
||||
const SMDS_MeshNode* node = meshDS->FindNode(static_cast<int>(n));
|
||||
if (!node) {
|
||||
throw std::runtime_error("Failed to get node of the given indices");
|
||||
}
|
||||
nodes.push_back(node);
|
||||
}
|
||||
|
||||
std::vector<const SMDS_MeshNode*>::iterator nodeIt = nodes.begin();
|
||||
SMDS_MeshFace* face = nullptr;
|
||||
Py::List result;
|
||||
int np = 0;
|
||||
for (Py::List::iterator it = npList.begin(); it != npList.end(); ++it, nodeIt += np) {
|
||||
np = Py::Long(*it);
|
||||
std::vector<const SMDS_MeshNode*> nodesElem(nodeIt, nodeIt + np);
|
||||
switch (np) {
|
||||
case 3:
|
||||
face = meshDS->AddFace(nodesElem[0], nodesElem[1], nodesElem[2]);
|
||||
break;
|
||||
case 4:
|
||||
face = meshDS->AddFace(nodesElem[0], nodesElem[1], nodesElem[2], nodesElem[3]);
|
||||
break;
|
||||
case 6:
|
||||
face = meshDS->AddFace(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5]);
|
||||
break;
|
||||
case 8:
|
||||
face = meshDS->AddFace(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5],
|
||||
nodesElem[6],
|
||||
nodesElem[7]);
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError, "Unknown node count, [3|4|6|8] are allowed");
|
||||
return nullptr;
|
||||
}
|
||||
if (face) {
|
||||
result.append(Py::Long(face->GetID()));
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError, "Failed to add face");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return Py::new_reference_to(result);
|
||||
}
|
||||
|
||||
|
||||
PyObject* FemMeshPy::addVolumeList(PyObject* args)
|
||||
{
|
||||
PyObject* nodesObj = nullptr;
|
||||
PyObject* npObj = nullptr;
|
||||
;
|
||||
if (!PyArg_ParseTuple(args, "O!O!", &PyList_Type, &nodesObj, &PyList_Type, &npObj)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py::List nodesList(nodesObj);
|
||||
Py::List npList(npObj);
|
||||
SMESHDS_Mesh* meshDS = getFemMeshPtr()->getSMesh()->GetMeshDS();
|
||||
|
||||
std::vector<const SMDS_MeshNode*> nodes;
|
||||
for (Py::List::iterator it = nodesList.begin(); it != nodesList.end(); ++it) {
|
||||
Py::Long n(*it);
|
||||
const SMDS_MeshNode* node = meshDS->FindNode(static_cast<int>(n));
|
||||
if (!node) {
|
||||
throw std::runtime_error("Failed to get node of the given indices");
|
||||
}
|
||||
nodes.push_back(node);
|
||||
}
|
||||
|
||||
std::vector<const SMDS_MeshNode*>::iterator nodeIt = nodes.begin();
|
||||
SMDS_MeshVolume* vol = nullptr;
|
||||
Py::List result;
|
||||
int np = 0;
|
||||
for (Py::List::iterator it = npList.begin(); it != npList.end(); ++it, nodeIt += np) {
|
||||
np = Py::Long(*it);
|
||||
std::vector<const SMDS_MeshNode*> nodesElem(nodeIt, nodeIt + np);
|
||||
switch (np) {
|
||||
case 4:
|
||||
vol = meshDS->AddVolume(nodesElem[0], nodesElem[1], nodesElem[2], nodesElem[3]);
|
||||
break;
|
||||
case 5:
|
||||
vol = meshDS->AddVolume(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4]);
|
||||
break;
|
||||
case 6:
|
||||
vol = meshDS->AddVolume(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5]);
|
||||
break;
|
||||
case 8:
|
||||
vol = meshDS->AddVolume(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5],
|
||||
nodesElem[6],
|
||||
nodesElem[7]);
|
||||
break;
|
||||
case 10:
|
||||
vol = meshDS->AddVolume(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5],
|
||||
nodesElem[6],
|
||||
nodesElem[7],
|
||||
nodesElem[8],
|
||||
nodesElem[9]);
|
||||
break;
|
||||
case 13:
|
||||
vol = meshDS->AddVolume(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5],
|
||||
nodesElem[6],
|
||||
nodesElem[7],
|
||||
nodesElem[8],
|
||||
nodesElem[9],
|
||||
nodesElem[10],
|
||||
nodesElem[11],
|
||||
nodesElem[12]);
|
||||
break;
|
||||
case 15:
|
||||
vol = meshDS->AddVolume(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5],
|
||||
nodesElem[6],
|
||||
nodesElem[7],
|
||||
nodesElem[8],
|
||||
nodesElem[9],
|
||||
nodesElem[10],
|
||||
nodesElem[11],
|
||||
nodesElem[12],
|
||||
nodesElem[13],
|
||||
nodesElem[14]);
|
||||
break;
|
||||
case 20:
|
||||
vol = meshDS->AddVolume(nodesElem[0],
|
||||
nodesElem[1],
|
||||
nodesElem[2],
|
||||
nodesElem[3],
|
||||
nodesElem[4],
|
||||
nodesElem[5],
|
||||
nodesElem[6],
|
||||
nodesElem[7],
|
||||
nodesElem[8],
|
||||
nodesElem[9],
|
||||
nodesElem[10],
|
||||
nodesElem[11],
|
||||
nodesElem[12],
|
||||
nodesElem[13],
|
||||
nodesElem[14],
|
||||
nodesElem[15],
|
||||
nodesElem[16],
|
||||
nodesElem[17],
|
||||
nodesElem[18],
|
||||
nodesElem[19]);
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Unknown node count, [4|5|6|8|10|13|15|20] are allowed");
|
||||
return nullptr;
|
||||
}
|
||||
if (vol) {
|
||||
result.append(Py::Long(vol->GetID()));
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError, "Failed to add face");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return Py::new_reference_to(result);
|
||||
}
|
||||
|
||||
|
||||
PyObject* FemMeshPy::copy(PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
|
||||
@@ -165,6 +165,7 @@ SET(FemMesh_SRCS
|
||||
femmesh/gmshtools.py
|
||||
femmesh/meshsetsgetter.py
|
||||
femmesh/meshtools.py
|
||||
femmesh/netgentools.py
|
||||
)
|
||||
|
||||
SET(FemObjects_SRCS
|
||||
@@ -194,6 +195,7 @@ SET(FemObjects_SRCS
|
||||
femobjects/mesh_boundarylayer.py
|
||||
femobjects/mesh_gmsh.py
|
||||
femobjects/mesh_group.py
|
||||
femobjects/mesh_netgen.py
|
||||
femobjects/mesh_region.py
|
||||
femobjects/mesh_result.py
|
||||
femobjects/result_mechanical.py
|
||||
@@ -592,6 +594,7 @@ SET(FemGuiTaskPanels_SRCS
|
||||
femtaskpanels/task_mesh_gmsh.py
|
||||
femtaskpanels/task_mesh_group.py
|
||||
femtaskpanels/task_mesh_region.py
|
||||
femtaskpanels/task_mesh_netgen.py
|
||||
femtaskpanels/task_result_mechanical.py
|
||||
femtaskpanels/task_solver_ccxtools.py
|
||||
)
|
||||
@@ -636,6 +639,7 @@ SET(FemGuiViewProvider_SRCS
|
||||
femviewprovider/view_mesh_boundarylayer.py
|
||||
femviewprovider/view_mesh_gmsh.py
|
||||
femviewprovider/view_mesh_group.py
|
||||
femviewprovider/view_mesh_netgen.py
|
||||
femviewprovider/view_mesh_region.py
|
||||
femviewprovider/view_mesh_result.py
|
||||
femviewprovider/view_result_mechanical.py
|
||||
|
||||
@@ -415,6 +415,7 @@ SET(FemGuiPythonUI_SRCS
|
||||
Resources/ui/MeshGmsh.ui
|
||||
Resources/ui/MeshGroup.ui
|
||||
Resources/ui/MeshGroupXDMFExport.ui
|
||||
Resources/ui/MeshNetgen.ui
|
||||
Resources/ui/MeshRegion.ui
|
||||
Resources/ui/ResultHints.ui
|
||||
Resources/ui/ResultShow.ui
|
||||
|
||||
277
src/Mod/Fem/Gui/Resources/ui/MeshNetgen.ui
Normal file
277
src/Mod/Fem/Gui/Resources/ui/MeshNetgen.ui
Normal file
@@ -0,0 +1,277 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NetgenMesh</class>
|
||||
<widget class="QWidget" name="NetgenMesh">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>475</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>FEM Mesh by Netgen</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gpb_mesh_params">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>1677215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Mesh Parameters</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_1">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_fineness">
|
||||
<property name="text">
|
||||
<string>Fineness:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="cb_fineness"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="l_max">
|
||||
<property name="text">
|
||||
<string>Maximal Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="qsb_max_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeft|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="keyboardTracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000000000000000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1000.000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="l_min">
|
||||
<property name="text">
|
||||
<string>Minimal Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="qsb_min_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true">mm</string>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeft|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="keyboardTracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000000000000000000000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="ckb_second_order">
|
||||
<property name="text">
|
||||
<string>Second Order</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Growth Rate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDoubleSpinBox" name="dsb_growth_rate">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Curvature Safety:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QDoubleSpinBox" name="dsb_curvature_safety">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Segments Per Edge:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QDoubleSpinBox" name="dsb_seg_per_edge">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb03_run_netgen">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>1677215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Netgen</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="1">
|
||||
<layout class="QGridLayout" name="gl_actions">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTextEdit" name="te_output">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="l_time">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QPushButton" name="pb_get_netgen_version">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Netgen version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -154,10 +154,8 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
|
||||
|
||||
Gui::ToolBarItem* mesh = new Gui::ToolBarItem(root);
|
||||
mesh->setCommand("Mesh");
|
||||
#ifdef FCWithNetgen
|
||||
*mesh << "FEM_MeshNetgenFromShape";
|
||||
#endif
|
||||
*mesh << "FEM_MeshGmshFromShape"
|
||||
*mesh << "FEM_MeshNetgenFromShape"
|
||||
<< "FEM_MeshGmshFromShape"
|
||||
<< "Separator"
|
||||
<< "FEM_MeshBoundaryLayer"
|
||||
<< "FEM_MeshRegion"
|
||||
@@ -311,10 +309,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const
|
||||
Gui::MenuItem* mesh = new Gui::MenuItem;
|
||||
root->insertItem(item, mesh);
|
||||
mesh->setCommand("M&esh");
|
||||
#ifdef FCWithNetgen
|
||||
*mesh << "FEM_MeshNetgenFromShape";
|
||||
#endif
|
||||
*mesh << "FEM_MeshGmshFromShape"
|
||||
*mesh << "FEM_MeshNetgenFromShape"
|
||||
<< "FEM_MeshGmshFromShape"
|
||||
<< "Separator"
|
||||
<< "FEM_MeshBoundaryLayer"
|
||||
<< "FEM_MeshRegion"
|
||||
|
||||
@@ -525,8 +525,15 @@ def makeMeshGroup(doc, base_mesh, use_label=False, name="MeshGroup"):
|
||||
|
||||
def makeMeshNetgen(doc, name="MeshNetgen"):
|
||||
"""makeMeshNetgen(document, [name]):
|
||||
makes a Fem MeshShapeNetgenObject object"""
|
||||
obj = doc.addObject("Fem::FemMeshShapeNetgenObject", name)
|
||||
makes a Netgen FEM mesh object"""
|
||||
obj = doc.addObject("Fem::FemMeshShapeBaseObjectPython", name)
|
||||
from femobjects import mesh_netgen
|
||||
|
||||
mesh_netgen.MeshNetgen(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
from femviewprovider import view_mesh_netgen
|
||||
|
||||
view_mesh_netgen.VPMeshNetgen(obj.ViewObject)
|
||||
return obj
|
||||
|
||||
|
||||
|
||||
@@ -830,6 +830,7 @@ class _MeshNetgenFromShape(CommandManager):
|
||||
self.selobj.Name
|
||||
)
|
||||
)
|
||||
FreeCADGui.doCommand("FreeCAD.ActiveDocument.ActiveObject.Fineness = 'Moderate'")
|
||||
# Netgen mesh object could be added without an active analysis
|
||||
# but if there is an active analysis move it in there
|
||||
import FemGui
|
||||
|
||||
297
src/Mod/Fem/femmesh/netgentools.py
Normal file
297
src/Mod/Fem/femmesh/netgentools.py
Normal file
@@ -0,0 +1,297 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "Tools for the work with Netgen mesher"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
import numpy as np
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import FreeCAD
|
||||
import Fem
|
||||
|
||||
try:
|
||||
from netgen import occ, config as ng_config
|
||||
except ModuleNotFoundError:
|
||||
FreeCAD.Console.PrintError("To use FemMesh Netgen objects, install the Netgen Python bindings")
|
||||
|
||||
|
||||
class NetgenTools:
|
||||
|
||||
# to change order of nodes from netgen to smesh
|
||||
order_edge = {
|
||||
2: [0, 1, 2], # seg2
|
||||
3: [0, 1, 2], # seg3
|
||||
}
|
||||
order_face = {
|
||||
3: [0, 1, 2, 3, 4, 5, 6, 7], # tria3
|
||||
6: [0, 1, 2, 5, 3, 4, 6, 7], # tria6
|
||||
4: [0, 1, 2, 3, 4, 5, 6, 7], # quad4
|
||||
8: [0, 1, 2, 3, 4, 7, 5, 6], # quad8
|
||||
}
|
||||
order_volume = {
|
||||
4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # tetra4
|
||||
10: [0, 1, 2, 3, 4, 7, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # tetra10
|
||||
8: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # hexa8
|
||||
20: [0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 9, 10, 12, 15, 13, 14, 16, 17, 18, 19], # hexa20
|
||||
5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # pyra5
|
||||
13: [0, 1, 2, 3, 4, 5, 8, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # pyra13
|
||||
6: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], # penta6
|
||||
15: [0, 1, 2, 3, 4, 5, 6, 8, 7, 12, 14, 13, 9, 10, 11, 15, 16, 17, 18, 19], # penta15
|
||||
}
|
||||
|
||||
name = "Netgen"
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
self.fem_mesh = None
|
||||
self.process = None
|
||||
self.tmpdir = ""
|
||||
|
||||
def write_geom(self):
|
||||
if not self.tmpdir:
|
||||
self.tmpdir = tempfile.mkdtemp(prefix="fem_")
|
||||
|
||||
sh = self.obj.Shape.getPropertyOfGeometry()
|
||||
self.brep_file = self.tmpdir + "/shape.brep"
|
||||
self.result_file = self.tmpdir + "/result.npy"
|
||||
sh.exportBrep(self.brep_file)
|
||||
|
||||
code = """
|
||||
from femmesh.netgentools import NetgenTools
|
||||
|
||||
NetgenTools.run_netgen(**{params})
|
||||
"""
|
||||
|
||||
def compute(self):
|
||||
self.write_geom()
|
||||
mesh_params = {
|
||||
"brep_file": self.brep_file,
|
||||
"threads": self.obj.Threads,
|
||||
"heal": self.obj.HealShape,
|
||||
"fineness": self.obj.Fineness,
|
||||
"params": self.get_meshing_parameters(),
|
||||
"second_order": self.obj.SecondOrder,
|
||||
"result_file": self.result_file,
|
||||
}
|
||||
|
||||
code_str = self.code.format(params=mesh_params)
|
||||
|
||||
cmd_list = [
|
||||
sys.executable,
|
||||
"-c",
|
||||
code_str,
|
||||
]
|
||||
self.process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = self.process.communicate()
|
||||
if self.process.returncode != 0:
|
||||
raise RuntimeError(err.decode("utf-8"))
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def run_netgen(brep_file, threads, heal, fineness, params, second_order, result_file):
|
||||
import pyngcore as ngcore
|
||||
from netgen import meshing
|
||||
|
||||
geom = occ.OCCGeometry(brep_file)
|
||||
ngcore.SetNumThreads(threads)
|
||||
|
||||
if fineness == "UserDefined":
|
||||
mp = meshing.MeshingParameters(**params)
|
||||
elif fineness == "VeryCoarse":
|
||||
mp = meshing.meshsize.very_coarse
|
||||
elif fineness == "Coarse":
|
||||
mp = meshing.meshsize.coarse
|
||||
elif fineness == "Moderate":
|
||||
mp = meshing.meshsize.moderate
|
||||
elif fineness == "Fine":
|
||||
mp = meshing.meshsize.fine
|
||||
elif fineness == "VeryFine":
|
||||
mp = meshing.meshsize.very_fine
|
||||
|
||||
with ngcore.TaskManager():
|
||||
if heal:
|
||||
geom.Heal()
|
||||
mesh = geom.GenerateMesh(mp=mp)
|
||||
|
||||
if second_order:
|
||||
mesh.SecondOrder()
|
||||
|
||||
coords = mesh.Coordinates()
|
||||
|
||||
edges = mesh.Elements1D().NumPy()
|
||||
faces = mesh.Elements2D().NumPy()
|
||||
volumes = mesh.Elements3D().NumPy()
|
||||
|
||||
nod_edges = edges["nodes"]
|
||||
nod_faces = faces["nodes"]
|
||||
nod_volumes = volumes["nodes"]
|
||||
|
||||
np_edges = (nod_edges != 0).sum(axis=1).tolist()
|
||||
np_faces = faces["np"].tolist()
|
||||
np_volumes = volumes["np"].tolist()
|
||||
|
||||
# set smesh node order
|
||||
for i in range(faces.size):
|
||||
nod_faces[i] = nod_faces[i][NetgenTools.order_face[np_faces[i]]]
|
||||
|
||||
for i in range(volumes.size):
|
||||
nod_volumes[i] = nod_volumes[i][NetgenTools.order_volume[np_volumes[i]]]
|
||||
|
||||
flat_edges = nod_edges[nod_edges != 0].tolist()
|
||||
flat_faces = nod_faces[nod_faces != 0].tolist()
|
||||
flat_volumes = nod_volumes[nod_volumes != 0].tolist()
|
||||
|
||||
result = {
|
||||
"coords": coords,
|
||||
"Edges": [flat_edges, np_edges],
|
||||
"Faces": [flat_faces, np_faces],
|
||||
"Volumes": [flat_volumes, np_volumes],
|
||||
}
|
||||
|
||||
# create groups
|
||||
nb_edges = edges.size
|
||||
nb_faces = faces.size
|
||||
nb_volumes = volumes.size
|
||||
|
||||
idx_edges = edges["index"]
|
||||
idx_faces = faces["index"]
|
||||
idx_volumes = volumes["index"]
|
||||
|
||||
groups = {"Edges": [], "Faces": [], "Solids": []}
|
||||
for i in np.unique(idx_edges):
|
||||
edge_i = (np.nonzero(idx_edges == i)[0] + 1).tolist()
|
||||
groups["Edges"].append([i, edge_i])
|
||||
for i in np.unique(idx_faces):
|
||||
face_i = (np.nonzero(idx_faces == i)[0] + nb_edges + 1).tolist()
|
||||
groups["Faces"].append([i, face_i])
|
||||
|
||||
for i in np.unique(idx_volumes):
|
||||
volume_i = (np.nonzero(idx_volumes == i)[0] + nb_edges + nb_faces + 1).tolist()
|
||||
groups["Solids"].append([i, volume_i])
|
||||
|
||||
np.save(result_file, [result, groups])
|
||||
|
||||
def fem_mesh_from_result(self):
|
||||
fem_mesh = Fem.FemMesh()
|
||||
|
||||
# load Netgen result
|
||||
netgen_result, groups = np.load(self.result_file, allow_pickle=True)
|
||||
|
||||
for node in netgen_result["coords"]:
|
||||
fem_mesh.addNode(*node)
|
||||
|
||||
fem_mesh.addEdgeList(*netgen_result["Edges"])
|
||||
fem_mesh.addFaceList(*netgen_result["Faces"])
|
||||
fem_mesh.addVolumeList(*netgen_result["Volumes"])
|
||||
|
||||
for g in groups["Edges"]:
|
||||
grp_id = fem_mesh.addGroup("Edge" + str(g[0]), "Edge")
|
||||
fem_mesh.addGroupElements(grp_id, g[1])
|
||||
|
||||
for g in groups["Faces"]:
|
||||
grp_id = fem_mesh.addGroup("Face" + str(g[0]), "Face")
|
||||
fem_mesh.addGroupElements(grp_id, g[1])
|
||||
|
||||
for g in groups["Solids"]:
|
||||
grp_id = fem_mesh.addGroup("Solid" + str(g[0]), "Volume")
|
||||
fem_mesh.addGroupElements(grp_id, g[1])
|
||||
|
||||
return fem_mesh
|
||||
|
||||
def update_properties(self):
|
||||
self.obj.FemMesh = self.fem_mesh_from_result()
|
||||
|
||||
def get_meshing_parameters(self):
|
||||
params = {
|
||||
"optimize3d": self.obj.Optimize3d,
|
||||
"optimize2d": self.obj.Optimize2d,
|
||||
"optsteps3d": self.obj.OptimizationSteps3d,
|
||||
"optsteps2d": self.obj.OptimizationSteps2d,
|
||||
"opterrpow": self.obj.OptimizationErrorPower,
|
||||
"blockfill": self.obj.BlockFill,
|
||||
"filldist": self.obj.FillDistance.Value,
|
||||
"safety": self.obj.Safety,
|
||||
"relinnersafety": self.obj.RelinnerSafety,
|
||||
"uselocalh": self.obj.UseLocalH,
|
||||
"grading": self.obj.GrowthRate,
|
||||
"delaunay": self.obj.Delaunay,
|
||||
"delaunay2d": self.obj.Delaunay2d,
|
||||
"maxh": self.obj.MaxSize.Value,
|
||||
"minh": self.obj.MinSize.Value,
|
||||
"startinsurface": self.obj.StartInSurface,
|
||||
"checkoverlap": self.obj.CheckOverlap,
|
||||
"checkoverlappingboundary": self.obj.CheckOverlappingBoundary,
|
||||
"checkchartboundary": self.obj.CheckChartBoundary,
|
||||
"curvaturesafety": self.obj.CurvatureSafety,
|
||||
"segmentsperedge": self.obj.SegmentsPerEdge,
|
||||
"elsizeweight": self.obj.ElementSizeWeight,
|
||||
"parthread": self.obj.ParallelMeshing,
|
||||
"perfstepsstart": self.obj.StartStep,
|
||||
"perfstepsend": self.obj.EndStep,
|
||||
"giveuptol2d": self.obj.GiveUpTolerance2d,
|
||||
"giveuptol": self.obj.GiveUpTolerance,
|
||||
"giveuptolopenquads": self.obj.GiveUpToleranceOpenQuads,
|
||||
"maxoutersteps": self.obj.MaxOuterSteps,
|
||||
"starshapeclass": self.obj.StarShapeClass,
|
||||
"baseelnp": self.obj.BaseElementNp,
|
||||
"sloppy": self.obj.Sloppy,
|
||||
"badellimit": self.obj.BadElementLimit,
|
||||
"check_impossible": self.obj.CheckImpossible,
|
||||
"only3D_domain_nr": self.obj.Only3dDomainNr,
|
||||
"secondorder": self.obj.SecondOrder,
|
||||
"elementorder": self.obj.ElementOrder,
|
||||
"quad_dominated": self.obj.QuadDominated,
|
||||
"try_hexes": self.obj.TryHexes,
|
||||
"inverttets": self.obj.InvertTets,
|
||||
"inverttrigs": self.obj.InvertTrigs,
|
||||
"autozrefine": self.obj.AutoZRefine,
|
||||
"parallel_meshing": self.obj.ParallelMeshing,
|
||||
"nthreads": self.obj.Threads,
|
||||
"closeedgefac": self.obj.CloseEdgeFactor,
|
||||
}
|
||||
|
||||
return params
|
||||
|
||||
@staticmethod
|
||||
def version():
|
||||
result = "{}: {}\n" + "{}: {}\n" + "{}: {}\n" + "{}: {}"
|
||||
return result.format(
|
||||
"Netgen",
|
||||
ng_config.version,
|
||||
"Python",
|
||||
ng_config.PYTHON_VERSION,
|
||||
"OpenCASCADE",
|
||||
occ.occ_version,
|
||||
"Use MPI",
|
||||
ng_config.USE_MPI,
|
||||
)
|
||||
|
||||
def __del__(self):
|
||||
if self.tmpdir:
|
||||
shutil.rmtree(self.tmpdir)
|
||||
486
src/Mod/Fem/femobjects/mesh_netgen.py
Normal file
486
src/Mod/Fem/femobjects/mesh_netgen.py
Normal file
@@ -0,0 +1,486 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM mesh netgen document object"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package mesh_netgen
|
||||
# \ingroup FEM
|
||||
# \brief mesh gmsh object
|
||||
|
||||
from FreeCAD import Base
|
||||
from . import base_fempythonobject
|
||||
|
||||
_PropHelper = base_fempythonobject._PropHelper
|
||||
|
||||
|
||||
class MeshNetgen(base_fempythonobject.BaseFemPythonObject):
|
||||
"""
|
||||
A Fem::FemMeshShapeBaseObject python type, add Netgen specific properties
|
||||
"""
|
||||
|
||||
Type = "Fem::FemMeshNetgen"
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
for prop in self._get_properties():
|
||||
prop.add_to_object(obj)
|
||||
|
||||
def _get_properties(self):
|
||||
prop = []
|
||||
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyString",
|
||||
name="Optimize3d",
|
||||
group="Mesh Parameters",
|
||||
doc="3d optimization strategy.\n"
|
||||
+ "m: move nodes, M: move nodes, cheap functional\n"
|
||||
+ "s: swap faces, c: combine elements, d: divide elements,\n"
|
||||
+ "D: divide and join opposite edges, remove element,\n"
|
||||
+ "p: plot, no pause, P: plot, Pause,\n"
|
||||
+ "h: Histogramm, no pause, H: Histogramm, pause",
|
||||
value="cmdDmustm",
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyEnumeration",
|
||||
name="Fineness",
|
||||
group="Mesh Parameters",
|
||||
doc="Fineness",
|
||||
value=["VeryCoarse", "Coarse", "Moderate", "Fine", "VeryFine", "UserDefined"],
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="OptimizationSteps3d",
|
||||
group="Mesh Parameters",
|
||||
doc="Number of 3d optimization steps",
|
||||
value=3,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyString",
|
||||
name="Optimize2d",
|
||||
group="Mesh Parameters",
|
||||
doc="2d optimization strategy.\n"
|
||||
+ "s: swap opt 6 lines/node, S: swap optimal elements,\n"
|
||||
+ "m: move nodes, p: plot, no pause\n"
|
||||
+ "P: plot, pause, c: combine",
|
||||
value="smcmSmcmSmcm",
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="OptimizationSteps2d",
|
||||
group="Mesh Parameters",
|
||||
doc="Number of 2d optimization steps",
|
||||
value=3,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="OptimizationErrorPower",
|
||||
group="Mesh Parameters",
|
||||
doc="Power of error to approximate max error optimization",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="BlockFill",
|
||||
group="Mesh Parameters",
|
||||
doc="Do block filling",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyLength",
|
||||
name="FillDistance",
|
||||
group="Mesh Parameters",
|
||||
doc="Block filling up to distance",
|
||||
value=0.1,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="Safety",
|
||||
group="Mesh Parameters",
|
||||
doc="Radius of local environment (times h)",
|
||||
value=5.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="RelinnerSafety",
|
||||
group="Mesh Parameters",
|
||||
doc="Radius of active environment (times h)",
|
||||
value=3.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="LocalH",
|
||||
group="Mesh Parameters",
|
||||
doc="Use local h",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="UseLocalH",
|
||||
group="Mesh Parameters",
|
||||
doc="Use local H",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="GrowthRate",
|
||||
group="Mesh Parameters",
|
||||
doc="Grading for local h",
|
||||
value=0.3,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="Delaunay",
|
||||
group="Mesh Parameters",
|
||||
doc="Use Delaunay for 3d meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="Delaunay2d",
|
||||
group="Mesh Parameters",
|
||||
doc="Use Delaunay for 2d meshing",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyLength",
|
||||
name="MaxSize",
|
||||
group="Mesh Parameters",
|
||||
doc="Maximal mesh size",
|
||||
value="1000 mm",
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyLength",
|
||||
name="MinSize",
|
||||
group="Mesh Parameters",
|
||||
doc="Minimal mesh size",
|
||||
value="0 mm",
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="CloseEdgeFactor",
|
||||
group="Mesh Parameters",
|
||||
doc="Factor to restrict meshing based on close edges",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="StartInSurface",
|
||||
group="Mesh Parameters",
|
||||
doc="Start surface meshing from everywhere in surface",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckOverlap",
|
||||
group="Mesh Parameters",
|
||||
doc="Check overlapping surfaces",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckOverlappingBoundary",
|
||||
group="Mesh Parameters",
|
||||
doc="Check overlapping surface mesh before volume meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckChartBoundary",
|
||||
group="Mesh Parameters",
|
||||
doc="Check chart boundary",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="CurvatureSafety",
|
||||
group="Mesh Parameters",
|
||||
doc="Safety factor for curvatures (elements per radius)",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="SegmentsPerEdge",
|
||||
group="Mesh Parameters",
|
||||
doc="Minimal number of segments per edge",
|
||||
value=2.0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="ElementSizeWeight",
|
||||
group="Mesh Parameters",
|
||||
doc="Weight of element size respect to element shape",
|
||||
value=0.2,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="StartStep",
|
||||
group="Mesh Parameters",
|
||||
doc="Start at step",
|
||||
value=0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="EndStep",
|
||||
group="Mesh Parameters",
|
||||
doc="EndStep",
|
||||
value=6,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="GiveUpTolerance2d",
|
||||
group="Mesh Parameters",
|
||||
doc="Give up quality class, 2d meshing",
|
||||
value=200,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="GiveUpTolerance",
|
||||
group="Mesh Parameters",
|
||||
doc="Give up quality class, 3d meshing",
|
||||
value=10,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="GiveUpToleranceOpenQuads",
|
||||
group="Mesh Parameters",
|
||||
doc="Give up quality class, for closing open quads, greather than 100 for free pyramids",
|
||||
value=15,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="MaxOuterSteps",
|
||||
group="Mesh Parameters",
|
||||
doc="Maximal outer steps",
|
||||
value=10,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="StarShapeClass",
|
||||
group="Mesh Parameters",
|
||||
doc="Class starting star-shape filling",
|
||||
value=5,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="BaseElementNp",
|
||||
group="Mesh Parameters",
|
||||
doc="If non-zero, baseelement must have BaseElementlNp points",
|
||||
value=0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="Sloppy",
|
||||
group="Mesh Parameters",
|
||||
doc="Quality tolerances are handled less careful",
|
||||
value=10,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyFloat",
|
||||
name="BadElementLimit",
|
||||
group="Mesh Parameters",
|
||||
doc="Limit for max element angle (150-180)",
|
||||
value=175,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="CheckImpossible",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="Only3dDomainNr",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=0,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="SecondOrder",
|
||||
group="Mesh Parameters",
|
||||
doc="Second order element meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="ElementOrder",
|
||||
group="Mesh Parameters",
|
||||
doc="High order element curvature",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="QuadDominated",
|
||||
group="Mesh Parameters",
|
||||
doc="Quad-dominated surface meshing",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="TryHexes",
|
||||
group="Mesh Parameters",
|
||||
doc="Try hexahedral elements",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="InvertTets",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="InvertTrigs",
|
||||
group="Mesh Parameters",
|
||||
doc="",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="AutoZRefine",
|
||||
group="Mesh Parameters",
|
||||
doc="Automatic Z refine",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="ParallelMeshing",
|
||||
group="Mesh Parameters",
|
||||
doc="Use parallel meshing",
|
||||
value=True,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyInteger",
|
||||
name="Threads",
|
||||
group="Mesh Parameters",
|
||||
doc="Number of threads for parallel meshing",
|
||||
value=4,
|
||||
)
|
||||
)
|
||||
prop.append(
|
||||
_PropHelper(
|
||||
type="App::PropertyBool",
|
||||
name="HealShape",
|
||||
group="Mesh Parameters",
|
||||
doc="Heal shape before meshing",
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
|
||||
return prop
|
||||
164
src/Mod/Fem/femtaskpanels/task_mesh_netgen.py
Normal file
164
src/Mod/Fem/femtaskpanels/task_mesh_netgen.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM mesh netgen task panel for mesh netgen object"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package task_mesh_netgen
|
||||
# \ingroup FEM
|
||||
# \brief task panel for mesh netgen object
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
from femmesh import netgentools
|
||||
|
||||
from . import base_femmeshtaskpanel
|
||||
|
||||
|
||||
class _TaskPanel(base_femmeshtaskpanel._BaseMeshTaskPanel):
|
||||
"""
|
||||
The TaskPanel for editing References property of
|
||||
MeshNetgen objects and creation of new FEM mesh
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self.form = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/MeshNetgen.ui"
|
||||
)
|
||||
|
||||
self.tool = netgentools.NetgenTools(obj)
|
||||
|
||||
QtCore.QObject.connect(
|
||||
self.form.qsb_max_size,
|
||||
QtCore.SIGNAL("valueChanged(Base::Quantity)"),
|
||||
self.max_size_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.qsb_min_size,
|
||||
QtCore.SIGNAL("valueChanged(Base::Quantity)"),
|
||||
self.min_size_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.dsb_seg_per_edge,
|
||||
QtCore.SIGNAL("valueChanged(double)"),
|
||||
self.seg_per_edge_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.dsb_curvature_safety,
|
||||
QtCore.SIGNAL("valueChanged(double)"),
|
||||
self.curvature_safety_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.dsb_growth_rate,
|
||||
QtCore.SIGNAL("valueChanged(double)"),
|
||||
self.growth_rate_changed,
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.ckb_second_order, QtCore.SIGNAL("toggled(bool)"), self.second_order_changed
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self.form.cb_fineness,
|
||||
QtCore.SIGNAL("currentIndexChanged(int)"),
|
||||
self.fineness_changed,
|
||||
)
|
||||
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.update_timer_text)
|
||||
QtCore.QObject.connect(
|
||||
self.form.pb_get_netgen_version, QtCore.SIGNAL("clicked()"), self.get_version
|
||||
)
|
||||
|
||||
self.get_mesh_params()
|
||||
self.set_widgets()
|
||||
|
||||
def get_mesh_params(self):
|
||||
self.min_size = self.obj.MinSize
|
||||
self.max_size = self.obj.MaxSize
|
||||
self.fineness = self.obj.Fineness
|
||||
self.growth_rate = self.obj.GrowthRate
|
||||
self.curvature_safety = self.obj.CurvatureSafety
|
||||
self.seg_per_edge = self.obj.SegmentsPerEdge
|
||||
self.second_order = self.obj.SecondOrder
|
||||
|
||||
def set_mesh_params(self):
|
||||
self.obj.MinSize = self.min_size
|
||||
self.obj.MaxSize = self.max_size
|
||||
self.obj.Fineness = self.fineness
|
||||
self.obj.GrowthRate = self.growth_rate
|
||||
self.obj.CurvatureSafety = self.curvature_safety
|
||||
self.obj.SegmentsPerEdge = self.seg_per_edge
|
||||
self.obj.SecondOrder = self.second_order
|
||||
|
||||
def set_widgets(self):
|
||||
"fills the widgets"
|
||||
self.form.qsb_max_size.setProperty("value", self.max_size)
|
||||
FreeCADGui.ExpressionBinding(self.form.qsb_max_size).bind(self.obj, "MaxSize")
|
||||
|
||||
self.form.qsb_min_size.setProperty("value", self.min_size)
|
||||
FreeCADGui.ExpressionBinding(self.form.qsb_min_size).bind(self.obj, "MinSize")
|
||||
|
||||
self.fineness_enum = self.obj.getEnumerationsOfProperty("Fineness")
|
||||
index = self.fineness_enum.index(self.fineness)
|
||||
self.form.cb_fineness.addItems(self.fineness_enum)
|
||||
self.form.cb_fineness.setCurrentIndex(index)
|
||||
self.form.dsb_growth_rate.setValue(self.growth_rate)
|
||||
self.form.dsb_curvature_safety.setValue(self.curvature_safety)
|
||||
self.form.dsb_seg_per_edge.setValue(self.seg_per_edge)
|
||||
|
||||
self.form.ckb_second_order.setChecked(self.second_order)
|
||||
|
||||
def max_size_changed(self, base_quantity_value):
|
||||
self.max_size = base_quantity_value
|
||||
|
||||
def min_size_changed(self, base_quantity_value):
|
||||
self.min_size = base_quantity_value
|
||||
|
||||
def seg_per_edge_changed(self, value):
|
||||
self.seg_per_edge = value
|
||||
|
||||
def curvature_safety_changed(self, value):
|
||||
self.curvature_safety = value
|
||||
|
||||
def growth_rate_changed(self, value):
|
||||
self.growth_rate = value
|
||||
|
||||
def fineness_changed(self, index):
|
||||
self.fineness = self.fineness_enum[index]
|
||||
if self.fineness == "UserDefined":
|
||||
self.form.qsb_min_size.setEnabled(True)
|
||||
self.form.qsb_max_size.setEnabled(True)
|
||||
self.form.dsb_seg_per_edge.setEnabled(True)
|
||||
self.form.dsb_growth_rate.setEnabled(True)
|
||||
self.form.dsb_curvature_safety.setEnabled(True)
|
||||
else:
|
||||
self.form.qsb_min_size.setEnabled(False)
|
||||
self.form.qsb_max_size.setEnabled(False)
|
||||
self.form.dsb_seg_per_edge.setEnabled(False)
|
||||
self.form.dsb_growth_rate.setEnabled(False)
|
||||
self.form.dsb_curvature_safety.setEnabled(False)
|
||||
|
||||
def second_order_changed(self, bool_value):
|
||||
self.second_order = bool_value
|
||||
@@ -240,9 +240,7 @@ class TestObjectType(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual("Fem::MeshGroup", type_of_obj(ObjectsFem.makeMeshGroup(doc, mesh)))
|
||||
self.assertEqual("Fem::MeshRegion", type_of_obj(ObjectsFem.makeMeshRegion(doc, mesh)))
|
||||
self.assertEqual(
|
||||
"Fem::FemMeshShapeNetgenObject", type_of_obj(ObjectsFem.makeMeshNetgen(doc))
|
||||
)
|
||||
self.assertEqual("Fem::FemMeshNetgen", type_of_obj(ObjectsFem.makeMeshNetgen(doc)))
|
||||
self.assertEqual("Fem::MeshResult", type_of_obj(ObjectsFem.makeMeshResult(doc)))
|
||||
self.assertEqual("Fem::ResultMechanical", type_of_obj(ObjectsFem.makeResultMechanical(doc)))
|
||||
solverelmer = ObjectsFem.makeSolverElmer(doc)
|
||||
@@ -409,7 +407,7 @@ class TestObjectType(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(is_of_type(ObjectsFem.makeMeshGroup(doc, mesh), "Fem::MeshGroup"))
|
||||
self.assertTrue(is_of_type(ObjectsFem.makeMeshRegion(doc, mesh), "Fem::MeshRegion"))
|
||||
self.assertTrue(is_of_type(ObjectsFem.makeMeshNetgen(doc), "Fem::FemMeshShapeNetgenObject"))
|
||||
self.assertTrue(is_of_type(ObjectsFem.makeMeshNetgen(doc), "Fem::FemMeshNetgen"))
|
||||
self.assertTrue(is_of_type(ObjectsFem.makeMeshResult(doc), "Fem::MeshResult"))
|
||||
self.assertTrue(is_of_type(ObjectsFem.makeResultMechanical(doc), "Fem::ResultMechanical"))
|
||||
solverelmer = ObjectsFem.makeSolverElmer(doc)
|
||||
@@ -745,7 +743,7 @@ class TestObjectType(unittest.TestCase):
|
||||
# FemMeshShapeNetgenObject
|
||||
mesh_netgen = ObjectsFem.makeMeshNetgen(doc)
|
||||
self.assertTrue(is_derived_from(mesh_netgen, "App::DocumentObject"))
|
||||
self.assertTrue(is_derived_from(mesh_netgen, "Fem::FemMeshShapeNetgenObject"))
|
||||
self.assertTrue(is_derived_from(mesh_netgen, "Fem::FemMeshShapeBaseObjectPython"))
|
||||
|
||||
# MeshResult
|
||||
mesh_result = ObjectsFem.makeMeshResult(doc)
|
||||
@@ -972,7 +970,7 @@ class TestObjectType(unittest.TestCase):
|
||||
self.assertTrue(ObjectsFem.makeMeshGroup(doc, mesh).isDerivedFrom("Fem::FeaturePython"))
|
||||
self.assertTrue(ObjectsFem.makeMeshRegion(doc, mesh).isDerivedFrom("Fem::FeaturePython"))
|
||||
self.assertTrue(
|
||||
ObjectsFem.makeMeshNetgen(doc).isDerivedFrom("Fem::FemMeshShapeNetgenObject")
|
||||
ObjectsFem.makeMeshNetgen(doc).isDerivedFrom("Fem::FemMeshShapeBaseObjectPython")
|
||||
)
|
||||
self.assertTrue(ObjectsFem.makeMeshResult(doc).isDerivedFrom("Fem::FemMeshObjectPython"))
|
||||
self.assertTrue(
|
||||
|
||||
158
src/Mod/Fem/femviewprovider/view_mesh_netgen.py
Normal file
158
src/Mod/Fem/femviewprovider/view_mesh_netgen.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
# * *
|
||||
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
# * under the terms of the GNU Lesser General Public License as *
|
||||
# * published by the Free Software Foundation, either version 2.1 of the *
|
||||
# * License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, but *
|
||||
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
# * Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Lesser General Public *
|
||||
# * License along with FreeCAD. If not, see *
|
||||
# * <https://www.gnu.org/licenses/>. *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD FEM mesh netgen ViewProvider for the document object"
|
||||
__author__ = "Mario Passaglia"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
## @package view_mesh_netgen
|
||||
# \ingroup FEM
|
||||
# \brief view provider for mesh netgen object
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
import FemGui
|
||||
from PySide import QtGui
|
||||
from femtaskpanels import task_mesh_netgen
|
||||
from femtools.femutils import is_of_type
|
||||
from femviewprovider import view_base_femobject
|
||||
|
||||
|
||||
class VPMeshNetgen(view_base_femobject.VPBaseFemObject):
|
||||
"""
|
||||
A View Provider for the MeshNetgen object
|
||||
"""
|
||||
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_MeshNetgenFromShape.svg"
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
# hide all FEM meshes and VTK FemPost* objects
|
||||
for obj in vobj.Object.Document.Objects:
|
||||
if obj.isDerivedFrom("Fem::FemMeshObject") or obj.isDerivedFrom("Fem::FemPostObject"):
|
||||
obj.ViewObject.hide()
|
||||
# show the mesh we like to edit
|
||||
self.ViewObject.show()
|
||||
# show task panel
|
||||
taskd = task_mesh_netgen._TaskPanel(self.Object)
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
return True
|
||||
|
||||
def doubleClicked(self, vobj):
|
||||
# Group meshing is only active on active analysis
|
||||
# we should make sure the analysis the mesh belongs too is active
|
||||
gui_doc = FreeCADGui.getDocument(vobj.Object.Document)
|
||||
if not gui_doc.getInEdit():
|
||||
# may be go the other way around and just activate the
|
||||
# analysis the user has doubleClicked on ?!
|
||||
# not a fast one, we need to iterate over all member of all
|
||||
# analysis to know to which analysis the object belongs too!!!
|
||||
# first check if there is an analysis in the active document
|
||||
found_an_analysis = False
|
||||
for o in gui_doc.Document.Objects:
|
||||
if o.isDerivedFrom("Fem::FemAnalysisPython"):
|
||||
found_an_analysis = True
|
||||
break
|
||||
if found_an_analysis:
|
||||
if FemGui.getActiveAnalysis() is not None:
|
||||
if FemGui.getActiveAnalysis().Document is FreeCAD.ActiveDocument:
|
||||
if self.Object in FemGui.getActiveAnalysis().Group:
|
||||
if not gui_doc.getInEdit():
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Activate the analysis this Netgen FEM "
|
||||
"mesh object belongs too!\n"
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Netgen FEM mesh object does not belong to the active analysis.\n"
|
||||
)
|
||||
found_mesh_analysis = False
|
||||
for o in gui_doc.Document.Objects:
|
||||
if o.isDerivedFrom("Fem::FemAnalysisPython"):
|
||||
for m in o.Group:
|
||||
if m == self.Object:
|
||||
found_mesh_analysis = True
|
||||
FemGui.setActiveAnalysis(o)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"The analysis the Netgen FEM mesh object "
|
||||
"belongs to was found and activated: {}\n".format(
|
||||
o.Name
|
||||
)
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
break
|
||||
if not found_mesh_analysis:
|
||||
FreeCAD.Console.PrintLog(
|
||||
"Netgen FEM mesh object does not belong to an analysis. "
|
||||
"Analysis group meshing can not be used.\n"
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
FreeCAD.Console.PrintError("Active analysis is not in active document.\n")
|
||||
else:
|
||||
FreeCAD.Console.PrintLog(
|
||||
"No active analysis in active document, "
|
||||
"we are going to have a look if the Netgen FEM mesh object "
|
||||
"belongs to a non active analysis.\n"
|
||||
)
|
||||
found_mesh_analysis = False
|
||||
for o in gui_doc.Document.Objects:
|
||||
if o.isDerivedFrom("Fem::FemAnalysisPython"):
|
||||
for m in o.Group:
|
||||
if m == self.Object:
|
||||
found_mesh_analysis = True
|
||||
FemGui.setActiveAnalysis(o)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"The analysis the Netgen FEM mesh object "
|
||||
"belongs to was found and activated: {}\n".format(o.Name)
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
break
|
||||
if not found_mesh_analysis:
|
||||
FreeCAD.Console.PrintLog(
|
||||
"Netgen FEM mesh object does not belong to an analysis. "
|
||||
"Analysis group meshing can not be used.\n"
|
||||
)
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
FreeCAD.Console.PrintLog("No analysis in the active document.\n")
|
||||
gui_doc.setEdit(vobj.Object.Name)
|
||||
else:
|
||||
from PySide.QtGui import QMessageBox
|
||||
|
||||
message = "Active Task Dialog found! Please close this one before opening a new one!"
|
||||
QMessageBox.critical(None, "Error in tree view", message)
|
||||
FreeCAD.Console.PrintError(message + "\n")
|
||||
return True
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
def loads(self, state):
|
||||
return None
|
||||
Reference in New Issue
Block a user