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