Salome HOME
0021374]: EDF 1898 SMESH: Extrusion of a node to have an edge
[modules/smesh.git] / src / SMESH_I / SMESH_MeshEditor_i.cxx
index 74fe9545d264f5ab057d0b02cc8026ffa77a4028..4c1ff5c3b3ebb22a64a3269ade51a9afaefbaf58 100644 (file)
@@ -1,25 +1,24 @@
-//  Copyright (C) 2007-2010  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2011  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
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
 //
-//  This library is free software; you can redistribute it and/or
-//  modify it under the terms of the GNU Lesser General Public
-//  License as published by the Free Software Foundation; either
-//  version 2.1 of the License.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
 //
-//  This library is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-//  Lesser General Public License for more details.
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
 //
-//  You should have received a copy of the GNU Lesser General Public
-//  License along with this library; if not, write to the Free Software
-//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 //
-//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
-//  SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses
 //  File   : SMESH_MeshEditor_i.cxx
 //  Author : Nicolas REJNERI
 //  Module : SMESH
 
 #include "SMESH_MeshEditor_i.hxx"
 
-#include "SMDS_Mesh0DElement.hxx"
 #include "SMDS_LinearEdge.hxx"
+#include "SMDS_Mesh0DElement.hxx"
 #include "SMDS_MeshFace.hxx"
 #include "SMDS_MeshVolume.hxx"
 #include "SMDS_PolyhedralVolumeOfNodes.hxx"
-#include "SMESH_subMeshEventListener.hxx"
-#include "SMESH_Gen_i.hxx"
+#include "SMDS_SetIterator.hxx"
+#include "SMESHDS_Group.hxx"
+#include "SMESH_ControlsDef.hxx"
 #include "SMESH_Filter_i.hxx"
-#include "SMESH_subMesh_i.hxx"
+#include "SMESH_Gen_i.hxx"
 #include "SMESH_Group_i.hxx"
 #include "SMESH_PythonDump.hxx"
-#include "SMESH_ControlsDef.hxx"
+#include "SMESH_subMeshEventListener.hxx"
+#include "SMESH_subMesh_i.hxx"
 
 #include "utilities.h"
 #include "Utils_ExceptHandlers.hxx"
@@ -93,7 +94,7 @@ namespace {
       myPreviewType = previewElements;
     }
     //!< Destructor
-    virtual ~TPreviewMesh() { delete _myMeshDS; }
+    virtual ~TPreviewMesh() { delete _myMeshDS; _myMeshDS = 0; }
     //!< Copy a set of elements
     void Copy(const TIDSortedElemSet & theElements,
               TIDSortedElemSet&        theCopyElements,
@@ -110,8 +111,12 @@ namespace {
         if ( type == theAvoidType ||
              ( theSelectType != SMDSAbs_All && type != theSelectType ))
           continue;
-
-        if ( const SMDS_MeshElement* anElemCopy = Copy( anElem ))
+        const SMDS_MeshElement* anElemCopy;
+        if ( type == SMDSAbs_Node)
+          anElemCopy = Copy( cast2Node(anElem) );
+        else
+          anElemCopy = Copy( anElem );
+        if ( anElemCopy )
           theCopyElements.insert( theCopyElements.end(), anElemCopy );
       }
     }
@@ -166,8 +171,10 @@ namespace {
   struct TSearchersDeleter : public SMESH_subMeshEventListener
   {
     SMESH_Mesh* myMesh;
+    string      myMeshPartIOR;
     //!< Constructor
-    TSearchersDeleter(): SMESH_subMeshEventListener( false ), // won't be deleted by submesh
+    TSearchersDeleter(): SMESH_subMeshEventListener( false, // won't be deleted by submesh
+                                                     "SMESH_MeshEditor_i::TSearchersDeleter"),
                          myMesh(0) {}
     //!< Delete theNodeSearcher
     static void Delete()
@@ -186,15 +193,16 @@ namespace {
       }
     }
     //!< set self on all submeshes and delete theNodeSearcher if other mesh is set
-    void Set(SMESH_Mesh* mesh)
+    void Set(SMESH_Mesh* mesh, const string& meshPartIOR = string())
     {
-      if ( myMesh != mesh )
+      if ( myMesh != mesh || myMeshPartIOR != meshPartIOR)
       {
         if ( myMesh ) {
           Delete();
           Unset( myMesh );
         }
         myMesh = mesh;
+        myMeshPartIOR = meshPartIOR;
         if ( SMESH_subMesh* myMainSubMesh = mesh->GetSubMeshContaining(1) ) {
           const TDependsOnMap & subMeshes = myMainSubMesh->DependsOn();
           TDependsOnMap::const_iterator sm;
@@ -311,7 +319,15 @@ namespace {
         if ( const SMDS_MeshNode * n = theMeshDS->FindNode( aElementsId[i] ))
           theNodeSet.insert( theNodeSet.end(), n);
     }
-    else {
+    else if ( SMESH::DownCast<SMESH_Mesh_i*>( theObject ))
+    {
+      SMDS_NodeIteratorPtr nIt = theMeshDS->nodesIterator();
+      while ( nIt->more( ))
+        if( const SMDS_MeshElement * elem = nIt->next() )
+          theNodeSet.insert( elem->begin_nodes(), elem->end_nodes());
+    }
+    else
+    {
       for(int i = 0; i < aElementsId->length(); i++)
         if( const SMDS_MeshElement * elem = theMeshDS->FindElement( aElementsId[i] ))
           theNodeSet.insert( elem->begin_nodes(), elem->end_nodes());
@@ -421,8 +437,10 @@ struct _IDSource : public POA_SMESH::SMESH_IDSource
   SMESH::array_of_ElementType* GetTypes()
   {
     SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType;
-    types->length( 1 );
-    types[0] = _type;
+    if ( _ids.length() > 0 ) {
+      types->length( 1 );
+      types[0] = _type;
+    }
     return types._retn();
   }
 };
@@ -517,12 +535,14 @@ CORBA::Long SMESH_MeshEditor_i::RemoveOrphanNodes()
   for ( int i = 0; i < seq.size(); i++ )
     IdList.push_back( seq[i] );
 
-  bool ret = anEditor.Remove( IdList, true );
+  int nbNodesBefore = myMesh->NbNodes();
+  anEditor.Remove( IdList, true );
   myMesh->GetMeshDS()->Modified();
   if ( IdList.size() )
     myMesh->SetIsModified( true );
+  int nbNodesAfter = myMesh->NbNodes();
 
-  return ret;
+  return nbNodesBefore - nbNodesAfter;
 }
 
 //=============================================================================
@@ -647,6 +667,10 @@ CORBA::Long SMESH_MeshEditor_i::AddFace(const SMESH::long_array & IDsOfNodes)
     elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2], nodes[3],
                                 nodes[4], nodes[5], nodes[6], nodes[7]);
   }
+  else if (NbNodes == 9) {
+    elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2], nodes[3],
+                                nodes[4], nodes[5], nodes[6], nodes[7], nodes[8] );
+  }
   else if (NbNodes > 2) {
     elem = GetMeshDS()->AddPolygonalFace(nodes);
   }
@@ -709,6 +733,9 @@ CORBA::Long SMESH_MeshEditor_i::AddVolume(const SMESH::long_array & IDsOfNodes)
   case 10:elem = GetMeshDS()->AddVolume(n[0],n[1],n[2],n[3],n[4],n[5],
                                         n[6],n[7],n[8],n[9]);
     break;
+  case 12: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]);
+    break;
   case 13: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]);
     break;
@@ -719,6 +746,11 @@ 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 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],
+                                        n[20],n[21],n[22],n[23],n[24],n[25],n[26]);
+    break;
   }
 
   // Update Python script
@@ -1859,9 +1891,17 @@ SMESH_MeshEditor_i::extrusionSweep(const SMESH::long_array & theIDsOfElements,
     gp_Vec stepVec( P->x, P->y, P->z );
 
     TIDSortedElemSet* workElements = & elements;
-    TPreviewMesh      tmpMesh( SMDSAbs_Face );
-    SMESH_Mesh*       mesh = myMesh;
-    
+
+    SMDSAbs_ElementType aType = SMDSAbs_Face;
+    //::SMESH_MeshEditor::ExtrusionFlags aFlag = ::SMESH_MeshEditor::ExtrusionFlags::EXTRUSION_FLAG_BOUNDARY;
+    if (theElementType == SMDSAbs_Node)
+    {
+      aType = SMDSAbs_Edge;
+      //aFlag = ::SMESH_MeshEditor::ExtrusionFlags::EXTRUSION_FLAG_SEW;
+    }
+    TPreviewMesh      tmpMesh( aType );
+    SMESH_Mesh* mesh = myMesh;
+
     if ( myPreviewMode ) {
       SMDSAbs_ElementType select = SMDSAbs_All, avoid = SMDSAbs_Volume;
       tmpMesh.Copy( elements, copyElements, select, avoid );
@@ -1872,7 +1912,7 @@ SMESH_MeshEditor_i::extrusionSweep(const SMESH::long_array & theIDsOfElements,
 
     TElemOfElemListMap aHystory;
     ::SMESH_MeshEditor anEditor( mesh );
-    ::SMESH_MeshEditor::PGroupIDs groupIds =
+    ::SMESH_MeshEditor::PGroupIDs groupIds = 
         anEditor.ExtrusionSweep (*workElements, stepVec, theNbOfSteps, aHystory, theMakeGroups);
 
     myMesh->GetMeshDS()->Modified();
@@ -1903,6 +1943,21 @@ void SMESH_MeshEditor_i::ExtrusionSweep(const SMESH::long_array & theIDsOfElemen
   }
 }
 
+//=======================================================================
+//function : ExtrusionSweep0D
+//purpose  :
+//=======================================================================
+
+void SMESH_MeshEditor_i::ExtrusionSweep0D(const SMESH::long_array & theIDsOfElements,
+                                        const SMESH::DirStruct &  theStepVector,
+                                        CORBA::Long               theNbOfSteps)
+{
+  extrusionSweep (theIDsOfElements, theStepVector, theNbOfSteps, false, SMDSAbs_Node );
+  if (!myPreviewMode) {
+    TPythonDump() << this << ".ExtrusionSweep0D( "
+                  << theIDsOfElements << ", " << theStepVector <<", " << theNbOfSteps << " )";
+  }
+}
 
 //=======================================================================
 //function : ExtrusionSweepObject
@@ -1921,6 +1976,23 @@ void SMESH_MeshEditor_i::ExtrusionSweepObject(SMESH::SMESH_IDSource_ptr theObjec
   }
 }
 
+//=======================================================================
+//function : ExtrusionSweepObject0D
+//purpose  :
+//=======================================================================
+
+void SMESH_MeshEditor_i::ExtrusionSweepObject0D(SMESH::SMESH_IDSource_ptr theObject,
+                                                const SMESH::DirStruct &  theStepVector,
+                                                CORBA::Long               theNbOfSteps)
+{
+  SMESH::long_array_var anElementsId = theObject->GetIDs();
+  extrusionSweep (anElementsId, theStepVector, theNbOfSteps, false, SMDSAbs_Node );
+  if ( !myPreviewMode ) {
+    TPythonDump() << this << ".ExtrusionSweepObject0D( "
+                  << theObject << ", " << theStepVector << ", " << theNbOfSteps << " )";
+  }
+}
+
 //=======================================================================
 //function : ExtrusionSweepObject1D
 //purpose  :
@@ -1977,6 +2049,28 @@ SMESH_MeshEditor_i::ExtrusionSweepMakeGroups(const SMESH::long_array& theIDsOfEl
   return aGroups;
 }
 
+//=======================================================================
+//function : ExtrusionSweepMakeGroups0D
+//purpose  :
+//=======================================================================
+
+SMESH::ListOfGroups*
+SMESH_MeshEditor_i::ExtrusionSweepMakeGroups0D(const SMESH::long_array& theIDsOfElements,
+                                             const SMESH::DirStruct&  theStepVector,
+                                             CORBA::Long              theNbOfSteps)
+{
+  TPythonDump aPythonDump; // it is here to prevent dump of GetGroups()
+
+  SMESH::ListOfGroups* aGroups = extrusionSweep(theIDsOfElements, theStepVector, theNbOfSteps, true,SMDSAbs_Node);
+
+  if (!myPreviewMode) {
+    DumpGroupsList(aPythonDump, aGroups);
+    aPythonDump << this << ".ExtrusionSweepMakeGroups0D( " << theIDsOfElements
+                << ", " << theStepVector <<", " << theNbOfSteps << " )";
+  }
+  return aGroups;
+}
+
 //=======================================================================
 //function : ExtrusionSweepObjectMakeGroups
 //purpose  :
@@ -2000,6 +2094,29 @@ SMESH_MeshEditor_i::ExtrusionSweepObjectMakeGroups(SMESH::SMESH_IDSource_ptr the
   return aGroups;
 }
 
+//=======================================================================
+//function : ExtrusionSweepObject0DMakeGroups
+//purpose  :
+//=======================================================================
+
+SMESH::ListOfGroups*
+SMESH_MeshEditor_i::ExtrusionSweepObject0DMakeGroups(SMESH::SMESH_IDSource_ptr theObject,
+                                                     const SMESH::DirStruct&   theStepVector,
+                                                     CORBA::Long               theNbOfSteps)
+{
+  TPythonDump aPythonDump; // it is here to prevent dump of GetGroups()
+
+  SMESH::long_array_var anElementsId = theObject->GetIDs();
+  SMESH::ListOfGroups * aGroups = extrusionSweep(anElementsId, theStepVector,
+                                                 theNbOfSteps, true, SMDSAbs_Node);
+  if (!myPreviewMode) {
+    DumpGroupsList(aPythonDump, aGroups);
+    aPythonDump << this << ".ExtrusionSweepObject0DMakeGroups( " << theObject
+                << ", " << theStepVector << ", " << theNbOfSteps << " )";
+  }
+  return aGroups;
+}
+
 //=======================================================================
 //function : ExtrusionSweepObject1DMakeGroups
 //purpose  :
@@ -2975,11 +3092,19 @@ SMESH_MeshEditor_i::mirror(TIDSortedElemSet &                  theElements,
       anEditor.Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh);
 
   if(theCopy || myPreviewMode)
-    storeResult(anEditor);
-  else
+    storeResult(anEditor); // store preview data or new elements
+
+  if ( !myPreviewMode )
   {
-    myMesh->SetIsModified( true );
-    myMesh->GetMeshDS()->Modified();
+    if ( theTargetMesh )
+    {
+      theTargetMesh->GetMeshDS()->Modified();
+    }
+    else
+    {
+      myMesh->GetMeshDS()->Modified();
+      myMesh->SetIsModified( true );
+    }
   }
   return theMakeGroups ? getGroups(groupIds.get()) : 0;
 }
@@ -3231,10 +3356,18 @@ SMESH_MeshEditor_i::translate(TIDSortedElemSet        & theElements,
 
   if(theCopy || myPreviewMode)
     storeResult(anEditor);
-  else
+
+  if ( !myPreviewMode )
   {
-    myMesh->GetMeshDS()->Modified();
-    myMesh->SetIsModified( true );
+    if ( theTargetMesh )
+    {
+      theTargetMesh->GetMeshDS()->Modified();
+    }
+    else
+    {
+      myMesh->GetMeshDS()->Modified();
+      myMesh->SetIsModified( true );
+    }
   }
 
   return theMakeGroups ? getGroups(groupIds.get()) : 0;
@@ -3473,10 +3606,18 @@ SMESH_MeshEditor_i::rotate(TIDSortedElemSet &        theElements,
 
   if(theCopy || myPreviewMode)
     storeResult(anEditor);
-  else
+
+  if ( !myPreviewMode )
   {
-    myMesh->GetMeshDS()->Modified();
-    myMesh->SetIsModified( true );
+    if ( theTargetMesh )
+    {
+      theTargetMesh->GetMeshDS()->Modified();
+    }
+    else
+    {
+      myMesh->GetMeshDS()->Modified();
+      myMesh->SetIsModified( true );
+    }
   }
 
   return theMakeGroups ? getGroups(groupIds.get()) : 0;
@@ -3744,11 +3885,20 @@ SMESH_MeshEditor_i::scale(SMESH::SMESH_IDSource_ptr  theObject,
 
   if(theCopy || myPreviewMode )
     storeResult(anEditor);
-  else
+
+  if ( !myPreviewMode )
   {
-    myMesh->GetMeshDS()->Modified();
-    myMesh->SetIsModified( true );
+    if ( theTargetMesh )
+    {
+      theTargetMesh->GetMeshDS()->Modified();
+    }
+    else
+    {
+      myMesh->GetMeshDS()->Modified();
+      myMesh->SetIsModified( true );
+    }
   }
+
   return theMakeGroups ? getGroups(groupIds.get()) : 0;
 }
 
@@ -4093,6 +4243,8 @@ void SMESH_MeshEditor_i::MergeEqualElements()
   ::SMESH_MeshEditor anEditor( myMesh );
   anEditor.MergeEqualElements();
 
+  myMesh->GetMeshDS()->Modified();
+
   TPythonDump() << this << ".MergeEqualElements()";
 }
 
@@ -4281,7 +4433,7 @@ SMESH::long_array* SMESH_MeshEditor_i::FindElementsByPoint(CORBA::Double      x,
     res[i] = foundElems[i]->GetID();
 
   if ( !myPreviewMode ) // call from tui
-    TPythonDump() << res << " = " << this << ".FindElementsByPoint( "
+    TPythonDump() << "res = " << this << ".FindElementsByPoint( "
                   << x << ", "
                   << y << ", "
                   << z << ", "
@@ -4290,6 +4442,77 @@ SMESH::long_array* SMESH_MeshEditor_i::FindElementsByPoint(CORBA::Double      x,
   return res._retn();
 }
 
+//=======================================================================
+//function : FindAmongElementsByPoint
+//purpose  : Searching among the given elements, return elements of given type 
+//           where the given point is IN or ON.
+//           'ALL' type means elements of any type excluding nodes
+//=======================================================================
+
+SMESH::long_array*
+SMESH_MeshEditor_i::FindAmongElementsByPoint(SMESH::SMESH_IDSource_ptr elementIDs,
+                                             CORBA::Double             x,
+                                             CORBA::Double             y,
+                                             CORBA::Double             z,
+                                             SMESH::ElementType        type)
+{
+  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 ) &&
+       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 );
+
+  string partIOR = SMESH_Gen_i::GetORB()->object_to_string( elementIDs );
+  if ( SMESH_Group_i* group_i = SMESH::DownCast<SMESH_Group_i*>( elementIDs ))
+    // take into account passible group modification
+    partIOR += SMESH_Comment( ((SMESHDS_Group*)group_i->GetGroupDS())->SMDSGroup().Tic() );
+  partIOR += SMESH_Comment( type );
+
+  TIDSortedElemSet elements; // elems should live until FindElementsByPoint() finishes
+
+  theSearchersDeleter.Set( myMesh, partIOR );
+  if ( !theElementSearcher )
+  {
+    // create a searcher from elementIDs
+    SMESH::SMESH_Mesh_var mesh = elementIDs->GetMesh();
+    SMESHDS_Mesh* meshDS = SMESH::DownCast<SMESH_Mesh_i*>( mesh )->GetImpl().GetMeshDS();
+
+    if ( !idSourceToSet( elementIDs, meshDS, elements,
+                         SMDSAbs_ElementType(type), /*emptyIfIsMesh=*/true))
+      return res._retn();
+
+    typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator > TIter;
+    SMDS_ElemIteratorPtr elemsIt( new TIter( elements.begin(), elements.end() ));
+
+    ::SMESH_MeshEditor anEditor( myMesh );
+    theElementSearcher = anEditor.GetElementSearcher(elemsIt);
+  }
+
+  vector< const SMDS_MeshElement* > foundElems;
+
+  theElementSearcher->FindElementsByPoint( gp_Pnt( x,y,z ),
+                                           SMDSAbs_ElementType( type ),
+                                           foundElems);
+  res->length( foundElems.size() );
+  for ( int i = 0; i < foundElems.size(); ++i )
+    res[i] = foundElems[i]->GetID();
+
+  if ( !myPreviewMode ) // call from tui
+    TPythonDump() << "res = " << this << ".FindAmongElementsByPoint( "
+                  << elementIDs << ", "
+                  << x << ", "
+                  << y << ", "
+                  << z << ", "
+                  << type << " )";
+
+  return res._retn();
+  
+}
 //=======================================================================
 //function : GetPointState
 //purpose  : Return point state in a closed 2D mesh in terms of TopAbs_State enumeration.
@@ -5580,6 +5803,7 @@ CORBA::Boolean SMESH_MeshEditor_i::Make2DMeshFrom3D()
  * The nodes of the internal faces at the boundaries of the groups are doubled.
  * In option, the internal faces are replaced by flat elements.
  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
+ * The flat elements are stored in groups of volumes.
  * @param theDomains - list of groups of volumes
  * @param createJointElems - if TRUE, create the elements
  * @return TRUE if operation has been completed successfully, FALSE otherwise
@@ -5588,6 +5812,7 @@ CORBA::Boolean SMESH_MeshEditor_i::Make2DMeshFrom3D()
 
 CORBA::Boolean SMESH_MeshEditor_i::DoubleNodesOnGroupBoundaries( const SMESH::ListOfGroups& theDomains,
                                                                  CORBA::Boolean createJointElems )
+  throw (SALOME::SALOME_Exception)
 {
   initData();
 
@@ -5601,8 +5826,10 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodesOnGroupBoundaries( const SMESH::Li
   for ( int i = 0, n = theDomains.length(); i < n; i++ )
   {
     SMESH::SMESH_GroupBase_var aGrp = theDomains[ i ];
-    if ( !CORBA::is_nil( aGrp ) && ( aGrp->GetType() != SMESH::NODE ) )
+    if ( !CORBA::is_nil( aGrp ) /*&& ( aGrp->GetType() != SMESH::NODE )*/ )
     {
+//      if ( aGrp->GetType() != SMESH::VOLUME )
+//        THROW_SALOME_CORBA_EXCEPTION("Not a volume group", SALOME::BAD_PARAM);
       TIDSortedElemSet domain;
       domain.clear();
       domains.push_back(domain);
@@ -5612,6 +5839,7 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodesOnGroupBoundaries( const SMESH::Li
   }
 
   bool aResult = aMeshEditor.DoubleNodesOnGroupBoundaries( domains, createJointElems );
+  // TODO publish the groups of flat elements in study
 
   storeResult( aMeshEditor) ;
   myMesh->GetMeshDS()->Modified();
@@ -5622,6 +5850,53 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodesOnGroupBoundaries( const SMESH::Li
   return aResult;
 }
 
+//================================================================================
+/*!
+ * \brief Double nodes on some external faces and create flat elements.
+ * Flat elements are mainly used by some types of mechanic calculations.
+ *
+ * Each group of the list must be constituted of faces.
+ * Triangles are transformed in prisms, and quadrangles in hexahedrons.
+ * @param theGroupsOfFaces - list of groups of faces
+ * @return TRUE if operation has been completed successfully, FALSE otherwise
+ */
+//================================================================================
+
+CORBA::Boolean SMESH_MeshEditor_i::CreateFlatElementsOnFacesGroups( const SMESH::ListOfGroups& theGroupsOfFaces )
+{
+  initData();
+
+  ::SMESH_MeshEditor aMeshEditor( myMesh );
+
+  SMESHDS_Mesh* aMeshDS = GetMeshDS();
+
+  vector<TIDSortedElemSet> faceGroups;
+  faceGroups.clear();
+
+  for ( int i = 0, n = theGroupsOfFaces.length(); i < n; i++ )
+  {
+    SMESH::SMESH_GroupBase_var aGrp = theGroupsOfFaces[ i ];
+    if ( !CORBA::is_nil( aGrp ) && ( aGrp->GetType() != SMESH::NODE ) )
+    {
+      TIDSortedElemSet faceGroup;
+      faceGroup.clear();
+      faceGroups.push_back(faceGroup);
+      SMESH::long_array_var anIDs = aGrp->GetIDs();
+      arrayToSet( anIDs, aMeshDS, faceGroups[ i ], SMDSAbs_All );
+    }
+  }
+
+  bool aResult = aMeshEditor.CreateFlatElementsOnFacesGroups( faceGroups );
+  // TODO publish the groups of flat elements in study
+
+  storeResult( aMeshEditor) ;
+  myMesh->GetMeshDS()->Modified();
+
+  // Update Python script
+  TPythonDump() << "isDone = " << this << ".CreateFlatElementsOnFacesGroups( " << &theGroupsOfFaces << " )";
+  return aResult;
+}
+
 // issue 20749 ===================================================================
 /*!
  * \brief Creates missing boundary elements
@@ -5750,20 +6025,37 @@ CORBA::Long SMESH_MeshEditor_i::MakeBoundaryElements(SMESH::Bnd_Dimension dim,
   if ( dim > SMESH::BND_1DFROM2D )
     THROW_SALOME_CORBA_EXCEPTION("Invalid boundary dimension", SALOME::BAD_PARAM);
 
-  // check that groups belongs to to this mesh and is not this mesh
-  const int nbGroups = groups.length();
-  for ( int i = 0; i < nbGroups; ++i )
+  // separate groups belonging to this and other mesh
+  SMESH::ListOfIDSources_var groupsOfThisMesh = new SMESH::ListOfIDSources;
+  SMESH::ListOfIDSources_var groupsOfOtherMesh = new SMESH::ListOfIDSources;
+  groupsOfThisMesh->length( groups.length() );
+  groupsOfOtherMesh->length( groups.length() );
+  int nbGroups = 0, nbGroupsOfOtherMesh = 0;
+  for ( int i = 0; i < groups.length(); ++i )
   {
     SMESH::SMESH_Mesh_var m = groups[i]->GetMesh();
     if ( myMesh_i != SMESH::DownCast<SMESH_Mesh_i*>( m ))
-      THROW_SALOME_CORBA_EXCEPTION("group does not belong to this mesh", SALOME::BAD_PARAM);
+      groupsOfOtherMesh[ nbGroupsOfOtherMesh++ ] = groups[i];
+    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);
   }
+  groupsOfThisMesh->length( nbGroups );
+  groupsOfOtherMesh->length( nbGroupsOfOtherMesh );
 
+  int nbAdded = 0;
   TPythonDump pyDump;
 
-  int nbAdded = 0;
+  if ( nbGroupsOfOtherMesh > 0 )
+  {
+    // process groups belonging to another mesh
+    SMESH::SMESH_Mesh_var    otherMesh = groupsOfOtherMesh[0]->GetMesh();
+    SMESH::SMESH_MeshEditor_var editor = otherMesh->GetMeshEditor();
+    nbAdded += editor->MakeBoundaryElements( dim, groupName, meshName, toCopyAll,
+                                             groupsOfOtherMesh, mesh, group );
+  }
+
   SMESH::SMESH_Mesh_var mesh_var;
   SMESH::SMESH_Group_var group_var;
 
@@ -5800,12 +6092,12 @@ CORBA::Long SMESH_MeshEditor_i::MakeBoundaryElements(SMESH::Bnd_Dimension dim,
 
   TIDSortedElemSet elements;
 
-  if ( nbGroups > 0 )
+  if ( groups.length() > 0 )
   {
     for ( int i = 0; i < nbGroups; ++i )
     {
       elements.clear();
-      if ( idSourceToSet( groups[i], srcMeshDS, elements, elemType,/*emptyIfIsMesh=*/false ))
+      if ( idSourceToSet( groupsOfThisMesh[i], srcMeshDS, elements, elemType,/*emptyIfIsMesh=*/0 ))
       {
         SMESH::Bnd_Dimension bdim = 
           ( elemType == SMDSAbs_Volume ) ? SMESH::BND_2DFROM3D : SMESH::BND_1DFROM2D;