From: eap Date: Mon, 20 Jan 2014 10:31:23 +0000 (+0000) Subject: 22316: EDF 2719 SMESH: Split hexas into prisms X-Git-Tag: V7_4_0a1~109 X-Git-Url: http://git.salome-platform.org/gitweb/?p=modules%2Fsmesh.git;a=commitdiff_plain;h=f500e5a8b62895e81275f73d8fecd7a977711d78 22316: EDF 2719 SMESH: Split hexas into prisms --- 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 000000000..6b9f77a2d Binary files /dev/null and b/doc/salome/gui/SMESH/images/split_into_prisms.png differ diff --git a/doc/salome/gui/SMESH/images/split_into_tetra.png b/doc/salome/gui/SMESH/images/split_into_tetra.png index fce071e7d..a54f44529 100644 Binary files a/doc/salome/gui/SMESH/images/split_into_tetra.png and b/doc/salome/gui/SMESH/images/split_into_tetra.png differ diff --git a/doc/salome/gui/SMESH/input/modifying_meshes.doc b/doc/salome/gui/SMESH/input/modifying_meshes.doc index b7b7e2e64..d32e56a48 100644 --- a/doc/salome/gui/SMESH/input/modifying_meshes.doc +++ b/doc/salome/gui/SMESH/input/modifying_meshes.doc @@ -39,7 +39,7 @@ with consequent transformation of all adjacent elements and edges. 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,