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 <fstream>
7 #include <iostream>
8 #include <iomanip>
9 #include <algorithm>
10 #include <string>
11 #include <numeric>
12 #include "BackgroundMesh3D.h"
13 #include "MElement.h"
14 #include "GFace.h"
15 #include "GRegion.h"
16 #include "BackgroundMeshManager.h"
17 #include "BackgroundMesh2D.h"
18 #include "MElementOctree.h"
19 #include "MTetrahedron.h"
20 #include "OS.h"
21 
22 #if defined(HAVE_SOLVER)
23 #include "dofManager.h"
24 #include "laplaceTerm.h"
25 #include "linearSystemPETSc.h"
26 #include "linearSystemFull.h"
27 #include "linearSystemCSR.h"
28 #endif
29 
30 /// \return -1 if value is negative, +1 if positive and zero otherwise
signof(T const value)31 template <class T> int signof(T const value)
32 {
33   return (T(0) < value) - (value < T(0));
34 }
35 
36 // TODO: virer les trucs "vertextorank", mettre cette classe-ci :
37 
38 // class evalDiffusivity3DFunction : public simpleFunction<double>{
39 //  public:
40 //    evalDiffusivity3DFunction(frameFieldBackgroundMesh3D *_bgm, double
41 //    t=0.95):bgm(_bgm),threshold(t){}; double operator () (double u, double v,
42 //    double w) const{
43 //      return ((bgm->get_smoothness(u,v) >= threshold) ? 1. : 1.e-3);
44 //    }
45 //  private:
46 //    frameFieldBackgroundMesh2D *bgm;
47 //    const double threshold;
48 //};
49 
backgroundMesh3D(GRegion * _gr)50 backgroundMesh3D::backgroundMesh3D(GRegion *_gr)
51   : BGMBase(3, _gr), debug(false), verbose(false)
52 {
53   computeSizeField();
54 }
55 
~backgroundMesh3D()56 backgroundMesh3D::~backgroundMesh3D() {}
57 
computeSizeField()58 void backgroundMesh3D::computeSizeField()
59 {
60   std::cout << "backgroundMesh3D::computeSizeField() " << std::endl;
61 
62   // fills dirichlet BC
63   GRegion *region = dynamic_cast<GRegion *>(gf);
64   if(!region) {
65     Msg::Error("Entity is not a region in background mesh");
66     return;
67   }
68   std::vector<GFace *> faces = region->faces();
69   MVertex *v;
70   MElement *e;
71 
72   for(std::vector<GFace *>::iterator it = faces.begin(); it != faces.end();
73       it++) { // for all GFace
74     GFace *face = *it;
75     frameFieldBackgroundMesh2D *bgm2d =
76       dynamic_cast<frameFieldBackgroundMesh2D *>(BGMManager::get(face));
77     if(!bgm2d) {
78       Msg::Error("Background mesh is not a 2D frame field");
79       return;
80     }
81     for(unsigned int i = 0; i < face->getNumMeshElements();
82         i++) { // for all elements
83       e = face->getMeshElement(i);
84       if(e->getDim() != 2) continue; // of dim=2
85       for(std::size_t iv = 0; iv < e->getNumVertices(); iv++) {
86         v = e->getVertex(iv);
87         SPoint2 p;
88         reparamMeshVertexOnFace(v, face, p);
89         sizeField[v] = bgm2d->size(p.x(), p.y());
90         //        std::cout << "sets sizefield for vertex " << v << std::endl;
91       }
92     }
93   }
94 
95   // propagate
96   simpleFunction<double> ONE(1.0);
97   propagateValues(sizeField, ONE);
98 
99   //  std::cout << "backgroundMesh3D::size of sizeField: " << sizeField.size()
100   //  << std::endl;
101 }
102 
propagateValues(DoubleStorageType & dirichlet,simpleFunction<double> & eval_diffusivity,bool in_parametric_plane)103 void backgroundMesh3D::propagateValues(DoubleStorageType &dirichlet,
104                                        simpleFunction<double> &eval_diffusivity,
105                                        bool in_parametric_plane)
106 {
107   // same as Size_field::solve()
108   GRegion *gr = dynamic_cast<GRegion *>(gf);
109   if(!gr) {
110     Msg::Error("Entity is not a region in background mesh");
111     return;
112   }
113 
114 #if defined(HAVE_SOLVER)
115 #if defined(HAVE_PETSC)
116   linearSystemPETSc<double> *lsys = new linearSystemPETSc<double>;
117 #elif defined(HAVE_GMM)
118   linearSystemCSRGmm<double> *lsys = new linearSystemCSRGmm<double>;
119   lsys->setGmres(1);
120 #else
121    linearSystemFull<double> *lsys = new linearSystemFull<double>;
122 #endif
123 
124   size_t i;
125   int count;
126   int count2;
127   double val;
128   double volume;
129   std::set<MVertex *> interior;
130   std::set<MVertex *>::iterator it;
131   DoubleStorageType::iterator it2;
132 
133   dofManager<double> assembler(lsys);
134 
135   count = 0;
136   for(it2 = dirichlet.begin(); it2 != dirichlet.end(); it2++) {
137     assembler.fixVertex(it2->first, 0, 1, it2->second);
138     count++;
139   }
140   // printf("\n");
141   // printf("number of boundary vertices = %d\n",count);
142 
143   for(i = 0; i < gr->tetrahedra.size(); i++) {
144     interior.insert(gr->tetrahedra[i]->getVertex(0));
145     interior.insert(gr->tetrahedra[i]->getVertex(1));
146     interior.insert(gr->tetrahedra[i]->getVertex(2));
147     interior.insert(gr->tetrahedra[i]->getVertex(3));
148   }
149 
150   for(it = interior.begin(); it != interior.end(); it++) {
151     it2 = dirichlet.find(*it);
152     if(it2 == dirichlet.end()) {
153       assembler.numberVertex(*it, 0, 1);
154     }
155   }
156 
157   for(i = 0; i < gr->tetrahedra.size(); i++) {
158     gr->tetrahedra[i]->setVolumePositive();
159   }
160 
161   count2 = 0;
162   volume = 0.0;
163   laplaceTerm term(0, 1, &eval_diffusivity);
164   for(i = 0; i < gr->tetrahedra.size(); i++) {
165     SElement se(gr->tetrahedra[i]);
166     term.addToMatrix(assembler, &se);
167     count2++;
168     volume = volume + gr->tetrahedra[i]->getVolume();
169   }
170   // printf("number of tetrahedra = %d\n",count2);
171   // printf("volume = %f\n",volume);
172 
173   if(assembler.sizeOfR()) {
174     lsys->systemSolve();
175   }
176 
177   for(it = interior.begin(); it != interior.end(); it++) {
178     assembler.getDofValue(*it, 0, 1, val);
179     dirichlet.insert(std::pair<MVertex *, double>(*it, val));
180   }
181 
182   delete lsys;
183 #endif
184 }
185 
get_GPoint_from_MVertex(const MVertex * v) const186 GPoint backgroundMesh3D::get_GPoint_from_MVertex(const MVertex *v) const
187 {
188   return GPoint(v->x(), v->y(), v->z(), dynamic_cast<GRegion *>(gf));
189 }
190 
get_nearest_neighbor(const MVertex * v)191 MVertex *backgroundMesh3D::get_nearest_neighbor(const MVertex *v)
192 {
193   double distance;
194   return get_nearest_neighbor(v->x(), v->y(), v->z(), distance);
195 }
196 
get_nearest_neighbor(const double * xyz)197 MVertex *backgroundMesh3D::get_nearest_neighbor(const double *xyz)
198 {
199   double distance;
200   return get_nearest_neighbor(xyz, distance);
201 }
202 
get_nearest_neighbor(double x,double y,double z)203 MVertex *backgroundMesh3D::get_nearest_neighbor(double x, double y, double z)
204 {
205   double distance;
206   return get_nearest_neighbor(x, y, z, distance);
207 }
208 
get_nearest_neighbor(double x,double y,double z,double & distance)209 MVertex *backgroundMesh3D::get_nearest_neighbor(double x, double y, double z,
210                                                 double &distance)
211 {
212   double xyz[3] = {x, y, z};
213   return get_nearest_neighbor(xyz, distance);
214 }
215 
getOctree()216 MElementOctree *backgroundMesh3D::getOctree()
217 {
218   if(!octree) {
219     GRegion *gr = dynamic_cast<GRegion *>(gf);
220     if(!gr) {
221       Msg::Error("Entity is not a region in background mesh");
222       return 0;
223     }
224     Msg::Debug("Rebuilding BackgroundMesh element octree");
225     std::vector<MElement *> copy(gr->tetrahedra.begin(), gr->tetrahedra.end());
226     octree = new MElementOctree(copy);
227   }
228   return octree;
229 }
230 
get_nearest_neighbor(const double * xyz,double & distance)231 MVertex *backgroundMesh3D::get_nearest_neighbor(const double *xyz,
232                                                 double &distance)
233 {
234   // using the octree instead of ANN, faster.
235   MElement *elem = const_cast<MElement *>(findElement(xyz[0], xyz[1], xyz[2]));
236 
237   if(!elem) return NULL;
238 
239   std::vector<MVertex *> candidates(elem->getNumVertices());
240   std::vector<double> distances(elem->getNumVertices());
241   SPoint3 p(xyz[0], xyz[1], xyz[2]);
242   for(std::size_t i = 0; i < elem->getNumVertices(); i++) {
243     MVertex *const v = elem->getVertex(i);
244     candidates[i] = v;
245     distances[i] = p.distance(v->point());
246   }
247   std::vector<double>::iterator itmax =
248     std::max_element(distances.begin(), distances.end());
249   return candidates[std::distance(distances.begin(), itmax)];
250 
251   //  map<double,MVertex*> distances;
252   //  SPoint3 p(xyz[0],xyz[1],xyz[2]);
253   //  for (int i=0;i<elem->getNumVertices();i++){
254   //    MVertex *v = elem->getVertex(i);
255   //    distances.insert(make_pair(p.distance(v->point()),v));
256   //  }
257   //  map<double,MVertex*>::iterator it = distances.begin();
258   //  distance = it->first;
259   //  return it->second;
260 }
261 
262 std::vector<montripletbis> frameFieldBackgroundMesh3D::permutation =
263   std::vector<montripletbis>();
264 
frameFieldBackgroundMesh3D(GRegion * _gr)265 frameFieldBackgroundMesh3D::frameFieldBackgroundMesh3D(GRegion *_gr)
266   : backgroundMesh3D(_gr)
267 {
268   //  readValue("param.dat","SMOOTHCF",smooth_the_crossfield);
269   smooth_the_crossfield = true;
270 
271   // for the different "quaternion" permutations...
272   if(permutation.empty()) {
273     permutation.push_back(montripletbis(1, 2, 3));
274     permutation.push_back(montripletbis(2, -1, 3));
275     permutation.push_back(montripletbis(-1, -2, 3));
276     permutation.push_back(montripletbis(-2, 1, 3));
277     permutation.push_back(montripletbis(2, 1, -3));
278     permutation.push_back(montripletbis(-1, 2, -3));
279     permutation.push_back(montripletbis(-2, -1, -3));
280     permutation.push_back(montripletbis(1, -2, -3));
281   }
282 
283   build_vertex_to_element_table();
284 
285   int max_recursion_level = 1;
286   if((max_recursion_level > 3) || (max_recursion_level < 1)) throw;
287   build_neighbors(max_recursion_level);
288 
289   initiate_ANN_research();
290   initiate_crossfield();
291 
292   if(smooth_the_crossfield) {
293     computeCrossField();
294   }
295   else {
296     computeSmoothnessOnlyFromBoundaries();
297   }
298 }
299 
~frameFieldBackgroundMesh3D()300 frameFieldBackgroundMesh3D::~frameFieldBackgroundMesh3D()
301 {
302 #if defined(HAVE_ANN)
303   if(annTreeBnd) delete annTreeBnd;
304   if(dataPtsBnd) annDeallocPts(dataPtsBnd);
305 #endif
306 }
307 
initiate_ANN_research()308 void frameFieldBackgroundMesh3D::initiate_ANN_research()
309 {
310 #ifdef HAVE_ANN
311   // ANN research for 2D !!!
312   int maxPts = listOfBndVertices.size();
313   dataPtsBnd = annAllocPts(maxPts, 3);
314   int i = 0;
315   MVertex *v;
316   for(std::set<MVertex *>::iterator it = listOfBndVertices.begin();
317       it != listOfBndVertices.end(); it++) {
318     v = *it;
319     for(int k = 0; k < 3; ++k) dataPtsBnd[i][k] = (v->point())[k];
320     ++i;
321   }
322   annTreeBnd = new ANNkd_tree(dataPtsBnd, maxPts, 3);
323 #endif
324   return;
325 }
326 
computeSmoothnessOnlyFromBoundaries()327 void frameFieldBackgroundMesh3D::computeSmoothnessOnlyFromBoundaries()
328 {
329   // this is used when the crossfield is not smoothed !!!
330   // otherwise, the smoothness is computed on the fly while smoothing the cf !
331 
332   smoothness_threshold = 0.;
333 
334   std::set<MVertex const *> neighbors;
335   std::multimap<double, MVertex const *> themap;
336   SVector3 mean_axis(0.);
337   double mean_angle = 0.;
338   std::vector<double> vectorial_smoothness(3);
339 
340   for(vert2elemtype::iterator it_vertex = vert2elem.begin();
341       it_vertex != vert2elem.end(); it_vertex++) { // for all vertices
342     themap.clear();
343     neighbors.clear();
344     MVertex const *current = it_vertex->first;
345     std::pair<graphtype::const_iterator, graphtype::iterator> range =
346       graph.equal_range(current);
347     graphtype::const_iterator itgraph = range.first;
348     for(; itgraph != range.second; itgraph++) { // for all neighbors
349       neighbors.insert(itgraph->second.second);
350     }
351     for(std::set<MVertex const *>::iterator it = neighbors.begin();
352         it != neighbors.end(); it++) {
353       themap.insert(std::make_pair(1., *it));
354     }
355 
356     TensorStorageType::iterator itcurrent = crossField.find(current);
357     STensor3 &ref = itcurrent->second;
358 
359     crossFieldSmoothness[current] =
360       compare_to_neighbors(current->point(), ref, themap.begin(), themap.end(),
361                            mean_axis, mean_angle, vectorial_smoothness);
362   }
363 }
364 
computeCrossField()365 void frameFieldBackgroundMesh3D::computeCrossField()
366 {
367   // TODO: mettre des parametres façon gmsh ???
368   // reading parameters
369   int Niter = 3;
370   double norme_threshold = 1.;
371   double localangle_threshold = 1.;
372 
373   Niter = 40;
374   norme_threshold = 3.e-2;
375   localangle_threshold = 5.e-3;
376   smoothness_threshold = 0.85;
377   double percentage_threshold = 0.98;
378 
379   std::vector<double> scal_prods, signs, angles;
380   STensor3 identity(1.);
381 
382   if(debug) exportCrossField("cf_initial.pos");
383 
384   // initiation
385   std::multimap<double, MVertex const *> rank, rank_temp;
386   std::map<MVertex const *const, bool> vertex_is_still;
387   std::map<MVertex const *const, double> vertex_movement;
388 
389   for(vert2elemtype::iterator it_vertex = vert2elem.begin();
390       it_vertex != vert2elem.end(); it_vertex++) {
391     MVertex const *const current = it_vertex->first;
392 
393     vertex_is_still[current] = current->onWhat()->dim() <= 2;
394 
395     rank.insert(std::make_pair(0., current));
396   }
397 
398   // OLD - NEW COMPARISON
399   std::map<MVertex const *, double> vertex_to_rank;
400   for(vert2elemtype::iterator it_vertex = vert2elem.begin();
401       it_vertex != vert2elem.end(); it_vertex++) { // for all vertices
402     // vertex_to_rank[it_vertex->first] = 0.;
403     vertex_to_rank[it_vertex->first] = 1.;
404     rank.insert(std::make_pair(0., it_vertex->first));
405   }
406   // END OLD - NEW COMPARISON
407 
408   double percentage_not_done = 0.;
409   double norme = 10. * norme_threshold;
410   double norme_inf = 10. * norme_threshold;
411   int iIter = 0;
412   SVector3 mean_axis(0.);
413   double mean_angle = 0.;
414   std::vector<double> vectorial_smoothness(3);
415   //  vector<int> nb_local_iterations;
416 
417   // main smoothing loop
418   while(((norme_inf > norme_threshold) &&
419          (percentage_not_done < percentage_threshold)) &&
420         (iIter < Niter)) { // for maximum Niter iterations, or until convergence
421     //    nb_local_iterations.clear();
422     angles.clear();
423     norme_inf = 0.;
424     int counter_done = 0;
425     int counter_not_done = 0;
426     while(rank.size()) { // for all vertices, i.e. all cavities
427       //    for (vert2elemtype::iterator
428       // it_vertex=vert2elem.begin();it_vertex!=vert2elem.end();it_vertex++)//
429       // for all vertices, i.e. all cavities MVertex *current =
430       // it_vertex->first;
431 
432       MVertex const *const current = rank.begin()->second;
433       rank.erase((rank.begin()));
434 
435       // smooth 3D vertices only
436       if(current->onWhat()->dim() <= 2) {
437         if(verbose)
438           std::cout << "-------------------- discard point "
439                     << current->getNum() << "  on dim "
440                     << current->onWhat()->dim() << "   coord (" << current->x()
441                     << "," << current->y() << "," << current->z() << ")"
442                     << std::endl;
443         continue;
444       }
445 
446       TensorStorageType::iterator itcurrent = crossField.find(current);
447       STensor3 &ref = itcurrent->second;
448       if(verbose)
449         std::cout << "-------------------- working on point "
450                   << current->getNum() << "  on dim "
451                   << current->onWhat()->dim() << "   coord (" << current->x()
452                   << "," << current->y() << "," << current->z() << ")"
453                   << std::endl;
454 
455       std::pair<graphtype::iterator, graphtype::iterator> range =
456         graph.equal_range(current);
457       graphtype::iterator itgraph = range.first;
458       bool all_neighbors_still = true; // if nothing has changed since previous
459                                        // iteration: nothing to do !
460       for(; itgraph != range.second; itgraph++) { // for all neighbors
461         if(vertex_is_still[itgraph->second.second] == false) {
462           all_neighbors_still = false;
463           break;
464         }
465       }
466 
467       // neighbors didn't move, and current didn't move either -> nothing to do
468       // !
469       if(all_neighbors_still && vertex_is_still[current]) {
470         vertex_movement[current] = 0.;
471         rank_temp.insert(std::make_pair(0., current));
472         counter_not_done++;
473         continue;
474       }
475 
476       // OLD - NEW COMPARISON
477       std::multimap<double, MVertex const *> neighbors_to_trust;
478       itgraph = range.first;
479       for(; itgraph != range.second; itgraph++) { // for all neighbors
480         neighbors_to_trust.insert(std::make_pair(
481           vertex_to_rank[itgraph->second.second], itgraph->second.second));
482 
483         //  if (vertex_to_rank[itgraph->second.second] <= 1. /180.*M_PI){
484         //     neighbors_to_trust.insert(make_pair(vertex_to_rank[itgraph->second.second],
485         //                                         itgraph->second.second));
486         //  }
487       }
488 
489       if(!neighbors_to_trust.size()) {
490         rank_temp.insert(std::make_pair(M_PI, current));
491         vertex_to_rank[current] = M_PI;
492         continue;
493       }
494       // END OLD - NEW COMPARISON
495 
496       counter_done++;
497 
498       double angle_to_go;
499       ;
500 
501       int Nlocaliter = 0;
502       STensor3 ref_original(ref);
503 
504       // iterations, convergence of the local cavity...
505       for(; Nlocaliter < 20; Nlocaliter++) {
506         std::multimap<double, MVertex const *>::iterator it_neighbors_to_trust =
507           neighbors_to_trust.begin();
508         crossFieldSmoothness[current] =
509           compare_to_neighbors(current->point(), ref, it_neighbors_to_trust,
510                                neighbors_to_trust.end(), mean_axis, mean_angle,
511                                vectorial_smoothness);
512         //  crossFieldVectorialSmoothness[current] = vectorial_smoothness;
513 
514         // rotating directions to align closest vectors...
515         angle_to_go = mean_angle;
516         ref = apply_rotation(mean_axis, angle_to_go, ref);
517         // std::cout << " iter " << Nlocaliter << ": mean_angle = " <<
518         // mean_angle <<
519         //                std::endl;
520         if(std::abs(mean_angle) < localangle_threshold / 3.0) break;
521       }
522       //  nb_local_iterations.push_back(Nlocaliter+1);
523       if(verbose)
524         std::cout << "iIter " << iIter << ", Nlocaliter = " << Nlocaliter
525                   << std::endl;
526 
527       // computing the total rotation
528       // get_min_rotation_matrix(ref_original, ref, mean_angle,
529       // mean_axis,localangle_threshold,true);
530       get_min_rotation_matrix(ref_original, ref, mean_angle, mean_axis,
531                               localangle_threshold); //,true);
532       norme_inf = std::max(norme_inf, mean_angle);
533       //            std::cout << "  #local_iter=" << Nlocaliter << "  final
534       //            mean_angle = " << mean_angle << "  threshold=" <<
535       //            localangle_threshold << "  condition :" << (mean_angle <=
536       //            localangle_threshold) << std::endl;
537 
538       angles.push_back(mean_angle);
539       vertex_is_still[current] = (mean_angle <= localangle_threshold);
540       vertex_movement[current] = mean_angle;
541       rank_temp.insert(std::make_pair(mean_angle, current));
542       // OLD - NEW COMPARISON
543       // vertex_to_rank[current] = mean_angle;
544       vertex_to_rank[current] = crossFieldSmoothness[current];
545       //      // HACK
546       //        vertex_to_rank[current] = 0.;
547       // END OLD - NEW COMPARISON
548 
549     } // end vertices loop
550 
551     // rank = rank_temp;
552     rank_temp.clear();
553 
554     norme = std::accumulate(angles.begin(), angles.end(), 0.);
555     if(angles.size()) norme = norme / M_PI * 180. / angles.size();
556     percentage_not_done =
557       ((double)counter_not_done) / (counter_done + counter_not_done);
558     std::cout << "   --- iter " << iIter << " NormeInf=" << norme_inf
559               << " mean Angle=" << norme
560               << " counter_not_done=" << counter_not_done
561               << " NotDone (%)=" << (percentage_not_done * 100.) << " %"
562               << std::endl;
563     //    std::cout << "Average number of local iterations: "
564     //          << ((double)std::accumulate(nb_local_iterations.begin(),
565     //               nb_local_iterations.end(),0))/nb_local_iterations.size() <<
566     //               std::endl;
567 
568     // if (debug){
569     double temp = std::log10(Niter);
570     std::stringstream ss;
571     ss << "cf_iter_" << std::setfill('0') << std::setw((int)(std::ceil(temp)))
572        << iIter << ".pos";
573     exportCrossField(ss.str().c_str());
574     //}
575 
576     iIter++;
577   } // end Niter iterations
578 
579   // also computes smoothness for boundary points
580   for(vert2elemtype::iterator it_vertex = vert2elem.begin();
581       it_vertex != vert2elem.end(); it_vertex++) {
582     MVertex const *const current = it_vertex->first;
583     if(current->onWhat()->dim() <= 2) {
584       TensorStorageType::iterator itcurrent = crossField.find(current);
585       STensor3 &ref = itcurrent->second;
586       std::pair<graphtype::iterator, graphtype::iterator> range =
587         graph.equal_range(current);
588       graphtype::iterator itgraph = range.first;
589 
590       std::multimap<double, MVertex const *> neighbors_to_trust;
591       itgraph = range.first;
592       for(; itgraph != range.second; itgraph++) { // for all neighbors
593         neighbors_to_trust.insert(std::make_pair(0., itgraph->second.second));
594       }
595       crossFieldSmoothness[current] = compare_to_neighbors(
596         current->point(), ref, neighbors_to_trust.begin(),
597         neighbors_to_trust.end(), mean_axis, mean_angle, vectorial_smoothness);
598 
599       // crossFieldSmoothness[current] = compare_to_neighbors
600       //     (current->point(), ref, itgraph, range.second, mean_axis,
601       //       mean_angle,vectorial_smoothness);
602       //      crossFieldVectorialSmoothness[current] = vectorial_smoothness;
603     }
604   }
605 }
606 
607 // STensor3 frameFieldBackgroundMesh3D::get_random_cross()const{
608 //  SVector3 vec1(rand()%10000/9999. , rand()%10000/9999. , rand()%10000/9999.
609 //  ); vec1.normalize(); SVector3 ref(1.,0.,0.); SVector3 vec2 =
610 //  crossprod(ref,vec1); vec2.normalize(); SVector3 vec3 = crossprod(vec1,vec2);
611 //  vec3.normalize();
612 //  STensor3 random_cross;
613 //  for (int j=0;j<3;j++){
614 //    random_cross(j,0) = vec1(j);
615 //    random_cross(j,1) = vec2(j);
616 //    random_cross(j,2) = vec3(j);
617 //  }
618 //  return random_cross;
619 //}
620 
initiate_crossfield()621 void frameFieldBackgroundMesh3D::initiate_crossfield()
622 {
623   crossField.clear();
624   MVertex *v;
625 
626   // first, for boundaries :
627   GRegion *gr = dynamic_cast<GRegion *>(gf);
628   if(!gr) {
629     Msg::Error("Entity is not a region in background mesh");
630     return;
631   }
632   std::vector<GFace *> faces = gr->faces();
633   // here, not using the gm2D since we are interested by the new 2D vertices,
634   // not the old (now erased) ones... alternative would be to reset the 2DBGM...
635   for(std::vector<GFace *>::iterator it = faces.begin(); it != faces.end();
636       it++) { // for all GFace
637     GFace *face = *it;
638     frameFieldBackgroundMesh2D *bgm2d =
639       dynamic_cast<frameFieldBackgroundMesh2D *>(BGMManager::get(face));
640     if(!bgm2d) {
641       Msg::Error("Background mesh is not a 2D frame field");
642       return;
643     }
644     // initializing the vertices on the faces
645     for(unsigned int i = 0; i < face->getNumMeshElements();
646         i++) { // for all elements
647       MElement *e = face->getMeshElement(i);
648       if(e->getDim() != 2) continue; // of dim=2
649 
650       for(std::size_t iv = 0; iv < e->getNumVertices(); iv++) {
651         v = e->getVertex(iv);
652 
653         // if already done: continue
654         TensorStorageType::iterator itfind = crossField.find(v);
655         if(itfind != crossField.end()) continue;
656 
657         STensor3 cf;
658         bgm2d->eval_crossfield(v, cf);
659         crossField[v] = cf;
660       }
661     }
662   }
663 
664   // then, for volume :
665   for(unsigned int i = 0; i < gr->getNumMeshElements();
666       i++) { // for all elements
667     MElement *e = gr->getMeshElement(i);
668     if(e->getDim() != 3) continue;
669 
670     for(std::size_t iv = 0; iv < e->getNumVertices(); iv++) {
671       v = e->getVertex(iv);
672 
673       // if not in volume: continue
674       if(v->onWhat()->dim() <= 2) continue;
675       // if already done: continue
676       TensorStorageType::iterator itfind = crossField.find(v);
677       if(itfind != crossField.end()) continue;
678       MVertex *closer_on_bnd = get_nearest_neighbor_on_boundary(v);
679       crossField[v] =
680         crossField[closer_on_bnd]; // prend l'info Bnd (ANN) la plus proche...
681     }
682   }
683 }
684 
685 MVertex *
get_nearest_neighbor_on_boundary(MVertex * v)686 frameFieldBackgroundMesh3D::get_nearest_neighbor_on_boundary(MVertex *v)
687 {
688   double distance;
689   return get_nearest_neighbor_on_boundary(v, distance);
690 }
691 
692 MVertex *
get_nearest_neighbor_on_boundary(MVertex * v,double & distance)693 frameFieldBackgroundMesh3D::get_nearest_neighbor_on_boundary(MVertex *v,
694                                                              double &distance)
695 {
696 #ifdef HAVE_ANN
697   ANNpoint q = annAllocPt(3);
698   for(int k = 0; k < 3; ++k) q[k] = v->point()[k];
699   ANNidxArray nn_idx = new ANNidx[1];
700   ANNdistArray dists = new ANNdist[1];
701   annTreeBnd->annkSearch(q, 1, nn_idx, dists);
702   distance = std::sqrt(dists[0]);
703   int i = nn_idx[0];
704   delete[] nn_idx;
705   delete[] dists;
706   annDeallocPt(q);
707 
708   std::set<MVertex *>::iterator it = listOfBndVertices.begin();
709   std::advance(it, i);
710   return (*it);
711 #else
712   return NULL;
713 #endif
714 }
715 
get_smoothness(double x,double y,double z)716 double frameFieldBackgroundMesh3D::get_smoothness(double x, double y, double z)
717 {
718   return get_field_value(x, y, z, crossFieldSmoothness);
719 };
720 
get_approximate_smoothness(double x,double y,double z)721 double frameFieldBackgroundMesh3D::get_approximate_smoothness(double x,
722                                                               double y,
723                                                               double z)
724 {
725   return crossFieldSmoothness[get_nearest_neighbor(x, y, z)];
726 }
727 
get_approximate_smoothness(MVertex * v)728 double frameFieldBackgroundMesh3D::get_approximate_smoothness(MVertex *v)
729 {
730   return get_approximate_smoothness(v->x(), v->y(), v->z());
731 }
732 
get_smoothness(MVertex * v)733 double frameFieldBackgroundMesh3D::get_smoothness(MVertex *v)
734 {
735   return get_nodal_value(v, crossFieldSmoothness);
736 };
737 
eval_approximate_crossfield(double x,double y,double z,STensor3 & cf)738 void frameFieldBackgroundMesh3D::eval_approximate_crossfield(double x, double y,
739                                                              double z,
740                                                              STensor3 &cf)
741 {
742   cf = crossField[get_nearest_neighbor(x, y, z)];
743 };
744 
eval_approximate_crossfield(const MVertex * vert,STensor3 & cf)745 void frameFieldBackgroundMesh3D::eval_approximate_crossfield(
746   const MVertex *vert, STensor3 &cf)
747 {
748   // check if vertex is ours...
749   TensorStorageType::const_iterator itfind =
750     crossField.find(const_cast<MVertex *>(vert));
751   if(itfind != crossField.end())
752     cf = itfind->second;
753   else // if not, find closest
754     eval_approximate_crossfield(vert->x(), vert->y(), vert->z(), cf);
755 }
756 
get_vectorial_smoothness(double x,double y,double z,SVector3 & cf)757 void frameFieldBackgroundMesh3D::get_vectorial_smoothness(double x, double y,
758                                                           double z,
759                                                           SVector3 &cf)
760 {
761   std::vector<double> res =
762     get_field_value(x, y, z, crossFieldVectorialSmoothness);
763   for(int i = 0; i < 3; i++) cf(i) = res[i];
764 }
765 
get_vectorial_smoothness(const MVertex * vert,SVector3 & cf)766 void frameFieldBackgroundMesh3D::get_vectorial_smoothness(const MVertex *vert,
767                                                           SVector3 &cf)
768 {
769   std::vector<double> res =
770     get_nodal_value(vert, crossFieldVectorialSmoothness);
771   for(int i = 0; i < 3; i++) cf(i) = res[i];
772 }
773 
get_vectorial_smoothness(const int idir,double x,double y,double z)774 double frameFieldBackgroundMesh3D::get_vectorial_smoothness(const int idir,
775                                                             double x, double y,
776                                                             double z)
777 {
778   return get_field_value(x, y, z, crossFieldVectorialSmoothness)[idir];
779 }
780 
get_vectorial_smoothness(const int idir,const MVertex * vert)781 double frameFieldBackgroundMesh3D::get_vectorial_smoothness(const int idir,
782                                                             const MVertex *vert)
783 {
784   return get_nodal_value(vert, crossFieldVectorialSmoothness)[idir];
785 }
786 
build_vertex_to_element_table()787 void frameFieldBackgroundMesh3D::build_vertex_to_element_table()
788 {
789   GRegion *gr = dynamic_cast<GRegion *>(gf);
790   if(!gr) {
791     Msg::Error("Entity is not a region in background mesh");
792     return;
793   }
794   MElement *e;
795   MVertex *v;
796 
797   //    std::cout << "build_vertex_to_element_table nbelem=" <<
798   //    gr->getNumMeshElements() << std::endl;
799 
800   for(unsigned int i = 0; i < gr->getNumMeshElements();
801       i++) { // for all elements
802     e = gr->getMeshElement(i);
803     if(e->getDim() != 3) continue; // of dim=3
804 
805     for(std::size_t iv = 0; iv < e->getNumVertices();
806         iv++) { // for all vertices
807       v = e->getVertex(iv);
808       vert2elem[v].insert(e);
809       elem2vert[e].insert(v);
810       //      std::cout << "node " << iv << " on " << v->onWhat()->dim() <<
811       //      std::endl;
812       if(v->onWhat()->dim() <= 2) {
813         //        std::cout << "adding " << std::endl;
814         listOfBndVertices.insert(v);
815       }
816     }
817   }
818 }
819 
getElement(unsigned int i) const820 const MElement *backgroundMesh3D::getElement(unsigned int i) const
821 {
822   GRegion *gr = dynamic_cast<GRegion *>(gf);
823   if(!gr) {
824     Msg::Error("Entity is not a region in background mesh");
825     return 0;
826   }
827   return gr->getMeshElement(i);
828 }
829 
getNumMeshElements() const830 unsigned int backgroundMesh3D::getNumMeshElements() const
831 {
832   GRegion *gr = dynamic_cast<GRegion *>(gf);
833   if(!gr) {
834     Msg::Error("Entity is not a region in background mesh");
835     return 0;
836   }
837   return gr->getNumMeshElements();
838 }
839 
840 // this function fills the multimap "graph": vertex to direct neighbors, or
841 // indirect neighbors, depending on the "max_recursion_level".
build_neighbors(const int & max_recursion_level)842 void frameFieldBackgroundMesh3D::build_neighbors(const int &max_recursion_level)
843 {
844   std::set<MVertex const *> visited, start;
845   std::set<MElement const *> visited_elements;
846   std::multimap<int, MVertex const *> proximity;
847   // int counter=0;
848 
849   for(vert2elemtype::iterator it_vertex = vert2elem.begin();
850       it_vertex != vert2elem.end(); it_vertex++) { // for all vertices
851     MVertex const *const current_vertex = it_vertex->first;
852     visited.clear();
853     visited_elements.clear();
854     visited.insert(current_vertex);
855     start.clear();
856     start.insert(current_vertex);
857     proximity.clear();
858 
859     get_recursive_neighbors(start, visited, visited_elements, proximity,
860                             max_recursion_level);
861 
862     for(std::multimap<int, MVertex const *>::iterator it1 = proximity.begin();
863         it1 != proximity.end(); it1++) {
864       graph.insert(std::make_pair(current_vertex,
865                                   std::make_pair(it1->first, it1->second)));
866     }
867 
868     //    if (debug){// check
869     //      if (verbose) std::cout << "vertex (" << counter++ << "): " <<
870     //      current_vertex->getNum() << "  connected to " <<
871     //      it_vertex->second.size() << " elements, coord: (" <<
872     //      current_vertex->x() << "," << current_vertex->y() << "," <<
873     //      current_vertex->z() << ")" << std::endl; set<MVertex*> allvertex;
874     //      for (multimap<int,MVertex*>::iterator it1 =
875     //      proximity.begin();it1!=proximity.end();it1++){
876     //        if (verbose) {
877     //          for (int i=0;i<it1->first;i++) std::cout << "  ";
878     //          std::cout << it1->first << "  :  " << it1->second->getNum() <<
879     //          std::endl;
880     //        }
881     //        set<MVertex*>::iterator itfind = allvertex.find(it1->second);
882     //        if (itfind!=allvertex.end()){
883     //          cerr << " ERROR : vertex duplicate !!!" << std::endl;
884     //          throw;
885     //        }
886     //        allvertex.insert(it1->second);
887     //      }
888     //    }// end check
889   }
890 }
891 
get_recursive_neighbors(std::set<MVertex const * > & start,std::set<MVertex const * > & visited,std::set<MElement const * > & visited_elements,std::multimap<int,MVertex const * > & proximity,int max_level,int level)892 void frameFieldBackgroundMesh3D::get_recursive_neighbors(
893   std::set<MVertex const *> &start, std::set<MVertex const *> &visited,
894   std::set<MElement const *> &visited_elements,
895   std::multimap<int, MVertex const *> &proximity, int max_level, int level)
896 {
897   level++;
898   if(level > max_level) return;
899 
900   std::set<MVertex const *> new_vertices;
901 
902   for(std::set<MVertex const *>::iterator it_start = start.begin();
903       it_start != start.end(); it_start++) { // for all initial vertices
904     MVertex const *current = *it_start;
905     //      std::cout << "get_recursive_neighbors : on vertex " <<
906     //      current->getNum()
907     //      << " (" << current << ")" << std::endl;
908     vert2elemtype::iterator itfind = vert2elem.find(current);
909     if(itfind == vert2elem.end()) continue;
910 
911     std::set<MElement *>::iterator it_elem = itfind->second.begin();
912 
913     for(; it_elem != itfind->second.end();
914         it_elem++) { // for all connected elements
915       MElement *current_elem = *it_elem;
916 
917       if(visited_elements.find(current_elem) != visited_elements.end()) {
918         continue;
919       }
920       //        std::cout << current_elem->getNum() << std::endl;
921       for(std::size_t i = 0; i < current_elem->getNumVertices(); i++) {
922         MVertex *ver = current_elem->getVertex(i);
923 
924         if(visited.find(ver) != visited.end()) continue;
925 
926         proximity.insert(std::make_pair(level, ver));
927         new_vertices.insert(ver);
928         visited.insert(ver);
929       }
930       visited_elements.insert(current_elem);
931     }
932   }
933 
934   get_recursive_neighbors(new_vertices, visited, visited_elements, proximity,
935                           max_level, level);
936 }
937 
compare_to_neighbors(const SPoint3 & current,STensor3 & ref,std::multimap<double,MVertex const * >::iterator itbegin,std::multimap<double,MVertex const * >::iterator itend,SVector3 & mean_axis,double & mean_angle,std::vector<double> & vectorial_smoothness)938 double frameFieldBackgroundMesh3D::compare_to_neighbors(
939   const SPoint3 &current, STensor3 &ref,
940   std::multimap<double, MVertex const *>::iterator itbegin,
941   std::multimap<double, MVertex const *>::iterator itend, SVector3 &mean_axis,
942   double &mean_angle, std::vector<double> &vectorial_smoothness)
943 {
944   for(int i = 0; i < 3; i++) mean_axis(i) = 0.;
945 
946   std::multimap<double, MVertex const *>::iterator it = itbegin;
947   SVector3 rotation_axis;
948   double minimum_angle;
949 
950   std::vector<double> all_angle;
951   std::vector<SVector3> all_axis;
952   TensorStorageType::iterator itneighbor;
953 
954   //  vectorial_smoothness.assign(3,0.);
955   //  vector<double> temp(3);
956 
957   std::vector<double> ponderations_vec;
958 
959   std::vector<double> alternative;
960 
961   for(; it != itend; it++) { // for all trusted neighbors
962     // neighbor = it->second.second;
963     MVertex const *neighbor = it->second;
964     // ponderations_vec.push_back(1.);
965     ponderations_vec.push_back(
966       (std::abs(it->first) >= smoothness_threshold) ? 1. : 1.e-3);
967     // ponderations_vec.push_back(((std::abs(it->first) >= smoothness_threshold)
968     // ?
969     // 1. : 1.e-3) / (current.distance(neighbor->point())));
970     itneighbor = crossField.find(neighbor);
971     STensor3 &neibcross = itneighbor->second;
972 
973     get_min_rotation_matrix(neibcross, ref, minimum_angle, rotation_axis);
974     all_axis.push_back(rotation_axis);
975     all_angle.push_back(minimum_angle);
976     alternative.push_back(std::abs(minimum_angle));
977 
978     //    // now, computing vectorial smoothness
979     //    for (int j=0;j<3;j++){// for every cross field vector
980     //      temp.assign(3,0.);
981     //      for (int ivec=0;ivec<3;ivec++){// for every neighbor vector
982     //        for (int k=0;k<3;k++){
983     //          temp[ivec] += (neibcross(k,ivec)*ref(k,j));// scalar products
984     //        }
985     //        temp[ivec] = std::abs(temp[ivec]);
986     //      }
987     //      vectorial_smoothness[j] +=
988     //      acos(fmin(*std::max_element(temp.begin(),temp.end()),1.));
989     //    }
990   }
991 
992   // Watch out !!!      acos(mean_angle)  !=  mean_acos    ->     second option
993   // gives better results, better contrast between smooth and not smooth
994   //  for (int j=0;j<3;j++){
995   //    vectorial_smoothness[j] = (1. -
996   //    (vectorial_smoothness[j]/all_angle.size())/M_PI*4.);// between 0 (not
997   //    smooth) and 1 (smooth)
998   //  }
999 
1000   // ----------------------------------------------------------------------------
1001   // Watch out !!!  The following definition of smoothness may change A LOT !!!
1002   // ----------------------------------------------------------------------------
1003   // may seem against intuition in some case, but min gives much better results
1004   // finally... mean angle !!!
1005 
1006   //  double smoothness_scalar =
1007   //  (*std::max_element(vectorial_smoothness.begin(),vectorial_smoothness.end()));
1008   //  double smoothness_scalar =
1009   //  (*std::min_element(vectorial_smoothness.begin(),vectorial_smoothness.end()));
1010   double smoothness_scalar =
1011     1. - (std::accumulate(alternative.begin(), alternative.end(), 0.) /
1012           alternative.size()) /
1013            M_PI * 4.; // APPROXIMATELY between 0 (not smooth) and 1 (smooth),
1014                       // (sometimes <0, always > 1).
1015 
1016   std::vector<double>::iterator itan = all_angle.begin();
1017   std::vector<double>::iterator itpond = ponderations_vec.begin();
1018 
1019   for(std::vector<SVector3>::iterator ita = all_axis.begin();
1020       ita != all_axis.end(); ita++, itan++, itpond++) {
1021     // mean_axis += ((*ita)*(*itan));
1022     mean_axis += ((*ita) * (*itan)) * (*itpond);
1023   }
1024 
1025   // mean_angle = mean_axis.norm()/all_angle.size();
1026   double pond_total =
1027     std::accumulate(ponderations_vec.begin(), ponderations_vec.end(), 0.);
1028   mean_angle = mean_axis.norm() / pond_total;
1029   mean_axis.normalize();
1030 
1031   return smoothness_scalar;
1032 }
1033 
apply_rotation(const SVector3 & axis,const double & angle,const STensor3 & thecross)1034 STensor3 frameFieldBackgroundMesh3D::apply_rotation(const SVector3 &axis,
1035                                                     const double &angle,
1036                                                     const STensor3 &thecross)
1037 {
1038   double rotmat[3][3];
1039   STensor3 res2;
1040   get_rotation_matrix(angle, axis, &rotmat[0][0]);
1041 
1042   //    std::cout << "from matrice, rotation of " << angle/M_PI*180 << "° axis("
1043   //    << axis(0) << "," << axis(1) << "," << axis(2) << ")" << std::endl;
1044   //        std::cout << "rotation matrix is :" << std::endl;
1045   //        for (int i=0;i<3;i++){
1046   //          for (int j=0;j<3;j++)
1047   //            std::cout << rotmat[i][j] << " ";
1048   //          std::cout << std::endl;
1049   //        }
1050   //
1051 
1052   for(int i = 0; i < 3; i++)
1053     for(int j = 0; j < 3; j++)
1054       for(int k = 0; k < 3; k++) res2(i, j) += rotmat[i][k] * thecross(k, j);
1055   //  return res;
1056 
1057   //  std::cout << "result:" << std::endl;
1058   //    for (int i=0;i<3;i++){
1059   //      for (int j=0;j<3;j++)
1060   //        std::cout << res2(i,j) << " ";
1061   //      std::cout << std::endl;
1062   //    }
1063 
1064   //  STensor3 res;
1065   //  double crossv[4],q[4],qconj[4],resq[4];
1066   //  double coss = cos(angle/2.);
1067   //  double sinn = sin(angle/2.);
1068   //  q[0] = coss;
1069   //  q[1] = sinn*axis[0];
1070   //  q[2] = sinn*axis[1];
1071   //  q[3] = sinn*axis[2];
1072   //  qconj[0] = q[0];
1073   //  for (int i=1;i<4;i++) qconj[i] = -q[i];
1074   //
1075   //  for (int i=0;i<3;i++){
1076   //    crossv[0] = 0.;
1077   //    crossv[1] = thecross(0,i);
1078   //    crossv[2] = thecross(1,i);
1079   //    crossv[3] = thecross(2,i);
1080   //
1081   ////    quat_prod(q,crossv,resq);
1082   ////    quat_prod(resq,qconj,resq);
1083   //    quat_prod(crossv,qconj,resq);
1084   //    quat_prod(q,resq,resq);
1085   //    for (int j=0;j<3;j++)
1086   //      res(j,i)=resq[j+1];
1087   //
1088   //  }
1089   //  std::cout << "from quat:" << std::endl;
1090   //  for (int i=0;i<3;i++){
1091   //    for (int j=0;j<3;j++)
1092   //      std::cout << res(j,i) << " ";
1093   //    std::cout << std::endl;
1094   //  }
1095   //  std::cout << "w="<<resq[0] << std::endl;
1096   return res2;
1097 
1098   //  STensor3 res;
1099   //  double coss = cos(angle);
1100   //  double sinn = sin(angle);
1101   //  SVector3 temp;
1102   //  for (int i=0;i<3;i++){
1103   //    SVector3 v(thecross(0,i),thecross(1,i),thecross(2,i));
1104   //    temp = axis*my_scal_prod(axis,v);
1105   //    SVector3 vres = (v - temp)*coss + crossprod(axis,v)*sinn + temp;
1106   //    for (int j=0;j<3;j++)
1107   //      res(j,i)=vres(j);
1108   //  }
1109   //  return res;
1110 }
1111 
1112 // TODO: ce genre de fct... mettre dans une classe FrameField ? Ou bien juste un
1113 // set de fcts à part ? Parce que ça devient bizarre d'avoir ça dans un BGM ???
get_rotation_matrix(const double & angle_to_go,const SVector3 & rotvec,double * rotmat)1114 void frameFieldBackgroundMesh3D::get_rotation_matrix(const double &angle_to_go,
1115                                                      const SVector3 &rotvec,
1116                                                      double *rotmat)
1117 {
1118   double c = std::cos(angle_to_go);
1119   double s = std::sin(angle_to_go);
1120   double temp01 = rotvec[0] * rotvec[1] * (1. - c);
1121   double temp02 = rotvec[0] * rotvec[2] * (1. - c);
1122   double temp12 = rotvec[1] * rotvec[2] * (1. - c);
1123   double square0 = rotvec[0] * rotvec[0];
1124   double square1 = rotvec[1] * rotvec[1];
1125   double square2 = rotvec[2] * rotvec[2];
1126   rotmat[0] = square0 + (1. - square0) * c;
1127   rotmat[1] = temp01 - rotvec[2] * s;
1128   rotmat[2] = temp02 + rotvec[1] * s;
1129   rotmat[3] = temp01 + rotvec[2] * s;
1130   rotmat[4] = square1 + (1. - square1) * c;
1131   rotmat[5] = temp12 - rotvec[0] * s;
1132   rotmat[6] = temp02 - rotvec[1] * s;
1133   rotmat[7] = temp12 + rotvec[0] * s;
1134   rotmat[8] = square2 + (1. - square2) * c;
1135 }
1136 
get_min_rotation_matrix(const STensor3 & reference,const STensor3 & thecross,double & minimum_angle,SVector3 & rotation_axis,double threshold,bool debugflag)1137 void frameFieldBackgroundMesh3D::get_min_rotation_matrix(
1138   const STensor3 &reference, const STensor3 &thecross, double &minimum_angle,
1139   SVector3 &rotation_axis, double threshold, bool debugflag)
1140 {
1141   minimum_angle = M_PI / 2.;
1142   std::pair<int, int> p;
1143 
1144   for(unsigned int iperm = 0; iperm < permutation.size(); iperm++) {
1145     if(minimum_angle < threshold) break;
1146     for(int i_rotation_perm = 0; i_rotation_perm < 3; i_rotation_perm++) {
1147       double a;
1148       SVector3 v;
1149       get_rotation_angle_and_axis(reference, thecross, a, v, i_rotation_perm,
1150                                   permutation[iperm]);
1151       if(debugflag) {
1152         if(std::abs(a) < M_PI / 2.) {
1153           std::cout << "     temp parameters:  angle=" << a * 180. / M_PI
1154                     << "pair=(" << iperm << "," << i_rotation_perm << ") axis=("
1155                     << v(0) << "," << v(1) << "," << v(2) << ")" << std::endl;
1156         }
1157         else
1158           std::cout << "     temp parameters:  angle=" << a * 180. / M_PI
1159                     << std::endl;
1160       }
1161       if(std::abs(a) < std::abs(minimum_angle)) {
1162         p = std::make_pair(iperm, i_rotation_perm);
1163         minimum_angle = a;
1164         rotation_axis = v;
1165       }
1166 
1167       if(minimum_angle < threshold) break;
1168     }
1169   }
1170   //  std::cout << "pair=(" << p.first << "," << p.second << ")" << std::endl;
1171   if(debugflag) {
1172     std::cout << " ---> MIN parameters:  angle=" << minimum_angle * 180. / M_PI
1173               << " axis=(" << rotation_axis(0) << "," << rotation_axis(1) << ","
1174               << rotation_axis(2) << ")" << std::endl;
1175   }
1176   return;
1177 }
1178 
1179 // compute the angle and axis of rotation between two (unit-orthogonal) cross
1180 // fields using crossfield vectors permutations defined by modulo and trip The
1181 // rotation matrix between cross fields C and M is R = C M^T
get_rotation_angle_and_axis(const STensor3 & reference,const STensor3 & thecross,double & minimum_angle,SVector3 & rotation_axis,int modulo,montripletbis & trip)1182 void frameFieldBackgroundMesh3D::get_rotation_angle_and_axis(
1183   const STensor3 &reference, const STensor3 &thecross, double &minimum_angle,
1184   SVector3 &rotation_axis, int modulo, montripletbis &trip)
1185 {
1186   // double MT[3][3],C[3][3],R[3][3];
1187   double C[3][3], R[3][3];
1188 
1189   for(int i = 0; i < 3; i++) {
1190     for(int j = 0; j < 3; j++) {
1191       C[i][j] =
1192         signof(trip(j)) * thecross(i, ((abs(trip(j)) - 1) + modulo) % 3);
1193       // MT[j][i] = reference(i,j);//transpose !
1194     }
1195   }
1196 
1197   //  ////////////////////
1198   //  for (int k=0;k<2;k++){
1199   //    double test1 = C[0][k]*C[0][k]*C[1][k]*C[1][k] +
1200   //    C[0][k]*C[0][k]*C[2][k]*C[2][k] +  C[2][k]*C[2][k]*C[1][k]*C[1][k];
1201   //    double test2 =
1202   //    reference(0,k)*reference(0,k)*reference(1,k)*reference(1,k) +
1203   //    reference(0,k)*reference(0,k)*reference(2,k)*reference(2,k) +
1204   //    reference(2,k)*reference(2,k)*reference(1,k)*reference(1,k); std::cout
1205   //    << "    --- k=" << k << " test1=" << test1 << std::endl; std::cout << "
1206   //    --- k=" << k << " test2=" << test2 << std::endl;
1207   //  }
1208   //  ////////////////////
1209 
1210   // computing the trace for the angle...
1211   // MT is transpose of reference !!!
1212   //  R[0][0] = C[0][0]*MT[0][0] + C[0][1]*MT[1][0] + C[0][2]*MT[2][0];
1213   //  R[1][1] = C[1][0]*MT[0][1] + C[1][1]*MT[1][1] + C[1][2]*MT[2][1];
1214   //  R[2][2] = C[2][0]*MT[0][2] + C[2][1]*MT[1][2] + C[2][2]*MT[2][2];
1215   R[0][0] = C[0][0] * reference(0, 0) + C[0][1] * reference(0, 1) +
1216             C[0][2] * reference(0, 2);
1217   R[1][1] = C[1][0] * reference(1, 0) + C[1][1] * reference(1, 1) +
1218             C[1][2] * reference(1, 2);
1219   R[2][2] = C[2][0] * reference(2, 0) + C[2][1] * reference(2, 1) +
1220             C[2][2] * reference(2, 2);
1221 
1222   // computing rotation angle
1223   double trace = R[0][0] + R[1][1] + R[2][2];
1224   minimum_angle =
1225     std::acos(std::max(std::min(0.5 * (trace - 1.), 1.), -1.)); // tjrs > 0
1226 
1227   //  std::cout << "minimum_angle=" << minimum_angle << " trace=" << trace <<
1228   //  std::endl;
1229 
1230   if(std::abs(minimum_angle) > M_PI / 2.) return; // no need to continue...
1231 
1232   // computing rotation axis
1233   // computing the remaining terms, not on diagonal
1234   if(std::abs(minimum_angle) < 1.e-6) {
1235     rotation_axis(0) = 0.;
1236     rotation_axis(1) = 0.;
1237     rotation_axis(2) = 1.;
1238     return;
1239   }
1240 
1241   R[0][1] = C[0][0] * reference(1, 0) + C[0][1] * reference(1, 1) +
1242             C[0][2] * reference(1, 2);
1243   R[0][2] = C[0][0] * reference(2, 0) + C[0][1] * reference(2, 1) +
1244             C[0][2] * reference(2, 2);
1245   R[1][0] = C[1][0] * reference(0, 0) + C[1][1] * reference(0, 1) +
1246             C[1][2] * reference(0, 2);
1247   R[1][2] = C[1][0] * reference(2, 0) + C[1][1] * reference(2, 1) +
1248             C[1][2] * reference(2, 2);
1249   R[2][0] = C[2][0] * reference(0, 0) + C[2][1] * reference(0, 1) +
1250             C[2][2] * reference(0, 2);
1251   R[2][1] = C[2][0] * reference(1, 0) + C[2][1] * reference(1, 1) +
1252             C[2][2] * reference(1, 2);
1253 
1254   double factor = -0.5 / std::sin(minimum_angle);
1255   rotation_axis(0) = (R[2][1] - R[1][2]) * factor;
1256   rotation_axis(1) = (R[0][2] - R[2][0]) * factor;
1257   rotation_axis(2) = (R[1][0] - R[0][1]) * factor;
1258   return;
1259 }
1260 
exportVectorialSmoothness(const std::string & filename)1261 void frameFieldBackgroundMesh3D::exportVectorialSmoothness(
1262   const std::string &filename)
1263 {
1264   FILE *f = Fopen(filename.c_str(), "w");
1265   if(!f) {
1266     Msg::Error("Could not open file '%s'", filename.c_str());
1267     return;
1268   }
1269 
1270   fprintf(f, "View \"Background Mesh\"{\n");
1271 
1272   std::set<const MVertex *> done;
1273 
1274   for(unsigned int ie = 0; ie < getNumMeshElements();
1275       ie++) { // for all elements
1276     const MElement *e = getElement(ie);
1277     for(std::size_t iv = 0; iv < e->getNumVertices(); iv++) {
1278       const MVertex *v = e->getVertex(iv);
1279       std::set<const MVertex *>::iterator itfind = done.find(v);
1280       if(itfind != done.end()) continue;
1281       done.insert(v);
1282       STensor3 cf;
1283       eval_approximate_crossfield(v, cf);
1284       for(int i = 0; i < 3; i++) {
1285         double vs = get_vectorial_smoothness(i, v);
1286         fprintf(f, "VP(%g,%g,%g){%g,%g,%g};\n", v->x(), v->y(), v->z(),
1287                 cf(0, i) * vs, cf(1, i) * vs, cf(2, i) * vs);
1288       }
1289     }
1290   }
1291   fprintf(f, "};\n");
1292   fclose(f);
1293 }
1294