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 ¤t, 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