]> OCCT Git - occt.git/commitdiff
0031586: BRepMesh hangs up CR31586
authoroan <oan@opencascade.com>
Tue, 15 Nov 2022 11:39:00 +0000 (14:39 +0300)
committeroan <oan@opencascade.com>
Thu, 22 Dec 2022 12:51:09 +0000 (15:51 +0300)
Use NCollection_UBTree to detect intersections between newly added links and contour of the polygon;
Perform check of the solutions for link insertion after all possible solutions are found and sorted according to their priority.

src/BRepMesh/BRepMesh_Delaun.cxx
src/BRepMesh/BRepMesh_Delaun.hxx
tests/bugs/mesh/bug31586 [new file with mode: 0644]

index 0aa1eeb2bab2fcb502cbc4dbe44f4ca8d8a63490..ef9f657b12d7191c467d4f22acc59b871126a0ac 100644 (file)
@@ -76,8 +76,137 @@ namespace {
     theBox.Add( thePnt2 );
     theBox.Enlarge(Precision);
   }
+
+  //=============================================================================
+  //function : IntSegSeg
+  //purpose  : Checks intersection between the two segments.
+  //=============================================================================
+  BRepMesh_GeomTool::IntFlag IntSegSeg(
+    const Handle(BRepMesh_DataStructureOfDelaun)& theMeshData,
+    const BRepMesh_Edge&                          theEdg1,
+    const BRepMesh_Edge&                          theEdg2,
+    const Standard_Boolean                        isConsiderEndPointTouch,
+    const Standard_Boolean                        isConsiderPointOnEdge,
+    gp_Pnt2d&                                     theIntPnt)
+  {
+    gp_XY p1, p2, p3, p4;
+    p1 = theMeshData->GetNode( theEdg1.FirstNode() ).Coord();
+    p2 = theMeshData->GetNode( theEdg1.LastNode () ).Coord();
+    p3 = theMeshData->GetNode( theEdg2.FirstNode() ).Coord();
+    p4 = theMeshData->GetNode( theEdg2.LastNode () ).Coord();
+
+    return BRepMesh_GeomTool::IntSegSeg( p1, p2, p3, p4,
+      isConsiderEndPointTouch, isConsiderPointOnEdge, theIntPnt );
+  }
 } // anonymous namespace
 
+//! Checks polygon links for intersection with current one of the same polygon.
+class BRepMesh_Delaun::EBTreeOfB2d_Selector : 
+  public NCollection_EBTree<Standard_Integer, Bnd_B2d>::Selector
+{
+public:
+  //! Constructor.
+  EBTreeOfB2d_Selector(
+    const Handle(BRepMesh_DataStructureOfDelaun)& theMeshData,
+    const DataMapOfPVoid&                         theSegmentsPolyMap,
+    const Standard_Boolean                        isConsiderEndPointTouch = Standard_False,
+    const Standard_Boolean                        isConsiderPointOnEdge   = Standard_False)
+    : myMeshData              ( theMeshData ),
+      mySegmentsPolyMap       ( theSegmentsPolyMap ),
+      myConsiderEndPointTouch ( isConsiderEndPointTouch ),
+      myConsiderPointOnEdge   ( isConsiderPointOnEdge ),
+      myPolygonPtr            ( nullptr )
+  {
+  }
+
+  //! Implementation of rejection method
+  //! @return
+  //!   True if the bounding box does not intersect with the current 
+  virtual Standard_Boolean Reject (const Bnd_B2d& theBox) const
+  {
+    return (myBox.IsOut( theBox ));
+  }
+
+  //! Implementation of acceptance method
+  //!   This method is called when the bounding box intersect with the current.
+  //!   It stores the object - the index of box in the list of accepted objects.
+  //! @return
+  //!   True, because the object is accepted
+  virtual Standard_Boolean Accept (const Standard_Integer& theObj)
+  {
+    if (mySkipLinks.Contains( theObj ))
+    {
+      return Standard_False;
+    }
+
+    auto aPtr = mySegmentsPolyMap.Seek( theObj );
+
+    if (aPtr != nullptr && *aPtr == myPolygonPtr)
+    {
+      const BRepMesh_Edge& aPolyLink = myMeshData->GetLink( Abs( theObj ) );
+
+      if (!myLink.IsEqual( aPolyLink ))
+      {
+        // intersection is possible...                  
+        gp_Pnt2d anIntPnt;
+        BRepMesh_GeomTool::IntFlag aIntFlag = IntSegSeg(
+          myMeshData, myLink, aPolyLink,
+          myConsiderEndPointTouch, myConsiderPointOnEdge, anIntPnt );
+
+        myStop = (aIntFlag != BRepMesh_GeomTool::NoIntersection);
+        return myStop;
+      }
+    }
+
+    return Standard_False;
+  }
+
+  //! Set current box to search for overlapping with him
+  void SetCurrent (const BRepMesh_Edge& theLink,
+                   const Bnd_B2d&       theBox,
+                   const void*          thePolygonPtr)
+  {
+    mySkipLinks.Clear();
+
+    myStop       = Standard_False;
+    myLink       = theLink;
+    myBox        = theBox;
+    myPolygonPtr = thePolygonPtr;
+  }
+
+  void SetSkipLink(const int theLink)
+  {
+    mySkipLinks.Add( theLink );
+  }
+
+  template<typename... T>
+  void SetSkipLink( const int theLink, const T... theLinks )
+  {
+    SetSkipLink( theLink );
+    SetSkipLink( theLinks... );
+  }
+
+  //! Returns true if the current segment intersects another one of the same polygon.
+  Standard_Boolean IsIntersected()
+  {
+    return myStop;
+  }
+
+protected:
+  const Handle(BRepMesh_DataStructureOfDelaun)& myMeshData;
+  const DataMapOfPVoid&                         mySegmentsPolyMap;
+
+  const Standard_Boolean                        myConsiderEndPointTouch;
+  const Standard_Boolean                        myConsiderPointOnEdge;
+
+  const void*                                   myPolygonPtr;
+
+  BRepMesh_Edge                                 myLink;
+  Bnd_B2d                                       myBox;
+  
+  NCollection_Map<Standard_Integer>             mySkipLinks;
+};
+
 //=======================================================================
 //function : BRepMesh_Delaun
 //purpose  : 
@@ -1883,24 +2012,31 @@ void BRepMesh_Delaun::meshPolygon(IMeshData::SequenceOfInteger&   thePolygon,
     }
   }
 
-  IMeshData::SequenceOfInteger* aPolygon1   = &thePolygon;
-  IMeshData::SequenceOfBndB2d*  aPolyBoxes1 = &thePolyBoxes;
+  SegmentsBoxes     aSegmentsBoxes;
+  EBTreeOfB2dFiller aTreeFiller( aSegmentsBoxes.Boxes );
+
+  Standard_Integer aLinkIt = thePolygon.Lower();
+  for (; aLinkIt <= thePolygon.Upper(); ++aLinkIt)
+  {
+    const Standard_Integer aLinkInfo = thePolygon( aLinkIt );
+
+    aTreeFiller   .Add   (aLinkInfo, thePolyBoxes( aLinkIt ));
+    aSegmentsBoxes.Rebind(aLinkInfo, &thePolygon);
+  }
+  aTreeFiller.Fill();
 
-  Handle(IMeshData::SequenceOfInteger) aPolygon2   = new IMeshData::SequenceOfInteger;
-  Handle(IMeshData::SequenceOfBndB2d)  aPolyBoxes2 = new IMeshData::SequenceOfBndB2d;
+  IMeshData::SequenceOfInteger*        aPolygon1 = &thePolygon;
+  Handle(IMeshData::SequenceOfInteger) aPolygon2 = new IMeshData::SequenceOfInteger;
 
   NCollection_Sequence<Handle(IMeshData::SequenceOfInteger)> aPolyStack;
-  NCollection_Sequence<Handle(IMeshData::SequenceOfBndB2d)>  aPolyBoxStack;
   for (;;)
   {
-    decomposeSimplePolygon(*aPolygon1, *aPolyBoxes1, *aPolygon2, *aPolyBoxes2);
+    decomposeSimplePolygon(*aPolygon1, *aPolygon2, aSegmentsBoxes);
     if (!aPolygon2->IsEmpty())
     {
       aPolyStack.Append(aPolygon2);
-      aPolyBoxStack.Append(aPolyBoxes2);
       
-      aPolygon2   = new IMeshData::SequenceOfInteger;
-      aPolyBoxes2 = new IMeshData::SequenceOfBndB2d;
+      aPolygon2 = new IMeshData::SequenceOfInteger;
     }
 
     if (aPolygon1->IsEmpty())
@@ -1908,14 +2044,12 @@ void BRepMesh_Delaun::meshPolygon(IMeshData::SequenceOfInteger&   thePolygon,
       if (!aPolyStack.IsEmpty() && aPolygon1 == &(*aPolyStack.First()))
       {
         aPolyStack.Remove(1);
-        aPolyBoxStack.Remove(1);
       }
 
       if (aPolyStack.IsEmpty())
         break;
 
-      aPolygon1   = &(*aPolyStack.ChangeFirst());
-      aPolyBoxes1 = &(*aPolyBoxStack.ChangeFirst());
+      aPolygon1 = &(*aPolyStack.ChangeFirst());
     }
   }
 }
@@ -1964,17 +2098,54 @@ Standard_Boolean BRepMesh_Delaun::meshElementaryPolygon(
 //function : meshSimplePolygon
 //purpose  : 
 //=======================================================================
+namespace
+{
+  struct Candidate
+  {
+    Standard_Integer PivotNode;
+    Standard_Integer LinkId;
+    Standard_Real    Dist;
+    Standard_Real    Angle;
+
+    Candidate()
+      : PivotNode (0),
+        LinkId    (0),
+        Dist      (RealLast()),
+        Angle     (0.)
+    {
+    }
+
+    Candidate(const Standard_Integer thePivotNode,
+              const Standard_Integer theLinkId,
+              const Standard_Real    theDist,
+              const Standard_Real    theAngle)
+      : PivotNode (thePivotNode),
+        LinkId    (theLinkId),
+        Dist      (theDist),
+        Angle     (theAngle)
+    {
+    }
+
+    bool operator<(const Candidate& theOther) const
+    {
+      const bool isWorse = ((Dist >= theOther.Dist) &&
+                           (Angle <= theOther.Angle || Angle > AngDeviation90Deg));
+
+      return !isWorse;
+    }
+  };
+}
+
 void BRepMesh_Delaun::decomposeSimplePolygon(
   IMeshData::SequenceOfInteger& thePolygon,
-  IMeshData::SequenceOfBndB2d&  thePolyBoxes,
   IMeshData::SequenceOfInteger& thePolygonCut,
-  IMeshData::SequenceOfBndB2d&  thePolyBoxesCut)
+  SegmentsBoxes&                theSegmentsBoxes)
 {
   // Check is the given polygon elementary
   if ( meshElementaryPolygon( thePolygon ) )
   {
+    theSegmentsBoxes.Rebind( thePolygon, nullptr );
     thePolygon.Clear();
-    thePolyBoxes.Clear();
     return;
   }
 
@@ -1994,8 +2165,8 @@ void BRepMesh_Delaun::decomposeSimplePolygon(
   Standard_Real aRefEdgeLen = aRefEdgeDir.Magnitude();
   if ( aRefEdgeLen < Precision )
   {
+    theSegmentsBoxes.Rebind( thePolygon, nullptr );
     thePolygon.Clear();
-    thePolyBoxes.Clear();
     return;
   }
 
@@ -2003,19 +2174,16 @@ void BRepMesh_Delaun::decomposeSimplePolygon(
 
   // Find a point with minimum distance respect
   // the end of reference link
-  Standard_Integer aUsedLinkId = 0;
-  Standard_Real    aOptAngle   = 0.0;
-  Standard_Real    aMinDist    = RealLast();
-  Standard_Integer aPivotNode  = aNodes[1];
-  Standard_Integer aPolyLen    = thePolygon.Length();
-  for ( Standard_Integer aLinkIt = 3; aLinkIt <= aPolyLen; ++aLinkIt )
+  NCollection_Vector<Candidate> aCandidates( thePolygon.Length() );
+
+  for ( Standard_Integer aLinkIt = 3; aLinkIt <= thePolygon.Length(); ++aLinkIt )
   {
-    Standard_Integer aLinkInfo = thePolygon( aLinkIt );
-    const BRepMesh_Edge& aNextEdge = GetEdge( Abs( aLinkInfo ) );
+    const Standard_Integer aLinkInfo = thePolygon( aLinkIt );
+    const BRepMesh_Edge&   aNextEdge = GetEdge( Abs( aLinkInfo ) );
 
-    aPivotNode = aLinkInfo > 0 ? 
+    const Standard_Integer aPivotNode = aLinkInfo > 0 ? 
       aNextEdge.FirstNode() : 
-      aNextEdge.LastNode();
+      aNextEdge.LastNode ();
 
     // We have end points touch case in the polygon - ignore it
     if (aPivotNode == aNodes[1])
@@ -2025,55 +2193,43 @@ void BRepMesh_Delaun::decomposeSimplePolygon(
     gp_Vec2d aDistanceDir( aRefVertices[1], aPivotVertex );
 
     Standard_Real aDist     = aRefEdgeDir ^ aDistanceDir;
-    Standard_Real aAngle    = Abs( aRefEdgeDir.Angle(aDistanceDir) );
+    Standard_Real aAngle    = Abs( aRefEdgeDir.Angle( aDistanceDir ) );
     Standard_Real anAbsDist = Abs( aDist );
     if (anAbsDist < Precision || aDist < 0.)
       continue;
 
-    if ( ( anAbsDist >= aMinDist                             ) &&
-         ( aAngle <= aOptAngle || aAngle > AngDeviation90Deg ) )
-    {
-      continue;
-    }
+    aCandidates.Append( Candidate{ aPivotNode, aLinkIt, anAbsDist, aAngle });
+  }
+
+  std::sort( aCandidates.begin(), aCandidates.end() );
+
+  Standard_Integer aUsedLinkId = 0;
+  EBTreeOfB2d_Selector aSelector( myMeshData, theSegmentsBoxes.PolyMap );
+
+  Standard_Integer aCandIt = aCandidates.Lower();
+  for (; aCandIt <= aCandidates.Upper(); ++aCandIt)
+  {
+    const Candidate& aCand = aCandidates.Value( aCandIt );
+
+    const gp_Pnt2d& aPivotVertex = GetVertex( aCand.PivotNode ).Coord();
 
     // Check is the test link crosses the polygon boundaries
     Standard_Boolean isIntersect = Standard_False;
     for ( Standard_Integer aRefLinkNodeIt = 0; aRefLinkNodeIt < 2; ++aRefLinkNodeIt )
     {
-      const Standard_Integer& aLinkFirstNode   = aNodes[aRefLinkNodeIt];
+      const Standard_Integer& aLinkFirstNode   = aNodes      [aRefLinkNodeIt];
       const gp_Pnt2d&         aLinkFirstVertex = aRefVertices[aRefLinkNodeIt];
 
       Bnd_B2d aBox;
-      UpdateBndBox(aLinkFirstVertex.Coord(), aPivotVertex.Coord(), aBox);
+      UpdateBndBox( aLinkFirstVertex.Coord(), aPivotVertex.Coord(), aBox );
 
-      BRepMesh_Edge aCheckLink( aLinkFirstNode, aPivotNode, BRepMesh_Free );
+      BRepMesh_Edge aCheckLink( aLinkFirstNode, aCand.PivotNode, BRepMesh_Free );
 
-      Standard_Integer aCheckLinkIt = 2;
-      for ( ; aCheckLinkIt <= aPolyLen; ++aCheckLinkIt )
-      {
-        if( aCheckLinkIt == aLinkIt )
-          continue;
-        
-        if ( !aBox.IsOut( thePolyBoxes.Value( aCheckLinkIt ) ) )
-        {
-          const BRepMesh_Edge& aPolyLink = 
-            GetEdge( Abs( thePolygon( aCheckLinkIt ) ) );
+      aSelector.SetCurrent ( aCheckLink, aBox, &thePolygon );
+      aSelector.SetSkipLink( thePolygon.First(), thePolygon( aCand.LinkId ) );
 
-          if ( aCheckLink.IsEqual( aPolyLink ) )
-            continue;
-
-          // intersection is possible...                  
-          gp_Pnt2d anIntPnt;
-          BRepMesh_GeomTool::IntFlag aIntFlag = intSegSeg( aCheckLink, aPolyLink,
-            Standard_False, Standard_False, anIntPnt );
-
-          if( aIntFlag != BRepMesh_GeomTool::NoIntersection )
-          {
-            isIntersect = Standard_True;
-            break;
-          }
-        }
-      }
+      theSegmentsBoxes.Boxes.Select( aSelector );
+      isIntersect = aSelector.IsIntersected();
 
       if ( isIntersect )
         break;
@@ -2083,17 +2239,16 @@ void BRepMesh_Delaun::decomposeSimplePolygon(
       continue;
 
 
-    aOptAngle       = aAngle;
-    aMinDist        = anAbsDist;
-    aNodes[2]       = aPivotNode;
+    aNodes      [2] = aCand.PivotNode;
     aRefVertices[2] = aPivotVertex;
-    aUsedLinkId     = aLinkIt;
+    aUsedLinkId     = aCand.LinkId;
+    break;
   }
 
   if ( aUsedLinkId == 0 )
   {
+    theSegmentsBoxes.Rebind( thePolygon, nullptr );
     thePolygon.Clear();
-    thePolyBoxes.Clear();
     return;
   }
 
@@ -2112,48 +2267,49 @@ void BRepMesh_Delaun::decomposeSimplePolygon(
   for ( Standard_Integer aTriEdgeIt = 0; aTriEdgeIt < 3; ++aTriEdgeIt )
   {
     const Standard_Integer& anEdgeInfo = aNewEdgesInfo[aTriEdgeIt];
-    anEdges[aTriEdgeIt]    = Abs( anEdgeInfo );
+    anEdges   [aTriEdgeIt] = Abs( anEdgeInfo );
     anEdgesOri[aTriEdgeIt] = anEdgeInfo > 0;
   }
   addTriangle( anEdges, anEdgesOri, aNodes );
 
   if (aUsedLinkId == 3)
   {
-    thePolygon.Remove  ( 1 );
-    thePolyBoxes.Remove( 1 );
+    theSegmentsBoxes.Rebind( thePolygon.First(), nullptr );
+    thePolygon      .Remove( 1 );
 
     thePolygon.SetValue( 1, -aNewEdgesInfo[2] );
 
     Bnd_B2d aBox;
-    UpdateBndBox(aRefVertices[0].Coord(), aRefVertices[2].Coord(), aBox);
-    thePolyBoxes.SetValue( 1, aBox );
+    UpdateBndBox( aRefVertices[0].Coord(), aRefVertices[2].Coord(), aBox );
+    theSegmentsBoxes.Add( thePolygon.First(), aBox, &thePolygon );
   }
   else
   {
     // Create triangle and split the source polygon on two 
     // parts (if possible) and mesh each part as independent
     // polygon.
-    if ( aUsedLinkId < aPolyLen )
+    if ( aUsedLinkId < thePolygon.Length() )
     {
-      thePolygon.Split(aUsedLinkId, thePolygonCut);
-      thePolygonCut.Prepend( -aNewEdgesInfo[2] );
-      thePolyBoxes.Split(aUsedLinkId, thePolyBoxesCut);
+      thePolygon.Split( aUsedLinkId, thePolygonCut );
+
+      theSegmentsBoxes.Rebind ( thePolygonCut, &thePolygonCut );
+      thePolygonCut   .Prepend( -aNewEdgesInfo[2] );
 
       Bnd_B2d aBox;
-      UpdateBndBox(aRefVertices[0].Coord(), aRefVertices[2].Coord(), aBox);
-      thePolyBoxesCut.Prepend( aBox );
+      UpdateBndBox( aRefVertices[0].Coord(), aRefVertices[2].Coord(), aBox );
+      theSegmentsBoxes.Add( thePolygonCut.First(), aBox, &thePolygonCut );
     }
     else
     {
-      thePolygon.Remove  ( aPolyLen );
-      thePolyBoxes.Remove( aPolyLen );
+      theSegmentsBoxes.Rebind( thePolygon.Last  (), nullptr );
+      thePolygon      .Remove( thePolygon.Length() );
     }
 
     thePolygon.SetValue( 1, -aNewEdgesInfo[1] );
 
     Bnd_B2d aBox;
-    UpdateBndBox(aRefVertices[1].Coord(), aRefVertices[2].Coord(), aBox);
-    thePolyBoxes.SetValue( 1, aBox );
+    UpdateBndBox( aRefVertices[1].Coord(), aRefVertices[2].Coord(), aBox );
+    theSegmentsBoxes.Add( thePolygon.First(), aBox, &thePolygon );
   }
 }
 
@@ -2477,13 +2633,7 @@ BRepMesh_GeomTool::IntFlag BRepMesh_Delaun::intSegSeg(
   const Standard_Boolean isConsiderPointOnEdge,
   gp_Pnt2d&              theIntPnt) const
 {
-  gp_XY p1, p2, p3, p4;
-  p1 = GetVertex( theEdg1.FirstNode() ).Coord();
-  p2 = GetVertex( theEdg1.LastNode()  ).Coord();
-  p3 = GetVertex( theEdg2.FirstNode() ).Coord();
-  p4 = GetVertex( theEdg2.LastNode()  ).Coord();
-  
-  return BRepMesh_GeomTool::IntSegSeg(p1, p2, p3, p4,
+  return IntSegSeg (myMeshData, theEdg1, theEdg2,
     isConsiderEndPointTouch, isConsiderPointOnEdge, theIntPnt);
 }
 
index fb575548b2c583267d02a16d075e037abb0566d8..b728055d33ba1aae90fb0a2f4ce25a0b7a9d5145 100755 (executable)
@@ -165,6 +165,42 @@ private:
   };
 
   typedef NCollection_DataMap<Standard_Integer, IMeshData::MapOfInteger> DataMapOfMap;
+  typedef NCollection_DataMap<Standard_Integer, void*>                   DataMapOfPVoid;
+  typedef NCollection_EBTree <Standard_Integer, Bnd_B2d>                 EBTreeOfB2d;
+  typedef NCollection_UBTreeFiller <Standard_Integer, Bnd_B2d>           EBTreeOfB2dFiller;
+  class EBTreeOfB2d_Selector;
+
+  struct SegmentsBoxes
+  {
+    EBTreeOfB2d    Boxes;
+    DataMapOfPVoid PolyMap;
+
+    void Rebind (const Standard_Integer theLinkInfo,
+                 void* const&           thePolygonPtr)
+    {
+      PolyMap.Bind (theLinkInfo, thePolygonPtr);
+    }
+
+    void Rebind (IMeshData::SequenceOfInteger& thePolygon,
+                 void* const&                  thePolygonPtr)
+    {
+      Standard_Integer aLinkIt = thePolygon.Lower();
+      for (; aLinkIt <= thePolygon.Upper(); ++aLinkIt)
+      {
+        const Standard_Integer aLinkInfo = thePolygon (aLinkIt);
+        Rebind (aLinkInfo, thePolygonPtr);
+      }
+    }
+
+    void Add (const Standard_Integer theLinkInfo,
+              const Bnd_B2d&         theBox,
+              void* const&           thePolygonPtr)
+    {
+      PolyMap.Bind (theLinkInfo, thePolygonPtr);
+      Boxes  .Add  (theLinkInfo, theBox);
+    }
+  };
+
 
   //! Performs initialization of circles cell filter tool.
   void initCirclesTool (const Bnd_Box2d&       theBox,
@@ -245,14 +281,12 @@ private:
   //! In case if source polygon consists of three links, creates new triangle 
   //! and clears source container.
   //! @param thePolygon source polygon to be decomposed (first part of decomposition).
-  //! @param thePolyBoxes bounding boxes corresponded to source polygon's links.
   //! @param thePolygonCut product of decomposition of source polygon (second part of decomposition).
-  //! @param thePolyBoxesCut bounding boxes corresponded to resulting polygon's links.
+  //! @param theSegmentsBoxes map of relations between semgents and their polygons.
   void decomposeSimplePolygon (
     IMeshData::SequenceOfInteger& thePolygon,
-    IMeshData::SequenceOfBndB2d&  thePolyBoxes,
     IMeshData::SequenceOfInteger& thePolygonCut,
-    IMeshData::SequenceOfBndB2d&  thePolyBoxesCut);
+    SegmentsBoxes&                theSegmentsBoxes);
 
   //! Triangulation of closed polygon containing only three edges.
   Standard_Boolean meshElementaryPolygon (const IMeshData::SequenceOfInteger& thePolygon);
diff --git a/tests/bugs/mesh/bug31586 b/tests/bugs/mesh/bug31586
new file mode 100644 (file)
index 0000000..a8ef891
--- /dev/null
@@ -0,0 +1,14 @@
+puts "========"
+puts "0031586: BRepMesh hangs up"
+puts "========"
+puts ""
+
+puts "TODO OCC31586 All: Not connected mesh inside face"
+
+restore [locate_data_file bug31586.brep] result
+
+incmesh result 0.022627417117969499
+
+checkview -display result -3d -path ${imagedir}/${test_image}.png
+
+tricheck result