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 "BackgroundMesh2D.h"
7 #include "BackgroundMeshTools.h"
8 
9 #include "GmshMessage.h"
10 #include "GModel.h"
11 #include "GVertex.h"
12 #include "GEdge.h"
13 #include "GFace.h"
14 #include "MElement.h"
15 #include "MElementOctree.h"
16 #include "MTriangle.h"
17 #include "MVertex.h"
18 #include "Numeric.h"
19 #include "MLine.h"
20 #include "MTriangle.h"
21 #include "Field.h"
22 #include "OS.h"
23 #include "Context.h"
24 #include "meshGFaceOptimize.h"
25 
26 #if defined(HAVE_SOLVER)
27 #include "dofManager.h"
28 #include "laplaceTerm.h"
29 #include "linearSystemCSR.h"
30 #include "linearSystemFull.h"
31 #include "linearSystemPETSc.h"
32 #endif
33 
34 class evalDiffusivityFunction : public simpleFunction<double> {
35 public:
evalDiffusivityFunction(frameFieldBackgroundMesh2D * _bgm,double t=0.95)36   evalDiffusivityFunction(frameFieldBackgroundMesh2D *_bgm, double t = 0.95)
37     : bgm(_bgm), threshold(t){};
operator ()(double u,double v,double w) const38   double operator()(double u, double v, double w) const
39   {
40     return ((bgm->get_smoothness(u, v) >= threshold) ? 1. : 1.e-3);
41   }
42 
43 private:
44   frameFieldBackgroundMesh2D *bgm;
45   const double threshold;
46 };
47 
48 // TODO: move this fct ???
49 /* applies rotations of amplitude pi to set the
50    angle in the first quadrant (in [0,pi/2[ ) */
normalizeAngle(double & angle)51 void normalizeAngle(double &angle)
52 {
53   if(angle < 0)
54     while(angle < 0) angle += (M_PI * .5);
55   else if(angle >= M_PI * .5)
56     while(angle >= M_PI * .5) angle -= (M_PI * .5);
57 }
58 
create_face_mesh()59 void backgroundMesh2D::create_face_mesh()
60 {
61   GFace *face = dynamic_cast<GFace *>(gf);
62   if(!face) {
63     Msg::Error("Entity is not a face in background mesh");
64     return;
65   }
66 
67   quadsToTriangles(face, 100000);
68 
69   // storing the initial mesh from GFace
70   tempTR.clear();
71 
72   for(unsigned int i = 0; i < face->triangles.size(); i++)
73     tempTR.push_back(new MTriangle(face->triangles[i]->getVertex(0),
74                                    face->triangles[i]->getVertex(1),
75                                    face->triangles[i]->getVertex(2)));
76 
77   // avoid computing curvatures on the fly : only on the
78   // BGM computes once curvatures at each node
79   //  Disable curvature control
80   int CurvControl = CTX::instance()->mesh.lcFromCurvature;
81   CTX::instance()->mesh.lcFromCurvature = 0;
82   //  Create a background mesh
83   bowyerWatson(face, 4000);
84   //  Re-enable curv control if asked
85   CTX::instance()->mesh.lcFromCurvature = CurvControl;
86 
87   // creates a copy of GFace's vertices and triangles
88   create_mesh_copy();
89 }
90 
getOctree()91 MElementOctree *backgroundMesh2D::getOctree()
92 {
93   if(!octree) {
94     Msg::Debug("Rebuilding BackgroundMesh element octree");
95     octree = new MElementOctree(elements);
96   }
97   return octree;
98 }
99 
getElement(unsigned int i) const100 const MElement *backgroundMesh2D::getElement(unsigned int i) const
101 {
102   return elements[i];
103 }
104 
reset(bool erase_2D3D)105 void backgroundMesh2D::reset(bool erase_2D3D)
106 {
107   unset();
108 
109   // create face mesh - this was previously done for old backgroundmesh in
110   // buildBackGroundMesh !
111   create_face_mesh();
112 
113   // computes the mesh sizes at nodes
114   if(CTX::instance()->mesh.lcFromPoints) {
115     computeSizeField();
116   }
117   else
118     for(std::map<MVertex const *const, MVertex *>::iterator itv2 =
119           _2Dto3D.begin();
120         itv2 != _2Dto3D.end(); ++itv2)
121       sizeField[itv2->first] = CTX::instance()->mesh.lcMax;
122 
123   // ensure that other criteria are fullfilled
124   updateSizes();
125 
126   if(erase_2D3D) {
127     _3Dto2D.clear();
128     _2Dto3D.clear();
129   }
130 }
131 
unset()132 void backgroundMesh2D::unset()
133 {
134   for(unsigned int i = 0; i < vertices.size(); i++) delete vertices[i];
135   for(unsigned int i = 0; i < getNumMeshElements(); i++) delete elements[i];
136   if(octree) delete octree;
137   octree = NULL;
138 }
139 
create_mesh_copy()140 void backgroundMesh2D::create_mesh_copy()
141 {
142   // TODO: useful to extend it to other elements ???
143   // std::set<SPoint2> myBCNodes;
144   GFace *face = dynamic_cast<GFace *>(gf);
145   if(!face) {
146     Msg::Error("Entity is not a face in background mesh");
147     return;
148   }
149   for(unsigned int i = 0; i < face->triangles.size(); i++) {
150     MTriangle *e = face->triangles[i];
151     MVertex *news[3];
152     for(int j = 0; j < 3; j++) {
153       MVertex *v = e->getVertex(j);
154       std::map<MVertex const *const, MVertex *>::iterator it = _3Dto2D.find(v);
155       MVertex *newv = 0;
156       if(it == _3Dto2D.end()) {
157         SPoint2 p;
158         reparamMeshVertexOnFace(v, face, p);
159         newv =
160           new MVertex(p.x(), p.y(), 0.0); // creates new vertex with xyz= u,v,0.
161         vertices.push_back(newv);
162         _3Dto2D[v] = newv;
163         _2Dto3D[newv] = v;
164         // if(v->onWhat()->dim()<2) myBCNodes.insert(p);
165       }
166       else {
167         newv = it->second;
168       }
169       news[j] = newv;
170     }
171     elements.push_back(new MTriangle(news[0], news[1], news[2]));
172   }
173 }
174 
get_GPoint_from_MVertex(const MVertex * v) const175 GPoint backgroundMesh2D::get_GPoint_from_MVertex(const MVertex *v) const
176 {
177   GFace *face = dynamic_cast<GFace *>(gf);
178   if(!face) {
179     Msg::Error("Entity is not a face in background mesh");
180     return GPoint();
181   }
182   return face->point(SPoint2(v->x(), v->y()));
183 }
184 
backgroundMesh2D(GFace * _gf,bool erase_2D3D)185 backgroundMesh2D::backgroundMesh2D(GFace *_gf, bool erase_2D3D)
186   : BGMBase(2, _gf), sizeFactor(1.)
187 {
188   reset(erase_2D3D);
189 
190   if(erase_2D3D) {
191     // now, the new mesh has been copied in local in backgroundMesh2D, deleting
192     // the mesh from GFace, back to the previous one !
193     GFace *face = dynamic_cast<GFace *>(gf);
194     if(!face)
195       Msg::Error("Entity is not a face in background mesh");
196     else
197       face->triangles = tempTR;
198   }
199 }
200 
~backgroundMesh2D()201 backgroundMesh2D::~backgroundMesh2D() { unset(); }
202 
propagateValues(DoubleStorageType & dirichlet,simpleFunction<double> & eval_diffusivity,bool in_parametric_plane)203 void backgroundMesh2D::propagateValues(DoubleStorageType &dirichlet,
204                                        simpleFunction<double> &eval_diffusivity,
205                                        bool in_parametric_plane)
206 {
207 #if defined(HAVE_SOLVER)
208 #if defined(HAVE_PETSC)
209   linearSystemPETSc<double> *_lsys = new linearSystemPETSc<double>;
210 #elif defined(HAVE_GMM)
211   linearSystemCSRGmm<double> *_lsys = new linearSystemCSRGmm<double>;
212   _lsys->setGmres(1);
213 #else
214    linearSystemFull<double> *_lsys = new linearSystemFull<double>;
215 #endif
216 
217   dofManager<double> myAssembler(_lsys);
218 
219   // fix boundary conditions
220   for(DoubleStorageType::iterator itv = dirichlet.begin();
221       itv != dirichlet.end(); ++itv) {
222     myAssembler.fixVertex(itv->first, 0, 1, itv->second);
223   }
224 
225   // Number vertices
226   std::set<MVertex *> vs;
227   GFace *face = dynamic_cast<GFace *>(gf);
228   if(!face) {
229     Msg::Error("Entity is not a face in background mesh");
230     delete _lsys;
231     return;
232   }
233   for(unsigned int k = 0; k < face->triangles.size(); k++)
234     for(int j = 0; j < 3; j++) vs.insert(face->triangles[k]->getVertex(j));
235   for(unsigned int k = 0; k < face->quadrangles.size(); k++)
236     for(int j = 0; j < 4; j++) vs.insert(face->quadrangles[k]->getVertex(j));
237 
238   std::map<MVertex *, SPoint3> theMap;
239   if(in_parametric_plane) {
240     for(std::set<MVertex *>::iterator it = vs.begin(); it != vs.end(); ++it) {
241       SPoint2 p;
242       reparamMeshVertexOnFace(*it, face, p);
243       theMap[*it] = SPoint3((*it)->x(), (*it)->y(), (*it)->z());
244       (*it)->setXYZ(p.x(), p.y(), 0.0);
245     }
246   }
247 
248   for(std::set<MVertex *>::iterator it = vs.begin(); it != vs.end(); ++it)
249     myAssembler.numberVertex(*it, 0, 1);
250 
251   // Assemble
252   laplaceTerm l(0, 1, &eval_diffusivity);
253   for(unsigned int k = 0; k < face->triangles.size(); k++) {
254     MTriangle *t = face->triangles[k];
255     SElement se(t);
256     l.addToMatrix(myAssembler, &se);
257   }
258 
259   // Solve
260   if(myAssembler.sizeOfR()) {
261     _lsys->systemSolve();
262   }
263 
264   // save solution
265   for(std::set<MVertex *>::iterator it = vs.begin(); it != vs.end(); ++it) {
266     myAssembler.getDofValue(*it, 0, 1, dirichlet[*it]);
267   }
268 
269   if(in_parametric_plane) {
270     for(std::set<MVertex *>::iterator it = vs.begin(); it != vs.end(); ++it) {
271       SPoint3 p = theMap[(*it)];
272       (*it)->setXYZ(p.x(), p.y(), p.z());
273     }
274   }
275   delete _lsys;
276 #endif
277 }
278 
computeSizeField()279 void backgroundMesh2D::computeSizeField()
280 {
281   GFace *face = dynamic_cast<GFace *>(gf);
282   if(!face) {
283     Msg::Error("Entity is not a face in background mesh");
284     return;
285   }
286 
287   std::vector<GEdge *> const &e = face->edges();
288   std::vector<GEdge *>::const_iterator it = e.begin();
289   DoubleStorageType sizes;
290 
291   for(; it != e.end(); ++it) {
292     if(!(*it)->isSeam(face)) {
293       for(unsigned int i = 0; i < (*it)->lines.size(); i++) {
294         MVertex *v1 = (*it)->lines[i]->getVertex(0);
295         MVertex *v2 = (*it)->lines[i]->getVertex(1);
296         if(v1 != v2) {
297           double d = std::sqrt((v1->x() - v2->x()) * (v1->x() - v2->x()) +
298                                (v1->y() - v2->y()) * (v1->y() - v2->y()) +
299                                (v1->z() - v2->z()) * (v1->z() - v2->z()));
300           for(int k = 0; k < 2; k++) {
301             MVertex *v = (*it)->lines[i]->getVertex(k);
302             DoubleStorageType::iterator itv = sizes.find(v);
303             if(itv == sizes.end())
304               sizes[v] = std::log(d);
305             else
306               itv->second = 0.5 * (itv->second + std::log(d));
307           }
308         }
309       }
310     }
311   }
312 
313   simpleFunction<double> ONE(1.0);
314   propagateValues(sizes, ONE);
315 
316   std::map<MVertex const *const, MVertex *>::iterator itv2 = _2Dto3D.begin();
317   for(; itv2 != _2Dto3D.end(); ++itv2) {
318     MVertex const *const v_2D = itv2->first;
319     MVertex *v_3D = itv2->second;
320     sizeField[v_2D] = std::exp(sizes[v_3D]);
321   }
322 }
323 
myAngle(const SVector3 & a,const SVector3 & b,const SVector3 & d)324 inline double myAngle(const SVector3 &a, const SVector3 &b, const SVector3 &d)
325 {
326   double cosTheta = dot(a, b);
327   double sinTheta = dot(crossprod(a, b), d);
328   return std::atan2(sinTheta, cosTheta);
329 }
330 
updateSizes()331 void backgroundMesh2D::updateSizes()
332 {
333   DoubleStorageType::iterator itv = sizeField.begin();
334   for(; itv != sizeField.end(); ++itv) {
335     SPoint2 p;
336     MVertex const *const v = _2Dto3D[itv->first];
337     double lc;
338     if(v->onWhat()->dim() == 0) {
339       lc = sizeFactor * BGM_MeshSize(v->onWhat(), 0, 0, v->x(), v->y(), v->z());
340     }
341     else if(v->onWhat()->dim() == 1) {
342       double u;
343       v->getParameter(0, u);
344       lc = sizeFactor * BGM_MeshSize(v->onWhat(), u, 0, v->x(), v->y(), v->z());
345     }
346     else {
347       GFace *face = dynamic_cast<GFace *>(gf);
348       if(!face) {
349         Msg::Error("Entity is not a face in background mesh");
350         return;
351       }
352       reparamMeshVertexOnFace(v, face, p);
353       lc =
354         sizeFactor * BGM_MeshSize(face, p.x(), p.y(), v->x(), v->y(), v->z());
355     }
356     // printf("2D -- %g %g 3D -- %g %g\n",p.x(),p.y(),v->x(),v->y());
357     itv->second = std::min(lc, itv->second);
358     itv->second =
359       std::max(itv->second, sizeFactor * CTX::instance()->mesh.lcMin);
360     itv->second =
361       std::min(itv->second, sizeFactor * CTX::instance()->mesh.lcMax);
362   }
363   // do not allow large variations in the size field
364   // (Int. J. Numer. Meth. Engng. 43, 1143-1165 (1998) MESH GRADATION
365   // CONTROL, BOROUCHAKI, HECHT, FREY)
366   std::set<MEdge, MEdgeLessThan> edges;
367   for(unsigned int i = 0; i < getNumMeshElements(); i++) {
368     for(int j = 0; j < getElement(i)->getNumEdges(); j++) {
369       edges.insert(getElement(i)->getEdge(j));
370     }
371   }
372   const double _beta = 1.3;
373   for(int i = 0; i < 0; i++) {
374     std::set<MEdge, MEdgeLessThan>::iterator it = edges.begin();
375     for(; it != edges.end(); ++it) {
376       MVertex *v0 = it->getVertex(0);
377       MVertex *v1 = it->getVertex(1);
378       MVertex *V0 = _2Dto3D[v0];
379       MVertex *V1 = _2Dto3D[v1];
380       DoubleStorageType::iterator s0 = sizeField.find(V0);
381       DoubleStorageType::iterator s1 = sizeField.find(V1);
382       if(s0->second < s1->second)
383         s1->second = std::min(s1->second, _beta * s0->second);
384       else
385         s0->second = std::min(s0->second, _beta * s1->second);
386     }
387   }
388 }
389 
frameFieldBackgroundMesh2D(GFace * _gf)390 frameFieldBackgroundMesh2D::frameFieldBackgroundMesh2D(GFace *_gf)
391   : backgroundMesh2D(_gf, false)
392 {
393   reset();
394 
395   // now, the new mesh has been copied in local in backgroundMesh2D, deleting
396   // the mesh from GFace, back to the previous one !
397   GFace *face = dynamic_cast<GFace *>(gf);
398   if(!face)
399     Msg::Error("Entity is not a face in background mesh");
400   else
401     face->triangles = tempTR;
402 }
403 
~frameFieldBackgroundMesh2D()404 frameFieldBackgroundMesh2D::~frameFieldBackgroundMesh2D() {}
405 
reset(bool erase_2D3D)406 void frameFieldBackgroundMesh2D::reset(bool erase_2D3D)
407 {
408   // computes cross field
409   simpleFunction<double> ONE(1.0);
410   computeCrossField(ONE);
411   computeSmoothness();
412 
413   //  evalDiffusivityFunction eval_diff(this);
414   //  exportSmoothness("smoothness_iter_0.pos");
415   //  for (int i=1;i<30;i++){
416   //    computeCrossField(eval_diff);
417   //    computeSmoothness();
418   //
419   //    stringstream ss;
420   //    ss << "smoothness_iter_" << i << ".pos";
421   //    exportSmoothness(ss.str());
422   //
423   //    stringstream sscf;
424   //    sscf << "crossfield_iter_" << i << ".pos";
425   //    exportCrossField(sscf.str());
426   //  }
427 
428   if(erase_2D3D) {
429     _3Dto2D.clear();
430     _2Dto3D.clear();
431   }
432 }
433 
get_smoothness(MVertex * v)434 double frameFieldBackgroundMesh2D::get_smoothness(MVertex *v)
435 {
436   return get_nodal_value(v, smoothness);
437 }
438 
get_smoothness(double u,double v)439 double frameFieldBackgroundMesh2D::get_smoothness(double u, double v)
440 {
441   return get_field_value(u, v, 0., smoothness);
442 }
443 
angle(MVertex * v)444 double frameFieldBackgroundMesh2D::angle(MVertex *v)
445 {
446   return get_nodal_value(v, angles);
447 }
448 
angle(double u,double v)449 double frameFieldBackgroundMesh2D::angle(double u, double v)
450 {
451   MElement *e = const_cast<MElement *>(findElement(u, v));
452   if(!e) return -1000.0;
453   std::vector<double> val = get_nodal_values(e, angles);
454   std::vector<double> element_uvw = get_element_uvw_from_xyz(e, u, v, 0.);
455   std::vector<double> cosvalues(e->getNumVertices()),
456     sinvalues(e->getNumVertices());
457   for(std::size_t i = 0; i < e->getNumVertices(); i++) {
458     cosvalues[i] = std::cos(4 * val[i]);
459     sinvalues[i] = std::sin(4 * val[i]);
460   }
461   double cos4 = e->interpolate(&cosvalues[0], element_uvw[0], element_uvw[1],
462                                element_uvw[2], 1, order);
463   double sin4 = e->interpolate(&sinvalues[0], element_uvw[0], element_uvw[1],
464                                element_uvw[2], 1, order);
465   double a = std::atan2(sin4, cos4) / 4.0;
466   normalizeAngle(a);
467   return a;
468 }
469 
computeCrossField(simpleFunction<double> & eval_diffusivity)470 void frameFieldBackgroundMesh2D::computeCrossField(
471   simpleFunction<double> &eval_diffusivity)
472 {
473   angles.clear();
474 
475   DoubleStorageType _cosines4, _sines4;
476 
477   GFace *face = dynamic_cast<GFace *>(gf);
478   if(!face) {
479     Msg::Error("Entity is not a face in background mesh");
480     return;
481   }
482   std::vector<GEdge *> const &e = face->edges();
483   std::vector<GEdge *>::const_iterator it = e.begin();
484 
485   for(; it != e.end(); ++it) {
486     if(!(*it)->isSeam(face)) {
487       for(unsigned int i = 0; i < (*it)->lines.size(); i++) {
488         MVertex *v[2];
489         v[0] = (*it)->lines[i]->getVertex(0);
490         v[1] = (*it)->lines[i]->getVertex(1);
491         SPoint2 p1, p2;
492         reparamMeshEdgeOnFace(v[0], v[1], face, p1, p2);
493         Pair<SVector3, SVector3> der = face->firstDer((p1 + p2) * .5);
494         SVector3 t1 = der.first();
495         SVector3 t2 = der.second();
496         SVector3 n = crossprod(t1, t2);
497         n.normalize();
498         SVector3 d1(v[1]->x() - v[0]->x(), v[1]->y() - v[0]->y(),
499                     v[1]->z() - v[0]->z());
500         t1.normalize();
501         d1.normalize();
502         double _angle = myAngle(t1, d1, n);
503         normalizeAngle(_angle);
504         for(int i = 0; i < 2; i++) {
505           DoubleStorageType::iterator itc = _cosines4.find(v[i]);
506           DoubleStorageType::iterator its = _sines4.find(v[i]);
507           if(itc != _cosines4.end()) {
508             itc->second = 0.5 * (itc->second + std::cos(4 * _angle));
509             its->second = 0.5 * (its->second + std::sin(4 * _angle));
510           }
511           else {
512             _cosines4[v[i]] = std::cos(4 * _angle);
513             _sines4[v[i]] = std::sin(4 * _angle);
514           }
515         }
516       }
517     }
518   }
519 
520   propagateValues(_cosines4, eval_diffusivity, false);
521   propagateValues(_sines4, eval_diffusivity, false);
522 
523   std::map<MVertex const *const, MVertex *>::iterator itv2 = _2Dto3D.begin();
524   for(; itv2 != _2Dto3D.end(); ++itv2) {
525     MVertex const *const v_2D = itv2->first;
526     MVertex *v_3D = itv2->second;
527     double angle = std::atan2(_sines4[v_3D], _cosines4[v_3D]) / 4.0;
528     normalizeAngle(angle);
529     angles[v_2D] = angle;
530   }
531 }
532 
eval_crossfield(double u,double v,STensor3 & cf)533 void frameFieldBackgroundMesh2D::eval_crossfield(double u, double v,
534                                                  STensor3 &cf)
535 {
536   double quadAngle = angle(u, v);
537   Pair<SVector3, SVector3> dirs =
538     compute_crossfield_directions(u, v, quadAngle);
539   SVector3 n = crossprod(dirs.first(), dirs.second());
540 
541   for(int i = 0; i < 3; i++) {
542     cf(i, 0) = dirs.first()[i];
543     cf(i, 1) = dirs.second()[i];
544     cf(i, 2) = n[i];
545   }
546 
547   //  SVector3 t1,t2,n;
548   //  GFace *face = dynamic_cast<GFace*>(gf);
549   //  Pair<SVector3, SVector3> der = face->firstDer(SPoint2(u,v));
550   //  SVector3 s1 = der.first();
551   //  SVector3 s2 = der.second();
552   //  n = crossprod(s1,s2);
553   //  n.normalize();
554   //  s1.normalize();
555   //  s2=crossprod(n,s1);
556   //  t1 = s1 * cos(quadAngle) + s2 * sin(quadAngle) ;
557   //  t1.normalize();
558   //  t2 = crossprod(n,t1);
559   //  for (int i=0;i<3;i++){
560   //    cf(i,0) = t1[i];
561   //    cf(i,1) = t2[i];
562   //    cf(i,2) = n[i];
563   //  }
564 }
565 
eval_crossfield(MVertex * vert,STensor3 & cf)566 void frameFieldBackgroundMesh2D::eval_crossfield(MVertex *vert, STensor3 &cf)
567 {
568   SPoint2 parampoint;
569   GFace *face = dynamic_cast<GFace *>(gf);
570   if(!face) {
571     Msg::Error("Entity is not a face in background mesh");
572     return;
573   }
574   reparamMeshVertexOnFace(vert, face, parampoint);
575   return eval_crossfield(parampoint[0], parampoint[1], cf);
576 }
577 
computeSmoothness()578 void frameFieldBackgroundMesh2D::computeSmoothness()
579 {
580   smoothness.clear();
581 
582   // build vertex -> neighbors table
583   std::multimap<MVertex *, MVertex *> vertex2vertex;
584   for(std::vector<MElement *>::iterator it = beginelements();
585       it != endelements(); it++) {
586     MElement *e = *it;
587     for(std::size_t i = 0; i < e->getNumVertices(); i++) {
588       MVertex *current = e->getVertex(i);
589       for(std::size_t j = 0; j < e->getNumVertices(); j++) {
590         if(i == j) continue;
591         MVertex *neighbor = e->getVertex(j);
592         vertex2vertex.insert(std::make_pair(current, neighbor));
593       }
594     }
595   }
596 
597   // compute smoothness
598   for(std::vector<MVertex *>::iterator it = beginvertices();
599       it != endvertices(); it++) {
600     MVertex *v = *it;
601     double angle_current = angle(v);
602     // compare to all neighbors...
603     std::pair<std::multimap<MVertex *, MVertex *>::iterator,
604               std::multimap<MVertex *, MVertex *>::iterator>
605       range = vertex2vertex.equal_range(v);
606     double minangle, totalangle = 0.;
607     int N = 0;
608     for(std::multimap<MVertex *, MVertex *>::iterator itneighbor = range.first;
609         itneighbor != range.second; itneighbor++) {
610       N++;
611       minangle = M_PI / 2;
612       MVertex *v_nb = itneighbor->second;
613       double angle_nb = angle(v_nb);
614       // angle comparison...
615       minangle = std::min(minangle, std::abs(angle_current - angle_nb));
616       minangle =
617         std::min(minangle, std::abs(angle_current - (angle_nb + M_PI / 2.)));
618       minangle =
619         std::min(minangle, std::abs(angle_current - (angle_nb - M_PI / 2.)));
620       totalangle += minangle;
621     }
622     totalangle /= N;
623     smoothness[v] = 1. - (totalangle / M_PI * 2);
624   }
625 }
626 
exportCrossField(const std::string & filename)627 void frameFieldBackgroundMesh2D::exportCrossField(const std::string &filename)
628 {
629   FILE *f = Fopen(filename.c_str(), "w");
630   if(!f) {
631     Msg::Error("Could not open file '%s'", filename.c_str());
632     return;
633   }
634   fprintf(f, "View \"Cross Field\"{\n");
635   std::vector<double> deltas(2);
636   deltas[0] = 0.;
637   deltas[1] = M_PI;
638 
639   for(std::vector<MVertex *>::iterator it = beginvertices();
640       it != endvertices(); it++) {
641     MVertex *v = *it;
642     double angle_current = angle(v);
643     GPoint p = get_GPoint_from_MVertex(v);
644     for(int i = 0; i < 2; i++) {
645       Pair<SVector3, SVector3> dirs = compute_crossfield_directions(
646         v->x(), v->y(), angle_current + deltas[i]);
647       fprintf(f, "VP(%g,%g,%g) {%g,%g,%g};\n", p.x(), p.y(), p.z(),
648               dirs.first()[0], dirs.first()[1], dirs.first()[2]);
649       fprintf(f, "VP(%g,%g,%g) {%g,%g,%g};\n", p.x(), p.y(), p.z(),
650               dirs.second()[0], dirs.second()[1], dirs.second()[2]);
651     }
652   }
653   fprintf(f, "};\n");
654   fclose(f);
655 }
656 
657 // returns the cross field as a pair of othogonal vectors (NOT in parametric
658 // coordinates, but real 3D coordinates)
659 Pair<SVector3, SVector3>
compute_crossfield_directions(double u,double v,double angle_current)660 frameFieldBackgroundMesh2D::compute_crossfield_directions(double u, double v,
661                                                           double angle_current)
662 {
663   // get the unit normal at that point
664   GFace *face = dynamic_cast<GFace *>(gf);
665   if(!face) {
666     Msg::Error("Entity is not a face in background mesh");
667     return Pair<SVector3, SVector3>(SVector3(), SVector3());
668   }
669 
670   Pair<SVector3, SVector3> der = face->firstDer(SPoint2(u, v));
671   SVector3 s1 = der.first();
672   SVector3 s2 = der.second();
673   SVector3 n = crossprod(s1, s2);
674   n.normalize();
675 
676   SVector3 basis_u = s1;
677   basis_u.normalize();
678   SVector3 basis_v = crossprod(n, basis_u);
679 
680   // normalize vector t1 that is tangent to gf at uv
681   SVector3 t1 =
682     basis_u * std::cos(angle_current) + basis_v * std::sin(angle_current);
683   t1.normalize();
684 
685   // compute the second direction t2 and normalize (t1,t2,n) is the tangent
686   // frame
687   SVector3 t2 = crossprod(n, t1);
688   t2.normalize();
689 
690   return Pair<SVector3, SVector3>(SVector3(t1[0], t1[1], t1[2]),
691                                   SVector3(t2[0], t2[1], t2[2]));
692 }
693 
compute_RK_infos(double u,double v,double x,double y,double z,RK_form & infos)694 bool frameFieldBackgroundMesh2D::compute_RK_infos(double u, double v, double x,
695                                                   double y, double z,
696                                                   RK_form &infos)
697 {
698   // check if point is in domain
699   if(!inDomain(u, v)) return false;
700 
701   // get stored angle
702 
703   double angle_current = angle(u, v);
704 
705   // compute t1,t2: cross field directions
706 
707   // get the unit normal at that point
708   GFace *face = dynamic_cast<GFace *>(gf);
709   if(!face) {
710     Msg::Error("Entity is not a face in background mesh");
711     return false;
712   }
713 
714   Pair<SVector3, SVector3> der = face->firstDer(SPoint2(u, v));
715   SVector3 s1 = der.first();
716   SVector3 s2 = der.second();
717   SVector3 n = crossprod(s1, s2);
718   n.normalize();
719   SVector3 basis_u = s1;
720   basis_u.normalize();
721   SVector3 basis_v = crossprod(n, basis_u);
722   // normalize vector t1 that is tangent to gf at uv
723   SVector3 t1 =
724     basis_u * std::cos(angle_current) + basis_v * std::sin(angle_current);
725   t1.normalize();
726   // compute the second direction t2 and normalize (t1,t2,n) is the tangent
727   // frame
728   SVector3 t2 = crossprod(n, t1);
729   t2.normalize();
730 
731   // get metric
732 
733   double L = size(u, v);
734   infos.metricField = SMetric3(1. / (L * L));
735   FieldManager *fields = gf->model()->getFields();
736   if(fields->getBackgroundField() > 0) {
737     Field *f = fields->get(fields->getBackgroundField());
738     if(!f->isotropic()) {
739       (*f)(x, y, z, infos.metricField, gf);
740     }
741     else {
742       L = (*f)(x, y, z, gf);
743       infos.metricField = SMetric3(1. / (L * L));
744     }
745   }
746   double M = dot(s1, s1);
747   double N = dot(s2, s2);
748   double E = dot(s1, s2);
749   // compute the first fundamental form i.e. the metric tensor at the point
750   // M_{ij} = s_i \cdot s_j
751   double metric[2][2] = {{M, E}, {E, N}};
752 
753   // get sizes
754 
755   double size_1 = std::sqrt(1. / dot(t1, infos.metricField, t1));
756   double size_2 = std::sqrt(1. / dot(t2, infos.metricField, t2));
757 
758   // compute covariant coordinates of t1 and t2 - cross field directions in
759   // parametric domain
760   double covar1[2], covar2[2];
761   // t1 = a s1 + b s2 -->
762   // t1 . s1 = a M + b E
763   // t1 . s2 = a E + b N --> solve the 2 x 2 system
764   // and get covariant coordinates a and b
765   double rhs1[2] = {dot(t1, s1), dot(t1, s2)};
766   bool singular = false;
767   if(!sys2x2(metric, rhs1, covar1)) {
768     Msg::Info("Argh surface %d %g %g %g -- %g %g %g -- %g %g", gf->tag(),
769               s1.x(), s1.y(), s1.z(), s2.x(), s2.y(), s2.z(), size_1, size_2);
770     covar1[1] = 1.0;
771     covar1[0] = 0.0;
772     singular = true;
773   }
774   double rhs2[2] = {dot(t2, s1), dot(t2, s2)};
775   if(!sys2x2(metric, rhs2, covar2)) {
776     Msg::Info("Argh surface %d %g %g %g -- %g %g %g", gf->tag(), s1.x(), s1.y(),
777               s1.z(), s2.x(), s2.y(), s2.z());
778     covar2[0] = 1.0;
779     covar2[1] = 0.0;
780     singular = true;
781   }
782 
783   // transform the sizes with respect to the metric
784   // consider a vector v of size 1 in the parameter plane
785   // its length is sqrt (v^T M v) --> if I want a real size
786   // of size1 in direction v, it should be sqrt(v^T M v) * size1
787   double l1 = std::sqrt(covar1[0] * covar1[0] + covar1[1] * covar1[1]);
788   double l2 = std::sqrt(covar2[0] * covar2[0] + covar2[1] * covar2[1]);
789 
790   covar1[0] /= l1;
791   covar1[1] /= l1;
792   covar2[0] /= l2;
793   covar2[1] /= l2;
794 
795   double size_param_1 = size_1 / std::sqrt(M * covar1[0] * covar1[0] +
796                                            2 * E * covar1[1] * covar1[0] +
797                                            N * covar1[1] * covar1[1]);
798   double size_param_2 = size_2 / std::sqrt(M * covar2[0] * covar2[0] +
799                                            2 * E * covar2[1] * covar2[0] +
800                                            N * covar2[1] * covar2[1]);
801   if(singular) {
802     size_param_1 = size_param_2 = std::min(size_param_1, size_param_2);
803   }
804 
805   // filling form...
806 
807   infos.t1 = t1;
808   infos.h.first = size_1;
809   infos.h.second = size_2;
810   infos.paramh.first = size_param_1;
811   infos.paramh.second = size_param_2;
812   infos.paramt1 = SPoint2(covar1[0], covar1[1]);
813   infos.paramt2 = SPoint2(covar2[0], covar2[1]);
814   infos.angle = angle_current;
815   infos.localsize = L;
816   infos.normal = n;
817 
818   return true;
819 }
820