Salome HOME
Fix for crash in fillet
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintFillet.cpp
index a870ff02b53febc11511168d52c6a867936c519e..bf47039b18183bb8768927d2328a0a7d6a9c98ad 100644 (file)
@@ -6,7 +6,9 @@
 
 #include "SketchPlugin_ConstraintFillet.h"
 
+#include <GeomAPI_Circ2d.h>
 #include <GeomAPI_Dir2d.h>
+#include <GeomAPI_Lin2d.h>
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_XY.h>
 #include <GeomDataAPI_Point2D.h>
@@ -38,6 +40,9 @@
 
 static const std::string PREVIOUS_VALUE("FilletPreviousRadius");
 
+const double tolerance = 1.e-7;
+const double paramTolerance = 1.e-4;
+
 /// \brief Attract specified point on theNewArc to the attribute of theFeature
 static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
   FeaturePtr theFeature, const std::string& theFeatureAttribute);
@@ -49,6 +54,15 @@ static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB
                                   std::shared_ptr<GeomAPI_XY>& theTangentA,
                                   std::shared_ptr<GeomAPI_XY>& theTangentB);
 
+/// Get point on 1/3 length of edge from fillet point
+static void getPointOnEdge(const FeaturePtr theFeature,
+                           const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
+                           std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
+
+/// Get distance from point to feature
+static double getProjectionDistance(const FeaturePtr theFeature,
+                             const std::shared_ptr<GeomAPI_Pnt2d> thePoint);
+
 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
 {
 }
@@ -70,6 +84,8 @@ void SketchPlugin_ConstraintFillet::initAttributes()
 
 void SketchPlugin_ConstraintFillet::execute()
 {
+  static const double aTol = 1.e-7;
+
   // the viewer update should be blocked in order to avoid the temporaty fillet sub-features visualization
   // before they are processed by the solver
   //std::shared_ptr<Events_Message> aMsg = std::shared_ptr<Events_Message>(
@@ -88,6 +104,14 @@ void SketchPlugin_ConstraintFillet::execute()
     return;
   }
 
+  // Obtain fillet point
+  std::shared_ptr<GeomDataAPI_Point2D> aBasePoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aBaseA->attr());
+  if (!aBasePoint) {
+    setError("Bad vertex selected");
+    return;
+  }
+  std::shared_ptr<GeomAPI_Pnt2d> aFilletPoint = aBasePoint->pnt();
+
   // Check the fillet shapes is not initialized yet
   AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
       aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
@@ -98,12 +122,11 @@ void SketchPlugin_ConstraintFillet::execute()
 
   // Obtain base features
   FeaturePtr anOldFeatureA, anOldFeatureB;
-  std::list<ObjectPtr> aNewFeatList = aRefListOfBaseLines->list();
-  std::list<ObjectPtr>::iterator aFeatIt = aNewFeatList.begin();
+  std::list<ObjectPtr> anOldFeatList = aRefListOfBaseLines->list();
+  std::list<ObjectPtr>::iterator aFeatIt = anOldFeatList.begin();
   anOldFeatureA = ModelAPI_Feature::feature(*aFeatIt++);
   anOldFeatureB = ModelAPI_Feature::feature(*aFeatIt);
 
-
   if(!anOldFeatureA.get() || !anOldFeatureB.get()) {
     setError("One of the edges is empty");
     return;
@@ -157,16 +180,12 @@ void SketchPlugin_ConstraintFillet::execute()
     aStartEndPnt[2*i+1] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
         aFeature[i]->attribute(aEndAttr))->pnt();
   }
-  for (int i = 0; i < aNbFeatures; i++) {
-    int j = aNbFeatures;
-    for (; j < 2 * aNbFeatures; j++)
-      if (aStartEndPnt[i]->distance(aStartEndPnt[j]) < 1.e-10) {
-        isStart[0] = i==0;
-        isStart[1] = j==aNbFeatures;
+  for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) {
+    for (int j = 0; j < 2; j++) // loop on start-end of each feature
+      if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPoint) < 1.e-10) {
+        isStart[aFeatInd] = (j==0);
         break;
       }
-    if (j < 2 * aNbFeatures)
-      break;
   }
   // tangent directions of the features
   for (int i = 0; i < aNbFeatures; i++) {
@@ -186,7 +205,7 @@ void SketchPlugin_ConstraintFillet::execute()
       double y = aDir->y();
       aDir->setX(-y);
       aDir->setY(x);
-      if (!isStart[i])
+      if (isStart[i] == std::dynamic_pointer_cast<SketchPlugin_Arc>(aFeature[i])->isReversed())
         aDir = aDir->multiplied(-1.0);
     }
     aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
@@ -207,6 +226,10 @@ void SketchPlugin_ConstraintFillet::execute()
   // Calculate fillet arc parameters
   std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
   calculateFilletCenter(anOldFeatureA, anOldFeatureB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
+  if(!aCenter.get() || !aTangentPntA.get() || !aTangentPntB.get()) {
+    setError("Can not create fillet with the specified parameters.");
+    return;
+  }
   // update features
   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
       aNewFeatureA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(
@@ -227,10 +250,16 @@ void SketchPlugin_ConstraintFillet::execute()
     aTangentPntA = aTangentPntB;
     aTangentPntB = aTmp;
   }
-  std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-      aNewArc->attribute(SketchPlugin_Arc::START_ID()))->setValue(aTangentPntA->x(), aTangentPntA->y());
-  std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-      aNewArc->attribute(SketchPlugin_Arc::END_ID()))->setValue(aTangentPntB->x(), aTangentPntB->y());
+  std::shared_ptr<GeomDataAPI_Point2D> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aNewArc->attribute(SketchPlugin_Arc::START_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aNewArc->attribute(SketchPlugin_Arc::END_ID()));
+  if (aStartPoint->isInitialized() && aEndPoint->isInitialized() &&
+     (aStartPoint->pnt()->xy()->distance(aTangentPntA) > aTol ||
+      aEndPoint->pnt()->xy()->distance(aTangentPntB) > aTol))
+    std::dynamic_pointer_cast<SketchPlugin_Arc>(aNewArc)->setReversed(false);
+  aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y());
+  aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y());
   aNewArc->data()->blockSendAttributeUpdated(false);
   aNewArc->execute();
 
@@ -240,10 +269,6 @@ void SketchPlugin_ConstraintFillet::execute()
     aRefListOfFillet->append(aNewFeatureB->lastResult());
     aRefListOfFillet->append(aNewArc->lastResult());
 
-    // attach base lines to the list
-    aRefListOfBaseLines->append(anOldFeatureA);
-    aRefListOfBaseLines->append(anOldFeatureB);
-
     myProducedFeatures.push_back(aNewFeatureA);
     myProducedFeatures.push_back(aNewFeatureB);
     myProducedFeatures.push_back(aNewArc);
@@ -396,14 +421,21 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
       (*aCIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
     myBaseObjects.clear();
 
+    // Obtain fillet point
     AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
         data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
     if(!aBaseA->isInitialized() || aBaseA->isObject()) {
       return;
     }
+    AttributePtr anAttrBaseA = aBaseA->attr();
+    std::shared_ptr<GeomDataAPI_Point2D> aBasePoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrBaseA);
+    if (!aBasePoint) {
+      return;
+    }
+    std::shared_ptr<GeomAPI_Pnt2d> aFilletPoint = aBasePoint->pnt();
 
-    AttributePtr anAttrBase = aBaseA->attr();
-    const std::set<AttributePtr>& aRefsList = anAttrBase->owner()->data()->refsToMe();
+    // Obtain conicident edges
+    const std::set<AttributePtr>& aRefsList = anAttrBaseA->owner()->data()->refsToMe();
     std::set<AttributePtr>::const_iterator aIt;
     FeaturePtr aCoincident;
     for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
@@ -416,14 +448,14 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
           aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
         if(anAttrRefA.get() && !anAttrRefA->isObject()) {
           AttributePtr anAttrA = anAttrRefA->attr();
-          if(anAttrBase == anAttrA) {
+          if(anAttrBaseA == anAttrA) {
             aCoincident = aConstrFeature;
             break;
           }
         }
         if(anAttrRefA.get() && !anAttrRefB->isObject()) {
           AttributePtr anAttrB = anAttrRefB->attr();
-          if(anAttrBase == anAttrB) {
+          if(anAttrBaseA == anAttrB) {
             aCoincident = aConstrFeature;
             break;
           }
@@ -455,12 +487,12 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
       aCoinsideLines = aNewLines;
     }
 
-
     if(aCoinsideLines.size() != 2) {
       setError("At selected vertex should be two coincident lines");
       return;
     }
 
+    // Store base lines
     FeaturePtr anOldFeatureA, anOldFeatureB;
     std::set<FeaturePtr>::iterator aLinesIt = aCoinsideLines.begin();
     anOldFeatureA = *aLinesIt++;
@@ -468,35 +500,18 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
     aRefListOfBaseLines->append(anOldFeatureA);
     aRefListOfBaseLines->append(anOldFeatureB);
 
+    // Getting points located at 1/3 of edge length from fillet point
+    std::shared_ptr<GeomAPI_Pnt2d> aPntA, aPntB;
+    getPointOnEdge(anOldFeatureA, aFilletPoint, aPntA);
+    getPointOnEdge(anOldFeatureB, aFilletPoint, aPntB);
 
-    // Set default value equal to 1/3 of the smallest line sharing the point.
-    static const int aNbFeatures = 2;
-    FeaturePtr aFeature[aNbFeatures] = {anOldFeatureA, anOldFeatureB};
-    double aLength = 0;
+    /// Getting distances
+    double aRadius = 1;
+    double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA);
+    double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB);
+    aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0;
 
-    double aLengths[aNbFeatures];
-    for (int i = 0; i < aNbFeatures; i++) {
-      std::shared_ptr<GeomAPI_Pnt2d> aStartPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFeature[i]->attribute(
-        aFeature[i]->getKind() == SketchPlugin_Line::ID() ? SketchPlugin_Line::START_ID() : SketchPlugin_Arc::START_ID()))->pnt();
-      std::shared_ptr<GeomAPI_Pnt2d> anEndPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFeature[i]->attribute(
-        aFeature[i]->getKind() == SketchPlugin_Line::ID() ? SketchPlugin_Line::END_ID() : SketchPlugin_Arc::END_ID()))->pnt();
-      if(aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
-        aLengths[i] = aStartPnt->distance(anEndPnt);
-      } else {
-        std::shared_ptr<GeomAPI_Pnt2d> anArcCenter = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFeature[i]->attribute(
-          SketchPlugin_Arc::CENTER_ID()))->pnt();
-        std::shared_ptr<GeomAPI_Dir2d> aStartDir(new GeomAPI_Dir2d(aStartPnt->xy()->decreased(anArcCenter->xy())));
-        std::shared_ptr<GeomAPI_Dir2d> anEndDir(new GeomAPI_Dir2d(anEndPnt->xy()->decreased(anArcCenter->xy())));
-        double aRadius = aStartPnt->distance(anArcCenter);
-        double anAngle = aStartDir->angle(anEndDir);
-        aLengths[i] = aRadius * abs(anAngle);
-      }
-    }
-    aLength = aLengths[0];
-    for(int i = 1; i < aNbFeatures; i++) {
-      if(aLengths[i] < aLength) aLength = aLengths[i];
-    }
-    std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aLength / 3.0);
+    std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aRadius);
   }
 }
 
@@ -703,23 +718,36 @@ void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
         new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd])));
     double anArcAngle = aEndArcDir->angle(aStartArcDir);
 
-    // get and filter possible centers
+    // get possible centers and filter them
     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
     possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], anArcRadius, theRadius, aSuspectCenters);
     double aDot = 0.0;
+    // the line is forward into the arc
+    double innerArc = aCenter[1-aLineInd]->decreased(aStart[aLineInd])->dot(aDirLine->xy());
     std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
+    // The possible centers are ranged by their positions.
+    // If the point is not satisfy one of criteria, the weight is decreased with penalty.
+    int aBestWeight = 0;
     std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
     for (; anIt != aSuspectCenters.end(); anIt++) {
+      int aWeight = 2;
       aDot = aDirT->xy()->dot(aStart[aLineInd]->decreased(*anIt));
       aLineTgPoint = (*anIt)->added(aDirT->xy()->multiplied(aDot));
-      if (aLineTgPoint->decreased(aStart[aLineInd])->dot(aDirLine->xy()) < 0.0)
-        continue; // incorrect position
+      // Check the point is placed on the correct arc (penalty if false)
+      if (aCenter[1-aLineInd]->distance(*anIt) * innerArc > anArcRadius * innerArc)
+        aWeight -= 1;
       std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
           new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1-aLineInd])));
       double aCurAngle = aCurDir->angle(aStartArcDir);
       if (anArcAngle < 0.0) aCurAngle *= -1.0;
       if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle))
-        continue; // incorrect position
+        continue;
+      if (aWeight > aBestWeight)
+        aBestWeight = aWeight;
+      else if (aWeight < aBestWeight ||
+               aStart[aLineInd]->distance(*anIt) >
+               aStart[aLineInd]->distance(theCenter)) // <-- take closer point
+        continue;
       // the center is found, stop searching
       theCenter = *anIt;
       anArcTgPoint = aCenter[1-aLineInd]->added(aCurDir->xy()->multiplied(anArcRadius));
@@ -730,7 +758,7 @@ void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
         theTangentA = anArcTgPoint;
         theTangentB = aLineTgPoint;
       }
-      return;
+      //return;
     }
   } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
       theFeatureB->getKind() == SketchPlugin_Arc::ID()) {
@@ -774,3 +802,72 @@ void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
     }
   }
 }
+
+void getPointOnEdge(const FeaturePtr theFeature,
+                    const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
+                    std::shared_ptr<GeomAPI_Pnt2d>& thePoint) {
+  if(theFeature->getKind() == SketchPlugin_Line::ID()) {
+    std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
+    if(aPntStart->distance(theFilletPoint) > 1.e-7) {
+      aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
+      aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
+    }
+    thePoint.reset( new GeomAPI_Pnt2d(aPntStart->xy()->added( aPntEnd->xy()->decreased( aPntStart->xy() )->multiplied(1.0 / 3.0) ) ) );
+  } else {
+    std::shared_ptr<GeomAPI_Pnt2d> aPntTemp;
+    std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
+    if(theFeature->attribute(SketchPlugin_Arc::INVERSED_ID())) {
+      aPntTemp = aPntStart;
+      aPntStart = aPntEnd;
+      aPntEnd = aPntTemp;
+    }
+    std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
+    double aStartParameter(0), anEndParameter(0);
+    aCirc->parameter(aPntStart, paramTolerance, aStartParameter);
+    aCirc->parameter(aPntEnd, paramTolerance, anEndParameter);
+    if(aPntStart->distance(theFilletPoint) > tolerance) {
+      double aTmpParameter = aStartParameter;
+      aStartParameter = anEndParameter;
+      anEndParameter = aTmpParameter;
+    }
+    double aPntParameter = aStartParameter + (anEndParameter - aStartParameter) / 3.0;
+    aCirc->D0(aPntParameter, thePoint);
+  }
+}
+
+double getProjectionDistance(const FeaturePtr theFeature,
+                             const std::shared_ptr<GeomAPI_Pnt2d> thePoint)
+{
+  std::shared_ptr<GeomAPI_Pnt2d> aProjectPnt;
+  if(theFeature->getKind() == SketchPlugin_Line::ID()) {
+    std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Lin2d> aLin(new GeomAPI_Lin2d(aPntStart, aPntEnd));
+    aProjectPnt = aLin->project(thePoint);
+  } else {
+    std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
+    std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
+    aProjectPnt = aCirc->project(thePoint);
+  }
+  if(aProjectPnt.get()) {
+    return aProjectPnt->distance(thePoint);
+  }
+  return -1;
+}