Salome HOME
Fix for some problems in the arc inversion and added a unit test for it
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintFillet.cpp
index 70aa19310f3f6d733416e98cd5edc54ad1d37180..4015ca52a1ba1f03e65402c4ac48c281ae225508 100644 (file)
@@ -13,7 +13,6 @@
 #include <GeomAPI_XY.h>
 #include <GeomDataAPI_Point2D.h>
 #include <ModelAPI_AttributeDouble.h>
-#include <ModelAPI_AttributeRefList.h>
 #include <ModelAPI_AttributeRefAttrList.h>
 #include <ModelAPI_Data.h>
 #include <ModelAPI_Events.h>
@@ -62,14 +61,16 @@ static std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincide
 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
 : myListOfPointsChangedInCode(false),
   myRadiusChangedByUser(false),
-  myRadiusChangedInCode(true)
+  myRadiusChangedInCode(false),
+  myRadiusInitialized(false)
 {
 }
 
 void SketchPlugin_ConstraintFillet::initAttributes()
 {
   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
-  data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(),
+                       ModelAPI_AttributeRefAttrList::typeId());
 }
 
 void SketchPlugin_ConstraintFillet::execute()
@@ -88,19 +89,18 @@ void SketchPlugin_ConstraintFillet::execute()
   double aFilletRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
     aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
 
-  // Check the fillet result edges is not initialized yet.
-  bool anIsNeedNewObjects = myResultEdges.size() == 0;
-
   // Wait all constraints being created, then send update events
   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
   if (isUpdateFlushed)
     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
 
-  std::list<FeaturePtr>::iterator aResultEdgesIt = myResultEdges.begin();
-  for(int anIndex = 0; anIndex < aPointsRefList->size(); ++anIndex) {
+  for(std::set<AttributePtr>::iterator aPointsIter = myNewPoints.begin();
+      aPointsIter != myNewPoints.end();
+      ++aPointsIter) {
+    AttributePtr aPointAttr = *aPointsIter;
     std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2d =
-      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointsRefList->attribute(anIndex));
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttr);
     if(!aFilletPoint2d.get()) {
       setError("Error: One of the selected points is empty.");
       return;
@@ -108,17 +108,24 @@ void SketchPlugin_ConstraintFillet::execute()
     std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2d->pnt();
 
     // Obtain base lines for fillet.
+    bool anIsNeedNewObjects = true;
+    FilletFeatures aFilletFeatures;
+    std::map<AttributePtr, FilletFeatures>::iterator aPrevPointsIter =
+      myPointFeaturesMap.find(aPointAttr);
+    if(aPrevPointsIter != myPointFeaturesMap.end()) {
+      anIsNeedNewObjects = false;
+      aFilletFeatures = aPrevPointsIter->second;
+    }
     FeaturePtr aBaseEdgeA, aBaseEdgeB;
-    if(myBaseEdges.size() > (unsigned int)(anIndex * 2)) {
-      std::list<FeaturePtr>::iterator anIter = myBaseEdges.begin();
-      std::advance(anIter, anIndex * 2);
-      aBaseEdgeA = *anIter++;
-      aBaseEdgeB = *anIter;
+    if(!anIsNeedNewObjects) {
+      aBaseEdgeA = aFilletFeatures.baseEdgesState.front().first;
+      aBaseEdgeB = aFilletFeatures.baseEdgesState.back().first;
     } else {
       // Obtain constraint coincidence for the fillet point.
       FeaturePtr aConstraintCoincidence;
       const std::set<AttributePtr>& aRefsList = aFilletPoint2d->owner()->data()->refsToMe();
-      for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
+      for(std::set<AttributePtr>::const_iterator
+          anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
         std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
         FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
         if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
@@ -158,6 +165,15 @@ void SketchPlugin_ConstraintFillet::execute()
       std::set<FeaturePtr>::iterator aLinesIt = aCoincides.begin();
       aBaseEdgeA = *aLinesIt++;
       aBaseEdgeB = *aLinesIt;
+
+      std::pair<FeaturePtr, bool> aBasePairA =
+        std::make_pair(aBaseEdgeA,
+                       aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
+      std::pair<FeaturePtr, bool> aBasePairB =
+        std::make_pair(aBaseEdgeB,
+                       aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
+      aFilletFeatures.baseEdgesState.push_back(aBasePairA);
+      aFilletFeatures.baseEdgesState.push_back(aBasePairB);
     }
 
     if(!aBaseEdgeA.get() || !aBaseEdgeB.get()) {
@@ -167,25 +183,34 @@ void SketchPlugin_ConstraintFillet::execute()
 
     // Create new edges and arc if needed.
     FeaturePtr aResultEdgeA, aResultEdgeB, aResultArc;
-    if(anIsNeedNewObjects) {
+    if(!anIsNeedNewObjects) {
+      // Obtain features from the list.
+      std::list<FeaturePtr>::iterator aResultEdgesIt = aFilletFeatures.resultEdges.begin();
+      aResultEdgeA = *aResultEdgesIt++;
+      aResultEdgeB = *aResultEdgesIt++;
+      aResultArc = *aResultEdgesIt;
+    } else {
       // Copy edges and create arc.
       aResultEdgeA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeA, sketch());
+      aResultEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
       aResultEdgeB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeB, sketch());
+      aResultEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
       aResultArc = sketch()->addFeature(SketchPlugin_Arc::ID());
-    } else {
-      // Obtain features from the list.
-      aResultEdgeA = *aResultEdgesIt++;
-      aResultEdgeB = *aResultEdgesIt++;
-      aResultArc = *aResultEdgesIt++;
+
+      aFilletFeatures.resultEdges.push_back(aResultEdgeA);
+      aFilletFeatures.resultEdges.push_back(aResultEdgeB);
+      aFilletFeatures.resultEdges.push_back(aResultArc);
     }
 
     // Calculate arc attributes
     static const int aNbFeatures = 2;
     FeaturePtr aBaseFeatures[aNbFeatures] = {aBaseEdgeA, aBaseEdgeB};
     FeaturePtr aResultFeatures[aNbFeatures] = {aResultEdgeA, aResultEdgeB};
-    std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
+    // tangent directions of the features in coincident point
+    std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures];
     bool isStart[aNbFeatures]; // indicates which point the features share
-    std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair -  to second
+    // first pair of points relate to first feature, second pair -  to second
+    std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2];
     std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features
     for (int i = 0; i < aNbFeatures; i++) {
       std::string aStartAttr, aEndAttr;
@@ -196,7 +221,6 @@ void SketchPlugin_ConstraintFillet::execute()
         aStartAttr = SketchPlugin_Arc::START_ID();
         aEndAttr = SketchPlugin_Arc::END_ID();
       } else { // wrong argument
-        myResultEdges.clear();
         setError("Error: One of the points has wrong coincide feature");
         return;
       }
@@ -222,7 +246,8 @@ void SketchPlugin_ConstraintFillet::execute()
         if (!isStart[i])
           aDir = aDir->multiplied(-1.0);
       } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
-        std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint =
+          std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
           aResultFeatures[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
         aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy();
         aDir = aDir->decreased(aCenterPoint->xy());
@@ -231,7 +256,8 @@ void SketchPlugin_ConstraintFillet::execute()
         double y = aDir->y();
         aDir->setX(-y);
         aDir->setY(x);
-        if (isStart[i] == std::dynamic_pointer_cast<SketchPlugin_Arc>(aBaseFeatures[i])->isReversed())
+        if (isStart[i] ==
+            std::dynamic_pointer_cast<SketchPlugin_Arc>(aBaseFeatures[i])->isReversed())
           aDir = aDir->multiplied(-1.0);
       }
       aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
@@ -240,26 +266,31 @@ void SketchPlugin_ConstraintFillet::execute()
     // By default, the start point of fillet arc is connected to FeatureA,
     // and the end point - to FeatureB. But when the angle between TangentDirA and
     // TangentDirB greater 180 degree, the sequaence of features need to be reversed.
-    double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis
+    double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A),
+    // where A and B - angles between corresponding tanget direction and the X axis
     bool isReversed = cosBA > 0.0;
 
     // Calculate fillet arc parameters
     std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
-    calculateFilletCenter(aBaseEdgeA, aBaseEdgeB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
+    calculateFilletCenter(aBaseEdgeA, aBaseEdgeB, 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>(
-      aResultEdgeA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(aTangentPntA->x(), aTangentPntA->y());
+      aResultEdgeA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->
+      setValue(aTangentPntA->x(), aTangentPntA->y());
     aResultEdgeA->execute();
     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-      aResultEdgeB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue(aTangentPntB->x(), aTangentPntB->y());
+      aResultEdgeB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->
+      setValue(aTangentPntB->x(), aTangentPntB->y());
     aResultEdgeB->execute();
-    // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated"
+    // 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
-    //aResultArc->data()->blockSendAttributeUpdated(true);
+    aResultArc->data()->blockSendAttributeUpdated(true);
     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
       aResultArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCenter->x(), aCenter->y());
     if(isReversed) {
@@ -267,7 +298,8 @@ void SketchPlugin_ConstraintFillet::execute()
       aTangentPntA = aTangentPntB;
       aTangentPntB = aTmp;
     }
-    std::shared_ptr<GeomDataAPI_Point2D> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+    std::shared_ptr<GeomDataAPI_Point2D> aStartPoint =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
       aResultArc->attribute(SketchPlugin_Arc::START_ID()));
     std::shared_ptr<GeomDataAPI_Point2D> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
       aResultArc->attribute(SketchPlugin_Arc::END_ID()));
@@ -278,19 +310,10 @@ void SketchPlugin_ConstraintFillet::execute()
     }
     aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y());
     aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y());
-    //aResultArc->data()->blockSendAttributeUpdated(false);
+    aResultArc->data()->blockSendAttributeUpdated(false);
     aResultArc->execute();
 
     if(anIsNeedNewObjects) {
-      // attach new arc to the list
-      myResultEdges.push_back(aResultEdgeA);
-      myResultEdges.push_back(aResultEdgeB);
-      myResultEdges.push_back(aResultArc);
-
-      myProducedFeatures.push_back(aResultEdgeA);
-      myProducedFeatures.push_back(aResultEdgeB);
-      myProducedFeatures.push_back(aResultArc);
-
       // Create list of additional constraints:
       // 1. Coincidence of boundary points of features (copied lines/arcs) and fillet arc
       // 1.1. coincidence
@@ -303,9 +326,10 @@ void SketchPlugin_ConstraintFillet::execute()
       int aFeatInd = isReversed ? 1 : 0;
       int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1);
       aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
-      recalculateAttributes(aResultArc, SketchPlugin_Arc::START_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
+      recalculateAttributes(aResultArc, SketchPlugin_Arc::START_ID(),
+                            aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
       aConstraint->execute();
-      myProducedFeatures.push_back(aConstraint);
+      aFilletFeatures.resultConstraints.push_back(aConstraint);
       ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
       // 1.2. coincidence
       aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
@@ -317,9 +341,10 @@ void SketchPlugin_ConstraintFillet::execute()
       aFeatInd = isReversed ? 0 : 1;
       anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1);
       aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
-      recalculateAttributes(aResultArc, SketchPlugin_Arc::END_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
+      recalculateAttributes(aResultArc, SketchPlugin_Arc::END_ID(),
+                            aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
       aConstraint->execute();
-      myProducedFeatures.push_back(aConstraint);
+      aFilletFeatures.resultConstraints.push_back(aConstraint);
       ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
       // 2. Fillet arc radius
       //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
@@ -343,9 +368,10 @@ void SketchPlugin_ConstraintFillet::execute()
         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
         bool isArc = aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID();
-        aRefAttr->setObject(isArc ? aResultFeatures[i]->lastResult() : aResultFeatures[i]->firstResult());
+        aRefAttr->setObject(isArc ? aResultFeatures[i]->lastResult() :
+                                    aResultFeatures[i]->firstResult());
         aConstraint->execute();
-        myProducedFeatures.push_back(aConstraint);
+        aFilletFeatures.resultConstraints.push_back(aConstraint);
         ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
       }
       // 4. Coincidence of free boundaries of base and copied features
@@ -358,7 +384,7 @@ void SketchPlugin_ConstraintFillet::execute()
         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
         aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
-        myProducedFeatures.push_back(aConstraint);
+        aFilletFeatures.resultConstraints.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.
@@ -373,7 +399,7 @@ void SketchPlugin_ConstraintFillet::execute()
             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
         aRefAttr->setObject(aResultFeatures[i]->lastResult());
         aConstraint->execute();
-        myProducedFeatures.push_back(aConstraint);
+        aFilletFeatures.resultConstraints.push_back(aConstraint);
         ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
       }
       // 5. Tangent points should be placed on the base features
@@ -386,17 +412,18 @@ void SketchPlugin_ConstraintFillet::execute()
         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
         aRefAttr->setObject(aBaseFeatures[i]->lastResult());
-        myProducedFeatures.push_back(aConstraint);
+        aFilletFeatures.resultConstraints.push_back(aConstraint);
       }
       // make base features auxiliary
       aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
       aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
 
-      myBaseEdges.push_back(aBaseEdgeA);
-      myBaseEdges.push_back(aBaseEdgeB);
       // exchange the naming IDs of newly created and old line that become auxiliary
       sketch()->exchangeIDs(aBaseEdgeA, aResultEdgeA);
       sketch()->exchangeIDs(aBaseEdgeB, aResultEdgeB);
+
+      // store point and features in the map.
+      myPointFeaturesMap[aPointAttr] = aFilletFeatures;
     } else {
       // Update radius value
       int aNbSubs = sketch()->numberOfSubs();
@@ -433,33 +460,20 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
       return;
     }
 
-    // Clear the list of fillet entities.
-    myResultEdges.clear();
+    // Clear results.
+    clearResults();
 
-    // Clear the list of base points.
-    myBasePoints.clear();
-
-    // Clear auxiliary flag on initial objects.
-    std::list<FeaturePtr>::const_iterator aFeatureIt;
-    for(aFeatureIt = myBaseEdges.cbegin(); aFeatureIt != myBaseEdges.cend(); ++aFeatureIt) {
-      (*aFeatureIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
-    }
-    myBaseEdges.clear();
-
-    // Remove all produced objects and constraints.
-    DocumentPtr aDoc = sketch()->document();
-    for(aFeatureIt = myProducedFeatures.cbegin(); aFeatureIt != myProducedFeatures.cend(); ++aFeatureIt) {
-      aDoc->removeFeature(*aFeatureIt);
-    }
-    myProducedFeatures.clear();
+    // Clear list of new points.
+    myNewPoints.clear();
 
     // Get list of points for fillets and current radius.
-    AttributeRefAttrListPtr aRefListOfFilletPoints = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
+    AttributeRefAttrListPtr aRefListOfFilletPoints =
+      std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
       data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
-    AttributeDoublePtr aRadiusAttribute = real(SketchPlugin_Constraint::VALUE());
+    AttributeDoublePtr aRadiusAttribute = real(VALUE());
     int aListSize = aRefListOfFilletPoints->size();
     if(aListSize == 0 && !myRadiusChangedByUser) {
-      // If list is empty just reset radius to zero (if it was not changed by user).
+      // If list is empty reset radius to zero (if it was not changed by user).
       myRadiusChangedInCode = true;
       aRadiusAttribute->setValue(0);
       myRadiusChangedInCode = false;
@@ -468,20 +482,22 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
 
     // Iterate over points to get base lines an calculate radius for fillets.
     double aMinimumRadius = 0;
-    std::list<std::pair<ObjectPtr, AttributePtr>> aSelectedPointsList = aRefListOfFilletPoints->list();
+    std::list<std::pair<ObjectPtr, AttributePtr>>
+      aSelectedPointsList = aRefListOfFilletPoints->list();
     std::list<std::pair<ObjectPtr, AttributePtr>>::iterator anIter = aSelectedPointsList.begin();
-    std::set<AttributePtr> aBasePoints;
+    std::set<AttributePtr> aPointsToSkeep;
     for(int anIndex = 0; anIndex < aListSize; anIndex++, anIter++) {
       AttributePtr aFilletPointAttr = (*anIter).second;
       std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2D =
         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFilletPointAttr);
       if(!aFilletPoint2D.get()) {
+        myNewPoints.clear();
         setError("Error: One of the selected points is invalid.");
         return;
       }
 
       // If point or coincident point is already in list remove it from attribute.
-      if(aBasePoints.find(aFilletPointAttr) != aBasePoints.end()) {
+      if(aPointsToSkeep.find(aFilletPointAttr) != aPointsToSkeep.end()) {
         myListOfPointsChangedInCode = true;
         aRefListOfFilletPoints->remove(aFilletPointAttr);
         myListOfPointsChangedInCode = false;
@@ -491,7 +507,8 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
       // Obtain constraint coincidence for the fillet point.
       FeaturePtr aConstraintCoincidence;
       const std::set<AttributePtr>& aRefsList = aFilletPointAttr->owner()->data()->refsToMe();
-      for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
+      for(std::set<AttributePtr>::const_iterator
+          anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
         std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
         FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
         if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
@@ -517,6 +534,7 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
       }
 
       if(!aConstraintCoincidence.get()) {
+        myNewPoints.clear();
         setError("Error: No coincident edges at one of the selected points.");
         return;
       }
@@ -532,44 +550,53 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
                                            SketchPlugin_ConstraintCoincidence::ENTITY_B(),
                                            aCoincides);
 
-      // Remove points from set of coincides. Also get all attributes which is equal to this point to exclude it.
+      // Remove points from set of coincides.
+      // Also get all attributes which is equal to this point to exclude it.
       std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
       std::set<FeaturePtr> aNewSetOfCoincides;
-      for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
+      for(std::set<FeaturePtr>::iterator
+          anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
         std::string aFeatureKind = (*anIt)->getKind();
         if(aFeatureKind == SketchPlugin_Point::ID()) {
           AttributePtr anAttr = (*anIt)->attribute(SketchPlugin_Point::COORD_ID());
           std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
           if(aPoint2D.get() && aFilletPnt2d->isEqual(aPoint2D->pnt())) {
-            aBasePoints.insert(anAttr);
+            aPointsToSkeep.insert(anAttr);
           }
         } else if(aFeatureKind == SketchPlugin_Line::ID()) {
           AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Line::START_ID());
           std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
           if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
-            aBasePoints.insert(anAttrStart);
+            aPointsToSkeep.insert(anAttrStart);
           }
           AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Line::END_ID());
           std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
           if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
-            aBasePoints.insert(anAttrEnd);
+            aPointsToSkeep.insert(anAttrEnd);
           }
           aNewSetOfCoincides.insert(*anIt);
         } else if(aFeatureKind == 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() && aFilletPnt2d->isEqual(aPointCenter2D->pnt())) {
+            aPointsToSkeep.insert(anAttrCenter);
+            continue;
+          }
           AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Arc::START_ID());
           std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
           if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
-            aBasePoints.insert(anAttrStart);
+            aPointsToSkeep.insert(anAttrStart);
           }
           AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Arc::END_ID());
           std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
           if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
-            aBasePoints.insert(anAttrEnd);
+            aPointsToSkeep.insert(anAttrEnd);
           }
           aNewSetOfCoincides.insert(*anIt);
         }
@@ -579,7 +606,8 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
       // 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) {
+        for(std::set<FeaturePtr>::iterator
+            anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
           if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
             aNewSetOfCoincides.insert(*anIt);
           }
@@ -588,13 +616,14 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
       }
 
       if(aCoincides.size() != 2) {
+        myNewPoints.clear();
         setError("Error: One of the selected points does not have two suitable edges for fillet.");
         return;
       }
 
       // Store base point for fillet.
-      aBasePoints.insert(aFilletPointAttr);
-      myBasePoints.push_back(aFilletPointAttr);
+      aPointsToSkeep.insert(aFilletPointAttr);
+      myNewPoints.insert(aFilletPointAttr);
 
       // Get base lines for fillet.
       FeaturePtr anOldFeatureA, anOldFeatureB;
@@ -614,7 +643,8 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
         double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA);
         double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB);
         double aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0;
-        aMinimumRadius = aMinimumRadius == 0 ? aRadius : aRadius < aMinimumRadius ? aRadius : aMinimumRadius;
+        aMinimumRadius = aMinimumRadius == 0 ? aRadius :
+          aRadius < aMinimumRadius ? aRadius : aMinimumRadius;
       }
     }
 
@@ -626,9 +656,12 @@ void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
     }
 
   } else if(theID == SketchPlugin_Constraint::VALUE()) {
-    if(!myRadiusChangedInCode) {
+    if(myRadiusInitialized && !myRadiusChangedInCode) {
       myRadiusChangedByUser = true;
     }
+    if(!myRadiusInitialized) {
+      myRadiusInitialized = true;
+    }
   }
 }
 
@@ -647,6 +680,47 @@ bool SketchPlugin_ConstraintFillet::isMacro() const
   return true;
 }
 
+void SketchPlugin_ConstraintFillet::clearResults()
+{
+  // Clear auxiliary flag on initial objects.
+  for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
+      aPointsIter != myPointFeaturesMap.end();) {
+    const FilletFeatures& aFilletFeatures = aPointsIter->second;
+    std::list<std::pair<FeaturePtr, bool>>::const_iterator aFeatureIt;
+    for(aFeatureIt = aFilletFeatures.baseEdgesState.cbegin();
+        aFeatureIt != aFilletFeatures.baseEdgesState.cend();
+        ++aFeatureIt) {
+      aFeatureIt->first->boolean(
+        SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(aFeatureIt->second);
+    }
+    ++aPointsIter;
+  }
+
+  // And remove all produced features.
+  DocumentPtr aDoc = sketch()->document();
+  for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
+      aPointsIter != myPointFeaturesMap.end();) {
+    // Remove all produced constraints.
+    const FilletFeatures& aFilletFeatures = aPointsIter->second;
+    std::list<FeaturePtr>::const_iterator aFeatureIt;
+    for(aFeatureIt = aFilletFeatures.resultConstraints.cbegin();
+        aFeatureIt != aFilletFeatures.resultConstraints.cend();
+        ++aFeatureIt) {
+      aDoc->removeFeature(*aFeatureIt);
+    }
+
+    // Remove all result edges.
+    for(aFeatureIt = aFilletFeatures.resultEdges.cbegin();
+        aFeatureIt != aFilletFeatures.resultEdges.cend();
+        ++aFeatureIt) {
+      aDoc->removeFeature(*aFeatureIt);
+    }
+
+    // Remove point from map.
+    myPointFeaturesMap.erase(aPointsIter++);
+  }
+};
+
 
 // =========   Auxiliary functions   =================
 void recalculateAttributes(FeaturePtr theNewArc,  const std::string& theNewArcAttribute,
@@ -743,11 +817,15 @@ void possibleFilletCenterArcArc(
       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 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;
+      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);
@@ -802,7 +880,8 @@ void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
 
     // get and filter possible centers
     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
-    possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1], theRadius, 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++) {
@@ -819,7 +898,7 @@ void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
       return;
     }
   } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
-      theFeatureB->getKind() == SketchPlugin_Line::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;
@@ -837,7 +916,8 @@ void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
 
     // get possible centers and filter them
     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
-    possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], anArcRadius, theRadius, 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());
@@ -893,7 +973,8 @@ void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
 
     // get and filter possible centers
     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
-    possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1], anArcRadius[1], theRadius, 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();
@@ -934,7 +1015,9 @@ void getPointOnEdge(const FeaturePtr theFeature,
       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) ) ) );
+    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>(
@@ -993,6 +1076,9 @@ std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
 {
   std::set<FeaturePtr> aCoincides;
 
+  std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt =
+    SketchPlugin_Tools::getCoincidencePoint(theConstraintCoincidence);
+
   SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
                                        SketchPlugin_ConstraintCoincidence::ENTITY_A(),
                                        aCoincides);
@@ -1003,8 +1089,15 @@ std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
   // Remove points from set of coincides.
   std::set<FeaturePtr> aNewSetOfCoincides;
   for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
-    if((*anIt)->getKind() == SketchPlugin_Line::ID() ||
-        (*anIt)->getKind() == SketchPlugin_Arc::ID() ) {
+    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);
     }
   }
@@ -1013,7 +1106,8 @@ std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
   // 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) {
+    for(std::set<FeaturePtr>::iterator
+        anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
       if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
         aNewSetOfCoincides.insert(*anIt);
       }