Salome HOME
Prevent crash when viewer opened
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_SketchBuilder.cpp
index 1828a7193368b9cba8950f1a18bd005fa0954228..4cd345b868b298f5fe861f6e962325c944a884b9 100644 (file)
@@ -7,6 +7,7 @@
 #include <set>
 
 #include <gp_Dir.hxx>
+#include <gp_Pln.hxx>
 
 #include <BOPAlgo_Builder.hxx>
 #include <BOPAlgo_Operation.hxx>
@@ -20,6 +21,8 @@
 #include <BRepBuilderAPI_MakeFace.hxx>
 #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>
 #include <TopoDS_Wire.hxx>
 
 #include <Precision.hxx>
+
+#ifndef DBL_MAX
+#define DBL_MAX 1.7976931348623158e+308 
+#endif
+
+
 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(
@@ -48,6 +61,7 @@ static void findNextVertex(
 static void createFace(const TopoDS_Shape&                      theStartVertex,
                        const std::list<TopoDS_Shape>::iterator& theStartEdge,
                        const std::list<TopoDS_Shape>::iterator& theEndOfEdges,
+                       const gp_Pln&                            thePlane,
                              TopoDS_Face&                       theResFace);
 
 /// \bief Create planar wire
@@ -74,6 +88,7 @@ static void removeWasteEdges(
 
 
 void GeomAlgoAPI_SketchBuilder::createFaces(
+          const boost::shared_ptr<GeomAPI_Pnt>&                theOrigin,
           const boost::shared_ptr<GeomAPI_Dir>&                theDirX,
           const boost::shared_ptr<GeomAPI_Dir>&                theDirY,
           const boost::shared_ptr<GeomAPI_Dir>&                theNorm,
@@ -81,31 +96,47 @@ 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>();
   gp_Dir aNorm = theNorm->impl<gp_Dir>();
 
+  gp_Pln aPlane(theOrigin->impl<gp_Pnt>(), aNorm);
+
   // Set of edges used in loops
   std::set<Handle(TopoDS_TShape)> anEdgesInLoops;
   // Lists for processed vertexes and edges
@@ -117,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
@@ -126,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;
 
@@ -146,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.
@@ -177,11 +208,11 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
       // When the orientation is correct or the edges looped through
       // the first element, create new face and remove unnecessary edges.
       TopoDS_Face aPatch;
-      createFace(*aVertIter, anEdgeIter, aProcEdges.end(), aPatch);
+      createFace(*aVertIter, anEdgeIter, aProcEdges.end(), aPlane, aPatch);
       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
@@ -212,7 +243,7 @@ void GeomAlgoAPI_SketchBuilder::createFaces(
         aNextVertex = aProcVertexes.back();
         if (!aProcEdges.empty())
           aNextDir = getOuterEdgeDirection(aProcEdges.back(), aNextVertex);
-        else aNextDir = -aDirY;
+        else aNextDir = aDirY;
       }
     }
 
@@ -223,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;
@@ -268,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);
       }
     }
 
@@ -282,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;
@@ -308,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())
       {
@@ -319,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())
         {
@@ -351,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++)
   {
@@ -362,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;
     }
   }
@@ -411,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);
+      }
     }
   }
 }
@@ -420,6 +472,7 @@ static void addEdgeToWire(const TopoDS_Edge&  theEdge,
                                 TopoDS_Shape& theSpliceVertex,
                                 TopoDS_Wire&  theWire)
 {
+  TopoDS_Edge anEdge = theEdge;
   bool isCurVertChanged = false;
   TopoDS_Shape aCurVertChanged;
 
@@ -430,13 +483,7 @@ static void addEdgeToWire(const TopoDS_Edge&  theEdge,
     if (aVertex.TShape() == theSpliceVertex.TShape() && 
         aVertex.Orientation() != theEdge.Orientation())
     { // Current vertex is the last for the edge, so its orientation is wrong, need to revert the edge
-      const Handle(BRep_TEdge)& aTEdge = (const Handle(BRep_TEdge)&)theEdge.TShape();
-      Handle(BRep_Curve3D) aEdgeCurve = 
-        Handle(BRep_Curve3D)::DownCast(aTEdge->Curves().First());
-      Handle(Geom_Curve) aCurve = aEdgeCurve->Curve3D();
-      aCurve->Reverse();
-
-      theBuilder.UpdateEdge(theEdge, aCurve, aTEdge->Tolerance());
+      anEdge.Reverse();
       break;
     }
     if (aVertex.TShape() != theSpliceVertex.TShape())
@@ -447,12 +494,13 @@ static void addEdgeToWire(const TopoDS_Edge&  theEdge,
   }
   theSpliceVertex = isCurVertChanged ? aCurVertChanged : aVertExp.Current();
 
-  theBuilder.Add(theWire, theEdge);
+  theBuilder.Add(theWire, anEdge);
 }
 
 void createFace(const TopoDS_Shape&                      theStartVertex,
                 const std::list<TopoDS_Shape>::iterator& theStartEdge,
                 const std::list<TopoDS_Shape>::iterator& theEndOfEdges,
+                const gp_Pln&                            thePlane,
                       TopoDS_Face&                       theResFace)
 {
   TopoDS_Wire aResWire;
@@ -464,10 +512,11 @@ 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(aResWire, Standard_True/*planar face*/);
+  BRepBuilderAPI_MakeFace aFaceBuilder(thePlane, aResWire);
   if (aFaceBuilder.Error() == BRepBuilderAPI_FaceDone)
     theResFace = aFaceBuilder.Face();
 }
@@ -539,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);
 }