Salome HOME
[bos #32517][EDF] Dynamic log messages switched on and off by SALOME_VERBOSE environm...
[modules/geom.git] / src / GEOMUtils / GEOMUtils.cxx
index 5b5cc2574a167a061354e90e033e212f285b127f..24d7318c0ef8ce06a30f3df0287aa693ca0570b3 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
 
 #include <GEOMUtils.hxx>
 
-#include <Basics_OCCTVersion.hxx>
-
-#include <utilities.h>
-#include <OpUtil.hxx>
 #include <Utils_ExceptHandlers.hxx>
 
 // OCCT Includes
 #include <BRepGProp.hxx>
 #include <BRepTools.hxx>
 
+#include <BRepClass_FaceClassifier.hxx>
 #include <BRepClass3d_SolidClassifier.hxx>
 
 #include <BRepBuilderAPI_MakeFace.hxx>
+#include <BRepBuilderAPI_Sewing.hxx>
+
+#include <BRepCheck_Analyzer.hxx>
 
 #include <Bnd_Box.hxx>
 
+#include <BOPAlgo_ArgumentAnalyzer.hxx>
+#include <BOPTools_AlgoTools.hxx>
+
 #include <TopAbs.hxx>
 #include <TopExp.hxx>
 #include <TopoDS.hxx>
@@ -62,6 +65,8 @@
 #include <TopTools_ListIteratorOfListOfShape.hxx>
 #include <TopTools_Array1OfShape.hxx>
 
+#include <GeomAPI_ProjectPointOnSurf.hxx>
+
 #include <Geom_Circle.hxx>
 #include <Geom_Surface.hxx>
 #include <Geom_Plane.hxx>
 
 #include <ShapeAnalysis.hxx>
 #include <ShapeFix_Shape.hxx>
+#include <ShapeFix_ShapeTolerance.hxx>
 
 #include <ProjLib.hxx>
 #include <ElSLib.hxx>
 
 #include <vector>
 #include <sstream>
+#include <algorithm>
+
+#include <V3d_Coordinate.hxx>
 
 #include <Standard_Failure.hxx>
 #include <Standard_NullObject.hxx>
 #include <Standard_ErrorHandler.hxx> // CAREFUL ! position of this file is critic : see Lucien PIGNOLONI / OCC
 
+#define MAX2(X, Y)    (Abs(X) > Abs(Y) ? Abs(X) : Abs(Y))
+#define MAX3(X, Y, Z) (MAX2(MAX2(X,Y), Z))
+
 #define STD_SORT_ALGO 1
 
+#define DEFAULT_TOLERANCE_TOLERANCE     1.e-02
+#define DEFAULT_MAX_TOLERANCE_TOLERANCE 1.e-06
+
+// When the following macro is defined, ShapeFix_ShapeTolerance function is used to set max tolerance of curve
+// in GEOMUtils::FixShapeCurves function; otherwise less restrictive BRep_Builder::UpdateEdge/UpdateVertex
+// approach is used
+// VSR (29/12/2014): macro disabled
+//#define USE_LIMIT_TOLERANCE
+
 namespace
 {
   /**
@@ -112,7 +133,6 @@ namespace
                                TopoDS_Shape  &theModifiedShape,
                                Standard_Real &theAddDist)
   {
-    Standard_Boolean isModified = Standard_False;
     TopExp_Explorer anExp;
     int nbf = 0;
 
@@ -136,80 +156,74 @@ namespace
         const Standard_Boolean isShell =
           (sh.ShapeType()==TopAbs_SHELL || sh.ShapeType()==TopAbs_FACE);
 
-        if( isShell || S->IsUPeriodic() ) {
-          // non solid case or any periodic surface (Mantis 22454).
-          double U1,U2,V1,V2;
-          // changes for 0020677: EDF 1219 GEOM: MinDistance gives 0 instead of 20.88
-          //S->Bounds(U1,U2,V1,V2); changed by
-          ShapeAnalysis::GetFaceUVBounds(TopoDS::Face(theModifiedShape),U1,U2,V1,V2);
-          // end of changes for 020677 (dmv)
-          Handle(Geom_RectangularTrimmedSurface) TrS1 =
-            new Geom_RectangularTrimmedSurface(S,U1,(U1+U2)/2.,V1,V2);
-          Handle(Geom_RectangularTrimmedSurface) TrS2 =
-            new Geom_RectangularTrimmedSurface(S,(U1+U2)/2.,U2,V1,V2);
+        if ( !isShell && S->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) ) {
+          Handle(Geom_SphericalSurface) SS = Handle(Geom_SphericalSurface)::DownCast(S);
+          gp_Pnt PC = SS->Location();
           BRep_Builder B;
-          TopoDS_Face F1,F2;
-          TopoDS_Shape aMShape;
-
-          if (isShell) {
-            B.MakeCompound(TopoDS::Compound(aMShape));
-          } else {
-            B.MakeShell(TopoDS::Shell(aMShape));
-          }
-
-          B.MakeFace(F1,TrS1,1.e-7);
-          B.Add(aMShape,F1);
-          B.MakeFace(F2,TrS2,1.e-7);
-          B.Add(aMShape,F2);
-          Handle(ShapeFix_Shape) sfs = new ShapeFix_Shape;
-
-          if (!isShell) {
-            // The original shape is a solid.
-            TopoDS_Solid aSolid;
-
-            B.MakeSolid(aSolid);
-            B.Add(aSolid, aMShape);
-            aMShape = aSolid;
-          }
-
-          sfs->Init(aMShape);
-          sfs->SetPrecision(1.e-6);
-          sfs->SetMaxTolerance(1.0);
-          sfs->Perform();
-          theModifiedShape = sfs->Shape();
-          isModified = Standard_True;
+          TopoDS_Vertex V;
+          B.MakeVertex(V,PC,1.e-7);
+          theModifiedShape = V;
+          theAddDist = SS->Radius();
+          return Standard_True;
         }
-        else {
-          if( S->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) ) {
-            Handle(Geom_SphericalSurface) SS = Handle(Geom_SphericalSurface)::DownCast(S);
-            gp_Pnt PC = SS->Location();
-            BRep_Builder B;
-            TopoDS_Vertex V;
-            B.MakeVertex(V,PC,1.e-7);
-            theModifiedShape = V;
-            theAddDist = SS->Radius();
-            isModified = Standard_True;
-          }
-          else {
-            Handle(Geom_ToroidalSurface) TS = Handle(Geom_ToroidalSurface)::DownCast(S);
-            gp_Ax3 ax3 = TS->Position();
-            Handle(Geom_Circle) C = new Geom_Circle(ax3.Ax2(),TS->MajorRadius());
-            BRep_Builder B;
-            TopoDS_Edge E;
-            B.MakeEdge(E,C,1.e-7);
-            theModifiedShape = E;
-            theAddDist = TS->MinorRadius();
-            isModified = Standard_True;
-          }
+        if ( !isShell && S->IsKind(STANDARD_TYPE(Geom_ToroidalSurface)) ) {
+          Handle(Geom_ToroidalSurface) TS = Handle(Geom_ToroidalSurface)::DownCast(S);
+          gp_Ax3 ax3 = TS->Position();
+          Handle(Geom_Circle) C = new Geom_Circle(ax3.Ax2(),TS->MajorRadius());
+          BRep_Builder B;
+          TopoDS_Edge E;
+          B.MakeEdge(E,C,1.e-7);
+          theModifiedShape = E;
+          theAddDist = TS->MinorRadius();
+          return Standard_True;
         }
-      } else {
-        theModifiedShape = theShape;
+
+        // non solid case or any periodic surface (Mantis 22454).
+        double U1,U2,V1,V2;
+        // changes for 0020677: EDF 1219 GEOM: MinDistance gives 0 instead of 20.88
+        //S->Bounds(U1,U2,V1,V2); changed by
+        ShapeAnalysis::GetFaceUVBounds(TopoDS::Face(theModifiedShape),U1,U2,V1,V2);
+        // end of changes for 020677 (dmv)
+        Handle(Geom_RectangularTrimmedSurface) TrS1 =
+          new Geom_RectangularTrimmedSurface(S,U1,(U1+U2)/2.,V1,V2);
+        Handle(Geom_RectangularTrimmedSurface) TrS2 =
+          new Geom_RectangularTrimmedSurface(S,(U1+U2)/2.,U2,V1,V2);
+        TopoDS_Shape aMShape;
+        
+        TopoDS_Face F1 = BRepBuilderAPI_MakeFace(TrS1, Precision::Confusion());
+        TopoDS_Face F2 = BRepBuilderAPI_MakeFace(TrS2, Precision::Confusion());
+        
+        if (isShell) {
+          BRep_Builder B;
+          B.MakeCompound(TopoDS::Compound(aMShape));
+          B.Add(aMShape, F1);
+          B.Add(aMShape, F2);
+        } else {
+          // The original shape is a solid.
+          BRepBuilderAPI_Sewing aSewing (Precision::Confusion()*10.0);
+          aSewing.Add(F1);
+          aSewing.Add(F2);
+          aSewing.Perform();
+          aMShape = aSewing.SewedShape();
+          BRep_Builder B;
+          TopoDS_Solid aSolid;
+          B.MakeSolid(aSolid);
+          B.Add(aSolid, aMShape);
+          aMShape = aSolid;
+        }
+        
+        Handle(ShapeFix_Shape) sfs = new ShapeFix_Shape;
+        sfs->Init(aMShape);
+        sfs->SetPrecision(1.e-6);
+        sfs->SetMaxTolerance(1.0);
+        sfs->Perform();
+        theModifiedShape = sfs->Shape();
+        return Standard_True;
       }
     }
-    else
-      theModifiedShape = theShape;
-
-    return isModified;
+    
+    theModifiedShape = theShape;
+    return Standard_False;
   }
 
   void parseWard( const GEOMUtils::LevelsList &theLevelList, std::string &treeStr )
@@ -250,7 +264,7 @@ namespace
         levelsListStr.push_back( substr );
     }
     GEOMUtils::LevelsList levelsListData;
-    for( int level = 0; level < levelsListStr.size(); level++ ) {
+    for( size_t level = 0; level < levelsListStr.size(); level++ ) {
       std::vector<std::string> namesListStr;
       std::stringstream ss1( levelsListStr[level] );
       while ( std::getline( ss1, substr, ',' ) ) {
@@ -258,7 +272,7 @@ namespace
           namesListStr.push_back( substr );
       }
       GEOMUtils::LevelInfo levelInfoData;
-      for( int node = 0; node < namesListStr.size(); node++ ) {
+      for( size_t node = 0; node < namesListStr.size(); node++ ) {
         std::vector<std::string> linksListStr;
         std::stringstream ss2( namesListStr[node] );
         while ( std::getline( ss2, substr, '_' ) ) {
@@ -268,7 +282,7 @@ namespace
         std::string nodeItem = linksListStr[0];
         if( !nodeItem.empty() ) {
           GEOMUtils::NodeLinks linksListData;
-          for( int link = 1; link < linksListStr.size(); link++ ) {
+          for( size_t link = 1; link < linksListStr.size(); link++ ) {
             std::string linkItem = linksListStr[link];
             linksListData.push_back( linkItem );
           }// Links
@@ -982,12 +996,13 @@ Standard_Real GEOMUtils::GetMinDistance
 
   // skl 30.06.2008
   // additional workaround for bugs 19899, 19908 and 19910 from Mantis
-  double dist = GEOMUtils::GetMinDistanceSingular
-      (theShape1, theShape2, thePnt1, thePnt2);
+  aResult = GEOMUtils::GetMinDistanceSingular(theShape1, theShape2, thePnt1, thePnt2);
 
+  /*
   if (dist > -1.0) {
     return dist;
   }
+  */
 
   BRepExtrema_DistShapeShape dst (theShape1, theShape2);
   if (dst.IsDone()) {
@@ -998,7 +1013,7 @@ Standard_Real GEOMUtils::GetMinDistance
       P2 = dst.PointOnShape2(i);
 
       Standard_Real Dist = P1.Distance(P2);
-      if (aResult > Dist) {
+      if (aResult < 0 || aResult > Dist) {
         aResult = Dist;
         thePnt1 = P1;
         thePnt2 = P2;
@@ -1009,23 +1024,82 @@ Standard_Real GEOMUtils::GetMinDistance
   return aResult;
 }
 
+//=======================================================================
+// function : ProjectPointOnFace()
+// purpose  : Returns the projection (3d point) if found, throws an exception otherwise
+//=======================================================================
+gp_Pnt GEOMUtils::ProjectPointOnFace(const gp_Pnt& thePoint,
+                                     const TopoDS_Shape& theFace,
+                                     double& theU, double& theV)
+{
+  if (theFace.IsNull() || theFace.ShapeType() != TopAbs_FACE)
+    Standard_TypeMismatch::Raise
+      ("Projection aborted : the target shape is not a face");
+
+  TopoDS_Face aFace = TopoDS::Face(theFace);
+  Handle(Geom_Surface) surface = BRep_Tool::Surface(aFace);
+  double U1, U2, V1, V2;
+  BRepTools::UVBounds(aFace, U1, U2, V1, V2);
+
+  // projector
+  Standard_Real tol = 1.e-4;        
+  GeomAPI_ProjectPointOnSurf proj;
+  proj.Init(surface, U1, U2, V1, V2, tol);
+  proj.Perform(thePoint);
+  if (!proj.IsDone())
+    StdFail_NotDone::Raise("Projection aborted : the algorithm failed");
+  int nbPoints = proj.NbPoints();
+  if (nbPoints < 1)
+    Standard_ConstructionError::Raise("Projection aborted : No solution found");
+  proj.LowerDistanceParameters(theU, theV);
+  gp_Pnt2d aProjPnt (theU, theV);
+
+  // classifier
+  BRepClass_FaceClassifier aClsf (aFace, aProjPnt, tol);
+  if (aClsf.State() != TopAbs_IN && aClsf.State() != TopAbs_ON) {
+    bool isSol = false;
+    double minDist = RealLast();
+    for (int i = 1; i <= nbPoints; i++) {
+      Standard_Real Ui, Vi;
+      proj.Parameters(i, Ui, Vi);
+      aProjPnt = gp_Pnt2d(Ui, Vi);
+      aClsf.Perform(aFace, aProjPnt, tol);
+      if (aClsf.State() == TopAbs_IN || aClsf.State() == TopAbs_ON) {
+        isSol = true;
+        double dist = proj.Distance(i);
+        if (dist < minDist) {
+          minDist = dist;
+          theU = Ui;
+          theV = Vi;
+        }
+      }
+    }
+    if (!isSol) {
+      Standard_ConstructionError::Raise("Projection aborted : No solution found");
+    }
+  }
+
+  gp_Pnt surfPnt = surface->Value(theU, theV);
+  return surfPnt;
+}
+
 //=======================================================================
 // function : ConvertClickToPoint()
 // purpose  : Returns the point clicked in 3D view
 //=======================================================================
 gp_Pnt GEOMUtils::ConvertClickToPoint( int x, int y, Handle(V3d_View) aView )
 {
-  V3d_Coordinate XEye, YEye, ZEye, XAt, YAt, ZAt;
-  aView->Eye( XEye, YEye, ZEye );
+  // We can't rely on Eye and At points to get EyeDir, because they collapse
+  // for views with flat bounding boxes. For example if you create a circle and
+  // switch to the top view, then both camera's Eye and At points will fall to zero
+  // inside V3d_View::FitMinMax() method. It's by occt design.
+  // So, we should use camera direction instead.
 
-  aView->At( XAt, YAt, ZAt );
-  gp_Pnt EyePoint( XEye, YEye, ZEye );
-  gp_Pnt AtPoint( XAt, YAt, ZAt );
+  V3d_Coordinate XAt, YAt, ZAt;
+  aView->At(XAt, YAt, ZAt);
+  gp_Pnt AtPoint(XAt, YAt, ZAt);
 
-  gp_Vec EyeVector( EyePoint, AtPoint );
-  gp_Dir EyeDir( EyeVector );
-
-  gp_Pln PlaneOfTheView = gp_Pln( AtPoint, EyeDir );
+  gp_Pln PlaneOfTheView = gp_Pln( AtPoint, aView->Camera()->Direction() );
   Standard_Real X, Y, Z;
   aView->Convert( x, y, X, Y, Z );
   gp_Pnt ConvertedPoint( X, Y, Z );
@@ -1039,19 +1113,19 @@ gp_Pnt GEOMUtils::ConvertClickToPoint( int x, int y, Handle(V3d_View) aView )
 // function : ConvertTreeToString()
 // purpose  : Returns the string representation of dependency tree
 //=======================================================================
-void GEOMUtils::ConvertTreeToString( const TreeModel &tree,
-                                     std::string &treeStr )
+void GEOMUtils::ConvertTreeToString( const TreeModeltree,
+                                     std::string& dependencyStr )
 {
   TreeModel::const_iterator i;
   for ( i = tree.begin(); i != tree.end(); ++i ) {
-    treeStr.append( i->first );
-    treeStr.append( "-" );
+    dependencyStr.append( i->first );
+    dependencyStr.append( "-" );
     std::vector<LevelInfo> upLevelList = i->second.first;
-    treeStr.append( "upward" );
-    parseWard( upLevelList, treeStr );
+    dependencyStr.append( "upward" );
+    parseWard( upLevelList, dependencyStr );
     std::vector<LevelInfo> downLevelList = i->second.second;
-    treeStr.append( "downward" );
-    parseWard( downLevelList, treeStr );
+    dependencyStr.append( "downward" );
+    parseWard( downLevelList, dependencyStr );
   }
 }
 
@@ -1059,23 +1133,298 @@ void GEOMUtils::ConvertTreeToString( const TreeModel &tree,
 // function : ConvertStringToTree()
 // purpose  : Returns the dependency tree
 //=======================================================================
-void GEOMUtils::ConvertStringToTree( const std::string &theData,
-                                     TreeModel &tree )
+void GEOMUtils::ConvertStringToTree( const std::string& dependencyStr,
+                                     TreeModeltree )
 {
   std::size_t cursor = 0;
 
-  while( theData.find('-',cursor) != std::string::npos ) //find next selected object
+  while( dependencyStr.find('-',cursor) != std::string::npos ) //find next selected object
   {
-    std::size_t objectIndex = theData.find( '-', cursor );
-    std::string objectEntry = theData.substr( cursor, objectIndex - cursor );
+    std::size_t objectIndex = dependencyStr.find( '-', cursor );
+    std::string objectEntry = dependencyStr.substr( cursor, objectIndex - cursor );
     cursor = objectIndex;
 
-    std::size_t upwardIndexBegin = theData.find("{",cursor) + 1;
-    std::size_t upwardIndexFinish = theData.find("}",upwardIndexBegin);
-    LevelsList upwardList = parseWard( theData, cursor );
+    //std::size_t upwardIndexBegin = dependencyStr.find("{",cursor) + 1;
+    //std::size_t upwardIndexFinish = dependencyStr.find("}",upwardIndexBegin);
+    LevelsList upwardList = parseWard( dependencyStr, cursor );
 
-    LevelsList downwardList = parseWard( theData, cursor );
+    LevelsList downwardList = parseWard( dependencyStr, cursor );
 
     tree[objectEntry] = std::pair<LevelsList,LevelsList>( upwardList, downwardList );
   }
 }
+
+bool GEOMUtils::CheckShape( TopoDS_Shape& shape,
+                            bool checkGeometry )
+{
+  BRepCheck_Analyzer analyzer( shape, checkGeometry );
+  return analyzer.IsValid();
+}
+
+bool GEOMUtils::CheckBOPArguments(const TopoDS_Shape &theShape)
+{
+  BOPAlgo_ArgumentAnalyzer aChecker;
+
+  aChecker.SetShape1(theShape);
+
+  // Set default options
+  aChecker.ArgumentTypeMode()   = Standard_True;
+  aChecker.SelfInterMode()      = Standard_True;
+  aChecker.SmallEdgeMode()      = Standard_True;
+  aChecker.RebuildFaceMode()    = Standard_True;
+  aChecker.ContinuityMode()     = Standard_True;
+  aChecker.CurveOnSurfaceMode() = Standard_True;
+
+  aChecker.StopOnFirstFaulty() = Standard_True;
+  aChecker.Perform();
+
+  // process result of checking
+  const bool isValid = !aChecker.HasFaulty();
+
+  return isValid;
+}
+
+bool GEOMUtils::FixShapeTolerance( TopoDS_Shape& shape,
+                                   TopAbs_ShapeEnum type,
+                                   Standard_Real tolerance,
+                                   bool checkGeometry )
+{
+  ShapeFix_ShapeTolerance aSft;
+  aSft.LimitTolerance( shape, tolerance, tolerance, type );
+  Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape( shape );
+  aSfs->Perform();
+  shape = aSfs->Shape();
+  return CheckShape( shape, checkGeometry );
+}
+
+bool GEOMUtils::FixShapeTolerance( TopoDS_Shape& shape,
+                                   Standard_Real tolerance,
+                                   bool checkGeometry )
+{
+  return FixShapeTolerance( shape, TopAbs_SHAPE, tolerance, checkGeometry );
+}
+
+bool GEOMUtils::FixShapeTolerance( TopoDS_Shape& shape,
+                                   bool checkGeometry )
+{
+  return FixShapeTolerance( shape, Precision::Confusion(), checkGeometry );
+}
+
+bool GEOMUtils::FixShapeCurves( TopoDS_Shape& shape )
+{
+  Standard_Real aT, aTolE, aD, aDMax;
+  TopExp_Explorer aExpF, aExpE;
+  NCollection_DataMap<TopoDS_Edge, Standard_Real, TopTools_ShapeMapHasher> aDMETol;
+  aExpF.Init(shape, TopAbs_FACE);
+  for (; aExpF.More(); aExpF.Next()) {
+    const TopoDS_Face& aF = *(TopoDS_Face*)&aExpF.Current();
+    aExpE.Init(aF, TopAbs_EDGE);
+    for (; aExpE.More(); aExpE.Next()) {
+      const TopoDS_Edge& aE = *(TopoDS_Edge*)&aExpE.Current();
+      try {
+        if (!BOPTools_AlgoTools::ComputeTolerance(aF, aE, aDMax, aT)) {
+          continue;
+        }
+      }
+      catch(...) {
+        continue;
+      }
+      aTolE = BRep_Tool::Tolerance(aE);
+      if (aDMax < aTolE) continue;
+      if (aDMETol.IsBound(aE)) {
+        aD = aDMETol.Find(aE);
+        if (aDMax > aD) {
+          aDMETol.UnBind(aE);
+          aDMETol.Bind(aE, aDMax);
+        }
+      }
+      else {
+        aDMETol.Bind(aE, aDMax);
+      }
+    }
+  }
+  NCollection_DataMap<TopoDS_Edge, Standard_Real, TopTools_ShapeMapHasher>::Iterator aDMETolIt(aDMETol);
+#ifdef USE_LIMIT_TOLERANCE
+  ShapeFix_ShapeTolerance sat;
+#else
+  BRep_Builder b;
+#endif
+  for (; aDMETolIt.More(); aDMETolIt.Next()) {
+#ifdef USE_LIMIT_TOLERANCE
+    sat.LimitTolerance(aDMETolIt.Key(), aDMETolIt.Value()*1.001);
+#else
+    TopoDS_Iterator itv(aDMETolIt.Key());
+    for (; itv.More(); itv.Next())
+      b.UpdateVertex(TopoDS::Vertex(itv.Value()), aDMETolIt.Value()*1.001);
+    b.UpdateEdge(aDMETolIt.Key(), aDMETolIt.Value()*1.001);
+#endif
+  }
+  return CheckShape( shape );
+}
+
+bool GEOMUtils::Write( const TopoDS_Shape& shape, const char* fileName )
+{
+  return BRepTools::Write( shape, fileName );
+}
+
+TopoDS_Shape GEOMUtils::ReduceCompound( const TopoDS_Shape& shape )
+{
+  TopoDS_Shape result = shape;
+
+  if ( shape.ShapeType() == TopAbs_COMPOUND ||
+       shape.ShapeType() == TopAbs_COMPSOLID ) {
+
+    TopTools_ListOfShape l;
+    
+    TopoDS_Iterator it ( shape );
+    for ( ; it.More(); it.Next() )
+      l.Append( it.Value() );
+    if ( l.Extent() == 1 && l.First() != shape )
+      result = ReduceCompound( l.First() );
+  }
+  
+  return result;
+}
+
+void GEOMUtils::MeshShape( const TopoDS_Shape shape,
+                           double deflection, bool theForced )
+{
+  Standard_Real aDeflection = ( deflection <= 0 ) ? DefaultDeflection() : deflection;
+  
+  // Is shape triangulated?
+  Standard_Boolean alreadyMeshed = true;
+  TopExp_Explorer ex;
+  TopLoc_Location aLoc;
+  for ( ex.Init( shape, TopAbs_FACE ); ex.More() && alreadyMeshed; ex.Next() ) {
+    const TopoDS_Face& aFace = TopoDS::Face( ex.Current() );
+    Handle(Poly_Triangulation) aPoly = BRep_Tool::Triangulation( aFace, aLoc );
+    alreadyMeshed = !aPoly.IsNull(); 
+  }
+  
+  if ( !alreadyMeshed || theForced ) {
+    // Compute bounding box
+    Bnd_Box B;
+    BRepBndLib::Add( shape, B );
+    if ( B.IsVoid() )
+      return; // NPAL15983 (Bug when displaying empty groups) 
+    Standard_Real aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
+    B.Get( aXmin, aYmin, aZmin, aXmax, aYmax, aZmax );
+    
+    // This magic line comes from Prs3d_ShadedShape.gxx in OCCT
+    aDeflection = MAX3(aXmax-aXmin, aYmax-aYmin, aZmax-aZmin) * aDeflection * 4;
+    
+    // Clean triangulation before compute incremental mesh
+    BRepTools::Clean( shape );
+    
+    // Compute triangulation
+    BRepMesh_IncrementalMesh mesh( shape, aDeflection ); 
+  }
+}
+
+double GEOMUtils::DefaultDeflection()
+{
+  return 0.001;
+}
+
+//=======================================================================
+//function : IsOpenPath
+//purpose  : 
+//=======================================================================
+bool GEOMUtils::IsOpenPath(const TopoDS_Shape &theShape)
+{
+  bool isOpen = true;
+
+  if (theShape.IsNull() == Standard_False) {
+    if (theShape.Closed()) {
+      // The shape is closed
+      isOpen = false;
+    } else {
+      const TopAbs_ShapeEnum aType = theShape.ShapeType();
+
+      if (aType == TopAbs_EDGE || aType == TopAbs_WIRE) {
+        // Check if path ends are coincident.
+        TopoDS_Vertex aV[2];
+
+        if (aType == TopAbs_EDGE) {
+          // Edge
+          TopExp::Vertices(TopoDS::Edge(theShape), aV[0], aV[1]);
+        } else {
+          // Wire
+          TopExp::Vertices(TopoDS::Wire(theShape), aV[0], aV[1]);
+        }
+
+        if (aV[0].IsNull() == Standard_False &&
+            aV[1].IsNull() == Standard_False) {
+          if (aV[0].IsSame(aV[1])) {
+            // The shape is closed
+            isOpen = false;
+          } else {
+            const Standard_Real aTol1 = BRep_Tool::Tolerance(aV[0]);
+            const Standard_Real aTol2 = BRep_Tool::Tolerance(aV[1]);
+            const gp_Pnt        aPnt1 = BRep_Tool::Pnt(aV[0]);
+            const gp_Pnt        aPnt2 = BRep_Tool::Pnt(aV[1]);
+
+            if (aPnt1.Distance(aPnt2) <= aTol1 + aTol2) {
+              // The shape is closed
+              isOpen = false;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  return isOpen;
+}
+
+//=======================================================================
+//function : CompareToleranceValues
+//purpose  : 
+//=======================================================================
+int GEOMUtils::CompareToleranceValues(const double theTolShape,
+                                      const double theTolRef)
+{
+  const double aTolTol = Min(DEFAULT_MAX_TOLERANCE_TOLERANCE,
+                             theTolRef*DEFAULT_TOLERANCE_TOLERANCE);
+
+  int aResult = 0;
+
+  if (theTolShape < theTolRef - aTolTol) {
+    aResult = -1;
+  } else if (theTolShape > theTolRef + aTolTol) {
+    aResult = 1;
+  }
+
+  return aResult;
+}
+
+//=======================================================================
+//function : IsFitCondition
+//purpose  : 
+//=======================================================================
+bool GEOMUtils::IsFitCondition(const ComparisonCondition theCondition,
+                               const double              theTolShape,
+                               const double              theTolRef)
+{
+  const int aCompValue = CompareToleranceValues(theTolShape, theTolRef);
+  bool      isFit      = false;
+
+  switch (theCondition) {
+    case CC_GT:
+      isFit = aCompValue == 1;
+      break;
+    case GEOMUtils::CC_GE:
+      isFit = aCompValue != -1;
+      break;
+    case GEOMUtils::CC_LT:
+      isFit = aCompValue == -1;
+      break;
+    case GEOMUtils::CC_LE:
+      isFit = aCompValue != 1;
+      break;
+    default:
+      break;
+  }
+
+  return isFit;
+}