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 "GmshConfig.h"
7 #include "GModel.h"
8 #include "discreteEdge.h"
9 #include "meshGEdge.h"
10 #include "GEdge.h"
11 #include "MLine.h"
12 #include "BackgroundMeshTools.h"
13 #include "boundaryLayersData.h"
14 #include "Numeric.h"
15 #include "GmshMessage.h"
16 #include "Context.h"
17 #include "STensor3.h"
18 #include "Field.h"
19 #include "OS.h"
20 
21 typedef struct {
22   int Num;
23   // t is the local coordinate of the point
24   // lc is x'(t)/h(x(t))
25   // p is the value of the primitive
26   // xp is the norm of the tangent vector
27   double t, lc, p, xp;
28 } IntPoint;
29 
smoothPrimitive(GEdge * ge,double alpha,std::vector<IntPoint> & Points)30 static double smoothPrimitive(GEdge *ge, double alpha,
31                               std::vector<IntPoint> &Points)
32 {
33   int ITER = 0;
34   while(1) {
35     int count1 = 0;
36     int count2 = 0;
37 
38     // use a gauss-seidel iteration; iterate forward and then backward;
39     // convergence is usually very fast
40     for(std::size_t i = 1; i < Points.size(); i++) {
41       double dh =
42         (Points[i].xp / Points[i].lc - Points[i - 1].xp / Points[i - 1].lc);
43       double dt = Points[i].t - Points[i - 1].t;
44       double dhdt = dh / dt;
45       if(dhdt / Points[i].xp > (alpha - 1.) * 1.01) {
46         double hnew =
47           Points[i - 1].xp / Points[i - 1].lc + dt * (alpha - 1) * Points[i].xp;
48         Points[i].lc = Points[i].xp / hnew;
49         count1++;
50       }
51     }
52 
53     for(int i = Points.size() - 1; i > 0; i--) {
54       double dh =
55         (Points[i - 1].xp / Points[i - 1].lc - Points[i].xp / Points[i].lc);
56       double dt = std::abs(Points[i - 1].t - Points[i].t);
57       double dhdt = dh / dt;
58       if(dhdt / Points[i - 1].xp > (alpha - 1.) * 1.01) {
59         double hnew =
60           Points[i].xp / Points[i].lc + dt * (alpha - 1) * Points[i].xp;
61         Points[i - 1].lc = Points[i - 1].xp / hnew;
62         count2++;
63       }
64     }
65 
66     ++ITER;
67     if(ITER > 2000) break;
68     if(!(count1 + count2)) break;
69   }
70 
71   // recompute the primitive
72   for(std::size_t i = 1; i < Points.size(); i++) {
73     IntPoint &pt2 = Points[i];
74     IntPoint &pt1 = Points[i - 1];
75     pt2.p = pt1.p + (pt2.t - pt1.t) * 0.5 * (pt2.lc + pt1.lc);
76   }
77   return Points[Points.size() - 1].p;
78 }
79 
80 struct F_LcB {
operator ()F_LcB81   double operator()(GEdge *ge, double t)
82   {
83     GPoint p = ge->point(t);
84     return BGM_MeshSize(ge, t, 0, p.x(), p.y(), p.z());
85   }
86 };
87 
88 struct F_Lc {
operator ()F_Lc89   double operator()(GEdge *ge, double t)
90   {
91     GPoint p = ge->point(t);
92     Range<double> bounds = ge->parBounds(0);
93     double t_begin = bounds.low();
94     double t_end = bounds.high();
95     double lc_here = 1.e22;
96     if(t == t_begin && ge->getBeginVertex())
97       lc_here = BGM_MeshSize(ge->getBeginVertex(), t, 0, p.x(), p.y(), p.z());
98     else if(t == t_end && ge->getEndVertex())
99       lc_here = BGM_MeshSize(ge->getEndVertex(), t, 0, p.x(), p.y(), p.z());
100 
101     lc_here = std::min(lc_here, BGM_MeshSize(ge, t, 0, p.x(), p.y(), p.z()));
102     SVector3 der = ge->firstDer(t);
103     return norm(der) / lc_here;
104   }
105 };
106 
107 struct F_Lc_aniso {
operator ()F_Lc_aniso108   double operator()(GEdge *ge, double t)
109   {
110     GPoint p = ge->point(t);
111     SMetric3 lc_here;
112 
113     Range<double> bounds = ge->parBounds(0);
114     double t_begin = bounds.low();
115     double t_end = bounds.high();
116 
117     if(t == t_begin && ge->getBeginVertex())
118       lc_here = BGM_MeshMetric(ge->getBeginVertex(), t, 0, p.x(), p.y(), p.z());
119     else if(t == t_end && ge->getEndVertex())
120       lc_here = BGM_MeshMetric(ge->getEndVertex(), t, 0, p.x(), p.y(), p.z());
121     else
122       lc_here = BGM_MeshMetric(ge, t, 0, p.x(), p.y(), p.z());
123 
124     FieldManager *fields = ge->model()->getFields();
125     for(int i = 0; i < fields->getNumBoundaryLayerFields(); ++i) {
126       Field *bl_field = fields->get(fields->getBoundaryLayerField(i));
127       if(bl_field == nullptr) continue;
128       BoundaryLayerField *blf = dynamic_cast<BoundaryLayerField *>(bl_field);
129       if(blf->isEdgeBL(ge->tag())) break;
130       SMetric3 lc_bgm;
131       blf->computeFor1dMesh(p.x(), p.y(), p.z(), lc_bgm);
132       lc_here = intersection_conserveM1(lc_here, lc_bgm);
133     }
134 
135     SVector3 der = ge->firstDer(t);
136     return std::sqrt(dot(der, lc_here, der));
137   }
138 };
139 
140 // static double dfbeta2 (double t, double beta){
141 //  double ratio = (1+beta)/(beta-1);
142 //  double zlog  = log(ratio);
143 //  return 1.-acosh(beta*zlog/t - 1.0)/zlog;
144 //  return beta*zlog / (1+cosh(zlog*(1-t)));
145 //}
146 
dfbeta(double t,double beta)147 static double dfbeta(double t, double beta)
148 {
149   double ratio = (1 + beta) / (beta - 1);
150   double zlog = log(ratio);
151   return 2 * beta / ((1 + beta - t) * (-1 + beta + t) * zlog);
152 }
153 
154 struct F_Transfinite {
operator ()F_Transfinite155   double operator()(GEdge *ge, double t_)
156   {
157     double length = ge->length();
158     if(length == 0.0) {
159       Msg::Error("Zero-length curve %d in transfinite mesh", ge->tag());
160       return 1.;
161     }
162 
163     SVector3 der = ge->firstDer(t_);
164     double d = norm(der);
165     double coef = ge->meshAttributes.coeffTransfinite;
166     int type = ge->meshAttributes.typeTransfinite;
167     int atype = std::abs(type);
168     int nbpt = ge->meshAttributes.nbPointsTransfinite;
169 
170     if(CTX::instance()->mesh.flexibleTransfinite &&
171        CTX::instance()->mesh.lcFactor)
172       nbpt /= CTX::instance()->mesh.lcFactor;
173 
174     Range<double> bounds = ge->parBounds(0);
175     double t_begin = bounds.low();
176     double t_end = bounds.high();
177     double t = (t_ - t_begin) / (t_end - t_begin);
178 
179     double val;
180 
181     if(coef <= 0.0 || coef == 1.0 || (atype == 3 && coef < 1.)) {
182       // coef < 0 should never happen
183       val = d * coef / ge->length();
184     }
185     else {
186       switch(atype) {
187       case 1: {
188         // geometric progression ar^i; Sum of n terms = length = a (r^n-1)/(r-1)
189         double r = (gmsh_sign(type) >= 0) ? coef : 1. / coef;
190         double a = length * (r - 1.) / (std::pow(r, nbpt - 1.) - 1.);
191         int i = (int)(std::log(t * length / a * (r - 1.) + 1.) / std::log(r));
192         val = d / (a * std::pow(r, (double)i));
193       } break;
194 
195       case 2: {
196         // "bump"
197         double a;
198         if(coef > 1.0) {
199           a = -4. * std::sqrt(coef - 1.) *
200               std::atan2(1.0, std::sqrt(coef - 1.)) / ((double)nbpt * length);
201         }
202         else {
203           a = 2. * std::sqrt(1. - coef) *
204               std::log(std::abs((1. + 1. / std::sqrt(1. - coef)) /
205                                 (1. - 1. / std::sqrt(1. - coef)))) /
206               ((double)nbpt * length);
207         }
208         double b = -a * length * length / (4. * (coef - 1.));
209         val = d / (-a * std::pow(t * length - (length)*0.5, 2) + b);
210         break;
211       }
212       case 3: {
213         // "beta" law
214         if(type < 0)
215           val = dfbeta(1. - t, coef);
216         else
217           val = dfbeta(t, coef);
218         break;
219       }
220       case 4: {
221         // standard boundary layer progression: TODO
222         val = d / (length * t);
223         break;
224       }
225       default:
226         Msg::Warning("Unknown case in Transfinite Line mesh");
227         val = 1.;
228         break;
229       }
230     }
231     return val;
232   }
233 };
234 
235 struct F_One {
operator ()F_One236   double operator()(GEdge *ge, double t)
237   {
238     SVector3 der = ge->firstDer(t);
239     return norm(der);
240   }
241 };
242 
trapezoidal(IntPoint * const P1,IntPoint * const P2)243 static double trapezoidal(IntPoint *const P1, IntPoint *const P2)
244 {
245   return 0.5 * (P1->lc + P2->lc) * (P2->t - P1->t);
246 }
247 
248 template <typename function>
RecursiveIntegration(GEdge * ge,IntPoint * from,IntPoint * to,function f,std::vector<IntPoint> & Points,double Prec,int * depth)249 static void RecursiveIntegration(GEdge *ge, IntPoint *from, IntPoint *to,
250                                  function f, std::vector<IntPoint> &Points,
251                                  double Prec, int *depth)
252 {
253   IntPoint P, p1;
254 
255   (*depth)++;
256 
257   P.t = 0.5 * (from->t + to->t);
258   P.lc = f(ge, P.t);
259 
260   double const val1 = trapezoidal(from, to);
261   double const val2 = trapezoidal(from, &P);
262   double const val3 = trapezoidal(&P, to);
263   double const err = std::abs(val1 - val2 - val3);
264 
265   if(((err < Prec) && (*depth > 6)) || (*depth > 25)) {
266     p1 = Points.back();
267     P.p = p1.p + val2;
268     Points.push_back(P);
269 
270     p1 = Points.back();
271     to->p = p1.p + val3;
272     Points.push_back(*to);
273   }
274   else {
275     RecursiveIntegration(ge, from, &P, f, Points, Prec, depth);
276     RecursiveIntegration(ge, &P, to, f, Points, Prec, depth);
277   }
278 
279   (*depth)--;
280 }
281 
282 template <typename function>
Integration(GEdge * ge,double t1,double t2,function f,std::vector<IntPoint> & Points,double Prec)283 static double Integration(GEdge *ge, double t1, double t2, function f,
284                           std::vector<IntPoint> &Points, double Prec)
285 {
286   IntPoint from, to;
287 
288   int depth = 0;
289 
290   from.t = t1;
291   from.lc = f(ge, from.t);
292   from.p = 0.0;
293   Points.push_back(from);
294 
295   to.t = t2;
296   to.lc = f(ge, to.t);
297 
298   RecursiveIntegration(ge, &from, &to, f, Points, Prec, &depth);
299 
300   return Points.back().p;
301 }
302 
transform(MVertex * vsource,const std::vector<double> & tfo)303 static SPoint3 transform(MVertex *vsource, const std::vector<double> &tfo)
304 {
305   double ps[4] = {vsource->x(), vsource->y(), vsource->z(), 1.};
306   double res[4] = {0., 0., 0., 0.};
307   int idx = 0;
308   for(int i = 0; i < 4; i++)
309     for(int j = 0; j < 4; j++) res[i] += tfo[idx++] * ps[j];
310 
311   return SPoint3(res[0], res[1], res[2]);
312 }
313 
copyMesh(GEdge * from,GEdge * to,int direction)314 static void copyMesh(GEdge *from, GEdge *to, int direction)
315 {
316   if(!from->getBeginVertex() || !from->getEndVertex() ||
317      !to->getBeginVertex() || !to->getEndVertex()) {
318     Msg::Error("Cannot copy mesh on curves without begin/end points");
319     return;
320   }
321 
322   Range<double> u_bounds = from->parBounds(0);
323   double u_min = u_bounds.low();
324   double u_max = u_bounds.high();
325 
326   Range<double> to_u_bounds = to->parBounds(0);
327   double to_u_min = to_u_bounds.low();
328 
329   // include begin and end point to avoid conflicts when realigning
330   MVertex *vt0 = to->getBeginVertex()->mesh_vertices[0];
331   MVertex *vt1 = to->getEndVertex()->mesh_vertices[0];
332   MVertex *vs0 = from->getBeginVertex()->mesh_vertices[0];
333   MVertex *vs1 = from->getEndVertex()->mesh_vertices[0];
334 
335   to->correspondingVertices[vt0] = direction > 0 ? vs0 : vs1;
336   to->correspondingVertices[vt1] = direction > 0 ? vs1 : vs0;
337 
338   for(std::size_t i = 0; i < from->mesh_vertices.size(); i++) {
339     int index = (direction < 0) ? (from->mesh_vertices.size() - 1 - i) : i;
340     MVertex *v = from->mesh_vertices[index];
341     GPoint gp;
342     double newu;
343     if(to->affineTransform.size() < 16) {
344       // assume identical parametrisations (dangerous...)
345       double u;
346       v->getParameter(0, u);
347       newu = (direction > 0) ? (u - u_min + to_u_min) : (u_max - u + to_u_min);
348       gp = to->point(newu);
349     }
350     else {
351       SPoint3 p = transform(v, to->affineTransform);
352       // for non-exact periodic curves we could use closestPoint; just use
353       // Mesh.HighOrderPeriodic=2, which will do that
354       newu = to->parFromPoint(p);
355       gp = to->point(newu);
356       // gp = to->closestPoint(p, newu);
357     }
358     MEdgeVertex *vv = new MEdgeVertex(gp.x(), gp.y(), gp.z(), to, newu);
359     to->mesh_vertices.push_back(vv);
360     to->correspondingVertices[vv] = v;
361   }
362   for(std::size_t i = 0; i < to->mesh_vertices.size() + 1; i++) {
363     MVertex *v0 = (i == 0) ? to->getBeginVertex()->mesh_vertices[0] :
364                              to->mesh_vertices[i - 1];
365     MVertex *v1 = (i == to->mesh_vertices.size()) ?
366                     to->getEndVertex()->mesh_vertices[0] :
367                     to->mesh_vertices[i];
368     to->lines.push_back(new MLine(v0, v1));
369   }
370 }
371 
operator ()(GEdge * ge)372 void deMeshGEdge::operator()(GEdge *ge)
373 {
374   if(ge->isFullyDiscrete()) return;
375   ge->deleteMesh();
376   ge->meshStatistics.status = GEdge::PENDING;
377 }
378 
379 /*
380 static void printFandPrimitive(int tag, std::vector<IntPoint> &Points)
381 {
382   char name[256];
383   sprintf(name, "line%d.dat", tag);
384   FILE *f = Fopen(name, "w");
385   if(!f) return;
386   double l = 0;
387   for (std::size_t i = 0; i < Points.size(); i++){
388     const IntPoint &P = Points[i];
389     if(i) l += (P.t - Points[i-1].t) * P.xp;
390     fprintf(f, "%g %g %g %g %g\n", P.t, P.xp/P.lc, P.p, P.lc, l);
391   }
392   fclose(f);
393 }
394 */
395 
396 // new algo for recombining + splitting
increaseN(int N)397 static int increaseN(int N)
398 {
399   if(((N + 1) / 2 - 1) % 2 != 0) return N + 2;
400   return N;
401 }
402 
403 // ensure not to have points that are too close to each other.
404 // can be caused by a coarse 1D mesh or by a noisy curve
filterPoints(GEdge * ge,int nMinimumPoints)405 static void filterPoints(GEdge *ge, int nMinimumPoints)
406 {
407   if(ge->mesh_vertices.empty()) return;
408   if(ge->meshAttributes.method == MESH_TRANSFINITE) return;
409 
410   bool forceOdd = false;
411   if((ge->meshAttributes.method != MESH_TRANSFINITE ||
412       CTX::instance()->mesh.flexibleTransfinite) &&
413      CTX::instance()->mesh.algoRecombine != 0) {
414     if(CTX::instance()->mesh.recombineAll) { forceOdd = true; }
415   }
416 
417   if(!ge->getBeginVertex() || !ge->getEndVertex()) return;
418 
419   MVertex *v0 = ge->getBeginVertex()->mesh_vertices[0];
420   std::vector<std::pair<double, MVertex *> > lengths;
421   for(std::size_t i = 0; i < ge->mesh_vertices.size(); i++) {
422     MEdgeVertex *v = dynamic_cast<MEdgeVertex *>(ge->mesh_vertices[i]);
423     if(!v) {
424       Msg::Error("Node not classified on curve in 1D mesh filtering");
425       return;
426     }
427     double d = distance(v, v0);
428     double t;
429     v->getParameter(0, t);
430     if(i != 0) {
431       double t0;
432       if(v0->onWhat()->dim() == 0) {
433         // Vertex is begin point
434         t0 = ge->parFromPoint(SPoint3(v0->x(), v0->y(), v0->z()));
435       }
436       else
437         v0->getParameter(0, t0);
438 
439       t = 0.5 * (t + t0);
440     }
441     double lc = F_LcB()(ge, t);
442     // double lc = v->getLc();
443     if(d < lc * .3) { lengths.push_back(std::make_pair(lc / d, v)); }
444     else
445       v0 = v;
446   }
447   std::sort(lengths.begin(), lengths.end());
448   int last = lengths.size();
449   if(forceOdd) {
450     while(last % 2 != 0) last--;
451   }
452   /*
453     if(CTX::instance()->mesh.algoRecombine == 2){
454       if(last < 4) last = 0;
455         while (last %4 != 0)last--;
456       }
457       else{
458         while (last %2 != 0)last--;
459       }
460     }
461   */
462 
463   bool filteringObservesMinimumN =
464     (((int)ge->mesh_vertices.size() - last) >= nMinimumPoints);
465   if(filteringObservesMinimumN) {
466     for(int i = 0; i < last; i++) {
467       auto it = std::find(ge->mesh_vertices.begin(), ge->mesh_vertices.end(),
468                           lengths[i].second);
469 
470       if(it != ge->mesh_vertices.end()) { ge->mesh_vertices.erase(it); }
471       delete lengths[i].second;
472     }
473   }
474 }
475 
createPoints(GVertex * gv,GEdge * ge,BoundaryLayerField * blf,std::vector<MVertex * > & v,const SVector3 & dir)476 static void createPoints(GVertex *gv, GEdge *ge, BoundaryLayerField *blf,
477                          std::vector<MVertex *> &v, const SVector3 &dir)
478 {
479   if(!ge->getBeginVertex() || !ge->getEndVertex()) return;
480 
481   const double hWall = blf->hWall(gv->tag());
482   double L = hWall;
483   double LEdge = distance(ge->getBeginVertex()->mesh_vertices[0],
484                           ge->getEndVertex()->mesh_vertices[0]);
485 
486   if(blf->betaLaw) {
487     std::vector<double> t(blf->nb_divisions);
488     double zlog = log((1 + blf->beta) / (blf->beta - 1));
489     for(int i = 0; i < blf->nb_divisions; i++) {
490       const double eta = (double)(i + 1) / blf->nb_divisions;
491       const double power = exp(zlog * (1. - eta));
492       const double ratio = (1. - power) / (1. + power);
493       t[i] = 1.0 + blf->beta * ratio;
494     }
495     for(int i = 0; i < blf->nb_divisions; i++) {
496       double L = hWall * t[i] / t[0];
497       SPoint3 p(gv->x() + dir.x() * L, gv->y() + dir.y() * L, 0.0);
498       v.push_back(new MEdgeVertex(p.x(), p.y(), p.z(), ge, ge->parFromPoint(p),
499                                   0, blf->hFar));
500     }
501   }
502   else {
503     while(1) {
504       if(L > blf->thickness || L > LEdge * .4) { break; }
505 
506       SPoint3 p(gv->x() + dir.x() * L, gv->y() + dir.y() * L, 0.0);
507       v.push_back(new MEdgeVertex(p.x(), p.y(), p.z(), ge, ge->parFromPoint(p),
508                                   0, blf->hFar));
509       int ith = v.size();
510       L += hWall * std::pow(blf->ratio, ith);
511     }
512   }
513 }
514 
addBoundaryLayerPoints(GEdge * ge,double & t_begin,double & t_end,std::vector<MVertex * > & _addBegin,std::vector<MVertex * > & _addEnd)515 static void addBoundaryLayerPoints(GEdge *ge, double &t_begin, double &t_end,
516                                    std::vector<MVertex *> &_addBegin,
517                                    std::vector<MVertex *> &_addEnd)
518 {
519   _addBegin.clear();
520   _addEnd.clear();
521 
522   // t_begin/t_end may change the left/right parameter of the interval
523   // _addBegin/_addEnd : additional points @ left/right
524   FieldManager *fields = ge->model()->getFields();
525   int n = fields->getNumBoundaryLayerFields();
526 
527   if(n == 0) return;
528 
529   if(!ge->getBeginVertex() || !ge->getEndVertex()) return;
530 
531   // Check if edge is a BL edge
532   for(int i = 0; i < n; ++i) {
533     Field *bl_field = fields->get(fields->getBoundaryLayerField(i));
534     if(bl_field == nullptr) continue;
535     BoundaryLayerField *blf = dynamic_cast<BoundaryLayerField *>(bl_field);
536     if(blf->isEdgeBL(ge->tag())) return;
537   }
538 
539   SVector3 dir(ge->getEndVertex()->x() - ge->getBeginVertex()->x(),
540                ge->getEndVertex()->y() - ge->getBeginVertex()->y(),
541                ge->getEndVertex()->z() - ge->getBeginVertex()->z());
542   dir.normalize();
543   GVertex *gvb = ge->getBeginVertex();
544   GVertex *gve = ge->getEndVertex();
545 
546   // Check if extremity nodes are BL node
547   for(int i = 0; i < n; ++i) {
548     Field *bl_field = fields->get(fields->getBoundaryLayerField(i));
549     if(bl_field == nullptr) continue;
550     BoundaryLayerField *blf = dynamic_cast<BoundaryLayerField *>(bl_field);
551     blf->setupFor1d(ge->tag());
552 
553     if(blf->isEndNode(gvb->tag())) {
554       if(ge->geomType() != GEntity::Line) {
555         Msg::Error("Boundary layer end point %d should lie on a straight line",
556                    gvb->tag());
557         return;
558       }
559       if(_addBegin.size()) {
560         Msg::Error("Different boundary layers cannot touch each other");
561         return;
562       }
563       createPoints(gvb, ge, blf, _addBegin, dir);
564       if(!_addBegin.empty())
565         _addBegin[_addBegin.size() - 1]->getParameter(0, t_begin);
566     }
567     if(blf->isEndNode(gve->tag())) {
568       if(ge->geomType() != GEntity::Line) {
569         Msg::Error("Boundary layer end point %d should lie on a straight line",
570                    gve->tag());
571         return;
572       }
573       if(_addEnd.size()) {
574         Msg::Error("Different boundary layers cannot touch each other");
575         return;
576       }
577       createPoints(gve, ge, blf, _addEnd, dir * -1.0);
578       if(!_addEnd.empty()) _addEnd[_addEnd.size() - 1]->getParameter(0, t_end);
579     }
580   }
581 }
582 
meshGEdgeProcessing(GEdge * ge,const double t_begin,double t_end,int & N,std::vector<IntPoint> & Points,double & a,int & filterMinimumN)583 int meshGEdgeProcessing(GEdge *ge, const double t_begin, double t_end, int &N,
584                         std::vector<IntPoint> &Points, double &a,
585                         int &filterMinimumN)
586 {
587   if(ge->geomType() == GEntity::BoundaryLayerCurve) return 0;
588   if(ge->meshAttributes.method == MESH_NONE) return 0;
589   if(CTX::instance()->mesh.meshOnlyVisible && !ge->getVisibility()) return 0;
590   if(CTX::instance()->mesh.meshOnlyEmpty && ge->getNumMeshElements()) return 0;
591 
592   // first compute the length of the curve by integrating one
593   double length;
594   Points.clear();
595   if(ge->geomType() == GEntity::Line && ge->getBeginVertex() &&
596      ge->getBeginVertex() == ge->getEndVertex() &&
597      // do not consider closed lines as degenerated
598      (ge->position(0.5) - ge->getBeginVertex()->xyz()).norm() <
599        CTX::instance()->geom.tolerance)
600     length = 0.0; // special case to avoid infinite loop in integration
601   else
602     length = Integration(ge, t_begin, t_end, F_One(), Points,
603                          CTX::instance()->mesh.lcIntegrationPrecision *
604                            CTX::instance()->lc);
605   ge->setLength(length);
606   Points.clear();
607 
608   if(length < CTX::instance()->mesh.toleranceEdgeLength) {
609     ge->setTooSmall(true);
610   }
611 
612   // Integrate detJ/lc du
613   filterMinimumN = 1;
614   if(length == 0. && CTX::instance()->mesh.toleranceEdgeLength == 0.) {
615     Msg::Debug("Curve %d has a zero length", ge->tag());
616     a = 0.;
617     N = 1;
618   }
619   else if(ge->degenerate(0)) {
620     Msg::Debug("Curve %d is degenerated", ge->tag());
621     a = 0.;
622     N = 1;
623   }
624   else if(ge->meshAttributes.method == MESH_TRANSFINITE &&
625           ge->meshAttributes.typeTransfinite == 4) {
626     // Transfinite (prescribed number of edges) but the points are positioned
627     // according to the standard size constraints (size map, etc)
628     a = Integration(ge, t_begin, t_end, F_Lc(), Points,
629                     CTX::instance()->mesh.lcIntegrationPrecision);
630     N = ge->meshAttributes.nbPointsTransfinite;
631   }
632   else if(ge->meshAttributes.method == MESH_TRANSFINITE) {
633     a = Integration(ge, t_begin, t_end, F_Transfinite(), Points,
634                     CTX::instance()->mesh.lcIntegrationPrecision);
635     N = ge->meshAttributes.nbPointsTransfinite;
636     if(CTX::instance()->mesh.flexibleTransfinite &&
637        CTX::instance()->mesh.lcFactor)
638       N /= CTX::instance()->mesh.lcFactor;
639   }
640   else {
641     if(CTX::instance()->mesh.algo2d == ALGO_2D_BAMG /* || blf*/) {
642       a = Integration(ge, t_begin, t_end, F_Lc_aniso(), Points,
643                       CTX::instance()->mesh.lcIntegrationPrecision);
644     }
645     else {
646       a = Integration(ge, t_begin, t_end, F_Lc(), Points,
647                       CTX::instance()->mesh.lcIntegrationPrecision);
648     }
649 
650     // we should maybe provide an option to disable the smoothing
651     for(std::size_t i = 0; i < Points.size(); i++) {
652       IntPoint &pt = Points[i];
653       SVector3 der = ge->firstDer(pt.t);
654       pt.xp = der.norm();
655     }
656     if(CTX::instance()->mesh.algo2d != ALGO_2D_BAMG)
657       a = smoothPrimitive(ge, std::sqrt(CTX::instance()->mesh.smoothRatio),
658                           Points);
659     filterMinimumN = ge->minimumMeshSegments() + 1;
660     N = std::max(filterMinimumN, (int)(a + 1.99));
661   }
662 
663   // force odd number of points if blossom is used for recombination
664   if((ge->meshAttributes.method != MESH_TRANSFINITE ||
665       CTX::instance()->mesh.flexibleTransfinite) &&
666      CTX::instance()->mesh.algoRecombine != 0) {
667     std::vector<GFace *> const &faces = ge->faces();
668     if(CTX::instance()->mesh.recombineAll && faces.size()) {
669       if(N % 2 == 0) N++;
670       if(CTX::instance()->mesh.algoRecombine == 2 ||
671          CTX::instance()->mesh.algoRecombine == 4)
672         N = increaseN(N);
673     }
674     else {
675       for(auto it = faces.begin(); it != faces.end(); it++) {
676         if((*it)->meshAttributes.recombine) {
677           if(N % 2 == 0) N++;
678           if(CTX::instance()->mesh.algoRecombine == 2 ||
679              CTX::instance()->mesh.algoRecombine == 4)
680             N = increaseN(N);
681           break;
682         }
683       }
684     }
685   }
686 
687   return N;
688 }
689 
operator ()(GEdge * ge)690 void meshGEdge::operator()(GEdge *ge)
691 {
692   // debug stuff
693   if(CTX::instance()->debugSurface > 0) {
694     std::vector<GFace *> f = ge->faces();
695     bool found = false;
696     for(size_t i = 0; i < f.size(); i++) {
697       if(f[i]->tag() == CTX::instance()->debugSurface) { found = true; }
698     }
699     if(!found) return;
700   }
701 
702   ge->model()->setCurrentMeshEntity(ge);
703 
704   if(ge->degenerate(1)) {
705     ge->meshStatistics.status = GEdge::DONE;
706     return;
707   }
708 
709   if(ge->geomType() == GEntity::BoundaryLayerCurve) return;
710   if(ge->meshAttributes.method == MESH_NONE) return;
711   if(CTX::instance()->mesh.meshOnlyVisible && !ge->getVisibility()) return;
712   if(CTX::instance()->mesh.meshOnlyEmpty && ge->getNumMeshElements()) return;
713 
714   // destroy the mesh if it exists
715   deMeshGEdge dem;
716   dem(ge);
717 
718   if(MeshExtrudedCurve(ge)) return;
719 
720   if(ge->getMeshMaster() != ge) {
721     GEdge *gef = dynamic_cast<GEdge *>(ge->getMeshMaster());
722     if(gef->meshStatistics.status == GEdge::PENDING) return;
723     Msg::Info("Meshing curve %d (%s) as a copy of curve %d", ge->tag(),
724               ge->getTypeString().c_str(), ge->getMeshMaster()->tag());
725     copyMesh(gef, ge, ge->masterOrientation);
726     ge->meshStatistics.status = GEdge::DONE;
727     return;
728   }
729 
730   Msg::Info("Meshing curve %d (%s)", ge->tag(), ge->getTypeString().c_str());
731 
732   // compute bounds
733   Range<double> bounds = ge->parBounds(0);
734   double t_begin = bounds.low();
735   double t_end = bounds.high();
736 
737   // if a BL is ending at one of the ends, then create specific points
738   std::vector<MVertex *> _addBegin, _addEnd;
739   addBoundaryLayerPoints(ge, t_begin, t_end, _addBegin, _addEnd);
740 
741   // integration to get length, number of points, etc
742   int N;
743   std::vector<IntPoint> Points;
744   double a;
745   int filterMinimumN;
746   meshGEdgeProcessing(ge, t_begin, t_end, N, Points, a, filterMinimumN);
747 
748   //  printFandPrimitive(ge->tag(),Points);
749 
750   // if the curve is periodic and if the begin vertex is identical to
751   // the end vertex and if this vertex has only one model curve
752   // adjacent to it, then the vertex is not connecting any other
753   // curve. So, the mesh vertex and its associated geom vertex are not
754   // necessary at the same location
755 
756   // look if we are doing the STL triangulation
757   std::vector<MVertex *> &mesh_vertices = ge->mesh_vertices;
758 
759   GPoint beg_p, end_p;
760   if(!ge->getBeginVertex() || !ge->getEndVertex()) {
761     Msg::Warning("Skipping curve with no begin or end point");
762     return;
763   }
764   else if(ge->getBeginVertex() == ge->getEndVertex() &&
765           ge->getBeginVertex()->edges().size() == 1) {
766     end_p = beg_p = ge->point(t_begin);
767     Msg::Debug("Meshing periodic closed curve (tag %i, %i points)", ge->tag(),
768                N);
769   }
770   else {
771     MVertex *v0 = ge->getBeginVertex()->mesh_vertices[0];
772     MVertex *v1 = ge->getEndVertex()->mesh_vertices[0];
773     beg_p = GPoint(v0->x(), v0->y(), v0->z());
774     end_p = GPoint(v1->x(), v1->y(), v1->z());
775   }
776 
777   // do not consider the first and the last vertex (those are not
778   // classified on this mesh edge)
779   if(N > 1) {
780     const double b = a / static_cast<double>(N - 1);
781     int count = 1, NUMP = 1;
782     IntPoint P1, P2;
783     mesh_vertices.resize(N - 2);
784 
785     while(NUMP < N - 1) {
786       P1 = Points[count - 1];
787       P2 = Points[count];
788       const double d = (double)NUMP * b;
789       if((std::abs(P2.p) >= std::abs(d)) && (std::abs(P1.p) < std::abs(d))) {
790         double const dt = P2.t - P1.t;
791         double const dlc = P2.lc - P1.lc;
792         double const dp = P2.p - P1.p;
793         double const t = P1.t + dt / dp * (d - P1.p);
794         SVector3 der = ge->firstDer(t);
795         const double d = norm(der);
796         double lc = d / (P1.lc + dlc / dp * (d - P1.p));
797         GPoint V = ge->point(t);
798         mesh_vertices[NUMP - 1] =
799           new MEdgeVertex(V.x(), V.y(), V.z(), ge, t, 0, lc);
800         NUMP++;
801       }
802       else {
803         count++;
804       }
805     }
806     mesh_vertices.resize(NUMP - 1);
807   }
808 
809   // Boundary Layer points are added
810   {
811     std::vector<MVertex *> vv;
812     vv.insert(vv.end(), _addBegin.begin(), _addBegin.end());
813     vv.insert(vv.end(), mesh_vertices.begin(), mesh_vertices.end());
814     for(std::size_t i = 0; i < _addEnd.size(); i++)
815       vv.push_back(_addEnd[_addEnd.size() - 1 - i]);
816     mesh_vertices = vv;
817   }
818 
819   if(CTX::instance()->mesh.algo2d != ALGO_2D_BAMG &&
820      CTX::instance()->mesh.algo2d != ALGO_2D_QUAD_QUASI_STRUCT &&
821      CTX::instance()->mesh.algo2d != ALGO_2D_PACK_PRLGRMS)
822     if(_addBegin.empty() && _addEnd.empty())
823       filterPoints(ge, filterMinimumN - 2);
824 
825   std::vector<MLine *> &lines = ge->lines;
826 
827   for(std::size_t i = 0; i <= mesh_vertices.size(); i++) {
828     MVertex *v0 =
829       (i == 0) ? ge->getBeginVertex()->mesh_vertices[0] : mesh_vertices[i - 1];
830 
831     MVertex *v1 = (i == mesh_vertices.size()) ?
832                     ge->getEndVertex()->mesh_vertices[0] :
833                     mesh_vertices[i];
834     lines.push_back(new MLine(v0, v1));
835   }
836 
837   if(ge->getBeginVertex() == ge->getEndVertex() &&
838      ge->getBeginVertex()->edges().size() == 1) {
839     MVertex *v0 = ge->getBeginVertex()->mesh_vertices[0];
840     v0->x() = beg_p.x();
841     v0->y() = beg_p.y();
842     v0->z() = beg_p.z();
843   }
844 
845   Msg::Debug("Meshing curve %d (%s): %li interior vertices", ge->tag(),
846              ge->getTypeString().c_str(), ge->mesh_vertices.size());
847 
848   ge->meshStatistics.status = GEdge::DONE;
849 }
850 
operator ()(GEdge * ge)851 void orientMeshGEdge::operator()(GEdge *ge)
852 {
853   // apply user-specified mesh orientation constraints
854   if(ge->meshAttributes.reverseMesh)
855     for(std::size_t k = 0; k < ge->getNumMeshElements(); k++)
856       ge->getMeshElement(k)->reverse();
857 }
858 
meshGEdgeTargetNumberOfPoints(GEdge * ge)859 int meshGEdgeTargetNumberOfPoints(GEdge *ge)
860 {
861   Range<double> bounds = ge->parBounds(0);
862   double t_begin = bounds.low();
863   double t_end = bounds.high();
864 
865   int N = 0;
866   std::vector<IntPoint> Points;
867   double a = 0.;
868   int filterMinimumN = 1;
869   meshGEdgeProcessing(ge, t_begin, t_end, N, Points, a, filterMinimumN);
870   return N;
871 }
872