1 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 // + This file is part of enGrid. +
5 // + Copyright 2008-2014 enGits GmbH +
7 // + enGrid is free software: you can redistribute it and/or modify +
8 // + it under the terms of the GNU General Public License as published by +
9 // + the Free Software Foundation, either version 3 of the License, or +
10 // + (at your option) any later version. +
12 // + enGrid is distributed in the hope that it will be useful, +
13 // + but WITHOUT ANY WARRANTY; without even the implied warranty of +
14 // + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +
15 // + GNU General Public License for more details. +
17 // + You should have received a copy of the GNU General Public License +
18 // + along with enGrid. If not, see <http://www.gnu.org/licenses/>. +
20 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
22 #include "tetgenoperation.h"
24 #include "guimainwindow.h"
25 #include "optimisenormalvector.h"
29 TetGenOperation::TetGenOperation()
31 // The following code will be used once the option to start TetGen in a
32 // separate process has been implemented
35 QDir cwd(GuiMainWindow::pointer()->getCwd());
36 m_TetGenPath = GuiMainWindow::pointer()->getCwd() + "tetgen";
37 QDir tetgen_dir(m_TetGenPath);
38 if (!tetgen_dir.exists()) {
44 void TetGenOperation::copyToTetGen(tetgenio
&tgio
, vtkUnstructuredGrid
*alt_grid
)
46 vtkUnstructuredGrid
*grid
= alt_grid
;
50 MeshPartition
part(grid
, true);
51 EG_VTKDCC(vtkIntArray
, cell_code
, grid
, "cell_code");
52 EG_VTKDCC(vtkIntArray
, cell_orgdir
, grid
, "cell_orgdir");
53 EG_VTKDCC(vtkIntArray
, cell_curdir
, grid
, "cell_curdir");
54 EG_VTKDCC(vtkIntArray
, cell_voldir
, grid
, "cell_voldir");
56 tgio
.numberofpoints
= grid
->GetNumberOfPoints();
57 tgio
.pointlist
= new REAL
[3*tgio
.numberofpoints
];
58 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
60 grid
->GetPoint(id_node
, x
.data());
61 for (int i
= 0; i
< 3; ++i
) {
62 tgio
.pointlist
[3*id_node
+ i
] = x
[i
];
65 QList
<vtkIdType
> triangles
, tetrahedra
;
66 for (vtkIdType id_cell
= 0; id_cell
< grid
->GetNumberOfCells(); ++id_cell
) {
67 if (grid
->GetCellType(id_cell
) == VTK_TRIANGLE
) triangles
.append(id_cell
);
68 else if (grid
->GetCellType(id_cell
) == VTK_TETRA
) tetrahedra
.append(id_cell
);
73 tgio
.numberoffacets
= triangles
.size();
74 tgio
.facetmarkerlist
= new int [triangles
.size()];
75 tgio
.facetlist
= new tetgenio::facet
[triangles
.size()];
78 foreach (vtkIdType id_cell
, triangles
) {
79 m_OrgDir
= cell_orgdir
->GetValue(id_cell
);
80 m_CurDir
= cell_curdir
->GetValue(id_cell
);
81 m_VolDir
= cell_voldir
->GetValue(id_cell
);
82 EG_GET_CELL(id_cell
, grid
);
83 tgio
.facetlist
[i
].numberofpolygons
= 1;
84 tgio
.facetlist
[i
].polygonlist
= new tetgenio::polygon
[1];
85 tgio
.facetlist
[i
].numberofholes
= 0;
86 tgio
.facetlist
[i
].holelist
= NULL
;
87 tetgenio::polygon
&poly
= tgio
.facetlist
[i
].polygonlist
[0];
88 poly
.numberofvertices
= 3;
89 poly
.vertexlist
= new int[3];
90 for (int j
= 0; j
< num_pts
; ++j
) {
91 poly
.vertexlist
[j
] = pts
[j
];
93 tgio
.facetmarkerlist
[i
] = cell_code
->GetValue(id_cell
);
98 tgio
.numberoftetrahedra
= tetrahedra
.size();
99 tgio
.tetrahedronlist
= new int [tetrahedra
.size()*4];
101 foreach (vtkIdType id_cell
, tetrahedra
) {
102 EG_GET_CELL(id_cell
, grid
);
103 for (int j
= 0; j
< num_pts
; ++j
) {
104 tgio
.tetrahedronlist
[i
*num_pts
+ j
] = pts
[j
];
109 QVector
<bool> provide_mesh_resolution(grid
->GetNumberOfPoints(), true);
110 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
111 provide_mesh_resolution
[id_node
] = false;
113 for (vtkIdType id_cell
= 0; id_cell
< grid
->GetNumberOfCells(); ++id_cell
) {
114 if (isSurface(id_cell
, grid
)) {
115 EG_GET_CELL(id_cell
, grid
);
116 for (int i
= 0; i
< num_pts
; ++i
) {
117 provide_mesh_resolution
[pts
[i
]] = true;
121 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
122 for (int i
= 0; i
< part
.n2nGSize(id_node
); ++i
) {
123 provide_mesh_resolution
[part
.n2nGG(id_node
,i
)] = true;
127 tgio
.numberofpointmtrs
= 1;
128 tgio
.pointmtrlist
= new REAL
[tgio
.numberofpoints
];
129 for (vtkIdType id_node
= 0; id_node
< grid
->GetNumberOfPoints(); ++id_node
) {
130 tgio
.pointmtrlist
[id_node
] = 0;
131 if (provide_mesh_resolution
[id_node
]) {
133 grid
->GetPoint(id_node
, x
.data());
134 /// @todo look into this
135 double l
= min(m_MaximalEdgeLength
, 0.66*m_ELSManager
.minEdgeLength(x
)); // apply empirical scaling to match surface resolution
136 tgio
.pointmtrlist
[id_node
] = l
;
142 void TetGenOperation::copyFromTetGen(tetgenio
&tgio
)
144 // build search tree to correct surface orientation
145 QVector
<vtkIdType
> tri_faces
;
146 getAllCellsOfType(VTK_TRIANGLE
, tri_faces
, m_Grid
);
147 QVector
<Triangle
> triangles(tri_faces
.size());
148 TriangleTree search_tree
;
151 foreach (vtkIdType id_cell
, tri_faces
) {
152 EG_GET_CELL(id_cell
, m_Grid
);
154 m_Grid
->GetPoint(pts
[0], a
.data());
155 m_Grid
->GetPoint(pts
[1], b
.data());
156 m_Grid
->GetPoint(pts
[2], c
.data());
157 triangles
[i
] = Triangle(Point(a
[0], a
[1], a
[2]), Point(b
[0], b
[1], b
[2]), Point(c
[0], c
[1], c
[2]));
161 search_tree
.rebuild(triangles
.begin(), triangles
.end());
162 search_tree
.accelerate_distance_queries();
164 EG_VTKSP(vtkUnstructuredGrid
, full_grid
);
165 allocateGrid(full_grid
, tgio
.numberoftetrahedra
+ tgio
.numberoftrifaces
, tgio
.numberofpoints
);
166 EG_VTKDCC(vtkIntArray
, cell_code
, full_grid
, "cell_code");
167 EG_VTKDCC(vtkIntArray
, cell_orgdir
, full_grid
, "cell_orgdir");
168 EG_VTKDCC(vtkIntArray
, cell_curdir
, full_grid
, "cell_curdir");
169 EG_VTKDCC(vtkIntArray
, cell_voldir
, full_grid
, "cell_voldir");
170 for (vtkIdType id_node
= 0; id_node
< tgio
.numberofpoints
; ++id_node
) {
172 for (int i
= 0; i
< 3; ++i
) {
173 x
[i
] = tgio
.pointlist
[3*id_node
+ i
];
175 full_grid
->GetPoints()->SetPoint(id_node
, x
.data());
177 for (int i
= 0; i
< tgio
.numberoftetrahedra
; ++i
) {
178 vtkIdType num_pts
= 4, pts
[4];
179 for (int j
= 0; j
< num_pts
; ++j
) {
180 pts
[j
] = tgio
.tetrahedronlist
[i
*num_pts
+ j
];
182 vtkIdType id_cell
= full_grid
->InsertNextCell(VTK_TETRA
, num_pts
, pts
);
183 cell_code
->SetValue(id_cell
, 0);
185 for (int i
= 0; i
< tgio
.numberoftrifaces
; ++i
) {
186 vtkIdType num_pts
= 3, pts
[3];
187 for (int j
= 0; j
< num_pts
; ++j
) {
188 //pts[2-j] = tgio.trifacelist[i*num_pts + j];
189 pts
[j
] = tgio
.trifacelist
[i
*num_pts
+ j
];
193 full_grid
->GetPoint(pts
[0], x1
.data());
194 full_grid
->GetPoint(pts
[1], x2
.data());
195 full_grid
->GetPoint(pts
[2], x3
.data());
196 vec3_t xm
= (1.0/3.0)*(x1
+ x2
+ x3
);
197 vec3_t n1
= GeometryTools::triNormal(x1
, x2
, x3
);
198 TriangleTree::Point_and_primitive_id cp
= search_tree
.closest_point_and_primitive(Point(xm
[0], xm
[1], xm
[2]));
200 Point pa
= cp
.second
->vertex(0);
201 vec3_t
xa(pa
[0], pa
[1], pa
[2]);
202 Point pb
= cp
.second
->vertex(1);
203 vec3_t
xb(pb
[0], pb
[1], pb
[2]);
204 Point pc
= cp
.second
->vertex(2);
205 vec3_t
xc(pc
[0], pc
[1], pc
[2]);
207 vec3_t n2
= GeometryTools::triNormal(xa
, xb
, xc
);
209 swap(pts
[0], pts
[1]);
212 vtkIdType id_cell
= full_grid
->InsertNextCell(VTK_TRIANGLE
, num_pts
, pts
);
213 cell_code
->SetValue(id_cell
, tgio
.trifacemarkerlist
[i
]);
214 cell_orgdir
->SetValue(id_cell
, m_OrgDir
);
215 cell_curdir
->SetValue(id_cell
, m_CurDir
);
216 cell_voldir
->SetValue(id_cell
, m_VolDir
);
219 QVector
<int> keep_cell(full_grid
->GetNumberOfCells(), 0);
220 MeshPartition
full_part(full_grid
, true);
221 QList
<vtkIdType
> seed_cells
;
222 EG_FORALL_CELLS(id_cell
, full_grid
) {
223 EG_GET_CELL(id_cell
, full_grid
);
224 if (type_cell
== VTK_TRIANGLE
) {
225 QVector
<QSet
<vtkIdType
> > cells(3);
226 for (int i
= 0; i
< 3; ++i
) {
227 for (int j
= 0; j
< full_part
.n2cGSize(pts
[i
]); ++j
) {
228 cells
[i
].insert(full_part
.n2cGG(pts
[i
], j
));
231 cells
[0].intersect(cells
[1]);
232 cells
[0].intersect(cells
[2]);
233 vec3_t x_face
= cellCentre(full_grid
, id_cell
);
234 vec3_t n_face
= cellNormal(full_grid
, id_cell
);
235 foreach (vtkIdType id_neigh
, cells
[0]) {
236 if (full_grid
->GetCellType(id_neigh
) == VTK_TETRA
) {
237 vec3_t x_cell
= cellCentre(full_grid
, id_neigh
);
238 if ((x_cell
- x_face
)*n_face
< 0) { // !!!
239 keep_cell
[id_neigh
] = 1;
241 if (keep_cell
[id_neigh
] == 0) {
242 keep_cell
[id_neigh
] = 2;
243 seed_cells
.append(id_neigh
);
250 while (seed_cells
.size() > 0) {
251 QList
<vtkIdType
> new_seed_cells
;
252 foreach (vtkIdType id_cell
, seed_cells
) {
253 for (int i
= 0; i
< full_part
.c2cGSize(id_cell
); ++i
) {
254 vtkIdType id_neigh
= full_part
.c2cGG(id_cell
, i
);
255 if (keep_cell
[id_neigh
] == 0) {
256 new_seed_cells
.append(id_neigh
);
257 keep_cell
[id_neigh
] = 2;
261 seed_cells
= new_seed_cells
;
263 QList
<vtkIdType
> keep_cells
;
264 EG_FORALL_CELLS(id_cell
, full_grid
) {
265 if (keep_cell
[id_cell
] < 2) {
266 keep_cells
.append(id_cell
);
269 cout
<< "keeping " << keep_cells
.size() << " cells" << endl
;
270 MeshPartition
keep_part(full_grid
);
271 keep_part
.setCells(keep_cells
);
272 keep_part
.extractToVtkGrid(m_Grid
);
273 //makeCopy(full_grid, m_Grid);
276 void TetGenOperation::readSettings()
278 QString buffer
= GuiMainWindow::pointer()->getXmlSection("engrid/surface/settings");
279 if(!buffer
.isEmpty()) {
280 QTextStream
in(&buffer
, QIODevice::ReadOnly
);
281 in
>> m_MaximalEdgeLength
;
282 in
>> m_MinimalEdgeLength
;
283 in
>> m_GrowthFactor
;
284 in
>> m_NodesPerQuarterCircle
;
288 for (int i
= 0; i
< num_bcs
; ++i
) {
291 in
>> m_2dFeatureResolution
;
292 in
>> m_3dFeatureResolution
;
296 m_ELSManager
.readRules(m_Grid
);
299 QString
TetGenOperation::qualityText()
301 using namespace GeometryTools
;
302 vec3_t
x1(1.0/sqrt(3.0), 0, 0);
303 vec3_t x2
= rotate(x1
, vec3_t(0, 0, 1), deg2rad(60.0));
304 vec3_t x3
= rotate(x2
, vec3_t(0, 0, 1), deg2rad(60.0));
305 vec3_t
x4(0, 0, 1.0);
306 vec3_t n1
= triNormal(x1
, x2
, x3
);
307 vec3_t n2
= triNormal(x1
, x4
, x2
);
308 vec3_t n3
= triNormal(x2
, x4
, x3
);
309 double alpha
= angle(n1
, n2
);
310 alpha
= min(alpha
, angle(n1
, n3
));
311 alpha
= min(alpha
, angle(n2
, n3
));
313 alpha_txt
.setNum(rad2deg(alpha
));
314 return QString("1.4") + "/" + alpha_txt
;
317 void TetGenOperation::tetgen(QString flags
, vtkUnstructuredGrid
*background_grid
)
320 tetgenio in
, out
, background
;
323 copyToTetGen(background
, background_grid
);
324 char *char_flags
= new char [flags
.size() + 1];
325 strcpy(char_flags
, qPrintable(flags
));
326 tetrahedralize(char_flags
, &in
, &out
, NULL
, &background
);
328 delete [] char_flags
;
331 case 1: // Out of memory.
332 EG_ERR_RETURN("TetGen error: Out of memory");
334 case 2: // Encounter an internal error.
335 EG_ERR_RETURN("TetGen error: internal error");
338 EG_ERR_RETURN("TetGen error: A self-intersection was detected.");
341 EG_ERR_RETURN("TetGen error: A very small input feature size was detected.");
344 EG_ERR_RETURN("TetGen error: Two very close input facets were detected.");
347 EG_ERR_RETURN("TetGen error: An input error was detected.");
351 EG_ERR_RETURN("TetGen error: unknown error");