Salome HOME
Regression of doc/salome/examples/transforming_meshes_ex11.py
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
index a37950e411a9a58af2f95936f903fcfbbc46bed2..7b0d86fdba9c23c097570b391eeef16067897d8d 100644 (file)
 
 #include "SMESH_MeshEditor.hxx"
 
-#include "SMDS_FaceOfNodes.hxx"
-#include "SMDS_VolumeTool.hxx"
+#include "SMDS_Downward.hxx"
 #include "SMDS_EdgePosition.hxx"
+#include "SMDS_FaceOfNodes.hxx"
 #include "SMDS_FacePosition.hxx"
-#include "SMDS_SpacePosition.hxx"
-#include "SMDS_MeshGroup.hxx"
 #include "SMDS_LinearEdge.hxx"
-#include "SMDS_Downward.hxx"
+#include "SMDS_MeshGroup.hxx"
 #include "SMDS_SetIterator.hxx"
-
+#include "SMDS_SpacePosition.hxx"
+#include "SMDS_VolumeTool.hxx"
 #include "SMESHDS_Group.hxx"
 #include "SMESHDS_Mesh.hxx"
-
 #include "SMESH_Algo.hxx"
 #include "SMESH_ControlsDef.hxx"
 #include "SMESH_Group.hxx"
+#include "SMESH_Mesh.hxx"
 #include "SMESH_MeshAlgos.hxx"
 #include "SMESH_MesherHelper.hxx"
 #include "SMESH_OctreeNode.hxx"
 #include "SMESH_subMesh.hxx"
 
-#include <Basics_OCCTVersion.hxx>
-
 #include "utilities.h"
 #include "chrono.hxx"
 
@@ -73,6 +70,7 @@
 #include <TopTools_ListOfShape.hxx>
 #include <TopTools_SequenceOfShape.hxx>
 #include <TopoDS.hxx>
+#include <TopoDS_Edge.hxx>
 #include <TopoDS_Face.hxx>
 #include <TopoDS_Solid.hxx>
 #include <gp.hxx>
 #include <sstream>
 
 #include <boost/tuple/tuple.hpp>
+#include <boost/container/flat_set.hpp>
 
 #include <Standard_Failure.hxx>
 #include <Standard_ErrorHandler.hxx>
+#include <OSD_Parallel.hxx>
+
+#include "SMESH_TryCatch.hxx" // include after OCCT headers!
 
 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
 
@@ -125,6 +127,18 @@ SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
 {
 }
 
+//================================================================================
+/*!
+ * \brief Return mesh DS
+ */
+//================================================================================
+
+SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
+{
+  return myMesh->GetMeshDS();
+}
+
+
 //================================================================================
 /*!
  * \brief Clears myLastCreatedNodes and myLastCreatedElems
@@ -462,27 +476,22 @@ int SMESH_MeshEditor::Remove (const list< int >& theIDs,
 
 //================================================================================
 /*!
- * \brief Create 0D elements on all nodes of the given object except those
- *        nodes on which a 0D element already exists.
+ * \brief Create 0D elements on all nodes of the given object.
  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
  *                    the all mesh is treated
  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
+ *  \param duplicateElements - to add one more 0D element to a node or not
  */
 //================================================================================
 
 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
-                                                   TIDSortedElemSet&       all0DElems )
+                                                   TIDSortedElemSet&       all0DElems,
+                                                   const bool              duplicateElements )
 {
   SMDS_ElemIteratorPtr elemIt;
-  vector< const SMDS_MeshElement* > allNodes;
   if ( elements.empty() )
   {
-    allNodes.reserve( GetMeshDS()->NbNodes() );
     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
-    while ( elemIt->more() )
-      allNodes.push_back( elemIt->next() );
-
-    elemIt = elemSetIterator( allNodes );
   }
   else
   {
@@ -497,12 +506,13 @@ void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& eleme
     {
       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
-      if ( it0D->more() )
-        all0DElems.insert( it0D->next() );
-      else {
+      if ( duplicateElements || !it0D->more() )
+      {
         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
         all0DElems.insert( myLastCreatedElems.Last() );
       }
+      while ( it0D->more() )
+        all0DElems.insert( it0D->next() );
     }
   }
 }
@@ -683,7 +693,6 @@ static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
                                     const SMDS_MeshElement * theTria2 )
 {
-  MESSAGE("InverseDiag");
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
@@ -888,8 +897,6 @@ bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  MESSAGE( "::InverseDiag()" );
-
   const SMDS_MeshElement *tr1, *tr2;
   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
     return false;
@@ -1011,8 +1018,6 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  MESSAGE( "::DeleteDiag()" );
-
   const SMDS_MeshElement *tr1, *tr2;
   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
     return false;
@@ -1105,7 +1110,6 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
 
 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
 {
-  MESSAGE("Reorient");
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
@@ -1166,7 +1170,7 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
  * \brief Reorient faces.
  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
  * \param theDirection - desired direction of normal of \a theFace
- * \param theFace - one of \a theFaces that sould be oriented according to
+ * \param theFace - one of \a theFaces that should be oriented according to
  *        \a theDirection and whose orientation defines orientation of other faces
  * \return number of reoriented faces.
  */
@@ -1276,7 +1280,7 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
         }
         if ( otherFace && otherFace != theFace)
         {
-          // link must be reverse in otherFace if orientation ot otherFace
+          // link must be reverse in otherFace if orientation to otherFace
           // is same as that of theFace
           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
           {
@@ -1498,7 +1502,7 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
 //=======================================================================
 /*!
  * \brief Split each of given quadrangles into 4 triangles.
- * \param theElems - The faces to be splitted. If empty all faces are split.
+ * \param theElems - The faces to be split. If empty all faces are split.
  */
 //=======================================================================
 
@@ -1518,7 +1522,7 @@ void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
   gp_XYZ xyz[9];
   vector< const SMDS_MeshNode* > nodes;
-  SMESHDS_SubMesh*               subMeshDS;
+  SMESHDS_SubMesh*               subMeshDS = 0;
   TopoDS_Face                    F;
   Handle(Geom_Surface)           surface;
   TopLoc_Location                loc;
@@ -1912,6 +1916,7 @@ namespace
         break;
       case SMDSEntity_Penta:
       case SMDSEntity_Quad_Penta:
+      case SMDSEntity_BiQuad_Penta:
         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
         break;
       default:
@@ -2130,7 +2135,7 @@ namespace
 
     // No adjacent prisms. Select a variant with a best aspect ratio.
 
-    double badness[2] = { 0, 0 };
+    double badness[2] = { 0., 0. };
     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
     const SMDS_MeshNode** nodes = vol.GetNodes();
     for ( int variant = 0; variant < nbVariants; ++variant )
@@ -2396,7 +2401,7 @@ void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
         }
         else
         {
-          // among possible triangles create ones discribed by split method
+          // among possible triangles create ones described by split method
           const int* nInd = volTool.GetFaceNodesIndices( iF );
           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
           int iCom = 0; // common node of triangle faces to split into
@@ -2522,7 +2527,7 @@ void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
 
   // Fill theFacets starting from facetID of startHex
 
-  // facets used for seach of volumes adjacent to already treated ones
+  // facets used for searching of volumes adjacent to already treated ones
   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
   TFacetMap facetsToCheck;
@@ -2931,8 +2936,6 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  MESSAGE( "::QuadToTri()" );
-
   SMESHDS_Mesh * aMesh = GetMeshDS();
 
   Handle(Geom_Surface) surface;
@@ -3153,8 +3156,6 @@ bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  MESSAGE( "::TriToQuad()" );
-
   if ( !theCrit.get() )
     return false;
 
@@ -3259,7 +3260,7 @@ bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
       if ( startElem ) {
         // Get candidates to be fused
         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
-        const SMESH_TLink *link12, *link13;
+        const SMESH_TLink *link12 = 0, *link13 = 0;
         startElem = 0;
         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
@@ -3938,8 +3939,6 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
-
   if ( theTgtAspectRatio < 1.0 )
     theTgtAspectRatio = 1.0;
 
@@ -3998,11 +3997,11 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
       fToler2 = BRep_Tool::Tolerance( face );
       fToler2 *= fToler2 * 10.;
       isUPeriodic = surface->IsUPeriodic();
-      if ( isUPeriodic )
-        surface->UPeriod();
+      // if ( isUPeriodic )
+      //   surface->UPeriod();
       isVPeriodic = surface->IsVPeriodic();
-      if ( isVPeriodic )
-        surface->VPeriod();
+      // if ( isVPeriodic )
+      //   surface->VPeriod();
       surface->Bounds( u1, u2, v1, v2 );
       helper.SetSubShape( face );
     }
@@ -4061,9 +4060,9 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
         {
           // check if all faces around the node are on faceSubMesh
           // because a node on edge may be bound to face
-          SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
           bool all = true;
           if ( faceSubMesh ) {
+            SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
             while ( eIt->more() && all ) {
               const SMDS_MeshElement* e = eIt->next();
               all = faceSubMesh->Contains( e );
@@ -4346,18 +4345,18 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
         }
       }
       if ( maxRatio <= theTgtAspectRatio ) {
-        MESSAGE("-- quality achived --");
+        //MESSAGE("-- quality achieved --");
         break;
       }
       if (it+1 == theNbIterations) {
-        MESSAGE("-- Iteration limit exceeded --");
+        //MESSAGE("-- Iteration limit exceeded --");
       }
     } // smoothing iterations
 
-    MESSAGE(" Face id: " << *fId <<
-            " Nb iterstions: " << it <<
-            " Displacement: " << maxDisplacement <<
-            " Aspect Ratio " << maxRatio);
+    // MESSAGE(" Face id: " << *fId <<
+    //         " Nb iterstions: " << it <<
+    //         " Displacement: " << maxDisplacement <<
+    //         " Aspect Ratio " << maxRatio);
 
     // ---------------------------------------
     // new nodes positions are computed,
@@ -4476,7 +4475,6 @@ void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
                                     const size_t                          nbSteps,
                                     SMESH_SequenceOfElemPtr&              srcElements)
 {
-  //MESSAGE("sweepElement " << nbSteps);
   SMESHDS_Mesh* aMesh = GetMeshDS();
 
   const int           nbNodes = elem->NbNodes();
@@ -4573,7 +4571,7 @@ void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
         std::swap( itNN[0],    itNN[1] );
         std::swap( prevNod[0], prevNod[1] );
         std::swap( nextNod[0], nextNod[1] );
-        isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
+        std::swap( isSingleNode[0], isSingleNode[1] );
         if ( nbSame > 0 )
           sames[0] = 1 - sames[0];
         iNotSameNode = 1 - iNotSameNode;
@@ -5368,7 +5366,6 @@ SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
   // source elements for each generated one
   SMESH_SequenceOfElemPtr srcElems, srcNodes;
 
-  MESSAGE( "RotationSweep()");
   gp_Trsf aTrsf;
   aTrsf.SetRotation( theAxis, theAngle );
   gp_Trsf aTrsf2;
@@ -5482,11 +5479,14 @@ SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
 //purpose  : standard construction
 //=======================================================================
 
-SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
-                                            const int      theNbSteps,
-                                            const int      theFlags,
-                                            const double   theTolerance):
+SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
+                                            const int                theNbSteps,
+                                            const std::list<double>& theScales,
+                                            const gp_XYZ*            theBasePoint,
+                                            const int                theFlags,
+                                            const double             theTolerance):
   myDir( theStep ),
+  myBaseP( Precision::Infinite(), 0, 0 ),
   myFlags( theFlags ),
   myTolerance( theTolerance ),
   myElemsToUse( NULL )
@@ -5496,6 +5496,37 @@ SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
   for (int i=1; i<=theNbSteps; i++ )
     mySteps->Append( stepSize );
 
+  int nbScales = theScales.size();
+  if ( nbScales > 0 )
+  {
+    if ( IsLinearVariation() && nbScales < theNbSteps )
+    {
+      myScales.reserve( theNbSteps );
+      std::list<double>::const_iterator scale = theScales.begin();
+      double prevScale = 1.0;
+      for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
+      {
+        int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
+        int    stDelta = Max( 1, iStep - myScales.size());
+        double scDelta = ( *scale - prevScale ) / stDelta;
+        for ( int iStep = 0; iStep < stDelta; ++iStep )
+        {
+          myScales.push_back( prevScale + scDelta );
+          prevScale = myScales.back();
+        }
+        prevScale = *scale;
+      }
+    }
+    else
+    {
+      myScales.assign( theScales.begin(), theScales.end() );
+    }
+  }
+  if ( theBasePoint )
+  {
+    myBaseP = *theBasePoint;
+  }
+
   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
       ( theTolerance > 0 ))
   {
@@ -5564,12 +5595,38 @@ SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
 //=======================================================================
 //function : ExtrusParam::SetElementsToUse
 //purpose  : stores elements to use for extrusion by normal, depending on
-//           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
+//           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
+//           define myBaseP for scaling
 //=======================================================================
 
-void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
+void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
+                                                      const TIDSortedElemSet& nodes )
 {
   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
+
+  if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
+  {
+    myBaseP.SetCoord( 0.,0.,0. );
+    TIDSortedElemSet newNodes;
+
+    const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
+    for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
+    {
+      const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
+      TIDSortedElemSet::const_iterator itElem = elements.begin();
+      for ( ; itElem != elements.end(); itElem++ )
+      {
+        const SMDS_MeshElement* elem = *itElem;
+        SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
+        while ( itN->more() ) {
+          const SMDS_MeshElement* node = itN->next();
+          if ( newNodes.insert( node ).second )
+            myBaseP += SMESH_TNodeXYZ( node );
+        }
+      }
+    }
+    myBaseP /= newNodes.size();
+  }
 }
 
 //=======================================================================
@@ -5639,6 +5696,41 @@ makeNodesByDir( SMESHDS_Mesh*                     mesh,
     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
     newNodes.push_back( newNode );
   }
+
+  if ( !myScales.empty() )
+  {
+    if ( makeMediumNodes && myMediumScales.empty() )
+    {
+      myMediumScales.resize( myScales.size() );
+      double prevFactor = 1.;
+      for ( size_t i = 0; i < myScales.size(); ++i )
+      {
+        myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
+        prevFactor = myScales[i];
+      }
+    }
+    typedef std::vector<double>::iterator ScaleIt;
+    ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
+
+    size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
+
+    gp_XYZ center = myBaseP;
+    std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
+    size_t iN  = 0;
+    for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
+    {
+      center += myDir.XYZ() * nextStep();
+
+      iSc += int( makeMediumNodes );
+      ScaleIt& scale = scales[ iSc % 2 ];
+      
+      gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
+      xyz = ( *scale * ( xyz - center )) + center;
+      mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
+
+      ++scale;
+    }
+  }
   return nbNodes;
 }
 
@@ -5813,7 +5905,7 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
                                   const int            theFlags,
                                   const double         theTolerance)
 {
-  ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
+  ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
   return ExtrusionSweep( theElems, aParams, newElemsMap );
 }
 
@@ -5834,16 +5926,12 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
   // source elements for each generated one
   SMESH_SequenceOfElemPtr srcElems, srcNodes;
 
-  //SMESHDS_Mesh* aMesh = GetMeshDS();
-
   setElemsFirst( theElemSets );
   const int nbSteps = theParams.NbSteps();
-  theParams.SetElementsToUse( theElemSets[0] );
+  theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
 
-  TNodeOfNodeListMap mapNewNodes;
-  //TNodeOfNodeVecMap mapNewNodes;
+  TNodeOfNodeListMap   mapNewNodes;
   TElemOfVecOfNnlmiMap mapElemNewNodes;
-  //TElemOfVecOfMapNodesMap mapElemNewNodes;
 
   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
                                      myMesh->NbFaces(ORDER_QUADRATIC) +
@@ -5939,7 +6027,6 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
                                        const gp_Pnt&        theRefPoint,
                                        const bool           theMakeGroups)
 {
-  MESSAGE("ExtrusionAlongTrack");
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
@@ -6007,7 +6094,7 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
       aPrms.push_back( aT );
     }
     //Extrusion_Error err =
-    MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
+    makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
   } else if( aS.ShapeType() == TopAbs_WIRE ) {
     list< SMESH_subMesh* > LSM;
     TopTools_SequenceOfShape Edges;
@@ -6052,10 +6139,10 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
         }
         list<SMESH_MeshEditor_PathPoint> LPP;
         //Extrusion_Error err =
-        MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
+        makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
         LLPPs.push_back(LPP);
         UsedNums.Add(k);
-        // update startN for search following egde
+        // update startN for search following edge
         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
         else startNid = aN1->GetID();
         break;
@@ -6095,7 +6182,7 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
     return EXTR_BAD_PATH_SHAPE;
   }
 
-  return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
+  return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
                           theHasRefPoint, theRefPoint, theMakeGroups);
 }
 
@@ -6234,7 +6321,7 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
       list<SMESH_MeshEditor_PathPoint> LPP;
       aPrms.clear();
-      MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
+      makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
       LLPPs.push_back(LPP);
       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
       else                                        startNid = aNodesList[i-1]->GetID();
@@ -6292,7 +6379,7 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
       aPrms.push_back( aT );
     }
     //Extrusion_Error err =
-    MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
+    makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
   }
   else if( aS.ShapeType() == TopAbs_WIRE ) {
     list< SMESH_subMesh* > LSM;
@@ -6351,10 +6438,10 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
         }
         list<SMESH_MeshEditor_PathPoint> LPP;
         //Extrusion_Error err =
-        MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
+        makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
         LLPPs.push_back(LPP);
         UsedNums.Add(k);
-        // update startN for search following egde
+        // update startN for search following edge
         if ( aN1isOK ) aVprev = aV2;
         else           aVprev = aV1;
         break;
@@ -6387,17 +6474,17 @@ SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
     return EXTR_BAD_PATH_SHAPE;
   }
 
-  return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
+  return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
                           theHasRefPoint, theRefPoint, theMakeGroups);
 }
 
 
 //=======================================================================
-//function : MakeEdgePathPoints
-//purpose  : auxilary for ExtrusionAlongTrack
+//function : makeEdgePathPoints
+//purpose  : auxiliary for ExtrusionAlongTrack
 //=======================================================================
 SMESH_MeshEditor::Extrusion_Error
-SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
+SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
                                      const TopoDS_Edge&                aTrackEdge,
                                      bool                              FirstIsStart,
                                      list<SMESH_MeshEditor_PathPoint>& LPP)
@@ -6448,11 +6535,11 @@ SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
 
 
 //=======================================================================
-//function : MakeExtrElements
-//purpose  : auxilary for ExtrusionAlongTrack
+//function : makeExtrElements
+//purpose  : auxiliary for ExtrusionAlongTrack
 //=======================================================================
 SMESH_MeshEditor::Extrusion_Error
-SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
+SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
                                    list<SMESH_MeshEditor_PathPoint>& fullList,
                                    const bool                        theHasAngles,
                                    list<double>&                     theAngles,
@@ -6465,7 +6552,7 @@ SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets
 
   // Angles
   if( theHasAngles && !theAngles.empty() && theLinearVariation )
-    LinearAngleVariation(aNbTP-1, theAngles);
+    linearAngleVariation(aNbTP-1, theAngles);
 
   // fill vector of path points with angles
   vector<SMESH_MeshEditor_PathPoint> aPPs;
@@ -6658,25 +6745,24 @@ SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets
 
 
 //=======================================================================
-//function : LinearAngleVariation
-//purpose  : auxilary for ExtrusionAlongTrack
+//function : linearAngleVariation
+//purpose  : spread values over nbSteps
 //=======================================================================
-void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
+
+void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
                                             list<double>& Angles)
 {
   int nbAngles = Angles.size();
-  if( nbSteps > nbAngles ) {
+  if( nbSteps > nbAngles && nbAngles > 0 )
+  {
     vector<double> theAngles(nbAngles);
-    list<double>::iterator it = Angles.begin();
-    int i = -1;
-    for(; it!=Angles.end(); it++) {
-      i++;
-      theAngles[i] = (*it);
-    }
+    theAngles.assign( Angles.begin(), Angles.end() );
+
     list<double> res;
     double rAn2St = double( nbAngles ) / double( nbSteps );
     double angPrev = 0, angle;
-    for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
+    for ( int iSt = 0; iSt < nbSteps; ++iSt )
+    {
       double angCur = rAn2St * ( iSt+1 );
       double angCurFloor  = floor( angCur );
       double angPrevFloor = floor( angPrev );
@@ -6698,10 +6784,7 @@ void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
       res.push_back(angle);
       angPrev = angCur;
     }
-    Angles.clear();
-    it = res.begin();
-    for(; it!=res.end(); it++)
-      Angles.push_back( *it );
+    Angles.swap( res );
   }
 }
 
@@ -6732,37 +6815,29 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
   string groupPostfix;
   switch ( theTrsf.Form() ) {
   case gp_PntMirror:
-    MESSAGE("gp_PntMirror");
     needReverse = true;
     groupPostfix = "mirrored";
     break;
   case gp_Ax1Mirror:
-    MESSAGE("gp_Ax1Mirror");
     groupPostfix = "mirrored";
     break;
   case gp_Ax2Mirror:
-    MESSAGE("gp_Ax2Mirror");
     needReverse = true;
     groupPostfix = "mirrored";
     break;
   case gp_Rotation:
-    MESSAGE("gp_Rotation");
     groupPostfix = "rotated";
     break;
   case gp_Translation:
-    MESSAGE("gp_Translation");
     groupPostfix = "translated";
     break;
   case gp_Scale:
-    MESSAGE("gp_Scale");
     groupPostfix = "scaled";
     break;
   case gp_CompoundTrsf: // different scale by axis
-    MESSAGE("gp_CompoundTrsf");
     groupPostfix = "scaled";
     break;
   default:
-    MESSAGE("default");
     needReverse = false;
     groupPostfix = "transformed";
   }
@@ -7182,7 +7257,7 @@ void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
       }
     else
       while ( nIt->more() )
-        theNodes.insert( theNodes.end(),nIt->next() );
+        theNodes.insert( theNodes.end(), nIt->next() );
   }
   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
   {
@@ -7269,17 +7344,18 @@ int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNod
 //           in all elements.
 //=======================================================================
 
-void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
+void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
+                                   const bool           theAvoidMakingHoles)
 {
-  MESSAGE("MergeNodes");
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  SMESHDS_Mesh* aMesh = GetMeshDS();
+  SMESHDS_Mesh* mesh = GetMeshDS();
 
   TNodeNodeMap nodeNodeMap; // node to replace - new node
   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
   list< int > rmElemIds, rmNodeIds;
+  vector< ElemFeatures > newElemDefs;
 
   // Fill nodeNodeMap and elems
 
@@ -7293,17 +7369,6 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
     {
       const SMDS_MeshNode* nToRemove = *nIt;
       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
-      if ( nToRemove != nToKeep )
-      {
-        rmNodeIds.push_back( nToRemove->GetID() );
-        AddToSameGroups( nToKeep, nToRemove, aMesh );
-        // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
-        // after MergeNodes() w/o creating node in place of merged ones.
-        const SMDS_PositionPtr& pos = nToRemove->GetPosition();
-        if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
-          if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
-            sm->SetIsAlwaysComputed( true );
-      }
       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
       while ( invElemIt->more() ) {
         const SMDS_MeshElement* elem = invElemIt->next();
@@ -7311,469 +7376,568 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
       }
     }
   }
-  // Change element nodes or remove an element
 
-  set<const SMDS_MeshNode*> nodeSet;
-  vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
-  vector<int> iRepl;
-  ElemFeatures elemType;
+  // Apply recursive replacements (BUG 0020185)
+  TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
+  for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
+  {
+    const SMDS_MeshNode* nToKeep = nnIt->second;
+    TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
+    while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
+      nToKeep = nnIt_i->second;
+    nnIt->second = nToKeep;
+  }
+
+  if ( theAvoidMakingHoles )
+  {
+    // find elements whose topology changes
+
+    vector<const SMDS_MeshElement*> pbElems;
+    set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
+    for ( ; eIt != elems.end(); ++eIt )
+    {
+      const SMDS_MeshElement* elem = *eIt;
+      SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
+      while ( itN->more() )
+      {
+        const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
+        TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
+        if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
+        {
+          // several nodes of elem stick
+          pbElems.push_back( elem );
+          break;
+        }
+      }
+    }
+    // exclude from merge nodes causing spoiling element
+    for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
+    {
+      bool nodesExcluded = false;
+      for ( size_t i = 0; i < pbElems.size(); ++i )
+      {
+        size_t prevNbMergeNodes = nodeNodeMap.size();
+        if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
+             prevNbMergeNodes < nodeNodeMap.size() )
+          nodesExcluded = true;
+      }
+      if ( !nodesExcluded )
+        break;
+    }
+  }
+
+  for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
+  {
+    const SMDS_MeshNode* nToRemove = nnIt->first;
+    const SMDS_MeshNode* nToKeep   = nnIt->second;
+    if ( nToRemove != nToKeep )
+    {
+      rmNodeIds.push_back( nToRemove->GetID() );
+      AddToSameGroups( nToKeep, nToRemove, mesh );
+      // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
+      // w/o creating node in place of merged ones.
+      const SMDS_PositionPtr& pos = nToRemove->GetPosition();
+      if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
+        if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
+          sm->SetIsAlwaysComputed( true );
+    }
+  }
+
+  // Change element nodes or remove an element
 
   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
   for ( ; eIt != elems.end(); eIt++ )
   {
     const SMDS_MeshElement* elem = *eIt;
-    const           int  nbNodes = elem->NbNodes();
-    const           int aShapeId = FindShape( elem );
-    SMDSAbs_EntityType    entity = elem->GetEntityType();
+    SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
 
-    nodeSet.clear();
-    curNodes.resize( nbNodes );
-    uniqueNodes.resize( nbNodes );
-    iRepl.resize( nbNodes );
-    int iUnique = 0, iCur = 0, nbRepl = 0;
+    bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
+    if ( !keepElem )
+      rmElemIds.push_back( elem->GetID() );
 
-    // get new seq of nodes
-    SMDS_ElemIteratorPtr itN = elem->nodesIterator();
-    while ( itN->more() )
-    {
-      const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
-
-      TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
-      if ( nnIt != nodeNodeMap.end() ) { // n sticks
-        n = (*nnIt).second;
-        { ////////// BUG 0020185: begin
-          bool stopRecur = false;
-          set<const SMDS_MeshNode*> nodesRecur;
-          nodesRecur.insert(n);
-          while (!stopRecur) {
-            TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
-            if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
-              n = (*nnIt_i).second;
-              if (!nodesRecur.insert(n).second) {
-                // error: recursive dependency
-                stopRecur = true;
-              }
-            }
-            else
-              stopRecur = true;
-          }
-        } ////////// BUG 0020185: end
+    for ( size_t i = 0; i < newElemDefs.size(); ++i )
+    {
+      if ( i > 0 || !mesh->ChangeElementNodes( elem,
+                                               & newElemDefs[i].myNodes[0],
+                                               newElemDefs[i].myNodes.size() ))
+      {
+        if ( i == 0 )
+        {
+          newElemDefs[i].SetID( elem->GetID() );
+          mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
+          if ( !keepElem ) rmElemIds.pop_back();
+        }
+        else
+        {
+          newElemDefs[i].SetID( -1 );
+        }
+        SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
+        if ( sm && newElem )
+          sm->AddElement( newElem );
+        if ( elem != newElem )
+          ReplaceElemInGroups( elem, newElem, mesh );
       }
-      curNodes[ iCur ] = n;
-      bool isUnique = nodeSet.insert( n ).second;
-      if ( isUnique )
-        uniqueNodes[ iUnique++ ] = n;
-      else
-        iRepl[ nbRepl++ ] = iCur;
-      iCur++;
     }
+  }
+
+  // Remove bad elements, then equal nodes (order important)
+  Remove( rmElemIds, /*isNodes=*/false );
+  Remove( rmNodeIds, /*isNodes=*/true );
+
+  return;
+}
+
+//=======================================================================
+//function : applyMerge
+//purpose  : Compute new connectivity of an element after merging nodes
+//  \param [in] elems - the element
+//  \param [out] newElemDefs - definition(s) of result element(s)
+//  \param [inout] nodeNodeMap - nodes to merge
+//  \param [in] avoidMakingHoles - if true and and the element becomes invalid
+//              after merging (but not degenerated), removes nodes causing
+//              the invalidity from \a nodeNodeMap.
+//  \return bool - true if the element should be removed
+//=======================================================================
+
+bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
+                                   vector< ElemFeatures >& newElemDefs,
+                                   TNodeNodeMap&           nodeNodeMap,
+                                   const bool              avoidMakingHoles )
+{
+  bool toRemove = false; // to remove elem
+  int nbResElems = 1;    // nb new elements
+
+  newElemDefs.resize(nbResElems);
+  newElemDefs[0].Init( elem );
+  newElemDefs[0].myNodes.clear();
+
+  set<const SMDS_MeshNode*> nodeSet;
+  vector< const SMDS_MeshNode*>   curNodes;
+  vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
+  vector<int> iRepl;
+
+  const        int  nbNodes = elem->NbNodes();
+  SMDSAbs_EntityType entity = elem->GetEntityType();
+
+  curNodes.resize( nbNodes );
+  uniqueNodes.resize( nbNodes );
+  iRepl.resize( nbNodes );
+  int iUnique = 0, iCur = 0, nbRepl = 0;
+
+  // Get new seq of nodes
+
+  SMDS_ElemIteratorPtr itN = elem->nodesIterator();
+  while ( itN->more() )
+  {
+    const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
+
+    TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
+    if ( nnIt != nodeNodeMap.end() ) {
+      n = (*nnIt).second;
+    }
+    curNodes[ iCur ] = n;
+    bool isUnique = nodeSet.insert( n ).second;
+    if ( isUnique )
+      uniqueNodes[ iUnique++ ] = n;
+    else
+      iRepl[ nbRepl++ ] = iCur;
+    iCur++;
+  }
+
+  // Analyse element topology after replacement
 
-    // Analyse element topology after replacement
+  int nbUniqueNodes = nodeSet.size();
+  if ( nbNodes != nbUniqueNodes ) // some nodes stick
+  {
+    toRemove = true;
+    nbResElems = 0;
 
-    bool isOk = true;
-    int nbUniqueNodes = nodeSet.size();
-    if ( nbNodes != nbUniqueNodes ) // some nodes stick
+    if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
     {
-      if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
+      // if corner nodes stick, remove medium nodes between them from uniqueNodes
+      int nbCorners = nbNodes / 2;
+      for ( int iCur = 0; iCur < nbCorners; ++iCur )
       {
-        if ( elem->GetType() == SMDSAbs_Face ) // Polygon
+        int iNext = ( iCur + 1 ) % nbCorners;
+        if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
         {
-          elemType.Init( elem );
-          const bool isQuad = elemType.myIsQuad;
-          if ( isQuad )
-            SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
-              ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
-
-          // a polygon can divide into several elements
-          vector<const SMDS_MeshNode *> polygons_nodes;
-          vector<int> quantities;
-          int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
-          if (nbNew > 0)
+          int iMedium = iCur + nbCorners;
+          vector< const SMDS_MeshNode* >::iterator i =
+            std::find( uniqueNodes.begin() + nbCorners - nbRepl,
+                       uniqueNodes.end(),
+                       curNodes[ iMedium ]);
+          if ( i != uniqueNodes.end() )
           {
-            vector<const SMDS_MeshNode *> face_nodes;
-            int inode = 0;
-            for (int iface = 0; iface < nbNew; iface++)
-            {
-              int nbNewNodes = quantities[iface];
-              face_nodes.assign( polygons_nodes.begin() + inode,
-                                 polygons_nodes.begin() + inode + nbNewNodes );
-              inode += nbNewNodes;
-              if ( isQuad ) // check if a result elem is a valid quadratic polygon
-              {
-                bool isValid = ( nbNewNodes % 2 == 0 );
-                for ( int i = 0; i < nbNewNodes && isValid; ++i )
-                  isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
-                elemType.SetQuad( isValid );
-                if ( isValid ) // put medium nodes after corners
-                  SMDS_MeshCell::applyInterlaceRev
-                    ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
-                                                          nbNewNodes ), face_nodes );
-              }
-              elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
-
-              SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
-              if ( aShapeId )
-                aMesh->SetMeshElementOnShape(newElem, aShapeId);
-            }
+            --nbUniqueNodes;
+            for ( ; i+1 != uniqueNodes.end(); ++i )
+              *i = *(i+1);
           }
-          rmElemIds.push_back(elem->GetID());
+        }
+      }
+    }
 
-        } // Polygon
+    switch ( entity )
+    {
+    case SMDSEntity_Polygon:
+    case SMDSEntity_Quad_Polygon: // Polygon
+    {
+      ElemFeatures* elemType = & newElemDefs[0];
+      const bool isQuad = elemType->myIsQuad;
+      if ( isQuad )
+        SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
+          ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
 
-        else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
-        {
-          if ( nbUniqueNodes < 4 ) {
-            rmElemIds.push_back(elem->GetID());
-          }
-          else {
-            // each face has to be analyzed in order to check volume validity
-            const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
-            if ( aPolyedre )
-            {
-              int nbFaces = aPolyedre->NbFaces();
+      // a polygon can divide into several elements
+      vector<const SMDS_MeshNode *> polygons_nodes;
+      vector<int> quantities;
+      nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
+      newElemDefs.resize( nbResElems );
+      for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
+      {
+        ElemFeatures* elemType = & newElemDefs[iface];
+        if ( iface ) elemType->Init( elem );
 
-              vector<const SMDS_MeshNode *> poly_nodes;
-              vector<int>                   quantities;
-              vector<const SMDS_MeshNode *> faceNodes;
+        vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
+        int nbNewNodes = quantities[iface];
+        face_nodes.assign( polygons_nodes.begin() + inode,
+                           polygons_nodes.begin() + inode + nbNewNodes );
+        inode += nbNewNodes;
+        if ( isQuad ) // check if a result elem is a valid quadratic polygon
+        {
+          bool isValid = ( nbNewNodes % 2 == 0 );
+          for ( int i = 0; i < nbNewNodes && isValid; ++i )
+            isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
+          elemType->SetQuad( isValid );
+          if ( isValid ) // put medium nodes after corners
+            SMDS_MeshCell::applyInterlaceRev
+              ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
+                                                    nbNewNodes ), face_nodes );
+        }
+        elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
+      }
+      nbUniqueNodes = newElemDefs[0].myNodes.size();
+      break;
+    } // Polygon
 
-              for (int iface = 1; iface <= nbFaces; iface++)
-              {
-                int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
-                faceNodes.resize( nbFaceNodes );
-                for (int inode = 1; inode <= nbFaceNodes; inode++)
-                {
-                  const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
-                  TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
-                  if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
-                    faceNode = (*nnIt).second;
-                  faceNodes[inode - 1] = faceNode;
-                }
-                SimplifyFace(faceNodes, poly_nodes, quantities);
-              }
+    case SMDSEntity_Polyhedra: // Polyhedral volume
+    {
+      if ( nbUniqueNodes >= 4 )
+      {
+        // each face has to be analyzed in order to check volume validity
+        if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
+        {
+          int nbFaces = aPolyedre->NbFaces();
 
-              if ( quantities.size() > 3 ) {
-                // TODO: remove coincident faces
-              }
+          vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
+          vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
+          vector<const SMDS_MeshNode *>  faceNodes;
+          poly_nodes.clear();
+          quantities.clear();
 
-              if ( quantities.size() > 3 )
-              {
-                const SMDS_MeshElement* newElem =
-                  aMesh->AddPolyhedralVolume( poly_nodes, quantities );
-                myLastCreatedElems.Append( newElem );
-                if ( aShapeId && newElem )
-                  aMesh->SetMeshElementOnShape( newElem, aShapeId );
-                rmElemIds.push_back( elem->GetID() );
-              }
-            }
-            else {
-              rmElemIds.push_back( elem->GetID() );
+          for (int iface = 1; iface <= nbFaces; iface++)
+          {
+            int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
+            faceNodes.resize( nbFaceNodes );
+            for (int inode = 1; inode <= nbFaceNodes; inode++)
+            {
+              const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
+              TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
+              if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
+                faceNode = (*nnIt).second;
+              faceNodes[inode - 1] = faceNode;
             }
+            SimplifyFace(faceNodes, poly_nodes, quantities);
           }
-        }
-        else {
-        }
-
-        continue;
-      } // poly element
 
-      // Regular elements
-      // TODO not all the possible cases are solved. Find something more generic?
-      switch ( entity ) {
-      case SMDSEntity_Edge: //////// EDGE
-      case SMDSEntity_Triangle: //// TRIANGLE
-      case SMDSEntity_Quad_Triangle:
-      case SMDSEntity_Tetra:
-      case SMDSEntity_Quad_Tetra: // TETRAHEDRON
-      {
-        isOk = false;
-        break;
+          if ( quantities.size() > 3 )
+          {
+            // TODO: remove coincident faces
+            nbResElems = 1;
+            nbUniqueNodes = newElemDefs[0].myNodes.size();
+          }
+        }
       }
-      case SMDSEntity_Quad_Edge:
+    }
+    break;
+
+    // Regular elements
+    // TODO not all the possible cases are solved. Find something more generic?
+    case SMDSEntity_Edge: //////// EDGE
+    case SMDSEntity_Triangle: //// TRIANGLE
+    case SMDSEntity_Quad_Triangle:
+    case SMDSEntity_Tetra:
+    case SMDSEntity_Quad_Tetra: // TETRAHEDRON
+    {
+      break;
+    }
+    case SMDSEntity_Quad_Edge:
+    {
+      break;
+    }
+    case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
+    {
+      if ( nbUniqueNodes < 3 )
+        toRemove = true;
+      else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
+        toRemove = true; // opposite nodes stick
+      else
+        toRemove = false;
+      break;
+    }
+    case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
+    {
+      //   1    5    2
+      //    +---+---+
+      //    |       |
+      //   4+       +6
+      //    |       |
+      //    +---+---+
+      //   0    7    3
+      if ( nbUniqueNodes == 6 &&
+           iRepl[0] < 4       &&
+           ( nbRepl == 1 || iRepl[1] >= 4 ))
       {
-        isOk = false; // to linear EDGE ???????
-        break;
+        toRemove = false;
       }
-      case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
+      break;
+    }
+    case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
+    {
+      //   1    5    2
+      //    +---+---+
+      //    |       |
+      //   4+  8+   +6
+      //    |       |
+      //    +---+---+
+      //   0    7    3
+      if ( nbUniqueNodes == 7 &&
+           iRepl[0] < 4       &&
+           ( nbRepl == 1 || iRepl[1] != 8 ))
       {
-        if ( nbUniqueNodes < 3 )
-          isOk = false;
-        else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
-          isOk = false; // opposite nodes stick
-        break;
+        toRemove = false;
       }
-      case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
-      {
-        //   1    5    2
-        //    +---+---+
-        //    |       |
-        //   4+       +6
-        //    |       |
-        //    +---+---+
-        //   0    7    3
-        if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
-            (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
-             ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
-             ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
-             ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
+      break;
+    }
+    case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
+    {
+      if ( nbUniqueNodes == 4 ) {
+        // ---------------------------------> tetrahedron
+        if ( curNodes[3] == curNodes[4] &&
+             curNodes[3] == curNodes[5] ) {
+          // top nodes stick
+          toRemove = false;
+        }
+        else if ( curNodes[0] == curNodes[1] &&
+                  curNodes[0] == curNodes[2] ) {
+          // bottom nodes stick: set a top before
+          uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
+          uniqueNodes[ 0 ] = curNodes [ 5 ];
+          uniqueNodes[ 1 ] = curNodes [ 4 ];
+          uniqueNodes[ 2 ] = curNodes [ 3 ];
+          toRemove = false;
+        }
+        else if (( curNodes[0] == curNodes[3] ) +
+                 ( curNodes[1] == curNodes[4] ) +
+                 ( curNodes[2] == curNodes[5] ) == 2 ) {
+          // a lateral face turns into a line
+          toRemove = false;
+        }
+      }
+      else if ( nbUniqueNodes == 5 ) {
+        // PENTAHEDRON --------------------> pyramid
+        if ( curNodes[0] == curNodes[3] )
         {
-          isOk = true;
-        }
-        break;
-      }
-      case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
-      {
-        //   1    5    2
-        //    +---+---+
-        //    |       |
-        //   4+  8+   +6
-        //    |       |
-        //    +---+---+
-        //   0    7    3
-        if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
-            (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
-             ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
-             ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
-             ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
+          uniqueNodes[ 0 ] = curNodes[ 1 ];
+          uniqueNodes[ 1 ] = curNodes[ 4 ];
+          uniqueNodes[ 2 ] = curNodes[ 5 ];
+          uniqueNodes[ 3 ] = curNodes[ 2 ];
+          uniqueNodes[ 4 ] = curNodes[ 0 ];
+          toRemove = false;
+        }
+        if ( curNodes[1] == curNodes[4] )
+        {
+          uniqueNodes[ 0 ] = curNodes[ 0 ];
+          uniqueNodes[ 1 ] = curNodes[ 2 ];
+          uniqueNodes[ 2 ] = curNodes[ 5 ];
+          uniqueNodes[ 3 ] = curNodes[ 3 ];
+          uniqueNodes[ 4 ] = curNodes[ 1 ];
+          toRemove = false;
+        }
+        if ( curNodes[2] == curNodes[5] )
         {
-          isOk = true;
+          uniqueNodes[ 0 ] = curNodes[ 0 ];
+          uniqueNodes[ 1 ] = curNodes[ 3 ];
+          uniqueNodes[ 2 ] = curNodes[ 4 ];
+          uniqueNodes[ 3 ] = curNodes[ 1 ];
+          uniqueNodes[ 4 ] = curNodes[ 2 ];
+          toRemove = false;
         }
-        break;
       }
-      case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
-      {
-        isOk = false;
-        if ( nbUniqueNodes == 4 ) {
-          // ---------------------------------> tetrahedron
-          if ( curNodes[3] == curNodes[4] &&
-               curNodes[3] == curNodes[5] ) {
-            // top nodes stick
-            isOk = true;
-          }
-          else if ( curNodes[0] == curNodes[1] &&
-                    curNodes[0] == curNodes[2] ) {
-            // bottom nodes stick: set a top before
-            uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
-            uniqueNodes[ 0 ] = curNodes [ 5 ];
-            uniqueNodes[ 1 ] = curNodes [ 4 ];
-            uniqueNodes[ 2 ] = curNodes [ 3 ];
-            isOk = true;
-          }
-          else if (( curNodes[0] == curNodes[3] ) +
-                   ( curNodes[1] == curNodes[4] ) +
-                   ( curNodes[2] == curNodes[5] ) == 2 ) {
-            // a lateral face turns into a line
-            isOk = true;
-          }
-        }
-        else if ( nbUniqueNodes == 5 ) {
-          // PENTAHEDRON --------------------> pyramid
-          if ( curNodes[0] == curNodes[3] )
-          {
-            uniqueNodes[ 0 ] = curNodes[ 1 ];
-            uniqueNodes[ 1 ] = curNodes[ 4 ];
-            uniqueNodes[ 2 ] = curNodes[ 5 ];
-            uniqueNodes[ 3 ] = curNodes[ 2 ];
-            uniqueNodes[ 4 ] = curNodes[ 0 ];
-            isOk = true;
-          }
-          if ( curNodes[1] == curNodes[4] )
-          {
-            uniqueNodes[ 0 ] = curNodes[ 0 ];
-            uniqueNodes[ 1 ] = curNodes[ 2 ];
-            uniqueNodes[ 2 ] = curNodes[ 5 ];
-            uniqueNodes[ 3 ] = curNodes[ 3 ];
-            uniqueNodes[ 4 ] = curNodes[ 1 ];
-            isOk = true;
-          }
-          if ( curNodes[2] == curNodes[5] )
-          {
-            uniqueNodes[ 0 ] = curNodes[ 0 ];
-            uniqueNodes[ 1 ] = curNodes[ 3 ];
-            uniqueNodes[ 2 ] = curNodes[ 4 ];
-            uniqueNodes[ 3 ] = curNodes[ 1 ];
-            uniqueNodes[ 4 ] = curNodes[ 2 ];
-            isOk = true;
+      break;
+    }
+    case SMDSEntity_Hexa:
+    {
+      //////////////////////////////////// HEXAHEDRON
+      SMDS_VolumeTool hexa (elem);
+      hexa.SetExternalNormal();
+      if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
+        //////////////////////// HEX ---> tetrahedron
+        for ( int iFace = 0; iFace < 6; iFace++ ) {
+          const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
+          if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
+              curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
+              curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
+            // one face turns into a point ...
+            int  pickInd = ind[ 0 ];
+            int iOppFace = hexa.GetOppFaceIndex( iFace );
+            ind = hexa.GetFaceNodesIndices( iOppFace );
+            int nbStick = 0;
+            uniqueNodes.clear();
+            for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
+              if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
+                nbStick++;
+              else
+                uniqueNodes.push_back( curNodes[ind[ iCur ]]);
+            }
+            if ( nbStick == 1 ) {
+              // ... and the opposite one - into a triangle.
+              // set a top node
+              uniqueNodes.push_back( curNodes[ pickInd ]);
+              toRemove = false;
+            }
+            break;
           }
         }
-        break;
       }
-      case SMDSEntity_Hexa:
-      {
-        //////////////////////////////////// HEXAHEDRON
-        isOk = false;
-        SMDS_VolumeTool hexa (elem);
-        hexa.SetExternalNormal();
-        if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
-          //////////////////////// HEX ---> tetrahedron
-          for ( int iFace = 0; iFace < 6; iFace++ ) {
-            const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
-            if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
-                curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
-                curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
-              // one face turns into a point ...
-              int  pickInd = ind[ 0 ];
-              int iOppFace = hexa.GetOppFaceIndex( iFace );
-              ind = hexa.GetFaceNodesIndices( iOppFace );
-              int nbStick = 0;
-              uniqueNodes.clear();
-              for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
-                if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
-                  nbStick++;
-                else
-                  uniqueNodes.push_back( curNodes[ind[ iCur ]]);
-              }
-              if ( nbStick == 1 ) {
-                // ... and the opposite one - into a triangle.
-                // set a top node
-                uniqueNodes.push_back( curNodes[ pickInd ]);
-                isOk = true;
+      else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
+        //////////////////////// HEX ---> prism
+        int nbTria = 0, iTria[3];
+        const int *ind; // indices of face nodes
+        // look for triangular faces
+        for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
+          ind = hexa.GetFaceNodesIndices( iFace );
+          TIDSortedNodeSet faceNodes;
+          for ( iCur = 0; iCur < 4; iCur++ )
+            faceNodes.insert( curNodes[ind[iCur]] );
+          if ( faceNodes.size() == 3 )
+            iTria[ nbTria++ ] = iFace;
+        }
+        // check if triangles are opposite
+        if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
+        {
+          // set nodes of the bottom triangle
+          ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
+          vector<int> indB;
+          for ( iCur = 0; iCur < 4; iCur++ )
+            if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
+              indB.push_back( ind[iCur] );
+          if ( !hexa.IsForward() )
+            std::swap( indB[0], indB[2] );
+          for ( iCur = 0; iCur < 3; iCur++ )
+            uniqueNodes[ iCur ] = curNodes[indB[iCur]];
+          // set nodes of the top triangle
+          const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
+          for ( iCur = 0; iCur < 3; ++iCur )
+            for ( int j = 0; j < 4; ++j )
+              if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
+              {
+                uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
+                break;
               }
-              break;
-            }
-          }
-        }
-        else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
-          //////////////////////// HEX ---> prism
-          int nbTria = 0, iTria[3];
-          const int *ind; // indices of face nodes
-          // look for triangular faces
-          for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
-            ind = hexa.GetFaceNodesIndices( iFace );
-            TIDSortedNodeSet faceNodes;
-            for ( iCur = 0; iCur < 4; iCur++ )
-              faceNodes.insert( curNodes[ind[iCur]] );
-            if ( faceNodes.size() == 3 )
-              iTria[ nbTria++ ] = iFace;
-          }
-          // check if triangles are opposite
-          if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
-          {
-            // set nodes of the bottom triangle
-            ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
-            vector<int> indB;
-            for ( iCur = 0; iCur < 4; iCur++ )
-              if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
-                indB.push_back( ind[iCur] );
-            if ( !hexa.IsForward() )
-              std::swap( indB[0], indB[2] );
-            for ( iCur = 0; iCur < 3; iCur++ )
-              uniqueNodes[ iCur ] = curNodes[indB[iCur]];
-            // set nodes of the top triangle
-            const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
-            for ( iCur = 0; iCur < 3; ++iCur )
-              for ( int j = 0; j < 4; ++j )
-                if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
-                {
-                  uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
-                  break;
-                }
-            isOk = true;
-            break;
-          }
+          toRemove = false;
+          break;
         }
-        else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
-          //////////////////// HEXAHEDRON ---> pyramid
-          for ( int iFace = 0; iFace < 6; iFace++ ) {
-            const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
-            if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
-                curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
-                curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
-              // one face turns into a point ...
-              int iOppFace = hexa.GetOppFaceIndex( iFace );
-              ind = hexa.GetFaceNodesIndices( iOppFace );
-              uniqueNodes.clear();
-              for ( iCur = 0; iCur < 4; iCur++ ) {
-                if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
-                  break;
-                else
-                  uniqueNodes.push_back( curNodes[ind[ iCur ]]);
-              }
-              if ( uniqueNodes.size() == 4 ) {
-                // ... and the opposite one is a quadrangle
-                // set a top node
-                const int* indTop = hexa.GetFaceNodesIndices( iFace );
-                uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
-                isOk = true;
-              }
-              break;
+      }
+      else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
+        //////////////////// HEXAHEDRON ---> pyramid
+        for ( int iFace = 0; iFace < 6; iFace++ ) {
+          const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
+          if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
+              curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
+              curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
+            // one face turns into a point ...
+            int iOppFace = hexa.GetOppFaceIndex( iFace );
+            ind = hexa.GetFaceNodesIndices( iOppFace );
+            uniqueNodes.clear();
+            for ( iCur = 0; iCur < 4; iCur++ ) {
+              if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
+                break;
+              else
+                uniqueNodes.push_back( curNodes[ind[ iCur ]]);
             }
+            if ( uniqueNodes.size() == 4 ) {
+              // ... and the opposite one is a quadrangle
+              // set a top node
+              const int* indTop = hexa.GetFaceNodesIndices( iFace );
+              uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
+              toRemove = false;
+            }
+            break;
           }
         }
+      }
 
-        if ( !isOk && nbUniqueNodes > 4 ) {
-          ////////////////// HEXAHEDRON ---> polyhedron
-          hexa.SetExternalNormal();
-          vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
-          vector<int>                   quantities; quantities.reserve( 6 );
-          for ( int iFace = 0; iFace < 6; iFace++ )
+      if ( toRemove && nbUniqueNodes > 4 ) {
+        ////////////////// HEXAHEDRON ---> polyhedron
+        hexa.SetExternalNormal();
+        vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
+        vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
+        poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
+        quantities.reserve( 6 );     quantities.clear();
+        for ( int iFace = 0; iFace < 6; iFace++ )
+        {
+          const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
+          if ( curNodes[ind[0]] == curNodes[ind[2]] ||
+               curNodes[ind[1]] == curNodes[ind[3]] )
           {
-            const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
-            if ( curNodes[ind[0]] == curNodes[ind[2]] ||
-                 curNodes[ind[1]] == curNodes[ind[3]] )
-            {
-              quantities.clear();
-              break; // opposite nodes stick
-            }
-            nodeSet.clear();
-            for ( iCur = 0; iCur < 4; iCur++ )
-            {
-              if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
-                poly_nodes.push_back( curNodes[ind[ iCur ]]);
-            }
-            if ( nodeSet.size() < 3 )
-              poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
-            else
-              quantities.push_back( nodeSet.size() );
+            quantities.clear();
+            break; // opposite nodes stick
           }
-          if ( quantities.size() >= 4 )
+          nodeSet.clear();
+          for ( iCur = 0; iCur < 4; iCur++ )
           {
-            const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
-            myLastCreatedElems.Append( newElem );
-            if ( aShapeId && newElem )
-              aMesh->SetMeshElementOnShape( newElem, aShapeId );
-            rmElemIds.push_back( elem->GetID() );
+            if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
+              poly_nodes.push_back( curNodes[ind[ iCur ]]);
           }
+          if ( nodeSet.size() < 3 )
+            poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
+          else
+            quantities.push_back( nodeSet.size() );
         }
-        break;
-      } // case HEXAHEDRON
+        if ( quantities.size() >= 4 )
+        {
+          nbResElems = 1;
+          nbUniqueNodes = poly_nodes.size();
+          newElemDefs[0].SetPoly(true);
+        }
+      }
+      break;
+    } // case HEXAHEDRON
 
-      default:
-        isOk = false;
-      } // switch ( nbNodes )
+    default:
+      toRemove = true;
 
-    } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
+    } // switch ( entity )
 
-    if ( isOk ) // a non-poly elem remains valid after sticking nodes
+    if ( toRemove && nbResElems == 0 && avoidMakingHoles )
     {
-      if ( nbNodes != nbUniqueNodes ||
-           !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
-      {
-        elemType.Init( elem ).SetID( elem->GetID() );
-
-        SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
-        aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
-
-        uniqueNodes.resize(nbUniqueNodes);
-        SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
-        if ( sm && newElem )
-          sm->AddElement( newElem );
-        if ( elem != newElem )
-          ReplaceElemInGroups( elem, newElem, aMesh );
-      }
-    }
-    else {
-      // Remove invalid regular element or invalid polygon
-      rmElemIds.push_back( elem->GetID() );
+      // erase from nodeNodeMap nodes whose merge spoils elem
+      vector< const SMDS_MeshNode* > noMergeNodes;
+      SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
+      for ( size_t i = 0; i < noMergeNodes.size(); ++i )
+        nodeNodeMap.erase( noMergeNodes[i] );
     }
+    
+  } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
 
-  } // loop on elements
+  uniqueNodes.resize( nbUniqueNodes );
 
-  // Remove bad elements, then equal nodes (order important)
+  if ( !toRemove && nbResElems == 0 )
+    nbResElems = 1;
 
-  Remove( rmElemIds, false );
-  Remove( rmNodeIds, true );
+  newElemDefs.resize( nbResElems );
 
-  return;
+  return !toRemove;
 }
 
 
@@ -7815,33 +7979,29 @@ void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
   typedef map< SortableElement, int > TMapOfNodeSet;
   typedef list<int> TGroupOfElems;
 
-  if ( theElements.empty() )
-  { // get all elements in the mesh
-    SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
-    while ( eIt->more() )
-      theElements.insert( theElements.end(), eIt->next() );
-  }
+  SMDS_ElemIteratorPtr elemIt;
+  if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
+  else                       elemIt = elemSetIterator( theElements );
 
   vector< TGroupOfElems > arrayOfGroups;
   TGroupOfElems groupOfElems;
   TMapOfNodeSet mapOfNodeSet;
 
-  TIDSortedElemSet::iterator elemIt = theElements.begin();
-  for ( int i = 0; elemIt != theElements.end(); ++elemIt )
+  for ( int iGroup = 0; elemIt->more(); )
   {
-    const SMDS_MeshElement* curElem = *elemIt;
+    const SMDS_MeshElement* curElem = elemIt->next();
     SortableElement SE(curElem);
     // check uniqueness
-    pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
+    pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, iGroup));
     if ( !pp.second ) { // one more coincident elem
       TMapOfNodeSet::iterator& itSE = pp.first;
-      int ind = (*itSE).second;
-      arrayOfGroups[ind].push_back( curElem->GetID() );
+      int iG = itSE->second;
+      arrayOfGroups[ iG ].push_back( curElem->GetID() );
     }
     else {
       arrayOfGroups.push_back( groupOfElems );
       arrayOfGroups.back().push_back( curElem->GetID() );
-      i++;
+      iGroup++;
     }
   }
 
@@ -8120,7 +8280,6 @@ SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  MESSAGE("::SewFreeBorder()");
   Sew_Error aResult = SEW_OK;
 
   // ====================================
@@ -8165,7 +8324,7 @@ SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
     // -------------------------------------------------------------------------
 
     // 1. Since sewing may break if there are volumes to split on the side 2,
-    //    we wont move nodes but just compute new coordinates for them
+    //    we won't move nodes but just compute new coordinates for them
     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
     TNodeXYZMap nBordXYZ;
     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
@@ -8228,7 +8387,7 @@ SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
     //const SMDS_MeshNode* faceNodes[ 4 ];
 
     const SMDS_MeshNode*    sideNode;
-    const SMDS_MeshElement* sideElem;
+    const SMDS_MeshElement* sideElem  = 0;
     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
     nBordIt = bordNodes.begin();
@@ -8253,7 +8412,7 @@ SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
       {
         const SMDS_MeshElement* elem = invElemIt->next();
         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
-        int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
+        int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
         bool isVolume = volume.Set( elem );
         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
@@ -8329,7 +8488,7 @@ SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
       } // loop on inverse elements of prevSideNode
 
       if ( !sideNode ) {
-        MESSAGE(" Cant find path by links of the Side 2 ");
+        MESSAGE(" Can't find path by links of the Side 2 ");
         return SEW_BAD_SIDE_NODES;
       }
       sideNodes.push_back( sideNode );
@@ -8801,7 +8960,7 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElemen
     }
     // decide how to split a quadrangle: compare possible variants
     // and choose which of splits to be a quadrangle
-    int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
+    int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
     if ( nbFaceNodes == 3 ) {
       iBestQuad = nbSplits;
       i4 = i3;
@@ -9065,6 +9224,7 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
                                              SMESH_MesherHelper& theHelper,
                                              const bool          theForce3d)
 {
+  //MESSAGE("convertElemToQuadratic");
   int nbElem = 0;
   if( !theSm ) return nbElem;
 
@@ -9090,18 +9250,20 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
       case SMDSEntity_Quad_Triangle:
       case SMDSEntity_Quad_Quadrangle:
       case SMDSEntity_Quad_Hexa:
+      case SMDSEntity_Quad_Penta:
         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
 
       case SMDSEntity_BiQuad_Triangle:
       case SMDSEntity_BiQuad_Quadrangle:
       case SMDSEntity_TriQuad_Hexa:
+      case SMDSEntity_BiQuad_Penta:
         alreadyOK = theHelper.GetIsBiQuadratic();
         hasCentralNodes = true;
         break;
       default:
         alreadyOK = true;
       }
-      // take into account already present modium nodes
+      // take into account already present medium nodes
       switch ( aType ) {
       case SMDSAbs_Volume:
         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
@@ -9127,7 +9289,7 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
     // remove a linear element
     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
 
-    // remove central nodes of biquadratic elements (biquad->quad convertion)
+    // remove central nodes of biquadratic elements (biquad->quad conversion)
     if ( hasCentralNodes )
       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
         if ( nodes[i]->NbInverseElements() == 0 )
@@ -9168,6 +9330,8 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
           break;
         case SMDSEntity_Penta:
+        case SMDSEntity_Quad_Penta:
+        case SMDSEntity_BiQuad_Penta:
           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
           break;
         case SMDSEntity_Hexa:
@@ -9198,6 +9362,7 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
 
 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
 {
+  //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
   SMESHDS_Mesh* meshDS = GetMeshDS();
 
   SMESH_MesherHelper aHelper(*myMesh);
@@ -9325,6 +9490,8 @@ void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theT
         {
         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
+        case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
+        case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
         default:                      alreadyOK = true;
         }
         if ( alreadyOK )
@@ -9362,8 +9529,13 @@ void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theT
                                       nodes[3], nodes[4], id, theForce3d);
         break;
       case SMDSEntity_Penta:
+      case SMDSEntity_Quad_Penta:
+      case SMDSEntity_BiQuad_Penta:
         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
+        for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
+          if ( nodes[i]->NbInverseElements() == 0 )
+            GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
         break;
       case SMDSEntity_Hexagonal_Prism:
       default:
@@ -9757,7 +9929,6 @@ SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
   myLastCreatedElems.Clear();
   myLastCreatedNodes.Clear();
 
-  MESSAGE ("::::SewSideElements()");
   if ( theSide1.size() != theSide2.size() )
     return SEW_DIFF_NB_OF_ELEMENTS;
 
@@ -9776,7 +9947,7 @@ SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
   //    face does not exist
 
   SMESHDS_Mesh* aMesh = GetMeshDS();
-  // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
+  // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
   TIDSortedElemSet             faceSet1, faceSet2;
   set<const SMDS_MeshElement*> volSet1,  volSet2;
@@ -10421,191 +10592,738 @@ SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
   return SEW_OK;
 }
 
-//================================================================================
-/*!
- * \brief Create elements equal (on same nodes) to given ones
- *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
- *              elements of the uppest dimension are duplicated.
- */
-//================================================================================
-
-void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
+namespace // automatically find theAffectedElems for DoubleNodes()
 {
-  ClearLastCreated();
-  SMESHDS_Mesh* mesh = GetMeshDS();
-
-  // get an element type and an iterator over elements
+  bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
 
-  SMDSAbs_ElementType type;
-  SMDS_ElemIteratorPtr elemIt;
-  vector< const SMDS_MeshElement* > allElems;
-  if ( theElements.empty() )
+  //--------------------------------------------------------------------------------
+  // Nodes shared by adjacent FissureBorder's.
+  // 1 node  if FissureBorder separates faces
+  // 2 nodes if FissureBorder separates volumes
+  struct SubBorder
   {
-    if ( mesh->NbNodes() == 0 )
-      return;
-    // get most complex type
-    SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
-      SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
-      SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
-    };
-    for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
-      if ( mesh->GetMeshInfo().NbElements( types[i] ))
+    const SMDS_MeshNode* _nodes[2];
+    int                  _nbNodes;
+
+    SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
+    {
+      _nodes[0] = n1;
+      _nodes[1] = n2;
+      _nbNodes = bool( n1 ) + bool( n2 );
+      if ( _nbNodes == 2 && n1 > n2 )
+        std::swap( _nodes[0], _nodes[1] );
+    }
+    bool operator<( const SubBorder& other ) const
+    {
+      for ( int i = 0; i < _nbNodes; ++i )
       {
-        type = types[i];
-        break;
+        if ( _nodes[i] < other._nodes[i] ) return true;
+        if ( _nodes[i] > other._nodes[i] ) return false;
       }
-    // put all elements in the vector <allElems>
-    allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
-    elemIt = mesh->elementsIterator( type );
-    while ( elemIt->more() )
-      allElems.push_back( elemIt->next());
-    elemIt = elemSetIterator( allElems );
-  }
-  else
+      return false;
+    }
+  };
+
+  //--------------------------------------------------------------------------------
+  // Map a SubBorder to all FissureBorder it bounds
+  struct FissureBorder;
+  typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
+  typedef TBorderLinks::iterator                               TMappedSub;
+
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Element border (volume facet or face edge) at a fissure
+   */
+  struct FissureBorder
   {
-    type = (*theElements.begin())->GetType();
-    elemIt = elemSetIterator( theElements );
-  }
+    std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
+    const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
 
-  // duplicate elements
+    std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
+    std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
 
-  ElemFeatures elemType;
+    FissureBorder( FissureBorder && from ) // move constructor
+    {
+      std::swap( _nodes,       from._nodes );
+      std::swap( _sortedNodes, from._sortedNodes );
+      _elems[0] = from._elems[0];
+      _elems[1] = from._elems[1];
+    }
 
-  vector< const SMDS_MeshNode* > nodes;
-  while ( elemIt->more() )
-  {
-    const SMDS_MeshElement* elem = elemIt->next();
-    if ( elem->GetType() != type )
-      continue;
+    FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
+                   std::vector< const SMDS_MeshElement* > & adjElems)
+      : _nodes( elemToDuplicate->NbCornerNodes() )
+    {
+      for ( size_t i = 0; i < _nodes.size(); ++i )
+        _nodes[i] = elemToDuplicate->GetNode( i );
 
-    elemType.Init( elem, /*basicOnly=*/false );
-    nodes.assign( elem->begin_nodes(), elem->end_nodes() );
+      SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
+      findAdjacent( type, adjElems );
+    }
 
-    AddElement( nodes, elemType );
-  }
-}
+    FissureBorder( const SMDS_MeshNode**                    nodes,
+                   const size_t                             nbNodes,
+                   const SMDSAbs_ElementType                adjElemsType,
+                   std::vector< const SMDS_MeshElement* > & adjElems)
+      : _nodes( nodes, nodes + nbNodes )
+    {
+      findAdjacent( adjElemsType, adjElems );
+    }
 
-//================================================================================
-/*!
-  \brief Creates a hole in a mesh by doubling the nodes of some particular elements
-  \param theElems - the list of elements (edges or faces) to be replicated
-  The nodes for duplication could be found from these elements
-  \param theNodesNot - list of nodes to NOT replicate
-  \param theAffectedElems - the list of elements (cells and edges) to which the
-  replicated nodes should be associated to.
-  \return TRUE if operation has been completed successfully, FALSE otherwise
-*/
-//================================================================================
+    void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
+                       std::vector< const SMDS_MeshElement* > & adjElems)
+    {
+      _elems[0] = _elems[1] = 0;
+      adjElems.clear();
+      if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
+        for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
+          _elems[i] = adjElems[i];
+    }
 
-bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
-                                    const TIDSortedElemSet& theNodesNot,
-                                    const TIDSortedElemSet& theAffectedElems )
-{
-  myLastCreatedElems.Clear();
-  myLastCreatedNodes.Clear();
+    bool operator<( const FissureBorder& other ) const
+    {
+      return GetSortedNodes() < other.GetSortedNodes();
+    }
 
-  if ( theElems.size() == 0 )
-    return false;
+    const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
+    {
+      if ( _sortedNodes.empty() && !_nodes.empty() )
+      {
+        FissureBorder* me = const_cast<FissureBorder*>( this );
+        me->_sortedNodes = me->_nodes;
+        std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
+      }
+      return _sortedNodes;
+    }
 
-  SMESHDS_Mesh* aMeshDS = GetMeshDS();
-  if ( !aMeshDS )
-    return false;
+    size_t NbSub() const
+    {
+      return _nodes.size();
+    }
 
-  bool res = false;
-  TNodeNodeMap anOldNodeToNewNode;
-  // duplicate elements and nodes
-  res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
-  // replce nodes by duplications
-  res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
-  return res;
-}
+    SubBorder Sub(size_t i) const
+    {
+      return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
+    }
 
-//================================================================================
-/*!
-  \brief Creates a hole in a mesh by doubling the nodes of some particular elements
-  \param theMeshDS - mesh instance
-  \param theElems - the elements replicated or modified (nodes should be changed)
-  \param theNodesNot - nodes to NOT replicate
-  \param theNodeNodeMap - relation of old node to new created node
-  \param theIsDoubleElem - flag os to replicate element or modify
-  \return TRUE if operation has been completed successfully, FALSE otherwise
-*/
-//================================================================================
+    void AddSelfTo( TBorderLinks& borderLinks )
+    {
+      _mappedSubs.resize( NbSub() );
+      for ( size_t i = 0; i < NbSub(); ++i )
+      {
+        TBorderLinks::iterator s2b =
+          borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
+        s2b->second.push_back( this );
+        _mappedSubs[ i ] = s2b;
+      }
+    }
 
-bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
-                                   const TIDSortedElemSet& theElems,
-                                   const TIDSortedElemSet& theNodesNot,
-                                   TNodeNodeMap&           theNodeNodeMap,
-                                   const bool              theIsDoubleElem )
-{
-  MESSAGE("doubleNodes");
-  // iterate through element and duplicate them (by nodes duplication)
-  bool res = false;
-  std::vector<const SMDS_MeshNode*> newNodes;
-  ElemFeatures elemType;
+    void Clear()
+    {
+      _nodes.clear();
+    }
 
-  TIDSortedElemSet::const_iterator elemItr = theElems.begin();
-  for ( ;  elemItr != theElems.end(); ++elemItr )
-  {
-    const SMDS_MeshElement* anElem = *elemItr;
-    if (!anElem)
-      continue;
+    const SMDS_MeshElement* GetMarkedElem() const
+    {
+      if ( _nodes.empty() ) return 0; // cleared
+      if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
+      if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
+      return 0;
+    }
 
-    // duplicate nodes to duplicate element
-    bool isDuplicate = false;
-    newNodes.resize( anElem->NbNodes() );
-    SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
-    int ind = 0;
-    while ( anIter->more() )
+    gp_XYZ GetNorm() const // normal to the border
     {
-      const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
-      const SMDS_MeshNode*  aNewNode = aCurrNode;
-      TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
-      if ( n2n != theNodeNodeMap.end() )
+      gp_XYZ norm;
+      if ( _nodes.size() == 2 )
       {
-        aNewNode = n2n->second;
+        gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
+        if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
+          avgNorm += norm;
+        if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
+          avgNorm += norm;
+
+        gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
+        norm = bordDir ^ avgNorm;
       }
-      else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
+      else
       {
-        // duplicate node
-        aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
-        copyPosition( aCurrNode, aNewNode );
-        theNodeNodeMap[ aCurrNode ] = aNewNode;
-        myLastCreatedNodes.Append( aNewNode );
+        SMESH_NodeXYZ p0( _nodes[0] );
+        SMESH_NodeXYZ p1( _nodes[1] );
+        SMESH_NodeXYZ p2( _nodes[2] );
+        norm = ( p0 - p1 ) ^ ( p2 - p1 );
       }
-      isDuplicate |= (aCurrNode != aNewNode);
-      newNodes[ ind++ ] = aNewNode;
+      if ( isOut( _nodes[0], norm, GetMarkedElem() ))
+        norm.Reverse();
+
+      return norm;
     }
-    if ( !isDuplicate )
-      continue;
 
-    if ( theIsDoubleElem )
-      AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
-    else
-      theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
+    void ChooseSide() // mark an _elem located at positive side of fissure
+    {
+      _elems[0]->setIsMarked( true );
+      gp_XYZ norm = GetNorm();
+      double maxX = norm.Coord(1);
+      if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
+      if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
+      if ( maxX < 0 )
+      {
+        _elems[0]->setIsMarked( false );
+        _elems[1]->setIsMarked( true );
+      }
+    }
 
-    res = true;
-  }
-  return res;
-}
+  }; // struct FissureBorder
 
-//================================================================================
-/*!
-  \brief Creates a hole in a mesh by doubling the nodes of some particular elements
-  \param theNodes - identifiers of nodes to be doubled
-  \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
-  nodes. If list of element identifiers is empty then nodes are doubled but
-  they not assigned to elements
-  \return TRUE if operation has been completed successfully, FALSE otherwise
-*/
-//================================================================================
+  //--------------------------------------------------------------------------------
+  /*!
+   * \brief Classifier of elements at fissure edge
+   */
+  class FissureNormal
+  {
+    std::vector< gp_XYZ > _normals;
+    bool                  _bothIn;
 
-bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
-                                    const std::list< int >& theListOfModifiedElems )
+  public:
+    void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
+    {
+      _bothIn = false;
+      _normals.reserve(2);
+      _normals.push_back( bord.GetNorm() );
+      if ( _normals.size() == 2 )
+        _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
+    }
+
+    bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
+    {
+      bool isIn = false;
+      switch ( _normals.size() ) {
+      case 1:
+      {
+        isIn = !isOut( n, _normals[0], elem );
+        break;
+      }
+      case 2:
+      {
+        bool in1 = !isOut( n, _normals[0], elem );
+        bool in2 = !isOut( n, _normals[1], elem );
+        isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
+      }
+      }
+      return isIn;
+    }
+  };
+
+  //================================================================================
+  /*!
+   * \brief Classify an element by a plane passing through a node
+   */
+  //================================================================================
+
+  bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
+  {
+    SMESH_NodeXYZ p = n;
+    double sumDot = 0;
+    for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
+    {
+      SMESH_NodeXYZ pi = elem->GetNode( i );
+      sumDot += norm * ( pi - p );
+    }
+    return sumDot < -1e-100;
+  }
+
+  //================================================================================
+  /*!
+   * \brief Find FissureBorder's by nodes to duplicate
+   */
+  //================================================================================
+
+  void findFissureBorders( const TIDSortedElemSet&        theNodes,
+                           std::vector< FissureBorder > & theFissureBorders )
+  {
+    TIDSortedElemSet::const_iterator nIt = theNodes.begin();
+    const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
+    if ( !n ) return;
+    SMDSAbs_ElementType elemType = SMDSAbs_Volume;
+    if ( n->NbInverseElements( elemType ) == 0 )
+    {
+      elemType = SMDSAbs_Face;
+      if ( n->NbInverseElements( elemType ) == 0 )
+        return;
+    }
+    // unmark elements touching the fissure
+    for ( ; nIt != theNodes.end(); ++nIt )
+      SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
+
+    // loop on elements touching the fissure to get their borders belonging to the fissure
+    std::set< FissureBorder >              fissureBorders;
+    std::vector< const SMDS_MeshElement* > adjElems;
+    std::vector< const SMDS_MeshNode* >    nodes;
+    SMDS_VolumeTool volTool;
+    for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
+    {
+      SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
+      while ( invIt->more() )
+      {
+        const SMDS_MeshElement* eInv = invIt->next();
+        if ( eInv->isMarked() ) continue;
+        eInv->setIsMarked( true );
+
+        if ( elemType == SMDSAbs_Volume )
+        {
+          volTool.Set( eInv );
+          int iQuad = eInv->IsQuadratic() ? 2 : 1;
+          for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
+          {
+            const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
+            int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
+            nodes.clear();
+            bool allOnFissure = true;
+            for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
+              if (( allOnFissure = theNodes.count( nn[ iN ])))
+                nodes.push_back( nn[ iN ]);
+            if ( allOnFissure )
+              fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
+                                                               elemType, adjElems )));
+          }
+        }
+        else // elemType == SMDSAbs_Face
+        {
+          const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
+          bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
+          for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
+          {
+            nn[1]      = eInv->GetNode( iN );
+            onFissure1 = theNodes.count( nn[1] );
+            if ( onFissure0 && onFissure1 )
+              fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
+            nn[0]      = nn[1];
+            onFissure0 = onFissure1;
+          }
+        }
+      }
+    }
+
+    theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
+    std::set< FissureBorder >::iterator bord = fissureBorders.begin();
+    for ( ; bord != fissureBorders.end(); ++bord )
+    {
+      theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
+    }
+    return;
+  } // findFissureBorders()
+
+  //================================================================================
+  /*!
+   * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
+   *  \param [in] theElemsOrNodes - elements or nodes to duplicate
+   *  \param [in] theNodesNot - nodes not to duplicate
+   *  \param [out] theAffectedElems - the found elements
+   */
+  //================================================================================
+
+  void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
+                          TIDSortedElemSet&       theAffectedElems)
+  {
+    if ( theElemsOrNodes.empty() ) return;
+
+    // find FissureBorder's
+
+    std::vector< FissureBorder >           fissure;
+    std::vector< const SMDS_MeshElement* > elemsByFacet;
+
+    TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
+    if ( (*elIt)->GetType() == SMDSAbs_Node )
+    {
+      findFissureBorders( theElemsOrNodes, fissure );
+    }
+    else
+    {
+      fissure.reserve( theElemsOrNodes.size() );
+      for ( ; elIt != theElemsOrNodes.end(); ++elIt )
+        fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
+    }
+    if ( fissure.empty() )
+      return;
+
+    // fill borderLinks
+
+    TBorderLinks borderLinks;
+
+    for ( size_t i = 0; i < fissure.size(); ++i )
+    {
+      fissure[i].AddSelfTo( borderLinks );
+    }
+
+    // get theAffectedElems
+
+    // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
+    for ( size_t i = 0; i < fissure.size(); ++i )
+      for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
+      {
+        SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
+                                        false, /*markElem=*/true );
+      }
+
+    std::vector<const SMDS_MeshNode *>                 facetNodes;
+    std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
+    boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
+
+    // choose a side of fissure
+    fissure[0].ChooseSide();
+    theAffectedElems.insert( fissure[0].GetMarkedElem() );
+
+    size_t nbCheckedBorders = 0;
+    while ( nbCheckedBorders < fissure.size() )
+    {
+      // find a FissureBorder to treat
+      FissureBorder* bord = 0;
+      for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
+        if ( fissure[i].GetMarkedElem() )
+          bord = & fissure[i];
+      for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
+        if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
+        {
+          bord = & fissure[i];
+          bord->ChooseSide();
+          theAffectedElems.insert( bord->GetMarkedElem() );
+        }
+      if ( !bord ) return;
+      ++nbCheckedBorders;
+
+      // treat FissureBorder's linked to bord
+      fissureNodes.clear();
+      fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
+      for ( size_t i = 0; i < bord->NbSub(); ++i )
+      {
+        TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
+        if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
+        std::vector< FissureBorder* >& linkedBorders = l2b->second;
+        const SubBorder&                          sb = l2b->first;
+        const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
+
+        if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
+        {
+          for ( int j = 0; j < sb._nbNodes; ++j )
+            fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
+          continue;
+        }
+
+        // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
+        // until an elem adjacent to a neighbour FissureBorder is found
+        facetNodes.clear();
+        facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
+        facetNodes.resize( sb._nbNodes + 1 );
+
+        while ( bordElem )
+        {
+          // check if bordElem is adjacent to a neighbour FissureBorder
+          for ( size_t j = 0; j < linkedBorders.size(); ++j )
+          {
+            FissureBorder* bord2 = linkedBorders[j];
+            if ( bord2 == bord ) continue;
+            if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
+              bordElem = 0;
+            else
+              fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
+          }
+          if ( !bordElem )
+            break;
+
+          // find the next bordElem
+          const SMDS_MeshElement* nextBordElem = 0;
+          for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
+          {
+            const SMDS_MeshNode* n = bordElem->GetNode( iN );
+            if ( fissureNodes.count( n )) continue;
+
+            facetNodes[ sb._nbNodes ] = n;
+            elemsByFacet.clear();
+            if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
+            {
+              for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
+                if ( elemsByFacet[ iE ] != bordElem &&
+                     !elemsByFacet[ iE ]->isMarked() )
+                {
+                  theAffectedElems.insert( elemsByFacet[ iE ]);
+                  elemsByFacet[ iE ]->setIsMarked( true );
+                  if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
+                    nextBordElem = elemsByFacet[ iE ];
+                }
+            }
+          }
+          bordElem = nextBordElem;
+
+        } // while ( bordElem )
+
+        linkedBorders.clear(); // not to treat this link any more
+
+      } // loop on SubBorder's of a FissureBorder
+
+      bord->Clear();
+
+    } // loop on FissureBorder's
+
+
+    // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
+
+    // mark nodes of theAffectedElems
+    SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
+
+    // unmark nodes of the fissure
+    elIt = theElemsOrNodes.begin();
+    if ( (*elIt)->GetType() == SMDSAbs_Node )
+      SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
+    else
+      SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
+
+    std::vector< gp_XYZ > normVec;
+
+    // loop on nodes of the fissure, add elements having marked nodes
+    for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
+    {
+      const SMDS_MeshElement* e = (*elIt);
+      if ( e->GetType() != SMDSAbs_Node )
+        e->setIsMarked( true ); // avoid adding a fissure element
+
+      for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
+      {
+        const SMDS_MeshNode* n = e->GetNode( iN );
+        if ( fissEdgeNodes2Norm.count( n ))
+          continue;
+
+        SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
+        while ( invIt->more() )
+        {
+          const SMDS_MeshElement* eInv = invIt->next();
+          if ( eInv->isMarked() ) continue;
+          eInv->setIsMarked( true );
+
+          SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
+          while( nIt->more() )
+            if ( nIt->next()->isMarked())
+            {
+              theAffectedElems.insert( eInv );
+              SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
+              n->setIsMarked( false );
+              break;
+            }
+        }
+      }
+    }
+
+    // add elements on the fissure edge
+    std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
+    for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
+    {
+      const SMDS_MeshNode* edgeNode = n2N->first;
+      const FissureNormal & normals = n2N->second;
+
+      SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
+      while ( invIt->more() )
+      {
+        const SMDS_MeshElement* eInv = invIt->next();
+        if ( eInv->isMarked() ) continue;
+        eInv->setIsMarked( true );
+
+        // classify eInv using normals
+        bool toAdd = normals.IsIn( edgeNode, eInv );
+        if ( toAdd ) // check if all nodes lie on the fissure edge
+        {
+          bool notOnEdge = false;
+          for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
+            notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
+          toAdd = notOnEdge;
+        }
+        if ( toAdd )
+        {
+          theAffectedElems.insert( eInv );
+        }
+      }
+    }
+
+    return;
+  } // findAffectedElems()
+} // namespace
+
+//================================================================================
+/*!
+ * \brief Create elements equal (on same nodes) to given ones
+ *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
+ *              elements of the uppest dimension are duplicated.
+ */
+//================================================================================
+
+void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
+{
+  ClearLastCreated();
+  SMESHDS_Mesh* mesh = GetMeshDS();
+
+  // get an element type and an iterator over elements
+
+  SMDSAbs_ElementType type = SMDSAbs_All;
+  SMDS_ElemIteratorPtr elemIt;
+  if ( theElements.empty() )
+  {
+    if ( mesh->NbNodes() == 0 )
+      return;
+    // get most complex type
+    SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
+      SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
+      SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
+    };
+    for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
+      if ( mesh->GetMeshInfo().NbElements( types[i] ))
+      {
+        type = types[i];
+        break;
+      }
+    elemIt = mesh->elementsIterator( type );
+  }
+  else
+  {
+    type = (*theElements.begin())->GetType();
+    elemIt = elemSetIterator( theElements );
+  }
+
+  // un-mark all elements to avoid duplicating just created elements
+  SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
+
+  // duplicate elements
+
+  ElemFeatures elemType;
+
+  vector< const SMDS_MeshNode* > nodes;
+  while ( elemIt->more() )
+  {
+    const SMDS_MeshElement* elem = elemIt->next();
+    if ( elem->GetType() != type || elem->isMarked() )
+      continue;
+
+    elemType.Init( elem, /*basicOnly=*/false );
+    nodes.assign( elem->begin_nodes(), elem->end_nodes() );
+
+    if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
+      newElem->setIsMarked( true );
+  }
+}
+
+//================================================================================
+/*!
+  \brief Creates a hole in a mesh by doubling the nodes of some particular elements
+  \param theElems - the list of elements (edges or faces) to be replicated
+  The nodes for duplication could be found from these elements
+  \param theNodesNot - list of nodes to NOT replicate
+  \param theAffectedElems - the list of elements (cells and edges) to which the
+  replicated nodes should be associated to.
+  \return TRUE if operation has been completed successfully, FALSE otherwise
+*/
+//================================================================================
+
+bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
+                                    const TIDSortedElemSet& theNodesNot,
+                                    const TIDSortedElemSet& theAffectedElems )
+{
+  ClearLastCreated();
+
+  if ( theElems.size() == 0 )
+    return false;
+
+  SMESHDS_Mesh* aMeshDS = GetMeshDS();
+  if ( !aMeshDS )
+    return false;
+
+  bool res = false;
+  TNodeNodeMap anOldNodeToNewNode;
+  // duplicate elements and nodes
+  res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
+  // replce nodes by duplications
+  res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
+  return res;
+}
+
+//================================================================================
+/*!
+  \brief Creates a hole in a mesh by doubling the nodes of some particular elements
+  \param theMeshDS - mesh instance
+  \param theElems - the elements replicated or modified (nodes should be changed)
+  \param theNodesNot - nodes to NOT replicate
+  \param theNodeNodeMap - relation of old node to new created node
+  \param theIsDoubleElem - flag os to replicate element or modify
+  \return TRUE if operation has been completed successfully, FALSE otherwise
+*/
+//================================================================================
+
+bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
+                                   const TIDSortedElemSet& theElems,
+                                   const TIDSortedElemSet& theNodesNot,
+                                   TNodeNodeMap&           theNodeNodeMap,
+                                   const bool              theIsDoubleElem )
+{
+  // iterate through element and duplicate them (by nodes duplication)
+  bool res = false;
+  std::vector<const SMDS_MeshNode*> newNodes;
+  ElemFeatures elemType;
+
+  TIDSortedElemSet::const_iterator elemItr = theElems.begin();
+  for ( ;  elemItr != theElems.end(); ++elemItr )
+  {
+    const SMDS_MeshElement* anElem = *elemItr;
+    // if (!anElem)
+    //   continue;
+
+    // duplicate nodes to duplicate element
+    bool isDuplicate = false;
+    newNodes.resize( anElem->NbNodes() );
+    SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
+    int ind = 0;
+    while ( anIter->more() )
+    {
+      const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
+      const SMDS_MeshNode*  aNewNode = aCurrNode;
+      TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
+      if ( n2n != theNodeNodeMap.end() )
+      {
+        aNewNode = n2n->second;
+      }
+      else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
+      {
+        // duplicate node
+        aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
+        copyPosition( aCurrNode, aNewNode );
+        theNodeNodeMap[ aCurrNode ] = aNewNode;
+        myLastCreatedNodes.Append( aNewNode );
+      }
+      isDuplicate |= (aCurrNode != aNewNode);
+      newNodes[ ind++ ] = aNewNode;
+    }
+    if ( !isDuplicate )
+      continue;
+
+    if ( theIsDoubleElem )
+      AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
+    else
+      theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
+
+    res = true;
+  }
+  return res;
+}
+
+//================================================================================
+/*!
+  \brief Creates a hole in a mesh by doubling the nodes of some particular elements
+  \param theNodes - identifiers of nodes to be doubled
+  \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
+  nodes. If list of element identifiers is empty then nodes are doubled but
+  they not assigned to elements
+  \return TRUE if operation has been completed successfully, FALSE otherwise
+*/
+//================================================================================
+
+bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
+                                    const std::list< int >& theListOfModifiedElems )
 {
-  MESSAGE("DoubleNodes");
-  myLastCreatedElems.Clear();
-  myLastCreatedNodes.Clear();
+  ClearLastCreated();
 
   if ( theListOfNodes.size() == 0 )
     return false;
@@ -10621,8 +11339,7 @@ bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
   std::list< int >::const_iterator aNodeIter;
   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
   {
-    int aCurr = *aNodeIter;
-    SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
+    const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
     if ( !aNode )
       continue;
 
@@ -10637,50 +11354,28 @@ bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
     }
   }
 
-  // Create map of new nodes for modified elements
+  // Change nodes of elements
 
-  std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
+  std::vector<const SMDS_MeshNode*> aNodeArr;
 
   std::list< int >::const_iterator anElemIter;
-  for ( anElemIter = theListOfModifiedElems.begin();
-        anElemIter != theListOfModifiedElems.end(); ++anElemIter )
+  for ( anElemIter =  theListOfModifiedElems.begin();
+        anElemIter != theListOfModifiedElems.end();
+        anElemIter++ )
   {
-    int aCurr = *anElemIter;
-    SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
+    const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
     if ( !anElem )
       continue;
 
-    vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
-
-    SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
-    int ind = 0;
-    while ( anIter->more() )
+    aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
+    for( size_t i = 0; i < aNodeArr.size(); ++i )
     {
-      SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
-      if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
-      {
-        const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
-        aNodeArr[ ind++ ] = aNewNode;
-      }
-      else
-        aNodeArr[ ind++ ] = aCurrNode;
+      std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
+        anOldNodeToNewNode.find( aNodeArr[ i ]);
+      if ( n2n != anOldNodeToNewNode.end() )
+        aNodeArr[ i ] = n2n->second;
     }
-    anElemToNodes[ anElem ] = aNodeArr;
-  }
-
-  // Change nodes of elements
-
-  std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
-    anElemToNodesIter = anElemToNodes.begin();
-  for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
-  {
-    const SMDS_MeshElement* anElem = anElemToNodesIter->first;
-    vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
-    if ( anElem )
-      {
-      MESSAGE("ChangeElementNodes");
-      aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
-      }
+    aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
   }
 
   return true;
@@ -10702,8 +11397,8 @@ namespace {
   {
     gp_XYZ centerXYZ (0, 0, 0);
     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
-    while (aNodeItr->more())
-      centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
+    while ( aNodeItr->more() )
+      centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
 
     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
     theClassifier.Perform(aPnt, theTol);
@@ -10758,9 +11453,9 @@ namespace {
          (select elements with a gravity center on the side given by faces normals).
          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
          The replicated nodes should be associated to affected elements.
-  \return groups of affected elements
+  \return true
   \sa DoubleNodeElemGroupsInRegion()
- */
+*/
 //================================================================================
 
 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
@@ -10770,101 +11465,7 @@ bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theEl
 {
   if ( theShape.IsNull() )
   {
-    std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
-    std::set<const SMDS_MeshElement*> alreadyCheckedElems;
-    std::set<const SMDS_MeshElement*> edgesToCheck;
-    alreadyCheckedNodes.clear();
-    alreadyCheckedElems.clear();
-    edgesToCheck.clear();
-
-    // --- iterates on elements to be replicated and get elements by back references from their nodes
-
-    TIDSortedElemSet::const_iterator elemItr = theElems.begin();
-    int ielem;
-    for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
-    {
-      SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
-      if (!anElem || (anElem->GetType() != SMDSAbs_Face))
-        continue;
-      gp_XYZ normal;
-      SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
-      MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
-      std::set<const SMDS_MeshNode*> nodesElem;
-      nodesElem.clear();
-      SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
-      while ( nodeItr->more() )
-      {
-        const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
-        nodesElem.insert(aNode);
-      }
-      std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
-      for (; nodit != nodesElem.end(); nodit++)
-      {
-        MESSAGE("  noeud ");
-        const SMDS_MeshNode* aNode = *nodit;
-        if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
-          continue;
-        if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
-          continue;
-        alreadyCheckedNodes.insert(aNode);
-        SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
-        while ( backElemItr->more() )
-        {
-          MESSAGE("    backelem ");
-          const SMDS_MeshElement* curElem = backElemItr->next();
-          if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
-            continue;
-          if (theElems.find(curElem) != theElems.end())
-            continue;
-          alreadyCheckedElems.insert(curElem);
-          double x=0, y=0, z=0;
-          int nb = 0;
-          SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
-          while ( nodeItr2->more() )
-          {
-            const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
-            x += anotherNode->X();
-            y += anotherNode->Y();
-            z += anotherNode->Z();
-            nb++;
-          }
-          gp_XYZ p;
-          p.SetCoord( x/nb -aNode->X(),
-                      y/nb -aNode->Y(),
-                      z/nb -aNode->Z() );
-          MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
-          if (normal*p > 0)
-          {
-            MESSAGE("    --- inserted")
-            theAffectedElems.insert( curElem );
-          }
-          else if (curElem->GetType() == SMDSAbs_Edge)
-            edgesToCheck.insert(curElem);
-        }
-      }
-    }
-    // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
-    std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
-    for( ; eit != edgesToCheck.end(); eit++)
-    {
-      bool onside = true;
-      const SMDS_MeshElement* anEdge = *eit;
-      SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
-      while ( nodeItr->more() )
-      {
-        const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
-        if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
-        {
-          onside = false;
-          break;
-        }
-      }
-      if (onside)
-      {
-        MESSAGE("    --- edge onside inserted")
-        theAffectedElems.insert(anEdge);
-      }
-    }
+    findAffectedElems( theElems, theAffectedElems );
   }
   else
   {
@@ -10883,29 +11484,23 @@ bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theEl
 
     // iterates on indicated elements and get elements by back references from their nodes
     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
-    int ielem;
-    for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
+    for ( ;  elemItr != theElems.end(); ++elemItr )
     {
-      MESSAGE("element " << ielem++);
-      SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
-      if (!anElem)
-        continue;
+      SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
       while ( nodeItr->more() )
       {
-        MESSAGE("  noeud ");
         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
           continue;
         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
         while ( backElemItr->more() )
         {
-          MESSAGE("    backelem ");
           const SMDS_MeshElement* curElem = backElemItr->next();
           if ( curElem && theElems.find(curElem) == theElems.end() &&
-              ( bsc3d.get() ?
-                isInside( curElem, *bsc3d, aTol ) :
-                isInside( curElem, *aFaceClassifier, aTol )))
+               ( bsc3d.get() ?
+                 isInside( curElem, *bsc3d, aTol ) :
+                 isInside( curElem, *aFaceClassifier, aTol )))
             theAffectedElems.insert( curElem );
         }
       }
@@ -10986,10 +11581,6 @@ bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
  */
 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
 {
-//  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
-//  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
-//  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
-//  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
   gp_Vec vref(p0, p1);
   gp_Vec v1(p0, g1);
   gp_Vec v2(p0, g2);
@@ -11024,9 +11615,9 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
                                                      bool                                 createJointElems,
                                                      bool                                 onAllBoundaries)
 {
-  MESSAGE("----------------------------------------------");
-  MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
-  MESSAGE("----------------------------------------------");
+  // MESSAGE("----------------------------------------------");
+  // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
+  // MESSAGE("----------------------------------------------");
 
   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
   meshDS->BuildDownWardConnectivity(true);
@@ -11049,7 +11640,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
   std::set<int> emptySet;
   emptyMap.clear();
 
-  MESSAGE(".. Number of domains :"<<theElems.size());
+  //MESSAGE(".. Number of domains :"<<theElems.size());
 
   TIDSortedElemSet theRestDomElems;
   const int iRestDom  = -1;
@@ -11089,7 +11680,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
     //     and corresponding volume of this domain, for each shared face.
     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
 
-    MESSAGE("... Neighbors of domain #" << idom);
+    //MESSAGE("... Neighbors of domain #" << idom);
     const TIDSortedElemSet& domain = theElems[idom];
     TIDSortedElemSet::const_iterator elemItr = domain.begin();
     for (; elemItr != domain.end(); ++elemItr)
@@ -11200,7 +11791,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
 
-  MESSAGE(".. Duplication of the nodes");
+  //MESSAGE(".. Duplication of the nodes");
   for (int idomain = idom0; idomain < nbDomains; idomain++)
   {
     itface = faceDomains.begin();
@@ -11261,7 +11852,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
     }
   }
 
-  MESSAGE(".. Creation of elements");
+  //MESSAGE(".. Creation of elements");
   for (int idomain = idom0; idomain < nbDomains; idomain++)
   {
     itface = faceDomains.begin();
@@ -11392,7 +11983,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
   std::map<int, std::map<long,int> > nodeQuadDomains;
   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
 
-  MESSAGE(".. Creation of elements: simple junction");
+  //MESSAGE(".. Creation of elements: simple junction");
   if (createJointElems)
   {
     int idg;
@@ -11443,7 +12034,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
   //     iterate on mutipleNodesToFace
   //     iterate on edgesMultiDomains
 
-  MESSAGE(".. Creation of elements: multiple junction");
+  //MESSAGE(".. Creation of elements: multiple junction");
   if (createJointElems)
   {
     // --- iterate on mutipleNodesToFace
@@ -11516,7 +12107,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
   faceOrEdgeDom.clear();
   feDom.clear();
 
-  MESSAGE(".. Modification of elements");
+  //MESSAGE(".. Modification of elements");
   for (int idomain = idom0; idomain < nbDomains; idomain++)
   {
     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
@@ -11599,7 +12190,7 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
   }
 
   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
-  grid->BuildLinks();
+  grid->DeleteLinks();
 
   CHRONOSTOP(50);
   counters::stats();
@@ -11618,9 +12209,9 @@ bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSorted
  */
 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
 {
-  MESSAGE("-------------------------------------------------");
-  MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
-  MESSAGE("-------------------------------------------------");
+  // MESSAGE("-------------------------------------------------");
+  // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
+  // MESSAGE("-------------------------------------------------");
 
   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
 
@@ -11782,9 +12373,9 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
                                       std::vector<double>&            nodesCoords,
                                       std::vector<std::vector<int> >& listOfListOfNodes)
 {
-  MESSAGE("--------------------------------");
-  MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
-  MESSAGE("--------------------------------");
+  // MESSAGE("--------------------------------");
+  // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
+  // MESSAGE("--------------------------------");
 
   // --- zone of volumes to remove is given :
   //     1 either by a geom shape (one or more vertices) and a radius,
@@ -11886,7 +12477,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
 
   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
   {
-    MESSAGE("group of nodes provided");
+    //MESSAGE("group of nodes provided");
     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
     while ( elemIt->more() )
     {
@@ -11908,7 +12499,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
   }
   else if (isNodeCoords)
   {
-    MESSAGE("list of nodes coordinates provided");
+    //MESSAGE("list of nodes coordinates provided");
     size_t i = 0;
     int k = 0;
     while ( i < nodesCoords.size()-2 )
@@ -11918,13 +12509,13 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
       double z = nodesCoords[i++];
       gp_Pnt p = gp_Pnt(x, y ,z);
       gpnts.push_back(p);
-      MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
+      //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
       k++;
     }
   }
   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
   {
-    MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
+    //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
     TopTools_IndexedMapOfShape vertexMap;
     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
     gp_Pnt p = gp_Pnt(0,0,0);
@@ -11936,20 +12527,17 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
       p = BRep_Tool::Pnt(vertex);
       gpnts.push_back(p);
-      MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
+      //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
     }
   }
 
   if (gpnts.size() > 0)
   {
-    int nodeId = 0;
     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
-    if (startNode)
-      nodeId = startNode->GetID();
-    MESSAGE("nodeId " << nodeId);
+    //MESSAGE("startNode->nodeId " << nodeId);
 
     double radius2 = radius*radius;
-    MESSAGE("radius2 " << radius2);
+    //MESSAGE("radius2 " << radius2);
 
     // --- volumes on start node
 
@@ -11978,7 +12566,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
     {
       std::set<int>::iterator it = setOfVolToCheck.begin();
       int vtkId = *it;
-      MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
+      //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
       bool volInside = false;
       vtkIdType npts = 0;
       vtkIdType* pts = 0;
@@ -11989,7 +12577,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
         if (mapOfNodeDistance2.count(pts[i]))
         {
           distance2 = mapOfNodeDistance2[pts[i]];
-          MESSAGE("point " << pts[i] << " distance2 " << distance2);
+          //MESSAGE("point " << pts[i] << " distance2 " << distance2);
         }
         else
         {
@@ -12007,7 +12595,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
             }
           }
           mapOfNodeDistance2[pts[i]] = distance2;
-          MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
+          //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
         }
         if (distance2 < radius2)
         {
@@ -12019,7 +12607,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
       if (volInside)
       {
         setOfInsideVol.insert(vtkId);
-        MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
+        //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
         int neighborsVtkIds[NBMAXNEIGHBORS];
         int downIds[NBMAXNEIGHBORS];
         unsigned char downTypes[NBMAXNEIGHBORS];
@@ -12031,7 +12619,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
       else
       {
         setOfOutsideVol.insert(vtkId);
-        MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
+        //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
       }
       setOfVolToCheck.erase(vtkId);
     }
@@ -12044,7 +12632,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
   std::set<int> setOfVolToReCheck;
   while (addedInside)
   {
-    MESSAGE(" --------------------------- re check");
+    //MESSAGE(" --------------------------- re check");
     addedInside = false;
     std::set<int>::iterator itv = setOfInsideVol.begin();
     for (; itv != setOfInsideVol.end(); ++itv)
@@ -12066,7 +12654,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
       int vtkId = *it;
       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
       {
-        MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
+        //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
         int countInside = 0;
         int neighborsVtkIds[NBMAXNEIGHBORS];
         int downIds[NBMAXNEIGHBORS];
@@ -12075,10 +12663,10 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
         for (int n = 0; n < nbNeighbors; n++)
           if (setOfInsideVol.count(neighborsVtkIds[n]))
             countInside++;
-        MESSAGE("countInside " << countInside);
+        //MESSAGE("countInside " << countInside);
         if (countInside > 1)
         {
-          MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
+          //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
           setOfInsideVol.insert(vtkId);
           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
           addedInside = true;
@@ -12175,7 +12763,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
   {
     int shapeId = itShape->first;
-    MESSAGE(" --- Shape ID --- "<< shapeId);
+    //MESSAGE(" --- Shape ID --- "<< shapeId);
     shapeIdToEdges[shapeId] = emptyEdges;
 
     std::vector<int> nodesEdges;
@@ -12184,7 +12772,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
     for (; its != itShape->second.end(); ++its)
     {
       int vtkId = *its;
-      MESSAGE("     " << vtkId);
+      //MESSAGE("     " << vtkId);
       int neighborsVtkIds[NBMAXNEIGHBORS];
       int downIds[NBMAXNEIGHBORS];
       unsigned char downTypes[NBMAXNEIGHBORS];
@@ -12205,7 +12793,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
             nodesEdges.push_back(vtkNodeId[0]);
             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
-            MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
+            //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
           }
         }
       }
@@ -12215,9 +12803,9 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
     order.clear();
     if (nodesEdges.size() > 0)
     {
-      order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
+      order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
       nodesEdges[0] = -1;
-      order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
+      order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
       nodesEdges[1] = -1; // do not reuse this edge
       bool found = true;
       while (found)
@@ -12236,7 +12824,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
               found = false;
             else
             {
-              order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
+              order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
               nodesEdges[i-1] = -1;
             }
           else // even ==> use the next one
@@ -12244,7 +12832,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
               found = false;
             else
             {
-              order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
+              order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
               nodesEdges[i+1] = -1;
             }
         }
@@ -12266,7 +12854,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
             found = false;
           else
           {
-            order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
+            order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
             nodesEdges[i-1] = -1;
           }
         else // even ==> use the next one
@@ -12274,7 +12862,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
             found = false;
           else
           {
-            order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
+            order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
             nodesEdges[i+1] = -1;
           }
       }
@@ -12287,7 +12875,7 @@ void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
     for (; itl != order.end(); itl++)
     {
       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
-      MESSAGE("              ordered node " << nodes[nodes.size()-1]);
+      //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
     }
     listOfListOfNodes.push_back(nodes);
   }
@@ -12689,3 +13277,482 @@ void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
   default:;
   }
 }
+
+namespace // utils for MakePolyLine
+{
+  //================================================================================
+  /*!
+   * \brief Sequence of found points and a current point data
+   */
+  struct Path
+  {
+    std::vector< gp_XYZ >   myPoints;
+    double                  myLength;
+
+    int                     mySrcPntInd; //!< start point index
+    const SMDS_MeshElement* myFace;
+    SMESH_NodeXYZ           myNode1;
+    SMESH_NodeXYZ           myNode2;
+    int                     myNodeInd1;
+    int                     myNodeInd2;
+    double                  myDot1;
+    double                  myDot2;
+    TIDSortedElemSet        myElemSet, myAvoidSet;
+
+    Path(): myLength(0.0), myFace(0) {}
+
+    bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
+                         const SMDS_MeshElement* face,
+                         const gp_XYZ&           plnNorm,
+                         const gp_XYZ&           plnOrig );
+
+    void AddPoint( const gp_XYZ& p );
+
+    bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
+
+    bool ReachSamePoint( const Path& other );
+
+    static void Remove( std::vector< Path > & paths, size_t& i );
+  };
+
+  //================================================================================
+  /*!
+   * \brief Return true if this Path meats another
+   */
+  //================================================================================
+
+  bool Path::ReachSamePoint( const Path& other )
+  {
+    return ( mySrcPntInd != other.mySrcPntInd &&
+             myFace == other.myFace );
+  }
+
+  //================================================================================
+  /*!
+   * \brief Remove a path from a vector
+   */
+  //================================================================================
+
+  void Path::Remove( std::vector< Path > & paths, size_t& i )
+  {
+    if ( paths.size() > 1 )
+    {
+      size_t j = paths.size() - 1; // last item to be removed
+      if ( i < j )
+      {
+        paths[ i ].myPoints.swap( paths[ j ].myPoints );
+        paths[ i ].myLength    = paths[ j ].myLength;
+        paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
+        paths[ i ].myFace      = paths[ j ].myFace;
+        paths[ i ].myNode1     = paths[ j ].myNode1;
+        paths[ i ].myNode2     = paths[ j ].myNode2;
+        paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
+        paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
+        paths[ i ].myDot1      = paths[ j ].myDot1;
+        paths[ i ].myDot2      = paths[ j ].myDot2;
+      }
+    }
+    paths.pop_back();
+    if ( i > 0 )
+      --i;
+  }
+
+  //================================================================================
+  /*!
+   * \brief Store a point that is at a node of a face if the face is intersected by plane.
+   *        Return false if the node is a sole intersection point of the face and the plane
+   */
+  //================================================================================
+
+  bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
+                             const SMDS_MeshElement* face,
+                             const gp_XYZ&           plnNorm,
+                             const gp_XYZ&           plnOrig )
+  {
+    if ( face == myFace )
+      return false;
+    myNodeInd1 = face->GetNodeIndex( cornerNode._node );
+    myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
+    int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
+    myNode1.Set( face->GetNode( ind3 ));
+    myNode2.Set( face->GetNode( myNodeInd2 ));
+
+    myDot1 = plnNorm * ( myNode1 - plnOrig );
+    myDot2 = plnNorm * ( myNode2 - plnOrig );
+
+    bool ok = ( myDot1 * myDot2 < 0 );
+    if ( !ok && myDot1 * myDot2 == 0 )
+    {
+      ok = ( myDot1 != myDot2 );
+      if ( ok && myFace )
+        ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
+    }
+    if ( ok )
+    {
+      myFace = face;
+      myDot1 = 0;
+      AddPoint( cornerNode );
+    }
+    return ok;
+  }
+
+  //================================================================================
+  /*!
+   * \brief Store a point and update myLength
+   */
+  //================================================================================
+
+  void Path::AddPoint( const gp_XYZ& p )
+  {
+    if ( !myPoints.empty() )
+      myLength += ( p - myPoints.back() ).Modulus();
+    else
+      myLength = 0;
+    myPoints.push_back( p );
+  }
+
+  //================================================================================
+  /*!
+   * \brief Try to find the next point
+   *  \param [in] plnNorm - cutting plane normal
+   *  \param [in] plnOrig - cutting plane origin
+   */
+  //================================================================================
+
+  bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
+  {
+    int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
+    if ( myNodeInd2 == nodeInd3 )
+      nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
+
+    SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
+    double         dot3 = plnNorm * ( node3 - plnOrig );
+
+    if ( dot3 * myDot1 < 0. )
+    {
+      myNode2    = node3;
+      myNodeInd2 = nodeInd3;
+      myDot2     = dot3;
+    }
+    else if ( dot3 * myDot2 < 0. )
+    {
+      myNode1    = node3;
+      myNodeInd1 = nodeInd3;
+      myDot1     = dot3;
+    }
+    else if ( dot3 == 0. )
+    {
+      SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
+      while ( fIt->more() )
+        if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
+          return true;
+      return false;
+    }
+    else if ( myDot2 == 0. )
+    {
+      SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
+      SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
+      while ( fIt->more() )
+        if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
+          return true;
+      return false;
+    }
+
+    double r = Abs( myDot1 / ( myDot2 - myDot1 ));
+    AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
+
+    myAvoidSet.clear();
+    myAvoidSet.insert( myFace );
+    myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
+                                             myElemSet,   myAvoidSet,
+                                             &myNodeInd1, &myNodeInd2 );
+    return myFace;
+  }
+
+  //================================================================================
+  /*!
+   * \brief Compute a path between two points of PolySegment
+   */
+  struct PolyPathCompute
+  {
+    SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
+    std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
+    SMESH_Mesh*                            myMesh;
+    mutable std::vector< std::string >     myErrors;
+
+    PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
+                     std::vector< Path >&                   thePaths,
+                     SMESH_Mesh*                            theMesh):
+      mySegments( theSegments ),
+      myPaths( thePaths ),
+      myMesh( theMesh ),
+      myErrors( theSegments.size() )
+    {
+    }
+#undef SMESH_CAUGHT
+#define SMESH_CAUGHT myErrors[i] =
+    void operator() ( const int i ) const
+    {
+      SMESH_TRY;
+      const_cast< PolyPathCompute* >( this )->Compute( i );
+      SMESH_CATCH( SMESH::returnError );
+    }
+#undef SMESH_CAUGHT
+    //================================================================================
+    /*!
+     * \brief Compute a path of a given segment
+     */
+    //================================================================================
+
+    void Compute( const int iSeg )
+    {
+      SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
+
+      // get a cutting plane
+
+      gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
+      gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
+      if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
+      if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
+
+      gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
+      gp_XYZ plnOrig = p2;
+
+      // find paths connecting the 2 end points of polySeg
+
+      std::vector< Path > paths; paths.reserve(10);
+
+      // initialize paths
+
+      for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
+      {
+        Path path;
+        path.mySrcPntInd = iP;
+        size_t nbPaths = paths.size();
+
+        if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
+        {
+          while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
+                                                                 polySeg.myNode2[ iP ],
+                                                                 path.myElemSet,
+                                                                 path.myAvoidSet,
+                                                                 &path.myNodeInd1,
+                                                                 &path.myNodeInd2 )))
+          {
+            path.myNode1.Set( polySeg.myNode1[ iP ]);
+            path.myNode2.Set( polySeg.myNode2[ iP ]);
+            path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
+            path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
+            path.myPoints.clear();
+            path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
+            path.myAvoidSet.insert( path.myFace );
+            paths.push_back( path );
+          }
+          if ( nbPaths == paths.size() )
+            throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
+                                     << " in a PolySegment " << iSeg );
+        }
+        else // an end point is at node
+        {
+          std::set<const SMDS_MeshNode* > nodes;
+          SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
+          while ( fIt->more() )
+          {
+            path.myPoints.clear();
+            if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
+            {
+              if (( path.myDot1 * path.myDot2 != 0 ) ||
+                  ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
+                paths.push_back( path );
+            }
+          }
+        }
+
+        // look for a one-segment path
+        for ( size_t i = 0; i < nbPaths; ++i )
+          for ( size_t j = nbPaths; j < paths.size(); ++j )
+            if ( paths[i].myFace == paths[j].myFace )
+            {
+              myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
+              myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
+              paths.clear();
+            }
+      }
+
+      // extend paths
+
+      myPaths[ iSeg ].myLength = 1e100;
+
+      while ( paths.size() >= 2 )
+      {
+        for ( size_t i = 0; i < paths.size(); ++i )
+        {
+          Path& path = paths[ i ];
+          if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
+               path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
+          {
+            Path::Remove( paths, i );
+            continue;
+          }
+
+          // join paths that reach same point
+          for ( size_t j = 0; j < paths.size(); ++j )
+          {
+            if ( i != j && paths[i].ReachSamePoint( paths[j] ))
+            {
+              double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
+              double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
+              if ( fullLength < myPaths[ iSeg ].myLength )
+              {
+                myPaths[ iSeg ].myLength = fullLength;
+                std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
+                allPoints.swap( paths[i].myPoints );
+                allPoints.insert( allPoints.end(),
+                                  paths[j].myPoints.rbegin(),
+                                  paths[j].myPoints.rend() );
+              }
+              Path::Remove( paths, i );
+              Path::Remove( paths, j );
+            }
+          }
+        }
+        if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
+          throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
+      }
+
+      if ( myPaths[ iSeg ].myPoints.empty() )
+        throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
+
+    } // PolyPathCompute::Compute()
+
+  }; // struct PolyPathCompute
+
+} // namespace
+
+//=======================================================================
+//function : MakePolyLine
+//purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
+//           the initial mesh
+//=======================================================================
+
+void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
+                                     SMESHDS_Group*         theGroup,
+                                     SMESH_ElementSearcher* theSearcher)
+{
+  std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
+
+  SMESH_ElementSearcher* searcher = theSearcher;
+  SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
+  if ( !searcher )
+  {
+    searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
+    delSearcher._obj = searcher;
+  }
+
+  // get cutting planes
+
+  std::vector< bool > isVectorOK( theSegments.size(), true );
+  const double planarCoef = 0.333; // plane height in planar case
+
+  for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
+  {
+    PolySegment& polySeg = theSegments[ iSeg ];
+
+    gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
+    gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
+    if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
+    if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
+
+    gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
+
+    isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
+    if ( !isVectorOK[ iSeg ])
+    {
+      gp_XYZ pMid = 0.5 * ( p1 + p2 );
+      const SMDS_MeshElement* face;
+      polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
+      polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
+
+      gp_XYZ faceNorm;
+      SMESH_MeshAlgos::FaceNormal( face, faceNorm );
+
+      if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
+           polySeg.myVector * faceNorm  < Precision::Confusion() )
+      {
+        polySeg.myVector = faceNorm;
+        polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
+      }
+    }
+    else
+    {
+      polySeg.myVector = plnNorm ^ ( p1 - p2 );
+    }
+  }
+
+  // assure that inverse elements are constructed, avoid their concurrent building in threads
+  GetMeshDS()->nodesIterator()->next()->NbInverseElements();
+
+  // find paths
+
+  PolyPathCompute algo( theSegments, segPaths, myMesh );
+  OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
+
+  for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
+    if ( !algo.myErrors[ iSeg ].empty() )
+      throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
+
+  // create an 1D mesh
+
+  const SMDS_MeshNode *n, *nPrev = 0;
+  SMESHDS_Mesh* mesh = GetMeshDS();
+
+  for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
+  {
+    const Path& path = segPaths[iSeg];
+    if ( path.myPoints.size() < 2 )
+      continue;
+
+    double tol = path.myLength / path.myPoints.size() / 1000.;
+    if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
+    {
+      nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
+      myLastCreatedNodes.Append( nPrev );
+    }
+    for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
+    {
+      n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
+      myLastCreatedNodes.Append( n );
+
+      const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
+      myLastCreatedElems.Append( elem );
+      if ( theGroup )
+        theGroup->Add( elem );
+
+      nPrev = n;
+    }
+
+    // return a vector
+
+    gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
+    if ( isVectorOK[ iSeg ])
+    {
+      // find the most distance point of a path
+      double maxDist = 0;
+      for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
+      {
+        double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
+        if ( dist > maxDist )
+        {
+          maxDist = dist;
+          theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
+        }
+      }
+      if ( maxDist < Precision::Confusion() ) // planar case
+        theSegments[iSeg].myMidProjPoint =
+          pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
+    }
+    theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
+  }
+
+  return;
+}