From: eap Date: Tue, 14 Mar 2017 12:43:27 +0000 (+0300) Subject: 23258: [CEA 1804] Do not merge the middle nodes of quadratic elements X-Git-Tag: V8_3_0a2~3 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=9dd045b97c68174d7ce52d592b239c6a1769e464;p=modules%2Fsmesh.git 23258: [CEA 1804] Do not merge the middle nodes of quadratic elements 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 --- diff --git a/doc/salome/gui/SMESH/images/mergenodes.png b/doc/salome/gui/SMESH/images/mergenodes.png index 5b34361cb..fc75f70dd 100644 Binary files a/doc/salome/gui/SMESH/images/mergenodes.png and b/doc/salome/gui/SMESH/images/mergenodes.png differ diff --git a/doc/salome/gui/SMESH/images/mergenodes_auto.png b/doc/salome/gui/SMESH/images/mergenodes_auto.png index 4f177f605..e27046908 100644 Binary files a/doc/salome/gui/SMESH/images/mergenodes_auto.png and b/doc/salome/gui/SMESH/images/mergenodes_auto.png differ diff --git a/doc/salome/gui/SMESH/input/merging_nodes.doc b/doc/salome/gui/SMESH/input/merging_nodes.doc index 8aff9aa09..872f9eeed 100644 --- a/doc/salome/gui/SMESH/input/merging_nodes.doc +++ b/doc/salome/gui/SMESH/input/merging_nodes.doc @@ -26,6 +26,9 @@ merging. cells 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. +
  • Activation of Avoid making holes check-box prevents merging + nodes that make elements invalid (but not degenerated) and hence + removed. Thus, no holes in place of removed elements appear.
  • Exclude groups from detection group allows to ignore the nodes which belong to the specified mesh groups. This control is active provided that the mesh includes groups.
  • diff --git a/idl/SMESH_MeshEditor.idl b/idl/SMESH_MeshEditor.idl index 577e39c58..ea19b5ebe 100644 --- a/idl/SMESH_MeshEditor.idl +++ b/idl/SMESH_MeshEditor.idl @@ -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); /*! diff --git a/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cxx b/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cxx index 9a531668c..ae7e48cca 100644 --- a/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cxx +++ b/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cxx @@ -69,7 +69,7 @@ namespace }; typedef NCollection_DataMap 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( 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 != 0 // 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( 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; diff --git a/src/DriverSTL/DriverSTL_R_SMDS_Mesh.h b/src/DriverSTL/DriverSTL_R_SMDS_Mesh.h index 5459b5181..d30366bfb 100644 --- a/src/DriverSTL/DriverSTL_R_SMDS_Mesh.h +++ b/src/DriverSTL/DriverSTL_R_SMDS_Mesh.h @@ -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 diff --git a/src/DriverSTL/DriverSTL_W_SMDS_Mesh.cxx b/src/DriverSTL/DriverSTL_W_SMDS_Mesh.cxx index cb42a321b..333c72d96 100644 --- a/src/DriverSTL/DriverSTL_W_SMDS_Mesh.cxx +++ b/src/DriverSTL/DriverSTL_W_SMDS_Mesh.cxx @@ -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 diff --git a/src/DriverSTL/DriverSTL_W_SMDS_Mesh.h b/src/DriverSTL/DriverSTL_W_SMDS_Mesh.h index efc027be1..66c4741bf 100644 --- a/src/DriverSTL/DriverSTL_W_SMDS_Mesh.h +++ b/src/DriverSTL/DriverSTL_W_SMDS_Mesh.h @@ -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 myVolumeFacets; // tmp faces }; diff --git a/src/SMDS/SMDS_VolumeTool.cxx b/src/SMDS/SMDS_VolumeTool.cxx index d7dd7f826..f7857c3eb 100644 --- a/src/SMDS/SMDS_VolumeTool.cxx +++ b/src/SMDS/SMDS_VolumeTool.cxx @@ -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* 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( 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( nodeIt->next() ); + } // check validity if ( !setFace(0) ) diff --git a/src/SMDS/SMDS_VolumeTool.hxx b/src/SMDS/SMDS_VolumeTool.hxx index b42a0ae07..5c2c882f6 100644 --- a/src/SMDS/SMDS_VolumeTool.hxx +++ b/src/SMDS/SMDS_VolumeTool.hxx @@ -58,15 +58,17 @@ class SMDS_EXPORT SMDS_VolumeTool SMDS_VolumeTool (); ~SMDS_VolumeTool (); - SMDS_VolumeTool (const SMDS_MeshElement* theVolume, - const bool ignoreCentralNodes=true); + SMDS_VolumeTool( const 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* 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; diff --git a/src/SMESH/SMESH_Mesh.cxx b/src/SMESH/SMESH_Mesh.cxx index 98725ba12..893a28aba 100644 --- a/src/SMESH/SMESH_Mesh.cxx +++ b/src/SMESH/SMESH_Mesh.cxx @@ -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(); } diff --git a/src/SMESH/SMESH_Mesh.hxx b/src/SMESH/SMESH_Mesh.hxx index 1fd14f397..a8ec5dfae 100644 --- a/src/SMESH/SMESH_Mesh.hxx +++ b/src/SMESH/SMESH_Mesh.hxx @@ -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, diff --git a/src/SMESH/SMESH_MeshEditor.cxx b/src/SMESH/SMESH_MeshEditor.cxx index f4dd0d7db..38b2eeb9f 100644 --- a/src/SMESH/SMESH_MeshEditor.cxx +++ b/src/SMESH/SMESH_MeshEditor.cxx @@ -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 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 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 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& aPrms, +SMESH_MeshEditor::makeEdgePathPoints(std::list& aPrms, const TopoDS_Edge& aTrackEdge, bool FirstIsStart, list& LPP) @@ -6536,11 +6536,11 @@ SMESH_MeshEditor::MakeEdgePathPoints(std::list& 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& fullList, const bool theHasAngles, list& 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 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& 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 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 nodeSet; - vector< const SMDS_MeshNode*> curNodes, uniqueNodes; - vector 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 pbElems; + set::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( 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::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( itN->next() ); - - TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n ); - if ( nnIt != nodeNodeMap.end() ) { // n sticks - n = (*nnIt).second; - { ////////// BUG 0020185: begin - bool stopRecur = false; - set 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 polygons_nodes; - vector quantities; - int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities ); - if (nbNew > 0) - { - vector 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( elem ); - if ( aPolyedre ) - { - int nbFaces = aPolyedre->NbFaces(); + newElemDefs.resize(nbResElems); + newElemDefs[0].Init( elem ); + newElemDefs[0].myNodes.clear(); - vector poly_nodes; - vector quantities; - vector faceNodes; + set nodeSet; + vector< const SMDS_MeshNode*> curNodes; + vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes; + vector 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( 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 polygons_nodes; + vector 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& 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( elem )) + { + int nbFaces = aPolyedre->NbFaces(); + + vector& poly_nodes = newElemDefs[0].myNodes; + vector & quantities = newElemDefs[0].myPolyhedQuantities; + vector 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 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 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 poly_nodes; poly_nodes.reserve( 6 * 4 ); - vector quantities; quantities.reserve( 6 ); - for ( int iFace = 0; iFace < 6; iFace++ ) + if ( toRemove && nbUniqueNodes > 4 ) { + ////////////////// HEXAHEDRON ---> polyhedron + hexa.SetExternalNormal(); + vector& poly_nodes = newElemDefs[0].myNodes; + vector & 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; } diff --git a/src/SMESH/SMESH_MeshEditor.hxx b/src/SMESH/SMESH_MeshEditor.hxx index fd0418c6a..663b6ae16 100644 --- a/src/SMESH/SMESH_MeshEditor.hxx +++ b/src/SMESH/SMESH_MeshEditor.hxx @@ -83,11 +83,12 @@ public: // -------------------------------------------------------------------------------- struct ElemFeatures //!< Features of element to create { - SMDSAbs_ElementType myType; - bool myIsPoly, myIsQuad; - int myID; - double myBallDiameter; - std::vector myPolyhedQuantities; + SMDSAbs_ElementType myType; + bool myIsPoly, myIsQuad; + int myID; + double myBallDiameter; + std::vector myPolyhedQuantities; + std::vector 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) {} @@ -750,6 +751,20 @@ public: const size_t nbSteps, 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 @@ -782,11 +797,11 @@ public: double Angle ()const { return myAngle; } double Parameter ()const { return myPrm; } }; - Extrusion_Error MakeEdgePathPoints(std::list& aPrms, + Extrusion_Error makeEdgePathPoints(std::list& aPrms, const TopoDS_Edge& aTrackEdge, bool aFirstIsStart, std::list& aLPP); - Extrusion_Error MakeExtrElements(TIDSortedElemSet theElements[2], + Extrusion_Error makeExtrElements(TIDSortedElemSet theElements[2], std::list& theFullList, const bool theHasAngles, std::list& 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& theAngles); bool doubleNodes( SMESHDS_Mesh* theMeshDS, diff --git a/src/SMESHGUI/SMESHGUI.cxx b/src/SMESHGUI/SMESHGUI.cxx index 41d4c42bd..1f3435241 100644 --- a/src/SMESHGUI/SMESHGUI.cxx +++ b/src/SMESHGUI/SMESHGUI.cxx @@ -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 diff --git a/src/SMESHGUI/SMESHGUI_MergeDlg.cxx b/src/SMESHGUI/SMESHGUI_MergeDlg.cxx index e5dcf5d12..2aba558af 100644 --- a/src/SMESHGUI/SMESHGUI_MergeDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_MergeDlg.cxx @@ -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 diff --git a/src/SMESHGUI/SMESHGUI_MergeDlg.h b/src/SMESHGUI/SMESHGUI_MergeDlg.h index 5e827abc8..6b087b631 100644 --- a/src/SMESHGUI/SMESHGUI_MergeDlg.h +++ b/src/SMESHGUI/SMESHGUI_MergeDlg.h @@ -130,6 +130,7 @@ private: QWidget* NodeSpecWidget; SMESHGUI_SpinBox* SpinBoxTolerance; QCheckBox* SeparateCornersAndMedium; + QCheckBox* AvoidMakingHoles; QGroupBox* GroupCoincident; //QWidget* GroupCoincidentWidget; diff --git a/src/SMESHGUI/SMESHGUI_Operations.h b/src/SMESHGUI/SMESHGUI_Operations.h index df810f7df..89f53472b 100644 --- a/src/SMESHGUI/SMESHGUI_Operations.h +++ b/src/SMESHGUI/SMESHGUI_Operations.h @@ -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 ---------------------------//-------------------------------- diff --git a/src/SMESHGUI/SMESH_msg_en.ts b/src/SMESHGUI/SMESH_msg_en.ts index bbbccbcfd..2a0601830 100644 --- a/src/SMESHGUI/SMESH_msg_en.ts +++ b/src/SMESHGUI/SMESH_msg_en.ts @@ -5322,6 +5322,10 @@ Please select a group and try again SEPARATE_CORNERS_AND_MEDIUM No merge of corner and medium nodes of quadratic cells + + AVOID_MAKING_HOLES + Avoid making holes + KEEP_NODES Nodes to keep during the merge diff --git a/src/SMESHUtils/CMakeLists.txt b/src/SMESHUtils/CMakeLists.txt index c21a8a6d5..3641d913c 100644 --- a/src/SMESHUtils/CMakeLists.txt +++ b/src/SMESHUtils/CMakeLists.txt @@ -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 index 000000000..427ce5270 --- /dev/null +++ b/src/SMESHUtils/SMESH_DeMerge.cxx @@ -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::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:; + } +} diff --git a/src/SMESHUtils/SMESH_MeshAlgos.hxx b/src/SMESHUtils/SMESH_MeshAlgos.hxx index b6c3661b0..1114bfe02 100644 --- a/src/SMESHUtils/SMESH_MeshAlgos.hxx +++ b/src/SMESHUtils/SMESH_MeshAlgos.hxx @@ -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 diff --git a/src/SMESH_I/SMESH_MeshEditor_i.cxx b/src/SMESH_I/SMESH_MeshEditor_i.cxx index 990af7876..c0248c87e 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.cxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.cxx @@ -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 << ")"; diff --git a/src/SMESH_I/SMESH_MeshEditor_i.hxx b/src/SMESH_I/SMESH_MeshEditor_i.hxx index b7022a968..74eaa36ad 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.hxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.hxx @@ -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) diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index 887014be0..a075dadce 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -496,7 +496,13 @@ int SMESH_Mesh_i::ImportSTLFile( const char* theFileName ) SMESH_TRY; // Read mesh with name = 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 << ")"; diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index 5b94cd28d..d87d0d553 100644 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -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)