Salome HOME
Crash when searching duplicated constraints
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintFillet.cpp
index e094b12a76f7028e5fc073bffab02ba9bb149cd6..0b45147523c952279bd3a689b3338a31d23a7d7b 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>
@@ -25,6 +27,7 @@
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintTangent.h>
 #include <SketchPlugin_ConstraintRadius.h>
+#include <SketchPlugin_Point.h>
 #include <SketchPlugin_Tools.h>
 
 #include <SketcherPrs_Factory.h>
 #include <Config_PropManager.h>
 #include <Events_Loop.h>
 
+#define _USE_MATH_DEFINES
 #include <math.h>
 
 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);
@@ -48,6 +55,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()
 {
 }
@@ -69,6 +85,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>(
@@ -87,6 +105,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()));
@@ -97,75 +123,10 @@ void SketchPlugin_ConstraintFillet::execute()
 
   // Obtain base features
   FeaturePtr anOldFeatureA, anOldFeatureB;
-  if(needNewObjects) {
-    AttributePtr anAttrBase = aBaseA->attr();
-    const std::set<AttributePtr>& aRefsList = anAttrBase->owner()->data()->refsToMe();
-    std::set<AttributePtr>::const_iterator aIt;
-    FeaturePtr aCoincident;
-    for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
-      std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
-      FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
-      if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
-        AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-          aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
-        AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-          aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
-        if(anAttrRefA.get() && !anAttrRefA->isObject()) {
-          AttributePtr anAttrA = anAttrRefA->attr();
-          if(anAttrBase == anAttrA) {
-            aCoincident = aConstrFeature;
-            break;
-          }
-        }
-        if(anAttrRefA.get() && !anAttrRefB->isObject()) {
-          AttributePtr anAttrB = anAttrRefB->attr();
-          if(anAttrBase == anAttrB) {
-            aCoincident = aConstrFeature;
-            break;
-          }
-        }
-      }
-    }
-
-    if(!aCoincident.get()) {
-      setError("No coincident edges at selected vertex");
-      return;
-    }
-
-    std::set<FeaturePtr> aCoinsideLines;
-    SketchPlugin_Tools::findCoincidences(aCoincident,
-                                         SketchPlugin_ConstraintCoincidence::ENTITY_A(),
-                                         aCoinsideLines);
-    SketchPlugin_Tools::findCoincidences(aCoincident,
-                                         SketchPlugin_ConstraintCoincidence::ENTITY_B(),
-                                         aCoinsideLines);
-
-    // Remove auxilary lines
-    if(aCoinsideLines.size() > 2) {
-      std::set<FeaturePtr> aNewLines;
-      for(std::set<FeaturePtr>::iterator anIt = aCoinsideLines.begin(); anIt != aCoinsideLines.end(); ++anIt) {
-        if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
-          aNewLines.insert(*anIt);
-        }
-      }
-      aCoinsideLines = aNewLines;
-    }
-
-
-    if(aCoinsideLines.size() != 2) {
-      setError("At selected vertex should be two coincident lines");
-      return;
-    }
-
-    std::set<FeaturePtr>::iterator aLinesIt = aCoinsideLines.begin();
-    anOldFeatureA = *aLinesIt++;
-    anOldFeatureB = *aLinesIt;
-  } else {
-    std::list<ObjectPtr> aNewFeatList = aRefListOfBaseLines->list();
-    std::list<ObjectPtr>::iterator aFeatIt = aNewFeatList.begin();
-    anOldFeatureA = ModelAPI_Feature::feature(*aFeatIt++);
-    anOldFeatureB = ModelAPI_Feature::feature(*aFeatIt++);
-  }
+  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");
@@ -176,7 +137,7 @@ void SketchPlugin_ConstraintFillet::execute()
   if (needNewObjects) {
     // Create list of objects composing a fillet
     // copy aFeatureA
-    aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureB, sketch());
+    aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureA, sketch());
     // copy aFeatureB
     aNewFeatureB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureB, sketch());
     // create filleting arc (it will be attached to the list later)
@@ -220,16 +181,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++) {
@@ -249,7 +206,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));
@@ -270,6 +227,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(
@@ -279,7 +240,9 @@ void SketchPlugin_ConstraintFillet::execute()
       aNewFeatureB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue(
       aTangentPntB->x(), aTangentPntB->y());
   aNewFeatureB->execute();
-  // update fillet arc
+  // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated"
+  // by arc; moreover, it may cause cyclicity in hte mechanism of updater
+  aNewArc->data()->blockSendAttributeUpdated(true);
   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
       aNewArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(
       aCenter->x(), aCenter->y());
@@ -288,10 +251,17 @@ 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();
 
   if (needNewObjects) {
@@ -300,10 +270,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);
@@ -377,6 +343,22 @@ void SketchPlugin_ConstraintFillet::execute()
       aRefAttr->setAttr(aNewFeature[i]->attribute(aFeatAttributes[anAttrInd]));
       myProducedFeatures.push_back(aConstraint);
     }
+    // 4.1. Additional tangency constraints when the fillet is based on arcs.
+    //      It is used to verify the created arc will be placed on a source.
+    for (int i = 0; i < aNbFeatures; ++i) {
+      if (aNewFeature[i]->getKind() != SketchPlugin_Arc::ID())
+        continue;
+      aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
+      aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+          aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
+      aRefAttr->setObject(aFeature[i]->lastResult());
+      aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+          aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
+      aRefAttr->setObject(aNewFeature[i]->lastResult());
+      aConstraint->execute();
+      myProducedFeatures.push_back(aConstraint);
+      ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
+    }
     // 5. Tangent points should be placed on the base features
     for (int i = 0; i < aNbFeatures; i++) {
       anAttrInd = 2*i + (isStart[i] ? 0 : 1);
@@ -439,6 +421,11 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
         data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
     aRefListOfFillet->clear();
 
+    // clear the list of base features
+    AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
+        data()->attribute(SketchPlugin_Constraint::ENTITY_C()));
+      aRefListOfBaseLines->clear();
+
     // remove all produced objects and constraints
     DocumentPtr aDoc = sketch()->document();
     std::list<FeaturePtr>::iterator aCIt = myProducedFeatures.begin();
@@ -450,6 +437,107 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
     for (aCIt = myBaseObjects.begin(); aCIt != myBaseObjects.end(); ++aCIt)
       (*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();
+
+    // 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) {
+      std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
+      FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
+      if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
+        AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+          aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
+        AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+          aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
+        if(anAttrRefA.get() && !anAttrRefA->isObject()) {
+          AttributePtr anAttrA = anAttrRefA->attr();
+          if(anAttrBaseA == anAttrA) {
+            aCoincident = aConstrFeature;
+            break;
+          }
+        }
+        if(anAttrRefA.get() && !anAttrRefB->isObject()) {
+          AttributePtr anAttrB = anAttrRefB->attr();
+          if(anAttrBaseA == anAttrB) {
+            aCoincident = aConstrFeature;
+            break;
+          }
+        }
+      }
+    }
+
+    if(!aCoincident.get()) {
+      setError("No coincident edges at selected vertex");
+      return;
+    }
+
+    std::set<FeaturePtr> aCoinsides;
+    SketchPlugin_Tools::findCoincidences(aCoincident,
+                                         SketchPlugin_ConstraintCoincidence::ENTITY_A(),
+                                         aCoinsides);
+    SketchPlugin_Tools::findCoincidences(aCoincident,
+                                         SketchPlugin_ConstraintCoincidence::ENTITY_B(),
+                                         aCoinsides);
+
+    // Remove points
+    std::set<FeaturePtr> aNewLines;
+    for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin(); anIt != aCoinsides.end(); ++anIt) {
+      if((*anIt)->getKind() != SketchPlugin_Point::ID()) {
+        aNewLines.insert(*anIt);
+      }
+    }
+    aCoinsides = aNewLines;
+
+    // Remove auxilary lines
+    if(aCoinsides.size() > 2) {
+      aNewLines.clear();
+      for(std::set<FeaturePtr>::iterator anIt = aCoinsides.begin(); anIt != aCoinsides.end(); ++anIt) {
+        if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
+          aNewLines.insert(*anIt);
+        }
+      }
+      aCoinsides = aNewLines;
+    }
+
+    if(aCoinsides.size() != 2) {
+      setError("At selected vertex should be two coincident lines");
+      return;
+    }
+
+    // Store base lines
+    FeaturePtr anOldFeatureA, anOldFeatureB;
+    std::set<FeaturePtr>::iterator aLinesIt = aCoinsides.begin();
+    anOldFeatureA = *aLinesIt++;
+    anOldFeatureB = *aLinesIt;
+    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);
+
+    /// Getting distances
+    double aRadius = 1;
+    double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA);
+    double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB);
+    aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0;
+
+    std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aRadius);
   }
 }
 
@@ -656,23 +744,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));
@@ -683,7 +784,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()) {
@@ -727,3 +828,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;
+}