From da80b2e06555cdf9d15f0b5573e42e9922019945 Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@uliege.be>
Date: Sun, 8 May 2022 18:29:04 +0200
Subject: [PATCH] use brute-force calculation of bboxes if
 Geometry.OCCBoundsUseStl is set: OCC's algorithm enlarges the box by a factor
 that is hard to predict (combining STL deflection and shape tolerance). While
 this is safe, it's annoying for our use of bboxes. So let's simply compute
 the bounds of using the STL nodes and be done with it.

---
 CHANGELOG.txt            |  3 ++-
 src/geo/GModelIO_OCC.cpp | 44 ++++++++++++++++++----------------------
 src/geo/GModelIO_OCC.h   |  2 --
 src/geo/OCCEdge.cpp      | 20 +++++++++++++-----
 src/geo/OCCFace.cpp      | 23 +++++++++++++++------
 src/geo/OCCRegion.cpp    | 14 ++++++-------
 6 files changed, 60 insertions(+), 46 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index bc965d74c9..ff0a84bfd3 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,5 +1,6 @@
 4.10.2 (Work-in-progress): fixed regression introduced in 4.9 for recombined
-meshes with boundary layers; small bug fixes.
+meshes with boundary layers; new HealShapes command in .geo files; simplified
+calculation of OCC STL bounding boxes; small bug fixes.
 
 4.10.1 (May 1, 2022): small bug fixes.
 
diff --git a/src/geo/GModelIO_OCC.cpp b/src/geo/GModelIO_OCC.cpp
index 7fc3bb0697..d99b082d20 100644
--- a/src/geo/GModelIO_OCC.cpp
+++ b/src/geo/GModelIO_OCC.cpp
@@ -4489,6 +4489,26 @@ bool OCC_Internals::_getBoundingBox(const TopoDS_Shape &shape, double &xmin,
     std::vector<SVector3> normals;
     std::vector<int> triangles;
     _makeSTL(shape, vertices, normals, triangles);
+    // BRepBndLib can use the STL mesh if available, but unfortunately it
+    // enlarges the box with the mesh deflection tolerance and the shape
+    // tolerance, which makes it hard to get the expected minimal box in simple
+    // cases (e.g. for plane surfaces), and always leads to boxes that are too
+    // large; so we simply compute the box from the STL vertices. The downside
+    // of this approach is that the bbox might be *smaller* than the actual box
+    // for curved shapes, but this is preferable for us as boxes are mostly used
+    // to find/identify entities
+    if(vertices.size()) {
+      SBoundingBox3d bbox;
+      for(std::size_t i = 0; i < vertices.size(); i++)
+        bbox += vertices[i];
+      xmin = bbox.min().x();
+      ymin = bbox.min().y();
+      zmin = bbox.min().z();
+      xmax = bbox.max().x();
+      ymax = bbox.max().y();
+      zmax = bbox.max().z();
+      return true;
+    }
   }
   Bnd_Box b;
   try {
@@ -4498,8 +4518,6 @@ bool OCC_Internals::_getBoundingBox(const TopoDS_Shape &shape, double &xmin,
     return false;
   }
   b.Get(xmin, ymin, zmin, xmax, ymax, zmax);
-  if(CTX::instance()->geom.occBoundsUseSTL)
-    fixSTLBounds(xmin, ymin, zmin, xmax, ymax, zmax);
   return true;
 }
 
@@ -5745,28 +5763,6 @@ bool OCC_Internals::makeTorusSTL(double x, double y, double z, double r1,
   return true;
 }
 
-void OCC_Internals::fixSTLBounds(double &xmin, double &ymin, double &zmin,
-                                 double &xmax, double &ymax, double &zmax)
-{
-  // When an STL exists, OCC enlarges the bounding box by the allowed linear
-  // deflection given to BRepMesh_IncrementalMesh. This is "safe", but on simple
-  // polyhedral geometries (a cube!) it will consistently lead to enlarging the
-  // bounding box by twice this value in all directions. Since we use bounds()
-  // mostly for locating entities, it's better to remove the tolerance (with the
-  // risk that the bbox is a bit too small for curved boundaries - but that's
-  // fine)
-  double eps = CTX::instance()->mesh.stlLinearDeflection;
-  // OCC also enlarges the bounding box by Precision::Confusion(): remove it as
-  // well
-  eps += Precision::Confusion();
-  xmin += eps;
-  xmax -= eps;
-  ymin += eps;
-  ymax -= eps;
-  zmin += eps;
-  zmax -= eps;
-}
-
 #endif
 
 void GModel::createOCCInternals()
diff --git a/src/geo/GModelIO_OCC.h b/src/geo/GModelIO_OCC.h
index 71c388ece9..6e7b976409 100644
--- a/src/geo/GModelIO_OCC.h
+++ b/src/geo/GModelIO_OCC.h
@@ -460,8 +460,6 @@ public:
                     double angle, std::vector<SPoint3> &vertices,
                     std::vector<SVector3> &normals,
                     std::vector<int> &triangles);
-  void fixSTLBounds(double &xmin, double &ymin, double &zmin, double &xmax,
-                    double &ymax, double &zmax);
 };
 
 #else
diff --git a/src/geo/OCCEdge.cpp b/src/geo/OCCEdge.cpp
index b62ca7a852..b08cff67d2 100644
--- a/src/geo/OCCEdge.cpp
+++ b/src/geo/OCCEdge.cpp
@@ -72,6 +72,21 @@ void OCCEdge::delFace(GFace *f)
 
 SBoundingBox3d OCCEdge::bounds(bool fast)
 {
+  if(CTX::instance()->geom.occBoundsUseSTL && stl_vertices_xyz.size()) {
+    // BRepBndLib can use the STL mesh if available, but unfortunately it
+    // enlarges the box with the mesh deflection tolerance and the shape
+    // tolerance, which makes it hard to get the expected minimal box in simple
+    // cases (e.g. for straight lines), and always leads to boxes that are too
+    // large; so we simply compute the box from the STL vertices. The downside
+    // of this approach is that the bbox might be *smaller* than the actual box
+    // for curved shapes, but this is preferable for us as boxes are mostly used
+    // to find/identify entities
+    SBoundingBox3d bbox;
+    for(std::size_t i = 0; i < stl_vertices_xyz.size(); i++)
+      bbox += stl_vertices_xyz[i];
+    return bbox;
+  }
+
   Bnd_Box b;
   try {
     BRepBndLib::Add(_c, b);
@@ -81,11 +96,6 @@ SBoundingBox3d OCCEdge::bounds(bool fast)
   }
   double xmin, ymin, zmin, xmax, ymax, zmax;
   b.Get(xmin, ymin, zmin, xmax, ymax, zmax);
-
-  if(CTX::instance()->geom.occBoundsUseSTL)
-    model()->getOCCInternals()->fixSTLBounds(xmin, ymin, zmin, xmax, ymax,
-                                             zmax);
-
   SBoundingBox3d bbox(xmin, ymin, zmin, xmax, ymax, zmax);
   return bbox;
 }
diff --git a/src/geo/OCCFace.cpp b/src/geo/OCCFace.cpp
index 538d867230..ff5654b2d8 100644
--- a/src/geo/OCCFace.cpp
+++ b/src/geo/OCCFace.cpp
@@ -193,7 +193,23 @@ void OCCFace::_setup()
 
 SBoundingBox3d OCCFace::bounds(bool fast)
 {
-  if(CTX::instance()->geom.occBoundsUseSTL) buildSTLTriangulation();
+  if(CTX::instance()->geom.occBoundsUseSTL) {
+    buildSTLTriangulation();
+    // BRepBndLib can use the STL mesh if available, but unfortunately it
+    // enlarges the box with the mesh deflection tolerance and the shape
+    // tolerance, which makes it hard to get the expected minimal box in simple
+    // cases (e.g. for plane surfaces), and always leads to boxes that are too
+    // large; so we simply compute the box from the STL vertices. The downside
+    // of this approach is that the bbox might be *smaller* than the actual box
+    // for curved shapes, but this is preferable for us as boxes are mostly used
+    // to find/identify entities
+    if(stl_vertices_xyz.size()) {
+      SBoundingBox3d bbox;
+      for(std::size_t i = 0; i < stl_vertices_xyz.size(); i++)
+        bbox += stl_vertices_xyz[i];
+      return bbox;
+    }
+  }
 
   Bnd_Box b;
   try {
@@ -204,11 +220,6 @@ SBoundingBox3d OCCFace::bounds(bool fast)
   }
   double xmin, ymin, zmin, xmax, ymax, zmax;
   b.Get(xmin, ymin, zmin, xmax, ymax, zmax);
-
-  if(CTX::instance()->geom.occBoundsUseSTL)
-    model()->getOCCInternals()->fixSTLBounds(xmin, ymin, zmin, xmax, ymax,
-                                             zmax);
-
   SBoundingBox3d bbox(xmin, ymin, zmin, xmax, ymax, zmax);
   return bbox;
 }
diff --git a/src/geo/OCCRegion.cpp b/src/geo/OCCRegion.cpp
index b979d87768..3a1b39d37b 100644
--- a/src/geo/OCCRegion.cpp
+++ b/src/geo/OCCRegion.cpp
@@ -97,10 +97,13 @@ void OCCRegion::_setup()
 SBoundingBox3d OCCRegion::bounds(bool fast)
 {
   if(CTX::instance()->geom.occBoundsUseSTL) {
-    // if a triangulation exist on a shape, OCC will use it to compute more
-    // accurate bounds
     std::vector<GFace *> f = faces();
-    for(std::size_t i = 0; i < f.size(); i++) f[i]->buildSTLTriangulation();
+    SBoundingBox3d bbox;
+    for(std::size_t i = 0; i < f.size(); i++) {
+      f[i]->buildSTLTriangulation();
+      bbox += f[i]->bounds();
+    }
+    return bbox;
   }
 
   Bnd_Box b;
@@ -112,11 +115,6 @@ SBoundingBox3d OCCRegion::bounds(bool fast)
   }
   double xmin, ymin, zmin, xmax, ymax, zmax;
   b.Get(xmin, ymin, zmin, xmax, ymax, zmax);
-
-  if(CTX::instance()->geom.occBoundsUseSTL)
-    model()->getOCCInternals()->fixSTLBounds(xmin, ymin, zmin, xmax, ymax,
-                                             zmax);
-
   SBoundingBox3d bbox(xmin, ymin, zmin, xmax, ymax, zmax);
   return bbox;
 }
-- 
GitLab