From f500e5a8b62895e81275f73d8fecd7a977711d78 Mon Sep 17 00:00:00 2001 From: eap Date: Mon, 20 Jan 2014 10:31:23 +0000 Subject: [PATCH] 22316: EDF 2719 SMESH: Split hexas into prisms --- .../gui/SMESH/images/split_into_prisms.png | Bin 0 -> 36064 bytes .../gui/SMESH/images/split_into_tetra.png | Bin 24205 -> 26614 bytes .../gui/SMESH/input/modifying_meshes.doc | 2 +- doc/salome/gui/SMESH/input/split_to_tetra.doc | 89 ++- idl/SMESH_MeshEditor.idl | 19 + src/SMDS/SMDS_VtkVolume.cxx | 3 +- src/SMESH/SMESH_MeshEditor.cxx | 529 ++++++++++++++++-- src/SMESH/SMESH_MeshEditor.hxx | 27 +- src/SMESHGUI/SMESHGUI.cxx | 2 +- src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx | 520 +++++++++++++++-- src/SMESHGUI/SMESHGUI_MultiEditDlg.h | 47 +- src/SMESHGUI/SMESH_msg_en.ts | 38 +- src/SMESH_I/SMESH_MeshEditor_i.cxx | 72 ++- src/SMESH_I/SMESH_MeshEditor_i.hxx | 5 + src/SMESH_SWIG/smeshBuilder.py | 88 ++- 15 files changed, 1262 insertions(+), 179 deletions(-) create mode 100644 doc/salome/gui/SMESH/images/split_into_prisms.png diff --git a/doc/salome/gui/SMESH/images/split_into_prisms.png b/doc/salome/gui/SMESH/images/split_into_prisms.png new file mode 100644 index 0000000000000000000000000000000000000000..6b9f77a2da91f5af7dbc30ff29fb29915aacbf06 GIT binary patch literal 36064 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 TwVsD3MLfKxB$J0Ue*XUe5F2Ef literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/split_into_tetra.png b/doc/salome/gui/SMESH/images/split_into_tetra.png index fce071e7df37c5914ca74ee34026bbf1fa79c973..a54f4452972e6e84792a95d1535879a537d08475 100644 GIT binary patch literal 26614 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

+5>=9ZS#Wxjd+r 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 literal 24205 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 of the selected elements.

  • \subpage reorient_faces_page "Reorient faces by vector".
  • \subpage cutting_quadrangles_page "Cut a quadrangle" into two triangles.
  • -
  • \subpage split_to_tetra_page "Split" volumic elements into tetrahedra.
  • +
  • \subpage split_to_tetra_page "Split" volumic elements into tetrahedra or prisms.
  • \subpage smoothing_page "Smooth" elements, reducung distortions in them by adjusting the locations of element corners.
  • Create an \subpage extrusion_page "extrusion" along a vector.
  • diff --git a/doc/salome/gui/SMESH/input/split_to_tetra.doc b/doc/salome/gui/SMESH/input/split_to_tetra.doc index 325c30c11..eb7fe0f26 100644 --- a/doc/salome/gui/SMESH/input/split_to_tetra.doc +++ b/doc/salome/gui/SMESH/input/split_to_tetra.doc @@ -1,53 +1,86 @@ /*! -\page split_to_tetra_page Splitting volumes into tetrahedra +\page split_to_tetra_page Splitting volumes -\n This operation allows to split volumic elements into tetrahedra. -2D mesh is modified accordingly. +\n This operation allows to split either any volumic elements into +tetrahedra or hexahedra into prisms. 2D mesh is modified accordingly. To split volumes:
      -
    1. Display a mesh or a submesh in the 3D viewer.
    2. -
    3. In the \b Modification menu select the Split into Tetrahedra item or -click "Split into Tetrahedra" button in the toolbar. +
    4. Display a mesh, a sub-mesh or a group in the 3D viewer.
    5. +
    6. In the \b Modification menu select the Split Volumes item or +click "Split Volumes" button in the toolbar. \image html split_into_tetra_icon.png -
      "Split into Tetrahedra" button
      +
      "Split Volumes" button
      The following dialog box will appear: \image html split_into_tetra.png -\par +
      +Target element type group of radio-buttons allows to select +a type of operation. If \b Tetrahedron button is checked, then the +operation will split volumes of any type into tetrahedra. +If \b Prism button is checked, then the operation will split hexahedra +into prisms, and the dialog will look as follows: + +\image html split_into_prisms.png +
        -
      • The main list contains the list of volumes. You can click on -a volume in the 3D viewer and it will be highlighted (lock Shift -keyboard button to select several volumes). Click \b Add button and -the ID of this volume will be added to the list. To remove the -selected element or elements from the list click \b Remove button. Sort -list button allows to sort the list of IDs. \b Filter button allows to -apply a definite filter to the selection of volumes. -
        Note: If you split not all adjacent non-tetrahedral volumes, your mesh becomes -non-conform.
      • +
      • The main list contains list of volumes to split. You can click on + a volume in the 3D viewer and it will be highlighted (lock Shift + keyboard button to select several volumes). Click \b Add button and + the ID of this volume will be added to the list. To remove the + selected element or elements from the list click \b Remove button. Sort + list button allows to sort the list of IDs. \b Filter button allows to + apply a definite filter to the selection of volumes. +
        Note: If you split not all adjacent non-tetrahedral + volumes, your mesh becomes non-conform.
      • Apply to all radio button allows to split all -volumes of the currently displayed mesh or submesh.
      • + volumes of the currently selected mesh.
        -
      • \b Split hexahedron +
      • Split hexahedron group allows to specify a method of + splitting hexahedra.
          -
        • Into 5 tetrahedra, Into 6 tetrahedra and Into 24 tetrahedra allows to -specify the number of tetrahedra a hexahedron will be split into. If the specified method does -not allow to get a conform mesh, a generic solution is applied: an additional node -is created at the gravity center of a hexahedron, serving an apex of tetrahedra, all quadrangle sides of the hexahedron are split into two triangles each serving a base of a new tetrahedron.
        • -
        - +
      • Into N tetrahedra/prisms allows to specify the number of + tetrahedra or prisms a hexahedron will be split into. If the + specified method does not allow to get a conform mesh, a generic + solution is applied: an additional node is created at the gravity + center of a hexahedron, serving an apex of tetrahedra, all + quadrangle sides of the hexahedron are split into two triangles each + serving a base of a new tetrahedron.
      • +
      • Facet to split group allows to specify a side (facet) of a + hexahedron to split into triangles when splitting into prisms. + The facet to split is defined by specifying a point and a direction + close to normal of the facet. The operation finds a hexahedron most + close to the specified point and splits a facet whose normal is most + close to the specified direction. Then the splitting is propagated + from that hexahedron to all adjacent hexahedra. +
          +
        • Hexa location allows to specify a start + point by which a first split hexahedron is found. + Selection button switches to selection of the element whose + barycenter will be used the start point and whose direction will be + used as a normal to facet to split into triangles. To return to + selection of volumes to split it is necessary to switch this button + off.
        • +
        • Facet normal allows to specify a direction of the + normal to hexahedron facet to split into triangles.
        • +
        +
      • All domains - if it is off the operation stops as all + hehexedra adjacent to the start hexahedron are split into + prisms. Else the operation tries to continue splitting starting from + another hexahedron closest to the Hexa location.
      • +
      -
    7. Select from a set of fields allows to choose a submesh or an -existing group whose elements will be automatically added to the -list.
    8. +
    9. Select from a set of fields allows to choose a sub-mesh or an + existing group whose elements will be added to the list as you ckick + \b Add button.
    10. Click the \b Apply or Apply and Close button to confirm the operation.
    11. diff --git a/idl/SMESH_MeshEditor.idl b/idl/SMESH_MeshEditor.idl index 43d7eac35..13107425d 100644 --- a/idl/SMESH_MeshEditor.idl +++ b/idl/SMESH_MeshEditor.idl @@ -323,6 +323,25 @@ module SMESH void SplitVolumesIntoTetra(in SMESH_IDSource elems, in short methodFlags) raises (SALOME::SALOME_Exception); + /*! + * \brief Split hexahedra into triangular prisms + * \param elems - elements to split + * \param facetToSplitNormal - normal used to find a facet of hexahedron + * to split into triangles. Location of this vector is used to + * find a hexahedron whose facets are tested using direction of this vector. + * \param methodFlags - flags passing splitting method: + * 1 - split the hexahedron into 2 prisms + * 2 - split the hexahedron into 4 prisms + * \param allDomains - if \c False, only hexahedra adjacent to one closest + * to \a facetToSplitNormal location are split, else \a facetToSplitNormal + * is used to find the facet to split in all domains present in \a elems. + */ + void SplitHexahedraIntoPrisms(in SMESH_IDSource elems, + in short methodFlags, + in SMESH::AxisStruct facetToSplitNormal, + in boolean allDomains) + raises (SALOME::SALOME_Exception); + enum Smooth_Method { LAPLACIAN_SMOOTH, CENTROIDAL_SMOOTH }; diff --git a/src/SMDS/SMDS_VtkVolume.cxx b/src/SMDS/SMDS_VtkVolume.cxx index 7acdbf37c..e65875f8b 100644 --- a/src/SMDS/SMDS_VtkVolume.cxx +++ b/src/SMDS/SMDS_VtkVolume.cxx @@ -557,7 +557,6 @@ bool SMDS_VtkVolume::IsMediumNode(const SMDS_MeshNode* node) const int SMDS_VtkVolume::NbCornerNodes() const { vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); - int nbN = grid->GetCell(myVtkID)->GetNumberOfPoints(); vtkIdType aVtkType = grid->GetCellType(myVtkID); switch (aVtkType) { @@ -568,7 +567,7 @@ int SMDS_VtkVolume::NbCornerNodes() const case VTK_TRIQUADRATIC_HEXAHEDRON: return 8; default:; } - return nbN; + return grid->GetCell(myVtkID)->GetNumberOfPoints(); } SMDSAbs_EntityType SMDS_VtkVolume::GetEntityType() const diff --git a/src/SMESH/SMESH_MeshEditor.cxx b/src/SMESH/SMESH_MeshEditor.cxx index 842b17750..8e444dec8 100644 --- a/src/SMESH/SMESH_MeshEditor.cxx +++ b/src/SMESH/SMESH_MeshEditor.cxx @@ -1603,44 +1603,110 @@ namespace const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3, thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 }; + // Methods of splitting hexahedron into prisms + + const int theHexTo4Prisms_BT[6*4+1] = // bottom-top + { + 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1 + }; + const int theHexTo4Prisms_LR[6*4+1] = // left-right + { + 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1 + }; + const int theHexTo4Prisms_FB[6*4+1] = // front-back + { + 0, 3, 8, 1, 2, 9, 3, 7, 8, 2, 6, 9, 7, 4, 8, 6, 5, 9, 4, 0, 8, 5, 1, 9, -1 + }; + + const int theHexTo2Prisms_BT_1[6*2+1] = + { + 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1 + }; + const int theHexTo2Prisms_BT_2[6*2+1] = + { + 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1 + }; + const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 }; + + const int theHexTo2Prisms_LR_1[6*2+1] = + { + 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1 + }; + const int theHexTo2Prisms_LR_2[6*2+1] = + { + 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1 + }; + const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 }; + + const int theHexTo2Prisms_FB_1[6*2+1] = + { + 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1 + }; + const int theHexTo2Prisms_FB_2[6*2+1] = + { + 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1 + }; + const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 }; + + 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; + bool hasAdjacentVol( const SMDS_MeshElement* elem, + const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const; }; struct TSplitMethod { - int _nbTetra; + int _nbSplits; + int _nbCorners; 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) {} + : _nbSplits(nbTet), _nbCorners(4), _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; + if ( _nbCorners == 4 ) + { + 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; + } + else // prism, _nbCorners == 6 + { + const int* prismConn = _connectivity; + for ( ; prismConn[0] >= 0; prismConn += 6 ) + { + if (( facet.contains( prismConn[0] ) && + facet.contains( prismConn[1] ) && + facet.contains( prismConn[2] )) + || + ( facet.contains( prismConn[3] ) && + facet.contains( prismConn[4] ) && + facet.contains( prismConn[5] ))) + return true; + } + } return false; } }; //======================================================================= /*! - * \brief return TSplitMethod for the given element + * \brief return TSplitMethod for the given element to split into tetrahedra */ //======================================================================= - TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags) + TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags) { const int iQ = vol.Element()->IsQuadratic() ? 2 : 1; @@ -1665,8 +1731,8 @@ namespace { 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 ); + if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 ); + else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 ); } else { @@ -1679,7 +1745,7 @@ namespace TTriangleFacet t023( nInd[ iQ * ( iCom )], nInd[ iQ * ( (iCom+2)%nbNodes )], nInd[ iQ * ( (iCom+3)%nbNodes )]); - if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() )) + if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() )) { triaSplits.push_back( t012 ); triaSplits.push_back( t023 ); @@ -1719,12 +1785,12 @@ namespace default: nbVariants = 0; } - for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant ) + for ( int variant = 0; variant < nbVariants && method._nbSplits == 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 ) + if ( hasAdjacentSplits && method._nbSplits > 0 ) { bool facetCreated = true; for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF ) @@ -1738,7 +1804,7 @@ namespace } } } - if ( method._nbTetra < 1 ) + if ( method._nbSplits < 1 ) { // No standard method is applicable, use a generic solution: // each facet of a volume is split into triangles and @@ -1832,7 +1898,7 @@ namespace connectivity[ connSize++ ] = baryCenInd; } } - method._nbTetra += nbTet; + method._nbSplits += nbTet; } // loop on volume faces @@ -1842,13 +1908,132 @@ namespace return method; } + //======================================================================= + /*! + * \brief return TSplitMethod to split haxhedron into prisms + */ + //======================================================================= + + TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol, + const int methodFlags, + const int facetToSplit) + { + // order of facets in HEX according to SMDS_VolumeTool::Hexa_F : + // B, T, L, B, R, F + const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2] + + if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS ) + { + static TSplitMethod to4methods[4]; // order BT, LR, FB + if ( to4methods[iF]._nbSplits == 0 ) + { + switch ( iF ) { + case 0: + to4methods[iF]._connectivity = theHexTo4Prisms_BT; + to4methods[iF]._faceBaryNode[ 0 ] = 0; + to4methods[iF]._faceBaryNode[ 1 ] = 0; + break; + case 1: + to4methods[iF]._connectivity = theHexTo4Prisms_LR; + to4methods[iF]._faceBaryNode[ 2 ] = 0; + to4methods[iF]._faceBaryNode[ 4 ] = 0; + break; + case 2: + to4methods[iF]._connectivity = theHexTo4Prisms_FB; + to4methods[iF]._faceBaryNode[ 3 ] = 0; + to4methods[iF]._faceBaryNode[ 5 ] = 0; + break; + default: return to4methods[3]; + } + to4methods[iF]._nbSplits = 4; + to4methods[iF]._nbCorners = 6; + } + return to4methods[iF]; + } + // else if ( methodFlags == HEXA_TO_2_PRISMS ) + + TSplitMethod method; + + const int iQ = vol.Element()->IsQuadratic() ? 2 : 1; + + const int nbVariants = 2, nbSplits = 2; + const int** connVariants = 0; + switch ( iF ) { + case 0: connVariants = theHexTo2Prisms_BT; break; + case 1: connVariants = theHexTo2Prisms_LR; break; + case 2: connVariants = theHexTo2Prisms_FB; break; + default: return method; + } + + // look for prisms adjacent via facetToSplit and an opposite one + for ( int is2nd = 0; is2nd < 2; ++is2nd ) + { + int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit; + int nbNodes = vol.NbFaceNodes( iFacet ) / iQ; + if ( nbNodes != 4 ) return method; + + const int* nInd = vol.GetFaceNodesIndices( iFacet ); + TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] ); + TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] ); + TTriangleFacet* t; + if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA )) + t = &t012; + else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA )) + t = &t123; + else + continue; + + // there are adjacent prism + for ( int variant = 0; variant < nbVariants; ++variant ) + { + // check method compliancy with adjacent prisms, + // the found prism facets must be among facets of prisms described by current method + method._nbSplits = nbSplits; + method._nbCorners = 6; + method._connectivity = connVariants[ variant ]; + if ( method.hasFacet( *t )) + return method; + } + } + + // No adjacent prisms. Select a variant with a best aspect ratio. + + double badness[2] = { 0, 0 }; + static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio); + const SMDS_MeshNode** nodes = vol.GetNodes(); + for ( int variant = 0; variant < nbVariants; ++variant ) + for ( int is2nd = 0; is2nd < 2; ++is2nd ) + { + int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit; + const int* nInd = vol.GetFaceNodesIndices( iFacet ); + + method._connectivity = connVariants[ variant ]; + TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] ); + TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] ); + TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123; + + SMDS_FaceOfNodes tria ( nodes[ t->_n1 ], + nodes[ t->_n2 ], + nodes[ t->_n3 ] ); + badness[ variant ] += getBadRate( &tria, aspectRatio ); + } + const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] ); + + method._nbSplits = nbSplits; + method._nbCorners = 6; + method._connectivity = connVariants[ iBetter ]; + + 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 + bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem, + const SMDSAbs_GeometryType geom ) const { // find the tetrahedron including the three nodes of facet const SMDS_MeshNode* n1 = elem->GetNode(_n1); @@ -1858,16 +2043,16 @@ namespace while ( volIt1->more() ) { const SMDS_MeshElement* v = volIt1->next(); - SMDSAbs_EntityType type = v->GetEntityType(); - if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra ) + if ( v->GetGeomType() != geom ) continue; - if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 ) + const int lastCornerInd = v->NbCornerNodes() - 1; + if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd ) continue; // medium node not allowed const int ind2 = v->GetNodeIndex( n2 ); - if ( ind2 < 0 || 3 < ind2 ) + if ( ind2 < 0 || lastCornerInd < ind2 ) continue; const int ind3 = v->GetNodeIndex( n3 ); - if ( ind3 < 0 || 3 < ind3 ) + if ( ind3 < 0 || lastCornerInd < ind3 ) continue; return true; } @@ -1900,19 +2085,23 @@ namespace } // namespace //======================================================================= -//function : SplitVolumesIntoTetra -//purpose : Split volume elements into tetrahedra. +//function : SplitVolumes +//purpose : Split volume elements into tetrahedra or prisms. +// If facet ID < 0, element is split into tetrahedra, +// else a hexahedron is split into prisms so that the given facet is +// split into triangles //======================================================================= -void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, - const int theMethodFlags) +void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems, + const int theMethodFlags) { // std-like iterator on coordinates of nodes of mesh element typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator; NXyzIterator xyzEnd; SMDS_VolumeTool volTool; - SMESH_MesherHelper helper( *GetMesh()); + SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh()); + fHelper.ToFixNodeParameters( true ); SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1); SMESHDS_SubMesh* fSubMesh = 0;//subMesh; @@ -1923,29 +2112,33 @@ void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode; double bc[3]; - TIDSortedElemSet::const_iterator elem = theElems.begin(); - for ( ; elem != theElems.end(); ++elem ) + TFacetOfElem::const_iterator elem2facet = theElems.begin(); + for ( ; elem2facet != theElems.end(); ++elem2facet ) { - if ( (*elem)->GetType() != SMDSAbs_Volume ) + const SMDS_MeshElement* elem = elem2facet->first; + const int facetToSplit = elem2facet->second; + if ( elem->GetType() != SMDSAbs_Volume ) continue; - SMDSAbs_EntityType geomType = (*elem)->GetEntityType(); + const SMDSAbs_EntityType geomType = elem->GetEntityType(); if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra ) continue; - if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange... + if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange... - TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags ); - if ( splitMethod._nbTetra < 1 ) continue; + TSplitMethod splitMethod = ( facetToSplit < 0 ? + getTetraSplitMethod( volTool, theMethodFlags ) : + getPrismSplitMethod( volTool, theMethodFlags, facetToSplit )); + if ( splitMethod._nbSplits < 1 ) continue; // find submesh to add new tetras to - if ( !subMesh || !subMesh->Contains( *elem )) + if ( !subMesh || !subMesh->Contains( elem )) { - int shapeID = FindShape( *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() ) + if ( elem->IsQuadratic() ) { iQ = 2; // add quadratic links to the helper @@ -1963,7 +2156,8 @@ void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, iQ = 1; helper.SetIsQuadratic( false ); } - vector nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() ); + vector nodes( volTool.GetNodes(), + volTool.GetNodes() + elem->NbCornerNodes() ); helper.SetElementsOnShape( true ); if ( splitMethod._baryNode ) { @@ -1991,16 +2185,25 @@ void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, } } - // make tetras - 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] ])); + // make new volumes + vector splitVols( splitMethod._nbSplits ); // splits of a volume + const int* volConn = splitMethod._connectivity; + if ( splitMethod._nbCorners == 4 ) // tetra + for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners ) + newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ], + nodes[ volConn[1] ], + nodes[ volConn[2] ], + nodes[ volConn[3] ])); + else // prisms + for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners ) + newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ], + nodes[ volConn[1] ], + nodes[ volConn[2] ], + nodes[ volConn[3] ], + nodes[ volConn[4] ], + nodes[ volConn[5] ])); - ReplaceElemInGroups( *elem, tetras, GetMeshDS() ); + ReplaceElemInGroups( elem, splitVols, GetMeshDS() ); // Split faces on sides of the split volume @@ -2029,17 +2232,37 @@ void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, map::iterator iF_n = splitMethod._faceBaryNode.find(iF); if ( iF_n != splitMethod._faceBaryNode.end() ) { + const SMDS_MeshNode *baryNode = iF_n->second; 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; + const SMDS_MeshNode *n3 = baryNode; if ( !volTool.IsFaceExternal( iF )) swap( n2, n3 ); triangles.push_back( helper.AddFace( n1,n2,n3 )); - - if ( fSubMesh && n3->getshapeId() < 1 ) - fSubMesh->AddNode( n3 ); + } + if ( fSubMesh ) // update position of the bary node on geometry + { + if ( subMesh ) + subMesh->RemoveNode( baryNode, false ); + GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() ); + const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() ); + if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE ) + { + fHelper.SetSubShape( s ); + gp_XY uv( 1e100, 1e100 ); + double distXYZ[4]; + if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode, + uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) && + uv.X() < 1e100 ) + { + // node is too far from the surface + GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] ); + const_cast( baryNode )->SetPosition + ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() ))); + } + } } } else @@ -2069,6 +2292,8 @@ void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, } } list< TTriangleFacet >::iterator facet = facets.begin(); + if ( facet == facets.end() ) + break; for ( ; facet != facets.end(); ++facet ) { if ( !volTool.IsFaceExternal( iF )) @@ -2087,11 +2312,11 @@ void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, } ReplaceElemInGroups( face, triangles, GetMeshDS() ); GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false ); - } + } // while a face based on facet nodes exists } // loop on volume faces to split them into triangles - GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false ); + GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false ); if ( geomType == SMDSEntity_TriQuad_Hexa ) { @@ -2106,6 +2331,198 @@ void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, myLastCreatedElems = newElems; } +//======================================================================= +//function : GetHexaFacetsToSplit +//purpose : For hexahedra that will be split into prisms, finds facets to +// split into triangles. Only hexahedra adjacent to the one closest +// to theFacetNormal.Location() are returned. +//param [in,out] theHexas - the hexahedra +//param [in] theFacetNormal - facet normal +//param [out] theFacets - the hexahedra and found facet IDs +//======================================================================= + +void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas, + const gp_Ax1& theFacetNormal, + TFacetOfElem & theFacets) +{ + #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): " + + // Find a hexa closest to the location of theFacetNormal + + const SMDS_MeshElement* startHex; + { + // get SMDS_ElemIteratorPtr on theHexas + typedef const SMDS_MeshElement* TValue; + typedef TIDSortedElemSet::iterator TSetIterator; + typedef SMDS::SimpleAccessor TAccesor; + typedef SMDS_MeshElement::GeomFilter TFilter; + typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter; + SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr + ( new TElemSetIter( theHexas.begin(), + theHexas.end(), + SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA ))); + + SMESH_ElementSearcher* searcher = + SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt ); + + startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume ); + + delete searcher; + + if ( !startHex ) + throw SALOME_Exception( THIS_METHOD "startHex not found"); + } + + // Select a facet of startHex by theFacetNormal + + SMDS_VolumeTool vTool( startHex ); + double norm[3], dot, maxDot = 0; + int facetID = -1; + for ( int iF = 0; iF < vTool.NbFaces(); ++iF ) + if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] )) + { + dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] ))); + if ( dot > maxDot ) + { + facetID = iF; + maxDot = dot; + } + } + if ( facetID < 0 ) + throw SALOME_Exception( THIS_METHOD "facet of startHex not found"); + + // Fill theFacets starting from facetID of startHex + + // facets used for seach of volumes adjacent to already treated ones + typedef pair< TFacetOfElem::iterator, int > TElemFacets; + typedef map< TVolumeFaceKey, TElemFacets > TFacetMap; + TFacetMap facetsToCheck; + + set facetNodes; + const SMDS_MeshElement* curHex; + + const bool allHex = ( theHexas.size() == myMesh->NbHexas() ); + + while ( startHex ) + { + // move in two directions from startHex via facetID + for ( int is2nd = 0; is2nd < 2; ++is2nd ) + { + curHex = startHex; + int curFacet = facetID; + if ( is2nd ) // do not treat startHex twice + { + vTool.Set( curHex ); + if ( vTool.IsFreeFace( curFacet, &curHex )) + { + curHex = 0; + } + else + { + vTool.GetFaceNodes( curFacet, facetNodes ); + vTool.Set( curHex ); + curFacet = vTool.GetFaceIndex( facetNodes ); + } + } + while ( curHex ) + { + // store a facet to split + if ( curHex->GetGeomType() != SMDSGeom_HEXA ) + { + theFacets.insert( make_pair( curHex, -1 )); + break; + } + if ( !allHex && !theHexas.count( curHex )) + break; + + pair< TFacetOfElem::iterator, bool > facetIt2isNew = + theFacets.insert( make_pair( curHex, curFacet )); + if ( !facetIt2isNew.second ) + break; + + // remember not-to-split facets in facetsToCheck + int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet ); + for ( int iF = 0; iF < vTool.NbFaces(); ++iF ) + { + if ( iF == curFacet && iF == oppFacet ) + continue; + TVolumeFaceKey facetKey ( vTool, iF ); + TElemFacets elemFacet( facetIt2isNew.first, iF ); + pair< TFacetMap::iterator, bool > it2isnew = + facetsToCheck.insert( make_pair( facetKey, elemFacet )); + if ( !it2isnew.second ) + facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked + } + // pass to a volume adjacent via oppFacet + if ( vTool.IsFreeFace( oppFacet, &curHex )) + { + curHex = 0; + } + else + { + // get a new curFacet + vTool.GetFaceNodes( oppFacet, facetNodes ); + vTool.Set( curHex ); + curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet ); + } + } + } // move in two directions from startHex via facetID + + // Find a new startHex by facetsToCheck + + startHex = 0; + facetID = -1; + TFacetMap::iterator fIt = facetsToCheck.begin(); + while ( !startHex && fIt != facetsToCheck.end() ) + { + const TElemFacets& elemFacets = fIt->second; + const SMDS_MeshElement* hex = elemFacets.first->first; + int splitFacet = elemFacets.first->second; + int lateralFacet = elemFacets.second; + facetsToCheck.erase( fIt ); + fIt = facetsToCheck.begin(); + + vTool.Set( hex ); + if ( vTool.IsFreeFace( lateralFacet, &curHex ) || + curHex->GetGeomType() != SMDSGeom_HEXA ) + continue; + if ( !allHex && !theHexas.count( curHex )) + continue; + + startHex = curHex; + + // find a facet of startHex to split + + set lateralNodes; + vTool.GetFaceNodes( lateralFacet, lateralNodes ); + vTool.GetFaceNodes( splitFacet, facetNodes ); + int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet ); + vTool.Set( startHex ); + lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet ); + + // look for a facet of startHex having common nodes with facetNodes + // but not lateralFacet + for ( int iF = 0; iF < vTool.NbFaces(); ++iF ) + { + if ( iF == lateralFacet ) + continue; + int nbCommonNodes = 0; + const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF ); + for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN ) + nbCommonNodes += facetNodes.count( nn[ iN ]); + + if ( nbCommonNodes >= 2 ) + { + facetID = iF; + break; + } + } + if ( facetID < 0 ) + throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found"); + } + } // while ( startHex ) +} + //======================================================================= //function : AddToSameGroups //purpose : add elemToAdd to the groups the elemInGroups belongs to diff --git a/src/SMESH/SMESH_MeshEditor.hxx b/src/SMESH/SMESH_MeshEditor.hxx index 870660a8a..8380cc900 100644 --- a/src/SMESH/SMESH_MeshEditor.hxx +++ b/src/SMESH/SMESH_MeshEditor.hxx @@ -169,11 +169,32 @@ public: SMESH::Controls::NumericalFunctorPtr theCriterion); - enum SplitVolumToTetraFlags { HEXA_TO_5 = 1, HEXA_TO_6 = 2, HEXA_TO_24 = 3 };//! TFacetOfElem; + + //!<2nd arg of SplitVolumes() + enum SplitVolumToTetraFlags { HEXA_TO_5 = 1, // split into tetrahedra + HEXA_TO_6, + HEXA_TO_24, + HEXA_TO_2_PRISMS, // split into prisms + HEXA_TO_4_PRISMS }; /*! - * \brief Split volumic elements into tetrahedra. + * \brief Split volumic elements into tetrahedra or prisms. + * If facet ID < 0, element is split into tetrahedra, + * else a hexahedron is split into prisms so that the given facet is + * split into triangles */ - void SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, const int theMethodFlags); + void SplitVolumes (const TFacetOfElem & theElems, const int theMethodFlags); + + /*! + * \brief For hexahedra that will be split into prisms, finds facets to + * split into triangles + * \param [in,out] theHexas - the hexahedra + * \param [in] theFacetNormal - facet normal + * \param [out] theFacets - the hexahedra and found facet IDs + */ + void GetHexaFacetsToSplit( TIDSortedElemSet& theHexas, + const gp_Ax1& theFacetNormal, + TFacetOfElem & theFacets); enum SmoothMethod { LAPLACIAN = 0, CENTROIDAL }; diff --git a/src/SMESHGUI/SMESHGUI.cxx b/src/SMESHGUI/SMESHGUI.cxx index 6fb58cea1..1dca53b61 100644 --- a/src/SMESHGUI/SMESHGUI.cxx +++ b/src/SMESHGUI/SMESHGUI.cxx @@ -2668,7 +2668,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) else if ( theCommandID == 410 ) aDlg = new SMESHGUI_UnionOfTrianglesDlg(this); else if ( theCommandID == 419 ) - aDlg = new SMESHGUI_CuttingIntoTetraDlg(this); + aDlg = new SMESHGUI_SplitVolumesDlg(this); else aDlg = new SMESHGUI_CuttingOfQuadsDlg(this); diff --git a/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx b/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx index d48e740f5..a252ce333 100755 --- a/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx @@ -36,9 +36,12 @@ #include "SMESHGUI_SpinBox.h" #include "SMESHGUI_MeshEditPreview.h" -#include -#include -#include +#include "SMDS_Mesh.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMDS_VolumeTool.hxx" +#include "SMESH_Actor.h" +#include "SMESH_MeshAlgos.hxx" +#include "SMESH_TypeFilter.hxx" // SALOME GUI includes #include @@ -58,9 +61,11 @@ #include // OCCT includes -#include +#include #include +#include #include +#include // VTK includes #include @@ -103,9 +108,10 @@ // Purpose : Constructor //======================================================================= SMESHGUI_MultiEditDlg -::SMESHGUI_MultiEditDlg(SMESHGUI* theModule, - const int theMode, - const bool the3d2d): +::SMESHGUI_MultiEditDlg(SMESHGUI* theModule, + const int theMode, + const bool the3d2d, + bool theDoInit): SMESHGUI_PreviewDlg(theModule), mySelector(SMESH::GetViewWindow(theModule)->GetSelector()), mySelectionMgr(SMESH::GetSelectionMgr(theModule)), @@ -128,7 +134,8 @@ SMESHGUI_MultiEditDlg aDlgLay->addWidget(aMainFrame); aDlgLay->addWidget(aBtnFrame); - Init(); + if ( theDoInit ) + Init(); } //======================================================================= @@ -162,6 +169,7 @@ QWidget* SMESHGUI_MultiEditDlg::createMainFrame (QWidget* theParent, const bool QRadioButton* aFaceRb = new QRadioButton(tr("SMESH_FACE"), aEntityTypeGrp); QRadioButton* aVolumeRb = new QRadioButton(tr("SMESH_VOLUME"), aEntityTypeGrp); + aEntityLayout->addWidget(aFaceRb); aEntityLayout->addWidget(aVolumeRb); @@ -226,9 +234,6 @@ QWidget* SMESHGUI_MultiEditDlg::createMainFrame (QWidget* theParent, const bool myComboBoxFunctor->addItem(tr("ASPECTRATIO_ELEMENTS")); myComboBoxFunctor->addItem(tr("MINIMUMANGLE_ELEMENTS")); myComboBoxFunctor->addItem(tr("SKEW_ELEMENTS")); - //myComboBoxFunctor->addItem(tr("AREA_ELEMENTS")); - //myComboBoxFunctor->addItem(tr("LENGTH2D_EDGES")); // for existing elements only - //myComboBoxFunctor->addItem(tr("MULTI2D_BORDERS")); // for existing elements only myComboBoxFunctor->setCurrentIndex(0); aCriterionLayout->addWidget(myChoiceWidget); @@ -305,7 +310,7 @@ QWidget* SMESHGUI_MultiEditDlg::createButtonFrame (QWidget* theParent) bool SMESHGUI_MultiEditDlg::isValid (const bool /*theMess*/) { return (!myMesh->_is_nil() && - (myListBox->count() > 0 || (myToAllChk->isChecked()/* && myActor*/))); + (myListBox->count() > 0 || (myToAllChk->isChecked() && nbElemsInMesh() > 0))); } //======================================================================= @@ -1071,6 +1076,11 @@ bool SMESHGUI_ChangeOrientationDlg::process (SMESH::SMESH_MeshEditor_ptr theEdit return theEditor->ReorientObject( obj ); } +int SMESHGUI_ChangeOrientationDlg::nbElemsInMesh() +{ + return ( myFilterType = SMESH::FaceFilter ) ? myMesh->NbFaces() : myMesh->NbVolumes(); +} + /*! * Class : SMESHGUI_UnionOfTrianglesDlg * Description : Construction of quadrangles by automatic association of triangles @@ -1163,39 +1173,44 @@ bool SMESHGUI_UnionOfTrianglesDlg::process (SMESH::SMESH_MeshEditor_ptr theEdito ok = theEditor->TriToQuadObject(obj, aCriterion, aMaxAngle); return ok; } + +int SMESHGUI_UnionOfTrianglesDlg::nbElemsInMesh() +{ + return myMesh->NbTriangles(); +} void SMESHGUI_UnionOfTrianglesDlg::onDisplaySimulation( bool toDisplayPreview ) { if ( myPreviewCheckBox->isChecked() && toDisplayPreview ) { if ( isValid( true ) ) { try{ - SUIT_OverrideCursor aWaitCursor; - // get Ids of elements - SMESH::SMESH_IDSource_var obj; - SMESH::long_array_var anElemIds = getIds( obj ); + SUIT_OverrideCursor aWaitCursor; + // get Ids of elements + SMESH::SMESH_IDSource_var obj; + SMESH::long_array_var anElemIds = getIds( obj ); - SMESH::NumericalFunctor_var aCriterion = getNumericalFunctor(); - SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + SMESH::NumericalFunctor_var aCriterion = getNumericalFunctor(); + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); - double aMaxAngle = myMaxAngleSpin->GetValue() * M_PI / 180.0; + double aMaxAngle = myMaxAngleSpin->GetValue() * M_PI / 180.0; - if ( CORBA::is_nil( obj ) ) - aMeshEditor->TriToQuad( anElemIds.inout(), aCriterion, aMaxAngle ); - else - aMeshEditor->TriToQuadObject( obj, aCriterion, aMaxAngle ); + if ( CORBA::is_nil( obj ) ) + aMeshEditor->TriToQuad( anElemIds.inout(), aCriterion, aMaxAngle ); + else + aMeshEditor->TriToQuadObject( obj, aCriterion, aMaxAngle ); - SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); - vtkProperty* aProp = vtkProperty::New(); - aProp->SetRepresentationToWireframe(); - aProp->SetColor( 250, 0, 250 ); - aProp->SetLineWidth( SMESH::GetFloat( "SMESH:element_width", 1 ) + 3 ); - mySimulation->GetActor()->SetProperty( aProp ); - aProp->Delete(); + vtkProperty* aProp = vtkProperty::New(); + aProp->SetRepresentationToWireframe(); + aProp->SetColor( 250, 0, 250 ); + aProp->SetLineWidth( SMESH::GetFloat( "SMESH:element_width", 1 ) + 3 ); + mySimulation->GetActor()->SetProperty( aProp ); + aProp->Delete(); - mySimulation->SetData( aMeshPreviewStruct._retn() ); + mySimulation->SetData( aMeshPreviewStruct._retn() ); } catch ( ... ) { - hidePreview(); + hidePreview(); } } else { hidePreview(); @@ -1279,6 +1294,12 @@ bool SMESHGUI_CuttingOfQuadsDlg::process (SMESH::SMESH_MeshEditor_ptr theEditor, return hasObj ? theEditor->QuadToTriObject(obj, aCrit) : theEditor->QuadToTri(theIds, aCrit); } +int SMESHGUI_CuttingOfQuadsDlg::nbElemsInMesh() +{ + return myMesh->NbQuadrangles(); +} + + void SMESHGUI_CuttingOfQuadsDlg::onCriterionRB() { if (myGroupChoice->checkedId() == 2) // Use numeric functor @@ -1488,51 +1509,147 @@ void SMESHGUI_CuttingOfQuadsDlg::displayPreview() } /*! - * Class : SMESHGUI_CuttingIntoTetraDlg - * Description : Modification of orientation of faces + * Class : SMESHGUI_SplitVolumesDlg + * Description : Spliter of volumes into tetrahedra or prisms */ -SMESHGUI_CuttingIntoTetraDlg::SMESHGUI_CuttingIntoTetraDlg(SMESHGUI* theModule) - : SMESHGUI_MultiEditDlg(theModule, SMESH::VolumeFilter, false) +SMESHGUI_SplitVolumesDlg::SMESHGUI_SplitVolumesDlg(SMESHGUI* theModule) + : SMESHGUI_MultiEditDlg(theModule, SMESH::VolumeFilter, /*the3d2d=*/true, /*doInit=*/false) { setWindowTitle(tr("CAPTION")); myHelpFileName = "split_to_tetra_page.html"; myEntityType = 1; + myCellSize = -1.; + + // Facet selection group + + myFacetSelGrp = new QGroupBox(tr("FACET_TO_SPLIT"), myCriterionGrp->parentWidget()); + QGridLayout* facetSelLayout = new QGridLayout( myFacetSelGrp ); + facetSelLayout->setMargin(MARGIN); + facetSelLayout->setSpacing(SPACING); + + QLabel* pointLbl = new QLabel( tr("START_POINT"), myFacetSelGrp); + QLabel* normalLbl = new QLabel( tr("FACET_NORMAL"), myFacetSelGrp); + myFacetSelBtn = new QPushButton( mySubmeshBtn->icon(), "", myFacetSelGrp ); + myFacetSelBtn->setCheckable( true ); + QLabel* XLbl = new QLabel( tr("SMESH_X"), myFacetSelGrp); + QLabel* YLbl = new QLabel( tr("SMESH_Y"), myFacetSelGrp); + QLabel* ZLbl = new QLabel( tr("SMESH_Z"), myFacetSelGrp); + QLabel* dXLbl = new QLabel( tr("SMESH_DX"), myFacetSelGrp); + QLabel* dYLbl = new QLabel( tr("SMESH_DY"), myFacetSelGrp); + QLabel* dZLbl = new QLabel( tr("SMESH_DZ"), myFacetSelGrp); + QPushButton* axisBtn[3]; + for ( int i = 0; i < 3; ++i ) + { + myPointSpin[i] = new SMESHGUI_SpinBox( myFacetSelGrp ); + myDirSpin [i] = new SMESHGUI_SpinBox( myFacetSelGrp ); + myPointSpin[i]->RangeStepAndValidator( -1e10, 1e10, 10 ); + myDirSpin [i]->RangeStepAndValidator( -1., 1., 0.1 ); + myPointSpin[i]->SetValue(0.); + myDirSpin [i]->SetValue(0.); + myAxisBtn [i] = new QPushButton( QString("|| O") + char('X'+i ), myFacetSelGrp); + } + myDirSpin[2]->SetValue(1.); + + myAllDomainsChk = new QCheckBox( tr("ALL_DOMAINS"), mySelGrp ); + + facetSelLayout->addWidget( pointLbl, 0, 0 ); + facetSelLayout->addWidget( myFacetSelBtn, 0, 1 ); + facetSelLayout->addWidget( XLbl, 0, 2 ); + facetSelLayout->addWidget( myPointSpin[0],0, 3 ); + facetSelLayout->addWidget( YLbl, 0, 4 ); + facetSelLayout->addWidget( myPointSpin[1],0, 5 ); + facetSelLayout->addWidget( ZLbl, 0, 6 ); + facetSelLayout->addWidget( myPointSpin[2],0, 7 ); + + facetSelLayout->addWidget( normalLbl, 1, 0 ); + facetSelLayout->addWidget( dXLbl, 1, 2 ); + facetSelLayout->addWidget( myDirSpin[0], 1, 3 ); + facetSelLayout->addWidget( dYLbl, 1, 4 ); + facetSelLayout->addWidget( myDirSpin[1], 1, 5 ); + facetSelLayout->addWidget( dZLbl, 1, 6 ); + facetSelLayout->addWidget( myDirSpin[2], 1, 7 ); + + facetSelLayout->addWidget( myAxisBtn[0], 2, 2, 1, 2 ); + facetSelLayout->addWidget( myAxisBtn[1], 2, 4, 1, 2 ); + facetSelLayout->addWidget( myAxisBtn[2], 2, 6, 1, 2 ); + + myCriterionGrp->layout()->addWidget( myFacetSelGrp ); + myCriterionGrp->layout()->addWidget( myAllDomainsChk ); + //myChoiceWidget->layout()->addWidget( myAllDomainsChk ); + + connect( myFacetSelBtn, SIGNAL(clicked(bool)), SLOT(onFacetSelection(bool)) ); + for ( int i = 0; i < 3; ++i ) + { + connect( myAxisBtn [i], SIGNAL(clicked()), SLOT(onSetDir()) ); + connect( myPointSpin[i], SIGNAL(valueChanged (const QString&)), + this, SLOT (updateNormalPreview(const QString&)) ); + connect( myDirSpin [i], SIGNAL(valueChanged (const QString&)), + this, SLOT (updateNormalPreview(const QString&)) ); + } + if ( myEntityTypeGrp ) + { + myEntityTypeGrp->button(0)->setText( tr("SMESH_TETRAS")); + myEntityTypeGrp->button(1)->setText( tr("SMESH_PRISM")); + if ( QGroupBox* gb = qobject_cast< QGroupBox* >( myEntityTypeGrp->button(0)->parent() )) + gb->setTitle( tr("TARGET_ELEM_TYPE")); + } myToAllChk->setChecked( true ); //aplly to the whole mesh by default bool hasHexa = true;//myMesh->_is_nil() ? false : myMesh->NbHexas(); - if ( hasHexa ) { - myGroupChoice->button(0)->setText( tr("SPLIT_HEX_TO_5_TETRA")); - myGroupChoice->button(1)->setText( tr("SPLIT_HEX_TO_6_TETRA")); - myGroupChoice->button(2)->setText( tr("SPLIT_HEX_TO_24_TETRA")); - myCriterionGrp->setTitle( tr("SPLIT_METHOD")); myCriterionGrp->show(); myComboBoxFunctor->hide(); myChoiceWidget->show(); } - setSelectionMode(); - updateButtons(); + + on3d2dChanged( 0 ); + Init(); } -SMESHGUI_CuttingIntoTetraDlg::~SMESHGUI_CuttingIntoTetraDlg() +SMESHGUI_SplitVolumesDlg::~SMESHGUI_SplitVolumesDlg() { } -bool SMESHGUI_CuttingIntoTetraDlg::process (SMESH::SMESH_MeshEditor_ptr theEditor, - const SMESH::long_array& theIds, - SMESH::SMESH_IDSource_ptr theObj) +bool SMESHGUI_SplitVolumesDlg::process (SMESH::SMESH_MeshEditor_ptr theEditor, + const SMESH::long_array& theIds, + SMESH::SMESH_IDSource_ptr theObj) { SMESH::SMESH_IDSource_wrap obj = theObj; if ( CORBA::is_nil( obj )) - obj = theEditor->MakeIDSource( theIds, myEntityType ? SMESH::VOLUME : SMESH::FACE ); + obj = theEditor->MakeIDSource( theIds, SMESH::VOLUME ); else obj->Register(); try { - theEditor->SplitVolumesIntoTetra( obj, myGroupChoice->checkedId()+1 ); + if ( isIntoPrisms() ) + { + QStringList aParameters; + aParameters << myPointSpin[0]->text(); + aParameters << myPointSpin[1]->text(); + aParameters << myPointSpin[2]->text(); + aParameters << myDirSpin[0]->text(); + aParameters << myDirSpin[1]->text(); + aParameters << myDirSpin[2]->text(); + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + + SMESH::AxisStruct_var axis = new SMESH::AxisStruct; + axis->x = myPointSpin[0]->GetValue(); + axis->y = myPointSpin[1]->GetValue(); + axis->z = myPointSpin[2]->GetValue(); + axis->vx = myDirSpin[0]->GetValue(); + axis->vy = myDirSpin[1]->GetValue(); + axis->vz = myDirSpin[2]->GetValue(); + + theEditor->SplitHexahedraIntoPrisms( obj, myGroupChoice->checkedId()+1, + axis, myAllDomainsChk->isChecked() ); + } + else + { + theEditor->SplitVolumesIntoTetra( obj, myGroupChoice->checkedId()+1 ); + } } catch ( const SALOME::SALOME_Exception& S_ex ) { SalomeApp_Tools::QtCatchCorbaException( S_ex ); @@ -1543,3 +1660,308 @@ bool SMESHGUI_CuttingIntoTetraDlg::process (SMESH::SMESH_MeshEditor_ptr theEdito } return true; } + +int SMESHGUI_SplitVolumesDlg::nbElemsInMesh() +{ + return isIntoPrisms() ? myMesh->NbHexas() : myMesh->NbVolumes() - myMesh->NbTetras(); +} + +bool SMESHGUI_SplitVolumesDlg::isIntoPrisms() +{ + return ( myEntityTypeGrp->checkedId() == 1 ); +} + +//================================================================================ +/*! + * \brief Slot called when a target element type changes + */ +//================================================================================ + +void SMESHGUI_SplitVolumesDlg::on3d2dChanged(int isPrism) +{ + if ( isPrism ) + { + myFacetSelGrp->show(); + myAllDomainsChk->show(); + myGroupChoice->button(2)->hide(); + myGroupChoice->button(0)->setText( tr("SPLIT_HEX_TO_2_PRISMS")); + myGroupChoice->button(1)->setText( tr("SPLIT_HEX_TO_4_PRISMS")); + } + else + { + myFacetSelGrp->hide(); + myAllDomainsChk->hide(); + myGroupChoice->button(2)->show(); + myGroupChoice->button(0)->setText( tr("SPLIT_HEX_TO_5_TETRA")); + myGroupChoice->button(1)->setText( tr("SPLIT_HEX_TO_6_TETRA")); + myGroupChoice->button(2)->setText( tr("SPLIT_HEX_TO_24_TETRA")); + } + SMESHGUI_MultiEditDlg::on3d2dChanged( !myEntityType ); + myEntityType = 1; // == VOLUME +} + +//================================================================================ +/*! + * \brief Set selection mode + */ +//================================================================================ + +void SMESHGUI_SplitVolumesDlg::setSelectionMode() +{ + if ( myBusy || !isEnabled() ) return; + + SMESH::RemoveFilters(); + + mySelectionMgr->clearFilters(); + + if (mySubmeshChk->isChecked()) { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + mySelectionMgr->installFilter(new SMESH_TypeFilter(SMESH::SUBMESH)); + myFacetSelBtn->setChecked( false ); + } + else if (myGroupChk->isChecked()) { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + mySelectionMgr->installFilter(new SMESH_TypeFilter(SMESH::GROUP)); + myFacetSelBtn->setChecked( false ); + } + + if ( myFacetSelBtn->isChecked() ) + { + // facet selection - select any element + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( CellSelection ); + myFilterType = SMESH::AllElementsFilter; + } + else + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( VolumeSelection ); + if ( isIntoPrisms() ) + { + SMESH::SetFilter(new SMESHGUI_VolumeShapeFilter( SMDSGeom_HEXA )); + myFilterType = SMESHGUI_VolumeShapeFilter::GetId( SMDSGeom_HEXA ); + } + else // to tetrahedra + { + SMESH::SetFilter(new SMESHGUI_VolumesFilter()); + myFilterType = SMESH::VolumeFilter; + } + } +} + +//================================================================================ +/*! + * \brief SLOT called when selection changed + */ +//================================================================================ + +void SMESHGUI_SplitVolumesDlg::onSelectionDone() +{ + if (myBusy || !isEnabled()) return; + + if ( !myFacetSelBtn->isChecked() ) + { + SMESHGUI_MultiEditDlg::onSelectionDone(); + } + else // set point and normal by a selected element + { + const SALOME_ListIO& aList = mySelector->StoredIObjects(); + int nbSel = aList.Extent(); + if (nbSel > 0) + { + Handle(SALOME_InteractiveObject) anIO = aList.First(); + + myActor = SMESH::FindActorByEntry( anIO->getEntry() ); + + SMESH::SMESH_Mesh_var aSelMesh = SMESH::GetMeshByIO(anIO); + if (!aSelMesh->_is_nil()) + myMesh = aSelMesh; + + TColStd_IndexedMapOfInteger aMapIndex; + mySelector->GetIndex( anIO, aMapIndex ); + if ( !aMapIndex.IsEmpty() ) + showFacetByElement( aMapIndex(1) ); + else if ( myCellSize < 0 ) + showFacetByElement( 1 ); + } + updateButtons(); + } +} + +//================================================================================ +/*! + * \brief Show facet normal by a selected element + */ +//================================================================================ + +void SMESHGUI_SplitVolumesDlg::showFacetByElement( int elemID ) +{ + if ( !isIntoPrisms() || !myActor ) + { + mySimulation->SetVisibility( false ); + return; + } + SMDS_Mesh* mesh = myActor->GetObject()->GetMesh(); + const SMDS_MeshElement* elem = mesh->FindElement( elemID ); + if ( !elem ) return; + + // set point XYZ by the element barycenter + gp_XYZ bc( 0,0,0 ); + Bnd_B3d bbox; + SMDS_NodeIteratorPtr nIt = elem->nodeIterator(); + vector< const SMDS_MeshNode* > nodes; + nodes.reserve( elem->NbNodes() ); + while ( nIt->more() ) + { + nodes.push_back( nIt->next() ); + gp_XYZ p = SMESH_TNodeXYZ( nodes.back() ); + bc += p; + bbox.Add( p ); + } + bc /= nodes.size(); + + myPointSpin[0]->SetValue( bc.X() ); + myPointSpin[1]->SetValue( bc.Y() ); + myPointSpin[2]->SetValue( bc.Z() ); + + // set size + myCellSize = sqrt( bbox.SquareExtent() ); + + // set normal and size + gp_XYZ norm; + switch ( elem->GetType()) + { + case SMDSAbs_Edge: + { + norm = SMESH_TNodeXYZ( nodes[1] ) - SMESH_TNodeXYZ( nodes[0] ); + break; + } + case SMDSAbs_Face: + { + if ( !SMESH_MeshAlgos::FaceNormal( elem, norm, /*normalized=*/false )) + return; + break; + } + case SMDSAbs_Volume: + { + SMDS_VolumeTool vTool( elem ); + vTool.SetExternalNormal(); + bool freeFacetFound = false; + double n[3]; + for ( int i = 0; i < vTool.NbFaces() && !freeFacetFound; ++i ) + if (( freeFacetFound = vTool.IsFreeFace( i ))) + vTool.GetFaceNormal( i, n[0], n[1], n[2] ); + if ( !freeFacetFound ) + vTool.GetFaceNormal( 0, n[0], n[1], n[2] ); + norm.SetCoord( n[0], n[1], n[2] ); + break; + } + default: return; + } + + double size = norm.Modulus(); + if ( size < 1e-20 ) + return; + norm /= size; + + myDirSpin[0]->SetValue( norm.X() ); + myDirSpin[1]->SetValue( norm.Y() ); + myDirSpin[2]->SetValue( norm.Z() ); + + if ( myCellSize > 0. ) + updateNormalPreview(); +} + +//================================================================================ +/*! + * \brief SLOT called when a point or a normal changes + */ +//================================================================================ + +void SMESHGUI_SplitVolumesDlg::updateNormalPreview(const QString&) +{ + if ( myCellSize < 0. ) + { + showFacetByElement( 1 ); + return; + } + + gp_Pnt point ( myPointSpin[0]->GetValue(), + myPointSpin[1]->GetValue(), + myPointSpin[2]->GetValue() ); + gp_XYZ norm ( myDirSpin[0]->GetValue(), + myDirSpin[1]->GetValue(), + myDirSpin[2]->GetValue() ); + if ( norm.Modulus() < 1e-20 ) + return; + + vtkUnstructuredGrid* grid = mySimulation->GetGrid(); + + // Initialize the preview mesh of an arrow + if ( grid->GetNumberOfPoints() == 0 ) + { + mySimulation->SetArrowShapeAndNb( /*nb=*/1, /*hLen=*/0.3, /*R=*/0.1, /*start=*/0 ); + } + + // Compute new coordinates of the grid according to the dialog controls + + gp_Ax1 axis( point, norm ); + mySimulation->SetArrows( &axis, 4 * myCellSize ); + mySimulation->SetVisibility(true); +} + +//================================================================================ +/*! + * \brief Slot called when facet selection button is clicked + */ +//================================================================================ + +void SMESHGUI_SplitVolumesDlg::onFacetSelection(bool isFacetSelection) +{ + setSelectionMode(); + onSelectionDone(); + mySelGrp->setEnabled( !isFacetSelection ); +} + +//================================================================================ +/*! + * \brief Slot called when an || axis button is clicked + */ +//================================================================================ + +void SMESHGUI_SplitVolumesDlg::onSetDir() +{ + myDirSpin[0]->SetValue(0.); + myDirSpin[1]->SetValue(0.); + myDirSpin[2]->SetValue(0.); + int i = 0; + for ( ; i < 3; ++i ) + if ( sender() == myAxisBtn[i] ) + break; + if ( i == 3 ) + i == 0; + myDirSpin[i]->SetValue(1.); + + if ( myActor && !myMesh->_is_nil() && myMesh->NbNodes() > 0 ) + { + double b[6]; + myActor->GetUnstructuredGrid()->GetBounds(b); + gp_XYZ center( 0.5 * ( b[0] + b[1] ), + 0.5 * ( b[2] + b[3] ), + 0.5 * ( b[4] + b[5] )); + gp_XYZ point ( myPointSpin[0]->GetValue(), + myPointSpin[1]->GetValue(), + myPointSpin[2]->GetValue() ); + gp_XYZ norm ( myDirSpin[0]->GetValue(), + myDirSpin[1]->GetValue(), + myDirSpin[2]->GetValue() ); + + gp_Vec cp( center, point ); + if ( cp.Dot( norm ) < 0. ) + myDirSpin[i]->SetValue(-1.); + } + + updateNormalPreview(); +} diff --git a/src/SMESHGUI/SMESHGUI_MultiEditDlg.h b/src/SMESHGUI/SMESHGUI_MultiEditDlg.h index 68847772b..1bd9cb5ad 100755 --- a/src/SMESHGUI/SMESHGUI_MultiEditDlg.h +++ b/src/SMESHGUI/SMESHGUI_MultiEditDlg.h @@ -70,7 +70,10 @@ class SMESHGUI_EXPORT SMESHGUI_MultiEditDlg : public SMESHGUI_PreviewDlg Q_OBJECT public: - SMESHGUI_MultiEditDlg( SMESHGUI*, const int, const bool = false ); + SMESHGUI_MultiEditDlg( SMESHGUI* theModule, + const int theMode, + const bool the3d2d = false, + bool theDoInit = true ); virtual ~SMESHGUI_MultiEditDlg(); void Init(); @@ -87,7 +90,7 @@ protected slots: void onHelp(); void onDeactivate(); - void onSelectionDone(); + virtual void onSelectionDone(); void onFilterBtn(); void onAddBtn(); @@ -98,7 +101,7 @@ protected slots: void onGroupChk(); virtual void onToAllChk(); void onFilterAccepted(); - void on3d2dChanged(int); + virtual void on3d2dChanged(int); SMESH::NumericalFunctor_ptr getNumericalFunctor(); @@ -110,11 +113,12 @@ protected: virtual bool isValid( const bool ); SMESH::long_array_var getIds(SMESH::SMESH_IDSource_var& obj); void updateButtons(); - void setSelectionMode(); + virtual void setSelectionMode(); virtual bool isIdValid( const int ) const; virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array& , SMESH::SMESH_IDSource_ptr obj) = 0; + virtual int nbElemsInMesh() = 0; int entityType(); protected: @@ -178,6 +182,7 @@ protected: virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array& , SMESH::SMESH_IDSource_ptr obj); + virtual int nbElemsInMesh(); }; /*! @@ -197,6 +202,7 @@ protected: virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array&, SMESH::SMESH_IDSource_ptr obj ); + virtual int nbElemsInMesh(); protected slots: virtual void onDisplaySimulation( bool ); @@ -221,6 +227,7 @@ protected: virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array& , SMESH::SMESH_IDSource_ptr obj); + virtual int nbElemsInMesh(); protected slots: virtual void reject(); @@ -237,21 +244,45 @@ private: }; /*! - * Class : SMESHGUI_CuttingIntoTetraDlg + * Class : SMESHGUI_SplitVolumesDlg * Description : Split all volumes into tetrahedrons */ -class SMESHGUI_CuttingIntoTetraDlg : public SMESHGUI_MultiEditDlg +class SMESHGUI_SplitVolumesDlg : public SMESHGUI_MultiEditDlg { Q_OBJECT public: - SMESHGUI_CuttingIntoTetraDlg( SMESHGUI* ); - virtual ~SMESHGUI_CuttingIntoTetraDlg(); + SMESHGUI_SplitVolumesDlg( SMESHGUI* ); + virtual ~SMESHGUI_SplitVolumesDlg(); + +protected slots: + + virtual void on3d2dChanged(int); + virtual void onSelectionDone(); + + void onFacetSelection(bool); + void onSetDir(); + void updateNormalPreview(const QString& s=""); protected: + virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array&, SMESH::SMESH_IDSource_ptr obj ); + virtual int nbElemsInMesh(); + + virtual void setSelectionMode(); + void showFacetByElement( int id ); + bool isIntoPrisms(); + + QGroupBox* myFacetSelGrp; + SMESHGUI_SpinBox* myPointSpin[3]; + SMESHGUI_SpinBox* myDirSpin [3]; + QPushButton* myFacetSelBtn; + QPushButton* myAxisBtn[3]; + QCheckBox* myAllDomainsChk; + + double myCellSize; }; #endif // SMESHGUI_MULTIEDITDLG_H diff --git a/src/SMESHGUI/SMESH_msg_en.ts b/src/SMESHGUI/SMESH_msg_en.ts index df41ace14..1825f17f4 100644 --- a/src/SMESHGUI/SMESH_msg_en.ts +++ b/src/SMESHGUI/SMESH_msg_en.ts @@ -1066,15 +1066,15 @@ MEN_SPLIT_TO_TETRA - Split into Tetrahedra + Split Volumes TOP_SPLIT_TO_TETRA - Split into Tetrahedra + Split Volumes STB_SPLIT_TO_TETRA - Split into Tetrahedra + Split Volumes MESHERS_FILE_CANT_OPEN @@ -6278,10 +6278,10 @@ It is impossible to read point coordinates from file - SMESHGUI_CuttingIntoTetraDlg + SMESHGUI_SplitVolumesDlg CAPTION - Splitting volumes into tetrahedra + Splitting volumes SPLIT_METHOD @@ -6299,6 +6299,34 @@ It is impossible to read point coordinates from file SPLIT_HEX_TO_24_TETRA Into 24 tetrahedra + + SPLIT_HEX_TO_2_PRISMS + Into 2 prisms + + + SPLIT_HEX_TO_4_PRISMS + Into 4 Prisms + + + TARGET_ELEM_TYPE + Target element type + + + FACET_TO_SPLIT + Facet to split + + + START_POINT + Hexa location + + + FACET_NORMAL + Facet normal + + + ALL_DOMAINS + All domains + SMESHGUI_PrecisionDlg diff --git a/src/SMESH_I/SMESH_MeshEditor_i.cxx b/src/SMESH_I/SMESH_MeshEditor_i.cxx index 69bf34ec1..b066d2492 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.cxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.cxx @@ -1999,13 +1999,15 @@ void SMESH_MeshEditor_i::SplitVolumesIntoTetra (SMESH::SMESH_IDSource_ptr elems, { SMESH_TRY; initData(); - prepareIdSource( elems ); - SMESH::long_array_var anElementsId = elems->GetIDs(); - TIDSortedElemSet elemSet; - arrayToSet( anElementsId, getMeshDS(), elemSet, SMDSAbs_Volume ); - getEditor().SplitVolumesIntoTetra( elemSet, int( methodFlags )); + ::SMESH_MeshEditor::TFacetOfElem elemSet; + const int noneFacet = -1; + SMDS_ElemIteratorPtr volIt = myMesh_i->GetElements( elems, SMESH::VOLUME ); + while( volIt->more() ) + elemSet.insert( elemSet.end(), make_pair( volIt->next(), noneFacet )); + + getEditor().SplitVolumes( elemSet, int( methodFlags )); declareMeshModified( /*isReComputeSafe=*/true ); // it does not influence Compute() TPythonDump() << this << ".SplitVolumesIntoTetra( " @@ -2014,6 +2016,66 @@ void SMESH_MeshEditor_i::SplitVolumesIntoTetra (SMESH::SMESH_IDSource_ptr elems, SMESH_CATCH( SMESH::throwCorbaException ); } +//================================================================================ +/*! + * \brief Split hexahedra into triangular prisms + * \param elems - elements to split + * \param facetToSplitNormal - normal used to find a facet of hexahedron + * to split into triangles + * \param methodFlags - flags passing splitting method: + * 1 - split the hexahedron into 2 prisms + * 2 - split the hexahedron into 4 prisms + */ +//================================================================================ + +void SMESH_MeshEditor_i::SplitHexahedraIntoPrisms (SMESH::SMESH_IDSource_ptr elems, + CORBA::Short methodFlags, + const SMESH::AxisStruct & facetToSplitNormal, + CORBA::Boolean allDomains) + throw (SALOME::SALOME_Exception) +{ + SMESH_TRY; + initData(); + prepareIdSource( elems ); + + gp_Ax1 facetNorm( gp_Pnt( facetToSplitNormal.x, + facetToSplitNormal.y, + facetToSplitNormal.z ), + gp_Dir( facetToSplitNormal.vx, + facetToSplitNormal.vy, + facetToSplitNormal.vz )); + TIDSortedElemSet elemSet; + SMESH::long_array_var anElementsId = elems->GetIDs(); + SMDS_MeshElement::GeomFilter filter( SMDSGeom_HEXA ); + arrayToSet( anElementsId, getMeshDS(), elemSet, SMDSAbs_Volume, &filter ); + + ::SMESH_MeshEditor::TFacetOfElem elemFacets; + while ( !elemSet.empty() ) + { + getEditor().GetHexaFacetsToSplit( elemSet, facetNorm, elemFacets ); + if ( !allDomains ) + break; + + ::SMESH_MeshEditor::TFacetOfElem::iterator ef = elemFacets.begin(); + for ( ; ef != elemFacets.end(); ++ef ) + elemSet.erase( ef->first ); + } + + if ( methodFlags == 2 ) + methodFlags = int( ::SMESH_MeshEditor::HEXA_TO_4_PRISMS ); + else + methodFlags = int( ::SMESH_MeshEditor::HEXA_TO_2_PRISMS ); + + getEditor().SplitVolumes( elemFacets, int( methodFlags )); + declareMeshModified( /*isReComputeSafe=*/true ); // it does not influence Compute() + + TPythonDump() << this << ".SplitHexahedraIntoPrisms( " + << elems << ", " << methodFlags<< ", " + << facetToSplitNormal<< ", " << allDomains << " )"; + + SMESH_CATCH( SMESH::throwCorbaException ); +} + //======================================================================= //function : Smooth //purpose : diff --git a/src/SMESH_I/SMESH_MeshEditor_i.hxx b/src/SMESH_I/SMESH_MeshEditor_i.hxx index e74df3788..ed67dabef 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.hxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.hxx @@ -233,6 +233,11 @@ public: void SplitVolumesIntoTetra(SMESH::SMESH_IDSource_ptr elems, CORBA::Short methodFlags) throw (SALOME::SALOME_Exception); + void SplitHexahedraIntoPrisms(SMESH::SMESH_IDSource_ptr elems, + CORBA::Short methodFlags, + const SMESH::AxisStruct & facetToSplitNormal, + CORBA::Boolean allDomains) + throw (SALOME::SALOME_Exception); CORBA::Boolean Smooth(const SMESH::long_array & IDsOfElements, const SMESH::long_array & IDsOfFixedNodes, diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index a5aae93d2..4e7c418e4 100644 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -73,7 +73,7 @@ ## @defgroup l2_modif_invdiag Diagonal inversion of elements ## @defgroup l2_modif_unitetri Uniting triangles ## @defgroup l2_modif_changori Changing orientation of elements -## @defgroup l2_modif_cutquadr Cutting quadrangles +## @defgroup l2_modif_cutquadr Cutting elements ## @defgroup l2_modif_smooth Smoothing ## @defgroup l2_modif_extrurev Extrusion and Revolution ## @defgroup l2_modif_patterns Pattern mapping @@ -307,7 +307,7 @@ class smeshBuilder(object, SMESH._objref_SMESH_Gen): [TopAbs_IN, TopAbs_OUT, TopAbs_ON, TopAbs_UNKNOWN] = range(4) # Methods of splitting a hexahedron into tetrahedra - Hex_5Tet, Hex_6Tet, Hex_24Tet = 1, 2, 3 + Hex_5Tet, Hex_6Tet, Hex_24Tet, Hex_2Prisms, Hex_4Prisms = 1, 2, 3, 1, 2 def __new__(cls): global engine @@ -851,11 +851,15 @@ class smeshBuilder(object, SMESH._objref_SMESH_Gen): ## Creates a filter from criteria # @param criteria a list of criteria + # @param binOp binary operator used when binary operator of criteria is undefined # @return SMESH_Filter # # Example of Filters usage # @ingroup l1_controls - def GetFilterFromCriteria(self,criteria): + def GetFilterFromCriteria(self,criteria, binOp=SMESH.FT_LogicalAND): + for i in range( len( criteria ) - 1 ): + if criteria[i].BinaryOp == self.EnumToLong( SMESH.FT_Undefined ): + criteria[i].BinaryOp = self.EnumToLong( binOp ) aFilterMgr = self.CreateFilterManager() aFilter = aFilterMgr.CreateFilter() aFilter.SetCriteria(criteria) @@ -2284,6 +2288,12 @@ class Mesh: def GetElementGeomType(self, id): return self.mesh.GetElementGeomType(id) + ## Returns the shape type of mesh element + # @return the value from SMESH::GeometryType enumeration + # @ingroup l1_meshinfo + def GetElementShape(self, id): + return self.mesh.GetElementShape(id) + ## Returns the list of submesh elements IDs # @param Shape a geom object(sub-shape) IOR # Shape must be the sub-shape of a ShapeToMesh() @@ -3026,18 +3036,54 @@ class Mesh: return self.editor.BestSplit(IDOfQuad, self.smeshpyD.GetFunctor(theCriterion)) ## Splits volumic elements into tetrahedrons - # @param elemIDs either list of elements or mesh or group or submesh - # @param method flags passing splitting method: Hex_5Tet, Hex_6Tet, Hex_24Tet - # Hex_5Tet - split the hexahedron into 5 tetrahedrons, etc + # @param elems either a list of elements or a mesh or a group or a submesh or a filter + # @param method flags passing splitting method: + # smesh.Hex_5Tet, smesh.Hex_6Tet, smesh.Hex_24Tet. + # smesh.Hex_5Tet - to split the hexahedron into 5 tetrahedrons, etc. + # @ingroup l2_modif_cutquadr + def SplitVolumesIntoTetra(self, elems, method=smeshBuilder.Hex_5Tet ): + unRegister = genObjUnRegister() + if isinstance( elems, Mesh ): + elems = elems.GetMesh() + if ( isinstance( elems, list )): + elems = self.editor.MakeIDSource(elems, SMESH.VOLUME) + unRegister.set( elems ) + self.editor.SplitVolumesIntoTetra(elems, method) + + ## Splits hexahedra into prisms + # @param elems either a list of elements or a mesh or a group or a submesh or a filter + # @param startHexPoint a point used to find a hexahedron for which @a facetNormal + # gives a normal vector defining facets to split into triangles. + # @a startHexPoint can be either a triple of coordinates or a vertex. + # @param facetNormal a normal to a facet to split into triangles of a + # hexahedron found by @a startHexPoint. + # @a facetNormal can be either a triple of coordinates or an edge. + # @param method flags passing splitting method: smesh.Hex_2Prisms, smesh.Hex_4Prisms. + # smesh.Hex_2Prisms - to split the hexahedron into 2 prisms, etc. + # @param allDomains if @c False, only hexahedra adjacent to one closest + # to @a startHexPoint are split, else @a startHexPoint + # is used to find the facet to split in all domains present in @a elems. # @ingroup l2_modif_cutquadr - def SplitVolumesIntoTetra(self, elemIDs, method=smeshBuilder.Hex_5Tet ): + def SplitHexahedraIntoPrisms(self, elems, startHexPoint, facetNormal, + method=smeshBuilder.Hex_2Prisms, allDomains=False ): + # IDSource unRegister = genObjUnRegister() - if isinstance( elemIDs, Mesh ): - elemIDs = elemIDs.GetMesh() - if ( isinstance( elemIDs, list )): - elemIDs = self.editor.MakeIDSource(elemIDs, SMESH.VOLUME) - unRegister.set( elemIDs ) - self.editor.SplitVolumesIntoTetra(elemIDs, method) + if isinstance( elems, Mesh ): + elems = elems.GetMesh() + if ( isinstance( elems, list )): + elems = self.editor.MakeIDSource(elems, SMESH.VOLUME) + unRegister.set( elems ) + pass + # axis + if isinstance( startHexPoint, geomBuilder.GEOM._objref_GEOM_Object): + startHexPoint = self.geompyD.PointCoordinates( startHexPoint ) + if isinstance( facetNormal, geomBuilder.GEOM._objref_GEOM_Object): + facetNormal = self.geompyD.VectorCoordinates( facetNormal ) + axis = SMESH.AxisStruct( startHexPoint[0], startHexPoint[1], startHexPoint[2], + facetNormal[0], facetNormal[1], facetNormal[2]) + self.mesh.SetParameters( axis.parameters ) + + self.editor.SplitHexahedraIntoPrisms(elems, method, axis, allDomains) ## Splits quadrangle faces near triangular facets of volumes # @@ -3187,8 +3233,8 @@ class Mesh: # Note that nodes built on edges and boundary nodes are always fixed. # @param MaxNbOfIterations the maximum number of iterations # @param MaxAspectRatio varies in range [1.0, inf] - # @param Method is either Laplacian (SMESH.SMESH_MeshEditor.LAPLACIAN_SMOOTH) - # or Centroidal (SMESH.SMESH_MeshEditor.CENTROIDAL_SMOOTH) + # @param Method is either Laplacian (smesh.LAPLACIAN_SMOOTH) + # or Centroidal (smesh.CENTROIDAL_SMOOTH) # @return TRUE in case of success, FALSE otherwise. # @ingroup l2_modif_smooth def Smooth(self, IDsOfElements, IDsOfFixedNodes, @@ -3206,8 +3252,8 @@ class Mesh: # Note that nodes built on edges and boundary nodes are always fixed. # @param MaxNbOfIterations the maximum number of iterations # @param MaxAspectRatio varies in range [1.0, inf] - # @param Method is either Laplacian (SMESH.SMESH_MeshEditor.LAPLACIAN_SMOOTH) - # or Centroidal (SMESH.SMESH_MeshEditor.CENTROIDAL_SMOOTH) + # @param Method is either Laplacian (smesh.LAPLACIAN_SMOOTH) + # or Centroidal (smesh.CENTROIDAL_SMOOTH) # @return TRUE in case of success, FALSE otherwise. # @ingroup l2_modif_smooth def SmoothObject(self, theObject, IDsOfFixedNodes, @@ -3223,8 +3269,8 @@ class Mesh: # Note that nodes built on edges and boundary nodes are always fixed. # @param MaxNbOfIterations the maximum number of iterations # @param MaxAspectRatio varies in range [1.0, inf] - # @param Method is either Laplacian (SMESH.SMESH_MeshEditor.LAPLACIAN_SMOOTH) - # or Centroidal (SMESH.SMESH_MeshEditor.CENTROIDAL_SMOOTH) + # @param Method is either Laplacian (smesh.LAPLACIAN_SMOOTH) + # or Centroidal (smesh.CENTROIDAL_SMOOTH) # @return TRUE in case of success, FALSE otherwise. # @ingroup l2_modif_smooth def SmoothParametric(self, IDsOfElements, IDsOfFixedNodes, @@ -3242,8 +3288,8 @@ class Mesh: # Note that nodes built on edges and boundary nodes are always fixed. # @param MaxNbOfIterations the maximum number of iterations # @param MaxAspectRatio varies in range [1.0, inf] - # @param Method is either Laplacian (SMESH.SMESH_MeshEditor.LAPLACIAN_SMOOTH) - # or Centroidal (SMESH.SMESH_MeshEditor.CENTROIDAL_SMOOTH) + # @param Method is either Laplacian (smesh.LAPLACIAN_SMOOTH) + # or Centroidal (smesh.CENTROIDAL_SMOOTH) # @return TRUE in case of success, FALSE otherwise. # @ingroup l2_modif_smooth def SmoothParametricObject(self, theObject, IDsOfFixedNodes, -- 2.30.2