Salome HOME
Update copyrights
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Fillet.cpp
index 7039a37ad1c79d4bf6d66fabe97770984f6affd3..e076f47c2454d2b1ba6081c6dc91f7fb6e9b8f43 100644 (file)
@@ -1,8 +1,21 @@
-// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
-
-// File:    SketchPlugin_Fillet.cpp
-// Created: 19 Mar 2015
-// Author:  Artem ZHIDKOV
+// Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
 
 #include "SketchPlugin_Fillet.h"
 
 #include "SketchPlugin_Line.h"
 #include "SketchPlugin_Point.h"
 #include "SketchPlugin_Sketch.h"
+#include "SketchPlugin_ConstraintDistance.h"
 #include "SketchPlugin_ConstraintEqual.h"
 #include "SketchPlugin_ConstraintCoincidence.h"
 #include "SketchPlugin_ConstraintLength.h"
+#include "SketchPlugin_ConstraintMiddle.h"
 #include "SketchPlugin_ConstraintTangent.h"
 #include "SketchPlugin_ConstraintRadius.h"
 #include "SketchPlugin_Tools.h"
 
+#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeRefAttr.h>
 #include <ModelAPI_Data.h>
 #include <ModelAPI_Events.h>
@@ -24,6 +41,7 @@
 #include <ModelAPI_Tools.h>
 #include <ModelAPI_Validator.h>
 
+#include <GeomAlgoAPI_Circ2dBuilder.h>
 #include <GeomAlgoAPI_EdgeBuilder.h>
 
 #include <GeomAPI_Circ2d.h>
 
 const double tolerance = 1.e-7;
 const double paramTolerance = 1.e-4;
+const double PI = 3.141592653589793238463;
 
 /// \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);
 
+
+/// \brief Calculate radius of a fillet.
+///        It should not be greater than 1/3 of shortest edge length.
+static double calculateFilletRadius(FeaturePtr theFilletFeatures[2]);
+
 /// \brief Calculates center of fillet arc and coordinates of tangency points
-static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
-                                  double theRadius, bool theNotInversed[2],
+static void calculateFilletCenter(FeaturePtr theFilletFeatures[2],
+                                  double theFilletRadius,
+                                  const std::shared_ptr<GeomAPI_Ax3>& theSketchPlane,
                                   std::shared_ptr<GeomAPI_XY>& theCenter,
                                   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);
-
-/// Get coincide edges for fillet
-static std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence);
-
 static std::set<FeaturePtr> findFeaturesToRemove(const FeaturePtr theFeature,
                                                  const AttributePtr theAttribute);
 
 SketchPlugin_Fillet::SketchPlugin_Fillet()
-: myFilletCreated(false)
+: myFilletCreated(false), myIsReversed(false)
 {
+  myIsNotInversed[0] = myIsNotInversed[1] = true;
 }
 
 void SketchPlugin_Fillet::initAttributes()
@@ -85,102 +99,68 @@ void SketchPlugin_Fillet::execute()
   if (isUpdateFlushed)
     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
 
-  // Calculate Fillet parameters if does not yet
-  if (!myBaseFeatures[0] || !myBaseFeatures[1])
-    calculateFilletParameters();
+  // set flag here to avoid building Fillet presentation if "Redisplay" event appears
+  myFilletCreated = true;
 
-  // Create arc feature.
-  FeaturePtr aFilletArc = sketch()->addFeature(SketchPlugin_Arc::ID());
+  // create feature for fillet arc
+  FeaturePtr aFilletArc = createFilletArc();
+  if (!aFilletArc) {
+    setError("Error: unable to create a fillet arc.");
+    return;
+  }
 
-  // Set arc attributes.
-  bool aWasBlocked = aFilletArc->data()->blockSendAttributeUpdated(true);
-  std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-      aFilletArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(myCenterXY->x(),
-                                                                      myCenterXY->y());
-  std::shared_ptr<GeomDataAPI_Point2D> aStartPoint =
-      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-          aFilletArc->attribute(SketchPlugin_Arc::START_ID()));
-  std::shared_ptr<GeomDataAPI_Point2D> aEndPoint =
-      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-          aFilletArc->attribute(SketchPlugin_Arc::END_ID()));
-  if(aStartPoint->isInitialized() && aEndPoint->isInitialized()
-      && (aStartPoint->pnt()->xy()->distance(myTangentXY1) > tolerance
-      || aEndPoint->pnt()->xy()->distance(myTangentXY2) > tolerance)) {
-    std::dynamic_pointer_cast<SketchPlugin_Arc>(aFilletArc)->setReversed(false);
+  // collect features referred to the edges participating in fillet
+  AttributePoint2DPtr aFilletPoints[2];
+  int aFeatInd[2];
+  int anAttrInd[2];
+  std::set<FeaturePtr> aFeaturesToBeRemoved;
+  for (int i = 0; i < 2; ++i) {
+    bool isFirstIndex = (i == 0);
+    aFeatInd[i] = myIsReversed == isFirstIndex ? 1 : 0;
+    anAttrInd[i] = (myIsReversed == isFirstIndex ? 2 : 0) + (myIsNotInversed[aFeatInd[i]] ? 0 : 1);
+    aFilletPoints[i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        myBaseFeatures[aFeatInd[i]]->attribute(myFeatAttributes[anAttrInd[i]]));
+    std::set<FeaturePtr> aRemove =
+        findFeaturesToRemove(myBaseFeatures[aFeatInd[i]], aFilletPoints[i]);
+    aFeaturesToBeRemoved.insert(aRemove.begin(), aRemove.end());
   }
-  aStartPoint->setValue(myTangentXY1->x(), myTangentXY1->y());
-  aEndPoint->setValue(myTangentXY2->x(), myTangentXY2->y());
-  aFilletArc->data()->blockSendAttributeUpdated(aWasBlocked);
-  aFilletArc->execute();
 
-  // Delete features with refs to points of edges.
-  std::shared_ptr<GeomDataAPI_Point2D> aStartPoint1;
-  int aFeatInd1 = myIsReversed ? 1 : 0;
-  int anAttrInd1 = (myIsReversed ? 2 : 0) + (myIsNotInversed[aFeatInd1] ? 0 : 1);
-  aStartPoint1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-      myBaseFeatures[aFeatInd1]->attribute(myFeatAttributes[anAttrInd1]));
-  std::set<FeaturePtr> aFeaturesToBeRemoved1 =
-    findFeaturesToRemove(myBaseFeatures[aFeatInd1], aStartPoint1);
-
-  std::shared_ptr<GeomDataAPI_Point2D> aStartPoint2;
-  int aFeatInd2 = myIsReversed ? 0 : 1;
-  int anAttrInd2 = (myIsReversed ? 0 : 2) + (myIsNotInversed[aFeatInd2] ? 0 : 1);
-  aStartPoint2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-      myBaseFeatures[aFeatInd2]->attribute(myFeatAttributes[anAttrInd2]));
-  std::set<FeaturePtr> aFeaturesToBeRemoved2 =
-    findFeaturesToRemove(myBaseFeatures[aFeatInd2], aStartPoint2);
-
-  aFeaturesToBeRemoved1.insert(aFeaturesToBeRemoved2.begin(), aFeaturesToBeRemoved2.end());
-  ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved1);
-  Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
+  // keep "distance" constraints and remove all other references
+  removeReferencesButKeepDistances(aFeaturesToBeRemoved, aFilletPoints);
 
   // Update fillet edges.
   recalculateAttributes(aFilletArc, SketchPlugin_Arc::START_ID(),
-                        myBaseFeatures[aFeatInd1], myFeatAttributes[anAttrInd1]);
+                        myBaseFeatures[aFeatInd[0]], myFeatAttributes[anAttrInd[0]]);
   recalculateAttributes(aFilletArc, SketchPlugin_Arc::END_ID(),
-                        myBaseFeatures[aFeatInd2], myFeatAttributes[anAttrInd2]);
+                        myBaseFeatures[aFeatInd[1]], myFeatAttributes[anAttrInd[1]]);
+
+  FeaturePtr aConstraint;
 
   // Create coincidence features.
-  FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
-  AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-      aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
-  aRefAttr->setAttr(aFilletArc->attribute(SketchPlugin_Arc::START_ID()));
-  aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-      aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
-  aRefAttr->setAttr(myBaseFeatures[aFeatInd1]->attribute(myFeatAttributes[anAttrInd1]));
-  aConstraint->execute();
+  aConstraint = SketchPlugin_Tools::createConstraintAttrAttr(sketch(),
+                    SketchPlugin_ConstraintCoincidence::ID(),
+                    aFilletArc->attribute(SketchPlugin_Arc::START_ID()),
+                    myBaseFeatures[aFeatInd[0]]->attribute(myFeatAttributes[anAttrInd[0]]));
   ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
-  aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
-  aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-      aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
-  aRefAttr->setAttr(aFilletArc->attribute(SketchPlugin_Arc::END_ID()));
-  aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-      aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
-  aRefAttr->setAttr(myBaseFeatures[aFeatInd2]->attribute(myFeatAttributes[anAttrInd2]));
-  aConstraint->execute();
+  aConstraint = SketchPlugin_Tools::createConstraintAttrAttr(sketch(),
+                    SketchPlugin_ConstraintCoincidence::ID(),
+                    aFilletArc->attribute(SketchPlugin_Arc::END_ID()),
+                    myBaseFeatures[aFeatInd[1]]->attribute(myFeatAttributes[anAttrInd[1]]));
   ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
 
   // Create tangent features.
   for (int i = 0; i < 2; i++) {
-    aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
-    aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-        aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
-    aRefAttr->setObject(aFilletArc->lastResult());
-    aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
-        aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
-    bool isArc = myBaseFeatures[i]->getKind() == SketchPlugin_Arc::ID();
-    aRefAttr->setObject(isArc ? myBaseFeatures[i]->lastResult() :
-                                myBaseFeatures[i]->firstResult());
+    aConstraint = SketchPlugin_Tools::createConstraintObjectObject(sketch(),
+                      SketchPlugin_ConstraintTangent::ID(),
+                      aFilletArc->lastResult(),
+                      myBaseFeatures[i]->lastResult());
     aConstraint->execute();
     ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
   }
 
   // Send events to update the sub-features by the solver.
-  if(isUpdateFlushed) {
+  if (isUpdateFlushed)
     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
-  }
-
-  myFilletCreated = true;
 }
 
 AISObjectPtr SketchPlugin_Fillet::getAISObject(AISObjectPtr thePrevious)
@@ -227,23 +207,28 @@ bool SketchPlugin_Fillet::calculateFilletParameters()
   if (!aFilletPoint2D.get())
     return false;
 
-  if (!findFeaturesContainingFilletPoint(aFilletPoint2D)) {
+  std::set<AttributePoint2DPtr> aCoincidentPoints =
+      SketchPlugin_Tools::findPointsCoincidentToPoint(aFilletPoint2D);
+  std::set<FeaturePtr> aFilletFeatures;
+  for (std::set<AttributePoint2DPtr>::iterator aCPIt = aCoincidentPoints.begin();
+       aCPIt != aCoincidentPoints.end(); ++aCPIt) {
+    std::shared_ptr<SketchPlugin_Feature> anOwner =
+        std::dynamic_pointer_cast<SketchPlugin_Feature>(
+        ModelAPI_Feature::feature((*aCPIt)->owner()));
+    if (anOwner && !anOwner->isExternal())
+      aFilletFeatures.insert(anOwner);
+  }
+  if (aFilletFeatures.size() != 2) {
     setError("Error: Selected point does not have two suitable edges for fillet.");
     return false;
   }
 
-  // Getting points located at 1/3 of edge length from fillet point.
-  std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
-  std::shared_ptr<GeomAPI_Pnt2d> aPnt1, aPnt2;
-  getPointOnEdge(myBaseFeatures[0], aFilletPnt2d, aPnt1);
-  getPointOnEdge(myBaseFeatures[1], aFilletPnt2d, aPnt2);
-
-  /// Getting distances.
-  double aDistance1 = getProjectionDistance(myBaseFeatures[1], aPnt1);
-  double aDistance2 = getProjectionDistance(myBaseFeatures[0], aPnt2);
+  std::set<FeaturePtr>::iterator aFIt = aFilletFeatures.begin();
+  myBaseFeatures[0] = *aFIt;
+  myBaseFeatures[1] = *(++aFIt);
 
-  // Calculate radius value for fillet.
-  double aRadius = aDistance1 < aDistance2 ? aDistance1 / 2.0 : aDistance2 / 2.0;
+  std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
+  double aRadius = calculateFilletRadius(myBaseFeatures);
 
   // Calculate arc attributes.
   static const int aNbFeatures = 2;
@@ -276,8 +261,9 @@ bool SketchPlugin_Fillet::calculateFilletParameters()
       }
   }
 
-  calculateFilletCenter(myBaseFeatures[0], myBaseFeatures[1], aRadius,
-      myIsNotInversed, myCenterXY, myTangentXY1, myTangentXY2);
+  std::shared_ptr<GeomAPI_Ax3> aSketchPlane = SketchPlugin_Sketch::plane(sketch());
+  calculateFilletCenter(myBaseFeatures, aRadius, aSketchPlane,
+                        myCenterXY, myTangentXY1, myTangentXY2);
 
   // Tangent directions of the features in coincident point.
   std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures];
@@ -320,49 +306,180 @@ bool SketchPlugin_Fillet::calculateFilletParameters()
   return true;
 }
 
-bool SketchPlugin_Fillet::findFeaturesContainingFilletPoint(
-    std::shared_ptr<GeomDataAPI_Point2D> theFilletPoint)
+FeaturePtr SketchPlugin_Fillet::createFilletArc()
 {
-  // Obtain constraint coincidence for the fillet point.
-  FeaturePtr aConstraintCoincidence;
-  const std::set<AttributePtr>& aRefsList = theFilletPoint->owner()->data()->refsToMe();
-  for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin();
-      anIt != aRefsList.cend();
-      ++anIt) {
-    std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
-    FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
-    if(aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
-      AttributeRefAttrPtr anAttrRefA =
-          aFeature->refattr(SketchPlugin_ConstraintCoincidence::ENTITY_A());
-      AttributeRefAttrPtr anAttrRefB =
-          aFeature->refattr(SketchPlugin_ConstraintCoincidence::ENTITY_B());
-      if(anAttrRefA.get() && !anAttrRefA->isObject()) {
-        AttributePtr anAttrA = anAttrRefA->attr();
-        if(theFilletPoint == anAttrA) {
-          aConstraintCoincidence = aFeature;
-          break;
+  // Calculate Fillet parameters if does not yet
+  if (!myBaseFeatures[0] || !myBaseFeatures[1])
+    calculateFilletParameters();
+
+  // fix for issue #2810 (sometimes, myCenterXY is NULL, fillet should report an error)
+  if (!myCenterXY)
+    return FeaturePtr();
+
+  // Create arc feature.
+  FeaturePtr aFilletArc = sketch()->addFeature(SketchPlugin_Arc::ID());
+
+  // Set arc attributes.
+  bool aWasBlocked = aFilletArc->data()->blockSendAttributeUpdated(true);
+  std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aFilletArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(myCenterXY->x(),
+                                                                      myCenterXY->y());
+  std::shared_ptr<GeomDataAPI_Point2D> aStartPoint =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+          aFilletArc->attribute(SketchPlugin_Arc::START_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aEndPoint =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+          aFilletArc->attribute(SketchPlugin_Arc::END_ID()));
+  if(aStartPoint->isInitialized() && aEndPoint->isInitialized()
+      && (aStartPoint->pnt()->xy()->distance(myTangentXY1) > tolerance
+      || aEndPoint->pnt()->xy()->distance(myTangentXY2) > tolerance)) {
+    std::dynamic_pointer_cast<SketchPlugin_Arc>(aFilletArc)->setReversed(false);
+  }
+  aStartPoint->setValue(myTangentXY1->x(), myTangentXY1->y());
+  aEndPoint->setValue(myTangentXY2->x(), myTangentXY2->y());
+  aFilletArc->data()->blockSendAttributeUpdated(aWasBlocked);
+  aFilletArc->execute();
+
+  return aFilletArc;
+}
+
+FeaturePtr SketchPlugin_Fillet::createFilletApex(const GeomPnt2dPtr& theCoordinates)
+{
+  FeaturePtr anApex = sketch()->addFeature(SketchPlugin_Point::ID());
+  AttributePoint2DPtr aCoord = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      anApex->attribute(SketchPlugin_Point::COORD_ID()));
+  aCoord->setValue(theCoordinates);
+  anApex->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true);
+
+  // additional coincidence constraints
+  static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
+  FeaturePtr aConstraint;
+  for (int i = 0; i < 2; i++) {
+    aConstraint = SketchPlugin_Tools::createConstraintAttrObject(sketch(),
+                      SketchPlugin_ConstraintCoincidence::ID(),
+                      aCoord,
+                      myBaseFeatures[i]->lastResult());
+    aConstraint->execute();
+    ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
+  }
+
+  return anApex;
+}
+
+struct Length {
+  AttributePtr myPoints[2];
+  std::string myValueText;
+  double myValueDouble;
+  GeomPnt2dPtr myFlyoutPoint;
+  int myLocationType;
+};
+
+void SketchPlugin_Fillet::removeReferencesButKeepDistances(
+    std::set<FeaturePtr>& theFeaturesToRemove,
+    const AttributePoint2DPtr theFilletPoints[2])
+{
+  FeaturePtr aFilletApex;
+  std::list<Length> aLengthToDistance;
+
+  std::set<FeaturePtr>::iterator aFeat = theFeaturesToRemove.begin();
+  while (aFeat != theFeaturesToRemove.end()) {
+    std::shared_ptr<SketchPlugin_ConstraintDistance> aDistance =
+        std::dynamic_pointer_cast<SketchPlugin_ConstraintDistance>(*aFeat);
+    if (aDistance) {
+      if (!aFilletApex)
+        aFilletApex = createFilletApex(theFilletPoints[0]->pnt());
+      // update attributes of distance constraints
+      bool isUpdated = false;
+      for (int attrInd = 0; attrInd < CONSTRAINT_ATTR_SIZE && !isUpdated; ++attrInd) {
+        AttributeRefAttrPtr aRefAttr =
+            aDistance->refattr(SketchPlugin_Constraint::ATTRIBUTE(attrInd));
+        if (aRefAttr && !aRefAttr->isObject() &&
+           (aRefAttr->attr() == theFilletPoints[0] || aRefAttr->attr() == theFilletPoints[1])) {
+          aRefAttr->setAttr(aFilletApex->attribute(SketchPlugin_Point::COORD_ID()));
+          isUpdated = true;
         }
       }
-      if(anAttrRefB.get() && !anAttrRefB->isObject()) {
-        AttributePtr anAttrB = anAttrRefB->attr();
-        if(theFilletPoint == anAttrB) {
-          aConstraintCoincidence = aFeature;
-          break;
+      // avoid distance from removing if it is updated
+      std::set<FeaturePtr>::iterator aKeepIt = aFeat++;
+      if (isUpdated)
+        theFeaturesToRemove.erase(aKeepIt);
+
+    } else {
+      std::shared_ptr<SketchPlugin_ConstraintLength> aLength =
+          std::dynamic_pointer_cast<SketchPlugin_ConstraintLength>(*aFeat);
+      if (aLength) {
+        if (!aFilletApex)
+          aFilletApex = createFilletApex(theFilletPoints[0]->pnt());
+        // remove Length, but create new distance constraint
+        AttributeRefAttrPtr aRefAttr =
+          aLength->refattr(SketchPlugin_Constraint::ENTITY_A());
+        FeaturePtr aLine = ModelAPI_Feature::feature(aRefAttr->object());
+        if (aLine) {
+          aLengthToDistance.push_back(Length());
+          Length& aNewLength = aLengthToDistance.back();
+          // main attrbutes
+          for (int i = 0; i < 2; ++i) {
+            AttributePtr anAttr = aLine->attribute(
+                i == 0 ? SketchPlugin_Line::START_ID() : SketchPlugin_Line::END_ID());
+            if (anAttr == theFilletPoints[0] || anAttr == theFilletPoints[1])
+              aNewLength.myPoints[i] = aFilletApex->attribute(SketchPlugin_Point::COORD_ID());
+            else
+              aNewLength.myPoints[i] = anAttr;
+          }
+          // value
+          AttributeDoublePtr aValue = aLength->real(SketchPlugin_Constraint::VALUE());
+          aNewLength.myValueDouble = aValue->value();
+          aNewLength.myValueText = aValue->text();
+          // auxiliary attributes
+          AttributePoint2DPtr aFlyoutAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+              aLength->attribute(SketchPlugin_ConstraintLength::FLYOUT_VALUE_PNT()));
+          if (aFlyoutAttr && aFlyoutAttr->isInitialized())
+            aNewLength.myFlyoutPoint = SketchPlugin_Tools::flyoutPointCoordinates(aLength);
+          AttributeIntegerPtr aLocationAttr =
+              aLength->integer(SketchPlugin_ConstraintLength::LOCATION_TYPE_ID());
+          if (aLocationAttr && aLocationAttr->isInitialized())
+            aNewLength.myLocationType = aLocationAttr->value();
+          else
+            aNewLength.myLocationType = -1;
         }
       }
+
+      ++aFeat;
     }
   }
 
-  if(!aConstraintCoincidence.get())
-    return false;
-
-  // Get coincide edges.
-  std::set<FeaturePtr> anEdgeFeatures = getCoincides(aConstraintCoincidence);
-  std::set<FeaturePtr>::iterator aLinesIt = anEdgeFeatures.begin();
-  for (int i = 0; aLinesIt != anEdgeFeatures.end() && i < 2; ++aLinesIt, ++i)
-    myBaseFeatures[i] = *aLinesIt;
+  // remove references
+  ModelAPI_Tools::removeFeaturesAndReferences(theFeaturesToRemove);
+  Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
 
-  return myBaseFeatures[0] && myBaseFeatures[1];
+  // restore Length constraints as point-point distances
+  FeaturePtr aConstraint;
+  std::list<Length>::iterator anIt = aLengthToDistance.begin();
+  for (; anIt != aLengthToDistance.end(); ++anIt) {
+    aConstraint = SketchPlugin_Tools::createConstraintAttrAttr(sketch(),
+        SketchPlugin_ConstraintDistance::ID(), anIt->myPoints[0], anIt->myPoints[1]);
+    // set value
+    AttributeDoublePtr aValue = aConstraint->real(SketchPlugin_Constraint::VALUE());
+    if (anIt->myValueText.empty())
+      aValue->setValue(anIt->myValueDouble);
+    else
+      aValue->setText(anIt->myValueText);
+    // set flyout point if exists
+    if (anIt->myFlyoutPoint) {
+      AttributePoint2DPtr aFlyoutAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+          aConstraint->attribute(SketchPlugin_ConstraintDistance::FLYOUT_VALUE_PNT()));
+      aFlyoutAttr->setValue(anIt->myFlyoutPoint);
+    }
+    // set location type if initialized
+    if (anIt->myLocationType >= 0) {
+      AttributeIntegerPtr aLocationType =
+          aConstraint->integer(SketchPlugin_ConstraintDistance::LOCATION_TYPE_ID());
+      aLocationType->setValue(anIt->myLocationType);
+    }
+    aConstraint->execute();
+    ModelAPI_EventCreator::get()->sendUpdated(aConstraint,
+        Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+  }
 }
 
 // =========   Auxiliary functions   =================
@@ -375,395 +492,88 @@ void recalculateAttributes(FeaturePtr theNewArc,  const std::string& theNewArcAt
       theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
 }
 
-/// \brief Find intersections of lines shifted along normal direction
-void possibleFilletCenterLineLine(
-    std::shared_ptr<GeomAPI_XY> thePointA, std::shared_ptr<GeomAPI_Dir2d> theDirA,
-    std::shared_ptr<GeomAPI_XY> thePointB, std::shared_ptr<GeomAPI_Dir2d> theDirB,
-    double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
+static std::shared_ptr<GeomAPI_Pnt2d> toPoint(const AttributePtr& theAttribute)
 {
-  std::shared_ptr<GeomAPI_Dir2d> aDirAT(new GeomAPI_Dir2d(-theDirA->y(), theDirA->x()));
-  std::shared_ptr<GeomAPI_Dir2d> aDirBT(new GeomAPI_Dir2d(-theDirB->y(), theDirB->x()));
-  std::shared_ptr<GeomAPI_XY> aPntA, aPntB;
-  double aDet = theDirA->cross(theDirB);
-  for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
-    aPntA = thePointA->added(aDirAT->xy()->multiplied(aStepA * theRadius));
-    for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
-      aPntB = thePointB->added(aDirBT->xy()->multiplied(aStepB * theRadius));
-      double aVX = aDirAT->xy()->dot(aPntA);
-      double aVY = aDirBT->xy()->dot(aPntB);
-      std::shared_ptr<GeomAPI_XY> aPoint(new GeomAPI_XY(
-          (theDirB->x() * aVX - theDirA->x() * aVY) / aDet,
-          (theDirB->y() * aVX - theDirA->y() * aVY) / aDet));
-      theCenters.push_back(aPoint);
-    }
-  }
+  std::shared_ptr<GeomAPI_Pnt2d> aPoint;
+  AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
+  if (aPointAttr)
+    aPoint = aPointAttr->pnt();
+  return aPoint;
 }
 
-/// \brief Find intersections of line shifted along normal direction in both sides
-///        and a circle with extended radius
-void possibleFilletCenterLineArc(
-    std::shared_ptr<GeomAPI_XY> theStartLine, std::shared_ptr<GeomAPI_Dir2d> theDirLine,
-    std::shared_ptr<GeomAPI_XY> theCenterArc, double theRadiusArc,
-    double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
+static std::shared_ptr<GeomAPI_Lin2d> toLine(const FeaturePtr& theFeature)
 {
-  std::shared_ptr<GeomAPI_Dir2d> aDirT(new GeomAPI_Dir2d(-theDirLine->y(), theDirLine->x()));
-  std::shared_ptr<GeomAPI_XY> aPnt;
-  double aDirNorm2 = theDirLine->dot(theDirLine);
-  double aRad = 0.0;
-  double aDirX = theDirLine->x();
-  double aDirX2 = theDirLine->x() * theDirLine->x();
-  double aDirY2 = theDirLine->y() * theDirLine->y();
-  double aDirXY = theDirLine->x() * theDirLine->y();
-  for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
-    aPnt = theStartLine->added(aDirT->xy()->multiplied(aStepA * theRadius));
-    double aCoeff = aDirT->xy()->dot(aPnt->decreased(theCenterArc));
-    double aCoeff2 = aCoeff * aCoeff;
-    for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
-      aRad = theRadiusArc + aStepB * theRadius;
-      double aD = aRad * aRad * aDirNorm2 - aCoeff2;
-      if (aD < 0.0)
-        continue;
-      double aDs = sqrt(aD);
-      double x1 = theCenterArc->x() + (aCoeff * aDirT->x() - aDirT->y() * aDs) / aDirNorm2;
-      double x2 = theCenterArc->x() + (aCoeff * aDirT->x() + aDirT->y() * aDs) / aDirNorm2;
-      double y1 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
-          aDirXY * (aPnt->x() - theCenterArc->x()) - theDirLine->y() * aDs) / aDirNorm2;
-      double y2 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
-          aDirXY * (aPnt->x() - theCenterArc->x()) + theDirLine->y() * aDs) / aDirNorm2;
-
-      std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
-      theCenters.push_back(aPoint1);
-      std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
-      theCenters.push_back(aPoint2);
-    }
+  std::shared_ptr<GeomAPI_Lin2d> aLine;
+  if (theFeature->getKind() == SketchPlugin_Line::ID()) {
+    std::shared_ptr<GeomAPI_Pnt2d> aStart =
+        toPoint( theFeature->attribute(SketchPlugin_Line::START_ID()) );
+    std::shared_ptr<GeomAPI_Pnt2d> aEnd =
+        toPoint( theFeature->attribute(SketchPlugin_Line::END_ID()) );
+    aLine = std::shared_ptr<GeomAPI_Lin2d>(new GeomAPI_Lin2d(aStart, aEnd));
   }
+  return aLine;
 }
 
-/// \brief Find intersections of two circles with extended radii
-void possibleFilletCenterArcArc(
-    std::shared_ptr<GeomAPI_XY> theCenterA, double theRadiusA,
-    std::shared_ptr<GeomAPI_XY> theCenterB, double theRadiusB,
-    double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
+static std::shared_ptr<GeomAPI_Circ2d> toCircle(const FeaturePtr& theFeature)
 {
-  std::shared_ptr<GeomAPI_XY> aCenterDir = theCenterB->decreased(theCenterA);
-  double aCenterDist2 = aCenterDir->dot(aCenterDir);
-  double aCenterDist = sqrt(aCenterDist2);
-
-  double aRadA, aRadB;
-  for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
-    aRadA = theRadiusA + aStepA * theRadius;
-    for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
-      aRadB = theRadiusB + aStepB * theRadius;
-      if (aRadA + aRadB < aCenterDist || fabs(aRadA - aRadB) > aCenterDist)
-        continue; // there is no intersections
-
-      double aMedDist = (aRadA * aRadA - aRadB * aRadB + aCenterDist2) / (2.0 * aCenterDist);
-      double aHeight = sqrt(aRadA * aRadA - aMedDist * aMedDist);
-
-      double x1 = theCenterA->x() +
-        (aMedDist * aCenterDir->x() + aCenterDir->y() * aHeight) / aCenterDist;
-      double y1 = theCenterA->y() +
-        (aMedDist * aCenterDir->y() - aCenterDir->x() * aHeight) / aCenterDist;
-
-      double x2 = theCenterA->x() +
-        (aMedDist * aCenterDir->x() - aCenterDir->y() * aHeight) / aCenterDist;
-      double y2 = theCenterA->y() +
-        (aMedDist * aCenterDir->y() + aCenterDir->x() * aHeight) / aCenterDist;
-
-      std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
-      theCenters.push_back(aPoint1);
-      std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
-      theCenters.push_back(aPoint2);
-    }
+  std::shared_ptr<GeomAPI_Circ2d> aCircle;
+  if (theFeature->getKind() == SketchPlugin_Arc::ID()) {
+    std::shared_ptr<GeomAPI_Pnt2d> aCenter =
+        toPoint( theFeature->attribute(SketchPlugin_Arc::CENTER_ID()) );
+    std::shared_ptr<GeomAPI_Pnt2d> aStart =
+        toPoint( theFeature->attribute(SketchPlugin_Arc::START_ID()) );
+    aCircle = std::shared_ptr<GeomAPI_Circ2d>(new GeomAPI_Circ2d(aCenter, aStart));
   }
+  return aCircle;
 }
 
-void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
-                           double theRadius, bool theNotInversed[2],
+
+void calculateFilletCenter(FeaturePtr theFilletFeatures[2],
+                           double theFilletRadius,
+                           const std::shared_ptr<GeomAPI_Ax3>& theSketchPlane,
                            std::shared_ptr<GeomAPI_XY>& theCenter,
                            std::shared_ptr<GeomAPI_XY>& theTangentA,
                            std::shared_ptr<GeomAPI_XY>& theTangentB)
 {
-  static const int aNbFeatures = 2;
-  FeaturePtr aFeature[aNbFeatures] = {theFeatureA, theFeatureB};
-  std::shared_ptr<GeomAPI_XY> aStart[aNbFeatures], aEnd[aNbFeatures], aCenter[aNbFeatures];
-  std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, aEndPoint;
-
-  for (int i = 0; i < aNbFeatures; i++) {
-    if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
-      aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-          aFeature[i]->attribute(SketchPlugin_Line::START_ID()));
-      aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-          aFeature[i]->attribute(SketchPlugin_Line::END_ID()));
-    } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
-      aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-          aFeature[i]->attribute(SketchPlugin_Arc::START_ID()));
-      aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-          aFeature[i]->attribute(SketchPlugin_Arc::END_ID()));
-      aCenter[i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-          aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt()->xy();
-    } else
-      return;
-    aStart[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
-        new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()) :
-        new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()));
-    aEnd[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
-        new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()) :
-        new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()));
-  }
-
-  if (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
-      theFeatureB->getKind() == SketchPlugin_Line::ID()) {
-    std::shared_ptr<GeomAPI_Dir2d> aDir[2];
-    std::shared_ptr<GeomAPI_Dir2d> aDirT[2];
-    for (int i = 0; i < aNbFeatures; i++) {
-      aDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aEnd[i]->decreased(aStart[i])));
-      aDirT[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(-aDir[i]->y(), aDir[i]->x()));
-    }
-
-    // get and filter possible centers
-    std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
-    possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1],
-                                 theRadius, aSuspectCenters);
-    double aDot = 0.0;
-    std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
-    for (; anIt != aSuspectCenters.end(); anIt++) {
-      aDot = aDirT[0]->xy()->dot(aStart[0]->decreased(*anIt));
-      theTangentA = (*anIt)->added(aDirT[0]->xy()->multiplied(aDot));
-      if (theTangentA->decreased(aStart[0])->dot(aDir[0]->xy()) < 0.0)
-        continue; // incorrect position
-      aDot = aDirT[1]->xy()->dot(aStart[1]->decreased(*anIt));
-      theTangentB = (*anIt)->added(aDirT[1]->xy()->multiplied(aDot));
-      if (theTangentB->decreased(aStart[1])->dot(aDir[1]->xy()) < 0.0)
-        continue; // incorrect position
-      // the center is found, stop searching
-      theCenter = *anIt;
-      return;
-    }
-  } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
-      theFeatureB->getKind() == SketchPlugin_Line::ID()) ||
-      (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
-      theFeatureB->getKind() == SketchPlugin_Arc::ID())) {
-    int aLineInd = theFeatureA->getKind() == SketchPlugin_Line::ID() ? 0 : 1;
-    double anArcRadius = aStart[1-aLineInd]->distance(aCenter[1-aLineInd]);
-    std::shared_ptr<GeomAPI_Dir2d> aDirLine = std::shared_ptr<GeomAPI_Dir2d>(
-        new GeomAPI_Dir2d(aEnd[aLineInd]->decreased(aStart[aLineInd])));
-    std::shared_ptr<GeomAPI_Dir2d> aDirT = std::shared_ptr<GeomAPI_Dir2d>(
-        new GeomAPI_Dir2d(-aDirLine->y(), aDirLine->x()));
-
-    std::shared_ptr<GeomAPI_Dir2d> aStartArcDir = std::shared_ptr<GeomAPI_Dir2d>(
-        new GeomAPI_Dir2d(aStart[1-aLineInd]->decreased(aCenter[1-aLineInd])));
-    std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
-        new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd])));
-    double anArcAngle = aEndArcDir->angle(aStartArcDir);
-
-    // 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));
-      // 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;
-      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));
-      if (theFeatureA->getKind() == SketchPlugin_Line::ID()) {
-        theTangentA = aLineTgPoint;
-        theTangentB = anArcTgPoint;
-      } else {
-        theTangentA = anArcTgPoint;
-        theTangentB = aLineTgPoint;
-      }
-      //return;
-    }
-  } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
-      theFeatureB->getKind() == SketchPlugin_Arc::ID()) {
-    double anArcRadius[aNbFeatures];
-    double anArcAngle[aNbFeatures];
-    std::shared_ptr<GeomAPI_Dir2d> aStartArcDir[aNbFeatures];
-    for (int i = 0; i < aNbFeatures; i++) {
-      anArcRadius[i] = aStart[i]->distance(aCenter[i]);
-      aStartArcDir[i] = std::shared_ptr<GeomAPI_Dir2d>(
-          new GeomAPI_Dir2d(aStart[i]->decreased(aCenter[i])));
-      std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
-          new GeomAPI_Dir2d(aEnd[i]->decreased(aCenter[i])));
-      anArcAngle[i] = aEndArcDir->angle(aStartArcDir[i]);
-    }
-
-    // get and filter possible centers
-    std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
-    possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1],
-                               anArcRadius[1], theRadius, aSuspectCenters);
-    double aDot = 0.0;
-    std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
-    std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
-    for (; anIt != aSuspectCenters.end(); anIt++) {
-      std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
-          new GeomAPI_Dir2d((*anIt)->decreased(aCenter[0])));
-      double aCurAngle = aCurDir->angle(aStartArcDir[0]);
-      if (anArcAngle[0] < 0.0) aCurAngle *= -1.0;
-      if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[0]))
-        continue; // incorrect position
-      theTangentA = aCenter[0]->added(aCurDir->xy()->multiplied(anArcRadius[0]));
-
-      aCurDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1])));
-      aCurAngle = aCurDir->angle(aStartArcDir[1]);
-      if (anArcAngle[1] < 0.0) aCurAngle *= -1.0;
-      if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[1]))
-        continue; // incorrect position
-      theTangentB = aCenter[1]->added(aCurDir->xy()->multiplied(anArcRadius[1]));
-
-      // the center is found, stop searching
-      theCenter = *anIt;
-      return;
+  GeomShapePtr aShapeA = theFilletFeatures[0]->lastResult()->shape();
+  GeomShapePtr aShapeB = theFilletFeatures[1]->lastResult()->shape();
+
+  GeomAlgoAPI_Circ2dBuilder aCircBuilder(theSketchPlane);
+  aCircBuilder.addTangentCurve(aShapeA);
+  aCircBuilder.addTangentCurve(aShapeB);
+  aCircBuilder.setRadius(theFilletRadius);
+
+  std::shared_ptr<GeomAPI_Circ2d> aFilletCircle = aCircBuilder.circle();
+  if (!aFilletCircle)
+    return;
+
+  theCenter = aFilletCircle->center()->xy();
+  // tangent points
+  std::shared_ptr<GeomAPI_Pnt2d> aTgPoints[2];
+  for (int i = 0; i < 2; ++i) {
+    std::shared_ptr<GeomAPI_Circ2d> aCircle = toCircle(theFilletFeatures[i]);
+    if (aCircle)
+      aTgPoints[i] = aCircle->project(aFilletCircle->center());
+    else {
+      std::shared_ptr<GeomAPI_Lin2d> aLine = toLine(theFilletFeatures[i]);
+      if (aLine)
+        aTgPoints[i] = aLine->project(aFilletCircle->center());
     }
   }
+  theTangentA = aTgPoints[0]->xy();
+  theTangentB = aTgPoints[1]->xy();
 }
 
-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::REVERSED_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;
-}
-
-std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
+double calculateFilletRadius(FeaturePtr theFilletFeatures[2])
 {
-  std::set<FeaturePtr> aCoincides;
-
-  std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt =
-    SketchPlugin_Tools::getCoincidencePoint(theConstraintCoincidence);
-
-  SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
-                                       SketchPlugin_ConstraintCoincidence::ENTITY_A(),
-                                       aCoincides);
-  SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
-                                       SketchPlugin_ConstraintCoincidence::ENTITY_B(),
-                                       aCoincides);
-
-  // Remove points from set of coincides.
-  std::set<FeaturePtr> aNewSetOfCoincides;
-  for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
-    std::shared_ptr<SketchPlugin_SketchEntity> aSketchEntity =
-      std::dynamic_pointer_cast<SketchPlugin_SketchEntity>(*anIt);
-    if(aSketchEntity.get() && aSketchEntity->isCopy()) {
-      continue;
-    }
-    if((*anIt)->getKind() == SketchPlugin_Line::ID()) {
-      aNewSetOfCoincides.insert(*anIt);
-    } else if((*anIt)->getKind() == SketchPlugin_Arc::ID()) {
-      AttributePtr anAttrCenter = (*anIt)->attribute(SketchPlugin_Arc::CENTER_ID());
-      std::shared_ptr<GeomDataAPI_Point2D> aPointCenter2D =
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrCenter);
-      if(aPointCenter2D.get() && aFilletPnt->isEqual(aPointCenter2D->pnt())) {
-        continue;
-      }
-      aNewSetOfCoincides.insert(*anIt);
-    }
+  double aLengths[2] = { 0, 0 };
+  for (int i = 0; i < 2; ++i) {
+    GeomShapePtr aShape = theFilletFeatures[i]->lastResult()->shape();
+    std::shared_ptr<GeomAPI_Edge> anEdge = std::dynamic_pointer_cast<GeomAPI_Edge>(aShape);
+    if (anEdge)
+      aLengths[i] = anEdge->length();
   }
-  aCoincides = aNewSetOfCoincides;
-
-  // If we still have more than two coincides remove auxilary entities from set of coincides.
-  if(aCoincides.size() > 2) {
-    aNewSetOfCoincides.clear();
-    for(std::set<FeaturePtr>::iterator
-        anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
-      if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
-        aNewSetOfCoincides.insert(*anIt);
-      }
-    }
-    aCoincides = aNewSetOfCoincides;
-  }
-
-  return aCoincides;
+  return (aLengths[0] < aLengths[1] ? aLengths[0] : aLengths[1]) / 6.0;
 }
 
 std::set<FeaturePtr> findFeaturesToRemove(const FeaturePtr theFeature,
@@ -787,7 +597,8 @@ std::set<FeaturePtr> findFeaturesToRemove(const FeaturePtr theFeature,
       continue;
     }
     if(aFeature->getKind() == SketchPlugin_ConstraintLength::ID()
-        || aFeature->getKind() == SketchPlugin_ConstraintEqual::ID()) {
+        || aFeature->getKind() == SketchPlugin_ConstraintEqual::ID()
+        || aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) {
       aFeaturesToBeRemoved.insert(aFeature);
     } else {
       std::list<AttributePtr> anAttrs =