gmsh-TingyuanDoc  0.1
An Open-Source Timing-driven Analytical Mixed-size FPGA Placer
meshGFaceTransfinite.cpp
Go to the documentation of this file.
1 // Gmsh - Copyright (C) 1997-2022 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 //
6 // Contributor(s):
7 // Michael Ermakov (ermakov@ipmnet.ru)
8 
9 #include <map>
10 #include <queue>
11 #include <array>
12 #include "meshGFace.h"
13 #include "meshGEdge.h"
14 #include "GVertex.h"
15 #include "GEdge.h"
16 #include "GFace.h"
17 #include "MVertex.h"
18 #include "MTriangle.h"
19 #include "MQuadrangle.h"
20 #include "Context.h"
21 #include "GmshMessage.h"
22 #include "Numeric.h"
23 
24 /*
25  s4 +-----c3-----+ s3
26  | |
27  | |
28  c4 c2
29  | |
30  | |
31  s1 +-----c1-----+ s2
32 */
33 
34 // f(u,v) = (1-u) c4(v) + u c2(v) + (1-v) c1(u) + v c3(u)
35 // - [ (1-u)(1-v) s1 + u(1-v) s2 + uv s3 + (1-u)v s4 ]
36 #define TRAN_QUA(c1, c2, c3, c4, s1, s2, s3, s4, u, v) \
37  (1. - u) * c4 + u * c2 + (1. - v) * c1 + v * c3 - \
38  ((1. - u) * (1. - v) * s1 + u * (1. - v) * s2 + u * v * s3 + \
39  (1. - u) * v * s4)
40 
41 // s1=s4=c4
42 // f(u,v) = u c2 (v) + (1-v) c1(u) + v c3(u) - u(1-v) s2 - uv s3
43 #define TRAN_TRI(c1, c2, c3, s1, s2, s3, u, v) \
44  u * c2 + (1. - v) * c1 + v * c3 - (u * (1. - v) * s2 + u * v * s3)
45 
46 void findTransfiniteCorners(GFace *gf, std::vector<MVertex *> &corners)
47 {
48  if(gf->meshAttributes.corners.size()) {
49  // corners have been specified explicitly
50  for(std::size_t i = 0; i < gf->meshAttributes.corners.size(); i++)
51  corners.push_back(gf->meshAttributes.corners[i]->mesh_vertices[0]);
52  }
53  else {
54  // try to find the corners automatically
55  std::vector<GEdge *> fedges = gf->edges();
56  GEdgeLoop el(fedges);
57  for(auto it = el.begin(); it != el.end(); it++)
58  corners.push_back(it->getBeginVertex()->mesh_vertices[0]);
59 
60  // try reaaally hard for 3-sided faces
61  if(corners.size() == 3) {
62  GEdge *first = nullptr, *last = nullptr;
63  for(auto it = fedges.begin(); it != fedges.end(); it++) {
64  if(((*it)->getBeginVertex()->mesh_vertices[0] == corners[0] &&
65  (*it)->getEndVertex()->mesh_vertices[0] == corners[1]) ||
66  ((*it)->getBeginVertex()->mesh_vertices[0] == corners[1] &&
67  (*it)->getEndVertex()->mesh_vertices[0] == corners[0])) {
68  first = *it;
69  }
70  if(((*it)->getBeginVertex()->mesh_vertices[0] == corners[2] &&
71  (*it)->getEndVertex()->mesh_vertices[0] == corners[0]) ||
72  ((*it)->getBeginVertex()->mesh_vertices[0] == corners[0] &&
73  (*it)->getEndVertex()->mesh_vertices[0] == corners[2])) {
74  last = *it;
75  }
76  }
77  if(first && last) {
78  if(first->mesh_vertices.size() != last->mesh_vertices.size()) {
79  std::vector<MVertex *> corners2(3);
80  corners2[0] = corners[1];
81  corners2[1] = corners[2];
82  corners2[2] = corners[0];
83  corners = corners2;
84  }
85  }
86  }
87  }
88 }
89 
90 static void computeEdgeLoops(const GFace *gf,
91  std::vector<MVertex *> &all_mvertices,
92  std::vector<int> &indices)
93 {
94  std::vector<GEdge *> const &e = gf->edges();
95  std::vector<int> const &o = gf->orientations();
96 
97  std::vector<GEdge *> edges;
98  std::vector<int> ori;
99  for(std::size_t i = 0; i < e.size(); i++) {
100  if(!e[i]->degenerate(0)) { // skip degenerate curves
101  edges.push_back(e[i]);
102  ori.push_back(i < o.size() ? o[i] : 1);
103  }
104  }
105 
106  auto it = edges.begin();
107  auto ito = ori.begin();
108  indices.push_back(0);
109  GVertex *start =
110  ((*ito) == 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
111  GVertex *v_end =
112  ((*ito) != 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
113 
114  all_mvertices.push_back(start->mesh_vertices[0]);
115  if(*ito == 1)
116  for(std::size_t i = 0; i < (*it)->mesh_vertices.size(); i++)
117  all_mvertices.push_back((*it)->mesh_vertices[i]);
118  else
119  for(int i = (*it)->mesh_vertices.size() - 1; i >= 0; i--)
120  all_mvertices.push_back((*it)->mesh_vertices[i]);
121 
122  GVertex *v_start = start;
123  while(1) {
124  ++it;
125  ++ito;
126  if(v_end == start) {
127  indices.push_back(all_mvertices.size());
128  if(it == edges.end()) break;
129  start = ((*ito) == 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
130  v_end = ((*ito) != 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
131  v_start = start;
132  }
133  else {
134  if(it == edges.end()) {
135  Msg::Error("Something wrong in edge loop computation");
136  return;
137  }
138  v_start = ((*ito) == 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
139  if(v_start != v_end) {
140  Msg::Error("Something wrong in edge loop computation");
141  return;
142  }
143  v_end = ((*ito) != 1) ? (*it)->getBeginVertex() : (*it)->getEndVertex();
144  }
145  all_mvertices.push_back(v_start->mesh_vertices[0]);
146  if(*ito == 1)
147  for(std::size_t i = 0; i < (*it)->mesh_vertices.size(); i++)
148  all_mvertices.push_back((*it)->mesh_vertices[i]);
149  else
150  for(int i = (*it)->mesh_vertices.size() - 1; i >= 0; i--)
151  all_mvertices.push_back((*it)->mesh_vertices[i]);
152  }
153 }
154 
155 double maxDistParam(const std::vector<double> &U, const std::vector<double> &V)
156 {
157  if(U.size() < 2 || (U.size() != V.size())) return 1e22;
158  double d =
159  sqrt(std::pow(U.back() - U.front(), 2) + std::pow(V.back() - V.front(), 2));
160  for(std::size_t i = 1; i < U.size(); i++)
161  d = std::max(
162  d, sqrt(std::pow(U[i] - U[i - 1], 2) + std::pow(V[i] - V[i - 1], 2)));
163  return d;
164 }
165 
167 {
168  if(gf->meshAttributes.method != MESH_TRANSFINITE) return 0;
169 
170  Msg::Info("Meshing surface %d (Transfinite)", gf->tag());
171 
172  // make sure that all bounding edges have begin/end points: everything in here
173  // depends on it
174  const std::vector<GEdge *> &edges = gf->edges();
175  for(auto it = edges.begin(); it != edges.end(); it++) {
176  if(!(*it)->getBeginVertex() || !(*it)->getEndVertex()) {
177  Msg::Error("Transfinite algorithm cannot be applied with curve %d which "
178  "has no begin or end point",
179  (*it)->tag());
180  return 0;
181  }
182  }
183 
184  std::vector<MVertex *> corners;
185  findTransfiniteCorners(gf, corners);
186  if(corners.size() != 3 && corners.size() != 4) {
187  Msg::Error("Surface %d is transfinite but has %d corner%s", gf->tag(),
188  corners.size(), corners.size() > 1 ? "s" : "");
189  return 0;
190  }
191 
192  std::vector<MVertex *> d_vertices;
193  std::vector<int> indices;
194  computeEdgeLoops(gf, d_vertices, indices);
195 
196  if(indices.size() != 2) {
197  int nh = indices.size() - 2;
198  Msg::Error("Surface %d is transfinite but has %d hole%s", gf->tag(), nh,
199  nh > 1 ? "s" : "");
200  return 0;
201  }
202 
203  // create a list of all boundary vertices, starting at the first
204  // transfinite corner
205  std::vector<MVertex *> m_vertices;
206  std::size_t I;
207  for(I = 0; I < d_vertices.size(); I++)
208  if(d_vertices[I] == corners[0]) break;
209  for(std::size_t j = 0; j < d_vertices.size(); j++)
210  m_vertices.push_back(d_vertices[(I + j) % d_vertices.size()]);
211 
212  // make the ordering of the list consistent with the ordering of the
213  // first two corners (if the second found corner is not the second
214  // corner, just reverse the list)
215  bool reverse = false;
216  for(std::size_t i = 1; i < m_vertices.size(); i++) {
217  MVertex *v = m_vertices[i];
218  if(v == corners[1] || v == corners[2] ||
219  (corners.size() == 4 && v == corners[3])) {
220  if(v != corners[1]) reverse = true;
221  break;
222  }
223  }
224  if(reverse) {
225  std::vector<MVertex *> tmp;
226  tmp.push_back(m_vertices[0]);
227  for(int i = m_vertices.size() - 1; i > 0; i--) tmp.push_back(m_vertices[i]);
228  m_vertices = tmp;
229  }
230 
231  // get the indices of the interpolation corners as well as the u,v
232  // coordinates of all the boundary vertices
233  int iCorner = 0, N[4] = {0, 0, 0, 0};
234  std::vector<double> U, V, U2, V2;
235  for(std::size_t i = 0; i < m_vertices.size(); i++) {
236  MVertex *v = m_vertices[i];
237  if(v == corners[0] || v == corners[1] || v == corners[2] ||
238  (corners.size() == 4 && v == corners[3])) {
239  if(iCorner > 3) {
240  Msg::Error("Surface %d transfinite parameters are incoherent",
241  gf->tag());
242  return 0;
243  }
244  N[iCorner++] = i;
245  }
246  SPoint2 param;
247  bool onSeam = !reparamMeshVertexOnFace(v, gf, param);
248  U.push_back(param[0]);
249  V.push_back(param[1]);
250  // if v is on a periodic seam, get the other parametric coordinates
251  if(onSeam) reparamMeshVertexOnFace(v, gf, param, true, true, -1);
252  U2.push_back(param[0]);
253  V2.push_back(param[1]);
254  }
255 
256  // quick fix for surfaces with a single seam: choose the trajectory in the
257  // parametric plane that leads to the smallest maximum distance between two
258  // nodes; this should be generalized if we want to handle cases with more than
259  // one seam
260  if(maxDistParam(U2, V2) < maxDistParam(U, V)) {
261  Msg::Debug("Choosing alternate parametrization on surface %d", gf->tag());
262  U = U2;
263  V = V2;
264  }
265 
266  int N1 = N[0], N2 = N[1], N3 = N[2], N4 = N[3];
267  int L = N2 - N1, H = N3 - N2;
268  if(corners.size() == 4) {
269  int Lb = N4 - N3, Hb = m_vertices.size() - N4;
270  if(Lb != L || Hb != H) {
271  Msg::Error("Surface %d cannot be meshed using the transfinite algo "
272  "(divisions %d != %d or %d != %d)",
273  gf->tag(), Lb, L, Hb, H);
274  return 0;
275  }
276  }
277  else {
278  int Lb = m_vertices.size() - N3;
279  if(CTX::instance()->mesh.transfiniteTri && Lb == L && H == L) {
280  gf->meshAttributes.transfinite3 = true;
281  Msg::Info("Using specific algorithm for 3-sided surface %d", gf->tag());
282  }
283  else {
284  if(Lb != L) {
285  Msg::Error("Surface %d cannot be meshed using the transfinite algo "
286  "(divisions %d != %d)",
287  gf->tag(), L, Lb);
288  return 0;
289  }
290  }
291  }
292 
293  /*
294  2L+H +------------+ L+H
295  | |
296  | |
297  | |
298  | |
299  2L+2H +------------+
300  0 L
301  */
302 
303  // use the average of the node spacing on the two opposing sides, so that we
304  // generate the same u, v coordinates whatever the ordering of the sides
305  bool symmetric = true;
306 
307  std::vector<double> lengths_i;
308  lengths_i.reserve(L);
309  lengths_i.push_back(0.0);
310  double L_i = 0.0;
311  for(int i = 0; i < L; i++) {
312  MVertex *v1 = m_vertices[i];
313  MVertex *v2 = m_vertices[i + 1];
314  double d1 = v1->distance(v2);
315  if(symmetric) {
316  MVertex *v3 =
317  m_vertices[(corners.size() == 3 && !i) ? 0 : (2 * L + H - i)];
318  MVertex *v4 = m_vertices[2 * L + H - i - 1];
319  double d2 = v3->distance(v4);
320  L_i += 0.5 * (d1 + d2);
321  }
322  else {
323  L_i += d1;
324  }
325  lengths_i.push_back(L_i);
326  }
327 
328  std::vector<double> lengths_j;
329  lengths_j.reserve(H);
330  lengths_j.push_back(0.0);
331  double L_j = 0.0;
332  for(int i = 0; i < H; i++) {
333  MVertex *v1 = m_vertices[L + i];
334  MVertex *v2 = m_vertices[L + i + 1];
335  double d1 = v1->distance(v2);
336  if(symmetric && corners.size() == 4) {
337  MVertex *v3 = m_vertices[(!i) ? 0 : (2 * L + 2 * H - i)];
338  MVertex *v4 = m_vertices[2 * L + 2 * H - i - 1];
339  double d2 = v3->distance(v4);
340  L_j += 0.5 * (d1 + d2);
341  }
342  else {
343  L_j += d1;
344  }
345  lengths_j.push_back(L_j);
346  }
347 
348  std::vector<std::vector<MVertex *> > &tab(gf->transfinite_vertices);
349  tab.resize(L + 1);
350  for(int i = 0; i <= L; i++) tab[i].resize(H + 1);
351 
352  if(corners.size() == 4) {
353  tab[0][0] = m_vertices[0];
354  tab[L][0] = m_vertices[L];
355  tab[L][H] = m_vertices[L + H];
356  tab[0][H] = m_vertices[2 * L + H];
357  for(int i = 1; i < L; i++) {
358  tab[i][0] = m_vertices[i];
359  tab[i][H] = m_vertices[2 * L + H - i];
360  }
361  for(int i = 1; i < H; i++) {
362  tab[L][i] = m_vertices[L + i];
363  tab[0][i] = m_vertices[2 * L + 2 * H - i];
364  }
365  }
366  else {
367  tab[0][0] = m_vertices[0];
368  tab[L][0] = m_vertices[L];
369  tab[L][H] = m_vertices[L + H];
370  // degenerated, only necessary for transfinite volume algo
371  tab[0][H] = m_vertices[0];
372  for(int i = 1; i < L; i++) {
373  tab[i][0] = m_vertices[i];
374  tab[i][H] = m_vertices[2 * L + H - i];
375  }
376  for(int i = 1; i < H; i++) {
377  tab[L][i] = m_vertices[L + i];
378  // degenerated, only necessary for transfinite volume algo
379  tab[0][i] = m_vertices[0];
380  }
381  }
382 
383  double UC1 = U[N1], UC2 = U[N2], UC3 = U[N3];
384  double VC1 = V[N1], VC2 = V[N2], VC3 = V[N3];
385 
386  // create points using transfinite interpolation
387  if(corners.size() == 4) {
388  double UC4 = U[N4];
389  double VC4 = V[N4];
390  for(int i = 1; i < L; i++) {
391  double u = lengths_i[i] / L_i;
392  for(int j = 1; j < H; j++) {
393  double v = lengths_j[j] / L_j;
394  int iP1 = N1 + i;
395  int iP2 = N2 + j;
396  int iP3 = N4 - i;
397  int iP4 = (N4 + (N3 - N2) - j) % m_vertices.size();
398  double Up =
399  TRAN_QUA(U[iP1], U[iP2], U[iP3], U[iP4], UC1, UC2, UC3, UC4, u, v);
400  double Vp =
401  TRAN_QUA(V[iP1], V[iP2], V[iP3], V[iP4], VC1, VC2, VC3, VC4, u, v);
402  GPoint gp = gf->point(SPoint2(Up, Vp));
403  MFaceVertex *newv = new MFaceVertex(gp.x(), gp.y(), gp.z(), gf, Up, Vp);
404  gf->mesh_vertices.push_back(newv);
405  tab[i][j] = newv;
406  }
407  }
408  }
409  else {
410  std::vector<double> u2, v2;
411  if(gf->meshAttributes.transfinite3) {
412  u2.reserve(H + 1);
413  for(int j = 0; j <= H; j++) u2.push_back(U[N2 + j]);
414  v2.reserve(H + 1);
415  for(int j = 0; j <= H; j++) v2.push_back(V[N2 + j]);
416  }
417 
418  for(int i = 1; i < L; i++) {
419  double u = lengths_i[i] / L_i;
420  for(int j = 1; j < H; j++) {
421  double v = lengths_j[j] / L_j;
422  int iP1 = N1 + i;
423  int iP2 = N2 + j;
424  int iP3 = ((N3 + N2) - i) % m_vertices.size();
425  double Up, Vp;
426  if(gf->geomType() != GEntity::RuledSurface) {
427  if(!gf->meshAttributes.transfinite3) {
428  Up = TRAN_TRI(U[iP1], U[iP2], U[iP3], UC1, UC2, UC3, u, v);
429  Vp = TRAN_TRI(V[iP1], V[iP2], V[iP3], VC1, VC2, VC3, u, v);
430  }
431  else {
432  if(j >= i) {
433  tab[i][j] = tab[i][H];
434  continue;
435  }
436 
437  double t = double(j) / double(i);
438  double v = t;
439  int k;
440  for(k = 1; k <= H; k++)
441  if(t < lengths_j[k] / L_j && t > lengths_j[k - 1] / L_j) break;
442 
443  double a =
444  (t * L_j - lengths_j[k - 1]) / (lengths_j[k] - lengths_j[k - 1]);
445  double UP2 = u2[k - 1] + a * (u2[k] - u2[k - 1]);
446  double VP2 = v2[k - 1] + a * (v2[k] - v2[k - 1]);
447  Up = TRAN_TRI(U[iP1], UP2, U[iP3], UC1, UC2, UC3, u, v);
448  Vp = TRAN_TRI(V[iP1], VP2, V[iP3], VC1, VC2, VC3, u, v);
449  }
450  }
451  else {
452  // FIXME: to get nice meshes we would need to make the u,v
453  // coords match with the (degenerate) coordinates of the
454  // underlying ruled surface; so instead we just interpolate
455  // in real space
456  double xp = TRAN_TRI(m_vertices[iP1]->x(), m_vertices[iP2]->x(),
457  m_vertices[iP3]->x(), m_vertices[N1]->x(),
458  m_vertices[N2]->x(), m_vertices[N3]->x(), u, v);
459  double yp = TRAN_TRI(m_vertices[iP1]->y(), m_vertices[iP2]->y(),
460  m_vertices[iP3]->y(), m_vertices[N1]->y(),
461  m_vertices[N2]->y(), m_vertices[N3]->y(), u, v);
462  double zp = TRAN_TRI(m_vertices[iP1]->z(), m_vertices[iP2]->z(),
463  m_vertices[iP3]->z(), m_vertices[N1]->z(),
464  m_vertices[N2]->z(), m_vertices[N3]->z(), u, v);
465  // xp,yp,zp can be off the surface so we cannot use parFromPoint
466  gf->XYZtoUV(xp, yp, zp, Up, Vp, 1.0, false);
467  }
468  GPoint gp = gf->point(SPoint2(Up, Vp));
469  MFaceVertex *newv = new MFaceVertex(gp.x(), gp.y(), gp.z(), gf, Up, Vp);
470  gf->mesh_vertices.push_back(newv);
471  tab[i][j] = newv;
472  }
473  }
474  }
475 
476  // should we apply the elliptic smoother?
477  int numSmooth = 0;
480  numSmooth = CTX::instance()->mesh.nbSmoothing;
481  else if(gf->meshAttributes.transfiniteSmoothing > 0)
482  numSmooth = gf->meshAttributes.transfiniteSmoothing;
483 
484  if(corners.size() == 4 && numSmooth && !gf->meshAttributes.transfinite3) {
485  std::vector<std::vector<double> > u(L + 1), v(L + 1);
486  for(int i = 0; i <= L; i++) {
487  u[i].resize(H + 1);
488  v[i].resize(H + 1);
489  }
490  for(int i = 0; i <= L; i++) {
491  for(int j = 0; j <= H; j++) {
492  int iP1 = N1 + i;
493  int iP2 = N2 + j;
494  int iP3 = N4 - i;
495  int iP4 = (N4 + (N3 - N2) - j) % m_vertices.size();
496  if(j == 0) {
497  u[i][j] = U[iP1];
498  v[i][j] = V[iP1];
499  }
500  else if(i == L) {
501  u[i][j] = U[iP2];
502  v[i][j] = V[iP2];
503  }
504  else if(j == H) {
505  u[i][j] = U[iP3];
506  v[i][j] = V[iP3];
507  }
508  else if(i == 0) {
509  u[i][j] = U[iP4];
510  v[i][j] = V[iP4];
511  }
512  else {
513  tab[i][j]->getParameter(0, u[i][j]);
514  tab[i][j]->getParameter(1, v[i][j]);
515  }
516  }
517  }
518  for(int IT = 0; IT < numSmooth; IT++) {
519  for(int i = 1; i < L; i++) {
520  for(int j = 1; j < H; j++) {
521  double alpha = 0.25 * (std::pow(u[i][j + 1] - u[i][j - 1], 2) +
522  std::pow(v[i][j + 1] - v[i][j - 1], 2));
523  double gamma = 0.25 * (std::pow(u[i + 1][j] - u[i - 1][j], 2) +
524  std::pow(v[i + 1][j] - v[i - 1][j], 2));
525  double beta =
526  0.0625 *
527  ((u[i + 1][j] - u[i - 1][j]) * (u[i][j + 1] - u[i][j - 1]) +
528  (v[i + 1][j] - v[i - 1][j]) * (v[i][j + 1] - v[i][j - 1]));
529  u[i][j] = 0.5 *
530  (alpha * (u[i + 1][j] + u[i - 1][j]) +
531  gamma * (u[i][j + 1] + u[i][j - 1]) -
532  2. * beta *
533  (u[i + 1][j + 1] - u[i - 1][j + 1] - u[i + 1][j - 1] +
534  u[i - 1][j - 1])) /
535  (alpha + gamma);
536  v[i][j] = 0.5 *
537  (alpha * (v[i + 1][j] + v[i - 1][j]) +
538  gamma * (v[i][j + 1] + v[i][j - 1]) -
539  2. * beta *
540  (v[i + 1][j + 1] - v[i - 1][j + 1] - v[i + 1][j - 1] +
541  v[i - 1][j - 1])) /
542  (alpha + gamma);
543  }
544  }
545  }
546  for(int i = 1; i < L; i++) {
547  for(int j = 1; j < H; j++) {
548  GPoint p = gf->point(SPoint2(u[i][j], v[i][j]));
549  tab[i][j]->x() = p.x();
550  tab[i][j]->y() = p.y();
551  tab[i][j]->z() = p.z();
552  tab[i][j]->setParameter(0, u[i][j]);
553  tab[i][j]->setParameter(1, v[i][j]);
554  }
555  }
556  }
557 
558  // create elements
559  if(corners.size() == 4) {
560  for(int i = 0; i < L; i++) {
561  for(int j = 0; j < H; j++) {
562  MVertex *v1 = tab[i][j];
563  MVertex *v2 = tab[i + 1][j];
564  MVertex *v3 = tab[i + 1][j + 1];
565  MVertex *v4 = tab[i][j + 1];
567  gf->quadrangles.push_back(new MQuadrangle(v1, v2, v3, v4));
568  else if(gf->meshAttributes.transfiniteArrangement == 1 ||
570  ((i % 2 == 0 && j % 2 == 1) || (i % 2 == 1 && j % 2 == 0))) ||
572  ((i % 2 == 0 && j % 2 == 0) || (i % 2 == 1 && j % 2 == 1)))) {
573  // else if(rand() % 2 == 0){
574  gf->triangles.push_back(new MTriangle(v1, v2, v3));
575  gf->triangles.push_back(new MTriangle(v3, v4, v1));
576  }
577  else {
578  gf->triangles.push_back(new MTriangle(v1, v2, v4));
579  gf->triangles.push_back(new MTriangle(v4, v2, v3));
580  }
581  }
582  }
583  }
584  else {
585  if(!gf->meshAttributes.transfinite3) {
586  for(int j = 0; j < H; j++) {
587  MVertex *v1 = tab[0][0];
588  MVertex *v2 = tab[1][j];
589  MVertex *v3 = tab[1][j + 1];
590  gf->triangles.push_back(new MTriangle(v1, v2, v3));
591  }
592  for(int i = 1; i < L; i++) {
593  for(int j = 0; j < H; j++) {
594  MVertex *v1 = tab[i][j];
595  MVertex *v2 = tab[i + 1][j];
596  MVertex *v3 = tab[i + 1][j + 1];
597  MVertex *v4 = tab[i][j + 1];
599  gf->quadrangles.push_back(new MQuadrangle(v1, v2, v3, v4));
600  else if(gf->meshAttributes.transfiniteArrangement == 1 ||
602  ((i % 2 == 0 && j % 2 == 1) ||
603  (i % 2 == 1 && j % 2 == 0))) ||
605  ((i % 2 == 0 && j % 2 == 0) ||
606  (i % 2 == 1 && j % 2 == 1)))) {
607  gf->triangles.push_back(new MTriangle(v1, v2, v3));
608  gf->triangles.push_back(new MTriangle(v3, v4, v1));
609  }
610  else {
611  gf->triangles.push_back(new MTriangle(v1, v2, v4));
612  gf->triangles.push_back(new MTriangle(v4, v2, v3));
613  }
614  }
615  }
616  }
617  else {
618  for(int i = 0; i < L; i++) {
619  int j = i;
620  MVertex *v1 = tab[i][j];
621  MVertex *v2 = tab[i + 1][j];
622  MVertex *v3 = tab[i + 1][j + 1];
623  gf->triangles.push_back(new MTriangle(v1, v2, v3));
624  }
625  for(int i = 1; i < L; i++) {
626  for(int j = 0; j < i; j++) {
627  MVertex *v1 = tab[i][j];
628  MVertex *v2 = tab[i + 1][j];
629  MVertex *v3 = tab[i + 1][j + 1];
630  MVertex *v4 = tab[i][j + 1];
631  gf->triangles.push_back(new MTriangle(v1, v3, v4));
632  gf->triangles.push_back(new MTriangle(v1, v2, v3));
633  }
634  }
635  }
636  }
637 
639  return 1;
640 }
641 
642 bool id_loop_from_closed_pairs(const std::vector<std::array<int, 2> > &pairs,
643  std::vector<int> &loop)
644 {
645  /* warning: does not verify if the loop is closed, eg [1,2], [2,3] will
646  * produce [1,2,3] */
647  loop.clear();
648  if(pairs.size() < 2) return false;
649  loop.reserve(pairs.size());
650  std::vector<bool> done(pairs.size(), false);
651  loop.resize(2);
652  loop[0] = pairs[0][0];
653  loop[1] = pairs[0][1];
654  done[0] = true;
655  while(loop.size() != pairs.size()) {
656  int cv = loop.back();
657  bool found = false;
658  for(std::size_t i = 0; i < pairs.size(); ++i)
659  if(!done[i]) {
660  const std::array<int, 2> &vs = pairs[i];
661  if(vs[0] == cv) {
662  loop.push_back(vs[1]);
663  done[i] = true;
664  found = true;
665  }
666  else if(vs[1] == cv) {
667  loop.push_back(vs[0]);
668  done[i] = true;
669  found = true;
670  }
671  }
672  if(!found) {
673  loop.clear();
674  return false;
675  }
676  }
677 
678  return true;
679 }
680 
681 bool faceIsValidQuad(GFace *gf, double angle_threshold_rad)
682 {
683  if(gf->edges().size() != 4) return false;
684 
685  std::vector<std::array<int, 2> > vpairs;
686  for(GEdge *ge : gf->edges()) {
687  vpairs.push_back(
688  {{ge->getBeginVertex()->tag(), ge->getEndVertex()->tag()}});
689  }
690  std::vector<int> loop;
691  bool ok = id_loop_from_closed_pairs(vpairs, loop);
692  if(!ok || loop.size() != vpairs.size()) return false;
693 
694  if(angle_threshold_rad > 0.) {
695  /* Check angle at each corner */
696  for(std::size_t i = 0; i < loop.size(); ++i) {
697  int v1 = loop[i];
698  int v2 = loop[(i + 1) % loop.size()];
699  int v3 = loop[(i + 2) % loop.size()];
700  SVector3 t21, t23;
701  for(GEdge *ge : gf->edges()) {
702  int e_v1 = ge->getBeginVertex()->tag();
703  int e_v2 = ge->getEndVertex()->tag();
704  if(e_v1 == v1 && e_v2 == v2) {
705  Range<double> range = ge->parBounds(0);
706  SVector3 t = ge->firstDer(range.high());
707  t.normalize();
708  t21 = -t;
709  }
710  else if(e_v1 == v2 && e_v2 == v1) {
711  Range<double> range = ge->parBounds(0);
712  SVector3 t = ge->firstDer(range.low());
713  t.normalize();
714  t21 = t;
715  }
716  else if(e_v1 == v2 && e_v2 == v3) {
717  Range<double> range = ge->parBounds(0);
718  SVector3 t = ge->firstDer(range.low());
719  t.normalize();
720  t23 = t;
721  }
722  else if(e_v1 == v3 && e_v2 == v2) {
723  Range<double> range = ge->parBounds(0);
724  SVector3 t = ge->firstDer(range.high());
725  t.normalize();
726  t23 = -t;
727  }
728  }
729  double agl = angle(t21, t23);
730  if(agl > angle_threshold_rad) {
731  Msg::Debug("quadrangular face %i rejected for automatic transfinite "
732  "because corner angle = %.3f deg > threshold = %.3f deg\n",
733  gf->tag(), 180. / M_PI * agl,
734  180. / M_PI * angle_threshold_rad);
735  return false;
736  }
737  }
738  }
739 
740  return true;
741 }
742 
744 {
745  if(face->edges().size() != 4) return nullptr;
746  GEdge *op = nullptr;
747  int v1 = edge->getBeginVertex()->tag();
748  int v2 = edge->getEndVertex()->tag();
749  bool edgeInside = false;
750  for(GEdge *ge : face->edges()) {
751  if(ge == edge) {
752  edgeInside = true;
753  continue;
754  }
755  int cv1 = ge->getBeginVertex()->tag();
756  int cv2 = ge->getEndVertex()->tag();
757  if(cv1 != v1 && cv1 != v2 && cv2 != v1 && cv2 != v2) {
758  if(op == nullptr) { op = ge; }
759  else { /* already found ? should not happen */
760  return nullptr;
761  }
762  }
763  }
764  if(!edgeInside) return nullptr;
765  return op;
766 }
767 
768 void build_chords(const std::set<GFace *> &faces,
769  std::vector<std::set<GEdge *> > &chords, double maxDiffRel,
770  bool ignoreEmbedded = false)
771 {
772  /* Connectivity */
773  std::map<GEdge *, std::vector<GFace *> > edge2faces;
774  for(GFace *gf : faces) {
775  if(!ignoreEmbedded &&
776  (gf->embeddedEdges().size() > 0 || gf->embeddedVertices().size() > 0))
777  continue;
778  for(GEdge *ge : gf->edges()) { edge2faces[ge].push_back(gf); }
779  }
780 
781  Msg::Debug("build chords: %li faces, %li edges", faces.size(),
782  edge2faces.size());
783 
784  std::map<GEdge *, bool> done;
785  for(auto &kv : edge2faces) {
786  GEdge *geInit = kv.first;
787  if(done.find(geInit) != done.end()) continue;
788 
789  /* Breath first search starting from a GEdge */
790  std::queue<GEdge *> Q;
791  Q.push(geInit);
792  done[geInit] = true;
793 
794  std::set<GEdge *> chord;
795  while(Q.size() > 0) {
796  GEdge *ge = Q.front();
797  Q.pop();
798  int n = meshGEdgeTargetNumberOfPoints(ge);
799  chord.insert(ge);
800  for(GFace *gf : edge2faces[ge]) {
801  GEdge *ge2 = quad_face_opposite_edge(gf, ge);
802  if(ge2 && done.find(ge2) == done.end()) {
803  int n2 = meshGEdgeTargetNumberOfPoints(ge2);
804  if(double(std::abs(n2 - n)) / double(std::max(n, n2)) > maxDiffRel) {
805  continue;
806  }
807  Q.push(ge2);
808  done[ge2] = true;
809  }
810  }
811  }
812 
813  if(chord.size() >= 2) { chords.push_back(chord); }
814  }
815 }
816 
817 bool MeshSetTransfiniteFacesAutomatic(std::set<GFace *> &candidate_faces,
818  double cornerAngle, bool setRecombine,
819  double maxDiffRel, bool ignoreEmbedded)
820 {
821  /* Filter with topology and geometry */
822  std::set<GFace *> faces;
823  for(GFace *gf : candidate_faces) {
824  if(!ignoreEmbedded &&
825  (gf->embeddedEdges().size() > 0 || gf->embeddedVertices().size() > 0))
826  continue;
827  if(faceIsValidQuad(gf, cornerAngle)) { faces.insert(gf); }
828  }
829 
830  /* Build the topological chords in quad structure */
831  Msg::Debug(
832  "transfinite automatic: building chords from %li quadrangular faces ...",
833  faces.size());
834  std::vector<std::set<GEdge *> > chords;
835  build_chords(faces, chords, maxDiffRel);
836  Msg::Debug("... found %li chords", chords.size());
837 
838  /* Determine the number of points, set the transfinite curves */
839  Msg::Debug("transfinite automatic: assigning number of points ...");
840  std::size_t ne = 0;
841  for(std::set<GEdge *> &chord : chords) {
842  double avgNbPoints = 0;
843  for(GEdge *ge : chord) {
844  int n = meshGEdgeTargetNumberOfPoints(ge);
845  avgNbPoints += double(n);
846  }
847  avgNbPoints /= chord.size();
848 
849  int N = int(avgNbPoints + 0.5);
850  if(N == 0) N = 2;
851  if(N % 2 == 1) N = N + 1;
852 
853  Msg::Debug("- chord with %li edges -> %i points\n", chord.size(), N);
854 
855  for(GEdge *ge : chord) {
856  ge->meshAttributes.method = MESH_TRANSFINITE;
857  ge->meshAttributes.nbPointsTransfinite = N;
858  ge->meshAttributes.typeTransfinite = 1; /* Progression */
859  ge->meshAttributes.coeffTransfinite = 1.;
860  if(CTX::instance()->mesh.algo2d == ALGO_2D_QUAD_QUASI_STRUCT) {
861  ge->meshAttributes.typeTransfinite = 4; /* Use size map */
862  }
863  ne += 1;
864  }
865  }
866  Msg::Debug("transfinite automatic: transfinite set on %li edges", ne);
867 
868  std::size_t nf = 0;
869  for(GFace *gf : faces) {
870  bool transfinite = true;
871  std::vector<int> nPoints(4, 0);
872  std::size_t count = 0;
873  for(GEdge *ge : gf->edges()) {
874  if(ge->meshAttributes.method != MESH_TRANSFINITE) {
875  transfinite = false;
876  break;
877  }
878  nPoints[count] = ge->meshAttributes.nbPointsTransfinite;
879  count += 1;
880  }
881  if(transfinite && nPoints[0] == nPoints[2] && nPoints[1] == nPoints[3]) {
882  nf += 1;
883  gf->meshAttributes.method = MESH_TRANSFINITE;
884  gf->meshAttributes.transfiniteArrangement = 1; /* Right */
885  if(setRecombine) {
886  gf->meshAttributes.recombine = 1;
887  gf->meshAttributes.recombineAngle = 45.;
888  }
889  }
890  }
891  Msg::Debug("transfinite automatic: transfinite set on %li faces", nf);
892  return true;
893 }
contextMeshOptions::recombineAll
int recombineAll
Definition: Context.h:30
MTriangle.h
GFace::transfinite3
bool transfinite3
Definition: GFace.h:367
GFace::edges
virtual std::vector< GEdge * > const & edges() const
Definition: GFace.h:121
GFace::status
GEntity::MeshGenerationStatus status
Definition: GFace.h:395
contextMeshOptions::nbSmoothing
int nbSmoothing
Definition: Context.h:29
maxDistParam
double maxDistParam(const std::vector< double > &U, const std::vector< double > &V)
Definition: meshGFaceTransfinite.cpp:155
GPoint::y
double y() const
Definition: GPoint.h:22
GFace::recombine
int recombine
Definition: GFace.h:344
GFace.h
TRAN_TRI
#define TRAN_TRI(c1, c2, c3, s1, s2, s3, u, v)
Definition: meshGFaceTransfinite.cpp:43
GFace
Definition: GFace.h:33
GFace::triangles
std::vector< MTriangle * > triangles
Definition: GFace.h:428
Msg::Info
static void Info(const char *fmt,...)
Definition: GmshMessage.cpp:587
computeEdgeLoops
static void computeEdgeLoops(const GFace *gf, std::vector< MVertex * > &all_mvertices, std::vector< int > &indices)
Definition: meshGFaceTransfinite.cpp:90
angle
double angle(const SVector3 &a, const SVector3 &b)
Definition: SVector3.h:157
SPoint2
Definition: SPoint2.h:12
Msg::Debug
static void Debug(const char *fmt,...)
Definition: GmshMessage.cpp:752
MESH_TRANSFINITE
#define MESH_TRANSFINITE
Definition: GmshDefines.h:259
MVertex
Definition: MVertex.h:24
meshGEdgeTargetNumberOfPoints
int meshGEdgeTargetNumberOfPoints(GEdge *ge)
Definition: meshGEdge.cpp:980
Msg::Error
static void Error(const char *fmt,...)
Definition: GmshMessage.cpp:482
GFace::transfiniteArrangement
int transfiniteArrangement
Definition: GFace.h:353
GFace::method
char method
Definition: GFace.h:348
reparamMeshVertexOnFace
bool reparamMeshVertexOnFace(MVertex const *v, const GFace *gf, SPoint2 &param, bool onSurface, bool failOnSeam, int dir)
Definition: MVertex.cpp:522
GFace::meshAttributes
struct GFace::@18 meshAttributes
GFace::orientations
virtual std::vector< int > const & orientations() const
Definition: GFace.h:114
Range::high
T high() const
Definition: Range.h:20
GFace::point
virtual GPoint point(double par1, double par2) const =0
SVector3
Definition: SVector3.h:16
MeshSetTransfiniteFacesAutomatic
bool MeshSetTransfiniteFacesAutomatic(std::set< GFace * > &candidate_faces, double cornerAngle, bool setRecombine, double maxDiffRel, bool ignoreEmbedded)
Automatically set transfinite constraints on curves and faces in the candidate_faces if possible....
Definition: meshGFaceTransfinite.cpp:817
GEdgeLoop::begin
iter begin()
Definition: GEdgeLoop.h:48
id_loop_from_closed_pairs
bool id_loop_from_closed_pairs(const std::vector< std::array< int, 2 > > &pairs, std::vector< int > &loop)
Definition: meshGFaceTransfinite.cpp:642
meshGEdge.h
GEntity::edges
virtual std::vector< GEdge * > const & edges() const
Definition: GEntity.h:211
GmshMessage.h
edges
static int edges[6][2]
Definition: meshGRegionLocalMeshMod.cpp:23
quad_face_opposite_edge
GEdge * quad_face_opposite_edge(GFace *face, GEdge *edge)
Definition: meshGFaceTransfinite.cpp:743
build_chords
void build_chords(const std::set< GFace * > &faces, std::vector< std::set< GEdge * > > &chords, double maxDiffRel, bool ignoreEmbedded=false)
Definition: meshGFaceTransfinite.cpp:768
TRAN_QUA
#define TRAN_QUA(c1, c2, c3, c4, s1, s2, s3, s4, u, v)
Definition: meshGFaceTransfinite.cpp:36
GPoint
Definition: GPoint.h:13
GFace::quadrangles
std::vector< MQuadrangle * > quadrangles
Definition: GFace.h:429
GPoint::z
double z() const
Definition: GPoint.h:23
CTX::instance
static CTX * instance()
Definition: Context.cpp:122
GEdge.h
GEdgeLoop
Definition: GEdgeLoop.h:36
Range
Definition: Range.h:10
GFace::meshStatistics
struct GFace::@19 meshStatistics
GEntity::mesh_vertices
std::vector< MVertex * > mesh_vertices
Definition: GEntity.h:56
GVertex
Definition: GVertex.h:23
MVertex.h
faceIsValidQuad
bool faceIsValidQuad(GFace *gf, double angle_threshold_rad)
Definition: meshGFaceTransfinite.cpp:681
Numeric.h
GEntity::DONE
@ DONE
Definition: GEntity.h:130
GEdge::getBeginVertex
virtual GVertex * getBeginVertex() const
Definition: GEdge.h:63
GEntity::geomType
virtual GeomType geomType() const
Definition: GEntity.h:238
CTX::mesh
contextMeshOptions mesh
Definition: Context.h:313
GEntity::tag
int tag() const
Definition: GEntity.h:280
MFaceVertex
Definition: MVertex.h:168
findTransfiniteCorners
void findTransfiniteCorners(GFace *gf, std::vector< MVertex * > &corners)
Definition: meshGFaceTransfinite.cpp:46
MTriangle
Definition: MTriangle.h:26
MeshTransfiniteSurface
int MeshTransfiniteSurface(GFace *gf)
Definition: meshGFaceTransfinite.cpp:166
meshGFace.h
Context.h
GFace::transfiniteSmoothing
int transfiniteSmoothing
Definition: GFace.h:355
GVertex.h
z
const double z
Definition: GaussQuadratureQuad.cpp:56
MQuadrangle.h
GEdge
Definition: GEdge.h:26
GFace::transfinite_vertices
std::vector< std::vector< MVertex * > > transfinite_vertices
Definition: GFace.h:420
GEdge::getEndVertex
virtual GVertex * getEndVertex() const
Definition: GEdge.h:64
Range::low
T low() const
Definition: Range.h:18
MVertex::distance
double distance(MVertex *const v)
Definition: MVertex.h:105
GEdgeLoop::end
iter end()
Definition: GEdgeLoop.h:49
GFace::XYZtoUV
void XYZtoUV(double X, double Y, double Z, double &U, double &V, double relax, bool onSurface=true, bool convTestXYZ=false) const
Definition: GFace.cpp:1144
GPoint::x
double x() const
Definition: GPoint.h:21
MQuadrangle
Definition: MQuadrangle.h:26
ALGO_2D_QUAD_QUASI_STRUCT
#define ALGO_2D_QUAD_QUASI_STRUCT
Definition: GmshDefines.h:247
GEntity::RuledSurface
@ RuledSurface
Definition: GEntity.h:111
SVector3::normalize
double normalize()
Definition: SVector3.h:38
faces
static int faces[4][3]
Definition: meshGRegionDelaunayInsertion.cpp:165
GFace::corners
std::vector< GVertex * > corners
Definition: GFace.h:350