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