1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 
6 #include <stdlib.h>
7 #include <vector>
8 #include "GmshConfig.h"
9 #include "GmshMessage.h"
10 #include "meshGRegion.h"
11 #include "GModel.h"
12 #include "GRegion.h"
13 #include "GFace.h"
14 #include "MTriangle.h"
15 #include "MTetrahedron.h"
16 #include "ExtrudeParams.h"
17 #include "BackgroundMeshTools.h"
18 #include "Context.h"
19 
20 #if defined(HAVE_NETGEN)
21 
22 namespace nglib {
23 #include "nglib_gmsh.h"
24 }
25 using namespace nglib;
26 
getAllBoundingVertices(GRegion * gr,std::set<MVertex *,MVertexPtrLessThan> & allBoundingVertices)27 static void getAllBoundingVertices(
28   GRegion *gr, std::set<MVertex *, MVertexPtrLessThan> &allBoundingVertices)
29 {
30   std::vector<GFace *> faces = gr->faces();
31   auto it = faces.begin();
32 
33   while(it != faces.end()) {
34     GFace *gf = (*it);
35     for(std::size_t i = 0; i < gf->triangles.size(); i++) {
36       MTriangle *t = gf->triangles[i];
37       for(int k = 0; k < 3; k++)
38         if(allBoundingVertices.find(t->getVertex(k)) ==
39            allBoundingVertices.end())
40           allBoundingVertices.insert(t->getVertex(k));
41     }
42     ++it;
43   }
44 }
45 
buildNetgenStructure(GRegion * gr,bool importVolumeMesh,std::vector<MVertex * > & numberedV)46 static Ng_Mesh *buildNetgenStructure(GRegion *gr, bool importVolumeMesh,
47                                      std::vector<MVertex *> &numberedV)
48 {
49   Ng_Init();
50   Ng_Mesh *ngmesh = Ng_NewMesh();
51 
52   std::set<MVertex *, MVertexPtrLessThan> allBoundingVertices;
53   getAllBoundingVertices(gr, allBoundingVertices);
54 
55   auto itv = allBoundingVertices.begin();
56   int I = 1;
57   while(itv != allBoundingVertices.end()) {
58     double tmp[3];
59     tmp[0] = (*itv)->x();
60     tmp[1] = (*itv)->y();
61     tmp[2] = (*itv)->z();
62     (*itv)->setIndex(I++);
63     numberedV.push_back(*itv);
64     Ng_AddPoint(ngmesh, tmp);
65     ++itv;
66   }
67 
68   if(importVolumeMesh) {
69     for(std::size_t i = 0; i < gr->mesh_vertices.size(); i++) {
70       double tmp[3];
71       tmp[0] = gr->mesh_vertices[i]->x();
72       tmp[1] = gr->mesh_vertices[i]->y();
73       tmp[2] = gr->mesh_vertices[i]->z();
74       gr->mesh_vertices[i]->setIndex(I++);
75       Ng_AddPoint(ngmesh, tmp);
76     }
77   }
78   std::vector<GFace *> faces = gr->faces();
79   auto it = faces.begin();
80   while(it != faces.end()) {
81     GFace *gf = (*it);
82     for(std::size_t i = 0; i < gf->triangles.size(); i++) {
83       MTriangle *t = gf->triangles[i];
84       int tmp[3];
85       tmp[0] = t->getVertex(0)->getIndex();
86       tmp[1] = t->getVertex(1)->getIndex();
87       tmp[2] = t->getVertex(2)->getIndex();
88       Ng_AddSurfaceElement(ngmesh, NG_TRIG, tmp);
89     }
90     ++it;
91   }
92 
93   if(importVolumeMesh) {
94     for(std::size_t i = 0; i < gr->tetrahedra.size(); i++) {
95       MTetrahedron *t = gr->tetrahedra[i];
96       // Netgen expects tet with negative volume
97       if(t->getVolumeSign() > 0) t->reverse();
98       int tmp[4];
99       tmp[0] = t->getVertex(0)->getIndex();
100       tmp[1] = t->getVertex(1)->getIndex();
101       tmp[2] = t->getVertex(2)->getIndex();
102       tmp[3] = t->getVertex(3)->getIndex();
103       Ng_AddVolumeElement(ngmesh, NG_TET, tmp);
104     }
105   }
106 
107   return ngmesh;
108 }
109 
TransferVolumeMesh(GRegion * gr,Ng_Mesh * ngmesh,std::vector<MVertex * > & numberedV)110 static void TransferVolumeMesh(GRegion *gr, Ng_Mesh *ngmesh,
111                                std::vector<MVertex *> &numberedV)
112 {
113   // get total number of vertices and elements of Netgen's mesh
114   int nbv = Ng_GetNP(ngmesh);
115   int nbe = Ng_GetNE(ngmesh);
116   if(!nbv || !nbe) return;
117 
118   int nbpts = numberedV.size();
119 
120   // record which vertices are actually used by tetrahedra (Netgen can return
121   // additional "isolated" vertices)
122   std::set<int> used;
123   for(int i = 0; i < nbe; i++) {
124     int tmp[4];
125     Ng_GetVolumeElement(ngmesh, i + 1, tmp);
126     for(int j = 0; j < 4; j++) {
127       if(tmp[j] > nbpts) used.insert(tmp[j]);
128     }
129   }
130 
131   // create new vertices
132   for(int i = nbpts; i < nbv; i++) {
133     double tmp[3];
134     Ng_GetPoint(ngmesh, i + 1, tmp);
135     if(used.count(i + 1)) {
136       MVertex *v = new MVertex(tmp[0], tmp[1], tmp[2], gr);
137       numberedV.push_back(v);
138       gr->mesh_vertices.push_back(v);
139     }
140     else{
141       numberedV.push_back(nullptr);
142     }
143   }
144 
145   // create new tets
146   for(int i = 0; i < nbe; i++) {
147     int tmp[4];
148     Ng_GetVolumeElement(ngmesh, i + 1, tmp);
149     MVertex *v[4];
150     for(int j = 0; j < 4; j++)
151       v[j] = numberedV[tmp[j] - 1];
152     if(v[0] && v[1] && v[2] && v[3])
153       gr->tetrahedra.push_back(new MTetrahedron(v[0], v[1], v[2], v[3]));
154     else
155       Msg::Error("Tetrahedron with unknown node - should never be here!");
156   }
157 }
158 
159 // X_1 (1-u-v) + X_2 u + X_3 v = P_x + t N_x
160 // Y_1 (1-u-v) + Y_2 u + Y_3 v = P_y + t N_y
161 // Z_1 (1-u-v) + Z_2 u + Z_3 v = P_z + t N_z
162 
intersectLineTriangle(double X[3],double Y[3],double Z[3],double P[3],double N[3],const double eps_prec)163 static int intersectLineTriangle(double X[3], double Y[3], double Z[3],
164                                  double P[3], double N[3],
165                                  const double eps_prec)
166 {
167   double mat[3][3], det;
168   double b[3], res[3];
169 
170   mat[0][0] = X[1] - X[0];
171   mat[0][1] = X[2] - X[0];
172   mat[0][2] = N[0];
173 
174   mat[1][0] = Y[1] - Y[0];
175   mat[1][1] = Y[2] - Y[0];
176   mat[1][2] = N[1];
177 
178   mat[2][0] = Z[1] - Z[0];
179   mat[2][1] = Z[2] - Z[0];
180   mat[2][2] = N[2];
181 
182   b[0] = P[0] - X[0];
183   b[1] = P[1] - Y[0];
184   b[2] = P[2] - Z[0];
185 
186   if(!sys3x3_with_tol(mat, b, res, &det)) { return 0; }
187   //  printf("coucou %g %g %g\n",res[0],res[1],res[2]);
188   if(res[0] >= eps_prec && res[0] <= 1.0 - eps_prec && res[1] >= eps_prec &&
189      res[1] <= 1.0 - eps_prec && 1 - res[0] - res[1] >= eps_prec &&
190      1 - res[0] - res[1] <= 1.0 - eps_prec) {
191     // the line clearly intersects the triangle
192     return (res[2] > 0) ? 1 : 0;
193   }
194   else if(res[0] < -eps_prec || res[0] > 1.0 + eps_prec || res[1] < -eps_prec ||
195           res[1] > 1.0 + eps_prec || 1 - res[0] - res[1] < -eps_prec ||
196           1 - res[0] - res[1] > 1.0 + eps_prec) {
197     // the line clearly does NOT intersect the triangle
198     return 0;
199   }
200   else {
201     // printf("non robust stuff\n");
202     // the intersection is not robust, try another triangle
203     return -10000;
204   }
205 }
206 
setRand(double r[6])207 static void setRand(double r[6])
208 {
209   for(int i = 0; i < 6; i++)
210     r[i] = 0.0001 * ((double)rand() / (double)RAND_MAX);
211 }
212 
meshNormalsPointOutOfTheRegion(GRegion * gr)213 static void meshNormalsPointOutOfTheRegion(GRegion *gr)
214 {
215   std::vector<GFace *> faces = gr->faces();
216   auto it = faces.begin();
217 
218   // perform intersection check in normalized coordinates
219   SBoundingBox3d bbox = gr->bounds();
220   double scaling = norm(SVector3(bbox.max(), bbox.min()));
221   if(!scaling) {
222     Msg::Warning("Bad scaling in meshNormalsPointOutOfTheRegion");
223     scaling = 1.;
224   }
225 
226   double rrr[6];
227   setRand(rrr);
228 
229   while(it != faces.end()) {
230     GFace *gf = (*it);
231     int nb_intersect = 0;
232     for(std::size_t i = 0; i < gf->triangles.size(); i++) {
233       MTriangle *t = gf->triangles[i];
234       double X[3] = {t->getVertex(0)->x(), t->getVertex(1)->x(),
235                      t->getVertex(2)->x()};
236       double Y[3] = {t->getVertex(0)->y(), t->getVertex(1)->y(),
237                      t->getVertex(2)->y()};
238       double Z[3] = {t->getVertex(0)->z(), t->getVertex(1)->z(),
239                      t->getVertex(2)->z()};
240       for(int j = 0; j < 3; j++) {
241         X[j] /= scaling;
242         Y[j] /= scaling;
243         Z[j] /= scaling;
244       }
245 
246       double P[3] = {(X[0] + X[1] + X[2]) / 3., (Y[0] + Y[1] + Y[2]) / 3.,
247                      (Z[0] + Z[1] + Z[2]) / 3.};
248       double v1[3] = {X[0] - X[1], Y[0] - Y[1], Z[0] - Z[1]};
249       double v2[3] = {X[2] - X[1], Y[2] - Y[1], Z[2] - Z[1]};
250       double N[3];
251       prodve(v1, v2, N);
252       norme(v1);
253       norme(v2);
254       norme(N);
255       N[0] += rrr[0] * v1[0] + rrr[1] * v2[0];
256       N[1] += rrr[2] * v1[1] + rrr[3] * v2[1];
257       N[2] += rrr[4] * v1[2] + rrr[5] * v2[2];
258       norme(N);
259       auto it_b = faces.begin();
260       while(it_b != faces.end()) {
261         GFace *gf_b = (*it_b);
262         for(std::size_t i_b = 0; i_b < gf_b->triangles.size(); i_b++) {
263           MTriangle *t_b = gf_b->triangles[i_b];
264           if(t_b != t) {
265             double X_b[3] = {t_b->getVertex(0)->x(), t_b->getVertex(1)->x(),
266                              t_b->getVertex(2)->x()};
267             double Y_b[3] = {t_b->getVertex(0)->y(), t_b->getVertex(1)->y(),
268                              t_b->getVertex(2)->y()};
269             double Z_b[3] = {t_b->getVertex(0)->z(), t_b->getVertex(1)->z(),
270                              t_b->getVertex(2)->z()};
271             for(int j = 0; j < 3; j++) {
272               X_b[j] /= scaling;
273               Y_b[j] /= scaling;
274               Z_b[j] /= scaling;
275             }
276             int inters = intersectLineTriangle(X_b, Y_b, Z_b, P, N, 1.e-9);
277             nb_intersect += inters;
278           }
279         }
280         ++it_b;
281       }
282       Msg::Info("Region %d Face %d, %d intersect", gr->tag(), gf->tag(),
283                 nb_intersect);
284       if(nb_intersect >= 0)
285         break; // negative value means intersection is not "robust"
286     }
287 
288     if(nb_intersect < 0) { setRand(rrr); }
289     else {
290       if(nb_intersect % 2 == 1) {
291         // odd nb of intersections: the normal points inside the region
292         for(std::size_t i = 0; i < gf->triangles.size(); i++) {
293           gf->triangles[i]->reverse();
294         }
295       }
296       ++it;
297     }
298   }
299 
300   // FILE *fp = Fopen("debug.pos", "w");
301   // if(fp){
302   //   fprintf(fp, "View \"debug\" {\n");
303   //   for(auto it = faces.begin(); it != faces.end(); it++)
304   //     for(std::size_t i = 0; i < (*it)->triangles.size(); i++)
305   //       (*it)->triangles[i]->writePOS(fp, 1., (*it)->tag());
306   //   fprintf(fp, "};\n");
307   //   fclose(fp);
308   // }
309 }
310 
311 #endif
312 
meshGRegionNetgen(GRegion * gr)313 void meshGRegionNetgen(GRegion *gr)
314 {
315 #if !defined(HAVE_NETGEN)
316   Msg::Error("Frontal algorithm requires Netgen");
317 #else
318   // sanity check for frontal algo
319   std::vector<GFace *> faces = gr->faces();
320   for(auto it = faces.begin(); it != faces.end(); it++) {
321     if((*it)->quadrangles.size()) {
322       Msg::Error(
323         "Cannot use frontal 3D algorithm with quadrangles on boundary");
324       return;
325     }
326   }
327 
328   Msg::Info("Meshing volume %d (Frontal)", gr->tag());
329   // orient the triangles of with respect to this region
330   meshNormalsPointOutOfTheRegion(gr);
331   std::vector<MVertex *> numberedV;
332   Ng_Mesh *ngmesh = buildNetgenStructure(gr, false, numberedV);
333   SPoint3 pt = gr->bounds().center();
334   double lc = BGM_MeshSize(gr, 0, 0, pt.x(), pt.y(), pt.z());
335   Ng_GenerateVolumeMesh(ngmesh, lc);
336   TransferVolumeMesh(gr, ngmesh, numberedV);
337   Ng_DeleteMesh(ngmesh);
338   Ng_Exit();
339 #endif
340 }
341 
operator ()(GRegion * gr,bool always)342 void optimizeMeshGRegionNetgen::operator()(GRegion *gr, bool always)
343 {
344   gr->model()->setCurrentMeshEntity(gr);
345 
346   if(!always && gr->geomType() == GEntity::DiscreteVolume) return;
347 
348   // don't optimize transfinite or extruded meshes
349   if(gr->meshAttributes.method == MESH_TRANSFINITE) return;
350   ExtrudeParams *ep = gr->meshAttributes.extrude;
351   if(ep && ep->mesh.ExtrudeMesh && ep->geo.Mode == EXTRUDED_ENTITY) return;
352 
353   if(gr->prisms.size() || gr->hexahedra.size() || gr->pyramids.size()) {
354     Msg::Info("Skipping Netgen optimizer for hybrid mesh");
355     return;
356   }
357 
358 #if !defined(HAVE_NETGEN)
359   Msg::Error("Netgen optimizer is not compiled in this version of Gmsh");
360 #else
361   Msg::Info("Optimizing volume %d", gr->tag());
362   // import mesh into netgen, including volume tets
363   std::vector<MVertex *> numberedV;
364   Ng_Mesh *ngmesh = buildNetgenStructure(gr, true, numberedV);
365   // delete volume vertices and tets
366   deMeshGRegion dem;
367   dem(gr);
368   // optimize mesh
369   SPoint3 pt = gr->bounds().center();
370   double lc = BGM_MeshSize(gr, 0, 0, pt.x(), pt.y(), pt.z());
371   Ng_OptimizeVolumeMesh(ngmesh, lc);
372   TransferVolumeMesh(gr, ngmesh, numberedV);
373   Ng_DeleteMesh(ngmesh);
374   Ng_Exit();
375 #endif
376 }
377