Salome HOME
23258: [CEA 1804] Do not merge the middle nodes of quadratic elements
authoreap <eap@opencascade.com>
Tue, 14 Mar 2017 12:43:27 +0000 (15:43 +0300)
committereap <eap@opencascade.com>
Tue, 14 Mar 2017 12:43:27 +0000 (15:43 +0300)
    Add AvoidMakingHoles argument in MergeNodes()

+ Save mesh name in STL file
+ Fix binary STL export after change of sizeof(Standard_Boolean)
+ Add Import menu to mesh component popup

26 files changed:
doc/salome/gui/SMESH/images/mergenodes.png
doc/salome/gui/SMESH/images/mergenodes_auto.png
doc/salome/gui/SMESH/input/merging_nodes.doc
idl/SMESH_MeshEditor.idl
src/DriverSTL/DriverSTL_R_SMDS_Mesh.cxx
src/DriverSTL/DriverSTL_R_SMDS_Mesh.h
src/DriverSTL/DriverSTL_W_SMDS_Mesh.cxx
src/DriverSTL/DriverSTL_W_SMDS_Mesh.h
src/SMDS/SMDS_VolumeTool.cxx
src/SMDS/SMDS_VolumeTool.hxx
src/SMESH/SMESH_Mesh.cxx
src/SMESH/SMESH_Mesh.hxx
src/SMESH/SMESH_MeshEditor.cxx
src/SMESH/SMESH_MeshEditor.hxx
src/SMESHGUI/SMESHGUI.cxx
src/SMESHGUI/SMESHGUI_MergeDlg.cxx
src/SMESHGUI/SMESHGUI_MergeDlg.h
src/SMESHGUI/SMESHGUI_Operations.h
src/SMESHGUI/SMESH_msg_en.ts
src/SMESHUtils/CMakeLists.txt
src/SMESHUtils/SMESH_DeMerge.cxx [new file with mode: 0644]
src/SMESHUtils/SMESH_MeshAlgos.hxx
src/SMESH_I/SMESH_MeshEditor_i.cxx
src/SMESH_I/SMESH_MeshEditor_i.hxx
src/SMESH_I/SMESH_Mesh_i.cxx
src/SMESH_SWIG/smeshBuilder.py

index 5b34361..fc75f70 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/mergenodes.png and b/doc/salome/gui/SMESH/images/mergenodes.png differ
index 4f177f6..e270469 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/mergenodes_auto.png and b/doc/salome/gui/SMESH/images/mergenodes_auto.png differ
index 8aff9aa..872f9ee 100644 (file)
@@ -26,6 +26,9 @@ merging.</li>
     cells</b> check-box prevents merging medium nodes of quadratic
     elements with corner nodes. This check-box is enabled provided
     that the selected mesh includes quadratic elements.</li>
+<li>Activation of <b>Avoid making holes</b> check-box prevents merging
+    nodes that make elements invalid (but not degenerated) and hence
+    removed. Thus, no holes in place of removed elements appear. </li>
 <li><b>Exclude groups from detection</b> group allows to ignore the
   nodes which belong to the specified mesh groups. This control is
   active provided that the mesh includes groups.</li>
index 577e39c..ea19b5e 100644 (file)
@@ -689,7 +689,8 @@ module SMESH
       raises (SALOME::SALOME_Exception);
 
     void MergeNodes (in array_of_long_array    GroupsOfNodes,
-                     in SMESH::ListOfIDSources NodesToKeep)
+                     in SMESH::ListOfIDSources NodesToKeep,
+                     in boolean                AvoidMakingHoles)
       raises (SALOME::SALOME_Exception);
 
     /*!
index 9a53166..ae7e48c 100644 (file)
@@ -69,7 +69,7 @@ namespace
   };
   typedef NCollection_DataMap<gp_Pnt,SMDS_MeshNode*,Hasher> TDataMapOfPntNodePtr;
 
-  const int HEADER_SIZE           = 84;
+  const int HEADER_SIZE           = 84; // 80 chars + int
   const int SIZEOF_STL_FACET      = 50;
   const int ASCII_LINES_PER_FACET = 7;
   const int SIZE_OF_FLOAT         = 4;
@@ -150,11 +150,12 @@ Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::Perform()
 static Standard_Real readFloat(SMESH_File& theFile)
 {
   union {
-    Standard_Boolean i;
-    Standard_ShortReal f;
+    int   i;
+    float f;
   } u;
 
   const char* c = theFile;
+  u.i  = 0;
   u.i  =  c[0] & 0xFF;
   u.i |= (c[1] & 0xFF) << 0x08;
   u.i |= (c[2] & 0xFF) << 0x10;
@@ -205,30 +206,48 @@ static SMDS_MeshNode* readNode(SMESH_File& theFile,
 
 //=======================================================================
 //function : readAscii
-//purpose  : 
+//purpose  :
 //=======================================================================
 
 Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::readAscii(SMESH_File& theFile) const
 {
   Status aResult = DRS_OK;
 
+  // get a solid name
+  if ( strncmp( "solid ", theFile, strlen("solid ")) == 0 ) // not empty
+  {
+    const char * header = theFile;
+    std::string& name = const_cast<std::string&>( myName );
+    for ( header += strlen("solid "); !iscntrl( *header ); ++header )
+      name.push_back( *header );
+
+    std::string::iterator i = name.begin();
+    while ( i != name.end() && isspace( *i )) ++i;
+    name.erase( name.begin(), i );
+
+    size_t n = name.size();
+    while ( n > 0 && isspace( name[ n - 1 ] )) --n;
+    name.resize( n );
+  }
+
   // get the file size
   long filesize = theFile.size();
   theFile.close();
 
-  // Open the file 
+  // Open the file
   FILE* file = fopen( myFile.c_str(),"r");
 
   // count the number of lines
   Standard_Integer nbLines = 0;
-  for (long ipos = 0; ipos < filesize; ++ipos) {
+  for (long ipos = 0; ipos < filesize; ++ipos)
+  {
     if (getc(file) == '\n')
       nbLines++;
   }
 
   // go back to the beginning of the file
   rewind(file);
-  
+
   Standard_Integer nbTri = (nbLines / ASCII_LINES_PER_FACET);
 
   TDataMapOfPntNodePtr uniqnodes;
@@ -236,8 +255,8 @@ Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::readAscii(SMESH_File& theFile) const
   while (getc(file) != '\n');
 
   // main reading
-  for (Standard_Integer iTri = 0; iTri < nbTri; ++iTri) {
-
+  for (Standard_Integer iTri = 0; iTri < nbTri; ++iTri)
+  {
     // skipping the facet normal
     Standard_ShortReal normal[3];
     fscanf(file,"%*s %*s %f %f %f\n",&normal[0],&normal[1],&normal[2]);
@@ -278,7 +297,7 @@ Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::readBinary(SMESH_File& file) const
 
   long filesize = file.size();
 
-  if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET !=0 
+  if ( (filesize - HEADER_SIZE) % SIZEOF_STL_FACET != 
       // Commented to allow reading small files (ex: 1 face)
       /*|| (filesize < STL_MIN_FILE_SIZE)*/) {
     Standard_NoMoreObject::Raise("DriverSTL_R_SMDS_MESH::readBinary (wrong file size)");
@@ -288,6 +307,19 @@ Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::readBinary(SMESH_File& file) const
   // sometimes it is wrong, and with this technique we don't need to swap endians for integer
   Standard_Integer nbTri = ((filesize - HEADER_SIZE) / SIZEOF_STL_FACET);
 
+  // get a solid name
+  if ( strncmp( "name: ", file, strlen("name: ")) == 0 ) // name present
+  {
+    const char * header = file;
+    std::string&   name = const_cast<std::string&>( myName );
+    header += strlen("name: ");
+    name.assign( header, HEADER_SIZE - strlen("name: ") - 4);
+
+    size_t n = name.size();
+    while ( n > 0 && isspace( name[ n - 1 ] )) --n;
+    name.resize( n );
+  }
+
   // skip the header
   file += HEADER_SIZE;
 
index 5459b51..d30366b 100644 (file)
@@ -35,16 +35,18 @@ class MESHDRIVERSTL_EXPORT DriverSTL_R_SMDS_Mesh: public Driver_SMDS_Mesh
   DriverSTL_R_SMDS_Mesh();
   virtual Status Perform();
   void           SetIsCreateFaces( const bool theIsCreate = true );
+  std::string    GetName() const { return myName; }
   
  private:
   // PRIVATE METHODS
-  Status           readAscii (SMESH_File& file) const;
-  Status           readBinary(SMESH_File& file) const;
-  
+  Status         readAscii (SMESH_File& file) const;
+  Status         readBinary(SMESH_File& file) const;
+
  private:
   // PRIVATE FIELDS
-  bool myIsCreateFaces;
-  bool myIsAscii;
+  bool        myIsCreateFaces;
+  bool        myIsAscii;
+  std::string myName;
 };
 
 #endif
index cb42a32..333c72d 100644 (file)
@@ -176,7 +176,7 @@ static void writeFloat( const Standard_ShortReal& theVal, SMESH_File& ofile)
 {
   union {
     Standard_ShortReal f;
-    char c[4]; 
+    char c[4];
   } u;
 
   u.f = theVal;
@@ -485,7 +485,8 @@ Driver_Mesh::Status DriverSTL_W_SMDS_Mesh::writeAscii() const
   SMESH_File aFile( myFile, /*openForReading=*/false );
   aFile.openForWriting();
 
-  std::string buf("solid\n");
+  std::string buf("solid ");
+  buf += myName + "\n";
   aFile.writeRaw( buf.c_str(), buf.size() );
 
   char sval[128];
@@ -520,7 +521,8 @@ Driver_Mesh::Status DriverSTL_W_SMDS_Mesh::writeAscii() const
                       " endfacet\n", 21 );
     }
   }
-  aFile.writeRaw ("endsolid\n" , 9 );
+  buf = "endsolid " + myName + "\n";
+  aFile.writeRaw( buf.c_str(), buf.size() );
 
   return aResult;
 }
@@ -554,6 +556,11 @@ Driver_Mesh::Status DriverSTL_W_SMDS_Mesh::writeBinary() const
     }
   }
   std::string sval( LABEL_SIZE, ' ' );
+  if ( !myName.empty() )
+  {
+    sval = "name: " + myName;
+    sval.resize( LABEL_SIZE, ' ' );
+  }
   aFile.write( sval.c_str(), LABEL_SIZE );
 
   // write number of triangles
index efc027b..66c4741 100644 (file)
@@ -45,6 +45,7 @@ class MESHDRIVERSTL_EXPORT DriverSTL_W_SMDS_Mesh: public Driver_SMDS_Mesh
   ~DriverSTL_W_SMDS_Mesh();
   virtual Status Perform();
   void    SetIsAscii( const bool theIsAscii = false );
+  void    SetName( const std::string name ) { myName = name; }
 
  private:
   // PRIVATE METHODS
@@ -56,7 +57,8 @@ class MESHDRIVERSTL_EXPORT DriverSTL_W_SMDS_Mesh: public Driver_SMDS_Mesh
 
  private:
   // PRIVATE FIELDS
-  bool myIsAscii;
+  bool        myIsAscii;
+  std::string myName;
   int                                  myNbVolumeTrias;
   std::vector<const SMDS_MeshElement*> myVolumeFacets; // tmp faces
 };
index d7dd7f8..f7857c3 100644 (file)
@@ -471,8 +471,9 @@ SMDS_VolumeTool::~SMDS_VolumeTool()
 //purpose  : Set volume to iterate on
 //=======================================================================
 
-bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume,
-                           const bool              ignoreCentralNodes)
+bool SMDS_VolumeTool::Set (const SMDS_MeshElement*                  theVolume,
+                           const bool                               ignoreCentralNodes,
+                           const std::vector<const SMDS_MeshNode*>* otherNodes)
 {
   // reset fields
   myVolume = 0;
@@ -510,11 +511,22 @@ bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume,
   }
 
   // set nodes
-  int iNode = 0;
   myVolumeNodes.resize( myVolume->NbNodes() );
-  SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator();
-  while ( nodeIt->more() )
-    myVolumeNodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
+  if ( otherNodes )
+  {
+    if ( otherNodes->size() != myVolumeNodes.size() )
+      return ( myVolume = 0 );
+    for ( size_t i = 0; i < otherNodes->size(); ++i )
+      if ( ! ( myVolumeNodes[i] = (*otherNodes)[0] ))
+        return ( myVolume = 0 );
+  }
+  else
+  {
+    int iNode = 0;
+    SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator();
+    while ( nodeIt->more() )
+      myVolumeNodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
+  }
 
   // check validity
   if ( !setFace(0) )
index b42a0ae..5c2c882 100644 (file)
@@ -58,15 +58,17 @@ class SMDS_EXPORT SMDS_VolumeTool
 
   SMDS_VolumeTool ();
   ~SMDS_VolumeTool ();
-  SMDS_VolumeTool (const SMDS_MeshElement* theVolume,
-                   const bool              ignoreCentralNodes=true);
+  SMDS_VolumeToolconst SMDS_MeshElement* theVolume,
+                   const bool              ignoreCentralNodes = true);
 
-  bool Set (const SMDS_MeshElement* theVolume,
-            const bool              ignoreCentralNodes=true);
+  bool Set( const SMDS_MeshElement*                  theVolume,
+            const bool                               ignoreCentralNodes = true,
+            const std::vector<const SMDS_MeshNode*>* nodes = NULL);
   // Set volume.
   // Return false if theVolume is not of type SMDSAbs_Volume.
   // ignoreCentralNodes makes skip nodes at face centers when returning
-  // nodes of faces of SMDSEntity_TriQuad_Hexa
+  // nodes of faces of SMDSEntity_TriQuad_Hexa.
+  // alternative nodes can be provided
 
   const SMDS_MeshVolume* Element() const;
   // return element
@@ -91,10 +93,10 @@ class SMDS_EXPORT SMDS_VolumeTool
   // top and bottom faces are reversed.
   // Result of IsForward() and methods returning nodes change
 
-  const SMDS_MeshNode** GetNodes() { return &myVolumeNodes[0]; }
+  const SMDS_MeshNode** GetNodes() const { return (const SMDS_MeshNode**) &myVolumeNodes[0]; }
   // Return array of volume nodes
 
-  int NbNodes() { return myVolumeNodes.size(); }
+  int NbNodes() const { return myVolumeNodes.size(); }
   // Return array of volume nodes
 
   double GetSize() const;
index 98725ba..893a28a 100644 (file)
@@ -559,7 +559,7 @@ int SMESH_Mesh::MEDToMesh(const char* theFileName, const char* theMeshName)
 //purpose  : 
 //=======================================================================
 
-int SMESH_Mesh::STLToMesh(const char* theFileName)
+std::string SMESH_Mesh::STLToMesh(const char* theFileName)
 {
   if(_isShapeToMesh)
     throw SALOME_Exception(LOCALIZED("a shape to mesh has already been defined"));
@@ -571,7 +571,7 @@ int SMESH_Mesh::STLToMesh(const char* theFileName)
   myReader.SetMeshId(-1);
   myReader.Perform();
 
-  return 1;
+  return myReader.GetName();
 }
 
 //================================================================================
@@ -1560,6 +1560,7 @@ void SMESH_Mesh::ExportUNV(const char *        file,
 
 void SMESH_Mesh::ExportSTL(const char *        file,
                            const bool          isascii,
+                           const char *        name,
                            const SMESHDS_Mesh* meshPart) throw(SALOME_Exception)
 {
   Unexpect aCatch(SalomeException);
@@ -1568,6 +1569,7 @@ void SMESH_Mesh::ExportSTL(const char *        file,
   myWriter.SetIsAscii( isascii );
   myWriter.SetMesh( meshPart ? (SMESHDS_Mesh*) meshPart : _myMeshDS);
   myWriter.SetMeshId(_id);
+  if ( name ) myWriter.SetName( name );
   myWriter.Perform();
 }
 
index 1fd14f3..a8ec5df 100644 (file)
@@ -121,7 +121,7 @@ class SMESH_EXPORT SMESH_Mesh
 
   int MEDToMesh(const char* theFileName, const char* theMeshName);
   
-  int STLToMesh(const char* theFileName);
+  std::string STLToMesh(const char* theFileName);
 
   int CGNSToMesh(const char* theFileName, const int theMeshIndex, std::string& theMeshName);
   
@@ -263,6 +263,7 @@ class SMESH_EXPORT SMESH_Mesh
                  const SMESHDS_Mesh* meshPart = 0) throw(SALOME_Exception);
   void ExportSTL(const char *        file,
                  const bool          isascii,
+                 const char *        name = 0,
                  const SMESHDS_Mesh* meshPart = 0) throw(SALOME_Exception);
   void ExportCGNS(const char *        file,
                   const SMESHDS_Mesh* mesh,
index f4dd0d7..38b2eeb 100644 (file)
@@ -6095,7 +6095,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;
@@ -6140,7 +6140,7 @@ 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
@@ -6183,7 +6183,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);
 }
 
@@ -6322,7 +6322,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();
@@ -6380,7 +6380,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;
@@ -6439,7 +6439,7 @@ 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
@@ -6475,17 +6475,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
+//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)
@@ -6536,11 +6536,11 @@ SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
 
 
 //=======================================================================
-//function : MakeExtrElements
+//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,
@@ -6553,7 +6553,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;
@@ -6746,11 +6746,11 @@ SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets
 
 
 //=======================================================================
-//function : LinearAngleVariation
+//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();
@@ -7258,7 +7258,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
   {
@@ -7351,11 +7351,12 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
   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
 
@@ -7369,17 +7370,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();
@@ -7387,469 +7377,548 @@ 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++;
     }
+  }
 
-    // Analyse element topology after replacement
+  // Remove bad elements, then equal nodes (order important)
+  Remove( rmElemIds, false );
+  Remove( rmNodeIds, true );
 
-    bool isOk = true;
-    int nbUniqueNodes = nodeSet.size();
-    if ( nbNodes != nbUniqueNodes ) // some nodes stick
-    {
-      if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
-      {
-        if ( elem->GetType() == SMDSAbs_Face ) // Polygon
-        {
-          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)
-          {
-            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 ));
+  return;
+}
 
-              SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
-              if ( aShapeId )
-                aMesh->SetMeshElementOnShape(newElem, aShapeId);
-            }
-          }
-          rmElemIds.push_back(elem->GetID());
+//=======================================================================
+//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
+//=======================================================================
 
-        } // Polygon
+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
 
-        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();
+  newElemDefs.resize(nbResElems);
+  newElemDefs[0].Init( elem );
+  newElemDefs[0].myNodes.clear();
 
-              vector<const SMDS_MeshNode *> poly_nodes;
-              vector<int>                   quantities;
-              vector<const SMDS_MeshNode *> faceNodes;
+  set<const SMDS_MeshNode*> nodeSet;
+  vector< const SMDS_MeshNode*>   curNodes;
+  vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
+  vector<int> iRepl;
 
-              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);
-              }
+  const        int  nbNodes = elem->NbNodes();
+  SMDSAbs_EntityType entity = elem->GetEntityType();
 
-              if ( quantities.size() > 3 ) {
-                // TODO: remove coincident faces
-              }
+  curNodes.resize( nbNodes );
+  uniqueNodes.resize( nbNodes );
+  iRepl.resize( nbNodes );
+  int iUnique = 0, iCur = 0, nbRepl = 0;
 
-              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() );
-            }
-          }
-        }
-        else {
-        }
+  // Get new seq of nodes
 
-        continue;
-      } // poly element
+  SMDS_ElemIteratorPtr itN = elem->nodesIterator();
+  while ( itN->more() )
+  {
+    const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
 
-      // 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;
-      }
-      case SMDSEntity_Quad_Edge:
-      {
-        isOk = false; // to linear EDGE ???????
-        break;
-      }
-      case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
+    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
+
+  int nbUniqueNodes = nodeSet.size();
+  if ( nbNodes != nbUniqueNodes ) // some nodes stick
+  {
+    toRemove = true;
+    nbResElems = 0;
+
+    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 );
+
+      // 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++ )
       {
-        if ( nbUniqueNodes < 3 )
-          isOk = false;
-        else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
-          isOk = false; // opposite nodes stick
-        break;
-      }
-      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] )))
-        {
-          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] )))
+        ElemFeatures* elemType = & newElemDefs[iface];
+        if ( iface ) elemType->Init( elem );
+
+        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
         {
-          isOk = true;
-        }
-        break;
-      }
-      case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
+          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
+
+    case SMDSEntity_Polyhedra: // Polyhedral volume
+    {
+      if ( nbUniqueNodes >= 4 )
       {
-        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] )
+        // 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();
+
+          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();
+
+          for (int iface = 1; iface <= nbFaces; iface++)
           {
-            uniqueNodes[ 0 ] = curNodes[ 0 ];
-            uniqueNodes[ 1 ] = curNodes[ 2 ];
-            uniqueNodes[ 2 ] = curNodes[ 5 ];
-            uniqueNodes[ 3 ] = curNodes[ 3 ];
-            uniqueNodes[ 4 ] = curNodes[ 1 ];
-            isOk = true;
+            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);
           }
-          if ( curNodes[2] == curNodes[5] )
+
+          if ( quantities.size() > 3 )
           {
-            uniqueNodes[ 0 ] = curNodes[ 0 ];
-            uniqueNodes[ 1 ] = curNodes[ 3 ];
-            uniqueNodes[ 2 ] = curNodes[ 4 ];
-            uniqueNodes[ 3 ] = curNodes[ 1 ];
-            uniqueNodes[ 4 ] = curNodes[ 2 ];
-            isOk = true;
+            // TODO: remove coincident faces
+            nbResElems = 1;
+            nbUniqueNodes = newElemDefs[0].myNodes.size();
           }
         }
-        break;
       }
-      case SMDSEntity_Hexa:
+    }
+    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 && 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] )))
       {
-        //////////////////////////////////// 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;
-              }
-              break;
-            }
-          }
+        toRemove = false;
+      }
+      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] )))
+      {
+        toRemove = false;
+      }
+      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] )
+        {
+          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] )
+        {
+          uniqueNodes[ 0 ] = curNodes[ 0 ];
+          uniqueNodes[ 1 ] = curNodes[ 3 ];
+          uniqueNodes[ 2 ] = curNodes[ 4 ];
+          uniqueNodes[ 3 ] = curNodes[ 1 ];
+          uniqueNodes[ 4 ] = curNodes[ 2 ];
+          toRemove = false;
         }
-        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;
+    }
+    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;
           }
         }
-        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;
+      }
+      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;
+          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 ]]);
+              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;
 }
 
 
index fd0418c..663b6ae 100644 (file)
@@ -83,11 +83,12 @@ public:
   // --------------------------------------------------------------------------------
   struct ElemFeatures //!< Features of element to create
   {
-    SMDSAbs_ElementType myType;
-    bool                myIsPoly, myIsQuad;
-    int                 myID;
-    double              myBallDiameter;
-    std::vector<int>    myPolyhedQuantities;
+    SMDSAbs_ElementType               myType;
+    bool                              myIsPoly, myIsQuad;
+    int                               myID;
+    double                            myBallDiameter;
+    std::vector<int>                  myPolyhedQuantities;
+    std::vector<const SMDS_MeshNode*> myNodes; // not managed by ElemFeatures
 
     SMESH_EXPORT ElemFeatures( SMDSAbs_ElementType type=SMDSAbs_All, bool isPoly=false, bool isQuad=false )
       :myType( type ), myIsPoly(isPoly), myIsQuad(isQuad), myID(-1), myBallDiameter(0) {}
@@ -751,6 +752,20 @@ public:
                     SMESH_SequenceOfElemPtr&                   srcElements);
 
   /*!
+   * \brief Computes 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 applyMerge( const SMDS_MeshElement*      elems,
+                   std::vector< ElemFeatures >& newElemDefs,
+                   TNodeNodeMap&                nodeNodeMap,
+                   const bool                   avoidMakingHoles );
+  /*!
    * \brief Create 1D and 2D elements around swept elements
    * \param mapNewNodes - source nodes and ones generated from them
    * \param newElemsMap - source elements and ones generated from them
@@ -782,11 +797,11 @@ public:
     double        Angle       ()const               { return myAngle; }
     double        Parameter   ()const               { return myPrm; }
   };
-  Extrusion_Error MakeEdgePathPoints(std::list<double>&                     aPrms,
+  Extrusion_Error makeEdgePathPoints(std::list<double>&                     aPrms,
                                      const TopoDS_Edge&                     aTrackEdge,
                                      bool                                   aFirstIsStart,
                                      std::list<SMESH_MeshEditor_PathPoint>& aLPP);
-  Extrusion_Error MakeExtrElements(TIDSortedElemSet                       theElements[2],
+  Extrusion_Error makeExtrElements(TIDSortedElemSet                       theElements[2],
                                    std::list<SMESH_MeshEditor_PathPoint>& theFullList,
                                    const bool                             theHasAngles,
                                    std::list<double>&                     theAngles,
@@ -794,7 +809,7 @@ public:
                                    const bool                             theHasRefPoint,
                                    const gp_Pnt&                          theRefPoint,
                                    const bool                             theMakeGroups);
-  static void LinearAngleVariation(const int          NbSteps,
+  static void linearAngleVariation(const int          NbSteps,
                                    std::list<double>& theAngles);
 
   bool doubleNodes( SMESHDS_Mesh*           theMeshDS,
index 41d4c42..1f34352 100644 (file)
@@ -215,29 +215,34 @@ namespace
     QStringList filter;
     std::string myExtension;
 
-    if ( theCommandID == SMESHOp::OpImportMED ) {
+    if ( theCommandID == SMESHOp::OpImportMED ||
+         theCommandID == SMESHOp::OpPopupImportMED ) {
       filter.append( QObject::tr( "MED_FILES_FILTER" ) + " (*.*med)" );
       filter.append( QObject::tr( "ALL_FILES_FILTER" ) + " (*)" );
     }
-    else if ( theCommandID == SMESHOp::OpImportUNV ) {
+    else if ( theCommandID == SMESHOp::OpImportUNV ||
+              theCommandID == SMESHOp::OpPopupImportUNV ) {
       filter.append( QObject::tr( "IDEAS_FILES_FILTER" ) + " (*.unv)" );
     }
-    else if ( theCommandID == SMESHOp::OpImportDAT ) {
+    else if ( theCommandID == SMESHOp::OpImportDAT ||
+              theCommandID == SMESHOp::OpPopupImportDAT ) {
       filter.append( QObject::tr( "DAT_FILES_FILTER" ) + " (*.dat)" );
     }
-    else if ( theCommandID == SMESHOp::OpImportSTL ) {
+    else if ( theCommandID == SMESHOp::OpImportSTL ||
+              theCommandID == SMESHOp::OpPopupImportSTL ) {
       filter.append( QObject::tr( "STL_FILES_FILTER" ) + " (*.stl)" );
     }
-  #ifdef WITH_CGNS
-    else if ( theCommandID == SMESHOp::OpImportCGNS ) {
+    else if ( theCommandID == SMESHOp::OpImportCGNS ||
+              theCommandID == SMESHOp::OpPopupImportCGNS ) {
       filter.append( QObject::tr( "CGNS_FILES_FILTER" ) + " (*.cgns)" );
     }
-  #endif
-    else if ( theCommandID == SMESHOp::OpImportSAUV ) {
+    else if ( theCommandID == SMESHOp::OpImportSAUV ||
+              theCommandID == SMESHOp::OpPopupImportSAUV ) {
       filter.append( QObject::tr( "SAUV files (*.sauv*)" ) );
       filter.append( QObject::tr( "All files (*)" ) );
     }
-    else if ( theCommandID == SMESHOp::OpImportGMF ) {
+    else if ( theCommandID == SMESHOp::OpImportGMF ||
+              theCommandID == SMESHOp::OpPopupImportGMF ) {
       filter.append( QObject::tr( "GMF_ASCII_FILES_FILTER" ) + " (*.mesh)"  );
       filter.append( QObject::tr( "GMF_BINARY_FILES_FILTER") + " (*.meshb)" );
     }
@@ -283,6 +288,7 @@ namespace
         try {
           switch ( theCommandID ) {
           case SMESHOp::OpImportDAT:
+          case SMESHOp::OpPopupImportDAT:
             {
               // DAT format (currently unsupported)
               errors.append( QString( "%1 :\n\t%2" ).arg( filename ).
@@ -290,6 +296,7 @@ namespace
               break;
             }
           case SMESHOp::OpImportUNV:
+          case SMESHOp::OpPopupImportUNV:
             {
               // UNV format
               aMeshes->length( 1 );
@@ -300,6 +307,7 @@ namespace
               break;
             }
           case SMESHOp::OpImportMED:
+          case SMESHOp::OpPopupImportMED:
             {
               // MED format
               SMESH::DriverMED_ReadStatus res;
@@ -311,6 +319,7 @@ namespace
               break;
             }
           case SMESHOp::OpImportSTL:
+          case SMESHOp::OpPopupImportSTL:
             {
               // STL format
               aMeshes->length( 1 );
@@ -321,8 +330,8 @@ namespace
               }
               break;
             }
-        #ifdef WITH_CGNS
           case SMESHOp::OpImportCGNS:
+          case SMESHOp::OpPopupImportCGNS:
             {
               // CGNS format
               SMESH::DriverMED_ReadStatus res;
@@ -333,8 +342,8 @@ namespace
               }
               break;
             }
-        #endif
           case SMESHOp::OpImportSAUV:
+          case SMESHOp::OpPopupImportSAUV:
             {
               // SAUV format
               SMESH::DriverMED_ReadStatus res;
@@ -346,6 +355,7 @@ namespace
               break;
             }
           case SMESHOp::OpImportGMF:
+          case SMESHOp::OpPopupImportGMF:
             {
               // GMF format
               SMESH::ComputeError_var res;
@@ -433,12 +443,8 @@ namespace
                          theCommandID == SMESHOp::OpPopupExportUNV );
     const bool isSTL = ( theCommandID == SMESHOp::OpExportSTL ||
                          theCommandID == SMESHOp::OpPopupExportSTL );
-#ifdef WITH_CGNS
     const bool isCGNS= ( theCommandID == SMESHOp::OpExportCGNS ||
                          theCommandID == SMESHOp::OpPopupExportCGNS );
-#else
-    const bool isCGNS= false;
-#endif
     const bool isSAUV= ( theCommandID == SMESHOp::OpExportSAUV ||
                          theCommandID == SMESHOp::OpPopupExportSAUV );
     const bool isGMF = ( theCommandID == SMESHOp::OpExportGMF ||
@@ -2451,11 +2457,16 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
   case SMESHOp::OpImportUNV:
   case SMESHOp::OpImportMED:
   case SMESHOp::OpImportSTL:
-#ifdef WITH_CGNS
   case SMESHOp::OpImportCGNS:
-#endif
   case SMESHOp::OpImportSAUV:
   case SMESHOp::OpImportGMF:
+  case SMESHOp::OpPopupImportDAT:
+  case SMESHOp::OpPopupImportUNV:
+  case SMESHOp::OpPopupImportMED:
+  case SMESHOp::OpPopupImportSTL:
+  case SMESHOp::OpPopupImportCGNS:
+  case SMESHOp::OpPopupImportSAUV:
+  case SMESHOp::OpPopupImportGMF:
     {
       if(checkLock(aStudy)) break;
       ::ImportMeshesFromFile(GetSMESHGen(),theCommandID);
@@ -2484,18 +2495,14 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
   case SMESHOp::OpExportMED:
   case SMESHOp::OpExportUNV:
   case SMESHOp::OpExportSTL:
-#ifdef WITH_CGNS
   case SMESHOp::OpExportCGNS:
-#endif
   case SMESHOp::OpExportSAUV:
   case SMESHOp::OpExportGMF:
   case SMESHOp::OpPopupExportDAT:
   case SMESHOp::OpPopupExportMED:
   case SMESHOp::OpPopupExportUNV:
   case SMESHOp::OpPopupExportSTL:
-#ifdef WITH_CGNS
   case SMESHOp::OpPopupExportCGNS:
-#endif
   case SMESHOp::OpPopupExportSAUV:
   case SMESHOp::OpPopupExportGMF:
     {
@@ -2671,9 +2678,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
       extractContainers( sel_objects, to_process );
 
       try {
-#if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100
         OCC_CATCH_SIGNALS;
-#endif
         if (vtkwnd) {
           SALOME_ListIteratorOfListIO It( to_process );
           for ( ; It.More(); It.Next())
@@ -3821,13 +3826,21 @@ void SMESHGUI::initialize( CAM_Application* app )
   //createSMESHAction(  SMESHOp::OpImportDAT, "IMPORT_DAT", "", (Qt::CTRL+Qt::Key_B) );
   createSMESHAction( SMESHOp::OpImportUNV, "IMPORT_UNV", "", (Qt::CTRL+Qt::Key_I) );
   createSMESHAction( SMESHOp::OpImportMED, "IMPORT_MED", "", (Qt::CTRL+Qt::Key_M) );
-  //createSMESHAction(  114, "NUM" );
   createSMESHAction( SMESHOp::OpImportSTL, "IMPORT_STL"  );
 #ifdef WITH_CGNS
   createSMESHAction( SMESHOp::OpImportCGNS, "IMPORT_CGNS" );
 #endif
   createSMESHAction( SMESHOp::OpImportSAUV, "IMPORT_SAUV" );
   createSMESHAction( SMESHOp::OpImportGMF,  "IMPORT_GMF"  );
+  createSMESHAction( SMESHOp::OpPopupImportUNV, "IMPORT_UNV");
+  createSMESHAction( SMESHOp::OpPopupImportMED, "IMPORT_MED");
+  createSMESHAction( SMESHOp::OpPopupImportSTL, "IMPORT_STL"  );
+#ifdef WITH_CGNS
+  createSMESHAction( SMESHOp::OpPopupImportCGNS, "IMPORT_CGNS" );
+#endif
+  createSMESHAction( SMESHOp::OpPopupImportSAUV, "IMPORT_SAUV" );
+  createSMESHAction( SMESHOp::OpPopupImportGMF,  "IMPORT_GMF"  );
+
   createSMESHAction( SMESHOp::OpExportDAT,  "DAT" );
   createSMESHAction( SMESHOp::OpExportMED,  "MED" );
   createSMESHAction( SMESHOp::OpExportUNV,  "UNV" );
@@ -4364,6 +4377,7 @@ void SMESHGUI::initialize( CAM_Application* app )
     group   = pat.arg( SMESHGUI_Selection::typeName( SMESH::GROUP ) ),
     hypo    = pat.arg( SMESHGUI_Selection::typeName( SMESH::HYPOTHESIS ) ),
     algo    = pat.arg( SMESHGUI_Selection::typeName( SMESH::ALGORITHM ) ),
+    smesh   = pat.arg( SMESHGUI_Selection::typeName( SMESH::COMPONENT ) ),
     elems   = QString( "'%1' '%2' '%3' '%4' '%5' '%6'" ).
     arg( SMESHGUI_Selection::typeName( SMESH::SUBMESH_VERTEX ) ).
     arg( SMESHGUI_Selection::typeName( SMESH::SUBMESH_EDGE ) ).
@@ -4442,6 +4456,17 @@ void SMESHGUI::initialize( CAM_Application* app )
   createPopupItem( SMESHOp::OpPopupExportDAT,  OB, mesh_group, only_one_non_empty, anId );
   createPopupItem( SMESHOp::OpDelete,          OB, mesh_part + " " + hyp_alg );
   createPopupItem( SMESHOp::OpDeleteGroup,     OB, group );
+
+  anId = popupMgr()->insert( tr( "MEN_IMPORT" ), -1, -1 );        // IMPORT submenu
+  createPopupItem( SMESHOp::OpPopupImportMED,  OB, smesh, "", anId );
+  createPopupItem( SMESHOp::OpPopupImportUNV,  OB, smesh, "", anId );
+  createPopupItem( SMESHOp::OpPopupImportSTL,  OB, smesh, "", anId );
+#ifdef WITH_CGNS
+  createPopupItem( SMESHOp::OpPopupImportCGNS, OB, smesh, "", anId );
+#endif
+  createPopupItem( SMESHOp::OpPopupImportSAUV, OB, smesh, "", anId );
+  createPopupItem( SMESHOp::OpPopupImportGMF,  OB, smesh, "", anId );
+  createPopupItem( SMESHOp::OpPopupImportDAT,  OB, smesh, "", anId );
   popupMgr()->insert( separator(), -1, 0 );
 
   // popup for viewer
index e5dcf5d..2aba558 100644 (file)
@@ -191,6 +191,9 @@ SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction)
     SeparateCornersAndMedium = new QCheckBox(tr("SEPARATE_CORNERS_AND_MEDIUM"), NodeSpecWidget );
     SeparateCornersAndMedium->setEnabled( false );
 
+    AvoidMakingHoles = new QCheckBox(tr("AVOID_MAKING_HOLES"), NodeSpecWidget );
+    AvoidMakingHoles->setChecked( false );
+
     QGridLayout* NodeSpecLayout = new QGridLayout(NodeSpecWidget);
     NodeSpecLayout->setSpacing(SPACING);
     NodeSpecLayout->setMargin(0);
@@ -198,6 +201,7 @@ SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction)
     NodeSpecLayout->addWidget(TextLabelTolerance,       0, 0 );
     NodeSpecLayout->addWidget(SpinBoxTolerance,         0, 1 );
     NodeSpecLayout->addWidget(SeparateCornersAndMedium, 1, 0, 1, 2 );
+    NodeSpecLayout->addWidget(AvoidMakingHoles,         2, 0, 1, 2 );
 
     /***************************************************************/
     // Exclude groups
@@ -585,12 +589,12 @@ bool SMESHGUI_MergeDlg::ClickOnApply()
     }
 
     if( myAction == MERGE_NODES )
-      aMeshEditor->MergeNodes (aGroupsOfElements.inout(), nodesToKeep);
+      aMeshEditor->MergeNodes( aGroupsOfElements.inout(), nodesToKeep, AvoidMakingHoles->isChecked() );
     else
-      aMeshEditor->MergeElements (aGroupsOfElements.inout());
+      aMeshEditor->MergeElements( aGroupsOfElements.inout() );
 
     if ( myTypeId == TYPE_AUTO ) {
-      if (myAction == MERGE_NODES )
+      if ( myAction == MERGE_NODES )
         SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"),
                                      tr("SMESH_MERGED_NODES").arg(QString::number(ListCoincident->count()).toLatin1().data()));
       else
index 5e827ab..6b087b6 100644 (file)
@@ -130,6 +130,7 @@ private:
   QWidget*                  NodeSpecWidget;
   SMESHGUI_SpinBox*         SpinBoxTolerance;
   QCheckBox*                SeparateCornersAndMedium;
+  QCheckBox*                AvoidMakingHoles;
 
   QGroupBox*                GroupCoincident;
   //QWidget*                  GroupCoincidentWidget;
index df810f7..89f5347 100644 (file)
@@ -32,37 +32,36 @@ namespace SMESHOp {
     OpShowScalarBar          = 1022,   // SHOW SCALAR BAR
     OpSaveDistribution       = 1030,   // SAVE DISTRIBUTION
     OpShowDistribution       = 1031,   // SHOW DISTRIBUTION
-#ifndef DISABLE_PLOT2DVIEWER
     OpPlotDistribution       = 1032,   // PLOT DISTRIBUTION
-#endif
     OpFileInformation        = 1040,   // POPUP MENU - FILE INFORMATION
     // Import -------------------------//--------------------------------
     OpImportDAT              = 1100,   // MENU FILE - IMPORT - DAT FILE
     OpImportUNV              = 1101,   // MENU FILE - IMPORT - UNV FILE
     OpImportMED              = 1102,   // MENU FILE - IMPORT - MED FILE
     OpImportSTL              = 1103,   // MENU FILE - IMPORT - STL FILE
-#ifdef WITH_CGNS
     OpImportCGNS             = 1104,   // MENU FILE - IMPORT - CGNS FILE
-#endif
     OpImportSAUV             = 1105,   // MENU FILE - IMPORT - SAUV FILE
     OpImportGMF              = 1106,   // MENU FILE - IMPORT - GMF FILE
+    OpPopupImportDAT         = 1120,   // POPUP MENU - IMPORT - DAT FILE
+    OpPopupImportUNV         = 1121,   // POPUP MENU - IMPORT - UNV FILE
+    OpPopupImportMED         = 1122,   // POPUP MENU - IMPORT - MED FILE
+    OpPopupImportSTL         = 1123,   // POPUP MENU - IMPORT - STL FILE
+    OpPopupImportCGNS        = 1124,   // POPUP MENU - IMPORT - CGNS FILE
+    OpPopupImportSAUV        = 1125,   // POPUP MENU - IMPORT - SAUV FILE
+    OpPopupImportGMF         = 1126,   // POPUP MENU - IMPORT - GMF FILE
     // Export -------------------------//--------------------------------
     OpExportDAT              = 1200,   // MENU FILE  - EXPORT - DAT FILE
     OpExportMED              = 1201,   // MENU FILE  - EXPORT - MED FILE
     OpExportUNV              = 1202,   // MENU FILE  - EXPORT - UNV FILE
     OpExportSTL              = 1203,   // MENU FILE  - EXPORT - STL FILE
-#ifdef WITH_CGNS
     OpExportCGNS             = 1204,   // MENU FILE  - EXPORT - CGNS FILE
-#endif
     OpExportSAUV             = 1205,   // MENU FILE  - EXPORT - SAUV FILE
     OpExportGMF              = 1206,   // MENU FILE  - EXPORT - GMF FILE
     OpPopupExportDAT         = 1210,   // POPUP MENU - EXPORT - DAT FILE
     OpPopupExportMED         = 1211,   // POPUP MENU - EXPORT - MED FILE
     OpPopupExportUNV         = 1212,   // POPUP MENU - EXPORT - UNV FILE
     OpPopupExportSTL         = 1213,   // POPUP MENU - EXPORT - STL FILE
-#ifdef WITH_CGNS
     OpPopupExportCGNS        = 1214,   // POPUP MENU - EXPORT - CGNS FILE
-#endif
     OpPopupExportSAUV        = 1215,   // POPUP MENU - EXPORT - SAUV FILE
     OpPopupExportGMF         = 1216,   // POPUP MENU - EXPORT - GMF FILE
     // Mesh ---------------------------//--------------------------------
index bbbccbc..2a06018 100644 (file)
@@ -5323,6 +5323,10 @@ Please select a group and try again</translation>
         <translation>No merge of corner and medium nodes of quadratic cells</translation>
     </message>
     <message>
+        <source>AVOID_MAKING_HOLES</source>
+        <translation>Avoid making holes</translation>
+    </message>
+    <message>
         <source>KEEP_NODES</source>
         <translation>Nodes to keep during the merge</translation>
     </message>
index c21a8a6..3641d91 100644 (file)
@@ -83,7 +83,8 @@ SET(SMESHUtils_SOURCES
   SMESH_MAT2d.cxx
   SMESH_FreeBorders.cxx
   SMESH_ControlPnt.cxx
-)
+  SMESH_DeMerge.cxx
+  )
 
 # --- rules ---
 
diff --git a/src/SMESHUtils/SMESH_DeMerge.cxx b/src/SMESHUtils/SMESH_DeMerge.cxx
new file mode 100644 (file)
index 0000000..427ce52
--- /dev/null
@@ -0,0 +1,215 @@
+// 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
+//
+// 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, or (at your option) any later version.
+//
+// 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
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SMESH_DeMerge.hxx
+// Created   : Fri Mar 10 16:06:54 2017
+// Author    : Edward AGAPOV (eap)
+
+// Implementation of SMESH_MeshAlgos::DeMerge()
+
+#include "SMESH_MeshAlgos.hxx"
+
+#include "SMDS_VolumeTool.hxx"
+#include "SMDS_MeshVolume.hxx"
+
+namespace
+{
+  bool isDegenFace(const std::vector< const SMDS_MeshNode* >& nodes)
+  {
+    // in a degenerated face each link sticks to another
+
+    typedef std::map< SMESH_TLink , int > TLink2Nb;
+    TLink2Nb link2nb;
+    for ( size_t iPrev = nodes.size() - 1, i = 0; i < nodes.size(); iPrev = i++ )
+    {
+      SMESH_TLink link( nodes[iPrev], nodes[i] );
+      TLink2Nb::iterator l2n = link2nb.insert( std::make_pair( link, 0 )).first;
+      l2n->second++;
+    }
+
+    if ( link2nb.size() == 1 )
+      return true;
+
+    for ( TLink2Nb::iterator l2n = link2nb.begin(); l2n != link2nb.end(); ++l2n )
+      if ( l2n->second == 1 )
+        return false;
+
+    return true;
+  }
+
+  void deMergeFace(const SMDS_MeshElement*              face,
+                   std::vector< const SMDS_MeshNode* >& newNodes,
+                   std::vector< const SMDS_MeshNode* >& noMergeNodes)
+  {
+    if ( face->IsQuadratic() )
+    {
+      const int nbCorners = face->NbCornerNodes();
+      const int nbNodes = (int) newNodes.size();
+
+      // de-merge sticking medium nodes
+      for ( int i = 1; i < nbNodes; i += 2 ) // loop om medium nodes
+      {
+        int iPrev = ( i - 1 );
+        int iNext = ( i + 1 ) % nbNodes;
+        if ( newNodes[ iPrev ] == newNodes[ iNext ] )
+        {
+          if ( newNodes[ iPrev ] != newNodes[ i ] || nbCorners == 3 )
+          {
+            // corners stick but the medium does not, or a link of triangle collapses
+            noMergeNodes.push_back( face->GetNode( iPrev / 2 ));
+            noMergeNodes.push_back( face->GetNode( iNext / 2 ));
+            noMergeNodes.push_back( face->GetNode( nbCorners + i / 2 ));
+          }
+        }
+        else if ( newNodes[ i ] == newNodes[ iPrev ] )
+        {
+          // the medium node sticks to a neighbor corner one
+          noMergeNodes.push_back( face->GetNode( nbCorners + i / 2 ));
+          noMergeNodes.push_back( face->GetNode( iPrev / 2 ));
+        }
+        else if ( newNodes[ i ] == newNodes[ iNext ] )
+        {
+          // the medium node sticks to a neighbor corner one
+          noMergeNodes.push_back( face->GetNode( nbCorners + i / 2 ));
+          noMergeNodes.push_back( face->GetNode( iNext / 2 ));
+        }
+        else
+        {
+          // find if the medium sticks to any other node
+          std::vector<const SMDS_MeshNode*>::iterator pos;
+          pos = std::find( newNodes.begin(), newNodes.begin() + iPrev, newNodes[i] );
+          if ( pos == newNodes.begin() + iPrev )
+            pos = std::find( newNodes.begin() + i + 1, newNodes.end(), newNodes[i] );
+          if ( pos == newNodes.end() )
+            continue;
+
+          int iStick = std::distance( newNodes.begin(), pos );
+          if ( iStick % 2 == 0 )
+          {
+            // the medium sticks to a distant corner
+            noMergeNodes.push_back( face->GetNode( nbCorners + i / 2 ));
+            noMergeNodes.push_back( face->GetNode( iStick / 2 ));
+          }
+          else
+          {
+            // the medium sticks to a distant medium;
+            // it's OK if two links stick
+            int iPrev2 = ( iStick - 1 );
+            int iNext2 = ( iStick + 1 ) % nbNodes;
+            if (( newNodes[ iPrev ] == newNodes[ iPrev2 ] &&
+                  newNodes[ iNext ] == newNodes[ iNext2 ] )
+                ||
+                ( newNodes[ iPrev ] == newNodes[ iNext2 ] &&
+                  newNodes[ iNext ] == newNodes[ iPrev2 ] ))
+              ; // OK
+            else
+            {
+              noMergeNodes.push_back( face->GetNode( nbCorners + i / 2 ));
+              noMergeNodes.push_back( face->GetNode( nbCorners + iStick / 2 ));
+            }
+          }
+        }
+      }
+    }
+  } // deMergeFace()
+
+  bool isDegenVolume(const SMDS_VolumeTool& vt)
+  {
+    // TMP: it's necessary to use a topological check instead of a geometrical one
+    return vt.GetSize() < 1e-100;
+  }
+
+  void deMergeVolume(const SMDS_VolumeTool&               vt,
+                     std::vector< const SMDS_MeshNode* >& noMergeNodes)
+  {
+    // temporary de-merge all nodes
+    for ( int i = 0; i < vt.NbNodes(); ++i )
+    {
+      const SMDS_MeshNode* n = vt.GetNodes()[i];
+      if ( n != vt.Element()->GetNode( i ))
+        noMergeNodes.push_back( n );
+    }
+  }
+
+} // namespace
+
+//================================================================================
+/*!
+ * \brief Find nodes whose merge makes the element invalid. (Degenerated elem is OK)
+ *  \param [in] elem - the element
+ *  \param [in] newNodes - nodes of the element after the merge
+ *  \param [out] noMergeNodes - nodes to undo merge 
+ */
+//================================================================================
+
+void SMESH_MeshAlgos::DeMerge(const SMDS_MeshElement*              elem,
+                              std::vector< const SMDS_MeshNode* >& newNodes,
+                              std::vector< const SMDS_MeshNode* >& noMergeNodes)
+{
+  switch ( elem->GetType() )
+  {
+  case SMDSAbs_Face:
+  {
+    if ( newNodes.size() <= 4 )
+      return; // degenerated
+
+    if ( elem->IsQuadratic() )
+      SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
+        ( SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), newNodes.size() ), newNodes );
+
+    if ( isDegenFace( newNodes ))
+      return;
+
+    deMergeFace( elem, newNodes, noMergeNodes );
+  }
+  break;
+
+  case SMDSAbs_Volume:
+  {
+    if ( newNodes.size() <= 4 )
+      return; // degenerated
+
+    SMDS_VolumeTool vt;
+    if ( !vt.Set( elem, /*skipCentral=*/true, &newNodes ))
+      return; // strange
+
+    if ( isDegenVolume( vt ))
+      return;
+
+    deMergeVolume( elem, noMergeNodes );
+  }
+  break;
+
+  case SMDSAbs_Edge:
+  {
+    if ( newNodes.size() == 3 )
+      if (( newNodes[2] == newNodes[0] && newNodes[2] != newNodes[1] ) ||
+          ( newNodes[2] == newNodes[1] && newNodes[2] != newNodes[0]))
+      {
+        // the medium node sticks to a corner
+        noMergeNodes.push_back( newNodes[2] );
+        noMergeNodes.push_back( newNodes[ newNodes[2] == newNodes[1] ]);
+      }
+  }
+  break;
+  default:;
+  }
+}
index b6c3661..1114bfe 100644 (file)
@@ -206,6 +206,16 @@ namespace SMESH_MeshAlgos
                                  CoincidentFreeBorders & foundFreeBordes);
   
 
-} // SMESH_MeshAlgos
+  /*!
+   * \brief Find nodes whose merge makes the element invalid
+   *
+   * (Implemented in SMESH_DeMerge.cxx)
+   */
+  SMESHUtils_EXPORT
+  void DeMerge(const SMDS_MeshElement*              elem,
+               std::vector< const SMDS_MeshNode* >& newNodes,
+               std::vector< const SMDS_MeshNode* >& noMergeNodes);
+
+} // namespace SMESH_MeshAlgos
 
 #endif
index 990af78..c0248c8 100644 (file)
@@ -4159,7 +4159,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;
@@ -4203,7 +4204,7 @@ void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfN
     aTPythonDump << aNodeGroup;
   }
 
-  getEditor().MergeNodes( aListOfListOfNodes );
+  getEditor().MergeNodes( aListOfListOfNodes, AvoidMakingHoles );
 
   aTPythonDump << "], " << NodesToKeep << ")";
 
index b7022a9..74eaa36 100644 (file)
@@ -495,7 +495,8 @@ public:
                                     CORBA::Boolean                 SeparateCornersAndMedium)
     throw (SALOME::SALOME_Exception);
   void MergeNodes (const SMESH::array_of_long_array& GroupsOfNodes,
-                   const SMESH::ListOfIDSources&     NodesToKeep )
+                   const SMESH::ListOfIDSources&     NodesToKeep,
+                   CORBA::Boolean                    AvoidMakingHoles )
     throw (SALOME::SALOME_Exception);
   void FindEqualElements(SMESH::SMESH_IDSource_ptr      Object,
                          SMESH::array_of_long_array_out GroupsOfElementsID)
index 887014b..a075dad 100644 (file)
@@ -496,7 +496,13 @@ int SMESH_Mesh_i::ImportSTLFile( const char* theFileName )
   SMESH_TRY;
 
   // Read mesh with name = <theMeshName> into SMESH_Mesh
-  _impl->STLToMesh( theFileName );
+  std::string name = _impl->STLToMesh( theFileName );
+  if ( !name.empty() )
+  {
+    SALOMEDS::Study_var     study = _gen_i->GetCurrentStudy();
+    SALOMEDS::SObject_wrap meshSO = _gen_i->ObjectToSObject( study, _this() );
+    _gen_i->SetName( meshSO, name.c_str() );
+  }
 
   SMESH_CATCH( SMESH::throwCorbaException );
 
@@ -3144,9 +3150,15 @@ void SMESH_Mesh_i::ExportSTL (const char *file, const bool isascii)
   TPythonDump() << SMESH::SMESH_Mesh_var(_this())
                 << ".ExportSTL( r'" << file << "', " << isascii << " )";
 
+  CORBA::String_var name;
+  SALOMEDS::Study_var study = _gen_i->GetCurrentStudy();
+  SALOMEDS::SObject_wrap so = _gen_i->ObjectToSObject( study, _this() );
+  if ( !so->_is_nil() )
+    name = so->GetName();
+
   // Perform Export
-  PrepareForWriting(file);
-  _impl->ExportSTL(file, isascii);
+  PrepareForWriting( file );
+  _impl->ExportSTL( file, isascii, name.in() );
 }
 
 //================================================================================
@@ -3595,8 +3607,14 @@ void SMESH_Mesh_i::ExportPartToSTL(::SMESH::SMESH_IDSource_ptr meshPart,
 
   PrepareForWriting(file);
 
+  CORBA::String_var name;
+  SALOMEDS::Study_var study = _gen_i->GetCurrentStudy();
+  SALOMEDS::SObject_wrap so = _gen_i->ObjectToSObject( study, meshPart );
+  if ( !so->_is_nil() )
+    name = so->GetName();
+
   SMESH_MeshPartDS partDS( meshPart );
-  _impl->ExportSTL(file, isascii, &partDS);
+  _impl->ExportSTL( file, isascii, name.in(), &partDS );
 
   TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportPartToSTL( "
                 << meshPart<< ", r'" << file << "', " << isascii << ")";
index 5b94cd2..d87d0d5 100644 (file)
@@ -4566,10 +4566,12 @@ class Mesh:
     #  @param NodesToKeep nodes to keep in the mesh: a list of groups, sub-meshes or node IDs.
     #         If @a NodesToKeep does not include a node to keep for some group to merge,
     #         then the first node in the group is kept.
+    #  @param AvoidMakingHoles prevent merging nodes which cause removal of elements becoming
+    #         invalid
     #  @ingroup l2_modif_trsf
-    def MergeNodes (self, GroupsOfNodes, NodesToKeep=[]):
+    def MergeNodes (self, GroupsOfNodes, NodesToKeep=[], AvoidMakingHoles=False):
         # NodesToKeep are converted to SMESH_IDSource in meshEditor.MergeNodes()
-        self.editor.MergeNodes(GroupsOfNodes,NodesToKeep)
+        self.editor.MergeNodes( GroupsOfNodes, NodesToKeep, AvoidMakingHoles )
 
     ## Find the elements built on the same nodes.
     #  @param MeshOrSubMeshOrGroup Mesh or SubMesh, or Group of elements for searching
@@ -5125,17 +5127,18 @@ class meshEditor(SMESH._objref_SMESH_MeshEditor):
     def FindCoincidentNodesOnPart(self,*args): # a 3d arg added (SeparateCornerAndMediumNodes)
         if len( args ) == 2: args += False,
         return SMESH._objref_SMESH_MeshEditor.FindCoincidentNodesOnPart( self, *args )
-    def MergeNodes(self,*args): # a 2nd arg added (NodesToKeep)
+    def MergeNodes(self,*args): # 2 args added (NodesToKeep,AvoidMakingHoles)
         if len( args ) == 1:
-            return SMESH._objref_SMESH_MeshEditor.MergeNodes( self, args[0], [] )
+            return SMESH._objref_SMESH_MeshEditor.MergeNodes( self, args[0], [], False )
         NodesToKeep = args[1]
+        AvoidMakingHoles = args[2] if len( args ) == 3 else False
         unRegister  = genObjUnRegister()
         if NodesToKeep:
             if isinstance( NodesToKeep, list ) and isinstance( NodesToKeep[0], int ):
                 NodesToKeep = self.MakeIDSource( NodesToKeep, SMESH.NODE )
             if not isinstance( NodesToKeep, list ):
                 NodesToKeep = [ NodesToKeep ]
-        return SMESH._objref_SMESH_MeshEditor.MergeNodes( self, args[0], NodesToKeep )
+        return SMESH._objref_SMESH_MeshEditor.MergeNodes( self, args[0], NodesToKeep, AvoidMakingHoles )
     pass
 omniORB.registerObjref(SMESH._objref_SMESH_MeshEditor._NP_RepositoryId, meshEditor)