1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 
6 #include <sstream>
7 #include "GmshConfig.h"
8 #include "GmshMessage.h"
9 #include "GModel.h"
10 #include "GFace.h"
11 #include "GEdge.h"
12 #include "MTriangle.h"
13 #include "MQuadrangle.h"
14 #include "MElementCut.h"
15 #include "VertexArray.h"
16 #include "fullMatrix.h"
17 #include "Numeric.h"
18 #include "GaussLegendre1D.h"
19 #include "Context.h"
20 #include "OS.h"
21 #include "discreteEdge.h"
22 #include "discreteFace.h"
23 #include "ExtrudeParams.h"
24 #include "Field.h"
25 
26 #if defined(HAVE_MESH)
27 #include "meshGFace.h"
28 #include "meshGFaceOptimize.h"
29 #include "BackgroundMeshTools.h"
30 #include "meshGFaceBipartiteLabelling.h"
31 #endif
32 
33 #if defined(HAVE_ALGLIB)
34 #include <stdafx.h>
35 #include <optimization.h>
36 #endif
37 
38 #if defined(HAVE_QUADMESHINGTOOLS)
39 #include "cppUtils.h"
40 #include "qmtMeshUtils.h"
41 #include "qmtCrossField.h"
42 #endif
43 
GFace(GModel * model,int tag)44 GFace::GFace(GModel *model, int tag)
45   : GEntity(model, tag), r1(nullptr), r2(nullptr), va_geom_triangles(nullptr),
46     compoundSurface(nullptr)
47 {
48   meshStatistics.status = GFace::PENDING;
49   meshStatistics.refineAllEdges = false;
50   GFace::resetMeshAttributes();
51 }
52 
~GFace()53 GFace::~GFace()
54 {
55   for(auto ge : l_edges) ge->delFace(this);
56 
57   if(va_geom_triangles) delete va_geom_triangles;
58 
59   GFace::deleteMesh();
60 }
61 
getMeshingAlgo() const62 int GFace::getMeshingAlgo() const
63 {
64   if(meshAttributes.algorithm)
65     return meshAttributes.algorithm;
66   else
67     return CTX::instance()->mesh.algo2d;
68 }
69 
getMeshSizeFromBoundary() const70 int GFace::getMeshSizeFromBoundary() const
71 {
72   if(meshAttributes.meshSizeFromBoundary >= 0)
73     return meshAttributes.meshSizeFromBoundary;
74   else
75     return CTX::instance()->mesh.lcExtendFromBoundary;
76 }
77 
delEdge(GEdge * edge)78 int GFace::delEdge(GEdge *edge)
79 {
80   const auto found = std::find(l_edges.begin(), l_edges.end(), edge);
81 
82   if(found != l_edges.end()) { l_edges.erase(found); }
83 
84   const auto pos = std::distance(l_edges.begin(), found);
85 
86   if(l_dirs.empty()) { return 0; }
87 
88   if(l_dirs.size() < static_cast<std::size_t>(pos)) {
89     l_dirs.erase(std::prev(l_dirs.end()));
90     return 0;
91   }
92 
93   const auto orientation = l_dirs.at(pos);
94   l_dirs.erase(std::next(begin(l_dirs), pos));
95   return orientation;
96 }
97 
setBoundEdges(const std::vector<int> & tagEdges)98 void GFace::setBoundEdges(const std::vector<int> &tagEdges)
99 {
100   std::vector<GEdge *> e;
101   for(std::size_t i = 0; i < tagEdges.size(); i++) {
102     GEdge *ge = model()->getEdgeByTag(tagEdges[i]);
103     if(ge) {
104       e.push_back(ge);
105       ge->addFace(this);
106     }
107     else {
108       Msg::Error("Unknown curve %d in surface %d", tagEdges[i], tag());
109     }
110   }
111   GEdgeLoop el(e);
112   el.getEdges(l_edges);
113   el.getSigns(l_dirs);
114 }
115 
setBoundEdges(const std::vector<int> & tagEdges,const std::vector<int> & signEdges)116 void GFace::setBoundEdges(const std::vector<int> &tagEdges,
117                           const std::vector<int> &signEdges)
118 {
119   if(signEdges.size() != tagEdges.size()) {
120     Msg::Error("Wrong number of curve signs in surface %d", tag());
121     setBoundEdges(tagEdges);
122   }
123   for(std::vector<int>::size_type i = 0; i < tagEdges.size(); i++) {
124     GEdge *ge = model()->getEdgeByTag(tagEdges[i]);
125     if(ge) {
126       if(std::find(l_edges.begin(), l_edges.end(), ge) == l_edges.end()) {
127         l_edges.push_back(ge);
128         l_dirs.push_back(signEdges[i]);
129         ge->addFace(this);
130       }
131     }
132     else {
133       Msg::Error("Unknown curve %d in surface %d", tagEdges[i], tag());
134     }
135   }
136 }
137 
deleteMesh()138 void GFace::deleteMesh()
139 {
140   for(std::size_t i = 0; i < mesh_vertices.size(); i++) delete mesh_vertices[i];
141   mesh_vertices.clear();
142   transfinite_vertices.clear();
143   for(std::size_t i = 0; i < triangles.size(); i++) delete triangles[i];
144   triangles.clear();
145   for(std::size_t i = 0; i < quadrangles.size(); i++) delete quadrangles[i];
146   quadrangles.clear();
147   for(std::size_t i = 0; i < polygons.size(); i++) delete polygons[i];
148   polygons.clear();
149   correspondingVertices.clear();
150   correspondingHighOrderVertices.clear();
151   deleteVertexArrays();
152   model()->destroyMeshCaches();
153 }
154 
getNumMeshElements() const155 std::size_t GFace::getNumMeshElements() const
156 {
157   return triangles.size() + quadrangles.size() + polygons.size();
158 }
159 
getNumMeshElementsByType(const int familyType) const160 std::size_t GFace::getNumMeshElementsByType(const int familyType) const
161 {
162   if(familyType == TYPE_TRI)
163     return triangles.size();
164   else if(familyType == TYPE_QUA)
165     return quadrangles.size();
166   else if(familyType == TYPE_POLYG)
167     return polygons.size();
168 
169   return 0;
170 }
171 
getNumMeshParentElements()172 std::size_t GFace::getNumMeshParentElements()
173 {
174   std::size_t n = 0;
175   for(std::size_t i = 0; i < polygons.size(); i++)
176     if(polygons[i]->ownsParent()) n++;
177   return n;
178 }
179 
getNumMeshElements(unsigned * const c) const180 void GFace::getNumMeshElements(unsigned *const c) const
181 {
182   c[0] += triangles.size();
183   c[1] += quadrangles.size();
184   c[2] += polygons.size();
185 }
186 
getStartElementType(int type) const187 MElement *const *GFace::getStartElementType(int type) const
188 {
189   switch(type) {
190   case 0:
191     if(triangles.empty()) return nullptr; // msvc would throw an exception
192     return reinterpret_cast<MElement *const *>(&triangles[0]);
193   case 1:
194     if(quadrangles.empty()) return nullptr; // msvc would throw an exception
195     return reinterpret_cast<MElement *const *>(&quadrangles[0]);
196   case 2:
197     if(polygons.empty()) return nullptr;
198     return reinterpret_cast<MElement *const *>(&polygons[0]);
199   }
200   return nullptr;
201 }
202 
getMeshElement(std::size_t index) const203 MElement *GFace::getMeshElement(std::size_t index) const
204 {
205   if(index < triangles.size())
206     return triangles[index];
207   else if(index < triangles.size() + quadrangles.size())
208     return quadrangles[index - triangles.size()];
209   else if(index < triangles.size() + quadrangles.size() + polygons.size())
210     return polygons[index - triangles.size() - quadrangles.size()];
211   return nullptr;
212 }
213 
getMeshElementByType(const int familyType,const std::size_t index) const214 MElement *GFace::getMeshElementByType(const int familyType,
215                                       const std::size_t index) const
216 {
217   if(familyType == TYPE_TRI)
218     return triangles[index];
219   else if(familyType == TYPE_QUA)
220     return quadrangles[index];
221   else if(familyType == TYPE_POLYG)
222     return polygons[index];
223 
224   return nullptr;
225 }
226 
resetMeshAttributes()227 void GFace::resetMeshAttributes()
228 {
229   meshAttributes.recombine = 0;
230   meshAttributes.recombineAngle = 45.;
231   meshAttributes.method = MESH_UNSTRUCTURED;
232   meshAttributes.transfiniteArrangement = 0;
233   meshAttributes.transfiniteSmoothing = -1;
234   meshAttributes.extrude = nullptr;
235   meshAttributes.reverseMesh = false;
236   meshAttributes.meshSize = MAX_LC;
237   meshAttributes.meshSizeFactor = 1.;
238   meshAttributes.algorithm = 0;
239   meshAttributes.meshSizeFromBoundary = -1;
240 }
241 
bounds(bool fast)242 SBoundingBox3d GFace::bounds(bool fast)
243 {
244   SBoundingBox3d res;
245   if(geomType() != DiscreteSurface && geomType() != BoundaryLayerSurface &&
246      geomType() != PartitionSurface) {
247     for(auto ge : l_edges) {
248       res += ge->bounds(fast);
249     }
250   }
251   else {
252     for(std::size_t i = 0; i < getNumMeshElements(); i++)
253       for(std::size_t j = 0; j < getMeshElement(i)->getNumVertices(); j++)
254         res += getMeshElement(i)->getVertex(j)->point();
255   }
256   return res;
257 }
258 
getOBB()259 SOrientedBoundingBox GFace::getOBB()
260 {
261   if(!_obb) {
262     std::vector<SPoint3> vertices;
263     if(getNumMeshVertices() > 0) {
264       int N = getNumMeshVertices();
265       for(int i = 0; i < N; i++) {
266         MVertex *mv = getMeshVertex(i);
267         vertices.push_back(mv->point());
268       }
269       std::vector<GEdge *> const &eds = edges();
270       for(auto ed = eds.begin(); ed != eds.end(); ed++) {
271         int N2 = (*ed)->getNumMeshVertices();
272         for(int i = 0; i < N2; i++) {
273           MVertex *mv = (*ed)->getMeshVertex(i);
274           vertices.push_back(mv->point());
275         }
276         // Don't forget to add the first and last vertices...
277         if((*ed)->getBeginVertex()) {
278           SPoint3 pt1((*ed)->getBeginVertex()->x(),
279                       (*ed)->getBeginVertex()->y(),
280                       (*ed)->getBeginVertex()->z());
281           vertices.push_back(pt1);
282         }
283         if((*ed)->getEndVertex()) {
284           SPoint3 pt2((*ed)->getEndVertex()->x(), (*ed)->getEndVertex()->y(),
285                       (*ed)->getEndVertex()->z());
286           vertices.push_back(pt2);
287         }
288       }
289     }
290     else if(buildSTLTriangulation()) {
291       vertices = stl_vertices_xyz;
292     }
293     else {
294       // Fallback, if we can't make a STL triangulation of the surface, use its
295       // edges..
296       std::vector<GEdge *> const &b_edges = edges();
297       int N = 10;
298       for(auto b_edge = b_edges.begin(); b_edge != b_edges.end(); b_edge++) {
299         Range<double> tr = (*b_edge)->parBounds(0);
300         for(int j = 0; j < N; j++) {
301           double t =
302             tr.low() + (double)j / (double)(N - 1) * (tr.high() - tr.low());
303           GPoint p = (*b_edge)->point(t);
304           SPoint3 pt(p.x(), p.y(), p.z());
305           vertices.push_back(pt);
306         }
307       }
308     }
309     _obb = SOrientedBoundingBox::buildOBB(vertices);
310   }
311   return SOrientedBoundingBox(_obb);
312 }
313 
getEmbeddedVertices(bool force) const314 std::vector<GVertex *> GFace::getEmbeddedVertices(bool force) const
315 {
316   if(!force && compound.size()) return std::vector<GVertex *>();
317   return std::vector<GVertex *>(embedded_vertices.begin(),
318                                 embedded_vertices.end());
319 }
320 
getEmbeddedEdges(bool force) const321 std::vector<GEdge *> GFace::getEmbeddedEdges(bool force) const
322 {
323   if(!force && compound.size()) return std::vector<GEdge *>();
324   return embedded_edges;
325 }
326 
getEmbeddedMeshVertices(bool force) const327 std::vector<MVertex *> GFace::getEmbeddedMeshVertices(bool force) const
328 {
329   if(!force && compound.size()) return std::vector<MVertex *>();
330 
331   std::set<MVertex *> tmp;
332   for(auto it = embedded_edges.begin(); it != embedded_edges.end(); it++) {
333     tmp.insert((*it)->mesh_vertices.begin(), (*it)->mesh_vertices.end());
334     if((*it)->getBeginVertex())
335       tmp.insert((*it)->getBeginVertex()->mesh_vertices.begin(),
336                  (*it)->getBeginVertex()->mesh_vertices.end());
337     if((*it)->getEndVertex())
338       tmp.insert((*it)->getEndVertex()->mesh_vertices.begin(),
339                  (*it)->getEndVertex()->mesh_vertices.end());
340   }
341   for(auto it = embedded_vertices.begin(); it != embedded_vertices.end();
342       it++) {
343     tmp.insert((*it)->mesh_vertices.begin(), (*it)->mesh_vertices.end());
344   }
345   return std::vector<MVertex *>(tmp.begin(), tmp.end());
346 }
347 
vertices() const348 std::vector<GVertex *> GFace::vertices() const
349 {
350   std::set<GVertex *> v;
351   for(auto ge : l_edges) {
352     GVertex *const v1 = ge->getBeginVertex();
353     if(v1) v.insert(v1);
354 
355     GVertex *const v2 = ge->getEndVertex();
356     if(v2) v.insert(v2);
357   }
358   return std::vector<GVertex *>(v.begin(), v.end());
359 }
360 
setVisibility(char val,bool recursive)361 void GFace::setVisibility(char val, bool recursive)
362 {
363   GEntity::setVisibility(val);
364   if(recursive) {
365     for(auto e : l_edges) e->setVisibility(val, recursive);
366     for(auto e : embedded_edges) e->setVisibility(val, recursive);
367     for(auto v : embedded_vertices) v->setVisibility(val);
368   }
369 }
370 
setColor(unsigned int val,bool recursive)371 void GFace::setColor(unsigned int val, bool recursive)
372 {
373   GEntity::setColor(val);
374   if(recursive) {
375     for(auto e : l_edges) e->setColor(val, recursive);
376     for(auto e : embedded_edges) e->setColor(val, recursive);
377     for(auto v : embedded_vertices) v->setColor(val);
378   }
379 }
380 
getAdditionalInfoString(bool multline)381 std::string GFace::getAdditionalInfoString(bool multline)
382 {
383   std::ostringstream sstream;
384   if(l_edges.size() > 20) {
385     sstream << "Boundary curves: " << l_edges.front()->tag() << ", ...,"
386             << l_edges.back()->tag();
387     if(multline)
388       sstream << "\n";
389     else
390       sstream << " ";
391   }
392   else if(l_edges.size()) {
393     sstream << "Boundary curves: ";
394     for(auto it = l_edges.begin(); it != l_edges.end(); ++it) {
395       if(it != l_edges.begin()) sstream << ", ";
396       sstream << (*it)->tag();
397     }
398     if(multline)
399       sstream << "\n";
400     else
401       sstream << " ";
402   }
403 
404   if(r1 || r2) {
405     sstream << "On boundary of volumes: ";
406     if(r1) {
407       sstream << r1->tag();
408       if(r2) sstream << ", ";
409     }
410     if(r2) sstream << r2->tag();
411     if(multline)
412       sstream << "\n";
413     else
414       sstream << " ";
415   }
416 
417   if(embedded_edges.size()) {
418     sstream << "Embedded curves: ";
419     for(auto it = embedded_edges.begin(); it != embedded_edges.end(); ++it) {
420       if(it != embedded_edges.begin()) sstream << ", ";
421       sstream << (*it)->tag();
422     }
423     if(multline)
424       sstream << "\n";
425     else
426       sstream << " ";
427   }
428 
429   if(embedded_vertices.size()) {
430     sstream << "Embedded points: ";
431     for(auto it = embedded_vertices.begin(); it != embedded_vertices.end();
432         ++it) {
433       if(it != embedded_vertices.begin()) sstream << ", ";
434       sstream << (*it)->tag();
435     }
436     if(multline)
437       sstream << "\n";
438     else
439       sstream << " ";
440   }
441 
442   if(meshAttributes.recombine || meshAttributes.method == MESH_TRANSFINITE ||
443      (meshAttributes.extrude && meshAttributes.extrude->mesh.ExtrudeMesh) ||
444      meshAttributes.reverseMesh ||
445      (getMeshMaster() && getMeshMaster() != this)) {
446     sstream << "Mesh attributes:";
447     if(meshAttributes.recombine) sstream << " recombined";
448     if(meshAttributes.method == MESH_TRANSFINITE) sstream << " transfinite";
449     if(meshAttributes.extrude && meshAttributes.extrude->mesh.ExtrudeMesh)
450       sstream << " extruded";
451     if(meshAttributes.reverseMesh) sstream << " reverse";
452     if(getMeshMaster() && getMeshMaster() != this)
453       sstream << " periodic copy of surface " << getMeshMaster()->tag();
454   }
455 
456   std::string str = sstream.str();
457   if(str.size() && (str[str.size() - 1] == '\n' || str[str.size() - 1] == ' '))
458     str.resize(str.size() - 1);
459   return str;
460 }
461 
writeGEO(FILE * fp)462 void GFace::writeGEO(FILE *fp)
463 {
464   if(geomType() == DiscreteSurface || geomType() == BoundaryLayerSurface) return;
465 
466   std::vector<GEdge *> const &edg = edges();
467   std::vector<int> const &dir = orientations();
468   if(edg.size() && dir.size() == edg.size()) {
469     std::vector<int> num, ori;
470     for(auto it = edg.begin(); it != edg.end(); it++)
471       num.push_back((*it)->tag());
472     for(auto it = dir.begin(); it != dir.end(); it++)
473       ori.push_back((*it) > 0 ? 1 : -1);
474     fprintf(fp, "Curve Loop(%d) = ", tag());
475     for(std::size_t i = 0; i < num.size(); i++) {
476       if(i)
477         fprintf(fp, ", %d", num[i] * ori[i]);
478       else
479         fprintf(fp, "{%d", num[i] * ori[i]);
480     }
481     fprintf(fp, "};\n");
482     if(geomType() == GEntity::Plane) {
483       fprintf(fp, "Plane Surface(%d) = {%d};\n", tag(), tag());
484     }
485     else if(edg.size() == 3 || edg.size() == 4) {
486       fprintf(fp, "Surface(%d) = {%d};\n", tag(), tag());
487     }
488     else {
489       Msg::Error("Skipping surface %d in export", tag());
490     }
491   }
492 
493   for(auto it = embedded_edges.begin(); it != embedded_edges.end(); it++)
494     fprintf(fp, "Line {%d} In Surface {%d};\n", (*it)->tag(), tag());
495 
496   for(auto it = embedded_vertices.begin(); it != embedded_vertices.end(); it++)
497     fprintf(fp, "Point {%d} In Surface {%d};\n", (*it)->tag(), tag());
498 
499   if(meshAttributes.method == MESH_TRANSFINITE) {
500     fprintf(fp, "Transfinite Surface {%d}", tag());
501     if(meshAttributes.corners.size()) {
502       fprintf(fp, " = {");
503       for(std::size_t i = 0; i < meshAttributes.corners.size(); i++) {
504         if(i) fprintf(fp, ",");
505         fprintf(fp, "%d", meshAttributes.corners[i]->tag());
506       }
507       fprintf(fp, "}");
508     }
509     fprintf(fp, ";\n");
510   }
511 
512   if(meshAttributes.recombine) fprintf(fp, "Recombine Surface {%d};\n", tag());
513 
514   if(meshAttributes.reverseMesh) fprintf(fp, "Reverse Surface {%d};\n", tag());
515 }
516 
computeMeanPlane()517 void GFace::computeMeanPlane()
518 {
519   std::vector<SPoint3> pts;
520 
521   if(geomType() == Plane) {
522     // Special case for planar CAD surfaces: we first try to compute the
523     // parametrization in a way that is more robust with respect to
524     // perturbations of the boundary. This is crucial for sensitivity analyses
525     // and GFace::relocateMeshVertices(): after perturbation of the boundary, we
526     // want a parametrization of the surface that is "close" to the original
527     // one. If this fails, we fallback to the classical (SVD-based) algorithm.
528     std::vector<GEdge *> const &edg = edges();
529     for(auto e : edg) {
530       if(e->geomType() == GEntity::DiscreteCurve ||
531          e->geomType() == GEntity::BoundaryLayerCurve) {
532         pts.clear();
533         break;
534       }
535       Range<double> b = e->parBounds(0);
536       GPoint p1 = e->point(b.low() + 0.333 * (b.high() - b.low()));
537       pts.push_back(SPoint3(p1.x(), p1.y(), p1.z()));
538       GPoint p2 = e->point(b.low() + 0.666 * (b.high() - b.low()));
539       pts.push_back(SPoint3(p2.x(), p2.y(), p2.z()));
540     }
541     bool ok = false;
542     double res[4] = {0., 0., 0., 0.}, xm = 0., ym = 0., zm = 0.;
543     if(pts.size() >= 3) {
544       SVector3 d01(pts[0], pts[1]);
545       for(std::size_t i = 2; i < pts.size(); i++) {
546         SVector3 d0i(pts[0], pts[i]);
547         SVector3 n = crossprod(d01, d0i);
548         // if too small, the points are almost colinear; tolerance is relatively
549         // high so that we don't accept points on plane surfaces defined by
550         // lines that are not exactly co-planar
551         if(norm(n) >
552            sqrt(CTX::instance()->geom.tolerance) * CTX::instance()->lc) {
553           res[0] = n.x();
554           res[1] = n.y();
555           res[2] = n.z();
556           xm = pts[0].x();
557           ym = pts[0].y();
558           zm = pts[0].z();
559           ok = true;
560           break;
561         }
562       }
563     }
564     if(ok) {
565       double ex[3], t1[3], t2[3];
566       ex[0] = ex[1] = ex[2] = 0.0;
567       if(res[0] == 0.)
568         ex[0] = 1.0;
569       else if(res[1] == 0.)
570         ex[1] = 1.0;
571       else
572         ex[2] = 1.0;
573       prodve(res, ex, t1);
574       norme(t1);
575       prodve(t1, res, t2);
576       norme(t2);
577       res[3] = (xm * res[0] + ym * res[1] + zm * res[2]);
578       fillMeanPlane(res, t1, t2, meanPlane);
579       return;
580     }
581   }
582 
583   std::vector<GVertex *> const &verts = vertices();
584   for(auto itv = verts.begin(); itv != verts.end(); itv++) {
585     const GVertex *v = *itv;
586     pts.push_back(SPoint3(v->x(), v->y(), v->z()));
587   }
588 
589   bool colinear = (pts.size() < 3);
590   if(pts.size() > 2) {
591     SVector3 d01(pts[0], pts[1]), d02(pts[0], pts[2]);
592     if(norm(crossprod(d01, d02)) < 1e-12) colinear = true;
593   }
594 
595   if(colinear) {
596     Msg::Debug("Adding curve points (%d) to compute mean plane of surface %d",
597                pts.size(), tag());
598     std::vector<GEdge *> const &edg = edges();
599     for(auto e : edg) {
600       if(e->mesh_vertices.size() > 1) {
601         for(std::size_t i = 0; i < e->mesh_vertices.size(); i++)
602           pts.push_back(e->mesh_vertices[i]->point());
603       }
604       else {
605         Range<double> b = e->parBounds(0);
606         GPoint p1 = e->point(b.low() + 0.333 * (b.high() - b.low()));
607         pts.push_back(SPoint3(p1.x(), p1.y(), p1.z()));
608         GPoint p2 = e->point(b.low() + 0.666 * (b.high() - b.low()));
609         pts.push_back(SPoint3(p2.x(), p2.y(), p2.z()));
610       }
611     }
612   }
613 
614   computeMeanPlane(pts);
615 }
616 
computeMeanPlane(const std::vector<MVertex * > & points)617 void GFace::computeMeanPlane(const std::vector<MVertex *> &points)
618 {
619   std::vector<SPoint3> pts;
620   for(std::size_t i = 0; i < points.size(); i++)
621     pts.push_back(SPoint3(points[i]->x(), points[i]->y(), points[i]->z()));
622   computeMeanPlane(pts);
623 }
624 
computeMeanPlane(const std::vector<SPoint3> & points)625 void GFace::computeMeanPlane(const std::vector<SPoint3> &points)
626 {
627   if(points.empty()) return;
628 
629   // The concept of a mean plane computed in the sense of least
630   // squares is fine for plane surfaces(!), but not really the best
631   // one for non-plane surfaces. Indeed, imagine a quarter of a circle
632   // extruded along a very small height: the mean plane will be in the
633   // plane of the circle... Until we implement something better, there
634   // is a test in this routine that checks the coherence of the
635   // computation for non-plane surfaces and reverts to a basic mean
636   // plane approximatation if the check fails.
637 
638   double xm = 0., ym = 0., zm = 0.;
639   int ndata = points.size();
640   int na = 3;
641   for(int i = 0; i < ndata; i++) {
642     xm += points[i].x();
643     ym += points[i].y();
644     zm += points[i].z();
645   }
646   xm /= (double)ndata;
647   ym /= (double)ndata;
648   zm /= (double)ndata;
649 
650   fullMatrix<double> U(ndata, na), V(na, na);
651   fullVector<double> sigma(na);
652   for(int i = 0; i < ndata; i++) {
653     U(i, 0) = points[i].x() - xm;
654     U(i, 1) = points[i].y() - ym;
655     U(i, 2) = points[i].z() - zm;
656   }
657   U.svd(V, sigma);
658   double res[4], svd[3];
659   svd[0] = sigma(0);
660   svd[1] = sigma(1);
661   svd[2] = sigma(2);
662   int min;
663   if(std::abs(svd[0]) < std::abs(svd[1]) && std::abs(svd[0]) < std::abs(svd[2]))
664     min = 0;
665   else if(std::abs(svd[1]) < std::abs(svd[0]) &&
666           std::abs(svd[1]) < std::abs(svd[2]))
667     min = 1;
668   else
669     min = 2;
670   res[0] = V(0, min);
671   res[1] = V(1, min);
672   res[2] = V(2, min);
673   norme(res);
674 
675   double ex[3], t1[3], t2[3];
676 
677   // check coherence of results for non-plane surfaces
678   if(geomType() != Plane && geomType() != DiscreteSurface &&
679      geomType() != BoundaryLayerSurface) {
680     double res2[3], c[3], sinc, angplan;
681     double eps = 1.e-3;
682 
683     GPoint v1 = point(0.5, 0.5);
684     GPoint v2 = point(0.5 + eps, 0.5);
685     GPoint v3 = point(0.5, 0.5 + eps);
686     t1[0] = v2.x() - v1.x();
687     t1[1] = v2.y() - v1.y();
688     t1[2] = v2.z() - v1.z();
689     t2[0] = v3.x() - v1.x();
690     t2[1] = v3.y() - v1.y();
691     t2[2] = v3.z() - v1.z();
692     norme(t1);
693     norme(t2);
694     // prodve(t1, t2, res2);
695     // WARNING: the rest of the code assumes res = t2 x t1, not t1 x t2 (WTF?)
696     prodve(t2, t1, res2);
697     norme(res2);
698 
699     prodve(res, res2, c);
700     double const cosc = prosca(res, res2);
701     sinc = std::sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]);
702     angplan = myatan2(sinc, cosc);
703     angplan = angle_02pi(angplan) * 180. / M_PI;
704     if((angplan > 70 && angplan < 110) || (angplan > 260 && angplan < 280)) {
705       Msg::Info("SVD failed (angle=%g): using rough algo...", angplan);
706       res[0] = res2[0];
707       res[1] = res2[1];
708       res[2] = res2[2];
709       goto end;
710     }
711   }
712 
713   ex[0] = ex[1] = ex[2] = 0.0;
714   if(res[0] == 0.)
715     ex[0] = 1.0;
716   else if(res[1] == 0.)
717     ex[1] = 1.0;
718   else
719     ex[2] = 1.0;
720 
721   prodve(res, ex, t1);
722   norme(t1);
723   prodve(t1, res, t2);
724   norme(t2);
725 
726 end:
727   res[3] = (xm * res[0] + ym * res[1] + zm * res[2]);
728 
729   fillMeanPlane(res, t1, t2, meanPlane);
730 
731   Msg::Debug("Surface: %d", tag());
732   Msg::Debug("SVD    : %g,%g,%g (min=%d)", svd[0], svd[1], svd[2], min);
733   Msg::Debug("Plane  : (%g x + %g y + %g z = %g)", meanPlane.a, meanPlane.b,
734              meanPlane.c, meanPlane.d);
735   Msg::Debug("Normal : (%g , %g , %g )", meanPlane.a, meanPlane.b, meanPlane.c);
736   Msg::Debug("t1     : (%g , %g , %g )", t1[0], t1[1], t1[2]);
737   Msg::Debug("t2     : (%g , %g , %g )", t2[0], t2[1], t2[2]);
738   Msg::Debug("pt     : (%g , %g , %g )", meanPlane.x, meanPlane.y, meanPlane.z);
739 
740   // check coherence for plane surfaces
741   if(geomType() == Plane) {
742     SBoundingBox3d bb = bounds();
743     double lc = norm(SVector3(bb.max(), bb.min()));
744     std::vector<GVertex *> const &verts = vertices();
745     for(auto itv = verts.begin(); itv != verts.end(); itv++) {
746       const GVertex *v = *itv;
747       double const d = meanPlane.a * v->x() + meanPlane.b * v->y() +
748                        meanPlane.c * v->z() - meanPlane.d;
749       if(std::abs(d) > lc * 1.e-3) {
750         Msg::Debug("Plane surface %d (%gx+%gy+%gz=%g) is not plane!", tag(),
751                    meanPlane.a, meanPlane.b, meanPlane.c, meanPlane.d);
752         Msg::Debug("Control point %d = (%g,%g,%g), val=%g", v->tag(), v->x(),
753                    v->y(), v->z(), d);
754         break;
755       }
756     }
757   }
758 }
759 
getMeanPlaneData(double VX[3],double VY[3],double & x,double & y,double & z) const760 void GFace::getMeanPlaneData(double VX[3], double VY[3], double &x, double &y,
761                              double &z) const
762 {
763   VX[0] = meanPlane.plan[0][0];
764   VX[1] = meanPlane.plan[0][1];
765   VX[2] = meanPlane.plan[0][2];
766   VY[0] = meanPlane.plan[1][0];
767   VY[1] = meanPlane.plan[1][1];
768   VY[2] = meanPlane.plan[1][2];
769   x = meanPlane.x;
770   y = meanPlane.y;
771   z = meanPlane.z;
772 }
773 
getMeanPlaneData(double plan[3][3]) const774 void GFace::getMeanPlaneData(double plan[3][3]) const
775 {
776   for(int i = 0; i < 3; i++)
777     for(int j = 0; j < 3; j++) plan[i][j] = meanPlane.plan[i][j];
778 }
779 
computeMeshSizeFieldAccuracy(double & avg,double & max_e,double & min_e,int & nE,int & GS)780 void GFace::computeMeshSizeFieldAccuracy(double &avg, double &max_e,
781                                          double &min_e, int &nE, int &GS)
782 {
783 #if defined(HAVE_MESH)
784   std::set<MEdge, MEdgeLessThan> es;
785   for(std::size_t i = 0; i < getNumMeshElements(); i++) {
786     MElement *e = getMeshElement(i);
787     for(int j = 0; j < e->getNumEdges(); j++) es.insert(e->getEdge(j));
788   }
789 
790   avg = 0;
791   min_e = 1.e22;
792   max_e = 0;
793   nE = es.size();
794   GS = 0;
795   double oneoversqr2 = 1. / sqrt(2.);
796   double sqr2 = sqrt(2.);
797   for(auto it = es.begin(); it != es.end(); ++it) {
798     double u1, v1, u2, v2;
799     MVertex *vert1 = it->getVertex(0);
800     vert1->getParameter(0, u1);
801     vert1->getParameter(1, v1);
802     MVertex *vert2 = it->getVertex(1);
803     vert2->getParameter(0, u2);
804     vert2->getParameter(1, v2);
805     double l1 = BGM_MeshSize(this, u1, v1, vert1->x(), vert1->y(), vert1->z());
806     double l2 = BGM_MeshSize(this, u2, v2, vert2->x(), vert2->y(), vert2->z());
807     double correctLC = 0.5 * (l1 + l2);
808     double lone = it->length() / correctLC;
809     if(lone > oneoversqr2 && lone < sqr2) GS++;
810     avg += lone > 1 ? (1. / lone) - 1. : lone - 1.;
811     max_e = std::max(max_e, lone);
812     min_e = std::min(min_e, lone);
813   }
814 #endif
815 }
816 
curvatureDiv(const SPoint2 & param) const817 double GFace::curvatureDiv(const SPoint2 &param) const
818 {
819   if(geomType() == Plane || geomType() == BoundaryLayerSurface) return 0;
820 
821   // X=X(u,v) Y=Y(u,v) Z=Z(u,v)
822   // curv = div n = dnx/dx + dny/dy + dnz/dz
823   // dnx/dx = dnx/du du/dx + dnx/dv dv/dx
824 
825   const double eps = 1.e-5;
826 
827   Pair<SVector3, SVector3> der = firstDer(param);
828 
829   SVector3 du = der.first();
830   SVector3 dv = der.second();
831   SVector3 nml = crossprod(du, dv);
832 
833   double detJ = norm(nml);
834 
835   du.normalize();
836   dv.normalize();
837 
838   SVector3 n1, n2, n3, n4;
839   if(param.x() - eps < 0.0) {
840     n1 = normal(SPoint2(param.x(), param.y()));
841     n2 = normal(SPoint2(param.x() + eps, param.y()));
842   }
843   else {
844     n1 = normal(SPoint2(param.x() - eps, param.y()));
845     n2 = normal(SPoint2(param.x(), param.y()));
846   }
847   if(param.y() - eps < 0.0) {
848     n3 = normal(SPoint2(param.x(), param.y()));
849     n4 = normal(SPoint2(param.x(), param.y() + eps));
850   }
851   else {
852     n3 = normal(SPoint2(param.x(), param.y() - eps));
853     n4 = normal(SPoint2(param.x(), param.y()));
854   }
855 
856   SVector3 dndu = 100000 * (n2 - n1);
857   SVector3 dndv = 100000 * (n4 - n3);
858 
859   SVector3 dudu = SVector3();
860   SVector3 dvdv = SVector3();
861   SVector3 dudv = SVector3();
862   secondDer(param, dudu, dvdv, dudv);
863 
864   double ddu = dot(dndu, du);
865   double ddv = dot(dndv, dv);
866 
867   return (fabs(ddu) + fabs(ddv)) / detJ;
868 }
869 
curvatureMax(const SPoint2 & param) const870 double GFace::curvatureMax(const SPoint2 &param) const
871 {
872   if(geomType() == Plane || geomType() == BoundaryLayerSurface) return 0.;
873 
874   double eigVal[2], eigVec[8];
875   getMetricEigenVectors(param, eigVal, eigVec);
876 
877   return fabs(eigVal[1]);
878 }
879 
curvatures(const SPoint2 & param,SVector3 & dirMax,SVector3 & dirMin,double & curvMax,double & curvMin) const880 double GFace::curvatures(const SPoint2 &param, SVector3 &dirMax,
881                          SVector3 &dirMin, double &curvMax,
882                          double &curvMin) const
883 {
884   Pair<SVector3, SVector3> D1 = firstDer(param);
885 
886   if(geomType() == Plane || geomType() == BoundaryLayerSurface) {
887     dirMax = D1.first();
888     dirMin = D1.second();
889     curvMax = 0.;
890     curvMin = 0.;
891     return 0.;
892   }
893 
894   if(geomType() == Sphere) {
895     dirMax = D1.first();
896     dirMin = D1.second();
897     curvMax = curvatureDiv(param);
898     curvMin = curvMax;
899     return curvMax;
900   }
901 
902   double eigVal[2], eigVec[8];
903   getMetricEigenVectors(param, eigVal, eigVec);
904 
905   // curvatures and main directions
906   curvMax = fabs(eigVal[1]);
907   curvMin = fabs(eigVal[0]);
908   dirMax = eigVec[1] * D1.first() + eigVec[3] * D1.second();
909   dirMin = eigVec[0] * D1.first() + eigVec[2] * D1.second();
910 
911   return curvMax;
912 }
913 
getMetricEigenvalue(const SPoint2 &)914 double GFace::getMetricEigenvalue(const SPoint2 &)
915 {
916   Msg::Error("Metric eigenvalue is not implemented for this type of surface");
917   return 0.;
918 }
919 
920 // eigen values are absolute values and sorted from min to max of absolute
921 // values eigen vectors are the corresponding COLUMNS of eigVec
getMetricEigenVectors(const SPoint2 & param,double eigVal[2],double eigVec[4]) const922 void GFace::getMetricEigenVectors(const SPoint2 &param, double eigVal[2],
923                                   double eigVec[4]) const
924 {
925   // first derivatives
926   Pair<SVector3, SVector3> D1 = firstDer(param);
927   SVector3 du = D1.first();
928   SVector3 dv = D1.second();
929   SVector3 nor = crossprod(du, dv);
930   nor.normalize();
931 
932   // second derivatives
933   SVector3 dudu = SVector3();
934   SVector3 dvdv = SVector3();
935   SVector3 dudv = SVector3();
936   secondDer(param, dudu, dvdv, dudv);
937 
938   // first form
939   double form1[2][2];
940   form1[0][0] = normSq(du);
941   form1[1][1] = normSq(dv);
942   form1[0][1] = form1[1][0] = dot(du, dv);
943 
944   // second form
945   double form2[2][2];
946   form2[0][0] = dot(nor, dudu);
947   form2[1][1] = dot(nor, dvdv);
948   form2[0][1] = form2[1][0] = dot(nor, dudv);
949 
950   // inverse of first form
951   double inv_form1[2][2];
952   double denom = (form1[0][0] * form1[1][1] - form1[1][0] * form1[0][1]);
953   if(denom) {
954     double inv_det_form1 = 1. / denom;
955     inv_form1[0][0] = inv_det_form1 * form1[1][1];
956     inv_form1[1][1] = inv_det_form1 * form1[0][0];
957     inv_form1[1][0] = inv_form1[0][1] = -1 * inv_det_form1 * form1[0][1];
958 
959     // N = (inverse of form1) X (form2)
960     fullMatrix<double> N(2, 2);
961     N(0, 0) = inv_form1[0][0] * form2[0][0] + inv_form1[0][1] * form2[1][0];
962     N(0, 1) = inv_form1[0][0] * form2[0][1] + inv_form1[0][1] * form2[1][1];
963     N(1, 0) = inv_form1[1][0] * form2[0][0] + inv_form1[1][1] * form2[1][0];
964     N(1, 1) = inv_form1[1][0] * form2[0][1] + inv_form1[1][1] * form2[1][1];
965 
966     // eigen values and vectors of N
967     fullMatrix<double> vl(2, 2), vr(2, 2);
968     fullVector<double> dr(2), di(2);
969     if(N.eig(dr, di, vl, vr, true)) {
970       eigVal[0] = fabs(dr(0));
971       eigVal[1] = fabs(dr(1));
972       eigVec[0] = vr(0, 0);
973       eigVec[2] = vr(1, 0);
974       eigVec[1] = vr(0, 1);
975       eigVec[3] = vr(1, 1);
976       if(fabs(di(0)) > 1.e-12 || fabs(di(1)) > 1.e-12) {
977         Msg::Warning("Imaginary eigenvalues in metric");
978       }
979       return;
980     }
981   }
982 
983   Msg::Warning("Could not compute metric eigenvectors");
984   for(int i = 0; i < 2; i++) eigVal[i] = 0.;
985   for(int i = 0; i < 4; i++) eigVec[i] = 0.;
986 }
987 
XYZtoUV(double X,double Y,double Z,double & U,double & V,double relax,bool onSurface,bool convTestXYZ) const988 void GFace::XYZtoUV(double X, double Y, double Z, double &U, double &V,
989                     double relax, bool onSurface, bool convTestXYZ) const
990 {
991   if(geomType() == BoundaryLayerSurface) return;
992 
993   const double Precision = onSurface ? 1.e-8 : 1.e-3;
994   const int MaxIter = onSurface ? 25 : 10;
995   const int NumInitGuess = 9;
996   bool testXYZ =
997     (convTestXYZ || CTX::instance()->mesh.NewtonConvergenceTestXYZ);
998 
999   double Unew = 0., Vnew = 0., err, err2;
1000   int iter;
1001   double mat[3][3], jac[3][3];
1002   double umin, umax, vmin, vmax;
1003   // don't use 0.9, 0.1 it fails with ruled surfaces
1004   double initu[NumInitGuess] = {0.5, 0.6, 0.4, 0.7, 0.3, 0.8, 0.2, 1.0, 0.0};
1005   double initv[NumInitGuess] = {0.5, 0.6, 0.4, 0.7, 0.3, 0.8, 0.2, 1.0, 0.0};
1006 
1007   Range<double> ru = parBounds(0);
1008   Range<double> rv = parBounds(1);
1009   umin = ru.low();
1010   umax = ru.high();
1011   vmin = rv.low();
1012   vmax = rv.high();
1013 
1014   const double tol =
1015     Precision * (std::pow(umax - umin, 2) + std::pow(vmax - vmin, 2));
1016   for(int i = 0; i < NumInitGuess; i++) {
1017     initu[i] = umin + initu[i] * (umax - umin);
1018     initv[i] = vmin + initv[i] * (vmax - vmin);
1019   }
1020 
1021   for(int i = 0; i < NumInitGuess; i++) {
1022     for(int j = 0; j < NumInitGuess; j++) {
1023       U = initu[i];
1024       V = initv[j];
1025       err = 1.0;
1026       iter = 1;
1027 
1028       GPoint P = point(U, V);
1029       err2 = std::sqrt(std::pow(X - P.x(), 2) + std::pow(Y - P.y(), 2) +
1030                        std::pow(Z - P.z(), 2));
1031       if(err2 < 1.e-8 * CTX::instance()->lc) return;
1032 
1033       while(err > tol && iter < MaxIter) {
1034         P = point(U, V);
1035         Pair<SVector3, SVector3> der = firstDer(SPoint2(U, V));
1036         mat[0][0] = der.left().x();
1037         mat[0][1] = der.left().y();
1038         mat[0][2] = der.left().z();
1039         mat[1][0] = der.right().x();
1040         mat[1][1] = der.right().y();
1041         mat[1][2] = der.right().z();
1042         mat[2][0] = 0.;
1043         mat[2][1] = 0.;
1044         mat[2][2] = 0.;
1045         invert_singular_matrix3x3(mat, jac);
1046 
1047         Unew = U + relax * (jac[0][0] * (X - P.x()) + jac[1][0] * (Y - P.y()) +
1048                             jac[2][0] * (Z - P.z()));
1049         Vnew = V + relax * (jac[0][1] * (X - P.x()) + jac[1][1] * (Y - P.y()) +
1050                             jac[2][1] * (Z - P.z()));
1051 
1052         // don't remove this test: it is important
1053         if((Unew > umax + tol || Unew < umin - tol) &&
1054            (Vnew > vmax + tol || Vnew < vmin - tol))
1055           break;
1056 
1057         err = std::pow(Unew - U, 2) + std::pow(Vnew - V, 2);
1058         err2 = std::sqrt(std::pow(X - P.x(), 2) + std::pow(Y - P.y(), 2) +
1059                          std::pow(Z - P.z(), 2));
1060 
1061         iter++;
1062         U = Unew;
1063         V = Vnew;
1064       }
1065 
1066       if(iter < MaxIter && err <= tol && Unew <= umax && Vnew <= vmax &&
1067          Unew >= umin && Vnew >= vmin) {
1068         if(onSurface && err2 > 1.e-4 * CTX::instance()->lc && !testXYZ) {
1069           Msg::Warning("Converged at iter. %d for initial guess (%d,%d) "
1070                        "with uv error = %g, but xyz error = %g in point "
1071                        "(%e, %e, %e) on surface %d",
1072                        iter, i, j, err, err2, X, Y, Z, tag());
1073         }
1074         if(onSurface && err2 > 1.e-4 * CTX::instance()->lc && testXYZ) {
1075           // not converged in XYZ coordinates: try again
1076         }
1077         else {
1078           return;
1079         }
1080       }
1081     }
1082   }
1083 
1084   if(!onSurface) return;
1085 
1086   if(relax < 1.e-3)
1087     Msg::Error("Inverse surface mapping could not converge");
1088   else {
1089     Msg::Info("point %g %g %g : Relaxation factor = %g", X, Y, Z, 0.75 * relax);
1090     XYZtoUV(X, Y, Z, U, V, 0.75 * relax, onSurface, convTestXYZ);
1091   }
1092 }
1093 
parFromPoint(const SPoint3 & p,bool onSurface,bool convTestXYZ) const1094 SPoint2 GFace::parFromPoint(const SPoint3 &p, bool onSurface,
1095                             bool convTestXYZ) const
1096 {
1097   if(geomType() == BoundaryLayerSurface) return SPoint2();
1098 
1099   double U = 0., V = 0.;
1100   XYZtoUV(p.x(), p.y(), p.z(), U, V, 1.0, onSurface, convTestXYZ);
1101   return SPoint2(U, V);
1102 }
1103 
1104 #if defined(HAVE_ALGLIB)
1105 
1106 class data_wrapper {
1107 private:
1108   const GFace *gf;
1109   SPoint3 point;
1110 
1111 public:
data_wrapper()1112   data_wrapper()
1113   {
1114     gf = nullptr;
1115     point = SPoint3();
1116   }
~data_wrapper()1117   ~data_wrapper() {}
get_face()1118   const GFace *get_face() { return gf; }
set_face(const GFace * face)1119   void set_face(const GFace *face) { gf = face; }
get_point()1120   SPoint3 get_point() { return point; }
set_point(const SPoint3 & _point)1121   void set_point(const SPoint3 &_point) { point = SPoint3(_point); }
1122 };
1123 
1124 // Callback function for ALGLIB
bfgs_callback(const alglib::real_1d_array & x,double & func,alglib::real_1d_array & grad,void * ptr)1125 void bfgs_callback(const alglib::real_1d_array &x, double &func,
1126                    alglib::real_1d_array &grad, void *ptr)
1127 {
1128   auto *w = static_cast<data_wrapper *>(ptr);
1129   SPoint3 p = w->get_point();
1130   const GFace *gf = w->get_face();
1131 
1132   // Value of the objective
1133   GPoint pnt = gf->point(x[0], x[1]);
1134   func = 0.5 * (p.x() - pnt.x()) * (p.x() - pnt.x()) +
1135          (p.y() - pnt.y()) * (p.y() - pnt.y()) +
1136          (p.z() - pnt.z()) * (p.z() - pnt.z());
1137   // printf("func : %f\n", func);
1138 
1139   // Value of the gradient
1140   Pair<SVector3, SVector3> der = gf->firstDer(SPoint2(x[0], x[1]));
1141   grad[0] = -(p.x() - pnt.x()) * der.left().x() -
1142             (p.y() - pnt.y()) * der.left().y() -
1143             (p.z() - pnt.z()) * der.left().z();
1144   grad[1] = -(p.x() - pnt.x()) * der.right().x() -
1145             (p.y() - pnt.y()) * der.right().y() -
1146             (p.z() - pnt.z()) * der.right().z();
1147   // printf("func %22.15E Gradients %22.15E %22.15E der %g %g %g\n", func,
1148   //         grad[0], grad[1],der.left().x(),der.left().y(),der.left().z());
1149 }
1150 #endif
1151 
closestPoint(const SPoint3 & queryPoint,const double initialGuess[2]) const1152 GPoint GFace::closestPoint(const SPoint3 &queryPoint,
1153                            const double initialGuess[2]) const
1154 {
1155   if(geomType() == BoundaryLayerSurface) return GPoint();
1156 
1157 #if defined(HAVE_ALGLIB)
1158   // Test initial guess
1159   double min_u = initialGuess[0];
1160   double min_v = initialGuess[1];
1161   GPoint pnt = point(min_u, min_v);
1162   SPoint3 spnt(pnt.x(), pnt.y(), pnt.z());
1163   double min_dist = queryPoint.distance(spnt);
1164 
1165   // Try to find a better initial guess by sampling full parameter range
1166   const double nGuesses = 10.;
1167   const Range<double> uu = parBounds(0);
1168   const Range<double> vv = parBounds(1);
1169   const double ru = uu.high() - uu.low(), rv = vv.high() - vv.low();
1170   const double epsU = 1e-5 * ru, epsV = 1e-5 * rv;
1171   const double du = ru / nGuesses, dv = rv / nGuesses;
1172   for(double u = uu.low(); u <= uu.high() + epsU; u += du) {
1173     for(double v = vv.low(); v <= vv.high() + epsV; v += dv) {
1174       GPoint pnt = point(u, v);
1175       SPoint3 spnt(pnt.x(), pnt.y(), pnt.z());
1176       double dist = queryPoint.distance(spnt);
1177       if(dist < min_dist) {
1178         min_dist = dist;
1179         min_u = u;
1180         min_v = v;
1181       }
1182     }
1183   }
1184 
1185   try {
1186     // Set up optimisation problem
1187     alglib::ae_int_t dim = 2;
1188     alglib::ae_int_t corr = 2; // Num of corrections in the scheme in [3,7]
1189     alglib::minlbfgsstate state;
1190     alglib::real_1d_array x;
1191     const double initialCond[2] = {min_u, min_v};
1192     x.setcontent(dim, initialCond);
1193     minlbfgscreate(2, corr, x, state);
1194 
1195     // Set stopping criteria
1196     const double epsg = 1.e-12;
1197     const double epsf = 0.;
1198     const double epsx = 0.;
1199     const alglib::ae_int_t maxits = 500;
1200     minlbfgssetcond(state, epsg, epsf, epsx, maxits);
1201 
1202     // Solve problem
1203     data_wrapper w;
1204     w.set_point(queryPoint);
1205     w.set_face(this);
1206     minlbfgsoptimize(state, bfgs_callback, nullptr, &w);
1207 
1208     // Get results
1209     alglib::minlbfgsreport rep;
1210     minlbfgsresults(state, x, rep);
1211     GPoint pntF = point(x[0], x[1]);
1212     return pntF;
1213   } catch(...) {
1214     Msg::Warning("Closest point failed, computing from parametric coordinate");
1215     SPoint2 p = parFromPoint(queryPoint, false);
1216     return point(p);
1217   }
1218 
1219 #else
1220   Msg::Error("Closest point not implemented for this type of surface");
1221   SPoint2 p = parFromPoint(queryPoint, false);
1222   return point(p);
1223 #endif
1224 }
1225 
containsParam(const SPoint2 & pt)1226 bool GFace::containsParam(const SPoint2 &pt)
1227 {
1228   if(geomType() == BoundaryLayerSurface) return false;
1229 
1230   Range<double> uu = parBounds(0);
1231   Range<double> vv = parBounds(1);
1232   if((pt.x() >= uu.low() && pt.x() <= uu.high()) &&
1233      (pt.y() >= vv.low() && pt.y() <= vv.high()))
1234     return true;
1235   else
1236     return false;
1237 }
1238 
normal(const SPoint2 & param) const1239 SVector3 GFace::normal(const SPoint2 &param) const
1240 {
1241   if(geomType() == BoundaryLayerSurface) return SVector3();
1242 
1243   Pair<SVector3, SVector3> der = firstDer(param);
1244   SVector3 n = crossprod(der.first(), der.second());
1245   n.normalize();
1246   return n;
1247 }
1248 
buildRepresentationCross(bool force)1249 bool GFace::buildRepresentationCross(bool force)
1250 {
1251   if(cross[0].size()) {
1252     if(force) {
1253       cross[0].clear();
1254       cross[1].clear();
1255     }
1256     else
1257       return true;
1258   }
1259 
1260   if(geomType() == DiscreteSurface || geomType() == BoundaryLayerSurface) {
1261     // TODO if the surface has been reparametrized
1262     if(cross[0].empty()) {
1263       cross[0].push_back(std::vector<SPoint3>());
1264       cross[0][0].push_back(bounds().center());
1265     }
1266     return false;
1267   }
1268 
1269   Range<double> ubounds = parBounds(0);
1270   Range<double> vbounds = parBounds(1);
1271   // try to compute something better for Gmsh surfaces
1272   if(getNativeType() == GmshModel && geomType() == Plane) {
1273     SBoundingBox3d bb;
1274     std::vector<GEdge *> ed = edges();
1275     for(auto it = ed.begin(); it != ed.end(); it++) {
1276       GEdge *ge = *it;
1277       if(ge->geomType() != DiscreteCurve &&
1278          ge->geomType() != BoundaryLayerCurve) {
1279         Range<double> t_bounds = ge->parBounds(0);
1280         const int N = 5;
1281         double t0 = t_bounds.low(), dt = t_bounds.high() - t_bounds.low();
1282         for(int i = 0; i < N; i++) {
1283           double t = t0 + dt / (double)(N - 1) * i;
1284           GPoint p = ge->point(t);
1285           SPoint2 uv = parFromPoint(SPoint3(p.x(), p.y(), p.z()));
1286           bb += SPoint3(uv.x(), uv.y(), 0.);
1287         }
1288       }
1289     }
1290     if(!bb.empty()) {
1291       ubounds = Range<double>(bb.min().x(), bb.max().x());
1292       vbounds = Range<double>(bb.min().y(), bb.max().y());
1293     }
1294   }
1295 
1296   bool tri = (geomType() == RuledSurface && getNativeType() == GmshModel &&
1297               edges().size() == 3 && !CTX::instance()->geom.oldRuledSurface);
1298   double tol = 1e-8;
1299   double ud = (ubounds.high() - ubounds.low()) - 2 * tol;
1300   double vd = (vbounds.high() - vbounds.low()) - 2 * tol;
1301   double u2 = 0.5 * (ubounds.high() + ubounds.low());
1302   double v2 = 0.5 * (vbounds.high() + vbounds.low());
1303   int N = 100;
1304   for(int dir = 0; dir < 2; dir++) {
1305     cross[dir].push_back(std::vector<SPoint3>());
1306     for(int i = 0; i < N; i++) {
1307       double t = (double)i / (double)(N - 1);
1308       SPoint2 uv;
1309       if(!dir) {
1310         if(tri)
1311           uv.setPosition(u2 + u2 * t, vbounds.low() + tol + v2 * t);
1312         else
1313           uv.setPosition(ubounds.low() + tol + ud * t, v2);
1314       }
1315       else {
1316         if(tri)
1317           uv.setPosition(u2 + u2 * t, v2 - v2 * t);
1318         else
1319           uv.setPosition(u2, vbounds.low() + tol + vd * t);
1320       }
1321       GPoint p = point(uv);
1322       SPoint3 pt(p.x(), p.y(), p.z());
1323       bool inside =
1324         (geomType() == Plane) ? containsPoint(pt) : containsParam(uv);
1325       if(inside) { cross[dir].back().push_back(pt); }
1326       else {
1327         if(cross[dir].back().size())
1328           cross[dir].push_back(std::vector<SPoint3>());
1329       }
1330     }
1331     while(cross[dir].back().empty())
1332       cross[dir].pop_back();
1333   }
1334 
1335 
1336   // draw additional small diamonds for plane surfaces, to make it easier to
1337   // select overlapping surfaces placed such that their crosses fully overlap
1338   // (e.g. when creating a hole centered in the middle of a surface, something
1339   // that happens quite often in practice)
1340   if(geomType() == Plane) {
1341     if(cross[0].size() > 0 && cross[0][0].size() > 1 &&
1342        cross[1].size() > 0 && cross[1][0].size() > 1) {
1343       SVector3 v0(cross[0][0][0], cross[0][0][1]);
1344       SVector3 v1(cross[1][0][0], cross[1][0][1]);
1345       double l0 = v0.normalize();
1346       double l1 = v1.normalize();
1347       SPoint3 p[4] = {cross[0].front().front(),
1348                       cross[0].back().back(),
1349                       cross[1].front().front(),
1350                       cross[1].back().back()};
1351       SVector3 vt[4] = {v0, -v0, v1, -v1};
1352       SVector3 vp[4] = {v1, -v1, v0, -v0};
1353       double l[4] = {l0, l0, l1, l1};
1354       for(int s = 0; s < 4; s++) {
1355         SPoint3 p0 = p[s];
1356         SPoint3 p1 = p0 + l[s] * vt[s];
1357         SPoint3 p2 = p1 + l[s] * vt[s] + l[s] * vp[s];
1358         SPoint3 p3 = p1 + 2 * l[s] * vt[s];
1359         SPoint3 p4 = p1 + l[s] * vt[s] - l[s] * vp[s];
1360         if(containsPoint(p1) && containsPoint(p3)) {
1361           if(containsPoint(p2)) {
1362             std::vector<SPoint3> c1 = {p1, p2};
1363             std::vector<SPoint3> c2 = {p2, p3};
1364             cross[0].push_back(c1);
1365             cross[0].push_back(c2);
1366           }
1367           if(containsPoint(p4)) {
1368             std::vector<SPoint3> c1 = {p1, p4};
1369             std::vector<SPoint3> c2 = {p4, p3};
1370             cross[0].push_back(c1);
1371             cross[0].push_back(c2);
1372           }
1373         }
1374       }
1375     }
1376   }
1377 
1378   // if we couldn't determine a cross, add a single point (center of the
1379   // bounding box) so that we won't try again unless we force the recomputation,
1380   // but we will still have a point to draw e.g. the label
1381   if(cross[0].empty()) {
1382     cross[0].push_back(std::vector<SPoint3>());
1383     cross[0][0].push_back(bounds().center());
1384     return false;
1385   }
1386   return true;
1387 }
1388 
buildSTLTriangulation(bool force)1389 bool GFace::buildSTLTriangulation(bool force)
1390 {
1391   if(stl_triangles.size() && !force) return true;
1392 
1393   stl_vertices_uv.clear();
1394   stl_vertices_xyz.clear();
1395   stl_triangles.clear();
1396 
1397   if(triangles.size()) {
1398     // if a mesh exists, import it as the STL representation
1399     std::map<MVertex *, int, MVertexPtrLessThan> nodes;
1400     for(std::size_t i = 0; i < triangles.size(); i++) {
1401       for(int j = 0; j < 3; j++) {
1402         MVertex *v = triangles[i]->getVertex(j);
1403         if(!nodes.count(v)) {
1404           stl_vertices_xyz.push_back(SPoint3(v->x(), v->y(), v->z()));
1405           nodes[v] = stl_vertices_xyz.size() - 1;
1406         }
1407       }
1408     }
1409     for(std::size_t i = 0; i < triangles.size(); i++) {
1410       for(int j = 0; j < 3; j++) {
1411         stl_triangles.push_back(nodes[triangles[i]->getVertex(j)]);
1412       }
1413     }
1414     return true;
1415   }
1416   else if(geomType() == ParametricSurface) {
1417     // build a simple triangulation for surfaces which we know are not trimmed
1418     const int nu = 64, nv = 64;
1419     Range<double> ubounds = parBounds(0);
1420     Range<double> vbounds = parBounds(1);
1421     double umin = ubounds.low(), umax = ubounds.high();
1422     double vmin = vbounds.low(), vmax = vbounds.high();
1423     for(int i = 0; i < nu; i++) {
1424       for(int j = 0; j < nv; j++) {
1425         double u = umin + (double)i / (double)(nu - 1) * (umax - umin);
1426         double v = vmin + (double)j / (double)(nv - 1) * (vmax - vmin);
1427         stl_vertices_uv.push_back(SPoint2(u, v));
1428         GPoint gp = point(u, v);
1429         stl_vertices_xyz.push_back(SPoint3(gp.x(), gp.y(), gp.z()));
1430       }
1431     }
1432     for(int i = 0; i < nu - 1; i++) {
1433       for(int j = 0; j < nv - 1; j++) {
1434         stl_triangles.push_back(i * nv + j);
1435         stl_triangles.push_back((i + 1) * nv + j);
1436         stl_triangles.push_back((i + 1) * nv + j + 1);
1437         stl_triangles.push_back(i * nv + j);
1438         stl_triangles.push_back((i + 1) * nv + j + 1);
1439         stl_triangles.push_back(i * nv + j + 1);
1440       }
1441     }
1442     return true;
1443   }
1444 
1445   return false;
1446 }
1447 
fillVertexArray(bool force)1448 bool GFace::fillVertexArray(bool force)
1449 {
1450   if(va_geom_triangles) {
1451     if(force)
1452       delete va_geom_triangles;
1453     else
1454       return true;
1455   }
1456 
1457   if(!buildSTLTriangulation(force)) return false;
1458   if(stl_triangles.empty()) return false;
1459 
1460   va_geom_triangles = new VertexArray(3, stl_triangles.size() / 3);
1461   unsigned int c =
1462     useColor() ? getColor() : CTX::instance()->color.geom.surface;
1463   unsigned int col[4] = {c, c, c, c};
1464   if(stl_vertices_xyz.size()) {
1465     for(std::size_t i = 0; i < stl_triangles.size(); i += 3) {
1466       SPoint3 &p1(stl_vertices_xyz[stl_triangles[i]]);
1467       SPoint3 &p2(stl_vertices_xyz[stl_triangles[i + 1]]);
1468       SPoint3 &p3(stl_vertices_xyz[stl_triangles[i + 2]]);
1469       double x[3] = {p1.x(), p2.x(), p3.x()};
1470       double y[3] = {p1.y(), p2.y(), p3.y()};
1471       double z[3] = {p1.z(), p2.z(), p3.z()};
1472       if(stl_vertices_xyz.size() == stl_normals.size()) {
1473         SVector3 n[3] = {stl_normals[stl_triangles[i]],
1474                          stl_normals[stl_triangles[i + 1]],
1475                          stl_normals[stl_triangles[i + 2]]};
1476         va_geom_triangles->add(x, y, z, n, col);
1477       }
1478       else {
1479         double nn[3];
1480         normal3points(x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2], nn);
1481         SVector3 n[3] = {SVector3(nn), SVector3(nn), SVector3(nn)};
1482         va_geom_triangles->add(x, y, z, n, col);
1483       }
1484     }
1485   }
1486   else if(stl_vertices_uv.size()) {
1487     for(std::size_t i = 0; i < stl_triangles.size(); i += 3) {
1488       SPoint2 &p1(stl_vertices_uv[stl_triangles[i]]);
1489       SPoint2 &p2(stl_vertices_uv[stl_triangles[i + 1]]);
1490       SPoint2 &p3(stl_vertices_uv[stl_triangles[i + 2]]);
1491       GPoint gp1 = point(p1);
1492       GPoint gp2 = point(p2);
1493       GPoint gp3 = point(p3);
1494       double x[3] = {gp1.x(), gp2.x(), gp3.x()};
1495       double y[3] = {gp1.y(), gp2.y(), gp3.y()};
1496       double z[3] = {gp1.z(), gp2.z(), gp3.z()};
1497       if(stl_vertices_uv.size() == stl_normals.size()) {
1498         SVector3 n[3] = {stl_normals[stl_triangles[i]],
1499                          stl_normals[stl_triangles[i + 1]],
1500                          stl_normals[stl_triangles[i + 2]]};
1501         va_geom_triangles->add(x, y, z, n, col);
1502       }
1503       else {
1504         SVector3 n[3] = {normal(p1), normal(p2), normal(p3)};
1505         va_geom_triangles->add(x, y, z, n, col);
1506       }
1507     }
1508   }
1509   va_geom_triangles->finalize();
1510   return true;
1511 }
1512 
genusGeom() const1513 int GFace::genusGeom() const
1514 {
1515   int nSeams = 0;
1516   std::set<GEdge *> single_seams;
1517   for(auto it = l_edges.begin(); it != l_edges.end(); ++it) {
1518     if((*it)->isSeam(this)) {
1519       nSeams++;
1520       auto it2 = single_seams.find(*it);
1521       if(it2 != single_seams.end())
1522         single_seams.erase(it2);
1523       else
1524         single_seams.insert(*it);
1525     }
1526   }
1527   return nSeams - single_seams.size();
1528 }
1529 
fillPointCloud(double maxDist,std::vector<SPoint3> * points,std::vector<SPoint2> * uvpoints,std::vector<SVector3> * normals)1530 bool GFace::fillPointCloud(double maxDist, std::vector<SPoint3> *points,
1531                            std::vector<SPoint2> *uvpoints,
1532                            std::vector<SVector3> *normals)
1533 {
1534   if(!points) return false;
1535 
1536   if(buildSTLTriangulation()) {
1537     if(stl_vertices_xyz.size()) {
1538       for(std::size_t i = 0; i < stl_triangles.size(); i += 3) {
1539         SPoint3 &p0(stl_vertices_xyz[stl_triangles[i]]);
1540         SPoint3 &p1(stl_vertices_xyz[stl_triangles[i + 1]]);
1541         SPoint3 &p2(stl_vertices_xyz[stl_triangles[i + 2]]);
1542         double maxEdge =
1543           std::max(p0.distance(p1), std::max(p1.distance(p2), p2.distance(p0)));
1544         int N = std::max((int)(maxEdge / maxDist), 1);
1545         for(double u = 0.; u < 1.; u += 1. / N) {
1546           for(double v = 0.; v < 1 - u; v += 1. / N) {
1547             SPoint3 p = p0 * (1. - u - v) + p1 * u + p2 * v;
1548             points->push_back(p);
1549           }
1550         }
1551       }
1552     }
1553     else if(stl_vertices_uv.size()) {
1554       for(std::size_t i = 0; i < stl_triangles.size(); i += 3) {
1555         SPoint2 &p0(stl_vertices_uv[stl_triangles[i]]);
1556         SPoint2 &p1(stl_vertices_uv[stl_triangles[i + 1]]);
1557         SPoint2 &p2(stl_vertices_uv[stl_triangles[i + 2]]);
1558         GPoint gp0 = point(p0);
1559         GPoint gp1 = point(p1);
1560         GPoint gp2 = point(p2);
1561         double maxEdge = std::max(
1562           gp0.distance(gp1), std::max(gp1.distance(gp2), gp2.distance(gp0)));
1563         int N = std::max((int)(maxEdge / maxDist), 1);
1564         for(double u = 0.; u < 1.; u += 1. / N) {
1565           for(double v = 0.; v < 1 - u; v += 1. / N) {
1566             SPoint2 p = p0 * (1. - u - v) + p1 * u + p2 * v;
1567             GPoint gp(point(p));
1568             points->push_back(SPoint3(gp.x(), gp.y(), gp.z()));
1569             if(uvpoints) uvpoints->push_back(p);
1570             if(normals) normals->push_back(normal(p));
1571           }
1572         }
1573       }
1574     }
1575   }
1576   else {
1577     // uniform sampling of underlying parametric plane
1578     int N = std::max((int)(bounds().diag() / maxDist), 2);
1579     Range<double> b1 = parBounds(0);
1580     Range<double> b2 = parBounds(1);
1581     for(int i = 0; i < N; i++) {
1582       for(int j = 0; j < N; j++) {
1583         double u = (double)i / (N - 1);
1584         double v = (double)j / (N - 1);
1585         double t1 = b1.low() + u * (b1.high() - b1.low());
1586         double t2 = b2.low() + v * (b2.high() - b2.low());
1587         GPoint gp = point(t1, t2);
1588         points->push_back(SPoint3(gp.x(), gp.y(), gp.z()));
1589         if(uvpoints) uvpoints->push_back(SPoint2(t1, t2));
1590         if(normals) normals->push_back(normal(SPoint2(t1, t2)));
1591       }
1592     }
1593   }
1594   return true;
1595 }
1596 
1597 #if defined(HAVE_MESH)
1598 
1599 #if defined(HAVE_QUADMESHINGTOOLS)
1600 
1601 int buildBackgroundField(
1602   GModel *gm, const std::vector<MTriangle *> &global_triangles,
1603   const std::vector<std::array<double, 9> > &global_triangle_directions,
1604   const std::unordered_map<MVertex *, double> &global_size_map,
1605   const std::vector<std::array<double, 5> > &global_singularity_list,
1606   const std::string &viewName);
1607 int fillSizemapFromScalarBackgroundField(
1608   GModel *gm, const std::vector<MTriangle *> &triangles,
1609   std::unordered_map<MVertex *, double> &sizeMap);
1610 
1611 #endif
1612 
meshCompoundMakeQuads(GFace * gf)1613 static int meshCompoundMakeQuads(GFace *gf)
1614 {
1615   if(gf->meshAttributes.algorithm != ALGO_2D_PACK_PRLGRMS &&
1616      gf->meshAttributes.algorithm != ALGO_2D_QUAD_QUASI_STRUCT)
1617     return 0;
1618 
1619   // recombineIntoQuads(gf, false, 2, true, .01);
1620 
1621   meshGFaceQuadrangulateBipartiteLabelling(gf->tag());
1622   return 0;
1623 }
1624 
meshCompoundComputeCrossFieldWithHeatEquation(GFace * gf)1625 static int meshCompoundComputeCrossFieldWithHeatEquation(GFace *gf)
1626 {
1627   if(gf->meshAttributes.algorithm != ALGO_2D_PACK_PRLGRMS &&
1628      gf->meshAttributes.algorithm != ALGO_2D_QUAD_QUASI_STRUCT)
1629     return 0;
1630 
1631 #if defined(HAVE_QUADMESHINGTOOLS)
1632 
1633   std::vector<std::array<double, 3> > triEdgeTheta;
1634   std::vector<MLine *> lines;
1635   std::vector<GEdge *> edges = gf->edges();
1636   for(auto ge : edges)
1637     lines.insert(lines.end(), ge->lines.begin(), ge->lines.end());
1638 
1639   int scf =
1640     computeCrossFieldWithHeatEquation(4, gf->triangles, lines, triEdgeTheta);
1641 
1642   if(scf != 0) {
1643     Msg::Warning("- Face %i: failed to compute cross field", gf->tag());
1644     return scf;
1645   }
1646 
1647   std::vector<std::array<double, 9> > triangleDirections;
1648   int sc = convertToPerTriangleCrossFieldDirections(
1649     4, gf->triangles, triEdgeTheta, triangleDirections);
1650   if(sc != 0) {
1651     Msg::Warning(
1652       "- Face %i: failed to resample cross field at triangle corners",
1653       gf->tag());
1654   }
1655 
1656   std::unordered_map<MVertex *, double> sizeMap;
1657 
1658   int sts =
1659     fillSizemapFromScalarBackgroundField(gf->model(), gf->triangles, sizeMap);
1660   if(sts != 0) {
1661     Msg::Warning("- Face %i: failed to fill size map from background field",
1662                  gf->tag());
1663   }
1664 
1665   std::vector<std::array<double, 5> > singularityList;
1666 
1667   FieldManager *fields = gf->model()->getFields();
1668   fields->setBackgroundFieldId(0);
1669 
1670   int TEMP = CTX::instance()->mesh.algo2d;
1671   CTX::instance()->mesh.algo2d = ALGO_2D_PACK_PRLGRMS;
1672 
1673   int sbf = buildBackgroundField(gf->model(), gf->triangles, triangleDirections,
1674                                  sizeMap, singularityList, "guiding_field");
1675 
1676   CTX::instance()->mesh.algo2d = TEMP;
1677 
1678   if(sbf != 0) {
1679     Msg::Warning("failed to build background guiding field");
1680     return -1;
1681   }
1682 
1683   return 0;
1684 #else
1685   return -1;
1686 #endif
1687 }
1688 
meshCompound(GFace * gf,bool verbose)1689 static void meshCompound(GFace *gf, bool verbose)
1690 {
1691   discreteFace *df = dynamic_cast<discreteFace*>
1692     (gf->model()->getFaceByTag(gf->tag() + 100000));
1693   if(df) {
1694     df->deleteMesh();
1695   }
1696   else{
1697     df = new discreteFace(gf->model(), gf->tag() + 100000);
1698     gf->model()->add(df);
1699   }
1700 
1701   // reclassify the elements on the original surfaces? (This is nice but it will
1702   // perturb algorithms that depend on the parametrization after the mesh is
1703   // done)
1704   bool magic = (CTX::instance()->mesh.compoundClassify == 1);
1705 
1706   if(CTX::instance()->geom.copyMeshingMethod) {
1707     df->meshAttributes.method = gf->meshAttributes.method;
1708     df->meshAttributes.transfiniteArrangement =
1709       gf->meshAttributes.transfiniteArrangement;
1710     df->meshAttributes.transfiniteSmoothing =
1711       gf->meshAttributes.transfiniteSmoothing;
1712     df->meshAttributes.algorithm = gf->meshAttributes.algorithm;
1713   }
1714 
1715   std::vector<GFace *> triangles_tag;
1716 
1717   std::set<GEdge *, GEntityPtrLessThan> bnd, emb1;
1718   std::set<GVertex *, GEntityPtrLessThan> emb0;
1719   std::vector<int> phys;
1720   for(std::size_t i = 0; i < gf->compound.size(); i++) {
1721     auto *c = (GFace *)gf->compound[i];
1722     df->triangles.insert(df->triangles.end(), c->triangles.begin(),
1723                          c->triangles.end());
1724     df->quadrangles.insert(df->quadrangles.end(), c->quadrangles.begin(),
1725                            c->quadrangles.end());
1726     df->mesh_vertices.insert(df->mesh_vertices.end(), c->mesh_vertices.begin(),
1727                              c->mesh_vertices.end());
1728     for(std::size_t j = 0; j < c->triangles.size(); j++)
1729       triangles_tag.push_back(c);
1730     std::vector<GEdge *> edges = c->edges();
1731     for(std::size_t j = 0; j < edges.size(); j++) {
1732       if(bnd.find(edges[j]) == bnd.end())
1733         bnd.insert(edges[j]);
1734       else
1735         bnd.erase(edges[j]);
1736     }
1737     // force retrieval of embedded entities
1738     std::vector<GVertex *> embv = c->getEmbeddedVertices(true);
1739     emb0.insert(embv.begin(), embv.end());
1740     std::vector<GEdge *> embe = c->getEmbeddedEdges(true);
1741     emb1.insert(embe.begin(), embe.end());
1742 
1743     if(magic) {
1744       c->triangles.clear();
1745       c->quadrangles.clear();
1746       c->mesh_vertices.clear();
1747     }
1748     c->compoundSurface = df;
1749     if(!magic) {
1750       phys.insert(phys.end(), c->physicals.begin(), c->physicals.end());
1751       c->physicals.clear();
1752     }
1753   }
1754 
1755   std::set<GEdge *, GEntityPtrLessThan> bndc;
1756   for(auto it = bnd.begin(); it != bnd.end(); it++) {
1757     GEdge *e = *it;
1758     if(e->compoundCurve)
1759       bndc.insert(e->compoundCurve);
1760     else
1761       bndc.insert(e);
1762   }
1763   std::vector<GEdge *> ed(bndc.begin(), bndc.end());
1764   df->set(ed);
1765 
1766   for(auto it = emb1.begin(); it != emb1.end(); it++) df->addEmbeddedEdge(*it);
1767 
1768   for(auto it = emb0.begin(); it != emb0.end(); it++)
1769     df->addEmbeddedVertex(*it);
1770 
1771   FieldManager *fields = gf->model()->getFields();
1772   int BGTAG = fields->getBackgroundField();
1773   Field *backgroundField = fields->get(BGTAG);
1774 
1775   if(df->createGeometry()) {
1776     Msg::Error("Could not create geometry of discrete face %d (check "
1777                "orientation of input triangulations)",
1778                df->tag());
1779   }
1780   else {
1781     int scf = meshCompoundComputeCrossFieldWithHeatEquation(df);
1782     if(scf != 0) { return; }
1783   }
1784 
1785   if(!magic) {
1786     df->triangles.clear();
1787     df->quadrangles.clear();
1788     df->mesh_vertices.clear();
1789   }
1790   df->mesh(verbose);
1791 
1792   meshCompoundMakeQuads(df);
1793 
1794   if(fields->getBackgroundField() > 0 &&
1795      fields->getBackgroundField() != BGTAG) {
1796     fields->deleteField(fields->getBackgroundField());
1797     fields->setBackgroundField(backgroundField);
1798   }
1799 
1800   if(!magic) {
1801     df->physicals = phys;
1802     return;
1803   }
1804 
1805   for(std::size_t i = 0; i < df->mesh_vertices.size(); i++) {
1806     double u, v;
1807     df->mesh_vertices[i]->getParameter(0, u);
1808     df->mesh_vertices[i]->getParameter(1, v);
1809     double U, V;
1810     // search triangle in original mesh used to compute the parametrization
1811     int position = df->trianglePosition(u, v, U, V);
1812     if(position >= 0 && position < (int)triangles_tag.size()) {
1813       triangles_tag[position]->mesh_vertices.push_back(df->mesh_vertices[i]);
1814       df->mesh_vertices[i]->setEntity(triangles_tag[position]);
1815     }
1816     else {
1817       gf->mesh_vertices.push_back(df->mesh_vertices[i]);
1818       gf->mesh_vertices[i]->setEntity(gf);
1819     }
1820   }
1821 
1822   for(std::size_t i = 0; i < df->triangles.size(); i++) {
1823     MTriangle *t = df->triangles[i];
1824     bool found = false;
1825     for(int i = 0; i < 3; i++) {
1826       if(t->getVertex(i)->onWhat()->dim() == 2) {
1827         ((GFace *)t->getVertex(i)->onWhat())->triangles.push_back(t);
1828         found = true;
1829         break;
1830       }
1831     }
1832     if(!found) {
1833       gf->triangles.push_back(t); // FIXME could be better!
1834     }
1835   }
1836 
1837   for(std::size_t i = 0; i < df->quadrangles.size(); i++) {
1838     MQuadrangle *q = df->quadrangles[i];
1839     bool found = false;
1840     for(int i = 0; i < 4; i++) {
1841       if(q->getVertex(i)->onWhat()->dim() == 2) {
1842         ((GFace *)q->getVertex(i)->onWhat())->quadrangles.push_back(q);
1843         found = true;
1844         break;
1845       }
1846     }
1847     if(!found) {
1848       gf->quadrangles.push_back(q); // FIXME could be better!
1849     }
1850   }
1851 
1852   df->triangles.clear();
1853   df->quadrangles.clear();
1854   df->mesh_vertices.clear();
1855 }
1856 #endif
1857 
mesh(bool verbose)1858 void GFace::mesh(bool verbose)
1859 {
1860   if(CTX::instance()->debugSurface > 0 &&
1861      tag() != CTX::instance()->debugSurface) {
1862     meshStatistics.status = GFace::DONE;
1863     return;
1864   }
1865 
1866 #if defined(HAVE_MESH)
1867   if(compound.size())
1868     meshAttributes.meshSizeFactor = CTX::instance()->mesh.compoundLcFactor;
1869 
1870   meshGFace mesher;
1871   mesher(this, verbose);
1872 
1873   if(compound.size()) { // Some faces are meshed together
1874     meshAttributes.meshSizeFactor = 1;
1875 
1876     orientMeshGFace orient;
1877     orient(this);
1878     if(compound[0] == this) { // I'm the one that makes the compound job
1879       bool ok = true;
1880       for(std::size_t i = 0; i < compound.size(); i++) {
1881         auto *gf = (GFace *)compound[i];
1882         ok &= (gf->meshStatistics.status == GFace::DONE);
1883       }
1884       if(!ok) { meshStatistics.status = GFace::PENDING; }
1885       else {
1886         meshCompound(this, verbose);
1887         meshStatistics.status = GFace::DONE;
1888         return;
1889       }
1890     }
1891   }
1892 #endif
1893 }
1894 
moveToValidRange(SPoint2 & pt) const1895 void GFace::moveToValidRange(SPoint2 &pt) const
1896 {
1897   for(int i = 0; i < 2; i++) {
1898     if(periodic(i)) {
1899       Range<double> range = parBounds(i);
1900       double tol = 1e-6 * (range.high() - range.low());
1901       if(pt[i] < range.low() - tol) pt[i] += period(i);
1902       if(pt[i] > range.high() + tol) pt[i] -= period(i);
1903       if(pt[i] < range.low()) pt[i] = range.low();
1904       if(pt[i] > range.high()) pt[i] = range.high();
1905     }
1906   }
1907 }
1908 
relocateMeshVertices()1909 void GFace::relocateMeshVertices()
1910 {
1911   for(std::size_t i = 0; i < mesh_vertices.size(); i++) {
1912     MVertex *v = mesh_vertices[i];
1913     double u0 = 0., u1 = 0.;
1914     if(v->getParameter(0, u0) && v->getParameter(1, u1)) {
1915       GPoint p = point(u0, u1);
1916       v->x() = p.x();
1917       v->y() = p.y();
1918       v->z() = p.z();
1919     }
1920   }
1921 }
1922 
setMeshMaster(GFace * master,const std::vector<double> & tfo)1923 void GFace::setMeshMaster(GFace *master, const std::vector<double> &tfo)
1924 {
1925   Msg::Info("Setting mesh master using transformation");
1926 
1927   // points and curves
1928   std::set<GVertex *> l_vertices;
1929   std::multimap<std::pair<GVertex *, GVertex *>, GEdge *> l_vtxToEdge;
1930   for(auto eIter = l_edges.begin(); eIter != l_edges.end(); ++eIter) {
1931     GVertex *v0 = (*eIter)->getBeginVertex();
1932     GVertex *v1 = (*eIter)->getEndVertex();
1933     if(v0 && v1) {
1934       l_vertices.insert(v0);
1935       l_vertices.insert(v1);
1936       l_vtxToEdge.insert(std::make_pair(std::make_pair(v0, v1), *eIter));
1937     }
1938   }
1939   for(auto eIter = embedded_edges.begin(); eIter != embedded_edges.end();
1940       ++eIter) {
1941     GVertex *v0 = (*eIter)->getBeginVertex();
1942     GVertex *v1 = (*eIter)->getEndVertex();
1943     if(v0 && v1) {
1944       l_vertices.insert(v0);
1945       l_vertices.insert(v1);
1946       l_vtxToEdge.insert(std::make_pair(std::make_pair(v0, v1), *eIter));
1947     }
1948   }
1949   l_vertices.insert(embedded_vertices.begin(), embedded_vertices.end());
1950 
1951   // master points and curves
1952   std::vector<GEdge *> const &m_edges = master->edges();
1953   std::set<GVertex *> m_vertices;
1954   std::multimap<std::pair<GVertex *, GVertex *>, GEdge *> m_vtxToEdge;
1955   for(auto eIter = m_edges.begin(); eIter != m_edges.end(); ++eIter) {
1956     GVertex *v0 = (*eIter)->getBeginVertex();
1957     GVertex *v1 = (*eIter)->getEndVertex();
1958     if(v0 && v1) {
1959       m_vertices.insert(v0);
1960       m_vertices.insert(v1);
1961       m_vtxToEdge.insert(std::make_pair(std::make_pair(v0, v1), *eIter));
1962     }
1963   }
1964   std::vector<GEdge *> const &m_embedded_edges = master->embeddedEdges();
1965   for(auto eIter = m_embedded_edges.begin(); eIter != m_embedded_edges.end();
1966       eIter++) {
1967     GVertex *v0 = (*eIter)->getBeginVertex();
1968     GVertex *v1 = (*eIter)->getEndVertex();
1969     if(v0 && v1) {
1970       m_vertices.insert(v0);
1971       m_vertices.insert(v1);
1972       m_vtxToEdge.insert(std::make_pair(std::make_pair(v0, v1), *eIter));
1973     }
1974   }
1975   std::set<GVertex *, GEntityPtrLessThan> m_embedded_vertices =
1976     master->embeddedVertices();
1977   m_vertices.insert(m_embedded_vertices.begin(), m_embedded_vertices.end());
1978 
1979   // check topological correspondence
1980   if(l_vertices.size() != m_vertices.size()) {
1981     Msg::Error(
1982       "Different number of points (%d vs %d) for periodic correspondence "
1983       "between surfaces %d and %d",
1984       l_vertices.size(), m_vertices.size(), master->tag(), tag());
1985     return;
1986   }
1987   if(l_vtxToEdge.size() != m_vtxToEdge.size()) {
1988     Msg::Error(
1989       "Different number of curves (%d vs %d) for periodic correspondence "
1990       "between surfaces %d and %d",
1991       l_vtxToEdge.size(), m_vtxToEdge.size(), master->tag(), tag());
1992     return;
1993   }
1994 
1995   // compute corresponding vertices
1996   std::map<GVertex *, GVertex *> gVertexCounterparts;
1997   for(auto mvIter = m_vertices.begin(); mvIter != m_vertices.end(); ++mvIter) {
1998     GVertex *m_vertex = *mvIter;
1999 
2000     SPoint3 xyzTfo((*mvIter)->x(), (*mvIter)->y(), (*mvIter)->z());
2001     xyzTfo.transform(tfo);
2002 
2003     GVertex *l_vertex = nullptr;
2004 
2005     double dist_min = 1.e22;
2006     for(auto lvIter = l_vertices.begin(); lvIter != l_vertices.end();
2007         ++lvIter) {
2008       SPoint3 xyz((*lvIter)->x(), (*lvIter)->y(), (*lvIter)->z());
2009       SVector3 dist = xyz - xyzTfo;
2010       dist_min = std::min(dist_min, dist.norm());
2011       if(dist.norm() < CTX::instance()->geom.tolerance * CTX::instance()->lc) {
2012         l_vertex = *lvIter;
2013         break;
2014       }
2015     }
2016 
2017     if(l_vertex == nullptr) {
2018       Msg::Error("No corresponding point %d for periodic connection of surface "
2019                  "%d to %d (min. distance = %g, tolerance = %g)",
2020                  m_vertex->tag(), master->tag(), tag(), dist_min,
2021                  CTX::instance()->geom.tolerance * CTX::instance()->lc);
2022       return;
2023     }
2024     gVertexCounterparts[l_vertex] = m_vertex;
2025   }
2026 
2027   if(gVertexCounterparts.size() != m_vertices.size()) {
2028     Msg::Error("Could not find all point correspondences for the periodic "
2029                "connection from surface %d to %d",
2030                master->tag(), tag());
2031     return;
2032   }
2033 
2034   // construct edge correspondence and update the edge masters
2035   std::map<GEdge *, std::pair<GEdge *, int> > gEdgeCounterparts;
2036 
2037   for(auto lv2eIter = l_vtxToEdge.begin(); lv2eIter != l_vtxToEdge.end();
2038       lv2eIter++) {
2039     std::pair<GVertex *, GVertex *> lPair = lv2eIter->first;
2040     GEdge *localEdge = lv2eIter->second;
2041 
2042     std::pair<GVertex *, GVertex *> forward(gVertexCounterparts[lPair.first],
2043                                             gVertexCounterparts[lPair.second]);
2044     int numf = m_vtxToEdge.count(forward);
2045     std::pair<GVertex *, GVertex *> backward(forward.second, forward.first);
2046     int numb = m_vtxToEdge.count(backward);
2047     int sign = 0;
2048     GEdge *masterEdge = nullptr;
2049 
2050     // unique matches
2051     if(!masterEdge && numf == 1 &&
2052        (numb == 0 || forward.first == forward.second)) {
2053       masterEdge = m_vtxToEdge.find(forward)->second;
2054       sign = 1;
2055     }
2056     if(!masterEdge && numb == 1 &&
2057        (numf == 0 || forward.first == forward.second)) {
2058       masterEdge = m_vtxToEdge.find(backward)->second;
2059       sign = -1;
2060     }
2061 
2062     // multiple matches (when several curves have the same begin/end points)
2063     SBoundingBox3d localbb = localEdge->bounds(true);
2064     // using the global geometrical tolerance does not work well, as the
2065     // accuracy of bounding boxes can be poor (e.g. in OCC, computed through a
2066     // mesh, ...)
2067     double tol = localbb.diag() * 1e-3;
2068 
2069     if(!masterEdge && numf) {
2070       auto ret = m_vtxToEdge.equal_range(forward);
2071       for(auto it = ret.first; it != ret.second; it++) {
2072         SBoundingBox3d masterbb = it->second->bounds(true);
2073         masterbb.transform(tfo);
2074         if(masterbb.min().distance(localbb.min()) < tol &&
2075            masterbb.max().distance(localbb.max()) < tol) {
2076           masterEdge = it->second;
2077           sign = 1;
2078           break;
2079         }
2080       }
2081     }
2082     if(!masterEdge && numb) {
2083       auto ret = m_vtxToEdge.equal_range(backward);
2084       for(auto it = ret.first; it != ret.second; it++) {
2085         SBoundingBox3d masterbb = it->second->bounds(true);
2086         masterbb.transform(tfo);
2087         if(masterbb.min().distance(localbb.min()) < tol &&
2088            masterbb.max().distance(localbb.max()) < tol) {
2089           masterEdge = it->second;
2090           sign = -1;
2091           break;
2092         }
2093       }
2094     }
2095 
2096     if(!masterEdge) {
2097       Msg::Error("Could not find counterpart of curve %d with end points %d-%d "
2098                  "(corresponding to curve with end points %d %d) in surface %d",
2099                  localEdge->tag(), lPair.first->tag(), lPair.second->tag(),
2100                  forward.first->tag(), forward.second->tag(), master->tag());
2101       return;
2102     }
2103 
2104     if(masterEdge->getMeshMaster() != localEdge) {
2105       localEdge->setMeshMaster(masterEdge, tfo);
2106       Msg::Info("Setting curve master %d - %d", localEdge->tag(),
2107                 masterEdge->tag());
2108     }
2109     gEdgeCounterparts[localEdge] = std::make_pair(masterEdge, sign);
2110   }
2111 
2112   // complete the information at the edge level
2113   edgeCounterparts = gEdgeCounterparts;
2114   vertexCounterparts = gVertexCounterparts;
2115   GEntity::setMeshMaster(master, tfo);
2116 }
2117 
myAngle(const SVector3 & a,const SVector3 & b,const SVector3 & d)2118 inline double myAngle(const SVector3 &a, const SVector3 &b, const SVector3 &d)
2119 {
2120   double const cosTheta = dot(a, b);
2121   double const sinTheta = dot(crossprod(a, b), d);
2122   return std::atan2(sinTheta, cosTheta);
2123 }
2124 
2125 struct myPlane {
2126   SPoint3 p;
2127   SVector3 n;
2128   double a;
2129   // nx x + ny y + nz z + a = 0
myPlanemyPlane2130   myPlane(const SPoint3 &_p, const SVector3 &_n) : p(_p), n(_n)
2131   {
2132     n.normalize();
2133     a = -(n.x() * p.x() + n.y() * p.y() + n.z() * p.z());
2134   }
evalmyPlane2135   double eval(double x, double y, double z)
2136   {
2137     return n.x() * x + n.y() * y + n.z() * z + a;
2138   }
2139 };
2140 
2141 struct myLine {
2142   SPoint3 p;
2143   SVector3 t;
myLinemyLine2144   myLine() : p(0, 0, 0), t(0, 0, 1) {}
myLinemyLine2145   myLine(myPlane &p1, myPlane &p2)
2146   {
2147     t = crossprod(p1.n, p2.n);
2148     if(t.norm() == 0.0) { Msg::Error("parallel planes do not intersect"); }
2149     else
2150       t.normalize();
2151     // find a point, assume z = 0
2152     double a[2][2] = {{p1.n.x(), p1.n.y()}, {p2.n.x(), p2.n.y()}};
2153     double b[2] = {-p1.a, -p2.a}, x[2];
2154     if(!sys2x2(a, b, x)) {
2155       // assume x = 0
2156       double az[2][2] = {{p1.n.y(), p1.n.z()}, {p2.n.y(), p2.n.z()}};
2157       double bz[2] = {-p1.a, -p2.a};
2158       if(!sys2x2(az, bz, x)) {
2159         // assume y = 0
2160         double ay[2][2] = {{p1.n.x(), p1.n.z()}, {p2.n.x(), p2.n.z()}};
2161         double by[2] = {-p1.a, -p2.a};
2162         if(!sys2x2(ay, by, x)) {
2163           Msg::Error("parallel planes do not intersect");
2164         }
2165         else {
2166           p = SPoint3(x[0], 0., x[1]);
2167         }
2168       }
2169       else {
2170         p = SPoint3(0., x[0], x[1]);
2171       }
2172     }
2173     else {
2174       p = SPoint3(x[0], x[1], 0.);
2175     }
2176   }
orthogonalProjectionmyLine2177   SPoint3 orthogonalProjection(SPoint3 &a)
2178   {
2179     // (x - a) * t = 0 -->
2180     // x = p + u t --> (p + ut - a) * t = 0 --> u = (a-p) * t
2181     const double u = dot(a - p, t);
2182     return SPoint3(p.x() + t.x() * u, p.y() + t.y() * u, p.z() + t.z() * u);
2183   }
2184 };
2185 
setMeshMaster(GFace * master,const std::map<int,int> & edgeCopies)2186 void GFace::setMeshMaster(GFace *master, const std::map<int, int> &edgeCopies)
2187 {
2188   std::map<GVertex *, GVertex *> vs2vt;
2189 
2190   for(auto it = l_edges.begin(); it != l_edges.end(); ++it) {
2191     // slave edge
2192     GEdge *le = *it;
2193 
2194     int sign = 1;
2195     auto adnksd = edgeCopies.find(le->tag());
2196     int source_e;
2197     if(adnksd != edgeCopies.end())
2198       source_e = adnksd->second;
2199     else {
2200       sign = -1;
2201       adnksd = edgeCopies.find(-(*it)->tag());
2202       if(adnksd != edgeCopies.end())
2203         source_e = adnksd->second;
2204       else {
2205         Msg::Error("Could not find curve counterpart %d in slave surface %d",
2206                    (*it)->tag(), master->tag());
2207         return;
2208       }
2209     }
2210 
2211     // master edge
2212     GEdge *me = master->model()->getEdgeByTag(abs(source_e));
2213 
2214     if(le->getBeginVertex() && le->getEndVertex() && me->getBeginVertex() &&
2215        me->getEndVertex()) {
2216       if(source_e * sign > 0) {
2217         vs2vt[me->getBeginVertex()] = le->getBeginVertex();
2218         vs2vt[me->getEndVertex()] = le->getEndVertex();
2219       }
2220       else {
2221         vs2vt[me->getBeginVertex()] = le->getEndVertex();
2222         vs2vt[me->getEndVertex()] = le->getBeginVertex();
2223       }
2224     }
2225   }
2226 
2227   // --- find out the transformation
2228 
2229   bool translation = true;
2230   SVector3 DX;
2231 
2232   int count = 0;
2233   for(auto it = vs2vt.begin(); it != vs2vt.end(); ++it) {
2234     GVertex *vs = it->first;
2235     GVertex *vt = it->second;
2236     if(count == 0)
2237       DX = SVector3(vt->x() - vs->x(), vt->y() - vs->y(), vt->z() - vs->z());
2238     else {
2239       SVector3 DX2(vt->x() - vs->x(), vt->y() - vs->y(), vt->z() - vs->z());
2240       SVector3 DDX(DX2 - DX);
2241       if(DDX.norm() > DX.norm() * 1.e-5) translation = false;
2242     }
2243     count++;
2244   }
2245 
2246   std::vector<double> tfo(16);
2247 
2248   if(translation) {
2249     Msg::Info(
2250       "Periodic mesh translation found between %d and %d: dx = (%g,%g,%g)",
2251       tag(), master->tag(), DX.x(), DX.y(), DX.z());
2252 
2253     for(size_t i = 0; i < 16; i++) tfo[i] = 0;
2254     for(size_t i = 0; i < 4; i++) tfo[i * 4 + i] = 1;
2255     tfo[3] = DX.x();
2256     tfo[7] = DX.y();
2257     tfo[11] = DX.z();
2258   }
2259 
2260   else {
2261     bool rotation = false;
2262     myLine LINE;
2263     double ANGLE = 0;
2264 
2265     count = 0;
2266     rotation = true;
2267     std::vector<SPoint3> mps, mpt;
2268     for(auto it = vs2vt.begin(); it != vs2vt.end(); ++it) {
2269       GVertex *vs = it->first;
2270       GVertex *vt = it->second;
2271       mps.push_back(SPoint3(vs->x(), vs->y(), vs->z()));
2272       mpt.push_back(SPoint3(vt->x(), vt->y(), vt->z()));
2273     }
2274     mean_plane mean_source, mean_target;
2275     computeMeanPlaneSimple(mps, mean_source);
2276     computeMeanPlaneSimple(mpt, mean_target);
2277 
2278     myPlane PLANE_SOURCE(SPoint3(mean_source.x, mean_source.y, mean_source.z),
2279                          SVector3(mean_source.a, mean_source.b, mean_source.c));
2280     myPlane PLANE_TARGET(SPoint3(mean_target.x, mean_target.y, mean_target.z),
2281                          SVector3(mean_target.a, mean_target.b, mean_target.c));
2282 
2283     LINE = myLine(PLANE_SOURCE, PLANE_TARGET);
2284 
2285     // LINE is the axis of rotation
2286     // let us compute the angle of rotation
2287     count = 0;
2288     for(auto it = vs2vt.begin(); it != vs2vt.end(); ++it) {
2289       GVertex *vs = it->first;
2290       GVertex *vt = it->second;
2291       // project both points on the axis: that should be the same point !
2292       SPoint3 ps = SPoint3(vs->x(), vs->y(), vs->z());
2293       SPoint3 pt = SPoint3(vt->x(), vt->y(), vt->z());
2294       SPoint3 p_ps = LINE.orthogonalProjection(ps);
2295       SPoint3 p_pt = LINE.orthogonalProjection(pt);
2296       SVector3 dist1 = ps - pt;
2297       SVector3 dist2 = p_ps - p_pt;
2298       if(dist1.norm() > CTX::instance()->geom.tolerance) {
2299         if(dist2.norm() > 1.e-8 * dist1.norm()) { rotation = false; }
2300         SVector3 t1 = ps - p_ps;
2301         SVector3 t2 = pt - p_pt;
2302         if(t1.norm() > 1.e-8 * dist1.norm()) {
2303           if(count == 0)
2304             ANGLE = myAngle(t1, t2, LINE.t);
2305           else {
2306             double ANGLE2 = myAngle(t1, t2, LINE.t);
2307             if(fabs(ANGLE2 - ANGLE) > 1.e-8) { rotation = false; }
2308           }
2309           count++;
2310         }
2311       }
2312     }
2313 
2314     if(rotation) {
2315       Msg::Info("Periodic mesh rotation found: axis (%g,%g,%g) point (%g %g "
2316                 "%g) angle %g",
2317                 LINE.t.x(), LINE.t.y(), LINE.t.z(), LINE.p.x(), LINE.p.y(),
2318                 LINE.p.z(), ANGLE * 180 / M_PI);
2319 
2320       double ux = LINE.t.x();
2321       double uy = LINE.t.y();
2322       double uz = LINE.t.z();
2323 
2324       tfo[0 * 4 + 0] = cos(ANGLE) + ux * ux * (1. - cos(ANGLE));
2325       tfo[0 * 4 + 1] = ux * uy * (1. - cos(ANGLE)) - uz * sin(ANGLE);
2326       tfo[0 * 4 + 2] = ux * uz * (1. - cos(ANGLE)) + uy * sin(ANGLE);
2327 
2328       tfo[1 * 4 + 0] = ux * uy * (1. - cos(ANGLE)) + uz * sin(ANGLE);
2329       tfo[1 * 4 + 1] = cos(ANGLE) + uy * uy * (1. - cos(ANGLE));
2330       tfo[1 * 4 + 2] = uy * uz * (1. - cos(ANGLE)) - ux * sin(ANGLE);
2331 
2332       tfo[2 * 4 + 0] = ux * uz * (1. - cos(ANGLE)) - uy * sin(ANGLE);
2333       tfo[2 * 4 + 1] = uy * uz * (1. - cos(ANGLE)) + ux * sin(ANGLE);
2334       tfo[2 * 4 + 2] = cos(ANGLE) + uz * uz * (1. - cos(ANGLE));
2335 
2336       double origin[3] = {LINE.p.x(), LINE.p.y(), LINE.p.z()};
2337 
2338       for(int i = 0; i < 3; i++) tfo[i * 4 + 3] = origin[i];
2339       for(int i = 0; i < 3; i++)
2340         for(int j = 0; j < 3; j++) tfo[i * 4 + 3] -= tfo[i * 4 + j] * origin[j];
2341       for(int i = 0; i < 3; i++) tfo[12 + i] = 0;
2342       tfo[15] = 1;
2343     }
2344     else {
2345       Msg::Error("Only rotations or translations can currently be computed "
2346                  "automatically for periodic faces: face %d not meshed",
2347                  tag());
2348       return;
2349     }
2350   }
2351 
2352   // --- now check and encode the transformation
2353   // --- including for edges and vertices
2354 
2355   setMeshMaster(master, tfo);
2356 }
2357 
addElement(int type,MElement * e)2358 void GFace::addElement(int type, MElement *e)
2359 {
2360   switch(type) {
2361   case TYPE_TRI: addTriangle(reinterpret_cast<MTriangle *>(e)); break;
2362   case TYPE_QUA: addQuadrangle(reinterpret_cast<MQuadrangle *>(e)); break;
2363   case TYPE_POLYG: addPolygon(reinterpret_cast<MPolygon *>(e)); break;
2364   default: Msg::Error("Trying to add unsupported element in face");
2365   }
2366 }
2367 
removeElement(int type,MElement * e)2368 void GFace::removeElement(int type, MElement *e)
2369 {
2370   switch(type) {
2371   case TYPE_TRI: {
2372     auto it = std::find(triangles.begin(), triangles.end(),
2373                         reinterpret_cast<MTriangle *>(e));
2374     if(it != triangles.end()) triangles.erase(it);
2375   } break;
2376   case TYPE_QUA: {
2377     auto it = std::find(quadrangles.begin(), quadrangles.end(),
2378                         reinterpret_cast<MQuadrangle *>(e));
2379     if(it != quadrangles.end()) quadrangles.erase(it);
2380   } break;
2381   case TYPE_POLYG: {
2382     auto it = std::find(polygons.begin(), polygons.end(),
2383                         reinterpret_cast<MPolygon *>(e));
2384     if(it != polygons.end()) polygons.erase(it);
2385   } break;
2386   default: Msg::Error("Trying to remove unsupported element in face");
2387   }
2388 }
2389 
reorder(const int elementType,const std::vector<std::size_t> & ordering)2390 bool GFace::reorder(const int elementType,
2391                     const std::vector<std::size_t> &ordering)
2392 {
2393   if(triangles.size() != 0) {
2394     if(triangles.front()->getTypeForMSH() == elementType) {
2395       if(ordering.size() != triangles.size()) return false;
2396 
2397       for(auto it = ordering.begin(); it != ordering.end(); ++it) {
2398         if(*it >= triangles.size()) return false;
2399       }
2400 
2401       std::vector<MTriangle *> newTrianglesOrder(triangles.size());
2402       for(std::size_t i = 0; i < ordering.size(); i++) {
2403         newTrianglesOrder[i] = triangles[ordering[i]];
2404       }
2405       triangles = std::move(newTrianglesOrder);
2406       return true;
2407     }
2408   }
2409 
2410   if(quadrangles.size() != 0) {
2411     if(quadrangles.front()->getTypeForMSH() == elementType) {
2412       if(ordering.size() != quadrangles.size()) return false;
2413 
2414       for(auto it = ordering.begin(); it != ordering.end(); ++it) {
2415         if(*it >= quadrangles.size()) return false;
2416       }
2417 
2418       std::vector<MQuadrangle *> newQuadranglesOrder(quadrangles.size());
2419       for(std::size_t i = 0; i < ordering.size(); i++) {
2420         newQuadranglesOrder[i] = quadrangles[ordering[i]];
2421       }
2422       quadrangles = std::move(newQuadranglesOrder);
2423       return true;
2424     }
2425   }
2426 
2427   if(polygons.size() != 0) {
2428     if(polygons.front()->getTypeForMSH() == elementType) {
2429       if(ordering.size() != polygons.size()) return false;
2430 
2431       for(auto it = ordering.begin(); it != ordering.end(); ++it) {
2432         if(*it >= polygons.size()) return false;
2433       }
2434 
2435       std::vector<MPolygon *> newPolygonsOrder(polygons.size());
2436       for(std::size_t i = 0; i < ordering.size(); i++) {
2437         newPolygonsOrder[i] = polygons[ordering[i]];
2438       }
2439       polygons = std::move(newPolygonsOrder);
2440       return true;
2441     }
2442   }
2443 
2444   return false;
2445 }
2446 
alignElementsWithMaster()2447 void GFace::alignElementsWithMaster()
2448 {
2449   GEntity *master = getMeshMaster();
2450 
2451   if(master != this) {
2452     std::set<MFace, MFaceLessThan> srcFaces;
2453 
2454     for(std::size_t i = 0; i < master->getNumMeshElements(); i++) {
2455       MElement *face = master->getMeshElement(i);
2456       std::vector<MVertex *> vtcs;
2457       vtcs.reserve(face->getNumVertices());
2458       for(std::size_t j = 0; j < face->getNumVertices(); j++) {
2459         vtcs.push_back(face->getVertex(j));
2460       }
2461       srcFaces.insert(MFace(vtcs));
2462     }
2463 
2464     for(std::size_t i = 0; i < getNumMeshElements(); i++) {
2465       MElement *face = getMeshElement(i);
2466       std::vector<MVertex *> vtcs;
2467       for(std::size_t j = 0; j < face->getNumVertices(); j++) {
2468         MVertex *tv = face->getVertex(j);
2469         auto cIter = correspondingVertices.find(tv);
2470         if(cIter != correspondingVertices.end()) vtcs.push_back(cIter->second);
2471       }
2472 
2473       MFace mf(vtcs);
2474 
2475       auto sIter = srcFaces.find(mf);
2476 
2477       if(sIter == srcFaces.end()) continue;
2478 
2479       MFace of = *sIter;
2480 
2481       int orientation;
2482       bool swap;
2483       mf.computeCorrespondence(of, orientation, swap);
2484 
2485       switch(face->getNumVertices()) {
2486       case 3: {
2487         auto *tri = dynamic_cast<MTriangle *>(face);
2488         if(tri) tri->reorient(orientation, swap);
2489         break;
2490       }
2491       case 4: {
2492         auto *qua = dynamic_cast<MQuadrangle *>(face);
2493         if(qua) qua->reorient(orientation, swap);
2494         break;
2495       }
2496       }
2497     }
2498   }
2499 }
2500 
isFullyDiscrete()2501 bool GFace::isFullyDiscrete()
2502 {
2503   if(geomType() != GEntity::DiscreteSurface) return false;
2504   auto *df = dynamic_cast<discreteFace *>(this);
2505   if(df && df->haveParametrization()) return false;
2506   std::vector<GEdge *> e = edges();
2507   for(std::size_t i = 0; i < e.size(); i++) {
2508     if(e[i]->geomType() != GEntity::DiscreteCurve) return false;
2509     auto *de = dynamic_cast<discreteEdge *>(e[i]);
2510     if(de && de->haveParametrization()) return false;
2511   }
2512   return true;
2513 }
2514