1 /*******************************************************************************
2 QuadTriExtruded3D.cpp
3
4 The code in this file was written by Dr. Trevor S. Strickler.
5 email: <trevor.strickler@gmail.com>
6
7 This file is part of the QuadTri contribution to Gmsh. QuadTri allows the
8 conformal interface of quadrangle faces to triangle faces using pyramids and
9 other mesh elements.
10
11 Trevor S. Strickler hereby transfers copyright of QuadTri files to Christophe
12 Geuzaine and J.-F. Remacle with the understanding that his contribution shall be
13 cited appropriately. See the README.txt file for license information.
14 ********************************************************************************/
15
16 #include "QuadTriExtruded3D.h"
17
18 // By Geuzaine, Remacle...
addTetrahedron(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,GRegion * to,MElement * source)19 static void addTetrahedron(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
20 GRegion *to, MElement *source)
21 {
22 MTetrahedron *newElem = new MTetrahedron(v1, v2, v3, v4);
23 to->tetrahedra.push_back(newElem);
24 }
25
26 // By Geuzaine, Remacle...
addPyramid(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,MVertex * v5,GRegion * to,MElement * source)27 static void addPyramid(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
28 MVertex *v5, GRegion *to, MElement *source)
29 {
30 MPyramid *newElem = new MPyramid(v1, v2, v3, v4, v5);
31 to->pyramids.push_back(newElem);
32 }
33
34 // By Geuzaine, Remacle...
addPrism(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,MVertex * v5,MVertex * v6,GRegion * to,MElement * source)35 static void addPrism(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
36 MVertex *v5, MVertex *v6, GRegion *to, MElement *source)
37 {
38 MPrism *newElem = new MPrism(v1, v2, v3, v4, v5, v6);
39 to->prisms.push_back(newElem);
40 }
41
42 // By Geuzaine, Remacle...
addHexahedron(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,MVertex * v5,MVertex * v6,MVertex * v7,MVertex * v8,GRegion * to,MElement * source)43 static void addHexahedron(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
44 MVertex *v5, MVertex *v6, MVertex *v7, MVertex *v8,
45 GRegion *to, MElement *source)
46 {
47 MHexahedron *newElem = new MHexahedron(v1, v2, v3, v4, v5, v6, v7, v8);
48 to->hexahedra.push_back(newElem);
49 }
50
51 // Does the pair of MVertex pointers v1 and v2 exist in the set 'edges'?
edgeExists(MVertex * v1,MVertex * v2,std::set<std::pair<MVertex *,MVertex * >> & edges)52 static int edgeExists(MVertex *v1, MVertex *v2,
53 std::set<std::pair<MVertex *, MVertex *> > &edges)
54 {
55 std::pair<MVertex *, MVertex *> p(std::min(v1, v2), std::max(v1, v2));
56 return edges.count(p);
57 }
58
59 // Create the pair of MVertex pointers v1 and v2 exist in the set 'edges.'
createEdge(MVertex * v1,MVertex * v2,std::set<std::pair<MVertex *,MVertex * >> & edges)60 static void createEdge(MVertex *v1, MVertex *v2,
61 std::set<std::pair<MVertex *, MVertex *> > &edges)
62 {
63 std::pair<MVertex *, MVertex *> p(std::min(v1, v2), std::max(v1, v2));
64 edges.insert(p);
65 }
66
67 // Create the entry for a forbidden edge in forbidden_edges (note that all four
68 // verts are forbidden, but only store two, using lowest vertex pointer
69 // diagonal).
70 static void
createForbidden(std::vector<MVertex * > v,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges)71 createForbidden(std::vector<MVertex *> v,
72 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges)
73 {
74 if(v.size() != 4) {
75 Msg::Error("In createForbidden(), number of vertices not equal 4.");
76 return;
77 }
78 int ind_low = 0;
79 if(v[1] < v[ind_low]) ind_low = 1;
80 if(v[2] < v[ind_low]) ind_low = 2;
81 if(v[3] < v[ind_low]) ind_low = 3;
82
83 std::pair<MVertex *, MVertex *> p(v[ind_low], v[(ind_low + 2) % 4]);
84 forbidden_edges.insert(p);
85 }
86
87 // Is the given vector of quad vertices forbidden to diagonalize (it is in
88 // forbidden_edges)?
89 static int
forbiddenExists(std::vector<MVertex * > v,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges)90 forbiddenExists(std::vector<MVertex *> v,
91 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges)
92 {
93 if(v.size() != 4) {
94 Msg::Error(
95 "forbiddenExists() was passed a vector argument that was not of size 4.");
96 return 0;
97 }
98
99 int ind_low = 0;
100 if(v[1] < v[ind_low]) ind_low = 1;
101 if(v[2] < v[ind_low]) ind_low = 2;
102 if(v[3] < v[ind_low]) ind_low = 3;
103
104 std::pair<MVertex *, MVertex *> pair(v[ind_low], v[(ind_low + 2) % 4]);
105 return forbidden_edges.count(pair);
106 }
107
108 // delete a pair of vertex pointers v1 and v2 from 'edges.'
deleteEdge(MVertex * v1,MVertex * v2,std::set<std::pair<MVertex *,MVertex * >> & edges)109 static void deleteEdge(MVertex *v1, MVertex *v2,
110 std::set<std::pair<MVertex *, MVertex *> > &edges)
111 {
112 std::pair<MVertex *, MVertex *> p(std::min(v1, v2), std::max(v1, v2));
113 edges.erase(p);
114 }
115
116 // Get the two mesh vertices extruded from vertices v0 and v1 on a lateral face
117 // at layer j, element k. Added 2010-01-26
118 static std::vector<MVertex *>
getExtrudedLateralVertices(MVertex * v0,MVertex * v1,GEntity * entity,unsigned int j,unsigned int k,ExtrudeParams * loop_ep,MVertexRTree & pos)119 getExtrudedLateralVertices(MVertex *v0, MVertex *v1, GEntity *entity,
120 unsigned int j, unsigned int k,
121 ExtrudeParams *loop_ep, MVertexRTree &pos)
122 {
123 std::vector<MVertex *> verts;
124 double x[4] = {v0->x(), v1->x(), v0->x(), v1->x()};
125 double y[4] = {v0->y(), v1->y(), v0->y(), v1->y()};
126 double z[4] = {v0->z(), v1->z(), v0->z(), v1->z()};
127 for(int p = 0; p < 2; p++) {
128 loop_ep->Extrude(j, k, x[p], y[p], z[p]);
129 loop_ep->Extrude(j, k + 1, x[p + 2], y[p + 2], z[p + 2]);
130 }
131 for(int p = 0; p < 4; p++) {
132 MVertex *tmp = pos.find(x[p], y[p], z[p]);
133 if(!tmp) {
134 Msg::Error("Could not find extruded vertex (%.16g, %.16g, %.16g) in "
135 "geometrical entity %d",
136 x[p], y[p], z[p], entity->tag());
137 verts.clear();
138 return verts;
139 }
140 verts.push_back(tmp);
141 }
142
143 return verts;
144 }
145
146 // Get the extruded vertices from MElement *elem at layer j, element k. Added
147 // 2010-01-26
get2DExtrudedVertices(MElement * elem,ExtrudeParams * ep,unsigned int j,unsigned int k,MVertexRTree & pos,std::vector<MVertex * > & verts)148 static int get2DExtrudedVertices(MElement *elem, ExtrudeParams *ep,
149 unsigned int j, unsigned int k,
150 MVertexRTree &pos,
151 std::vector<MVertex *> &verts)
152 {
153 std::vector<MVertex *> source_verts;
154 elem->getVertices(source_verts);
155
156 int sz = source_verts.size();
157 std::vector<double> x(sz), y(sz), z(sz);
158 for(int p = 0; p < sz; p++) {
159 x[p] = source_verts[p]->x();
160 y[p] = source_verts[p]->y();
161 z[p] = source_verts[p]->z();
162 }
163 for(int p = 0; p < sz; p++) {
164 ep->Extrude(j, k, x[p], y[p], z[p]);
165 MVertex *tmp = pos.find(x[p], y[p], z[p]);
166 if(!tmp) {
167 Msg::Error("Could not find extruded vertex (%.16g, %.16g, %.16g).", x[p],
168 y[p], z[p]);
169 verts.clear();
170 return verts.size();
171 }
172 verts.push_back(tmp);
173 }
174
175 return verts.size();
176 }
177
178 // Copied from meshGRegionExtruded.cpp, By Geuzaine, Remacle... Extrudes a set
179 // of source vertices in 3D added 2010-01-18
getExtrudedVertices(MElement * ele,ExtrudeParams * ep,int j,int k,MVertexRTree & pos,std::vector<MVertex * > & verts)180 static int getExtrudedVertices(MElement *ele, ExtrudeParams *ep, int j, int k,
181 MVertexRTree &pos, std::vector<MVertex *> &verts)
182 {
183 double x[8], y[8], z[8];
184 int n = ele->getNumVertices();
185 for(int p = 0; p < n; p++) {
186 MVertex *v = ele->getVertex(p);
187 x[p] = x[p + n] = v->x();
188 y[p] = y[p + n] = v->y();
189 z[p] = z[p + n] = v->z();
190 }
191 for(int p = 0; p < n; p++) {
192 ep->Extrude(j, k, x[p], y[p], z[p]);
193 ep->Extrude(j, k + 1, x[p + n], y[p + n], z[p + n]);
194 }
195 for(int p = 0; p < 2 * n; p++) {
196 MVertex *tmp = pos.find(x[p], y[p], z[p]);
197 if(!tmp)
198 Msg::Error("Could not find extruded vertex (%.16g, %.16g, %.16g)", x[p],
199 y[p], z[p]);
200 else
201 verts.push_back(tmp);
202 }
203 return verts.size();
204 }
205
206 // Determines whether the region is a valid QuadToTri region. Performs some
207 // basic checks, including whether there is a valid top, valid source, and that
208 // the surfaces serving as laterals are structured Added 2010-12-30
IsValidQuadToTriRegion(GRegion * region,bool * allNonGlobalSharedLaterals)209 bool IsValidQuadToTriRegion(GRegion *region, bool *allNonGlobalSharedLaterals)
210 {
211 ExtrudeParams *ep = region->meshAttributes.extrude;
212
213 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) return false;
214
215 GModel *model = region->model();
216
217 // find source face
218 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
219 if(!reg_source) {
220 Msg::Error("In IsValidQuadToTriRegion(), could not find source face "
221 "%d for region %d.",
222 std::abs(ep->geo.Source), region->tag());
223 return false;
224 }
225
226 bool is_toroidal = IsInToroidalQuadToTri(reg_source);
227 GFace *root = findRootSourceFaceForFace(reg_source);
228
229 // Find a source surface. Then find a COPIED_ENTITY that is the top surface.
230 // Then determine if all the laterals are either all quad or all triangle. If
231 // shared laterals are all static (quad or non subdivide triangles), set the
232 // allNonGlobalSharedLaterals argument to true. If any lateral is
233 // unstructured, error.
234
235 bool foundTop = false, foundSource = false, foundNoStruct = false,
236 foundRoot = false;
237
238 std::vector<GFace *> faces = region->faces();
239 std::vector<GFace *>::iterator it = faces.begin();
240
241 (*allNonGlobalSharedLaterals) = true;
242
243 for(it = faces.begin(); it != faces.end(); it++) {
244 ExtrudeParams *face_tmp_ep = (*it)->meshAttributes.extrude;
245 if((*it) == root) foundRoot = true;
246 if((*it) == reg_source)
247 foundSource = true;
248 else if(face_tmp_ep && face_tmp_ep->geo.Mode == COPIED_ENTITY) {
249 GFace *top_source_tmp =
250 model->getFaceByTag(std::abs(face_tmp_ep->geo.Source));
251 if(!top_source_tmp) {
252 Msg::Error("In IsValidQuadToTriRegion(), could not find source face "
253 "%d for copied surface %d of region %d.",
254 std::abs(face_tmp_ep->geo.Source), (*it)->tag(),
255 region->tag());
256 return false;
257 }
258 else if(top_source_tmp == reg_source &&
259 !IsSurfaceALateralForRegion(region, *it))
260 foundTop = true;
261 }
262 // This is a check to see if there are lateral surface triangles that need
263 // to be edged globally in subdivide operation
264 else if(IsSurfaceALateralForRegion(region, *it)) {
265 std::vector<GRegion *> neighbors;
266 if((*allNonGlobalSharedLaterals) && (*it)->triangles.size() &&
267 !(*it)->quadrangles.size() &&
268 GetNeighborRegionsOfFace(*it, neighbors) > 1) {
269 GRegion *other_region =
270 neighbors[0] != region ? neighbors[0] : neighbors[1];
271 ExtrudeParams *oth_ep = other_region->meshAttributes.extrude;
272 if((ep && ep->mesh.ExtrudeMesh && !ep->mesh.Recombine) ||
273 (oth_ep && oth_ep->mesh.ExtrudeMesh && !oth_ep->mesh.Recombine &&
274 IsSurfaceALateralForRegion(other_region, *it)))
275 (*allNonGlobalSharedLaterals) = false;
276 }
277 }
278 else if(!is_toroidal)
279 foundNoStruct = true;
280 }
281
282 // if didn't find the copied entity, maybe this is toroidal and the top has
283 // been replaced
284 if(is_toroidal && !foundTop && foundRoot && root != reg_source)
285 foundTop = true;
286
287 // test for errors
288 bool detectConflict = false;
289 if(!foundTop) {
290 Msg::Error("In IsValidQuadToTriRegion(), could not find top face "
291 "of region %d.",
292 region->tag());
293 detectConflict = true;
294 }
295 if(!foundSource) {
296 Msg::Error("In IsValidQuadToTriRegion(), source "
297 "face %d of region %d was not found in region.",
298 region->tag());
299 detectConflict = true;
300 }
301 if(foundNoStruct) {
302 Msg::Error("In IsValidQuadToTriRegion(), found unstructured "
303 "lateral in QuadToTri region %d.",
304 region->tag());
305 detectConflict = true;
306 }
307
308 if(detectConflict) return false;
309
310 // no errors, return true
311 return true;
312 }
313
314 // This function returns a vector of integer values, each of which are codes for
315 // the status of each face of an undivided prism or hexahedron (including
316 // degenerate versions): 0 = degenerate line, 1 = single triangle, 2 =
317 // recombined quad, 3 = fixed diagonal, 4 = adjustable diagonal, 5 = totally
318 // free to diagonalize or not. Ordering of faces starts with the lateral face
319 // containing verts[0] and verts[1], then goes in order of increasing vertex
320 // index around element. Finally, the bottom, then the top. Added 2010-01-21
getFaceTypes(GRegion * gr,MElement * elem,int j,int k,std::vector<MVertex * > & verts,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::vector<bool> & vert_bnd,std::vector<int> & nfix1,std::vector<int> & nfix2,std::vector<int> & nadj1,std::vector<int> & nadj2,std::vector<int> & free_flag)321 static std::map<std::string, std::vector<int> > getFaceTypes(
322 GRegion *gr, MElement *elem, int j, int k, std::vector<MVertex *> &verts,
323 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
324 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
325 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
326 std::vector<bool> &vert_bnd, std::vector<int> &nfix1, std::vector<int> &nfix2,
327 std::vector<int> &nadj1, std::vector<int> &nadj2, std::vector<int> &free_flag)
328 {
329 std::map<std::string, std::vector<int> > face_types;
330 ExtrudeParams *ep = gr->meshAttributes.extrude;
331
332 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
333 Msg::Error("In getFaceTypes(), invalid extrusion "
334 "in region %d for performing QuadToTri mesh generation.",
335 gr->tag());
336 return face_types;
337 }
338
339 // get the starting indices of the top quadToTri layer
340 int j_top_start = 0, k_top_start = 0;
341 j_top_start = ep->mesh.NbLayer - 1;
342 k_top_start = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1;
343
344 // check nature of each face
345 int n_lat_tmp = 0;
346 if(verts.size() == 6)
347 n_lat_tmp = 3;
348 else if(verts.size() == 8)
349 n_lat_tmp = 4;
350 else {
351 Msg::Error(
352 "In getFaceTypes(), size of verts vector was not 6 or 8 (region %d).",
353 gr->tag());
354 return face_types;
355 }
356
357 const int n_lat = n_lat_tmp;
358
359 // set defaults for the nfix, nadj vectors that store the nodes for each fixed
360 // or adjustable diagonal And set the defaults for free_flag which tells
361 // whether a face is free (0)
362 nfix1.clear();
363 nfix1.assign(n_lat + 2, 0);
364 nfix2.clear();
365 nfix2.assign(n_lat + 2, 0);
366 nadj1.clear();
367 nadj1.assign(n_lat + 2, 0);
368 nadj2.clear();
369 nadj2.assign(n_lat + 2, 0);
370 free_flag.clear();
371 free_flag.assign(n_lat + 2, 0);
372 for(int p = 0; p < n_lat + 2; p++) {
373 nfix1[p] = -p * p - p - 1;
374 nfix2[p] = -p * p - p - 2;
375 nadj1[p] = -p * p - p - 1;
376 nadj2[p] = -p * p - p - 2;
377 free_flag[p] = 0;
378 }
379
380 // create an array that holds info about whether a face touches a boundary
381 std::vector<int> touch_bnd(n_lat);
382
383 fill_touch_bnd(&touch_bnd[0], vert_bnd, n_lat);
384
385 // Classify the laterals of each little mesh volume:
386
387 for(int p = 0; p < n_lat; p++) {
388 int p2 = (p + 1) % n_lat;
389 std::vector<MVertex *> v_face;
390 v_face.assign(4, (MVertex *)(0));
391 v_face[0] = verts[p];
392 v_face[1] = verts[p2];
393 v_face[2] = verts[p2 + n_lat];
394 v_face[3] = verts[p + n_lat];
395
396 // is the face a degenerate line:
397 if(verts[p] == verts[n_lat + p] && verts[p2] == verts[n_lat + p2]) {
398 face_types["degen"].push_back(p);
399 }
400 // is face a single triangle?
401 else if((verts[p] == verts[n_lat + p] && verts[p2] != verts[n_lat + p2]) ||
402 (verts[p] != verts[n_lat + p] && verts[p2] == verts[n_lat + p2])) {
403 face_types["single_tri"].push_back(p);
404 }
405 // is face a recombined quad?
406 else if(forbiddenExists(v_face, forbidden_edges))
407 face_types["recomb"].push_back(p);
408
409 // Does face contain a fixed diagonal edge?
410 else if(edgeExists(verts[p], verts[p2 + n_lat], quadToTri_edges)) {
411 nfix1[p] = p;
412 nfix2[p] = p2 + n_lat;
413 face_types["fixed_diag"].push_back(p);
414 }
415 else if(edgeExists(verts[n_lat + p], verts[p2], quadToTri_edges)) {
416 nfix1[p] = p + n_lat;
417 nfix2[p] = p2;
418 face_types["fixed_diag"].push_back(p);
419 }
420
421 // Does the face have an adjustable but required diagonal?
422 // ( this else if requires that the previous one about fixed diagonals
423 // comes FIRST )
424 else if(edgeExists(verts[p], verts[p2 + n_lat], lat_tri_diags)) {
425 nadj1[p] = p;
426 nadj2[p] = p2 + n_lat;
427 face_types["adj_diag"].push_back(p);
428 }
429 else if(edgeExists(verts[n_lat + p], verts[p2], lat_tri_diags)) {
430 nadj1[p] = p + n_lat;
431 nadj2[p] = p2;
432 face_types["adj_diag"].push_back(p);
433 }
434
435 // If no previous option was true, then this face is an internal face.
436
437 // If this face has NO vertices on a lateral surface, and is a triangle or
438 // not in the top quadToTri layer:
439 // then the face is a recombined quad (QuadToTri only used in recombined
440 // extrusions).
441 else if(!touch_bnd[p] &&
442 (j < j_top_start || (j == j_top_start && k < k_top_start)))
443 face_types["recomb"].push_back(p);
444
445 // Face is possibly free...will need to do tests after this loop is complete
446 // to finalize that. But, for now....
447 else {
448 face_types["free"].push_back(p);
449 free_flag[p] = 1;
450 }
451 }
452
453 // set the bottom:
454 if(n_lat == 3)
455 face_types["single_tri"].push_back(3);
456
457 else {
458 std::vector<MVertex *> v_bot;
459 v_bot.assign(4, (MVertex *)(0));
460 v_bot[0] = verts[0];
461 v_bot[1] = verts[1];
462 v_bot[2] = verts[2];
463 v_bot[3] = verts[3];
464 // is forbidden?
465 if((j == 0 && k == 0) || forbiddenExists(v_bot, forbidden_edges))
466 face_types["recomb"].push_back(4);
467 else if(edgeExists(verts[0], verts[2], quadToTri_edges)) {
468 nfix1[4] = 0;
469 nfix2[4] = 2;
470 face_types["fixed_diag"].push_back(4);
471 }
472 else if(edgeExists(verts[1], verts[3], quadToTri_edges)) {
473 nfix1[4] = 1;
474 nfix2[4] = 3;
475 face_types["fixed_diag"].push_back(4);
476 }
477 // Does the face have an adjustable but required diagonal?
478 // ( this else if requires that the previous one about fixed diagonals
479 // comes FIRST )
480 // NOTE: THIS SITUATION SHOULD NEVER HAPPEN ON THIS FACE
481 else if(edgeExists(verts[0], verts[2], lat_tri_diags)) {
482 nadj1[4] = 0;
483 nadj2[4] = 2;
484 face_types["adj_diag"].push_back(4);
485 }
486 else if(edgeExists(verts[1], verts[3], lat_tri_diags)) {
487 nadj1[4] = 1;
488 nadj2[4] = 3;
489 face_types["adj_diag"].push_back(4);
490 }
491 else {
492 face_types["free"].push_back(4);
493 free_flag[4] = 1;
494 }
495 }
496
497 // set the top:
498 if(n_lat == 3) face_types["single_tri"].push_back(4);
499 // if a hexahedron:
500 else {
501 std::vector<MVertex *> v_top;
502 v_top.assign(4, (MVertex *)(0));
503 v_top[0] = verts[4];
504 v_top[1] = verts[5];
505 v_top[2] = verts[6];
506 v_top[3] = verts[7];
507 // is forbidden ?
508 if(forbiddenExists(v_top, forbidden_edges))
509 face_types["recomb"].push_back(5);
510 // search for a fixed edge:
511 else if(edgeExists(verts[4], verts[6], quadToTri_edges)) {
512 nfix1[5] = 4;
513 nfix2[5] = 6;
514 face_types["fixed_diag"].push_back(5);
515 }
516 else if(edgeExists(verts[5], verts[7], quadToTri_edges)) {
517 nfix1[5] = 5;
518 nfix2[5] = 7;
519 face_types["fixed_diag"].push_back(5);
520 }
521 // Does the face have an adjustable but required diagonal?
522 // ( this else if requires that the previous one about fixed diagonals
523 // comes FIRST )
524 // NOTE: THIS SITUATION SHOULD NEVER HAPPEN ON THIS FACE
525 else if(edgeExists(verts[4], verts[6], lat_tri_diags)) {
526 nadj1[5] = 4;
527 nadj2[5] = 6;
528 face_types["adj_diag"].push_back(5);
529 }
530 else if(edgeExists(verts[5], verts[7], lat_tri_diags)) {
531 nadj1[5] = 5;
532 nadj2[5] = 7;
533 face_types["adj_diag"].push_back(5);
534 }
535 else {
536 face_types["free"].push_back(5);
537 free_flag[5] = 1;
538 }
539 }
540
541 // now return face_types
542 return face_types;
543 }
544
545 // Generate face diagonals used to subdivide prisms by BRUTE FORCE...NOT
546 // RECOMMENDED for general use, but it is required for some elements which have
547 // all vertices on an external region boundary. Added 2010-01-29
bruteForceEdgeQuadToTriPrism(GRegion * gr,MElement * elem,int j,int k,std::vector<MVertex * > verts,std::map<std::string,std::vector<int>> & face_types,std::set<std::pair<MVertex *,MVertex * >> & edges_new,std::set<std::pair<MVertex *,MVertex * >> & forbidden_new,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::vector<int> nfix1,std::vector<int> nfix2,std::vector<int> nadj1,std::vector<int> nadj2,std::vector<int> free_flag)548 static void bruteForceEdgeQuadToTriPrism(
549 GRegion *gr, MElement *elem, int j, int k, std::vector<MVertex *> verts,
550 std::map<std::string, std::vector<int> > &face_types,
551 std::set<std::pair<MVertex *, MVertex *> > &edges_new,
552 std::set<std::pair<MVertex *, MVertex *> > &forbidden_new,
553 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
554 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
555 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
556 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
557 &problems_new,
558 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
559 &problems,
560 std::vector<int> nfix1, std::vector<int> nfix2, std::vector<int> nadj1,
561 std::vector<int> nadj2, std::vector<int> free_flag)
562 {
563 ExtrudeParams *ep = gr->meshAttributes.extrude;
564
565 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
566 Msg::Error("In bruteForceEdgeQuadToTriPrism(), invalid extrusion "
567 "in region %d for performing QuadToTri mesh generation.",
568 gr->tag());
569 return;
570 }
571
572 GModel *model = gr->model();
573 if(!model) {
574 Msg::Error("In bruteForceEdgeQuadToTriPrism(), invalid model for region "
575 "%d.",
576 gr->tag());
577 return;
578 }
579
580 // now find and verify the source of region
581
582 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
583 if(!reg_source) {
584 Msg::Error(
585 "In bruteForceEdgeQuadToTriPrism(), invalid source face for region "
586 "%d.",
587 gr->tag());
588 return;
589 }
590
591 // verify number of vertices
592 // int n_lat;
593 if(verts.size() != 6) {
594 Msg::Error(
595 "In bruteForceEdgeQuadToTriPrism(), number of vertices not equal "
596 "6.");
597 return;
598 }
599 // else
600 // n_lat = 3;
601
602 // NOTE THAT FACES ARE NUMBERED CONSECUTIVELY FROM 0, starting from vertex 0
603 // and moving around laterally. Hence, the face after vertex 0 is face 0, and
604 // so forth. The bottom and top faces are numbered (number of laterals + 1)
605 // and (number of laterals + 2), respectively.
606
607 // numbers of each type of face
608 int num_degen = face_types["degen"].size();
609 int num_single_tri = face_types["single_tri"].size();
610 int num_recomb = face_types["recomb"].size();
611 int num_fixed_diag = face_types["fixed_diag"].size();
612 int num_adj_diag = face_types["adj_diag"].size();
613 int num_free = face_types["free"].size();
614
615 // Make sure all faces marked forbidden in face_types have all diagonals in
616 // forbidden_edges;
617 for(int p = 0; p < num_recomb; p++) {
618 int ind = face_types["recomb"][p];
619 std::vector<MVertex *> v;
620 v.push_back(verts[ind]);
621 v.push_back(verts[(ind + 1) % 3]);
622 v.push_back(verts[(ind + 1) % 3 + 3]);
623 v.push_back(verts[ind + 3]);
624 createForbidden(v, forbidden_new);
625 createForbidden(v, forbidden_edges);
626 }
627
628 // following must be true if this is a triangle extrusion
629 if(find(face_types["single_tri"].begin(), face_types["single_tri"].end(),
630 3) == face_types["single_tri"].end() ||
631 find(face_types["single_tri"].begin(), face_types["single_tri"].end(),
632 4) == face_types["single_tri"].end()) {
633 Msg::Error(
634 "In bruteForceEdgeQuadToTriPrism(), invalid face code for top and/or "
635 "bottom (region %d).",
636 gr->tag());
637 return;
638 }
639
640 // Take care of the trivial case: tetrahedron:
641 if(num_single_tri == 4 && num_degen == 1) return;
642
643 // Pyramid also very easy:
644
645 else if(num_single_tri == 4) {
646 int ind = 0;
647 if(num_adj_diag) {
648 ind = face_types["adj_diag"][0];
649 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
650 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
651 }
652 return;
653 }
654
655 // A PRISM IS MORE DIFFICULT:
656
657 // First case, if exactly two laterals are forbidden to diagonalize:
658 if(num_recomb == 2) {
659 if(num_free) {
660 for(int p = 0; p < num_free; p++) {
661 int ind = face_types["free"][p];
662 std::vector<MVertex *> v;
663 v.push_back(verts[ind]);
664 v.push_back(verts[(ind + 1) % 3]);
665 v.push_back(verts[(ind + 1) % 3 + 3]);
666 v.push_back(verts[ind + 3]);
667 createForbidden(v, forbidden_new);
668 createForbidden(v, forbidden_edges);
669 }
670 }
671 else {
672 if(num_adj_diag) {
673 int ind = face_types["adj_diag"][0];
674 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
675 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
676 }
677 std::pair<unsigned int, unsigned int> jkpair(j, k);
678 problems[elem].insert(jkpair);
679 problems_new[elem].insert(jkpair);
680 }
681 return;
682 }
683
684 // If 1 lateral is forbidden to diagonalize and two free, diagonalize to
685 // lowest point vector on free edge:
686 if(num_recomb == 1 && num_free == 2) {
687 int p_recomb = face_types["recomb"][0];
688 int p_free = (p_recomb + 2) % 3;
689 int ind_low = verts[p_free] < verts[p_free + 3] ? p_free : p_free + 3;
690 int add = ind_low < 3 ? 0 : 3;
691 createEdge(verts[ind_low], verts[(ind_low - add + 1) % 3 + 3 - add],
692 quadToTri_edges);
693 createEdge(verts[ind_low], verts[(ind_low - add + 1) % 3 + 3 - add],
694 edges_new);
695 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 3 + 3 - add],
696 quadToTri_edges);
697 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 3 + 3 - add],
698 edges_new);
699
700 return;
701 }
702
703 // If all faces are free, then diagonalize TWO faces based on smallest vertex
704 // pointer
705 if(num_free >= 3) {
706 int ind_low;
707 ind_low = 0;
708 for(int p = 1; p < 6; p++) {
709 if(verts[ind_low] > verts[p]) ind_low = p;
710 }
711
712 int add = ind_low < 3 ? 0 : 3;
713
714 createEdge(verts[ind_low], verts[(ind_low - add + 1) % 3 + 3 - add],
715 quadToTri_edges);
716 createEdge(verts[ind_low], verts[(ind_low - add + 1) % 3 + 3 - add],
717 edges_new);
718 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 3 + 3 - add],
719 quadToTri_edges);
720 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 3 + 3 - add],
721 edges_new);
722
723 return;
724 }
725
726 // If reach this point, then go through the loop of progressively permitting
727 // more face types to be considered for diagonalization. This loop could be
728 // slow, that's why previous special cases were tried first. t=0, fixed and
729 // preferred diagonals only. t=1, allow preferred diagonals to be meshed with
730 // lowest vertex pointer in diagonal t=2, allow free faces to be meshed with
731 // lowest vertex pointer in diagonal t=3, allow any diagonal that works
732 bool valid_division = false;
733 int face_done[2] = {
734 0, 0}; // holds the face numbers for the two faces that are done
735 for(int t = 0; t < 4; t++) {
736 // variables that hold the face diagonal nodes.
737 int n1[3], n2[3];
738
739 // if the face is completely free (t==3), face_is_free = true;
740 bool face_is_free[3];
741
742 // set default values of the n variables
743 // to unique negative numbers; bool to false.
744 for(int p = 0; p < 3; p++) {
745 n1[p] = -p * p - p - 1;
746 n2[p] = -p * p - p - 2;
747 face_is_free[p] = false;
748 }
749
750 for(int p = 0; p < 3; p++) {
751 // fixed diagonals
752 if(nfix1[p] >= 0) {
753 n1[p] = nfix1[p];
754 n2[p] = nfix2[p];
755 }
756 // preferred adjustable diagonals
757 else if(!t && nadj1[p] >= 0) {
758 n1[p] = nadj1[p];
759 n2[p] = nadj2[p];
760 }
761 // choose lowest vertex for t < 3, any vertex for t == 3
762 else if((t >= 1 && t < 3 && nadj1[p] >= 0) || (t == 2 && free_flag[p])) {
763 if((verts[p] < verts[(p + 1) % 3] && verts[p] < verts[p + 3]) ||
764 (verts[(p + 1) % 3 + 3] < verts[(p + 1) % 3] &&
765 verts[(p + 1) % 3 + 3] < verts[p + 3])) {
766 n1[p] = p;
767 n2[p] = (p + 1) % 3 + 3;
768 }
769 else {
770 n1[p] = p + 3;
771 n2[p] = (p + 1) % 3;
772 }
773 }
774 else if((t == 3 && (nadj1[p] >= 0)) || free_flag[p])
775 face_is_free[p] = true;
776 }
777
778 // search for two faces that have diagonals which meet at a vertex
779 for(int p = 0; p < 3; p++) {
780 // if both faces are free, fix one by using lowest vertex pointer
781 if(face_is_free[p] && face_is_free[(p + 1) % 3]) {
782 face_is_free[p] = false;
783 if(verts[(p + 1) % 3] < verts[(p + 1) % 3 + 3]) {
784 n1[p] = p + 3;
785 n2[p] = (p + 1) % 3;
786 }
787 else {
788 n1[p] = p;
789 n2[p] = (p + 1) % 3 + 3;
790 }
791 }
792 // if first face is free and the other has a definite diagonal
793 if(face_is_free[p] && n1[(p + 1) % 3] >= 0) {
794 valid_division = true;
795 face_done[0] = p;
796 face_done[1] = (p + 1) % 3;
797 if(n1[(p + 1) % 3] == (p + 1) % 3 || n2[(p + 1) % 3] == (p + 1) % 3) {
798 n1[p] = p + 3;
799 n2[p] = (p + 1) % 3;
800 }
801 else {
802 n1[p] = p;
803 n2[p] = (p + 1) % 3 + 3;
804 }
805 }
806 // if second face is free and the other has a definite diagonal
807 else if(face_is_free[(p + 1) % 3] && n1[p] >= 0) {
808 valid_division = true;
809 face_done[0] = p;
810 face_done[1] = (p + 1) % 3;
811 if(n2[p] == (p + 1) % 3 || n1[p] == (p + 1) % 3) {
812 n1[(p + 1) % 3] = (p + 1) % 3;
813 n2[(p + 1) % 3] = (p + 2) % 3 + 3;
814 }
815 else {
816 n1[(p + 1) % 3] = (p + 1) % 3 + 3;
817 n2[(p + 1) % 3] = (p + 2) % 3;
818 }
819 }
820 // if both faces had definite diagonals
821 else if(n2[p] == n1[(p + 1) % 3] || n2[p] == n2[(p + 1) % 3] ||
822 n1[p] == n1[(p + 1) % 3] || n1[p] == n2[(p + 1) % 3]) {
823 valid_division = true;
824 face_done[0] = p;
825 face_done[1] = (p + 1) % 3;
826 }
827
828 if(valid_division) {
829 createEdge(verts[n1[p]], verts[n2[p]], quadToTri_edges);
830 createEdge(verts[n1[p]], verts[n2[p]], edges_new);
831 createEdge(verts[n1[(p + 1) % 3]], verts[n2[(p + 1) % 3]],
832 quadToTri_edges);
833 createEdge(verts[n1[(p + 1) % 3]], verts[n2[(p + 1) % 3]], edges_new);
834 break;
835 }
836 }
837
838 // At this point, can break out if valid_division... OR if there is NO valid
839 // division AND if num_fixed_diag == 3, or 2 and a recomb break out of loop.
840 // This will need an internal vertex.
841 if(valid_division || (!valid_division && num_fixed_diag + num_recomb == 3))
842 break;
843
844 } // end of outer t-loop
845
846 // create adjustable but required diagonals that weren't yet added
847 for(int s = 0; s < num_adj_diag; s++) {
848 int ind = face_types["adj_diag"][s];
849 if(ind != face_done[0] && ind != face_done[1]) {
850 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
851 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
852 }
853 }
854
855 if(!valid_division) {
856 std::pair<unsigned int, unsigned int> jk_pair(j, k);
857 problems[elem].insert(jk_pair);
858 problems_new[elem].insert(jk_pair);
859 }
860
861 return;
862 }
863
864 // Divide hexahedron degenerated at two points (degenerate face is a line) by
865 // brute force
addEdgesForQuadToTriTwoPtDegenHexa(GRegion * gr,MElement * elem,ExtrudeParams * ep,int j,int k,std::vector<MVertex * > verts,std::map<std::string,std::vector<int>> & face_types,std::set<std::pair<MVertex *,MVertex * >> & edges_new,std::set<std::pair<MVertex *,MVertex * >> & forbidden_new,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::vector<int> nfix1,std::vector<int> nfix2,std::vector<int> nadj1,std::vector<int> nadj2,std::vector<int> free_flag)866 static void addEdgesForQuadToTriTwoPtDegenHexa(
867 GRegion *gr, MElement *elem, ExtrudeParams *ep, int j, int k,
868 std::vector<MVertex *> verts,
869 std::map<std::string, std::vector<int> > &face_types,
870 std::set<std::pair<MVertex *, MVertex *> > &edges_new,
871 std::set<std::pair<MVertex *, MVertex *> > &forbidden_new,
872 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
873 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
874 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
875 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
876 &problems_new,
877 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
878 &problems,
879 std::vector<int> nfix1, std::vector<int> nfix2, std::vector<int> nadj1,
880 std::vector<int> nadj2, std::vector<int> free_flag)
881 {
882 if(!ep) return;
883
884 // numbers of each type of face
885 // int num_degen = face_types["degen"].size();
886 // int num_single_tri = face_types["single_tri"].size();
887 // int num_recomb = face_types["recomb"].size();
888 // int num_fixed_diag = face_types["fixed_diag"].size();
889 // int num_adj_diag = face_types["adj_diag"].size();
890 int num_free = face_types["free"].size();
891
892 int degen_face = face_types["degen"][0];
893
894 // If all faces are free, then diagonalize TWO faces based on smallest vertex
895 // pointer
896 if(num_free >= 3) {
897 int ind_low;
898 ind_low = 0;
899 for(int p = 1; p < 8; p++) {
900 if(verts[ind_low] > verts[p]) ind_low = p;
901 }
902
903 int add = ind_low < 4 ? 0 : 4;
904
905 if(verts[ind_low - add] == verts[ind_low + 4 - add]) {
906 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4],
907 quadToTri_edges);
908 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4], edges_new);
909 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4 + 4],
910 quadToTri_edges);
911 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4 + 4], edges_new);
912 }
913 else {
914 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4 + 4 - add],
915 quadToTri_edges);
916 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4 + 4 - add],
917 edges_new);
918 int ind2;
919 if(verts[(ind_low - add + 1) % 4] == verts[(ind_low - add + 1) % 4 + 4])
920 ind2 = (ind_low - add + 3) % 4 + 4 - add;
921 else
922 ind2 = (ind_low - add + 1) % 4 + 4 - add;
923 createEdge(verts[ind_low], verts[ind2], quadToTri_edges);
924 createEdge(verts[ind_low], verts[ind2], edges_new);
925 }
926
927 return;
928 }
929
930 // If faces of prism not all free, look for a way to diagonalize and
931 // subdivide. t = 0, fixed and preferred adjustable diagonals t >= 1 < 3,
932 // lowest pointer on adjustables t = 2, include free faces, lowest vertex
933 // pointer diagonal on adjustable and free t = 3, not lowest vertex pointer
934 // diagonal on adjustable and free
935
936 int face_done[2];
937 face_done[0] = -1;
938 face_done[1] = -2;
939
940 // valid_division tells whether a valid division was found
941 bool valid_division = false;
942
943 for(int t = 0; t < 4; t++) {
944 // these variables hold the faces and their diagonal nodes in (n1,n2)
945 int p_top = 5, n1_top = -2, n2_top = -3;
946 bool p_top_is_free = false;
947 int p_bot = 4, n1_bot = -4, n2_bot = -5;
948 bool p_bot_is_free = false;
949 int p_lat = (degen_face + 2) % 4, n1_lat = -10, n2_lat = -11;
950 bool p_lat_is_free = false;
951 for(int s = 0; s < 3; s++) {
952 int p_tmp = 0, *n1_tmp = 0, *n2_tmp = 0;
953 bool *p_is_free_tmp = 0;
954 if(!s) {
955 p_tmp = p_bot;
956 n1_tmp = &n1_bot;
957 n2_tmp = &n2_bot;
958 p_is_free_tmp = &p_bot_is_free;
959 }
960 if(s == 1) {
961 p_tmp = p_top;
962 n1_tmp = &n1_top;
963 n2_tmp = &n2_top;
964 p_is_free_tmp = &p_top_is_free;
965 }
966 if(s == 2) {
967 p_tmp = p_lat;
968 n1_tmp = &n1_lat;
969 n2_tmp = &n2_lat;
970 p_is_free_tmp = &p_lat_is_free;
971 }
972
973 // fixed diagonals
974 if(nfix1[p_tmp] >= 0) {
975 *n1_tmp = nfix1[p_tmp];
976 *n2_tmp = nfix2[p_tmp];
977 }
978 // preferred adjustable diagonals
979 else if(!t && nadj1[p_tmp] >= 0) {
980 *n1_tmp = nadj1[p_tmp];
981 *n2_tmp = nadj2[p_tmp];
982 }
983 // choose lowest vertex for t < 3, non-lowest vertex for t == 3
984 else if((t >= 1 && t < 3 && nadj1[p_tmp] >= 0) ||
985 (t == 2 && free_flag[p_tmp])) {
986 if(p_tmp < 4) {
987 if((verts[p_tmp] < verts[(p_tmp + 1) % 4] &&
988 verts[p_tmp] < verts[p_tmp + 4]) ||
989 (verts[(p_tmp + 1) % 4 + 4] < verts[(p_tmp + 1) % 4] &&
990 verts[(p_tmp + 1) % 4 + 4] < verts[p_tmp + 4])) {
991 *n1_tmp = p_tmp;
992 *n2_tmp = (p_tmp + 1) % 4 + 4;
993 }
994 else {
995 *n1_tmp = p_tmp + 4;
996 *n2_tmp = (p_tmp + 1) % 4;
997 }
998 }
999 else {
1000 int add = p_tmp == 4 ? 0 : 4;
1001 if((verts[0 + add] < verts[1 + add] &&
1002 verts[0 + add] < verts[3 + add]) ||
1003 (verts[2 + add] < verts[1 + add] &&
1004 verts[2 + add] < verts[3 + add])) {
1005 *n1_tmp = 0 + add;
1006 *n2_tmp = 2 + add;
1007 }
1008 else {
1009 *n1_tmp = 1 + add;
1010 *n2_tmp = 3 + add;
1011 }
1012 }
1013 }
1014 else if(t == 3 && (nadj1[p_tmp] >= 0 || free_flag[p_tmp]))
1015 *p_is_free_tmp = true;
1016 }
1017
1018 // Find any diagonals that meet at a vertex:
1019
1020 // start with top surface and bottom surface
1021 if((n1_top >= 0 || p_top_is_free) && (n1_bot >= 0 || p_bot_is_free)) {
1022 if(p_top_is_free && p_bot_is_free) {
1023 if((verts[4] < verts[5] && verts[4] < verts[7]) ||
1024 (verts[6] < verts[5] && verts[6] < verts[7])) {
1025 n1_top = 4;
1026 n2_top = 6;
1027 }
1028 else {
1029 n1_top = 5;
1030 n2_top = 7;
1031 }
1032 n1_bot = n1_top - 4;
1033 n2_bot = n2_top - 4;
1034 }
1035 else if(n1_top >= 0 && p_bot_is_free) {
1036 n1_bot = n1_top - 4;
1037 n2_bot = n2_top - 4;
1038 }
1039 else if(n1_bot >= 0 && p_top_is_free) {
1040 n1_top = n1_bot + 4;
1041 n2_top = n2_bot + 4;
1042 }
1043 if(verts[n1_top] == verts[n1_bot] || verts[n1_top] == verts[n2_bot] ||
1044 verts[n2_top] == verts[n1_bot] || verts[n2_top] == verts[n2_bot]) {
1045 valid_division = true;
1046 face_done[0] = p_top;
1047 face_done[1] = p_bot;
1048 createEdge(verts[n1_top], verts[n2_top], quadToTri_edges);
1049 createEdge(verts[n1_top], verts[n2_top], edges_new);
1050 createEdge(verts[n1_bot], verts[n2_bot], quadToTri_edges);
1051 createEdge(verts[n1_bot], verts[n2_bot], edges_new);
1052 }
1053 }
1054
1055 // Top surface and lateral surface
1056 if(!valid_division && (n1_top >= 0 || p_top_is_free) &&
1057 (n1_lat >= 0 || p_lat_is_free)) {
1058 if(p_top_is_free && p_lat_is_free) {
1059 if((verts[4] < verts[5] && verts[4] < verts[7]) ||
1060 (verts[6] < verts[5] && verts[6] < verts[7])) {
1061 n1_top = 4;
1062 n2_top = 6;
1063 }
1064 else {
1065 n1_top = 5;
1066 n2_top = 7;
1067 }
1068 if(n1_top == (degen_face + 2) % 4 + 4 ||
1069 n2_top == (degen_face + 2) % 4 + 4) {
1070 n1_lat = (degen_face + 2) % 4 + 4;
1071 n2_lat = (n1_lat - 4 + 1) % 4;
1072 }
1073 else {
1074 n1_lat = (degen_face + 3) % 4 + 4;
1075 n2_lat = (n1_lat - 4 + 3) % 4;
1076 }
1077 }
1078 else if(n1_top >= 0 && p_lat_is_free) {
1079 if(n1_top == (degen_face + 2) % 4 + 4 ||
1080 n2_top == (degen_face + 2) % 4 + 4) {
1081 n1_lat = (degen_face + 2) % 4 + 4;
1082 n2_lat = (n1_lat - 4 + 1) % 4;
1083 }
1084 else {
1085 n1_lat = (degen_face + 3) % 4 + 4;
1086 n2_lat = (n1_lat - 4 + 3) % 4;
1087 }
1088 }
1089 else if(n1_lat >= 0 && p_top_is_free) {
1090 if(n1_lat == (degen_face + 2) % 4 + 4 ||
1091 n2_lat == (degen_face + 2) % 4 + 4) {
1092 n1_top = (degen_face + 2) % 4 + 4;
1093 n2_top = (n1_top - 4 + 2) % 4 + 4;
1094 }
1095 else {
1096 n1_top = (degen_face + 3) % 4 + 4;
1097 n2_top = (n1_top - 4 + 2) % 4 + 4;
1098 }
1099 }
1100 if(verts[n1_top] == verts[n1_lat] || verts[n1_top] == verts[n2_lat] ||
1101 verts[n2_top] == verts[n1_lat] || verts[n2_top] == verts[n2_lat]) {
1102 valid_division = true;
1103 face_done[0] = p_top;
1104 face_done[1] = p_lat;
1105 createEdge(verts[n1_top], verts[n2_top], quadToTri_edges);
1106 createEdge(verts[n1_top], verts[n2_top], edges_new);
1107 createEdge(verts[n1_lat], verts[n2_lat], quadToTri_edges);
1108 createEdge(verts[n1_lat], verts[n2_lat], edges_new);
1109 }
1110 }
1111
1112 // Bottom surface and lateral surface
1113 if(!valid_division && (n1_bot >= 0 || p_bot_is_free) &&
1114 (n1_lat >= 0 || p_lat_is_free)) {
1115 if(p_bot_is_free && p_lat_is_free) {
1116 if((verts[0] < verts[1] && verts[0] < verts[3]) ||
1117 (verts[2] < verts[1] && verts[2] < verts[3])) {
1118 n1_bot = 0;
1119 n2_bot = 2;
1120 }
1121 else {
1122 n1_bot = 1;
1123 n2_bot = 3;
1124 }
1125 if(n1_bot == (degen_face + 2) % 4 || n2_bot == (degen_face + 2) % 4) {
1126 n1_lat = (degen_face + 2) % 4;
1127 n2_lat = (n1_lat + 1) % 4 + 4;
1128 }
1129 else {
1130 n1_lat = (degen_face + 3) % 4;
1131 n2_lat = (n1_lat + 3) % 4 + 4;
1132 }
1133 }
1134 else if(n1_bot >= 0 && p_lat_is_free) {
1135 if(n1_bot == (degen_face + 2) % 4 || n2_bot == (degen_face + 2) % 4) {
1136 n1_lat = (degen_face + 2) % 4;
1137 n2_lat = (n1_lat + 1) % 4 + 4;
1138 }
1139 else {
1140 n1_lat = (degen_face + 3) % 4;
1141 n2_lat = (n1_lat + 3) % 4 + 4;
1142 }
1143 }
1144 else if(n1_lat >= 0 && p_bot_is_free) {
1145 if(n1_lat == (degen_face + 2) % 4 || n2_lat == (degen_face + 2) % 4) {
1146 n1_bot = (degen_face + 2) % 4;
1147 n2_bot = (n1_bot + 2) % 4;
1148 }
1149 else {
1150 n1_bot = (degen_face + 3) % 4;
1151 n2_bot = (n1_bot + 2) % 4;
1152 }
1153 }
1154 if(verts[n1_bot] == verts[n1_lat] || verts[n1_bot] == verts[n2_lat] ||
1155 verts[n2_bot] == verts[n1_lat] || verts[n2_bot] == verts[n2_lat]) {
1156 valid_division = true;
1157 face_done[0] = p_bot;
1158 face_done[1] = p_lat;
1159 createEdge(verts[n1_bot], verts[n2_bot], quadToTri_edges);
1160 createEdge(verts[n1_bot], verts[n2_bot], edges_new);
1161 createEdge(verts[n1_lat], verts[n2_lat], quadToTri_edges);
1162 createEdge(verts[n1_lat], verts[n2_lat], edges_new);
1163 }
1164 }
1165
1166 if(valid_division) break;
1167
1168 } // end of t-loop (outer loop)
1169
1170 // take care of any adjustable but required diagonals not yet added
1171 for(unsigned int s = 0; s < face_types["adj_diag"].size(); s++) {
1172 int ind = face_types["adj_diag"][s];
1173 if(ind != face_done[0] && ind != face_done[1]) {
1174 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
1175 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
1176 }
1177 }
1178
1179 // if no division top found, need internal vertex.
1180 if(!valid_division) {
1181 std::pair<unsigned int, unsigned int> jkpair(j, k);
1182 problems[elem].insert(jkpair);
1183 problems_new[elem].insert(jkpair);
1184 }
1185
1186 return;
1187 }
1188
1189 // Divide a hexahedron degenerate at one point (one degenerate corner) by brute
1190 // force.
addEdgesForQuadToTriOnePtDegenHexa(GRegion * gr,MElement * elem,ExtrudeParams * ep,int j,int k,std::vector<MVertex * > verts,std::map<std::string,std::vector<int>> & face_types,std::set<std::pair<MVertex *,MVertex * >> & edges_new,std::set<std::pair<MVertex *,MVertex * >> & forbidden_new,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::vector<int> nfix1,std::vector<int> nfix2,std::vector<int> nadj1,std::vector<int> nadj2,std::vector<int> free_flag)1191 static void addEdgesForQuadToTriOnePtDegenHexa(
1192 GRegion *gr, MElement *elem, ExtrudeParams *ep, int j, int k,
1193 std::vector<MVertex *> verts,
1194 std::map<std::string, std::vector<int> > &face_types,
1195 std::set<std::pair<MVertex *, MVertex *> > &edges_new,
1196 std::set<std::pair<MVertex *, MVertex *> > &forbidden_new,
1197 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
1198 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
1199 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
1200 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
1201 &problems_new,
1202 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
1203 &problems,
1204 std::vector<int> nfix1, std::vector<int> nfix2, std::vector<int> nadj1,
1205 std::vector<int> nadj2, std::vector<int> free_flag)
1206 {
1207 if(!ep) return;
1208
1209 // numbers of each type of face
1210 // int num_degen = face_types["degen"].size();
1211 // int num_single_tri = face_types["single_tri"].size();
1212 // int num_recomb = face_types["recomb"].size();
1213 // int num_fixed_diag = face_types["fixed_diag"].size();
1214 // int num_adj_diag = face_types["adj_diag"].size();
1215 int num_free = face_types["free"].size();
1216
1217 // index of the degenerate corner
1218 int degen_ind = verts[face_types["single_tri"][0]] ==
1219 verts[face_types["single_tri"][0] + 4] ?
1220 face_types["single_tri"][0] :
1221 face_types["single_tri"][1];
1222
1223 // If all faces are free, slice from degen corner top and bottom. done.
1224 if(num_free >= 4) {
1225 createEdge(verts[degen_ind], verts[(degen_ind + 2) % 4], quadToTri_edges);
1226 createEdge(verts[degen_ind], verts[(degen_ind + 2) % 4], edges_new);
1227 createEdge(verts[degen_ind], verts[(degen_ind + 2) % 4 + 4],
1228 quadToTri_edges);
1229 createEdge(verts[degen_ind], verts[(degen_ind + 2) % 4 + 4], edges_new);
1230 return;
1231 }
1232
1233 // If faces of degenerated hexahedron are not all free,
1234 // look for a way to diagonalize and subdivide.
1235 // t = 0, fixed and preferred adjustable diagonals
1236 // t >= 1 < 3, lowest pointer on adjustables
1237 // t = 2, include free faces, lowest vertex pointer diagonal on adjustable and
1238 // free t = 3, not lowest vertex pointer diagonal on adjustable and free
1239 int face_done[3];
1240 face_done[0] = -1;
1241 face_done[1] = -2;
1242 face_done[2] = -3;
1243 bool valid_division = false;
1244 for(int t = 0; t < 4; t++) {
1245 // first test for top bottom parallel diagonals (g=0), then pyramid with
1246 // top on corner opposite degen corner (g=1), then pyramid with top on a
1247 // corner NOT opposite to the degenerate corner (g=2), then test for
1248 // non-adjoining lateral face diagonals (g=3).
1249 int p_top = 5, n1_top = -2, n2_top = -3;
1250 bool p_top_is_free = false;
1251 int p_bot = 4, n1_bot = -4, n2_bot = -5;
1252 bool p_bot_is_free = false;
1253 int p_lat1 = (degen_ind + 1) % 4, n1_lat1 = -10, n2_lat1 = -11;
1254 bool p_lat1_is_free = false;
1255 int p_lat2 = (degen_ind + 2) % 4, n1_lat2 = -12, n2_lat2 = -13;
1256 bool p_lat2_is_free = false;
1257
1258 // get diagonals:
1259 for(int s = 0; s < 4; s++) {
1260 int *n1_tmp, *n2_tmp, p_tmp;
1261 bool *p_tmp_is_free;
1262 if(!s) {
1263 p_tmp = p_top;
1264 n1_tmp = &n1_top;
1265 n2_tmp = &n2_top;
1266 p_tmp_is_free = &p_top_is_free;
1267 }
1268 else if(s == 1) {
1269 p_tmp = p_bot;
1270 n1_tmp = &n1_bot;
1271 n2_tmp = &n2_bot;
1272 p_tmp_is_free = &p_bot_is_free;
1273 }
1274 else if(s == 2) {
1275 p_tmp = p_lat1;
1276 n1_tmp = &n1_lat1;
1277 n2_tmp = &n2_lat1;
1278 p_tmp_is_free = &p_lat1_is_free;
1279 }
1280 else {
1281 p_tmp = p_lat2;
1282 n1_tmp = &n1_lat2;
1283 n2_tmp = &n2_lat2;
1284 p_tmp_is_free = &p_lat2_is_free;
1285 }
1286 // fixed diagonals
1287 if(nfix1[p_tmp] >= 0) {
1288 *n1_tmp = nfix1[p_tmp];
1289 *n2_tmp = nfix2[p_tmp];
1290 }
1291 // preferred adjustable diagonals
1292 else if(!t && nadj1[p_tmp] >= 0) {
1293 *n1_tmp = nadj1[p_tmp];
1294 *n2_tmp = nadj2[p_tmp];
1295 }
1296 // choose lowest vertex for t < 3, any vertex for t == 3
1297 else if((t >= 1 && t < 3 && nadj1[p_tmp] >= 0) ||
1298 (t == 2 && free_flag[p_tmp])) {
1299 if(p_tmp < 4) {
1300 if((verts[p_tmp] < verts[(p_tmp + 1) % 4] &&
1301 verts[p_tmp] < verts[p_tmp + 4]) ||
1302 (verts[(p_tmp + 1) % 4 + 4] < verts[(p_tmp + 1) % 4] &&
1303 verts[(p_tmp + 1) % 4 + 4] < verts[p_tmp + 4])) {
1304 *n1_tmp = p_tmp;
1305 *n2_tmp = (p_tmp + 1) % 4 + 4;
1306 }
1307 else {
1308 *n1_tmp = p_tmp + 4;
1309 *n2_tmp = (p_tmp + 1) % 4;
1310 }
1311 }
1312 else {
1313 int add = p_tmp == 4 ? 0 : 4;
1314 if((verts[0 + add] < verts[1 + add] &&
1315 verts[0 + add] < verts[3 + add]) ||
1316 (verts[2 + add] < verts[1 + add] &&
1317 verts[2 + add] < verts[3 + add])) {
1318 *n1_tmp = 0 + add;
1319 *n2_tmp = 2 + add;
1320 }
1321 else {
1322 *n1_tmp = 1 + add;
1323 *n2_tmp = 3 + add;
1324 }
1325 }
1326 }
1327 else if(t == 3 && (nadj1[p_tmp] >= 0 || free_flag[p_tmp]))
1328 *p_tmp_is_free = true;
1329 }
1330
1331 // Look for valid diagonalizations
1332
1333 // look for aligned top and bottom diags.
1334 if(!valid_division) {
1335 // assign diagonals to the 'free' faces from above
1336 if(p_top_is_free && p_bot_is_free) {
1337 if((verts[0] < verts[1] && verts[0] < verts[3]) ||
1338 (verts[2] < verts[1] && verts[2] < verts[3])) {
1339 n1_bot = 0;
1340 n2_bot = 2;
1341 }
1342 else {
1343 n1_bot = 1;
1344 n2_bot = 3;
1345 }
1346 n1_top = n1_bot + 4;
1347 n2_top = n2_bot + 4;
1348 }
1349 else if(p_top_is_free && n1_bot >= 0) {
1350 n1_top = n1_bot + 4;
1351 n2_top = n2_bot + 4;
1352 }
1353 else if(p_bot_is_free && n1_top >= 0) {
1354 n1_bot = n1_top - 4;
1355 n2_bot = n2_top - 4;
1356 }
1357 // test for valid division
1358 if(n1_top - 4 == n1_bot || n2_top - 4 == n1_bot || n1_top - 4 == n2_bot ||
1359 n2_top - 4 == n2_bot) {
1360 valid_division = true;
1361 face_done[0] = 4;
1362 face_done[1] = 5;
1363 createEdge(verts[n1_top], verts[n2_top], quadToTri_edges);
1364 createEdge(verts[n1_top], verts[n2_top], edges_new);
1365 createEdge(verts[n1_bot], verts[n2_bot], quadToTri_edges);
1366 createEdge(verts[n1_bot], verts[n2_bot], edges_new);
1367 }
1368 }
1369
1370 // pyramid with top opposite degenerate corner
1371 if(!valid_division) {
1372 if((n1_top == (degen_ind + 2) % 4 + 4 ||
1373 n2_top == (degen_ind + 2) % 4 + 4 || p_top_is_free) &&
1374 (n2_lat1 == (degen_ind + 2) % 4 + 4 || p_lat1_is_free) &&
1375 (n1_lat2 == (degen_ind + 2) % 4 + 4 || p_lat2_is_free)) {
1376 valid_division = true;
1377 if(p_top_is_free) {
1378 n1_top = degen_ind + 4;
1379 n2_top = (degen_ind + 2) % 4 + 4;
1380 }
1381 if(p_lat1_is_free) {
1382 n1_lat1 = (degen_ind + 1) % 4;
1383 n2_lat1 = (degen_ind + 2) % 4 + 4;
1384 }
1385 if(p_lat2_is_free) {
1386 n1_lat2 = (degen_ind + 2) % 4 + 4;
1387 n2_lat2 = (degen_ind + 3) % 4;
1388 }
1389 face_done[0] = 5;
1390 face_done[1] = (degen_ind + 1) % 4;
1391 face_done[2] = (degen_ind + 2) % 4;
1392 createEdge(verts[n1_top], verts[n2_top], quadToTri_edges);
1393 createEdge(verts[n1_top], verts[n2_top], edges_new);
1394 }
1395 else if((n1_bot == (degen_ind + 2) % 4 || n2_bot == (degen_ind + 2) % 4 ||
1396 p_bot_is_free) &&
1397 (n2_lat1 == (degen_ind + 2) % 4 || p_lat1_is_free) &&
1398 (n1_lat2 == (degen_ind + 2) % 4 || p_lat2_is_free)) {
1399 valid_division = true;
1400 if(p_bot_is_free) {
1401 n1_bot = degen_ind;
1402 n2_bot = (degen_ind + 2) % 4;
1403 }
1404 if(p_lat1_is_free) {
1405 n1_lat1 = (degen_ind + 1) % 4 + 4;
1406 n2_lat1 = (degen_ind + 2) % 4;
1407 }
1408 if(p_lat2_is_free) {
1409 n1_lat2 = (degen_ind + 2) % 4;
1410 n2_lat2 = (degen_ind + 3) % 4 + 4;
1411 }
1412 face_done[0] = 4;
1413 face_done[1] = (degen_ind + 1) % 4;
1414 face_done[2] = (degen_ind + 2) % 4;
1415 createEdge(verts[n1_bot], verts[n2_bot], quadToTri_edges);
1416 createEdge(verts[n1_bot], verts[n2_bot], edges_new);
1417 }
1418
1419 if(valid_division) {
1420 createEdge(verts[n1_lat1], verts[n2_lat1], quadToTri_edges);
1421 createEdge(verts[n1_lat1], verts[n2_lat1], edges_new);
1422 createEdge(verts[n1_lat2], verts[n2_lat2], quadToTri_edges);
1423 createEdge(verts[n1_lat2], verts[n2_lat2], edges_new);
1424 }
1425 }
1426
1427 // Pyramid top on corner NOT opposite to degenerate corner
1428 if(!valid_division) {
1429 // pyramid top on top face
1430 if((n1_top >= 0 && n1_top != (degen_ind + 2) % 4 + 4 &&
1431 n2_top != (degen_ind + 2) % 4 + 4) ||
1432 p_top_is_free) {
1433 if(n1_lat1 == (degen_ind + 1) % 4 + 4 || p_lat1_is_free) {
1434 valid_division = true;
1435 face_done[0] = (degen_ind + 1) % 4;
1436 if(p_lat1_is_free) {
1437 n1_lat1 = (degen_ind + 1) % 4 + 4;
1438 n2_lat1 = (degen_ind + 2) % 4;
1439 }
1440 createEdge(verts[n1_lat1], verts[n2_lat1], quadToTri_edges);
1441 createEdge(verts[n1_lat1], verts[n2_lat1], edges_new);
1442 }
1443 else if(n2_lat2 == (degen_ind + 3) % 4 + 4 || p_lat2_is_free) {
1444 valid_division = true;
1445 face_done[0] = (degen_ind + 2) % 4;
1446 if(p_lat2_is_free) {
1447 n1_lat2 = (degen_ind + 2) % 4;
1448 n2_lat2 = (degen_ind + 3) % 4 + 4;
1449 }
1450 createEdge(verts[n1_lat2], verts[n2_lat2], quadToTri_edges);
1451 createEdge(verts[n1_lat2], verts[n2_lat2], edges_new);
1452 }
1453 if(valid_division) {
1454 face_done[1] = 5;
1455 if(p_top_is_free) {
1456 n1_top = (degen_ind + 1) % 4 + 4;
1457 n2_top = (degen_ind + 3) % 4 + 4;
1458 }
1459 createEdge(verts[n1_top], verts[n2_top], quadToTri_edges);
1460 createEdge(verts[n1_top], verts[n2_top], edges_new);
1461 }
1462 }
1463 // pyramid top on bottom face
1464 if((!valid_division && n1_bot >= 0 && n1_bot != (degen_ind + 2) % 4 &&
1465 n2_bot != (degen_ind + 2) % 4) ||
1466 p_bot_is_free) {
1467 if(n1_lat1 == (degen_ind + 1) % 4 || p_lat1_is_free) {
1468 valid_division = true;
1469 face_done[0] = (degen_ind + 1) % 4;
1470 if(p_lat1_is_free) {
1471 n1_lat1 = (degen_ind + 1) % 4;
1472 n2_lat1 = (degen_ind + 2) % 4 + 4;
1473 }
1474 createEdge(verts[n1_lat1], verts[n2_lat1], quadToTri_edges);
1475 createEdge(verts[n1_lat1], verts[n2_lat1], edges_new);
1476 }
1477 else if(n2_lat2 == (degen_ind + 3) % 4 || p_lat2_is_free) {
1478 valid_division = true;
1479 face_done[0] = (degen_ind + 2) % 4;
1480 if(p_lat2_is_free) {
1481 n1_lat2 = (degen_ind + 2) % 4 + 4;
1482 n2_lat2 = (degen_ind + 3) % 4;
1483 }
1484 createEdge(verts[n1_lat2], verts[n2_lat2], quadToTri_edges);
1485 createEdge(verts[n1_lat2], verts[n2_lat2], edges_new);
1486 }
1487 if(valid_division) {
1488 face_done[1] = 4;
1489 if(p_bot_is_free) {
1490 n1_bot = (degen_ind + 1) % 4;
1491 n2_bot = (degen_ind + 3) % 4;
1492 }
1493 createEdge(verts[n1_bot], verts[n2_bot], quadToTri_edges);
1494 createEdge(verts[n1_bot], verts[n2_bot], edges_new);
1495 }
1496 }
1497 }
1498 // see if the lateral diagonals have non-adjoining diagonals
1499 if(!valid_division) {
1500 if((n1_lat1 == (degen_ind + 1) % 4 || p_lat1_is_free) &&
1501 (n1_lat2 == (degen_ind + 2) % 4 || p_lat2_is_free)) {
1502 valid_division = true;
1503 n1_lat1 = (degen_ind + 1) % 4;
1504 n2_lat1 = (degen_ind + 2) % 4 + 4;
1505 n1_lat2 = (degen_ind + 2) % 4;
1506 n2_lat2 = (degen_ind + 3) % 4 + 4;
1507 }
1508 else if((n1_lat1 == (degen_ind + 1) % 4 + 4 || p_lat1_is_free) &&
1509 (n1_lat2 == (degen_ind + 2) % 4 + 4 || p_lat2_is_free)) {
1510 valid_division = true;
1511 n1_lat1 = (degen_ind + 1) % 4 + 4;
1512 n2_lat1 = (degen_ind + 2) % 4;
1513 n1_lat2 = (degen_ind + 2) % 4 + 4;
1514 n2_lat2 = (degen_ind + 3) % 4;
1515 }
1516 if(valid_division) {
1517 face_done[0] = (degen_ind + 1) % 4;
1518 face_done[1] = (degen_ind + 2) % 4;
1519 createEdge(verts[n1_lat1], verts[n2_lat1], quadToTri_edges);
1520 createEdge(verts[n1_lat1], verts[n2_lat1], edges_new);
1521 createEdge(verts[n1_lat2], verts[n2_lat2], quadToTri_edges);
1522 createEdge(verts[n1_lat2], verts[n2_lat2], edges_new);
1523 }
1524 }
1525
1526 if(valid_division) break;
1527 }
1528
1529 // if no subdivisiion, still take care of any adjustable but required
1530 // diagonals not yet added
1531 for(unsigned int s = 0; s < face_types["adj_diag"].size(); s++) {
1532 int ind = face_types["adj_diag"][s];
1533 if(ind != face_done[0] && ind != face_done[1] && ind != face_done[2]) {
1534 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
1535 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
1536 }
1537 }
1538
1539 // if no division top found, need internal vertex.
1540 if(!valid_division) {
1541 std::pair<unsigned int, unsigned int> jkpair(j, k);
1542 problems[elem].insert(jkpair);
1543 problems_new[elem].insert(jkpair);
1544 }
1545 }
1546
1547 // Divide a fully non-degenerate hexahedron by brute force.
addEdgesForQuadToTriFullHexa(GRegion * gr,MElement * elem,ExtrudeParams * ep,int j,int k,std::vector<MVertex * > verts,std::map<std::string,std::vector<int>> & face_types,std::set<std::pair<MVertex *,MVertex * >> & edges_new,std::set<std::pair<MVertex *,MVertex * >> & forbidden_new,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::vector<int> nfix1,std::vector<int> nfix2,std::vector<int> nadj1,std::vector<int> nadj2,std::vector<int> free_flag)1548 static void addEdgesForQuadToTriFullHexa(
1549 GRegion *gr, MElement *elem, ExtrudeParams *ep, int j, int k,
1550 std::vector<MVertex *> verts,
1551 std::map<std::string, std::vector<int> > &face_types,
1552 std::set<std::pair<MVertex *, MVertex *> > &edges_new,
1553 std::set<std::pair<MVertex *, MVertex *> > &forbidden_new,
1554 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
1555 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
1556 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
1557 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
1558 &problems_new,
1559 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
1560 &problems,
1561 std::vector<int> nfix1, std::vector<int> nfix2, std::vector<int> nadj1,
1562 std::vector<int> nadj2, std::vector<int> free_flag)
1563 {
1564 // There are 4 main possibilities for minimum diags to guarantee slice:
1565 // 1. Three diagonals at a corner
1566 // 2. One PAIR of opposite diagonals, ABSOLUTELY ALL OTHER FACES FORBIDDEN
1567 // 2. Two PAIRS of opposite diags
1568 // 3. One PAIR of opposite diags PLUS a vertex with two ADDITIONAL diags
1569 // meeting on it.
1570
1571 if(!ep) return;
1572
1573 // numbers of each type of face
1574 // int num_degen = face_types["degen"].size();
1575 // int num_single_tri = face_types["single_tri"].size();
1576 // int num_recomb = face_types["recomb"].size();
1577 int num_fixed_diag = face_types["fixed_diag"].size();
1578 int num_adj_diag = face_types["adj_diag"].size();
1579 int num_free = face_types["free"].size();
1580
1581 // If all faces are free, then diagonalize the three faces adjacent to vertex
1582 // with
1583 // smallest vertex pointer
1584 if(num_free >= 6) {
1585 int ind_low = getIndexForLowestVertexPointer(verts);
1586 int add = ind_low < 4 ? 0 : 4;
1587
1588 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4 + add],
1589 quadToTri_edges);
1590 createEdge(verts[ind_low], verts[(ind_low - add + 2) % 4 + add], edges_new);
1591 createEdge(verts[ind_low], verts[(ind_low - add + 1) % 4 + 4 - add],
1592 quadToTri_edges);
1593 createEdge(verts[ind_low], verts[(ind_low - add + 1) % 4 + 4 - add],
1594 edges_new);
1595 createEdge(verts[ind_low], verts[(ind_low - add + 3) % 4 + 4 - add],
1596 quadToTri_edges);
1597 createEdge(verts[ind_low], verts[(ind_low - add + 3) % 4 + 4 - add],
1598 edges_new);
1599 return;
1600 }
1601
1602 // Start the loop to progressively become more permissive in what diagonals to
1603 // allow for subdivision. On t-loop, select types of diagonals to look for. t
1604 // = 0, fixed and preferred adjustables. t = 1, include non-preferred
1605 // adjustables with lowest vertex. t = 2, include free face diagonal, lowest
1606 // vertex. t = 3, on adjustable or free faces, take any diagonal that works.
1607
1608 // this variable records the faces selected for diagonalization
1609 int face_done[4];
1610 for(int p = 0; p < 4; p++) face_done[p] = -1 - p;
1611
1612 // if find just two diagonals that could slice a prism with no other diags,
1613 // remember ('hold') those diagonals until end of loop in the following 'hold'
1614 // variables. Might have to resort to forbidding free surfaces and cutting two
1615 // prisms only. don't really want to forbid surfaces, though...that's why two
1616 // prisms are not immediately cut when found.
1617 int p1_hold = -1,
1618 p2_hold = -2; // if found two opposite diags that could work alone
1619 int n1_hold[2] = {0, 0},
1620 n2_hold[2] = {0, 0}; // hold diag nodes for p1_hold, p2_hold.
1621 bool valid_division = false;
1622
1623 for(int t = 0; t < 4; t++) {
1624 // variables that hold the face diagonal nodes.
1625 int n1[6], n2[6];
1626
1627 // if the face is completely free (t==3), face_is_free = true;
1628 bool face_is_free[6];
1629
1630 // set default values of the n variables
1631 // to unique negative numbers; bool to false.
1632 for(int p = 0; p < 6; p++) {
1633 n1[p] = -p * p - p - 1;
1634 n2[p] = -p * p - p - 2;
1635 face_is_free[p] = false;
1636 }
1637
1638 for(int p = 0; p < 6; p++) {
1639 // fixed diagonals
1640 if(nfix1[p] >= 0) {
1641 n1[p] = nfix1[p];
1642 n2[p] = nfix2[p];
1643 }
1644 // preferred adjustable diagonals
1645 else if(!t && nadj1[p] >= 0) {
1646 n1[p] = nadj1[p];
1647 n2[p] = nadj2[p];
1648 }
1649 // choose lowest vertex for t < 3, any vertex for t == 3
1650 else if((t >= 1 && t < 3 && nadj1[p] >= 0) || (t == 2 && free_flag[p])) {
1651 if(p < 4) {
1652 if((verts[p] < verts[(p + 1) % 4] && verts[p] < verts[p + 4]) ||
1653 (verts[(p + 1) % 4 + 4] < verts[(p + 1) % 4] &&
1654 verts[(p + 1) % 4 + 4] < verts[p + 4])) {
1655 n1[p] = p;
1656 n2[p] = (p + 1) % 4 + 4;
1657 }
1658 else {
1659 n1[p] = p + 4;
1660 n2[p] = (p + 1) % 4;
1661 }
1662 }
1663 else {
1664 int add = p == 4 ? 0 : 4;
1665 if((verts[0 + add] < verts[1 + add] &&
1666 verts[0 + add] < verts[3 + add]) ||
1667 (verts[2 + add] < verts[1 + add] &&
1668 verts[2 + add] < verts[3 + add])) {
1669 n1[p] = 0 + add;
1670 n2[p] = 2 + add;
1671 }
1672 else {
1673 n1[p] = 1 + add;
1674 n2[p] = 3 + add;
1675 }
1676 }
1677 }
1678 else if(t == 3 && (nadj1[p] >= 0 || free_flag[p]))
1679 face_is_free[p] = true;
1680 }
1681
1682 // Now perform the tests to find the valid subdivision
1683
1684 // first, do the test to find opposite aligned diagonals for "prism slice
1685 // through" Not verified, but I believe this gives better quality elements
1686 // than 3-diag corners
1687 if(!valid_division) {
1688 for(int p = 0; p < 3; p++) {
1689 int p1 = -1, p2 = -2;
1690 // prism slicing faces
1691 if(p > 1) {
1692 p1 = 4;
1693 p2 = 5;
1694 }
1695 else {
1696 p1 = p;
1697 p2 = (p + 2) % 4;
1698 }
1699
1700 // if these two faces do not work for opposite aligned diagonals,
1701 // continue
1702 if((n1[p1] < 0 && !face_is_free[p1]) ||
1703 (n1[p2] < 0 && !face_is_free[p2]))
1704 continue;
1705 if(n1[p1] >= 0 && n1[p2] >= 0) {
1706 if(p1 < 4) {
1707 if(((n1[p1] == p1 || n2[p1] == p1) && n1[p2] != p2 + 4 &&
1708 n2[p2] != p2 + 4) ||
1709 ((n1[p1] == p1 + 4 || n2[p1] == p1 + 4) && n1[p2] != p2 &&
1710 n2[p2] != p2))
1711 continue;
1712 }
1713 else {
1714 int add = p1 == 4 ? 4 : -4;
1715 if(n1[p1] + add != n1[p2] && n2[p1] + add != n1[p2]) continue;
1716 }
1717 }
1718
1719 // if TWO faces are free, set one to the lowest pointer
1720 if(face_is_free[p1] && face_is_free[p2]) {
1721 face_is_free[p1] = false;
1722 if(p1 < 4) {
1723 if((verts[p1] < verts[p1 + 4] && verts[p1] < verts[(p1 + 1) % 4]) ||
1724 (verts[(p1 + 1) % 4 + 4] < verts[p1 + 4] &&
1725 verts[(p1 + 1) % 4 + 4] < verts[(p1 + 1) % 4])) {
1726 n1[p1] = p1;
1727 n2[p1] = (p1 + 1) % 4 + 4;
1728 }
1729 else {
1730 n1[p1] = (p1 + 4);
1731 n2[p1] = (p1 + 1) % 4;
1732 }
1733 }
1734 else {
1735 int add = p1 == 4 ? 0 : 4;
1736 if((verts[0 + add] < verts[1 + add] &&
1737 verts[0 + add] < verts[3 + add]) ||
1738 (verts[2 + add] < verts[1 + add] &&
1739 verts[2 + add] < verts[3 + add])) {
1740 n1[p1] = 0 + add;
1741 n2[p1] = 2 + add;
1742 }
1743 else {
1744 n1[p1] = 1 + add;
1745 n2[p1] = 3 + add;
1746 }
1747 }
1748 }
1749
1750 // if one and ONLY one face is free, go ahead and set it to match the
1751 // other now.
1752 if(n1[p1] >= 0 && face_is_free[p2]) {
1753 if(p1 < 4) {
1754 if(n1[p1] == p1 || n2[p1] == p1) {
1755 n1[p2] = p2 + 4;
1756 n2[p2] = (p2 + 1) % 4;
1757 }
1758 else {
1759 n1[p2] = p2;
1760 n2[p2] = (p2 + 1) % 4 + 4;
1761 }
1762 }
1763 else {
1764 int add = p1 == 4 ? 4 : -4;
1765 n1[p2] = n1[p1] + add;
1766 n2[p2] = n2[p1] + add;
1767 }
1768 face_is_free[p2] = false;
1769 }
1770 else if(n1[p2] >= 0 && face_is_free[p1]) {
1771 if(p1 < 4) {
1772 if(n1[p2] == p2 || n2[p2] == p2) {
1773 n1[p1] = p1 + 4;
1774 n2[p1] = (p1 + 1) % 4;
1775 }
1776 else {
1777 n1[p1] = p1;
1778 n2[p1] = (p1 + 1) % 4 + 4;
1779 }
1780 }
1781 else {
1782 int add = p2 == 4 ? 4 : -4;
1783 n1[p1] = n1[p2] + add;
1784 n2[p1] = n2[p2] + add;
1785 }
1786 face_is_free[p1] = false;
1787 }
1788
1789 // In case the whole loop finishes and
1790 // cannot make further diagonalizations on any faces to make the prism
1791 // slice work, AND if there are no oter required diagonals other than
1792 // among these two diagonals, then it will be possible to come back to
1793 // these two opposing diagonals to make a simple two-prism slice. So,
1794 // if there are no other diags, hold these two valid opposing diagonals
1795 // in case we want to come back to them later to just have two opposing
1796 // diagonals with the other faces forbidden.
1797 if(p1_hold < 0 || p2_hold < 0) {
1798 int required_diag_count = 0;
1799 if(nfix1[p1] >= 0 || nadj1[p1] >= 0) required_diag_count++;
1800 if(nfix1[p2] >= 0 || nadj1[p2] >= 0) required_diag_count++;
1801 if(p1_hold < 0 &&
1802 num_fixed_diag + num_adj_diag <= required_diag_count) {
1803 p1_hold = p1;
1804 p2_hold = p2;
1805 n1_hold[0] = n1[p1];
1806 n2_hold[0] = n2[p1];
1807 n1_hold[1] = n1[p2];
1808 n2_hold[1] = n2[p2];
1809 }
1810 }
1811
1812 // Two tests to see if this prism slice-through will work:
1813 // 1: if there is another set of opposing faces with aligned diagonals.
1814 // 2: if two diagonals on one of the remaining 4 faces meet, valid.
1815
1816 // Test 1: Another set of opposite, aligned diagonals.
1817 for(int s = 0; s < 3; s++) {
1818 if(s == p1 || (s > 1 && (p1 == 4 || p1 == 5))) continue;
1819 int s1, s2;
1820 if(s > 1) {
1821 s1 = 4;
1822 s2 = 5;
1823 }
1824 else {
1825 s1 = s;
1826 s2 = (s + 2) % 4;
1827 }
1828 // if these two faces do not work for opposite aligned diagonals,
1829 // continue
1830 if((n1[s1] < 0 && !face_is_free[s1]) ||
1831 (n1[s2] < 0 && !face_is_free[s2]))
1832 continue;
1833 if(n1[s1] >= 0 && n1[s2] >= 0) {
1834 if(s1 < 4) {
1835 if(((n1[s1] == s1 || n2[s1] == s1) && n1[s2] != s2 + 4 &&
1836 n2[s2] != s2 + 4) ||
1837 ((n1[s1] == s1 + 4 || n2[s1] == s1 + 4) && n1[s2] != s2 &&
1838 n2[s2] != s2))
1839 continue;
1840 }
1841 else {
1842 int add = s1 == 4 ? 4 : -4;
1843 if(n1[s1] + add != n1[s2] && n2[s1] + add != n1[s2]) continue;
1844 }
1845 }
1846
1847 valid_division = true;
1848 face_done[0] = p1;
1849 face_done[1] = p2;
1850 face_done[2] = s1;
1851 face_done[3] = s2;
1852
1853 // if TWO faces are free, set one to the lowest pointer
1854 if(face_is_free[s1] && face_is_free[s2]) {
1855 face_is_free[s1] = false;
1856 if(s1 < 4) {
1857 if((verts[s1] < verts[s1 + 4] &&
1858 verts[s1] < verts[(s1 + 1) % 4]) ||
1859 (verts[(s1 + 1) % 4 + 4] < verts[s1 + 4] &&
1860 verts[(s1 + 1) % 4 + 4] < verts[(s1 + 1) % 4])) {
1861 n1[s1] = s1;
1862 n2[s1] = (s1 + 1) % 4 + 4;
1863 }
1864 else {
1865 n1[s1] = (s1 + 4);
1866 n2[s1] = (s1 + 1) % 4;
1867 }
1868 }
1869 else {
1870 int add = s1 == 4 ? 0 : 4;
1871 if((verts[0 + add] < verts[1 + add] &&
1872 verts[0 + add] < verts[3 + add]) ||
1873 (verts[2 + add] < verts[1 + add] &&
1874 verts[2 + add] < verts[3 + add])) {
1875 n1[s1] = 0 + add;
1876 n2[s1] = 2 + add;
1877 }
1878 else {
1879 n1[s1] = 1 + add;
1880 n2[s1] = 3 + add;
1881 }
1882 }
1883 }
1884
1885 // if ONLY one face is free, go ahead and set it to match the other
1886 // now.
1887 if(n1[s1] >= 0 && face_is_free[s2]) {
1888 if(s1 < 4) {
1889 if(n1[s1] == s1 || n2[s1] == s1) {
1890 n1[s2] = s2 + 4;
1891 n2[s2] = (s2 + 1) % 4;
1892 }
1893 else {
1894 n1[s2] = s2;
1895 n2[s2] = (s2 + 1) % 4 + 4;
1896 }
1897 }
1898 else {
1899 int add = s1 == 4 ? 4 : -4;
1900 n1[s2] = n1[s1] + add;
1901 n2[s2] = n2[s1] + add;
1902 }
1903 face_is_free[s2] = false;
1904 }
1905 else if(n1[s2] >= 0 && face_is_free[s1]) {
1906 if(s1 < 4) {
1907 if(n1[s2] == s2 || n2[s2] == s2) {
1908 n1[s1] = s1 + 4;
1909 n2[s1] = (s1 + 1) % 4;
1910 }
1911 else {
1912 n1[s1] = s1;
1913 n2[s1] = (s1 + 1) % 4 + 4;
1914 }
1915 }
1916 else {
1917 int add = s2 == 4 ? 4 : -4;
1918 n1[s1] = n1[s2] + add;
1919 n2[s1] = n2[s2] + add;
1920 }
1921 face_is_free[s1] = false;
1922 }
1923
1924 if(valid_division) break;
1925 }
1926
1927 // Test 2: any vertex has two diagonals meeting at it, NOT including the
1928 // diagonals on p1, p2;
1929 if(!valid_division) {
1930 for(int s = 0; s < 8; s++) { // looping over vertices now, be careful
1931 int add = s < 4 ? 0 : 4;
1932 std::vector<int> faces;
1933 faces.assign(2, -1);
1934 int third_face = s < 4 ? 4 : 5;
1935 int count_diags = 0;
1936 if(s - add != p1 && s - add != p2 &&
1937 (n1[(s - add)] == s || n2[(s - add)] == s ||
1938 face_is_free[(s - add)])) {
1939 faces[count_diags] = s - add;
1940 count_diags++;
1941 }
1942 if((s - add + 3) % 4 != p1 && (s - add + 3) % 4 != p2 &&
1943 (n1[(s - add + 3) % 4] == s || n2[(s - add + 3) % 4] == s ||
1944 face_is_free[(s - add + 3) % 4])) {
1945 faces[count_diags] = (s - add + 3) % 4;
1946 count_diags++;
1947 }
1948 if(count_diags < 2 && third_face != p1 && third_face != p2 &&
1949 (n1[third_face] == s || n2[third_face] == s ||
1950 face_is_free[third_face])) {
1951 faces[count_diags] = third_face;
1952 count_diags++;
1953 }
1954
1955 // if valid subdivision NOT found
1956 if(count_diags < 2) continue;
1957
1958 // if valid subdivision found
1959
1960 valid_division = true;
1961
1962 face_done[0] = p1;
1963 face_done[1] = p2;
1964 face_done[2] = faces[0];
1965 face_done[3] = faces[1];
1966
1967 if(face_is_free[s - add]) {
1968 n1[s - add] = s;
1969 n2[s - add] = (s - add + 1) % 4 + 4 - add;
1970 }
1971 if(face_is_free[(s - add + 3) % 4]) {
1972 n1[(s - add + 3) % 4] = s;
1973 n2[(s - add + 3) % 4] = (s - add + 3) % 4 + 4 - add;
1974 }
1975 if(face_is_free[third_face]) {
1976 n1[third_face] = s;
1977 n2[third_face] = (s - add + 2) % 4 + add;
1978 }
1979
1980 if(valid_division) break;
1981 }
1982 } // end of test 2
1983
1984 if(valid_division) {
1985 createEdge(verts[n1[face_done[0]]], verts[n2[face_done[0]]],
1986 quadToTri_edges);
1987 createEdge(verts[n1[face_done[0]]], verts[n2[face_done[0]]],
1988 edges_new);
1989 createEdge(verts[n1[face_done[1]]], verts[n2[face_done[1]]],
1990 quadToTri_edges);
1991 createEdge(verts[n1[face_done[1]]], verts[n2[face_done[1]]],
1992 edges_new);
1993 createEdge(verts[n1[face_done[2]]], verts[n2[face_done[2]]],
1994 quadToTri_edges);
1995 createEdge(verts[n1[face_done[2]]], verts[n2[face_done[2]]],
1996 edges_new);
1997 createEdge(verts[n1[face_done[3]]], verts[n2[face_done[3]]],
1998 quadToTri_edges);
1999 createEdge(verts[n1[face_done[3]]], verts[n2[face_done[3]]],
2000 edges_new);
2001 break;
2002 }
2003 } // end of p loop over first set of opposite faces
2004 }
2005
2006 // Test for 3 diagonals on a corner.
2007 if(!valid_division) {
2008 for(int p = 0; p < 8; p++) { // looping over vertices now, be careful
2009 int add = p < 4 ? 0 : 4;
2010 int third_face = p < 4 ? 4 : 5;
2011 if((n1[(p - add)] == p || n2[(p - add)] == p ||
2012 face_is_free[(p - add)]) &&
2013 (n1[(p - add + 3) % 4] == p || n2[(p - add + 3) % 4] == p ||
2014 face_is_free[(p - add + 3) % 4]) &&
2015 (n1[third_face] == p || n2[third_face] == p ||
2016 face_is_free[third_face])) {
2017 valid_division = true;
2018 if(face_is_free[p - add]) {
2019 n1[p - add] = p;
2020 n2[p - add] = (p - add + 1) % 4 + 4 - add;
2021 }
2022 if(face_is_free[(p - add + 3) % 4]) {
2023 n1[(p - add + 3) % 4] = p;
2024 n2[(p - add + 3) % 4] = (p - add + 3) % 4 + 4 - add;
2025 }
2026 if(face_is_free[third_face]) {
2027 n1[third_face] = p;
2028 n2[third_face] = (p - add + 2) % 4 + add;
2029 }
2030
2031 face_done[0] = (p - add);
2032 face_done[1] = (p - add + 3) % 4;
2033 face_done[2] = third_face;
2034
2035 createEdge(verts[n1[(p - add)]], verts[n2[(p - add)]],
2036 quadToTri_edges);
2037 createEdge(verts[n1[(p - add)]], verts[n2[(p - add)]], edges_new);
2038 createEdge(verts[n1[(p - add + 3) % 4]], verts[n2[(p - add + 3) % 4]],
2039 quadToTri_edges);
2040 createEdge(verts[n1[(p - add + 3) % 4]], verts[n2[(p - add + 3) % 4]],
2041 edges_new);
2042 createEdge(verts[n1[third_face]], verts[n2[third_face]],
2043 quadToTri_edges);
2044 createEdge(verts[n1[third_face]], verts[n2[third_face]], edges_new);
2045 break;
2046 }
2047 }
2048 }
2049
2050 if(valid_division) break;
2051
2052 } // end of t loop (outer loop)
2053
2054 // If no valid division yet but yet there were two opposite faces once
2055 // ( if this won't work, should be the case that p1_hold < 0 here )
2056 if(!valid_division && p1_hold >= 0) {
2057 valid_division = true;
2058 face_done[0] = p1_hold;
2059 face_done[1] = p2_hold;
2060 createEdge(verts[n1_hold[0]], verts[n2_hold[0]], quadToTri_edges);
2061 createEdge(verts[n1_hold[0]], verts[n2_hold[0]], edges_new);
2062 createEdge(verts[n1_hold[1]], verts[n2_hold[1]], quadToTri_edges);
2063 createEdge(verts[n1_hold[1]], verts[n2_hold[1]], edges_new);
2064
2065 // create forbidden faces
2066 for(unsigned int s = 0; s < face_types["free"].size(); s++) {
2067 int s_tmp = face_types["free"][s];
2068 std::vector<MVertex *> v_free;
2069 if(s_tmp == p1_hold || s_tmp == p2_hold) continue;
2070 if(s_tmp < 4) {
2071 v_free.push_back(verts[s_tmp]);
2072 v_free.push_back(verts[(s_tmp + 1) % 4]);
2073 v_free.push_back(verts[(s_tmp + 1) % 4 + 4]);
2074 v_free.push_back(verts[s_tmp + 4]);
2075 }
2076 else {
2077 int add = s_tmp == 4 ? 0 : 4;
2078 v_free.push_back(verts[add]);
2079 v_free.push_back(verts[add + 1]);
2080 v_free.push_back(verts[add + 2]);
2081 v_free.push_back(verts[add + 3]);
2082 }
2083 createForbidden(v_free, forbidden_edges);
2084 createForbidden(v_free, forbidden_new);
2085 }
2086 }
2087
2088 // take care of any adjustable but required diagonals not yet added
2089 for(unsigned int s = 0; s < face_types["adj_diag"].size(); s++) {
2090 int ind = face_types["adj_diag"][s];
2091 if(ind == face_done[0] || ind == face_done[1] || ind == face_done[2] ||
2092 ind == face_done[3])
2093 continue;
2094 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
2095 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
2096 }
2097
2098 // if no valid division found, problematic.
2099 if(!valid_division) {
2100 std::pair<unsigned int, unsigned int> jkpair(j, k);
2101 problems[elem].insert(jkpair);
2102 problems_new[elem].insert(jkpair);
2103 }
2104 }
2105
2106 // Generate face diagonals to subdivide hexahedra by BRUTE FORCE. Not
2107 // recommended for general use, but it is required for some elements which have
2108 // all vertices on an external region boundary. Added 2010-01-29
bruteForceEdgeQuadToTriHexa(GRegion * gr,MElement * elem,int j,int k,std::vector<MVertex * > verts,std::map<std::string,std::vector<int>> & face_types,std::set<std::pair<MVertex *,MVertex * >> & edges_new,std::set<std::pair<MVertex *,MVertex * >> & forbidden_new,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,const std::vector<int> & nfix1,const std::vector<int> & nfix2,std::vector<int> nadj1,std::vector<int> nadj2,const std::vector<int> & free_flag)2109 static void bruteForceEdgeQuadToTriHexa(
2110 GRegion *gr, MElement *elem, int j, int k, std::vector<MVertex *> verts,
2111 std::map<std::string, std::vector<int> > &face_types,
2112 std::set<std::pair<MVertex *, MVertex *> > &edges_new,
2113 std::set<std::pair<MVertex *, MVertex *> > &forbidden_new,
2114 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
2115 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
2116 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
2117 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
2118 &problems_new,
2119 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
2120 &problems,
2121 const std::vector<int> &nfix1, const std::vector<int> &nfix2,
2122 std::vector<int> nadj1, std::vector<int> nadj2,
2123 const std::vector<int> &free_flag)
2124 {
2125 ExtrudeParams *ep = gr->meshAttributes.extrude;
2126
2127 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
2128 Msg::Error("In bruteForceEdgeQuadToTriHexa(), invalid extrusion "
2129 "in region %d for performing QuadToTri mesh generation.",
2130 gr->tag());
2131 return;
2132 }
2133
2134 GModel *model = gr->model();
2135 if(!model) {
2136 Msg::Error("In bruteForceEdgeQuadToTriHexa(), invalid model for region "
2137 "%d.",
2138 gr->tag());
2139 return;
2140 }
2141
2142 // now find and verify the source and the top of region
2143
2144 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
2145 if(!reg_source) {
2146 Msg::Error(
2147 "In bruteForceEdgeQuadToTriHexa(), invalid source face for region "
2148 "%d.",
2149 gr->tag());
2150 return;
2151 }
2152
2153 // verify number of vertices
2154 // int n_lat;
2155 if(verts.size() != 8) {
2156 Msg::Error(
2157 "In bruteForceEdgeQuadToTriHexa(), number of vertices not equal 8.");
2158 return;
2159 }
2160 // else
2161 // n_lat = 4;
2162
2163 // numbers of each type of face
2164 // int num_degen = face_types["degen"].size();
2165 // int num_single_tri = face_types["single_tri"].size();
2166 int num_recomb = face_types["recomb"].size();
2167 // int num_fixed_diag = face_types["fixed_diag"].size();
2168 int num_adj_diag = face_types["adj_diag"].size();
2169 // int num_free = face_types["free"].size();
2170
2171 // Make sure all faces marked forbidden in face_types have all diagonals in
2172 // forbidden_edges;
2173 for(int p = 0; p < num_recomb; p++) {
2174 int ind = face_types["recomb"][p];
2175 std::vector<MVertex *> v;
2176 if(ind == 4 || ind == 5) {
2177 int add = (ind == 4) ? 0 : 4;
2178 v.push_back(verts[0 + add]);
2179 v.push_back(verts[1 + add]);
2180 v.push_back(verts[2 + add]);
2181 v.push_back(verts[3 + add]);
2182 }
2183 else {
2184 v.push_back(verts[ind]);
2185 v.push_back(verts[(ind + 1) % 4]);
2186 v.push_back(verts[(ind + 1) % 4 + 4]);
2187 v.push_back(verts[ind + 4]);
2188 }
2189
2190 createForbidden(v, forbidden_new);
2191 createForbidden(v, forbidden_edges);
2192 }
2193
2194 // If this element is marked problematic, make all the adjustable diags (none
2195 // should exist if it's already marked as a problem, but just make sure) and
2196 // return.
2197 std::pair<unsigned int, unsigned int> jkpair(j, k);
2198 if(problems.find(elem) != problems.end() && problems[elem].count(jkpair)) {
2199 for(int s = 0; s < num_adj_diag; s++) {
2200 int ind = face_types["adj_diag"][s];
2201 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
2202 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
2203 }
2204 return;
2205 }
2206
2207 // test for right number of triangles with degenerate edges
2208 if((face_types["single_tri"].size() != 0 &&
2209 face_types["single_tri"].size() != 2) ||
2210 (face_types["degen"].size() && face_types["single_tri"].size() != 2)) {
2211 Msg::Error("In bruteForceEdgeQuadToTriHexa(), bad degenerated extrusion "
2212 "encountered.");
2213 std::pair<unsigned int, unsigned int> jkpair(j, k);
2214 problems[elem].insert(jkpair);
2215 problems_new[elem].insert(jkpair);
2216 if(num_adj_diag) {
2217 int ind = face_types["adj_diag"][0];
2218 if(nadj1[ind] >= 0) {
2219 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
2220 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
2221 }
2222 }
2223 return;
2224 }
2225
2226 // if there are not enough diagonals:
2227 if(6 - face_types["single_tri"].size() - face_types["recomb"].size() < 2) {
2228 // Can't be meshed without internal vertex
2229 if(face_types["adj_diag"].size() + face_types["fixed_diag"].size()) {
2230 std::pair<unsigned int, unsigned int> jkpair(j, k);
2231 problems[elem].insert(jkpair);
2232 problems_new[elem].insert(jkpair);
2233 for(unsigned int s = 0; s < face_types["adj_diag"].size(); s++) {
2234 int ind = face_types["adj_diag"][s];
2235 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], quadToTri_edges);
2236 createEdge(verts[nadj1[ind]], verts[nadj2[ind]], edges_new);
2237 }
2238 }
2239 // set the (at most) one free surface to forbidden and return
2240 else if(face_types["free"].size()) {
2241 int p_tmp = face_types["free"][0];
2242 std::vector<MVertex *> v_free;
2243 if(p_tmp < 4) {
2244 v_free.push_back(verts[p_tmp]);
2245 v_free.push_back(verts[(p_tmp + 1) % 4]);
2246 v_free.push_back(verts[(p_tmp + 1) % 4 + 4]);
2247 v_free.push_back(verts[p_tmp + 4]);
2248 }
2249 else {
2250 int add = p_tmp == 4 ? 0 : 4;
2251 v_free.push_back(verts[add]);
2252 v_free.push_back(verts[add + 1]);
2253 v_free.push_back(verts[add + 2]);
2254 v_free.push_back(verts[add + 3]);
2255 }
2256 createForbidden(v_free, forbidden_edges);
2257 createForbidden(v_free, forbidden_new);
2258 }
2259 return;
2260 }
2261
2262 // Start looking at the different shape possibilities:
2263
2264 // First shape: A PRISM
2265 // Only two possibilities. Either the bottom can be divided along same
2266 // diagonal as the top, or the one quad side can be divided joining the top
2267 // diagonal. Otherwise, problem.
2268 if(face_types["degen"].size()) {
2269 addEdgesForQuadToTriTwoPtDegenHexa(
2270 gr, elem, ep, j, k, verts, face_types, edges_new, forbidden_new,
2271 quadToTri_edges, forbidden_edges, lat_tri_diags, problems_new, problems,
2272 nfix1, nfix2, nadj1, nadj2, free_flag);
2273 return;
2274 }
2275
2276 // Second shape type: DEGENERATED HEXAHEDRON
2277 // Since this shape can be divided to create legitimate tets and/or pyramids,
2278 // let us try it.
2279 if(!face_types["degen"].size() && face_types["single_tri"].size() == 2) {
2280 addEdgesForQuadToTriOnePtDegenHexa(
2281 gr, elem, ep, j, k, verts, face_types, edges_new, forbidden_new,
2282 quadToTri_edges, forbidden_edges, lat_tri_diags, problems_new, problems,
2283 nfix1, nfix2, nadj1, nadj2, free_flag);
2284 return;
2285 }
2286
2287 // Third SHAPE: FULL HEXAHEDRON
2288 // There are 4 main possibilities for minimum diags to guarantee slice:
2289 // 1. Three diagonals at a corner
2290 // 2. One PAIR of opposite diagonals, ABSOLUTELY ALL OTHER FACES FORBIDDEN
2291 // 2. Two PAIRS of opposite diags
2292 // 3. One PAIR of opposite diags PLUS a vertex with two ADDITIONAL diags
2293 // meeting on it.
2294 if(!face_types["single_tri"].size() && !face_types["degen"].size()) {
2295 addEdgesForQuadToTriFullHexa(
2296 gr, elem, ep, j, k, verts, face_types, edges_new, forbidden_new,
2297 quadToTri_edges, forbidden_edges, lat_tri_diags, problems_new, problems,
2298 nfix1, nfix2, nadj1, nadj2, free_flag);
2299 return;
2300 }
2301 }
2302
2303 // This is a shortcut function to simply copy face diagonals all the way up a
2304 // vertical column of extruded elements. This function may not save very many
2305 // operations, but it is going to stay...
ExtrudeDiags(GRegion * gr,std::vector<MVertex * > v,unsigned int j_start,unsigned int k_start,unsigned int j_top,unsigned int k_top,MElement * elem,ExtrudeParams * loop_ep,std::set<std::pair<MVertex *,MVertex * >> & edges_new,std::set<std::pair<MVertex *,MVertex * >> & forbidden_new,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,MVertexRTree & pos)2306 static int ExtrudeDiags(
2307 GRegion *gr, std::vector<MVertex *> v, unsigned int j_start,
2308 unsigned int k_start, unsigned int j_top, unsigned int k_top, MElement *elem,
2309 ExtrudeParams *loop_ep, std::set<std::pair<MVertex *, MVertex *> > &edges_new,
2310 std::set<std::pair<MVertex *, MVertex *> > &forbidden_new,
2311 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
2312 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
2313 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
2314 &problems_new,
2315 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
2316 &problems,
2317 MVertexRTree &pos)
2318 {
2319 if(!loop_ep || !loop_ep->mesh.QuadToTri || !loop_ep->mesh.ExtrudeMesh) {
2320 Msg::Error("In ExtrudeDiags(), invalid extrusion "
2321 "in region %d for performing QuadToTri mesh generation.",
2322 gr->tag());
2323 return 0;
2324 }
2325
2326 GModel *model = gr->model();
2327 if(!model) {
2328 Msg::Error("In ExtrudeDiags(), invalid model for region "
2329 "%d.",
2330 gr->tag());
2331 return 0;
2332 }
2333
2334 int elem_size = elem->getNumVertices();
2335 if(!((v.size() == 6 && elem_size == 3) ||
2336 (v.size() == 8 && elem_size == 4))) {
2337 Msg::Error("In ExtrudeDiags(), vertex number mismatch "
2338 "between 2D source element and extruded volume.");
2339 return 0;
2340 }
2341
2342 // first add the forbidden edges, then the new quadToTri_edges
2343 // w = 0 forbidden, w = 1 quadToTri_edges (fixed)
2344 for(int w = 0; w < 2; w++) {
2345 std::set<std::pair<MVertex *, MVertex *> >::iterator it;
2346 std::set<std::pair<MVertex *, MVertex *> >::iterator it_start;
2347 std::set<std::pair<MVertex *, MVertex *> >::iterator it_end;
2348 it_start = (!w) ? forbidden_new.begin() : edges_new.begin();
2349 it_end = (!w) ? forbidden_new.end() : edges_new.end();
2350
2351 for(it = it_start; it != it_end; it++) {
2352 MVertex *v1, *v2;
2353 v1 = (*it).first;
2354 v2 = (*it).second;
2355 // find indices in v vector
2356 int ind1 = -1, ind2 = -1;
2357 for(unsigned int p = 0; p < v.size(); p++) {
2358 if(v[p] == v1) ind1 = p;
2359 if(v[p] == v2) ind2 = p;
2360 }
2361 if(ind1 < 0 || ind2 < 0) {
2362 Msg::Error("Error in ExtrudeDiags(): could not find vertex indices.");
2363 return 0;
2364 }
2365 // source verts:
2366 MVertex *sv1 = (ind1 < elem_size) ? elem->getVertex(ind1) :
2367 elem->getVertex(ind1 - elem_size);
2368 MVertex *sv2 = (ind2 < elem_size) ? elem->getVertex(ind2) :
2369 elem->getVertex(ind2 - elem_size);
2370 // extrude these two verts
2371 for(unsigned int j = j_start; j <= j_top; j++) {
2372 int k_start_tmp = (j == j_start) ? k_start + 1 : 0;
2373 int k_stop = (j == j_top) ? k_top : loop_ep->mesh.NbElmLayer[j];
2374 for(int k = k_start_tmp; k < k_stop; k++) {
2375 std::vector<MVertex *> v_extr =
2376 getExtrudedLateralVertices(sv1, sv2, gr, j, k, loop_ep, pos);
2377 if(v_extr.size() != 4) return 0;
2378 if(!w) {
2379 // reorder v_ext
2380 MVertex *tmp = v_extr[2];
2381 v_extr[2] = v_extr[3];
2382 v_extr[3] = tmp;
2383 createForbidden(v_extr, forbidden_edges);
2384 }
2385 else {
2386 MVertex *v_final_1 = (ind1 < elem_size) ? v_extr[0] : v_extr[2];
2387 MVertex *v_final_2 = (ind2 < elem_size) ? v_extr[1] : v_extr[3];
2388 createEdge(v_final_1, v_final_2, quadToTri_edges);
2389 }
2390 }
2391 }
2392 }
2393 }
2394
2395 // add problem elements
2396 std::pair<unsigned int, unsigned int> jk_start(j_start, k_start);
2397 std::map<MElement *,
2398 std::set<std::pair<unsigned int, unsigned int> > >::iterator itprob;
2399
2400 if(problems_new.size()) {
2401 for(itprob = problems_new.begin(); itprob != problems_new.end(); itprob++) {
2402 for(unsigned int j = j_start; j <= j_top; j++) {
2403 int k_start_tmp = (j == j_start) ? k_start + 1 : 0;
2404 int k_stop = (j == j_top) ? k_top : loop_ep->mesh.NbElmLayer[j];
2405 for(int k = k_start_tmp; k < k_stop; k++) {
2406 std::pair<unsigned int, unsigned int> pair_tmp(j, k);
2407 problems[(*itprob).first].insert(pair_tmp);
2408 }
2409 }
2410 }
2411 }
2412
2413 return 1;
2414 }
2415
2416 // Get set of locally fixed edges, forbidden edges, and all lateral surface
2417 // diagonals. Fixed edges include top surface diagonals and any lateral surface
2418 // diagonals that cannot be swapped. Added 2010-01-24
QuadToTriGetRegionDiags(GRegion * gr,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,MVertexRTree & pos)2419 static bool QuadToTriGetRegionDiags(
2420 GRegion *gr, std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
2421 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
2422 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags, MVertexRTree &pos)
2423 {
2424 ExtrudeParams *ep = gr->meshAttributes.extrude;
2425
2426 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) return false;
2427
2428 GModel *model = gr->model();
2429
2430 // find source face
2431 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
2432 if(!reg_source) {
2433 Msg::Error("In QuadToTriGetRegionDiags(), could not find source face "
2434 "%d for region %d.",
2435 std::abs(ep->geo.Source), gr->tag());
2436 return false;
2437 }
2438
2439 // Find a source surface, find a COPIED_ENTITY that is the top surface, or if
2440 // toroidal, find what is now the top
2441
2442 bool foundSource = false, foundTop = false, foundRoot = false;
2443 GFace *reg_top = NULL;
2444 GFace *root_face = NULL;
2445 std::vector<GFace *> faces = gr->faces();
2446 std::vector<GFace *>::iterator it = faces.begin();
2447
2448 // top faces in toroidal quadtri need special treatment
2449 bool is_toroidal = IsInToroidalQuadToTri(reg_source);
2450 if(is_toroidal) root_face = findRootSourceFaceForFace(reg_source);
2451
2452 for(it = faces.begin(); it != faces.end(); it++) {
2453 ExtrudeParams *face_tmp_ep = (*it)->meshAttributes.extrude;
2454 if((*it) == root_face) foundRoot = true;
2455 if((*it) == reg_source)
2456 foundSource = true;
2457 else if(face_tmp_ep && face_tmp_ep->geo.Mode == COPIED_ENTITY) {
2458 GFace *top_source_tmp =
2459 model->getFaceByTag(std::abs(face_tmp_ep->geo.Source));
2460 if(!top_source_tmp) {
2461 Msg::Error("In QuadToTriGetRegionDiags(), could not find source face "
2462 "%d for copied surface %d of region %d.",
2463 std::abs(face_tmp_ep->geo.Source), (*it)->tag(), gr->tag());
2464 }
2465 else if(top_source_tmp == reg_source) {
2466 foundTop = true;
2467 reg_top = (*it);
2468 }
2469 }
2470 }
2471
2472 if(!foundTop && is_toroidal && foundRoot && root_face != reg_source) {
2473 foundTop = true;
2474 reg_top = root_face;
2475 }
2476 if(!foundTop)
2477 Msg::Warning("In QuadToTriGetRegionDiags(), could not find top face "
2478 "for region %d.",
2479 gr->tag());
2480
2481 if(!foundSource) {
2482 Msg::Error("In QuadToTriGetRegionDiags(), source face "
2483 "for region %d is not found in region face list.",
2484 gr->tag());
2485 return false;
2486 }
2487
2488 // Get Fixed and adjustable lateral diagonal element edges
2489 int counter = 0;
2490 for(it = faces.begin(); it != faces.end(); it++) {
2491 counter++;
2492 // true if surface is a lateral...
2493 // ...The conditional is redundant for a reason.
2494 if((*it) != reg_top && (*it) != reg_source &&
2495 IsSurfaceALateralForRegion(gr, *it)) {
2496 // take care of forbidden edges
2497 // test whether this surface is a lateral bounded by two quadtri regions.
2498 // if so, and the other is not already meshed,
2499 // then don't make these forbidden. This is worked out in a
2500 // lateral remesh later
2501 std::vector<GRegion *> adj_regions;
2502 int numNeighbors = 0;
2503 numNeighbors = GetNeighborRegionsOfFace((*it), adj_regions);
2504 int ind_notcurrent = adj_regions[0] == gr ? 1 : 0;
2505 if(!(numNeighbors == 2 && adj_regions[0]->meshAttributes.extrude &&
2506 adj_regions[1]->meshAttributes.extrude &&
2507 adj_regions[0]->meshAttributes.extrude->mesh.ExtrudeMesh &&
2508 adj_regions[1]->meshAttributes.extrude->mesh.ExtrudeMesh &&
2509 adj_regions[0]->meshAttributes.extrude->geo.Mode ==
2510 EXTRUDED_ENTITY &&
2511 adj_regions[1]->meshAttributes.extrude->geo.Mode ==
2512 EXTRUDED_ENTITY &&
2513 adj_regions[0]->meshAttributes.extrude->mesh.QuadToTri &&
2514 adj_regions[1]->meshAttributes.extrude->mesh.QuadToTri &&
2515 IsSurfaceALateralForRegion(adj_regions[ind_notcurrent], *it) &&
2516 !adj_regions[ind_notcurrent]->getNumMeshElements())) {
2517 for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++) {
2518 std::vector<MVertex *> v;
2519 (*it)->quadrangles[i]->getVertices(v);
2520 createForbidden(v, forbidden_edges);
2521 }
2522 }
2523
2524 // at this point, if there are no triangles, continue
2525 if(!(*it)->triangles.size()) continue;
2526
2527 ExtrudeParams *face_ep_tmp = (*it)->meshAttributes.extrude;
2528 // If face is shared with a neighbor region that ALREADY has elements,
2529 // if face is the source of the neighbor, if the neighbor is TRANSFINITE,
2530 // or if the face is not an EXTRUDED_ENTITY, then these edges are fixed.
2531 std::vector<GRegion *> neighbors;
2532 GetNeighborRegionsOfFace(*it, neighbors);
2533 GRegion *other_region = NULL;
2534 ExtrudeParams *oth_ep = NULL;
2535 ExtrudeParams *face_ep = (*it)->meshAttributes.extrude;
2536 if(neighbors.size() > 1) {
2537 other_region = neighbors[0] != gr ? neighbors[0] : neighbors[1];
2538 oth_ep = other_region->meshAttributes.extrude;
2539 }
2540 bool is_fixed = false;
2541
2542 // see if neighbor has already been meshed
2543 if(other_region &&
2544 (other_region->pyramids.size() || other_region->tetrahedra.size() ||
2545 other_region->hexahedra.size() || other_region->prisms.size() ||
2546 other_region->polyhedra.size() ||
2547 other_region->getNumMeshElements())) {
2548 is_fixed = true;
2549 }
2550 // see if the diagonals are fixed for other reasons
2551 if(!(face_ep_tmp && face_ep_tmp->mesh.ExtrudeMesh &&
2552 face_ep_tmp->geo.Mode == EXTRUDED_ENTITY) ||
2553 (other_region && oth_ep && oth_ep->mesh.ExtrudeMesh &&
2554 (*it) == model->getFaceByTag(std::abs(oth_ep->geo.Source))) ||
2555 (other_region &&
2556 other_region->meshAttributes.method == MESH_TRANSFINITE)) {
2557 is_fixed = true;
2558 }
2559
2560 // Now extrude all vertices and find diagonal edges.
2561 // NOTE: This seems like the "hard way" because it is written to work
2562 // even if the original lateral for the region is replaced by another
2563 // structured surface. (first find common edge between source and this
2564 // lateral)
2565 std::vector<GEdge *> const &source_edges = reg_source->edges();
2566 std::vector<GEdge *> const &face_edges = (*it)->edges();
2567 std::vector<GEdge *>::const_iterator itse;
2568 GEdge *common = NULL;
2569 int common_count = 0;
2570 for(itse = source_edges.begin(); itse != source_edges.end(); itse++) {
2571 if(std::find(face_edges.begin(), face_edges.end(), (*itse)) !=
2572 face_edges.end()) {
2573 common = (*itse);
2574 common_count++;
2575 }
2576 }
2577 if(!common || common_count != 1)
2578 Msg::Error(
2579 "In QuadToTriGetRegionDiags(), lateral surface and "
2580 "source surface of region %d do not share one and only one edge.",
2581 gr->tag());
2582
2583 // now find face source edge, if it exists:
2584 GEdge *face_source = NULL;
2585 if(face_ep && face_ep->mesh.ExtrudeMesh &&
2586 face_ep->geo.Mode == EXTRUDED_ENTITY) {
2587 face_source = model->getEdgeByTag(std::abs(face_ep->geo.Source));
2588 if(!face_source) {
2589 Msg::Error("In QuadToTriGetRegionDiags(), extruded face %d "
2590 "has invalid source.",
2591 (*it)->tag());
2592 }
2593 }
2594 // set up vertex finding loops according to whether this surface
2595 // is extruded or not (alternative to extruded is unrecombined transfinite
2596 // surf with vertices coincidentally in the right place).
2597 unsigned int num_lines = 0;
2598 std::vector<MLine *> *source_lines = NULL;
2599 ExtrudeParams *loop_ep = NULL;
2600 if(face_source) {
2601 num_lines = face_source->lines.size();
2602 source_lines = &face_source->lines;
2603 loop_ep = face_ep;
2604 }
2605 else {
2606 num_lines = common->lines.size();
2607 source_lines = &common->lines;
2608 loop_ep = ep;
2609 }
2610 unsigned int index_guess = 0;
2611
2612 for(unsigned int i = 0; i < num_lines; i++) {
2613 MVertex *v0 = (*source_lines)[i]->getVertex(0);
2614 MVertex *v1 = (*source_lines)[i]->getVertex(1);
2615
2616 std::vector<MVertex *> verts;
2617 // test to see if this is a degenerate quad as triangle. If so,
2618 // continue.
2619 verts = getExtrudedLateralVertices(v0, v1, (*it), 0, 0, loop_ep, pos);
2620 if(verts[0] == verts[2] || verts[1] == verts[3]) {
2621 for(int p = 0; p < loop_ep->mesh.NbLayer; p++)
2622 index_guess += loop_ep->mesh.NbElmLayer[p];
2623 continue;
2624 }
2625 for(int j = 0; j < loop_ep->mesh.NbLayer; j++) {
2626 for(int k = 0; k < loop_ep->mesh.NbElmLayer[j]; k++) {
2627 verts.clear();
2628 verts =
2629 getExtrudedLateralVertices(v0, v1, (*it), j, k, loop_ep, pos);
2630
2631 // Find diagonal:
2632
2633 std::pair<int, int> diag(0, 0);
2634 diag = FindDiagonalEdgeIndices(verts, (*it), true, index_guess);
2635 if(diag.first || diag.second) {
2636 int add = diag.first < 2 ? 1 : -1;
2637 // have to test if conflicting edge already exists in
2638 // quadToTri_edges in case of global subdivide
2639 if(!edgeExists(verts[diag.first + add], verts[diag.second - add],
2640 quadToTri_edges)) {
2641 createEdge(verts[diag.first], verts[diag.second],
2642 lat_tri_diags);
2643 if(is_fixed)
2644 createEdge(verts[diag.first], verts[diag.second],
2645 quadToTri_edges);
2646 }
2647 else
2648 createEdge(verts[diag.first + add], verts[diag.second - add],
2649 lat_tri_diags);
2650 }
2651 else if(!(*it)->quadrangles.size())
2652 Msg::Error("In QuadToTriGetRegionDiags(), failed to find a "
2653 "diagonal in lateral surface %d.",
2654 (*it)->tag());
2655
2656 index_guess += 2;
2657 /*
2658
2659 std::vector<MVertex*> vface;
2660 vface.push_back(verts[0]);
2661 vface.push_back(verts[1]);
2662 vface.push_back(verts[3]);
2663 vface.push_back(verts[2]);
2664 if( k==2 && ( counter == 3 || counter == 3) ){
2665 if( k==0 || k ){
2666 createEdge( verts[1], verts[2], lat_tri_diags );
2667 createEdge( verts[1], verts[2], quadToTri_edges );
2668 }
2669 else{
2670 createEdge( verts[0], verts[3], lat_tri_diags );
2671 createEdge( verts[0], verts[3], quadToTri_edges );
2672 }
2673 }
2674 else if( k==2 && (counter == 5 || counter == 4 || counter == 6 ) ){
2675 createEdge( verts[0], verts[3], lat_tri_diags );
2676 createEdge( verts[0], verts[3], quadToTri_edges );
2677 }
2678 else if( k<2 )
2679 createForbidden(vface, forbidden_edges);
2680 else
2681 createEdge( elemEdge_tmp.first, elemEdge_tmp.second,
2682 lat_tri_diags );
2683 */
2684 }
2685 }
2686 }
2687 }
2688 }
2689
2690 // Insert diagonals of the top surface into quadToTri_edges;
2691 unsigned int index_guess = reg_source->triangles.size();
2692 if(reg_top->quadrangles.size() && !is_toroidal) {
2693 Msg::Error("In QuadToTriGetRegionDiags(), top surface of region "
2694 "%d has quads in a non-toroidal QuadToTri extrusion.",
2695 gr->tag());
2696 return false;
2697 }
2698
2699 for(unsigned int i = 0; i < reg_source->quadrangles.size(); i++) {
2700 int j_top = ep->mesh.NbLayer - 1;
2701 int k_top = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1];
2702 MElement *elem = reg_source->quadrangles[i];
2703 std::vector<MVertex *> verts;
2704 get2DExtrudedVertices(elem, ep, j_top, k_top, pos, verts);
2705 if(verts.size() != 4) break;
2706 if(!is_toroidal) {
2707 // Find diagonal:
2708 std::pair<int, int> diag(0, 0);
2709 diag = FindDiagonalEdgeIndices(verts, reg_top, false, index_guess);
2710 if(diag.first || diag.second)
2711 createEdge(verts[diag.first], verts[diag.second], quadToTri_edges);
2712 else
2713 Msg::Error("In QuadToTriGetRegionDiags(), failed to find a diagonal on "
2714 "top surface %d, but should have.",
2715 reg_top->tag());
2716 index_guess += 2;
2717 }
2718 else
2719 createForbidden(verts, forbidden_edges);
2720 }
2721 return true;
2722 }
2723
2724 // For use in QuadToTriEdgeGenerator: Controls BRUTE FORCE edging of elements
2725 // with ALL vertices on a lateral boundary surface. Added 04/08/2011
makeEdgesForElemsWithAllVertsOnBnd(GRegion * gr,bool is_addverts,CategorizedSourceElements & cat_src_elems,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,MVertexRTree & pos)2726 static int makeEdgesForElemsWithAllVertsOnBnd(
2727 GRegion *gr, bool is_addverts, CategorizedSourceElements &cat_src_elems,
2728 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
2729 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
2730 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
2731 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
2732 &problems,
2733 MVertexRTree &pos)
2734 {
2735 ExtrudeParams *ep = gr->meshAttributes.extrude;
2736
2737 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
2738 Msg::Error("In makeEdgesForElemsWithAllVertsOnBnd(), invalid extrusion "
2739 "in region %d for performing QuadToTri mesh generation.",
2740 gr->tag());
2741 return 0;
2742 }
2743
2744 GModel *model = gr->model();
2745 if(!model) {
2746 Msg::Error(
2747 "In makeEdgesForElemsWithAllVertsOnBnd(), invalid model for region "
2748 "%d.",
2749 gr->tag());
2750 return 0;
2751 }
2752
2753 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
2754 if(!reg_source) {
2755 Msg::Error(
2756 "In makeEdgesForElemsWithAllVertsOnBnd(), invalid source face for region "
2757 "%d.",
2758 gr->tag());
2759 return 0;
2760 }
2761
2762 if(gr != cat_src_elems.region || reg_source != cat_src_elems.source_face) {
2763 Msg::Error(
2764 "In makeEdgesForElemsWithAllVertsOnBnd(), too many elements in the "
2765 "CategorizedSourceElements structure for given source face %d.",
2766 reg_source->tag());
2767 return 0;
2768 }
2769
2770 // find edge verts of source face
2771 MVertexRTree pos_src_edge(CTX::instance()->geom.tolerance *
2772 CTX::instance()->lc);
2773 QuadToTriInsertFaceEdgeVertices(reg_source, pos_src_edge);
2774
2775 // while Loop to diagonalize 3-boundary point triangles and 4-boundary point
2776 // quadrangles:
2777 bool finish_all_tri = false;
2778 // can afford temporary copies since these sets should be relatively small for
2779 // all cases.
2780 std::set<unsigned int> tri_tmp, quad_tmp;
2781 tri_tmp = cat_src_elems.three_bnd_pt_tri;
2782 // if is_addverts, then don't track the quads
2783 if(!is_addverts) quad_tmp = cat_src_elems.four_bnd_pt_quad;
2784
2785 while(tri_tmp.size() || quad_tmp.size()) {
2786 std::set<unsigned int>::iterator it;
2787 std::vector<unsigned int> done;
2788 // 3 bnd point triangle and 4 bnd point quad loop
2789 // s = 0 for triangles, s = 1 for quads
2790 for(int s = 0; s < 2; s++) {
2791 std::set<unsigned int> *set_elems;
2792 std::set<std::pair<MVertex *, MVertex *> > edges_new, forbidden_new;
2793 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
2794 problems_new;
2795 if(!s)
2796 set_elems = &tri_tmp;
2797 else
2798 set_elems = &quad_tmp;
2799
2800 for(it = set_elems->begin(); it != set_elems->end(); it++) {
2801 MElement *elem;
2802 if(!s)
2803 elem = reg_source->triangles[(*it)];
2804 else
2805 elem = reg_source->quadrangles[(*it)];
2806
2807 // int elem_size = elem->getNumVertices();
2808
2809 std::vector<bool> vert_bnd;
2810 if(!s)
2811 vert_bnd.insert(vert_bnd.begin(),
2812 cat_src_elems.tri_bool.begin() + (4 * (*it) + 1),
2813 cat_src_elems.tri_bool.begin() + (4 * (*it) + 4));
2814 else
2815 vert_bnd.insert(vert_bnd.begin(),
2816 cat_src_elems.quad_bool.begin() + (5 * (*it) + 1),
2817 cat_src_elems.quad_bool.begin() + (5 * (*it) + 5));
2818 int j_start, k_start;
2819
2820 // if has lat_tri_diags or is a rotation with quads (may have to do the
2821 // one point bnd quads to get rid of degenerate hexahedra...they are
2822 // invalid mesh volumes.
2823 if(lat_tri_diags.size() ||
2824 (reg_source->quadrangles.size() &&
2825 (ep->geo.Type == ROTATE || ep->geo.Type == TRANSLATE_ROTATE))) {
2826 j_start = 0;
2827 k_start = 0;
2828 }
2829 else {
2830 j_start = ep->mesh.NbLayer - 1;
2831 k_start = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1;
2832 }
2833
2834 int num_levels = 0;
2835 if(lat_tri_diags.size() ||
2836 (reg_source->quadrangles.size() &&
2837 (ep->geo.Type == ROTATE || ep->geo.Type == TRANSLATE_ROTATE))) {
2838 for(int p = 0; p < ep->mesh.NbLayer; p++)
2839 num_levels += ep->mesh.NbElmLayer[p];
2840 }
2841 else
2842 num_levels = 1;
2843
2844 int num_edged = 0;
2845
2846 // before starting extrusion loop, get the node numbers for the diagonal
2847 // in the region's top surface extruded from this source element
2848 // (will need this below)
2849 std::vector<MVertex *> verts_top;
2850 int ntop1 = -1, ntop2 = -2;
2851 getExtrudedVertices(elem, ep, ep->mesh.NbLayer - 1,
2852 ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1, pos,
2853 verts_top);
2854 if(edgeExists(verts_top[4], verts_top[6], quadToTri_edges)) {
2855 ntop1 = 4;
2856 ntop2 = 6;
2857 }
2858 else if(edgeExists(verts_top[5], verts_top[7], quadToTri_edges)) {
2859 ntop1 = 5;
2860 ntop2 = 7;
2861 }
2862
2863 // start extrusion loop
2864 for(int j = j_start; j < ep->mesh.NbLayer; j++) {
2865 int k_start_tmp, k_stop;
2866 if(j == j_start)
2867 k_start_tmp = k_start;
2868 else
2869 k_start_tmp = 0;
2870 k_stop = ep->mesh.NbElmLayer[j];
2871 for(int k = k_start_tmp; k < k_stop; k++) {
2872 std::vector<MVertex *> verts;
2873 std::map<std::string, std::vector<int> > face_types;
2874 std::vector<int> nfix1, nfix2, nadj1, nadj2, free_flag;
2875 int vert_num = getExtrudedVertices(elem, ep, j, k, pos, verts);
2876 // if just starting and there is no diagonal on the bottom edge,
2877 // forbid it (this prevents some conflicts)
2878 if(vert_num == 8 && j == j_start && k == k_start &&
2879 !edgeExists(verts[0], verts[2], quadToTri_edges) &&
2880 !edgeExists(verts[1], verts[3], quadToTri_edges)) {
2881 std::vector<MVertex *> v_bot;
2882 v_bot.assign(4, (MVertex *)(0));
2883 v_bot[0] = verts[0];
2884 v_bot[1] = verts[1];
2885 v_bot[2] = verts[2];
2886 v_bot[3] = verts[3];
2887 createForbidden(v_bot, forbidden_edges);
2888 createForbidden(v_bot, forbidden_new);
2889 }
2890
2891 if(!s) {
2892 face_types = getFaceTypes(
2893 gr, elem, j, k, verts, quadToTri_edges, forbidden_edges,
2894 lat_tri_diags, vert_bnd, nfix1, nfix2, nadj1, nadj2, free_flag);
2895
2896 if(finish_all_tri ||
2897 face_types["free"].size() + face_types["adj_diag"].size() <
2898 2) {
2899 bruteForceEdgeQuadToTriPrism(
2900 gr, elem, j, k, verts, face_types, edges_new, forbidden_new,
2901 quadToTri_edges, forbidden_edges, lat_tri_diags, problems_new,
2902 problems, nfix1, nfix2, nadj1, nadj2, free_flag);
2903 // IMPORTANT
2904 num_edged++;
2905 }
2906 }
2907 else if(s) {
2908 // a loop to try to force the top diagonal to align with the
2909 // diagonal in the region's top surface above this element.
2910 bool changed_diag = false;
2911 for(int g = 0; g < 2; g++) {
2912 // on first step, see if can put in the aligned top diagonal
2913 if(g == 0 && ntop1 >= 0 && ntop2 >= 0 &&
2914 !edgeExists(verts[4], verts[6], quadToTri_edges) &&
2915 !edgeExists(verts[5], verts[7], quadToTri_edges)) {
2916 std::vector<MVertex *> v_top;
2917 v_top.assign(4, (MVertex *)(0));
2918 v_top[0] = verts[4];
2919 v_top[1] = verts[5];
2920 v_top[2] = verts[6];
2921 v_top[3] = verts[7];
2922 if(!forbiddenExists(v_top, forbidden_edges)) {
2923 changed_diag = true;
2924 createEdge(verts[ntop1], verts[ntop2], quadToTri_edges);
2925 }
2926 }
2927 // on second step, see if a problem was created from inserting
2928 // aligned diagonal if so, remove it and try again.
2929 std::pair<unsigned int, unsigned int> jk_pair(j, k);
2930 if(g == 1 && changed_diag &&
2931 problems[elem].find(jk_pair) != problems[elem].end()) {
2932 problems[elem].erase(jk_pair);
2933 if(!problems[elem].size()) problems.erase(elem);
2934 deleteEdge(verts[ntop1], verts[ntop2], quadToTri_edges);
2935 }
2936 else if(g == 1) {
2937 if(!problems[elem].size()) problems.erase(elem);
2938 break;
2939 }
2940 face_types =
2941 getFaceTypes(gr, elem, j, k, verts, quadToTri_edges,
2942 forbidden_edges, lat_tri_diags, vert_bnd, nfix1,
2943 nfix2, nadj1, nadj2, free_flag);
2944
2945 bruteForceEdgeQuadToTriHexa(
2946 gr, elem, j, k, verts, face_types, edges_new, forbidden_new,
2947 quadToTri_edges, forbidden_edges, lat_tri_diags, problems_new,
2948 problems, nfix1, nfix2, nadj1, nadj2, free_flag);
2949 }
2950 // IMPORTANT
2951 num_edged++;
2952 }
2953 }
2954 }
2955
2956 // IMPORTANT
2957 if(num_edged == num_levels) done.push_back((*it));
2958
2959 // If quads, break ( only do quads one at a time so can keep 3 bnd point
2960 // triangles from being trapped so that they can't be meshed without an
2961 // internal vertex)
2962 if(s) break;
2963
2964 } // end loop over set of element indices (tri or quad)
2965
2966 // remove elements that are done from list
2967 if(done.size()) {
2968 for(unsigned int i = 0; i < done.size(); i++) set_elems->erase(done[i]);
2969 done.clear();
2970 }
2971
2972 // skip over quads on this iteration if some modifications were made to
2973 // triangle extrusion elements ( need to make sure no triangles left with
2974 // only one adjustable or free face or else
2975 // meshing a quad might trap them so they can't be meshed without
2976 // internal vertex ).
2977 if(!s && (edges_new.size() || problems_new.size())) s = -1;
2978
2979 // if quads are done, finish all triangles
2980 if(!quad_tmp.size()) finish_all_tri = true;
2981
2982 } // end of s-loop to choose between tri or quad
2983
2984 } // end of while
2985
2986 return 1;
2987 }
2988
2989 // For use in QuadToTriEdgeGenerator: Does the edging of prisms with some but
2990 // not all vertices on a lateral boundary surface. Added 04/08/2011
makeEdgesForOtherBndPrisms(GRegion * gr,bool is_addverts,CategorizedSourceElements & cat_src_elems,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,MVertexRTree & pos)2991 static int makeEdgesForOtherBndPrisms(
2992 GRegion *gr, bool is_addverts, CategorizedSourceElements &cat_src_elems,
2993 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
2994 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
2995 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
2996 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
2997 &problems,
2998 MVertexRTree &pos)
2999 {
3000 ExtrudeParams *ep = gr->meshAttributes.extrude;
3001
3002 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
3003 Msg::Error("In makeEdgesForOtherBndPrisms(), invalid extrusion "
3004 "in region %d for performing QuadToTri mesh generation.",
3005 gr->tag());
3006 return 0;
3007 }
3008
3009 GModel *model = gr->model();
3010 if(!model) {
3011 Msg::Error("In makeEdgesForOtherBndPrisms(), invalid model for region "
3012 "%d.",
3013 gr->tag());
3014 return 0;
3015 }
3016
3017 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
3018 if(!reg_source) {
3019 Msg::Error(
3020 "In makeEdgesForOtherBndPrisms(), invalid source face for region "
3021 "%d.",
3022 gr->tag());
3023 return 0;
3024 }
3025
3026 if(gr != cat_src_elems.region || reg_source != cat_src_elems.source_face) {
3027 Msg::Error("In makeEdgesForOtherBndPrisms(), CatergorizedSourceElements "
3028 "structure data "
3029 " does not correspond to region %d.",
3030 reg_source->tag());
3031 return 0;
3032 }
3033
3034 std::set<unsigned int>::iterator it;
3035
3036 // skip the whole thing if this is true...NO reason to divide!!
3037 if(!lat_tri_diags.size() && !reg_source->quadrangles.size()) return 1;
3038
3039 // edge other_bnd triangles loop
3040 // (draw from boundaries up toward interior)
3041 for(it = cat_src_elems.other_bnd_tri.begin();
3042 it != cat_src_elems.other_bnd_tri.end(); it++) {
3043 std::set<std::pair<MVertex *, MVertex *> > edges_new, forbidden_new;
3044 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
3045 problems_new;
3046 std::vector<MVertex *> verts;
3047 std::vector<bool> vert_bnd;
3048 MElement *elem = reg_source->triangles[(*it)];
3049 int elem_size = elem->getNumVertices();
3050
3051 vert_bnd.insert(vert_bnd.begin(),
3052 cat_src_elems.tri_bool.begin() + (4 * (*it) + 1),
3053 cat_src_elems.tri_bool.begin() + (4 * (*it) + 4));
3054
3055 int j_start, k_start;
3056 // if has lat_tri_diags or is a rotation with quads (may have to do the one
3057 // point bnd quads to get rid of degenerate hexahedra...they are invalid
3058 // mesh volumes.
3059 if(lat_tri_diags.size() ||
3060 (reg_source->quadrangles.size() &&
3061 (ep->geo.Type == ROTATE || ep->geo.Type == TRANSLATE_ROTATE))) {
3062 j_start = 0;
3063 k_start = 0;
3064 }
3065 else {
3066 j_start = ep->mesh.NbLayer - 1;
3067 k_start = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1;
3068 }
3069
3070 // add edges
3071 for(int j = j_start; j < ep->mesh.NbLayer; j++) {
3072 int k_start_tmp, k_stop;
3073 if(j == j_start)
3074 k_start_tmp = k_start;
3075 else
3076 k_start_tmp = 0;
3077 k_stop = ep->mesh.NbElmLayer[j];
3078 for(int k = k_start_tmp; k < k_stop; k++) {
3079 std::vector<MVertex *> verts;
3080 getExtrudedVertices(elem, ep, j, k, pos, verts);
3081 // NOTE: bnd_face might not really be a bnd_face, but the code takes
3082 // that into account below.
3083 for(int p = 0; p < elem_size; p++) {
3084 if(vert_bnd[p]) {
3085 bool degen = false;
3086 int p2 = (p + 1) % elem_size;
3087 int p3 = (p + elem_size - 1) % elem_size;
3088 // test for degeneracy
3089 if(verts[p] == verts[p + elem_size]) degen = true;
3090 if(!degen && !vert_bnd[p2]) {
3091 createEdge(verts[p], verts[p2 + elem_size], quadToTri_edges);
3092 createEdge(verts[p], verts[p2 + elem_size], edges_new);
3093 }
3094 if(!degen && !vert_bnd[p3]) {
3095 createEdge(verts[p], verts[p3 + elem_size], quadToTri_edges);
3096 createEdge(verts[p], verts[p3 + elem_size], edges_new);
3097 }
3098
3099 // make the adjustable diag (if it exists)
3100 // Note: p might not really be a bnd face, but this takes that into
3101 // account
3102 if(edgeExists(verts[p], verts[p2 + elem_size], lat_tri_diags) &&
3103 !edgeExists(verts[p + elem_size], verts[p2], quadToTri_edges))
3104 createEdge(verts[p], verts[p2 + elem_size], quadToTri_edges);
3105 else if(edgeExists(verts[p + elem_size], verts[p2],
3106 lat_tri_diags) &&
3107 !edgeExists(verts[p], verts[p2 + elem_size],
3108 quadToTri_edges))
3109 createEdge(verts[p + elem_size], verts[p2], quadToTri_edges);
3110 }
3111 }
3112 }
3113 }
3114
3115 } // end of boundary triangles loop
3116
3117 return 1;
3118 }
3119
3120 // For use in QuadToTriEdgeGenerator: Does the edging of hexahedra with some
3121 // but not all vertices on a lateral boundary surface. Added 04/08/2011
makeEdgesForOtherBndHexa(GRegion * gr,bool is_addverts,CategorizedSourceElements & cat_src_elems,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,MVertexRTree & pos)3122 static int makeEdgesForOtherBndHexa(
3123 GRegion *gr, bool is_addverts, CategorizedSourceElements &cat_src_elems,
3124 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
3125 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
3126 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
3127 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
3128 &problems,
3129 MVertexRTree &pos)
3130 {
3131 // Edge creation for extruded quadrangles with some but not all vertices on a
3132 // boundary. (draw from boundaries up toward interior) If no lat_tri_diags,
3133 // just do the top layer. Follow these instructions: For 2 pts on boundary:
3134 // Below top level, draw top diags only if there are lat_tri_diags touching
3135 // elemnt or if element is not adjacent to any boundary (only touching it)
3136 // Order of precedence in top edging decisions:
3137 // a. if bottom diag exists, align top diagonal to it.
3138 // b. if bnd vertices are on diametrically opposite corners, draw top diag
3139 // NOT on them. c. if lateral surface diagonals touch element, connect to
3140 // one at some vertex. d. otherwise, don't draw top diagonal.
3141 // In any case, draw the lateral edges up from bottom boundary vertices
3142 // toward interior. At top level, top diagonals should already be done in 2D
3143 // mesh step.
3144 // For 3 pts on boundary:
3145 // Draw the top diag from corner inward. Draw laterals up from bottom
3146 // boundary vertices.
3147 // For 1 point on boundary:
3148 // Draw lateral diagonals up from bottom boundary verts, draw top diagonal
3149 // completely off the boundary. Draw diagonals toward the "pivot vertex",
3150 // which is the vertex in the top element face diametrically opposite to the
3151 // top face vertex on the boundary. Actually, this is only done in the first
3152 // two extrusion layers, if those layers are not in the top layer. In first
3153 // layer, draw diags to the top vertex diametrically opposite bnd vert, in
3154 // the second layer, draw them down to the bottom such vertex. Same vertex
3155 // for both layers. This is done to ensure that a one pt boundary quad in an
3156 // unstructured surface can be divided without dividing the entire bottom
3157 // layer. If two pivot vertices share an edge and these rules would conflict,
3158 // just give precedence to the first pivot vertex encountered in the edging
3159 // loop (this works, yes). Also, the internal elements touching this pivot
3160 // vertex should have diagonals drawn to pivot vertex as well (done in
3161 // different function, with same precedence).
3162
3163 ExtrudeParams *ep = gr->meshAttributes.extrude;
3164
3165 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
3166 Msg::Error("In makeEdgesForOtherBndHexa(), invalid extrusion "
3167 "in region %d for performing QuadToTri mesh generation.",
3168 gr->tag());
3169 return 0;
3170 }
3171
3172 GModel *model = gr->model();
3173 if(!model) {
3174 Msg::Error("In makeEdgesForOtherBndHexa(), invalid model for region "
3175 "%d.",
3176 gr->tag());
3177 return 0;
3178 }
3179
3180 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
3181 if(!reg_source) {
3182 Msg::Error("In makeEdgesForOtherBndHexa(), invalid source face for region "
3183 "%d.",
3184 gr->tag());
3185 return 0;
3186 }
3187
3188 if(gr != cat_src_elems.region || reg_source != cat_src_elems.source_face) {
3189 Msg::Error("In makeEdgesForOtherBndHexa(), CatergorizedSourceElements "
3190 "structure data "
3191 " does not correspond to region %d.",
3192 reg_source->tag());
3193 return 0;
3194 }
3195
3196 std::set<unsigned int>::iterator it;
3197
3198 for(it = cat_src_elems.other_bnd_quad.begin();
3199 it != cat_src_elems.other_bnd_quad.end(); it++) {
3200 MElement *elem = reg_source->quadrangles[(*it)];
3201 const int elem_size = elem->getNumVertices();
3202
3203 std::vector<bool> vert_bnd;
3204 vert_bnd.insert(vert_bnd.begin(),
3205 cat_src_elems.quad_bool.begin() + (5 * (*it) + 1),
3206 cat_src_elems.quad_bool.begin() + (5 * (*it) + 5));
3207
3208 // these locally hold each added edge/problem element temporarily
3209 std::set<std::pair<MVertex *, MVertex *> > edges_new;
3210 std::set<std::pair<MVertex *, MVertex *> > forbidden_new;
3211 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
3212 problems_new;
3213
3214 // Get a count of bnd verts. If one point, that index will be in
3215 // one_point_ind. For 3 bnd vert quads record the empty spot in 'skip.'
3216 int bnd_count = 0, skip = 0, one_point_ind = 0;
3217 for(int s = 0; s < elem_size; s++) {
3218 if(vert_bnd[s]) {
3219 bnd_count++;
3220 one_point_ind = s;
3221 }
3222 else
3223 skip = s;
3224 }
3225
3226 int j_start, k_start;
3227
3228 // if has lat_tri_diags or is a rotation with quads (may have to do the one
3229 // point bnd quads to get rid of degenerate hexahedra...they are invalid
3230 // mesh volumes.
3231 if(lat_tri_diags.size() ||
3232 (reg_source->quadrangles.size() &&
3233 (ep->geo.Type == ROTATE || ep->geo.Type == TRANSLATE_ROTATE))) {
3234 j_start = 0;
3235 k_start = 0;
3236 }
3237 else {
3238 j_start = ep->mesh.NbLayer - 1;
3239 k_start = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1;
3240 }
3241
3242 // start of top layer
3243 int j_top_start = ep->mesh.NbLayer - 1;
3244 int k_top_start = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1;
3245
3246 // add edges:
3247
3248 // lateral diags in any case
3249 for(int j = j_start; j < ep->mesh.NbLayer; j++) {
3250 int k_start_tmp, k_stop;
3251 if(j == j_start)
3252 k_start_tmp = k_start;
3253 else
3254 k_start_tmp = 0;
3255 k_stop = ep->mesh.NbElmLayer[j];
3256 for(int k = k_start_tmp; k < k_stop; k++) {
3257 std::vector<MVertex *> verts;
3258 getExtrudedVertices(elem, ep, j, k, pos, verts);
3259 // int bnd_face = -1;
3260 // NOTE: bnd_face might not really be a bnd_face, but don't worry--it
3261 // won't be diagonalized if not
3262 for(int p = 0; p < elem_size; p++) {
3263 int p2 = (p + 1) % elem_size;
3264 int p3 = (p + elem_size - 1) % elem_size;
3265 if(vert_bnd[p]) {
3266 bool degen = false;
3267 // test for degeneracy
3268 if(verts[p] == verts[p + elem_size]) degen = true;
3269 if(!degen && !vert_bnd[p2])
3270 createEdge(verts[p], verts[p2 + elem_size], quadToTri_edges);
3271 if(!degen && !vert_bnd[p3])
3272 createEdge(verts[p], verts[p3 + elem_size], quadToTri_edges);
3273
3274 // make the adjustable diag
3275 if(edgeExists(verts[p], verts[p2 + elem_size], lat_tri_diags) &&
3276 !edgeExists(verts[p + elem_size], verts[p2], quadToTri_edges))
3277 createEdge(verts[p], verts[p2 + elem_size], quadToTri_edges);
3278 else if(edgeExists(verts[p + elem_size], verts[p2],
3279 lat_tri_diags) &&
3280 !edgeExists(verts[p], verts[p2 + elem_size],
3281 quadToTri_edges))
3282 createEdge(verts[p + elem_size], verts[p2], quadToTri_edges);
3283 }
3284 // if face not on boundary and in top layer...make an internal lateral
3285 else if(j == j_top_start && k == k_top_start && !vert_bnd[p2]) {
3286 int ind_low, ind2;
3287 if(verts[p + elem_size] < verts[p2 + elem_size]) {
3288 ind_low = p + elem_size;
3289 ind2 = p2;
3290 }
3291 else {
3292 ind_low = p2 + elem_size;
3293 ind2 = p;
3294 }
3295 createEdge(verts[ind_low], verts[ind2], quadToTri_edges);
3296 }
3297 }
3298 }
3299 }
3300
3301 // Add top diags for 2-bnd point quads, below top layer
3302 // If there is a lateral diagonal, connect to it. If this is a two boundary
3303 // point quad that is not adjacent to a boundary (it is possible), draw
3304 // diagonal completely off boundary. If neither of the above, don't make the
3305 // top diagonal. IF degenerate, be careful.
3306 if(bnd_count == 2) {
3307 for(int j = j_start; j < ep->mesh.NbLayer; j++) {
3308 int k_start_tmp, k_stop;
3309 if(j == j_start)
3310 k_start_tmp = k_start;
3311 else
3312 k_start_tmp = 0;
3313 k_stop = ep->mesh.NbElmLayer[j];
3314 if(j == ep->mesh.NbLayer - 1) k_stop--; // dont go to top elements
3315 for(int k = k_start_tmp; k < k_stop; k++) {
3316 std::vector<MVertex *> verts;
3317 getExtrudedVertices(elem, ep, j, k, pos, verts);
3318 int p1 = -1, p2 = -1;
3319 for(int p = 0; p < elem_size; p++) {
3320 if(vert_bnd[p]) {
3321 p1 = p;
3322 int p2_tmp = (p + 1) % elem_size;
3323 int p3_tmp = (p + elem_size - 1) % elem_size;
3324 if(vert_bnd[p2_tmp]) {
3325 p1 = p;
3326 p2 = p2_tmp;
3327 }
3328 else if(vert_bnd[p3_tmp]) {
3329 p1 = p3_tmp;
3330 p2 = p;
3331 }
3332 break;
3333 }
3334 }
3335 if(p2 < 0 && p1 < 0) {
3336 Msg::Error(
3337 "In makeEdgesForOtherBndHexa(), error finding boundary points on "
3338 "2-boundary point quadrangle.");
3339 break;
3340 }
3341
3342 // First, if bottom diagonal exists, align top diagonal to it,
3343 // OTHERWISE:
3344 // if bnd verts oppose each other, draw top diagonal off boundary
3345 // otherwise, connect to any existing lateral diagonal either
3346 // totally ON boundary or totally OFF, but ONLY draw top diag if
3347 // such a lateral exists
3348 // NOTE: since laterals were added first, any lat_tri_diag here is in
3349 // quadToTri_edges already
3350 if(edgeExists(verts[0], verts[2], quadToTri_edges))
3351 createEdge(verts[4], verts[6], quadToTri_edges);
3352 else if(edgeExists(verts[1], verts[3], quadToTri_edges))
3353 createEdge(verts[5], verts[7], quadToTri_edges);
3354 else if(p2 < 0 && p1 >= 0)
3355 createEdge(verts[(p1 + 1) % elem_size + elem_size],
3356 verts[(p1 + elem_size - 1) % elem_size + elem_size],
3357 quadToTri_edges);
3358 else if(p1 >= 0 &&
3359 edgeExists(verts[p1], verts[p2 + elem_size], quadToTri_edges))
3360 createEdge(verts[p2 + elem_size],
3361 verts[(p2 + 2) % elem_size + elem_size],
3362 quadToTri_edges);
3363 else if(p1 >= 0 &&
3364 edgeExists(verts[p1 + elem_size], verts[p2], quadToTri_edges))
3365 createEdge(verts[p1 + elem_size],
3366 verts[(p1 + 2) % elem_size + elem_size],
3367 quadToTri_edges);
3368 }
3369 }
3370 }
3371
3372 // top diags on corner quad ( I *think* this always works for non-degen
3373 // stuff, even for free wall spanning cases )
3374 else if(bnd_count == 3 &&
3375 (j_start < j_top_start || k_start < k_top_start)) {
3376 std::vector<MVertex *> verts;
3377 getExtrudedVertices(elem, ep, j_start, k_start, pos, verts);
3378 problems_new.clear();
3379 forbidden_new.clear();
3380 edges_new.clear();
3381 createEdge(verts[skip + elem_size],
3382 verts[(skip + 2) % elem_size + elem_size], quadToTri_edges);
3383 createEdge(verts[skip + elem_size],
3384 verts[(skip + 2) % elem_size + elem_size], edges_new);
3385 // copies the diags in edges_new on up the extrusion but NOT on the top
3386 // surface
3387 ExtrudeDiags(gr, verts, j_start, k_start, j_top_start, k_top_start, elem,
3388 ep, edges_new, forbidden_new, quadToTri_edges,
3389 forbidden_edges, problems_new, problems, pos);
3390 }
3391
3392 // finally, top diagonal and other two laterals for 1 bnd point quad
3393 else if(bnd_count == 1 &&
3394 (j_start < j_top_start || k_start < k_top_start)) {
3395 std::vector<MVertex *> verts;
3396 getExtrudedVertices(elem, ep, j_start, k_start, pos, verts);
3397 int p = one_point_ind;
3398
3399 // to determine if there is a second level above the start but not in top
3400 // layer
3401 int j_next, k_next;
3402 bool divide_next = false;
3403 std::vector<MVertex *> verts_next;
3404 if(ep->mesh.NbElmLayer[0] > 1) {
3405 j_next = 0;
3406 k_next = 1;
3407 }
3408 else {
3409 j_next = std::min(ep->mesh.NbLayer - 1, 1);
3410 k_next = 0;
3411 }
3412 if(j_next < j_top_start || k_next < k_top_start) {
3413 divide_next = true;
3414 getExtrudedVertices(elem, ep, j_next, k_next, pos, verts_next);
3415 }
3416
3417 problems_new.clear();
3418 forbidden_new.clear();
3419 edges_new.clear();
3420
3421 // Create the laterals that run up to the "pivot vertex" for this 1 bnd pt
3422 // quad Other internal elements have diagonals converging on the pivot
3423 // index. Only create it if there is NO conflicting diagonal in place,
3424 // though--this CAN happen, but this approach still give no conflicts.
3425 // NOTE THAT THESE ARE NOT PROPAGATED UP THE ENTIRE EXTRUSION.
3426 int p2 = (p + 1) % elem_size;
3427 int p3 = (p + 2) % elem_size;
3428 int p4 = (p + elem_size - 1) % elem_size;
3429 if(!edgeExists(verts[p2 + elem_size], verts[p3], quadToTri_edges))
3430 createEdge(verts[p2], verts[p3 + elem_size], quadToTri_edges);
3431 if(!edgeExists(verts[p4 + elem_size], verts[p3], quadToTri_edges))
3432 createEdge(verts[p4], verts[p3 + elem_size], quadToTri_edges);
3433
3434 // if there is a second extrusion level not on the top layer, add the
3435 // 'reverse' of the previous two diagonals
3436 if(divide_next && !edgeExists(verts_next[p2], verts_next[p3 + elem_size],
3437 quadToTri_edges))
3438 createEdge(verts_next[p2 + elem_size], verts_next[p3], quadToTri_edges);
3439 if(divide_next && !edgeExists(verts_next[p4], verts_next[p3 + elem_size],
3440 quadToTri_edges))
3441 createEdge(verts_next[p4 + elem_size], verts_next[p3], quadToTri_edges);
3442
3443 // If this quad has degenerate vertex on boundary and the pivot laterals
3444 // run to the top pivot vertex and there is no pre-existing top diagonal
3445 // on other two vertices, draw diagonal out to the pivot_vertex.
3446 // Otherwise, draw it on the other two verts
3447 if(verts[p] == verts[p + elem_size] &&
3448 (edgeExists(verts[p2], verts[p3 + elem_size], quadToTri_edges) ||
3449 verts[p2] == verts[p2 + elem_size]) &&
3450 (edgeExists(verts[p4], verts[p3 + elem_size], quadToTri_edges) ||
3451 verts[p4] == verts[p4 + elem_size]) &&
3452 !edgeExists(verts[p2 + elem_size], verts[p4 + elem_size],
3453 quadToTri_edges)) {
3454 createEdge(verts[p + elem_size], verts[p3 + elem_size],
3455 quadToTri_edges);
3456 createEdge(verts[p + elem_size], verts[p3 + elem_size], edges_new);
3457 }
3458 else {
3459 createEdge(verts[p2 + elem_size], verts[p4 + elem_size],
3460 quadToTri_edges);
3461 createEdge(verts[p2 + elem_size], verts[p4 + elem_size], edges_new);
3462 }
3463 // copies the top diag in edges_new up the extrusion but NOT to the top
3464 // surface of region.
3465 ExtrudeDiags(gr, verts, 0, 0, j_top_start, k_top_start, elem, ep,
3466 edges_new, forbidden_new, quadToTri_edges, forbidden_edges,
3467 problems_new, problems, pos);
3468 }
3469
3470 } // end of boundary quadrangles loop
3471
3472 return 1;
3473 }
3474
3475 // For use in QuadToTriEdgeGenerator: Does the lateral edging of internal
3476 // elements that touch a pivot vertex of a hexahedral element that has ONE
3477 // SOURCE vertex on a lateral boundary surface. See inside function for a
3478 // definition of the "pivot vertex." Added 04/08/2011
makeEdgesForElemsTouchPivotVert(GRegion * gr,bool is_addverts,CategorizedSourceElements & cat_src_elems,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::set<std::pair<MVertex *,MVertex * >> & forbidden_edges,MVertexRTree & pos)3479 static int makeEdgesForElemsTouchPivotVert(
3480 GRegion *gr, bool is_addverts, CategorizedSourceElements &cat_src_elems,
3481 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
3482 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
3483 std::set<std::pair<MVertex *, MVertex *> > &forbidden_edges,
3484 MVertexRTree &pos)
3485 {
3486 // Draw diagonals toward the "pivot vertex" of a hexahedron whose source quad
3487 // has only one vertex on a lateral boundary. Actually, this is only done in
3488 // the first two extrusion layers, if those layers are not in the top layer.
3489 // The pivot vertex is the vertex in the top face of the element that is
3490 // diametrically opposite the top vertex on the lateral boundary of the
3491 // region. In first layer, draw diags up from bottom to the pivot vertex, in
3492 // the second layer, draw them down to pivot vertex. Same vertex for both
3493 // layers. This is done to ensure that a one pt boundary quad in an
3494 // unstructured surface can be divided without dividing the entire bottom
3495 // layer. If two pivot vertices share an edge and these rules would conflict,
3496 // just give precedence to the first pivot vertex encountered in the edging
3497 // loop (this works, yes).
3498
3499 ExtrudeParams *ep = gr->meshAttributes.extrude;
3500
3501 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
3502 Msg::Error("In makeEdgesForElemsTouchPivotVert(), invalid extrusion "
3503 "in region %d for performing QuadToTri mesh generation.",
3504 gr->tag());
3505 return 0;
3506 }
3507
3508 GModel *model = gr->model();
3509 if(!model) {
3510 Msg::Error("In makeEdgesForElemsTouchPivotVert(), invalid model for region "
3511 "%d.",
3512 gr->tag());
3513 return 0;
3514 }
3515
3516 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
3517 if(!reg_source) {
3518 Msg::Error(
3519 "In makeEdgesForForElemsTouchPivotVert(), invalid source face for region "
3520 "%d.",
3521 gr->tag());
3522 return 0;
3523 }
3524
3525 if(gr != cat_src_elems.region || reg_source != cat_src_elems.source_face) {
3526 Msg::Error("In makeEdgesForElemsTouchPivotVert(), "
3527 "CatergorizedSourceElements structure data "
3528 " does not correspond to region %d.",
3529 reg_source->tag());
3530 return 0;
3531 }
3532
3533 // return if there are no lateral surface diagonals and if not a rotation with
3534 // quadrangles.
3535 if(!lat_tri_diags.size() &&
3536 (!reg_source->quadrangles.size() ||
3537 (ep->geo.Type != ROTATE && ep->geo.Type != TRANSLATE_ROTATE)))
3538 return 1;
3539
3540 // Return if there is only one extrude layer!!!!
3541 // (everything gets taken care of because all elements in top layer are
3542 // divided) ( in fact, this will cause problems if performed )
3543 if(ep->mesh.NbLayer <= 1 && ep->mesh.NbElmLayer[0] <= 1) return 1;
3544
3545 int j_top_start, k_top_start;
3546 j_top_start = ep->mesh.NbLayer - 1;
3547 k_top_start = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1;
3548
3549 // s = 0 for triangles; s = 1 for quadrangles
3550 // These diags are all repeated identically up the extrusion.
3551 for(int s = 0; s < 2; s++) {
3552 std::set<unsigned int> *set_elems;
3553 std::set<unsigned int>::iterator it;
3554
3555 if(!s)
3556 set_elems = &cat_src_elems.internal_tri_touch_one_bnd_pt_quad;
3557 else
3558 set_elems = &cat_src_elems.internal_quad_touch_one_bnd_pt_quad;
3559
3560 for(it = set_elems->begin(); it != set_elems->end(); it++) {
3561 MElement *elem;
3562 if(!s)
3563 elem = reg_source->triangles[(*it)];
3564 else
3565 elem = reg_source->quadrangles[(*it)];
3566
3567 int elem_size = elem->getNumVertices();
3568
3569 // find pivot vertex index
3570 int v_index;
3571 bool v_index_found = false;
3572 for(int p = 0; p < elem_size; p++) {
3573 if(!s && cat_src_elems.tri_bool[4 * (*it) + 1 + p]) {
3574 v_index = p;
3575 v_index_found = true;
3576 break;
3577 }
3578 else if(s && cat_src_elems.quad_bool[5 * (*it) + 1 + p]) {
3579 v_index = p;
3580 v_index_found = true;
3581 break;
3582 }
3583 }
3584 if(!v_index_found) {
3585 Msg::Error("In makeEdgesForElemsTouchPivotVert(), could not find the "
3586 "pivot vertex for an element touching "
3587 "a quad with one vertex on a face edge boundary in region "
3588 "%d. Skipping...",
3589 gr->tag());
3590 continue;
3591 }
3592
3593 std::vector<MVertex *> verts;
3594 getExtrudedVertices(elem, ep, 0, 0, pos, verts);
3595
3596 // determine the j, k for the next layer above 0,0 so that the elements
3597 // directly above the base elements can divided AND higher layers may be
3598 // closed off (no need to divide the elements all the way up)
3599 int j_next, k_next;
3600 bool divide_next = false;
3601 std::vector<MVertex *> verts_next;
3602 if(ep->mesh.NbElmLayer[0] > 1) {
3603 j_next = 0;
3604 k_next = 1;
3605 }
3606 else {
3607 j_next = std::min(ep->mesh.NbLayer - 1, 1);
3608 k_next = 0;
3609 }
3610 if(j_next < j_top_start || k_next < k_top_start) {
3611 divide_next = true;
3612 getExtrudedVertices(elem, ep, j_next, k_next, pos, verts_next);
3613 }
3614
3615 // add the lateral diagonals (skip faces that are already diagonalized)
3616 // note these are created in two layers, mirrored about the plane
3617 // separating the layers
3618 int p2 = (v_index + 1) % elem_size;
3619 int p3 = (v_index + elem_size - 1) % elem_size;
3620 if(!edgeExists(verts[v_index], verts[p2 + elem_size], quadToTri_edges)) {
3621 createEdge(verts[v_index + elem_size], verts[p2], quadToTri_edges);
3622 if(divide_next)
3623 createEdge(verts_next[v_index], verts_next[p2 + elem_size],
3624 quadToTri_edges);
3625 }
3626 if(!edgeExists(verts[v_index], verts[p3 + elem_size], quadToTri_edges)) {
3627 createEdge(verts[v_index + elem_size], verts[p3], quadToTri_edges);
3628 if(divide_next)
3629 createEdge(verts_next[v_index], verts_next[p3 + elem_size],
3630 quadToTri_edges);
3631 }
3632 // make top diagonals for quads -- note: can't just draw to the preferred
3633 // pivot because it can touch two and the other might have laterals drawn
3634 // to it already. have to find the one with two lateral diagonals drawn
3635 // to it.
3636 if(s == 1 && verts[v_index + elem_size] != verts[v_index] &&
3637 !edgeExists(verts[p2 + elem_size], verts[p3 + elem_size],
3638 quadToTri_edges))
3639 createEdge(verts[v_index + elem_size],
3640 verts[(v_index + 2) % elem_size + elem_size],
3641 quadToTri_edges);
3642 }
3643 }
3644
3645 // Now revisit the boundary quads that touched a one boundary point quad
3646 // pivot. These MAY have had edges added and now need a top diagonal
3647 std::set<unsigned int>::iterator it;
3648 for(it = cat_src_elems.two_bnd_pt_quad_touch_one_bnd_pt_quad.begin();
3649 it != cat_src_elems.two_bnd_pt_quad_touch_one_bnd_pt_quad.end(); it++) {
3650 MElement *elem = reg_source->quadrangles[(*it)];
3651 int elem_size = elem->getNumVertices();
3652 std::vector<bool> vert_bnd;
3653 vert_bnd.insert(vert_bnd.begin(),
3654 cat_src_elems.quad_bool.begin() + (5 * (*it) + 1),
3655 cat_src_elems.quad_bool.begin() + (5 * (*it) + 5));
3656 // find num boundary points
3657 int bnd_count = 0;
3658 for(int s = 0; s < elem_size; s++) {
3659 if(vert_bnd[s]) bnd_count++;
3660 }
3661 if(bnd_count != 2) continue;
3662
3663 std::vector<MVertex *> verts;
3664 getExtrudedVertices(elem, ep, 0, 0, pos, verts);
3665 int p1 = -1, p2 = -1;
3666 for(int p = 0; p < elem_size; p++) {
3667 if(vert_bnd[p]) {
3668 p1 = p;
3669 int p2_tmp = (p + 1) % elem_size;
3670 int p3_tmp = (p + elem_size - 1) % elem_size;
3671 if(vert_bnd[p2_tmp]) {
3672 p1 = p;
3673 p2 = p2_tmp;
3674 }
3675 else if(vert_bnd[p3_tmp]) {
3676 p1 = p3_tmp;
3677 p2 = p;
3678 }
3679 break;
3680 }
3681 }
3682 if(p2 < 0 && p1 < 0) {
3683 Msg::Error("In makeEdgesForElemsTouchPivotVert(), error finding boundary "
3684 "points on "
3685 "2-boundary point quadrangle.");
3686 break;
3687 }
3688
3689 // find lateral boundary or internal lateral diagonals. If found, add top
3690 // diagonal
3691 std::set<std::pair<MVertex *, MVertex *> > edges_new;
3692 std::set<std::pair<MVertex *, MVertex *> > forbidden_new;
3693 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
3694 problems, problems_new;
3695 if(p1 >= 0 && p2 >= 0 &&
3696 edgeExists(verts[(p1 + 2) % elem_size],
3697 verts[(p1 + elem_size - 1) % elem_size + elem_size],
3698 quadToTri_edges) &&
3699 !edgeExists(verts[p1 + elem_size],
3700 verts[(p1 + 2) % elem_size + elem_size], quadToTri_edges)) {
3701 createEdge(verts[(p1 + 1) % elem_size + elem_size],
3702 verts[(p1 + elem_size - 1) % elem_size + elem_size],
3703 quadToTri_edges);
3704 createEdge(verts[(p1 + 1) % elem_size + elem_size],
3705 verts[(p1 + elem_size - 1) % elem_size + elem_size],
3706 edges_new);
3707 }
3708 else if(p1 >= 0 && p2 >= 0 &&
3709 edgeExists(verts[(p1 + 2) % elem_size + elem_size],
3710 verts[(p1 + elem_size - 1) % elem_size],
3711 quadToTri_edges) &&
3712 !edgeExists(verts[(p1 + 1) % elem_size + elem_size],
3713 verts[(p1 + elem_size - 1) % elem_size + elem_size],
3714 quadToTri_edges)) {
3715 createEdge(verts[p1 + elem_size], verts[(p1 + 2) % elem_size + elem_size],
3716 quadToTri_edges);
3717 createEdge(verts[p1 + elem_size], verts[(p1 + 2) % elem_size + elem_size],
3718 edges_new);
3719 }
3720
3721 if(edges_new.size()) {
3722 ExtrudeDiags(gr, verts, 0, 0, j_top_start, k_top_start, elem, ep,
3723 edges_new, forbidden_new, quadToTri_edges, forbidden_edges,
3724 problems_new, problems, pos);
3725 }
3726 }
3727
3728 return 1;
3729 }
3730
3731 // For use in QuadToTriEdgeGenerator: Does the lateral edging of internal
3732 // elements in the top extrusion layer by lowest vertex pointer value in TOP
3733 // FACE. Added 04/08/2011
makeEdgesInternalTopLayer(GRegion * gr,bool is_addverts,CategorizedSourceElements & cat_src_elems,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,MVertexRTree & pos)3734 static int makeEdgesInternalTopLayer(
3735 GRegion *gr, bool is_addverts, CategorizedSourceElements &cat_src_elems,
3736 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
3737 MVertexRTree &pos)
3738 {
3739 ExtrudeParams *ep = gr->meshAttributes.extrude;
3740
3741 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
3742 Msg::Error("In makeEdgesInternalTopLayer(), invalid extrusion "
3743 "in region %d for performing QuadToTri mesh generation.",
3744 gr->tag());
3745 return 0;
3746 }
3747
3748 GModel *model = gr->model();
3749 if(!model) {
3750 Msg::Error("In makeEdgesInternalTopLayer(), invalid model for region "
3751 "%d.",
3752 gr->tag());
3753 return 0;
3754 }
3755
3756 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
3757 if(!reg_source) {
3758 Msg::Error(
3759 "In makeEdgesForInternalTopLayer(), invalid source face for region "
3760 "%d.",
3761 gr->tag());
3762 return 0;
3763 }
3764
3765 if(gr != cat_src_elems.region || reg_source != cat_src_elems.source_face) {
3766 Msg::Error("In makeEdgesInternalTopLayer(), CatergorizedSourceElements "
3767 "structure data "
3768 " does not correspond to region %d.",
3769 reg_source->tag());
3770 return 0;
3771 }
3772
3773 // s = 0 for triangles; s = 1 for quadrangles
3774 for(int s = 0; s < 2; s++) {
3775 // no need to do this if there are no quads!!!
3776 if(!reg_source->quadrangles.size()) break;
3777
3778 std::set<unsigned int> *set_elems;
3779 std::set<unsigned int>::iterator it;
3780
3781 if(!s)
3782 set_elems = &cat_src_elems.internal_tri;
3783 else
3784 set_elems = &cat_src_elems.internal_quad;
3785
3786 for(it = set_elems->begin(); it != set_elems->end(); it++) {
3787 MElement *elem;
3788 if(!s)
3789 elem = reg_source->triangles[(*it)];
3790 else
3791 elem = reg_source->quadrangles[(*it)];
3792 int elem_size = elem->getNumVertices();
3793 std::vector<MVertex *> verts;
3794 int j = ep->mesh.NbLayer - 1;
3795 int k = ep->mesh.NbElmLayer[ep->mesh.NbLayer - 1] - 1;
3796
3797 getExtrudedVertices(elem, ep, j, k, pos, verts);
3798
3799 // do this by lowest pointer value IN THE TOP SURFACE
3800 for(int p = 0; p < elem_size; p++) {
3801 int ind1, ind2, ind_low, ind_low_2;
3802 ind1 = p + elem_size;
3803 ind2 = (p + 1) % elem_size + elem_size;
3804 if(verts[ind1] < verts[ind2]) {
3805 ind_low = ind1;
3806 ind_low_2 = ind2 - elem_size;
3807 }
3808 else {
3809 ind_low = ind2;
3810 ind_low_2 = ind1 - elem_size;
3811 }
3812
3813 if(!edgeExists(verts[ind_low - elem_size], verts[ind_low_2 + elem_size],
3814 quadToTri_edges))
3815 createEdge(verts[ind_low], verts[ind_low_2], quadToTri_edges);
3816 }
3817 }
3818 }
3819
3820 return 1;
3821 }
3822
3823 // Generate the set of QuadToTri diagonal edges to subdivide elements,
3824 // and records problematic elements that need to be subvided with an internal
3825 // vertex. Added 2010-01-19
QuadToTriEdgeGenerator(GRegion * gr,CategorizedSourceElements & cat_src_elems,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,MVertexRTree & pos)3826 int QuadToTriEdgeGenerator(
3827 GRegion *gr, CategorizedSourceElements &cat_src_elems,
3828 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
3829 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
3830 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
3831 &problems,
3832 MVertexRTree &pos)
3833 {
3834 ExtrudeParams *ep = gr->meshAttributes.extrude;
3835
3836 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) {
3837 Msg::Error("In QuadToTriEdgeGenerator(), invalid extrusion "
3838 "in region %d for performing QuadToTri mesh generation.",
3839 gr->tag());
3840 return 0;
3841 }
3842
3843 GModel *model = gr->model();
3844 if(!model) {
3845 Msg::Error("In QuadToTriEdgeGenerator(), invalid model for region "
3846 "%d.",
3847 gr->tag());
3848 return 0;
3849 }
3850
3851 // number of extrusion layers
3852 int num_layers = 0;
3853 for(int p = 0; p < ep->mesh.NbLayer; p++)
3854 num_layers += ep->mesh.NbElmLayer[p];
3855
3856 // is this a valid 'add internal vertex' extrusion?
3857 bool is_addverts = false;
3858 if(ep->mesh.QuadToTri == QUADTRI_ADDVERTS_1 ||
3859 ep->mesh.QuadToTri == QUADTRI_ADDVERTS_1_RECOMB)
3860 is_addverts = true;
3861
3862 // now find and verify the source and the top of region
3863
3864 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
3865 if(!reg_source) {
3866 Msg::Error("In QuadToTriEdgeGenerator(), invalid source face for region "
3867 "%d.",
3868 gr->tag());
3869 return 0;
3870 }
3871
3872 // need for toroidal loop extrusions...top layer treated specially
3873 bool is_toroidal = IsInToroidalQuadToTri(reg_source);
3874
3875 std::vector<GFace *> reg_faces = gr->faces();
3876 std::vector<GFace *>::iterator itf = reg_faces.begin();
3877
3878 // find top surface of extrusion and first root dependency of source
3879 GFace *reg_top = NULL;
3880 GFace *root = findRootSourceFaceForFace(reg_source);
3881 bool foundRoot = false;
3882 for(itf = reg_faces.begin(); itf != reg_faces.end(); itf++) {
3883 ExtrudeParams *face_ep = (*itf)->meshAttributes.extrude;
3884 if(face_ep && face_ep->geo.Mode == COPIED_ENTITY &&
3885 reg_source == model->getFaceByTag(std::abs(face_ep->geo.Source))) {
3886 reg_top = (*itf);
3887 }
3888 if((*itf) == root) foundRoot = true;
3889 if(reg_top && (foundRoot || !is_toroidal)) break;
3890 }
3891
3892 if(is_toroidal && !reg_top && foundRoot && root != reg_source) reg_top = root;
3893 if(!reg_top) {
3894 Msg::Error("In QuadToTriEdgeGenerator(), invalid top surface for region "
3895 "%d.",
3896 gr->tag());
3897 return 0;
3898 }
3899
3900 // list of forbidden edges and boundary verts that are in triangles
3901 std::set<std::pair<MVertex *, MVertex *> > forbidden_edges;
3902
3903 // insert ALL fixed edges into quadToTri_edges, all forbidden edges on
3904 // recombined quads into forbidden_edges, and insert into lat_tri_diags ALL
3905 // lateral diagonal edges.
3906 QuadToTriGetRegionDiags(gr, quadToTri_edges, forbidden_edges, lat_tri_diags,
3907 pos);
3908 /*unsigned int Rnum = gr->tag()-1;
3909 std::vector<MVertex*> verts;
3910 MElement *elem;
3911 elem = reg_source->triangles[0];
3912 int vert_num = getExtrudedVertices(elem, ep, 0, 0, pos, verts);
3913 const int elem_size = verts.size() == 8 ? 4 : 3;
3914 for( int p = 0; p < elem_size; p++ ){
3915 int state = Rnum / std::pow(3,(elem_size-1-p));
3916 Rnum -= state*std::pow(3,(elem_size-1-p));
3917 if( p < elem_size ){
3918 if( verts[p] == verts[p+elem_size] || verts[(p+1)%elem_size] ==
3919 verts[(p+1)%elem_size+elem_size] ) continue; if( state == 1 ){
3920 //createEdge( verts[p], verts[(p+1)%elem_size+elem_size],
3921 quadToTri_edges ); createEdge( verts[p], verts[(p+1)%elem_size+elem_size],
3922 lat_tri_diags );
3923 }
3924 else if( state == 2 ){
3925 //createEdge( verts[p+elem_size], verts[(p+1)%elem_size],
3926 quadToTri_edges ); createEdge( verts[p+elem_size], verts[(p+1)%elem_size],
3927 lat_tri_diags );
3928 }
3929 else if( state == 0 ){
3930 std::vector<MVertex *> v_face;
3931 v_face.assign(4, (MVertex*)(NULL) );
3932 v_face[0] = verts[p]; v_face[1] = verts[(p+1)%elem_size];
3933 v_face[2] = verts[(p+1)%elem_size+elem_size];
3934 v_face[3] = verts[p+elem_size];
3935 createForbidden(v_face, forbidden_edges);
3936 }
3937 else if( state != 0 )
3938 Msg::Error("Failed in finding all combos...state = %d.", state);
3939 }
3940 else{
3941 int add = (p==4) ? 0 : 4;
3942 if( state == 1 )
3943 createEdge( verts[0+add], verts[2+add], quadToTri_edges );
3944 else if( state == 2 )
3945 createEdge( verts[1+add], verts[3+add], quadToTri_edges );
3946 else if( state == 0 ){
3947 std::vector<MVertex *> v_face;
3948 v_face.assign(4, (MVertex*)(NULL) );
3949 v_face[0] = verts[0+add]; v_face[1] = verts[1+add];
3950 v_face[2] = verts[2+add]; v_face[3] = verts[3+add];
3951 createForbidden(v_face, forbidden_edges);
3952 }
3953 else
3954 Msg::Error("Failed in finding all combos...state = %d.", state);
3955 }
3956 }*/
3957
3958 // if this is an' add internal vertex' extrusion, don't need adjustable
3959 // lateral edges, so put all lat_tri_diags into quadToTri_edges
3960 if(is_addverts)
3961 quadToTri_edges.insert(lat_tri_diags.begin(), lat_tri_diags.end());
3962
3963 // If there are no lat_tri_diags and no quads, there is nothing left to do
3964 if(!lat_tri_diags.size() && !reg_source->quadrangles.size()) return 1;
3965
3966 // can return now if this is an 'add internal vertex' extrusion...nothing left
3967 // to do
3968 if(is_addverts) return 1;
3969
3970 // BRUTE FORCE diagonalization of elements with all vertices on a lateral
3971 // boundary of region: This has to be done for all cases with such elements if
3972 if(!makeEdgesForElemsWithAllVertsOnBnd(gr, is_addverts, cat_src_elems,
3973 quadToTri_edges, lat_tri_diags,
3974 forbidden_edges, problems, pos)) {
3975 Msg::Error("In QuadToTriEdgeGenerator(), failed to make edges for the "
3976 "elements in region %d "
3977 "with all vertices on a lateral boundary",
3978 gr->tag());
3979 return 0;
3980 }
3981
3982 // now do the "elegant" diagonalization of all the rest of the surface
3983 // elements....
3984
3985 // Extrude source triangles that are on the source boundary edges and find any
3986 // diagonals
3987 if(!makeEdgesForOtherBndPrisms(gr, is_addverts, cat_src_elems,
3988 quadToTri_edges, lat_tri_diags,
3989 forbidden_edges, problems, pos)) {
3990 Msg::Error(
3991 "In QuadToTriEdgeGenerator(), failed to make edges for the prism "
3992 "extrusions in region %d with "
3993 "source triangles having some but not all vertices on the boundary",
3994 gr->tag());
3995 return 0;
3996 }
3997
3998 // For a region with a structured all-quad source surface, none of the
3999 // previous edging will be executed, so this is the first place a region with
4000 // a structured quad source surface starts getting edges.
4001
4002 // The rest of this function is only necessary for a single layer quadToTri
4003 // method extrusions:
4004
4005 // Edge creation for extruded quadrangles with some but not all vertices on a
4006 // boundary.
4007 if(!makeEdgesForOtherBndHexa(gr, is_addverts, cat_src_elems, quadToTri_edges,
4008 lat_tri_diags, forbidden_edges, problems, pos)) {
4009 Msg::Error("In QuadToTriEdgeGenerator(), failed to make edges for the "
4010 "hexahedral extrusions in region %d with "
4011 "source quads having some but not all vertices on the boundary",
4012 gr->tag());
4013 return 0;
4014 }
4015
4016 // Find diagonals for elements touching a "pivot vertex" of a hexa element
4017 // that has a source quad with only one vertex on a lateral boundary (see
4018 // inside makeEdgesForOtherBndHexa() and makeEdgesForElemsTouchingPivotVert()
4019 // for details of "pivot vertex".
4020 if(!makeEdgesForElemsTouchPivotVert(gr, is_addverts, cat_src_elems,
4021 quadToTri_edges, lat_tri_diags,
4022 forbidden_edges, pos)) {
4023 Msg::Error("In QuadToTriEdgeGenerator(), failed to make edges for "
4024 "the elements in region %d touching a \'pivot vertex\' of a "
4025 "hexa element with source quad having one vertex on a boundary.",
4026 gr->tag());
4027 return 0;
4028 }
4029
4030 // Mesh internal elements in the top layer (just add lateral diagonals for the
4031 // Do this by lowest pointer in top surface
4032 if(!is_toroidal && !makeEdgesInternalTopLayer(gr, is_addverts, cat_src_elems,
4033 quadToTri_edges, pos)) {
4034 Msg::Error("In QuadToTriEdgeGenerator(), failed to make internal edges "
4035 "in top extrusion layer of region %d.",
4036 gr->tag());
4037 return 0;
4038 }
4039
4040 return 1;
4041 }
4042
4043 // Remesh the lateral 2D faces of QuadToTri regions using edges in
4044 // quadToTri_edges as constraints Added 2010-01-24
QuadToTriLateralRemesh(GRegion * gr,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges)4045 static bool QuadToTriLateralRemesh(
4046 GRegion *gr, std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges)
4047 {
4048 ExtrudeParams *ep = gr->meshAttributes.extrude;
4049
4050 if(!ep || !ep->mesh.QuadToTri || !ep->mesh.ExtrudeMesh) return false;
4051
4052 GModel *model = gr->model();
4053
4054 // find source face
4055 GFace *reg_source = model->getFaceByTag(std::abs(ep->geo.Source));
4056 if(!reg_source) {
4057 Msg::Error("In QuadToTriLateralRemesh(), could not find source face "
4058 "%d for region %d.",
4059 std::abs(ep->geo.Source), gr->tag());
4060 return false;
4061 }
4062
4063 // Find a source surface, find a COPIED_ENTITY that is the top surface,
4064 // If shared laterals are all static (quad or non subdivide triangles),
4065 // set the allStaticSharedLaterals argument to true.
4066 // If any lateral is unstructured, error.
4067
4068 bool is_toroidal = IsInToroidalQuadToTri(reg_source);
4069 GFace *root = findRootSourceFaceForFace(reg_source);
4070
4071 bool foundTop = false, foundRoot = false;
4072 GFace *reg_top = NULL;
4073 std::vector<GFace *> faces = gr->faces();
4074 std::vector<GFace *>::iterator it = faces.begin();
4075
4076 for(it = faces.begin(); it != faces.end(); it++) {
4077 ExtrudeParams *face_tmp_ep = (*it)->meshAttributes.extrude;
4078 if((*it) == root) foundRoot = true;
4079 if(face_tmp_ep && face_tmp_ep->geo.Mode == COPIED_ENTITY) {
4080 GFace *top_source_tmp =
4081 model->getFaceByTag(std::abs(face_tmp_ep->geo.Source));
4082 if(!top_source_tmp) {
4083 Msg::Error("In QuadToTriLateralRemesh(), could not find source face "
4084 "%d for copied surface %d of region %d.",
4085 std::abs(face_tmp_ep->geo.Source), (*it)->tag(), gr->tag());
4086 }
4087 else if(top_source_tmp == reg_source) {
4088 foundTop = true;
4089 reg_top = (*it);
4090 }
4091 }
4092 }
4093
4094 // if didn't find the copied entity, maybe this is toroidal and the top has
4095 // been replaced
4096 if(is_toroidal && !foundTop && foundRoot && root != reg_source)
4097 foundTop = true;
4098
4099 if(!foundTop)
4100 Msg::Warning("In QuadToTriLateralRemesh(), could not find top face "
4101 "for region %d.",
4102 gr->tag());
4103
4104 Msg::Info("Remeshing lateral surfaces for QuadToTri region %d.", gr->tag());
4105
4106 // now loop through faces again, remeshing all laterals that need it.
4107 for(it = faces.begin(); it != faces.end(); it++) {
4108 if((*it) != reg_top && (*it) != reg_source &&
4109 IsSurfaceALateralForRegion(gr, *it)) {
4110 // *** JUST REMESH EVERY SURFACE AGAIN TO BE SURE ***
4111
4112 for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
4113 delete(*it)->triangles[i];
4114 (*it)->triangles.clear();
4115 for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++)
4116 delete(*it)->quadrangles[i];
4117 (*it)->quadrangles.clear();
4118 MeshExtrudedSurface((*it), &quadToTri_edges);
4119 }
4120 }
4121 return foundTop;
4122 }
4123
4124 // Adds the face- or body-center vertices needed for some QuadToTri elements
addBodyCenteredVertices(GRegion * to,CategorizedSourceElements & c,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,bool is_addverts,unsigned int lat_tri_diags_size,MVertexRTree & pos)4125 static bool addBodyCenteredVertices(
4126 GRegion *to, CategorizedSourceElements &c,
4127 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
4128 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4129 &problems,
4130 bool is_addverts, unsigned int lat_tri_diags_size, MVertexRTree &pos)
4131 {
4132 ExtrudeParams *ep = to->meshAttributes.extrude;
4133 if(!ep || !ep->mesh.ExtrudeMesh || !ep->mesh.QuadToTri) return false;
4134
4135 GModel *model = to->model();
4136 GFace *from = model->getFaceByTag(std::abs(ep->geo.Source));
4137 if(!from) return false;
4138
4139 // find number of layers;
4140 unsigned int num_layer = 0;
4141 for(int p = 0; p < ep->mesh.NbLayer; p++) num_layer += ep->mesh.NbElmLayer[p];
4142
4143 // create reserve capacity for the temp vector of new vertices:
4144 unsigned int cap_add = 0;
4145 if(!is_addverts) {
4146 std::map<MElement *,
4147 std::set<std::pair<unsigned int, unsigned int> > >::iterator itmap;
4148 for(itmap = problems.begin(); itmap != problems.end(); itmap++)
4149 cap_add += itmap->second.size();
4150 }
4151 else {
4152 unsigned int NbBndElems = c.four_bnd_pt_quad.size() +
4153 c.three_bnd_pt_tri.size() +
4154 c.other_bnd_quad.size() + c.other_bnd_tri.size();
4155 unsigned int NbSourceElems =
4156 from->triangles.size() + from->quadrangles.size();
4157 if(findRootSourceFaceForFace(from) ==
4158 from) // if extruded back on the source in a ring
4159 cap_add = NbBndElems * num_layer;
4160 else
4161 cap_add = NbSourceElems + NbBndElems * (num_layer - 1);
4162 }
4163 to->mesh_vertices.reserve(to->mesh_vertices.size() + cap_add);
4164
4165 // first the !is_addverts case
4166 if(problems.size() && !is_addverts) {
4167 std::map<MElement *,
4168 std::set<std::pair<unsigned int, unsigned int> > >::iterator itmap;
4169 for(itmap = problems.begin(); itmap != problems.end(); itmap++) {
4170 MElement *elem = itmap->first;
4171 std::set<std::pair<unsigned int, unsigned int> >::iterator itpairs;
4172 for(itpairs = itmap->second.begin(); itpairs != itmap->second.end();
4173 itpairs++) {
4174 std::vector<MVertex *> verts;
4175 int j = (*itpairs).first;
4176 int k = (*itpairs).second;
4177 getExtrudedVertices(elem, ep, j, k, pos, verts);
4178 QtMakeCentroidVertex(verts, &(to->mesh_vertices), to, pos);
4179 }
4180 }
4181 }
4182
4183 if(!is_addverts) return 1;
4184
4185 // The rest of the function works for is_addverts
4186
4187 // Holds the new vertices...put them in to->mesh_vertices only at the end
4188 std::vector<MVertex *> v_tmp;
4189 v_tmp.reserve(cap_add);
4190
4191 // triangles and quadrangles
4192 // t =0 triangles, t=1 quadrangles
4193 std::vector<MVertex *> verts3D;
4194 for(int t = 0; t < 2; t++) {
4195 for(int s = 0; s < 3; s++) {
4196 std::set<unsigned int> *set_elems;
4197 if(!t) {
4198 if(!s)
4199 set_elems = &c.three_bnd_pt_tri;
4200 else if(s == 1)
4201 set_elems = &c.other_bnd_tri;
4202 else
4203 set_elems = &c.internal_tri;
4204 }
4205 else {
4206 if(!s)
4207 set_elems = &c.four_bnd_pt_quad;
4208 else if(s == 1)
4209 set_elems = &c.other_bnd_quad;
4210 else
4211 set_elems = &c.internal_quad;
4212 }
4213 std::set<unsigned int>::iterator itset;
4214 for(itset = set_elems->begin(); itset != set_elems->end(); itset++) {
4215 MElement *elem;
4216 if(!t)
4217 elem = from->triangles[(*itset)];
4218 else
4219 elem = from->quadrangles[(*itset)];
4220 int elem_size = elem->getNumVertices();
4221 // see if this element is on a boundary with lat_tri_diags or a
4222 // degenerated hexahedron (always divide those)
4223 bool found_diags = false;
4224 bool degen_hex = false;
4225 verts3D.resize(0);
4226 getExtrudedVertices(elem, ep, 0, 0, pos, verts3D);
4227 for(int p = 0; p < elem_size; p++) {
4228 // always look for degen hex
4229 if(t && verts3D[p] == verts3D[p + elem_size] &&
4230 verts3D[(p + 1) % elem_size] !=
4231 verts3D[(p + 1) % elem_size + elem_size] &&
4232 verts3D[(p + elem_size - 1) % elem_size] !=
4233 verts3D[(p + elem_size - 1) % elem_size + elem_size]) {
4234 degen_hex = true;
4235 break;
4236 }
4237 // skip the rest if no lat_tri_diags or if not on lateral boundary
4238 if(!lat_tri_diags_size || s >= 2) continue;
4239 // if a triangle face, skip it
4240 if(verts3D[p] == verts3D[p + elem_size] ||
4241 verts3D[(p + 1) % elem_size] ==
4242 verts3D[(p + 1) % elem_size + elem_size])
4243 continue;
4244 else if(edgeExists(verts3D[p],
4245 verts3D[(p + 1) % elem_size + elem_size],
4246 quadToTri_edges))
4247 found_diags = true;
4248 else if(edgeExists(verts3D[p + elem_size],
4249 verts3D[(p + 1) % elem_size], quadToTri_edges))
4250 found_diags = true;
4251 }
4252 // triangle extrusions don't need body centered verts if NO diags found
4253 // or if not on lateral boundary
4254 if(!t && (!found_diags || s == 2)) continue;
4255
4256 int j_start, k_start;
4257 if((s < 2 && found_diags) || degen_hex) {
4258 j_start = 0;
4259 k_start = 0;
4260 }
4261 else { // only non-bnd quads or columns with no lateral diags and not
4262 // extruded into degen hexa should execute this
4263 j_start = ep->mesh.NbLayer - 1;
4264 k_start = ep->mesh.NbElmLayer[j_start] - 1;
4265 }
4266 std::vector<MVertex *> verts;
4267 for(int j = j_start; j < ep->mesh.NbLayer; j++) {
4268 int k_stop = ep->mesh.NbElmLayer[j];
4269 for(int k = k_start; k < k_stop; k++) {
4270 verts.resize(0);
4271 getExtrudedVertices(elem, ep, j, k, pos, verts);
4272 QtMakeCentroidVertex(verts, &v_tmp, to, pos);
4273 }
4274 }
4275 }
4276 }
4277 }
4278 // Don't forget to add v_tmp to to->mesh_vertices!!!
4279 to->mesh_vertices.reserve(to->mesh_vertices.size() + v_tmp.size());
4280 for(unsigned int p = 0; p < v_tmp.size(); p++)
4281 to->mesh_vertices.push_back(v_tmp[p]);
4282 return 1;
4283 }
4284
4285 // Meshes either a prism or a hexahedral set of mesh vertices with an internal
4286 // vertex created here in the function. Added 2010-03-30
MeshWithInternalVertex(GRegion * to,MElement * source,std::vector<MVertex * > v,std::vector<int> n1,std::vector<int> n2,MVertexRTree & pos)4287 static void MeshWithInternalVertex(GRegion *to, MElement *source,
4288 std::vector<MVertex *> v,
4289 std::vector<int> n1, std::vector<int> n2,
4290 MVertexRTree &pos)
4291 {
4292 int v_size = v.size();
4293 int n_lat_tmp;
4294 if(v_size == 6)
4295 n_lat_tmp = 3;
4296 else if(v_size == 8)
4297 n_lat_tmp = 4;
4298 else {
4299 Msg::Error("In MeshWithInternalVertex(), number of element vertices does "
4300 "not equal 6 or 8.");
4301 return;
4302 }
4303
4304 const int n_lat = n_lat_tmp;
4305
4306 if((n_lat == 3 && n1.size() != 3) || (n_lat == 4 && n2.size() != 6)) {
4307 Msg::Error("In MeshWithInternalVertex(), size of diagonal node vectors is "
4308 "not does not equal 3 or 6.");
4309 return;
4310 }
4311
4312 // find the internal vertex
4313 std::vector<double> centroid = QtFindVertsCentroid(v);
4314
4315 // it's too dangerous to use the 'new' command in here even with body-centered
4316 // vertices.
4317
4318 MVertex *tmp = pos.find(centroid[0], centroid[1], centroid[2]);
4319 if(!tmp) {
4320 Msg::Error("Could not find extruded vertex (%.16g, %.16g, %.16g) in "
4321 "geometrical entity %d",
4322 centroid[0], centroid[1], centroid[2], to->tag());
4323 Msg::Error("MeshWithInternalVertex() failed to find body-centered vertex.");
4324 return;
4325 }
4326
4327 MVertex *v_int = tmp;
4328
4329 // build all pyramids/tetra
4330 for(int p = 0; p < n_lat; p++) {
4331 int p2 = (p + 1) % n_lat;
4332 if(v[p] == v[p + n_lat] && v[p2] == v[p2 + n_lat])
4333 continue;
4334 else if(v[p] == v[p + n_lat] || v[p2] == v[p2 + n_lat]) {
4335 MVertex *v_dup = (v[p] == v[p + n_lat]) ? v[p] : v[p2];
4336 MVertex *v_non_dup = (v_dup == v[p]) ? v[p2] : v[p];
4337 MVertex *v_non_dup2 = (v_non_dup == v[p]) ? v[p + n_lat] : v[p2 + n_lat];
4338 addTetrahedron(v_dup, v_non_dup, v_non_dup2, v_int, to, source);
4339 }
4340 else if(n1[p] == p || n2[p] == p) {
4341 addTetrahedron(v[p], v[p2], v[p2 + n_lat], v_int, to, source);
4342 addTetrahedron(v[p], v[p2 + n_lat], v[p + n_lat], v_int, to, source);
4343 }
4344 else if(n1[p] == p + n_lat || n2[p] == p + n_lat) {
4345 addTetrahedron(v[p], v[p2], v[p + n_lat], v_int, to, source);
4346 addTetrahedron(v[p2], v[p2 + n_lat], v[p + n_lat], v_int, to, source);
4347 }
4348 else
4349 addPyramid(v[p], v[p2], v[p2 + n_lat], v[p + n_lat], v_int, to, source);
4350 }
4351
4352 if(n_lat == 3) {
4353 // bottom and top
4354 addTetrahedron(v[0], v[1], v[2], v_int, to, source);
4355 addTetrahedron(v[3], v[5], v[4], v_int, to, source);
4356 }
4357 else if(n_lat == 4) {
4358 for(int p = 4; p < 6; p++) {
4359 int add = (p == 4) ? 0 : 4;
4360 if(n1[p] == 0 + add || n2[p] == 0 + add) {
4361 addTetrahedron(v[add], v[1 + add], v[2 + add], v_int, to, source);
4362 addTetrahedron(v[add], v[2 + add], v[3 + add], v_int, to, source);
4363 }
4364 else if(n1[p] == 1 + add || n2[p] == 1 + add) {
4365 addTetrahedron(v[1 + add], v[2 + add], v[3 + add], v_int, to, source);
4366 addTetrahedron(v[1 + add], v[3 + add], v[add], v_int, to, source);
4367 }
4368 else
4369 addPyramid(v[add], v[1 + add], v[2 + add], v[3 + add], v_int, to,
4370 source);
4371 }
4372 }
4373 }
4374
4375 // Construct the elements that subdivide a prism (or degenerated prism) in a
4376 // QuadToTri interface; Added 2010-01-24
QuadToTriPriPyrTet(std::vector<MVertex * > & v,GRegion * to,int j,int k,MElement * source,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,unsigned int lat_tri_diags_size,bool bnd_elem,bool is_addverts,bool diag_search,MVertexRTree & pos)4377 static inline void QuadToTriPriPyrTet(
4378 std::vector<MVertex *> &v, GRegion *to, int j, int k, MElement *source,
4379 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
4380 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4381 &problems,
4382 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4383 &problems_new,
4384 unsigned int lat_tri_diags_size, bool bnd_elem, bool is_addverts,
4385 bool diag_search, MVertexRTree &pos)
4386 {
4387 int dup[3];
4388 int m = 0;
4389 for(int i = 0; i < 3; i++)
4390 if(v[i] == v[i + 3]) dup[m++] = i;
4391
4392 bool is_problem = false;
4393 if(!is_addverts) {
4394 std::pair<unsigned int, unsigned int> jk_pair(j, k);
4395 std::map<MElement *,
4396 std::set<std::pair<unsigned int, unsigned int> > >::iterator
4397 itprob;
4398 itprob = problems.find(source);
4399 if(itprob != problems.end()) {
4400 if((*itprob).second.find(jk_pair) != (*itprob).second.end())
4401 is_problem = true;
4402 }
4403 }
4404
4405 if(m && is_problem) {
4406 Msg::Error("In QuadToTriPriPyrTet(), non-prismatic extrusion of triangle "
4407 "in region %d "
4408 "marked as needing internal vertex to complete. Skipping....",
4409 to->tag());
4410 return;
4411 }
4412
4413 // BAD SHAPE
4414 if(m > 2 || m < 0) {
4415 Msg::Error("In QuadToTriPriPyrTet(), bad number of degenerate corners.");
4416 return;
4417 }
4418
4419 // find vertex node indices for diagonalized faces
4420 std::vector<int> n1, n2;
4421 bool found_diags = false;
4422 n1.assign(3, -1);
4423 n2.assign(3, -2);
4424 if(diag_search) {
4425 for(int p = 0; p < 3; p++) {
4426 n1[p] = -p * p - p - 1;
4427 n2[p] = -p * p - p - 2;
4428 if(v[p] == v[p + 3] || v[(p + 1) % 3] == v[(p + 1) % 3 + 3])
4429 continue;
4430 else if(edgeExists(v[p], v[(p + 1) % 3 + 3], quadToTri_edges)) {
4431 n1[p] = p;
4432 n2[p] = (p + 1) % 3 + 3;
4433 found_diags = true;
4434 }
4435 else if(edgeExists(v[p + 3], v[(p + 1) % 3], quadToTri_edges)) {
4436 n1[p] = p + 3;
4437 n2[p] = (p + 1) % 3;
4438 found_diags = true;
4439 }
4440 }
4441 }
4442
4443 // mesh with added internal body centered vertex
4444 // is this prism part of a QuadToTri internal vertex extrusion and does it
4445 // need to be extruded as such?
4446 if(is_addverts && bnd_elem && found_diags) {
4447 MeshWithInternalVertex(to, source, v, n1, n2, pos);
4448 return;
4449 }
4450
4451 // The rest are for 'no new vertex' extrusions or degenerate prisms:
4452
4453 // tetrahedron
4454 if(m == 2 && !is_problem) {
4455 int dup_face = (dup[0] + 1) % 3 == dup[1] ? dup[0] : dup[1];
4456 addTetrahedron(v[dup_face], v[(dup_face + 1) % 3], v[(dup_face + 2) % 3],
4457 v[(dup_face + 2) % 3 + 3], to, source);
4458 return;
4459 }
4460 // pyramid
4461 else if(m == 1 && !is_problem) {
4462 int p = (dup[0] + 1) % 3;
4463 // determine if the pyramid is sliced into two tetrahedra
4464 if(n1[p] >= 0) {
4465 int add = n1[p] < 3 ? 3 : -3;
4466 addTetrahedron(v[n1[p]], v[n2[p]], v[n1[p] + add], v[dup[0]], to, source);
4467 addTetrahedron(v[n1[p]], v[n2[p] - add], v[n2[p]], v[dup[0]], to, source);
4468 }
4469 else // pyramid
4470 addPyramid(v[p], v[p + 3], v[(p + 1) % 3 + 3], v[(p + 1) % 3], v[dup[0]],
4471 to, source);
4472
4473 return;
4474 }
4475
4476 // Full prism: Start looking for a diagonal on a lateral.
4477 else if(m == 0 && !is_problem) {
4478 if(!found_diags) {
4479 addPrism(v[0], v[1], v[2], v[3], v[4], v[5], to, source);
4480 return;
4481 }
4482 else {
4483 for(int p = 0; p < 3; p++) {
4484 if(n1[p] >= 0 && n2[p] == n1[(p + 1) % 3]) {
4485 int add = n2[p] < 3 ? 3 : -3;
4486 int tet_top = n2[p] + add;
4487 int pyr_top = n2[p];
4488 int base = (p + 2) % 3;
4489 addTetrahedron(v[n1[p]], v[n2[p]], v[n2[(p + 1) % 3]], v[tet_top], to,
4490 source);
4491 if(n1[(p + 2) % 3] >= 0) {
4492 int add_2 = n1[base] < 3 ? 3 : -3;
4493 addTetrahedron(v[n1[base]], v[n2[base]], v[n1[base] + add_2],
4494 v[pyr_top], to, source);
4495 addTetrahedron(v[n1[base]], v[n2[base]], v[n2[base] - add_2],
4496 v[pyr_top], to, source);
4497 }
4498 else
4499 addPyramid(v[base], v[base + 3], v[(base + 1) % 3 + 3],
4500 v[(base + 1) % 3], v[pyr_top], to, source);
4501 return;
4502 }
4503 }
4504 }
4505 }
4506
4507 // At this point, if the function has not returned, element must be meshed
4508 // with an additional vertex
4509
4510 if(!is_problem) {
4511 Msg::Error(
4512 "In QuadToTriPriPyrTet(), Extruded prism needs subdivision, but cannot "
4513 " be divided without internal vertex, but was not previously detected as "
4514 "such. "
4515 " This is a bug. Please Report.");
4516 Msg::Error("j: %d, k: %d", j, k);
4517 QtMakeCentroidVertex(v, &(to->mesh_vertices), to, pos);
4518 std::pair<unsigned int, unsigned int> jk_pair(j, k);
4519 problems_new[source].insert(jk_pair);
4520 is_problem = true;
4521 }
4522
4523 if(is_problem) {
4524 MeshWithInternalVertex(to, source, v, n1, n2, pos);
4525 return;
4526 }
4527 }
4528
4529 // Construct the elements that subdivde a two-point degenerated hexahedron
4530 // (prism).
createTwoPtDegenHexElems(std::vector<MVertex * > & v,GRegion * to,ExtrudeParams * ep,int j,int k,int dup[],MElement * source,std::vector<int> n1,std::vector<int> n2,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,unsigned int lat_tri_diags_size,bool bnd_elem,bool is_addverts,bool found_diags)4531 static inline bool createTwoPtDegenHexElems(
4532 std::vector<MVertex *> &v, GRegion *to, ExtrudeParams *ep, int j, int k,
4533 int dup[], MElement *source, std::vector<int> n1, std::vector<int> n2,
4534 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
4535 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4536 &problems,
4537 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4538 &problems_new,
4539 unsigned int lat_tri_diags_size, bool bnd_elem, bool is_addverts,
4540 bool found_diags)
4541 {
4542 if(!ep) return 0;
4543
4544 // first find out what is the degenerate face
4545 int degen_face = (dup[0] + 1) % 4 == dup[1] ? dup[0] : dup[1];
4546
4547 // if no diags found, make a prism and return
4548 if(!found_diags) {
4549 addPrism(v[degen_face], v[(degen_face + 3) % 4],
4550 v[(degen_face + 3) % 4 + 4], v[(degen_face + 1) % 4],
4551 v[(degen_face + 2) % 4], v[(degen_face + 2) % 4 + 4], to, source);
4552 return 1;
4553 }
4554
4555 int tet_top = -1;
4556 int pyr_top = -2;
4557
4558 // if faces 4 and 5 can make a slice
4559 if(n1[4] >= 0 && (n1[4] + 4 == n1[5] || n2[4] + 4 == n1[5])) {
4560 if(n1[4] == degen_face || n2[4] == degen_face) {
4561 pyr_top = degen_face;
4562 tet_top = (degen_face + 1) % 4;
4563 }
4564 else {
4565 pyr_top = (degen_face + 1) % 4;
4566 tet_top = degen_face;
4567 }
4568 addTetrahedron(v[pyr_top], v[(pyr_top + 2) % 4], v[(pyr_top + 2) % 4 + 4],
4569 v[tet_top], to, source);
4570
4571 int base = (degen_face + 2) % 4;
4572 if(n1[base] >= 0) {
4573 int add = n1[base] > 3 ? -4 : 4;
4574 addTetrahedron(v[n1[base]], v[n2[base]], v[n1[base] + add], v[pyr_top],
4575 to, source);
4576 addTetrahedron(v[n1[base]], v[n2[base] - add], v[n2[base]], v[pyr_top],
4577 to, source);
4578 }
4579 else
4580 addPyramid(v[base], v[base + 4], v[(base + 1) % 4 + 4], v[(base + 1) % 4],
4581 v[pyr_top], to, source);
4582
4583 return 1;
4584 }
4585
4586 // if faces 4 and (degen_face+2)%4 make a slice
4587 if(tet_top < 0 && n1[4] >= 0 &&
4588 (n1[4] == n1[(degen_face + 2) % 4] || n2[4] == n1[(degen_face + 2) % 4] ||
4589 n1[4] == n2[(degen_face + 2) % 4] || n2[4] == n2[(degen_face + 2) % 4])) {
4590 pyr_top = n1[(degen_face + 2) % 4] < 4 ? n1[(degen_face + 2) % 4] :
4591 n2[(degen_face + 2) % 4];
4592 tet_top = pyr_top == (degen_face + 2) % 4 ? (degen_face + 3) % 4 :
4593 (degen_face + 2) % 4;
4594
4595 addTetrahedron(v[pyr_top], v[tet_top + 4], v[(pyr_top + 2) % 4], v[tet_top],
4596 to, source);
4597
4598 int base = 5;
4599 if(n1[base] >= 0) {
4600 addTetrahedron(v[n1[base]], v[n2[base]], v[(n1[base] - 4 + 1) % 4 + 4],
4601 v[pyr_top], to, source);
4602 addTetrahedron(v[n1[base]], v[n2[base]], v[(n1[base] - 4 + 3) % 4 + 4],
4603 v[pyr_top], to, source);
4604 }
4605 else
4606 addPyramid(v[4], v[5], v[6], v[7], v[pyr_top], to, source);
4607
4608 return 1;
4609 }
4610
4611 // if faces 5 and (degen_face+2)%4 make a slice
4612 if(tet_top < 0 && n1[5] >= 0 &&
4613 (n1[5] == n1[(degen_face + 2) % 4] || n2[5] == n1[(degen_face + 2) % 4] ||
4614 n1[5] == n2[(degen_face + 2) % 4] || n2[5] == n2[(degen_face + 2) % 4])) {
4615 pyr_top = n1[(degen_face + 2) % 4] < 4 ? n2[(degen_face + 2) % 4] :
4616 n1[(degen_face + 2) % 4];
4617 tet_top = pyr_top == (degen_face + 2) % 4 + 4 ? (degen_face + 3) % 4 + 4 :
4618 (degen_face + 2) % 4 + 4;
4619
4620 addTetrahedron(v[pyr_top], v[(pyr_top - 4 + 2) % 4 + 4], v[tet_top - 4],
4621 v[tet_top], to, source);
4622
4623 int base = 4;
4624 if(n1[base] >= 0) {
4625 addTetrahedron(v[n1[base]], v[n2[base]], v[(n1[base] + 1) % 4],
4626 v[pyr_top], to, source);
4627 addTetrahedron(v[n1[base]], v[n2[base]], v[(n1[base] + 3) % 4],
4628 v[pyr_top], to, source);
4629 }
4630 else
4631 addPyramid(v[0], v[1], v[2], v[3], v[pyr_top], to, source);
4632
4633 return 1;
4634 }
4635
4636 return 0;
4637 }
4638
4639 // Construct the elements that subdivide a one-point degenerated hexahedron
4640 // extrusion
createOnePtDegenHexElems(std::vector<MVertex * > & v,GRegion * to,ExtrudeParams * ep,int j,int k,int dup[],MElement * source,std::vector<int> n1,std::vector<int> n2,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,unsigned int lat_tri_diags_size,bool bnd_elem,bool is_addverts,bool found_diags)4641 static inline bool createOnePtDegenHexElems(
4642 std::vector<MVertex *> &v, GRegion *to, ExtrudeParams *ep, int j, int k,
4643 int dup[], MElement *source, std::vector<int> n1, std::vector<int> n2,
4644 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
4645 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4646 &problems,
4647 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4648 &problems_new,
4649 unsigned int lat_tri_diags_size, bool bnd_elem, bool is_addverts,
4650 bool found_diags)
4651 {
4652 if(!ep) return 0;
4653
4654 // if no diags, then just make the degenerate hexahedron and be done
4655 if(!found_diags) {
4656 addHexahedron(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], to, source);
4657 Msg::Error("Degenerated hexahedron in extrusion of volume %d", to->tag());
4658 return 1;
4659 }
4660 // test for top and bottom diagonals parallel and incident on degen vertex
4661 else if((n1[4] == dup[0] || n2[4] == dup[0]) &&
4662 (n1[5] == dup[0] + 4 || n2[5] == dup[0] + 4)) {
4663 // first pyramid / tet set
4664 if(n1[(dup[0] + 1) % 4] >= 0) {
4665 if(n1[(dup[0] + 1) % 4] == (dup[0] + 1) % 4) {
4666 addTetrahedron(v[(dup[0] + 1) % 4], v[(dup[0] + 1) % 4 + 4],
4667 v[(dup[0] + 2) % 4 + 4], v[dup[0]], to, source);
4668 addTetrahedron(v[(dup[0] + 1) % 4], v[(dup[0] + 2) % 4 + 4],
4669 v[(dup[0] + 2) % 4], v[dup[0]], to, source);
4670 }
4671 else if(n1[(dup[0] + 1) % 4] == (dup[0] + 1) % 4 + 4) {
4672 addTetrahedron(v[(dup[0] + 1) % 4 + 4], v[(dup[0] + 2) % 4],
4673 v[(dup[0] + 1) % 4], v[dup[0]], to, source);
4674 addTetrahedron(v[(dup[0] + 1) % 4 + 4], v[(dup[0] + 2) % 4 + 4],
4675 v[(dup[0] + 2) % 4], v[dup[0]], to, source);
4676 }
4677 else
4678 Msg::Error("1: In QuadToTriHexPri(), badly subdivided degenerate "
4679 "hexahedron. Mesh for region %d has errors.",
4680 to->tag());
4681 }
4682 else
4683 addPyramid(v[(dup[0] + 1) % 4], v[(dup[0] + 1) % 4 + 4],
4684 v[(dup[0] + 2) % 4 + 4], v[(dup[0] + 2) % 4], v[dup[0]], to,
4685 source);
4686 // second pyramid / tet set
4687 if(n1[(dup[0] + 2) % 4] >= 0) {
4688 if(n1[(dup[0] + 2) % 4] == (dup[0] + 2) % 4) {
4689 addTetrahedron(v[(dup[0] + 2) % 4], v[(dup[0] + 2) % 4 + 4],
4690 v[(dup[0] + 3) % 4 + 4], v[dup[0]], to, source);
4691 addTetrahedron(v[(dup[0] + 2) % 4], v[(dup[0] + 3) % 4 + 4],
4692 v[(dup[0] + 3) % 4], v[dup[0]], to, source);
4693 }
4694 else if(n1[(dup[0] + 2) % 4] == (dup[0] + 2) % 4 + 4) {
4695 addTetrahedron(v[(dup[0] + 2) % 4 + 4], v[(dup[0] + 3) % 4],
4696 v[(dup[0] + 2) % 4], v[dup[0]], to, source);
4697 addTetrahedron(v[(dup[0] + 2) % 4 + 4], v[(dup[0] + 3) % 4 + 4],
4698 v[(dup[0] + 3) % 4], v[dup[0]], to, source);
4699 }
4700 else
4701 Msg::Error("2: In QuadToTriHexPri(), badly subdivided degenerate "
4702 "hexahedron. Mesh for region %d has errors.",
4703 to->tag());
4704 }
4705 else
4706 addPyramid(v[(dup[0] + 2) % 4], v[(dup[0] + 2) % 4 + 4],
4707 v[(dup[0] + 3) % 4 + 4], v[(dup[0] + 3) % 4], v[dup[0]], to,
4708 source);
4709 return 1;
4710 }
4711
4712 // test for top and bottom diagonals parallel and NOT on degen vertex
4713 else if((n1[4] == (dup[0] + 1) % 4 || n2[4] == (dup[0] + 1) % 4) &&
4714 (n1[5] == (dup[0] + 1) % 4 + 4 || n2[5] == (dup[0] + 1) % 4 + 4)) {
4715 // See if the prism section has to be divided and if it requires the inner
4716 // surface to be cut
4717
4718 int tet_top = -1;
4719 int n_inner[2];
4720 n_inner[0] = -1;
4721 n_inner[1] = -2;
4722 if(n1[(dup[0] + 1) % 4] >= 0 &&
4723 n1[(dup[0] + 2) % 4] != n2[(dup[0] + 1) % 4]) {
4724 tet_top = n1[(dup[0] + 1) % 4];
4725 n_inner[0] = n1[(dup[0] + 1) % 4];
4726 int add = n_inner[0] > 3 ? 0 : 4;
4727 n_inner[1] = (dup[0] + 3) % 4 + add;
4728 }
4729 else if(n1[(dup[0] + 2) % 4] >= 0 &&
4730 n1[(dup[0] + 2) % 4] != n2[(dup[0] + 1) % 4]) {
4731 tet_top = n2[(dup[0] + 2) % 4];
4732 n_inner[1] = n2[(dup[0] + 2) % 4];
4733 int add = n_inner[1] > 3 ? 0 : 4;
4734 n_inner[0] = (dup[0] + 1) % 4 + add;
4735 }
4736 else if(n1[(dup[0] + 1) % 4] >= 0)
4737 tet_top = n2[(dup[0] + 1) % 4];
4738
4739 if(tet_top >= 0) {
4740 int add = tet_top > 3 ? 0 : 4;
4741 addTetrahedron(v[(dup[0] + 1) % 4 + add], v[(dup[0] + 2) % 4 + add],
4742 v[(dup[0] + 3) % 4 + add], v[tet_top], to, source);
4743 if(n_inner[0] < 0)
4744 addPyramid(v[(dup[0] + 1) % 4], v[(dup[0] + 1) % 4 + 4],
4745 v[(dup[0] + 3) % 4 + 4], v[(dup[0] + 3) % 4], v[tet_top], to,
4746 source);
4747 else {
4748 int base = tet_top - 4 + add == (dup[0] + 1) % 4 ? (dup[0] + 2) % 4 :
4749 (dup[0] + 1) % 4;
4750 if(n1[base] < 0)
4751 addPyramid(v[base], v[base + 4], v[(base + 1) % 4 + 4],
4752 v[(base + 1) % 4], v[tet_top], to, source);
4753 else {
4754 int add_2 = n1[base] > 3 ? -4 : 4;
4755 addTetrahedron(v[n1[base]], v[n1[base] + add_2], v[n2[base]],
4756 v[tet_top], to, source);
4757 addTetrahedron(v[n1[base]], v[n2[base]], v[n2[base] - add_2],
4758 v[tet_top], to, source);
4759 }
4760 }
4761 }
4762 else
4763 addPrism(v[(dup[0] + 1) % 4], v[(dup[0] + 2) % 4], v[(dup[0] + 3) % 4],
4764 v[(dup[0] + 1) % 4 + 4], v[(dup[0] + 2) % 4 + 4],
4765 v[(dup[0] + 3) % 4 + 4], to, source);
4766 // add the pyramid or tets that include the degen vertex
4767 if(n_inner[0] < 0)
4768 addPyramid(v[(dup[0] + 1) % 4], v[(dup[0] + 1) % 4 + 4],
4769 v[(dup[0] + 3) % 4 + 4], v[(dup[0] + 3) % 4], v[dup[0]], to,
4770 source);
4771 else {
4772 int add = n_inner[0] > 3 ? -4 : 4;
4773 addTetrahedron(v[n_inner[0]], v[n_inner[0] + add], v[n_inner[1]],
4774 v[dup[0]], to, source);
4775 addTetrahedron(v[n_inner[0]], v[n_inner[1]], v[n_inner[1] - add],
4776 v[dup[0]], to, source);
4777 }
4778 return 1;
4779 }
4780
4781 // see if there is a way to divide non-parallel top and bottom verts
4782 else if(n1[(dup[0] + 1) % 4] >= 0 || n1[(dup[0] + 2) % 4] >= 0) {
4783 // parameters for a pyramid (that may or may not be divided into tets)
4784 int base = -1, top = -2;
4785 // if corner opposite degen corner has two diagonals meeting on it:
4786 if(n2[(dup[0] + 1) % 4] >= 0 &&
4787 n1[(dup[0] + 2) % 4] == n2[(dup[0] + 1) % 4]) {
4788 if(n1[4] == n1[(dup[0] + 2) % 4] || n2[4] == n1[(dup[0] + 2) % 4]) {
4789 top = n1[(dup[0] + 2) % 4];
4790 base = 5;
4791 }
4792 else if(n1[5] == n1[(dup[0] + 2) % 4] || n2[5] == n1[(dup[0] + 2) % 4]) {
4793 top = n1[(dup[0] + 2) % 4];
4794 base = 4;
4795 }
4796 }
4797 // if a side corner not opposite the degenerate vertex has two diagonals
4798 // meeting
4799 if(base < 0 && n2[(dup[0] + 2) % 4] >= 0) {
4800 if(n2[(dup[0] + 2) % 4] == (dup[0] + 3) % 4 + 4 &&
4801 (n1[5] == (dup[0] + 3) % 4 + 4 || n2[5] == (dup[0] + 3) % 4 + 4)) {
4802 top = (dup[0] + 3) % 4 + 4;
4803 base = 4;
4804 }
4805 else if(n2[(dup[0] + 2) % 4] == (dup[0] + 3) % 4 &&
4806 (n1[4] == (dup[0] + 3) % 4 || n2[4] == (dup[0] + 3) % 4)) {
4807 top = (dup[0] + 3) % 4;
4808 base = 5;
4809 }
4810 }
4811 if(base < 0 && n1[(dup[0] + 1) % 4] >= 0) {
4812 if(n1[(dup[0] + 1) % 4] == (dup[0] + 1) % 4 + 4 &&
4813 (n1[5] == (dup[0] + 1) % 4 + 4 || n2[5] == (dup[0] + 1) % 4 + 4)) {
4814 top = (dup[0] + 1) % 4 + 4;
4815 base = 4;
4816 }
4817 else if(n1[(dup[0] + 1) % 4] == (dup[0] + 1) % 4 &&
4818 (n1[4] == (dup[0] + 1) % 4 || n2[4] == (dup[0] + 1) % 4)) {
4819 top = (dup[0] + 1) % 4;
4820 base = 5;
4821 }
4822 }
4823
4824 // if 4-corner lateral faces have diagonals that don't meet...
4825 if(base < 0 && n1[(dup[0] + 1) % 4] >= 0 && n1[(dup[0] + 2) % 4] >= 0 &&
4826 n1[(dup[0] + 2) % 4] != n2[(dup[0] + 1) % 4]) {
4827 top = n1[(dup[0] + 1) % 4];
4828 base = (top < 4) ? 5 : 4;
4829 }
4830
4831 // if there is a valid top and base, make the element
4832 if(base >= 0 && top >= 0) {
4833 // if pyramid top is NOT opposite degen vertex
4834 if(top == n1[(dup[0] + 1) % 4] || top == n2[(dup[0] + 2) % 4]) {
4835 int add = base == 4 ? 0 : 4;
4836 if(n1[base] < 0) {
4837 addPyramid(v[0 + add], v[1 + add], v[2 + add], v[3 + add], v[top], to,
4838 source);
4839 }
4840 else {
4841 addTetrahedron(v[n1[base]], v[(n1[base] - add + 1) % 4 + add],
4842 v[n2[base]], v[top], to, source);
4843 addTetrahedron(v[n1[base]], v[n2[base]],
4844 v[(n1[base] - add + 3) % 4 + add], v[top], to, source);
4845 }
4846 int base2 = -1;
4847 if(base == 4 && (n1[5] == top || n2[5] == top)) {
4848 base2 =
4849 top == (dup[0] + 1) % 4 + 4 ? (dup[0] + 2) % 4 : (dup[0] + 1) % 4;
4850 }
4851 else if(base == 5 && (n1[4] == top || n2[4] == top)) {
4852 base2 = top == (dup[0] + 1) % 4 ? (dup[0] + 2) % 4 : (dup[0] + 1) % 4;
4853 }
4854 else
4855 base2 = base == 5 ? 4 : 5;
4856
4857 if(base2 != 4 && base2 != 5) {
4858 addTetrahedron(v[top], v[(top - 4 + add + 2) % 4],
4859 v[(top - 4 + add + 2) % 4 + 4], v[dup[0]], to, source);
4860 if(n1[base2] >= 0) {
4861 int add_base2 = n1[base2] < 4 ? 4 : -4;
4862 addTetrahedron(v[n1[base2]], v[n1[base2] + add_base2], v[n2[base2]],
4863 v[top], to, source);
4864 addTetrahedron(v[n1[base2]], v[n2[base2]], v[n2[base2] - add_base2],
4865 v[top], to, source);
4866 }
4867 else
4868 addPyramid(v[base2], v[(base2 + 4)], v[(base2 + 1) % 4 + 4],
4869 v[(base2 + 1) % 4], v[top], to, source);
4870 }
4871 else {
4872 int top2 = (top - 4 + add + 2) % 4 + add;
4873 if(n1[base2] >= 0) {
4874 addTetrahedron(v[n1[base2]],
4875 v[(n1[base2] - 4 + add + 1) % 4 + 4 - add],
4876 v[n2[base2]], v[top2], to, source);
4877 addTetrahedron(v[n1[base2]],
4878 v[(n1[base2] - 4 + add + 3) % 4 + 4 - add],
4879 v[n2[base2]], v[top2], to, source);
4880 }
4881 else
4882 addPyramid(v[0 + 4 - add], v[1 + 4 - add], v[2 + 4 - add],
4883 v[3 + 4 - add], v[top2], to, source);
4884
4885 int tet_base = top2 - add == (dup[0] + 1) % 4 ? (dup[0] + 1) % 4 :
4886 (dup[0] + 2) % 4;
4887 if(tet_base == (dup[0] + 1) % 4) {
4888 int add_tet_base = n2[tet_base] > 3 ? -4 : 4;
4889 addTetrahedron(v[n1[tet_base]], v[n2[tet_base]],
4890 v[n2[tet_base] + add_tet_base], v[top], to, source);
4891 }
4892 else {
4893 int add_tet_base = n1[tet_base] > 3 ? -4 : 4;
4894 addTetrahedron(v[n1[tet_base]], v[n2[tet_base]],
4895 v[n1[tet_base] + add_tet_base], v[top], to, source);
4896 }
4897 }
4898 return 1;
4899 }
4900
4901 // if pyramid top is opposite degenerate vertex
4902 else if(top == (dup[0] + 2) % 4 || top == (dup[0] + 2) % 4 + 4) {
4903 int add = base == 4 ? 0 : 4;
4904 if(n1[base] >= 0) {
4905 addTetrahedron(v[n1[base]], v[(n1[base] - add + 1) % 4 + add],
4906 v[n2[base]], v[top], to, source);
4907 addTetrahedron(v[n1[base]], v[(n1[base] - add + 3) % 4 + add],
4908 v[n2[base]], v[top], to, source);
4909 }
4910 else
4911 addPyramid(v[0 + add], v[1 + add], v[2 + add], v[3 + add], v[top], to,
4912 source);
4913
4914 addTetrahedron(v[dup[0]], v[(dup[0] + 1) % 4], v[(dup[0] + 1) % 4 + 4],
4915 v[top], to, source);
4916 addTetrahedron(v[dup[0]], v[(dup[0] + 3) % 4], v[(dup[0] + 3) % 4 + 4],
4917 v[top], to, source);
4918 return 1;
4919 }
4920 else
4921 Msg::Error("3: In createOnePtDegenHexElems(), badly subdivided "
4922 "degenerate hexahedron. Mesh for region %d has errors.",
4923 to->tag());
4924 }
4925 }
4926
4927 return 0;
4928 }
4929
4930 // Construct the elements that subdivide a full hexahedron extrusion.
createFullHexElems(std::vector<MVertex * > & v,GRegion * to,ExtrudeParams * ep,int j,int k,int dup[],MElement * source,std::vector<int> n1,std::vector<int> n2,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,unsigned int lat_tri_diags_size,bool bnd_elem,bool is_addverts,bool found_diags)4931 static inline bool createFullHexElems(
4932 std::vector<MVertex *> &v, GRegion *to, ExtrudeParams *ep, int j, int k,
4933 int dup[], MElement *source, std::vector<int> n1, std::vector<int> n2,
4934 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
4935 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4936 &problems,
4937 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
4938 &problems_new,
4939 unsigned int lat_tri_diags_size, bool bnd_elem, bool is_addverts,
4940 bool found_diags)
4941 {
4942 if(!ep) return 0;
4943
4944 // First: does this hexa have ANY dividing diagonals? If no, return
4945 if(!found_diags) {
4946 addHexahedron(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], to, source);
4947 return 1;
4948 }
4949
4950 // First test: Try to identify a corner with three diagonals converging on it
4951 // This is second because first test gives better quality...i think.
4952
4953 int pyramid_top = -1, base_face_1 = -1;
4954
4955 // variables to hold pyramid base corners and diagonal node indices:
4956 /*int b1_diag[2], b2_diag[2], b3_diag[2];
4957 for( int p = 0; p < 2; p++ ){
4958 b1_diag[p] = -1-p; b2_diag[p] = -2-p; b3_diag[p] = -3-p;
4959 }*/
4960
4961 // find a 3-diag corner
4962 for(int s = 0; s < 2; s++) {
4963 int p = !s ? 4 : 5;
4964 if(n1[p] >= 0) {
4965 int add = !s ? 0 : 4;
4966 int p1 = n1[p] - add, p2 = (n1[p] - add + 3) % 4;
4967 if(n1[p1] >= 0 && n1[p2] >= 0 && (n1[p] == n1[p1] || n1[p] == n2[p1]) &&
4968 (n1[p] == n1[p2] || n1[p] == n2[p2])) {
4969 pyramid_top = n1[p];
4970 base_face_1 = !s ? 5 : 4;
4971 break;
4972 }
4973 p1 = n2[p] - add;
4974 p2 = (n2[p] - add + 3) % 4;
4975 if(n1[p1] >= 0 && n1[p2] >= 0 && (n2[p] == n1[p1] || n2[p] == n2[p1]) &&
4976 (n2[p] == n1[p2] || n2[p] == n2[p2])) {
4977 pyramid_top = n2[p];
4978 base_face_1 = !s ? 5 : 4;
4979 break;
4980 }
4981 }
4982 }
4983
4984 // If pyramid top (3-diag corner) is found, then check for diagonal division
4985 // of bases:
4986
4987 if(pyramid_top >= 0) {
4988 // ( base of first pyramid always the hexa's top or bottom )
4989 int add = pyramid_top < 4 ? 0 : 4;
4990 int base_face_2 = (pyramid_top - add + 1) % 4;
4991 int base_face_3 = (pyramid_top - add + 2) % 4;
4992
4993 // Now construct shapes.
4994 for(int p = 0; p < 3; p++) {
4995 int b[4], bn1, bn2;
4996 if(!p) {
4997 int add = base_face_1 == 4 ? 0 : 4;
4998 b[0] = 0 + add;
4999 b[1] = 1 + add;
5000 b[2] = 2 + add;
5001 b[3] = 3 + add;
5002 bn1 = n1[base_face_1];
5003 bn2 = n2[base_face_1];
5004 }
5005 else if(p == 1) {
5006 b[0] = base_face_2;
5007 b[1] = b[0] + 4;
5008 b[2] = (b[0] + 1) % 4 + 4;
5009 b[3] = (b[0] + 1) % 4;
5010 bn1 = n1[base_face_2];
5011 bn2 = n2[base_face_3];
5012 }
5013 else {
5014 b[0] = base_face_3;
5015 b[1] = b[0] + 4;
5016 b[2] = (b[0] + 1) % 4 + 4;
5017 b[3] = (b[0] + 1) % 4;
5018 bn1 = n1[base_face_3];
5019 bn2 = n2[base_face_3];
5020 }
5021
5022 if(bn1 < 0)
5023 addPyramid(v[b[0]], v[b[1]], v[b[2]], v[b[3]], v[pyramid_top], to,
5024 source);
5025 else {
5026 if(bn1 == b[0] || bn2 == b[0]) {
5027 addTetrahedron(v[b[0]], v[b[1]], v[b[2]], v[pyramid_top], to, source);
5028 addTetrahedron(v[b[0]], v[b[2]], v[b[3]], v[pyramid_top], to, source);
5029 }
5030 else {
5031 addTetrahedron(v[b[0]], v[b[1]], v[b[3]], v[pyramid_top], to, source);
5032 addTetrahedron(v[b[1]], v[b[2]], v[b[3]], v[pyramid_top], to, source);
5033 }
5034 }
5035 }
5036 // return if successful
5037 return 1;
5038 }
5039
5040 // Second full hexa division possibility (two prisms, possibly subdivided):
5041
5042 // YES, THIS IS HORRIBLE!!!!
5043
5044 // Diagram of prism-centric coordinates:
5045 /* ___________ p5
5046 |\ /|\
5047 | \ / | \
5048 | \ / | \
5049 | \/______|___\
5050 | / | | /|p2
5051 p3 |/___|___p4_| / |
5052 \ | /\ | prism 1,
5053 \ | / \ | external face 2 (side)
5054 \ | / \ |
5055 \|/_________\|
5056 p0 p1
5057 prism 1,
5058 external face 1 (bottom)
5059
5060 NOTE: for Prism 2, the prism coords MIRROR the coords for
5061 prism 1 about the internal shared face.
5062 */
5063
5064 // prism_v holds prism-centric indices of vertices--new coords!!!
5065 int prism_v[2][6], prism_face[2][2];
5066 // t_prism arrays for tet verts...p_prism arrays for pyramid verts (if any)
5067 int t_prism[2][3][4], p_prism[2][5];
5068 for(int s = 0; s < 2; s++) {
5069 for(int t = 0; t < 5; t++) p_prism[s][t] = -2 * s - 2;
5070 for(int t = 0; t < 3; t++) {
5071 for(int w = 0; w < 4; w++) t_prism[s][t][w] = -2 * s * t * w - 2;
5072 }
5073 }
5074
5075 int prism_slice_valid = false; // set to true when found a valid slicing
5076
5077 // find valid prism slicing
5078 for(int p_ind = 0; p_ind < 3; p_ind++) {
5079 int p = p_ind;
5080 if(p_ind == 2) p = 4; // move to the top-to-bottom slicing
5081
5082 prism_slice_valid = false;
5083
5084 // n_div1, n_div2 are indices of the vertice in the first diagonal
5085 int n_div1 = -1, n_div2 = -2;
5086 if(p < 4) {
5087 if((n1[p] == p || n2[p] == p) && (n1[(p + 2) % 4] == (p + 2) % 4 + 4 ||
5088 n2[(p + 2) % 4] == (p + 2) % 4 + 4)) {
5089 n_div1 = p;
5090 n_div2 = (p + 1) % 4 + 4;
5091 }
5092 if((n1[p] == p + 4 || n2[p] == p + 4) &&
5093 (n1[(p + 2) % 4] == (p + 2) % 4 || n2[(p + 2) % 4] == (p + 2) % 4)) {
5094 n_div1 = p + 4;
5095 n_div2 = (p + 1) % 4;
5096 }
5097 }
5098 else {
5099 if((n1[4] == 0 || n2[4] == 0) && (n1[5] == 4 || n2[5] == 4)) {
5100 n_div1 = 0;
5101 n_div2 = 2;
5102 }
5103 if((n1[4] == 1 || n2[4] == 1) && (n1[5] == 5 || n2[5] == 5)) {
5104 n_div1 = 1;
5105 n_div2 = 3;
5106 }
5107 }
5108
5109 if(n_div1 < 0) continue;
5110
5111 // There are two prisms. Find the vertices of any external and internal
5112 // prism
5113 // face diagonals.
5114 int ext_diag[2][2][2], intern_diag[2];
5115 for(int s = 0; s < 2; s++) {
5116 intern_diag[s] = -5 - s * s - s;
5117 for(int t = 0; t < 2; t++) {
5118 ext_diag[s][t][0] = -1 - t * t - t;
5119 ext_diag[s][t][1] = -2 - t * t - t;
5120 }
5121 }
5122
5123 // Create arrays of verts of both prisms in prism-centric coords (see ascii
5124 // diagram above) to keep sanity. p0 always refereces a vertex position on a
5125 // slicing diagonal (see ascii diagram above again.
5126 int p0;
5127 if((n_div1 > 3 && n_div2 < 4) || (n_div1 < 4 && n_div2 > 3)) {
5128 p0 = n_div1 < 4 ? p : (p + 2) % 4;
5129 prism_v[0][0] = p0;
5130 prism_v[0][1] = (p0 + 1) % 4;
5131 prism_v[0][2] = (p0 + 1) % 4 + 4;
5132 prism_v[0][3] = (p0 + 3) % 4;
5133 prism_v[0][4] = (p0 + 2) % 4;
5134 prism_v[0][5] = (p0 + 2) % 4 + 4;
5135 prism_v[1][0] = p0;
5136 prism_v[1][1] = p0 + 4;
5137 prism_v[1][2] = (p0 + 1) % 4 + 4;
5138 prism_v[1][3] = (p0 + 3) % 4;
5139 prism_v[1][4] = (p0 + 3) % 4 + 4;
5140 prism_v[1][5] = (p0 + 2) % 4 + 4;
5141 prism_face[0][0] = 4;
5142 prism_face[0][1] = (p0 + 1) % 4;
5143 prism_face[1][0] = (p0 + 3) % 4;
5144 prism_face[1][1] = 5;
5145 }
5146 else {
5147 p0 = n_div1;
5148 prism_v[0][0] = p0;
5149 prism_v[0][1] = (p0 + 1) % 4;
5150 prism_v[0][2] = (p0 + 2) % 4;
5151 prism_v[0][3] = p0 + 4;
5152 prism_v[0][4] = (p0 + 1) % 4 + 4;
5153 prism_v[0][5] = (p0 + 2) % 4 + 4;
5154 prism_v[1][0] = p0;
5155 prism_v[1][1] = (p0 + 3) % 4;
5156 prism_v[1][2] = (p0 + 2) % 4;
5157 prism_v[1][3] = p0 + 4;
5158 prism_v[1][4] = (p0 + 3) % 4 + 4;
5159 prism_v[1][5] = (p0 + 2) % 4 + 4;
5160 prism_face[0][0] = p0;
5161 prism_face[0][1] = (p0 + 1) % 4;
5162 prism_face[1][0] = (p0 + 3) % 4;
5163 prism_face[1][1] = (p0 + 2) % 4;
5164 }
5165
5166 // 3 ways this prism slice can work:
5167 // 1. Two additional diagonals which meet at a vertex.
5168 // 2. Two additional opposing aligned diagonals
5169 // 3. No more diagonals, just two prisms
5170
5171 // get external diagonals for first and second prism
5172 for(int s = 0; s < 2; s++) {
5173 if(n1[prism_face[s][0]] == prism_v[s][1] ||
5174 n2[prism_face[s][0]] == prism_v[s][1]) {
5175 ext_diag[s][0][0] = 1;
5176 ext_diag[s][0][1] = 3;
5177 }
5178 else if(n1[prism_face[s][0]] == prism_v[s][4] ||
5179 n2[prism_face[s][0]] == prism_v[s][4]) {
5180 ext_diag[s][0][0] = 4;
5181 ext_diag[s][0][1] = 0;
5182 }
5183 if(n1[prism_face[s][1]] == prism_v[s][1] ||
5184 n2[prism_face[s][1]] == prism_v[s][1]) {
5185 ext_diag[s][1][0] = 1;
5186 ext_diag[s][1][1] = 5;
5187 }
5188 else if(n1[prism_face[s][1]] == prism_v[s][4] ||
5189 n2[prism_face[s][1]] == prism_v[s][4]) {
5190 ext_diag[s][1][0] = 4;
5191 ext_diag[s][1][1] = 2;
5192 }
5193 }
5194
5195 // if first prism needs the internal diagonal
5196 if(ext_diag[0][0][0] >= 0 && ext_diag[0][1][0] != ext_diag[0][0][0]) {
5197 intern_diag[0] = ext_diag[0][0][1];
5198 if(ext_diag[0][0][1] == 0)
5199 intern_diag[1] = 5;
5200 else
5201 intern_diag[1] = 2;
5202 }
5203 else if(ext_diag[0][1][0] >= 0 && ext_diag[0][0][0] != ext_diag[0][1][0]) {
5204 intern_diag[0] = ext_diag[0][1][1];
5205 if(ext_diag[0][1][1] == 2)
5206 intern_diag[1] = 3;
5207 else
5208 intern_diag[1] = 0;
5209 }
5210
5211 // if 2nd prism needs the internal diagonal to work, check to see if the
5212 // internal diagonal exists and, if so, if it is consistent. If it doesn't
5213 // exist, make it
5214 if((ext_diag[1][0][0] != ext_diag[1][1][0] ||
5215 (ext_diag[1][0][0] < 0 && ext_diag[1][1][0] < 0)) &&
5216 intern_diag[0] >= 0 && intern_diag[0] != ext_diag[1][0][1] &&
5217 intern_diag[0] != ext_diag[1][1][1] &&
5218 intern_diag[1] != ext_diag[1][0][1] &&
5219 intern_diag[1] != ext_diag[1][1][1]) {
5220 continue;
5221 }
5222 // add internal diagonal if needed
5223 else if(intern_diag[0] < 0 && ext_diag[1][0][0] >= 0 &&
5224 ext_diag[1][1][0] != ext_diag[1][0][0]) {
5225 intern_diag[0] = ext_diag[1][0][1];
5226 if(ext_diag[1][0][1] == 0)
5227 intern_diag[1] = 5;
5228 else
5229 intern_diag[1] = 2;
5230 }
5231 else if(intern_diag[0] < 0 && ext_diag[1][1][0] >= 0 &&
5232 ext_diag[1][0][0] != ext_diag[1][1][0]) {
5233 intern_diag[0] = ext_diag[1][1][1];
5234 if(ext_diag[1][1][1] == 2)
5235 intern_diag[1] = 3;
5236 else
5237 intern_diag[1] = 0;
5238 }
5239
5240 // this check sees if the internal shared prism face is diagonalized, but
5241 // one prism has no other diags
5242 if(intern_diag[0] >= 0 &&
5243 ((ext_diag[0][0][0] < 0 && ext_diag[0][1][0] < 0) ||
5244 (ext_diag[1][0][0] < 0 && ext_diag[1][1][0] < 0))) {
5245 continue;
5246 }
5247
5248 // if make it to here, prism slice is valid
5249 prism_slice_valid = true;
5250
5251 // make arrays of vertices for making the polyhedra
5252 if(prism_slice_valid) {
5253 for(int s = 0; s < 2; s++) {
5254 if(ext_diag[s][0][0] < 0 && ext_diag[s][1][0] < 0)
5255 continue;
5256 else if(ext_diag[s][0][0] >= 0 &&
5257 ext_diag[s][0][0] == ext_diag[s][1][0]) {
5258 int add = (ext_diag[s][0][0] < 3) ? 3 : -3;
5259 t_prism[s][0][0] = ext_diag[s][0][0];
5260 t_prism[s][0][1] = ext_diag[s][0][1];
5261 t_prism[s][0][2] = ext_diag[s][1][1];
5262 t_prism[s][0][3] = ext_diag[s][0][0] + add;
5263 if(intern_diag[0] < 0) {
5264 p_prism[s][0] = 0;
5265 p_prism[s][1] = 2;
5266 p_prism[s][2] = 5;
5267 p_prism[s][3] = 3;
5268 p_prism[s][4] = ext_diag[s][0][0];
5269 }
5270 else {
5271 int v_tmp1 = (intern_diag[0] == 0 || intern_diag[1] == 0) ? 2 : 0;
5272 int v_tmp2 = (intern_diag[0] == 0 || intern_diag[1] == 0) ? 3 : 5;
5273
5274 t_prism[s][1][0] = intern_diag[0];
5275 t_prism[s][1][1] = intern_diag[1];
5276 t_prism[s][1][2] = v_tmp1;
5277 t_prism[s][1][3] = ext_diag[s][0][0];
5278 t_prism[s][2][0] = intern_diag[0];
5279 t_prism[s][2][1] = intern_diag[1];
5280 t_prism[s][2][2] = v_tmp2;
5281 t_prism[s][2][3] = ext_diag[s][0][0];
5282 }
5283 }
5284 else if(intern_diag[0] >= 0) {
5285 int p_tet_start =
5286 (ext_diag[s][0][0] >= 0) ? ext_diag[s][0][0] : ext_diag[s][1][0];
5287 int p_pyr_top = (p_tet_start == ext_diag[s][0][0]) ?
5288 ext_diag[s][0][1] :
5289 ext_diag[s][1][1];
5290 int p_tet_top;
5291 if(p_tet_start == ext_diag[s][0][0])
5292 p_tet_top = (ext_diag[s][0][1] == 3) ? 0 : 3;
5293 else
5294 p_tet_top = (ext_diag[s][1][1] == 5) ? 2 : 5;
5295
5296 t_prism[s][0][0] = p_tet_start;
5297 t_prism[s][0][1] = intern_diag[0];
5298 t_prism[s][0][2] = intern_diag[1];
5299 t_prism[s][0][3] = p_tet_top;
5300
5301 if(p_tet_start == ext_diag[s][0][0]) {
5302 if(ext_diag[s][1][0] < 0) {
5303 p_prism[s][0] = 1;
5304 p_prism[s][1] = 2;
5305 p_prism[s][2] = 5;
5306 p_prism[s][3] = 4;
5307 p_prism[s][4] = p_pyr_top;
5308 }
5309 else {
5310 t_prism[s][1][0] = ext_diag[s][1][0];
5311 t_prism[s][1][1] = ext_diag[s][1][1];
5312 t_prism[s][1][2] = (ext_diag[s][1][0] == 4) ? 1 : 4;
5313 t_prism[s][1][3] = p_pyr_top;
5314 t_prism[s][2][0] = ext_diag[s][1][0];
5315 t_prism[s][2][1] = ext_diag[s][1][1];
5316 t_prism[s][2][2] = (ext_diag[s][1][0] == 4) ? 5 : 2;
5317 t_prism[s][2][3] = p_pyr_top;
5318 }
5319 }
5320 else {
5321 if(ext_diag[s][0][0] < 0) {
5322 p_prism[s][0] = 0;
5323 p_prism[s][1] = 1;
5324 p_prism[s][2] = 4;
5325 p_prism[s][3] = 3;
5326 p_prism[s][4] = p_pyr_top;
5327 }
5328 else {
5329 t_prism[s][1][0] = ext_diag[s][0][0];
5330 t_prism[s][1][1] = ext_diag[s][0][1];
5331 t_prism[s][1][2] = (ext_diag[s][0][0] == 4) ? 3 : 4;
5332 t_prism[s][1][3] = p_pyr_top;
5333 t_prism[s][2][0] = ext_diag[s][0][0];
5334 t_prism[s][2][1] = ext_diag[s][0][1];
5335 t_prism[s][2][2] = (ext_diag[s][0][0] == 4) ? 1 : 0;
5336 t_prism[s][2][3] = p_pyr_top;
5337 }
5338 }
5339 }
5340 // translate arrays to 'real' vertex coordinates
5341 for(int t = 0; t < 3; t++) {
5342 for(int w = 0; w < 4; w++)
5343 t_prism[s][t][w] = (t_prism[s][t][w] >= 0) ?
5344 prism_v[s][t_prism[s][t][w]] :
5345 t_prism[s][t][w];
5346 }
5347 for(int t = 0; t < 5; t++)
5348 p_prism[s][t] =
5349 (p_prism[s][t] >= 0) ? prism_v[s][p_prism[s][t]] : p_prism[s][t];
5350 }
5351
5352 // Now construct shapes for prism slice through configuration
5353 for(int s = 0; s < 2; s++) {
5354 if(t_prism[s][0][0] < 0 && p_prism[s][0] < 0) {
5355 addPrism(v[prism_v[s][0]], v[prism_v[s][1]], v[prism_v[s][2]],
5356 v[prism_v[s][3]], v[prism_v[s][4]], v[prism_v[s][5]], to,
5357 source);
5358 }
5359 else {
5360 for(int t = 0; t < 3; t++) {
5361 if(t_prism[s][t][0] >= 0)
5362 addTetrahedron(v[t_prism[s][t][0]], v[t_prism[s][t][1]],
5363 v[t_prism[s][t][2]], v[t_prism[s][t][3]], to,
5364 source);
5365 }
5366 if(p_prism[s][0] >= 0)
5367 addPyramid(v[p_prism[s][0]], v[p_prism[s][1]], v[p_prism[s][2]],
5368 v[p_prism[s][3]], v[p_prism[s][4]], to, source);
5369 }
5370 }
5371 // return now
5372 return 1;
5373
5374 } // end of "if prism_slice_valid" statement
5375 } // end of p_ind loop over opposite pairs of diagonals
5376
5377 return 0; // if exhaust possibilities, default to this
5378 }
5379
5380 // Overall function that creates the elements that subdivide any whole element
5381 // extruded from a quadrangle.
QuadToTriHexPri(std::vector<MVertex * > & v,GRegion * to,int j,int k,MElement * source,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems_new,unsigned int lat_tri_diags_size,bool bnd_elem,bool is_addverts,bool diag_search,MVertexRTree & pos)5382 static inline void QuadToTriHexPri(
5383 std::vector<MVertex *> &v, GRegion *to, int j, int k, MElement *source,
5384 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
5385 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
5386 &problems,
5387 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
5388 &problems_new,
5389 unsigned int lat_tri_diags_size, bool bnd_elem, bool is_addverts,
5390 bool diag_search, MVertexRTree &pos)
5391 {
5392 int dup[4];
5393 int m = 0;
5394 for(int i = 0; i < 4; i++)
5395 if(v[i] == v[i + 4]) dup[m++] = i;
5396
5397 bool is_problem = false;
5398
5399 // is element marked as needing internal vertex?
5400 if(!is_addverts) {
5401 std::pair<unsigned int, unsigned int> jk_pair(j, k);
5402 std::map<MElement *,
5403 std::set<std::pair<unsigned int, unsigned int> > >::iterator
5404 itprob;
5405 itprob = problems.find(source);
5406 if(itprob != problems.end()) {
5407 if((*itprob).second.find(jk_pair) != (*itprob).second.end())
5408 is_problem = true;
5409 }
5410 }
5411
5412 ExtrudeParams *ep = to->meshAttributes.extrude;
5413
5414 // variables to hold of each faces's diagonal vertex nodes
5415 bool found_diags = false;
5416 std::vector<int> n1, n2;
5417 n1.assign(6, -3);
5418 n2.assign(6, -4);
5419 if(diag_search) {
5420 for(int p = 0; p < 6; p++) {
5421 n1[p] = -p * p - p - 1;
5422 n2[p] = -p * p - p - 2; // unique negative numbers
5423 if(p < 4) {
5424 if(edgeExists(v[p], v[(p + 1) % 4 + 4], quadToTri_edges)) {
5425 n1[p] = p;
5426 n2[p] = (p + 1) % 4 + 4;
5427 found_diags = true;
5428 }
5429 else if(edgeExists(v[(p + 1) % 4], v[p + 4], quadToTri_edges)) {
5430 n1[p] = (p + 4);
5431 n2[p] = (p + 1) % 4;
5432 found_diags = true;
5433 }
5434 }
5435 else {
5436 int add = (p == 4) ? 0 : 4;
5437 if(edgeExists(v[0 + add], v[2 + add], quadToTri_edges)) {
5438 n1[p] = 0 + add;
5439 n2[p] = 2 + add;
5440 found_diags = true;
5441 }
5442 else if(edgeExists(v[1 + add], v[3 + add], quadToTri_edges)) {
5443 n1[p] = 1 + add;
5444 n2[p] = 3 + add;
5445 found_diags = true;
5446 }
5447 }
5448 }
5449 }
5450
5451 // BAD SHAPE
5452 if(m > 2 || m < 0) {
5453 Msg::Error("In QuadToTriHexPri(), bad number of degenerate corners.");
5454 return;
5455 }
5456
5457 // Divide by new internal vertex extrusion method?
5458 if(is_addverts && (found_diags || m == 1)) {
5459 MeshWithInternalVertex(to, source, v, n1, n2, pos);
5460 return;
5461 }
5462
5463 // The of the possibilites are for a 'no new vertex' extrusion
5464
5465 // PRISM
5466 else if(m == 2 && !is_problem) {
5467 if(createTwoPtDegenHexElems(
5468 v, to, ep, j, k, dup, source, n1, n2, quadToTri_edges, problems,
5469 problems_new, lat_tri_diags_size, bnd_elem, is_addverts, found_diags))
5470 return;
5471 }
5472
5473 // DEGENERATE HEXAHEDRON
5474 else if(m == 1 && !is_problem) {
5475 if(createOnePtDegenHexElems(
5476 v, to, ep, j, k, dup, source, n1, n2, quadToTri_edges, problems,
5477 problems_new, lat_tri_diags_size, bnd_elem, is_addverts, found_diags))
5478 return;
5479 }
5480
5481 // FULL HEXAHEDRON
5482 else if(!is_problem) {
5483 if(createFullHexElems(v, to, ep, j, k, dup, source, n1, n2, quadToTri_edges,
5484 problems, problems_new, lat_tri_diags_size, bnd_elem,
5485 is_addverts, found_diags))
5486 return;
5487 }
5488
5489 // now take care of unexpected failure to divide without internal vertex
5490 if(!is_problem) {
5491 Msg::Error(
5492 "In QuadToTriHexPri(), Extruded hexahedron needs subdivision, but cannot "
5493 " be divided without internal vertex, and was not previously detected as "
5494 "such. "
5495 " This is a bug. Please Report.");
5496 Msg::Error("j: %d, k: %d", j, k);
5497 QtMakeCentroidVertex(v, &(to->mesh_vertices), to, pos);
5498 std::pair<unsigned int, unsigned int> jk_pair(j, k);
5499 problems_new[source].insert(jk_pair);
5500 is_problem = true;
5501 }
5502
5503 // Mesh with internal vertex
5504 if(is_problem) {
5505 MeshWithInternalVertex(to, source, v, n1, n2, pos);
5506 return;
5507 }
5508 }
5509
5510 // reserves approximately the right amount of memory for quadToTri extrusions
5511 // in the element vectors in the region 'to'
5512 // *** STILL EXPERIMENTAL -- It *kind* of works to limit memory footprint of
5513 // vectors.
5514 /*
5515 static void reserveQuadToTriCapacityForRegion( GRegion *to, GFace *from, bool
5516 is_addverts, unsigned int num_layers, unsigned int lat_tri_diags_size,
5517 CategorizedSourceElements *c, std::map<MElement*, std::set<std::pair<unsigned
5518 int, unsigned int> > > *problems )
5519 {
5520
5521 ExtrudeParams *ep = to->meshAttributes.extrude;
5522 if( !ep )
5523 return;
5524
5525 std::map<MElement*, std::set<std::pair<unsigned int, unsigned int> >
5526 >::iterator itprob; unsigned int num_prob_tri = 0, num_prob_quad = 0; for(
5527 itprob = problems->begin(); itprob != problems->end(); itprob++ ){ if(
5528 itprob->first->getType() == TYPE_TRI ) num_prob_tri += itprob->second.size();
5529 else
5530 num_prob_quad += itprob->second.size();
5531 }
5532 // unsigned int num_bnd_tri = c->three_bnd_pt_tri.size() +
5533 c->other_bnd_tri.size();
5534 // unsigned int num_bnd_quad = c->four_bnd_pt_quad.size() +
5535 c->other_bnd_quad.size();
5536 // unsigned int num_int_tri = c->internal_tri.size();
5537 // unsigned int num_int_quad = c->internal_quad.size();
5538 unsigned int num_tri = from->triangles.size();
5539 unsigned int num_quad = from->quadrangles.size();
5540 // in case !ep->Recombine is ever allowed...
5541 if( !ep->mesh.Recombine )
5542 to->tetrahedra.reserve( num_layers*(3*num_tri + 6*num_quad + 8*num_prob_tri
5543 + 12*num_prob_quad) ); else if( !is_addverts ){
5544 //to->tetrahedra.reserve( (6*num_quad + 3*num_tri +2*lat_tri_diags_size +
5545 8*num_prob_tri + 12*num_prob_quad) ); to->prisms.reserve( num_tri*num_layers );
5546 to->hexahedra.reserve( num_quad*(num_layers-1) );
5547 }
5548 else{
5549 unsigned int extra_verts = to->mesh_vertices.size() -
5550 from->mesh_vertices.size()*(num_layers-1); to->hexahedra.reserve(
5551 num_quad*(num_layers-1) ); to->prisms.reserve( num_tri*num_layers );
5552 to->pyramids.reserve( num_prob_quad + num_prob_tri + extra_verts*4 +
5553 num_quad ); to->tetrahedra.reserve( num_prob_quad + num_prob_tri + extra_verts
5554 );
5555 }
5556
5557 }
5558 */
5559
5560 // displays for the user a list of the body centered vertices created for
5561 // problem elements.
listBodyCenteredVertices(GRegion * to,bool is_addverts,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> * problems,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> * problems_new,MVertexRTree * pos)5562 static void listBodyCenteredVertices(
5563 GRegion *to, bool is_addverts,
5564 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
5565 *problems,
5566 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
5567 *problems_new,
5568 MVertexRTree *pos)
5569 {
5570 ExtrudeParams *ep = to->meshAttributes.extrude;
5571
5572 if(!ep) return;
5573
5574 if(problems->size() || problems_new->size()) {
5575 std::map<MElement *,
5576 std::set<std::pair<unsigned int, unsigned int> > >::iterator
5577 it_begin;
5578 std::map<MElement *,
5579 std::set<std::pair<unsigned int, unsigned int> > >::iterator
5580 it_end;
5581 std::map<MElement *,
5582 std::set<std::pair<unsigned int, unsigned int> > >::iterator itmap;
5583
5584 // insert all of problems_new into problems
5585 for(itmap = problems_new->begin(); itmap != problems_new->end(); itmap++)
5586 (*problems)[itmap->first].insert(itmap->second.begin(),
5587 itmap->second.end());
5588
5589 if(is_addverts) {
5590 it_begin = problems_new->begin();
5591 it_end = problems_new->end();
5592 }
5593 else {
5594 it_begin = problems->begin();
5595 it_end = problems->end();
5596 }
5597
5598 unsigned int int_verts_count = 0;
5599 for(itmap = it_begin; itmap != it_end; itmap++) {
5600 if(itmap->second.size()) int_verts_count += itmap->second.size();
5601 }
5602
5603 if(int_verts_count) {
5604 if(int_verts_count == 1)
5605 Msg::Warning("QuadToTri meshed %d element in region %d "
5606 "with a body-centered internal vertex.",
5607 int_verts_count, to->tag());
5608 else
5609 Msg::Warning("QuadToTri meshed %d elements in region %d "
5610 "with body-centered internal vertices.",
5611 int_verts_count, to->tag());
5612 Msg::Warning("( Mesh *should* still conformal, but the user should be "
5613 "aware of these internal vertices. )");
5614
5615 unsigned int int_verts_count2 = 0;
5616
5617 for(itmap = it_begin; itmap != it_end; itmap++) {
5618 if(itmap->second.size()) {
5619 std::set<std::pair<unsigned int, unsigned int> >::iterator itset;
5620 for(itset = itmap->second.begin(); itset != itmap->second.end();
5621 itset++) {
5622 std::vector<MVertex *> verts;
5623 getExtrudedVertices(itmap->first, ep, (*itset).first,
5624 (*itset).second, (*pos), verts);
5625 // find centroid
5626 std::vector<double> centroid = QtFindVertsCentroid(verts);
5627 int_verts_count2++;
5628 Msg::Warning("Internal Vertex %d at (x,y,z) = (%g, %g, %g).",
5629 int_verts_count2, centroid[0], centroid[1],
5630 centroid[2]);
5631 }
5632 }
5633 }
5634 }
5635 }
5636 }
5637
5638 // Function that makes all the elements in a QuadToTri region, both
5639 // the divided elements and the whole elements, using already-created
5640 // subdivision edges.
QuadToTriCreateElements(GRegion * to,CategorizedSourceElements & cat_src_elems,std::set<std::pair<MVertex *,MVertex * >> & quadToTri_edges,std::set<std::pair<MVertex *,MVertex * >> & lat_tri_diags,std::map<MElement *,std::set<std::pair<unsigned int,unsigned int>>> & problems,MVertexRTree & pos)5641 bool QuadToTriCreateElements(
5642 GRegion *to, CategorizedSourceElements &cat_src_elems,
5643 std::set<std::pair<MVertex *, MVertex *> > &quadToTri_edges,
5644 std::set<std::pair<MVertex *, MVertex *> > &lat_tri_diags,
5645 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
5646 &problems,
5647 MVertexRTree &pos)
5648 {
5649 ExtrudeParams *ep = to->meshAttributes.extrude;
5650 if(!ep || !ep->mesh.ExtrudeMesh || !ep->mesh.QuadToTri) return false;
5651
5652 GModel *model = to->model();
5653 GFace *from = model->getFaceByTag(std::abs(ep->geo.Source));
5654 if(!from) return false;
5655
5656 // set lat_tri_diags_size
5657 unsigned int lat_tri_diags_size = lat_tri_diags.size();
5658
5659 // number of element layers
5660 unsigned int num_layers = 0;
5661 for(int j = 0; j < ep->mesh.NbLayer; j++)
5662 num_layers += ep->mesh.NbElmLayer[j];
5663
5664 // Is this a valid 'add internal vertex' extrusion?
5665 bool is_addverts = false;
5666 if(ep->mesh.QuadToTri == QUADTRI_ADDVERTS_1 ||
5667 ep->mesh.QuadToTri == QUADTRI_ADDVERTS_1_RECOMB)
5668 is_addverts = true;
5669
5670 // Find where top divided layer starts
5671 /*int j_top_start = 0, k_top_start = 0;
5672 if( is_addverts ){ // second from top
5673 if( ep->mesh.NbElmLayer[ep->mesh.NbLayer-1] > 1 ){
5674 j_top_start = ep->mesh.NbLayer-1;
5675 k_top_start = ep->mesh.NbElmLayer[j_top_start]-2;
5676 }
5677 else{
5678 j_top_start = std::max(ep->mesh.NbLayer-2, 0);
5679 k_top_start = ep->mesh.NbElmLayer[j_top_start]-1;
5680 }
5681 }
5682 else{ // first from top
5683 j_top_start = ep->mesh.NbLayer-1;
5684 k_top_start = ep->mesh.NbElmLayer[j_top_start]-1;
5685 }*/
5686
5687 // for one point bd quads
5688 /*int j_second_from_bottom = ep->mesh.NbLayer-1,
5689 k_second_from_bottom = ep->mesh.NbElmLayer[ep->mesh.NbLayer-1]-1;
5690 if( ep->mesh.NbElmLayer[0] > 1 ){
5691 j_second_from_bottom = 0;
5692 k_second_from_bottom = 1;
5693 }
5694 else if( ep->mesh.NbLayer > 1 ){
5695 j_second_from_bottom = 1;
5696 k_second_from_bottom = 0;
5697 }*/
5698
5699 // a container for new problem elements (if such new problems are found,
5700 // there's a bug)
5701 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
5702 problems_new;
5703
5704 // Make the extra vertices needed for Some QuadToTri elements
5705 if(!addBodyCenteredVertices(to, cat_src_elems, quadToTri_edges, problems,
5706 is_addverts, lat_tri_diags_size, pos)) {
5707 Msg::Error("QuadToTriCreateElements() could not add face or body vertices "
5708 "for QuadToTri region %d.",
5709 to->tag());
5710 return false;
5711 }
5712
5713 // reserve enough capacity for all possible elements, try to find combination
5714 // of simplicity and memory efficiency.
5715 // *** EXPERIMENTAL ***
5716 // reserveQuadToTriCapacityForRegion( to, from, is_addverts, num_layers,
5717 // lat_tri_diags_size, &cat_src_elems, &problems );
5718
5719 // create elements:
5720
5721 /* std::vector<MElement*> elem_vec;
5722 elem_vec.reserve(num_layers);
5723 */
5724 // triangles
5725 for(int s = 0; s < 3; s++) {
5726 std::vector<MVertex *> verts;
5727 verts.reserve(3);
5728 bool bnd_elem = s < 2 ? true : false;
5729 std::set<unsigned int> *set_elems;
5730 std::set<unsigned int>::iterator itset;
5731 if(!s)
5732 set_elems = &cat_src_elems.three_bnd_pt_tri;
5733 else if(s == 1)
5734 set_elems = &cat_src_elems.other_bnd_tri;
5735 else
5736 set_elems = &cat_src_elems.internal_tri;
5737 for(itset = set_elems->begin(); itset != set_elems->end(); itset++) {
5738 /* // *** SPEED IMPROVEMENT ***
5739 unsigned int hex, pyr, pri, tet;
5740 hex = to->hexahedra.size();
5741 pyr = to->pyramids.size();
5742 pri = to->prisms.size();
5743 tet = to->tetrahedra.size();
5744 // keeps old allocation
5745 elem_vec.resize(0);
5746 */
5747 MElement *elem = from->triangles[(*itset)];
5748 for(int j = 0; j < ep->mesh.NbLayer; j++) {
5749 for(int k = 0; k < ep->mesh.NbElmLayer[j]; k++) {
5750 // keeps old allocation
5751 verts.resize(0);
5752 if(getExtrudedVertices(elem, ep, j, k, pos, verts) == 6) {
5753 QuadToTriPriPyrTet(verts, to, j, k, elem, quadToTri_edges, problems,
5754 problems_new, lat_tri_diags_size, bnd_elem,
5755 is_addverts, 1, pos);
5756 }
5757 }
5758 }
5759 /*// *** SPEED IMPROVEMENT ***
5760 elem_vec.insert(elem_vec.end(), to->hexahedra.begin()+hex,
5761 to->hexahedra.end() ); elem_vec.insert(elem_vec.end(),
5762 to->tetrahedra.begin()+tet, to->tetrahedra.end() );
5763 elem_vec.insert(elem_vec.end(), to->prisms.begin()+pri, to->prisms.end()
5764 ); elem_vec.insert(elem_vec.end(), to->pyramids.begin()+pyr,
5765 to->pyramids.end() );
5766 */
5767 }
5768 }
5769
5770 if(from->quadrangles.size() && !ep->mesh.Recombine) {
5771 Msg::Error("In QuadToTriCreateElements(), cannot extrude quadrangles "
5772 "without Recombine");
5773 return false;
5774 }
5775 else {
5776 std::vector<MVertex *> verts;
5777 verts.reserve(4);
5778 for(int s = 0; s < 3; s++) {
5779 bool bnd_elem = s < 2 ? true : false;
5780 std::set<unsigned int> *set_elems;
5781 std::set<unsigned int>::iterator itset;
5782 if(!s)
5783 set_elems = &cat_src_elems.four_bnd_pt_quad;
5784 else if(s == 1)
5785 set_elems = &cat_src_elems.other_bnd_quad;
5786 else
5787 set_elems = &cat_src_elems.internal_quad;
5788
5789 for(itset = set_elems->begin(); itset != set_elems->end(); itset++) {
5790 /*// *** SPEED IMPROVEMENT ***
5791 unsigned int hex, pyr, pri, tet;
5792 hex = to->hexahedra.size();
5793 pyr = to->pyramids.size();
5794 pri = to->prisms.size();
5795 tet = to->tetrahedra.size();
5796
5797 // keeps old allocation
5798 elem_vec.resize(0);
5799 */
5800 MElement *elem = from->quadrangles[(*itset)];
5801 for(int j = 0; j < ep->mesh.NbLayer; j++) {
5802 for(int k = 0; k < ep->mesh.NbElmLayer[j]; k++) {
5803 // keeps old allocation
5804 verts.resize(0);
5805 if(getExtrudedVertices(elem, ep, j, k, pos, verts) == 8) {
5806 QuadToTriHexPri(verts, to, j, k, elem, quadToTri_edges, problems,
5807 problems_new, lat_tri_diags_size, bnd_elem,
5808 is_addverts, 1, pos);
5809 }
5810 }
5811 }
5812 /*// *** SPEED IMPROVEMENT ***
5813 elem_vec.insert(elem_vec.end(), to->hexahedra.begin()+hex,
5814 to->hexahedra.end() ); elem_vec.insert(elem_vec.end(),
5815 to->tetrahedra.begin()+tet, to->tetrahedra.end() );
5816 elem_vec.insert(elem_vec.end(), to->prisms.begin()+pri, to->prisms.end()
5817 ); elem_vec.insert(elem_vec.end(), to->pyramids.begin()+pyr,
5818 to->pyramids.end() );
5819 */
5820 }
5821 }
5822 }
5823
5824 // List for the user any elements with internal vertices:
5825 listBodyCenteredVertices(to, is_addverts, &problems, &problems_new, &pos);
5826
5827 // Now revert any elements that have positive volume.
5828 // Does this even need to be done?
5829 // *** KEEP THIS COMMENTED OUT UNLESS IT IS NEEDED ***
5830 /*for( int i = 0; i < to->tetrahedra.size(); i++ ){
5831 if( to->tetrahedra[i]->getVolumeSign() > 0 )
5832 to->tetrahedra[i]->reverse();
5833
5834 }
5835 for( int i = 0; i < to->prisms.size(); i++ ){
5836 if( to->prisms[i]->getVolumeSign() > 0 )
5837 to->prisms[i]->reverse();
5838 }
5839 for( int i = 0; i < to->pyramids.size(); i++ ){
5840 if( to->pyramids[i]->getVolumeSign() > 0 )
5841 to->pyramids[i]->reverse();
5842 }
5843 */
5844
5845 // ***THE FOLLOWING COMMENTED OUT LINES ARE FOR DEBUGGING PURPOSES ONLY AND
5846 // WON'T WORK WITHOUT
5847 // A SPECIAL INCLUDE FILE. THESE ARE USELESS WITHOUT THE DEBUG INCLUDE. ***
5848 /*GModel::riter rit;
5849 if( std::abs(to->tag() ) == 4 ){
5850 for( rit = model->firstRegion(); rit != model->lastRegion(); rit++ ){
5851 double total_volume = RegionVolByElems( (*rit) );
5852 Msg::Error( "Region %3d Volume = %14.7f.", (*rit)->tag(), total_volume );
5853 }
5854 for( rit = model->firstRegion(); rit != model->lastRegion(); rit++ ){
5855 unsigned int num_nonconformal = TestRegionConformality( (*rit) );
5856 // if( num_nonconformal )
5857 Msg::Error( "Region %3d Noncomformal faces = %d.", (*rit)->tag(),
5858 num_nonconformal );
5859 }
5860
5861 }*/
5862
5863 return true;
5864 }
5865
5866 // Mesh QuadToTri region from extrudeMesh() in meshGRegionExtruded.cpp
5867 // Added 04/08/2011:
meshQuadToTriRegion(GRegion * gr,MVertexRTree & pos)5868 int meshQuadToTriRegion(GRegion *gr, MVertexRTree &pos)
5869 {
5870 // Perform some checks to see if this is a valid QuadToTri region.
5871 // If so, a decision has to be made: if this surface is NOT laterally adjacent
5872 // to any subdivided extrusion, then it may be meshed here without worrying
5873 // about a global subdivide operation.
5874 // If the region has a lateral shared with a subdivide region, then it should
5875 // be part of the subdivide operation later...so just let the default methods
5876 // here create the vertices and quit. Otherwise, engage in the meshing here.
5877
5878 ExtrudeParams *ep = gr->meshAttributes.extrude;
5879
5880 if(!ep || !ep->mesh.ExtrudeMesh || !ep->mesh.QuadToTri || !ep->mesh.Recombine)
5881 return 0;
5882
5883 // QuadToTri validity check:
5884 bool validQuadToTriReg = false;
5885 // if any laterals are shared with a subdivided region or an region that
5886 // otherwise should not have it's lateral diags changed,
5887 // IsValidQuadToTriRegion will set following to 'false.'
5888 bool allNonGlobalSharedLaterals = true; // IsValidQuadToTriRegion will set
5889 // this properly regardless of initial
5890 // value
5891 validQuadToTriReg = IsValidQuadToTriRegion(gr, &allNonGlobalSharedLaterals);
5892
5893 if(!validQuadToTriReg && ep->mesh.QuadToTri)
5894 Msg::Error("Mesh of QuadToTri region %d likely has errors.", gr->tag());
5895
5896 if(!allNonGlobalSharedLaterals) {
5897 Msg::Info("Delaying mesh of QuadToTri Region %d until after global "
5898 "subdivide operation....",
5899 gr->tag());
5900 return 0;
5901 }
5902
5903 // mesh quadToTri even if validQuadToTri is false. Try it anyway!
5904 if(allNonGlobalSharedLaterals) {
5905 std::set<std::pair<MVertex *, MVertex *> > quadToTri_edges;
5906 std::set<std::pair<MVertex *, MVertex *> > lat_tri_diags;
5907 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
5908 problems;
5909
5910 // first thing is first
5911 // data structure for boundary status-categorized source elements,
5912 // (member data containers defined in .h file)
5913 CategorizedSourceElements cat_src_elems(gr);
5914 if(!cat_src_elems.valid) {
5915 Msg::Error("In meshQuadToTriRegion(), failed to classify QuadToTri "
5916 "region %d's source face elements "
5917 "according to boundary status.",
5918 gr->tag());
5919 return 0;
5920 }
5921
5922 if(!QuadToTriEdgeGenerator(gr, cat_src_elems, quadToTri_edges,
5923 lat_tri_diags, problems, pos)) {
5924 Msg::Error(
5925 "In meshQuadToTriRegion(), failed to create edges for QuadToTri "
5926 "region %d.",
5927 gr->tag());
5928 return 0;
5929 }
5930 if(!QuadToTriCreateElements(gr, cat_src_elems, quadToTri_edges,
5931 lat_tri_diags, problems, pos)) {
5932 Msg::Error(
5933 "In meshQuadToTriRegion, failed to create elements for QuadToTri "
5934 "region %d.",
5935 gr->tag());
5936 return 0;
5937 }
5938
5939 QuadToTriLateralRemesh(gr, quadToTri_edges);
5940
5941 return 1;
5942 }
5943
5944 return 0;
5945 }
5946
5947 // The function that is called from meshGRegionExtruded.cpp to mesh QuadToTri
5948 // regions that are adjacent to subdivided regions, after the global Subdivide
5949 // command is called. Added 04/08/11.
meshQuadToTriRegionAfterGlobalSubdivide(GRegion * gr,std::set<std::pair<MVertex *,MVertex * >> * edges,MVertexRTree & pos)5950 int meshQuadToTriRegionAfterGlobalSubdivide(
5951 GRegion *gr, std::set<std::pair<MVertex *, MVertex *> > *edges,
5952 MVertexRTree &pos)
5953 {
5954 ExtrudeParams *ep = gr->meshAttributes.extrude;
5955
5956 if(!ep || !ep->mesh.ExtrudeMesh || !ep->mesh.QuadToTri || !ep->mesh.Recombine)
5957 return 0;
5958
5959 // QuadToTri validity check:
5960 bool validQuadToTriReg = false;
5961 // if any laterals are shared with a subdivided region or any region that
5962 // should not have its lateral diags swapped, IsValidQuadToTriRegion() will
5963 // set following to 'false.'
5964 bool allNonGlobalSharedLaterals = true; // IsValidQuadToTriRegion will set
5965 // this properly regardless of initial
5966 // value
5967 validQuadToTriReg = IsValidQuadToTriRegion(gr, &allNonGlobalSharedLaterals);
5968
5969 if(!validQuadToTriReg && ep->mesh.QuadToTri)
5970 Msg::Error("Mesh of QuadToTri region %d likely has errors.", gr->tag());
5971
5972 // If all lateral edges were non-global, skip. (Region should already be
5973 // meshed properly).
5974 if(allNonGlobalSharedLaterals) return 0;
5975
5976 Msg::Info("Meshing Region %d (extruded).", gr->tag());
5977
5978 GFace *gr_src_face = gr->model()->getFaceByTag(std::abs(ep->geo.Source));
5979 if(!gr_src_face) {
5980 Msg::Error("In meshQuadToTriRegionAfterGlobalSubdivide(), no source face "
5981 "for QuadToTri region %d.",
5982 gr->tag());
5983 return 0;
5984 }
5985
5986 for(unsigned int i = 0; i < gr->hexahedra.size(); i++)
5987 delete gr->hexahedra[i];
5988 gr->hexahedra.clear();
5989 for(unsigned int i = 0; i < gr->prisms.size(); i++) delete gr->prisms[i];
5990 gr->prisms.clear();
5991 for(unsigned int i = 0; i < gr->pyramids.size(); i++) delete gr->pyramids[i];
5992 gr->pyramids.clear();
5993 for(unsigned int i = 0; i < gr->tetrahedra.size(); i++)
5994 delete gr->tetrahedra[i];
5995 gr->tetrahedra.clear();
5996
5997 std::set<std::pair<MVertex *, MVertex *> > quadToTri_edges;
5998 std::set<std::pair<MVertex *, MVertex *> > lat_tri_diags;
5999 std::map<MElement *, std::set<std::pair<unsigned int, unsigned int> > >
6000 problems;
6001
6002 // add edges to quadToTri_edges
6003 quadToTri_edges.insert(edges->begin(), edges->end());
6004
6005 // categorize source face elements
6006 CategorizedSourceElements cat_src_elems(gr);
6007
6008 if(!cat_src_elems.valid) {
6009 Msg::Error("In meshQuadToTriRegionAfterGlobalSubdivide(), "
6010 "Failed to classify QuadToTri region %d's source face elements "
6011 "according to boundary status.",
6012 gr->tag());
6013 return 0;
6014 }
6015
6016 // Mesh quadToTri
6017 if(!QuadToTriEdgeGenerator(gr, cat_src_elems, quadToTri_edges, lat_tri_diags,
6018 problems, pos)) {
6019 Msg::Error("In meshQuadToTriRegionAfterGlobalSubdivide(), edge generation "
6020 "failed for QuadToTri "
6021 "region %d.",
6022 gr->tag());
6023 return 0;
6024 }
6025 if(!QuadToTriCreateElements(gr, cat_src_elems, quadToTri_edges, lat_tri_diags,
6026 problems, pos)) {
6027 Msg::Error("In meshQuadToTriRegionAfterGlobalSubdivide(), element creation "
6028 "failed for QuadToTri "
6029 "region %d.",
6030 gr->tag());
6031 return 0;
6032 }
6033
6034 QuadToTriLateralRemesh(gr, quadToTri_edges);
6035
6036 return 1;
6037 }
6038