X-Git-Url: http://git.salome-platform.org/gitweb/?p=modules%2Fsmesh.git;a=blobdiff_plain;f=src%2FSMESH%2FSMESH_MeshEditor.cxx;h=48c10820fcfa14520ac6ed91d9b081cf865bf2be;hp=b73f854d2e02183bdd38380e856e61733bb49344;hb=9150fb3db9f16daea68ed8b24447aaadc171b61e;hpb=423f097f5e34c955b122291ad657d8ac49d8aae0 diff --git a/src/SMESH/SMESH_MeshEditor.cxx b/src/SMESH/SMESH_MeshEditor.cxx index b73f854d2..48c10820f 100644 --- a/src/SMESH/SMESH_MeshEditor.cxx +++ b/src/SMESH/SMESH_MeshEditor.cxx @@ -1,6 +1,6 @@ -// SMESH SMESH : idl implementation based on 'SMESH' unit's classes +// Copyright (C) 2007-2010 CEA/DEN, EDF R&D, OPEN CASCADE // -// Copyright (C) 2003 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// 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 @@ -17,15 +17,14 @@ // 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.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org -// -// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + +// SMESH SMESH : idl implementation based on 'SMESH' unit's classes // File : SMESH_MeshEditor.cxx // Created : Mon Apr 12 16:10:22 2004 // Author : Edward AGAPOV (eap) - - +// #include "SMESH_MeshEditor.hxx" #include "SMDS_FaceOfNodes.hxx" @@ -34,57 +33,232 @@ #include "SMDS_PolyhedralVolumeOfNodes.hxx" #include "SMDS_FacePosition.hxx" #include "SMDS_SpacePosition.hxx" +#include "SMDS_QuadraticFaceOfNodes.hxx" +#include "SMDS_MeshGroup.hxx" +#include "SMDS_SetIterator.hxx" #include "SMESHDS_Group.hxx" #include "SMESHDS_Mesh.hxx" -#include "SMESH_subMesh.hxx" +#include "SMESH_Algo.hxx" #include "SMESH_ControlsDef.hxx" +#include "SMESH_Group.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_OctreeNode.hxx" +#include "SMESH_subMesh.hxx" #include "utilities.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include +#include #include +#include #include +#include +#include +#include #include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include #include +#include +#include +#include + +#define cast2Node(elem) static_cast( elem ) using namespace std; using namespace SMESH::Controls; -typedef map TNodeNodeMap; typedef map > TElemOfNodeListMap; typedef map > TElemOfElemListMap; -typedef map > TNodeOfNodeListMap; -typedef TNodeOfNodeListMap::iterator TNodeOfNodeListMapItr; -typedef map > TElemOfVecOfNnlmiMap; + +typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator; //======================================================================= //function : SMESH_MeshEditor //purpose : //======================================================================= -SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh ): -myMesh( theMesh ) +SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh ) + :myMesh( theMesh ) // theMesh may be NULL +{ +} + +//======================================================================= +/*! + * \brief Add element + */ +//======================================================================= + +SMDS_MeshElement* +SMESH_MeshEditor::AddElement(const vector & node, + const SMDSAbs_ElementType type, + const bool isPoly, + const int ID) +{ + SMDS_MeshElement* e = 0; + int nbnode = node.size(); + SMESHDS_Mesh* mesh = GetMeshDS(); + switch ( type ) { + + case SMDSAbs_Face: + if ( !isPoly ) { + if (nbnode == 3) + if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID); + else e = mesh->AddFace (node[0], node[1], node[2] ); + else if (nbnode == 4) + if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID); + else e = mesh->AddFace (node[0], node[1], node[2], node[3] ); + else if (nbnode == 6) + if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], + node[4], node[5], ID); + else e = mesh->AddFace (node[0], node[1], node[2], node[3], + node[4], node[5] ); + else if (nbnode == 8) + if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], ID); + else e = mesh->AddFace (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7] ); + } else { + if ( ID ) e = mesh->AddPolygonalFaceWithID(node, ID); + else e = mesh->AddPolygonalFace (node ); + } + break; + + case SMDSAbs_Volume: + if ( !isPoly ) { + if (nbnode == 4) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3] ); + else if (nbnode == 5) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4] ); + else if (nbnode == 6) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5] ); + else if (nbnode == 8) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7] ); + else if (nbnode == 10) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9] ); + else if (nbnode == 13) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12] ); + else if (nbnode == 15) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14] ); + else if (nbnode == 20) + if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],node[15], + node[16],node[17],node[18],node[19],ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],node[15], + node[16],node[17],node[18],node[19] ); + } + break; + + case SMDSAbs_Edge: + if ( nbnode == 2 ) + if ( ID ) e = mesh->AddEdgeWithID(node[0], node[1], ID); + else e = mesh->AddEdge (node[0], node[1] ); + else if ( nbnode == 3 ) + if ( ID ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID); + else e = mesh->AddEdge (node[0], node[1], node[2] ); + break; + + case SMDSAbs_0DElement: + if ( nbnode == 1 ) + if ( ID ) e = mesh->Add0DElementWithID(node[0], ID); + else e = mesh->Add0DElement (node[0] ); + break; + + case SMDSAbs_Node: + if ( ID ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID); + else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z()); + break; + + default:; + } + if ( e ) myLastCreatedElems.Append( e ); + return e; +} + +//======================================================================= +/*! + * \brief Add element + */ +//======================================================================= + +SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector & nodeIDs, + const SMDSAbs_ElementType type, + const bool isPoly, + const int ID) { + vector nodes; + nodes.reserve( nodeIDs.size() ); + vector::const_iterator id = nodeIDs.begin(); + while ( id != nodeIDs.end() ) { + if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ )) + nodes.push_back( node ); + else + return 0; + } + return AddElement( nodes, type, isPoly, ID ); } //======================================================================= @@ -93,16 +267,18 @@ myMesh( theMesh ) // Modify a compute state of sub-meshes which become empty //======================================================================= -bool SMESH_MeshEditor::Remove (const list< int >& theIDs, - const bool isNodes ) +int SMESH_MeshEditor::Remove (const list< int >& theIDs, + const bool isNodes ) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); SMESHDS_Mesh* aMesh = GetMeshDS(); set< SMESH_subMesh *> smmap; + int removed = 0; list::const_iterator it = theIDs.begin(); - for ( ; it != theIDs.end(); it++ ) - { + for ( ; it != theIDs.end(); it++ ) { const SMDS_MeshElement * elem; if ( isNodes ) elem = aMesh->FindNode( *it ); @@ -111,28 +287,33 @@ bool SMESH_MeshEditor::Remove (const list< int >& theIDs, if ( !elem ) continue; - // Find sub-meshes to notify about modification - SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); - while ( nodeIt->more() ) - { - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - const SMDS_PositionPtr& aPosition = node->GetPosition(); - if ( aPosition.get() ) { - int aShapeID = aPosition->GetShapeId(); - if ( aShapeID ) { - TopoDS_Shape aShape = aMesh->IndexToShape( aShapeID ); - SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShape ); - if ( sm ) + // Notify VERTEX sub-meshes about modification + if ( isNodes ) { + const SMDS_MeshNode* node = cast2Node( elem ); + if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) + if ( int aShapeID = node->GetPosition()->GetShapeId() ) + if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) ) smmap.insert( sm ); - } - } } + // Find sub-meshes to notify about modification + // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); + // while ( nodeIt->more() ) { + // const SMDS_MeshNode* node = static_cast( nodeIt->next() ); + // const SMDS_PositionPtr& aPosition = node->GetPosition(); + // if ( aPosition.get() ) { + // if ( int aShapeID = aPosition->GetShapeId() ) { + // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) ) + // smmap.insert( sm ); + // } + // } + // } // Do remove if ( isNodes ) aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem )); else aMesh->RemoveElement( elem ); + removed++; } // Notify sub-meshes about modification @@ -141,7 +322,12 @@ bool SMESH_MeshEditor::Remove (const list< int >& theIDs, for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ ) (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED ); } - return true; + + // // Check if the whole mesh becomes empty + // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) ) + // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); + + return removed; } //======================================================================= @@ -152,12 +338,14 @@ bool SMESH_MeshEditor::Remove (const list< int >& theIDs, int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + SMESHDS_Mesh * aMesh = GetMeshDS(); if ( aMesh->ShapeToMesh().IsNull() ) return 0; - if ( theElem->GetType() == SMDSAbs_Node ) - { + if ( theElem->GetType() == SMDSAbs_Node ) { const SMDS_PositionPtr& aPosition = static_cast( theElem )->GetPosition(); if ( aPosition.get() ) @@ -168,25 +356,22 @@ int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem) TopoDS_Shape aShape; // the shape a node is on SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); - while ( nodeIt->more() ) - { + while ( nodeIt->more() ) { const SMDS_MeshNode* node = static_cast( nodeIt->next() ); const SMDS_PositionPtr& aPosition = node->GetPosition(); if ( aPosition.get() ) { - int aShapeID = aPosition->GetShapeId(); - SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ); - if ( sm ) - { - if ( sm->Contains( theElem )) - return aShapeID; - if ( aShape.IsNull() ) - aShape = aMesh->IndexToShape( aShapeID ); - } - else - { - //MESSAGE ( "::FindShape() No SubShape for aShapeID " << aShapeID ); - } + int aShapeID = aPosition->GetShapeId(); + SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ); + if ( sm ) { + if ( sm->Contains( theElem )) + return aShapeID; + if ( aShape.IsNull() ) + aShape = aMesh->IndexToShape( aShapeID ); } + else { + //MESSAGE ( "::FindShape() No SubShape for aShapeID " << aShapeID ); + } + } } // None of nodes is on a proper shape, @@ -196,17 +381,106 @@ int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem) return 0; } TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape )); - for ( ; ancIt.More(); ancIt.Next() ) - { - SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() ); - if ( sm && sm->Contains( theElem )) - return aMesh->ShapeToIndex( ancIt.Value() ); + for ( ; ancIt.More(); ancIt.Next() ) { + SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() ); + if ( sm && sm->Contains( theElem )) + return aMesh->ShapeToIndex( ancIt.Value() ); } //MESSAGE ("::FindShape() - SHAPE NOT FOUND") return 0; } +//======================================================================= +//function : IsMedium +//purpose : +//======================================================================= + +bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node, + const SMDSAbs_ElementType typeToCheck) +{ + bool isMedium = false; + SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck); + while (it->more() && !isMedium ) { + const SMDS_MeshElement* elem = it->next(); + isMedium = elem->IsMediumNode(node); + } + return isMedium; +} + +//======================================================================= +//function : ShiftNodesQuadTria +//purpose : auxilary +// Shift nodes in the array corresponded to quadratic triangle +// example: (0,1,2,3,4,5) -> (1,2,0,4,5,3) +//======================================================================= +static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[]) +{ + const SMDS_MeshNode* nd1 = aNodes[0]; + aNodes[0] = aNodes[1]; + aNodes[1] = aNodes[2]; + aNodes[2] = nd1; + const SMDS_MeshNode* nd2 = aNodes[3]; + aNodes[3] = aNodes[4]; + aNodes[4] = aNodes[5]; + aNodes[5] = nd2; +} + +//======================================================================= +//function : GetNodesFromTwoTria +//purpose : auxilary +// Shift nodes in the array corresponded to quadratic triangle +// example: (0,1,2,3,4,5) -> (1,2,0,4,5,3) +//======================================================================= +static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1, + const SMDS_MeshElement * theTria2, + const SMDS_MeshNode* N1[], + const SMDS_MeshNode* N2[]) +{ + SMDS_ElemIteratorPtr it = theTria1->nodesIterator(); + int i=0; + while(i<6) { + N1[i] = static_cast( it->next() ); + i++; + } + if(it->more()) return false; + it = theTria2->nodesIterator(); + i=0; + while(i<6) { + N2[i] = static_cast( it->next() ); + i++; + } + if(it->more()) return false; + + int sames[3] = {-1,-1,-1}; + int nbsames = 0; + int j; + for(i=0; i<3; i++) { + for(j=0; j<3; j++) { + if(N1[i]==N2[j]) { + sames[i] = j; + nbsames++; + break; + } + } + } + if(nbsames!=2) return false; + if(sames[0]>-1) { + ShiftNodesQuadTria(N1); + if(sames[1]>-1) { + ShiftNodesQuadTria(N1); + } + } + i = sames[0] + sames[1] + sames[2]; + for(; i<2; i++) { + ShiftNodesQuadTria(N2); + } + // now we receive following N1 and N2 (using numeration as above image) + // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6) + // i.e. first nodes from both arrays determ new diagonal + return true; +} + //======================================================================= //function : InverseDiag //purpose : Replace two neighbour triangles with ones built on the same 4 nodes @@ -217,73 +491,121 @@ int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem) bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1, const SMDS_MeshElement * theTria2 ) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + if (!theTria1 || !theTria2) return false; + const SMDS_FaceOfNodes* F1 = dynamic_cast( theTria1 ); - if (!F1) return false; const SMDS_FaceOfNodes* F2 = dynamic_cast( theTria2 ); - if (!F2) return false; + if (F1 && F2) { - // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A - // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ | - // |/ | | \| - // B +--+ 2 B +--+ 2 + // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A + // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ | + // |/ | | \| + // B +--+ 2 B +--+ 2 - // put nodes in array and find out indices of the same ones - const SMDS_MeshNode* aNodes [6]; - int sameInd [] = { 0, 0, 0, 0, 0, 0 }; - int i = 0; - SMDS_ElemIteratorPtr it = theTria1->nodesIterator(); - while ( it->more() ) - { - aNodes[ i ] = static_cast( it->next() ); - - if ( i > 2 ) // theTria2 - // find same node of theTria1 - for ( int j = 0; j < 3; j++ ) - if ( aNodes[ i ] == aNodes[ j ]) { - sameInd[ j ] = i; - sameInd[ i ] = j; - break; - } - // next - i++; - if ( i == 3 ) { - if ( it->more() ) - return false; // theTria1 is not a triangle - it = theTria2->nodesIterator(); + // put nodes in array and find out indices of the same ones + const SMDS_MeshNode* aNodes [6]; + int sameInd [] = { 0, 0, 0, 0, 0, 0 }; + int i = 0; + SMDS_ElemIteratorPtr it = theTria1->nodesIterator(); + while ( it->more() ) { + aNodes[ i ] = static_cast( it->next() ); + + if ( i > 2 ) // theTria2 + // find same node of theTria1 + for ( int j = 0; j < 3; j++ ) + if ( aNodes[ i ] == aNodes[ j ]) { + sameInd[ j ] = i; + sameInd[ i ] = j; + break; + } + // next + i++; + if ( i == 3 ) { + if ( it->more() ) + return false; // theTria1 is not a triangle + it = theTria2->nodesIterator(); + } + if ( i == 6 && it->more() ) + return false; // theTria2 is not a triangle } - if ( i == 6 && it->more() ) - return false; // theTria2 is not a triangle - } - // find indices of 1,2 and of A,B in theTria1 - int iA = 0, iB = 0, i1 = 0, i2 = 0; - for ( i = 0; i < 6; i++ ) - { - if ( sameInd [ i ] == 0 ) - if ( i < 3 ) i1 = i; - else i2 = i; - else if (i < 3) - if ( iA ) iB = i; - else iA = i; - } - // nodes 1 and 2 should not be the same - if ( aNodes[ i1 ] == aNodes[ i2 ] ) - return false; + // find indices of 1,2 and of A,B in theTria1 + int iA = 0, iB = 0, i1 = 0, i2 = 0; + for ( i = 0; i < 6; i++ ) { + if ( sameInd [ i ] == 0 ) + if ( i < 3 ) i1 = i; + else i2 = i; + else if (i < 3) + if ( iA ) iB = i; + else iA = i; + } + // nodes 1 and 2 should not be the same + if ( aNodes[ i1 ] == aNodes[ i2 ] ) + return false; + // theTria1: A->2 + aNodes[ iA ] = aNodes[ i2 ]; + // theTria2: B->1 + aNodes[ sameInd[ iB ]] = aNodes[ i1 ]; - // theTria1: A->2 - aNodes[ iA ] = aNodes[ i2 ]; - // theTria2: B->1 - aNodes[ sameInd[ iB ]] = aNodes[ i1 ]; + //MESSAGE( theTria1 << theTria2 ); - //MESSAGE( theTria1 << theTria2 ); + GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 ); + GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 ); - GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 ); - GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 ); + //MESSAGE( theTria1 << theTria2 ); - //MESSAGE( theTria1 << theTria2 ); + return true; + + } // end if(F1 && F2) + + // check case of quadratic faces + const SMDS_QuadraticFaceOfNodes* QF1 = + dynamic_cast (theTria1); + if(!QF1) return false; + const SMDS_QuadraticFaceOfNodes* QF2 = + dynamic_cast (theTria2); + if(!QF2) return false; + + // 5 + // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9) + // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8) + // | / | + // 7 + + + 6 + // | /9 | + // |/ | + // 4 +--+--+ 3 + // 8 + + const SMDS_MeshNode* N1 [6]; + const SMDS_MeshNode* N2 [6]; + if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2)) + return false; + // now we receive following N1 and N2 (using numeration as above image) + // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6) + // i.e. first nodes from both arrays determ new diagonal + + const SMDS_MeshNode* N1new [6]; + const SMDS_MeshNode* N2new [6]; + N1new[0] = N1[0]; + N1new[1] = N2[0]; + N1new[2] = N2[1]; + N1new[3] = N1[4]; + N1new[4] = N2[3]; + N1new[5] = N1[5]; + N2new[0] = N1[0]; + N2new[1] = N1[1]; + N2new[2] = N2[0]; + N2new[3] = N1[3]; + N2new[4] = N2[5]; + N2new[5] = N1[4]; + // replaces nodes in faces + GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 ); + GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 ); return true; } @@ -303,21 +625,28 @@ static bool findTriangles(const SMDS_MeshNode * theNode1, theTria1 = theTria2 = 0; set< const SMDS_MeshElement* > emap; - SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(); + SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face); while (it->more()) { const SMDS_MeshElement* elem = it->next(); - if ( elem->GetType() == SMDSAbs_Face && elem->NbNodes() == 3 ) + if ( elem->NbNodes() == 3 ) emap.insert( elem ); } - it = theNode2->GetInverseElementIterator(); + it = theNode2->GetInverseElementIterator(SMDSAbs_Face); while (it->more()) { const SMDS_MeshElement* elem = it->next(); - if ( elem->GetType() == SMDSAbs_Face && - emap.find( elem ) != emap.end() ) + if ( emap.find( elem ) != emap.end() ) if ( theTria1 ) { - theTria2 = elem; + // theTria1 must be element with minimum ID + if( theTria1->GetID() < elem->GetID() ) { + theTria2 = elem; + } + else { + theTria2 = theTria1; + theTria1 = elem; + } break; - } else { + } + else { theTria1 = elem; } } @@ -334,6 +663,9 @@ static bool findTriangles(const SMDS_MeshNode * theNode1, bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1, const SMDS_MeshNode * theNode2) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE( "::InverseDiag()" ); const SMDS_MeshElement *tr1, *tr2; @@ -341,55 +673,65 @@ bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1, return false; const SMDS_FaceOfNodes* F1 = dynamic_cast( tr1 ); - if (!F1) return false; + //if (!F1) return false; const SMDS_FaceOfNodes* F2 = dynamic_cast( tr2 ); - if (!F2) return false; - - // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A - // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ | - // |/ | | \| - // B +--+ 2 B +--+ 2 - - // put nodes in array - // and find indices of 1,2 and of A in tr1 and of B in tr2 - int i, iA1 = 0, i1 = 0; - const SMDS_MeshNode* aNodes1 [3]; - SMDS_ElemIteratorPtr it; - for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) { - aNodes1[ i ] = static_cast( it->next() ); - if ( aNodes1[ i ] == theNode1 ) - iA1 = i; // node A in tr1 - else if ( aNodes1[ i ] != theNode2 ) - i1 = i; // node 1 - } - int iB2 = 0, i2 = 0; - const SMDS_MeshNode* aNodes2 [3]; - for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) { - aNodes2[ i ] = static_cast( it->next() ); - if ( aNodes2[ i ] == theNode2 ) - iB2 = i; // node B in tr2 - else if ( aNodes2[ i ] != theNode1 ) - i2 = i; // node 2 - } - - // nodes 1 and 2 should not be the same - if ( aNodes1[ i1 ] == aNodes2[ i2 ] ) - return false; + //if (!F2) return false; + if (F1 && F2) { + + // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A + // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ | + // |/ | | \| + // B +--+ 2 B +--+ 2 + + // put nodes in array + // and find indices of 1,2 and of A in tr1 and of B in tr2 + int i, iA1 = 0, i1 = 0; + const SMDS_MeshNode* aNodes1 [3]; + SMDS_ElemIteratorPtr it; + for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) { + aNodes1[ i ] = static_cast( it->next() ); + if ( aNodes1[ i ] == theNode1 ) + iA1 = i; // node A in tr1 + else if ( aNodes1[ i ] != theNode2 ) + i1 = i; // node 1 + } + int iB2 = 0, i2 = 0; + const SMDS_MeshNode* aNodes2 [3]; + for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) { + aNodes2[ i ] = static_cast( it->next() ); + if ( aNodes2[ i ] == theNode2 ) + iB2 = i; // node B in tr2 + else if ( aNodes2[ i ] != theNode1 ) + i2 = i; // node 2 + } - // tr1: A->2 - aNodes1[ iA1 ] = aNodes2[ i2 ]; - // tr2: B->1 - aNodes2[ iB2 ] = aNodes1[ i1 ]; + // nodes 1 and 2 should not be the same + if ( aNodes1[ i1 ] == aNodes2[ i2 ] ) + return false; - //MESSAGE( tr1 << tr2 ); + // tr1: A->2 + aNodes1[ iA1 ] = aNodes2[ i2 ]; + // tr2: B->1 + aNodes2[ iB2 ] = aNodes1[ i1 ]; - GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 ); - GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 ); + //MESSAGE( tr1 << tr2 ); - //MESSAGE( tr1 << tr2 ); + GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 ); + GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 ); - return true; + //MESSAGE( tr1 << tr2 ); + + return true; + } + // check case of quadratic faces + const SMDS_QuadraticFaceOfNodes* QF1 = + dynamic_cast (tr1); + if(!QF1) return false; + const SMDS_QuadraticFaceOfNodes* QF2 = + dynamic_cast (tr2); + if(!QF2) return false; + return InverseDiag(tr1,tr2); } //======================================================================= @@ -405,12 +747,15 @@ bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [], const SMDS_MeshElement * tr1, const SMDS_MeshElement * tr2 ) { + if( tr1->NbNodes() != tr2->NbNodes() ) + return false; // find the 4-th node to insert into tr1 const SMDS_MeshNode* n4 = 0; SMDS_ElemIteratorPtr it = tr2->nodesIterator(); - while ( !n4 && it->more() ) - { - const SMDS_MeshNode * n = static_cast( it->next() ); + int i=0; + while ( !n4 && i<3 ) { + const SMDS_MeshNode * n = cast2Node( it->next() ); + i++; bool isDiag = ( n == theNode1 || n == theNode2 ); if ( !isDiag ) n4 = n; @@ -418,19 +763,18 @@ bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [], // Make an array of nodes to be in a quadrangle int iNode = 0, iFirstDiag = -1; it = tr1->nodesIterator(); - while ( it->more() ) - { - const SMDS_MeshNode * n = static_cast( it->next() ); + i=0; + while ( i<3 ) { + const SMDS_MeshNode * n = cast2Node( it->next() ); + i++; bool isDiag = ( n == theNode1 || n == theNode2 ); - if ( isDiag ) - { + if ( isDiag ) { if ( iFirstDiag < 0 ) iFirstDiag = iNode; else if ( iNode - iFirstDiag == 1 ) theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes } - else if ( n == n4 ) - { + else if ( n == n4 ) { return false; // tr1 and tr2 should not have all the same nodes } theQuadNodes[ iNode++ ] = n; @@ -451,6 +795,9 @@ bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [], bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1, const SMDS_MeshNode * theNode2) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE( "::DeleteDiag()" ); const SMDS_MeshElement *tr1, *tr2; @@ -458,20 +805,68 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1, return false; const SMDS_FaceOfNodes* F1 = dynamic_cast( tr1 ); - if (!F1) return false; + //if (!F1) return false; const SMDS_FaceOfNodes* F2 = dynamic_cast( tr2 ); - if (!F2) return false; + //if (!F2) return false; + if (F1 && F2) { - const SMDS_MeshNode* aNodes [ 4 ]; - if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 )) - return false; + const SMDS_MeshNode* aNodes [ 4 ]; + if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 )) + return false; + + //MESSAGE( endl << tr1 << tr2 ); + + GetMeshDS()->ChangeElementNodes( tr1, aNodes, 4 ); + myLastCreatedElems.Append(tr1); + GetMeshDS()->RemoveElement( tr2 ); + + //MESSAGE( endl << tr1 ); - //MESSAGE( endl << tr1 << tr2 ); + return true; + } - GetMeshDS()->ChangeElementNodes( tr1, aNodes, 4 ); + // check case of quadratic faces + const SMDS_QuadraticFaceOfNodes* QF1 = + dynamic_cast (tr1); + if(!QF1) return false; + const SMDS_QuadraticFaceOfNodes* QF2 = + dynamic_cast (tr2); + if(!QF2) return false; + + // 5 + // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9) + // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8) + // | / | + // 7 + + + 6 + // | /9 | + // |/ | + // 4 +--+--+ 3 + // 8 + + const SMDS_MeshNode* N1 [6]; + const SMDS_MeshNode* N2 [6]; + if(!GetNodesFromTwoTria(tr1,tr2,N1,N2)) + return false; + // now we receive following N1 and N2 (using numeration as above image) + // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6) + // i.e. first nodes from both arrays determ new diagonal + + const SMDS_MeshNode* aNodes[8]; + aNodes[0] = N1[0]; + aNodes[1] = N1[1]; + aNodes[2] = N2[0]; + aNodes[3] = N2[1]; + aNodes[4] = N1[3]; + aNodes[5] = N2[5]; + aNodes[6] = N2[3]; + aNodes[7] = N1[5]; + + GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 ); + myLastCreatedElems.Append(tr1); GetMeshDS()->RemoveElement( tr2 ); - //MESSAGE( endl << tr1 ); + // remove middle node (9) + GetMeshDS()->RemoveNode( N1[4] ); return true; } @@ -483,6 +878,9 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1, bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + if (!theElem) return false; SMDS_ElemIteratorPtr it = theElem->nodesIterator(); @@ -492,16 +890,39 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem) switch ( theElem->GetType() ) { case SMDSAbs_Edge: - case SMDSAbs_Face: - { - int i = theElem->NbNodes(); - vector aNodes( i ); - while ( it->more() ) - aNodes[ --i ]= static_cast( it->next() ); - return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() ); + case SMDSAbs_Face: { + if(!theElem->IsQuadratic()) { + int i = theElem->NbNodes(); + vector aNodes( i ); + while ( it->more() ) + aNodes[ --i ]= static_cast( it->next() ); + return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() ); + } + else { + // quadratic elements + if(theElem->GetType()==SMDSAbs_Edge) { + vector aNodes(3); + aNodes[1]= static_cast( it->next() ); + aNodes[0]= static_cast( it->next() ); + aNodes[2]= static_cast( it->next() ); + return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 ); + } + else { + int nbn = theElem->NbNodes(); + vector aNodes(nbn); + aNodes[0]= static_cast( it->next() ); + int i=1; + for(; i( it->next() ); + } + for(i=0; i( it->next() ); + } + return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn ); + } + } } - case SMDSAbs_Volume: - { + case SMDSAbs_Volume: { if (theElem->IsPoly()) { const SMDS_PolyhedralVolumeOfNodes* aPolyedre = static_cast( theElem ); @@ -527,7 +948,8 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem) return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities ); - } else { + } + else { SMDS_VolumeTool vTool; if ( !vTool.Set( theElem )) return false; @@ -562,9 +984,12 @@ static double getBadRate (const SMDS_MeshElement* theElem, // theCrit is used to select a diagonal to cut //======================================================================= -bool SMESH_MeshEditor::QuadToTri (set & theElems, +bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, SMESH::Controls::NumericalFunctorPtr theCrit) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE( "::QuadToTri()" ); if ( !theCrit.get() ) @@ -572,15 +997,19 @@ bool SMESH_MeshEditor::QuadToTri (set & theElems, SMESHDS_Mesh * aMesh = GetMeshDS(); - set< const SMDS_MeshElement * >::iterator itElem; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) - { - const SMDS_MeshElement* elem = (*itElem); - if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() != 4 ) + Handle(Geom_Surface) surface; + SMESH_MesherHelper helper( *GetMesh() ); + + TIDSortedElemSet::iterator itElem; + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + const SMDS_MeshElement* elem = *itElem; + if ( !elem || elem->GetType() != SMDSAbs_Face ) + continue; + if ( elem->NbNodes() != ( elem->IsQuadratic() ? 8 : 4 )) continue; // retrieve element nodes - const SMDS_MeshNode* aNodes [4]; + const SMDS_MeshNode* aNodes [8]; SMDS_ElemIteratorPtr itN = elem->nodesIterator(); int i = 0; while ( itN->more() ) @@ -597,30 +1026,107 @@ bool SMESH_MeshEditor::QuadToTri (set & theElems, aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit ); int aShapeId = FindShape( elem ); - //MESSAGE( "aBadRate1 = " << aBadRate1 << "; aBadRate2 = " << aBadRate2 - // << " ShapeID = " << aShapeId << endl << elem ); + const SMDS_MeshElement* newElem = 0; - if ( aBadRate1 <= aBadRate2 ) { - // tr1 + tr2 is better - aMesh->ChangeElementNodes( elem, aNodes, 3 ); - //MESSAGE( endl << elem ); + if( !elem->IsQuadratic() ) { - elem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); + // split liner quadrangle + + if ( aBadRate1 <= aBadRate2 ) { + // tr1 + tr2 is better + aMesh->ChangeElementNodes( elem, aNodes, 3 ); + newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); + } + else { + // tr3 + tr4 is better + aMesh->ChangeElementNodes( elem, &aNodes[1], 3 ); + newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); + } } else { - // tr3 + tr4 is better - aMesh->ChangeElementNodes( elem, &aNodes[1], 3 ); - //MESSAGE( endl << elem ); - elem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); - } - //MESSAGE( endl << elem ); + // split quadratic quadrangle + + // get surface elem is on + if ( aShapeId != helper.GetSubShapeID() ) { + surface.Nullify(); + TopoDS_Shape shape; + if ( aShapeId > 0 ) + shape = aMesh->IndexToShape( aShapeId ); + if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) { + TopoDS_Face face = TopoDS::Face( shape ); + surface = BRep_Tool::Surface( face ); + if ( !surface.IsNull() ) + helper.SetSubShape( shape ); + } + } + // get elem nodes + const SMDS_MeshNode* aNodes [8]; + const SMDS_MeshNode* inFaceNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + int i = 0; + while ( itN->more() ) { + aNodes[ i++ ] = static_cast( itN->next() ); + if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() && + aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) + { + inFaceNode = aNodes[ i-1 ]; + } + } + // find middle point for (0,1,2,3) + // and create a node in this point; + gp_XYZ p( 0,0,0 ); + if ( surface.IsNull() ) { + for(i=0; i<4; i++) + p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() ); + p /= 4; + } + else { + TopoDS_Face face = TopoDS::Face( helper.GetSubShape() ); + gp_XY uv( 0,0 ); + for(i=0; i<4; i++) + uv += helper.GetNodeUV( face, aNodes[i], inFaceNode ); + uv /= 4.; + p = surface->Value( uv.X(), uv.Y() ).XYZ(); + } + const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() ); + myLastCreatedNodes.Append(newN); + + // create a new element + const SMDS_MeshNode* N[6]; + if ( aBadRate1 <= aBadRate2 ) { + N[0] = aNodes[0]; + N[1] = aNodes[1]; + N[2] = aNodes[2]; + N[3] = aNodes[4]; + N[4] = aNodes[5]; + N[5] = newN; + newElem = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0], + aNodes[6], aNodes[7], newN ); + } + else { + N[0] = aNodes[1]; + N[1] = aNodes[2]; + N[2] = aNodes[3]; + N[3] = aNodes[5]; + N[4] = aNodes[6]; + N[5] = newN; + newElem = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1], + aNodes[7], aNodes[4], newN ); + } + aMesh->ChangeElementNodes( elem, N, 6 ); + + } // quadratic case + + // care of a new element + + myLastCreatedElems.Append(newElem); + AddToSameGroups( newElem, elem, aMesh ); // put a new triangle on the same shape if ( aShapeId ) - aMesh->SetMeshElementOnShape( elem, aShapeId ); + aMesh->SetMeshElementOnShape( newElem, aShapeId ); } - return true; } @@ -628,53 +1134,666 @@ bool SMESH_MeshEditor::QuadToTri (set & theElems, //function : BestSplit //purpose : Find better diagonal for cutting. //======================================================================= + int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad, SMESH::Controls::NumericalFunctorPtr theCrit) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + if (!theCrit.get()) return -1; - if (!theQuad || theQuad->GetType() != SMDSAbs_Face || theQuad->NbNodes() != 4) + if (!theQuad || theQuad->GetType() != SMDSAbs_Face ) return -1; - // retrieve element nodes - const SMDS_MeshNode* aNodes [4]; - SMDS_ElemIteratorPtr itN = theQuad->nodesIterator(); - int i = 0; - while (itN->more()) - aNodes[ i++ ] = static_cast( itN->next() ); + if( theQuad->NbNodes()==4 || + (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) { - // compare two sets of possible triangles - double aBadRate1, aBadRate2; // to what extent a set is bad - SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] ); - SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] ); - aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit ); + // retrieve element nodes + const SMDS_MeshNode* aNodes [4]; + SMDS_ElemIteratorPtr itN = theQuad->nodesIterator(); + int i = 0; + //while (itN->more()) + while (i<4) { + aNodes[ i++ ] = static_cast( itN->next() ); + } + // compare two sets of possible triangles + double aBadRate1, aBadRate2; // to what extent a set is bad + SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] ); + SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] ); + aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit ); - SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] ); - SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] ); - aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit ); + SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] ); + SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] ); + aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit ); - if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better - return 1; // diagonal 1-3 + if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better + return 1; // diagonal 1-3 - return 2; // diagonal 2-4 + return 2; // diagonal 2-4 + } + return -1; } -//======================================================================= -//function : AddToSameGroups -//purpose : add elemToAdd to the groups the elemInGroups belongs to -//======================================================================= - -void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd, - const SMDS_MeshElement* elemInGroups, - SMESHDS_Mesh * aMesh) +namespace { - const set& groups = aMesh->GetGroups(); - set::const_iterator grIt = groups.begin(); - for ( ; grIt != groups.end(); grIt++ ) { - SMESHDS_Group* group = dynamic_cast( *grIt ); - if ( group && group->SMDSGroup().Contains( elemInGroups )) - group->SMDSGroup().Add( elemToAdd ); + // Methods of splitting volumes into tetra + + const int theHexTo5_1[5*4+1] = + { + 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1 + }; + const int theHexTo5_2[5*4+1] = + { + 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1 + }; + const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 }; + + const int theHexTo6_1[6*4+1] = + { + 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1 + }; + const int theHexTo6_2[6*4+1] = + { + 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1 + }; + const int theHexTo6_3[6*4+1] = + { + 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1 + }; + const int theHexTo6_4[6*4+1] = + { + 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1 + }; + const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 }; + + const int thePyraTo2_1[2*4+1] = + { + 0, 1, 2, 4, 0, 2, 3, 4, -1 + }; + const int thePyraTo2_2[2*4+1] = + { + 1, 2, 3, 4, 1, 3, 0, 4, -1 + }; + const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 }; + + const int thePentaTo3_1[3*4+1] = + { + 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1 + }; + const int thePentaTo3_2[3*4+1] = + { + 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1 + }; + const int thePentaTo3_3[3*4+1] = + { + 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1 + }; + const int thePentaTo3_4[3*4+1] = + { + 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1 + }; + const int thePentaTo3_5[3*4+1] = + { + 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1 + }; + const int thePentaTo3_6[3*4+1] = + { + 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1 + }; + const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3, + thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 }; + + struct TTriangleFacet //!< stores indices of three nodes of tetra facet + { + int _n1, _n2, _n3; + TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {} + bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); } + bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const; + }; + struct TSplitMethod + { + int _nbTetra; + const int* _connectivity; //!< foursomes of tetra connectivy finished by -1 + bool _baryNode; //!< additional node is to be created at cell barycenter + bool _ownConn; //!< to delete _connectivity in destructor + map _faceBaryNode; //!< map face index to node at BC of face + + TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false) + : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {} + ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; } + bool hasFacet( const TTriangleFacet& facet ) const + { + const int* tetConn = _connectivity; + for ( ; tetConn[0] >= 0; tetConn += 4 ) + if (( facet.contains( tetConn[0] ) + + facet.contains( tetConn[1] ) + + facet.contains( tetConn[2] ) + + facet.contains( tetConn[3] )) == 3 ) + return true; + return false; + } + }; + + //======================================================================= + /*! + * \brief return TSplitMethod for the given element + */ + //======================================================================= + + TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags) + { + const int iQ = vol.Element()->IsQuadratic() ? 2 : 1; + + // at HEXA_TO_24 method, each face of volume is split into triangles each based on + // an edge and a face barycenter; tertaherdons are based on triangles and + // a volume barycenter + const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 ); + + // Find out how adjacent volumes are split + + vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side + int hasAdjacentSplits = 0, maxTetConnSize = 0; + for ( int iF = 0; iF < vol.NbFaces(); ++iF ) + { + int nbNodes = vol.NbFaceNodes( iF ) / iQ; + maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2)); + if ( nbNodes < 4 ) continue; + + list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ]; + const int* nInd = vol.GetFaceNodesIndices( iF ); + if ( nbNodes == 4 ) + { + TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] ); + TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] ); + if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 ); + else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 ); + } + else + { + int iCom = 0; // common node of triangle faces to split into + for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom ) + { + TTriangleFacet t012( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+1)%nbNodes )], + nInd[ iQ * ( (iCom+2)%nbNodes )]); + TTriangleFacet t023( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+2)%nbNodes )], + nInd[ iQ * ( (iCom+3)%nbNodes )]); + if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() )) + { + triaSplits.push_back( t012 ); + triaSplits.push_back( t023 ); + break; + } + } + } + if ( !triaSplits.empty() ) + hasAdjacentSplits = true; + } + + // Among variants of split method select one compliant with adjacent volumes + + TSplitMethod method; + if ( !vol.Element()->IsPoly() && !is24TetMode ) + { + int nbVariants = 2, nbTet = 0; + const int** connVariants = 0; + switch ( vol.Element()->GetEntityType() ) + { + case SMDSEntity_Hexa: + case SMDSEntity_Quad_Hexa: + if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 ) + connVariants = theHexTo5, nbTet = 5; + else + connVariants = theHexTo6, nbTet = 6, nbVariants = 4; + break; + case SMDSEntity_Pyramid: + case SMDSEntity_Quad_Pyramid: + connVariants = thePyraTo2; nbTet = 2; + break; + case SMDSEntity_Penta: + case SMDSEntity_Quad_Penta: + connVariants = thePentaTo3; nbTet = 3; nbVariants = 6; + break; + default: + nbVariants = 0; + } + for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant ) + { + // check method compliancy with adjacent tetras, + // all found splits must be among facets of tetras described by this method + method = TSplitMethod( nbTet, connVariants[variant] ); + if ( hasAdjacentSplits && method._nbTetra > 0 ) + { + bool facetCreated = true; + for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF ) + { + list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin(); + for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet ) + facetCreated = method.hasFacet( *facet ); + } + if ( !facetCreated ) + method = TSplitMethod(0); // incompatible method + } + } + } + if ( method._nbTetra < 1 ) + { + // No standard method is applicable, use a generic solution: + // each facet of a volume is split into triangles and + // each of triangles and a volume barycenter form a tetrahedron. + + int* connectivity = new int[ maxTetConnSize + 1 ]; + method._connectivity = connectivity; + method._ownConn = true; + method._baryNode = true; + + int connSize = 0; + int baryCenInd = vol.NbNodes(); + for ( int iF = 0; iF < vol.NbFaces(); ++iF ) + { + const int nbNodes = vol.NbFaceNodes( iF ) / iQ; + const int* nInd = vol.GetFaceNodesIndices( iF ); + // find common node of triangle facets of tetra to create + int iCommon = 0; // index in linear numeration + const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ]; + if ( !triaSplits.empty() ) + { + // by found facets + const TTriangleFacet* facet = &triaSplits.front(); + for ( ; iCommon < nbNodes-1 ; ++iCommon ) + if ( facet->contains( nInd[ iQ * iCommon ]) && + facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ])) + break; + } + else if ( nbNodes > 3 && !is24TetMode ) + { + // find the best method of splitting into triangles by aspect ratio + SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio); + map< double, int > badness2iCommon; + const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF ); + int nbVariants = ( nbNodes == 4 ? 2 : nbNodes ); + for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon ) + for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast ) + { + SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )], + nodes[ iQ*((iLast-1)%nbNodes)], + nodes[ iQ*((iLast )%nbNodes)]); + double badness = getBadRate( &tria, aspectRatio ); + badness2iCommon.insert( make_pair( badness, iCommon )); + } + // use iCommon with lowest badness + iCommon = badness2iCommon.begin()->second; + } + if ( iCommon >= nbNodes ) + iCommon = 0; // something wrong + + // fill connectivity of tetrahedra based on a current face + int nbTet = nbNodes - 2; + if ( is24TetMode && nbNodes > 3 && triaSplits.empty()) + { + method._faceBaryNode.insert( make_pair( iF, (const SMDS_MeshNode*)0 )); + int faceBaryCenInd = baryCenInd + method._faceBaryNode.size(); + nbTet = nbNodes; + for ( int i = 0; i < nbTet; ++i ) + { + int i1 = i, i2 = (i+1) % nbNodes; + if ( !vol.IsFaceExternal( iF )) swap( i1, i2 ); + connectivity[ connSize++ ] = nInd[ iQ * i1 ]; + connectivity[ connSize++ ] = nInd[ iQ * i2 ]; + connectivity[ connSize++ ] = faceBaryCenInd; + connectivity[ connSize++ ] = baryCenInd; + } + } + else + { + for ( int i = 0; i < nbTet; ++i ) + { + int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes; + if ( !vol.IsFaceExternal( iF )) swap( i1, i2 ); + connectivity[ connSize++ ] = nInd[ iQ * iCommon ]; + connectivity[ connSize++ ] = nInd[ iQ * i1 ]; + connectivity[ connSize++ ] = nInd[ iQ * i2 ]; + connectivity[ connSize++ ] = baryCenInd; + } + } + method._nbTetra += nbTet; + } + connectivity[ connSize++ ] = -1; + } + return method; + } + //================================================================================ + /*! + * \brief Check if there is a tetraherdon adjacent to the given element via this facet + */ + //================================================================================ + + bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const + { + // find the tetrahedron including the three nodes of facet + const SMDS_MeshNode* n1 = elem->GetNode(_n1); + const SMDS_MeshNode* n2 = elem->GetNode(_n2); + const SMDS_MeshNode* n3 = elem->GetNode(_n3); + SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume); + while ( volIt1->more() ) + { + const SMDS_MeshElement* v = volIt1->next(); + if ( v->GetEntityType() != ( v->IsQuadratic() ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra )) + continue; + SMDS_ElemIteratorPtr volIt2 = n2->GetInverseElementIterator(SMDSAbs_Volume); + while ( volIt2->more() ) + if ( v != volIt2->next() ) + continue; + SMDS_ElemIteratorPtr volIt3 = n3->GetInverseElementIterator(SMDSAbs_Volume); + while ( volIt3->more() ) + if ( v == volIt3->next() ) + return true; + } + return false; + } + + //======================================================================= + /*! + * \brief A key of a face of volume + */ + //======================================================================= + + struct TVolumeFaceKey: pair< int, pair< int, int> > + { + TVolumeFaceKey( SMDS_VolumeTool& vol, int iF ) + { + TIDSortedNodeSet sortedNodes; + const int iQ = vol.Element()->IsQuadratic() ? 2 : 1; + int nbNodes = vol.NbFaceNodes( iF ); + const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF ); + for ( int i = 0; i < nbNodes; i += iQ ) + sortedNodes.insert( fNodes[i] ); + TIDSortedNodeSet::iterator n = sortedNodes.begin(); + first = (*(n++))->GetID(); + second.first = (*(n++))->GetID(); + second.second = (*(n++))->GetID(); + } + }; +} // namespace + +//======================================================================= +//function : SplitVolumesIntoTetra +//purpose : Split volumic elements into tetrahedra. +//======================================================================= + +void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, + const int theMethodFlags) +{ + // std-like iterator on coordinates of nodes of mesh element + typedef SMDS_StdIterator< TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator; + NXyzIterator xyzEnd; + + SMDS_VolumeTool volTool; + SMESH_MesherHelper helper( *GetMesh()); + + SMESHDS_SubMesh* subMesh = GetMeshDS()->MeshElements(1); + SMESHDS_SubMesh* fSubMesh = subMesh; + + SMESH_SequenceOfElemPtr newNodes, newElems; + + // map face of volume to it's baricenrtic node + map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode; + double bc[3]; + + TIDSortedElemSet::const_iterator elem = theElems.begin(); + for ( ; elem != theElems.end(); ++elem ) + { + SMDSAbs_EntityType geomType = (*elem)->GetEntityType(); + if ( geomType <= SMDSEntity_Quad_Tetra ) + continue; // tetra or face or ... + + if ( !volTool.Set( *elem )) continue; // not volume? strange... + + TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags ); + if ( splitMethod._nbTetra < 1 ) continue; + + // find submesh to add new tetras to + if ( !subMesh || !subMesh->Contains( *elem )) + { + int shapeID = FindShape( *elem ); + helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh + subMesh = GetMeshDS()->MeshElements( shapeID ); + } + int iQ; + if ( (*elem)->IsQuadratic() ) + { + iQ = 2; + // add quadratic links to the helper + for ( int iF = 0; iF < volTool.NbFaces(); ++iF ) + { + const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF ); + for ( int iN = 0; iN < volTool.NbFaceNodes( iF ); iN += iQ ) + helper.AddTLinkNode( fNodes[iF], fNodes[iF+2], fNodes[iF+1] ); + } + helper.SetIsQuadratic( true ); + } + else + { + iQ = 1; + helper.SetIsQuadratic( false ); + } + vector nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() ); + if ( splitMethod._baryNode ) + { + // make a node at barycenter + volTool.GetBaryCenter( bc[0], bc[1], bc[2] ); + SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] ); + nodes.push_back( gcNode ); + newNodes.Append( gcNode ); + } + if ( !splitMethod._faceBaryNode.empty() ) + { + // make or find baricentric nodes of faces + map::iterator iF_n = splitMethod._faceBaryNode.begin(); + for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n ) + { + map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n = + volFace2BaryNode.insert + ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), (const SMDS_MeshNode*)0) ).first; + if ( !f_n->second ) + { + volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] ); + newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] )); + } + nodes.push_back( iF_n->second = f_n->second ); + } + } + + // make tetras + helper.SetElementsOnShape( true ); + vector tetras( splitMethod._nbTetra ); // splits of a volume + const int* tetConn = splitMethod._connectivity; + for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 ) + newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ], + nodes[ tetConn[1] ], + nodes[ tetConn[2] ], + nodes[ tetConn[3] ])); + + ReplaceElemInGroups( *elem, tetras, GetMeshDS() ); + + // Split faces on sides of the split volume + + const SMDS_MeshNode** volNodes = volTool.GetNodes(); + for ( int iF = 0; iF < volTool.NbFaces(); ++iF ) + { + const int nbNodes = volTool.NbFaceNodes( iF ) / iQ; + if ( nbNodes < 4 ) continue; + + // find an existing face + vector fNodes( volTool.GetFaceNodes( iF ), + volTool.GetFaceNodes( iF ) + nbNodes*iQ ); + while ( const SMDS_MeshElement* face = GetMeshDS()->FindFace( fNodes )) + { + // make triangles + helper.SetElementsOnShape( false ); + vector< const SMDS_MeshElement* > triangles; + + map::iterator iF_n = splitMethod._faceBaryNode.find(iF); + if ( iF_n != splitMethod._faceBaryNode.end() ) + { + for ( int iN = 0; iN < nbNodes*iQ; iN += iQ ) + { + const SMDS_MeshNode* n1 = fNodes[iN]; + const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%nbNodes*iQ]; + const SMDS_MeshNode *n3 = iF_n->second; + if ( !volTool.IsFaceExternal( iF )) + swap( n2, n3 ); + triangles.push_back( helper.AddFace( n1,n2,n3 )); + } + } + else + { + // among possible triangles create ones discribed by split method + const int* nInd = volTool.GetFaceNodesIndices( iF ); + int nbVariants = ( nbNodes == 4 ? 2 : nbNodes ); + int iCom = 0; // common node of triangle faces to split into + list< TTriangleFacet > facets; + for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom ) + { + TTriangleFacet t012( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+1)%nbNodes )], + nInd[ iQ * ( (iCom+2)%nbNodes )]); + TTriangleFacet t023( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+2)%nbNodes )], + nInd[ iQ * ( (iCom+3)%nbNodes )]); + if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 )) + { + facets.push_back( t012 ); + facets.push_back( t023 ); + for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast ) + facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )], + nInd[ iQ * ((iLast-1)%nbNodes )], + nInd[ iQ * ((iLast )%nbNodes )])); + break; + } + } + list< TTriangleFacet >::iterator facet = facets.begin(); + for ( ; facet != facets.end(); ++facet ) + { + if ( !volTool.IsFaceExternal( iF )) + swap( facet->_n2, facet->_n3 ); + triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ], + volNodes[ facet->_n2 ], + volNodes[ facet->_n3 ])); + } + } + // find submesh to add new triangles in + if ( !fSubMesh || !fSubMesh->Contains( face )) + { + int shapeID = FindShape( face ); + fSubMesh = GetMeshDS()->MeshElements( shapeID ); + } + for ( int i = 0; i < triangles.size(); ++i ) + { + if ( !triangles.back() ) continue; + if ( fSubMesh ) + fSubMesh->AddElement( triangles.back()); + newElems.Append( triangles.back() ); + } + ReplaceElemInGroups( face, triangles, GetMeshDS() ); + GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false ); + } + + } // loop on volume faces to split them into triangles + + GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false ); + + } // loop on volumes to split + + myLastCreatedNodes = newNodes; + myLastCreatedElems = newElems; +} + +//======================================================================= +//function : AddToSameGroups +//purpose : add elemToAdd to the groups the elemInGroups belongs to +//======================================================================= + +void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd, + const SMDS_MeshElement* elemInGroups, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) { + set::const_iterator grIt = groups.begin(); + for ( ; grIt != groups.end(); grIt++ ) { + SMESHDS_Group* group = dynamic_cast( *grIt ); + if ( group && group->Contains( elemInGroups )) + group->SMDSGroup().Add( elemToAdd ); + } + } +} + + +//======================================================================= +//function : RemoveElemFromGroups +//purpose : Remove removeelem to the groups the elemInGroups belongs to +//======================================================================= +void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) + { + set::const_iterator GrIt = groups.begin(); + for (; GrIt != groups.end(); GrIt++) + { + SMESHDS_Group* grp = dynamic_cast(*GrIt); + if (!grp || grp->IsEmpty()) continue; + grp->SMDSGroup().Remove(removeelem); + } + } +} + +//================================================================================ +/*! + * \brief Replace elemToRm by elemToAdd in the all groups + */ +//================================================================================ + +void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm, + const SMDS_MeshElement* elemToAdd, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) { + set::const_iterator grIt = groups.begin(); + for ( ; grIt != groups.end(); grIt++ ) { + SMESHDS_Group* group = dynamic_cast( *grIt ); + if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd ) + group->SMDSGroup().Add( elemToAdd ); + } + } +} + +//================================================================================ +/*! + * \brief Replace elemToRm by elemToAdd in the all groups + */ +//================================================================================ + +void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm, + const vector& elemToAdd, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) + { + set::const_iterator grIt = groups.begin(); + for ( ; grIt != groups.end(); grIt++ ) { + SMESHDS_Group* group = dynamic_cast( *grIt ); + if ( group && group->SMDSGroup().Remove( elemToRm ) ) + for ( int i = 0; i < elemToAdd.size(); ++i ) + group->SMDSGroup().Add( elemToAdd[ i ] ); + } } } @@ -684,46 +1803,133 @@ void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd, // theCrit is used to select a diagonal to cut //======================================================================= -bool SMESH_MeshEditor::QuadToTri (std::set & theElems, - const bool the13Diag) +bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, + const bool the13Diag) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE( "::QuadToTri()" ); SMESHDS_Mesh * aMesh = GetMeshDS(); - set< const SMDS_MeshElement * >::iterator itElem; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) - { - const SMDS_MeshElement* elem = (*itElem); - if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() != 4 ) + Handle(Geom_Surface) surface; + SMESH_MesherHelper helper( *GetMesh() ); + + TIDSortedElemSet::iterator itElem; + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + const SMDS_MeshElement* elem = *itElem; + if ( !elem || elem->GetType() != SMDSAbs_Face ) continue; + bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8; + if(!isquad) continue; - // retrieve element nodes - const SMDS_MeshNode* aNodes [4]; - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - int i = 0; - while ( itN->more() ) - aNodes[ i++ ] = static_cast( itN->next() ); + if(elem->NbNodes()==4) { + // retrieve element nodes + const SMDS_MeshNode* aNodes [4]; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + int i = 0; + while ( itN->more() ) + aNodes[ i++ ] = static_cast( itN->next() ); - int aShapeId = FindShape( elem ); - const SMDS_MeshElement* newElem = 0; - if ( the13Diag ) - { - aMesh->ChangeElementNodes( elem, aNodes, 3 ); - newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); - } - else - { - aMesh->ChangeElementNodes( elem, &aNodes[1], 3 ); - newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); + int aShapeId = FindShape( elem ); + const SMDS_MeshElement* newElem = 0; + if ( the13Diag ) { + aMesh->ChangeElementNodes( elem, aNodes, 3 ); + newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); + } + else { + aMesh->ChangeElementNodes( elem, &aNodes[1], 3 ); + newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); + } + myLastCreatedElems.Append(newElem); + // put a new triangle on the same shape and add to the same groups + if ( aShapeId ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + AddToSameGroups( newElem, elem, aMesh ); } - // put a new triangle on the same shape and add to the same groups + // Quadratic quadrangle - if ( aShapeId ) - aMesh->SetMeshElementOnShape( newElem, aShapeId ); + if( elem->NbNodes()==8 && elem->IsQuadratic() ) { - AddToSameGroups( newElem, elem, aMesh ); + // get surface elem is on + int aShapeId = FindShape( elem ); + if ( aShapeId != helper.GetSubShapeID() ) { + surface.Nullify(); + TopoDS_Shape shape; + if ( aShapeId > 0 ) + shape = aMesh->IndexToShape( aShapeId ); + if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) { + TopoDS_Face face = TopoDS::Face( shape ); + surface = BRep_Tool::Surface( face ); + if ( !surface.IsNull() ) + helper.SetSubShape( shape ); + } + } + + const SMDS_MeshNode* aNodes [8]; + const SMDS_MeshNode* inFaceNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + int i = 0; + while ( itN->more() ) { + aNodes[ i++ ] = static_cast( itN->next() ); + if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() && + aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) + { + inFaceNode = aNodes[ i-1 ]; + } + } + + // find middle point for (0,1,2,3) + // and create a node in this point; + gp_XYZ p( 0,0,0 ); + if ( surface.IsNull() ) { + for(i=0; i<4; i++) + p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() ); + p /= 4; + } + else { + TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() ); + gp_XY uv( 0,0 ); + for(i=0; i<4; i++) + uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode ); + uv /= 4.; + p = surface->Value( uv.X(), uv.Y() ).XYZ(); + } + const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() ); + myLastCreatedNodes.Append(newN); + + // create a new element + const SMDS_MeshElement* newElem = 0; + const SMDS_MeshNode* N[6]; + if ( the13Diag ) { + N[0] = aNodes[0]; + N[1] = aNodes[1]; + N[2] = aNodes[2]; + N[3] = aNodes[4]; + N[4] = aNodes[5]; + N[5] = newN; + newElem = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0], + aNodes[6], aNodes[7], newN ); + } + else { + N[0] = aNodes[1]; + N[1] = aNodes[2]; + N[2] = aNodes[3]; + N[3] = aNodes[5]; + N[4] = aNodes[6]; + N[5] = newN; + newElem = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1], + aNodes[7], aNodes[4], newN ); + } + myLastCreatedElems.Append(newElem); + aMesh->ChangeElementNodes( elem, N, 6 ); + // put a new triangle on the same shape and add to the same groups + if ( aShapeId ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + AddToSameGroups( newElem, elem, aMesh ); + } } return true; @@ -746,18 +1952,24 @@ double getAngle(const SMDS_MeshElement * tr1, if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) || !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 )) return angle; - gp_Vec N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) ); + gp_Vec N1,N2; + if(!tr1->IsQuadratic()) + N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) ); + else + N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) ); if ( N1.SquareMagnitude() <= gp::Resolution() ) return angle; - gp_Vec N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) ); + if(!tr2->IsQuadratic()) + N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) ); + else + N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) ); if ( N2.SquareMagnitude() <= gp::Resolution() ) return angle; // find the first diagonal node n1 in the triangles: // take in account a diagonal link orientation const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 }; - for ( int t = 0; t < 2; t++ ) - { + for ( int t = 0; t < 2; t++ ) { SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator(); int i = 0, iDiag = -1; while ( it->more()) { @@ -787,9 +1999,8 @@ double getAngle(const SMDS_MeshElement * tr1, // class generating a unique ID for a pair of nodes // and able to return nodes by that ID // ================================================= - class LinkID_Gen { - public: +public: LinkID_Gen( const SMESHDS_Mesh* theMesh ) :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1) @@ -812,12 +2023,13 @@ class LinkID_Gen { return true; } - private: +private: LinkID_Gen(); const SMESHDS_Mesh* myMesh; long myMaxID; }; + //======================================================================= //function : TriToQuad //purpose : Fuse neighbour triangles into quadrangles. @@ -826,75 +2038,73 @@ class LinkID_Gen { // fusion is still performed. //======================================================================= -bool SMESH_MeshEditor::TriToQuad (set & theElems, +bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems, SMESH::Controls::NumericalFunctorPtr theCrit, const double theMaxAngle) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE( "::TriToQuad()" ); if ( !theCrit.get() ) return false; SMESHDS_Mesh * aMesh = GetMeshDS(); - LinkID_Gen aLinkID_Gen( aMesh ); - // Prepare data for algo: build // 1. map of elements with their linkIDs // 2. map of linkIDs with their elements - map< long, list< const SMDS_MeshElement* > > mapLi_listEl; - map< long, list< const SMDS_MeshElement* > >::iterator itLE; - map< const SMDS_MeshElement*, set< long > > mapEl_setLi; - map< const SMDS_MeshElement*, set< long > >::iterator itEL; + map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl; + map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE; + map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi; + map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL; - set::iterator itElem; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) - { - const SMDS_MeshElement* elem = (*itElem); - if ( !elem || elem->NbNodes() != 3 ) - continue; + TIDSortedElemSet::iterator itElem; + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + const SMDS_MeshElement* elem = *itElem; + if(!elem || elem->GetType() != SMDSAbs_Face ) continue; + bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic()); + if(!IsTria) continue; // retrieve element nodes const SMDS_MeshNode* aNodes [4]; SMDS_ElemIteratorPtr itN = elem->nodesIterator(); int i = 0; - while ( itN->more() ) - aNodes[ i++ ] = static_cast( itN->next() ); - ASSERT( i == 3 ); + while ( i<3 ) + aNodes[ i++ ] = cast2Node( itN->next() ); aNodes[ 3 ] = aNodes[ 0 ]; // fill maps - for ( i = 0; i < 3; i++ ) - { - long linkID = aLinkID_Gen.GetLinkID( aNodes[ i ], aNodes[ i+1 ] ); + for ( i = 0; i < 3; i++ ) { + SMESH_TLink link( aNodes[i], aNodes[i+1] ); // check if elements sharing a link can be fused - itLE = mapLi_listEl.find( linkID ); - if ( itLE != mapLi_listEl.end() ) - { + itLE = mapLi_listEl.find( link ); + if ( itLE != mapLi_listEl.end() ) { if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link continue; const SMDS_MeshElement* elem2 = (*itLE).second.front(); -// if ( FindShape( elem ) != FindShape( elem2 )) -// continue; // do not fuse triangles laying on different shapes + //if ( FindShape( elem ) != FindShape( elem2 )) + // continue; // do not fuse triangles laying on different shapes if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle ) continue; // avoid making badly shaped quads (*itLE).second.push_back( elem ); } - else - mapLi_listEl[ linkID ].push_back( elem ); - mapEl_setLi [ elem ].insert( linkID ); + else { + mapLi_listEl[ link ].push_back( elem ); + } + mapEl_setLi [ elem ].insert( link ); } } // Clean the maps from the links shared by a sole element, ie // links to which only one element is bound in mapLi_listEl - for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) - { + for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) { int nbElems = (*itLE).second.size(); if ( nbElems < 2 ) { const SMDS_MeshElement* elem = (*itLE).second.front(); - long link = (*itLE).first; + SMESH_TLink link = (*itLE).first; mapEl_setLi[ elem ].erase( link ); if ( mapEl_setLi[ elem ].empty() ) mapEl_setLi.erase( elem ); @@ -903,18 +2113,14 @@ bool SMESH_MeshEditor::TriToQuad (set & theElems, // Algo: fuse triangles into quadrangles - while ( ! mapEl_setLi.empty() ) - { + while ( ! mapEl_setLi.empty() ) { // Look for the start element: // the element having the least nb of shared links - const SMDS_MeshElement* startElem = 0; int minNbLinks = 4; - for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) - { + for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) { int nbLinks = (*itEL).second.size(); - if ( nbLinks < minNbLinks ) - { + if ( nbLinks < minNbLinks ) { startElem = (*itEL).first; minNbLinks = nbLinks; if ( minNbLinks == 1 ) @@ -924,17 +2130,14 @@ bool SMESH_MeshEditor::TriToQuad (set & theElems, // search elements to fuse starting from startElem or links of elements // fused earlyer - startLinks - list< long > startLinks; - while ( startElem || !startLinks.empty() ) - { - while ( !startElem && !startLinks.empty() ) - { + list< SMESH_TLink > startLinks; + while ( startElem || !startLinks.empty() ) { + while ( !startElem && !startLinks.empty() ) { // Get an element to start, by a link - long linkId = startLinks.front(); + SMESH_TLink linkId = startLinks.front(); startLinks.pop_front(); itLE = mapLi_listEl.find( linkId ); - if ( itLE != mapLi_listEl.end() ) - { + if ( itLE != mapLi_listEl.end() ) { list< const SMDS_MeshElement* > & listElem = (*itLE).second; list< const SMDS_MeshElement* >::iterator itE = listElem.begin(); for ( ; itE != listElem.end() ; itE++ ) @@ -944,69 +2147,66 @@ bool SMESH_MeshEditor::TriToQuad (set & theElems, } } - if ( startElem ) - { + if ( startElem ) { // Get candidates to be fused - const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0; - long link12, link13; + const SMESH_TLink *link12, *link13; startElem = 0; ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() ); - set< long >& setLi = mapEl_setLi[ tr1 ]; + set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ]; ASSERT( !setLi.empty() ); - set< long >::iterator itLi; + set< SMESH_TLink >::iterator itLi; for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ ) { - long linkID = (*itLi); - itLE = mapLi_listEl.find( linkID ); + const SMESH_TLink & link = (*itLi); + itLE = mapLi_listEl.find( link ); if ( itLE == mapLi_listEl.end() ) continue; + const SMDS_MeshElement* elem = (*itLE).second.front(); if ( elem == tr1 ) elem = (*itLE).second.back(); mapLi_listEl.erase( itLE ); if ( mapEl_setLi.find( elem ) == mapEl_setLi.end()) continue; - if ( tr2 ) - { + if ( tr2 ) { tr3 = elem; - link13 = linkID; + link13 = &link; } - else - { + else { tr2 = elem; - link12 = linkID; + link12 = &link; } // add other links of elem to list of links to re-start from - set< long >& links = mapEl_setLi[ elem ]; - set< long >::iterator it; - for ( it = links.begin(); it != links.end(); it++ ) - { - long linkID2 = (*it); - if ( linkID2 != linkID ) - startLinks.push_back( linkID2 ); + set< SMESH_TLink >& links = mapEl_setLi[ elem ]; + set< SMESH_TLink >::iterator it; + for ( it = links.begin(); it != links.end(); it++ ) { + const SMESH_TLink& link2 = (*it); + if ( link2 != link ) + startLinks.push_back( link2 ); } } // Get nodes of possible quadrangles - const SMDS_MeshNode *n12 [4], *n13 [4]; bool Ok12 = false, Ok13 = false; const SMDS_MeshNode *linkNode1, *linkNode2; - if ( tr2 && - aLinkID_Gen.GetNodes( link12, linkNode1, linkNode2 ) && - getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 )) - Ok12 = true; - if ( tr3 && - aLinkID_Gen.GetNodes( link13, linkNode1, linkNode2 ) && - getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 )) - Ok13 = true; + if(tr2) { + linkNode1 = link12->first; + linkNode2 = link12->second; + if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 )) + Ok12 = true; + } + if(tr3) { + linkNode1 = link13->first; + linkNode2 = link13->second; + if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 )) + Ok13 = true; + } // Choose a pair to fuse - - if ( Ok12 && Ok13 ) - { + if ( Ok12 && Ok13 ) { SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] ); SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] ); double aBadRate12 = getBadRate( &quad12, theCrit ); @@ -1017,24 +2217,98 @@ bool SMESH_MeshEditor::TriToQuad (set & theElems, Ok13 = false; } - // Make quadrangles // and remove fused elems and removed links from the maps - mapEl_setLi.erase( tr1 ); - if ( Ok12 ) - { + if ( Ok12 ) { mapEl_setLi.erase( tr2 ); - mapLi_listEl.erase( link12 ); - aMesh->ChangeElementNodes( tr1, n12, 4 ); - aMesh->RemoveElement( tr2 ); + mapLi_listEl.erase( *link12 ); + if(tr1->NbNodes()==3) { + if( tr1->GetID() < tr2->GetID() ) { + aMesh->ChangeElementNodes( tr1, n12, 4 ); + myLastCreatedElems.Append(tr1); + aMesh->RemoveElement( tr2 ); + } + else { + aMesh->ChangeElementNodes( tr2, n12, 4 ); + myLastCreatedElems.Append(tr2); + aMesh->RemoveElement( tr1); + } + } + else { + const SMDS_MeshNode* N1 [6]; + const SMDS_MeshNode* N2 [6]; + GetNodesFromTwoTria(tr1,tr2,N1,N2); + // now we receive following N1 and N2 (using numeration as above image) + // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6) + // i.e. first nodes from both arrays determ new diagonal + const SMDS_MeshNode* aNodes[8]; + aNodes[0] = N1[0]; + aNodes[1] = N1[1]; + aNodes[2] = N2[0]; + aNodes[3] = N2[1]; + aNodes[4] = N1[3]; + aNodes[5] = N2[5]; + aNodes[6] = N2[3]; + aNodes[7] = N1[5]; + if( tr1->GetID() < tr2->GetID() ) { + GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 ); + myLastCreatedElems.Append(tr1); + GetMeshDS()->RemoveElement( tr2 ); + } + else { + GetMeshDS()->ChangeElementNodes( tr2, aNodes, 8 ); + myLastCreatedElems.Append(tr2); + GetMeshDS()->RemoveElement( tr1 ); + } + // remove middle node (9) + GetMeshDS()->RemoveNode( N1[4] ); + } } - else if ( Ok13 ) - { + else if ( Ok13 ) { mapEl_setLi.erase( tr3 ); - mapLi_listEl.erase( link13 ); - aMesh->ChangeElementNodes( tr1, n13, 4 ); - aMesh->RemoveElement( tr3 ); + mapLi_listEl.erase( *link13 ); + if(tr1->NbNodes()==3) { + if( tr1->GetID() < tr2->GetID() ) { + aMesh->ChangeElementNodes( tr1, n13, 4 ); + myLastCreatedElems.Append(tr1); + aMesh->RemoveElement( tr3 ); + } + else { + aMesh->ChangeElementNodes( tr3, n13, 4 ); + myLastCreatedElems.Append(tr3); + aMesh->RemoveElement( tr1 ); + } + } + else { + const SMDS_MeshNode* N1 [6]; + const SMDS_MeshNode* N2 [6]; + GetNodesFromTwoTria(tr1,tr3,N1,N2); + // now we receive following N1 and N2 (using numeration as above image) + // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6) + // i.e. first nodes from both arrays determ new diagonal + const SMDS_MeshNode* aNodes[8]; + aNodes[0] = N1[0]; + aNodes[1] = N1[1]; + aNodes[2] = N2[0]; + aNodes[3] = N2[1]; + aNodes[4] = N1[3]; + aNodes[5] = N2[5]; + aNodes[6] = N2[3]; + aNodes[7] = N1[5]; + if( tr1->GetID() < tr2->GetID() ) { + GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 ); + myLastCreatedElems.Append(tr1); + GetMeshDS()->RemoveElement( tr3 ); + } + else { + GetMeshDS()->ChangeElementNodes( tr3, aNodes, 8 ); + myLastCreatedElems.Append(tr3); + GetMeshDS()->RemoveElement( tr1 ); + } + // remove middle node (9) + GetMeshDS()->RemoveNode( N1[4] ); + } } // Next element to fuse: the rejected one @@ -1058,15 +2332,15 @@ bool SMESH_MeshEditor::TriToQuad (set & theElems, //============================================================================= static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] ) { - if ( i1 == i2 ) - return; - int tmp = idNodes[ i1 ]; - idNodes[ i1 ] = idNodes[ i2 ]; - idNodes[ i2 ] = tmp; - gp_Pnt Ptmp = P[ i1 ]; - P[ i1 ] = P[ i2 ]; - P[ i2 ] = Ptmp; - DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")"); +if ( i1 == i2 ) +return; +int tmp = idNodes[ i1 ]; +idNodes[ i1 ] = idNodes[ i2 ]; +idNodes[ i2 ] = tmp; +gp_Pnt Ptmp = P[ i1 ]; +P[ i1 ] = P[ i2 ]; +P[ i2 ] = Ptmp; +DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")"); } //======================================================================= @@ -1077,7 +2351,7 @@ static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] ) //======================================================================= int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh, - int idNodes[] ) +int idNodes[] ) { gp_Pnt P[4]; int i; @@ -1106,10 +2380,10 @@ int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh, i = 1; swap ( i, i + 1, idNodes, P ); -// for ( int ii = 0; ii < 4; ii++ ) { -// const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); -// DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); -// } + // for ( int ii = 0; ii < 4; ii++ ) { + // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); + // DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); + // } } return i; } @@ -1225,7 +2499,7 @@ bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh, faceNodes.insert( idNodes[ 2 ] ); faceNodes.insert( idNodes[ iMin ] ); DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ] - << " leastDist = " << leastDist); + << " leastDist = " << leastDist); if ( leastDist <= DBL_MIN ) break; } @@ -1263,11 +2537,11 @@ bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh, P[ i ] = P[ i+1 ]; P[ i+1 ] = Ptmp; } -// else -// for ( int ii = 0; ii < 4; ii++ ) { -// const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); -// DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); -// } + // else + // for ( int ii = 0; ii < 4; ii++ ) { + // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); + // DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); + // } // Gravity center of the top and bottom faces gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.; @@ -1319,15 +2593,64 @@ bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh, swap( 5, 7, idNodes, P ); } -// DUMPSO( "OUTPUT: ========================================"); -// for ( i = 0; i < 8; i++ ) { -// float *p = ugrid->GetPoint(idNodes[i]); -// DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]); -// } + // DUMPSO( "OUTPUT: ========================================"); + // for ( i = 0; i < 8; i++ ) { + // float *p = ugrid->GetPoint(idNodes[i]); + // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]); + // } return true; }*/ +//================================================================================ +/*! + * \brief Return nodes linked to the given one + * \param theNode - the node + * \param linkedNodes - the found nodes + * \param type - the type of elements to check + * + * Medium nodes are ignored + */ +//================================================================================ + +void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode, + TIDSortedElemSet & linkedNodes, + SMDSAbs_ElementType type ) +{ + SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type); + while ( elemIt->more() ) + { + const SMDS_MeshElement* elem = elemIt->next(); + SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); + if ( elem->GetType() == SMDSAbs_Volume ) + { + SMDS_VolumeTool vol( elem ); + while ( nodeIt->more() ) { + const SMDS_MeshNode* n = cast2Node( nodeIt->next() ); + if ( theNode != n && vol.IsLinked( theNode, n )) + linkedNodes.insert( n ); + } + } + else + { + for ( int i = 0; nodeIt->more(); ++i ) { + const SMDS_MeshNode* n = cast2Node( nodeIt->next() ); + if ( n == theNode ) { + int iBefore = i - 1; + int iAfter = i + 1; + if ( elem->IsQuadratic() ) { + int nb = elem->NbNodes() / 2; + iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb ); + iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb ); + } + linkedNodes.insert( elem->GetNodeWrap( iAfter )); + linkedNodes.insert( elem->GetNodeWrap( iBefore )); + } + } + } + } +} + //======================================================================= //function : laplacianSmooth //purpose : pulls theNode toward the center of surrounding nodes directly @@ -1340,38 +2663,15 @@ void laplacianSmooth(const SMDS_MeshNode* theNode, { // find surrounding nodes - set< const SMDS_MeshNode* > nodeSet; - SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(); - while ( elemIt->more() ) - { - const SMDS_MeshElement* elem = elemIt->next(); - if ( elem->GetType() != SMDSAbs_Face ) - continue; - - // put all nodes in array - int nbNodes = 0, iNode = 0; - vector< const SMDS_MeshNode*> aNodes( elem->NbNodes() ); - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) - { - aNodes[ nbNodes ] = static_cast( itN->next() ); - if ( aNodes[ nbNodes ] == theNode ) - iNode = nbNodes; // index of theNode within aNodes - nbNodes++; - } - // add linked nodes - int iAfter = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1; - nodeSet.insert( aNodes[ iAfter ]); - int iBefore = ( iNode == 0 ) ? nbNodes - 1 : iNode - 1; - nodeSet.insert( aNodes[ iBefore ]); - } + TIDSortedElemSet nodeSet; + SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face ); // compute new coodrs double coord[] = { 0., 0., 0. }; - set< const SMDS_MeshNode* >::iterator nodeSetIt = nodeSet.begin(); + TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin(); for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) { - const SMDS_MeshNode* node = (*nodeSetIt); + const SMDS_MeshNode* node = cast2Node(*nodeSetIt); if ( theSurface.IsNull() ) { // smooth in 3D coord[0] += node->X(); coord[1] += node->Y(); @@ -1423,20 +2723,22 @@ void centroidalSmooth(const SMDS_MeshNode* theNode, // compute new XYZ - SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(); + SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face); while ( elemIt->more() ) { const SMDS_MeshElement* elem = elemIt->next(); - if ( elem->GetType() != SMDSAbs_Face ) - continue; nbElems++; gp_XYZ elemCenter(0.,0.,0.); SMESH::Controls::TSequenceOfXYZ aNodePoints; SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) - { + int nn = elem->NbNodes(); + if(elem->IsQuadratic()) nn = nn/2; + int i=0; + //while ( itN->more() ) { + while ( i( itN->next() ); + i++; gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() ); aNodePoints.push_back( aP ); if ( !theSurface.IsNull() ) { // smooth in 2D @@ -1448,12 +2750,11 @@ void centroidalSmooth(const SMDS_MeshNode* theNode, } double elemArea = anAreaFunc.GetValue( aNodePoints ); totalArea += elemArea; - elemCenter /= elem->NbNodes(); + elemCenter /= nn; aNewXYZ += elemCenter * elemArea; } aNewXYZ /= totalArea; if ( !theSurface.IsNull() ) { - ASSERT( theUVMap.find( theNode ) != theUVMap.end() ); theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() ); aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ(); } @@ -1496,18 +2797,23 @@ static bool getClosestUV (Extrema_GenExtPS& projector, // on edges and boundary nodes are always fixed. //======================================================================= -void SMESH_MeshEditor::Smooth (set & theElems, - set & theFixedNodes, - const SmoothMethod theSmoothMethod, - const int theNbIterations, - double theTgtAspectRatio, - const bool the2D) +void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, + set & theFixedNodes, + const SmoothMethod theSmoothMethod, + const int theNbIterations, + double theTgtAspectRatio, + const bool the2D) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()"); if ( theTgtAspectRatio < 1.0 ) theTgtAspectRatio = 1.0; + const double disttol = 1.e-16; + SMESH::Controls::AspectRatio aQualityFunc; SMESHDS_Mesh* aMesh = GetMeshDS(); @@ -1515,12 +2821,14 @@ void SMESH_MeshEditor::Smooth (set & theElems, if ( theElems.empty() ) { // add all faces to theElems SMDS_FaceIteratorPtr fIt = aMesh->facesIterator(); - while ( fIt->more() ) - theElems.insert( fIt->next() ); + while ( fIt->more() ) { + const SMDS_MeshElement* face = fIt->next(); + theElems.insert( face ); + } } // get all face ids theElems are on set< int > faceIdSet; - set< const SMDS_MeshElement* >::iterator itElem; + TIDSortedElemSet::iterator itElem; if ( the2D ) for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { int fId = FindShape( *itElem ); @@ -1540,8 +2848,7 @@ void SMESH_MeshEditor::Smooth (set & theElems, // =============================================== set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end - for ( ; fId != faceIdSet.rend(); ++fId ) - { + for ( ; fId != faceIdSet.rend(); ++fId ) { // get face surface and submesh Handle(Geom_Surface) surface; SMESHDS_SubMesh* faceSubMesh = 0; @@ -1568,6 +2875,7 @@ void SMESH_MeshEditor::Smooth (set & theElems, // compute UV for them // --------------------------------------------------------- bool checkBoundaryNodes = false; + bool isQuadratic = false; set setMovableNodes; map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2; list< gp_XY > listUV; // uvs the 2 uvMaps refer to @@ -1581,15 +2889,14 @@ void SMESH_MeshEditor::Smooth (set & theElems, } int nbElemOnFace = 0; itElem = theElems.begin(); - // loop on not yet smoothed elements: look for elems on a face - while ( itElem != theElems.end() ) - { + // loop on not yet smoothed elements: look for elems on a face + while ( itElem != theElems.end() ) { if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() ) break; // all elements found - const SMDS_MeshElement* elem = (*itElem); + const SMDS_MeshElement* elem = *itElem; if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 || - ( faceSubMesh && !faceSubMesh->Contains( elem ))) { + ( faceSubMesh && !faceSubMesh->Contains( elem ))) { ++itElem; continue; } @@ -1597,11 +2904,17 @@ void SMESH_MeshEditor::Smooth (set & theElems, theElems.erase( itElem++ ); nbElemOnFace++; + if ( !isQuadratic ) + isQuadratic = elem->IsQuadratic(); + // get movable nodes of elem const SMDS_MeshNode* node; SMDS_TypeOfPosition posType; SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) { + int nn = 0, nbn = elem->NbNodes(); + if(elem->IsQuadratic()) + nbn = nbn/2; + while ( nn++ < nbn ) { node = static_cast( itN->next() ); const SMDS_PositionPtr& pos = node->GetPosition(); posType = pos.get() ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE; @@ -1611,13 +2924,12 @@ void SMESH_MeshEditor::Smooth (set & theElems, { // check if all faces around the node are on faceSubMesh // because a node on edge may be bound to face - SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(); + SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face); bool all = true; if ( faceSubMesh ) { while ( eIt->more() && all ) { const SMDS_MeshElement* e = eIt->next(); - if ( e->GetType() == SMDSAbs_Face ) - all = faceSubMesh->Contains( e ); + all = faceSubMesh->Contains( e ); } } if ( all ) @@ -1635,29 +2947,31 @@ void SMESH_MeshEditor::Smooth (set & theElems, // get nodes to check UV list< const SMDS_MeshNode* > uvCheckNodes; itN = elem->nodesIterator(); - while ( itN->more() ) { + nn = 0; nbn = elem->NbNodes(); + if(elem->IsQuadratic()) + nbn = nbn/2; + while ( nn++ < nbn ) { node = static_cast( itN->next() ); if ( uvMap.find( node ) == uvMap.end() ) uvCheckNodes.push_back( node ); // add nodes of elems sharing node -// SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(); -// while ( eIt->more() ) { -// const SMDS_MeshElement* e = eIt->next(); -// if ( e != elem && e->GetType() == SMDSAbs_Face ) { -// SMDS_ElemIteratorPtr nIt = e->nodesIterator(); -// while ( nIt->more() ) { -// const SMDS_MeshNode* n = -// static_cast( nIt->next() ); -// if ( uvMap.find( n ) == uvMap.end() ) -// uvCheckNodes.push_back( n ); -// } -// } -// } + // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face); + // while ( eIt->more() ) { + // const SMDS_MeshElement* e = eIt->next(); + // if ( e != elem ) { + // SMDS_ElemIteratorPtr nIt = e->nodesIterator(); + // while ( nIt->more() ) { + // const SMDS_MeshNode* n = + // static_cast( nIt->next() ); + // if ( uvMap.find( n ) == uvMap.end() ) + // uvCheckNodes.push_back( n ); + // } + // } + // } } // check UV on face list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin(); - for ( ; n != uvCheckNodes.end(); ++n ) - { + for ( ; n != uvCheckNodes.end(); ++n ) { node = *n; gp_XY uv( 0, 0 ); const SMDS_PositionPtr& pos = node->GetPosition(); @@ -1724,29 +3038,24 @@ void SMESH_MeshEditor::Smooth (set & theElems, // fix nodes on mesh boundary - if ( checkBoundaryNodes ) - { - typedef pair TLink; - map< TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace - map< TLink, int >::iterator link_nb; + if ( checkBoundaryNodes ) { + map< NLink, int > linkNbMap; // how many times a link encounters in elemsOnFace + map< NLink, int >::iterator link_nb; // put all elements links to linkNbMap list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin(); - for ( ; elemIt != elemsOnFace.end(); ++elemIt ) - { - // put elem nodes in array - vector< const SMDS_MeshNode* > nodes; - nodes.reserve( (*elemIt)->NbNodes() + 1 ); - SMDS_ElemIteratorPtr itN = (*elemIt)->nodesIterator(); - while ( itN->more() ) - nodes.push_back( static_cast( itN->next() )); - nodes.push_back( nodes.front() ); + for ( ; elemIt != elemsOnFace.end(); ++elemIt ) { + const SMDS_MeshElement* elem = (*elemIt); + int nbn = elem->NbNodes(); + if(elem->IsQuadratic()) + nbn = nbn/2; // loop on elem links: insert them in linkNbMap - for ( int iN = 1; iN < nodes.size(); ++iN ) { - TLink link; - if ( nodes[ iN-1 ]->GetID() < nodes[ iN ]->GetID() ) - link = make_pair( nodes[ iN-1 ], nodes[ iN ] ); - else - link = make_pair( nodes[ iN ], nodes[ iN-1 ] ); + const SMDS_MeshNode* curNode, *prevNode = elem->GetNodeWrap( nbn ); + for ( int iN = 0; iN < nbn; ++iN ) { + curNode = elem->GetNode( iN ); + NLink link; + if ( curNode < prevNode ) link = make_pair( curNode , prevNode ); + else link = make_pair( prevNode , curNode ); + prevNode = curNode; link_nb = linkNbMap.find( link ); if ( link_nb == linkNbMap.end() ) linkNbMap.insert( make_pair ( link, 1 )); @@ -1770,11 +3079,9 @@ void SMESH_MeshEditor::Smooth (set & theElems, // ----------------------------------------------------- set nodesNearSeam; // to smooth using uvMap2 - if ( !surface.IsNull() ) - { + if ( !surface.IsNull() ) { TopExp_Explorer eExp( face, TopAbs_EDGE ); - for ( ; eExp.More(); eExp.Next() ) - { + for ( ; eExp.More(); eExp.Next() ) { TopoDS_Edge edge = TopoDS::Edge( eExp.Current() ); if ( !BRep_Tool::IsClosed( edge, face )) continue; @@ -1798,8 +3105,11 @@ void SMESH_MeshEditor::Smooth (set & theElems, // get nodes on seam and its vertices list< const SMDS_MeshNode* > seamNodes; SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes(); - while ( nSeamIt->more() ) - seamNodes.push_back( nSeamIt->next() ); + while ( nSeamIt->more() ) { + const SMDS_MeshNode* node = nSeamIt->next(); + if ( !isQuadratic || !IsMedium( node )) + seamNodes.push_back( node ); + } TopExp_Explorer vExp( edge, TopAbs_VERTEX ); for ( ; vExp.More(); vExp.Next() ) { sm = aMesh->MeshElements( vExp.Current() ); @@ -1811,8 +3121,7 @@ void SMESH_MeshEditor::Smooth (set & theElems, } // loop on nodes on seam list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin(); - for ( ; noSeIt != seamNodes.end(); ++noSeIt ) - { + for ( ; noSeIt != seamNodes.end(); ++noSeIt ) { const SMDS_MeshNode* nSeam = *noSeIt; map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam ); if ( n_uv == uvMap.end() ) @@ -1827,15 +3136,14 @@ void SMESH_MeshEditor::Smooth (set & theElems, uvMap2[ nSeam ] = &listUV.back(); // collect movable nodes linked to ones on seam in nodesNearSeam - SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(); - while ( eIt->more() ) - { + SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face); + while ( eIt->more() ) { const SMDS_MeshElement* e = eIt->next(); - if ( e->GetType() != SMDSAbs_Face ) - continue; int nbUseMap1 = 0, nbUseMap2 = 0; SMDS_ElemIteratorPtr nIt = e->nodesIterator(); - while ( nIt->more() ) + int nn = 0, nbn = e->NbNodes(); + if(e->IsQuadratic()) nbn = nbn/2; + while ( nn++ < nbn ) { const SMDS_MeshNode* n = static_cast( nIt->next() ); @@ -1857,10 +3165,10 @@ void SMESH_MeshEditor::Smooth (set & theElems, } // for centroidalSmooth all element nodes must // be on one side of a seam - if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) - { + if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) { SMDS_ElemIteratorPtr nIt = e->nodesIterator(); - while ( nIt->more() ) { + nn = 0; + while ( nn++ < nbn ) { const SMDS_MeshNode* n = static_cast( nIt->next() ); setMovableNodes.erase( n ); @@ -1883,12 +3191,10 @@ void SMESH_MeshEditor::Smooth (set & theElems, int it = -1; double maxRatio = -1., maxDisplacement = -1.; set::iterator nodeToMove; - for ( it = 0; it < theNbIterations; it++ ) - { + for ( it = 0; it < theNbIterations; it++ ) { maxDisplacement = 0.; nodeToMove = setMovableNodes.begin(); - for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) - { + for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) { const SMDS_MeshNode* node = (*nodeToMove); gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() ); @@ -1906,7 +3212,8 @@ void SMESH_MeshEditor::Smooth (set & theElems, maxDisplacement = aDispl; } // no node movement => exit - if ( maxDisplacement < 1.e-16 ) { + //if ( maxDisplacement < 1.e-16 ) { + if ( maxDisplacement < disttol ) { MESSAGE("-- no node movement --"); break; } @@ -1914,8 +3221,7 @@ void SMESH_MeshEditor::Smooth (set & theElems, // check elements quality maxRatio = 0; list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin(); - for ( ; elemIt != elemsOnFace.end(); ++elemIt ) - { + for ( ; elemIt != elemsOnFace.end(); ++elemIt ) { const SMDS_MeshElement* elem = (*elemIt); if ( !elem || elem->GetType() != SMDSAbs_Face ) continue; @@ -1944,10 +3250,8 @@ void SMESH_MeshEditor::Smooth (set & theElems, // new nodes positions are computed, // record movement in DS and set new UV // --------------------------------------- - nodeToMove = setMovableNodes.begin(); - for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) - { + for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) { SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove); aMesh->MoveNode( node, node->X(), node->Y(), node->Z() ); map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node ); @@ -1958,7 +3262,50 @@ void SMESH_MeshEditor::Smooth (set & theElems, } } + // move medium nodes of quadratic elements + if ( isQuadratic ) + { + SMESH_MesherHelper helper( *GetMesh() ); + if ( !face.IsNull() ) + helper.SetSubShape( face ); + list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin(); + for ( ; elemIt != elemsOnFace.end(); ++elemIt ) { + const SMDS_QuadraticFaceOfNodes* QF = + dynamic_cast (*elemIt); + if(QF) { + vector Ns; + Ns.reserve(QF->NbNodes()+1); + SMDS_NodeIteratorPtr anIter = QF->interlacedNodesIterator(); + while ( anIter->more() ) + Ns.push_back( anIter->next() ); + Ns.push_back( Ns[0] ); + double x, y, z; + for(int i=0; iNbNodes(); i=i+2) { + if ( !surface.IsNull() ) { + gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] ); + gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] ); + gp_XY uv = ( uv1 + uv2 ) / 2.; + gp_Pnt xyz = surface->Value( uv.X(), uv.Y() ); + x = xyz.X(); y = xyz.Y(); z = xyz.Z(); + } + else { + x = (Ns[i]->X() + Ns[i+2]->X())/2; + y = (Ns[i]->Y() + Ns[i+2]->Y())/2; + z = (Ns[i]->Z() + Ns[i+2]->Z())/2; + } + if( fabs( Ns[i+1]->X() - x ) > disttol || + fabs( Ns[i+1]->Y() - y ) > disttol || + fabs( Ns[i+1]->Z() - z ) > disttol ) { + // we have to move i+1 node + aMesh->MoveNode( Ns[i+1], x, y, z ); + } + } + } + } + } + } // loop on face ids + } //======================================================================= @@ -1968,8 +3315,8 @@ void SMESH_MeshEditor::Smooth (set & theElems, // iNotSame is where prevNodes and nextNodes are different //======================================================================= -static bool isReverse(const SMDS_MeshNode* prevNodes[], - const SMDS_MeshNode* nextNodes[], +static bool isReverse(vector prevNodes, + vector nextNodes, const int nbNodes, const int iNotSame) { @@ -1992,142 +3339,388 @@ static bool isReverse(const SMDS_MeshNode* prevNodes[], } //======================================================================= -//function : sweepElement -//purpose : +/*! + * \brief Create elements by sweeping an element + * \param elem - element to sweep + * \param newNodesItVec - nodes generated from each node of the element + * \param newElems - generated elements + * \param nbSteps - number of sweeping steps + * \param srcElements - to append elem for each generated element + */ //======================================================================= -static void sweepElement(SMESHDS_Mesh* aMesh, - const SMDS_MeshElement* elem, - const vector & newNodesItVec, - list& newElems) +void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem, + const vector & newNodesItVec, + list& newElems, + const int nbSteps, + SMESH_SequenceOfElemPtr& srcElements) { + SMESHDS_Mesh* aMesh = GetMeshDS(); + // Loop on elem nodes: // find new nodes and detect same nodes indices int nbNodes = elem->NbNodes(); - list::const_iterator itNN[ nbNodes ]; - const SMDS_MeshNode* prevNod[ nbNodes ], *nextNod[ nbNodes ]; + vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes ); + vector prevNod( nbNodes ); + vector nextNod( nbNodes ); + vector midlNod( nbNodes ); + int iNode, nbSame = 0, iNotSameNode = 0, iSameNode = 0; + vector sames(nbNodes); + vector issimple(nbNodes); - for ( iNode = 0; iNode < nbNodes; iNode++ ) - { + for ( iNode = 0; iNode < nbNodes; iNode++ ) { TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ]; const SMDS_MeshNode* node = nnIt->first; const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second; - if ( listNewNodes.empty() ) + if ( listNewNodes.empty() ) { return; + } + + issimple[iNode] = (listNewNodes.size()==nbSteps); // is node medium itNN[ iNode ] = listNewNodes.begin(); prevNod[ iNode ] = node; nextNod[ iNode ] = listNewNodes.front(); - if ( prevNod[ iNode ] != nextNod [ iNode ]) - iNotSameNode = iNode; - else { - iSameNode = iNode; - nbSame++; + if( !elem->IsQuadratic() || !issimple[iNode] ) { + if ( prevNod[ iNode ] != nextNod [ iNode ]) + iNotSameNode = iNode; + else { + iSameNode = iNode; + //nbSame++; + sames[nbSame++] = iNode; + } } } + + //cout<<" nbSame = "< 2) { MESSAGE( " Too many same nodes of element " << elem->GetID() ); + //INFOS( " Too many same nodes of element " << elem->GetID() ); return; } + // if( elem->IsQuadratic() && nbSame>0 ) { + // MESSAGE( "Can not rotate quadratic element " << elem->GetID() ); + // return; + // } + int iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0; + int nbBaseNodes = ( elem->IsQuadratic() ? nbNodes/2 : nbNodes ); if ( nbSame > 0 ) { - iBeforeSame = ( iSameNode == 0 ? nbNodes - 1 : iSameNode - 1 ); - iAfterSame = ( iSameNode + 1 == nbNodes ? 0 : iSameNode + 1 ); + iBeforeSame = ( iSameNode == 0 ? nbBaseNodes - 1 : iSameNode - 1 ); + iAfterSame = ( iSameNode + 1 == nbBaseNodes ? 0 : iSameNode + 1 ); iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 ); } + //if(nbNodes==8) + //cout<<" prevNod[0]="<< prevNod[0]<<" prevNod[1]="<< prevNod[1] + // <<" prevNod[2]="<< prevNod[2]<<" prevNod[3]="<< prevNod[4] + // <<" prevNod[4]="<< prevNod[4]<<" prevNod[5]="<< prevNod[5] + // <<" prevNod[6]="<< prevNod[6]<<" prevNod[7]="<< prevNod[7]< 2 && !isReverse( prevNod, nextNod, nbNodes, iNotSameNode )) { //MESSAGE("Reversed elem " << elem ); i0 = 2; i2 = 0; - if ( nbSame > 0 ) { - int iAB = iAfterSame + iBeforeSame; - iBeforeSame = iAB - iBeforeSame; - iAfterSame = iAB - iAfterSame; - } + if ( nbSame > 0 ) + std::swap( iBeforeSame, iAfterSame ); } // make new elements - int iStep, nbSteps = newNodesItVec[ 0 ]->second.size(); - for (iStep = 0; iStep < nbSteps; iStep++ ) - { + for (int iStep = 0; iStep < nbSteps; iStep++ ) { // get next nodes for ( iNode = 0; iNode < nbNodes; iNode++ ) { - nextNod[ iNode ] = *itNN[ iNode ]; - itNN[ iNode ]++; + if(issimple[iNode]) { + nextNod[ iNode ] = *itNN[ iNode ]; + itNN[ iNode ]++; + } + else { + if( elem->GetType()==SMDSAbs_Node ) { + // we have to use two nodes + midlNod[ iNode ] = *itNN[ iNode ]; + itNN[ iNode ]++; + nextNod[ iNode ] = *itNN[ iNode ]; + itNN[ iNode ]++; + } + else if(!elem->IsQuadratic() || elem->IsMediumNode(prevNod[iNode]) ) { + // we have to use each second node + //itNN[ iNode ]++; + nextNod[ iNode ] = *itNN[ iNode ]; + itNN[ iNode ]++; + } + else { + // we have to use two nodes + midlNod[ iNode ] = *itNN[ iNode ]; + itNN[ iNode ]++; + nextNod[ iNode ] = *itNN[ iNode ]; + itNN[ iNode ]++; + } + } } SMDS_MeshElement* aNewElem = 0; - switch ( nbNodes ) - { - case 0: - return; - case 1: { // NODE - if ( nbSame == 0 ) - aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] ); - break; - } - case 2: { // EDGE - - if ( nbSame == 0 ) - aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], - nextNod[ 1 ], nextNod[ 0 ] ); - else - aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], - nextNod[ iNotSameNode ] ); - break; - } - case 3: { // TRIANGLE - - if ( nbSame == 0 ) // --- pentahedron - aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], - nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ] ); + if(!elem->IsPoly()) { + switch ( nbNodes ) { + case 0: + return; + case 1: { // NODE + if ( nbSame == 0 ) { + if(issimple[0]) + aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] ); + else + aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] ); + } + break; + } + case 2: { // EDGE + if ( nbSame == 0 ) + aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], + nextNod[ 1 ], nextNod[ 0 ] ); + else + aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], + nextNod[ iNotSameNode ] ); + break; + } - else if ( nbSame == 1 ) // --- pyramid - aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], - nextNod[ iAfterSame ], nextNod[ iBeforeSame ], - nextNod[ iSameNode ]); + case 3: { // TRIANGLE or quadratic edge + if(elem->GetType() == SMDSAbs_Face) { // TRIANGLE - else // 2 same nodes: --- tetrahedron - aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], - nextNod[ iNotSameNode ]); - break; - } - case 4: { // QUADRANGLE + if ( nbSame == 0 ) // --- pentahedron + aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], + nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ] ); - if ( nbSame == 0 ) // --- hexahedron - aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], prevNod[ 3 ], - nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ], nextNod[ 3 ]); + else if ( nbSame == 1 ) // --- pyramid + aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], + nextNod[ iAfterSame ], nextNod[ iBeforeSame ], + nextNod[ iSameNode ]); - else if ( nbSame == 1 ) // --- pyramid + pentahedron - { - aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], - nextNod[ iAfterSame ], nextNod[ iBeforeSame ], - nextNod[ iSameNode ]); - newElems.push_back( aNewElem ); - aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ], - prevNod[ iBeforeSame ], nextNod[ iAfterSame ], - nextNod[ iOpposSame ], nextNod[ iBeforeSame ] ); - } - else if ( nbSame == 2 ) // pentahedron - { - if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) - // iBeforeSame is same too - aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ], - nextNod[ iOpposSame ], prevNod[ iSameNode ], - prevNod[ iAfterSame ], nextNod[ iAfterSame ]); - else - // iAfterSame is same too - aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ], - nextNod[ iBeforeSame ], prevNod[ iAfterSame ], - prevNod[ iOpposSame ], nextNod[ iOpposSame ]); + else // 2 same nodes: --- tetrahedron + aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], + nextNod[ iNotSameNode ]); + } + else { // quadratic edge + if(nbSame==0) { // quadratic quadrangle + aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], nextNod[1], prevNod[1], + midlNod[0], nextNod[2], midlNod[1], prevNod[2]); + } + else if(nbSame==1) { // quadratic triangle + if(sames[0]==2) { + return; // medium node on axis + } + else if(sames[0]==0) { + aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1], + nextNod[2], midlNod[1], prevNod[2]); + } + else { // sames[0]==1 + aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1], + midlNod[0], nextNod[2], prevNod[2]); + } + } + else { + return; + } + } + break; + } + case 4: { // QUADRANGLE + + if ( nbSame == 0 ) // --- hexahedron + aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], prevNod[ 3 ], + nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ], nextNod[ 3 ]); + + else if ( nbSame == 1 ) { // --- pyramid + pentahedron + aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], + nextNod[ iAfterSame ], nextNod[ iBeforeSame ], + nextNod[ iSameNode ]); + newElems.push_back( aNewElem ); + aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ], + prevNod[ iBeforeSame ], nextNod[ iAfterSame ], + nextNod[ iOpposSame ], nextNod[ iBeforeSame ] ); + } + else if ( nbSame == 2 ) { // pentahedron + if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) + // iBeforeSame is same too + aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ], + nextNod[ iOpposSame ], prevNod[ iSameNode ], + prevNod[ iAfterSame ], nextNod[ iAfterSame ]); + else + // iAfterSame is same too + aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ], + nextNod[ iBeforeSame ], prevNod[ iAfterSame ], + prevNod[ iOpposSame ], nextNod[ iOpposSame ]); + } + break; + } + case 6: { // quadratic triangle + // create pentahedron with 15 nodes + if(nbSame==0) { + if(i0>0) { // reversed case + aNewElem = aMesh->AddVolume (prevNod[0], prevNod[2], prevNod[1], + nextNod[0], nextNod[2], nextNod[1], + prevNod[5], prevNod[4], prevNod[3], + nextNod[5], nextNod[4], nextNod[3], + midlNod[0], midlNod[2], midlNod[1]); + } + else { // not reversed case + aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], + nextNod[0], nextNod[1], nextNod[2], + prevNod[3], prevNod[4], prevNod[5], + nextNod[3], nextNod[4], nextNod[5], + midlNod[0], midlNod[1], midlNod[2]); + } + } + else if(nbSame==1) { + // 2d order pyramid of 13 nodes + //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, + // int n12,int n23,int n34,int n41, + // int n15,int n25,int n35,int n45, int ID); + int n5 = iSameNode; + int n1,n4,n41,n15,n45; + if(i0>0) { // reversed case + n1 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 ); + n4 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 ); + n41 = n1 + 3; + n15 = n5 + 3; + n45 = n4 + 3; + } + else { + n1 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 ); + n4 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 ); + n41 = n4 + 3; + n15 = n1 + 3; + n45 = n5 + 3; + } + aNewElem = aMesh->AddVolume(prevNod[n1], nextNod[n1], + nextNod[n4], prevNod[n4], prevNod[n5], + midlNod[n1], nextNod[n41], + midlNod[n4], prevNod[n41], + prevNod[n15], nextNod[n15], + nextNod[n45], prevNod[n45]); + } + else if(nbSame==2) { + // 2d order tetrahedron of 10 nodes + //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, + // int n12,int n23,int n31, + // int n14,int n24,int n34, int ID); + int n1 = iNotSameNode; + int n2,n3,n12,n23,n31; + if(i0>0) { // reversed case + n2 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 ); + n3 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 ); + n12 = n2 + 3; + n23 = n3 + 3; + n31 = n1 + 3; + } + else { + n2 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 ); + n3 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 ); + n12 = n1 + 3; + n23 = n2 + 3; + n31 = n3 + 3; + } + aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1], + prevNod[n12], prevNod[n23], prevNod[n31], + midlNod[n1], nextNod[n12], nextNod[n31]); + } + break; + } + case 8: { // quadratic quadrangle + if(nbSame==0) { + // create hexahedron with 20 nodes + if(i0>0) { // reversed case + aNewElem = aMesh->AddVolume (prevNod[0], prevNod[3], prevNod[2], prevNod[1], + nextNod[0], nextNod[3], nextNod[2], nextNod[1], + prevNod[7], prevNod[6], prevNod[5], prevNod[4], + nextNod[7], nextNod[6], nextNod[5], nextNod[4], + midlNod[0], midlNod[3], midlNod[2], midlNod[1]); + } + else { // not reversed case + aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3], + nextNod[0], nextNod[1], nextNod[2], nextNod[3], + prevNod[4], prevNod[5], prevNod[6], prevNod[7], + nextNod[4], nextNod[5], nextNod[6], nextNod[7], + midlNod[0], midlNod[1], midlNod[2], midlNod[3]); + } + } + else if(nbSame==1) { + // --- pyramid + pentahedron - can not be created since it is needed + // additional middle node ot the center of face + INFOS( " Sweep for face " << elem->GetID() << " can not be created" ); + return; + } + else if(nbSame==2) { + // 2d order Pentahedron with 15 nodes + //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, + // int n12,int n23,int n31,int n45,int n56,int n64, + // int n14,int n25,int n36, int ID); + int n1,n2,n4,n5; + if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) { + // iBeforeSame is same too + n1 = iBeforeSame; + n2 = iOpposSame; + n4 = iSameNode; + n5 = iAfterSame; + } + else { + // iAfterSame is same too + n1 = iSameNode; + n2 = iBeforeSame; + n4 = iAfterSame; + n5 = iOpposSame; + } + int n12,n45,n14,n25; + if(i0>0) { //reversed case + n12 = n1 + 4; + n45 = n5 + 4; + n14 = n4 + 4; + n25 = n2 + 4; + } + else { + n12 = n2 + 4; + n45 = n4 + 4; + n14 = n1 + 4; + n25 = n5 + 4; + } + aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2], + prevNod[n4], prevNod[n5], nextNod[n5], + prevNod[n12], midlNod[n2], nextNod[n12], + prevNod[n45], midlNod[n5], nextNod[n45], + prevNod[n14], prevNod[n25], nextNod[n25]); + } + break; + } + default: { + // realized for extrusion only + //vector polyedre_nodes (nbNodes*2 + 4*nbNodes); + //vector quantities (nbNodes + 2); + + //quantities[0] = nbNodes; // bottom of prism + //for (int inode = 0; inode < nbNodes; inode++) { + // polyedre_nodes[inode] = prevNod[inode]; + //} + + //quantities[1] = nbNodes; // top of prism + //for (int inode = 0; inode < nbNodes; inode++) { + // polyedre_nodes[nbNodes + inode] = nextNod[inode]; + //} + + //for (int iface = 0; iface < nbNodes; iface++) { + // quantities[iface + 2] = 4; + // int inextface = (iface == nbNodes - 1) ? 0 : iface + 1; + // polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface]; + // polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface]; + // polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface]; + // polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface]; + //} + //aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities); + break; + } } - break; } - default: { + + if(!aNewElem) { // realized for extrusion only vector polyedre_nodes (nbNodes*2 + 4*nbNodes); vector quantities (nbNodes + 2); @@ -2152,9 +3745,12 @@ static void sweepElement(SMESHDS_Mesh* aMesh, } aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities); } - } - if ( aNewElem ) + + if ( aNewElem ) { newElems.push_back( aNewElem ); + myLastCreatedElems.Append(aNewElem); + srcElements.Append( elem ); + } // set new prev nodes for ( iNode = 0; iNode < nbNodes; iNode++ ) @@ -2164,34 +3760,55 @@ static void sweepElement(SMESHDS_Mesh* aMesh, } //======================================================================= -//function : makeWalls -//purpose : create 1D and 2D elements around swept elements +/*! + * \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 + * \param elemNewNodesMap - nodes generated from each node of each element + * \param elemSet - all swept elements + * \param nbSteps - number of sweeping steps + * \param srcElements - to append elem for each generated element + */ //======================================================================= -static void makeWalls (SMESHDS_Mesh* aMesh, - TNodeOfNodeListMap & mapNewNodes, - TElemOfElemListMap & newElemsMap, - TElemOfVecOfNnlmiMap & elemNewNodesMap, - set& elemSet) +void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, + TElemOfElemListMap & newElemsMap, + TElemOfVecOfNnlmiMap & elemNewNodesMap, + TIDSortedElemSet& elemSet, + const int nbSteps, + SMESH_SequenceOfElemPtr& srcElements) { ASSERT( newElemsMap.size() == elemNewNodesMap.size() ); + SMESHDS_Mesh* aMesh = GetMeshDS(); // Find nodes belonging to only one initial element - sweep them to get edges. TNodeOfNodeListMapItr nList = mapNewNodes.begin(); - for ( ; nList != mapNewNodes.end(); nList++ ) - { + for ( ; nList != mapNewNodes.end(); nList++ ) { const SMDS_MeshNode* node = static_cast( nList->first ); SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(); int nbInitElems = 0; - while ( eIt->more() && nbInitElems < 2 ) - if ( elemSet.find( eIt->next() ) != elemSet.end() ) + const SMDS_MeshElement* el = 0; + SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only + while ( eIt->more() && nbInitElems < 2 ) { + el = eIt->next(); + SMDSAbs_ElementType type = el->GetType(); + if ( type == SMDSAbs_Volume || type < highType ) continue; + if ( type > highType ) { + nbInitElems = 0; + highType = type; + } + if ( elemSet.find(el) != elemSet.end() ) nbInitElems++; + } if ( nbInitElems < 2 ) { - vector newNodesItVec( 1, nList ); - list newEdges; - sweepElement( aMesh, node, newNodesItVec, newEdges ); + bool NotCreateEdge = el && el->IsQuadratic() && el->IsMediumNode(node); + if(!NotCreateEdge) { + vector newNodesItVec( 1, nList ); + list newEdges; + sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements ); + } } } @@ -2200,938 +3817,2970 @@ static void makeWalls (SMESHDS_Mesh* aMesh, TElemOfElemListMap::iterator itElem = newElemsMap.begin(); TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin(); - for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) - { + for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) { const SMDS_MeshElement* elem = itElem->first; vector& vecNewNodes = itElemNodes->second; - if ( elem->GetType() == SMDSAbs_Edge ) - { + if ( elem->GetType() == SMDSAbs_Edge ) { // create a ceiling edge - aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(), - vecNewNodes[ 1 ]->second.back() ); + if (!elem->IsQuadratic()) { + if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(), + vecNewNodes[ 1 ]->second.back())) { + myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(), + vecNewNodes[ 1 ]->second.back())); + srcElements.Append( myLastCreatedElems.Last() ); + } + } + else { + if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(), + vecNewNodes[ 1 ]->second.back(), + vecNewNodes[ 2 ]->second.back())) { + myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(), + vecNewNodes[ 1 ]->second.back(), + vecNewNodes[ 2 ]->second.back())); + srcElements.Append( myLastCreatedElems.Last() ); + } + } } if ( elem->GetType() != SMDSAbs_Face ) continue; + if(itElem->second.size()==0) continue; + bool hasFreeLinks = false; - set avoidSet; + TIDSortedElemSet avoidSet; avoidSet.insert( elem ); - // loop on a face nodes set aFaceLastNodes; int iNode, nbNodes = vecNewNodes.size(); - for ( iNode = 0; iNode < nbNodes; iNode++ ) - { - aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() ); - // look for free links of a face - int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1; - const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first; - const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first; - // check if a link is free - if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) - { - hasFreeLinks = true; - // make an edge and a ceiling for a new edge - if ( !aMesh->FindEdge( n1, n2 )) - aMesh->AddEdge( n1, n2 ); - n1 = vecNewNodes[ iNode ]->second.back(); - n2 = vecNewNodes[ iNext ]->second.back(); - if ( !aMesh->FindEdge( n1, n2 )) - aMesh->AddEdge( n1, n2 ); + if(!elem->IsQuadratic()) { + // loop on the face nodes + for ( iNode = 0; iNode < nbNodes; iNode++ ) { + aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() ); + // look for free links of the face + int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1; + const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first; + const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first; + // check if a link is free + if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) { + hasFreeLinks = true; + // make an edge and a ceiling for a new edge + if ( !aMesh->FindEdge( n1, n2 )) { + myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge + srcElements.Append( myLastCreatedElems.Last() ); + } + n1 = vecNewNodes[ iNode ]->second.back(); + n2 = vecNewNodes[ iNext ]->second.back(); + if ( !aMesh->FindEdge( n1, n2 )) { + myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge + srcElements.Append( myLastCreatedElems.Last() ); + } + } + } + } + else { // elem is quadratic face + int nbn = nbNodes/2; + for ( iNode = 0; iNode < nbn; iNode++ ) { + aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() ); + int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1; + const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first; + const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first; + // check if a link is free + if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) { + hasFreeLinks = true; + // make an edge and a ceiling for a new edge + // find medium node + const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first; + if ( !aMesh->FindEdge( n1, n2, n3 )) { + myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge + srcElements.Append( myLastCreatedElems.Last() ); + } + n1 = vecNewNodes[ iNode ]->second.back(); + n2 = vecNewNodes[ iNext ]->second.back(); + n3 = vecNewNodes[ iNode+nbn ]->second.back(); + if ( !aMesh->FindEdge( n1, n2, n3 )) { + myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge + srcElements.Append( myLastCreatedElems.Last() ); + } + } + } + for ( iNode = nbn; iNode < 2*nbn; iNode++ ) { + aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() ); } } + // sweep free links into faces - if ( hasFreeLinks ) - { + if ( hasFreeLinks ) { list & newVolumes = itElem->second; - int iStep, nbSteps = vecNewNodes[0]->second.size(); int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps; - set initNodeSet, faceNodeSet; - for ( iNode = 0; iNode < nbNodes; iNode++ ) + set initNodeSet, topNodeSet, faceNodeSet; + for ( iNode = 0; iNode < nbNodes; iNode++ ) { initNodeSet.insert( vecNewNodes[ iNode ]->first ); - - for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) - { + topNodeSet .insert( vecNewNodes[ iNode ]->second.back() ); + } + for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) { list::iterator v = newVolumes.begin(); iVol = 0; while ( iVol++ < volNb ) v++; - // find indices of free faces of a volume - list< int > fInd; + // find indices of free faces of a volume and their source edges + list< int > freeInd; + list< const SMDS_MeshElement* > srcEdges; // source edges of free faces SMDS_VolumeTool vTool( *v ); int iF, nbF = vTool.NbFaces(); - for ( iF = 0; iF < nbF; iF ++ ) + for ( iF = 0; iF < nbF; iF ++ ) { if (vTool.IsFreeFace( iF ) && vTool.GetFaceNodes( iF, faceNodeSet ) && initNodeSet != faceNodeSet) // except an initial face - fInd.push_back( iF ); - if ( fInd.empty() ) - continue; - - // create faces for all steps - for ( iStep = 0; iStep < nbSteps; iStep++ ) - { - vTool.Set( *v ); + { + if ( nbSteps == 1 && faceNodeSet == topNodeSet ) + continue; + freeInd.push_back( iF ); + // find source edge of a free face iF + vector commonNodes; // shared by the initial and free faces + commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory + std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(), + initNodeSet.begin(), initNodeSet.end(), + commonNodes.begin()); + if ( (*v)->IsQuadratic() ) + srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2])); + else + srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1])); +#ifdef _DEBUG_ + if ( !srcEdges.back() ) + { + cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #" + << iF << " of volume #" << vTool.ID() << endl; + } +#endif + } + } + if ( freeInd.empty() ) + continue; + + // create faces for all steps; + // if such a face has been already created by sweep of edge, + // assure that its orientation is OK + for ( int iStep = 0; iStep < nbSteps; iStep++ ) { + vTool.Set( *v ); vTool.SetExternalNormal(); - list< int >::iterator ind = fInd.begin(); - for ( ; ind != fInd.end(); ind++ ) + list< int >::iterator ind = freeInd.begin(); + list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin(); + for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces { const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind ); - switch ( vTool.NbFaceNodes( *ind ) ) { - case 3: - aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ); break; - case 4: - aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ); break; + int nbn = vTool.NbFaceNodes( *ind ); + switch ( nbn ) { + case 3: { ///// triangle + const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]); + if ( !f ) + myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] )); + else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) + aMesh->ChangeElementNodes( f, nodes, nbn ); + break; + } + case 4: { ///// quadrangle + const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]); + if ( !f ) + myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] )); + else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) + aMesh->ChangeElementNodes( f, nodes, nbn ); + break; + } default: - { - int nbPolygonNodes = vTool.NbFaceNodes( *ind ); - vector polygon_nodes (nbPolygonNodes); - for (int inode = 0; inode < nbPolygonNodes; inode++) { - polygon_nodes[inode] = nodes[inode]; + if( (*v)->IsQuadratic() ) { + if(nbn==6) { /////// quadratic triangle + const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], + nodes[1], nodes[3], nodes[5] ); + if ( !f ) { + myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], + nodes[1], nodes[3], nodes[5])); + } + else if ( nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) { + const SMDS_MeshNode** tmpnodes = new const SMDS_MeshNode*[6]; + tmpnodes[0] = nodes[0]; + tmpnodes[1] = nodes[2]; + tmpnodes[2] = nodes[4]; + tmpnodes[3] = nodes[1]; + tmpnodes[4] = nodes[3]; + tmpnodes[5] = nodes[5]; + aMesh->ChangeElementNodes( f, tmpnodes, nbn ); + } } - aMesh->AddPolygonalFace(polygon_nodes); - break; + else { /////// quadratic quadrangle + const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7] ); + if ( !f ) { + myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7])); + } + else if ( nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) { + const SMDS_MeshNode** tmpnodes = new const SMDS_MeshNode*[8]; + tmpnodes[0] = nodes[0]; + tmpnodes[1] = nodes[2]; + tmpnodes[2] = nodes[4]; + tmpnodes[3] = nodes[6]; + tmpnodes[4] = nodes[1]; + tmpnodes[5] = nodes[3]; + tmpnodes[6] = nodes[5]; + tmpnodes[7] = nodes[7]; + aMesh->ChangeElementNodes( f, tmpnodes, nbn ); + } + } + } + else { //////// polygon + vector polygon_nodes ( nodes, &nodes[nbn] ); + const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes ); + if ( !f ) + myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes)); + else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) + aMesh->ChangeElementNodes( f, nodes, nbn ); } } - } + while ( srcElements.Length() < myLastCreatedElems.Length() ) + srcElements.Append( *srcEdge ); + + } // loop on free faces + // go to the next volume iVol = 0; while ( iVol++ < nbVolumesByStep ) v++; } } - } // sweep free links into faces + } // sweep free links into faces + + // Make a ceiling face with a normal external to a volume + + SMDS_VolumeTool lastVol( itElem->second.back() ); + + int iF = lastVol.GetFaceIndex( aFaceLastNodes ); + if ( iF >= 0 ) { + lastVol.SetExternalNormal(); + const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF ); + int nbn = lastVol.NbFaceNodes( iF ); + switch ( nbn ) { + case 3: + if (!hasFreeLinks || + !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ])) + myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] )); + break; + case 4: + if (!hasFreeLinks || + !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ])) + myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] )); + break; + default: + if(itElem->second.back()->IsQuadratic()) { + if(nbn==6) { + if (!hasFreeLinks || + !aMesh->FindFace(nodes[0], nodes[2], nodes[4], + nodes[1], nodes[3], nodes[5]) ) { + myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], + nodes[1], nodes[3], nodes[5])); + } + } + else { // nbn==8 + if (!hasFreeLinks || + !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7]) ) + myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7])); + } + } + else { + vector polygon_nodes ( nodes, &nodes[nbn] ); + if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes)) + myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes)); + } + } // switch + + while ( srcElements.Length() < myLastCreatedElems.Length() ) + srcElements.Append( myLastCreatedElems.Last() ); + } + } // loop on swept elements +} + +//======================================================================= +//function : RotationSweep +//purpose : +//======================================================================= + +SMESH_MeshEditor::PGroupIDs +SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems, + const gp_Ax1& theAxis, + const double theAngle, + const int theNbSteps, + const double theTol, + const bool theMakeGroups, + const bool theMakeWalls) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + // source elements for each generated one + SMESH_SequenceOfElemPtr srcElems, srcNodes; + + MESSAGE( "RotationSweep()"); + gp_Trsf aTrsf; + aTrsf.SetRotation( theAxis, theAngle ); + gp_Trsf aTrsf2; + aTrsf2.SetRotation( theAxis, theAngle/2. ); + + gp_Lin aLine( theAxis ); + double aSqTol = theTol * theTol; + + SMESHDS_Mesh* aMesh = GetMeshDS(); + + TNodeOfNodeListMap mapNewNodes; + TElemOfVecOfNnlmiMap mapElemNewNodes; + TElemOfElemListMap newElemsMap; + + // loop on theElems + TIDSortedElemSet::iterator itElem; + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + const SMDS_MeshElement* elem = *itElem; + if ( !elem || elem->GetType() == SMDSAbs_Volume ) + continue; + vector & newNodesItVec = mapElemNewNodes[ elem ]; + newNodesItVec.reserve( elem->NbNodes() ); + + // loop on elem nodes + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + // check if a node has been already sweeped + const SMDS_MeshNode* node = cast2Node( itN->next() ); + + gp_XYZ aXYZ( node->X(), node->Y(), node->Z() ); + double coord[3]; + aXYZ.Coord( coord[0], coord[1], coord[2] ); + bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol ); + + TNodeOfNodeListMapItr nIt = mapNewNodes.find( node ); + if ( nIt == mapNewNodes.end() ) { + nIt = mapNewNodes.insert( make_pair( node, list() )).first; + list& listNewNodes = nIt->second; + + // make new nodes + //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() ); + //double coord[3]; + //aXYZ.Coord( coord[0], coord[1], coord[2] ); + //bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol ); + const SMDS_MeshNode * newNode = node; + for ( int i = 0; i < theNbSteps; i++ ) { + if ( !isOnAxis ) { + if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { + // create two nodes + aTrsf2.Transforms( coord[0], coord[1], coord[2] ); + //aTrsf.Transforms( coord[0], coord[1], coord[2] ); + newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + aTrsf2.Transforms( coord[0], coord[1], coord[2] ); + //aTrsf.Transforms( coord[0], coord[1], coord[2] ); + } + else { + aTrsf.Transforms( coord[0], coord[1], coord[2] ); + } + newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + } + else { + listNewNodes.push_back( newNode ); + if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { + listNewNodes.push_back( newNode ); + } + } + } + } + /* + else { + // if current elem is quadratic and current node is not medium + // we have to check - may be it is needed to insert additional nodes + if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { + list< const SMDS_MeshNode* > & listNewNodes = nIt->second; + if(listNewNodes.size()==theNbSteps) { + listNewNodes.clear(); + // make new nodes + //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() ); + //double coord[3]; + //aXYZ.Coord( coord[0], coord[1], coord[2] ); + const SMDS_MeshNode * newNode = node; + if ( !isOnAxis ) { + for(int i = 0; iAddNode( coord[0], coord[1], coord[2] ); + cout<<" 3 AddNode: "<AddNode( coord[0], coord[1], coord[2] ); + cout<<" 4 AddNode: "<GetMeshDS(); + + // try to search in sequence of existing nodes + // if aNodes.Length()>0 we 'nave to use given sequence + // else - use all nodes of mesh + if(aNodes.Length()>0) { + int i; + for(i=1; i<=aNodes.Length(); i++) { + gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z()); + if(P1.Distance(P2)nodesIterator(); + while(itn->more()) { + const SMDS_MeshNode* aN = static_cast (itn->next()); + gp_Pnt P2(aN->X(),aN->Y(),aN->Z()); + if(P1.Distance(P2)AddNode(x,y,z); + myLastCreatedNodes.Append(NewNode); + return NewNode; +} + + +//======================================================================= +//function : ExtrusionSweep +//purpose : +//======================================================================= + +SMESH_MeshEditor::PGroupIDs +SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, + const gp_Vec& theStep, + const int theNbSteps, + TElemOfElemListMap& newElemsMap, + const bool theMakeGroups, + const int theFlags, + const double theTolerance) +{ + ExtrusParam aParams; + aParams.myDir = gp_Dir(theStep); + aParams.myNodes.Clear(); + aParams.mySteps = new TColStd_HSequenceOfReal; + int i; + for(i=1; i<=theNbSteps; i++) + aParams.mySteps->Append(theStep.Magnitude()); + + return + ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance); +} + + +//======================================================================= +//function : ExtrusionSweep +//purpose : +//======================================================================= + +SMESH_MeshEditor::PGroupIDs +SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, + ExtrusParam& theParams, + TElemOfElemListMap& newElemsMap, + const bool theMakeGroups, + const int theFlags, + const double theTolerance) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + // source elements for each generated one + SMESH_SequenceOfElemPtr srcElems, srcNodes; + + SMESHDS_Mesh* aMesh = GetMeshDS(); + + int nbsteps = theParams.mySteps->Length(); + + TNodeOfNodeListMap mapNewNodes; + //TNodeOfNodeVecMap mapNewNodes; + TElemOfVecOfNnlmiMap mapElemNewNodes; + //TElemOfVecOfMapNodesMap mapElemNewNodes; + + // loop on theElems + TIDSortedElemSet::iterator itElem; + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + // check element type + const SMDS_MeshElement* elem = *itElem; + if ( !elem || elem->GetType() == SMDSAbs_Volume ) + continue; + + vector & newNodesItVec = mapElemNewNodes[ elem ]; + //vector & newNodesItVec = mapElemNewNodes[ elem ]; + newNodesItVec.reserve( elem->NbNodes() ); + + // loop on elem nodes + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) + { + // check if a node has been already sweeped + const SMDS_MeshNode* node = cast2Node( itN->next() ); + TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node ); + //TNodeOfNodeVecMap::iterator nIt = mapNewNodes.find( node ); + if ( nIt == mapNewNodes.end() ) { + nIt = mapNewNodes.insert( make_pair( node, list() )).first; + //nIt = mapNewNodes.insert( make_pair( node, vector() )).first; + list& listNewNodes = nIt->second; + //vector& vecNewNodes = nIt->second; + //vecNewNodes.reserve(nbsteps); + + // make new nodes + double coord[] = { node->X(), node->Y(), node->Z() }; + //int nbsteps = theParams.mySteps->Length(); + for ( int i = 0; i < nbsteps; i++ ) { + if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { + // create additional node + double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.; + double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.; + double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.; + if( theFlags & EXTRUSION_FLAG_SEW ) { + const SMDS_MeshNode * newNode = CreateNode(x, y, z, + theTolerance, theParams.myNodes); + listNewNodes.push_back( newNode ); + } + else { + const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + } + } + //aTrsf.Transforms( coord[0], coord[1], coord[2] ); + coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1); + coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1); + coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1); + if( theFlags & EXTRUSION_FLAG_SEW ) { + const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2], + theTolerance, theParams.myNodes); + listNewNodes.push_back( newNode ); + //vecNewNodes[i]=newNode; + } + else { + const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + //vecNewNodes[i]=newNode; + } + } + } + else { + // if current elem is quadratic and current node is not medium + // we have to check - may be it is needed to insert additional nodes + if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { + list< const SMDS_MeshNode* > & listNewNodes = nIt->second; + if(listNewNodes.size()==nbsteps) { + listNewNodes.clear(); + double coord[] = { node->X(), node->Y(), node->Z() }; + for ( int i = 0; i < nbsteps; i++ ) { + double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1); + double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1); + double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1); + if( theFlags & EXTRUSION_FLAG_SEW ) { + const SMDS_MeshNode * newNode = CreateNode(x, y, z, + theTolerance, theParams.myNodes); + listNewNodes.push_back( newNode ); + } + else { + const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + } + coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1); + coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1); + coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1); + if( theFlags & EXTRUSION_FLAG_SEW ) { + const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2], + theTolerance, theParams.myNodes); + listNewNodes.push_back( newNode ); + } + else { + const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + } + } + } + } + } + newNodesItVec.push_back( nIt ); + } + // make new elements + sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems ); + } + + if( theFlags & EXTRUSION_FLAG_BOUNDARY ) { + makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems ); + } + PGroupIDs newGroupIDs; + if ( theMakeGroups ) + newGroupIDs = generateGroups( srcNodes, srcElems, "extruded"); + + return newGroupIDs; +} + +/* +//======================================================================= +//class : SMESH_MeshEditor_PathPoint +//purpose : auxiliary class +//======================================================================= +class SMESH_MeshEditor_PathPoint { +public: +SMESH_MeshEditor_PathPoint() { +myPnt.SetCoord(99., 99., 99.); +myTgt.SetCoord(1.,0.,0.); +myAngle=0.; +myPrm=0.; +} +void SetPnt(const gp_Pnt& aP3D){ +myPnt=aP3D; +} +void SetTangent(const gp_Dir& aTgt){ +myTgt=aTgt; +} +void SetAngle(const double& aBeta){ +myAngle=aBeta; +} +void SetParameter(const double& aPrm){ +myPrm=aPrm; +} +const gp_Pnt& Pnt()const{ +return myPnt; +} +const gp_Dir& Tangent()const{ +return myTgt; +} +double Angle()const{ +return myAngle; +} +double Parameter()const{ +return myPrm; +} + +protected: +gp_Pnt myPnt; +gp_Dir myTgt; +double myAngle; +double myPrm; +}; +*/ + +//======================================================================= +//function : ExtrusionAlongTrack +//purpose : +//======================================================================= +SMESH_MeshEditor::Extrusion_Error +SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements, + SMESH_subMesh* theTrack, + const SMDS_MeshNode* theN1, + const bool theHasAngles, + list& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + int aNbE; + std::list aPrms; + TIDSortedElemSet::iterator itElem; + + gp_XYZ aGC; + TopoDS_Edge aTrackEdge; + TopoDS_Vertex aV1, aV2; + + SMDS_ElemIteratorPtr aItE; + SMDS_NodeIteratorPtr aItN; + SMDSAbs_ElementType aTypeE; + + TNodeOfNodeListMap mapNewNodes; + + // 1. Check data + aNbE = theElements.size(); + // nothing to do + if ( !aNbE ) + return EXTR_NO_ELEMENTS; + + // 1.1 Track Pattern + ASSERT( theTrack ); + + SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS(); + + aItE = pSubMeshDS->GetElements(); + while ( aItE->more() ) { + const SMDS_MeshElement* pE = aItE->next(); + aTypeE = pE->GetType(); + // Pattern must contain links only + if ( aTypeE != SMDSAbs_Edge ) + return EXTR_PATH_NOT_EDGE; + } + + list fullList; + + const TopoDS_Shape& aS = theTrack->GetSubShape(); + // Sub shape for the Pattern must be an Edge or Wire + if( aS.ShapeType() == TopAbs_EDGE ) { + aTrackEdge = TopoDS::Edge( aS ); + // the Edge must not be degenerated + if ( BRep_Tool::Degenerated( aTrackEdge ) ) + return EXTR_BAD_PATH_SHAPE; + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1 == theN1 || aN2 == theN1 ) ) + return EXTR_BAD_STARTING_NODE; + aItN = pSubMeshDS->GetNodes(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition().get() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList); + } + else if( aS.ShapeType() == TopAbs_WIRE ) { + list< SMESH_subMesh* > LSM; + TopTools_SequenceOfShape Edges; + SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true); + while(itSM->more()) { + SMESH_subMesh* SM = itSM->next(); + LSM.push_back(SM); + const TopoDS_Shape& aS = SM->GetSubShape(); + Edges.Append(aS); + } + list< list > LLPPs; + int startNid = theN1->GetID(); + TColStd_MapOfInteger UsedNums; + int NbEdges = Edges.Length(); + int i = 1; + for(; i<=NbEdges; i++) { + int k = 0; + list< SMESH_subMesh* >::iterator itLSM = LSM.begin(); + for(; itLSM!=LSM.end(); itLSM++) { + k++; + if(UsedNums.Contains(k)) continue; + aTrackEdge = TopoDS::Edge( Edges.Value(k) ); + SMESH_subMesh* locTrack = *itLSM; + SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS(); + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue; + // 2. Collect parameters on the track edge + aPrms.clear(); + aItN = locMeshDS->GetNodes(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition().get() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + list LPP; + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP); + LLPPs.push_back(LPP); + UsedNums.Add(k); + // update startN for search following egde + if( aN1->GetID() == startNid ) startNid = aN2->GetID(); + else startNid = aN1->GetID(); + break; + } + } + list< list >::iterator itLLPP = LLPPs.begin(); + list firstList = *itLLPP; + list::iterator itPP = firstList.begin(); + for(; itPP!=firstList.end(); itPP++) { + fullList.push_back( *itPP ); + } + SMESH_MeshEditor_PathPoint PP1 = fullList.back(); + fullList.pop_back(); + itLLPP++; + for(; itLLPP!=LLPPs.end(); itLLPP++) { + list currList = *itLLPP; + itPP = currList.begin(); + SMESH_MeshEditor_PathPoint PP2 = currList.front(); + gp_Dir D1 = PP1.Tangent(); + gp_Dir D2 = PP2.Tangent(); + gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2, + (D1.Z()+D2.Z())/2 ) ); + PP1.SetTangent(Dnew); + fullList.push_back(PP1); + itPP++; + for(; itPP!=firstList.end(); itPP++) { + fullList.push_back( *itPP ); + } + PP1 = fullList.back(); + fullList.pop_back(); + } + // if wire not closed + fullList.push_back(PP1); + // else ??? + } + else { + return EXTR_BAD_PATH_SHAPE; + } + + return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation, + theHasRefPoint, theRefPoint, theMakeGroups); +} + + +//======================================================================= +//function : ExtrusionAlongTrack +//purpose : +//======================================================================= +SMESH_MeshEditor::Extrusion_Error +SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements, + SMESH_Mesh* theTrack, + const SMDS_MeshNode* theN1, + const bool theHasAngles, + list& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + int aNbE; + std::list aPrms; + TIDSortedElemSet::iterator itElem; + + gp_XYZ aGC; + TopoDS_Edge aTrackEdge; + TopoDS_Vertex aV1, aV2; + + SMDS_ElemIteratorPtr aItE; + SMDS_NodeIteratorPtr aItN; + SMDSAbs_ElementType aTypeE; + + TNodeOfNodeListMap mapNewNodes; + + // 1. Check data + aNbE = theElements.size(); + // nothing to do + if ( !aNbE ) + return EXTR_NO_ELEMENTS; + + // 1.1 Track Pattern + ASSERT( theTrack ); + + SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS(); + + aItE = pMeshDS->elementsIterator(); + while ( aItE->more() ) { + const SMDS_MeshElement* pE = aItE->next(); + aTypeE = pE->GetType(); + // Pattern must contain links only + if ( aTypeE != SMDSAbs_Edge ) + return EXTR_PATH_NOT_EDGE; + } + + list fullList; + + const TopoDS_Shape& aS = theTrack->GetShapeToMesh(); + // Sub shape for the Pattern must be an Edge or Wire + if( aS.ShapeType() == TopAbs_EDGE ) { + aTrackEdge = TopoDS::Edge( aS ); + // the Edge must not be degenerated + if ( BRep_Tool::Degenerated( aTrackEdge ) ) + return EXTR_BAD_PATH_SHAPE; + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1 == theN1 || aN2 == theN1 ) ) + return EXTR_BAD_STARTING_NODE; + aItN = pMeshDS->nodesIterator(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + if( pNode==aN1 || pNode==aN2 ) continue; + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition().get() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList); + } + else if( aS.ShapeType() == TopAbs_WIRE ) { + list< SMESH_subMesh* > LSM; + TopTools_SequenceOfShape Edges; + TopExp_Explorer eExp(aS, TopAbs_EDGE); + for(; eExp.More(); eExp.Next()) { + TopoDS_Edge E = TopoDS::Edge( eExp.Current() ); + if( BRep_Tool::Degenerated(E) ) continue; + SMESH_subMesh* SM = theTrack->GetSubMesh(E); + if(SM) { + LSM.push_back(SM); + Edges.Append(E); + } + } + list< list > LLPPs; + int startNid = theN1->GetID(); + TColStd_MapOfInteger UsedNums; + int NbEdges = Edges.Length(); + int i = 1; + for(; i<=NbEdges; i++) { + int k = 0; + list< SMESH_subMesh* >::iterator itLSM = LSM.begin(); + for(; itLSM!=LSM.end(); itLSM++) { + k++; + if(UsedNums.Contains(k)) continue; + aTrackEdge = TopoDS::Edge( Edges.Value(k) ); + SMESH_subMesh* locTrack = *itLSM; + SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS(); + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue; + // 2. Collect parameters on the track edge + aPrms.clear(); + aItN = locMeshDS->GetNodes(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition().get() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + list LPP; + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP); + LLPPs.push_back(LPP); + UsedNums.Add(k); + // update startN for search following egde + if( aN1->GetID() == startNid ) startNid = aN2->GetID(); + else startNid = aN1->GetID(); + break; + } + } + list< list >::iterator itLLPP = LLPPs.begin(); + list firstList = *itLLPP; + list::iterator itPP = firstList.begin(); + for(; itPP!=firstList.end(); itPP++) { + fullList.push_back( *itPP ); + } + SMESH_MeshEditor_PathPoint PP1 = fullList.back(); + fullList.pop_back(); + itLLPP++; + for(; itLLPP!=LLPPs.end(); itLLPP++) { + list currList = *itLLPP; + itPP = currList.begin(); + SMESH_MeshEditor_PathPoint PP2 = currList.front(); + gp_Pnt P1 = PP1.Pnt(); + //cout<<" PP1: Pnt("<& aPrms, + const TopoDS_Edge& aTrackEdge, + bool FirstIsStart, + list& LPP) +{ + Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2; + aTolVec=1.e-7; + aTolVec2=aTolVec*aTolVec; + double aT1, aT2; + TopoDS_Vertex aV1, aV2; + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aT1=BRep_Tool::Parameter( aV1, aTrackEdge ); + aT2=BRep_Tool::Parameter( aV2, aTrackEdge ); + // 2. Collect parameters on the track edge + aPrms.push_front( aT1 ); + aPrms.push_back( aT2 ); + // sort parameters + aPrms.sort(); + if( FirstIsStart ) { + if ( aT1 > aT2 ) { + aPrms.reverse(); + } + } + else { + if ( aT2 > aT1 ) { + aPrms.reverse(); + } + } + // 3. Path Points + SMESH_MeshEditor_PathPoint aPP; + Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 ); + std::list::iterator aItD = aPrms.begin(); + for(; aItD != aPrms.end(); ++aItD) { + double aT = *aItD; + gp_Pnt aP3D; + gp_Vec aVec; + aC3D->D1( aT, aP3D, aVec ); + aL2 = aVec.SquareMagnitude(); + if ( aL2 < aTolVec2 ) + return EXTR_CANT_GET_TANGENT; + gp_Dir aTgt( aVec ); + aPP.SetPnt( aP3D ); + aPP.SetTangent( aTgt ); + aPP.SetParameter( aT ); + LPP.push_back(aPP); + } + return EXTR_OK; +} + + +//======================================================================= +//function : MakeExtrElements +//purpose : auxilary for ExtrusionAlongTrack +//======================================================================= +SMESH_MeshEditor::Extrusion_Error +SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements, + list& fullList, + const bool theHasAngles, + list& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups) +{ + //cout<<"MakeExtrElements fullList.size() = "< aPPs(aNbTP); + // Angles + if( theHasAngles && theAngles.size()>0 && theLinearVariation ) { + LinearAngleVariation(aNbTP-1, theAngles); + } + vector aAngles( aNbTP ); + int j = 0; + for(; j::iterator aItD = theAngles.begin(); + for ( j=1; (aItD != theAngles.end()) && (j::iterator itPP = fullList.begin(); + for(; itPP!=fullList.end(); itPP++) { + j++; + SMESH_MeshEditor_PathPoint PP = *itPP; + PP.SetAngle(aAngles[j]); + aPPs[j] = PP; + } + + TNodeOfNodeListMap mapNewNodes; + TElemOfVecOfNnlmiMap mapElemNewNodes; + TElemOfElemListMap newElemsMap; + TIDSortedElemSet::iterator itElem; + double aX, aY, aZ; + int aNb; + SMDSAbs_ElementType aTypeE; + // source elements for each generated one + SMESH_SequenceOfElemPtr srcElems, srcNodes; + + // 3. Center of rotation aV0 + gp_Pnt aV0 = theRefPoint; + gp_XYZ aGC; + if ( !theHasRefPoint ) { + aNb = 0; + aGC.SetCoord( 0.,0.,0. ); + + itElem = theElements.begin(); + for ( ; itElem != theElements.end(); itElem++ ) { + const SMDS_MeshElement* elem = *itElem; + + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + const SMDS_MeshNode* node = static_cast( itN->next() ); + aX = node->X(); + aY = node->Y(); + aZ = node->Z(); + + if ( mapNewNodes.find( node ) == mapNewNodes.end() ) { + list aLNx; + mapNewNodes[node] = aLNx; + // + gp_XYZ aXYZ( aX, aY, aZ ); + aGC += aXYZ; + ++aNb; + } + } + } + aGC /= aNb; + aV0.SetXYZ( aGC ); + } // if (!theHasRefPoint) { + mapNewNodes.clear(); + + // 4. Processing the elements + SMESHDS_Mesh* aMesh = GetMeshDS(); + + for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) { + // check element type + const SMDS_MeshElement* elem = *itElem; + aTypeE = elem->GetType(); + if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) ) + continue; + + vector & newNodesItVec = mapElemNewNodes[ elem ]; + newNodesItVec.reserve( elem->NbNodes() ); + + // loop on elem nodes + int nodeIndex = -1; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) + { + ++nodeIndex; + // check if a node has been already processed + const SMDS_MeshNode* node = + static_cast( itN->next() ); + TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node ); + if ( nIt == mapNewNodes.end() ) { + nIt = mapNewNodes.insert( make_pair( node, list() )).first; + list& listNewNodes = nIt->second; + + // make new nodes + aX = node->X(); aY = node->Y(); aZ = node->Z(); + + Standard_Real aAngle1x, aAngleT1T0, aTolAng; + gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x; + gp_Ax1 anAx1, anAxT1T0; + gp_Dir aDT1x, aDT0x, aDT1T0; + + aTolAng=1.e-4; + + aV0x = aV0; + aPN0.SetCoord(aX, aY, aZ); + + const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0]; + aP0x = aPP0.Pnt(); + aDT0x= aPP0.Tangent(); + //cout<<"j = 0 PP: Pnt("< aTolAng) { + aDT1T0=aDT1x^aDT0x; + anAxT1T0.SetLocation( aV1x ); + anAxT1T0.SetDirection( aDT1T0 ); + aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 ); + + aPN1 = aPN1.Transformed( aTrsfRotT1T0 ); + } + + // rotation 2 + if ( theHasAngles ) { + anAx1.SetLocation( aV1x ); + anAx1.SetDirection( aDT1x ); + aTrsfRot.SetRotation( anAx1, aAngle1x ); + + aPN1 = aPN1.Transformed( aTrsfRot ); + } + + // make new node + if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { + // create additional node + double x = ( aPN1.X() + aPN0.X() )/2.; + double y = ( aPN1.Y() + aPN0.Y() )/2.; + double z = ( aPN1.Z() + aPN0.Z() )/2.; + const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + } + aX = aPN1.X(); + aY = aPN1.Y(); + aZ = aPN1.Z(); + const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ ); + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + listNewNodes.push_back( newNode ); + + aPN0 = aPN1; + aP0x = aP1x; + aV0x = aV1x; + aDT0x = aDT1x; + } + } + + else { + // if current elem is quadratic and current node is not medium + // we have to check - may be it is needed to insert additional nodes + if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { + list< const SMDS_MeshNode* > & listNewNodes = nIt->second; + if(listNewNodes.size()==aNbTP-1) { + vector aNodes(2*(aNbTP-1)); + gp_XYZ P(node->X(), node->Y(), node->Z()); + list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin(); + int i; + for(i=0; iX() + P.X() )/2.; + double y = ( N->Y() + P.Y() )/2.; + double z = ( N->Z() + P.Z() )/2.; + const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z); + srcNodes.Append( node ); + myLastCreatedNodes.Append(newN); + aNodes[2*i] = newN; + aNodes[2*i+1] = N; + P = gp_XYZ(N->X(),N->Y(),N->Z()); + } + listNewNodes.clear(); + for(i=0; i<2*(aNbTP-1); i++) { + listNewNodes.push_back(aNodes[i]); + } + } + } + } + + newNodesItVec.push_back( nIt ); + } + // make new elements + //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem], + // newNodesItVec[0]->second.size(), myLastCreatedElems ); + sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems ); + } + + makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems ); + + if ( theMakeGroups ) + generateGroups( srcNodes, srcElems, "extruded"); + + return EXTR_OK; +} + + +//======================================================================= +//function : LinearAngleVariation +//purpose : auxilary for ExtrusionAlongTrack +//======================================================================= +void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps, + list& Angles) +{ + int nbAngles = Angles.size(); + if( nbSteps > nbAngles ) { + vector theAngles(nbAngles); + list::iterator it = Angles.begin(); + int i = -1; + for(; it!=Angles.end(); it++) { + i++; + theAngles[i] = (*it); + } + list res; + double rAn2St = double( nbAngles ) / double( nbSteps ); + double angPrev = 0, angle; + for ( int iSt = 0; iSt < nbSteps; ++iSt ) { + double angCur = rAn2St * ( iSt+1 ); + double angCurFloor = floor( angCur ); + double angPrevFloor = floor( angPrev ); + if ( angPrevFloor == angCurFloor ) + angle = rAn2St * theAngles[ int( angCurFloor ) ]; + else { + int iP = int( angPrevFloor ); + double angPrevCeil = ceil(angPrev); + angle = ( angPrevCeil - angPrev ) * theAngles[ iP ]; + + int iC = int( angCurFloor ); + if ( iC < nbAngles ) + angle += ( angCur - angCurFloor ) * theAngles[ iC ]; + + iP = int( angPrevCeil ); + while ( iC-- > iP ) + angle += theAngles[ iC ]; + } + res.push_back(angle); + angPrev = angCur; + } + Angles.clear(); + it = res.begin(); + for(; it!=res.end(); it++) + Angles.push_back( *it ); + } +} + + +//================================================================================ +/*! + * \brief Move or copy theElements applying theTrsf to their nodes + * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes + * \param theTrsf - transformation to apply + * \param theCopy - if true, create translated copies of theElems + * \param theMakeGroups - if true and theCopy, create translated groups + * \param theTargetMesh - mesh to copy translated elements into + * \retval SMESH_MeshEditor::PGroupIDs - list of ids of created groups + */ +//================================================================================ + +SMESH_MeshEditor::PGroupIDs +SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, + const gp_Trsf& theTrsf, + const bool theCopy, + const bool theMakeGroups, + SMESH_Mesh* theTargetMesh) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + bool needReverse = false; + string groupPostfix; + switch ( theTrsf.Form() ) { + case gp_PntMirror: + case gp_Ax1Mirror: + case gp_Ax2Mirror: + needReverse = true; + groupPostfix = "mirrored"; + break; + case gp_Rotation: + groupPostfix = "rotated"; + break; + case gp_Translation: + groupPostfix = "translated"; + break; + case gp_Scale: + case gp_CompoundTrsf: // different scale by axis + groupPostfix = "scaled"; + break; + default: + needReverse = false; + groupPostfix = "transformed"; + } + + SMESH_MeshEditor targetMeshEditor( theTargetMesh ); + SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0; + SMESHDS_Mesh* aMesh = GetMeshDS(); + + + // map old node to new one + TNodeNodeMap nodeMap; + + // elements sharing moved nodes; those of them which have all + // nodes mirrored but are not in theElems are to be reversed + TIDSortedElemSet inverseElemSet; + + // source elements for each generated one + SMESH_SequenceOfElemPtr srcElems, srcNodes; + + // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh + list orphanCopy; // copies of orphan nodes + vector orphanNode; // original orphan nodes + + if ( theElems.empty() ) // transform the whole mesh + { + // add all elements + SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator(); + while ( eIt->more() ) theElems.insert( eIt->next() ); + // add orphan nodes + SMDS_MeshElementIDFactory idFactory; + SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator(); + while ( nIt->more() ) + { + const SMDS_MeshNode* node = nIt->next(); + if ( node->NbInverseElements() == 0 && !theElems.insert( node ).second ) + { + // node was not inserted into theElems because an element with the same ID + // is already there. As a work around we insert a copy of node with + // an ID = - + orphanCopy.push_back( *node ); // copy node + SMDS_MeshNode* nodeCopy = &orphanCopy.back(); + int uniqueID = -orphanNode.size(); + orphanNode.push_back( node ); + idFactory.BindID( uniqueID, nodeCopy ); + theElems.insert( nodeCopy ); + } + } + } + // loop on theElems to transorm nodes + TIDSortedElemSet::iterator itElem; + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + const SMDS_MeshElement* elem = *itElem; + if ( !elem ) + continue; + + // loop on elem nodes + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + + const SMDS_MeshNode* node = cast2Node( itN->next() ); + if ( node->GetID() < 0 ) + node = orphanNode[ -node->GetID() ]; + // check if a node has been already transformed + pair n2n_isnew = + nodeMap.insert( make_pair ( node, node )); + if ( !n2n_isnew.second ) + continue; + + double coord[3]; + coord[0] = node->X(); + coord[1] = node->Y(); + coord[2] = node->Z(); + theTrsf.Transforms( coord[0], coord[1], coord[2] ); + if ( theTargetMesh ) { + const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] ); + n2n_isnew.first->second = newNode; + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + } + else if ( theCopy ) { + const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); + n2n_isnew.first->second = newNode; + myLastCreatedNodes.Append(newNode); + srcNodes.Append( node ); + } + else { + aMesh->MoveNode( node, coord[0], coord[1], coord[2] ); + // node position on shape becomes invalid + const_cast< SMDS_MeshNode* > ( node )->SetPosition + ( SMDS_SpacePosition::originSpacePosition() ); + } + + // keep inverse elements + if ( !theCopy && !theTargetMesh && needReverse ) { + SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator(); + while ( invElemIt->more() ) { + const SMDS_MeshElement* iel = invElemIt->next(); + inverseElemSet.insert( iel ); + } + } + } + } + + // either create new elements or reverse mirrored ones + if ( !theCopy && !needReverse && !theTargetMesh ) + return PGroupIDs(); + + TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin(); + for ( ; invElemIt != inverseElemSet.end(); invElemIt++ ) + theElems.insert( *invElemIt ); + + // replicate or reverse elements + + enum { + REV_TETRA = 0, // = nbNodes - 4 + REV_PYRAMID = 1, // = nbNodes - 4 + REV_PENTA = 2, // = nbNodes - 4 + REV_FACE = 3, + REV_HEXA = 4, // = nbNodes - 4 + FORWARD = 5 + }; + int index[][8] = { + { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA + { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID + { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA + { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE + { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA + { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD + }; + + for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) + { + const SMDS_MeshElement* elem = *itElem; + if ( !elem || elem->GetType() == SMDSAbs_Node ) + continue; + + int nbNodes = elem->NbNodes(); + int elemType = elem->GetType(); + + if (elem->IsPoly()) { + // Polygon or Polyhedral Volume + switch ( elemType ) { + case SMDSAbs_Face: + { + vector poly_nodes (nbNodes); + int iNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while (itN->more()) { + const SMDS_MeshNode* node = + static_cast(itN->next()); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); + if (nodeMapIt == nodeMap.end()) + break; // not all nodes transformed + if (needReverse) { + // reverse mirrored faces and volumes + poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second; + } else { + poly_nodes[iNode] = (*nodeMapIt).second; + } + iNode++; + } + if ( iNode != nbNodes ) + continue; // not all nodes transformed + + if ( theTargetMesh ) { + myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes)); + srcElems.Append( elem ); + } + else if ( theCopy ) { + myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes)); + srcElems.Append( elem ); + } + else { + aMesh->ChangePolygonNodes(elem, poly_nodes); + } + } + break; + case SMDSAbs_Volume: + { + // ATTENTION: Reversing is not yet done!!! + const SMDS_PolyhedralVolumeOfNodes* aPolyedre = + dynamic_cast( elem ); + if (!aPolyedre) { + MESSAGE("Warning: bad volumic element"); + continue; + } + + vector poly_nodes; + vector quantities; + + bool allTransformed = true; + int nbFaces = aPolyedre->NbFaces(); + for (int iface = 1; iface <= nbFaces && allTransformed; iface++) { + int nbFaceNodes = aPolyedre->NbFaceNodes(iface); + for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) { + const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); + if (nodeMapIt == nodeMap.end()) { + allTransformed = false; // not all nodes transformed + } else { + poly_nodes.push_back((*nodeMapIt).second); + } + } + quantities.push_back(nbFaceNodes); + } + if ( !allTransformed ) + continue; // not all nodes transformed + + if ( theTargetMesh ) { + myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities)); + srcElems.Append( elem ); + } + else if ( theCopy ) { + myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities)); + srcElems.Append( elem ); + } + else { + aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); + } + } + break; + default:; + } + continue; + } + + // Regular elements + int* i = index[ FORWARD ]; + if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes + if ( elemType == SMDSAbs_Face ) + i = index[ REV_FACE ]; + else + i = index[ nbNodes - 4 ]; + + if(elem->IsQuadratic()) { + static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}; + i = anIds; + if(needReverse) { + if(nbNodes==3) { // quadratic edge + static int anIds[] = {1,0,2}; + i = anIds; + } + else if(nbNodes==6) { // quadratic triangle + static int anIds[] = {0,2,1,5,4,3}; + i = anIds; + } + else if(nbNodes==8) { // quadratic quadrangle + static int anIds[] = {0,3,2,1,7,6,5,4}; + i = anIds; + } + else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes + static int anIds[] = {0,2,1,3,6,5,4,7,9,8}; + i = anIds; + } + else if(nbNodes==13) { // quadratic pyramid of 13 nodes + static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10}; + i = anIds; + } + else if(nbNodes==15) { // quadratic pentahedron with 15 nodes + static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13}; + i = anIds; + } + else { // nbNodes==20 - quadratic hexahedron with 20 nodes + static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17}; + i = anIds; + } + } + } - // make a ceiling face with a normal external to a volume + // find transformed nodes + vector nodes(nbNodes); + int iNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + const SMDS_MeshNode* node = + static_cast( itN->next() ); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node ); + if ( nodeMapIt == nodeMap.end() ) + break; // not all nodes transformed + nodes[ i [ iNode++ ]] = (*nodeMapIt).second; + } + if ( iNode != nbNodes ) + continue; // not all nodes transformed - SMDS_VolumeTool lastVol( itElem->second.back() ); - int iF = lastVol.GetFaceIndex( aFaceLastNodes ); - if ( iF >= 0 ) - { - lastVol.SetExternalNormal(); - const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF ); - switch ( lastVol.NbFaceNodes( iF ) ) { - case 3: - if (!hasFreeLinks || - !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ])) - aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ); - break; - case 4: - if (!hasFreeLinks || - !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ])) - aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ); - break; - default: - { - int nbPolygonNodes = lastVol.NbFaceNodes( iF ); - vector polygon_nodes (nbPolygonNodes); - for (int inode = 0; inode < nbPolygonNodes; inode++) { - polygon_nodes[inode] = nodes[inode]; - } - if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes)) - aMesh->AddPolygonalFace(polygon_nodes); - } - break; + if ( theTargetMesh ) { + if ( SMDS_MeshElement* copy = + targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) { + myLastCreatedElems.Append( copy ); + srcElems.Append( elem ); } } + else if ( theCopy ) { + if ( AddElement( nodes, elem->GetType(), elem->IsPoly() )) + srcElems.Append( elem ); + } + else { + // reverse element as it was reversed by transformation + if ( nbNodes > 2 ) + aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes ); + } + } - } // loop on swept elements + PGroupIDs newGroupIDs; + + if ( theMakeGroups && theCopy || + theMakeGroups && theTargetMesh ) + newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh ); + + return newGroupIDs; } //======================================================================= -//function : RotationSweep -//purpose : +/*! + * \brief Create groups of elements made during transformation + * \param nodeGens - nodes making corresponding myLastCreatedNodes + * \param elemGens - elements making corresponding myLastCreatedElems + * \param postfix - to append to names of new groups + */ //======================================================================= -void SMESH_MeshEditor::RotationSweep(set & theElems, - const gp_Ax1& theAxis, - const double theAngle, - const int theNbSteps, - const double theTol) +SMESH_MeshEditor::PGroupIDs +SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens, + const SMESH_SequenceOfElemPtr& elemGens, + const std::string& postfix, + SMESH_Mesh* targetMesh) { - MESSAGE( "RotationSweep()"); - gp_Trsf aTrsf; - aTrsf.SetRotation( theAxis, theAngle ); + PGroupIDs newGroupIDs( new list ); + SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh(); - gp_Lin aLine( theAxis ); - double aSqTol = theTol * theTol; + // Sort existing groups by types and collect their names - SMESHDS_Mesh* aMesh = GetMeshDS(); + // to store an old group and a generated new one + typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup; + vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes ); + // group names + set< string > groupNames; + // + SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0; + SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups(); + while ( groupIt->more() ) { + SMESH_Group * group = groupIt->next(); + if ( !group ) continue; + SMESHDS_GroupBase* groupDS = group->GetGroupDS(); + if ( !groupDS || groupDS->IsEmpty() ) continue; + groupNames.insert( group->GetName() ); + groupDS->SetStoreName( group->GetName() ); + groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup )); + } - TNodeOfNodeListMap mapNewNodes; - TElemOfVecOfNnlmiMap mapElemNewNodes; - TElemOfElemListMap newElemsMap; + // Groups creation - // loop on theElems - set< const SMDS_MeshElement* >::iterator itElem; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) + // loop on nodes and elements + for ( int isNodes = 0; isNodes < 2; ++isNodes ) { - const SMDS_MeshElement* elem = (*itElem); - if ( !elem ) - continue; - vector & newNodesItVec = mapElemNewNodes[ elem ]; - newNodesItVec.reserve( elem->NbNodes() ); + const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens; + const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems; + if ( gens.Length() != elems.Length() ) + throw SALOME_Exception(LOCALIZED("invalid args")); - // loop on elem nodes - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) { + // loop on created elements + for (int iElem = 1; iElem <= elems.Length(); ++iElem ) + { + const SMDS_MeshElement* sourceElem = gens( iElem ); + if ( !sourceElem ) { + MESSAGE("generateGroups(): NULL source element"); + continue; + } + list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ]; + if ( groupsOldNew.empty() ) { + while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem ) + ++iElem; // skip all elements made by sourceElem + continue; + } + // collect all elements made by sourceElem + list< const SMDS_MeshElement* > resultElems; + if ( const SMDS_MeshElement* resElem = elems( iElem )) + if ( resElem != sourceElem ) + resultElems.push_back( resElem ); + while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem ) + if ( const SMDS_MeshElement* resElem = elems( ++iElem )) + if ( resElem != sourceElem ) + resultElems.push_back( resElem ); + // do not generate element groups from node ones + if ( sourceElem->GetType() == SMDSAbs_Node && + elems( iElem )->GetType() != SMDSAbs_Node ) + continue; - // check if a node has been already sweeped - const SMDS_MeshNode* node = - static_cast( itN->next() ); - TNodeOfNodeListMapItr nIt = mapNewNodes.find( node ); - if ( nIt == mapNewNodes.end() ) + // add resultElems to groups made by ones the sourceElem belongs to + list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end(); + for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew ) { - nIt = mapNewNodes.insert( make_pair( node, list() )).first; - list& listNewNodes = nIt->second; - - // make new nodes - gp_XYZ aXYZ( node->X(), node->Y(), node->Z() ); - double coord[3]; - aXYZ.Coord( coord[0], coord[1], coord[2] ); - bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol ); - const SMDS_MeshNode * newNode = node; - for ( int i = 0; i < theNbSteps; i++ ) { - if ( !isOnAxis ) { - aTrsf.Transforms( coord[0], coord[1], coord[2] ); - newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); + SMESHDS_GroupBase* oldGroup = gOldNew->first; + if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup + { + SMDS_MeshGroup* & newGroup = gOldNew->second; + if ( !newGroup )// create a new group + { + // make a name + string name = oldGroup->GetStoreName(); + if ( !targetMesh ) { + name += "_"; + name += postfix; + int nb = 0; + while ( !groupNames.insert( name ).second ) // name exists + { + if ( nb == 0 ) { + name += "_1"; + } + else { + TCollection_AsciiString nbStr(nb+1); + name.resize( name.rfind('_')+1 ); + name += nbStr.ToCString(); + } + ++nb; + } + } + // make a group + int id; + SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(), + name.c_str(), id ); + SMESHDS_Group* groupDS = static_cast(group->GetGroupDS()); + newGroup = & groupDS->SMDSGroup(); + newGroupIDs->push_back( id ); } - listNewNodes.push_back( newNode ); + + // fill in a new group + list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt; + for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt ) + newGroup->Add( *resElemIt ); } } - newNodesItVec.push_back( nIt ); - } - // make new elements - sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem] ); - } - - makeWalls( aMesh, mapNewNodes, newElemsMap, mapElemNewNodes, theElems ); + } // loop on created elements + }// loop on nodes and elements + return newGroupIDs; } - -//======================================================================= -//function : CreateNode -//purpose : -//======================================================================= -const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x, - const double y, - const double z, - const double tolnode) +//================================================================================ +/*! + * \brief Return list of group of nodes close to each other within theTolerance + * Search among theNodes or in the whole mesh if theNodes is empty using + * an Octree algorithm + */ +//================================================================================ + +void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes, + const double theTolerance, + TListOfListOfNodes & theGroupsOfNodes) { - gp_Pnt P1(x,y,z); - SMESHDS_Mesh * aMesh = myMesh->GetMeshDS(); - // try to search in sequence of existing nodes - SMDS_NodeIteratorPtr itn = aMesh->nodesIterator(); - while(itn->more()) { - const SMDS_MeshNode* aN = static_cast (itn->next()); - gp_Pnt P2(aN->X(),aN->Y(),aN->Z()); - if(P1.Distance(P2)AddNode(x,y,z); - return NewNode; + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + if ( theNodes.empty() ) + { // get all nodes in the mesh + SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true); + while ( nIt->more() ) + theNodes.insert( theNodes.end(),nIt->next()); + } + + SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance); } //======================================================================= -//function : ExtrusionSweep -//purpose : +/*! + * \brief Implementation of search for the node closest to point + */ //======================================================================= -void SMESH_MeshEditor::ExtrusionSweep - (set & theElems, - const gp_Vec& theStep, - const int theNbSteps, - TElemOfElemListMap& newElemsMap, - const int theFlags, - const double theTolerance) +struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher { - gp_Trsf aTrsf; - aTrsf.SetTranslation( theStep ); + //--------------------------------------------------------------------- + /*! + * \brief Constructor + */ + SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh ) + { + myMesh = ( SMESHDS_Mesh* ) theMesh; - SMESHDS_Mesh* aMesh = GetMeshDS(); + TIDSortedNodeSet nodes; + if ( theMesh ) { + SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true); + while ( nIt->more() ) + nodes.insert( nodes.end(), nIt->next() ); + } + myOctreeNode = new SMESH_OctreeNode(nodes) ; - TNodeOfNodeListMap mapNewNodes; - TElemOfVecOfNnlmiMap mapElemNewNodes; - //TElemOfElemListMap newElemsMap; + // get max size of a leaf box + SMESH_OctreeNode* tree = myOctreeNode; + while ( !tree->isLeaf() ) + { + SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator(); + if ( cIt->more() ) + tree = cIt->next(); + } + myHalfLeafSize = tree->maxSize() / 2.; + } - // loop on theElems - set< const SMDS_MeshElement* >::iterator itElem; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) + //--------------------------------------------------------------------- + /*! + * \brief Move node and update myOctreeNode accordingly + */ + void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt ) { - // check element type - const SMDS_MeshElement* elem = (*itElem); - if ( !elem ) - continue; - - vector & newNodesItVec = mapElemNewNodes[ elem ]; - newNodesItVec.reserve( elem->NbNodes() ); - - // loop on elem nodes - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) { + myOctreeNode->UpdateByMoveNode( node, toPnt ); + myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() ); + } - // check if a node has been already sweeped - const SMDS_MeshNode* node = - static_cast( itN->next() ); - TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node ); - if ( nIt == mapNewNodes.end() ) + //--------------------------------------------------------------------- + /*! + * \brief Do it's job + */ + const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt ) + { + SMDS_MeshNode tgtNode( thePnt.X(), thePnt.Y(), thePnt.Z() ); + map dist2Nodes; + myOctreeNode->NodesAround( &tgtNode, dist2Nodes, myHalfLeafSize ); + if ( !dist2Nodes.empty() ) + return dist2Nodes.begin()->second; + list nodes; + //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize ); + + double minSqDist = DBL_MAX; + if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt + { + // sort leafs by their distance from thePnt + typedef map< double, SMESH_OctreeNode* > TDistTreeMap; + TDistTreeMap treeMap; + list< SMESH_OctreeNode* > treeList; + list< SMESH_OctreeNode* >::iterator trIt; + treeList.push_back( myOctreeNode ); + + SMDS_MeshNode pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() ); + bool pointInside = myOctreeNode->isInside( &pointNode, myHalfLeafSize ); + for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt) { - nIt = mapNewNodes.insert( make_pair( node, list() )).first; - list& listNewNodes = nIt->second; - - // make new nodes - double coord[] = { node->X(), node->Y(), node->Z() }; - for ( int i = 0; i < theNbSteps; i++ ) { - aTrsf.Transforms( coord[0], coord[1], coord[2] ); - if( theFlags & EXTRUSION_FLAG_SEW ) { - const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], - coord[2], theTolerance); - listNewNodes.push_back( newNode ); - } - else { - const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); - listNewNodes.push_back( newNode ); - } + SMESH_OctreeNode* tree = *trIt; + if ( !tree->isLeaf() ) // put children to the queue + { + if ( pointInside && !tree->isInside( &pointNode, myHalfLeafSize )) continue; + SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator(); + while ( cIt->more() ) + treeList.push_back( cIt->next() ); + } + else if ( tree->NbNodes() ) // put a tree to the treeMap + { + const Bnd_B3d& box = tree->getBox(); + double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() )); + pair it_in = treeMap.insert( make_pair( sqDist, tree )); + if ( !it_in.second ) // not unique distance to box center + treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree )); } } - newNodesItVec.push_back( nIt ); + // find distance after which there is no sense to check tree's + double sqLimit = DBL_MAX; + TDistTreeMap::iterator sqDist_tree = treeMap.begin(); + if ( treeMap.size() > 5 ) { + SMESH_OctreeNode* closestTree = sqDist_tree->second; + const Bnd_B3d& box = closestTree->getBox(); + double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() ); + sqLimit = limit * limit; + } + // get all nodes from trees + for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) { + if ( sqDist_tree->first > sqLimit ) + break; + SMESH_OctreeNode* tree = sqDist_tree->second; + tree->NodesAround( tree->GetNodeIterator()->next(), &nodes ); + } } - // make new elements - sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem] ); - } - if( theFlags & EXTRUSION_FLAG_BOUNDARY ) { - makeWalls( aMesh, mapNewNodes, newElemsMap, mapElemNewNodes, theElems ); - } -} - -//======================================================================= -//class : SMESH_MeshEditor_PathPoint -//purpose : auxiliary class -//======================================================================= -class SMESH_MeshEditor_PathPoint { -public: - SMESH_MeshEditor_PathPoint() { - myPnt.SetCoord(99., 99., 99.); - myTgt.SetCoord(1.,0.,0.); - myAngle=0.; - myPrm=0.; - } - void SetPnt(const gp_Pnt& aP3D){ - myPnt=aP3D; - } - void SetTangent(const gp_Dir& aTgt){ - myTgt=aTgt; - } - void SetAngle(const double& aBeta){ - myAngle=aBeta; - } - void SetParameter(const double& aPrm){ - myPrm=aPrm; - } - const gp_Pnt& Pnt()const{ - return myPnt; - } - const gp_Dir& Tangent()const{ - return myTgt; - } - double Angle()const{ - return myAngle; - } - double Parameter()const{ - return myPrm; + // find closest among nodes + minSqDist = DBL_MAX; + const SMDS_MeshNode* closestNode = 0; + list::iterator nIt = nodes.begin(); + for ( ; nIt != nodes.end(); ++nIt ) { + double sqDist = thePnt.SquareDistance( SMESH_MeshEditor::TNodeXYZ( *nIt ) ); + if ( minSqDist > sqDist ) { + closestNode = *nIt; + minSqDist = sqDist; + } + } + return closestNode; } -protected: - gp_Pnt myPnt; - gp_Dir myTgt; - double myAngle; - double myPrm; + //--------------------------------------------------------------------- + /*! + * \brief Destructor + */ + ~SMESH_NodeSearcherImpl() { delete myOctreeNode; } + + //--------------------------------------------------------------------- + /*! + * \brief Return the node tree + */ + const SMESH_OctreeNode* getTree() const { return myOctreeNode; } + +private: + SMESH_OctreeNode* myOctreeNode; + SMESHDS_Mesh* myMesh; + double myHalfLeafSize; // max size of a leaf box }; //======================================================================= -//function : ExtrusionAlongTrack -//purpose : +/*! + * \brief Return SMESH_NodeSearcher + */ //======================================================================= -SMESH_MeshEditor::Extrusion_Error - SMESH_MeshEditor::ExtrusionAlongTrack (std::set & theElements, - SMESH_subMesh* theTrack, - const SMDS_MeshNode* theN1, - const bool theHasAngles, - std::list& theAngles, - const bool theHasRefPoint, - const gp_Pnt& theRefPoint) -{ - MESSAGE("SMESH_MeshEditor::ExtrusionAlongTrack") - int j, aNbTP, aNbE, aNb; - double aT1, aT2, aT, aAngle, aX, aY, aZ; - std::list aPrms; - std::list::iterator aItD; - std::set< const SMDS_MeshElement* >::iterator itElem; - Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2; - gp_Pnt aP3D, aV0; - gp_Vec aVec; - gp_XYZ aGC; - Handle(Geom_Curve) aC3D; - TopoDS_Edge aTrackEdge; - TopoDS_Vertex aV1, aV2; +SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher() +{ + return new SMESH_NodeSearcherImpl( GetMeshDS() ); +} - SMDS_ElemIteratorPtr aItE; - SMDS_NodeIteratorPtr aItN; - SMDSAbs_ElementType aTypeE; +// ======================================================================== +namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() +{ + const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree + const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152 + const double NodeRadius = 1e-9; // to enlarge bnd box of element - TNodeOfNodeListMap mapNewNodes; - TElemOfVecOfNnlmiMap mapElemNewNodes; - TElemOfElemListMap newElemsMap; + //======================================================================= + /*! + * \brief Octal tree of bounding boxes of elements + */ + //======================================================================= - aTolVec=1.e-7; - aTolVec2=aTolVec*aTolVec; + class ElementBndBoxTree : public SMESH_Octree + { + public: + + ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, double tolerance = NodeRadius ); + void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems); + void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems); + ~ElementBndBoxTree(); + + protected: + ElementBndBoxTree() {} + SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; } + void buildChildrenData(); + Bnd_B3d* buildRootBox(); + private: + //!< Bounding box of element + struct ElementBox : public Bnd_B3d + { + const SMDS_MeshElement* _element; + int _refCount; // an ElementBox can be included in several tree branches + ElementBox(const SMDS_MeshElement* elem, double tolerance); + }; + vector< ElementBox* > _elements; + }; - // 1. Check data - aNbE = theElements.size(); - // nothing to do - if ( !aNbE ) - return EXTR_NO_ELEMENTS; + //================================================================================ + /*! + * \brief ElementBndBoxTree creation + */ + //================================================================================ - // 1.1 Track Pattern - ASSERT( theTrack ); + ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, double tolerance) + :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. )) + { + int nbElems = mesh.GetMeshInfo().NbElements( elemType ); + _elements.reserve( nbElems ); - SMESHDS_SubMesh* pSubMeshDS=theTrack->GetSubMeshDS(); + SMDS_ElemIteratorPtr elemIt = mesh.elementsIterator( elemType ); + while ( elemIt->more() ) + _elements.push_back( new ElementBox( elemIt->next(),tolerance )); - aItE = pSubMeshDS->GetElements(); - while ( aItE->more() ) { - const SMDS_MeshElement* pE = aItE->next(); - aTypeE = pE->GetType(); - // Pattern must contain links only - if ( aTypeE != SMDSAbs_Edge ) - return EXTR_PATH_NOT_EDGE; + if ( _elements.size() > MaxNbElemsInLeaf ) + compute(); + else + myIsLeaf = true; } - const TopoDS_Shape& aS = theTrack->GetSubShape(); - // Sub shape for the Pattern must be an Edge - if ( aS.ShapeType() != TopAbs_EDGE ) - return EXTR_BAD_PATH_SHAPE; - - aTrackEdge = TopoDS::Edge( aS ); - // the Edge must not be degenerated - if ( BRep_Tool::Degenerated( aTrackEdge ) ) - return EXTR_BAD_PATH_SHAPE; + //================================================================================ + /*! + * \brief Destructor + */ + //================================================================================ - TopExp::Vertices( aTrackEdge, aV1, aV2 ); - aT1=BRep_Tool::Parameter( aV1, aTrackEdge ); - aT2=BRep_Tool::Parameter( aV2, aTrackEdge ); + ElementBndBoxTree::~ElementBndBoxTree() + { + for ( int i = 0; i < _elements.size(); ++i ) + if ( --_elements[i]->_refCount <= 0 ) + delete _elements[i]; + } - aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes(); - const SMDS_MeshNode* aN1 = aItN->next(); + //================================================================================ + /*! + * \brief Return the maximal box + */ + //================================================================================ - aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes(); - const SMDS_MeshNode* aN2 = aItN->next(); + Bnd_B3d* ElementBndBoxTree::buildRootBox() + { + Bnd_B3d* box = new Bnd_B3d; + for ( int i = 0; i < _elements.size(); ++i ) + box->Add( *_elements[i] ); + return box; + } - // starting node must be aN1 or aN2 - if ( !( aN1 == theN1 || aN2 == theN1 ) ) - return EXTR_BAD_STARTING_NODE; + //================================================================================ + /*! + * \brief Redistrubute element boxes among children + */ + //================================================================================ - aNbTP = pSubMeshDS->NbNodes() + 2; + void ElementBndBoxTree::buildChildrenData() + { + for ( int i = 0; i < _elements.size(); ++i ) + { + for (int j = 0; j < 8; j++) + { + if ( !_elements[i]->IsOut( myChildren[j]->getBox() )) + { + _elements[i]->_refCount++; + ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]); + } + } + _elements[i]->_refCount--; + } + _elements.clear(); - // 1.2. Angles - vector aAngles( aNbTP ); + for (int j = 0; j < 8; j++) + { + ElementBndBoxTree* child = static_cast( myChildren[j]); + if ( child->_elements.size() <= MaxNbElemsInLeaf ) + child->myIsLeaf = true; - for ( j=0; j < aNbTP; ++j ) { - aAngles[j] = 0.; + if ( child->_elements.capacity() - child->_elements.size() > 1000 ) + child->_elements.resize( child->_elements.size() ); // compact + } } - if ( theHasAngles ) { - aItD = theAngles.begin(); - for ( j=1; (aItD != theAngles.end()) && (jIsOut( point.XYZ() )) + foundElems.insert( _elements[i]->_element ); + } + else + { + for (int i = 0; i < 8; i++) + ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems ); } } - // 2. Collect parameters on the track edge - aPrms.push_back( aT1 ); - aPrms.push_back( aT2 ); + //================================================================================ + /*! + * \brief Return elements which can be intersected by the line + */ + //================================================================================ - aItN = pSubMeshDS->GetNodes(); - while ( aItN->more() ) { - const SMDS_MeshNode* pNode = aItN->next(); - const SMDS_EdgePosition* pEPos = - static_cast( pNode->GetPosition().get() ); - aT = pEPos->GetUParameter(); - aPrms.push_back( aT ); - } + void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line, + TIDSortedElemSet& foundElems) + { + if ( level() && getBox().IsOut( line )) + return; - // sort parameters - aPrms.sort(); - if ( aN1 == theN1 ) { - if ( aT1 > aT2 ) { - aPrms.reverse(); + if ( isLeaf() ) + { + for ( int i = 0; i < _elements.size(); ++i ) + if ( !_elements[i]->IsOut( line )) + foundElems.insert( _elements[i]->_element ); } - } - else { - if ( aT2 > aT1 ) { - aPrms.reverse(); + else + { + for (int i = 0; i < 8; i++) + ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems ); } } - // 3. Path Points - SMESH_MeshEditor_PathPoint aPP; - vector aPPs( aNbTP ); - // - aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 ); - // - aItD = aPrms.begin(); - for ( j=0; aItD != aPrms.end(); ++aItD, ++j ) { - aT = *aItD; - aC3D->D1( aT, aP3D, aVec ); - aL2 = aVec.SquareMagnitude(); - if ( aL2 < aTolVec2 ) - return EXTR_CANT_GET_TANGENT; + //================================================================================ + /*! + * \brief Construct the element box + */ + //================================================================================ - gp_Dir aTgt( aVec ); - aAngle = aAngles[j]; + ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance) + { + _element = elem; + _refCount = 1; + SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); + while ( nIt->more() ) + Add( SMESH_MeshEditor::TNodeXYZ( cast2Node( nIt->next() ))); + Enlarge( tolerance ); + } - aPP.SetPnt( aP3D ); - aPP.SetTangent( aTgt ); - aPP.SetAngle( aAngle ); - aPP.SetParameter( aT ); - aPPs[j]=aPP; +} // namespace + +//======================================================================= +/*! + * \brief Implementation of search for the elements by point and + * of classification of point in 2D mesh + */ +//======================================================================= + +struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher +{ + SMESHDS_Mesh* _mesh; + ElementBndBoxTree* _ebbTree; + SMESH_NodeSearcherImpl* _nodeSearcher; + SMDSAbs_ElementType _elementType; + double _tolerance; + bool _outerFacesFound; + set _outerFaces; // empty means "no internal faces at all" + + SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh ) + : _mesh(&mesh),_ebbTree(0),_nodeSearcher(0), _tolerance(-1), _outerFacesFound(false) {} + ~SMESH_ElementSearcherImpl() + { + if ( _ebbTree ) delete _ebbTree; _ebbTree = 0; + if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0; + } + virtual int FindElementsByPoint(const gp_Pnt& point, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElements); + virtual TopAbs_State GetPointState(const gp_Pnt& point); + + void GetElementsNearLine( const gp_Ax1& line, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElems); + double getTolerance(); + bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face, + const double tolerance, double & param); + void findOuterBoundary(const SMDS_MeshElement* anyOuterFace); + bool isOuterBoundary(const SMDS_MeshElement* face) const + { + return _outerFaces.empty() || _outerFaces.count(face); } + struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState()) + { + const SMDS_MeshElement* _face; + gp_Vec _faceNorm; + bool _coincides; //!< the line lays in face plane + TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false) + : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {} + }; + struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary()) + { + SMESH_TLink _link; + TIDSortedElemSet _faces; + TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face) + : _link( n1, n2 ), _faces( &face, &face + 1) {} + }; +}; - // 3. Center of rotation aV0 - aV0 = theRefPoint; - if ( !theHasRefPoint ) { - aNb = 0; - aGC.SetCoord( 0.,0.,0. ); +ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i) +{ + return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0) + << ", _coincides="<nodesIterator(); - while ( itN->more() ) { - const SMDS_MeshNode* node = static_cast( itN->next() ); - aX = node->X(); - aY = node->Y(); - aZ = node->Z(); +double SMESH_ElementSearcherImpl::getTolerance() +{ + if ( _tolerance < 0 ) + { + const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo(); - if ( mapNewNodes.find( node ) == mapNewNodes.end() ) { - list aLNx; - mapNewNodes[node] = aLNx; - // - gp_XYZ aXYZ( aX, aY, aZ ); - aGC += aXYZ; - ++aNb; - } + _tolerance = 0; + if ( _nodeSearcher && meshInfo.NbNodes() > 1 ) + { + double boxSize = _nodeSearcher->getTree()->maxSize(); + _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/; + } + else if ( _ebbTree && meshInfo.NbElements() > 0 ) + { + double boxSize = _ebbTree->maxSize(); + _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/; + } + if ( _tolerance == 0 ) + { + // define tolerance by size of a most complex element + int complexType = SMDSAbs_Volume; + while ( complexType > SMDSAbs_All && + meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 ) + --complexType; + if ( complexType == SMDSAbs_All ) return 0; // empty mesh + + double elemSize; + if ( complexType == int( SMDSAbs_Node )) + { + SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator(); + elemSize = 1; + if ( meshInfo.NbNodes() > 2 ) + elemSize = SMESH_MeshEditor::TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() ); + } + else + { + const SMDS_MeshElement* elem = + _mesh->elementsIterator( SMDSAbs_ElementType( complexType ))->next(); + SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); + SMESH_MeshEditor::TNodeXYZ n1( cast2Node( nodeIt->next() )); + while ( nodeIt->more() ) + { + double dist = n1.Distance( cast2Node( nodeIt->next() )); + elemSize = max( dist, elemSize ); + } } + _tolerance = 1e-4 * elemSize; } - aGC /= aNb; - aV0.SetXYZ( aGC ); - } // if (!theHasRefPoint) { - mapNewNodes.clear(); + } + return _tolerance; +} - // 4. Processing the elements - SMESHDS_Mesh* aMesh = GetMeshDS(); +//================================================================================ +/*! + * \brief Find intersection of the line and an edge of face and return parameter on line + */ +//================================================================================ - for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) { - // check element type - const SMDS_MeshElement* elem = (*itElem); - aTypeE = elem->GetType(); - if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) ) - continue; +bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line, + const SMDS_MeshElement* face, + const double tol, + double & param) +{ + int nbInts = 0; + param = 0; + + GeomAPI_ExtremaCurveCurve anExtCC; + Handle(Geom_Curve) lineCurve = new Geom_Line( line ); + + int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes(); + for ( int i = 0; i < nbNodes && nbInts < 2; ++i ) + { + GC_MakeSegment edge( SMESH_MeshEditor::TNodeXYZ( face->GetNode( i )), + SMESH_MeshEditor::TNodeXYZ( face->GetNode( (i+1)%nbNodes) )); + anExtCC.Init( lineCurve, edge); + if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol) + { + Quantity_Parameter pl, pe; + anExtCC.LowerDistanceParameters( pl, pe ); + param += pl; + if ( ++nbInts == 2 ) + break; + } + } + if ( nbInts > 0 ) param /= nbInts; + return nbInts > 0; +} +//================================================================================ +/*! + * \brief Find all faces belonging to the outer boundary of mesh + */ +//================================================================================ - vector & newNodesItVec = mapElemNewNodes[ elem ]; - newNodesItVec.reserve( elem->NbNodes() ); +void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace) +{ + if ( _outerFacesFound ) return; - // loop on elem nodes - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) { + // Collect all outer faces by passing from one outer face to another via their links + // and BTW find out if there are internal faces at all. - // check if a node has been already processed - const SMDS_MeshNode* node = - static_cast( itN->next() ); - TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node ); - if ( nIt == mapNewNodes.end() ) { - nIt = mapNewNodes.insert( make_pair( node, list() )).first; - list& listNewNodes = nIt->second; + // checked links and links where outer boundary meets internal one + set< SMESH_TLink > visitedLinks, seamLinks; + + // links to treat with already visited faces sharing them + list < TFaceLink > startLinks; - // make new nodes - aX = node->X(); aY = node->Y(); aZ = node->Z(); - - Standard_Real aAngle1x, aAngleT1T0, aTolAng; - gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x; - gp_Ax1 anAx1, anAxT1T0; - gp_Dir aDT1x, aDT0x, aDT1T0; - - aTolAng=1.e-4; - - aV0x = aV0; - aPN0.SetCoord(aX, aY, aZ); - - const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0]; - aP0x = aPP0.Pnt(); - aDT0x= aPP0.Tangent(); - - for ( j = 1; j < aNbTP; ++j ) { - const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j]; - aP1x = aPP1.Pnt(); - aDT1x = aPP1.Tangent(); - aAngle1x = aPP1.Angle(); - - gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0; - // Translation - gp_Vec aV01x( aP0x, aP1x ); - aTrsf.SetTranslation( aV01x ); - - // traslated point - aV1x = aV0x.Transformed( aTrsf ); - aPN1 = aPN0.Transformed( aTrsf ); - - // rotation 1 [ T1,T0 ] - aAngleT1T0=-aDT1x.Angle( aDT0x ); - if (fabs(aAngleT1T0) > aTolAng) { - aDT1T0=aDT1x^aDT0x; - anAxT1T0.SetLocation( aV1x ); - anAxT1T0.SetDirection( aDT1T0 ); - aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 ); - - aPN1 = aPN1.Transformed( aTrsfRotT1T0 ); - } - - // rotation 2 - if ( theHasAngles ) { - anAx1.SetLocation( aV1x ); - anAx1.SetDirection( aDT1x ); - aTrsfRot.SetRotation( anAx1, aAngle1x ); - - aPN1 = aPN1.Transformed( aTrsfRot ); - } - - // make new node - aX = aPN1.X(); - aY = aPN1.Y(); - aZ = aPN1.Z(); - const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ ); - listNewNodes.push_back( newNode ); - - aPN0 = aPN1; - aP0x = aP1x; - aV0x = aV1x; - aDT0x = aDT1x; - } + // load startLinks with the first outerFace + startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace)); + _outerFaces.insert( outerFace ); + + TIDSortedElemSet emptySet; + while ( !startLinks.empty() ) + { + const SMESH_TLink& link = startLinks.front()._link; + TIDSortedElemSet& faces = startLinks.front()._faces; + + outerFace = *faces.begin(); + // find other faces sharing the link + const SMDS_MeshElement* f; + while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces ))) + faces.insert( f ); + + // select another outer face among the found + const SMDS_MeshElement* outerFace2 = 0; + if ( faces.size() == 2 ) + { + outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin()); + } + else if ( faces.size() > 2 ) + { + seamLinks.insert( link ); + + // link direction within the outerFace + gp_Vec n1n2( SMESH_MeshEditor::TNodeXYZ( link.node1()), + SMESH_MeshEditor::TNodeXYZ( link.node2())); + int i1 = outerFace->GetNodeIndex( link.node1() ); + int i2 = outerFace->GetNodeIndex( link.node2() ); + bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 ); + if ( rev ) n1n2.Reverse(); + // outerFace normal + gp_XYZ ofNorm, fNorm; + if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false )) + { + // direction from the link inside outerFace + gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2; + // sort all other faces by angle with the dirInOF + map< double, const SMDS_MeshElement* > angle2Face; + set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin(); + for ( ; face != faces.end(); ++face ) + { + if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false )) + continue; + gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2; + double angle = dirInOF.AngleWithRef( dirInF, n1n2 ); + if ( angle < 0 ) angle += 2*PI; + angle2Face.insert( make_pair( angle, *face )); + } + if ( !angle2Face.empty() ) + outerFace2 = angle2Face.begin()->second; } - newNodesItVec.push_back( nIt ); } - // make new elements - sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem] ); + // store the found outer face and add its links to continue seaching from + if ( outerFace2 ) + { + _outerFaces.insert( outerFace ); + int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 ); + for ( int i = 0; i < nbNodes; ++i ) + { + SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes)); + if ( visitedLinks.insert( link2 ).second ) + startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 )); + } + } + startLinks.pop_front(); } + _outerFacesFound = true; - makeWalls( aMesh, mapNewNodes, newElemsMap, mapElemNewNodes, theElements ); - - return EXTR_OK; + if ( !seamLinks.empty() ) + { + // There are internal boundaries touching the outher one, + // find all faces of internal boundaries in order to find + // faces of boundaries of holes, if any. + + } + else + { + _outerFaces.clear(); + } } //======================================================================= -//function : Transform -//purpose : +/*! + * \brief Find elements of given type where the given point is IN or ON. + * Returns nb of found elements and elements them-selves. + * + * 'ALL' type means elements of any type excluding nodes and 0D elements + */ //======================================================================= -void SMESH_MeshEditor::Transform (set & theElems, - const gp_Trsf& theTrsf, - const bool theCopy) +int SMESH_ElementSearcherImpl:: +FindElementsByPoint(const gp_Pnt& point, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElements) { - bool needReverse; - switch ( theTrsf.Form() ) { - case gp_PntMirror: - case gp_Ax2Mirror: - needReverse = true; - break; - default: - needReverse = false; - } + foundElements.clear(); - SMESHDS_Mesh* aMesh = GetMeshDS(); + double tolerance = getTolerance(); - // map old node to new one - TNodeNodeMap nodeMap; + // ================================================================================= + if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement ) + { + if ( !_nodeSearcher ) + _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh ); - // elements sharing moved nodes; those of them which have all - // nodes mirrored but are not in theElems are to be reversed - set inverseElemSet; + const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point ); + if ( !closeNode ) return foundElements.size(); - // loop on theElems - set< const SMDS_MeshElement* >::iterator itElem; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) + if ( point.Distance( SMESH_MeshEditor::TNodeXYZ( closeNode )) > tolerance ) + return foundElements.size(); // to far from any node + + if ( type == SMDSAbs_Node ) + { + foundElements.push_back( closeNode ); + } + else + { + SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement ); + while ( elemIt->more() ) + foundElements.push_back( elemIt->next() ); + } + } + // ================================================================================= + else // elements more complex than 0D { - const SMDS_MeshElement* elem = (*itElem); - if ( !elem ) - continue; + if ( !_ebbTree || _elementType != type ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, tolerance ); + } + TIDSortedElemSet suspectElems; + _ebbTree->getElementsNearPoint( point, suspectElems ); + TIDSortedElemSet::iterator elem = suspectElems.begin(); + for ( ; elem != suspectElems.end(); ++elem ) + if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance )) + foundElements.push_back( *elem ); + } + return foundElements.size(); +} - // loop on elem nodes - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) { +//================================================================================ +/*! + * \brief Classify the given point in the closed 2D mesh + */ +//================================================================================ - // check if a node has been already transformed - const SMDS_MeshNode* node = - static_cast( itN->next() ); - if (nodeMap.find( node ) != nodeMap.end() ) - continue; +TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point) +{ + double tolerance = getTolerance(); + if ( !_ebbTree || _elementType != SMDSAbs_Face ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face ); + } + // Algo: analyse transition of a line starting at the point through mesh boundary; + // try three lines parallel to axis of the coordinate system and perform rough + // analysis. If solution is not clear perform thorough analysis. + + const int nbAxes = 3; + gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() }; + map< double, TInters > paramOnLine2TInters[ nbAxes ]; + list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line + multimap< int, int > nbInt2Axis; // to find the simplest case + for ( int axis = 0; axis < nbAxes; ++axis ) + { + gp_Ax1 lineAxis( point, axisDir[axis]); + gp_Lin line ( lineAxis ); - double coord[3]; - coord[0] = node->X(); - coord[1] = node->Y(); - coord[2] = node->Z(); - theTrsf.Transforms( coord[0], coord[1], coord[2] ); - const SMDS_MeshNode * newNode = node; - if ( theCopy ) - newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); - else { - aMesh->MoveNode( node, coord[0], coord[1], coord[2] ); - // node position on shape becomes invalid - const_cast< SMDS_MeshNode* > ( node )->SetPosition - ( SMDS_SpacePosition::originSpacePosition() ); - } - nodeMap.insert( TNodeNodeMap::value_type( node, newNode )); + TIDSortedElemSet suspectFaces; // faces possibly intersecting the line + _ebbTree->getElementsNearLine( lineAxis, suspectFaces ); - // keep inverse elements - if ( !theCopy && needReverse ) { - SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator(); - while ( invElemIt->more() ) - inverseElemSet.insert( invElemIt->next() ); + // Intersect faces with the line + + map< double, TInters > & u2inters = paramOnLine2TInters[ axis ]; + TIDSortedElemSet::iterator face = suspectFaces.begin(); + for ( ; face != suspectFaces.end(); ++face ) + { + // get face plane + gp_XYZ fNorm; + if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue; + gp_Pln facePlane( SMESH_MeshEditor::TNodeXYZ( (*face)->GetNode(0)), fNorm ); + + // perform intersection + IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane )); + if ( !intersection.IsDone() ) + continue; + if ( intersection.IsInQuadric() ) + { + tangentInters[ axis ].push_back( TInters( *face, fNorm, true )); + } + else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 ) + { + gp_Pnt intersectionPoint = intersection.Point(1); + if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance )) + u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm ))); } } - } + // Analyse intersections roughly - // either new elements are to be created - // or a mirrored element are to be reversed - if ( !theCopy && !needReverse) - return; + int nbInter = u2inters.size(); + if ( nbInter == 0 ) + return TopAbs_OUT; - if ( !inverseElemSet.empty()) { - set::iterator invElemIt = inverseElemSet.begin(); - for ( ; invElemIt != inverseElemSet.end(); invElemIt++ ) - theElems.insert( *invElemIt ); - } + double f = u2inters.begin()->first, l = u2inters.rbegin()->first; + if ( nbInter == 1 ) // not closed mesh + return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN; - // replicate or reverse elements + if ( fabs( f ) < tolerance || fabs( l ) < tolerance ) + return TopAbs_ON; - enum { - REV_TETRA = 0, // = nbNodes - 4 - REV_PYRAMID = 1, // = nbNodes - 4 - REV_PENTA = 2, // = nbNodes - 4 - REV_FACE = 3, - REV_HEXA = 4, // = nbNodes - 4 - FORWARD = 5 - }; - int index[][8] = { - { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA - { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID - { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA - { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE - { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA - { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD - }; + if ( (f<0) == (l<0) ) + return TopAbs_OUT; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) - { - const SMDS_MeshElement* elem = (*itElem); - if ( !elem || elem->GetType() == SMDSAbs_Node ) - continue; + int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0)); + int nbIntAfterPoint = nbInter - nbIntBeforePoint; + if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 ) + return TopAbs_IN; - int nbNodes = elem->NbNodes(); - int elemType = elem->GetType(); + nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis )); - if (elem->IsPoly()) { - // Polygon or Polyhedral Volume - switch ( elemType ) { - case SMDSAbs_Face: + if ( _outerFacesFound ) break; // pass to thorough analysis + + } // three attempts - loop on CS axes + + // Analyse intersections thoroughly. + // We make two loops maximum, on the first one we only exclude touching intersections, + // on the second, if situation is still unclear, we gather and use information on + // position of faces (internal or outer). If faces position is already gathered, + // we make the second loop right away. + + for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo ) + { + multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin(); + for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis ) + { + int axis = nb_axis->second; + map< double, TInters > & u2inters = paramOnLine2TInters[ axis ]; + + gp_Ax1 lineAxis( point, axisDir[axis]); + gp_Lin line ( lineAxis ); + + // add tangent intersections to u2inters + double param; + list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin(); + for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt ) + if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param )) + u2inters.insert(make_pair( param, *tgtInt )); + tangentInters[ axis ].clear(); + + // Count intersections before and after the point excluding touching ones. + // If hasPositionInfo we count intersections of outer boundary only + + int nbIntBeforePoint = 0, nbIntAfterPoint = 0; + double f = numeric_limits::max(), l = -numeric_limits::max(); + map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1; + bool ok = ! u_int1->second._coincides; + while ( ok && u_int1 != u2inters.end() ) + { + double u = u_int1->first; + bool touchingInt = false; + if ( ++u_int2 != u2inters.end() ) { - vector poly_nodes (nbNodes); - int iNode = 0; - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while (itN->more()) { - const SMDS_MeshNode* node = - static_cast(itN->next()); - TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); - if (nodeMapIt == nodeMap.end()) - break; // not all nodes transformed - if (needReverse) { - // reverse mirrored faces and volumes - poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second; - } else { - poly_nodes[iNode] = (*nodeMapIt).second; + // skip intersections at the same point (if the line passes through edge or node) + int nbSamePnt = 0; + while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance ) + { + ++nbSamePnt; + ++u_int2; + } + + // skip tangent intersections + int nbTgt = 0; + const SMDS_MeshElement* prevFace = u_int1->second._face; + while ( ok && u_int2->second._coincides ) + { + if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() ) + ok = false; + else + { + nbTgt++; + u_int2++; + ok = ( u_int2 != u2inters.end() ); } - iNode++; } - if ( iNode != nbNodes ) - continue; // not all nodes transformed + if ( !ok ) break; - if ( theCopy ) { - aMesh->AddPolygonalFace(poly_nodes); - } else { - aMesh->ChangePolygonNodes(elem, poly_nodes); + // skip intersections at the same point after tangent intersections + if ( nbTgt > 0 ) + { + double u2 = u_int2->first; + ++u_int2; + while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance ) + { + ++nbSamePnt; + ++u_int2; + } + } + // decide if we skipped a touching intersection + if ( nbSamePnt + nbTgt > 0 ) + { + double minDot = numeric_limits::max(), maxDot = -numeric_limits::max(); + map< double, TInters >::iterator u_int = u_int1; + for ( ; u_int != u_int2; ++u_int ) + { + if ( u_int->second._coincides ) continue; + double dot = u_int->second._faceNorm * line.Direction(); + if ( dot > maxDot ) maxDot = dot; + if ( dot < minDot ) minDot = dot; + } + touchingInt = ( minDot*maxDot < 0 ); } } - break; - case SMDSAbs_Volume: + if ( !touchingInt ) { - // ATTENTION: Reversing is not yet done!!! - const SMDS_PolyhedralVolumeOfNodes* aPolyedre = - (const SMDS_PolyhedralVolumeOfNodes*) elem; - if (!aPolyedre) { - MESSAGE("Warning: bad volumic element"); - continue; + if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face )) + { + if ( u < 0 ) + ++nbIntBeforePoint; + else + ++nbIntAfterPoint; } + if ( u < f ) f = u; + if ( u > l ) l = u; + } - vector poly_nodes; - vector quantities; + u_int1 = u_int2; // to next intersection - bool allTransformed = true; - int nbFaces = aPolyedre->NbFaces(); - for (int iface = 1; iface <= nbFaces && allTransformed; iface++) { - int nbFaceNodes = aPolyedre->NbFaceNodes(iface); - for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) { - const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode); - TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); - if (nodeMapIt == nodeMap.end()) { - allTransformed = false; // not all nodes transformed - } else { - poly_nodes.push_back((*nodeMapIt).second); - } - } - quantities.push_back(nbFaceNodes); - } - if ( !allTransformed ) - continue; // not all nodes transformed + } // loop on intersections with one line - if ( theCopy ) { - aMesh->AddPolyhedralVolume(poly_nodes, quantities); - } else { - aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); - } - } - break; - default:; - } - continue; - } + if ( ok ) + { + if ( fabs( f ) < tolerance || fabs( l ) < tolerance ) + return TopAbs_ON; - // Regular elements - int* i = index[ FORWARD ]; - if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes - if ( elemType == SMDSAbs_Face ) - i = index[ REV_FACE ]; - else - i = index[ nbNodes - 4 ]; + if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0) + return TopAbs_OUT; - // find transformed nodes - const SMDS_MeshNode* nodes[8]; - int iNode = 0; - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) - { - const SMDS_MeshNode* node = - static_cast( itN->next() ); - TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node ); - if ( nodeMapIt == nodeMap.end() ) - break; // not all nodes transformed - nodes[ i [ iNode++ ]] = (*nodeMapIt).second; - } - if ( iNode != nbNodes ) - continue; // not all nodes transformed + if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh + return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN; - if ( theCopy ) - { - // add a new element - switch ( elemType ) { - case SMDSAbs_Edge: - aMesh->AddEdge( nodes[ 0 ], nodes[ 1 ] ); - break; - case SMDSAbs_Face: - if ( nbNodes == 3 ) - aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ); - else - aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] , nodes[ 3 ]); - break; - case SMDSAbs_Volume: - if ( nbNodes == 4 ) - aMesh->AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] , nodes[ 3 ] ); - else if ( nbNodes == 8 ) - aMesh->AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] , nodes[ 3 ], - nodes[ 4 ], nodes[ 5 ], nodes[ 6 ] , nodes[ 7 ]); - else if ( nbNodes == 6 ) - aMesh->AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] , nodes[ 3 ], - nodes[ 4 ], nodes[ 5 ]); - else if ( nbNodes == 5 ) - aMesh->AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] , nodes[ 3 ], - nodes[ 4 ]); - break; - default:; + if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 ) + return TopAbs_IN; + + if ( (f<0) == (l<0) ) + return TopAbs_OUT; + + if ( hasPositionInfo ) + return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT; } - } - else + } // loop on intersections of the tree lines - thorough analysis + + if ( !hasPositionInfo ) { - // reverse element as it was reversed by transformation - if ( nbNodes > 2 ) - aMesh->ChangeElementNodes( elem, nodes, nbNodes ); + // gather info on faces position - is face in the outer boundary or not + map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ]; + findOuterBoundary( u2inters.begin()->second._face ); } + + } // two attempts - with and w/o faces position info in the mesh + + return TopAbs_UNKNOWN; +} + +//======================================================================= +/*! + * \brief Return elements possibly intersecting the line + */ +//======================================================================= + +void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElems) +{ + if ( !_ebbTree || _elementType != type ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type ); } + TIDSortedElemSet suspectFaces; // elements possibly intersecting the line + _ebbTree->getElementsNearLine( line, suspectFaces ); + foundElems.assign( suspectFaces.begin(), suspectFaces.end()); } //======================================================================= -//function : FindCoincidentNodes -//purpose : Return list of group of nodes close to each other within theTolerance -// Search among theNodes or in the whole mesh if theNodes is empty. +/*! + * \brief Return SMESH_ElementSearcher + */ //======================================================================= -void SMESH_MeshEditor::FindCoincidentNodes (set & theNodes, - const double theTolerance, - TListOfListOfNodes & theGroupsOfNodes) +SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher() { - double tol2 = theTolerance * theTolerance; + return new SMESH_ElementSearcherImpl( *GetMeshDS() ); +} - list nodes; - if ( theNodes.empty() ) - { // get all nodes in the mesh - SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(); - while ( nIt->more() ) - nodes.push_back( nIt->next() ); - } - else +//======================================================================= +/*! + * \brief Return true if the point is IN or ON of the element + */ +//======================================================================= + +bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol ) +{ + if ( element->GetType() == SMDSAbs_Volume) { - nodes.insert( nodes.end(), theNodes.begin(), theNodes.end() ); + return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol ); } - list::iterator it2, it1 = nodes.begin(); - for ( ; it1 != nodes.end(); it1++ ) - { - const SMDS_MeshNode* n1 = *it1; - gp_Pnt p1( n1->X(), n1->Y(), n1->Z() ); + // get ordered nodes + + vector< gp_XYZ > xyz; + + SMDS_ElemIteratorPtr nodeIt = element->nodesIterator(); + if ( element->IsQuadratic() ) + if (const SMDS_QuadraticFaceOfNodes* f=dynamic_cast(element)) + nodeIt = f->interlacedNodesElemIterator(); + else if (const SMDS_QuadraticEdge* e =dynamic_cast(element)) + nodeIt = e->interlacedNodesElemIterator(); + + while ( nodeIt->more() ) + xyz.push_back( TNodeXYZ( cast2Node( nodeIt->next() ))); + + int i, nbNodes = element->NbNodes(); - list * groupPtr = 0; - it2 = it1; - for ( it2++; it2 != nodes.end(); it2++ ) + if ( element->GetType() == SMDSAbs_Face ) // -------------------------------------------------- + { + // compute face normal + gp_Vec faceNorm(0,0,0); + xyz.push_back( xyz.front() ); + for ( i = 0; i < nbNodes; ++i ) + { + gp_Vec edge1( xyz[i+1], xyz[i]); + gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] ); + faceNorm += edge1 ^ edge2; + } + double normSize = faceNorm.Magnitude(); + if ( normSize <= tol ) + { + // degenerated face: point is out if it is out of all face edges + for ( i = 0; i < nbNodes; ++i ) + { + SMDS_MeshNode n1( xyz[i].X(), xyz[i].Y(), xyz[i].Z() ); + SMDS_MeshNode n2( xyz[i+1].X(), xyz[i+1].Y(), xyz[i+1].Z() ); + SMDS_MeshEdge edge( &n1, &n2 ); + if ( !isOut( &edge, point, tol )) + return false; + } + return true; + } + faceNorm /= normSize; + + // check if the point lays on face plane + gp_Vec n2p( xyz[0], point ); + if ( fabs( n2p * faceNorm ) > tol ) + return true; // not on face plane + + // check if point is out of face boundary: + // define it by closest transition of a ray point->infinity through face boundary + // on the face plane. + // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool + // to find intersections of the ray with the boundary. + gp_Vec ray = n2p; + gp_Vec plnNorm = ray ^ faceNorm; + normSize = plnNorm.Magnitude(); + if ( normSize <= tol ) return false; // point coincides with the first node + plnNorm /= normSize; + // for each node of the face, compute its signed distance to the plane + vector dist( nbNodes + 1); + for ( i = 0; i < nbNodes; ++i ) + { + gp_Vec n2p( xyz[i], point ); + dist[i] = n2p * plnNorm; + } + dist.back() = dist.front(); + // find the closest intersection + int iClosest = -1; + double rClosest, distClosest = 1e100;; + gp_Pnt pClosest; + for ( i = 0; i < nbNodes; ++i ) { - const SMDS_MeshNode* n2 = *it2; - gp_Pnt p2( n2->X(), n2->Y(), n2->Z() ); - if ( p1.SquareDistance( p2 ) <= tol2 ) + double r; + if ( fabs( dist[i]) < tol ) + r = 0.; + else if ( fabs( dist[i+1]) < tol ) + r = 1.; + else if ( dist[i] * dist[i+1] < 0 ) + r = dist[i] / ( dist[i] - dist[i+1] ); + else + continue; // no intersection + gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r; + gp_Vec p2int ( point, pInt); + if ( p2int * ray > -tol ) // right half-space { - if ( !groupPtr ) { - theGroupsOfNodes.push_back( list() ); - groupPtr = & theGroupsOfNodes.back(); - groupPtr->push_back( n1 ); + double intDist = p2int.SquareMagnitude(); + if ( intDist < distClosest ) + { + iClosest = i; + rClosest = r; + pClosest = pInt; + distClosest = intDist; } - groupPtr->push_back( n2 ); - it2 = nodes.erase( it2 ); - it2--; } } + if ( iClosest < 0 ) + return true; // no intesections - out + + // analyse transition + gp_Vec edge( xyz[iClosest], xyz[iClosest+1] ); + gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face + gp_Vec p2int ( point, pClosest ); + bool out = (edgeNorm * p2int) < -tol; + if ( rClosest > 0. && rClosest < 1. ) // not node intersection + return out; + + // ray pass through a face node; analyze transition through an adjacent edge + gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ]; + gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ]; + gp_Vec edgeAdjacent( p1, p2 ); + gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm ); + bool out2 = (edgeNorm2 * p2int) < -tol; + + bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0; + return covexCorner ? (out || out2) : (out && out2); + } + if ( element->GetType() == SMDSAbs_Edge ) // -------------------------------------------------- + { + // point is out of edge if it is NOT ON any straight part of edge + // (we consider quadratic edge as being composed of two straight parts) + for ( i = 1; i < nbNodes; ++i ) + { + gp_Vec edge( xyz[i-1], xyz[i]); + gp_Vec n1p ( xyz[i-1], point); + double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude(); + if ( dist > tol ) + continue; + gp_Vec n2p( xyz[i], point ); + if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol ) + continue; + return false; // point is ON this part + } + return true; + } + // Node or 0D element ------------------------------------------------------------------------- + { + gp_Vec n2p ( xyz[0], point ); + return n2p.Magnitude() <= tol; } + return true; } //======================================================================= @@ -3150,7 +6799,8 @@ int SMESH_MeshEditor::SimplifyFace (const vector faceNode set nodeSet; // get simple seq of nodes - const SMDS_MeshNode* simpleNodes[ nbNodes ]; + //const SMDS_MeshNode* simpleNodes[ nbNodes ]; + vector simpleNodes( nbNodes ); int iSimple = 0, nbUnique = 0; simpleNodes[iSimple++] = faceNodes[0]; @@ -3224,6 +6874,9 @@ int SMESH_MeshEditor::SimplifyFace (const vector faceNode void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + SMESHDS_Mesh* aMesh = GetMeshDS(); TNodeNodeMap nodeNodeMap; // node to replace - new node @@ -3233,13 +6886,11 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) // Fill nodeNodeMap and elems TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin(); - for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) - { + for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) { list& nodes = *grIt; list::iterator nIt = nodes.begin(); const SMDS_MeshNode* nToKeep = *nIt; - for ( ; nIt != nodes.end(); nIt++ ) - { + for ( ++nIt; nIt != nodes.end(); nIt++ ) { const SMDS_MeshNode* nToRemove = *nIt; nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep )); if ( nToRemove != nToKeep ) { @@ -3248,33 +6899,53 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator(); - while ( invElemIt->more() ) - elems.insert( invElemIt->next() ); + while ( invElemIt->more() ) { + const SMDS_MeshElement* elem = invElemIt->next(); + elems.insert(elem); + } } } // Change element nodes or remove an element set::iterator eIt = elems.begin(); - for ( ; eIt != elems.end(); eIt++ ) - { + for ( ; eIt != elems.end(); eIt++ ) { const SMDS_MeshElement* elem = *eIt; int nbNodes = elem->NbNodes(); int aShapeId = FindShape( elem ); set nodeSet; - const SMDS_MeshNode* curNodes[ nbNodes ], *uniqueNodes[ nbNodes ]; - int iUnique = 0, iCur = 0, nbRepl = 0, iRepl [ nbNodes ]; + vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes ); + int iUnique = 0, iCur = 0, nbRepl = 0; + vector iRepl( nbNodes ); // get new seq of nodes SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) - { + 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 dependancy + stopRecur = true; + } + } + else + stopRecur = true; + } + } + // BUG 0020185: end iRepl[ nbRepl++ ] = iCur; } curNodes[ iCur ] = n; @@ -3288,8 +6959,7 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) bool isOk = true; int nbUniqueNodes = nodeSet.size(); - if ( nbNodes != nbUniqueNodes ) // some nodes stick - { + if ( nbNodes != nbUniqueNodes ) { // some nodes stick // Polygons and Polyhedral volumes if (elem->IsPoly()) { @@ -3314,19 +6984,23 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) poly_nodes[ii] = polygons_nodes[inode]; } SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes); + myLastCreatedElems.Append(newElem); if (aShapeId) aMesh->SetMeshElementOnShape(newElem, aShapeId); } aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]); - } else { + } + else { rmElemIds.push_back(elem->GetID()); } - } else if (elem->GetType() == SMDSAbs_Volume) { + } + else if (elem->GetType() == SMDSAbs_Volume) { // Polyhedral volume if (nbUniqueNodes < 4) { rmElemIds.push_back(elem->GetID()); - } else { + } + else { // each face has to be analized in order to check volume validity const SMDS_PolyhedralVolumeOfNodes* aPolyedre = static_cast( elem ); @@ -3361,11 +7035,13 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) else rmElemIds.push_back(elem->GetID()); - } else { + } + else { rmElemIds.push_back(elem->GetID()); } } - } else { + } + else { } continue; @@ -3423,6 +7099,7 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) curNodes[ 4 ], curNodes[ 5 ], curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]); + myLastCreatedElems.Append(newElem); if ( aShapeId ) aMesh->SetMeshElementOnShape( newElem, aShapeId ); // 2. : reverse a bottom @@ -3436,7 +7113,96 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) else isOk = false; break; - case 8: { //////////////////////////////////// HEXAHEDRON + case 8: { + if(elem->IsQuadratic()) { // Quadratic quadrangle + // 1 5 2 + // +---+---+ + // | | + // | | + // 4+ +6 + // | | + // | | + // +---+---+ + // 0 7 3 + isOk = false; + if(nbRepl==3) { + nbUniqueNodes = 6; + if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) { + uniqueNodes[0] = curNodes[0]; + uniqueNodes[1] = curNodes[2]; + uniqueNodes[2] = curNodes[3]; + uniqueNodes[3] = curNodes[5]; + uniqueNodes[4] = curNodes[6]; + uniqueNodes[5] = curNodes[7]; + isOk = true; + } + if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) { + uniqueNodes[0] = curNodes[0]; + uniqueNodes[1] = curNodes[1]; + uniqueNodes[2] = curNodes[2]; + uniqueNodes[3] = curNodes[4]; + uniqueNodes[4] = curNodes[5]; + uniqueNodes[5] = curNodes[6]; + isOk = true; + } + if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) { + uniqueNodes[0] = curNodes[1]; + uniqueNodes[1] = curNodes[2]; + uniqueNodes[2] = curNodes[3]; + uniqueNodes[3] = curNodes[5]; + uniqueNodes[4] = curNodes[6]; + uniqueNodes[5] = curNodes[0]; + isOk = true; + } + if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) { + uniqueNodes[0] = curNodes[0]; + uniqueNodes[1] = curNodes[1]; + uniqueNodes[2] = curNodes[3]; + uniqueNodes[3] = curNodes[4]; + uniqueNodes[4] = curNodes[6]; + uniqueNodes[5] = curNodes[7]; + isOk = true; + } + if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) { + uniqueNodes[0] = curNodes[0]; + uniqueNodes[1] = curNodes[2]; + uniqueNodes[2] = curNodes[3]; + uniqueNodes[3] = curNodes[1]; + uniqueNodes[4] = curNodes[6]; + uniqueNodes[5] = curNodes[7]; + isOk = true; + } + if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) { + uniqueNodes[0] = curNodes[0]; + uniqueNodes[1] = curNodes[1]; + uniqueNodes[2] = curNodes[2]; + uniqueNodes[3] = curNodes[4]; + uniqueNodes[4] = curNodes[5]; + uniqueNodes[5] = curNodes[7]; + isOk = true; + } + if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) { + uniqueNodes[0] = curNodes[0]; + uniqueNodes[1] = curNodes[1]; + uniqueNodes[2] = curNodes[3]; + uniqueNodes[3] = curNodes[4]; + uniqueNodes[4] = curNodes[2]; + uniqueNodes[5] = curNodes[7]; + isOk = true; + } + if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) { + uniqueNodes[0] = curNodes[0]; + uniqueNodes[1] = curNodes[1]; + uniqueNodes[2] = curNodes[2]; + uniqueNodes[3] = curNodes[4]; + uniqueNodes[4] = curNodes[5]; + uniqueNodes[5] = curNodes[3]; + isOk = true; + } + } + break; + } + //////////////////////////////////// HEXAHEDRON isOk = false; SMDS_VolumeTool hexa (elem); hexa.SetExternalNormal(); @@ -3499,6 +7265,7 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) curNodes[ind[ 3 ]], curNodes[ind[ 2 ]], curNodes[indTop[ 0 ]]); + myLastCreatedElems.Append(newElem); if ( aShapeId ) aMesh->SetMeshElementOnShape( newElem, aShapeId ); isOk = true; @@ -3564,6 +7331,7 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) curNodes[ i2 ], curNodes[ i3d ], curNodes[ i2t ]); + myLastCreatedElems.Append(newElem); if ( aShapeId ) aMesh->SetMeshElementOnShape( newElem, aShapeId ); isOk = true; @@ -3640,11 +7408,13 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities ); } - } else { + } + else { // Change regular element or polygon - aMesh->ChangeElementNodes( elem, uniqueNodes, nbUniqueNodes ); + aMesh->ChangeElementNodes( elem, & uniqueNodes[0], nbUniqueNodes ); } - } else { + } + else { // Remove invalid regular element or invalid polygon rmElemIds.push_back( elem->GetID() ); } @@ -3658,95 +7428,208 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } -//======================================================================= -//function : MergeEqualElements -//purpose : Remove all but one of elements built on the same nodes. -//======================================================================= -void SMESH_MeshEditor::MergeEqualElements() +// ======================================================== +// class : SortableElement +// purpose : allow sorting elements basing on their nodes +// ======================================================== +class SortableElement : public set { - SMESHDS_Mesh* aMesh = GetMeshDS(); +public: - SMDS_EdgeIteratorPtr eIt = aMesh->edgesIterator(); - SMDS_FaceIteratorPtr fIt = aMesh->facesIterator(); - SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator(); + SortableElement( const SMDS_MeshElement* theElem ) + { + myElem = theElem; + SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); + while ( nodeIt->more() ) + this->insert( nodeIt->next() ); + } - list< int > rmElemIds; // IDs of elems to remove + const SMDS_MeshElement* Get() const + { return myElem; } - for ( int iDim = 1; iDim <= 3; iDim++ ) { + void Set(const SMDS_MeshElement* e) const + { myElem = e; } - set< set > setOfNodeSet; - while ( 1 ) { - // get next element - const SMDS_MeshElement* elem = 0; - if ( iDim == 1 ) { - if ( eIt->more() ) elem = eIt->next(); - } else if ( iDim == 2 ) { - if ( fIt->more() ) elem = fIt->next(); - } else { - if ( vIt->more() ) elem = vIt->next(); - } - if ( !elem ) break; +private: + mutable const SMDS_MeshElement* myElem; +}; - // get elem nodes - set nodeSet; - SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); - while ( nodeIt->more() ) - nodeSet.insert( nodeIt->next() ); +//======================================================================= +//function : FindEqualElements +//purpose : Return list of group of elements built on the same nodes. +// Search among theElements or in the whole mesh if theElements is empty +//======================================================================= +void SMESH_MeshEditor::FindEqualElements(set & theElements, + TListOfListOfElementsID & theGroupsOfElementsID) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + typedef set TElemsSet; + typedef map< SortableElement, int > TMapOfNodeSet; + typedef list TGroupOfElems; + + TElemsSet elems; + if ( theElements.empty() ) + { // get all elements in the mesh + SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator(); + while ( eIt->more() ) + elems.insert( elems.end(), eIt->next()); + } + else + elems = theElements; + + vector< TGroupOfElems > arrayOfGroups; + TGroupOfElems groupOfElems; + TMapOfNodeSet mapOfNodeSet; + + TElemsSet::iterator elemIt = elems.begin(); + for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) { + const SMDS_MeshElement* curElem = *elemIt; + SortableElement SE(curElem); + int ind = -1; + // check uniqueness + pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i)); + if( !(pp.second) ) { + TMapOfNodeSet::iterator& itSE = pp.first; + ind = (*itSE).second; + arrayOfGroups[ind].push_back(curElem->GetID()); + } + else { + groupOfElems.clear(); + groupOfElems.push_back(curElem->GetID()); + arrayOfGroups.push_back(groupOfElems); + i++; + } + } + + vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin(); + for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) { + groupOfElems = *groupIt; + if ( groupOfElems.size() > 1 ) { + groupOfElems.sort(); + theGroupsOfElementsID.push_back(groupOfElems); + } + } +} + +//======================================================================= +//function : MergeElements +//purpose : In each given group, substitute all elements by the first one. +//======================================================================= + +void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + typedef list TListOfIDs; + TListOfIDs rmElemIds; // IDs of elems to remove - // check uniqueness - bool isUnique = setOfNodeSet.insert( nodeSet ).second; - if ( !isUnique ) - rmElemIds.push_back( elem->GetID() ); + SMESHDS_Mesh* aMesh = GetMeshDS(); + + TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin(); + while ( groupsIt != theGroupsOfElementsID.end() ) { + TListOfIDs& aGroupOfElemID = *groupsIt; + aGroupOfElemID.sort(); + int elemIDToKeep = aGroupOfElemID.front(); + const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep); + aGroupOfElemID.pop_front(); + TListOfIDs::iterator idIt = aGroupOfElemID.begin(); + while ( idIt != aGroupOfElemID.end() ) { + int elemIDToRemove = *idIt; + const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove); + // add the kept element in groups of removed one (PAL15188) + AddToSameGroups( elemToKeep, elemToRemove, aMesh ); + rmElemIds.push_back( elemIDToRemove ); + ++idIt; } + ++groupsIt; } Remove( rmElemIds, false ); } +//======================================================================= +//function : MergeEqualElements +//purpose : Remove all but one of elements built on the same nodes. +//======================================================================= + +void SMESH_MeshEditor::MergeEqualElements() +{ + set aMeshElements; /* empty input - + to merge equal elements in the whole mesh */ + TListOfListOfElementsID aGroupsOfElementsID; + FindEqualElements(aMeshElements, aGroupsOfElementsID); + MergeElements(aGroupsOfElementsID); +} + //======================================================================= //function : FindFaceInSet //purpose : Return a face having linked nodes n1 and n2 and which is // - not in avoidSet, // - in elemSet provided that !elemSet.empty() +// i1 and i2 optionally returns indices of n1 and n2 //======================================================================= const SMDS_MeshElement* - SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const set& elemSet, - const set& avoidSet) +SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const TIDSortedElemSet& elemSet, + const TIDSortedElemSet& avoidSet, + int* n1ind, + int* n2ind) { - SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(); - while ( invElemIt->more() ) { // loop on inverse elements of n1 + int i1, i2; + const SMDS_MeshElement* face = 0; + + SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face); + while ( invElemIt->more() && !face ) // loop on inverse faces of n1 + { const SMDS_MeshElement* elem = invElemIt->next(); - if (elem->GetType() != SMDSAbs_Face || - avoidSet.find( elem ) != avoidSet.end() ) + if (avoidSet.count( elem )) continue; - if ( !elemSet.empty() && elemSet.find( elem ) == elemSet.end()) + if ( !elemSet.empty() && !elemSet.count( elem )) continue; - // get face nodes and find index of n1 - int i1, nbN = elem->NbNodes(), iNode = 0; - const SMDS_MeshNode* faceNodes[ nbN ], *n; - SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); - while ( nIt->more() ) { - faceNodes[ iNode ] = static_cast( nIt->next() ); - if ( faceNodes[ iNode++ ] == n1 ) - i1 = iNode - 1; - } + // index of n1 + i1 = elem->GetNodeIndex( n1 ); // find a n2 linked to n1 - for ( iNode = 0; iNode < 2; iNode++ ) { - if ( iNode ) // node before n1 - n = faceNodes[ i1 == 0 ? nbN - 1 : i1 - 1 ]; - else // node after n1 - n = faceNodes[ i1 + 1 == nbN ? 0 : i1 + 1 ]; - if ( n == n2 ) - return elem; + int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes(); + for ( int di = -1; di < 2 && !face; di += 2 ) + { + i2 = (i1+di+nbN) % nbN; + if ( elem->GetNode( i2 ) == n2 ) + face = elem; + } + if ( !face && elem->IsQuadratic()) + { + // analysis for quadratic elements using all nodes + const SMDS_QuadraticFaceOfNodes* F = + static_cast(elem); + // use special nodes iterator + SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + const SMDS_MeshNode* prevN = cast2Node( anIter->next() ); + for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ ) + { + const SMDS_MeshNode* n = cast2Node( anIter->next() ); + if ( n1 == prevN && n2 == n ) + { + face = elem; + } + else if ( n2 == prevN && n1 == n ) + { + face = elem; swap( i1, i2 ); + } + prevN = n; + } } } - return 0; + if ( n1ind ) *n1ind = i1; + if ( n2ind ) *n2ind = i2; + return face; } //======================================================================= @@ -3758,24 +7641,24 @@ static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* elem) { - set elemSet, avoidSet; + TIDSortedElemSet elemSet, avoidSet; if ( elem ) avoidSet.insert ( elem ); return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet ); } //======================================================================= -//function : findFreeBorder +//function : FindFreeBorder //purpose : //======================================================================= #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge -static bool findFreeBorder (const SMDS_MeshNode* theFirstNode, - const SMDS_MeshNode* theSecondNode, - const SMDS_MeshNode* theLastNode, - list< const SMDS_MeshNode* > & theNodes, - list< const SMDS_MeshElement* > & theFaces) +bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode, + const SMDS_MeshNode* theSecondNode, + const SMDS_MeshNode* theLastNode, + list< const SMDS_MeshNode* > & theNodes, + list< const SMDS_MeshElement* >& theFaces) { if ( !theFirstNode || !theSecondNode ) return false; @@ -3788,12 +7671,12 @@ static bool findFreeBorder (const SMDS_MeshNode* theFirstNode, theNodes.push_back( theFirstNode ); theNodes.push_back( theSecondNode ); - const SMDS_MeshNode* nodes [5], *nIgnore = theFirstNode, * nStart = theSecondNode; - set < const SMDS_MeshElement* > foundElems; + //vector nodes; + const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode; + TIDSortedElemSet foundElems; bool needTheLast = ( theLastNode != 0 ); - while ( nStart != theLastNode ) - { + while ( nStart != theLastNode ) { if ( nStart == theFirstNode ) return !needTheLast; @@ -3801,16 +7684,29 @@ static bool findFreeBorder (const SMDS_MeshNode* theFirstNode, list< const SMDS_MeshElement* > curElemList; list< const SMDS_MeshNode* > nStartList; - SMDS_ElemIteratorPtr invElemIt = nStart->facesIterator(); + SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face); while ( invElemIt->more() ) { const SMDS_MeshElement* e = invElemIt->next(); - if ( e == curElem || foundElems.insert( e ).second ) - { + if ( e == curElem || foundElems.insert( e ).second ) { // get nodes - SMDS_ElemIteratorPtr nIt = e->nodesIterator(); int iNode = 0, nbNodes = e->NbNodes(); - while ( nIt->more() ) - nodes[ iNode++ ] = static_cast( nIt->next() ); + //const SMDS_MeshNode* nodes[nbNodes+1]; + vector nodes(nbNodes+1); + + if(e->IsQuadratic()) { + const SMDS_QuadraticFaceOfNodes* F = + static_cast(e); + // use special nodes iterator + SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + while( anIter->more() ) { + nodes[ iNode++ ] = anIter->next(); + } + } + else { + SMDS_ElemIteratorPtr nIt = e->nodesIterator(); + while ( nIt->more() ) + nodes[ iNode++ ] = static_cast( nIt->next() ); + } nodes[ iNode ] = nodes[ 0 ]; // check 2 links for ( iNode = 0; iNode < nbNodes; iNode++ ) @@ -3863,7 +7759,7 @@ static bool findFreeBorder (const SMDS_MeshNode* theFirstNode, cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ]; cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ]; // find one more free border - if ( ! findFreeBorder( nIgnore, nStart, theLastNode, *cNL, *cFL )) { + if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) { cNL->clear(); cFL->clear(); } @@ -3907,7 +7803,7 @@ bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1, { list< const SMDS_MeshNode* > nodes; list< const SMDS_MeshElement* > faces; - return findFreeBorder( theNode1, theNode2, theNode3, nodes, faces); + return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces); } //======================================================================= @@ -3916,16 +7812,19 @@ bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1, //======================================================================= SMESH_MeshEditor::Sew_Error - SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode, - const SMDS_MeshNode* theBordSecondNode, - const SMDS_MeshNode* theBordLastNode, - const SMDS_MeshNode* theSideFirstNode, - const SMDS_MeshNode* theSideSecondNode, - const SMDS_MeshNode* theSideThirdNode, - const bool theSideIsFreeBorder, - const bool toCreatePolygons, - const bool toCreatePolyedrs) +SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode, + const SMDS_MeshNode* theBordSecondNode, + const SMDS_MeshNode* theBordLastNode, + const SMDS_MeshNode* theSideFirstNode, + const SMDS_MeshNode* theSideSecondNode, + const SMDS_MeshNode* theSideThirdNode, + const bool theSideIsFreeBorder, + const bool toCreatePolygons, + const bool toCreatePolyedrs) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE("::SewFreeBorder()"); Sew_Error aResult = SEW_OK; @@ -3940,16 +7839,15 @@ SMESH_MeshEditor::Sew_Error // Free border 1 // -------------- - if (!findFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode, + if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode, nSide[0], eSide[0])) { MESSAGE(" Free Border 1 not found " ); aResult = SEW_BORDER1_NOT_FOUND; } - if (theSideIsFreeBorder) - { + if (theSideIsFreeBorder) { // Free border 2 // -------------- - if (!findFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode, + if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode, nSide[1], eSide[1])) { MESSAGE(" Free Border 2 not found " ); aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND ); @@ -3958,8 +7856,7 @@ SMESH_MeshEditor::Sew_Error if ( aResult != SEW_OK ) return aResult; - if (!theSideIsFreeBorder) - { + if (!theSideIsFreeBorder) { // Side 2 // -------------- @@ -3985,8 +7882,7 @@ SMESH_MeshEditor::Sew_Error gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() ); double tol2 = 1.e-8; gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 ); - if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) - { + if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) { // Need node movement. // find X and Z axes to create trsf @@ -4015,8 +7911,7 @@ SMESH_MeshEditor::Sew_Error nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz )); } } - else - { + else { // just insert nodes XYZ in the nBordXYZ map for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) { const SMDS_MeshNode* n = *nBordIt; @@ -4057,24 +7952,38 @@ SMESH_MeshEditor::Sew_Error checkedLinkIDs.clear(); gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() ); - SMDS_ElemIteratorPtr invElemIt - = prevSideNode->GetInverseElementIterator(); - while ( invElemIt->more() ) { // loop on inverse elements on the Side 2 + // loop on inverse elements of current node (prevSideNode) on the Side 2 + SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator(); + while ( invElemIt->more() ) + { const SMDS_MeshElement* elem = invElemIt->next(); - // prepare data for a loop on links, of a face or a volume + // prepare data for a loop on links coming to prevSideNode, of a face or a volume int iPrevNode, iNode = 0, nbNodes = elem->NbNodes(); - const SMDS_MeshNode* faceNodes[ nbNodes ]; + vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 ); bool isVolume = volume.Set( elem ); - const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : faceNodes; + const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0]; if ( isVolume ) // --volume hasVolumes = true; - else if ( nbNodes > 2 ) { // --face + else if ( elem->GetType()==SMDSAbs_Face ) { // --face // retrieve all face nodes and find iPrevNode - an index of the prevSideNode - SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); - while ( nIt->more() ) { - nodes[ iNode ] = static_cast( nIt->next() ); - if ( nodes[ iNode++ ] == prevSideNode ) - iPrevNode = iNode - 1; + if(elem->IsQuadratic()) { + const SMDS_QuadraticFaceOfNodes* F = + static_cast(elem); + // use special nodes iterator + SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + while( anIter->more() ) { + nodes[ iNode ] = anIter->next(); + if ( nodes[ iNode++ ] == prevSideNode ) + iPrevNode = iNode - 1; + } + } + else { + SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); + while ( nIt->more() ) { + nodes[ iNode ] = cast2Node( nIt->next() ); + if ( nodes[ iNode++ ] == prevSideNode ) + iPrevNode = iNode - 1; + } } // there are 2 links to check nbNodes = 2; @@ -4087,7 +7996,8 @@ SMESH_MeshEditor::Sew_Error if ( isVolume ) { if ( !volume.IsLinked( n, prevSideNode )) continue; - } else { + } + else { if ( iNode ) // a node before prevSideNode n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ]; else // a node after prevSideNode @@ -4097,11 +8007,12 @@ SMESH_MeshEditor::Sew_Error long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n ); bool isJustChecked = !checkedLinkIDs.insert( iLink ).second; if (!isJustChecked && - foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() ) { + foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() ) + { // test a link geometrically gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() ); bool linkIsBetter = false; - double dot, dist; + double dot = 0.0, dist = 0.0; if ( searchByDir ) { // choose most co-directed link dot = bordDir * ( nextXYZ - prevXYZ ).Normalized(); linkIsBetter = ( dot > maxDot ); @@ -4136,6 +8047,7 @@ SMESH_MeshEditor::Sew_Error // find the next border link to compare with gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() ); searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 ); + // move to next border node if sideNode is before forward border node (bordPos) while ( *nBordIt != theBordLastNode && !searchByDir ) { prevBordNode = *nBordIt; nBordIt++; @@ -4162,7 +8074,7 @@ SMESH_MeshEditor::Sew_Error TListOfListOfNodes nodeGroupsToMerge; if ( nbNodes[0] == nbNodes[1] || - ( theSideIsFreeBorder && !theSideThirdNode)) { + ( theSideIsFreeBorder && !theSideThirdNode)) { // all nodes are to be merged @@ -4172,7 +8084,7 @@ SMESH_MeshEditor::Sew_Error { nodeGroupsToMerge.push_back( list() ); nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep - nodeGroupsToMerge.back().push_back( *nIt[0] ); // tp remove + nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove } } else { @@ -4180,7 +8092,10 @@ SMESH_MeshEditor::Sew_Error // insert new nodes into the border and the side to get equal nb of segments // get normalized parameters of nodes on the borders - double param[ 2 ][ maxNbNodes ]; + //double param[ 2 ][ maxNbNodes ]; + double* param[ 2 ]; + param[0] = new double [ maxNbNodes ]; + param[1] = new double [ maxNbNodes ]; int iNode, iBord; for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders list< const SMDS_MeshNode* >& nodes = nSide[ iBord ]; @@ -4341,6 +8256,8 @@ SMESH_MeshEditor::Sew_Error } } + delete param[0]; + delete param[1]; } // end: insert new nodes MergeNodes ( nodeGroupsToMerge ); @@ -4365,19 +8282,41 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, // find indices of 2 link nodes and of the rest nodes int iNode = 0, il1, il2, i3, i4; il1 = il2 = i3 = i4 = -1; - const SMDS_MeshNode* nodes[ theFace->NbNodes() ]; - SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator(); - while ( nodeIt->more() ) { - const SMDS_MeshNode* n = static_cast( nodeIt->next() ); - if ( n == theBetweenNode1 ) - il1 = iNode; - else if ( n == theBetweenNode2 ) - il2 = iNode; - else if ( i3 < 0 ) - i3 = iNode; - else - i4 = iNode; - nodes[ iNode++ ] = n; + //const SMDS_MeshNode* nodes[ theFace->NbNodes() ]; + vector nodes( theFace->NbNodes() ); + + if(theFace->IsQuadratic()) { + const SMDS_QuadraticFaceOfNodes* F = + static_cast(theFace); + // use special nodes iterator + SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + while( anIter->more() ) { + const SMDS_MeshNode* n = anIter->next(); + if ( n == theBetweenNode1 ) + il1 = iNode; + else if ( n == theBetweenNode2 ) + il2 = iNode; + else if ( i3 < 0 ) + i3 = iNode; + else + i4 = iNode; + nodes[ iNode++ ] = n; + } + } + else { + SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator(); + while ( nodeIt->more() ) { + const SMDS_MeshNode* n = static_cast( nodeIt->next() ); + if ( n == theBetweenNode1 ) + il1 = iNode; + else if ( n == theBetweenNode2 ) + il2 = iNode; + else if ( i3 < 0 ) + i3 = iNode; + else + i4 = iNode; + nodes[ iNode++ ] = n; + } } if ( il1 < 0 || il2 < 0 || i3 < 0 ) return ; @@ -4406,25 +8345,48 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, // add nodes of face up to first node of link bool isFLN = false; - nodeIt = theFace->nodesIterator(); - while ( nodeIt->more() && !isFLN ) { - const SMDS_MeshNode* n = static_cast( nodeIt->next() ); - poly_nodes[iNode++] = n; - if (n == nodes[il1]) { - isFLN = true; - } - } - // add nodes to insert - list::iterator nIt = aNodesToInsert.begin(); - for (; nIt != aNodesToInsert.end(); nIt++) { - poly_nodes[iNode++] = *nIt; + if(theFace->IsQuadratic()) { + const SMDS_QuadraticFaceOfNodes* F = + static_cast(theFace); + // use special nodes iterator + SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + while( anIter->more() && !isFLN ) { + const SMDS_MeshNode* n = anIter->next(); + poly_nodes[iNode++] = n; + if (n == nodes[il1]) { + isFLN = true; + } + } + // add nodes to insert + list::iterator nIt = aNodesToInsert.begin(); + for (; nIt != aNodesToInsert.end(); nIt++) { + poly_nodes[iNode++] = *nIt; + } + // add nodes of face starting from last node of link + while ( anIter->more() ) { + poly_nodes[iNode++] = anIter->next(); + } } - - // add nodes of face starting from last node of link - while ( nodeIt->more() ) { - const SMDS_MeshNode* n = static_cast( nodeIt->next() ); - poly_nodes[iNode++] = n; + else { + SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator(); + while ( nodeIt->more() && !isFLN ) { + const SMDS_MeshNode* n = static_cast( nodeIt->next() ); + poly_nodes[iNode++] = n; + if (n == nodes[il1]) { + isFLN = true; + } + } + // add nodes to insert + list::iterator nIt = aNodesToInsert.begin(); + for (; nIt != aNodesToInsert.end(); nIt++) { + poly_nodes[iNode++] = *nIt; + } + // add nodes of face starting from last node of link + while ( nodeIt->more() ) { + const SMDS_MeshNode* n = static_cast( nodeIt->next() ); + poly_nodes[iNode++] = n; + } } // edit or replace the face @@ -4432,11 +8394,12 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, if (theFace->IsPoly()) { aMesh->ChangePolygonNodes(theFace, poly_nodes); - - } else { + } + else { int aShapeId = FindShape( theFace ); SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes); + myLastCreatedElems.Append(newElem); if ( aShapeId && newElem ) aMesh->SetMeshElementOnShape( newElem, aShapeId ); @@ -4445,81 +8408,196 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, return; } - // put aNodesToInsert between theBetweenNode1 and theBetweenNode2 - int nbLinkNodes = 2 + aNodesToInsert.size(); - const SMDS_MeshNode* linkNodes[ nbLinkNodes ]; - linkNodes[ 0 ] = nodes[ il1 ]; - linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ]; - list::iterator nIt = aNodesToInsert.begin(); - for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) { - linkNodes[ iNode++ ] = *nIt; - } - // decide how to split a quadrangle: compare possible variants - // and choose which of splits to be a quadrangle - int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad; - if ( nbFaceNodes == 3 ) - { - iBestQuad = nbSplits; - i4 = i3; - } - else if ( nbFaceNodes == 4 ) - { - SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio); - double aBestRate = DBL_MAX; - for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) { - i1 = 0; i2 = 1; - double aBadRate = 0; - // evaluate elements quality - for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) { - if ( iSplit == iQuad ) { - SMDS_FaceOfNodes quad (linkNodes[ i1++ ], - linkNodes[ i2++ ], - nodes[ i3 ], - nodes[ i4 ]); - aBadRate += getBadRate( &quad, aCrit ); + if( !theFace->IsQuadratic() ) { + + // put aNodesToInsert between theBetweenNode1 and theBetweenNode2 + int nbLinkNodes = 2 + aNodesToInsert.size(); + //const SMDS_MeshNode* linkNodes[ nbLinkNodes ]; + vector linkNodes( nbLinkNodes ); + linkNodes[ 0 ] = nodes[ il1 ]; + linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ]; + list::iterator nIt = aNodesToInsert.begin(); + for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) { + linkNodes[ iNode++ ] = *nIt; + } + // decide how to split a quadrangle: compare possible variants + // and choose which of splits to be a quadrangle + int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad; + if ( nbFaceNodes == 3 ) { + iBestQuad = nbSplits; + i4 = i3; + } + else if ( nbFaceNodes == 4 ) { + SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio); + double aBestRate = DBL_MAX; + for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) { + i1 = 0; i2 = 1; + double aBadRate = 0; + // evaluate elements quality + for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) { + if ( iSplit == iQuad ) { + SMDS_FaceOfNodes quad (linkNodes[ i1++ ], + linkNodes[ i2++ ], + nodes[ i3 ], + nodes[ i4 ]); + aBadRate += getBadRate( &quad, aCrit ); + } + else { + SMDS_FaceOfNodes tria (linkNodes[ i1++ ], + linkNodes[ i2++ ], + nodes[ iSplit < iQuad ? i4 : i3 ]); + aBadRate += getBadRate( &tria, aCrit ); + } } - else { - SMDS_FaceOfNodes tria (linkNodes[ i1++ ], - linkNodes[ i2++ ], - nodes[ iSplit < iQuad ? i4 : i3 ]); - aBadRate += getBadRate( &tria, aCrit ); + // choice + if ( aBadRate < aBestRate ) { + iBestQuad = iQuad; + aBestRate = aBadRate; } } - // choice - if ( aBadRate < aBestRate ) { - iBestQuad = iQuad; - aBestRate = aBadRate; - } } - } - // create new elements - SMESHDS_Mesh *aMesh = GetMeshDS(); - int aShapeId = FindShape( theFace ); + // create new elements + SMESHDS_Mesh *aMesh = GetMeshDS(); + int aShapeId = FindShape( theFace ); + + i1 = 0; i2 = 1; + for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) { + SMDS_MeshElement* newElem = 0; + if ( iSplit == iBestQuad ) + newElem = aMesh->AddFace (linkNodes[ i1++ ], + linkNodes[ i2++ ], + nodes[ i3 ], + nodes[ i4 ]); + else + newElem = aMesh->AddFace (linkNodes[ i1++ ], + linkNodes[ i2++ ], + nodes[ iSplit < iBestQuad ? i4 : i3 ]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } - i1 = 0; i2 = 1; - for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) { - SMDS_MeshElement* newElem = 0; - if ( iSplit == iBestQuad ) - newElem = aMesh->AddFace (linkNodes[ i1++ ], - linkNodes[ i2++ ], - nodes[ i3 ], - nodes[ i4 ]); - else - newElem = aMesh->AddFace (linkNodes[ i1++ ], - linkNodes[ i2++ ], - nodes[ iSplit < iBestQuad ? i4 : i3 ]); - if ( aShapeId && newElem ) - aMesh->SetMeshElementOnShape( newElem, aShapeId ); - } + // change nodes of theFace + const SMDS_MeshNode* newNodes[ 4 ]; + newNodes[ 0 ] = linkNodes[ i1 ]; + newNodes[ 1 ] = linkNodes[ i2 ]; + newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ]; + newNodes[ 3 ] = nodes[ i4 ]; + aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 ); + } // end if(!theFace->IsQuadratic()) + else { // theFace is quadratic + // we have to split theFace on simple triangles and one simple quadrangle + int tmp = il1/2; + int nbshift = tmp*2; + // shift nodes in nodes[] by nbshift + int i,j; + for(i=0; i= iBestQuad ? i3 : i4 ]; - newNodes[ 3 ] = nodes[ i4 ]; - aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 ); + int n1,n2,n3; + if(nbFaceNodes==6) { // quadratic triangle + SMDS_MeshElement* newElem = + aMesh->AddFace(nodes[3],nodes[4],nodes[5]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + if(theFace->IsMediumNode(nodes[il1])) { + // create quadrangle + newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + n1 = 1; + n2 = 2; + n3 = 3; + } + else { + // create quadrangle + newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + n1 = 0; + n2 = 1; + n3 = 5; + } + } + else { // nbFaceNodes==8 - quadratic quadrangle + SMDS_MeshElement* newElem = + aMesh->AddFace(nodes[3],nodes[4],nodes[5]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + if(theFace->IsMediumNode(nodes[il1])) { + // create quadrangle + newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + n1 = 1; + n2 = 2; + n3 = 3; + } + else { + // create quadrangle + newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + n1 = 0; + n2 = 1; + n3 = 7; + } + } + // create needed triangles using n1,n2,n3 and inserted nodes + int nbn = 2 + aNodesToInsert.size(); + //const SMDS_MeshNode* aNodes[nbn]; + vector aNodes(nbn); + aNodes[0] = nodes[n1]; + aNodes[nbn-1] = nodes[n2]; + list::iterator nIt = aNodesToInsert.begin(); + for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) { + aNodes[iNode++] = *nIt; + } + for(i=1; iAddFace(aNodes[i-1],aNodes[i],nodes[n3]); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + // remove old quadratic face + aMesh->RemoveElement(theFace); + } } //======================================================================= @@ -4530,11 +8608,12 @@ void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode const SMDS_MeshNode* theBetweenNode2, list& theNodesToInsert) { - SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(); + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume); while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1 const SMDS_MeshElement* elem = invElemIt->next(); - if (elem->GetType() != SMDSAbs_Volume) - continue; // check, if current volume has link theBetweenNode1 - theBetweenNode2 SMDS_VolumeTool aVolume (elem); @@ -4565,7 +8644,8 @@ void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode poly_nodes.push_back(*nIt); } } - } else if (faceNodes[inode] == theBetweenNode2) { + } + else if (faceNodes[inode] == theBetweenNode2) { if (faceNodes[inode + 1] == theBetweenNode1) { nbInserted = theNodesToInsert.size(); @@ -4577,7 +8657,8 @@ void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode } poly_nodes.push_back(*nIt); } - } else { + } + else { } } } @@ -4590,11 +8671,13 @@ void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode if (elem->IsPoly()) { aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); - } else { + } + else { int aShapeId = FindShape( elem ); SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities); + myLastCreatedElems.Append(newElem); if (aShapeId && newElem) aMesh->SetMeshElementOnShape(newElem, aShapeId); @@ -4603,19 +8686,326 @@ void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode } } +//======================================================================= +/*! + * \brief Convert elements contained in a submesh to quadratic + * \retval int - nb of checked elements + */ +//======================================================================= + +int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm, + SMESH_MesherHelper& theHelper, + const bool theForce3d) +{ + int nbElem = 0; + if( !theSm ) return nbElem; + + vector nbNodeInFaces; + SMDS_ElemIteratorPtr ElemItr = theSm->GetElements(); + while(ElemItr->more()) + { + nbElem++; + const SMDS_MeshElement* elem = ElemItr->next(); + if( !elem || elem->IsQuadratic() ) continue; + + int id = elem->GetID(); + int nbNodes = elem->NbNodes(); + SMDSAbs_ElementType aType = elem->GetType(); + + vector nodes (elem->begin_nodes(), elem->end_nodes()); + if ( elem->GetEntityType() == SMDSEntity_Polyhedra ) + nbNodeInFaces = static_cast( elem )->GetQuanities(); + + GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false); + + const SMDS_MeshElement* NewElem = 0; + + switch( aType ) + { + case SMDSAbs_Edge : + { + NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d); + break; + } + case SMDSAbs_Face : + { + switch(nbNodes) + { + case 3: + NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d); + break; + case 4: + NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + break; + default: + NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d); + continue; + } + break; + } + case SMDSAbs_Volume : + { + switch(nbNodes) + { + case 4: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + break; + case 5: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d); + break; + case 6: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d); + break; + case 8: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d); + break; + default: + NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d); + } + break; + } + default : + continue; + } + ReplaceElemInGroups( elem, NewElem, GetMeshDS()); + if( NewElem ) + theSm->AddElement( NewElem ); + } + return nbElem; +} + +//======================================================================= +//function : ConvertToQuadratic +//purpose : +//======================================================================= +void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d) +{ + SMESHDS_Mesh* meshDS = GetMeshDS(); + + SMESH_MesherHelper aHelper(*myMesh); + aHelper.SetIsQuadratic( true ); + + int nbCheckedElems = 0; + if ( myMesh->HasShapeToMesh() ) + { + if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh())) + { + SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false); + while ( smIt->more() ) { + SMESH_subMesh* sm = smIt->next(); + if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) { + aHelper.SetSubShape( sm->GetSubShape() ); + nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d); + } + } + } + } + int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes(); + if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes + { + SMESHDS_SubMesh *smDS = 0; + SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator(); + while(aEdgeItr->more()) + { + const SMDS_MeshEdge* edge = aEdgeItr->next(); + if(edge && !edge->IsQuadratic()) + { + int id = edge->GetID(); + const SMDS_MeshNode* n1 = edge->GetNode(0); + const SMDS_MeshNode* n2 = edge->GetNode(1); + + meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false); + + const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d); + ReplaceElemInGroups( edge, NewEdge, GetMeshDS()); + } + } + SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator(); + while(aFaceItr->more()) + { + const SMDS_MeshFace* face = aFaceItr->next(); + if(!face || face->IsQuadratic() ) continue; + + int id = face->GetID(); + int nbNodes = face->NbNodes(); + vector nodes ( face->begin_nodes(), face->end_nodes()); + + meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false); + + SMDS_MeshFace * NewFace = 0; + switch(nbNodes) + { + case 3: + NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d); + break; + case 4: + NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + break; + default: + NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d); + } + ReplaceElemInGroups( face, NewFace, GetMeshDS()); + } + vector nbNodeInFaces; + SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator(); + while(aVolumeItr->more()) + { + const SMDS_MeshVolume* volume = aVolumeItr->next(); + if(!volume || volume->IsQuadratic() ) continue; + + int id = volume->GetID(); + int nbNodes = volume->NbNodes(); + vector nodes (volume->begin_nodes(), volume->end_nodes()); + if ( volume->GetEntityType() == SMDSEntity_Polyhedra ) + nbNodeInFaces = static_cast(volume)->GetQuanities(); + + meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false); + + SMDS_MeshVolume * NewVolume = 0; + switch(nbNodes) + { + case 4: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], + nodes[3], id, theForce3d ); + break; + case 5: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], + nodes[3], nodes[4], id, theForce3d); + break; + case 6: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], + nodes[3], nodes[4], nodes[5], id, theForce3d); + break; + case 8: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d); + break; + default: + NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d); + } + ReplaceElemInGroups(volume, NewVolume, meshDS); + } + } + + if ( !theForce3d && !getenv("NO_FixQuadraticElements")) + { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion + aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh + aHelper.FixQuadraticElements(); + } +} + +//======================================================================= +/*! + * \brief Convert quadratic elements to linear ones and remove quadratic nodes + * \retval int - nb of checked elements + */ +//======================================================================= + +int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm, + SMDS_ElemIteratorPtr theItr, + const int theShapeID) +{ + int nbElem = 0; + SMESHDS_Mesh* meshDS = GetMeshDS(); + const bool notFromGroups = false; + + while( theItr->more() ) + { + const SMDS_MeshElement* elem = theItr->next(); + nbElem++; + if( elem && elem->IsQuadratic()) + { + int id = elem->GetID(); + int nbNodes = elem->NbNodes(); + vector nodes, mediumNodes; + nodes.reserve( nbNodes ); + mediumNodes.reserve( nbNodes ); + + for(int i = 0; i < nbNodes; i++) + { + const SMDS_MeshNode* n = elem->GetNode(i); + + if( elem->IsMediumNode( n ) ) + mediumNodes.push_back( n ); + else + nodes.push_back( n ); + } + if( nodes.empty() ) continue; + SMDSAbs_ElementType aType = elem->GetType(); + + //remove old quadratic element + meshDS->RemoveFreeElement( elem, theSm, notFromGroups ); + + SMDS_MeshElement * NewElem = AddElement( nodes, aType, false, id ); + ReplaceElemInGroups(elem, NewElem, meshDS); + if( theSm && NewElem ) + theSm->AddElement( NewElem ); + + // remove medium nodes + vector::iterator nIt = mediumNodes.begin(); + for ( ; nIt != mediumNodes.end(); ++nIt ) { + const SMDS_MeshNode* n = *nIt; + if ( n->NbInverseElements() == 0 ) { + if ( n->GetPosition()->GetShapeId() != theShapeID ) + meshDS->RemoveFreeNode( n, meshDS->MeshElements + ( n->GetPosition()->GetShapeId() )); + else + meshDS->RemoveFreeNode( n, theSm ); + } + } + } + } + return nbElem; +} + +//======================================================================= +//function : ConvertFromQuadratic +//purpose : +//======================================================================= +bool SMESH_MeshEditor::ConvertFromQuadratic() +{ + int nbCheckedElems = 0; + if ( myMesh->HasShapeToMesh() ) + { + if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh())) + { + SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false); + while ( smIt->more() ) { + SMESH_subMesh* sm = smIt->next(); + if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) + nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() ); + } + } + } + + int totalNbElems = + GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes(); + if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes + { + SMESHDS_SubMesh *aSM = 0; + removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 ); + } + + return true; +} + //======================================================================= //function : SewSideElements //purpose : //======================================================================= SMESH_MeshEditor::Sew_Error - SMESH_MeshEditor::SewSideElements (set& theSide1, - set& theSide2, - const SMDS_MeshNode* theFirstNode1, - const SMDS_MeshNode* theFirstNode2, - const SMDS_MeshNode* theSecondNode1, - const SMDS_MeshNode* theSecondNode2) +SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1, + TIDSortedElemSet& theSide2, + const SMDS_MeshNode* theFirstNode1, + const SMDS_MeshNode* theFirstNode2, + const SMDS_MeshNode* theSecondNode1, + const SMDS_MeshNode* theSecondNode2) { + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + MESSAGE ("::::SewSideElements()"); if ( theSide1.size() != theSide2.size() ) return SEW_DIFF_NB_OF_ELEMENTS; @@ -4642,27 +9032,44 @@ SMESH_MeshEditor::Sew_Error set * faceSetPtr[] = { &faceSet1, &faceSet2 }; set * volSetPtr[] = { &volSet1, &volSet2 }; set * nodeSetPtr[] = { &nodeSet1, &nodeSet2 }; - set * elemSetPtr[] = { &theSide1, &theSide2 }; + TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 }; int iSide, iFace, iNode; for ( iSide = 0; iSide < 2; iSide++ ) { set * nodeSet = nodeSetPtr[ iSide ]; - set * elemSet = elemSetPtr[ iSide ]; + TIDSortedElemSet * elemSet = elemSetPtr[ iSide ]; set * faceSet = faceSetPtr[ iSide ]; set * volSet = volSetPtr [ iSide ]; - set::iterator vIt, eIt; + set::iterator vIt; + TIDSortedElemSet::iterator eIt; set::iterator nIt; - // ----------------------------------------------------------- - // 1a. Collect nodes of existing faces - // and build set of face nodes in order to detect missing - // faces corresponing to sides of volumes - // ----------------------------------------------------------- + // check that given nodes belong to given elements + const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2; + const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2; + int firstIndex = -1, secondIndex = -1; + for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) { + const SMDS_MeshElement* elem = *eIt; + if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 ); + if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 ); + if ( firstIndex > -1 && secondIndex > -1 ) break; + } + if ( firstIndex < 0 || secondIndex < 0 ) { + // we can simply return until temporary faces created + return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES; + } + + // ----------------------------------------------------------- + // 1a. Collect nodes of existing faces + // and build set of face nodes in order to detect missing + // faces corresponing to sides of volumes + // ----------------------------------------------------------- set< set > setOfFaceNodeSet; // loop on the given element of a side for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) { + //const SMDS_MeshElement* elem = *eIt; const SMDS_MeshElement* elem = *eIt; if ( elem->GetType() == SMDSAbs_Face ) { faceSet->insert( elem ); @@ -4683,7 +9090,7 @@ SMESH_MeshEditor::Sew_Error // ------------------------------------------------------------------------------ for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide - SMDS_ElemIteratorPtr fIt = (*nIt)->facesIterator(); + SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face); while ( fIt->more() ) { // loop on faces sharing a node const SMDS_MeshElement* f = fIt->next(); if ( faceSet->find( f ) == faceSet->end() ) { @@ -4712,8 +9119,7 @@ SMESH_MeshEditor::Sew_Error // face does not exist // ------------------------------------------------------------------------- - if ( !volSet->empty() ) - { + if ( !volSet->empty() ) { //int nodeSetSize = nodeSet->size(); // loop on given volumes @@ -4736,13 +9142,12 @@ SMESH_MeshEditor::Sew_Error // no such a face is given but it still can exist, check it if ( nbNodes == 3 ) { aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] ); - } else if ( nbNodes == 4 ) { + } + else if ( nbNodes == 4 ) { aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] ); - } else { - vector poly_nodes (nbNodes); - for (int inode = 0; inode < nbNodes; inode++) { - poly_nodes[inode] = fNodes[inode]; - } + } + else { + vector poly_nodes ( fNodes, & fNodes[nbNodes]); aFreeFace = aMesh->FindFace(poly_nodes); } } @@ -4750,13 +9155,12 @@ SMESH_MeshEditor::Sew_Error // create a temporary face if ( nbNodes == 3 ) { aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] ); - } else if ( nbNodes == 4 ) { + } + else if ( nbNodes == 4 ) { aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] ); - } else { - vector poly_nodes (nbNodes); - for (int inode = 0; inode < nbNodes; inode++) { - poly_nodes[inode] = fNodes[inode]; - } + } + else { + vector poly_nodes ( fNodes, & fNodes[nbNodes]); aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes); } } @@ -4799,7 +9203,7 @@ SMESH_MeshEditor::Sew_Error // choose a face most close to the bary center of the opposite side gp_XYZ aBC( 0., 0., 0. ); set addedNodes; - set * elemSet2 = elemSetPtr[ 1 - iSide ]; + TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ]; eIt = elemSet2->begin(); for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) { SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator(); @@ -4836,40 +9240,40 @@ SMESH_MeshEditor::Sew_Error const SMDS_MeshElement* aFreeFace = freeFaceList.front(); faceSet->insert( aFreeFace ); // complete a node set with nodes of a found free face -// for ( iNode = 0; iNode < ; iNode++ ) -// nodeSet->insert( fNodes[ iNode ] ); + // for ( iNode = 0; iNode < ; iNode++ ) + // nodeSet->insert( fNodes[ iNode ] ); } } // loop on volumes of a side -// // complete a set of faces if new nodes in a nodeSet appeared -// // ---------------------------------------------------------- -// if ( nodeSetSize != nodeSet->size() ) { -// for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide -// SMDS_ElemIteratorPtr fIt = (*nIt)->facesIterator(); -// while ( fIt->more() ) { // loop on faces sharing a node -// const SMDS_MeshElement* f = fIt->next(); -// if ( faceSet->find( f ) == faceSet->end() ) { -// // check if all nodes are in nodeSet and -// // complete setOfFaceNodeSet if they are -// set faceNodeSet; -// SMDS_ElemIteratorPtr nodeIt = f->nodesIterator(); -// bool allInSet = true; -// while ( nodeIt->more() && allInSet ) { // loop on nodes of a face -// const SMDS_MeshNode* n = static_cast( nodeIt->next() ); -// if ( nodeSet->find( n ) == nodeSet->end() ) -// allInSet = false; -// else -// faceNodeSet.insert( n ); -// } -// if ( allInSet ) { -// faceSet->insert( f ); -// setOfFaceNodeSet.insert( faceNodeSet ); -// } -// } -// } -// } -// } + // // complete a set of faces if new nodes in a nodeSet appeared + // // ---------------------------------------------------------- + // if ( nodeSetSize != nodeSet->size() ) { + // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide + // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face); + // while ( fIt->more() ) { // loop on faces sharing a node + // const SMDS_MeshElement* f = fIt->next(); + // if ( faceSet->find( f ) == faceSet->end() ) { + // // check if all nodes are in nodeSet and + // // complete setOfFaceNodeSet if they are + // set faceNodeSet; + // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator(); + // bool allInSet = true; + // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face + // const SMDS_MeshNode* n = static_cast( nodeIt->next() ); + // if ( nodeSet->find( n ) == nodeSet->end() ) + // allInSet = false; + // else + // faceNodeSet.insert( n ); + // } + // if ( allInSet ) { + // faceSet->insert( f ); + // setOfFaceNodeSet.insert( faceNodeSet ); + // } + // } + // } + // } + // } } // Create temporary faces, if there are volumes given } // loop on sides @@ -4897,16 +9301,15 @@ SMESH_MeshEditor::Sew_Error set< long > linkIdSet; // links to process linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 )); - typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > TPairOfNodes; - list< TPairOfNodes > linkList[2]; - linkList[0].push_back( TPairOfNodes( theFirstNode1, theSecondNode1 )); - linkList[1].push_back( TPairOfNodes( theFirstNode2, theSecondNode2 )); + typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink; + list< NLink > linkList[2]; + linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 )); + linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 )); // loop on links in linkList; find faces by links and append links // of the found faces to linkList - list< TPairOfNodes >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ; - for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) - { - TPairOfNodes link[] = { *linkIt[0], *linkIt[1] }; + list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ; + for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) { + NLink link[] = { *linkIt[0], *linkIt[1] }; long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second ); if ( linkIdSet.find( linkID ) == linkIdSet.end() ) continue; @@ -4917,8 +9320,12 @@ SMESH_MeshEditor::Sew_Error // --------------------------------------------------------------- const SMDS_MeshElement* face[] = { 0, 0 }; - const SMDS_MeshNode* faceNodes[ 2 ][ 5 ]; - const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ; + //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ]; + vector fnodes1(9); + vector fnodes2(9); + //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ; + vector notLinkNodes1(6); + vector notLinkNodes2(6); int iLinkNode[2][2]; for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides const SMDS_MeshNode* n1 = link[iSide].first; @@ -4927,7 +9334,7 @@ SMESH_MeshEditor::Sew_Error set< const SMDS_MeshElement* > fMap; for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link - SMDS_ElemIteratorPtr fIt = n->facesIterator(); + SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face); while ( fIt->more() ) { // loop on faces sharing a node const SMDS_MeshElement* f = fIt->next(); if (faceSet->find( f ) != faceSet->end() && // f is in face set @@ -4938,44 +9345,113 @@ SMESH_MeshEditor::Sew_Error aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES; break; } - face[ iSide ] = f; - faceSet->erase( f ); - // get face nodes and find ones of a link - iNode = 0; - SMDS_ElemIteratorPtr nIt = f->nodesIterator(); - while ( nIt->more() ) { - const SMDS_MeshNode* n = - static_cast( nIt->next() ); - if ( n == n1 ) - iLinkNode[ iSide ][ 0 ] = iNode; - else if ( n == n2 ) - iLinkNode[ iSide ][ 1 ] = iNode; - else if ( notLinkNodes[ iSide ][ 0 ] ) - notLinkNodes[ iSide ][ 1 ] = n; - else - notLinkNodes[ iSide ][ 0 ] = n; - faceNodes[ iSide ][ iNode++ ] = n; + face[ iSide ] = f; + faceSet->erase( f ); + // get face nodes and find ones of a link + iNode = 0; + int nbl = -1; + if(f->IsPoly()) { + if(iSide==0) { + fnodes1.resize(f->NbNodes()+1); + notLinkNodes1.resize(f->NbNodes()-2); + } + else { + fnodes2.resize(f->NbNodes()+1); + notLinkNodes2.resize(f->NbNodes()-2); + } + } + if(!f->IsQuadratic()) { + SMDS_ElemIteratorPtr nIt = f->nodesIterator(); + while ( nIt->more() ) { + const SMDS_MeshNode* n = + static_cast( nIt->next() ); + if ( n == n1 ) { + iLinkNode[ iSide ][ 0 ] = iNode; + } + else if ( n == n2 ) { + iLinkNode[ iSide ][ 1 ] = iNode; + } + //else if ( notLinkNodes[ iSide ][ 0 ] ) + // notLinkNodes[ iSide ][ 1 ] = n; + //else + // notLinkNodes[ iSide ][ 0 ] = n; + else { + nbl++; + if(iSide==0) + notLinkNodes1[nbl] = n; + //notLinkNodes1.push_back(n); + else + notLinkNodes2[nbl] = n; + //notLinkNodes2.push_back(n); + } + //faceNodes[ iSide ][ iNode++ ] = n; + if(iSide==0) { + fnodes1[iNode++] = n; + } + else { + fnodes2[iNode++] = n; + } + } + } + else { // f->IsQuadratic() + const SMDS_QuadraticFaceOfNodes* F = + static_cast(f); + // use special nodes iterator + SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + while ( anIter->more() ) { + const SMDS_MeshNode* n = + static_cast( anIter->next() ); + if ( n == n1 ) { + iLinkNode[ iSide ][ 0 ] = iNode; + } + else if ( n == n2 ) { + iLinkNode[ iSide ][ 1 ] = iNode; + } + else { + nbl++; + if(iSide==0) { + notLinkNodes1[nbl] = n; + } + else { + notLinkNodes2[nbl] = n; + } + } + if(iSide==0) { + fnodes1[iNode++] = n; + } + else { + fnodes2[iNode++] = n; + } + } + } + //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ]; + if(iSide==0) { + fnodes1[iNode] = fnodes1[0]; + } + else { + fnodes2[iNode] = fnodes1[0]; } - faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ]; } } } } + // check similarity of elements of the sides if (aResult == SEW_OK && ( face[0] && !face[1] ) || ( !face[0] && face[1] )) { MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 )); - if ( nReplaceMap.size() == 2 ) // faces on input nodes not found + if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); - else + } + else { aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + } break; // do not return because it s necessary to remove tmp faces } // set nodes to merge // ------------------- - if ( face[0] && face[1] ) - { + if ( face[0] && face[1] ) { int nbNodes = face[0]->NbNodes(); if ( nbNodes != face[1]->NbNodes() ) { MESSAGE("Diff nb of face nodes"); @@ -4983,9 +9459,12 @@ SMESH_MeshEditor::Sew_Error break; // do not return because it s necessary to remove tmp faces } bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle - if ( nbNodes == 3 ) + if ( nbNodes == 3 ) { + //nReplaceMap.insert( TNodeNodeMap::value_type + // ( notLinkNodes[0][0], notLinkNodes[1][0] )); nReplaceMap.insert( TNodeNodeMap::value_type - ( notLinkNodes[0][0], notLinkNodes[1][0] )); + ( notLinkNodes1[0], notLinkNodes2[0] )); + } else { for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides // analyse link orientation in faces @@ -4999,46 +9478,56 @@ SMESH_MeshEditor::Sew_Error reverse[ iSide ] = !reverse[ iSide ]; } if ( reverse[0] == reverse[1] ) { - nReplaceMap.insert( TNodeNodeMap::value_type - ( notLinkNodes[0][0], notLinkNodes[1][0] )); - nReplaceMap.insert( TNodeNodeMap::value_type - ( notLinkNodes[0][1], notLinkNodes[1][1] )); + //nReplaceMap.insert( TNodeNodeMap::value_type + // ( notLinkNodes[0][0], notLinkNodes[1][0] )); + //nReplaceMap.insert( TNodeNodeMap::value_type + // ( notLinkNodes[0][1], notLinkNodes[1][1] )); + for(int nn=0; nn::iterator, bool > iter_isnew = linkIdSet.insert( linkID ); if ( !iter_isnew.second ) { // already in a set: no need to process linkIdSet.erase( iter_isnew.first ); } else // new in set == encountered for the first time: add { - const SMDS_MeshNode* n1 = nodes[ iNode ]; - const SMDS_MeshNode* n2 = nodes[ iNode + 1]; - linkList[0].push_back ( TPairOfNodes( n1, n2 )); - linkList[1].push_back ( TPairOfNodes( nReplaceMap[n1], nReplaceMap[n2] )); + //const SMDS_MeshNode* n1 = nodes[ iNode ]; + //const SMDS_MeshNode* n2 = nodes[ iNode + 1]; + const SMDS_MeshNode* n1 = fnodes1[ iNode ]; + const SMDS_MeshNode* n2 = fnodes1[ iNode + 1]; + linkList[0].push_back ( NLink( n1, n2 )); + linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] )); } } } // 2 faces found } // loop on link lists if ( aResult == SEW_OK && - ( linkIt[0] != linkList[0].end() || - !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) { + ( linkIt[0] != linkList[0].end() || + !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) { MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) << - " " << (faceSetPtr[1]->empty())); + " " << (faceSetPtr[1]->empty())); aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; } @@ -5058,8 +9547,7 @@ SMESH_MeshEditor::Sew_Error // loop on nodes replacement map TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt; for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ ) - if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) - { + if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) { const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first; nodeIDsToRemove.push_back( nToRemove->GetID() ); // loop on elements sharing nToRemove @@ -5068,7 +9556,7 @@ SMESH_MeshEditor::Sew_Error const SMDS_MeshElement* e = invElemIt->next(); // get a new suite of nodes: make replacement int nbReplaced = 0, i = 0, nbNodes = e->NbNodes(); - const SMDS_MeshNode* nodes[ 8 ]; + vector< const SMDS_MeshNode*> nodes( nbNodes ); SMDS_ElemIteratorPtr nIt = e->nodesIterator(); while ( nIt->more() ) { const SMDS_MeshNode* n = @@ -5084,11 +9572,733 @@ SMESH_MeshEditor::Sew_Error // elemIDsToRemove.push_back( e->GetID() ); // else if ( nbReplaced ) - aMesh->ChangeElementNodes( e, nodes, nbNodes ); + aMesh->ChangeElementNodes( e, & nodes[0], nbNodes ); } - } + } Remove( nodeIDsToRemove, true ); return aResult; } + +//================================================================================ +/*! + * \brief Find corresponding nodes in two sets of faces + * \param theSide1 - first face set + * \param theSide2 - second first face + * \param theFirstNode1 - a boundary node of set 1 + * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1 + * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1 + * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1 + * \param nReplaceMap - output map of corresponding nodes + * \retval bool - is a success or not + */ +//================================================================================ + +#ifdef _DEBUG_ +//#define DEBUG_MATCHING_NODES +#endif + +SMESH_MeshEditor::Sew_Error +SMESH_MeshEditor::FindMatchingNodes(set& theSide1, + set& theSide2, + const SMDS_MeshNode* theFirstNode1, + const SMDS_MeshNode* theFirstNode2, + const SMDS_MeshNode* theSecondNode1, + const SMDS_MeshNode* theSecondNode2, + TNodeNodeMap & nReplaceMap) +{ + set * faceSetPtr[] = { &theSide1, &theSide2 }; + + nReplaceMap.clear(); + if ( theFirstNode1 != theFirstNode2 ) + nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 )); + if ( theSecondNode1 != theSecondNode2 ) + nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 )); + + set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored + linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 )); + + list< NLink > linkList[2]; + linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 )); + linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 )); + + // loop on links in linkList; find faces by links and append links + // of the found faces to linkList + list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ; + for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) { + NLink link[] = { *linkIt[0], *linkIt[1] }; + if ( linkSet.find( link[0] ) == linkSet.end() ) + continue; + + // by links, find faces in the face sets, + // and find indices of link nodes in the found faces; + // in a face set, there is only one or no face sharing a link + // --------------------------------------------------------------- + + const SMDS_MeshElement* face[] = { 0, 0 }; + list notLinkNodes[2]; + //bool reverse[] = { false, false }; // order of notLinkNodes + int nbNodes[2]; + for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides + { + const SMDS_MeshNode* n1 = link[iSide].first; + const SMDS_MeshNode* n2 = link[iSide].second; + set * faceSet = faceSetPtr[ iSide ]; + set< const SMDS_MeshElement* > facesOfNode1; + for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link + { + // during a loop of the first node, we find all faces around n1, + // during a loop of the second node, we find one face sharing both n1 and n2 + const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link + SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face); + while ( fIt->more() ) { // loop on faces sharing a node + const SMDS_MeshElement* f = fIt->next(); + if (faceSet->find( f ) != faceSet->end() && // f is in face set + ! facesOfNode1.insert( f ).second ) // f encounters twice + { + if ( face[ iSide ] ) { + MESSAGE( "2 faces per link " ); + return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); + } + face[ iSide ] = f; + faceSet->erase( f ); + + // get not link nodes + int nbN = f->NbNodes(); + if ( f->IsQuadratic() ) + nbN /= 2; + nbNodes[ iSide ] = nbN; + list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ]; + int i1 = f->GetNodeIndex( n1 ); + int i2 = f->GetNodeIndex( n2 ); + int iEnd = nbN, iBeg = -1, iDelta = 1; + bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 ); + if ( reverse ) { + std::swap( iEnd, iBeg ); iDelta = -1; + } + int i = i2; + while ( true ) { + i += iDelta; + if ( i == iEnd ) i = iBeg + iDelta; + if ( i == i1 ) break; + nodes.push_back ( f->GetNode( i ) ); + } + } + } + } + } + // check similarity of elements of the sides + if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) { + MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 )); + if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found + return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); + } + else { + return SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + } + } + + // set nodes to merge + // ------------------- + + if ( face[0] && face[1] ) { + if ( nbNodes[0] != nbNodes[1] ) { + MESSAGE("Diff nb of face nodes"); + return SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + } +#ifdef DEBUG_MATCHING_NODES + MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID() + << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" " + << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ; +#endif + int nbN = nbNodes[0]; + { + list::iterator n1 = notLinkNodes[0].begin(); + list::iterator n2 = notLinkNodes[1].begin(); + for ( int i = 0 ; i < nbN - 2; ++i ) { +#ifdef DEBUG_MATCHING_NODES + MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() ); +#endif + nReplaceMap.insert( make_pair( *(n1++), *(n2++) )); + } + } + + // add other links of the face 1 to linkList + // ----------------------------------------- + + const SMDS_MeshElement* f0 = face[0]; + const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 ); + for ( int i = 0; i < nbN; i++ ) + { + const SMDS_MeshNode* n2 = f0->GetNode( i ); + pair< set< SMESH_TLink >::iterator, bool > iter_isnew = + linkSet.insert( SMESH_TLink( n1, n2 )); + if ( !iter_isnew.second ) { // already in a set: no need to process + linkSet.erase( iter_isnew.first ); + } + else // new in set == encountered for the first time: add + { +#ifdef DEBUG_MATCHING_NODES + MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " " + << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " ); +#endif + linkList[0].push_back ( NLink( n1, n2 )); + linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] )); + } + n1 = n2; + } + } // 2 faces found + } // loop on link lists + + return SEW_OK; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - the list of elements (edges or faces) to be replicated + The nodes for duplication could be found from these elements + \param theNodesNot - list of nodes to NOT replicate + \param theAffectedElems - the list of elements (cells and edges) to which the + replicated nodes should be associated to. + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + const TIDSortedElemSet& theAffectedElems ) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + if ( theElems.size() == 0 ) + return false; + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + if ( !aMeshDS ) + return false; + + bool res = false; + std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode; + // duplicate elements and nodes + res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true ); + // replce nodes by duplications + res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false ); + return res; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theMeshDS - mesh instance + \param theElems - the elements replicated or modified (nodes should be changed) + \param theNodesNot - nodes to NOT replicate + \param theNodeNodeMap - relation of old node to new created node + \param theIsDoubleElem - flag os to replicate element or modify + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS, + const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + std::map< const SMDS_MeshNode*, + const SMDS_MeshNode* >& theNodeNodeMap, + const bool theIsDoubleElem ) +{ + // iterate on through element and duplicate them (by nodes duplication) + bool res = false; + TIDSortedElemSet::const_iterator elemItr = theElems.begin(); + for ( ; elemItr != theElems.end(); ++elemItr ) + { + const SMDS_MeshElement* anElem = *elemItr; + if (!anElem) + continue; + + bool isDuplicate = false; + // duplicate nodes to duplicate element + std::vector newNodes( anElem->NbNodes() ); + SMDS_ElemIteratorPtr anIter = anElem->nodesIterator(); + int ind = 0; + while ( anIter->more() ) + { + + SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next(); + SMDS_MeshNode* aNewNode = aCurrNode; + if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() ) + aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ]; + else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() ) + { + // duplicate node + aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() ); + theNodeNodeMap[ aCurrNode ] = aNewNode; + myLastCreatedNodes.Append( aNewNode ); + } + isDuplicate |= (aCurrNode != aNewNode); + newNodes[ ind++ ] = aNewNode; + } + if ( !isDuplicate ) + continue; + + if ( theIsDoubleElem ) + AddElement(newNodes, anElem->GetType(), anElem->IsPoly()); + else + theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() ); + + res = true; + } + return res; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theNodes - identifiers of nodes to be doubled + \param theModifiedElems - identifiers of elements to be updated by the new (doubled) + nodes. If list of element identifiers is empty then nodes are doubled but + they not assigned to elements + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes, + const std::list< int >& theListOfModifiedElems ) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + if ( theListOfNodes.size() == 0 ) + return false; + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + if ( !aMeshDS ) + return false; + + // iterate through nodes and duplicate them + + std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode; + + std::list< int >::const_iterator aNodeIter; + for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter ) + { + int aCurr = *aNodeIter; + SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr ); + if ( !aNode ) + continue; + + // duplicate node + + const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() ); + if ( aNewNode ) + { + anOldNodeToNewNode[ aNode ] = aNewNode; + myLastCreatedNodes.Append( aNewNode ); + } + } + + // Create map of new nodes for modified elements + + std::map< SMDS_MeshElement*, vector > anElemToNodes; + + std::list< int >::const_iterator anElemIter; + for ( anElemIter = theListOfModifiedElems.begin(); + anElemIter != theListOfModifiedElems.end(); ++anElemIter ) + { + int aCurr = *anElemIter; + SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr ); + if ( !anElem ) + continue; + + vector aNodeArr( anElem->NbNodes() ); + + SMDS_ElemIteratorPtr anIter = anElem->nodesIterator(); + int ind = 0; + while ( anIter->more() ) + { + SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next(); + if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() ) + { + const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ]; + aNodeArr[ ind++ ] = aNewNode; + } + else + aNodeArr[ ind++ ] = aCurrNode; + } + anElemToNodes[ anElem ] = aNodeArr; + } + + // Change nodes of elements + + std::map< SMDS_MeshElement*, vector >::iterator + anElemToNodesIter = anElemToNodes.begin(); + for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter ) + { + const SMDS_MeshElement* anElem = anElemToNodesIter->first; + vector aNodeArr = anElemToNodesIter->second; + if ( anElem ) + aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() ); + } + + return true; +} + +namespace { + + //================================================================================ + /*! + \brief Check if element located inside shape + \return TRUE if IN or ON shape, FALSE otherwise + */ + //================================================================================ + + template + bool isInside(const SMDS_MeshElement* theElem, + Classifier& theClassifier, + const double theTol) + { + gp_XYZ centerXYZ (0, 0, 0); + SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); + while (aNodeItr->more()) + centerXYZ += SMESH_MeshEditor::TNodeXYZ(cast2Node( aNodeItr->next())); + + gp_Pnt aPnt = centerXYZ / theElem->NbNodes(); + theClassifier.Perform(aPnt, theTol); + TopAbs_State aState = theClassifier.State(); + return (aState == TopAbs_IN || aState == TopAbs_ON ); + } + + //================================================================================ + /*! + * \brief Classifier of the 3D point on the TopoDS_Face + * with interaface suitable for isInside() + */ + //================================================================================ + + struct _FaceClassifier + { + Extrema_ExtPS _extremum; + BRepAdaptor_Surface _surface; + TopAbs_State _state; + + _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT) + { + _extremum.Initialize( _surface, + _surface.FirstUParameter(), _surface.LastUParameter(), + _surface.FirstVParameter(), _surface.LastVParameter(), + _surface.Tolerance(), _surface.Tolerance() ); + } + void Perform(const gp_Pnt& aPnt, double theTol) + { + _state = TopAbs_OUT; + _extremum.Perform(aPnt); + if ( _extremum.IsDone() ) + for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol) + _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT ); + } + TopAbs_State State() const + { + return _state; + } + }; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - group of of elements (edges or faces) to be replicated + \param theNodesNot - group of nodes not to replicate + \param theShape - shape to detect affected elements (element which geometric center + located on or inside shape). + The replicated nodes should be associated to affected elements. + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + const TopoDS_Shape& theShape ) +{ + if ( theShape.IsNull() ) + return false; + + const double aTol = Precision::Confusion(); + auto_ptr< BRepClass3d_SolidClassifier> bsc3d; + auto_ptr<_FaceClassifier> aFaceClassifier; + if ( theShape.ShapeType() == TopAbs_SOLID ) + { + bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));; + bsc3d->PerformInfinitePoint(aTol); + } + else if (theShape.ShapeType() == TopAbs_FACE ) + { + aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape))); + } + + // iterates on indicated elements and get elements by back references from their nodes + TIDSortedElemSet anAffected; + TIDSortedElemSet::const_iterator elemItr = theElems.begin(); + for ( ; elemItr != theElems.end(); ++elemItr ) + { + SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr; + if (!anElem) + continue; + + SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator(); + while ( nodeItr->more() ) + { + const SMDS_MeshNode* aNode = cast2Node(nodeItr->next()); + if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() ) + continue; + SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator(); + while ( backElemItr->more() ) + { + const SMDS_MeshElement* curElem = backElemItr->next(); + if ( curElem && theElems.find(curElem) == theElems.end() && + ( bsc3d.get() ? + isInside( curElem, *bsc3d, aTol ) : + isInside( curElem, *aFaceClassifier, aTol ))) + anAffected.insert( curElem ); + } + } + } + return DoubleNodes( theElems, theNodesNot, anAffected ); +} + +//================================================================================ +/*! + * \brief Generates skin mesh (containing 2D cells) from 3D mesh + * The created 2D mesh elements based on nodes of free faces of boundary volumes + * \return TRUE if operation has been completed successfully, FALSE otherwise + */ +//================================================================================ + +bool SMESH_MeshEditor::Make2DMeshFrom3D() +{ + // iterates on volume elements and detect all free faces on them + SMESHDS_Mesh* aMesh = GetMeshDS(); + if (!aMesh) + return false; + //bool res = false; + int nbFree = 0, nbExisted = 0, nbCreated = 0; + SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator(); + while(vIt->more()) + { + const SMDS_MeshVolume* volume = vIt->next(); + SMDS_VolumeTool vTool( volume ); + vTool.SetExternalNormal(); + const bool isPoly = volume->IsPoly(); + const bool isQuad = volume->IsQuadratic(); + for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ ) + { + if (!vTool.IsFreeFace(iface)) + continue; + nbFree++; + vector nodes; + int nbFaceNodes = vTool.NbFaceNodes(iface); + const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface); + int inode = 0; + for ( ; inode < nbFaceNodes; inode += isQuad ? 2 : 1) + nodes.push_back(faceNodes[inode]); + if (isQuad) + for ( inode = 1; inode < nbFaceNodes; inode += 2) + nodes.push_back(faceNodes[inode]); + + // add new face based on volume nodes + if (aMesh->FindFace( nodes ) ) { + nbExisted++; + continue; // face already exsist + } + AddElement(nodes, SMDSAbs_Face, isPoly && iface == 1); + nbCreated++; + } + } + return ( nbFree==(nbExisted+nbCreated) ); +} + +namespace +{ + inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node) + { + if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() )) + return n; + return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() ); + } +} +//================================================================================ +/*! + * \brief Creates missing boundary elements + * \param elements - elements whose boundary is to be checked + * \param dimension - defines type of boundary elements to create + * \param group - a group to store created boundary elements in + * \param targetMesh - a mesh to store created boundary elements in + * \param toCopyElements - if true, the checked elements will be copied into the targetMesh + * \param toCopyExistingBondary - if true, not only new but also pre-existing + * boundary elements will be copied into the targetMesh + */ +//================================================================================ + +void SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements, + Bnd_Dimension dimension, + SMESH_Group* group/*=0*/, + SMESH_Mesh* targetMesh/*=0*/, + bool toCopyElements/*=false*/, + bool toCopyExistingBondary/*=false*/) +{ + SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge; + SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume; + // hope that all elements are of the same type, do not check them all + if ( !elements.empty() && (*elements.begin())->GetType() != elemType ) + throw SALOME_Exception(LOCALIZED("wrong element type")); + + if ( !targetMesh ) + toCopyElements = toCopyExistingBondary = false; + + SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh ); + SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS(); + + SMDS_VolumeTool vTool; + TIDSortedElemSet emptySet, avoidSet; + int inode; + + typedef vector TConnectivity; + + SMDS_ElemIteratorPtr eIt; + if (elements.empty()) + eIt = aMesh->elementsIterator(elemType); + else + eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() )); + + while (eIt->more()) + { + const SMDS_MeshElement* elem = eIt->next(); + const int iQuad = elem->IsQuadratic(); + + // 1. For an elem, get present bnd elements and connectivities of missing bnd elements + vector presentBndElems; + vector missingBndElems; + TConnectivity nodes; + if ( vTool.Set(elem) ) // elem is a volume ------------------------------------------ + { + vTool.SetExternalNormal(); + for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ ) + { + if (!vTool.IsFreeFace(iface)) + continue; + int nbFaceNodes = vTool.NbFaceNodes(iface); + const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface); + if ( missType == SMDSAbs_Edge ) // boundary edges + { + nodes.resize( 2+iQuad ); + for ( int i = 0; i < nbFaceNodes; i += 1+iQuad) + { + for ( int j = 0; j < nodes.size(); ++j ) + nodes[j] =nn[i+j]; + if ( const SMDS_MeshElement* edge = + aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/0)) + presentBndElems.push_back( edge ); + else + missingBndElems.push_back( nodes ); + } + } + else // boundary face + { + nodes.clear(); + for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad) + nodes.push_back( nn[inode] ); + if (iQuad) + for ( inode = 1; inode < nbFaceNodes; inode += 2) + nodes.push_back( nn[inode] ); + + if (const SMDS_MeshFace * f = aMesh->FindFace( nodes ) ) + presentBndElems.push_back( f ); + else + missingBndElems.push_back( nodes ); + } + } + } + else // elem is a face ------------------------------------------ + { + avoidSet.clear(), avoidSet.insert( elem ); + int nbNodes = elem->NbCornerNodes(); + nodes.resize( 2 /*+ iQuad*/); + for ( int i = 0; i < nbNodes; i++ ) + { + nodes[0] = elem->GetNode(i); + nodes[1] = elem->GetNode((i+1)%nbNodes); + if ( FindFaceInSet( nodes[0], nodes[1], emptySet, avoidSet)) + continue; // not free link + + //if ( iQuad ) + //nodes[2] = elem->GetNode( i + nbNodes ); + if ( const SMDS_MeshElement* edge = + aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true)) + presentBndElems.push_back( edge ); + else + missingBndElems.push_back( nodes ); + } + } + + // 2. Add missing boundary elements + if ( targetMesh != myMesh ) + // instead of making a map of nodes in this mesh and targetMesh, + // we create nodes with same IDs. We can renumber them later, if needed + for ( int i = 0; i < missingBndElems.size(); ++i ) + { + TConnectivity& srcNodes = missingBndElems[i]; + TConnectivity nodes( srcNodes.size() ); + for ( inode = 0; inode < nodes.size(); ++inode ) + nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] ); + tgtEditor.AddElement(nodes, missType, elem->IsPoly() && nodes.size()/(iQuad+1)>4); + } + else + for ( int i = 0; i < missingBndElems.size(); ++i ) + { + TConnectivity& nodes = missingBndElems[i]; + tgtEditor.AddElement(nodes, missType, elem->IsPoly() && nodes.size()/(iQuad+1)>4); + } + + // 3. Copy present boundary elements + if ( toCopyExistingBondary ) + for ( int i = 0 ; i < presentBndElems.size(); ++i ) + { + const SMDS_MeshElement* e = presentBndElems[i]; + TConnectivity nodes( e->NbNodes() ); + for ( inode = 0; inode < nodes.size(); ++inode ) + nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) ); + tgtEditor.AddElement(nodes, missType, e->IsPoly()); + // leave only missing elements in tgtEditor.myLastCreatedElems + tgtEditor.myLastCreatedElems.Remove( tgtEditor.myLastCreatedElems.Size() ); + } + } // loop on given elements + + // 4. Fill group with missing boundary elements + if ( group ) + { + if ( SMESHDS_Group* g = dynamic_cast( group->GetGroupDS() )) + for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i ) + g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 )); + } + tgtEditor.myLastCreatedElems.Clear(); + + // 5. Copy given elements + if ( toCopyElements ) + { + if (elements.empty()) + eIt = aMesh->elementsIterator(elemType); + else + eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() )); + while (eIt->more()) + { + const SMDS_MeshElement* elem = eIt->next(); + TConnectivity nodes( elem->NbNodes() ); + for ( inode = 0; inode < nodes.size(); ++inode ) + nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) ); + tgtEditor.AddElement(nodes, elemType, elem->IsPoly()); + + tgtEditor.myLastCreatedElems.Clear(); + } + } + return; +}