Salome HOME
bos #29098: Help panel for SHAPER module
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Offset.cpp
index 0183cec9b6740ca898fef1a64dad48ee2bf92e18..27d957a51f76f4d7f1c5a27dae69b8fde2ed10d7 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2020  CEA/DEN, EDF R&D
+// Copyright (C) 2020-2022  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
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_AttributeDoubleArray.h>
 #include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_AttributeIntArray.h>
 #include <ModelAPI_AttributeRefList.h>
+#include <ModelAPI_AttributeString.h>
+#include <ModelAPI_Events.h>
 #include <ModelAPI_ResultConstruction.h>
 #include <ModelAPI_Tools.h>
+#include <ModelAPI_Validator.h>
 
+#include <GeomAlgoAPI_MakeShapeList.h>
+#include <GeomAlgoAPI_MapShapesAndAncestors.h>
 #include <GeomAlgoAPI_Offset.h>
 #include <GeomAlgoAPI_ShapeTools.h>
 #include <GeomAlgoAPI_WireBuilder.h>
+#include <GeomAlgoAPI_Fillet1D.h>
+#include <GeomAlgoAPI_Tools.h>
 
-#include <GeomAPI_Edge.h>
+#include <GeomAPI_BSpline.h>
 #include <GeomAPI_Circ.h>
+#include <GeomAPI_Edge.h>
 #include <GeomAPI_Ellipse.h>
-#include <GeomAPI_BSpline.h>
+#include <GeomAPI_ShapeExplorer.h>
+#include <GeomAPI_Wire.h>
+#include <GeomAPI_WireExplorer.h>
 
 #include <GeomDataAPI_Point2D.h>
 #include <GeomDataAPI_Point2DArray.h>
 
-#include <iostream>
+#include <math.h>
+
+static const double tolerance = 1.e-7;
 
 SketchPlugin_Offset::SketchPlugin_Offset()
-  : SketchPlugin_SketchEntity()
 {
 }
 
-void SketchPlugin_Offset::initDerivedClassAttributes()
+void SketchPlugin_Offset::initAttributes()
 {
   data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
   data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
   data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
+
+  // store original entities
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId());
+  // store offset entities
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
+  // store mapping between original entity and index of the corresponding offset entity
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeIntArray::typeId());
+
+  ModelAPI_Session::get()->validators()->
+      registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_A());
+  ModelAPI_Session::get()->validators()->
+      registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
+  ModelAPI_Session::get()->validators()->
+      registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
+
+  AttributeStringPtr aJointAttr = std::dynamic_pointer_cast<ModelAPI_AttributeString>
+    (data()->addAttribute(JOINT_ID(), ModelAPI_AttributeString::typeId()));
+  if (!aJointAttr->isInitialized())
+    aJointAttr->setValue(JOINT_KEEP_DISTANCE());
 }
 
 void SketchPlugin_Offset::execute()
 {
-  ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
-  myCreatedFeatures.clear();
-
   SketchPlugin_Sketch* aSketch = sketch();
   if (!aSketch) return;
 
+  // 0. Joint type
+  AttributeStringPtr aJointAttr = string(JOINT_ID());
+  std::string aType = JOINT_KEEP_DISTANCE();
+  if (aJointAttr->isInitialized())
+    aType = aJointAttr->value();
+
+  GeomAlgoAPI_OffsetJoint aJoint;
+  if (aType == JOINT_ARCS())
+    aJoint = GeomAlgoAPI_OffsetJoint::Arcs;
+  else if (aType == JOINT_LINES())
+    aJoint = GeomAlgoAPI_OffsetJoint::Lines;
+  else // Default mode
+    aJoint = GeomAlgoAPI_OffsetJoint::KeepDistance;
+
   // 1. Sketch plane
   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
 
@@ -83,7 +125,6 @@ void SketchPlugin_Offset::execute()
   AttributeDoublePtr aValueAttr = real(VALUE_ID());
   if (!aValueAttr->isInitialized()) return;
   double aValue = aValueAttr->value();
-  const double tolerance = 1.e-7;
   if (aValue < tolerance) return;
 
   // 2.a. Reversed?
@@ -105,11 +146,24 @@ void SketchPlugin_Offset::execute()
     }
   }
 
+  // Wait all objects 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);
+
+  // Save the current feature of the document, because new features may appear while executing.
+  // In this case, they will become current. But if the number of copies is updated from outside
+  // of sketch (e.g. by parameter change), the history line should not hold in sketch.
+  keepCurrentFeature();
+
   // 5. Gather wires and make offset for each wire
+  ListOfMakeShape anOffsetAlgos;
+  std::set<FeaturePtr> aProcessedEdgesSet;
   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
     if (aFeature.get()) {
-      if (anEdgesSet.find(aFeature) == anEdgesSet.end())
+      if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
         continue;
 
       // 5.a. End points (if any)
@@ -119,14 +173,16 @@ void SketchPlugin_Offset::execute()
       // 5.b. Find a chain of edges
       std::list<FeaturePtr> aChain;
       aChain.push_back(aFeature);
-      if (aStartPoint && anEndPoint) { // not closed edge
-        bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
-        if (!isClosed)
-          findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
+      bool isClosed = !(aStartPoint && anEndPoint);  // not closed edge
+      if (!isClosed) {
+        isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
+                                  aProcessedEdgesSet, aChain, true);
+        if (!isClosed) {
+          isClosed = findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
+                                    aProcessedEdgesSet, aChain, false);
+        }
       }
-      std::set<FeaturePtr>::iterator aPos = anEdgesSet.find(aFeature);
-      if (aPos != anEdgesSet.end())
-        anEdgesSet.erase(aPos);
+      aProcessedEdgesSet.insert(aFeature);
 
       // 5.c. Make wire
       ListOfShape aTopoChain;
@@ -138,48 +194,86 @@ void SketchPlugin_Offset::execute()
           aTopoChain.push_back(aTopoEdge);
         }
       }
-      GeomShapePtr anEdgeOrWire = GeomAlgoAPI_WireBuilder::wire(aTopoChain);
+      std::shared_ptr<GeomAlgoAPI_WireBuilder> aWireBuilder(
+          new GeomAlgoAPI_WireBuilder(aTopoChain, !isClosed));
+
+      GeomShapePtr aWireShape = aWireBuilder->shape();
+      GeomWirePtr aWire (new GeomAPI_Wire (aWireShape));
+
+      // Fix for a problem of offset side change with selection change.
+      // Wire direction is defined by the first selected edge of this wire.
+      double aSign = 1.;
+      if (!aWire->isClosed()) {
+        ListOfShape aModified;
+        // First selected edge of current chain
+        GeomShapePtr aFirstSel = aFeature->lastResult()->shape();
+        aWireBuilder->modified(aFirstSel, aModified);
+        GeomShapePtr aModFS = aModified.front();
+        if (aModFS->orientation() != aFirstSel->orientation())
+          aSign = -1.;
+      }
 
-      // 5.d. Make offset for each wire
-      std::shared_ptr<GeomAPI_Shape> anOffsetShape =
-        GeomAlgoAPI_Offset::OffsetInPlane(aPlane, anEdgeOrWire, aValue);
+      // 5.d. Make offset for the wire
+      std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape
+        (new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign, aJoint));
 
-      // 5.e. Store offset results.
-      //      Create sketch feature for each edge of anOffsetShape, and also store
-      //      created features in myCreatedFeatures to remove them on next execute()
-      addToSketch(anOffsetShape);
+      if (anOffsetShape->isDone()) {
+        if (aJoint == GeomAlgoAPI_OffsetJoint::Arcs) {
+          // For Arcs joint make fillet at all straight edges intersections
+          // of the wire, resulting from GeomAlgoAPI_Offset algorithm
+          makeFillet(fabs(aValue), aWireBuilder, anOffsetShape, anOffsetAlgos);
+        }
+        else {
+          std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
+          aMakeList->appendAlgo(aWireBuilder);
+          aMakeList->appendAlgo(anOffsetShape);
+          anOffsetAlgos.push_back(aMakeList);
+        }
+      }
+      else {
+        setError("Offset algorithm failed");
+      }
     }
   }
+
+  // 6. Store offset results.
+  //    Create sketch feature for each edge of anOffsetShape, and also store
+  //    created features in CREATED_ID() to remove them on next execute()
+  addToSketch(anOffsetAlgos);
+
+  restoreCurrentFeature();
+
+  // send events to update the sub-features by the solver
+  if (isUpdateFlushed)
+    Events_Loop::loop()->setFlushed(anUpdateEvent, true);
 }
 
 bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
                                           const FeaturePtr& theEdge,
                                           const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
                                           std::set<FeaturePtr>& theEdgesSet,
-                                          std::list<FeaturePtr>& theChain)
+                                          std::set<FeaturePtr>& theProcessedEdgesSet,
+                                          std::list<FeaturePtr>& theChain,
+                                          const bool isPrepend)
 {
   // 1. Find a single edge, coincident to theEndPoint by one of its ends
   if (!theEndPoint) return false;
 
-  std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
-
   FeaturePtr aNextEdgeFeature;
   int nbFound = 0;
 
-  std::set<AttributePoint2DPtr> aCoincPoints;
-  std::map<AttributePoint2DArrayPtr, int> aCoincPointsInArray;
-  SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint, aCoincPoints, aCoincPointsInArray);
-
-  // store all found attributes to a single array
-  std::set<AttributePtr> anAllCoincPoints;
-  anAllCoincPoints.insert(aCoincPoints.begin(), aCoincPoints.end());
-  for (auto it = aCoincPointsInArray.begin(); it != aCoincPointsInArray.end(); ++it)
-    anAllCoincPoints.insert(it->first);
+  std::set<AttributePoint2DPtr> aCoincPoints =
+      SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
 
-  std::set<AttributePtr>::iterator aPointsIt = anAllCoincPoints.begin();
-  for (; aPointsIt != anAllCoincPoints.end(); aPointsIt++) {
-    AttributePtr aP = (*aPointsIt);
+  std::set<AttributePoint2DPtr>::iterator aPointsIt = aCoincPoints.begin();
+  for (; aPointsIt != aCoincPoints.end(); aPointsIt++) {
+    AttributePoint2DPtr aP = (*aPointsIt);
     FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
+    bool isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
+
+    // Condition 0: not auxiliary
+    if (!isInSet && aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
+      continue;
 
     // Condition 1: not a point feature
     if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
@@ -187,11 +281,7 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
       if (aCoincFeature != theEdge) {
         // Condition 3: it is in the set of interest.
         //              Empty set means all sketch edges.
-        bool isInSet = true;
-        if (theEdgesSet.size()) {
-          isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
-        }
-        if (isInSet) {
+        if (isInSet || theEdgesSet.empty()) {
           // Condition 4: consider only features with two end points
           std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
           SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
@@ -226,13 +316,11 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
     return true;
 
   // 3. Add the found edge to the chain
-  theChain.push_back(aNextEdgeFeature);
-  // remove from the set, if the set is used
-  if (theEdgesSet.size()) {
-    std::set<FeaturePtr>::iterator aPos = theEdgesSet.find(aNextEdgeFeature);
-    if (aPos != theEdgesSet.end())
-      theEdgesSet.erase(aPos);
-  }
+  if (isPrepend)
+    theChain.push_front(aNextEdgeFeature);
+  else
+    theChain.push_back(aNextEdgeFeature);
+  theProcessedEdgesSet.insert(aNextEdgeFeature);
 
   // 4. Which end of aNextEdgeFeature we need to proceed
   std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
@@ -243,135 +331,327 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
   }
 
   // 5. Continue gathering the chain (recursive)
-  return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
+  return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet,
+                         theProcessedEdgesSet, theChain, isPrepend);
 }
 
-void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
+static void setRefListValue(AttributeRefListPtr theList, int theListSize,
+                            ObjectPtr theValue, int theIndex)
 {
-  //GeomAPI_ShapeExplorer::GeomAPI_ShapeExplorer
-  ListOfShape aResEdges = GeomAlgoAPI_ShapeTools::getLowLevelSubShapes(anOffsetShape);
-  std::list<GeomShapePtr>::const_iterator aResEdgesIt = aResEdges.begin();
-  for (; aResEdgesIt != aResEdges.end(); aResEdgesIt++) {
-    GeomShapePtr aResShape = (*aResEdgesIt);
-    if (aResShape->shapeType() == GeomAPI_Shape::EDGE) {
-      // Add new feature
-      FeaturePtr aResFeature;
-      std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
-
-      std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
-      std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
-      std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
-      //if (aFP3d.get() && aLP3d.get()) {
-      if (aFP3d && aLP3d) {
-        aFP = sketch()->to2D(aFP3d);
-        aLP = sketch()->to2D(aLP3d);
-      }
+  if (theIndex < theListSize) {
+    ObjectPtr aCur = theList->object(theIndex);
+    if (aCur != theValue)
+      theList->substitute(aCur, theValue);
+  }
+  else
+    theList->append(theValue);
+}
 
-      if (aResEdge->isLine()) {
-        aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
+// Reorder shapes according to the wire's order
+static void reorderShapes(ListOfShape& theShapes, GeomShapePtr theWire)
+{
+  std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aShapes;
+  aShapes.insert(theShapes.begin(), theShapes.end());
+  theShapes.clear();
+
+  GeomWirePtr aWire(new GeomAPI_Wire(theWire));
+  GeomAPI_WireExplorer anExp(aWire);
+  for (; anExp.more(); anExp.next()) {
+    GeomShapePtr aCurEdge = anExp.current();
+    auto aFound = aShapes.find(aCurEdge);
+    if (aFound != aShapes.end()) {
+      theShapes.push_back(aCurEdge);
+      aShapes.erase(aFound);
+    }
+  }
+}
 
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-          (aResFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-          (aResFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
-      }
-      else if (aResEdge->isArc()) {
-        std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
-        std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
-        std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
-
-        aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
-
-        bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-          (aResFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-          (aResFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-          (aResFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
-        aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
-      }
-      else if (aResEdge->isCircle()) {
-        std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
-        std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
-        std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
-
-        aResFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-          (aResFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
-        aResFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
-      }
-      else if (aResEdge->isEllipse()) {
-        std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
-
-        GeomPointPtr aCP3d = anEllipseEdge->center();
-        GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
-
-        GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
-        GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
-
-        if (aFP3d && aLP3d) {
-          // Elliptic arc
-          aResFeature = sketch()->addFeature(SketchPlugin_EllipticArc::ID());
-
-          bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
-          std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-            (aResFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
-          std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-            (aResFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
-          std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-            (aResFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
-          std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-            (aResFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
-          aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
-        }
-        else {
-          // Ellipse
-          aResFeature = sketch()->addFeature(SketchPlugin_Ellipse::ID());
-
-          std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-            (aResFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
-          std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-            (aResFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
-          aResFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipseEdge->minorRadius());
-        }
-      }
-      else if (aResEdge->isBSpline()) {
-        mkBSpline(aResFeature, aResEdge);
+static void removeLastFromIndex(AttributeRefListPtr theList, int theListSize, int& theLastIndex)
+{
+  if (theLastIndex < theListSize) {
+    std::set<int> anIndicesToRemove;
+    for (; theLastIndex < theListSize; ++theLastIndex)
+      anIndicesToRemove.insert(theLastIndex);
+    theList->remove(anIndicesToRemove);
+  }
+}
+
+void SketchPlugin_Offset::addToSketch(const ListOfMakeShape& theOffsetAlgos)
+{
+  AttributeRefListPtr aSelectedRefList = reflist(EDGES_ID());
+  AttributeRefListPtr aBaseRefList = reflist(ENTITY_A());
+  AttributeRefListPtr anOffsetRefList = reflist(ENTITY_B());
+  AttributeIntArrayPtr anOffsetToBaseMap = intArray(ENTITY_C());
+
+  // compare the list of selected edges and the previously stored,
+  // and store maping between them
+  std::map<ObjectPtr, std::list<ObjectPtr> > aMapExistent;
+  std::list<ObjectPtr> anObjectsToRemove;
+  std::list<ObjectPtr> aSelectedList = aSelectedRefList->list();
+  for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
+       aSIt != aSelectedList.end(); ++aSIt) {
+    aMapExistent[*aSIt] = std::list<ObjectPtr>();
+  }
+  for (int anIndex = 0, aSize = anOffsetRefList->size(); anIndex < aSize; ++anIndex) {
+    ObjectPtr aCurrent = anOffsetRefList->object(anIndex);
+    int aBaseIndex = anOffsetToBaseMap->value(anIndex);
+    if (aBaseIndex >= 0) {
+      ObjectPtr aBaseObj = aBaseRefList->object(aBaseIndex);
+      std::map<ObjectPtr, std::list<ObjectPtr> >::iterator aFound = aMapExistent.find(aBaseObj);
+      if (aFound != aMapExistent.end())
+        aFound->second.push_back(aCurrent);
+      else
+        anObjectsToRemove.push_back(aCurrent);
+    }
+    else
+      anObjectsToRemove.push_back(aCurrent);
+  }
+
+  // update lists of base shapes and of offset shapes
+  int aBaseListSize = aBaseRefList->size();
+  int anOffsetListSize = anOffsetRefList->size();
+  int aBaseListIndex = 0, anOffsetListIndex = 0;
+  std::list<int> anOffsetBaseBackRefs;
+  std::set<GeomShapePtr, GeomAPI_Shape::ComparatorWithOri> aProcessedOffsets;
+  for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
+       aSIt != aSelectedList.end(); ++aSIt) {
+    // find an offseted edge
+    FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aSIt);
+    GeomShapePtr aBaseShape = aBaseFeature->lastResult()->shape();
+    ListOfShape aNewShapes;
+    for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
+         anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
+      (*anAlgoIt)->generated(aBaseShape, aNewShapes);
+      if (!aNewShapes.empty()) {
+        reorderShapes(aNewShapes, (*anAlgoIt)->shape());
+        break;
       }
-      else {
+    }
+
+    // store base feature
+    setRefListValue(aBaseRefList, aBaseListSize, *aSIt, aBaseListIndex);
+
+    // create or update an offseted feature
+    const std::list<ObjectPtr>& anImages = aMapExistent[*aSIt];
+    std::list<ObjectPtr>::const_iterator anImgIt = anImages.begin();
+    for (ListOfShape::iterator aNewIt = aNewShapes.begin(); aNewIt != aNewShapes.end(); ++aNewIt) {
+      FeaturePtr aNewFeature;
+      if (anImgIt != anImages.end())
+        aNewFeature = ModelAPI_Feature::feature(*anImgIt++);
+      updateExistentOrCreateNew(*aNewIt, aNewFeature, anObjectsToRemove);
+      aProcessedOffsets.insert(*aNewIt);
+
+      // store an offseted feature
+      setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
+
+      anOffsetBaseBackRefs.push_back(aBaseListIndex);
+      ++anOffsetListIndex;
+    }
+    ++aBaseListIndex;
+    anObjectsToRemove.insert(anObjectsToRemove.end(), anImgIt, anImages.end());
+  }
+  // create arcs generated from vertices
+  for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
+       anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
+    GeomShapePtr aCurWire = (*anAlgoIt)->shape();
+    GeomAPI_ShapeExplorer anExp(aCurWire, GeomAPI_Shape::EDGE);
+    for (; anExp.more(); anExp.next()) {
+      GeomShapePtr aCurEdge = anExp.current();
+      if (aProcessedOffsets.find(aCurEdge) == aProcessedOffsets.end()) {
+        FeaturePtr aNewFeature;
+        updateExistentOrCreateNew(aCurEdge, aNewFeature, anObjectsToRemove);
+        aProcessedOffsets.insert(aCurEdge);
+
+        // store an offseted feature
+        setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
+
+        anOffsetBaseBackRefs.push_back(-1);
+        ++anOffsetListIndex;
       }
+    }
+  }
+
+  removeLastFromIndex(aBaseRefList, aBaseListSize, aBaseListIndex);
+  removeLastFromIndex(anOffsetRefList, anOffsetListSize, anOffsetListIndex);
+
+  anOffsetToBaseMap->setSize((int)anOffsetBaseBackRefs.size(), false);
+  int anIndex = 0;
+  for (std::list<int>::iterator anIt = anOffsetBaseBackRefs.begin();
+       anIt != anOffsetBaseBackRefs.end(); ++anIt) {
+    anOffsetToBaseMap->setValue(anIndex++, *anIt, false);
+  }
 
-      if (aResFeature.get()) {
-        myCreatedFeatures.insert(aResFeature);
+  // remove unused objects
+  std::set<FeaturePtr> aSet;
+  for (std::list<ObjectPtr>::iterator anIt = anObjectsToRemove.begin();
+       anIt != anObjectsToRemove.end(); ++anIt) {
+    FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
+    if (aFeature)
+      aSet.insert(aFeature);
+  }
+  ModelAPI_Tools::removeFeaturesAndReferences(aSet);
+}
 
-        aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
-          (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
-        aResFeature->execute();
+static void findOrCreateFeatureByKind(SketchPlugin_Sketch* theSketch,
+                                      const std::string& theFeatureKind,
+                                      FeaturePtr& theFeature,
+                                      std::list<ObjectPtr>& thePoolOfFeatures)
+{
+  if (theFeature) {
+    // check the feature type is the same as required
+    if (theFeature->getKind() != theFeatureKind) {
+      // return feature to the pool and try to find the most appropriate
+      thePoolOfFeatures.push_back(theFeature);
+      theFeature = FeaturePtr();
+    }
+  }
+  if (!theFeature) {
+    // try to find appropriate feature in the pool
+    for (std::list<ObjectPtr>::iterator it = thePoolOfFeatures.begin();
+         it != thePoolOfFeatures.end(); ++it) {
+      FeaturePtr aCurFeature = ModelAPI_Feature::feature(*it);
+      if (aCurFeature->getKind() == theFeatureKind) {
+        theFeature = aCurFeature;
+        thePoolOfFeatures.erase(it);
+        break;
       }
     }
+    // feature not found, create new
+    if (!theFeature)
+      theFeature = theSketch->addFeature(theFeatureKind);
   }
 }
 
-void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
-                                     const GeomEdgePtr& theEdge)
+void SketchPlugin_Offset::updateExistentOrCreateNew(const GeomShapePtr& theShape,
+                                                    FeaturePtr& theFeature,
+                                                    std::list<ObjectPtr>& thePoolOfFeatures)
 {
-  if (!theEdge->isBSpline())
+  if (theShape->shapeType() != GeomAPI_Shape::EDGE)
     return;
 
+  std::shared_ptr<GeomAPI_Edge> aResEdge(new GeomAPI_Edge(theShape));
+
+  std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
+  std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
+  std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
+  if (aFP3d && aLP3d) {
+    aFP = sketch()->to2D(aFP3d);
+    aLP = sketch()->to2D(aLP3d);
+  }
+
+  if (aResEdge->isLine()) {
+    findOrCreateFeatureByKind(sketch(), SketchPlugin_Line::ID(), theFeature, thePoolOfFeatures);
+
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+      (theFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+      (theFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
+  }
+  else if (aResEdge->isArc()) {
+    std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
+    std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
+    std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
+
+    findOrCreateFeatureByKind(sketch(), SketchPlugin_Arc::ID(), theFeature, thePoolOfFeatures);
+
+    GeomDirPtr aCircNormal = aCircEdge->normal();
+    GeomDirPtr aSketchNormal = sketch()->coordinatePlane()->normal();
+    if (aSketchNormal->dot(aCircNormal) < -tolerance)
+      std::swap(aFP, aLP);
+
+    bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+      (theFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+      (theFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+      (theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
+    theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
+  }
+  else if (aResEdge->isCircle()) {
+    std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
+    std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
+    std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
+
+    findOrCreateFeatureByKind(sketch(), SketchPlugin_Circle::ID(), theFeature, thePoolOfFeatures);
+
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+      (theFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
+    theFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
+  }
+  else if (aResEdge->isEllipse()) {
+    std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
+
+    GeomPointPtr aCP3d = anEllipseEdge->center();
+    GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
+
+    GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
+    GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
+
+    if (aFP3d && aLP3d) {
+      // Elliptic arc
+      findOrCreateFeatureByKind(sketch(), SketchPlugin_EllipticArc::ID(),
+                                theFeature, thePoolOfFeatures);
+
+      bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+        (theFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+        (theFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+        (theFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+        (theFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
+      theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
+    }
+    else {
+      // Ellipse
+      findOrCreateFeatureByKind(sketch(), SketchPlugin_Ellipse::ID(),
+                                theFeature, thePoolOfFeatures);
+
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+        (theFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+        (theFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
+      theFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(
+        anEllipseEdge->minorRadius());
+    }
+  }
+  else {
+    // convert to b-spline
+    mkBSpline(theFeature, aResEdge, thePoolOfFeatures);
+  }
+
+  if (theFeature.get()) {
+    theFeature->boolean(SketchPlugin_SketchEntity::COPY_ID())->setValue(true);
+    theFeature->execute();
+
+    static Events_ID aRedisplayEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
+    ModelAPI_EventCreator::get()->sendUpdated(theFeature, aRedisplayEvent);
+    const std::list<ResultPtr>& aResults = theFeature->results();
+    for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
+         anIt != aResults.end(); ++anIt)
+      ModelAPI_EventCreator::get()->sendUpdated(*anIt, aRedisplayEvent);
+  }
+}
+
+void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
+                                     const GeomEdgePtr& theEdge,
+                                     std::list<ObjectPtr>& thePoolOfFeatures)
+{
   GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
-  GeomAPI_BSpline aBSpline (aCurve);
+  // Forced conversion to b-spline, if aCurve is not b-spline
+  GeomBSplinePtr aBSpline = GeomAPI_BSpline::convertToBSpline(aCurve);
 
-  if (aBSpline.isPeriodic())
-    theResult = sketch()->addFeature(SketchPlugin_BSplinePeriodic::ID());
-  else
-    theResult = sketch()->addFeature(SketchPlugin_BSpline::ID());
+  const std::string& aBSplineKind = aBSpline->isPeriodic() ? SketchPlugin_BSplinePeriodic::ID()
+                                                           : SketchPlugin_BSpline::ID();
+  findOrCreateFeatureByKind(sketch(), aBSplineKind, theResult, thePoolOfFeatures);
 
-  theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
+  theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline->degree());
 
   AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
     (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
-  std::list<GeomPointPtr> aPoles = aBSpline.poles();
+  std::list<GeomPointPtr> aPoles = aBSpline->poles();
   aPolesAttr->setSize((int)aPoles.size());
   std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
   for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
@@ -381,7 +661,7 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
 
   AttributeDoubleArrayPtr aWeightsAttr =
       theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
-  std::list<double> aWeights = aBSpline.weights();
+  std::list<double> aWeights = aBSpline->weights();
   if (aWeights.empty()) { // rational B-spline
     int aSize = (int)aPoles.size();
     aWeightsAttr->setSize(aSize);
@@ -390,14 +670,14 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
   }
   else { // non-rational B-spline
     aWeightsAttr->setSize((int)aWeights.size());
-    std::list<double>::iterator anIt = aWeights.begin();
-    for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
-      aWeightsAttr->setValue(anIndex, *anIt);
+    std::list<double>::iterator aWIt = aWeights.begin();
+    for (int anIndex = 0; aWIt != aWeights.end(); ++aWIt, ++anIndex)
+      aWeightsAttr->setValue(anIndex, *aWIt);
   }
 
   AttributeDoubleArrayPtr aKnotsAttr =
       theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
-  std::list<double> aKnots = aBSpline.knots();
+  std::list<double> aKnots = aBSpline->knots();
   int aSize = (int)aKnots.size();
   aKnotsAttr->setSize(aSize);
   std::list<double>::iterator aKIt = aKnots.begin();
@@ -406,7 +686,7 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
 
   AttributeIntArrayPtr aMultsAttr =
       theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
-  std::list<int> aMultiplicities = aBSpline.mults();
+  std::list<int> aMultiplicities = aBSpline->mults();
   aSize = (int)aMultiplicities.size();
   aMultsAttr->setSize(aSize);
   std::list<int>::iterator aMIt = aMultiplicities.begin();
@@ -416,8 +696,27 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
 
 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
 {
-  ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
-  myCreatedFeatures.clear();
+  if (theID == EDGES_ID()) {
+    AttributeRefListPtr aSelected = reflist(EDGES_ID());
+    if (aSelected->size() == 0) {
+      // Clear list of objects
+      AttributeRefListPtr anOffsetAttr = reflist(SketchPlugin_Constraint::ENTITY_B());
+      std::list<ObjectPtr> anOffsetList = anOffsetAttr->list();
+      std::set<FeaturePtr> aFeaturesToBeRemoved;
+      for (std::list<ObjectPtr>::iterator anIt = anOffsetList.begin();
+           anIt != anOffsetList.end(); ++anIt) {
+        FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
+        if (aFeature)
+          aFeaturesToBeRemoved.insert(aFeature);
+      }
+
+      reflist(SketchPlugin_Constraint::ENTITY_A())->clear();
+      anOffsetAttr->clear();
+      intArray(SketchPlugin_Constraint::ENTITY_C())->setSize(0);
+
+      ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved);
+    }
+  }
 }
 
 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
@@ -442,7 +741,7 @@ bool SketchPlugin_Offset::findWires()
   std::set<FeaturePtr> anEdgesSet;
 
   // Processed set
-  std::set<FeaturePtr> aProcessedSet;
+  std::set<FeaturePtr> aProcessedEdgesSet;
 
   // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
   std::set<FeaturePtr> aSelectedSet;
@@ -454,13 +753,15 @@ bool SketchPlugin_Offset::findWires()
     }
   }
 
+  bool aWasBlocked = data()->blockSendAttributeUpdated(true);
+
   // Gather chains of edges
   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
     if (aFeature.get()) {
-      if (aProcessedSet.find(aFeature) != aProcessedSet.end())
+      if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
         continue;
-      aProcessedSet.insert(aFeature);
+      aProcessedEdgesSet.insert(aFeature);
 
       // End points (if any)
       std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
@@ -468,22 +769,24 @@ bool SketchPlugin_Offset::findWires()
 
       std::list<FeaturePtr> aChain;
       aChain.push_back(aFeature);
-      bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
-      if (!isClosed)
-        findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
+      bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
+                                     aProcessedEdgesSet, aChain, true);
+      if (!isClosed) {
+        findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
+                       aProcessedEdgesSet, aChain, false);
+      }
 
       std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
       for (; aChainIt != aChain.end(); ++aChainIt) {
         FeaturePtr aChainFeature = (*aChainIt);
-        aProcessedSet.insert(aChainFeature);
         if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
           aSelectedEdges->append(aChainFeature->lastResult());
         }
       }
     }
   }
-  // TODO: hilight selection in the viewer
 
+  data()->blockSendAttributeUpdated(aWasBlocked);
   return true;
 }
 
@@ -497,3 +800,111 @@ AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
     thePrevious);
   return anAIS;
 }
+
+
+void SketchPlugin_Offset::makeFillet
+     (const double theValue,
+      const std::shared_ptr<GeomAlgoAPI_WireBuilder>& theWireBuilder,
+      const std::shared_ptr<GeomAlgoAPI_Offset>& theOffsetShape,
+      ListOfMakeShape& theOffsetAlgos)
+{
+  std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
+  aMakeList->appendAlgo(theWireBuilder);
+  aMakeList->appendAlgo(theOffsetShape);
+
+  bool isOK = true;
+
+  GeomShapePtr aResWire = theOffsetShape->shape();
+  GeomAlgoAPI_MapShapesAndAncestors aMapVE
+    (aResWire, GeomAPI_Shape::VERTEX, GeomAPI_Shape::EDGE);
+  const MapShapeToShapes& aSubshapes = aMapVE.map();
+
+  // find vertices for fillet
+  std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aFilletVertices;
+  for (MapShapeToShapes::const_iterator anIt = aSubshapes.begin();
+       anIt != aSubshapes.end(); ++anIt) {
+    // vertex should have 2 adjacent edges
+    if (anIt->second.size() != 2)
+      continue;
+
+    // both edges should be linear
+    ListOfShape anEdges;
+    anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
+    GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
+    GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
+    if (!anEdge1->isLine() || !anEdge2->isLine())
+      continue;
+
+    // skip vertices, which smoothly connect adjacent edges
+    GeomVertexPtr aSharedVertex(new GeomAPI_Vertex(anIt->first));
+    if (GeomAlgoAPI_ShapeTools::isTangent(anEdge1, anEdge2, aSharedVertex))
+      continue;
+
+    aFilletVertices.insert(anIt->first);
+  }
+
+  if (!aFilletVertices.empty()) {
+    isOK = false; // the wire needs correction
+    ListOfShape aVerticesList (aFilletVertices.begin(), aFilletVertices.end());
+
+    // Fillet1D on all linear edges intersections
+    std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder
+      (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList, theValue));
+
+    std::string anError;
+    if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
+        (aFilletBuilder, getKind(), anError)) {
+      aMakeList->appendAlgo(aFilletBuilder);
+      isOK = true;
+    }
+    else {
+      ListOfShape aFailedVertices = aFilletBuilder->failedVertices();
+      if (aFailedVertices.size() != 0) {
+        // Exclude failed vertices and also vertices, joined
+        // with failed by one edge, and run algorithm once again
+        ListOfShape::iterator itVertices = aFailedVertices.begin();
+        for (; itVertices != aFailedVertices.end(); itVertices++) {
+          GeomShapePtr aFailedVertex = *itVertices;
+          aFilletVertices.erase(aFailedVertex);
+          // remove also neighbour vertices
+          MapShapeToShapes::const_iterator anIt = aSubshapes.find(aFailedVertex);
+          if (anIt != aSubshapes.end()) { // should be always true
+            ListOfShape anEdges;
+            anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
+            GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
+            GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
+            GeomVertexPtr V1, V2;
+            anEdge1->vertices(V1, V2);
+            if (V1->isEqual(aFailedVertex)) V1 = V2;
+            aFilletVertices.erase(V1);
+            anEdge2->vertices(V1, V2);
+            if (V1->isEqual(aFailedVertex)) V1 = V2;
+            aFilletVertices.erase(V1);
+          }
+        }
+        if (aFilletVertices.size() == 0) {
+          // there are no suitable vertices for fillet
+          isOK = true;
+        }
+        else {
+          // Fillet1D one more try
+          ListOfShape aVerticesList1 (aFilletVertices.begin(), aFilletVertices.end());
+
+          std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder1
+            (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList1, theValue));
+
+          if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
+              (aFilletBuilder1, getKind(), anError)) {
+            aMakeList->appendAlgo(aFilletBuilder1);
+            isOK = true;
+          }
+        }
+      }
+    }
+  }
+
+  if (isOK)
+    theOffsetAlgos.push_back(aMakeList);
+  else
+    setError("Offset algorithm failed");
+}