Salome HOME
Prevent crash when viewer opened
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_SketchBuilder.cpp
index 08f39e8c0b27bf75ba562b14b8ca9b5fcd30301e..4cd345b868b298f5fe861f6e962325c944a884b9 100644 (file)
@@ -22,6 +22,7 @@
 #include <BRepClass_FaceClassifier.hxx>
 #include <Geom_Curve.hxx>
 #include <Geom_Plane.hxx>
+#include <TopExp.hxx>
 #include <TopExp_Explorer.hxx>
 #include <TopoDS_Edge.hxx>
 #include <TopoDS_Face.hxx>
 
 
 const double tolerance = Precision::Confusion();
+// This value helps to find direction on the boundaries of curve.
+// It is not significant for lines, but is used for circles to avoid 
+// wrong directions of movement (when two edges are tangent on the certain vertex)
+const double shift     = acos(1.0 - 2.0 * tolerance);
 
 /// \brief Search first vertex - the vertex with lowest x coordinate, which is used in 2 edges at least
 static const TopoDS_Shape& findStartVertex(
@@ -91,26 +96,40 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
                 std::list< boost::shared_ptr<GeomAPI_Shape> >& theResultFaces,
                 std::list< boost::shared_ptr<GeomAPI_Shape> >& theResultWires)
 {
+  if (theFeatures.empty())
+    return ;
+
   // Create the list of edges with shared vertexes
   BOPAlgo_Builder aBuilder;
   BOPAlgo_PaveFiller aPF;
+  TopoDS_Shape aFeaturesCompound;
 
-  std::list< boost::shared_ptr<GeomAPI_Shape> >::const_iterator anIt = theFeatures.begin();
-  for (; anIt != theFeatures.end(); anIt++)
+  if (theFeatures.size() == 1)
+  { // If there is only one feature, BOPAlgo_Builder will decline to work. Need to process it anyway
+    aFeaturesCompound = theFeatures.front()->impl<TopoDS_Shape>();
+  }
+  else
   {
-    boost::shared_ptr<GeomAPI_Shape> aPreview(*anIt);
-    aBuilder.AddArgument(aPreview->impl<TopoDS_Edge>());
+    std::list< boost::shared_ptr<GeomAPI_Shape> >::const_iterator anIt = theFeatures.begin();
+    for (; anIt != theFeatures.end(); anIt++)
+    {
+      boost::shared_ptr<GeomAPI_Shape> aPreview(*anIt);
+      aBuilder.AddArgument(aPreview->impl<TopoDS_Edge>());
+    }
+    aPF.SetArguments(aBuilder.Arguments());
+    aPF.Perform();
+    int aErr = aPF.ErrorStatus();
+    if (aErr) return ;
+    aBuilder.PerformWithFiller(aPF);
+    aErr = aBuilder.ErrorStatus();
+    if (aErr) return ;
+    aFeaturesCompound = aBuilder.Shape();
   }
-  aPF.SetArguments(aBuilder.Arguments());
-  aPF.Perform();
-  int aErr = aPF.ErrorStatus();
-  if (aErr) return ;
-  aBuilder.PerformWithFiller(aPF);
-  aErr = aBuilder.ErrorStatus();
-  if (aErr) return ;
 
   BOPCol_IndexedDataMapOfShapeListOfShape aMapVE; // map between vertexes and edges
-  BOPTools::MapShapesAndAncestors(aBuilder.Shape(), TopAbs_VERTEX, TopAbs_EDGE, aMapVE);
+  BOPTools::MapShapesAndAncestors(aFeaturesCompound, TopAbs_VERTEX, TopAbs_EDGE, aMapVE);
+  if (aMapVE.IsEmpty()) // in case of not-initialized circle
+    return;
 
   gp_Dir aDirX = theDirX->impl<gp_Dir>();
   gp_Dir aDirY = theDirY->impl<gp_Dir>();
@@ -129,7 +148,7 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
   aProcVertexes.push_back(aStartVertex);
 
   TopoDS_Shape aCurVertex = aStartVertex;
-  gp_Dir aCurDir = aDirY;
+  gp_Dir aCurDir = aDirY.Reversed();
   gp_Dir aCurNorm = aNorm.Reversed();
 
   // Go through the edges and find loops
@@ -138,6 +157,8 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
   gp_Dir aNextDir;
   while (aMapVE.Extent() > 0)
   {
+    if (aCurVertex.IsNull())
+      return;
     findNextVertex(aCurVertex, aMapVE, aCurDir, aCurNorm, aNextVertex, aBindingEdge, aNextDir);
     aCurNorm = aNorm;
 
@@ -158,20 +179,18 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
     // The loop was found
     if (aVertIter != aProcVertexes.end())
     {
+      // If the binding edge is a full circle, then the list may be empty before addition. Need to update edge iterator
+      if (aProcEdges.size() == 1)
+        anEdgeIter = aProcEdges.begin();
+
       if (aVertIter != aProcVertexes.begin())
       {
         // Check the orientation of the loop
-        Handle(BRep_TVertex) aVert = Handle(BRep_TVertex)::DownCast(aVertIter->TShape());
-        const gp_Pnt& aCurPnt = aVert->Pnt();
-        aVert = Handle(BRep_TVertex)::DownCast((++aVertIter)->TShape());
-        aVertIter--;
-        const gp_Pnt& aNextPnt = aVert->Pnt();
-        std::list<TopoDS_Shape>::reverse_iterator anItBeforeLast = aProcVertexes.rbegin();
-        aVert = Handle(BRep_TVertex)::DownCast((++anItBeforeLast)->TShape()); // get the vertex before last one, because the last duplicates the start of loop
-        const gp_Pnt& aPrevPnt = aVert->Pnt();
-        gp_Vec aCN(aCurPnt, aNextPnt);
-        gp_Vec aCP(aCurPnt, aPrevPnt);
-        if (aCN.DotCross(aCP, gp_Vec(aNorm)) < -tolerance)
+        gp_Dir aCN = getOuterEdgeDirection(*anEdgeIter, *aVertIter);
+        gp_Dir aCP = getOuterEdgeDirection(aProcEdges.back(), *aVertIter);
+        aCN.Reverse();
+        aCP.Reverse();
+        if (aCN.DotCross(aCP, aNorm) < -tolerance)
         {
           // The found loop has wrong orientation and may contain sub-loops.
           // Need to check it onle again with another initial direction.
@@ -193,7 +212,7 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
       if (!aPatch.IsNull())
       {
         boost::shared_ptr<GeomAPI_Shape> aFace(new GeomAPI_Shape);
-        aFace->setImpl(new TopoDS_Shape(aPatch));
+        aFace->setImpl(new TopoDS_Face(aPatch));
         theResultFaces.push_back(aFace);
       }
       // push the edges used in the loop to the map
@@ -224,7 +243,7 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
         aNextVertex = aProcVertexes.back();
         if (!aProcEdges.empty())
           aNextDir = getOuterEdgeDirection(aProcEdges.back(), aNextVertex);
-        else aNextDir = -aDirY;
+        else aNextDir = aDirY;
       }
     }
 
@@ -235,10 +254,16 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
     {
       std::list<TopoDS_Shape>::reverse_iterator aVRIter = aProcVertexes.rbegin();
       std::list<TopoDS_Shape>::reverse_iterator aERIter = aProcEdges.rbegin();
-      for (++aERIter, ++aVRIter; aERIter != aProcEdges.rend(); aERIter++, aVRIter++)
+      if (aVRIter != aProcVertexes.rend())
+        aVRIter++;
+      if (aERIter != aProcEdges.rend())
+        aERIter++;
+
+      for ( ; aERIter != aProcEdges.rend(); aERIter++, aVRIter++)
         if (aMapVE.FindFromKey(*aVRIter).Size() > 2)
           break;
-      if (aERIter != aProcEdges.rend() || aMapVE.FindFromKey(*aVRIter).Size() == 1)
+      if (aERIter != aProcEdges.rend() || 
+         (aVRIter != aProcVertexes.rend() && aMapVE.FindFromKey(*aVRIter).Size() == 1))
       { // the branching vertex was found or current list of edges is a wire without branches
         std::list<TopoDS_Shape>::iterator aEIter;
         TopoDS_Shape aCurEdge;
@@ -280,7 +305,8 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
         aProcVertexes.reverse();
         aProcEdges.reverse();
         aNextVertex = aProcVertexes.back();
-        aNextDir = getOuterEdgeDirection(aProcEdges.back(), aNextVertex);
+        aNextDir = aProcEdges.empty() ? aDirY : 
+                   getOuterEdgeDirection(aProcEdges.back(), aNextVertex);
       }
     }
 
@@ -294,9 +320,9 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
       TopoDS_Shape aStartEdge;
       aStartVertex = findStartVertex(aMapVE, aDirX, aDirY);
       aProcVertexes.push_back(aStartVertex);
-      findNextVertex(aStartVertex, aMapVE, aDirY, aNorm.Reversed(), aNextVertex, aStartEdge, aNextDir);
-      aProcVertexes.push_back(aNextVertex);
-      aProcEdges.push_back(aStartEdge);
+      aNextVertex = aStartVertex;
+      aNextDir = aDirY.Reversed();
+      aCurNorm = aNorm.Reversed();
     }
 
     aCurVertex = aNextVertex;
@@ -320,6 +346,8 @@ void GeomAlgoAPI_SketchBuilder::fixIntersections(
     for (++anIter2; anIter2 != theFaces.end(); anIter2++)
     {
       const TopoDS_Face& aF1 = (*anIter1)->impl<TopoDS_Face>();
+      if (aF1.ShapeType() != TopAbs_FACE) // TODO: MPV - this workaround must be fixed later by AZV, now it just removes crash
+        continue;
       TopExp_Explorer aVert2((*anIter2)->impl<TopoDS_Shape>(), TopAbs_VERTEX);
       for ( ; aVert2.More(); aVert2.Next())
       {
@@ -331,6 +359,8 @@ void GeomAlgoAPI_SketchBuilder::fixIntersections(
       if (aVert2.More())
       { // second shape is not inside first, change the shapes order and repeat comparision
         const TopoDS_Face& aF2 = (*anIter2)->impl<TopoDS_Face>();
+        if (aF2.ShapeType() != TopAbs_FACE) // TODO: MPV - this workaround must be fixed later by AZV, now it just removes crash
+          continue;
         TopExp_Explorer aVert1((*anIter1)->impl<TopoDS_Shape>(), TopAbs_VERTEX);
         for ( ; aVert1.More(); aVert1.Next())
         {
@@ -363,8 +393,8 @@ const TopoDS_Shape& findStartVertex(
           const gp_Dir& theDirX, const gp_Dir& theDirY)
 {
   int aStartVertexInd = 1;
-  double aMinX = DBL_MAX;
-  double aMinY = DBL_MAX;
+  double aMaxX = -DBL_MAX;
+  double aMaxY = -DBL_MAX;
   int aNbVert = theMapVE.Extent();
   for (int i = 1; i <= aNbVert; i++)
   {
@@ -374,11 +404,11 @@ const TopoDS_Shape& findStartVertex(
 
     double aX = aVertPnt.XYZ().Dot(theDirX.XYZ());
     double aY = aVertPnt.XYZ().Dot(theDirY.XYZ());
-    if ((aX < aMinX || (fabs(aX - aMinX) < tolerance && aY < aMinY)) && 
+    if ((aX > aMaxX || (fabs(aX - aMaxX) < tolerance && aY > aMaxY)) && 
         theMapVE.FindFromIndex(i).Extent() > 1)
     {
-      aMinX = aX;
-      aMinY = aY;
+      aMaxX = aX;
+      aMaxY = aY;
       aStartVertexInd = i;
     }
   }
@@ -423,6 +453,16 @@ void findNextVertex(
           theNextDir = getOuterEdgeDirection(aEdIter.Value(), theNextVertex);
           break;
         }
+      if (!aVertExp.More())
+      { // This edge is a full circle
+        TopoDS_Vertex aV1, aV2;
+        TopExp::Vertices(*(const TopoDS_Edge*)(&theNextEdge), aV1, aV2);
+        if (aV1.Orientation() == theStartVertex.Orientation())
+          theNextVertex = aV2;
+        else
+          theNextVertex = aV1;
+        theNextDir = getOuterEdgeDirection(aEdIter.Value(), theNextVertex);
+      }
     }
   }
 }
@@ -472,7 +512,8 @@ void createFace(const TopoDS_Shape&                      theStartVertex,
   for ( ; anEdgeIter != theEndOfEdges; anEdgeIter++)
   {
     TopoDS_Edge anEdge = *((TopoDS_Edge*)(&(*anEdgeIter)));
-    addEdgeToWire(anEdge, aBuilder, aCurVertex, aResWire);
+    if (!anEdge.IsNull())
+      addEdgeToWire(anEdge, aBuilder, aCurVertex, aResWire);
   }
 
   BRepBuilderAPI_MakeFace aFaceBuilder(thePlane, aResWire);
@@ -547,11 +588,12 @@ gp_Dir getOuterEdgeDirection(const TopoDS_Shape& theEdge,
 
   gp_Pnt aPnt;
   gp_Vec aTang;
-  aCurve->D1(aFirst, aPnt, aTang);
+  aCurve->D1(aFirst + shift, aPnt, aTang);
+  aCurve->D0(aFirst, aPnt);
   if (aVertexPnt.IsEqual(aPnt, tolerance))
     return gp_Dir(aTang.Reversed());
 
-  aCurve->D1(aLast aPnt, aTang);
+  aCurve->D1(aLast - shift, aPnt, aTang);
   return gp_Dir(aTang);
 }