gmsh-TingyuanDoc  0.1
An Open-Source Timing-driven Analytical Mixed-size FPGA Placer
GModelParametrize.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 #include <vector>
7 #include <set>
8 #include <map>
9 #include <stack>
10 #include <sstream>
11 #include <string.h>
12 #include "GmshConfig.h"
13 #include "GModel.h"
14 #include "GFace.h"
15 #include "discreteFace.h"
16 #include "discreteEdge.h"
17 #include "discreteVertex.h"
18 #include "MTriangle.h"
19 #include "MEdge.h"
20 #include "GEdge.h"
21 #include "MLine.h"
22 #include "MPoint.h"
23 #include "Context.h"
24 #include "OS.h"
25 #include "GmshMessage.h"
26 #include "GModelParametrize.h"
27 #include "Context.h"
28 #include "curvature.h"
29 
30 #if defined(HAVE_MESH)
31 #include "meshPartition.h"
32 #include "meshGFaceOptimize.h"
33 #endif
34 
35 #if defined(HAVE_SOLVER)
36 #include "linearSystemPETSc.h"
37 #include "linearSystemCSR.h"
38 #include "linearSystemFull.h"
39 #endif
40 
41 #if defined(HAVE_MESH)
42 
43 static GEdge *
44 getModelEdge(GModel *gm, std::vector<GFace *> &gfs,
45  std::vector<std::pair<GEdge *, std::vector<GFace *> > > &newEdges,
46  size_t &MAX1)
47 {
48  if(gfs.size() == 2 && gfs[0] == gfs[1]) return nullptr;
49  for(size_t i = 0; i < newEdges.size(); i++) {
50  if(gfs.size() == newEdges[i].second.size()) {
51  bool found = true;
52  for(size_t j = 0; j < newEdges[i].second.size(); j++)
53  if(std::find(gfs.begin(), gfs.end(), newEdges[i].second[j]) ==
54  gfs.end()) {
55  found = false;
56  break;
57  }
58  if(found) return newEdges[i].first;
59  }
60  }
61 
62  discreteEdge *ge = new discreteEdge(gm, (MAX1++) + 1, nullptr, nullptr);
63  newEdges.push_back(std::make_pair(ge, gfs));
64  return ge;
65 }
66 
67 static void
69  std::map<MEdge, std::vector<MTriangle *>, MEdgeLessThan> &tris)
70 {
71  for(int i = 0; i < 3; i++) {
72  MEdge e = t->getEdge(i);
73  auto it = tris.find(e);
74  if(it == tris.end()) {
75  std::vector<MTriangle *> v;
76  v.push_back(t);
77  tris[e] = v;
78  }
79  else {
80  it->second.push_back(t);
81  }
82  }
83 }
84 
85 static bool breakForLargeAngle(MVertex *vprev, MVertex *vmid, MVertex *vpos,
86  double threshold)
87 {
88  if(threshold >= M_PI - 1e-12) return false;
89  if(threshold <= 0) return true;
90  SVector3 v1(vprev->point(), vmid->point());
91  SVector3 v2(vmid->point(), vpos->point());
92  double a = angle(v1, v2);
93  if((a > threshold && a < M_PI - threshold) ||
94  (a > M_PI + threshold && a < 2 * M_PI - threshold)) {
95  Msg::Debug("Breaking curve for angle = %g", a);
96  return true;
97  }
98  return false;
99 }
100 
101 #endif
102 
103 void classifyFaces(GModel *gm, double curveAngleThreshold)
104 {
105 #if defined(HAVE_MESH)
106  size_t MAX0 = gm->getMaxElementaryNumber(0);
107  size_t MAX1 = gm->getMaxElementaryNumber(1);
108  size_t MAX2 = gm->getMaxElementaryNumber(2);
109 
110  // check if mesh is high-order
111  bool ho = false;
112  for(auto it = gm->firstFace(); it != gm->lastFace(); it++) {
113  GFace *gf = *it;
114  for(std::size_t i = 0; i < gf->triangles.size(); i++) {
115  if(gf->triangles[i]->getPolynomialOrder() > 1) {
116  ho = true;
117  break;
118  }
119  }
120  if(ho) break;
121  }
122  if(ho) {
123  Msg::Warning("Reverting to first order mesh for classification");
124  gm->setOrderN(1, 0, 0, 0);
125  }
126 
127  // create a structure from mesh edges to geometrical curves, and remove curves
128  // from the model
129  std::set<MLine *, MLinePtrLessThan> lines;
130  std::vector<GEdge *> edgesToRemove;
131  for(auto it = gm->firstEdge(); it != gm->lastEdge(); ++it) {
132  for(std::size_t i = 0; i < (*it)->lines.size(); i++) {
133  lines.insert((*it)->lines[i]);
134  }
135  edgesToRemove.push_back(*it);
136  }
137  for(std::size_t i = 0; i < edgesToRemove.size(); ++i) {
138  gm->remove(edgesToRemove[i]);
139  }
140 
141  // remove points from model
142  std::vector<GVertex *> pointsToRemove;
143  for(auto it = gm->firstVertex(); it != gm->lastVertex(); ++it) {
144  pointsToRemove.push_back(*it);
145  }
146  for(std::size_t i = 0; i < pointsToRemove.size(); ++i) {
147  gm->remove(pointsToRemove[i]);
148  }
149 
150  // create triangle-triangle connections
151  std::map<MTriangle *, GFace *> reverse_old;
152  std::map<MEdge, std::vector<MTriangle *>, MEdgeLessThan> tris;
153  std::set<MTriangle *, MElementPtrLessThan> touched;
154  for(auto it = gm->firstFace(); it != gm->lastFace(); it++) {
155  GFace *gf = *it;
156  for(std::size_t i = 0; i < gf->triangles.size(); i++) {
157  touched.insert(gf->triangles[i]);
158  reverse_old[gf->triangles[i]] = gf;
159  addTriangle(gf->triangles[i], tris);
160  }
161  gf->triangles.clear();
162  gf->mesh_vertices.clear();
163  }
164 
165  if(touched.empty()) {
166  Msg::Warning("No triangles to reclassify in surface mesh");
167  return;
168  }
169 
170  // reset classification of all mesh nodes
171  for(auto it = touched.begin(); it != touched.end(); it++) {
172  for(std::size_t j = 0; j < (*it)->getNumVertices(); j++)
173  (*it)->getVertex(j)->setEntity(nullptr);
174  }
175 
176  std::map<MTriangle *, GFace *> reverse;
177  std::multimap<GFace *, GFace *> replacedBy;
178  std::list<GFace *> newf;
179 
180  {
181  while(!touched.empty()) {
182  std::stack<MTriangle *> st;
183  st.push(*(touched.begin()));
184  touched.erase(touched.begin());
185  discreteFace *gf = new discreteFace(gm, (MAX2++) + 1);
186  while(!st.empty()) {
187  MTriangle *t = st.top();
188  gf->triangles.push_back(t);
189  reverse[t] = gf;
190  st.pop();
191  for(int i = 0; i < 3; i++) {
192  MEdge e = t->getEdge(i);
193  auto it = tris.find(e);
194  if(it == tris.end()) {
195  Msg::Error("Could not find triangle linked to edge");
196  break;
197  }
198  MLine ll(e.getVertex(0), e.getVertex(1));
199  auto itl = lines.find(&ll);
200  if(itl == lines.end()) {
201  MTriangle *tt = it->second[0] == t ? it->second[1] : it->second[0];
202  auto it2 = touched.find(tt);
203  if(it2 != touched.end()) {
204  st.push(tt);
205  touched.erase(it2);
206  }
207  }
208  }
209  }
210  gm->add(gf);
211  newf.push_back(gf);
212 
213  for(std::size_t i = 0; i < gf->triangles.size(); i++) {
214  replacedBy.insert(std::make_pair(reverse_old[gf->triangles[i]], gf));
215  }
216  }
217  }
218  Msg::Info("Found %d model surfaces", newf.size());
219 
220  // now we have all faces coloured. If some regions were existing, replace
221  // their faces by the new ones
222  for(auto rit = gm->firstRegion(); rit != gm->lastRegion(); ++rit) {
223  std::vector<GFace *> _xfaces = (*rit)->faces();
224  std::set<GFace *, GEntityPtrLessThan> _newFaces;
225  for(auto itf = _xfaces.begin(); itf != _xfaces.end(); ++itf) {
226  auto itLow = replacedBy.lower_bound(*itf);
227  auto itUp = replacedBy.upper_bound(*itf);
228 
229  for(; itLow != itUp; ++itLow) _newFaces.insert(itLow->second);
230  }
231  (*rit)->set(std::vector<GFace *>(_newFaces.begin(), _newFaces.end()));
232  }
233 
234  std::vector<std::pair<GEdge *, std::vector<GFace *> > > newEdges;
235  {
236  auto it = tris.begin();
237  for(; it != tris.end(); ++it) {
238  MLine ml(it->first.getVertex(0), it->first.getVertex(1));
239  auto itl = lines.find(&ml);
240  if(itl != lines.end()) {
241  std::vector<GFace *> faces;
242  for(size_t i = 0; i < it->second.size(); ++i)
243  faces.push_back(reverse[it->second[i]]);
244  GEdge *ge = getModelEdge(gm, faces, newEdges, MAX1);
245  if(ge) ge->lines.push_back(*itl);
246  }
247  }
248  }
249  Msg::Info("Found %d model curves", newEdges.size());
250 
251  // check if new curves should not be split;
252 
253  std::map<discreteFace *, std::vector<int>, GEntityPtrLessThan>
254  newFaceTopology;
255  std::map<MVertex *, GVertex *> modelVertices;
256 
257  for(auto ite = newEdges.begin(); ite != newEdges.end(); ++ite) {
258  std::vector<MEdge> allEdges;
259 
260  for(std::size_t i = 0; i < ite->first->lines.size(); i++) {
261  allEdges.push_back(MEdge(ite->first->lines[i]->getVertex(0),
262  ite->first->lines[i]->getVertex(1)));
263  delete ite->first->lines[i];
264  }
265  ite->first->lines.clear();
266  std::vector<std::vector<MVertex *> > vs_;
267 
268  SortEdgeConsecutive(allEdges, vs_);
269 
270  std::vector<std::vector<MVertex *> > vs;
271  for(size_t i = 0; i < vs_.size(); i++) {
272  bool periodic = (vs_[i][vs_[i].size() - 1] == vs_[i][0]);
273  if(periodic) {
274  for(size_t j = 0; j < vs_[i].size() - 1; j++) {
275  MVertex *v0 = vs_[i][j == 0 ? (vs_[i].size() - 2) : (j - 1)];
276  MVertex *v1 = vs_[i][j];
277  MVertex *v2 = vs_[i][j + 1];
278  if(breakForLargeAngle(v0, v1, v2, curveAngleThreshold)) {
279  std::vector<MVertex *> temp;
280  for(size_t k = j; k < vs_[i].size() + j; k++) {
281  temp.push_back(vs_[i][k % vs_[i].size()]);
282  }
283  vs_[i] = temp;
284  break;
285  }
286  }
287  }
288 
289  std::vector<size_t> cuts_;
290  cuts_.push_back(0);
291  for(size_t j = 1; j < vs_[i].size() - 1; j++) {
292  MVertex *v0 = vs_[i][j - 1];
293  MVertex *v1 = vs_[i][j];
294  MVertex *v2 = vs_[i][j + 1];
295  if(breakForLargeAngle(v0, v1, v2, curveAngleThreshold))
296  cuts_.push_back(j);
297  }
298  cuts_.push_back(vs_[i].size() - 1);
299 
300  MVertex *first = vs_[i][cuts_[0]];
301  for(size_t k = 1; k < cuts_.size(); k++) {
302  std::vector<MVertex *> vv_;
303  for(size_t j = cuts_[k - 1]; j <= cuts_[k]; j++) {
304  if(j == cuts_[k - 1] || vs_[i][j] != vs_[i][j - 1]) {
305  vv_.push_back(vs_[i][j]);
306  }
307  }
308  if(periodic && k == cuts_.size() - 1 && cuts_.size() > 2) {
309  vv_.push_back(first);
310  }
311  vs.push_back(vv_);
312  }
313  }
314 
315  for(size_t i = 0; i < vs.size(); i++) {
316  MVertex *vB = vs[i][0];
317  MVertex *vE = vs[i][vs[i].size() - 1];
318 
319  auto itMV = modelVertices.find(vB);
320  if(itMV == modelVertices.end()) {
321  GVertex *newGv =
322  new discreteVertex(gm, (MAX0++) + 1, vB->x(), vB->y(), vB->z());
323  newGv->mesh_vertices.push_back(vB);
324  vB->setEntity(newGv);
325  newGv->points.push_back(new MPoint(vB));
326  gm->add(newGv);
327  modelVertices[vB] = newGv;
328  }
329  itMV = modelVertices.find(vE);
330  if(itMV == modelVertices.end()) {
331  GVertex *newGv =
332  new discreteVertex(gm, (MAX0++) + 1, vE->x(), vE->y(), vE->z());
333  newGv->mesh_vertices.push_back(vE);
334  newGv->points.push_back(new MPoint(vE));
335  vE->setEntity(newGv);
336  gm->add(newGv);
337  modelVertices[vE] = newGv;
338  }
339 
340  GEdge *newGe = new discreteEdge(gm, (MAX1++) + 1, modelVertices[vB],
341  modelVertices[vE]);
342 
343  for(size_t j = 1; j < vs[i].size(); j++) {
344  MVertex *v1 = vs[i][j - 1];
345  MVertex *v2 = vs[i][j];
346  newGe->lines.push_back(new MLine(v1, v2));
347  }
348 
349  for(size_t j = 0; j < newGe->lines.size(); j++) {
350  MLine *l = newGe->lines[j];
351  if(l->getVertex(0)->onWhat()) {
352  newGe->mesh_vertices.push_back(l->getVertex(0));
353  l->getVertex(0)->setEntity(newGe);
354  }
355  }
356 
357  gm->add(newGe);
358  for(size_t K = 0; K < ite->second.size(); K++) {
359  discreteFace *gf1 = dynamic_cast<discreteFace *>(ite->second[K]);
360  if(gf1) newFaceTopology[gf1].push_back(newGe->tag());
361  }
362  }
363  }
364 
365  for(auto itFT = newFaceTopology.begin(); itFT != newFaceTopology.end();
366  ++itFT) {
367  itFT->first->setBoundEdges(itFT->second);
368  }
369 
370  for(auto ite = newEdges.begin(); ite != newEdges.end(); ++ite) {
371  GEdge *ge = ite->first;
372  gm->remove(ge);
373  // delete ge;
374  }
375 
376  // delete empty mesh faces and reclasssify
377  std::set<GFace *, GEntityPtrLessThan> fac = gm->getFaces();
378  for(auto fit = fac.begin(); fit != fac.end(); ++fit) {
379  std::set<MVertex *, MVertexPtrLessThan> verts;
380  (*fit)->mesh_vertices.clear();
381  for(std::size_t i = 0; i < (*fit)->triangles.size(); i++) {
382  for(int j = 0; j < 3; j++) {
383  if(!(*fit)->triangles[i]->getVertex(j)->onWhat()) {
384  (*fit)->triangles[i]->getVertex(j)->setEntity(*fit);
385  verts.insert((*fit)->triangles[i]->getVertex(j));
386  }
387  }
388  }
389  if((*fit)->triangles.size())
390  (*fit)->mesh_vertices.insert((*fit)->mesh_vertices.begin(), verts.begin(),
391  verts.end());
392  else
393  gm->remove(*fit);
394  }
395 #endif
396 }
397 
398 void classifyFaces(GModel *gm, double angleThreshold, bool includeBoundary,
399  bool forParametrization, double curveAngleThreshold)
400 {
401 #if defined(HAVE_MESH)
402  size_t MAX1 = gm->getMaxElementaryNumber(1);
403 
404  Msg::StatusBar(true, "Classifying surfaces (angle: %g)...",
405  angleThreshold * 180. / M_PI);
406  double t1 = Cpu(), w1 = TimeOfDay();
407 
408  std::vector<MElement *> elements;
409  for(auto it = gm->firstFace(); it != gm->lastFace(); ++it) {
410  elements.insert(elements.end(), (*it)->triangles.begin(),
411  (*it)->triangles.end());
412  elements.insert(elements.end(), (*it)->quadrangles.begin(),
413  (*it)->quadrangles.end());
414  }
415 
416  discreteEdge *edge = new discreteEdge(gm, (MAX1++) + 1, nullptr, nullptr);
417  gm->add(edge);
418 
419  e2t_cont adj;
420  buildEdgeToElements(elements, adj);
421  std::vector<edge_angle> edges_detected, edges_lonely;
422  buildListOfEdgeAngle(adj, edges_detected, edges_lonely);
423  for(std::size_t i = 0; i < edges_detected.size(); i++) {
424  edge_angle ea = edges_detected[i];
425  if(ea.angle <= angleThreshold) break;
426  edge->lines.push_back(new MLine(ea.v1, ea.v2));
427  }
428  if(includeBoundary) {
429  for(std::size_t i = 0; i < edges_lonely.size(); i++) {
430  edge_angle ea = edges_lonely[i];
431  edge->lines.push_back(new MLine(ea.v1, ea.v2));
432  }
433  }
434 
436  if(forParametrization)
438  computeNonManifoldEdges(gm, edge->lines, true);
439  classifyFaces(gm, curveAngleThreshold);
440 
441  gm->remove(edge);
442  edge->lines.clear();
443  delete edge;
444 
446  gm->destroyMeshCaches();
447  gm->deleteVertexArrays();
448 
449  // we have created and deleted discrete entities; call this to reset the
450  // handles in the old GEO database (without this, empty discrete entities will
451  // show up at the next sync between the GEO database and the GModel).
453 
454  double t2 = Cpu(), w2 = TimeOfDay();
455  Msg::StatusBar(true, "Done classifying surfaces (Wall %gs, CPU %gs)", w2 - w1,
456  t2 - t1);
457 #else
458  Msg::Error("Surface classification requires the mesh module");
459 #endif
460 }
461 
463 {
464  std::map<MVertex *, std::pair<SVector3, SVector3> > &C = gm->getCurvatures();
465  C.clear();
466  for(auto it = gm->firstFace(); it != gm->lastFace(); ++it) {
467  GFace *gf = *it;
468  std::map<MVertex *, int> nodeIndex;
469  std::vector<SPoint3> nodes;
470  std::vector<int> tris;
471  std::vector<std::pair<SVector3, SVector3> > curv;
472  for(std::size_t i = 0; i < gf->triangles.size(); i++) {
473  MTriangle *t = gf->triangles[i];
474  for(int j = 0; j < 3; j++) {
475  MVertex *v = t->getVertex(j);
476  if(nodeIndex.find(v) == nodeIndex.end()) {
477  int idx = nodes.size();
478  nodeIndex[v] = idx;
479  nodes.push_back(v->point());
480  tris.push_back(idx);
481  }
482  else {
483  tris.push_back(nodeIndex[v]);
484  }
485  }
486  }
487  CurvatureRusinkiewicz(tris, nodes, curv);
488  for(auto itv = nodeIndex.begin(); itv != nodeIndex.end(); itv++) {
489  C[itv->first] = curv[itv->second];
490  }
491  }
492  return 0;
493 }
494 
495 bool computeParametrization(const std::vector<MTriangle *> &triangles,
496  std::vector<MVertex *> &nodes,
497  std::vector<SPoint2> &stl_vertices_uv,
498  std::vector<SPoint3> &stl_vertices_xyz,
499  std::vector<int> &stl_triangles)
500 {
501  stl_vertices_uv.clear();
502  stl_vertices_xyz.clear();
503  stl_triangles.clear();
504  nodes.clear();
505 
506  if(triangles.empty()) return false;
507 
508  // get nodes and edges
509  std::map<MVertex *, int> nodeIndex;
510  std::map<MEdge, std::vector<MTriangle *>, MEdgeLessThan> edges;
511  for(std::size_t i = 0; i < triangles.size(); i++) {
512  MTriangle *t = triangles[i];
513  for(int j = 0; j < 3; j++) {
514  MVertex *v = t->getVertex(j);
515  if(nodeIndex.find(v) == nodeIndex.end()) {
516  nodeIndex[v] = nodes.size();
517  nodes.push_back(v);
518  }
519  edges[t->getEdge(j)].push_back(t);
520  }
521  }
522 
523  // compute edge loops
524  std::vector<MEdge> es;
525  for(auto it = edges.begin(); it != edges.end(); ++it) {
526  if(it->second.size() == 1) { // on boundary
527  es.push_back(it->first);
528  }
529  else if(it->second.size() == 2) { // inside
530  }
531  else { // non-manifold: not supported
532  Msg::Error("Wrong topology of triangulation for parametrization: one "
533  "edge is incident to %d triangles",
534  it->second.size());
535  return false;
536  }
537  }
538  std::vector<std::vector<MVertex *> > vs;
539  if(!SortEdgeConsecutive(es, vs)) {
540  Msg::Error("Wrong topology of boundary mesh for parametrization");
541  return false;
542  }
543  if(vs.empty() || vs[0].size() < 2) {
544  Msg::Error("Invalid exterior boundary mesh for parametrization");
545  return false;
546  }
547 
548  Msg::Debug("Parametrisation of surface with %lu triangles, %lu edges and "
549  "%lu holes",
550  triangles.size(), edges.size(), vs.size() - 1);
551 
552  // find longest loop and use it as the "exterior" loop
553  int loop = 0;
554  double longest = 0.;
555  for(std::size_t i = 0; i < vs.size(); i++) {
556  double l = 0.;
557  for(std::size_t j = 1; j < vs[i].size(); j++) {
558  l += vs[i][j]->point().distance(vs[i][j - 1]->point());
559  }
560  if(l > longest) {
561  longest = l;
562  loop = i;
563  }
564  }
565 
566  // check orientation of the loop and reverse if necessary
567  bool reverse = true;
568  MEdge ref(vs[loop][0], vs[loop][1]);
569  for(std::size_t i = 0; i < triangles.size(); i++) {
570  MTriangle *t = triangles[i];
571  for(int j = 0; j < 3; j++) {
572  MEdge e = t->getEdge(j);
573  if(e.getVertex(0) == ref.getVertex(0) &&
574  e.getVertex(1) == ref.getVertex(1)) {
575  reverse = false;
576  break;
577  }
578  }
579  if(!reverse) break;
580  }
581  if(reverse) { std::reverse(vs[0].begin(), vs[0].end()); }
582 
583  std::vector<double> u(nodes.size(), 0.), v(nodes.size(), 0.);
584 
585  // boundary conditions
586  std::vector<bool> bc(nodes.size(), false);
587  double currentLength = 0;
588  int index = nodeIndex[vs[loop][0]];
589  bc[index] = true;
590  u[index] = 1.;
591  v[index] = 0.;
592  for(std::size_t i = 1; i < vs[loop].size() - 1; i++) {
593  currentLength += vs[loop][i]->point().distance(vs[loop][i - 1]->point());
594  double angle = 2 * M_PI * currentLength / longest;
595  index = nodeIndex[vs[loop][i]];
596  bc[index] = true;
597  u[index] = cos(angle);
598  v[index] = sin(angle);
599  }
600 
601  // assemble matrix
602 #if defined(HAVE_SOLVER)
603 #if defined(HAVE_PETSC)
605  std::string options = "-ksp_type preonly -pc_type lu ";
606 #if defined(PETSC_HAVE_MUMPS)
607  options += "-pc_factor_mat_solver_type mumps";
608 #elif defined(PETSC_HAVE_MKL_PARDISO)
609  options += "-pc_factor_mat_solver_type mkl_pardiso";
610 #elif defined(PETSC_HAVE_UMFPACK) || defined(PETSC_HAVE_SUITESPARSE)
611  options += "-pc_factor_mat_solver_type umfpack";
612 #endif
613  lsys->setParameter("petsc_solver_options", options);
614  lsys->setParameter("matrix_reuse", "same_matrix");
615 #elif defined(HAVE_GMM)
617 #else
619 #endif
620 
621  lsys->allocate(nodes.size());
622 
623 #if defined(HAVE_PETSC)
624  for(auto it = edges.begin(); it != edges.end(); ++it) {
625  for(int i = 0; i < 2; i++) {
626  for(int j = 0; j < 2; j++) {
627  lsys->insertInSparsityPattern(nodeIndex[it->first.getVertex(i)],
628  nodeIndex[it->first.getVertex(j)]);
629  }
630  }
631  }
632 #endif
633 
634  for(auto it = edges.begin(); it != edges.end(); ++it) {
635  for(int ij = 0; ij < 2; ij++) {
636  MVertex *v0 = it->first.getVertex(ij);
637  int index0 = nodeIndex[v0];
638  if(bc[index0]) continue; // boundary condition
639  MVertex *v1 = it->first.getVertex(1 - ij);
640  int index1 = nodeIndex[v1];
641  MTriangle *tLeft = it->second[0];
642  MVertex *vLeft = tLeft->getVertex(0);
643  if(vLeft == v0 || vLeft == v1) vLeft = tLeft->getVertex(1);
644  if(vLeft == v0 || vLeft == v1) vLeft = tLeft->getVertex(2);
645  double e[3] = {v1->x() - v0->x(), v1->y() - v0->y(), v1->z() - v0->z()};
646  double ne = sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
647  double a[3] = {vLeft->x() - v0->x(), vLeft->y() - v0->y(),
648  vLeft->z() - v0->z()};
649  double na = sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
650  double thetaL =
651  acos((a[0] * e[0] + a[1] * e[1] + a[2] * e[2]) / (na * ne));
652  double thetaR = 0.;
653  if(it->second.size() == 2) {
654  MTriangle *tRight = it->second[1];
655  MVertex *vRight = tRight->getVertex(0);
656  if(vRight == v0 || vRight == v1) vRight = tRight->getVertex(1);
657  if(vRight == v0 || vRight == v1) vRight = tRight->getVertex(2);
658  double b[3] = {vRight->x() - v0->x(), vRight->y() - v0->y(),
659  vRight->z() - v0->z()};
660  double nb = sqrt(b[0] * b[0] + b[1] * b[1] + b[2] * b[2]);
661  thetaR = acos((b[0] * e[0] + b[1] * e[1] + b[2] * e[2]) / (nb * ne));
662  }
663  double c = (tan(.5 * thetaL) + tan(.5 * thetaR)) / ne;
664  lsys->addToMatrix(index0, index1, -c);
665  lsys->addToMatrix(index0, index0, c);
666  }
667  }
668  for(std::size_t i = 0; i < vs[loop].size() - 1; i++) {
669  int row = nodeIndex[vs[loop][i]];
670  lsys->addToMatrix(row, row, 1);
671  }
672 
673  lsys->zeroRightHandSide();
674  for(std::size_t i = 0; i < vs[loop].size() - 1; i++) {
675  int row = nodeIndex[vs[loop][i]];
676  lsys->addToRightHandSide(row, u[row]);
677  }
678  lsys->systemSolve();
679  for(std::size_t i = 0; i < nodes.size(); i++) lsys->getFromSolution(i, u[i]);
680 
681  lsys->zeroRightHandSide();
682  for(std::size_t i = 0; i < vs[loop].size() - 1; i++) {
683  int row = nodeIndex[vs[loop][i]];
684  lsys->addToRightHandSide(row, v[row]);
685  }
686  lsys->systemSolve();
687  for(std::size_t i = 0; i < nodes.size(); i++) lsys->getFromSolution(i, v[i]);
688 
689  delete lsys;
690 #endif
691 
692  stl_vertices_uv.resize(nodes.size());
693  stl_vertices_xyz.resize(nodes.size());
694  for(std::size_t i = 0; i < nodes.size(); i++) {
695  stl_vertices_uv[i] = SPoint2(u[i], v[i]);
696  stl_vertices_xyz[i] = nodes[i]->point();
697  }
698  stl_triangles.resize(3 * triangles.size());
699  for(std::size_t i = 0; i < triangles.size(); i++) {
700  stl_triangles[3 * i + 0] = nodeIndex[triangles[i]->getVertex(0)];
701  stl_triangles[3 * i + 1] = nodeIndex[triangles[i]->getVertex(1)];
702  stl_triangles[3 * i + 2] = nodeIndex[triangles[i]->getVertex(2)];
703  }
704 
705  return true;
706 }
707 
708 #if 0
709 static void debugTriangulation(const std::vector<MTriangle *> &triangles,
710  const std::string &label)
711 {
712  FILE *fp = Fopen("debug.pos", "w");
713  if(!fp) return;
714  fprintf(fp, "View\"%s\"{\n", label.c_str());
715  for(auto t : triangles) t->writePOS(fp, false, true, false, false, false, false);
716  fprintf(fp, "};\n");
717  fclose(fp);
718  exit(1);
719 }
720 #endif
721 
722 static int isTriangulationParametrizable(const std::vector<MTriangle *> &t,
723  int Nmax, std::ostringstream &why)
724 {
725  if(Nmax > 1 && (int)t.size() > Nmax) {
726  int np = t.size() / (Nmax - 1) + 1;
727  if(np > 1) {
728  why << "too many triangles (" << t.size() << " vs. " << Nmax << ")";
729  return np;
730  }
731  }
732 
733  std::set<MVertex *> v;
734  std::map<MEdge, int, MEdgeLessThan> e;
735  for(std::size_t i = 0; i < t.size(); ++i) {
736  for(int j = 0; j < 3; j++) {
737  v.insert(t[i]->getVertex(j));
738  auto it = e.find(t[i]->getEdge(j));
739  if(it == e.end())
740  e[t[i]->getEdge(j)] = 1;
741  else
742  it->second++;
743  }
744  }
745  std::vector<MEdge> bnd;
746  for(auto it = e.begin(); it != e.end(); ++it) {
747  if(it->second == 1) bnd.push_back(it->first);
748  }
749  if(bnd.empty()) {
750  why << "poincare characteristic 2 is not 0";
751  //debugTriangulation(t, why.str());
752  return 2;
753  }
754 
755  std::vector<std::vector<MVertex *> > vs;
756  if(!SortEdgeConsecutive(bnd, vs)) {
757  why << "boundary not manifold";
758  return 2;
759  }
760 
761  std::size_t poincare =
762  t.size() - (2 * (v.size() - 1) - bnd.size() + 2 * (vs.size() - 1));
763  if(poincare != 0) {
764  why << "poincare characteristic " << poincare << " is not 0";
765  return 2;
766  }
767 
768  std::vector<MVertex *> nodes;
769  std::vector<SPoint2> stl_nodes_uv;
770  std::vector<SPoint3> stl_nodes_xyz;
771  std::vector<int> stl_triangles;
772  computeParametrization(t, nodes, stl_nodes_uv, stl_nodes_xyz, stl_triangles);
773  for(std::size_t i = 0; i < stl_triangles.size(); i += 3) {
774  double u0 = stl_nodes_uv[stl_triangles[i + 0]].x();
775  double v0 = stl_nodes_uv[stl_triangles[i + 0]].y();
776  double u1 = stl_nodes_uv[stl_triangles[i + 1]].x();
777  double v1 = stl_nodes_uv[stl_triangles[i + 1]].y();
778  double u2 = stl_nodes_uv[stl_triangles[i + 2]].x();
779  double v2 = stl_nodes_uv[stl_triangles[i + 2]].y();
780  double det = fabs((u1 - u0) * (v2 - v0) - (v1 - v0) * (u2 - u0));
781  if(det < 1.e-8) {
782  why << "parametrized triangles are too small (" << det << ")";
783  return 2;
784  }
785  }
786  return 1;
787 }
788 
789 void makeMLinesUnique(std::vector<MLine *> &v)
790 {
791  std::sort(v.begin(), v.end(), MLinePtrLessThan());
792  v.erase(std::unique(v.begin(), v.end(), MLinePtrEqual()), v.end());
793 }
794 
795 class twoT {
796 public:
798  twoT(MTriangle *t) : t1(t), t2(nullptr) {}
800  {
801  if(t == t1) return t2;
802  if(t == t2) return t1;
803  return nullptr;
804  }
805 };
806 
807 bool
808 makePartitionSimplyConnected(std::vector<MTriangle *> &t,
809  std::vector<std::vector<MTriangle *> > &ts)
810 {
811  std::map<MEdge, twoT, MEdgeLessThan> conn;
812  for(std::size_t i = 0; i < t.size(); i++) {
813  for(int j = 0; j < 3; j++) {
814  MEdge e = t[i]->getEdge(j);
815  auto it = conn.find(e);
816  twoT twt(t[i]);
817  if(it == conn.end())
818  conn.insert(std::make_pair(e, twt));
819  else
820  it->second.t2 = t[i];
821  }
822  }
823 
824  while(!t.empty()) {
825  std::stack<MTriangle *> _s;
826  _s.push(t[0]);
827  std::set<MTriangle *, MElementPtrLessThan> touch;
828  while(!_s.empty()) {
829  MTriangle *x = _s.top();
830  touch.insert(x);
831  _s.pop();
832  for(int j = 0; j < 3; j++) {
833  MEdge e = x->getEdge(j);
834  auto it = conn.find(e);
835  if(it->second.t2) {
836  MTriangle *tt = it->second.other(x);
837  if(tt && touch.find(tt) == touch.end()) _s.push(tt);
838  }
839  }
840  }
841  std::vector<MTriangle *> _part;
842  _part.insert(_part.begin(), touch.begin(), touch.end());
843  ts.push_back(_part);
844  std::vector<MTriangle *> update;
845  for(std::size_t i = 0; i < t.size(); i++)
846  if(touch.find(t[i]) == touch.end()) update.push_back(t[i]);
847  t = update;
848  }
849  return true;
850 }
851 
852 void computeEdgeCut(GModel *gm, std::vector<MLine *> &cut,
853  int max_elems_per_cut)
854 {
855  Msg::Info("Splitting triangulations to make them parametrizable:");
856 
857  for(auto it = gm->firstFace(); it != gm->lastFace(); ++it) {
858  int part = 0;
859  if((*it)->triangles.empty()) continue;
860  std::vector<MVertex *> verts = (*it)->mesh_vertices;
861  std::map<MTriangle *, int, MElementPtrLessThan> global;
862  std::map<MEdge, int, MEdgeLessThan> cuts;
863  std::stack<std::vector<MTriangle *> > partitions;
864  std::stack<int> _levels;
865  partitions.push((*it)->triangles);
866  _levels.push(0);
867  (*it)->triangles.clear();
868 
869  while(!partitions.empty()) {
870  int level = _levels.top();
871  _levels.pop();
872  (*it)->triangles = partitions.top();
873  (*it)->mesh_vertices.clear();
874  std::set<MVertex *, MVertexPtrLessThan> vs;
875  for(std::size_t i = 0; i < (*it)->triangles.size(); ++i) {
876  for(std::size_t j = 0; j < 3; ++j)
877  vs.insert((*it)->triangles[i]->getVertex(j));
878  }
879  (*it)->mesh_vertices.insert((*it)->mesh_vertices.begin(), vs.begin(),
880  vs.end());
881  partitions.pop();
882  std::ostringstream why;
883  int np =
884  isTriangulationParametrizable((*it)->triangles, max_elems_per_cut, why);
885  if(np > 1) {
886  Msg::Info(" - Level %d partition with %d triangles split in %d "
887  "parts because %s",
888  level, (*it)->triangles.size(), np, why.str().c_str());
889  }
890  else if(np < 0) {
891  Msg::Error("Could not create parametrization (check orientation of "
892  "input triangulations)");
893  break;
894  }
895  if(np == 1) {
896  for(std::size_t i = 0; i < (*it)->triangles.size(); i++)
897  global[(*it)->triangles[i]] = part;
898  part++;
899  }
900  else {
901 #if defined(HAVE_MESH)
902  if(!PartitionFaceMinEdgeLength(*it, np)) {
903  std::vector<std::vector<MTriangle *> > t(np);
904  for(std::size_t i = 0; i < (*it)->triangles.size(); i++) {
905  int p = (*it)->triangles[i]->getPartition();
906  if(p >= 0 && p < np)
907  t[p].push_back((*it)->triangles[i]);
908  else
909  Msg::Error("Invalid partition index");
910  }
911  for(std::size_t i = 0; i < t.size(); i++) {
912  std::vector<std::vector<MTriangle *> > ts;
913  if(!makePartitionSimplyConnected(t[i], ts)) {
914  Msg::Warning("Could not make partition simply connected");
915  break;
916  }
917  for(std::size_t j = 0; j < ts.size(); j++) {
918  _levels.push(level + 1);
919  partitions.push(ts[j]);
920  }
921  }
922  }
923 #else
924  Msg::Error("Partitioning surface requires Mesh module");
925 #endif
926  }
927  }
928  (*it)->triangles.clear();
929  for(auto it2 = global.begin(); it2 != global.end(); ++it2) {
930  MTriangle *t = it2->first;
931  (*it)->triangles.push_back(t);
932  for(int i = 0; i < 3; i++) {
933  MEdge ed = t->getEdge(i);
934  auto it3 = cuts.find(ed);
935  if(it3 == cuts.end())
936  cuts[ed] = it2->second;
937  else {
938  if(it3->second != it2->second)
939  cut.push_back(new MLine(ed.getVertex(0), ed.getVertex(1)));
940  }
941  }
942  }
943  (*it)->mesh_vertices = verts;
944  }
945  makeMLinesUnique(cut);
946 }
947 
948 void computeNonManifoldEdges(GModel *gm, std::vector<MLine *> &cut,
949  bool addBoundary)
950 {
951  std::map<MEdge, int, MEdgeLessThan> m;
952  for(auto it = gm->firstFace(); it != gm->lastFace(); ++it) {
953  for(std::size_t i = 0; i < (*it)->triangles.size(); i++) {
954  for(int j = 0; j < 3; j++) {
955  auto it2 = m.find((*it)->triangles[i]->getEdge(j));
956  if(it2 == m.end())
957  m[(*it)->triangles[i]->getEdge(j)] = 1;
958  else
959  it2->second++;
960  }
961  }
962  }
963  {
964  int countNM = 0, countBND = 0;
965  auto it = m.begin();
966  for(; it != m.end(); ++it) {
967  if(it->second > 2) {
968  cut.push_back(
969  new MLine(it->first.getVertex(0), it->first.getVertex(1)));
970  countNM++;
971  }
972  if(addBoundary && it->second == 1) {
973  cut.push_back(
974  new MLine(it->first.getVertex(0), it->first.getVertex(1)));
975  countBND++;
976  }
977  }
978  if(countNM + countBND > 0)
979  Msg::Info(
980  "Model has %d non manifold mesh edges and %d boundary mesh edges",
981  countNM, countBND);
982  }
983  makeMLinesUnique(cut);
984 }
makePartitionSimplyConnected
bool makePartitionSimplyConnected(std::vector< MTriangle * > &t, std::vector< std::vector< MTriangle * > > &ts)
Definition: GModelParametrize.cpp:808
GModel::setOrderN
int setOrderN(int order, int linear, int incomplete, int onlyVisible)
Definition: GModel.cpp:1454
linearSystemFull::zeroRightHandSide
virtual void zeroRightHandSide()
Definition: linearSystemFull.h:64
MTriangle.h
TimeOfDay
double TimeOfDay()
Definition: OS.cpp:399
computeParametrization
bool computeParametrization(const std::vector< MTriangle * > &triangles, std::vector< MVertex * > &nodes, std::vector< SPoint2 > &stl_vertices_uv, std::vector< SPoint3 > &stl_vertices_xyz, std::vector< int > &stl_triangles)
Definition: GModelParametrize.cpp:495
linearSystemFull
Definition: linearSystemFull.h:17
twoT::twoT
twoT(MTriangle *t)
Definition: GModelParametrize.cpp:798
GModel::firstEdge
eiter firstEdge()
Definition: GModel.h:356
buildListOfEdgeAngle
void buildListOfEdgeAngle(e2t_cont adj, std::vector< edge_angle > &edges_detected, std::vector< edge_angle > &edges_lonly)
Definition: meshGFaceOptimize.cpp:455
twoT
Definition: GModelParametrize.cpp:795
MEdge
Definition: MEdge.h:14
global
Definition: OctreeInternals.h:40
GFace::setBoundEdges
void setBoundEdges(const std::vector< int > &tagEdges)
Definition: GFace.cpp:113
GModel::getCurvatures
std::map< MVertex *, std::pair< SVector3, SVector3 > > & getCurvatures()
Definition: GModel.h:719
GFace.h
GModel::getMaxElementaryNumber
int getMaxElementaryNumber(int dim)
Definition: GModel.cpp:817
linearSystemPETSc
Definition: linearSystemPETSc.h:150
computeDiscreteCurvatures
int computeDiscreteCurvatures(GModel *gm)
Definition: GModelParametrize.cpp:462
MLine::getVertex
virtual MVertex * getVertex(int num)
Definition: MLine.h:45
GModel::getFaces
std::set< GFace *, GEntityPtrLessThan > getFaces() const
Definition: GModel.h:376
curvature.h
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
GEdge::lines
std::vector< MLine * > lines
Definition: GEdge.h:42
angle
double angle(const SVector3 &a, const SVector3 &b)
Definition: SVector3.h:157
SPoint2
Definition: SPoint2.h:12
OS.h
Msg::Debug
static void Debug(const char *fmt,...)
Definition: GmshMessage.cpp:752
c
static double c(int i, int j, fullMatrix< double > &CA, const std::vector< SPoint3 > &P, const std::vector< SPoint3 > &Q)
Definition: discreteFrechetDistance.cpp:15
discreteEdge
Definition: discreteEdge.h:12
MVertex
Definition: MVertex.h:24
computeEdgeCut
void computeEdgeCut(GModel *gm, std::vector< MLine * > &cut, int max_elems_per_cut)
Definition: GModelParametrize.cpp:852
GModel::remove
bool remove(GRegion *r)
Definition: GModel.cpp:435
Msg::Warning
static void Warning(const char *fmt,...)
Definition: GmshMessage.cpp:543
Msg::Error
static void Error(const char *fmt,...)
Definition: GmshMessage.cpp:482
Msg::StatusBar
static void StatusBar(bool log, const char *fmt,...)
Definition: GmshMessage.cpp:686
MVertex::z
double z() const
Definition: MVertex.h:62
edge_angle::angle
double angle
Definition: meshGFaceOptimize.h:24
linearSystemPETSc.h
GModelParametrize.h
SVector3
Definition: SVector3.h:16
meshGFaceOptimize.h
twoT::t1
MTriangle * t1
Definition: GModelParametrize.cpp:797
MTriangle::getVertex
virtual MVertex * getVertex(int num)
Definition: MTriangle.h:62
linearSystemCSR.h
MVertex::onWhat
GEntity * onWhat() const
Definition: MVertex.h:82
MPoint.h
GModel::firstVertex
viter firstVertex()
Definition: GModel.h:357
edge_angle::v2
MVertex * v2
Definition: meshGFaceOptimize.h:23
MVertex::point
SPoint3 point() const
Definition: MVertex.h:67
GModel::destroyMeshCaches
void destroyMeshCaches()
Definition: GModel.cpp:214
GmshMessage.h
MLine.h
edges
static int edges[6][2]
Definition: meshGRegionLocalMeshMod.cpp:23
w1
const double w1
Definition: GaussQuadratureHex.cpp:18
computeNonManifoldEdges
void computeNonManifoldEdges(GModel *gm, std::vector< MLine * > &cut, bool addBoundary)
Definition: GModelParametrize.cpp:948
MEdgeLessThan
Definition: MEdge.h:122
GEntityPtrLessThan
Definition: GEntity.h:419
Fopen
FILE * Fopen(const char *f, const char *mode)
Definition: OS.cpp:273
discreteVertex.h
MLine
Definition: MLine.h:21
GVertex::points
std::vector< MPoint * > points
Definition: GVertex.h:120
PartitionFaceMinEdgeLength
int PartitionFaceMinEdgeLength(GFace *gf, int np, double tol)
Definition: meshPartition.cpp:2663
CTX::instance
static CTX * instance()
Definition: Context.cpp:122
linearSystemFull::addToRightHandSide
virtual void addToRightHandSide(int row, const scalar &val, int ith=0)
Definition: linearSystemFull.h:50
GModel::lastFace
fiter lastFace()
Definition: GModel.h:359
linearSystemFull::allocate
virtual void allocate(int nbRows)
Definition: linearSystemFull.h:25
conn
Definition: delaunay3d.cpp:257
SortEdgeConsecutive
bool SortEdgeConsecutive(const std::vector< MEdge > &e, std::vector< std::vector< MVertex * > > &vs)
Definition: MEdge.cpp:68
GEdge.h
discreteVertex
Definition: discreteVertex.h:15
MEdge::getVertex
MVertex * getVertex(std::size_t i) const
Definition: MEdge.h:39
GEntity::mesh_vertices
std::vector< MVertex * > mesh_vertices
Definition: GEntity.h:56
GModel::lastVertex
viter lastVertex()
Definition: GModel.h:361
GVertex
Definition: GVertex.h:23
linearSystemFull::systemSolve
virtual int systemSolve()
Definition: linearSystemFull.h:77
linearSystemBase::setParameter
void setParameter(const std::string &key, std::string value)
Definition: linearSystem.cpp:10
meshPartition.h
edge_angle::v1
MVertex * v1
Definition: meshGFaceOptimize.h:23
linearSystemFull::addToMatrix
virtual void addToMatrix(int row, int col, const scalar &val)
Definition: linearSystemFull.h:42
MLinePtrEqual
Definition: MLine.h:297
GModel
Definition: GModel.h:44
Cpu
double Cpu()
Definition: OS.cpp:366
twoT::other
MTriangle * other(MTriangle *t) const
Definition: GModelParametrize.cpp:799
GModel::firstFace
fiter firstFace()
Definition: GModel.h:355
e2t_cont
std::map< MEdge, std::pair< MElement *, MElement * >, MEdgeLessThan > e2t_cont
Definition: meshGFaceOptimize.h:35
linearSystemBase::insertInSparsityPattern
virtual void insertInSparsityPattern(int _row, int _col)
Definition: linearSystem.h:33
addTriangle
static void addTriangle(MVertex *v1, MVertex *v2, MVertex *v3, GFace *to)
Definition: meshGFaceExtruded.cpp:21
GModel::add
bool add(GRegion *r)
Definition: GModel.h:394
CTX::mesh
contextMeshOptions mesh
Definition: Context.h:313
GEntity::tag
int tag() const
Definition: GEntity.h:280
discreteFace.h
linearSystemFull.h
MLinePtrLessThan
Definition: MLine.h:289
GModel::exportDiscreteGEOInternals
int exportDiscreteGEOInternals()
Definition: GModelIO_GEO.cpp:1910
linearSystemCSRGmm
Definition: linearSystemCSR.h:176
buildEdgeToElements
void buildEdgeToElements(std::vector< MElement * > &tris, e2t_cont &adj)
Definition: meshGFaceOptimize.cpp:449
MTriangle
Definition: MTriangle.h:26
contextMeshOptions::reparamMaxTriangles
int reparamMaxTriangles
Definition: Context.h:48
GModel::firstRegion
riter firstRegion()
Definition: GModel.h:354
GModel::lastRegion
riter lastRegion()
Definition: GModel.h:358
makeMLinesUnique
void makeMLinesUnique(std::vector< MLine * > &v)
Definition: GModelParametrize.cpp:789
MEdge.h
discreteEdge.h
Context.h
MTriangle::getEdge
virtual MEdge getEdge(int num) const
Definition: MTriangle.h:74
MPoint
Definition: MPoint.h:16
GEdge
Definition: GEdge.h:26
GModel::pruneMeshVertexAssociations
void pruneMeshVertexAssociations()
Definition: GModel.cpp:2527
linearSystemFull::getFromSolution
virtual void getFromSolution(int row, scalar &val) const
Definition: linearSystemFull.h:62
GModel::lastEdge
eiter lastEdge()
Definition: GModel.h:360
GModel.h
MVertex::y
double y() const
Definition: MVertex.h:61
classifyFaces
void classifyFaces(GModel *gm, double curveAngleThreshold)
Definition: GModelParametrize.cpp:103
twoT::t2
MTriangle * t2
Definition: GModelParametrize.cpp:797
MVertex::setEntity
void setEntity(GEntity *ge)
Definition: MVertex.h:83
GModel::deleteVertexArrays
void deleteVertexArrays()
Definition: GModel.cpp:299
MElement::writePOS
virtual void writePOS(FILE *fp, bool printElementary, bool printElementNumber, bool printSICN, bool printSIGE, bool printGamma, bool printDisto, double scalingFactor=1.0, int elementary=1)
Definition: MElement.cpp:1515
CurvatureRusinkiewicz
bool CurvatureRusinkiewicz(const std::vector< int > &triangles, const std::vector< SPoint3 > &nodes, std::vector< std::pair< SVector3, SVector3 > > &nodalCurvatures)
Definition: curvature.cpp:172
edge_angle
Definition: meshGFaceOptimize.h:22
isTriangulationParametrizable
static int isTriangulationParametrizable(const std::vector< MTriangle * > &t, int Nmax, std::ostringstream &why)
Definition: GModelParametrize.cpp:722
MVertex::x
double x() const
Definition: MVertex.h:60
discreteFace
Definition: discreteFace.h:18
faces
static int faces[4][3]
Definition: meshGRegionDelaunayInsertion.cpp:165