Salome HOME
Fix for crash in fillet
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintFillet.cpp
index a9f00c9f48b58520803e6a7b11f23c71869d9a60..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>
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintTangent.h>
 #include <SketchPlugin_ConstraintRadius.h>
+#include <SketchPlugin_Tools.h>
 
 #include <SketcherPrs_Factory.h>
+#include <SketcherPrs_Tools.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);
@@ -46,6 +54,14 @@ 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()
 {
@@ -55,10 +71,12 @@ void SketchPlugin_ConstraintFillet::initAttributes()
 {
   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
   data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
-  data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
+  // This attribute used to store base edges
   data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::typeId());
   data()->addAttribute(PREVIOUS_VALUE, ModelAPI_AttributeDouble::typeId());
   // initialize attribute not applicable for user
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
   data()->attribute(PREVIOUS_VALUE)->setInitialized();
   std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(PREVIOUS_VALUE))->setValue(0.0);
@@ -66,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>(
@@ -79,32 +99,46 @@ void SketchPlugin_ConstraintFillet::execute()
       aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
   AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
       aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
-  AttributeRefAttrPtr aBaseB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-      aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
-  if (!aBaseA->isInitialized() || !aBaseB->isInitialized() ||
-      !aBaseA->isObject() || !aBaseB->isObject())
+  if (!aBaseA->isInitialized() || aBaseA->isObject()) {
+    setError("Bad vertex selected");
     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_C()));
+      aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
   bool needNewObjects = aRefListOfFillet->size() == 0;
 
-  // Obtain features for the base objects
-  FeaturePtr aFeatureA, aFeatureB;
-  aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aBaseA->object());
-  if (aRC) aFeatureA = aRC->document()->feature(aRC);
-  aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aBaseB->object());
-  if (aRC) aFeatureB = aRC->document()->feature(aRC);
-  if (!aFeatureA || !aFeatureB)
+  AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
+      aData->attribute(SketchPlugin_Constraint::ENTITY_C()));
+
+  // Obtain base features
+  FeaturePtr anOldFeatureA, anOldFeatureB;
+  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;
+  }
 
   FeaturePtr aNewFeatureA, aNewFeatureB, aNewArc;
   if (needNewObjects) {
     // Create list of objects composing a fillet
     // copy aFeatureA
-    aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aFeatureA, sketch());
+    aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureA, sketch());
     // copy aFeatureB
-    aNewFeatureB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aFeatureB, sketch());
+    aNewFeatureB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureB, sketch());
     // create filleting arc (it will be attached to the list later)
     aNewArc = sketch()->addFeature(SketchPlugin_Arc::ID());
   } else {
@@ -118,7 +152,7 @@ void SketchPlugin_ConstraintFillet::execute()
 
   // Calculate arc attributes
   static const int aNbFeatures = 2;
-  FeaturePtr aFeature[aNbFeatures] = {aFeatureA, aFeatureB};
+  FeaturePtr aFeature[aNbFeatures] = {anOldFeatureA, anOldFeatureB};
   FeaturePtr aNewFeature[aNbFeatures] = {aNewFeatureA, aNewFeatureB};
   std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
   bool isStart[aNbFeatures]; // indicates which point the features share
@@ -136,6 +170,7 @@ void SketchPlugin_ConstraintFillet::execute()
       aRefListOfFillet->remove(aNewFeatureA);
       aRefListOfFillet->remove(aNewFeatureB);
       aRefListOfFillet->remove(aNewArc);
+      aRefListOfBaseLines->clear();
       return;
     }
     aFeatAttributes[2*i] = aStartAttr;
@@ -145,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++) {
@@ -174,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));
@@ -194,7 +225,11 @@ void SketchPlugin_ConstraintFillet::execute()
 
   // Calculate fillet arc parameters
   std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
-  calculateFilletCenter(aFeatureA, aFeatureB, aFilletRadius, isStart, 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(
@@ -204,7 +239,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());
@@ -213,10 +250,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) {
@@ -260,18 +304,18 @@ void SketchPlugin_ConstraintFillet::execute()
     myProducedFeatures.push_back(aConstraint);
     ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
     // 2. Fillet arc radius
-    aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
-    aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-        aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
-    aRefAttr->setObject(aNewArc->lastResult());
-    std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
-        aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
-    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-        aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
-        isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
-    aConstraint->execute();
-    myProducedFeatures.push_back(aConstraint);
-    ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
+    //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
+    //aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+    //    aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
+    //aRefAttr->setObject(aNewArc->lastResult());
+    //std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
+    //    aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
+    //std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+    //    aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
+    //    isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
+    //aConstraint->execute();
+    //myProducedFeatures.push_back(aConstraint);
+    //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
     // 3. Tangency of fillet arc and features
     for (int i = 0; i < aNbFeatures; i++) {
       aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
@@ -311,14 +355,14 @@ void SketchPlugin_ConstraintFillet::execute()
       myProducedFeatures.push_back(aConstraint);
     }
     // make base features auxiliary
-    aFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
-    aFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
+    anOldFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
+    anOldFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
     myBaseObjects.clear();
-    myBaseObjects.push_back(aFeatureA);
-    myBaseObjects.push_back(aFeatureB);
+    myBaseObjects.push_back(anOldFeatureA);
+    myBaseObjects.push_back(anOldFeatureB);
     // exchange the naming IDs of newly created and old line that become auxiliary
-    sketch()->exchangeIDs(aFeatureA, aNewFeatureA);
-    sketch()->exchangeIDs(aFeatureB, aNewFeatureB);
+    sketch()->exchangeIDs(anOldFeatureA, aNewFeatureA);
+    sketch()->exchangeIDs(anOldFeatureB, aNewFeatureB);
   } else {
     // Update radius value
     int aNbSubs = sketch()->numberOfSubs();
@@ -354,13 +398,17 @@ void SketchPlugin_ConstraintFillet::execute()
 
 void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
 {
-  if (theID == SketchPlugin_Constraint::ENTITY_A() ||
-      theID == SketchPlugin_Constraint::ENTITY_B()) {
+  if (theID == SketchPlugin_Constraint::ENTITY_A()) {
     // clear the list of fillet entities
     AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
-        data()->attribute(SketchPlugin_Constraint::ENTITY_C()));
+        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();
@@ -372,6 +420,98 @@ 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> 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;
+    }
+
+    // Store base lines
+    FeaturePtr anOldFeatureA, anOldFeatureB;
+    std::set<FeaturePtr>::iterator aLinesIt = aCoinsideLines.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);
   }
 }
 
@@ -578,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));
@@ -605,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()) {
@@ -649,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;
+}