Salome HOME
Fix regression of doc/salome/examples/transforming_meshes_ex11.py
[modules/smesh.git] / src / SMESH_I / SMESH_MeshEditor_i.cxx
index f31e583ebe935941469bc286e6a5755fa6469fd7..d7afbed39f5ec69c7c73eefbbf905161daf70672 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2015  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -64,7 +64,6 @@
 #include <Utils_CorbaException.hxx>
 #include <SALOMEDS_wrap.hxx>
 #include <SALOME_GenericObj_i.hh>
-#include <Basics_OCCTVersion.hxx>
 
 #include <BRepAdaptor_Surface.hxx>
 #include <BRep_Tool.hxx>
 #include <gp_Ax2.hxx>
 #include <gp_Vec.hxx>
 
-#if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
-#define NO_CAS_CATCH
-#endif
-
 #include <Standard_Failure.hxx>
-
-#ifdef NO_CAS_CATCH
 #include <Standard_ErrorHandler.hxx>
-#endif
 
 #include <sstream>
 #include <limits>
@@ -173,7 +165,10 @@ namespace MeshEditor_I {
     }
     void Remove( SMDSAbs_ElementType type )
     {
-      SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator( type );
+      Remove( GetMeshDS()->elementsIterator( type ));
+    }
+    void Remove( SMDS_ElemIteratorPtr eIt )
+    {
       while ( eIt->more() )
         GetMeshDS()->RemoveFreeElement( eIt->next(), /*sm=*/0, /*fromGroups=*/false );
     }
@@ -184,7 +179,7 @@ namespace MeshEditor_I {
 
   //=============================================================================
   /*!
-   * \brief Deleter of theNodeSearcher at any compute event occured
+   * \brief Deleter of theNodeSearcher at any compute event occurred
    */
   //=============================================================================
 
@@ -467,9 +462,9 @@ void SMESH_MeshEditor_i::initData(bool deleteSearchers)
 //================================================================================
 /*!
  * \brief Increment mesh modif time and optionally record that the performed
- *        modification may influence futher mesh re-compute.
+ *        modification may influence further mesh re-compute.
  *  \param [in] isReComputeSafe - true if the modification does not influence
- *              futher mesh re-compute
+ *              further mesh re-compute
  */
 //================================================================================
 
@@ -501,7 +496,7 @@ void SMESH_MeshEditor_i::declareMeshModified( bool isReComputeSafe )
  * \brief Initialize and return myPreviewMesh
  *  \param previewElements - type of elements to show in preview
  *
- *  WARNING: call it once par a method!
+ *  WARNING: call it once per method!
  */
 //================================================================================
 
@@ -530,14 +525,14 @@ SMESH::MeshPreviewStruct* SMESH_MeshEditor_i::GetPreviewData()
   SMESH_TRY;
   const bool hasBadElems = ( getEditor().GetError() && getEditor().GetError()->HasBadElems() );
 
-  if ( myIsPreviewMode || hasBadElems ) { // --- MeshPreviewStruct filling ---
-
+  if ( myIsPreviewMode || hasBadElems )
+  {
     list<int> aNodesConnectivity;
     typedef map<int, int> TNodesMap;
     TNodesMap nodesMap;
 
     SMESHDS_Mesh* aMeshDS;
-    std::auto_ptr< SMESH_MeshPartDS > aMeshPartDS;
+    std::unique_ptr< SMESH_MeshPartDS > aMeshPartDS;
     if ( hasBadElems ) {
       aMeshPartDS.reset( new SMESH_MeshPartDS( getEditor().GetError()->myBadElements ));
       aMeshDS = aMeshPartDS.get();
@@ -623,9 +618,9 @@ SMESH::long_array* SMESH_MeshEditor_i::GetLastCreatedNodes()
   SMESH::long_array_var myLastCreatedNodes = new SMESH::long_array();
 
   const SMESH_SequenceOfElemPtr& aSeq = getEditor().GetLastCreatedNodes();
-  myLastCreatedNodes->length( aSeq.Length() );
-  for (int i = 1; i <= aSeq.Length(); i++)
-    myLastCreatedNodes[i-1] = aSeq.Value(i)->GetID();
+  myLastCreatedNodes->length( aSeq.size() );
+  for ( size_t i = 0; i < aSeq.size(); i++)
+    myLastCreatedNodes[i] = aSeq[i]->GetID();
 
   return myLastCreatedNodes._retn();
   SMESH_CATCH( SMESH::throwCorbaException );
@@ -646,9 +641,9 @@ SMESH::long_array* SMESH_MeshEditor_i::GetLastCreatedElems()
   SMESH::long_array_var myLastCreatedElems = new SMESH::long_array();
 
   const SMESH_SequenceOfElemPtr& aSeq = getEditor().GetLastCreatedElems();
-  myLastCreatedElems->length( aSeq.Length() );
-  for ( int i = 1; i <= aSeq.Length(); i++ )
-    myLastCreatedElems[i-1] = aSeq.Value(i)->GetID();
+  myLastCreatedElems->length( aSeq.size() );
+  for ( size_t i = 0; i < aSeq.size(); i++ )
+    myLastCreatedElems[i] = aSeq[i]->GetID();
 
   return myLastCreatedElems._retn();
   SMESH_CATCH( SMESH::throwCorbaException );
@@ -669,7 +664,7 @@ void SMESH_MeshEditor_i::ClearLastCreated() throw (SALOME::SALOME_Exception)
 
 //=======================================================================
 /*
- * Returns description of an error/warning occured during the last operation
+ * Returns description of an error/warning occurred during the last operation
  * WARNING: ComputeError.code >= 100 and no corresponding enum in IDL API
  */
 //=======================================================================
@@ -903,14 +898,19 @@ CORBA::Long SMESH_MeshEditor_i::AddNode(CORBA::Double x,CORBA::Double y, CORBA::
  */
 //=============================================================================
 
-CORBA::Long SMESH_MeshEditor_i::Add0DElement(CORBA::Long IDOfNode)
+CORBA::Long SMESH_MeshEditor_i::Add0DElement(CORBA::Long    IDOfNode,
+                                             CORBA::Boolean DuplicateElements)
   throw (SALOME::SALOME_Exception)
 {
   SMESH_TRY;
   initData();
 
   const SMDS_MeshNode* aNode = getMeshDS()->FindNode(IDOfNode);
-  SMDS_MeshElement* elem = getMeshDS()->Add0DElement(aNode);
+  SMDS_ElemIteratorPtr it0D = aNode->GetInverseElementIterator( SMDSAbs_0DElement );
+  
+  SMDS_MeshElement* elem = 0;
+  if ( DuplicateElements || !it0D->more() )
+    elem = getMeshDS()->Add0DElement(aNode);
 
   // Update Python script
   TPythonDump() << "elem0d = " << this << ".Add0DElement( " << IDOfNode <<" )";
@@ -1146,6 +1146,10 @@ CORBA::Long SMESH_MeshEditor_i::AddVolume(const SMESH::long_array & IDsOfNodes)
                                         n[8],n[9],n[10],n[11],n[12],n[13],n[14],
                                         n[15],n[16],n[17],n[18],n[19]);
     break;
+  case 18:elem = getMeshDS()->AddVolume(n[0],n[1],n[2],n[3],n[4],n[5],n[6],n[7],
+                                        n[8],n[9],n[10],n[11],n[12],n[13],n[14],
+                                        n[15],n[16],n[17]);
+    break;
   case 27:elem = getMeshDS()->AddVolume(n[0],n[1],n[2],n[3],n[4],n[5],n[6],n[7],
                                         n[8],n[9],n[10],n[11],n[12],n[13],n[14],
                                         n[15],n[16],n[17],n[18],n[19],
@@ -1243,11 +1247,11 @@ CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolumeByFaces (const SMESH::long_ar
 
 //=============================================================================
 //
-// \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 theObject object on whose nodes 0D elements will be created.
 //  \param theGroupName optional name of a group to add 0D elements created
 //         and/or found on nodes of \a theObject.
+//  \param DuplicateElements to add one more 0D element to a node or not.
 //  \return an object (a new group or a temporary SMESH_IDSource) holding
 //          ids of new and/or found 0D elements.
 //
@@ -1255,7 +1259,8 @@ CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolumeByFaces (const SMESH::long_ar
 
 SMESH::SMESH_IDSource_ptr
 SMESH_MeshEditor_i::Create0DElementsOnAllNodes(SMESH::SMESH_IDSource_ptr theObject,
-                                               const char*               theGroupName)
+                                               const char*               theGroupName,
+                                               CORBA::Boolean            theDuplicateElements)
   throw (SALOME::SALOME_Exception)
 {
   SMESH_TRY;
@@ -1266,7 +1271,7 @@ SMESH_MeshEditor_i::Create0DElementsOnAllNodes(SMESH::SMESH_IDSource_ptr theObje
 
   TIDSortedElemSet elements, elems0D;
   if ( idSourceToSet( theObject, getMeshDS(), elements, SMDSAbs_All, /*emptyIfIsMesh=*/1))
-    getEditor().Create0DElementsOnAllNodes( elements, elems0D );
+    getEditor().Create0DElementsOnAllNodes( elements, elems0D, theDuplicateElements );
 
   SMESH::long_array_var newElems = new SMESH::long_array;
   newElems->length( elems0D.size() );
@@ -2011,7 +2016,7 @@ CORBA::Boolean SMESH_MeshEditor_i::SplitQuadObject (SMESH::SMESH_IDSource_ptr th
 //=============================================================================
 /*!
  * Find better splitting of the given quadrangle.
- *  \param IDOfQuad  ID of the quadrangle to be splitted.
+ *  \param IDOfQuad  ID of the quadrangle to be split.
  *  \param Criterion A criterion to choose a diagonal for splitting.
  *  \return 1 if 1-3 diagonal is better, 2 if 2-4
  *          diagonal is better, 0 if error occurs.
@@ -2484,6 +2489,7 @@ namespace MeshEditor_I
     bool myIsExtrusionByNormal;
 
     static int makeFlags( CORBA::Boolean MakeGroups,
+                          CORBA::Boolean LinearVariation = false,
                           CORBA::Boolean ByAverageNormal = false,
                           CORBA::Boolean UseInputElemsOnly = false,
                           CORBA::Long    Flags = 0,
@@ -2492,18 +2498,24 @@ namespace MeshEditor_I
       if ( MakeGroups       ) Flags |= ::SMESH_MeshEditor::EXTRUSION_FLAG_GROUPS;
       if ( ByAverageNormal  ) Flags |= ::SMESH_MeshEditor::EXTRUSION_FLAG_BY_AVG_NORMAL;
       if ( UseInputElemsOnly) Flags |= ::SMESH_MeshEditor::EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY;
+      if ( LinearVariation  ) Flags |= ::SMESH_MeshEditor::EXTRUSION_FLAG_SCALE_LINEAR_VARIATION;
       if ( MakeBoundary     ) Flags |= ::SMESH_MeshEditor::EXTRUSION_FLAG_BOUNDARY;
       return Flags;
     }
     // standard params
-    ExtrusionParams(const SMESH::DirStruct &  theDir,
-                    CORBA::Long               theNbOfSteps,
-                    CORBA::Boolean            theMakeGroups):
+    ExtrusionParams(const SMESH::DirStruct &    theDir,
+                    CORBA::Long                 theNbOfSteps,
+                    const SMESH::double_array & theScaleFactors,
+                    CORBA::Boolean              theLinearVariation,
+                    const SMESH::double_array & theBasePoint,
+                    CORBA::Boolean              theMakeGroups):
       ::SMESH_MeshEditor::ExtrusParam ( gp_Vec( theDir.PS.x,
                                                 theDir.PS.y,
                                                 theDir.PS.z ),
                                         theNbOfSteps,
-                                        makeFlags( theMakeGroups )),
+                                        toList( theScaleFactors ),
+                                        TBasePoint( theBasePoint ),
+                                        makeFlags( theMakeGroups, theLinearVariation )),
       myIsExtrusionByNormal( false )
     {
     }
@@ -2517,7 +2529,9 @@ namespace MeshEditor_I
                                                 theDir.PS.y,
                                                 theDir.PS.z ),
                                         theNbOfSteps,
-                                        makeFlags( theMakeGroups, false, false,
+                                        std::list<double>(),
+                                        0,
+                                        makeFlags( theMakeGroups, false, false, false,
                                                    theExtrFlags, false ),
                                         theSewTolerance ),
       myIsExtrusionByNormal( false )
@@ -2532,7 +2546,7 @@ namespace MeshEditor_I
                     CORBA::Boolean theMakeGroups ):
       ::SMESH_MeshEditor::ExtrusParam ( theStepSize, 
                                         theNbOfSteps,
-                                        makeFlags( theMakeGroups,
+                                        makeFlags( theMakeGroups, false,
                                                    theByAverageNormal, theUseInputElemsOnly ),
                                         theDim),
       myIsExtrusionByNormal( true )
@@ -2543,6 +2557,32 @@ namespace MeshEditor_I
     {
       Flags() &= ~(::SMESH_MeshEditor::EXTRUSION_FLAG_GROUPS);
     }
+
+  private:
+
+    static std::list<double> toList( const SMESH::double_array & theScaleFactors )
+    {
+      std::list<double> scales;
+      for ( CORBA::ULong i = 0; i < theScaleFactors.length(); ++i )
+        scales.push_back( theScaleFactors[i] );
+      return scales;
+    }
+
+    // structure used to convert SMESH::double_array to gp_XYZ*
+    struct TBasePoint
+    {
+      gp_XYZ *pp, p;
+      TBasePoint( const SMESH::double_array & theBasePoint )
+      {
+        pp = 0;
+        if ( theBasePoint.length() == 3 )
+        {
+          p.SetCoord( theBasePoint[0], theBasePoint[1], theBasePoint[2] );
+          pp = &p;
+        }
+      }
+      operator const gp_XYZ*() const { return pp; }
+    };
   };
 }
 
@@ -2566,13 +2606,17 @@ SMESH_MeshEditor_i::ExtrusionSweepObjects(const SMESH::ListOfIDSources & theNode
                                           const SMESH::ListOfIDSources & theFaces,
                                           const SMESH::DirStruct &       theStepVector,
                                           CORBA::Long                    theNbOfSteps,
+                                          const SMESH::double_array &    theScaleFactors,
+                                          CORBA::Boolean                 theLinearVariation,
+                                          const SMESH::double_array &    theBasePoint,
                                           CORBA::Boolean                 theToMakeGroups)
   throw (SALOME::SALOME_Exception)
 {
   SMESH_TRY;
   initData();
 
-  ExtrusionParams params( theStepVector, theNbOfSteps, theToMakeGroups );
+  ExtrusionParams params( theStepVector, theNbOfSteps, theScaleFactors,
+                          theLinearVariation, theBasePoint, theToMakeGroups );
 
   TIDSortedElemSet elemsNodes[2];
   for ( int i = 0, nb = theNodes.length(); i < nb; ++i ) {
@@ -2907,8 +2951,9 @@ SMESH_MeshEditor_i::ExtrusionAlongPathObjects(const SMESH::ListOfIDSources & the
   }
 
   if ( !myIsPreviewMode ) {
-    aPythonDump << "(" << aGroups << ", error) = "
-                << this << ".ExtrusionAlongPathObjects( "
+    if ( aGroups->length() > 0 ) aPythonDump << "(" << aGroups << ", error) = ";
+    else                         aPythonDump << "(_noGroups, error) = ";
+    aPythonDump << this << ".ExtrusionAlongPathObjects( "
                 << theNodes            << ", "
                 << theEdges            << ", "
                 << theFaces            << ", "
@@ -2916,13 +2961,13 @@ SMESH_MeshEditor_i::ExtrusionAlongPathObjects(const SMESH::ListOfIDSources & the
                 << thePathShape        << ", "
                 << theNodeStart        << ", "
                 << theHasAngles        << ", "
-                << theAngles           << ", "
+                << TVar( theAngles )   << ", "
                 << theLinearVariation  << ", "
                 << theHasRefPoint      << ", "
                 << "SMESH.PointStruct( "
-                << ( theHasRefPoint ? theRefPoint.x : 0 ) << ", "
-                << ( theHasRefPoint ? theRefPoint.y : 0 ) << ", "
-                << ( theHasRefPoint ? theRefPoint.z : 0 ) << " ), "
+                << TVar( theHasRefPoint ? theRefPoint.x : 0 ) << ", "
+                << TVar( theHasRefPoint ? theRefPoint.y : 0 ) << ", "
+                << TVar( theHasRefPoint ? theRefPoint.z : 0 ) << " ), "
                 << theMakeGroups       << " )";
   }
   else
@@ -3062,17 +3107,14 @@ SMESH_MeshEditor_i::mirror(TIDSortedElemSet &                  theElements,
   ::SMESH_MeshEditor::PGroupIDs groupIds =
       getEditor().Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh);
 
-  if ( theCopy && !myIsPreviewMode)
+  if ( !myIsPreviewMode )
   {
     if ( theTargetMesh )
-    {
       theTargetMesh->GetMeshDS()->Modified();
-    }
     else
-    {
       declareMeshModified( /*isReComputeSafe=*/false );
-    }
   }
+
   return theMakeGroups ? getGroups(groupIds.get()) : 0;
 
   SMESH_CATCH( SMESH::throwCorbaException );
@@ -3329,16 +3371,12 @@ SMESH_MeshEditor_i::translate(TIDSortedElemSet        & theElements,
   ::SMESH_MeshEditor::PGroupIDs groupIds =
       getEditor().Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh);
 
-  if ( theCopy && !myIsPreviewMode )
+  if ( !myIsPreviewMode )
   {
     if ( theTargetMesh )
-    {
       theTargetMesh->GetMeshDS()->Modified();
-    }
     else
-    {
       declareMeshModified( /*isReComputeSafe=*/false );
-    }
   }
 
   return theMakeGroups ? getGroups(groupIds.get()) : 0;
@@ -3586,7 +3624,7 @@ SMESH_MeshEditor_i::rotate(TIDSortedElemSet &        theElements,
   ::SMESH_MeshEditor::PGroupIDs groupIds =
       getEditor().Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh);
 
-  if ( theCopy && !myIsPreviewMode)
+  if ( !myIsPreviewMode)
   {
     if ( theTargetMesh ) theTargetMesh->GetMeshDS()->Modified();
     else                 declareMeshModified( /*isReComputeSafe=*/false );
@@ -3847,7 +3885,6 @@ SMESH_MeshEditor_i::scale(SMESH::SMESH_IDSource_ptr  theObject,
   };
   gp_Trsf aTrsf;
 
-#if OCC_VERSION_LARGE > 0x06070100
   // fight against orthogonalization
   // aTrsf.SetValues( S[0], 0,    0,    thePoint.x * (1-S[0]),
   //                  0,    S[1], 0,    thePoint.y * (1-S[1]),
@@ -3860,13 +3897,6 @@ SMESH_MeshEditor_i::scale(SMESH::SMESH_IDSource_ptr  theObject,
                 thePoint.z * (1-S[2]));
   M.SetDiagonal( S[0], S[1], S[2] );
 
-#else
-  double tol = std::numeric_limits<double>::max();
-  aTrsf.SetValues( S[0], 0,    0,    thePoint.x * (1-S[0]),
-                   0,    S[1], 0,    thePoint.y * (1-S[1]),
-                   0,    0,    S[2], thePoint.z * (1-S[2]),   tol, tol);
-#endif
-
   TIDSortedElemSet  copyElements;
   TIDSortedElemSet* workElements = &elements;
   if ( myIsPreviewMode )
@@ -3886,7 +3916,7 @@ SMESH_MeshEditor_i::scale(SMESH::SMESH_IDSource_ptr  theObject,
   ::SMESH_MeshEditor::PGroupIDs groupIds =
       getEditor().Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh);
 
-  if ( theCopy && !myIsPreviewMode )
+  if ( !myIsPreviewMode )
   {
     if ( theTargetMesh ) theTargetMesh->GetMeshDS()->Modified();
     else                 declareMeshModified( /*isReComputeSafe=*/false );
@@ -3963,7 +3993,7 @@ SMESH_MeshEditor_i::ScaleMakeMesh(SMESH::SMESH_IDSource_ptr  theObject,
     // and then "GetGroups" using SMESH_Mesh::GetGroups()
 
     TPythonDump pydump; // to prevent dump at mesh creation
-    mesh = makeMesh( theMeshName );
+    mesh   = makeMesh( theMeshName );
     mesh_i = SMESH::DownCast<SMESH_Mesh_i*>( mesh );
 
     if ( mesh_i )
@@ -3987,6 +4017,84 @@ SMESH_MeshEditor_i::ScaleMakeMesh(SMESH::SMESH_IDSource_ptr  theObject,
   return mesh._retn();
 }
 
+//================================================================================
+/*!
+ * \brief Make an offset mesh from a source 2D mesh
+ *  \param [inout] theObject - source mesh. New elements are added to this mesh
+ *         if \a theMeshName is empty.
+ *  \param [in] theValue - offset value
+ *  \param [in] theCopyGroups - to generate groups
+ *  \param [in] theMeshName - optional name of a new mesh
+ *  \param [out] theGroups - new groups
+ *  \return SMESH::SMESH_Mesh_ptr - the modified mesh
+ */
+//================================================================================
+
+SMESH::SMESH_Mesh_ptr SMESH_MeshEditor_i::Offset( SMESH::SMESH_IDSource_ptr theObject,
+                                                  CORBA::Double             theValue,
+                                                  CORBA::Boolean            theCopyGroups,
+                                                  const char*               theMeshName,
+                                                  SMESH::ListOfGroups_out   theGroups)
+  throw (SALOME::SALOME_Exception)
+{
+  SMESH_TRY;
+  initData();
+
+  SMESHDS_Mesh* aMeshDS = getMeshDS();
+
+  SMESH::SMESH_Mesh_var         mesh_var;
+  ::SMESH_MeshEditor::PGroupIDs groupIds;
+
+  TPythonDump pyDump;
+
+  TIDSortedElemSet elements, copyElements;
+  if ( idSourceToSet( theObject, aMeshDS, elements, SMDSAbs_Face,
+                      /*emptyIfIsMesh=*/ !myIsPreviewMode ))
+  {
+    // mesh to modify
+    SMESH_Mesh* tgtMesh = 0;
+    if ( myIsPreviewMode )
+    {
+      TPreviewMesh * tmpMesh = getPreviewMesh();
+      tgtMesh = tmpMesh;
+      tmpMesh->Copy( elements, copyElements );
+      theCopyGroups = false;
+    }
+    else
+    {
+      mesh_var =
+        *theMeshName ? makeMesh( theMeshName ) : SMESH::SMESH_Mesh::_duplicate( myMesh_i->_this() );
+      SMESH_Mesh_i* mesh_i = SMESH::DownCast<SMESH_Mesh_i*>( mesh_var );
+      tgtMesh = & mesh_i->GetImpl();
+    }
+    groupIds = getEditor().Offset( elements, theValue, tgtMesh, theCopyGroups, !myIsPreviewMode );
+
+    tgtMesh->GetMeshDS()->Modified();
+  }
+
+  if ( myIsPreviewMode )
+  {
+    getPreviewMesh()->Remove( SMESHUtils::elemSetIterator( copyElements ));
+  }
+  else
+  {
+    theGroups = theCopyGroups ? getGroups( groupIds.get() ) : new SMESH::ListOfGroups;
+
+    // result of Offset() is a tuple (mesh, groups)
+    if ( mesh_var->_is_nil() ) pyDump << myMesh_i->_this() << ", ";
+    else                       pyDump << mesh_var          << ", ";
+    pyDump << theGroups << " = "
+           << this << ".Offset( "
+           << theValue << ", "
+           << theCopyGroups << ", "
+           << "'" << theMeshName<< "')";
+  }
+
+  return mesh_var._retn();
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+  return SMESH::SMESH_Mesh::_nil();
+}
 
 //=======================================================================
 //function : findCoincidentNodes
@@ -4113,7 +4221,8 @@ FindCoincidentNodesOnPartBut(SMESH::SMESH_IDSource_ptr      theObject,
 //=======================================================================
 
 void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfNodes,
-                                     const SMESH::ListOfIDSources&     NodesToKeep)
+                                     const SMESH::ListOfIDSources&     NodesToKeep,
+                                     CORBA::Boolean                    AvoidMakingHoles)
   throw (SALOME::SALOME_Exception)
 {
   SMESH_TRY;
@@ -4157,9 +4266,9 @@ void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfN
     aTPythonDump << aNodeGroup;
   }
 
-  getEditor().MergeNodes( aListOfListOfNodes );
+  getEditor().MergeNodes( aListOfListOfNodes, AvoidMakingHoles );
 
-  aTPythonDump << "], " << NodesToKeep << ")";
+  aTPythonDump << "], " << NodesToKeep << ", " << AvoidMakingHoles << ")";
 
   declareMeshModified( /*isReComputeSafe=*/false );
 
@@ -4480,13 +4589,15 @@ SMESH_MeshEditor_i::FindAmongElementsByPoint(SMESH::SMESH_IDSource_ptr elementID
 {
   SMESH_TRY;
   SMESH::long_array_var res = new SMESH::long_array;
-  
-  SMESH::array_of_ElementType_var types = elementIDs->GetTypes();
-  if ( types->length() == 1 && // a part contains only nodes or 0D elements
-       ( types[0] == SMESH::NODE || types[0] == SMESH::ELEM0D || types[0] == SMESH::BALL) &&
-       type != types[0] ) // but search of elements of dim > 0
-    return res._retn();
 
+  if ( type != SMESH::NODE )
+  {
+    SMESH::array_of_ElementType_var types = elementIDs->GetTypes();
+    if ( types->length() == 1 && // a part contains only nodes or 0D elements
+         ( types[0] == SMESH::NODE || types[0] == SMESH::ELEM0D || types[0] == SMESH::BALL) &&
+         type != types[0] ) // but search of elements of dim > 0
+      return res._retn();
+  }
   if ( SMESH::DownCast<SMESH_Mesh_i*>( elementIDs )) // elementIDs is the whole mesh 
     return FindElementsByPoint( x,y,z, type );
 
@@ -4500,7 +4611,8 @@ SMESH_MeshEditor_i::FindAmongElementsByPoint(SMESH::SMESH_IDSource_ptr elementID
     SMESHDS_Mesh* meshDS = SMESH::DownCast<SMESH_Mesh_i*>( mesh )->GetImpl().GetMeshDS();
 
     if ( !idSourceToSet( elementIDs, meshDS, elements,
-                         SMDSAbs_ElementType(type), /*emptyIfIsMesh=*/true))
+                         ( type == SMESH::NODE ? SMDSAbs_All : (SMDSAbs_ElementType) type ),
+                         /*emptyIfIsMesh=*/true))
       return res._retn();
 
     typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator > TIter;
@@ -4546,6 +4658,150 @@ CORBA::Short SMESH_MeshEditor_i::GetPointState(CORBA::Double x,
   return 0;
 }
 
+//=======================================================================
+//function : IsManifold
+//purpose  : Check if a 2D mesh is manifold
+//=======================================================================
+
+CORBA::Boolean SMESH_MeshEditor_i::IsManifold()
+  throw (SALOME::SALOME_Exception)
+{
+  bool isManifold = true;
+
+  SMESH_TRY;
+  SMESH_MeshAlgos::TFreeBorderVec foundFreeBordes;
+  SMESH_MeshAlgos::FindFreeBorders( *getMeshDS(),
+                                    foundFreeBordes,
+                                    /*closedOnly=*/true,
+                                    &isManifold );
+  SMESH_CATCH( SMESH::throwCorbaException );
+
+  return isManifold;
+}
+
+//=======================================================================
+//function : IsCoherentOrientation2D
+//purpose  : Check if orientation of 2D elements is coherent
+//=======================================================================
+
+CORBA::Boolean SMESH_MeshEditor_i::IsCoherentOrientation2D()
+  throw (SALOME::SALOME_Exception)
+{
+  bool isGoodOri = true;
+
+  SMESH_TRY;
+  SMESH_MeshAlgos::TFreeBorderVec foundFreeBordes;
+  SMESH_MeshAlgos::FindFreeBorders( *getMeshDS(),
+                                    foundFreeBordes,
+                                    /*closedOnly=*/true,
+                                    /*isManifold=*/0,
+                                    &isGoodOri);
+  SMESH_CATCH( SMESH::throwCorbaException );
+
+  return isGoodOri;
+}
+
+//=======================================================================
+//function : FindFreeBorders
+//purpose  : Returns all or only closed FreeBorder's.
+//=======================================================================
+
+SMESH::ListOfFreeBorders* SMESH_MeshEditor_i::FindFreeBorders(CORBA::Boolean closedOnly)
+  throw (SALOME::SALOME_Exception)
+{
+  SMESH::ListOfFreeBorders_var resBorders = new SMESH::ListOfFreeBorders;
+  SMESH_TRY;
+
+  SMESH_MeshAlgos::TFreeBorderVec foundFreeBordes;
+  SMESH_MeshAlgos::FindFreeBorders( *getMeshDS(), foundFreeBordes, closedOnly );
+
+  resBorders->length( foundFreeBordes.size() );
+  for ( size_t i = 0; i < foundFreeBordes.size(); ++i )
+  {
+    const SMESH_MeshAlgos::TFreeBorder& bordNodes = foundFreeBordes[i];
+    SMESH::FreeBorder&                    bordOut = resBorders[i];
+    bordOut.nodeIDs.length( bordNodes.size() );
+    for ( size_t iN = 0; iN < bordNodes.size(); ++iN )
+      bordOut.nodeIDs[ iN ] = bordNodes[ iN ]->GetID();
+  }
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+
+  return resBorders._retn();
+}
+
+//=======================================================================
+//function : FillHole
+//purpose  : Fill with 2D elements a hole defined by a FreeBorder.
+//=======================================================================
+
+void SMESH_MeshEditor_i::FillHole(const SMESH::FreeBorder& theHole)
+  throw (SALOME::SALOME_Exception)
+{
+  initData();
+
+  if ( theHole.nodeIDs.length() < 4 )
+    THROW_SALOME_CORBA_EXCEPTION("A hole should be bound by at least 3 nodes", SALOME::BAD_PARAM);
+  if ( theHole.nodeIDs[0] != theHole.nodeIDs[ theHole.nodeIDs.length()-1 ] )
+    THROW_SALOME_CORBA_EXCEPTION("Not closed hole boundary. "
+                                 "First and last nodes must be same", SALOME::BAD_PARAM);
+
+  SMESH_MeshAlgos::TFreeBorder bordNodes;
+  bordNodes.resize( theHole.nodeIDs.length() );
+  for ( size_t iN = 0; iN < theHole.nodeIDs.length(); ++iN )
+  {
+    bordNodes[ iN ] = getMeshDS()->FindNode( theHole.nodeIDs[ iN ]);
+    if ( !bordNodes[ iN ] )
+      THROW_SALOME_CORBA_EXCEPTION(SMESH_Comment("Node #") << theHole.nodeIDs[ iN ]
+                                   << " does not exist", SALOME::BAD_PARAM);
+  }
+
+  SMESH_TRY;
+
+  MeshEditor_I::TPreviewMesh* previewMesh = 0;
+  SMDS_Mesh* meshDS = getMeshDS();
+  if ( myIsPreviewMode )
+  {
+    // copy faces sharing nodes of theHole
+    TIDSortedElemSet holeFaces;
+    previewMesh = getPreviewMesh( SMDSAbs_Face );
+    for ( size_t i = 0; i < bordNodes.size(); ++i )
+    {
+      SMDS_ElemIteratorPtr fIt = bordNodes[i]->GetInverseElementIterator( SMDSAbs_Face );
+      while ( fIt->more() )
+      {
+        const SMDS_MeshElement* face = fIt->next();
+        if ( holeFaces.insert( face ).second )
+          previewMesh->Copy( face );
+      }
+      bordNodes[i] = previewMesh->GetMeshDS()->FindNode( bordNodes[i]->GetID() );
+      ASSERT( bordNodes[i] );
+    }
+    meshDS = previewMesh->GetMeshDS();
+  }
+
+  std::vector<const SMDS_MeshElement*> newFaces;
+  SMESH_MeshAlgos::FillHole( bordNodes, *meshDS, newFaces );
+
+  if ( myIsPreviewMode )
+  {
+    previewMesh->Clear();
+    for ( size_t i = 0; i < newFaces.size(); ++i )
+      previewMesh->Copy( newFaces[i] );
+  }
+  else
+  {
+    getEditor().ClearLastCreated();
+    SMESH_SequenceOfElemPtr& aSeq =
+      const_cast<SMESH_SequenceOfElemPtr&>( getEditor().GetLastCreatedElems() );
+    aSeq.swap( newFaces );
+
+    TPythonDump() << this << ".FillHole( SMESH.FreeBorder(" << theHole.nodeIDs << " ))";
+  }
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+}
+
 //=======================================================================
 //function : convError
 //purpose  :
@@ -5068,7 +5324,6 @@ CORBA::Boolean SMESH_MeshEditor_i::ChangeElemNodes(CORBA::Long ide,
   TPythonDump() << "isDone = " << this << ".ChangeElemNodes( "
                 << ide << ", " << newIDs << " )";
 
-  MESSAGE("ChangeElementNodes");
   bool res = getMeshDS()->ChangeElementNodes( elem, & aNodes[0], nbn1+1 );
 
   declareMeshModified( /*isReComputeSafe=*/ !res );
@@ -5249,10 +5504,10 @@ void SMESH_MeshEditor_i::dumpGroupsList(TPythonDump &               theDumpPytho
 */
 //================================================================================
 
-string SMESH_MeshEditor_i::generateGroupName(const string& thePrefix)
+std::string SMESH_MeshEditor_i::generateGroupName(const std::string& thePrefix)
 {
   SMESH::ListOfGroups_var groups = myMesh_i->GetGroups();
-  set<string> groupNames;
+  set<std::string> groupNames;
 
   // Get existing group names
   for (int i = 0, nbGroups = groups->length(); i < nbGroups; i++ ) {
@@ -5265,7 +5520,7 @@ string SMESH_MeshEditor_i::generateGroupName(const string& thePrefix)
   }
 
   // Find new name
-  string name = thePrefix;
+  std::string name = thePrefix;
   int index = 0;
 
   while (!groupNames.insert(name).second)
@@ -5304,7 +5559,7 @@ bool SMESH_MeshEditor_i::idSourceToSet(SMESH::SMESH_IDSource_ptr  theIDSource,
 {
   if ( error ) *error = IDSource_OK;
 
-  if ( CORBA::is_nil( theIDSource ) )
+  if ( CORBA::is_nil( theIDSource ))
   {
     if ( error ) *error = IDSource_INVALID;
     return false;
@@ -5354,7 +5609,7 @@ bool SMESH_MeshEditor_i::idSourceToSet(SMESH::SMESH_IDSource_ptr  theIDSource,
  * \param theElements - container of elements to duplicate.
  * \param theGroupName - a name of group to contain the generated elements.
  *                    If a group with such a name already exists, the new elements
- *                    are added to the existng group, else a new group is created.
+ *                    are added to the existing group, else a new group is created.
  *                    If \a theGroupName is empty, new elements are not added 
  *                    in any group.
  * \return a group where the new elements are added. NULL if theGroupName == "".
@@ -5379,11 +5634,11 @@ SMESH_MeshEditor_i::DoubleElements(SMESH::SMESH_IDSource_ptr theElements,
   {
     getEditor().DoubleElements( elems );
 
-    if ( strlen( theGroupName ) && !getEditor().GetLastCreatedElems().IsEmpty() )
+    if ( strlen( theGroupName ) && !getEditor().GetLastCreatedElems().empty() )
     {
       // group type
       SMESH::ElementType type =
-        SMESH::ElementType( getEditor().GetLastCreatedElems().Value(1)->GetType() );
+        SMESH::ElementType( getEditor().GetLastCreatedElems()[0]->GetType() );
       // find existing group
       SMESH::ListOfGroups_var groups = myMesh_i->GetGroups();
       for ( size_t i = 0; i < groups->length(); ++i )
@@ -5403,8 +5658,8 @@ SMESH_MeshEditor_i::DoubleElements(SMESH::SMESH_IDSource_ptr theElements,
       {
         SMESHDS_Group* groupDS = static_cast< SMESHDS_Group* >( group_i->GetGroupDS() );
         const SMESH_SequenceOfElemPtr& aSeq = getEditor().GetLastCreatedElems();
-        for ( int i = 1; i <= aSeq.Length(); i++ )
-          groupDS->SMDSGroup().Add( aSeq(i) );
+        for ( size_t i = 0; i < aSeq.size(); i++ )
+          groupDS->SMDSGroup().Add( aSeq[i] );
       }
     }
   }
@@ -5516,10 +5771,7 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroup(SMESH::SMESH_GroupBase_ptr th
   if ( !CORBA::is_nil( theModifiedElems ) )
     aModifiedElems = theModifiedElems->GetListOfID();
   else
-  {
     aModifiedElems = new SMESH::long_array;
-    aModifiedElems->length( 0 );
-  }
 
   TPythonDump pyDump; // suppress dump by the next line
 
@@ -5573,8 +5825,8 @@ SMESH_MeshEditor_i::DoubleNodeGroupNew( SMESH::SMESH_GroupBase_ptr theNodes,
     // Create group with newly created nodes
     SMESH::long_array_var anIds = GetLastCreatedNodes();
     if (anIds->length() > 0) {
-      string anUnindexedName (theNodes->GetName());
-      string aNewName = generateGroupName(anUnindexedName + "_double");
+      std::string anUnindexedName (theNodes->GetName());
+      std::string aNewName = generateGroupName(anUnindexedName + "_double");
       aNewGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str());
       aNewGroup->Add(anIds);
       pyDump << aNewGroup << " = ";
@@ -5672,8 +5924,8 @@ SMESH_MeshEditor_i::DoubleNodeGroupsNew( const SMESH::ListOfGroups& theNodes,
     // Create group with newly created nodes
     SMESH::long_array_var anIds = GetLastCreatedNodes();
     if (anIds->length() > 0) {
-      string anUnindexedName (theNodes[0]->GetName());
-      string aNewName = generateGroupName(anUnindexedName + "_double");
+      std::string anUnindexedName (theNodes[0]->GetName());
+      std::string aNewName = generateGroupName(anUnindexedName + "_double");
       aNewGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str());
       aNewGroup->Add(anIds);
       pyDump << aNewGroup << " = ";
@@ -5897,15 +6149,15 @@ SMESH_MeshEditor_i::DoubleNodeElemGroup2New(SMESH::SMESH_GroupBase_ptr theElems,
   {
     // Create group with newly created elements
     CORBA::String_var elemGroupName = theElems->GetName();
-    string aNewName = generateGroupName( string(elemGroupName.in()) + "_double");
-    if ( !getEditor().GetLastCreatedElems().IsEmpty() && theElemGroupNeeded )
+    std::string aNewName = generateGroupName( std::string(elemGroupName.in()) + "_double");
+    if ( !getEditor().GetLastCreatedElems().empty() && theElemGroupNeeded )
     {
       SMESH::long_array_var anIds = GetLastCreatedElems();
       SMESH::ElementType aGroupType = myMesh_i->GetElementType(anIds[0], true);
       aNewElemGroup = myMesh_i->CreateGroup(aGroupType, aNewName.c_str());
       aNewElemGroup->Add(anIds);
     }
-    if ( !getEditor().GetLastCreatedNodes().IsEmpty() && theNodeGroupNeeded )
+    if ( !getEditor().GetLastCreatedNodes().empty() && theNodeGroupNeeded )
     {
       SMESH::long_array_var anIds = GetLastCreatedNodes();
       aNewNodeGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str());
@@ -6129,15 +6381,15 @@ SMESH_MeshEditor_i::DoubleNodeElemGroups2New(const SMESH::ListOfGroups& theElems
   {
     // Create group with newly created elements
     CORBA::String_var elemGroupName = theElems[0]->GetName();
-    string aNewName = generateGroupName( string(elemGroupName.in()) + "_double");
-    if ( !getEditor().GetLastCreatedElems().IsEmpty() && theElemGroupNeeded )
+    std::string aNewName = generateGroupName( std::string(elemGroupName.in()) + "_double");
+    if ( !getEditor().GetLastCreatedElems().empty() && theElemGroupNeeded )
     {
       SMESH::long_array_var anIds = GetLastCreatedElems();
       SMESH::ElementType aGroupType = myMesh_i->GetElementType(anIds[0], true);
       aNewElemGroup = myMesh_i->CreateGroup(aGroupType, aNewName.c_str());
       aNewElemGroup->Add(anIds);
     }
-    if ( !getEditor().GetLastCreatedNodes().IsEmpty() && theNodeGroupNeeded )
+    if ( !getEditor().GetLastCreatedNodes().empty() && theNodeGroupNeeded )
     {
       SMESH::long_array_var anIds = GetLastCreatedNodes();
       aNewNodeGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str());
@@ -6231,14 +6483,10 @@ SMESH_MeshEditor_i::AffectedElemGroupsInRegion( const SMESH::ListOfGroups& theEl
   throw (SALOME::SALOME_Exception)
 {
   SMESH_TRY;
-  MESSAGE("AffectedElemGroupsInRegion");
   SMESH::ListOfGroups_var aListOfGroups = new SMESH::ListOfGroups();
-  bool isEdgeGroup = false;
-  bool isFaceGroup = false;
-  bool isVolumeGroup = false;
-  SMESH::SMESH_Group_var aNewEdgeGroup = myMesh_i->CreateGroup(SMESH::EDGE, "affectedEdges");
-  SMESH::SMESH_Group_var aNewFaceGroup = myMesh_i->CreateGroup(SMESH::FACE, "affectedFaces");
-  SMESH::SMESH_Group_var aNewVolumeGroup = myMesh_i->CreateGroup(SMESH::VOLUME, "affectedVolumes");
+  SMESH::SMESH_Group_var aNewEdgeGroup   = SMESH::SMESH_Group::_nil();
+  SMESH::SMESH_Group_var aNewFaceGroup   = SMESH::SMESH_Group::_nil();
+  SMESH::SMESH_Group_var aNewVolumeGroup = SMESH::SMESH_Group::_nil();
 
   initData();
 
@@ -6246,76 +6494,75 @@ SMESH_MeshEditor_i::AffectedElemGroupsInRegion( const SMESH::ListOfGroups& theEl
 
   SMESHDS_Mesh* aMeshDS = getMeshDS();
   TIDSortedElemSet anElems, aNodes;
-  listOfGroupToSet(theElems, aMeshDS, anElems, false);
+  bool isNodeGrp = theElems.length() ? theElems[0]->GetType() == SMESH::NODE : false;
+  listOfGroupToSet(theElems, aMeshDS, anElems, isNodeGrp);
   listOfGroupToSet(theNodesNot, aMeshDS, aNodes, true);
 
   TopoDS_Shape aShape = SMESH_Gen_i::GetSMESHGen()->GeomObjectToShape(theShape);
   TIDSortedElemSet anAffected;
   bool aResult = aMeshEditor.AffectedElemGroupsInRegion(anElems, aNodes, aShape, anAffected);
 
-
   declareMeshModified( /*isReComputeSafe=*/ !aResult );
 
   TPythonDump pyDump;
-  if (aResult)
+  if ( aResult && anAffected.size() > 0 )
   {
-    int lg = anAffected.size();
-    MESSAGE("lg="<< lg);
     SMESH::long_array_var volumeIds = new SMESH::long_array;
-    volumeIds->length(lg);
-    SMESH::long_array_var faceIds = new SMESH::long_array;
-    faceIds->length(lg);
-    SMESH::long_array_var edgeIds = new SMESH::long_array;
-    edgeIds->length(lg);
+    SMESH::long_array_var   faceIds = new SMESH::long_array;
+    SMESH::long_array_var   edgeIds = new SMESH::long_array;
+    volumeIds->length( anAffected.size() );
+    faceIds  ->length( anAffected.size() );
+    edgeIds  ->length( anAffected.size() );
+
     int ivol = 0;
     int iface = 0;
     int iedge = 0;
-
     TIDSortedElemSet::const_iterator eIt = anAffected.begin();
     for (; eIt != anAffected.end(); ++eIt)
     {
       const SMDS_MeshElement* anElem = *eIt;
-      if (!anElem)
-        continue;
       int elemId = anElem->GetID();
-      if (myMesh->GetElementType(elemId, true) == SMDSAbs_Volume)
-        volumeIds[ivol++] = elemId;
-      else if (myMesh->GetElementType(elemId, true) == SMDSAbs_Face)
-        faceIds[iface++] = elemId;
-      else if (myMesh->GetElementType(elemId, true) == SMDSAbs_Edge)
-        edgeIds[iedge++] = elemId;
+      switch ( anElem->GetType() ) {
+      case SMDSAbs_Volume: volumeIds[ivol++] = elemId; break;
+      case SMDSAbs_Face:    faceIds[iface++] = elemId; break;
+      case SMDSAbs_Edge:    edgeIds[iedge++] = elemId; break;
+      default:;
+      }
     }
     volumeIds->length(ivol);
     faceIds->length(iface);
     edgeIds->length(iedge);
 
-    aNewVolumeGroup->Add(volumeIds);
-    aNewFaceGroup->Add(faceIds);
-    aNewEdgeGroup->Add(edgeIds);
-    isVolumeGroup = (aNewVolumeGroup->Size() > 0);
-    isFaceGroup = (aNewFaceGroup->Size() > 0);
-    isEdgeGroup = (aNewEdgeGroup->Size() > 0);
+    int nbGroups = 0;
+    if ( ivol > 0 )
+    {
+      aNewVolumeGroup = myMesh_i->CreateGroup(SMESH::VOLUME,
+                                              generateGroupName("affectedVolumes").c_str());
+      aNewVolumeGroup->Add(volumeIds);
+      aListOfGroups->length( nbGroups+1 );
+      aListOfGroups[ nbGroups++ ] = aNewVolumeGroup._retn();
+    }
+    if ( iface > 0 )
+    {
+      aNewFaceGroup = myMesh_i->CreateGroup(SMESH::FACE,
+                                            generateGroupName("affectedFaces").c_str());
+      aNewFaceGroup->Add(faceIds);
+      aListOfGroups->length( nbGroups+1 );
+      aListOfGroups[ nbGroups++ ] = aNewFaceGroup._retn();
+    }
+    if ( iedge > 0 )
+    {
+      aNewEdgeGroup = myMesh_i->CreateGroup(SMESH::EDGE,
+                                            generateGroupName("affectedEdges").c_str());
+      aNewEdgeGroup->Add(edgeIds);
+      aListOfGroups->length( nbGroups+1 );
+      aListOfGroups[ nbGroups++ ] = aNewEdgeGroup._retn();
+    }
   }
 
-  int nbGroups = 0;
-  if (isEdgeGroup)   nbGroups++;
-  if (isFaceGroup)   nbGroups++;
-  if (isVolumeGroup) nbGroups++;
-  aListOfGroups->length(nbGroups);
-
-  int i = 0;
-  if (isEdgeGroup)   aListOfGroups[i++] = aNewEdgeGroup._retn();
-  if (isFaceGroup)   aListOfGroups[i++] = aNewFaceGroup._retn();
-  if (isVolumeGroup) aListOfGroups[i++] = aNewVolumeGroup._retn();
-
   // Update Python script
 
-  pyDump << "[ ";
-  if (isEdgeGroup)   pyDump << aNewEdgeGroup << ", ";
-  if (isFaceGroup)   pyDump << aNewFaceGroup << ", ";
-  if (isVolumeGroup) pyDump << aNewVolumeGroup << ", ";
-  pyDump << "] = ";
-  pyDump << this << ".AffectedElemGroupsInRegion( "
+  pyDump << aListOfGroups << " = " << this << ".AffectedElemGroupsInRegion( "
          << &theElems << ", " << &theNodesNot << ", " << theShape << " )";
 
   return aListOfGroups._retn();
@@ -6327,7 +6574,7 @@ SMESH_MeshEditor_i::AffectedElemGroupsInRegion( const SMESH::ListOfGroups& theEl
 //================================================================================
 /*!
   \brief Generated skin mesh (containing 2D cells) from 3D mesh
-   The created 2D mesh elements based on nodes of free faces of boundary volumes
+  The created 2D mesh elements based on nodes of free faces of boundary volumes
   \return TRUE if operation has been completed successfully, FALSE otherwise
 */
 //================================================================================
@@ -6609,7 +6856,7 @@ SMESH_MeshEditor_i::MakeBoundaryMesh(SMESH::SMESH_IDSource_ptr idSource,
   else
     pyDump << mesh_var << ", ";
   if ( group_var->_is_nil() )
-    pyDump << "_NoneGroup = "; // assignment to None is forbiden
+    pyDump << "_NoneGroup = "; // assignment to None is forbidden
   else
     pyDump << group_var << " = ";
   pyDump << this << ".MakeBoundaryMesh( "
@@ -6673,7 +6920,7 @@ CORBA::Long SMESH_MeshEditor_i::MakeBoundaryElements(SMESH::Bnd_Dimension dim,
     else
       groupsOfThisMesh[ nbGroups++ ] = groups[i];
     if ( SMESH::DownCast<SMESH_Mesh_i*>( groups[i] ))
-      THROW_SALOME_CORBA_EXCEPTION("expect a group but recieve a mesh", SALOME::BAD_PARAM);
+      THROW_SALOME_CORBA_EXCEPTION("expected a group but received a mesh", SALOME::BAD_PARAM);
   }
   groupsOfThisMesh->length( nbGroups );
   groupsOfOtherMesh->length( nbGroupsOfOtherMesh );
@@ -6767,7 +7014,7 @@ CORBA::Long SMESH_MeshEditor_i::MakeBoundaryElements(SMESH::Bnd_Dimension dim,
   else
     pyDump << mesh_var << ", ";
   if ( group_var->_is_nil() )
-    pyDump << "_NoneGroup = "; // assignment to None is forbiden
+    pyDump << "_NoneGroup = "; // assignment to None is forbidden
   else
     pyDump << group_var << " = ";
   pyDump << this << ".MakeBoundaryElements( "
@@ -6784,3 +7031,130 @@ CORBA::Long SMESH_MeshEditor_i::MakeBoundaryElements(SMESH::Bnd_Dimension dim,
   SMESH_CATCH( SMESH::throwCorbaException );
   return 0;
 }
+
+//================================================================================
+/*!
+ * \brief Create a polyline consisting of 1D mesh elements each lying on a 2D element of
+ *        the initial mesh. Positions of new nodes are found by cutting the mesh by the
+ *        plane passing through pairs of points specified by each PolySegment structure.
+ *        If there are several paths connecting a pair of points, the shortest path is
+ *        selected by the module. Position of the cutting plane is defined by the two
+ *        points and an optional vector lying on the plane specified by a PolySegment.
+ *        By default the vector is defined by Mesh module as following. A middle point
+ *        of the two given points is computed. The middle point is projected to the mesh.
+ *        The vector goes from the middle point to the projection point. In case of planar
+ *        mesh, the vector is normal to the mesh.
+ *  \param [inout] segments - PolySegment's defining positions of cutting planes.
+ *        Return the used vector and position of the middle point.
+ *  \param [in] groupName - optional name of a group where created mesh segments will
+ *        be added.
+ */
+//================================================================================
+
+void SMESH_MeshEditor_i::MakePolyLine(SMESH::ListOfPolySegments& theSegments,
+                                      const char*                theGroupName)
+  throw (SALOME::SALOME_Exception)
+{
+  if ( theSegments.length() == 0 )
+    THROW_SALOME_CORBA_EXCEPTION("No segments given", SALOME::BAD_PARAM );
+  if ( myMesh->NbFaces() == 0 )
+    THROW_SALOME_CORBA_EXCEPTION("No faces in the mesh", SALOME::BAD_PARAM );
+
+  SMESH_TRY;
+  initData(/*deleteSearchers=*/false);
+
+  SMESHDS_Group* groupDS = 0;
+  SMESHDS_Mesh*   meshDS = getMeshDS();
+  if ( myIsPreviewMode ) // copy faces to the tmp mesh
+  {
+    TPreviewMesh * tmpMesh = getPreviewMesh( SMDSAbs_Edge );
+    SMDS_ElemIteratorPtr faceIt = getMeshDS()->elementsIterator( SMDSAbs_Face );
+    while ( faceIt->more() )
+      tmpMesh->Copy( faceIt->next() );
+    meshDS = tmpMesh->GetMeshDS();
+  }
+  else if ( theGroupName[0] ) // find/create a group of segments
+  {
+    SMESH_Mesh::GroupIteratorPtr grpIt = myMesh->GetGroups();
+    while ( !groupDS && grpIt->more() )
+    {
+      SMESH_Group* group = grpIt->next();
+      if ( group->GetGroupDS()->GetType() == SMDSAbs_Edge &&
+           strcmp( group->GetName(), theGroupName ) == 0 )
+      {
+        groupDS = dynamic_cast< SMESHDS_Group* >( group->GetGroupDS() );
+      }
+    }
+    if ( !groupDS )
+    {
+      SMESH::SMESH_Group_var groupVar = myMesh_i->CreateGroup( SMESH::EDGE, theGroupName );
+
+      if ( SMESH_Group_i* groupImpl = SMESH::DownCast<SMESH_Group_i*>( groupVar ))
+        groupDS = dynamic_cast< SMESHDS_Group* >( groupImpl->GetGroupDS() );
+    }
+  }
+
+  // convert input polySegments
+  ::SMESH_MeshEditor::TListOfPolySegments segments( theSegments.length() );
+  for ( CORBA::ULong i = 0; i < theSegments.length(); ++i )
+  {
+    SMESH::PolySegment&               segIn = theSegments[ i ];
+    ::SMESH_MeshEditor::PolySegment& segOut = segments[ i ];
+    segOut.myNode1[0] = meshDS->FindNode( segIn.node1ID1 );
+    segOut.myNode2[0] = meshDS->FindNode( segIn.node1ID2 );
+    segOut.myNode1[1] = meshDS->FindNode( segIn.node2ID1 );
+    segOut.myNode2[1] = meshDS->FindNode( segIn.node2ID2 );
+    segOut.myVector.SetCoord( segIn.vector.PS.x,
+                              segIn.vector.PS.y,
+                              segIn.vector.PS.z );
+    if ( !segOut.myNode1[0] )
+      THROW_SALOME_CORBA_EXCEPTION( SMESH_Comment( "Invalid node ID: ") << segIn.node1ID1,
+                                    SALOME::BAD_PARAM );
+    if ( !segOut.myNode1[1] )
+      THROW_SALOME_CORBA_EXCEPTION( SMESH_Comment( "Invalid node ID: ") << segIn.node2ID1,
+                                    SALOME::BAD_PARAM );
+  }
+
+  // get a static ElementSearcher
+  SMESH::SMESH_IDSource_var idSource = SMESH::SMESH_IDSource::_narrow( myMesh_i->_this() );
+  theSearchersDeleter.Set( myMesh, getPartIOR( idSource, SMESH::FACE ));
+  if ( !theElementSearcher )
+    theElementSearcher = SMESH_MeshAlgos::GetElementSearcher( *getMeshDS() );
+
+  // compute
+  getEditor().MakePolyLine( segments, groupDS, theElementSearcher );
+
+  // return vectors
+  if ( myIsPreviewMode )
+  {
+    for ( CORBA::ULong i = 0; i < theSegments.length(); ++i )
+    {
+      SMESH::PolySegment&             segOut = theSegments[ i ];
+      ::SMESH_MeshEditor::PolySegment& segIn = segments[ i ];
+      segOut.vector.PS.x = segIn.myVector.X();
+      segOut.vector.PS.y = segIn.myVector.Y();
+      segOut.vector.PS.z = segIn.myVector.Z();
+    }
+  }
+  else
+  {
+    TPythonDump() << "_segments = []";
+    for ( CORBA::ULong i = 0; i < theSegments.length(); ++i )
+    {
+      SMESH::PolySegment& segIn = theSegments[ i ];
+      TPythonDump() << "_segments.append( SMESH.PolySegment( "
+                    << segIn.node1ID1 << ", "
+                    << segIn.node1ID2 << ", "
+                    << segIn.node2ID1 << ", "
+                    << segIn.node2ID2 << ", "
+                    << "smeshBuilder.MakeDirStruct( "
+                    << segIn.vector.PS.x << ", "
+                    << segIn.vector.PS.y << ", "
+                    << segIn.vector.PS.z << ")))";
+    }
+    TPythonDump() << this << ".MakePolyLine( _segments, '" << theGroupName << "')";
+  }
+  meshDS->Modified();
+  SMESH_CATCH( SMESH::throwCorbaException );
+  return;
+}