// Copyright (C) 2007-2024 CEA, EDF, OPEN CASCADE // // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // // File : StdMeshers_PolyhedronPerSolid_3D.cxx // Module : SMESH // Created : Fri Oct 20 11:37:07 2006 // Author : Edward AGAPOV (eap) // #include "StdMeshers_PolyhedronPerSolid_3D.hxx" #include "SMESHDS_Mesh.hxx" #include "SMESH_ControlsDef.hxx" #include "SMESH_Gen.hxx" #include "SMESH_Mesh.hxx" #include "SMESH_MeshAlgos.hxx" #include "SMESH_MeshEditor.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_ProxyMesh.hxx" #include "SMESH_subMesh.hxx" #include "StdMeshers_PolygonPerFace_2D.hxx" #include "StdMeshers_Regular_1D.hxx" #include "StdMeshers_ViscousLayers.hxx" #include #include #include namespace { struct _EdgeMesher : public StdMeshers_Regular_1D { _EdgeMesher( int hypId, SMESH_Gen* gen ) : StdMeshers_Regular_1D( hypId, gen ) { _hypType = NB_SEGMENTS; _ivalue[ NB_SEGMENTS_IND ] = 1; } }; //======================================================================= //function : addHexa //purpose : //======================================================================= const SMDS_MeshElement* addHexa( std::vector< const SMDS_MeshElement* >& faces, const std::vector< int > & quantities, SMESH_MesherHelper & helper ) { const SMDS_MeshElement* newHexa = 0; // check nb of nodes in faces for ( size_t i = 0; i < quantities.size(); ++i ) if ( quantities[ i ] != 4 ) return newHexa; // look for a top face const SMDS_MeshElement* topFace = 0; const SMDS_MeshElement* botFace = faces[0]; std::vector< const SMDS_MeshNode* > nodes( 16 ); // last 8 is a working buffer nodes.assign( botFace->begin_nodes(), botFace->end_nodes() ); for ( size_t iF = 1; iF < faces.size() && !topFace; ++iF ) { bool hasCommonNode = false; for ( int iN = 0; iN < quantities[ 0 ] && !hasCommonNode; ++iN ) hasCommonNode = ( faces[ iF ]->GetNodeIndex( nodes[ iN ]) >= 0 ); if ( !hasCommonNode ) topFace = faces[ iF ]; } nodes.resize( 8 ); // set top nodes after hexa nodes - [8-11] nodes.insert( nodes.end(), topFace->begin_nodes(), topFace->end_nodes() ); nodes.resize( 12 ); nodes.insert( nodes.end(), nodes.begin() + 8, nodes.begin() + 12 ); // find corresponding nodes of top and bottom by finding a side face including 2 node of each SMESHDS_Mesh* mesh = helper.GetMeshDS(); const SMDS_MeshElement* sideFace = 0; size_t i; for ( i = 8; i < nodes.size()-1 && !sideFace; ++i ) { sideFace = mesh->FindFace( nodes[0], nodes[1], nodes[ i ], nodes[ i + 1 ]); } if ( !sideFace ) return newHexa; --i; // restore after ++i in the loop bool botOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 0 ], nodes[ 1 ] ); bool topOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ i ], nodes[ i + 1 ] ); if ( botOriRight == topOriRight ) { nodes[ 4 ] = nodes[ i + 1 ]; nodes[ 5 ] = nodes[ i + 0 ]; nodes[ 6 ] = nodes[ i + 3 ]; nodes[ 7 ] = nodes[ i + 2 ]; } else { nodes[ 4 ] = nodes[ i + 0 ]; nodes[ 5 ] = nodes[ i + 1 ]; nodes[ 6 ] = nodes[ i + 2 ]; nodes[ 7 ] = nodes[ i + 3 ]; } newHexa = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ], nodes[ 4 ], nodes[ 5 ], nodes[ 6 ], nodes[ 7 ]); return newHexa; } //======================================================================= //function : addTetra //purpose : //======================================================================= const SMDS_MeshElement* addTetra( std::vector< const SMDS_MeshElement* >& faces, const std::vector< int > & quantities, SMESH_MesherHelper & helper ) { const SMDS_MeshElement* newTetra = 0; // check nb of nodes in faces for ( size_t i = 0; i < quantities.size(); ++i ) if ( quantities[ i ] != 3 ) return newTetra; const SMDS_MeshElement* botFace = faces[0]; std::vector< const SMDS_MeshNode* > nodes( 6 ); nodes.assign( botFace->begin_nodes(), botFace->end_nodes() ); nodes.resize( 3 ); const SMDS_MeshNode* topNode = 0; for ( size_t i = 0; i < 3 && !topNode; ++i ) { topNode = faces[ 1 ]->GetNode( i ); if ( botFace->GetNodeIndex( topNode ) >= 0 ) topNode = 0; } if ( !topNode ) return newTetra; nodes.push_back( topNode ); newTetra = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]); return newTetra; } //======================================================================= //function : addPenta //purpose : //======================================================================= const SMDS_MeshElement* addPenta( std::vector< const SMDS_MeshElement* >& faces, const std::vector< int > & quantities, SMESH_MesherHelper & helper ) { const SMDS_MeshElement* newPenta = 0; // check nb of nodes in faces and find triangle faces int trias[2] = { -1, -1 }; for ( size_t i = 0; i < quantities.size(); ++i ) if ( quantities[ i ] != 4 ) { if ( quantities[ i ] != 3 ) return newPenta; int iTria = ( trias[0] != -1 ); if ( trias[ iTria ] != -1 ) return newPenta; trias[ iTria ] = i; } if ( trias[1] == -1 ) return newPenta; int iSide = trias[0] + 1; if ( iSide == trias[1] ) ++iSide; if (iSide == 5) // use first side (otherwise, out of bounds) iSide = 0; const SMDS_MeshElement* botFace = faces[ trias[0]]; const SMDS_MeshElement* topFace = faces[ trias[1]]; const SMDS_MeshElement* sideFace = faces[ iSide ]; const SMDS_MeshNode* nodes[ 6 ] = { 0,0,0,0,0,0 }; for ( int i = 0 ; i < 3; ++i ) { const SMDS_MeshNode* botNode = botFace->GetNode( i ); if ( sideFace->GetNodeIndex( botNode ) < 0 ) nodes[2] = botNode; else nodes[ bool( nodes[0] )] = botNode; const SMDS_MeshNode* topNode = topFace->GetNode( i ); if ( sideFace->GetNodeIndex( topNode ) < 0 ) nodes[5] = topNode; else nodes[ 3 + bool( nodes[3]) ] = topNode; } bool botOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 0 ], nodes[ 1 ]); bool topOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 3 ], nodes[ 4 ]); if ( botOriRight == topOriRight ) std::swap( nodes[ 3 ], nodes[ 4 ]); newPenta = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ], nodes[ 4 ], nodes[ 5 ]); return newPenta; } //======================================================================= //function : addPyra //purpose : //======================================================================= const SMDS_MeshElement* addPyra( std::vector< const SMDS_MeshElement* >& faces, const std::vector< int > & quantities, SMESH_MesherHelper & helper ) { const SMDS_MeshElement* newPyra = 0; // check nb of nodes in faces int iBot = -1; for ( size_t i = 0; i < quantities.size(); ++i ) if ( quantities[ i ] != 3 ) { if ( quantities[ i ] != 4 || iBot != -1 ) return newPyra; iBot = i; } const SMDS_MeshElement* botFace = faces[ iBot ]; std::vector< const SMDS_MeshNode* > nodes( 8 ); nodes.assign( botFace->begin_nodes(), botFace->end_nodes() ); nodes.resize( 4 ); const SMDS_MeshNode* topNode = 0; for ( size_t i = 0; i < 4 && !topNode; ++i ) { topNode = faces[ 1 ]->GetNode( i ); if ( botFace->GetNodeIndex( topNode ) >= 0 ) topNode = 0; } if ( !topNode ) return newPyra; nodes.push_back( topNode ); newPyra = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ], nodes[4] ); return newPyra; } //======================================================================= //function : addHPrism //purpose : add hexagonal prism //======================================================================= const SMDS_MeshElement* addHPrism( std::vector< const SMDS_MeshElement* >& faces, const std::vector< int > & quantities, SMESH_MesherHelper & helper ) { const SMDS_MeshElement* newHexPrism = 0; // check nb of nodes in faces and find hexagons int hexa[2] = { -1, -1 }; for ( size_t i = 0; i < quantities.size(); ++i ) if ( quantities[ i ] != 4 ) { if ( quantities[ i ] != 6 ) return newHexPrism; int iHex = ( hexa[0] != -1 ); if ( hexa[ iHex ] != -1 ) return newHexPrism; hexa[ iHex ] = i; } if ( hexa[1] == -1 ) return newHexPrism; const SMDS_MeshElement* botFace = faces[ hexa[ 0 ]]; const SMDS_MeshElement* topFace = faces[ hexa[ 1 ]]; std::vector< const SMDS_MeshNode* > nodes( 24 ); // last 12 is a working buffer nodes.assign( botFace->begin_nodes(), botFace->end_nodes() ); nodes.resize( 12 ); // set top nodes after hexa nodes - [12-17] nodes.insert( nodes.end(), topFace->begin_nodes(), topFace->end_nodes() ); nodes.resize( 18 ); nodes.insert( nodes.end(), nodes.begin() + 12, nodes.begin() + 18 ); // find corresponding nodes of top and bottom by finding a side face including 2 node of each SMESHDS_Mesh* mesh = helper.GetMeshDS(); const SMDS_MeshElement* sideFace = 0; size_t i; for ( i = 12; i < nodes.size()-1 && !sideFace; ++i ) { sideFace = mesh->FindFace( nodes[0], nodes[1], nodes[ i ], nodes[ i + 1 ]); } if ( !sideFace ) return newHexPrism; --i; // restore after ++i in the loop bool botOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ 0 ], nodes[ 1 ] ); bool topOriRight = SMESH_MeshAlgos::IsRightOrder( sideFace, nodes[ i ], nodes[ i + 1 ] ); if ( botOriRight == topOriRight ) { nodes[ 6 ] = nodes[ i + 1 ]; nodes[ 7 ] = nodes[ i + 0 ]; nodes[ 8 ] = nodes[ i + 5 ]; nodes[ 9 ] = nodes[ i + 4 ]; nodes[ 10 ] = nodes[ i + 3 ]; nodes[ 11 ] = nodes[ i + 2 ]; } else { nodes[ 6 ] = nodes[ i + 0 ]; nodes[ 7 ] = nodes[ i + 1 ]; nodes[ 8 ] = nodes[ i + 2 ]; nodes[ 9 ] = nodes[ i + 3 ]; nodes[ 10 ] = nodes[ i + 4 ]; nodes[ 11 ] = nodes[ i + 5 ]; } newHexPrism = helper.AddVolume( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ], nodes[ 4 ], nodes[ 5 ], nodes[ 6 ], nodes[ 7 ], nodes[ 8 ], nodes[ 9 ], nodes[10 ], nodes[11 ]); return newHexPrism; } //======================================================================= //function : addPoly //purpose : //======================================================================= const SMDS_MeshElement* addPoly( std::vector< const SMDS_MeshElement* >& faces, const std::vector< int > & quantities, SMESH_MesherHelper & helper ) { const SMDS_MeshElement* newPoly = 0; std::vector< const SMDS_MeshNode* > nodes; for ( size_t iF = 0; iF < faces.size(); ++iF ) nodes.insert( nodes.end(), faces[iF]->begin_nodes(), faces[iF]->end_nodes() ); newPoly = helper.AddPolyhedralVolume( nodes, quantities ); return newPoly; } } // namespace //======================================================================= //function : StdMeshers_PolyhedronPerSolid_3D //purpose : //======================================================================= StdMeshers_PolyhedronPerSolid_3D::StdMeshers_PolyhedronPerSolid_3D(int hypId, SMESH_Gen* gen) :SMESH_3D_Algo(hypId, gen), myEdgeMesher( new _EdgeMesher( gen->GetANewId(), gen )), myFaceMesher( new StdMeshers_PolygonPerFace_2D( gen->GetANewId(), gen )) { _name = "PolyhedronPerSolid_3D"; _requireDiscreteBoundary = false; _supportSubmeshes = true; _compatibleHypothesis.push_back("ViscousLayers"); _neededLowerHyps[0] = _neededLowerHyps[1] = _neededLowerHyps[2] = true; } //======================================================================= //function : ~StdMeshers_PolyhedronPerSolid_3D //purpose : //======================================================================= StdMeshers_PolyhedronPerSolid_3D::~StdMeshers_PolyhedronPerSolid_3D() { delete myEdgeMesher; delete myFaceMesher; } //======================================================================= //function : CheckHypothesis //purpose : //======================================================================= bool StdMeshers_PolyhedronPerSolid_3D::CheckHypothesis(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape, Hypothesis_Status& theStatus) { myViscousLayersHyp = NULL; const std::list& hyps = GetUsedHypothesis( theMesh, theShape, /*ignoreAuxiliary=*/false); std::list ::const_iterator h = hyps.begin(); if ( h == hyps.end()) { theStatus = SMESH_Hypothesis::HYP_OK; return true; } // only StdMeshers_ViscousLayers can be used theStatus = HYP_OK; for ( ; h != hyps.end(); ++h ) { if ( !(myViscousLayersHyp = dynamic_cast< const StdMeshers_ViscousLayers*> ( *h ))) break; } if ( !myViscousLayersHyp ) theStatus = HYP_INCOMPATIBLE; else error( myViscousLayersHyp->CheckHypothesis( theMesh, theShape, theStatus )); return theStatus == HYP_OK; } //======================================================================= //function : Compute //purpose : //======================================================================= bool StdMeshers_PolyhedronPerSolid_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape) { const SMDS_MeshElement* newVolume = 0; SMESH_subMesh* sm = theMesh.GetSubMesh( theShape ); SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator( /*includeSelf=*/true, /*complexFirst=*/false); while ( smIt->more() ) { sm = smIt->next(); if ( !sm->IsEmpty() ) continue; const TopoDS_Shape & shape = sm->GetSubShape(); switch ( shape.ShapeType() ) { case TopAbs_VERTEX: sm->ComputeStateEngine( SMESH_subMesh::COMPUTE ); break; case TopAbs_EDGE: sm->ComputeStateEngine( SMESH_subMesh::COMPUTE ); if ( sm->IsEmpty() ) myEdgeMesher->Compute( theMesh, shape ); break; case TopAbs_FACE: sm->ComputeStateEngine( SMESH_subMesh::COMPUTE ); if ( sm->IsEmpty() && !myFaceMesher->Compute( theMesh, shape )) { sm->GetComputeError() = myFaceMesher->GetComputeError(); sm->GetComputeError()->myAlgo = myFaceMesher; return false; } break; case TopAbs_SOLID: { SMESH_MesherHelper helper( theMesh ); helper.SetElementsOnShape( true ); _quadraticMesh = helper.IsQuadraticSubMesh( shape ); SMESH_ProxyMesh::Ptr proxymesh( new SMESH_ProxyMesh( theMesh )); if ( myViscousLayersHyp ) { proxymesh = myViscousLayersHyp->Compute( theMesh, theShape ); if ( !proxymesh ) return false; } std::vector< const SMDS_MeshElement* > faces; faces.reserve( 20 ); for ( TopExp_Explorer faceEx( shape, TopAbs_FACE ); faceEx.More(); faceEx.Next() ) { const SMESHDS_SubMesh* smDS = proxymesh->GetSubMesh( faceEx.Current() ); for ( SMDS_ElemIteratorPtr faceIt = smDS->GetElements(); faceIt->more(); ) faces.push_back( faceIt->next() ); } bool useMediumNodes = false; if ( !_quadraticMesh && theMesh.GetMeshDS()->GetMeshInfo().NbFaces( ORDER_QUADRATIC )) for ( size_t i = 0; i < faces.size() && !useMediumNodes ; ++i ) useMediumNodes = faces[ i ]->IsQuadratic(); std::vector< int > quantities( faces.size() ); std::set< const SMDS_MeshNode* > nodes; for ( size_t i = 0; i < faces.size(); ++i ) { quantities[ i ] = useMediumNodes ? faces[ i ]->NbNodes() : faces[ i ]->NbCornerNodes(); for ( int iN = 0; iN < quantities[ i ]; ++iN ) nodes.insert( faces[ i ]->GetNode( iN )); } const size_t nbNodes = nodes.size(), nbFaces = faces.size(); if ( nbNodes == 8 && nbFaces == 6 ) newVolume = addHexa ( faces, quantities, helper ); else if ( nbNodes == 4 && nbFaces == 4 ) newVolume = addTetra ( faces, quantities, helper ); else if ( nbNodes == 6 && nbFaces == 5 ) newVolume = addPenta ( faces, quantities, helper ); else if ( nbNodes == 5 && nbFaces == 5 ) newVolume = addPyra ( faces, quantities, helper ); else if ( nbNodes == 12 && nbFaces == 8 ) newVolume = addHPrism( faces, quantities, helper ); if ( !newVolume ) newVolume = addPoly ( faces, quantities, helper ); if ( newVolume ) { SMESH::Controls::BadOrientedVolume checker; checker.SetMesh( theMesh.GetMeshDS() ); if ( checker.IsSatisfy( newVolume->GetID() )) { SMESH_MeshEditor editor( &theMesh ); editor.Reorient( newVolume ); } } } default:; } // switch ( shape.ShapeType() ) } // loop on sub-meshes return newVolume; } //======================================================================= //function : Evaluate //purpose : //======================================================================= bool StdMeshers_PolyhedronPerSolid_3D::Evaluate(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape, MapShapeNbElems& theResMap) { _quadraticMesh = false; SMESH_subMesh* sm = theMesh.GetSubMesh( theShape ); SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator( /*includeSelf=*/true, /*complexFirst=*/false); while ( smIt->more() ) { sm = smIt->next(); MapShapeNbElems::iterator sm2vec = theResMap.find( sm ); if ( sm2vec != theResMap.end() && !sm2vec->second.empty() ) continue; const TopoDS_Shape & shape = sm->GetSubShape(); switch ( shape.ShapeType() ) { case TopAbs_EDGE: myEdgeMesher->Evaluate( theMesh, shape, theResMap ); break; case TopAbs_FACE: { myFaceMesher->Evaluate( theMesh, shape, theResMap ); std::vector & quantities = theResMap[ sm ]; _quadraticMesh = ( !quantities.empty() && ( quantities[ SMDSEntity_Quad_Triangle ] + quantities[ SMDSEntity_Quad_Quadrangle ] + quantities[ SMDSEntity_Quad_Polygon ])); break; } case TopAbs_SOLID: { std::vector & quantities = theResMap[ sm ]; quantities.resize( SMDSEntity_Last, 0 ); SMESH_MesherHelper helper( theMesh ); const int nbNodes = helper.Count( shape, TopAbs_VERTEX, /*ignoreSame=*/true ); const int nbFaces = helper.Count( shape, TopAbs_FACE, /*ignoreSame=*/false ); if ( nbNodes == 8 && nbFaces == 6 ) quantities[ _quadraticMesh ? SMDSEntity_Quad_Hexa : SMDSEntity_Hexa ] = 1; else if ( nbNodes == 4 && nbFaces == 4 ) quantities[ _quadraticMesh ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra ] = 1; else if ( nbNodes == 6 && nbFaces == 5 ) quantities[ _quadraticMesh ? SMDSEntity_Quad_Penta : SMDSEntity_Penta ] = 1; else if ( nbNodes == 5 && nbFaces == 5 ) quantities[ _quadraticMesh ? SMDSEntity_Quad_Pyramid : SMDSEntity_Pyramid ] = 1; else if ( nbNodes == 12 && nbFaces == 8 ) quantities[ /*_quadraticMesh ? SMDSEntity_Quad_Pyramid :*/ SMDSEntity_Hexagonal_Prism ] = 1; else quantities[ /*_quadraticMesh ? SMDSEntity_Quad_Polyhedra : */SMDSEntity_Polyhedra ] = 1; return true; } default:; } // switch ( shape.ShapeType() ) } // loop on sub-meshes return false; }