gmsh-TingyuanDoc  0.1
An Open-Source Timing-driven Analytical Mixed-size FPGA Placer
OCCEdge.cpp
Go to the documentation of this file.
1 // Gmsh - Copyright (C) 1997-2022 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 <limits>
7 #include "GmshConfig.h"
8 #include "GmshMessage.h"
9 #include "GModel.h"
10 #include "GModelIO_OCC.h"
11 #include "OCCEdge.h"
12 #include "OCCFace.h"
13 #include "Context.h"
14 
15 #if defined(HAVE_OCC)
16 
17 #include <Standard_Version.hxx>
18 #include <TopoDS.hxx>
19 #include <Geom2dLProp_CLProps2d.hxx>
20 #include <Geom_BezierCurve.hxx>
21 #include <Geom_OffsetCurve.hxx>
22 #include <Geom_Ellipse.hxx>
23 #include <Geom_Parabola.hxx>
24 #include <Geom_Hyperbola.hxx>
25 #include <Geom_TrimmedCurve.hxx>
26 #include <Geom_Circle.hxx>
27 #include <Geom_Line.hxx>
28 #include <Geom_Conic.hxx>
29 #include <Geom_BSplineCurve.hxx>
30 #include <Bnd_Box.hxx>
31 #include <BRepLProp_CLProps.hxx>
32 #include <BRepBndLib.hxx>
33 #include <BRepAdaptor_Curve.hxx>
34 #include <BRepAdaptor_Surface.hxx>
35 #include <BRep_Builder.hxx>
36 #include <BOPTools_AlgoTools.hxx>
37 
38 OCCEdge::OCCEdge(GModel *m, TopoDS_Edge c, int num, GVertex *v1, GVertex *v2)
39  : GEdge(m, num, v1, v2), _c(c), _trimmed(nullptr)
40 {
41  // force orientation of internal/external edges: otherwise reverse will not
42  // produce the expected result
43  if(_c.Orientation() == TopAbs_INTERNAL ||
44  _c.Orientation() == TopAbs_EXTERNAL) {
45  _c = TopoDS::Edge(_c.Oriented(TopAbs_FORWARD));
46  }
47  _curve = BRep_Tool::Curve(_c, _s0, _s1);
48  // build the reverse curve
49  _c_rev = _c;
50  _c_rev.Reverse();
51 
52  _nbpoles = 0;
53 
54  if(!_curve.IsNull()) {
55  // initialize projector, with a little tolerance to converge on the boundary
56  // points
57  double umin = _s0;
58  double umax = _s1;
59  if(_v0 != _v1) {
60  const double du = umax - umin;
61  const double utol = std::max(fabs(du) * 1e-8, 1e-12);
62  umin -= utol;
63  umax += utol;
64  }
65  _projector.Init(_curve, umin, umax);
66 
67  // keep track of number of poles for drawing
68  if(_curve->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve))
69  _nbpoles = Handle(Geom_BSplineCurve)::DownCast(_curve)->NbPoles();
70  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_BezierCurve))
71  _nbpoles = Handle(Geom_BezierCurve)::DownCast(_curve)->NbPoles();
72  }
73 }
74 
75 void OCCEdge::delFace(GFace *f)
76 {
77  if(_trimmed == f) _trimmed = nullptr;
79 }
80 
81 SBoundingBox3d OCCEdge::bounds(bool fast)
82 {
83  if(CTX::instance()->geom.occBoundsUseSTL && stl_vertices_xyz.size()) {
84  // BRepBndLib can use the STL mesh if available, but unfortunately it
85  // enlarges the box with the mesh deflection tolerance and the shape
86  // tolerance, which makes it hard to get the expected minimal box in simple
87  // cases (e.g. for straight lines), and always leads to boxes that are too
88  // large; so we simply compute the box from the STL vertices. The downside
89  // of this approach is that the bbox might be *smaller* than the actual box
90  // for curved shapes, but this is preferable for us as boxes are mostly used
91  // to find/identify entities
92  SBoundingBox3d bbox;
93  for(std::size_t i = 0; i < stl_vertices_xyz.size(); i++)
94  bbox += stl_vertices_xyz[i];
95  return bbox;
96  }
97 
98  Bnd_Box b;
99  try {
100  BRepBndLib::Add(_c, b);
101  } catch(Standard_Failure &err) {
102  Msg::Error("OpenCASCADE exception %s", err.GetMessageString());
103  return SBoundingBox3d();
104  }
105  double xmin, ymin, zmin, xmax, ymax, zmax;
106  b.Get(xmin, ymin, zmin, xmax, ymax, zmax);
107  SBoundingBox3d bbox(xmin, ymin, zmin, xmax, ymax, zmax);
108  return bbox;
109 }
110 
111 Range<double> OCCEdge::parBounds(int i) const
112 {
113  return Range<double>(_s0, _s1);
114 }
115 
116 Range<double> OCCEdge::parBoundsOnFace(GFace *face) const
117 {
118  if(face->getNativeType() != GEntity::OpenCascadeModel || !degenerate(0)) {
119  return parBounds(0);
120  }
121 
122  const TopoDS_Face *s = (TopoDS_Face *)((OCCFace *)face->getNativePtr());
123  double s0, s1;
124  _curve2d = BRep_Tool::CurveOnSurface(_c, *s, s0, s1);
125  return {s0, s1};
126 }
127 
128 void OCCEdge::setTrimmed(OCCFace *f)
129 {
130  if(!_trimmed) {
131  _trimmed = f;
132  const TopoDS_Face *s = (TopoDS_Face *)_trimmed->getNativePtr();
133  _curve2d = BRep_Tool::CurveOnSurface(_c, *s, _s0, _s1);
134  if(_curve2d.IsNull()) _trimmed = nullptr;
135  }
136 }
137 
138 SPoint2 OCCEdge::reparamOnFace(const GFace *face, double epar, int dir) const
139 {
141  const GPoint pt = point(epar);
142  SPoint3 sp(pt.x(), pt.y(), pt.z());
143  return face->parFromPoint(sp);
144  }
145  else {
146  const TopoDS_Face *s = (TopoDS_Face *)face->getNativePtr();
147  double t0, t1;
148  Handle(Geom2d_Curve) c2d;
149 
150  if(dir == 1) { c2d = BRep_Tool::CurveOnSurface(_c, *s, t0, t1); }
151  else {
152  c2d = BRep_Tool::CurveOnSurface(_c_rev, *s, t0, t1);
153  }
154 
155  if(c2d.IsNull()) {
156  Msg::Warning("Curve %d is not on surface %d - computing closest point",
157  tag(), face->tag());
158  const GPoint pt = point(epar);
159  SPoint3 sp(pt.x(), pt.y(), pt.z());
160  double guess[2] = {0, 0};
161  GPoint pp = face->closestPoint(sp, guess);
162  return SPoint2(pp.u(), pp.v());
163  }
164 
165  double u, v;
166  gp_Pnt2d pnt = c2d->Value(epar);
167  pnt.Coord(u, v);
168 
169  // sometimes OCC miserably fails ...
170  if(CTX::instance()->geom.reparamOnFaceRobust) {
171  GPoint p1 = point(epar);
172  GPoint p2 = face->point(u, v);
173  double dx = p1.x() - p2.x();
174  double dy = p1.y() - p2.y();
175  double dz = p1.z() - p2.z();
176  if(sqrt(dx * dx + dy * dy + dz * dz) > CTX::instance()->geom.tolerance) {
177  Msg::Debug(
178  "Reparam on surface was inaccurate for curve %d on surface %d "
179  "at point %g",
180  tag(), face->tag(), epar);
181  Msg::Debug("On the surface %d local (%g %g) global (%g %g %g)",
182  face->tag(), u, v, p2.x(), p2.y(), p2.z());
183  Msg::Debug("On the curve %d local (%g) global (%g %g %g)", tag(), epar,
184  p1.x(), p1.y(), p1.z());
185  double guess[2] = {u, v};
186  GPoint pp = face->closestPoint(SPoint3(p1.x(), p1.y(), p1.z()), guess);
187  u = pp.u();
188  v = pp.v();
189 
190  GPoint p2 = face->point(u, v);
191  dx = p1.x() - p2.x();
192  dy = p1.y() - p2.y();
193  dz = p1.z() - p2.z();
194  if(sqrt(dx * dx + dy * dy + dz * dz) >
195  CTX::instance()->geom.tolerance) {
196  Msg::Warning(
197  "Closest point was inaccurate for curve %d on surface %d "
198  "at point %g",
199  tag(), face->tag(), epar);
200  Msg::Warning("On the surface %d local (%g %g) global (%g %g %g)",
201  face->tag(), u, v, p2.x(), p2.y(), p2.z());
202  Msg::Warning("On the curve %d local (%g) global (%g %g %g)", tag(),
203  epar, p1.x(), p1.y(), p1.z());
204  }
205  }
206  }
207  return SPoint2(u, v);
208  }
209 }
210 
211 bool OCCEdge::_project(const double p[3], double &u, double xyz[3]) const
212 {
213  if(_curve.IsNull()) {
214  Msg::Error("OpenCASCADE curve is null in projection");
215  return false;
216  }
217 
218  gp_Pnt pnt(p[0], p[1], p[2]);
219  _projector.Perform(pnt);
220 
221  if(!_projector.NbPoints()) {
222  Msg::Debug("Projection of point (%g, %g, %g) on curve %d failed", p[0],
223  p[1], p[2], tag());
224  return false;
225  }
226 
227  u = _projector.LowerDistanceParameter();
228 
229  if(u < _s0 || u > _s1)
230  Msg::Debug("Point projection is out of curve parameter bounds");
231 
232  if(xyz) {
233  pnt = _projector.NearestPoint();
234  xyz[0] = pnt.X();
235  xyz[1] = pnt.Y();
236  xyz[2] = pnt.Z();
237  }
238  return true;
239 }
240 
241 GPoint OCCEdge::closestPoint(const SPoint3 &qp, double &param) const
242 {
243  // less robust but can be faster
244  if(CTX::instance()->geom.occUseGenericClosestPoint)
245  return GEdge::closestPoint(qp, param);
246  double u, xyz[3];
247  if(_project(qp.data(), u, xyz))
248  return GPoint(xyz[0], xyz[1], xyz[2], this, u);
249  else
250  return GEdge::closestPoint(qp, param);
251 }
252 
253 double OCCEdge::parFromPoint(const SPoint3 &qp) const
254 {
255  // less robust but can be faster
256  if(CTX::instance()->geom.occUseGenericClosestPoint)
257  return GEdge::parFromPoint(qp);
258  double u;
259  if(_project(qp.data(), u, nullptr))
260  return u;
261  else
262  return GEdge::parFromPoint(qp);
263 }
264 
265 bool OCCEdge::containsPoint(const SPoint3 &pt) const
266 {
267  double u;
268  SPoint3 xyz;
269  if(_project(pt.data(), u, xyz.data())) {
270  const Standard_Real tolerance = BRep_Tool::Tolerance(_c);
271  if(pt.distance(xyz) <= tolerance) return true;
272  }
273  return false;
274 }
275 
276 bool OCCEdge::isSeam(const GFace *face) const
277 {
278  if(face->getNativeType() != GEntity::OpenCascadeModel) return false;
279  const TopoDS_Face *s = (TopoDS_Face *)face->getNativePtr();
280  // use IsClosed() variant taking Geom_Surface instead of TopoDS_Face as
281  // argument, as the latter also tests the STL triangulation if available,
282  // which can lead to different results depending if the STL mesh is available
283  // or not; e.g. it can return true on plane surfaces with an internal curve,
284  // which is not expected by Gmsh 2D meshing algorithms
285  TopLoc_Location l;
286  const Handle(Geom_Surface)& surf = BRep_Tool::Surface(*s, l);
287  bool ret = BRep_Tool::IsClosed(_c, surf, l);
288  return ret;
289 }
290 
291 GPoint OCCEdge::point(double par) const
292 {
293  if(_trimmed) {
294  double u, v;
295  _curve2d->Value(par).Coord(u, v);
296  return _trimmed->point(u, v);
297  }
298  else if(!_curve.IsNull()) {
299  gp_Pnt pnt = _curve->Value(par);
300  return GPoint(pnt.X(), pnt.Y(), pnt.Z(), this, par);
301  }
302  else if(degenerate(0)) {
303  return GPoint(getBeginVertex()->x(), getBeginVertex()->y(),
304  getBeginVertex()->z());
305  }
306  else {
307  Msg::Warning(
308  "OpenCASCADE curve %d is neither a 3D curve nor a trimmed curve", tag());
309  return GPoint(0, 0, 0);
310  }
311 }
312 
313 SVector3 OCCEdge::firstDer(double par) const
314 {
315  BRepAdaptor_Curve brepc(_c);
316  BRepLProp_CLProps prop(brepc, 1, 1e-5);
317  prop.SetParameter(par);
318  gp_Vec d1 = prop.D1();
319  return SVector3(d1.X(), d1.Y(), d1.Z());
320 }
321 
322 GEntity::GeomType OCCEdge::geomType() const
323 {
324  if(_curve.IsNull()) {
325  if(_curve2d.IsNull())
326  return Unknown;
327  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_Circle))
328  return Circle;
329  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_Line))
330  return Line;
331  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_Ellipse))
332  return Ellipse;
333  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_Parabola))
334  return Parabola;
335  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_Hyperbola))
336  return Hyperbola;
337  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
338  return TrimmedCurve;
339  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_OffsetCurve))
340  return OffsetCurve;
341  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve))
342  return BSpline;
343  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_BezierCurve))
344  return Bezier;
345  else if(_curve2d->DynamicType() == STANDARD_TYPE(Geom_Conic))
346  return Conic;
347  return Unknown;
348  }
349  else {
350  if(_curve->DynamicType() == STANDARD_TYPE(Geom_Circle))
351  return Circle;
352  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_Line))
353  return Line;
354  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_Parabola))
355  return Parabola;
356  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_Hyperbola))
357  return Hyperbola;
358  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
359  return TrimmedCurve;
360  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_OffsetCurve))
361  return OffsetCurve;
362  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_Ellipse))
363  return Ellipse;
364  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve))
365  return BSpline;
366  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_BezierCurve))
367  return Bezier;
368  else if(_curve->DynamicType() == STANDARD_TYPE(Geom_Conic))
369  return Conic;
370  return Unknown;
371  }
372 }
373 
374 int OCCEdge::minimumMeshSegments() const
375 {
376  int np;
377 
378  // if it is a seam, then return 1 - but why? removed this in Gmsh 4.10.0
379  //if(_faces.size() == 1 && isSeam(_faces[0])) return 1;
380 
381  if(geomType() == Line) { np = CTX::instance()->mesh.minLineNodes - 1; }
382  else if(geomType() == Circle || geomType() == Ellipse) {
383  double a = fabs(_s0 - _s1);
384  double n = CTX::instance()->mesh.minCircleNodes;
385  if(a > 6.28)
386  np = n;
387  else
388  np = (int)(0.99 + (n - 1) * a / (2 * M_PI));
389  }
390  else {
391  np = CTX::instance()->mesh.minCurveNodes - 1;
392  }
393 
394  // if the edge is closed, ensure that at least 3 points are generated in the
395  // 1D mesh (4 segments, one of which is degenerated)
396  if(getBeginVertex() == getEndVertex()) np = std::max(4, np);
397 
398  return std::max(np, meshAttributes.minimumMeshSegments);
399 }
400 
401 int OCCEdge::minimumDrawSegments() const
402 {
403  int n = _nbpoles;
404  if(n <= 0) n = GEdge::minimumDrawSegments();
405 
406  if(geomType() == Line)
407  return n;
408  else
409  return CTX::instance()->geom.numSubEdges * n;
410 }
411 
412 double OCCEdge::curvature(double par) const
413 {
414  const double eps = 1.e-15;
415 
416  if(degenerate(0)) return eps;
417 
418  Standard_Real Crv;
419  if(_curve.IsNull()) {
420  Geom2dLProp_CLProps2d aCLProps(_curve2d, 2, eps);
421  aCLProps.SetParameter(par);
422  if(!aCLProps.IsTangentDefined())
423  Crv = eps;
424  else
425  Crv = aCLProps.Curvature();
426  }
427  else {
428  BRepAdaptor_Curve brepc(_c);
429  BRepLProp_CLProps prop(brepc, 2, eps);
430  prop.SetParameter(par);
431  if(!prop.IsTangentDefined())
432  Crv = eps;
433  else
434  Crv = prop.Curvature();
435  }
436  if(Crv <= eps) Crv = eps;
437  return Crv;
438 }
439 
440 void OCCEdge::writeGEO(FILE *fp)
441 {
442  if(geomType() == Circle) {
443  gp_Pnt center;
444  if(_curve.IsNull()) {
445  center = Handle(Geom_Circle)::DownCast(_curve2d)->Location();
446  }
447  else {
448  center = Handle(Geom_Circle)::DownCast(_curve)->Location();
449  }
450  // GEO supports only circle arcs < Pi
451  if(_s1 - _s0 < M_PI && getBeginVertex() && getEndVertex()) {
452  fprintf(fp, "p%d = newp;\n", tag());
453  fprintf(fp, "Point(p%d + 1) = {%.16g, %.16g, %.16g};\n", tag(),
454  center.X(), center.Y(), center.Z());
455  fprintf(fp, "Circle(%d) = {%d, p%d + 1, %d};\n", tag(),
456  getBeginVertex()->tag(), tag(), getEndVertex()->tag());
457  }
458  else
459  GEdge::writeGEO(fp);
460  }
461  else
462  GEdge::writeGEO(fp);
463 }
464 
465 #endif
GPoint::y
double y() const
Definition: GPoint.h:22
contextGeometryOptions::numSubEdges
int numSubEdges
Definition: Context.h:117
GFace
Definition: GFace.h:33
SPoint2
Definition: SPoint2.h:12
GEdge::minimumDrawSegments
virtual int minimumDrawSegments() const
Definition: GEdge.h:151
Msg::Debug
static void Debug(const char *fmt,...)
Definition: GmshMessage.cpp:752
c
static double c(int i, int j, fullMatrix< double > &CA, const std::vector< SPoint3 > &P, const std::vector< SPoint3 > &Q)
Definition: discreteFrechetDistance.cpp:15
Msg::Warning
static void Warning(const char *fmt,...)
Definition: GmshMessage.cpp:543
GEntity::OpenCascadeModel
@ OpenCascadeModel
Definition: GEntity.h:82
Msg::Error
static void Error(const char *fmt,...)
Definition: GmshMessage.cpp:482
OCCFace.h
SPoint3
Definition: SPoint3.h:14
LegendrePolynomials::f
void f(int n, double u, double *val)
Definition: orthogonalBasis.cpp:77
GEdge::writeGEO
virtual void writeGEO(FILE *fp)
Definition: GEdge.cpp:354
GFace::point
virtual GPoint point(double par1, double par2) const =0
SVector3
Definition: SVector3.h:16
GModelIO_OCC.h
GEntity::getNativeType
virtual ModelType getNativeType() const
Definition: GEntity.h:268
GmshMessage.h
GPoint
Definition: GPoint.h:13
GFace::closestPoint
virtual GPoint closestPoint(const SPoint3 &queryPoint, const double initialGuess[2]) const
Definition: GFace.cpp:1323
contextMeshOptions::minLineNodes
int minLineNodes
Definition: Context.h:37
GPoint::z
double z() const
Definition: GPoint.h:23
GEntity::getNativePtr
virtual void * getNativePtr() const
Definition: GEntity.h:271
CTX::instance
static CTX * instance()
Definition: Context.cpp:122
GEdge::delFace
virtual void delFace(GFace *f)
Definition: GEdge.cpp:206
OCCEdge.h
Range
Definition: Range.h:10
SPoint3::data
const double * data() const
Definition: SPoint3.h:76
GPoint::u
double u() const
Definition: GPoint.h:27
GVertex
Definition: GVertex.h:23
tolerance
#define tolerance
Definition: curvature.cpp:11
GModel
Definition: GModel.h:44
GFace::parFromPoint
virtual SPoint2 parFromPoint(const SPoint3 &, bool onSurface=true, bool convTestXYZ=false) const
Definition: GFace.cpp:1254
contextMeshOptions::minCurveNodes
int minCurveNodes
Definition: Context.h:37
CTX::mesh
contextMeshOptions mesh
Definition: Context.h:313
GEntity::tag
int tag() const
Definition: GEntity.h:280
CTX::geom
contextGeometryOptions geom
Definition: Context.h:311
GEntity::GeomType
GeomType
Definition: GEntity.h:88
contextMeshOptions::minCircleNodes
int minCircleNodes
Definition: Context.h:37
SPoint3::distance
double distance(const SPoint3 &p) const
Definition: SPoint3.h:176
GEdge::closestPoint
virtual GPoint closestPoint(const SPoint3 &queryPoint, double &param) const
Definition: GEdge.cpp:552
Context.h
z
const double z
Definition: GaussQuadratureQuad.cpp:56
GEdge
Definition: GEdge.h:26
GPoint::v
double v() const
Definition: GPoint.h:28
GModel.h
GPoint::x
double x() const
Definition: GPoint.h:21
GEdge::parFromPoint
virtual double parFromPoint(const SPoint3 &P) const
Definition: GEdge.cpp:595
SBoundingBox3d
Definition: SBoundingBox3d.h:21
closestPoint
double closestPoint(const std::vector< SPoint3 > &P, const SPoint3 &p, SPoint3 &result)
Definition: hausdorffDistance.cpp:50