From: cconopoima Date: Sun, 26 May 2024 12:04:12 +0000 (+0100) Subject: [bos #42002][EDF] (2024) Bodyfitting refactoring. Implement classes Hexahedron and... X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=9f7d4a55e29a362d5518b00ccb6cc05001d62d59;p=modules%2Fsmesh.git [bos #42002][EDF] (2024) Bodyfitting refactoring. Implement classes Hexahedron and Grid in independent sources, write c++ unit tests for those utility classes. Replace parallel iterator of TBB by std::thread implementation to have control on the number of threads implemented. Solve random behavior. Add Hexahedron test to be run by ctest. Include CPP unit test headers and libs to we use testAsserts. --- diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..d373bc121 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,73 @@ +{ + "C_Cpp.dimInactiveRegions": false, + "files.associations": { + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "array": "cpp", + "atomic": "cpp", + "strstream": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "chrono": "cpp", + "codecvt": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "cstdint": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "shared_mutex": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cfenv": "cpp", + "cinttypes": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp", + "*.ipp": "cpp" + } +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7fb5fc413..4c308fb96 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,6 +37,7 @@ SET(SUBDIRS_COMMON SMESHClient SMESH_SWIG StdMeshers + StdMeshers.test StdMeshers_I SMESH_PY Tools diff --git a/src/SMESH/SMESH_Hypothesis.cxx b/src/SMESH/SMESH_Hypothesis.cxx index d75aead36..d0fed1911 100644 --- a/src/SMESH/SMESH_Hypothesis.cxx +++ b/src/SMESH/SMESH_Hypothesis.cxx @@ -47,8 +47,12 @@ SMESH_Hypothesis::SMESH_Hypothesis(int hypId, _type = PARAM_ALGO; _shapeType = 0; // to be set by algo with TopAbs_Enum _param_algo_dim = -1; // to be set by algo parameter - StudyContextStruct* myStudyContext = gen->GetStudyContext(); - myStudyContext->mapHypothesis[hypId] = this; + + if ( _gen ) + { + StudyContextStruct* myStudyContext = gen->GetStudyContext(); + myStudyContext->mapHypothesis[hypId] = this; + } } //============================================================================= @@ -107,16 +111,18 @@ int SMESH_Hypothesis::GetShapeType() const void SMESH_Hypothesis::NotifySubMeshesHypothesisModification() { // for all meshes in study - - StudyContextStruct* myStudyContext = _gen->GetStudyContext(); - map::iterator itm; - for (itm = myStudyContext->mapMesh.begin(); - itm != myStudyContext->mapMesh.end(); - itm++) + if ( _gen ) { - SMESH_Mesh* mesh = (*itm).second; - mesh->NotifySubMeshesHypothesisModification( this ); - } + StudyContextStruct* myStudyContext = _gen->GetStudyContext(); + map::iterator itm; + for (itm = myStudyContext->mapMesh.begin(); + itm != myStudyContext->mapMesh.end(); + itm++) + { + SMESH_Mesh* mesh = (*itm).second; + mesh->NotifySubMeshesHypothesisModification( this ); + } + } } //============================================================================= @@ -148,13 +154,16 @@ void SMESH_Hypothesis::SetLibName(const char* theLibName) SMESH_Mesh* SMESH_Hypothesis::GetMeshByPersistentID(int id) const { - StudyContextStruct* myStudyContext = _gen->GetStudyContext(); - map::iterator itm = myStudyContext->mapMesh.begin(); - for ( ; itm != myStudyContext->mapMesh.end(); itm++) + if ( _gen ) { - SMESH_Mesh* mesh = (*itm).second; - if ( mesh->GetMeshDS()->GetPersistentId() == id ) - return mesh; + StudyContextStruct* myStudyContext = _gen->GetStudyContext(); + map::iterator itm = myStudyContext->mapMesh.begin(); + for ( ; itm != myStudyContext->mapMesh.end(); itm++) + { + SMESH_Mesh* mesh = (*itm).second; + if ( mesh->GetMeshDS()->GetPersistentId() == id ) + return mesh; + } } return 0; } diff --git a/src/StdMeshers.test/CMakeLists.txt b/src/StdMeshers.test/CMakeLists.txt new file mode 100644 index 000000000..ee9e01335 --- /dev/null +++ b/src/StdMeshers.test/CMakeLists.txt @@ -0,0 +1,73 @@ +# Copyright (C) 2012-2024 CEA, EDF, OPEN CASCADE +# +# 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 +# + +INCLUDE(tests.set) + +SALOME_GENERATE_TESTS_ENVIRONMENT(_test_env) + +SET(TEST_INSTALL_DIRECTORY ${SMESH_TEST_DIR}/other) + +INCLUDE_DIRECTORIES( + ${OpenCASCADE_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} + ${MEDCOUPLING_INCLUDE_DIRS} + ${CPPUNIT_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/src/StdMeshers + ${PROJECT_SOURCE_DIR}/src/SMESHUtils + ${PROJECT_SOURCE_DIR}/src/SMESH + ${PROJECT_SOURCE_DIR}/src/SMESHDS + ${PROJECT_SOURCE_DIR}/src/SMDS + ${PROJECT_SOURCE_DIR}/src/Controls +) +# additional preprocessor / compiler flags +ADD_DEFINITIONS( + ${OpenCASCADE_DEFINITIONS} + ${BOOST_DEFINITIONS} + ${CPPUNIT_DEFINITIONS} +) + +IF(SALOME_SMESH_USE_TBB) + SET(TBB_LIBS ${TBB_LIBRARIES}) +ENDIF(SALOME_SMESH_USE_TBB) + +FOREACH(_test ${UNIT_TESTS}) + GET_FILENAME_COMPONENT(testname ${_test} NAME_WE) + SET(testname "TESTS_${testname}") + + add_executable(${_test} ${_test}.cxx) + target_link_libraries(${_test} StdMeshers ${CPPUNIT_LIBRARIES} ) + + ADD_TEST(NAME ${testname} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/${_test} ) + SET_TESTS_PROPERTIES(${testname} PROPERTIES ENVIRONMENT "${tests_env}" LABELS "tests") +ENDFOREACH() + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${UNIT_TESTS} PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ DESTINATION ${TEST_INSTALL_DIRECTORY}) + +# IF(WIN32) +# FOREACH(_test ${UNIT_TESTS}) +# INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${_test}${CMAKE_EXECUTABLE_SUFFIX} PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ DESTINATION ${TEST_INSTALL_DIRECTORY}) +# ENDFOREACH() +# ELSE() +# FOREACH(_test ${CPP_TESTS}) +# INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_test} PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ DESTINATION ${TEST_INSTALL_DIRECTORY}) +# ENDFOREACH() +# ENDIF(WIN32) + + diff --git a/src/StdMeshers.test/HexahedronTest.cxx b/src/StdMeshers.test/HexahedronTest.cxx new file mode 100644 index 000000000..8467ac857 --- /dev/null +++ b/src/StdMeshers.test/HexahedronTest.cxx @@ -0,0 +1,163 @@ +// Copyright (C) 2016-2024 CEA, EDF +// +// 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 : HexahedronTest.cxx +// Module : SMESH +// Purpose: Implement unit tests for StdMeshers_Cartesian_3D_Hexahedron class to reproduce bugs that manifest in integration tests. +// The main difference between this unit test and integration tests is the fine grained control we have over the class methods and the hability to diagnose/solve bugs before the code goes into production enviroment. +// This test class can be used as reference for the development of future tests in other stdMesh algorithms + +#include "StdMeshers_Cartesian_3D_Hexahedron.hxx" +#include "StdMeshers_CartesianParameters3D.hxx" + +// CPP TEST +#include + +// OCC +#include +#include + +#include +#include + +// Helper functions! +// Build Grid +// Require building mesh +// Require building shape. For test load shapes from memory in .brep files seems the simplest +// + +/*! + * \brief Mock mesh + */ +struct SMESH_Mesh_Test: public SMESH_Mesh +{ + SMESH_Mesh_Test() { + _isShapeToMesh = (_id = 0); + _meshDS = new SMESHDS_Mesh( _id, true ); + } +}; + +/*! + * \brief Mock Hypothesis + */ +struct CartesianHypo: public StdMeshers_CartesianParameters3D +{ + CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/) + { + } +}; + +/*! + * \brief Shape loader + */ +void loadBrepShape( std::string shapeName, TopoDS_Shape & shape ) +{ + BRep_Builder b; + BRepTools::Read(shape, shapeName.c_str(), b); +} + +// Initialize the grid and intesersectors of grid with the geometry +void GridInitAndInterserctWithShape( Grid& grid, double gridSpacing, TopoDS_Shape& theShape, + std::map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, const int numOfThreads ) +{ + std::vector< TopoDS_Shape > faceVec; + TopTools_MapOfShape faceMap; + TopExp_Explorer fExp; + for ( fExp.Init( theShape, TopAbs_FACE ); fExp.More(); fExp.Next() ) + { + bool isNewFace = faceMap.Add( fExp.Current() ); + if ( !grid._toConsiderInternalFaces ) + if ( !isNewFace || fExp.Current().Orientation() == TopAbs_INTERNAL ) + // remove an internal face + faceMap.Remove( fExp.Current() ); + } + faceVec.reserve( faceMap.Extent() ); + faceVec.assign( faceMap.cbegin(), faceMap.cend() ); + + vector facesItersectors( faceVec.size() ); + + Bnd_Box shapeBox; + for ( size_t i = 0; i < faceVec.size(); ++i ) + { + facesItersectors[i]._face = TopoDS::Face( faceVec[i] ); + facesItersectors[i]._faceID = grid.ShapeID( faceVec[i] ); + facesItersectors[i]._grid = &grid; + shapeBox.Add( facesItersectors[i].GetFaceBndBox() ); + } + // Canonical axes(i,j,k) + double axisDirs[9] = {1.,0.,0.,0.,1.,0.,0.,0.,1.}; + + Tools::GetExactBndBox( faceVec, axisDirs, shapeBox ); + vector xCoords, yCoords, zCoords; + std::unique_ptr myHypo( new CartesianHypo() ); + std::vector grdSpace = { std::to_string(gridSpacing) }; + std::vector intPnts; + myHypo->SetGridSpacing(grdSpace, intPnts, 0 ); // Spacing in dir 0 + myHypo->SetGridSpacing(grdSpace, intPnts, 1 ); // Spacing in dir 1 + myHypo->SetGridSpacing(grdSpace, intPnts, 2 ); // Spacing in dir 2 + myHypo->SetSizeThreshold(4.0); // set threshold + myHypo->GetCoordinates(xCoords, yCoords, zCoords, shapeBox); + grid.SetCoordinates( xCoords, yCoords, zCoords, axisDirs, shapeBox ); + + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + facesItersectors[i].Intersect(); + + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + facesItersectors[i].StoreIntersections(); + + grid.ComputeNodes( *grid._helper ); + grid.GetEdgesToImplement( edge2faceIDsMap, theShape, faceVec ); +} + +// ADD test for parallel intersection of grid with solid + +// Reproduce conditions of TBPERF_GRIDS_PERF_SMESH_M1 test to detect and solve segfault in unit test. +bool testNRTM1() +{ + for (auto numOfThreads : {1, 2, 12, 16} ) + { + for (size_t i = 0; i < 10; i++) + { + TopoDS_Shape myShape; + loadBrepShape( "data/HexahedronTest/NRTM1.brep", myShape ); + CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !myShape.IsNull() ); + std::unique_ptr myMesh( new SMESH_Mesh_Test() ); + myMesh->ShapeToMesh( myShape ); + SMESH_MesherHelper helper( *myMesh ); + Grid grid; + grid._helper = &helper; + grid._toAddEdges = false; grid._toCreateFaces = false; grid._toConsiderInternalFaces = false; grid._toUseThresholdForInternalFaces = false; grid._toUseQuanta = false; + grid._sizeThreshold = 4.0; + grid.InitGeometry( myShape ); + + std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; + GridInitAndInterserctWithShape( grid, 1.0, myShape, edge2faceIDsMap, numOfThreads ); + Hexahedron hex( &grid ); + int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, numOfThreads ); + CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 1024 ); + } + } + return true; +} + +// Entry point for test +int main() +{ + auto t0 = testNRTM1(); + return 0; +} \ No newline at end of file diff --git a/src/StdMeshers.test/tests.set b/src/StdMeshers.test/tests.set new file mode 100644 index 000000000..26e79035d --- /dev/null +++ b/src/StdMeshers.test/tests.set @@ -0,0 +1,27 @@ +# Copyright (C) 2015-2024 CEA, EDF, OPEN CASCADE +# +# 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 +# + +# The following tests cannot be executed with 'make test' because they use +# external meshing plug-ins. +# On the other hand these tests can be executed with 'salome test'. +# --------------------------------------------------------------------------- + +SET(UNIT_TESTS + HexahedronTest + ) \ No newline at end of file diff --git a/src/StdMeshers/CMakeLists.txt b/src/StdMeshers/CMakeLists.txt index 7d8b58a6d..dbe8eda57 100644 --- a/src/StdMeshers/CMakeLists.txt +++ b/src/StdMeshers/CMakeLists.txt @@ -119,6 +119,8 @@ SET(StdMeshers_HEADERS StdMeshers_ViscousLayers2D.hxx StdMeshers_Projection_1D2D.hxx StdMeshers_CartesianParameters3D.hxx + StdMeshers_Cartesian_3D_Grid.hxx + StdMeshers_Cartesian_3D_Hexahedron.hxx StdMeshers_Cartesian_3D.hxx StdMeshers_Cartesian_VL.hxx StdMeshers_QuadFromMedialAxis_1D2D.hxx @@ -184,6 +186,8 @@ SET(StdMeshers_SOURCES StdMeshers_ViscousLayers2D.cxx StdMeshers_Projection_1D2D.cxx StdMeshers_CartesianParameters3D.cxx + StdMeshers_Cartesian_3D_Grid.cxx + StdMeshers_Cartesian_3D_Hexahedron.cxx StdMeshers_Cartesian_3D.cxx StdMeshers_Cartesian_VL.cxx StdMeshers_Adaptive1D.cxx diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx index c392deeea..98733b703 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx @@ -25,6612 +25,107 @@ #include "StdMeshers_Cartesian_3D.hxx" #include "StdMeshers_CartesianParameters3D.hxx" #include "StdMeshers_Cartesian_VL.hxx" -#include "StdMeshers_FaceSide.hxx" #include "StdMeshers_ViscousLayers.hxx" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +// StdMeshersAlgos +#include"StdMeshers_Cartesian_3D_Hexahedron.hxx" -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//STD -#include -#include -#include - -#include - -#ifdef _DEBUG_ -// #define _MY_DEBUG_ -// #undef WITH_TBB -#endif - -#ifdef WITH_TBB - -#ifdef WIN32 -// See https://docs.microsoft.com/en-gb/cpp/porting/modifying-winver-and-win32-winnt?view=vs-2019 -// Windows 10 = 0x0A00 -#define WINVER 0x0A00 -#define _WIN32_WINNT 0x0A00 -#endif -#include -#include -#endif - -using namespace std; -using namespace SMESH; -std::mutex _eMutex; -std::mutex _bMutex; - -//============================================================================= -/*! - * Constructor - */ -//============================================================================= - -StdMeshers_Cartesian_3D::StdMeshers_Cartesian_3D(int hypId, SMESH_Gen * gen) - :SMESH_3D_Algo(hypId, gen) -{ - _name = "Cartesian_3D"; - _shapeType = (1 << TopAbs_SOLID); // 1 bit /shape type - _compatibleHypothesis.push_back( "CartesianParameters3D" ); - _compatibleHypothesis.push_back( StdMeshers_ViscousLayers::GetHypType() ); - - _onlyUnaryInput = false; // to mesh all SOLIDs at once - _requireDiscreteBoundary = false; // 2D mesh not needed - _supportSubmeshes = false; // do not use any existing mesh -} - -//============================================================================= -/*! - * Check presence of a hypothesis - */ -//============================================================================= - -bool StdMeshers_Cartesian_3D::CheckHypothesis (SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape, - Hypothesis_Status& aStatus) -{ - aStatus = SMESH_Hypothesis::HYP_MISSING; - - const list& hyps = GetUsedHypothesis(aMesh, aShape, /*skipAux=*/false); - list ::const_iterator h = hyps.begin(); - if ( h == hyps.end()) - { - return false; - } - - _hyp = nullptr; - _hypViscousLayers = nullptr; - _isComputeOffset = false; - - for ( ; h != hyps.end(); ++h ) - { - if ( !_hyp && ( _hyp = dynamic_cast( *h ))) - { - aStatus = _hyp->IsDefined() ? HYP_OK : HYP_BAD_PARAMETER; - } - else - { - _hypViscousLayers = dynamic_cast( *h ); - } - } - - return aStatus == HYP_OK; -} - -namespace -{ - /*! - * \brief Temporary mesh to hold - */ - struct TmpMesh: public SMESH_Mesh - { - TmpMesh() { - _isShapeToMesh = (_id = 0); - _meshDS = new SMESHDS_Mesh( _id, true ); - } - }; - - typedef int TGeomID; // IDs of sub-shapes - typedef TopTools_ShapeMapHasher TShapeHasher; // non-oriented shape hasher - typedef std::array< int, 3 > TIJK; - - const TGeomID theUndefID = 1e+9; - - //============================================================================= - // Definitions of internal utils - // -------------------------------------------------------------------------- - enum Transition { - Trans_TANGENT = IntCurveSurface_Tangent, - Trans_IN = IntCurveSurface_In, - Trans_OUT = IntCurveSurface_Out, - Trans_APEX, - Trans_INTERNAL // for INTERNAL FACE - }; - // -------------------------------------------------------------------------- - /*! - * \brief Sub-entities of a FACE neighboring its concave VERTEX. - * Help to avoid linking nodes on EDGEs that seem connected - * by the concave FACE but the link actually lies outside the FACE - */ - struct ConcaveFace - { - TGeomID _concaveFace; - TGeomID _edge1, _edge2; - TGeomID _v1, _v2; - ConcaveFace( int f=0, int e1=0, int e2=0, int v1=0, int v2=0 ) - : _concaveFace(f), _edge1(e1), _edge2(e2), _v1(v1), _v2(v2) {} - bool HasEdge( TGeomID edge ) const { return edge == _edge1 || edge == _edge2; } - bool HasVertex( TGeomID v ) const { return v == _v1 || v == _v2; } - void SetEdge( TGeomID edge ) { ( _edge1 ? _edge2 : _edge1 ) = edge; } - void SetVertex( TGeomID v ) { ( _v1 ? _v2 : _v1 ) = v; } - }; - typedef NCollection_DataMap< TGeomID, ConcaveFace > TConcaveVertex2Face; - // -------------------------------------------------------------------------- - /*! - * \brief Container of IDs of SOLID sub-shapes - */ - class Solid // sole SOLID contains all sub-shapes - { - TGeomID _id; // SOLID id - bool _hasInternalFaces; - TConcaveVertex2Face _concaveVertex; // concave VERTEX -> ConcaveFace - public: - virtual ~Solid() {} - virtual bool Contains( TGeomID /*subID*/ ) const { return true; } - virtual bool ContainsAny( const vector< TGeomID>& /*subIDs*/ ) const { return true; } - virtual TopAbs_Orientation Orientation( const TopoDS_Shape& s ) const { return s.Orientation(); } - virtual bool IsOutsideOriented( TGeomID /*faceID*/ ) const { return true; } - void SetID( TGeomID id ) { _id = id; } - TGeomID ID() const { return _id; } - void SetHasInternalFaces( bool has ) { _hasInternalFaces = has; } - bool HasInternalFaces() const { return _hasInternalFaces; } - void SetConcave( TGeomID V, TGeomID F, TGeomID E1, TGeomID E2, TGeomID V1, TGeomID V2 ) - { _concaveVertex.Bind( V, ConcaveFace{ F, E1, E2, V1, V2 }); } - bool HasConcaveVertex() const { return !_concaveVertex.IsEmpty(); } - const ConcaveFace* GetConcave( TGeomID V ) const { return _concaveVertex.Seek( V ); } - }; - // -------------------------------------------------------------------------- - class OneOfSolids : public Solid - { - TColStd_MapOfInteger _subIDs; - TopTools_MapOfShape _faces; // keep FACE orientation - TColStd_MapOfInteger _outFaceIDs; // FACEs of shape_to_mesh oriented outside the SOLID - public: - void Init( const TopoDS_Shape& solid, - TopAbs_ShapeEnum subType, - const SMESHDS_Mesh* mesh ); - virtual bool Contains( TGeomID i ) const { return i == ID() || _subIDs.Contains( i ); } - virtual bool ContainsAny( const vector< TGeomID>& subIDs ) const - { - for ( size_t i = 0; i < subIDs.size(); ++i ) if ( Contains( subIDs[ i ])) return true; - return false; - } - virtual TopAbs_Orientation Orientation( const TopoDS_Shape& face ) const - { - const TopoDS_Shape& sInMap = const_cast< OneOfSolids* >(this)->_faces.Added( face ); - return sInMap.Orientation(); - } - virtual bool IsOutsideOriented( TGeomID faceID ) const - { - return faceID == 0 || _outFaceIDs.Contains( faceID ); - } - }; - // -------------------------------------------------------------------------- - /*! - * \brief Hold a vector of TGeomID and clear it at destruction - */ - class GeomIDVecHelder - { - typedef std::vector< TGeomID > TVector; - const TVector& myVec; - bool myOwn; - - public: - GeomIDVecHelder( const TVector& idVec, bool isOwner ): myVec( idVec ), myOwn( isOwner ) {} - GeomIDVecHelder( const GeomIDVecHelder& holder ): myVec( holder.myVec ), myOwn( holder.myOwn ) - { - const_cast< bool& >( holder.myOwn ) = false; - } - ~GeomIDVecHelder() { if ( myOwn ) const_cast( myVec ).clear(); } - size_t size() const { return myVec.size(); } - TGeomID operator[]( size_t i ) const { return i < size() ? myVec[i] : theUndefID; } - bool operator==( const GeomIDVecHelder& other ) const { return myVec == other.myVec; } - bool contain( const TGeomID& id ) const { - return std::find( myVec.begin(), myVec.end(), id ) != myVec.end(); - } - TGeomID otherThan( const TGeomID& id ) const { - for ( const TGeomID& id2 : myVec ) - if ( id != id2 ) - return id2; - return theUndefID; - } - TGeomID oneCommon( const GeomIDVecHelder& other ) const { - TGeomID common = theUndefID; - for ( const TGeomID& id : myVec ) - if ( other.contain( id )) - { - if ( common != theUndefID ) - return theUndefID; - common = id; - } - return common; - } - }; - // -------------------------------------------------------------------------- - /*! - * \brief Geom data - */ - struct Geometry - { - TopoDS_Shape _mainShape; - vector< vector< TGeomID > > _solidIDsByShapeID;// V/E/F ID -> SOLID IDs - Solid _soleSolid; - map< TGeomID, OneOfSolids > _solidByID; - TColStd_MapOfInteger _boundaryFaces; // FACEs on boundary of mesh->ShapeToMesh() - TColStd_MapOfInteger _strangeEdges; // EDGEs shared by strange FACEs - TGeomID _extIntFaceID; // pseudo FACE - extension of INTERNAL FACE - - TopTools_DataMapOfShapeInteger _shape2NbNodes; // nb of pre-existing nodes on shapes - - Controls::ElementsOnShape _edgeClassifier; - Controls::ElementsOnShape _vertexClassifier; - - bool IsOneSolid() const { return _solidByID.size() < 2; } - GeomIDVecHelder GetSolidIDsByShapeID( const vector< TGeomID >& shapeIDs ) const; - }; - // -------------------------------------------------------------------------- - /*! - * \brief Common data of any intersection between a Grid and a shape - */ - struct B_IntersectPoint - { - mutable const SMDS_MeshNode* _node; - mutable vector< TGeomID > _faceIDs; - - B_IntersectPoint(): _node(NULL) {} - bool Add( const vector< TGeomID >& fIDs, const SMDS_MeshNode* n=NULL ) const; - TGeomID HasCommonFace( const B_IntersectPoint * other, TGeomID avoidFace=-1 ) const; - size_t GetCommonFaces( const B_IntersectPoint * other, TGeomID * commonFaces ) const; - bool IsOnFace( TGeomID faceID ) const; - virtual ~B_IntersectPoint() {} - }; - // -------------------------------------------------------------------------- - /*! - * \brief Data of intersection between a GridLine and a TopoDS_Face - */ - struct F_IntersectPoint : public B_IntersectPoint - { - double _paramOnLine; - double _u, _v; - mutable Transition _transition; - mutable size_t _indexOnLine; - - bool operator< ( const F_IntersectPoint& o ) const { return _paramOnLine < o._paramOnLine; } - }; - // -------------------------------------------------------------------------- - /*! - * \brief Data of intersection between GridPlanes and a TopoDS_EDGE - */ - struct E_IntersectPoint : public B_IntersectPoint - { - gp_Pnt _point; - double _uvw[3]; - TGeomID _shapeID; // ID of EDGE or VERTEX - }; - // -------------------------------------------------------------------------- - /*! - * \brief A line of the grid and its intersections with 2D geometry - */ - struct GridLine - { - gp_Lin _line; - double _length; // line length - multiset< F_IntersectPoint > _intPoints; - - void RemoveExcessIntPoints( const double tol ); - TGeomID GetSolidIDBefore( multiset< F_IntersectPoint >::iterator ip, - const TGeomID prevID, - const Geometry& geom); - }; - // -------------------------------------------------------------------------- - /*! - * \brief Planes of the grid used to find intersections of an EDGE with a hexahedron - */ - struct GridPlanes - { - gp_XYZ _zNorm; - vector< gp_XYZ > _origins; // origin points of all planes in one direction - vector< double > _zProjs; // projections of origins to _zNorm - }; - // -------------------------------------------------------------------------- - /*! - * \brief Iterator on the parallel grid lines of one direction - */ - struct LineIndexer - { - size_t _size [3]; - size_t _curInd[3]; - size_t _iVar1, _iVar2, _iConst; - string _name1, _name2, _nameConst; - LineIndexer() {} - LineIndexer( size_t sz1, size_t sz2, size_t sz3, - size_t iv1, size_t iv2, size_t iConst, - const string& nv1, const string& nv2, const string& nConst ) - { - _size[0] = sz1; _size[1] = sz2; _size[2] = sz3; - _curInd[0] = _curInd[1] = _curInd[2] = 0; - _iVar1 = iv1; _iVar2 = iv2; _iConst = iConst; - _name1 = nv1; _name2 = nv2; _nameConst = nConst; - } - - size_t I() const { return _curInd[0]; } - size_t J() const { return _curInd[1]; } - size_t K() const { return _curInd[2]; } - void SetIJK( size_t i, size_t j, size_t k ) - { - _curInd[0] = i; _curInd[1] = j; _curInd[2] = k; - } - void SetLineIndex(size_t i) - { - _curInd[_iVar2] = i / _size[_iVar1]; - _curInd[_iVar1] = i % _size[_iVar1]; - } - void operator++() - { - if ( ++_curInd[_iVar1] == _size[_iVar1] ) - _curInd[_iVar1] = 0, ++_curInd[_iVar2]; - } - bool More() const { return _curInd[_iVar2] < _size[_iVar2]; } - size_t LineIndex () const { return _curInd[_iVar1] + _curInd[_iVar2]* _size[_iVar1]; } - size_t LineIndex10 () const { return (_curInd[_iVar1] + 1 ) + _curInd[_iVar2]* _size[_iVar1]; } - size_t LineIndex01 () const { return _curInd[_iVar1] + (_curInd[_iVar2] + 1 )* _size[_iVar1]; } - size_t LineIndex11 () const { return (_curInd[_iVar1] + 1 ) + (_curInd[_iVar2] + 1 )* _size[_iVar1]; } - void SetIndexOnLine (size_t i) { _curInd[ _iConst ] = i; } - bool IsValidIndexOnLine (size_t i) const { return i < _size[ _iConst ]; } - size_t NbLines() const { return _size[_iVar1] * _size[_iVar2]; } - }; - struct FaceGridIntersector; - // -------------------------------------------------------------------------- - /*! - * \brief Container of GridLine's - */ - struct Grid - { - vector< double > _coords[3]; // coordinates of grid nodes - gp_XYZ _axes [3]; // axis directions - vector< GridLine > _lines [3]; // in 3 directions - double _tol, _minCellSize; - gp_XYZ _origin; - gp_Mat _invB; // inverted basis of _axes - - // index shift within _nodes of nodes of a cell from the 1st node - int _nodeShift[8]; - - vector< const SMDS_MeshNode* > _nodes; // mesh nodes at grid nodes - vector< const SMDS_MeshNode* > _allBorderNodes; // mesh nodes between the bounding box and the geometry boundary - - vector< const F_IntersectPoint* > _gridIntP; // grid node intersection with geometry - ObjectPool< E_IntersectPoint > _edgeIntPool; // intersections with EDGEs - ObjectPool< F_IntersectPoint > _extIntPool; // intersections with extended INTERNAL FACEs - //list< E_IntersectPoint > _edgeIntP; // intersections with EDGEs - - Geometry _geometry; - bool _toAddEdges; - bool _toCreateFaces; - bool _toConsiderInternalFaces; - bool _toUseThresholdForInternalFaces; - double _sizeThreshold; - bool _toUseQuanta; - double _quanta; - - SMESH_MesherHelper* _helper; - - size_t CellIndex( size_t i, size_t j, size_t k ) const - { - return i + j*(_coords[0].size()-1) + k*(_coords[0].size()-1)*(_coords[1].size()-1); - } - size_t NodeIndex( size_t i, size_t j, size_t k ) const - { - return i + j*_coords[0].size() + k*_coords[0].size()*_coords[1].size(); - } - size_t NodeIndex( const TIJK& ijk ) const - { - return NodeIndex( ijk[0], ijk[1], ijk[2] ); - } - size_t NodeIndexDX() const { return 1; } - size_t NodeIndexDY() const { return _coords[0].size(); } - size_t NodeIndexDZ() const { return _coords[0].size() * _coords[1].size(); } - - LineIndexer GetLineIndexer(size_t iDir) const; - size_t GetLineDir( const GridLine* line, size_t & index ) const; - - E_IntersectPoint* Add( const E_IntersectPoint& ip ) - { - E_IntersectPoint* eip = _edgeIntPool.getNew(); - *eip = ip; - return eip; - } - void Remove( E_IntersectPoint* eip ) { _edgeIntPool.destroy( eip ); } - - TGeomID ShapeID( const TopoDS_Shape& s ) const; - const TopoDS_Shape& Shape( TGeomID id ) const; - TopAbs_ShapeEnum ShapeType( TGeomID id ) const { return Shape(id).ShapeType(); } - void InitGeometry( const TopoDS_Shape& theShape ); - void InitClassifier( const TopoDS_Shape& mainShape, - TopAbs_ShapeEnum shapeType, - Controls::ElementsOnShape& classifier ); - void GetEdgesToImplement( map< TGeomID, vector< TGeomID > > & edge2faceMap, - const TopoDS_Shape& shape, - const vector< TopoDS_Shape >& faces ); - void SetSolidFather( const TopoDS_Shape& s, const TopoDS_Shape& theShapeToMesh ); - bool IsShared( TGeomID faceID ) const; - bool IsAnyShared( const std::vector< TGeomID >& faceIDs ) const; - bool IsInternal( TGeomID faceID ) const { - return ( faceID == PseudoIntExtFaceID() || - Shape( faceID ).Orientation() == TopAbs_INTERNAL ); } - bool IsSolid( TGeomID shapeID ) const { - if ( _geometry.IsOneSolid() ) return _geometry._soleSolid.ID() == shapeID; - else return _geometry._solidByID.count( shapeID ); } - bool IsStrangeEdge( TGeomID id ) const { return _geometry._strangeEdges.Contains( id ); } - TGeomID PseudoIntExtFaceID() const { return _geometry._extIntFaceID; } - Solid* GetSolid( TGeomID solidID = 0 ); - Solid* GetOneOfSolids( TGeomID solidID ); - const vector< TGeomID > & GetSolidIDs( TGeomID subShapeID ) const; - bool IsCorrectTransition( TGeomID faceID, const Solid* solid ); - bool IsBoundaryFace( TGeomID face ) const { return _geometry._boundaryFaces.Contains( face ); } - void SetOnShape( const SMDS_MeshNode* n, const F_IntersectPoint& ip, - TopoDS_Vertex* vertex = nullptr, bool unset = false ); - void UpdateFacesOfVertex( const B_IntersectPoint& ip, const TopoDS_Vertex& vertex ); - bool IsToCheckNodePos() const { return !_toAddEdges && _toCreateFaces; } - bool IsToRemoveExcessEntities() const { return !_toAddEdges; } - - void SetCoordinates(const vector& xCoords, - const vector& yCoords, - const vector& zCoords, - const double* axesDirs, - const Bnd_Box& bndBox ); - void ComputeUVW(const gp_XYZ& p, double uvw[3]); - void ComputeNodes(SMESH_MesherHelper& helper); - }; - // -------------------------------------------------------------------------- - /*! - * \brief Return cells sharing a link - */ - struct CellsAroundLink - { - int _iDir; - int _dInd[4][3]; - size_t _nbCells[3]; - int _i,_j,_k; - Grid* _grid; - - CellsAroundLink( Grid* grid, int iDir ): - _iDir( iDir ), - _dInd{ {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }, - _nbCells{ grid->_coords[0].size() - 1, - grid->_coords[1].size() - 1, - grid->_coords[2].size() - 1 }, - _grid( grid ) - { - const int iDirOther[3][2] = {{ 1,2 },{ 0,2 },{ 0,1 }}; - _dInd[1][ iDirOther[iDir][0] ] = -1; - _dInd[2][ iDirOther[iDir][1] ] = -1; - _dInd[3][ iDirOther[iDir][0] ] = -1; _dInd[3][ iDirOther[iDir][1] ] = -1; - } - void Init( int i, int j, int k, int link12 = 0 ) - { - int iL = link12 % 4; - _i = i - _dInd[iL][0]; - _j = j - _dInd[iL][1]; - _k = k - _dInd[iL][2]; - } - bool GetCell( int iL, int& i, int& j, int& k, int& cellIndex, int& linkIndex ) - { - i = _i + _dInd[iL][0]; - j = _j + _dInd[iL][1]; - k = _k + _dInd[iL][2]; - if ( i < 0 || i >= (int)_nbCells[0] || - j < 0 || j >= (int)_nbCells[1] || - k < 0 || k >= (int)_nbCells[2] ) - return false; - cellIndex = _grid->CellIndex( i,j,k ); - linkIndex = iL + _iDir * 4; - return true; - } - }; - // -------------------------------------------------------------------------- - /*! - * \brief Intersector of TopoDS_Face with all GridLine's - */ - struct FaceGridIntersector - { - TopoDS_Face _face; - TGeomID _faceID; - Grid* _grid; - Bnd_Box _bndBox; - IntCurvesFace_Intersector* _surfaceInt; - vector< std::pair< GridLine*, F_IntersectPoint > > _intersections; - - FaceGridIntersector(): _grid(0), _surfaceInt(0) {} - void Intersect(); - - void StoreIntersections() - { - for ( size_t i = 0; i < _intersections.size(); ++i ) - { - multiset< F_IntersectPoint >::iterator ip = - _intersections[i].first->_intPoints.insert( _intersections[i].second ); - ip->_faceIDs.reserve( 1 ); - ip->_faceIDs.push_back( _faceID ); - } - } - const Bnd_Box& GetFaceBndBox() - { - GetCurveFaceIntersector(); - return _bndBox; - } - IntCurvesFace_Intersector* GetCurveFaceIntersector() - { - if ( !_surfaceInt ) - { - _surfaceInt = new IntCurvesFace_Intersector( _face, Precision::PConfusion() ); - _bndBox = _surfaceInt->Bounding(); - if ( _bndBox.IsVoid() ) - BRepBndLib::Add (_face, _bndBox); - } - return _surfaceInt; - } - bool IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const; - }; - // -------------------------------------------------------------------------- - /*! - * \brief Intersector of a surface with a GridLine - */ - struct FaceLineIntersector - { - double _tol; - double _u, _v, _w; // params on the face and the line - Transition _transition; // transition at intersection (see IntCurveSurface.cdl) - Transition _transIn, _transOut; // IN and OUT transitions depending of face orientation - - gp_Pln _plane; - gp_Cylinder _cylinder; - gp_Cone _cone; - gp_Sphere _sphere; - gp_Torus _torus; - IntCurvesFace_Intersector* _surfaceInt; - - vector< F_IntersectPoint > _intPoints; - - void IntersectWithPlane (const GridLine& gridLine); - void IntersectWithCylinder(const GridLine& gridLine); - void IntersectWithCone (const GridLine& gridLine); - void IntersectWithSphere (const GridLine& gridLine); - void IntersectWithTorus (const GridLine& gridLine); - void IntersectWithSurface (const GridLine& gridLine); - - bool UVIsOnFace() const; - void addIntPoint(const bool toClassify=true); - bool isParamOnLineOK( const double linLength ) - { - return -_tol < _w && _w < linLength + _tol; - } - FaceLineIntersector():_surfaceInt(0) {} - ~FaceLineIntersector() { if (_surfaceInt ) delete _surfaceInt; _surfaceInt = 0; } - }; - // -------------------------------------------------------------------------- - /*! - * \brief Class representing topology of the hexahedron and creating a mesh - * volume basing on analysis of hexahedron intersection with geometry - */ - class Hexahedron - { - // -------------------------------------------------------------------------------- - struct _Face; - struct _Link; - enum IsInternalFlag { IS_NOT_INTERNAL, IS_INTERNAL, IS_CUT_BY_INTERNAL_FACE }; - // -------------------------------------------------------------------------------- - struct _Node //!< node either at a hexahedron corner or at intersection - { - const SMDS_MeshNode* _node; // mesh node at hexahedron corner - const SMDS_MeshNode* _boundaryCornerNode; // missing mesh node due to hex truncation on the boundary - const B_IntersectPoint* _intPoint; - const _Face* _usedInFace; - char _isInternalFlags; - - _Node(const SMDS_MeshNode* n=0, const B_IntersectPoint* ip=0) - :_node(n), _intPoint(ip), _usedInFace(0), _isInternalFlags(0) {} - const SMDS_MeshNode* Node() const - { return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; } - const SMDS_MeshNode* BoundaryNode() const - { return _node ? _node : _boundaryCornerNode; } - const E_IntersectPoint* EdgeIntPnt() const - { return static_cast< const E_IntersectPoint* >( _intPoint ); } - const F_IntersectPoint* FaceIntPnt() const - { return static_cast< const F_IntersectPoint* >( _intPoint ); } - const vector< TGeomID >& faces() const { return _intPoint->_faceIDs; } - TGeomID face(size_t i) const { return _intPoint->_faceIDs[ i ]; } - void SetInternal( IsInternalFlag intFlag ) { _isInternalFlags |= intFlag; } - bool IsCutByInternal() const { return _isInternalFlags & IS_CUT_BY_INTERNAL_FACE; } - bool IsUsedInFace( const _Face* polygon = 0 ) - { - return polygon ? ( _usedInFace == polygon ) : bool( _usedInFace ); - } - TGeomID IsLinked( const B_IntersectPoint* other, - TGeomID avoidFace=-1 ) const // returns id of a common face - { - return _intPoint ? _intPoint->HasCommonFace( other, avoidFace ) : 0; - } - bool IsOnFace( TGeomID faceID ) const // returns true if faceID is found - { - return _intPoint ? _intPoint->IsOnFace( faceID ) : false; - } - size_t GetCommonFaces( const B_IntersectPoint * other, TGeomID* common ) const - { - return _intPoint && other ? _intPoint->GetCommonFaces( other, common ) : 0; - } - gp_Pnt Point() const - { - if ( const SMDS_MeshNode* n = Node() ) - return SMESH_NodeXYZ( n ); - if ( const E_IntersectPoint* eip = - dynamic_cast< const E_IntersectPoint* >( _intPoint )) - return eip->_point; - return gp_Pnt( 1e100, 0, 0 ); - } - TGeomID ShapeID() const - { - if ( const E_IntersectPoint* eip = dynamic_cast< const E_IntersectPoint* >( _intPoint )) - return eip->_shapeID; - return 0; - } - void Add( const E_IntersectPoint* ip ) - { - const std::lock_guard lock(_eMutex); - // Possible cases before Add(ip): - /// 1) _node != 0 --> _Node at hex corner ( _intPoint == 0 || _intPoint._node == 0 ) - /// 2) _node == 0 && _intPoint._node != 0 --> link intersected by FACE - /// 3) _node == 0 && _intPoint._node == 0 --> _Node at EDGE intersection - // - // If ip is added in cases 1) and 2) _node position must be changed to ip._shapeID - // at creation of elements - // To recognize this case, set _intPoint._node = Node() - const SMDS_MeshNode* node = Node(); - if ( !_intPoint ) { - _intPoint = ip; - } - else { - ip->Add( _intPoint->_faceIDs ); - _intPoint = ip; - } - if ( node ) - _node = _intPoint->_node = node; - } - }; - // -------------------------------------------------------------------------------- - struct _Link // link connecting two _Node's - { - _Node* _nodes[2]; - _Face* _faces[2]; // polygons sharing a link - vector< const F_IntersectPoint* > _fIntPoints; // GridLine intersections with FACEs - vector< _Node* > _fIntNodes; // _Node's at _fIntPoints - vector< _Link > _splits; - _Link(): _faces{ 0, 0 } {} - }; - // -------------------------------------------------------------------------------- - struct _OrientedLink - { - _Link* _link; - bool _reverse; - _OrientedLink( _Link* link=0, bool reverse=false ): _link(link), _reverse(reverse) {} - void Reverse() { _reverse = !_reverse; } - size_t NbResultLinks() const { return _link->_splits.size(); } - _OrientedLink ResultLink(int i) const - { - return _OrientedLink(&_link->_splits[_reverse ? NbResultLinks()-i-1 : i],_reverse); - } - _Node* FirstNode() const { return _link->_nodes[ _reverse ]; } - _Node* LastNode() const { return _link->_nodes[ !_reverse ]; } - operator bool() const { return _link; } - vector< TGeomID > GetNotUsedFace(const set& usedIDs ) const // returns supporting FACEs - { - vector< TGeomID > faces; - const B_IntersectPoint *ip0, *ip1; - if (( ip0 = _link->_nodes[0]->_intPoint ) && - ( ip1 = _link->_nodes[1]->_intPoint )) - { - for ( size_t i = 0; i < ip0->_faceIDs.size(); ++i ) - if ( ip1->IsOnFace ( ip0->_faceIDs[i] ) && - !usedIDs.count( ip0->_faceIDs[i] ) ) - faces.push_back( ip0->_faceIDs[i] ); - } - return faces; - } - bool HasEdgeNodes() const - { - return ( dynamic_cast< const E_IntersectPoint* >( _link->_nodes[0]->_intPoint ) || - dynamic_cast< const E_IntersectPoint* >( _link->_nodes[1]->_intPoint )); - } - int NbFaces() const - { - return !_link->_faces[0] ? 0 : 1 + bool( _link->_faces[1] ); - } - void AddFace( _Face* f ) - { - if ( _link->_faces[0] ) - { - _link->_faces[1] = f; - } - else - { - _link->_faces[0] = f; - _link->_faces[1] = 0; - } - } - void RemoveFace( _Face* f ) - { - if ( !_link->_faces[0] ) return; - - if ( _link->_faces[1] == f ) - { - _link->_faces[1] = 0; - } - else if ( _link->_faces[0] == f ) - { - _link->_faces[0] = 0; - if ( _link->_faces[1] ) - { - _link->_faces[0] = _link->_faces[1]; - _link->_faces[1] = 0; - } - } - } - }; - // -------------------------------------------------------------------------------- - struct _SplitIterator //! set to _hexLinks splits on one side of INTERNAL FACEs - { - struct _Split // data of a link split - { - int _linkID; // hex link ID - _Node* _nodes[2]; - int _iCheckIteration; // iteration where split is tried as Hexahedron split - _Link* _checkedSplit; // split set to hex links - bool _isUsed; // used in a volume - - _Split( _Link & split, int iLink ): - _linkID( iLink ), _nodes{ split._nodes[0], split._nodes[1] }, - _iCheckIteration( 0 ), _isUsed( false ) - {} - bool IsCheckedOrUsed( bool used ) const { return used ? _isUsed : _iCheckIteration > 0; } - }; - _Link* _hexLinks; - std::vector< _Split > _splits; - int _iterationNb; - size_t _nbChecked; - size_t _nbUsed; - std::vector< _Node* > _freeNodes; // nodes reached while composing a split set - - _SplitIterator( _Link* hexLinks ): - _hexLinks( hexLinks ), _iterationNb(0), _nbChecked(0), _nbUsed(0) - { - _freeNodes.reserve( 12 ); - _splits.reserve( 24 ); - for ( int iL = 0; iL < 12; ++iL ) - for ( size_t iS = 0; iS < _hexLinks[ iL ]._splits.size(); ++iS ) - _splits.emplace_back( _hexLinks[ iL ]._splits[ iS ], iL ); - Next(); - } - bool More() const { return _nbUsed < _splits.size(); } - bool Next(); - }; - // -------------------------------------------------------------------------------- - struct _Face - { - SMESH_Block::TShapeID _name; - vector< _OrientedLink > _links; // links on GridLine's - vector< _Link > _polyLinks; // links added to close a polygonal face - vector< _Node* > _eIntNodes; // nodes at intersection with EDGEs - - _Face():_name( SMESH_Block::ID_NONE ) - {} - bool IsPolyLink( const _OrientedLink& ol ) - { - return _polyLinks.empty() ? false : - ( &_polyLinks[0] <= ol._link && ol._link <= &_polyLinks.back() ); - } - void AddPolyLink(_Node* n0, _Node* n1, _Face* faceToFindEqual=0) - { - if ( faceToFindEqual && faceToFindEqual != this ) { - for ( size_t iL = 0; iL < faceToFindEqual->_polyLinks.size(); ++iL ) - if ( faceToFindEqual->_polyLinks[iL]._nodes[0] == n1 && - faceToFindEqual->_polyLinks[iL]._nodes[1] == n0 ) - { - _links.push_back - ( _OrientedLink( & faceToFindEqual->_polyLinks[iL], /*reverse=*/true )); - return; - } - } - _Link l; - l._nodes[0] = n0; - l._nodes[1] = n1; - _polyLinks.push_back( l ); - _links.push_back( _OrientedLink( &_polyLinks.back() )); - } - }; - // -------------------------------------------------------------------------------- - struct _volumeDef // holder of nodes of a volume mesh element - { - typedef void* _ptr; - - struct _nodeDef - { - const SMDS_MeshNode* _node; // mesh node at hexahedron corner - const B_IntersectPoint* _intPoint; - - _nodeDef(): _node(0), _intPoint(0) {} - _nodeDef( _Node* n ): _node( n->_node), _intPoint( n->_intPoint ) {} - const SMDS_MeshNode* Node() const - { return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; } - const E_IntersectPoint* EdgeIntPnt() const - { return static_cast< const E_IntersectPoint* >( _intPoint ); } - _ptr Ptr() const { return Node() ? (_ptr) Node() : (_ptr) EdgeIntPnt(); } - bool operator==(const _nodeDef& other ) const { return Ptr() == other.Ptr(); } - }; - - vector< _nodeDef > _nodes; - vector< int > _quantities; - _volumeDef* _next; // to store several _volumeDefs in a chain - TGeomID _solidID; - double _size; - const SMDS_MeshElement* _volume; // new volume - std::vector _brotherVolume; // produced due to poly split - - vector< SMESH_Block::TShapeID > _names; // name of side a polygon originates from - - _volumeDef(): _next(0), _solidID(0), _size(0), _volume(0) {} - ~_volumeDef() { delete _next; } - _volumeDef( _volumeDef& other ): - _next(0), _solidID( other._solidID ), _size( other._size ), _volume( other._volume ) - { _nodes.swap( other._nodes ); _quantities.swap( other._quantities ); other._volume = 0; - _names.swap( other._names ); } - - size_t size() const { return 1 + ( _next ? _next->size() : 0 ); } // nb _volumeDef in a chain - _volumeDef* at(int index) - { return index == 0 ? this : ( _next ? _next->at(index-1) : _next ); } - - void Set( _Node** nodes, int nb ) - { _nodes.assign( nodes, nodes + nb ); } - - void SetNext( _volumeDef* vd ) - { if ( _next ) { _next->SetNext( vd ); } else { _next = vd; }} - - bool IsEmpty() const { return (( _nodes.empty() ) && - ( !_next || _next->IsEmpty() )); } - bool IsPolyhedron() const { return ( !_quantities.empty() || - ( _next && !_next->_quantities.empty() )); } - - - struct _linkDef: public std::pair<_ptr,_ptr> // to join polygons in removeExcessSideDivision() - { - _nodeDef _node1;//, _node2; - mutable /*const */_linkDef *_prev, *_next; - size_t _loopIndex; - - _linkDef():_prev(0), _next(0) {} - - void init( const _nodeDef& n1, const _nodeDef& n2, size_t iLoop ) - { - _node1 = n1; //_node2 = n2; - _loopIndex = iLoop; - first = n1.Ptr(); - second = n2.Ptr(); - if ( first > second ) std::swap( first, second ); - } - void setNext( _linkDef* next ) - { - _next = next; - next->_prev = this; - } - }; - }; - - // topology of a hexahedron - _Node _hexNodes [8]; - _Link _hexLinks [12]; - _Face _hexQuads [6]; - - // faces resulted from hexahedron intersection - vector< _Face > _polygons; - - // intresections with EDGEs - vector< const E_IntersectPoint* > _eIntPoints; - - // additional nodes created at intersection points - vector< _Node > _intNodes; - - // nodes inside the hexahedron (at VERTEXes) refer to _intNodes - vector< _Node* > _vIntNodes; - - // computed volume elements - _volumeDef _volumeDefs; - - Grid* _grid; - double _sideLength[3]; - int _nbCornerNodes, _nbFaceIntNodes, _nbBndNodes; - int _origNodeInd; // index of _hexNodes[0] node within the _grid - size_t _i,_j,_k; - bool _hasTooSmall; - int _cellID; - - public: - Hexahedron(Grid* grid); - int MakeElements(SMESH_MesherHelper& helper, - const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap); - void computeElements( const Solid* solid = 0, int solidIndex = -1 ); - - private: - Hexahedron(const Hexahedron& other, size_t i, size_t j, size_t k, int cellID ); - void init( size_t i, size_t j, size_t k, const Solid* solid=0 ); - void init( size_t i ); - void setIJK( size_t i ); - bool compute( const Solid* solid, const IsInternalFlag intFlag ); - size_t getSolids( TGeomID ids[] ); - bool isCutByInternalFace( IsInternalFlag & maxFlag ); - void addEdges(SMESH_MesherHelper& helper, - vector< Hexahedron* >& intersectedHex, - const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap); - gp_Pnt findIntPoint( double u1, double proj1, double u2, double proj2, - double proj, BRepAdaptor_Curve& curve, - const gp_XYZ& axis, const gp_XYZ& origin ); - int getEntity( const E_IntersectPoint* ip, int* facets, int& sub ); - bool addIntersection( const E_IntersectPoint* ip, - vector< Hexahedron* >& hexes, - int ijk[], int dIJK[] ); - bool isQuadOnFace( const size_t iQuad ); - bool findChain( _Node* n1, _Node* n2, _Face& quad, vector<_Node*>& chainNodes ); - bool closePolygon( _Face* polygon, vector<_Node*>& chainNodes ) const; - bool findChainOnEdge( const vector< _OrientedLink >& splits, - const _OrientedLink& prevSplit, - const _OrientedLink& avoidSplit, - const std::set< TGeomID > & concaveFaces, - size_t & iS, - _Face& quad, - vector<_Node*>& chn); - int addVolumes(SMESH_MesherHelper& helper ); - void addFaces( SMESH_MesherHelper& helper, - const vector< const SMDS_MeshElement* > & boundaryVolumes ); - void addSegments( SMESH_MesherHelper& helper, - const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap ); - void getVolumes( vector< const SMDS_MeshElement* > & volumes ); - void getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryVolumes ); - void removeExcessSideDivision(const vector< Hexahedron* >& allHexa); - void removeExcessNodes(vector< Hexahedron* >& allHexa); - void preventVolumesOverlapping(); - TGeomID getAnyFace() const; - void cutByExtendedInternal( std::vector< Hexahedron* >& hexes, - const TColStd_MapOfInteger& intEdgeIDs ); - gp_Pnt mostDistantInternalPnt( int hexIndex, const gp_Pnt& p1, const gp_Pnt& p2 ); - bool isOutPoint( _Link& link, int iP, SMESH_MesherHelper& helper, const Solid* solid ) const; - void sortVertexNodes(vector<_Node*>& nodes, _Node* curNode, TGeomID face); - bool isInHole() const; - bool hasStrangeEdge() const; - bool checkPolyhedronSize( bool isCutByInternalFace, double & volSize ) const; - int checkPolyhedronValidity( _volumeDef* volDef, std::vector>& splitQuantities, - std::vector>& splitNodes ); - const SMDS_MeshElement* addPolyhedronToMesh( _volumeDef* volDef, SMESH_MesherHelper& helper, const std::vector& nodes, - const std::vector& quantities ); - bool addHexa (); - bool addTetra(); - bool addPenta(); - bool addPyra (); - bool debugDumpLink( _Link* link ); - _Node* findEqualNode( vector< _Node* >& nodes, - const E_IntersectPoint* ip, - const double tol2 ) - { - for ( size_t i = 0; i < nodes.size(); ++i ) - if ( nodes[i]->EdgeIntPnt() == ip || - nodes[i]->Point().SquareDistance( ip->_point ) <= tol2 ) - return nodes[i]; - return 0; - } - bool isCorner( const _Node* node ) const { return ( node >= &_hexNodes[0] && - node - &_hexNodes[0] < 8 ); } - bool hasEdgesAround( const ConcaveFace* cf ) const; - bool isImplementEdges() const { return _grid->_edgeIntPool.nbElements(); } - bool isOutParam(const double uvw[3]) const; - - typedef boost::container::flat_map< TGeomID, size_t > TID2Nb; - static void insertAndIncrement( TGeomID id, TID2Nb& id2nbMap ) - { - TID2Nb::value_type s0( id, 0 ); - TID2Nb::iterator id2nb = id2nbMap.insert( s0 ).first; - id2nb->second++; - } - }; // class Hexahedron - -#ifdef WITH_TBB - // -------------------------------------------------------------------------- - /*! - * \brief Hexahedron computing volumes in one thread - */ - struct ParallelHexahedron - { - vector< Hexahedron* >& _hexVec; - ParallelHexahedron( vector< Hexahedron* >& hv ): _hexVec(hv) {} - void operator() ( const tbb::blocked_range& r ) const - { - for ( size_t i = r.begin(); i != r.end(); ++i ) - if ( Hexahedron* hex = _hexVec[ i ] ) - hex->computeElements(); - } - }; - // -------------------------------------------------------------------------- - /*! - * \brief Structure intersecting certain nb of faces with GridLine's in one thread - */ - struct ParallelIntersector - { - vector< FaceGridIntersector >& _faceVec; - ParallelIntersector( vector< FaceGridIntersector >& faceVec): _faceVec(faceVec){} - void operator() ( const tbb::blocked_range& r ) const - { - for ( size_t i = r.begin(); i != r.end(); ++i ) - _faceVec[i].Intersect(); - } - }; -#endif - - //============================================================================= - // Implementation of internal utils - //============================================================================= - /*! - * \brief adjust \a i to have \a val between values[i] and values[i+1] - */ - inline void locateValue( int & i, double val, const vector& values, - int& di, double tol ) - { - //val += values[0]; // input \a val is measured from 0. - if ( i > (int) values.size()-2 ) - i = values.size()-2; - else - while ( i+2 < (int) values.size() && val > values[ i+1 ]) - ++i; - while ( i > 0 && val < values[ i ]) - --i; - - if ( i > 0 && val - values[ i ] < tol ) - di = -1; - else if ( i+2 < (int) values.size() && values[ i+1 ] - val < tol ) - di = 1; - else - di = 0; - } - //============================================================================= - /* - * Return a vector of SOLIDS sharing given shapes - */ - GeomIDVecHelder Geometry::GetSolidIDsByShapeID( const vector< TGeomID >& theShapeIDs ) const - { - if ( theShapeIDs.size() == 1 ) - return GeomIDVecHelder( _solidIDsByShapeID[ theShapeIDs[ 0 ]], /*owner=*/false ); - - // look for an empty slot in _solidIDsByShapeID - vector< TGeomID > * resultIDs = 0; - for ( const vector< TGeomID >& vec : _solidIDsByShapeID ) - if ( vec.empty() ) - { - resultIDs = const_cast< vector< TGeomID > * >( & vec ); - break; - } - // fill in resultIDs - for ( const TGeomID& id : theShapeIDs ) - for ( const TGeomID& solid : _solidIDsByShapeID[ id ]) - { - if ( std::find( resultIDs->begin(), resultIDs->end(), solid ) == resultIDs->end() ) - resultIDs->push_back( solid ); - } - return GeomIDVecHelder( *resultIDs, /*owner=*/true ); - } - //============================================================================= - /* - * Remove coincident intersection points - */ - void GridLine::RemoveExcessIntPoints( const double tol ) - { - if ( _intPoints.size() < 2 ) return; - - set< Transition > tranSet; - multiset< F_IntersectPoint >::iterator ip1, ip2 = _intPoints.begin(); - while ( ip2 != _intPoints.end() ) - { - tranSet.clear(); - ip1 = ip2++; - while ( ip2 != _intPoints.end() && ip2->_paramOnLine - ip1->_paramOnLine <= tol ) - { - tranSet.insert( ip1->_transition ); - tranSet.insert( ip2->_transition ); - ip2->Add( ip1->_faceIDs ); - _intPoints.erase( ip1 ); - ip1 = ip2++; - } - if ( tranSet.size() > 1 ) // points with different transition coincide - { - bool isIN = tranSet.count( Trans_IN ); - bool isOUT = tranSet.count( Trans_OUT ); - if ( isIN && isOUT ) - (*ip1)._transition = Trans_TANGENT; - else - (*ip1)._transition = isIN ? Trans_IN : Trans_OUT; - } - } - } - //================================================================================ - /* - * Return ID of SOLID for nodes before the given intersection point - */ - TGeomID GridLine::GetSolidIDBefore( multiset< F_IntersectPoint >::iterator ip, - const TGeomID prevID, - const Geometry& geom ) - { - if ( ip == _intPoints.begin() ) - return 0; - - if ( geom.IsOneSolid() ) - { - bool isOut = true; - switch ( ip->_transition ) { - case Trans_IN: isOut = true; break; - case Trans_OUT: isOut = false; break; - case Trans_TANGENT: isOut = ( prevID != 0 ); break; - case Trans_APEX: - { - // singularity point (apex of a cone) - multiset< F_IntersectPoint >::iterator ipBef = ip, ipAft = ++ip; - if ( ipAft == _intPoints.end() ) - isOut = false; - else - { - --ipBef; - if ( ipBef->_transition != ipAft->_transition ) - isOut = ( ipBef->_transition == Trans_OUT ); - else - isOut = ( ipBef->_transition != Trans_OUT ); - } - break; - } - case Trans_INTERNAL: isOut = false; - default:; - } - return isOut ? 0 : geom._soleSolid.ID(); - } - - GeomIDVecHelder solids = geom.GetSolidIDsByShapeID( ip->_faceIDs ); - - --ip; - if ( ip->_transition == Trans_INTERNAL ) - return prevID; - - GeomIDVecHelder solidsBef = geom.GetSolidIDsByShapeID( ip->_faceIDs ); - - if ( ip->_transition == Trans_IN || - ip->_transition == Trans_OUT ) - { - if ( solidsBef.size() == 1 ) - { - if ( solidsBef[0] == prevID ) - return ip->_transition == Trans_OUT ? 0 : solidsBef[0]; - else - return solidsBef[0]; - } - - if ( solids.size() == 2 ) - { - if ( solids == solidsBef ) - return solids.contain( prevID ) ? solids.otherThan( prevID ) : theUndefID; // bos #29212 - } - return solids.oneCommon( solidsBef ); - } - - if ( solidsBef.size() == 1 ) - return solidsBef[0]; - - return solids.oneCommon( solidsBef ); - } - //================================================================================ - /* - * Adds face IDs - */ - bool B_IntersectPoint::Add( const vector< TGeomID >& fIDs, - const SMDS_MeshNode* n) const - { - const std::lock_guard lock(_bMutex); - size_t prevNbF = _faceIDs.size(); - - if ( _faceIDs.empty() ) - _faceIDs = fIDs; - else - for ( size_t i = 0; i < fIDs.size(); ++i ) - { - vector< TGeomID >::iterator it = - std::find( _faceIDs.begin(), _faceIDs.end(), fIDs[i] ); - if ( it == _faceIDs.end() ) - _faceIDs.push_back( fIDs[i] ); - } - if ( !_node && n != NULL ) - _node = n; - - return prevNbF < _faceIDs.size(); - } - //================================================================================ - /* - * Return ID of a common face if any, else zero - */ - TGeomID B_IntersectPoint::HasCommonFace( const B_IntersectPoint * other, TGeomID avoidFace ) const - { - if ( other ) - for ( size_t i = 0; i < other->_faceIDs.size(); ++i ) - if ( avoidFace != other->_faceIDs[i] && - IsOnFace ( other->_faceIDs[i] )) - return other->_faceIDs[i]; - return 0; - } - //================================================================================ - /* - * Return faces common with other point - */ - size_t B_IntersectPoint::GetCommonFaces( const B_IntersectPoint * other, TGeomID* common ) const - { - size_t nbComm = 0; - if ( !other ) - return nbComm; - if ( _faceIDs.size() > other->_faceIDs.size() ) - return other->GetCommonFaces( this, common ); - for ( const TGeomID& face : _faceIDs ) - if ( other->IsOnFace( face )) - common[ nbComm++ ] = face; - return nbComm; - } - //================================================================================ - /* - * Return \c true if \a faceID in in this->_faceIDs - */ - bool B_IntersectPoint::IsOnFace( TGeomID faceID ) const // returns true if faceID is found - { - vector< TGeomID >::const_iterator it = - std::find( _faceIDs.begin(), _faceIDs.end(), faceID ); - return ( it != _faceIDs.end() ); - } - //================================================================================ - /* - * OneOfSolids initialization - */ - void OneOfSolids::Init( const TopoDS_Shape& solid, - TopAbs_ShapeEnum subType, - const SMESHDS_Mesh* mesh ) - { - SetID( mesh->ShapeToIndex( solid )); - - if ( subType == TopAbs_FACE ) - SetHasInternalFaces( false ); - - for ( TopExp_Explorer sub( solid, subType ); sub.More(); sub.Next() ) - { - _subIDs.Add( mesh->ShapeToIndex( sub.Current() )); - if ( subType == TopAbs_FACE ) - { - _faces.Add( sub.Current() ); - if ( sub.Current().Orientation() == TopAbs_INTERNAL ) - SetHasInternalFaces( true ); - - TGeomID faceID = mesh->ShapeToIndex( sub.Current() ); - if ( sub.Current().Orientation() == TopAbs_INTERNAL || - sub.Current().Orientation() == mesh->IndexToShape( faceID ).Orientation() ) - _outFaceIDs.Add( faceID ); - } - } - } - //================================================================================ - /* - * Return an iterator on GridLine's in a given direction - */ - LineIndexer Grid::GetLineIndexer(size_t iDir) const - { - const size_t indices[] = { 1,2,0, 0,2,1, 0,1,2 }; - const string s [] = { "X", "Y", "Z" }; - LineIndexer li( _coords[0].size(), _coords[1].size(), _coords[2].size(), - indices[iDir*3], indices[iDir*3+1], indices[iDir*3+2], - s[indices[iDir*3]], s[indices[iDir*3+1]], s[indices[iDir*3+2]]); - return li; - } - //================================================================================ - /* - * Return direction [0,1,2] of a GridLine - */ - size_t Grid::GetLineDir( const GridLine* line, size_t & index ) const - { - for ( size_t iDir = 0; iDir < 3; ++iDir ) - if ( &_lines[ iDir ][0] <= line && line <= &_lines[ iDir ].back() ) - { - index = line - &_lines[ iDir ][0]; - return iDir; - } - return -1; - } - //============================================================================= - /* - * Creates GridLine's of the grid - */ - void Grid::SetCoordinates(const vector& xCoords, - const vector& yCoords, - const vector& zCoords, - const double* axesDirs, - const Bnd_Box& shapeBox) - { - _coords[0] = xCoords; - _coords[1] = yCoords; - _coords[2] = zCoords; - - _axes[0].SetCoord( axesDirs[0], - axesDirs[1], - axesDirs[2]); - _axes[1].SetCoord( axesDirs[3], - axesDirs[4], - axesDirs[5]); - _axes[2].SetCoord( axesDirs[6], - axesDirs[7], - axesDirs[8]); - _axes[0].Normalize(); - _axes[1].Normalize(); - _axes[2].Normalize(); - - _invB.SetCols( _axes[0], _axes[1], _axes[2] ); - _invB.Invert(); - - // compute tolerance - _minCellSize = Precision::Infinite(); - for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions - { - for ( size_t i = 1; i < _coords[ iDir ].size(); ++i ) - { - double cellLen = _coords[ iDir ][ i ] - _coords[ iDir ][ i-1 ]; - if ( cellLen < _minCellSize ) - _minCellSize = cellLen; - } - } - if ( _minCellSize < Precision::Confusion() ) - throw SMESH_ComputeError (COMPERR_ALGO_FAILED, - SMESH_Comment("Too small cell size: ") << _minCellSize ); - _tol = _minCellSize / 1000.; - - // attune grid extremities to shape bounding box - - double sP[6]; // aXmin, aYmin, aZmin, aXmax, aYmax, aZmax - shapeBox.Get(sP[0],sP[1],sP[2],sP[3],sP[4],sP[5]); - double* cP[6] = { &_coords[0].front(), &_coords[1].front(), &_coords[2].front(), - &_coords[0].back(), &_coords[1].back(), &_coords[2].back() }; - for ( int i = 0; i < 6; ++i ) - if ( fabs( sP[i] - *cP[i] ) < _tol ) - *cP[i] = sP[i];// + _tol/1000. * ( i < 3 ? +1 : -1 ); - - for ( int iDir = 0; iDir < 3; ++iDir ) - { - if ( _coords[iDir][0] - sP[iDir] > _tol ) - { - _minCellSize = Min( _minCellSize, _coords[iDir][0] - sP[iDir] ); - _coords[iDir].insert( _coords[iDir].begin(), sP[iDir] + _tol/1000.); - } - if ( sP[iDir+3] - _coords[iDir].back() > _tol ) - { - _minCellSize = Min( _minCellSize, sP[iDir+3] - _coords[iDir].back() ); - _coords[iDir].push_back( sP[iDir+3] - _tol/1000.); - } - } - _tol = _minCellSize / 1000.; - - _origin = ( _coords[0][0] * _axes[0] + - _coords[1][0] * _axes[1] + - _coords[2][0] * _axes[2] ); - - // create lines - for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions - { - LineIndexer li = GetLineIndexer( iDir ); - _lines[iDir].resize( li.NbLines() ); - double len = _coords[ iDir ].back() - _coords[iDir].front(); - for ( ; li.More(); ++li ) - { - GridLine& gl = _lines[iDir][ li.LineIndex() ]; - gl._line.SetLocation( _coords[0][li.I()] * _axes[0] + - _coords[1][li.J()] * _axes[1] + - _coords[2][li.K()] * _axes[2] ); - gl._line.SetDirection( _axes[ iDir ]); - gl._length = len; - } - } - } - //================================================================================ - /* - * Return local ID of shape - */ - TGeomID Grid::ShapeID( const TopoDS_Shape& s ) const - { - return _helper->GetMeshDS()->ShapeToIndex( s ); - } - //================================================================================ - /* - * Return a shape by its local ID - */ - const TopoDS_Shape& Grid::Shape( TGeomID id ) const - { - return _helper->GetMeshDS()->IndexToShape( id ); - } - //================================================================================ - /* - * Initialize _geometry - */ - void Grid::InitGeometry( const TopoDS_Shape& theShapeToMesh ) - { - SMESH_Mesh* mesh = _helper->GetMesh(); - - _geometry._mainShape = theShapeToMesh; - _geometry._extIntFaceID = mesh->GetMeshDS()->MaxShapeIndex() * 100; - _geometry._soleSolid.SetID( 0 ); - _geometry._soleSolid.SetHasInternalFaces( false ); - - InitClassifier( theShapeToMesh, TopAbs_VERTEX, _geometry._vertexClassifier ); - InitClassifier( theShapeToMesh, TopAbs_EDGE , _geometry._edgeClassifier ); - - TopExp_Explorer solidExp( theShapeToMesh, TopAbs_SOLID ); - - bool isSeveralSolids = false; - if ( _toConsiderInternalFaces ) // check nb SOLIDs - { - solidExp.Next(); - isSeveralSolids = solidExp.More(); - _toConsiderInternalFaces = isSeveralSolids; - solidExp.ReInit(); - - if ( !isSeveralSolids ) // look for an internal FACE - { - TopExp_Explorer fExp( theShapeToMesh, TopAbs_FACE ); - for ( ; fExp.More() && !_toConsiderInternalFaces; fExp.Next() ) - _toConsiderInternalFaces = ( fExp.Current().Orientation() == TopAbs_INTERNAL ); - - _geometry._soleSolid.SetHasInternalFaces( _toConsiderInternalFaces ); - _geometry._soleSolid.SetID( ShapeID( solidExp.Current() )); - } - else // fill Geometry::_solidByID - { - for ( ; solidExp.More(); solidExp.Next() ) - { - OneOfSolids & solid = _geometry._solidByID[ ShapeID( solidExp.Current() )]; - solid.Init( solidExp.Current(), TopAbs_FACE, mesh->GetMeshDS() ); - solid.Init( solidExp.Current(), TopAbs_EDGE, mesh->GetMeshDS() ); - solid.Init( solidExp.Current(), TopAbs_VERTEX, mesh->GetMeshDS() ); - } - } - } - else - { - _geometry._soleSolid.SetID( ShapeID( solidExp.Current() )); - } - - if ( !_toCreateFaces ) - { - int nbSolidsGlobal = _helper->Count( mesh->GetShapeToMesh(), TopAbs_SOLID, false ); - int nbSolidsLocal = _helper->Count( theShapeToMesh, TopAbs_SOLID, false ); - _toCreateFaces = ( nbSolidsLocal < nbSolidsGlobal ); - } - - TopTools_IndexedMapOfShape faces; - TopExp::MapShapes( theShapeToMesh, TopAbs_FACE, faces ); - - // find boundary FACEs on boundary of mesh->ShapeToMesh() - if ( _toCreateFaces ) - for ( int i = 1; i <= faces.Size(); ++i ) - if ( faces(i).Orientation() != TopAbs_INTERNAL && - _helper->NbAncestors( faces(i), *mesh, TopAbs_SOLID ) == 1 ) - { - _geometry._boundaryFaces.Add( ShapeID( faces(i) )); - } - - if ( isSeveralSolids ) - for ( int i = 1; i <= faces.Size(); ++i ) - { - SetSolidFather( faces(i), theShapeToMesh ); - for ( TopExp_Explorer eExp( faces(i), TopAbs_EDGE ); eExp.More(); eExp.Next() ) - { - const TopoDS_Edge& edge = TopoDS::Edge( eExp.Current() ); - SetSolidFather( edge, theShapeToMesh ); - SetSolidFather( _helper->IthVertex( 0, edge ), theShapeToMesh ); - SetSolidFather( _helper->IthVertex( 1, edge ), theShapeToMesh ); - } - } - - // fill in _geometry._shape2NbNodes == find already meshed sub-shapes - _geometry._shape2NbNodes.Clear(); - if ( mesh->NbNodes() > 0 ) - { - for ( TopAbs_ShapeEnum type : { TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX }) - for ( TopExp_Explorer exp( theShapeToMesh, type ); exp.More(); exp.Next() ) - { - if ( _geometry._shape2NbNodes.IsBound( exp.Current() )) - continue; - if ( SMESHDS_SubMesh* sm = mesh->GetMeshDS()->MeshElements( exp.Current() )) - if ( sm->NbNodes() > 0 ) - _geometry._shape2NbNodes.Bind( exp.Current(), sm->NbNodes() ); - } - } - - // fill in Solid::_concaveVertex - vector< TGeomID > soleSolidID( 1, _geometry._soleSolid.ID() ); - for ( int i = 1; i <= faces.Size(); ++i ) - { - const TopoDS_Face& F = TopoDS::Face( faces( i )); - TError error; - TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *mesh, 0, error, - nullptr, nullptr, false ); - for ( StdMeshers_FaceSidePtr& wire : wires ) - { - const int nbEdges = wire->NbEdges(); - if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wire->Edge(0))) - continue; - for ( int iE1 = 0; iE1 < nbEdges; ++iE1 ) - { - if ( SMESH_Algo::isDegenerated( wire->Edge( iE1 ))) continue; - int iE2 = ( iE1 + 1 ) % nbEdges; - while ( SMESH_Algo::isDegenerated( wire->Edge( iE2 ))) - iE2 = ( iE2 + 1 ) % nbEdges; - TopoDS_Vertex V = wire->FirstVertex( iE2 ); - double angle = _helper->GetAngle( wire->Edge( iE1 ), - wire->Edge( iE2 ), F, V ); - if ( angle < -5. * M_PI / 180. ) - { - TGeomID faceID = ShapeID( F ); - const vector< TGeomID > & solids = - _geometry.IsOneSolid() ? soleSolidID : GetSolidIDs( faceID ); - for ( const TGeomID & solidID : solids ) - { - Solid* solid = GetSolid( solidID ); - TGeomID V1 = ShapeID( wire->FirstVertex( iE1 )); - TGeomID V2 = ShapeID( wire->LastVertex ( iE2 )); - solid->SetConcave( ShapeID( V ), faceID, - wire->EdgeID( iE1 ), wire->EdgeID( iE2 ), V1, V2 ); - } - } - } - } - } - - return; - } - //================================================================================ - /* - * Store ID of SOLID as father of its child shape ID - */ - void Grid::SetSolidFather( const TopoDS_Shape& s, const TopoDS_Shape& theShapeToMesh ) - { - if ( _geometry._solidIDsByShapeID.empty() ) - _geometry._solidIDsByShapeID.resize( _helper->GetMeshDS()->MaxShapeIndex() + 1 ); - - vector< TGeomID > & solidIDs = _geometry._solidIDsByShapeID[ ShapeID( s )]; - if ( !solidIDs.empty() ) - return; - solidIDs.reserve(2); - PShapeIteratorPtr solidIt = _helper->GetAncestors( s, - *_helper->GetMesh(), - TopAbs_SOLID, - & theShapeToMesh ); - while ( const TopoDS_Shape* solid = solidIt->next() ) - solidIDs.push_back( ShapeID( *solid )); - } - //================================================================================ - /* - * Return IDs of solids given sub-shape belongs to - */ - const vector< TGeomID > & Grid::GetSolidIDs( TGeomID subShapeID ) const - { - return _geometry._solidIDsByShapeID[ subShapeID ]; - } - //================================================================================ - /* - * Check if a sub-shape belongs to several SOLIDs - */ - bool Grid::IsShared( TGeomID shapeID ) const - { - return !_geometry.IsOneSolid() && ( _geometry._solidIDsByShapeID[ shapeID ].size() > 1 ); - } - //================================================================================ - /* - * Check if any of FACEs belongs to several SOLIDs - */ - bool Grid::IsAnyShared( const std::vector< TGeomID >& faceIDs ) const - { - for ( size_t i = 0; i < faceIDs.size(); ++i ) - if ( IsShared( faceIDs[ i ])) - return true; - return false; - } - //================================================================================ - /* - * Return Solid by ID - */ - Solid* Grid::GetSolid( TGeomID solidID ) - { - if ( !solidID || _geometry.IsOneSolid() || _geometry._solidByID.empty() ) - return & _geometry._soleSolid; - - return & _geometry._solidByID[ solidID ]; - } - //================================================================================ - /* - * Return OneOfSolids by ID - */ - Solid* Grid::GetOneOfSolids( TGeomID solidID ) - { - map< TGeomID, OneOfSolids >::iterator is2s = _geometry._solidByID.find( solidID ); - if ( is2s != _geometry._solidByID.end() ) - return & is2s->second; - - return & _geometry._soleSolid; - } - //================================================================================ - /* - * Check if transition on given FACE is correct for a given SOLID - */ - bool Grid::IsCorrectTransition( TGeomID faceID, const Solid* solid ) - { - if ( _geometry.IsOneSolid() ) - return true; - - const vector< TGeomID >& solidIDs = _geometry._solidIDsByShapeID[ faceID ]; - return solidIDs[0] == solid->ID(); - } - - //================================================================================ - /* - * Assign to geometry a node at FACE intersection - * Return a found supporting VERTEX - */ - void Grid::SetOnShape( const SMDS_MeshNode* n, const F_IntersectPoint& ip, - TopoDS_Vertex* vertex, bool unset ) - { - TopoDS_Shape s; - SMESHDS_Mesh* mesh = _helper->GetMeshDS(); - if ( ip._faceIDs.size() == 1 ) - { - mesh->SetNodeOnFace( n, ip._faceIDs[0], ip._u, ip._v ); - } - else if ( _geometry._vertexClassifier.IsSatisfy( n, &s )) - { - if ( unset ) mesh->UnSetNodeOnShape( n ); - mesh->SetNodeOnVertex( n, TopoDS::Vertex( s )); - if ( vertex ) - *vertex = TopoDS::Vertex( s ); - } - else if ( _geometry._edgeClassifier.IsSatisfy( n, &s )) - { - if ( unset ) mesh->UnSetNodeOnShape( n ); - mesh->SetNodeOnEdge( n, TopoDS::Edge( s )); - } - else if ( ip._faceIDs.size() > 0 ) - { - mesh->SetNodeOnFace( n, ip._faceIDs[0], ip._u, ip._v ); - } - else if ( !unset && _geometry.IsOneSolid() ) - { - mesh->SetNodeInVolume( n, _geometry._soleSolid.ID() ); - } - } - //================================================================================ - /* - * Fill in B_IntersectPoint::_faceIDs with all FACEs sharing a VERTEX - */ - void Grid::UpdateFacesOfVertex( const B_IntersectPoint& ip, const TopoDS_Vertex& vertex ) - { - if ( vertex.IsNull() ) - return; - std::vector< int > faceID(1); - PShapeIteratorPtr fIt = _helper->GetAncestors( vertex, *_helper->GetMesh(), - TopAbs_FACE, & _geometry._mainShape ); - while ( const TopoDS_Shape* face = fIt->next() ) - { - faceID[ 0 ] = ShapeID( *face ); - ip.Add( faceID ); - } - } - //================================================================================ - /* - * Initialize a classifier - */ - void Grid::InitClassifier( const TopoDS_Shape& mainShape, - TopAbs_ShapeEnum shapeType, - Controls::ElementsOnShape& classifier ) - { - TopTools_IndexedMapOfShape shapes; - TopExp::MapShapes( mainShape, shapeType, shapes ); - - TopoDS_Compound compound; BRep_Builder builder; - builder.MakeCompound( compound ); - for ( int i = 1; i <= shapes.Size(); ++i ) - builder.Add( compound, shapes(i) ); - - classifier.SetMesh( _helper->GetMeshDS() ); - //classifier.SetTolerance( _tol ); // _tol is not initialised - classifier.SetShape( compound, SMDSAbs_Node ); - } - - //================================================================================ - /* - * Return EDGEs with FACEs to implement into the mesh - */ - void Grid::GetEdgesToImplement( map< TGeomID, vector< TGeomID > > & edge2faceIDsMap, - const TopoDS_Shape& shape, - const vector< TopoDS_Shape >& faces ) - { - // check if there are strange EDGEs - TopTools_IndexedMapOfShape faceMap; - TopExp::MapShapes( _helper->GetMesh()->GetShapeToMesh(), TopAbs_FACE, faceMap ); - int nbFacesGlobal = faceMap.Size(); - faceMap.Clear( false ); - TopExp::MapShapes( shape, TopAbs_FACE, faceMap ); - int nbFacesLocal = faceMap.Size(); - bool hasStrangeEdges = ( nbFacesGlobal > nbFacesLocal ); - if ( !_toAddEdges && !hasStrangeEdges ) - return; // no FACEs in contact with those meshed by other algo - - for ( size_t i = 0; i < faces.size(); ++i ) - { - _helper->SetSubShape( faces[i] ); - for ( TopExp_Explorer eExp( faces[i], TopAbs_EDGE ); eExp.More(); eExp.Next() ) - { - const TopoDS_Edge& edge = TopoDS::Edge( eExp.Current() ); - if ( hasStrangeEdges ) - { - bool hasStrangeFace = false; - PShapeIteratorPtr faceIt = _helper->GetAncestors( edge, *_helper->GetMesh(), TopAbs_FACE); - while ( const TopoDS_Shape* face = faceIt->next() ) - if (( hasStrangeFace = !faceMap.Contains( *face ))) - break; - if ( !hasStrangeFace && !_toAddEdges ) - continue; - _geometry._strangeEdges.Add( ShapeID( edge )); - _geometry._strangeEdges.Add( ShapeID( _helper->IthVertex( 0, edge ))); - _geometry._strangeEdges.Add( ShapeID( _helper->IthVertex( 1, edge ))); - } - if ( !SMESH_Algo::isDegenerated( edge ) && - !_helper->IsRealSeam( edge )) - { - edge2faceIDsMap[ ShapeID( edge )].push_back( ShapeID( faces[i] )); - } - } - } - return; - } - - //================================================================================ - /* - * Computes coordinates of a point in the grid CS - */ - void Grid::ComputeUVW(const gp_XYZ& P, double UVW[3]) - { - gp_XYZ p = P * _invB; - p.Coord( UVW[0], UVW[1], UVW[2] ); - } - //================================================================================ - /* - * Creates all nodes - */ - void Grid::ComputeNodes(SMESH_MesherHelper& helper) - { - // state of each node of the grid relative to the geometry - const size_t nbGridNodes = _coords[0].size() * _coords[1].size() * _coords[2].size(); - vector< TGeomID > shapeIDVec( nbGridNodes, theUndefID ); - _nodes.resize( nbGridNodes, 0 ); - _allBorderNodes.resize( nbGridNodes, 0 ); - _gridIntP.resize( nbGridNodes, NULL ); - - SMESHDS_Mesh* mesh = helper.GetMeshDS(); - - for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions - { - LineIndexer li = GetLineIndexer( iDir ); - - // find out a shift of node index while walking along a GridLine in this direction - li.SetIndexOnLine( 0 ); - size_t nIndex0 = NodeIndex( li.I(), li.J(), li.K() ); - li.SetIndexOnLine( 1 ); - const size_t nShift = NodeIndex( li.I(), li.J(), li.K() ) - nIndex0; - - const vector & coords = _coords[ iDir ]; - for ( ; li.More(); ++li ) // loop on lines in iDir - { - li.SetIndexOnLine( 0 ); - nIndex0 = NodeIndex( li.I(), li.J(), li.K() ); - - GridLine& line = _lines[ iDir ][ li.LineIndex() ]; - const gp_XYZ lineLoc = line._line.Location().XYZ(); - const gp_XYZ lineDir = line._line.Direction().XYZ(); - - line.RemoveExcessIntPoints( _tol ); - multiset< F_IntersectPoint >& intPnts = line._intPoints; - multiset< F_IntersectPoint >::iterator ip = intPnts.begin(); - - // Create mesh nodes at intersections with geometry - // and set OUT state of nodes between intersections - - TGeomID solidID = 0; - const double* nodeCoord = & coords[0]; - const double* coord0 = nodeCoord; - const double* coordEnd = coord0 + coords.size(); - double nodeParam = 0; - for ( ; ip != intPnts.end(); ++ip ) - { - solidID = line.GetSolidIDBefore( ip, solidID, _geometry ); - - // set OUT state or just skip IN nodes before ip - if ( nodeParam < ip->_paramOnLine - _tol ) - { - while ( nodeParam < ip->_paramOnLine - _tol ) - { - TGeomID & nodeShapeID = shapeIDVec[ nIndex0 + nShift * ( nodeCoord-coord0 ) ]; - nodeShapeID = Min( solidID, nodeShapeID ); - if ( ++nodeCoord < coordEnd ) - nodeParam = *nodeCoord - *coord0; - else - break; - } - if ( nodeCoord == coordEnd ) break; - } - - // create a mesh node on a GridLine at ip if it does not coincide with a grid node - if ( nodeParam > ip->_paramOnLine + _tol ) - { - gp_XYZ xyz = lineLoc + ip->_paramOnLine * lineDir; - ip->_node = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); - ip->_indexOnLine = nodeCoord-coord0-1; - TopoDS_Vertex v; - SetOnShape( ip->_node, *ip, & v ); - UpdateFacesOfVertex( *ip, v ); - } - // create a mesh node at ip coincident with a grid node - else - { - int nodeIndex = nIndex0 + nShift * ( nodeCoord-coord0 ); - if ( !_nodes[ nodeIndex ] ) - { - gp_XYZ xyz = lineLoc + nodeParam * lineDir; - _nodes [ nodeIndex ] = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); - //_gridIntP[ nodeIndex ] = & * ip; - //SetOnShape( _nodes[ nodeIndex ], *ip ); - } - if ( _gridIntP[ nodeIndex ] ) - _gridIntP[ nodeIndex ]->Add( ip->_faceIDs ); - else - _gridIntP[ nodeIndex ] = & * ip; - // ip->_node = _nodes[ nodeIndex ]; -- to differ from ip on links - ip->_indexOnLine = nodeCoord-coord0; - if ( ++nodeCoord < coordEnd ) - nodeParam = *nodeCoord - *coord0; - } - } - // set OUT state to nodes after the last ip - for ( ; nodeCoord < coordEnd; ++nodeCoord ) - shapeIDVec[ nIndex0 + nShift * ( nodeCoord-coord0 ) ] = 0; - } - } - - // Create mesh nodes at !OUT nodes of the grid - - for ( size_t z = 0; z < _coords[2].size(); ++z ) - for ( size_t y = 0; y < _coords[1].size(); ++y ) - for ( size_t x = 0; x < _coords[0].size(); ++x ) - { - size_t nodeIndex = NodeIndex( x, y, z ); - if ( !_nodes[ nodeIndex ] && - 0 < shapeIDVec[ nodeIndex ] && shapeIDVec[ nodeIndex ] < theUndefID ) - { - gp_XYZ xyz = ( _coords[0][x] * _axes[0] + - _coords[1][y] * _axes[1] + - _coords[2][z] * _axes[2] ); - _nodes[ nodeIndex ] = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); - mesh->SetNodeInVolume( _nodes[ nodeIndex ], shapeIDVec[ nodeIndex ]); - } - else if ( _nodes[ nodeIndex ] && _gridIntP[ nodeIndex ] /*&& - !_nodes[ nodeIndex]->GetShapeID()*/ ) - { - TopoDS_Vertex v; - SetOnShape( _nodes[ nodeIndex ], *_gridIntP[ nodeIndex ], & v ); - UpdateFacesOfVertex( *_gridIntP[ nodeIndex ], v ); - } - else if ( _toUseQuanta && !_allBorderNodes[ nodeIndex ] /*add all nodes outside the body. Used to reconstruct the hexahedrals when polys are not desired!*/) - { - gp_XYZ xyz = ( _coords[0][x] * _axes[0] + - _coords[1][y] * _axes[1] + - _coords[2][z] * _axes[2] ); - _allBorderNodes[ nodeIndex ] = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); - mesh->SetNodeInVolume( _allBorderNodes[ nodeIndex ], shapeIDVec[ nodeIndex ]); - } - } - -#ifdef _MY_DEBUG_ - // check validity of transitions - const char* trName[] = { "TANGENT", "IN", "OUT", "APEX" }; - for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions - { - LineIndexer li = GetLineIndexer( iDir ); - for ( ; li.More(); ++li ) - { - multiset< F_IntersectPoint >& intPnts = _lines[ iDir ][ li.LineIndex() ]._intPoints; - if ( intPnts.empty() ) continue; - if ( intPnts.size() == 1 ) - { - if ( intPnts.begin()->_transition != Trans_TANGENT && - intPnts.begin()->_transition != Trans_APEX ) - throw SMESH_ComputeError (COMPERR_ALGO_FAILED, - SMESH_Comment("Wrong SOLE transition of GridLine (") - << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] - << ") along " << li._nameConst - << ": " << trName[ intPnts.begin()->_transition] ); - } - else - { - if ( intPnts.begin()->_transition == Trans_OUT ) - throw SMESH_ComputeError (COMPERR_ALGO_FAILED, - SMESH_Comment("Wrong START transition of GridLine (") - << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] - << ") along " << li._nameConst - << ": " << trName[ intPnts.begin()->_transition ]); - if ( intPnts.rbegin()->_transition == Trans_IN ) - throw SMESH_ComputeError (COMPERR_ALGO_FAILED, - SMESH_Comment("Wrong END transition of GridLine (") - << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] - << ") along " << li._nameConst - << ": " << trName[ intPnts.rbegin()->_transition ]); - } - } - } -#endif - - return; - } - - //============================================================================= - /* - * Intersects TopoDS_Face with all GridLine's - */ - void FaceGridIntersector::Intersect() - { - FaceLineIntersector intersector; - intersector._surfaceInt = GetCurveFaceIntersector(); - intersector._tol = _grid->_tol; - intersector._transOut = _face.Orientation() == TopAbs_REVERSED ? Trans_IN : Trans_OUT; - intersector._transIn = _face.Orientation() == TopAbs_REVERSED ? Trans_OUT : Trans_IN; - - typedef void (FaceLineIntersector::* PIntFun )(const GridLine& gridLine); - PIntFun interFunction; - - bool isDirect = true; - BRepAdaptor_Surface surf( _face ); - switch ( surf.GetType() ) { - case GeomAbs_Plane: - intersector._plane = surf.Plane(); - interFunction = &FaceLineIntersector::IntersectWithPlane; - isDirect = intersector._plane.Direct(); - break; - case GeomAbs_Cylinder: - intersector._cylinder = surf.Cylinder(); - interFunction = &FaceLineIntersector::IntersectWithCylinder; - isDirect = intersector._cylinder.Direct(); - break; - case GeomAbs_Cone: - intersector._cone = surf.Cone(); - interFunction = &FaceLineIntersector::IntersectWithCone; - //isDirect = intersector._cone.Direct(); - break; - case GeomAbs_Sphere: - intersector._sphere = surf.Sphere(); - interFunction = &FaceLineIntersector::IntersectWithSphere; - isDirect = intersector._sphere.Direct(); - break; - case GeomAbs_Torus: - intersector._torus = surf.Torus(); - interFunction = &FaceLineIntersector::IntersectWithTorus; - //isDirect = intersector._torus.Direct(); - break; - default: - interFunction = &FaceLineIntersector::IntersectWithSurface; - } - if ( !isDirect ) - std::swap( intersector._transOut, intersector._transIn ); - - _intersections.clear(); - for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions - { - if ( surf.GetType() == GeomAbs_Plane ) - { - // check if all lines in this direction are parallel to a plane - if ( intersector._plane.Axis().IsNormal( _grid->_lines[iDir][0]._line.Position(), - Precision::Angular())) - continue; - // find out a transition, that is the same for all lines of a direction - gp_Dir plnNorm = intersector._plane.Axis().Direction(); - gp_Dir lineDir = _grid->_lines[iDir][0]._line.Direction(); - intersector._transition = - ( plnNorm * lineDir < 0 ) ? intersector._transIn : intersector._transOut; - } - if ( surf.GetType() == GeomAbs_Cylinder ) - { - // check if all lines in this direction are parallel to a cylinder - if ( intersector._cylinder.Axis().IsParallel( _grid->_lines[iDir][0]._line.Position(), - Precision::Angular())) - continue; - } - - // intersect the grid lines with the face - for ( size_t iL = 0; iL < _grid->_lines[iDir].size(); ++iL ) - { - GridLine& gridLine = _grid->_lines[iDir][iL]; - if ( _bndBox.IsOut( gridLine._line )) continue; - - intersector._intPoints.clear(); - (intersector.*interFunction)( gridLine ); // <- intersection with gridLine - for ( size_t i = 0; i < intersector._intPoints.size(); ++i ) - _intersections.push_back( make_pair( &gridLine, intersector._intPoints[i] )); - } - } - - if ( _face.Orientation() == TopAbs_INTERNAL ) - { - for ( size_t i = 0; i < _intersections.size(); ++i ) - if ( _intersections[i].second._transition == Trans_IN || - _intersections[i].second._transition == Trans_OUT ) - { - _intersections[i].second._transition = Trans_INTERNAL; - } - } - return; - } - //================================================================================ - /* - * Return true if (_u,_v) is on the face - */ - bool FaceLineIntersector::UVIsOnFace() const - { - TopAbs_State state = _surfaceInt->ClassifyUVPoint(gp_Pnt2d( _u,_v )); - return ( state == TopAbs_IN || state == TopAbs_ON ); - } - //================================================================================ - /* - * Store an intersection if it is IN or ON the face - */ - void FaceLineIntersector::addIntPoint(const bool toClassify) - { - if ( !toClassify || UVIsOnFace() ) - { - F_IntersectPoint p; - p._paramOnLine = _w; - p._u = _u; - p._v = _v; - p._transition = _transition; - _intPoints.push_back( p ); - } - } - //================================================================================ - /* - * Intersect a line with a plane - */ - void FaceLineIntersector::IntersectWithPlane(const GridLine& gridLine) - { - IntAna_IntConicQuad linPlane( gridLine._line, _plane, Precision::Angular()); - _w = linPlane.ParamOnConic(1); - if ( isParamOnLineOK( gridLine._length )) - { - ElSLib::Parameters(_plane, linPlane.Point(1) ,_u,_v); - addIntPoint(); - } - } - //================================================================================ - /* - * Intersect a line with a cylinder - */ - void FaceLineIntersector::IntersectWithCylinder(const GridLine& gridLine) - { - IntAna_IntConicQuad linCylinder( gridLine._line, _cylinder ); - if ( linCylinder.IsDone() && linCylinder.NbPoints() > 0 ) - { - _w = linCylinder.ParamOnConic(1); - if ( linCylinder.NbPoints() == 1 ) - _transition = Trans_TANGENT; - else - _transition = _w < linCylinder.ParamOnConic(2) ? _transIn : _transOut; - if ( isParamOnLineOK( gridLine._length )) - { - ElSLib::Parameters(_cylinder, linCylinder.Point(1) ,_u,_v); - addIntPoint(); - } - if ( linCylinder.NbPoints() > 1 ) - { - _w = linCylinder.ParamOnConic(2); - if ( isParamOnLineOK( gridLine._length )) - { - ElSLib::Parameters(_cylinder, linCylinder.Point(2) ,_u,_v); - _transition = ( _transition == Trans_OUT ) ? Trans_IN : Trans_OUT; - addIntPoint(); - } - } - } - } - //================================================================================ - /* - * Intersect a line with a cone - */ - void FaceLineIntersector::IntersectWithCone (const GridLine& gridLine) - { - IntAna_IntConicQuad linCone(gridLine._line,_cone); - if ( !linCone.IsDone() ) return; - gp_Pnt P; - gp_Vec du, dv, norm; - for ( int i = 1; i <= linCone.NbPoints(); ++i ) - { - _w = linCone.ParamOnConic( i ); - if ( !isParamOnLineOK( gridLine._length )) continue; - ElSLib::Parameters(_cone, linCone.Point(i) ,_u,_v); - if ( UVIsOnFace() ) - { - ElSLib::D1( _u, _v, _cone, P, du, dv ); - norm = du ^ dv; - double normSize2 = norm.SquareMagnitude(); - if ( normSize2 > Precision::Angular() * Precision::Angular() ) - { - double cos = norm.XYZ() * gridLine._line.Direction().XYZ(); - cos /= sqrt( normSize2 ); - if ( cos < -Precision::Angular() ) - _transition = _transIn; - else if ( cos > Precision::Angular() ) - _transition = _transOut; - else - _transition = Trans_TANGENT; - } - else - { - _transition = Trans_APEX; - } - addIntPoint( /*toClassify=*/false); - } - } - } - //================================================================================ - /* - * Intersect a line with a sphere - */ - void FaceLineIntersector::IntersectWithSphere (const GridLine& gridLine) - { - IntAna_IntConicQuad linSphere(gridLine._line,_sphere); - if ( linSphere.IsDone() && linSphere.NbPoints() > 0 ) - { - _w = linSphere.ParamOnConic(1); - if ( linSphere.NbPoints() == 1 ) - _transition = Trans_TANGENT; - else - _transition = _w < linSphere.ParamOnConic(2) ? _transIn : _transOut; - if ( isParamOnLineOK( gridLine._length )) - { - ElSLib::Parameters(_sphere, linSphere.Point(1) ,_u,_v); - addIntPoint(); - } - if ( linSphere.NbPoints() > 1 ) - { - _w = linSphere.ParamOnConic(2); - if ( isParamOnLineOK( gridLine._length )) - { - ElSLib::Parameters(_sphere, linSphere.Point(2) ,_u,_v); - _transition = ( _transition == Trans_OUT ) ? Trans_IN : Trans_OUT; - addIntPoint(); - } - } - } - } - //================================================================================ - /* - * Intersect a line with a torus - */ - void FaceLineIntersector::IntersectWithTorus (const GridLine& gridLine) - { - IntAna_IntLinTorus linTorus(gridLine._line,_torus); - if ( !linTorus.IsDone()) return; - gp_Pnt P; - gp_Vec du, dv, norm; - for ( int i = 1; i <= linTorus.NbPoints(); ++i ) - { - _w = linTorus.ParamOnLine( i ); - if ( !isParamOnLineOK( gridLine._length )) continue; - linTorus.ParamOnTorus( i, _u,_v ); - if ( UVIsOnFace() ) - { - ElSLib::D1( _u, _v, _torus, P, du, dv ); - norm = du ^ dv; - double normSize = norm.Magnitude(); - double cos = norm.XYZ() * gridLine._line.Direction().XYZ(); - cos /= normSize; - if ( cos < -Precision::Angular() ) - _transition = _transIn; - else if ( cos > Precision::Angular() ) - _transition = _transOut; - else - _transition = Trans_TANGENT; - addIntPoint( /*toClassify=*/false); - } - } - } - //================================================================================ - /* - * Intersect a line with a non-analytical surface - */ - void FaceLineIntersector::IntersectWithSurface (const GridLine& gridLine) - { - _surfaceInt->Perform( gridLine._line, 0.0, gridLine._length ); - if ( !_surfaceInt->IsDone() ) return; - for ( int i = 1; i <= _surfaceInt->NbPnt(); ++i ) - { - _transition = Transition( _surfaceInt->Transition( i ) ); - _w = _surfaceInt->WParameter( i ); - addIntPoint(/*toClassify=*/false); - } - } - //================================================================================ - /* - * check if its face can be safely intersected in a thread - */ - bool FaceGridIntersector::IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const - { - bool isSafe = true; - - // check surface - TopLoc_Location loc; - Handle(Geom_Surface) surf = BRep_Tool::Surface( _face, loc ); - Handle(Geom_RectangularTrimmedSurface) ts = - Handle(Geom_RectangularTrimmedSurface)::DownCast( surf ); - while( !ts.IsNull() ) { - surf = ts->BasisSurface(); - ts = Handle(Geom_RectangularTrimmedSurface)::DownCast(surf); - } - if ( surf->IsKind( STANDARD_TYPE(Geom_BSplineSurface )) || - surf->IsKind( STANDARD_TYPE(Geom_BezierSurface ))) - if ( !noSafeTShapes.insert( _face.TShape().get() ).second ) - isSafe = false; - - double f, l; - TopExp_Explorer exp( _face, TopAbs_EDGE ); - for ( ; exp.More(); exp.Next() ) - { - bool edgeIsSafe = true; - const TopoDS_Edge& e = TopoDS::Edge( exp.Current() ); - // check 3d curve - { - Handle(Geom_Curve) c = BRep_Tool::Curve( e, loc, f, l); - if ( !c.IsNull() ) - { - Handle(Geom_TrimmedCurve) tc = Handle(Geom_TrimmedCurve)::DownCast(c); - while( !tc.IsNull() ) { - c = tc->BasisCurve(); - tc = Handle(Geom_TrimmedCurve)::DownCast(c); - } - if ( c->IsKind( STANDARD_TYPE(Geom_BSplineCurve )) || - c->IsKind( STANDARD_TYPE(Geom_BezierCurve ))) - edgeIsSafe = false; - } - } - // check 2d curve - if ( edgeIsSafe ) - { - Handle(Geom2d_Curve) c2 = BRep_Tool::CurveOnSurface( e, surf, loc, f, l); - if ( !c2.IsNull() ) - { - Handle(Geom2d_TrimmedCurve) tc = Handle(Geom2d_TrimmedCurve)::DownCast(c2); - while( !tc.IsNull() ) { - c2 = tc->BasisCurve(); - tc = Handle(Geom2d_TrimmedCurve)::DownCast(c2); - } - if ( c2->IsKind( STANDARD_TYPE(Geom2d_BSplineCurve )) || - c2->IsKind( STANDARD_TYPE(Geom2d_BezierCurve ))) - edgeIsSafe = false; - } - } - if ( !edgeIsSafe && !noSafeTShapes.insert( e.TShape().get() ).second ) - isSafe = false; - } - return isSafe; - } - //================================================================================ - /*! - * \brief Creates topology of the hexahedron - */ - Hexahedron::Hexahedron(Grid* grid) - : _grid( grid ), _nbFaceIntNodes(0), _hasTooSmall( false ) - { - _polygons.reserve(100); // to avoid reallocation; - - //set nodes shift within grid->_nodes from the node 000 - size_t dx = _grid->NodeIndexDX(); - size_t dy = _grid->NodeIndexDY(); - size_t dz = _grid->NodeIndexDZ(); - size_t i000 = 0; - size_t i100 = i000 + dx; - size_t i010 = i000 + dy; - size_t i110 = i010 + dx; - size_t i001 = i000 + dz; - size_t i101 = i100 + dz; - size_t i011 = i010 + dz; - size_t i111 = i110 + dz; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V000 )] = i000; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V100 )] = i100; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V010 )] = i010; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V110 )] = i110; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V001 )] = i001; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V101 )] = i101; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V011 )] = i011; - grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V111 )] = i111; - - vector< int > idVec; - // set nodes to links - for ( int linkID = SMESH_Block::ID_Ex00; linkID <= SMESH_Block::ID_E11z; ++linkID ) - { - SMESH_Block::GetEdgeVertexIDs( linkID, idVec ); - _Link& link = _hexLinks[ SMESH_Block::ShapeIndex( linkID )]; - link._nodes[0] = &_hexNodes[ SMESH_Block::ShapeIndex( idVec[0] )]; - link._nodes[1] = &_hexNodes[ SMESH_Block::ShapeIndex( idVec[1] )]; - } - - // set links to faces - int interlace[4] = { 0, 3, 1, 2 }; // to walk by links around a face: { u0, 1v, u1, 0v } - for ( int faceID = SMESH_Block::ID_Fxy0; faceID <= SMESH_Block::ID_F1yz; ++faceID ) - { - _Face& quad = _hexQuads[ SMESH_Block::ShapeIndex( faceID )]; - quad._name = (SMESH_Block::TShapeID) faceID; - - SMESH_Block::GetFaceEdgesIDs( faceID, idVec ); - bool revFace = ( faceID == SMESH_Block::ID_Fxy0 || - faceID == SMESH_Block::ID_Fx1z || - faceID == SMESH_Block::ID_F0yz ); - quad._links.resize(4); - vector<_OrientedLink>::iterator frwLinkIt = quad._links.begin(); - vector<_OrientedLink>::reverse_iterator revLinkIt = quad._links.rbegin(); - for ( int i = 0; i < 4; ++i ) - { - bool revLink = revFace; - if ( i > 1 ) // reverse links u1 and v0 - revLink = !revLink; - _OrientedLink& link = revFace ? *revLinkIt++ : *frwLinkIt++; - link = _OrientedLink( & _hexLinks[ SMESH_Block::ShapeIndex( idVec[interlace[i]] )], - revLink ); - } - } - } - //================================================================================ - /*! - * \brief Copy constructor - */ - Hexahedron::Hexahedron( const Hexahedron& other, size_t i, size_t j, size_t k, int cellID ) - :_grid( other._grid ), _nbFaceIntNodes(0), _i( i ), _j( j ), _k( k ), _hasTooSmall( false ) - { - _polygons.reserve(100); // to avoid reallocation; - - // copy topology - for ( int i = 0; i < 12; ++i ) - { - const _Link& srcLink = other._hexLinks[ i ]; - _Link& tgtLink = this->_hexLinks[ i ]; - tgtLink._nodes[0] = _hexNodes + ( srcLink._nodes[0] - other._hexNodes ); - tgtLink._nodes[1] = _hexNodes + ( srcLink._nodes[1] - other._hexNodes ); - } - - for ( int i = 0; i < 6; ++i ) - { - const _Face& srcQuad = other._hexQuads[ i ]; - _Face& tgtQuad = this->_hexQuads[ i ]; - tgtQuad._name = srcQuad._name; - tgtQuad._links.resize(4); - for ( int j = 0; j < 4; ++j ) - { - const _OrientedLink& srcLink = srcQuad._links[ j ]; - _OrientedLink& tgtLink = tgtQuad._links[ j ]; - tgtLink._reverse = srcLink._reverse; - tgtLink._link = _hexLinks + ( srcLink._link - other._hexLinks ); - } - } - - if (SALOME::VerbosityActivated()) - _cellID = cellID; - } - - //================================================================================ - /*! - * \brief Return IDs of SOLIDs interfering with this Hexahedron - */ - size_t Hexahedron::getSolids( TGeomID ids[] ) - { - if ( _grid->_geometry.IsOneSolid() ) - { - ids[0] = _grid->GetSolid()->ID(); - return 1; - } - // count intersection points belonging to each SOLID - TID2Nb id2NbPoints; - id2NbPoints.reserve( 3 ); - - _origNodeInd = _grid->NodeIndex( _i,_j,_k ); - for ( int iN = 0; iN < 8; ++iN ) - { - _hexNodes[iN]._node = _grid->_nodes [ _origNodeInd + _grid->_nodeShift[iN] ]; - _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ]; - - if ( _hexNodes[iN]._intPoint ) // intersection with a FACE - { - for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) - { - const vector< TGeomID > & solidIDs = - _grid->GetSolidIDs( _hexNodes[iN]._intPoint->_faceIDs[iF] ); - for ( size_t i = 0; i < solidIDs.size(); ++i ) - insertAndIncrement( solidIDs[i], id2NbPoints ); - } - } - else if ( _hexNodes[iN]._node ) // node inside a SOLID - { - insertAndIncrement( _hexNodes[iN]._node->GetShapeID(), id2NbPoints ); - } - } - - for ( int iL = 0; iL < 12; ++iL ) - { - const _Link& link = _hexLinks[ iL ]; - for ( size_t iP = 0; iP < link._fIntPoints.size(); ++iP ) - { - for ( size_t iF = 0; iF < link._fIntPoints[iP]->_faceIDs.size(); ++iF ) - { - const vector< TGeomID > & solidIDs = - _grid->GetSolidIDs( link._fIntPoints[iP]->_faceIDs[iF] ); - for ( size_t i = 0; i < solidIDs.size(); ++i ) - insertAndIncrement( solidIDs[i], id2NbPoints ); - } - } - } - - for ( size_t iP = 0; iP < _eIntPoints.size(); ++iP ) - { - const vector< TGeomID > & solidIDs = _grid->GetSolidIDs( _eIntPoints[iP]->_shapeID ); - for ( size_t i = 0; i < solidIDs.size(); ++i ) - insertAndIncrement( solidIDs[i], id2NbPoints ); - } - - size_t nbSolids = 0; - for ( TID2Nb::iterator id2nb = id2NbPoints.begin(); id2nb != id2NbPoints.end(); ++id2nb ) - if ( id2nb->second >= 3 ) - ids[ nbSolids++ ] = id2nb->first; - - return nbSolids; - } - - //================================================================================ - /*! - * \brief Count cuts by INTERNAL FACEs and set _Node::_isInternalFlags - */ - bool Hexahedron::isCutByInternalFace( IsInternalFlag & maxFlag ) - { - TID2Nb id2NbPoints; - id2NbPoints.reserve( 3 ); - - for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) - for ( size_t iF = 0; iF < _intNodes[iN]._intPoint->_faceIDs.size(); ++iF ) - { - if ( _grid->IsInternal( _intNodes[iN]._intPoint->_faceIDs[iF])) - insertAndIncrement( _intNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); - } - for ( size_t iN = 0; iN < 8; ++iN ) - if ( _hexNodes[iN]._intPoint ) - for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) - { - if ( _grid->IsInternal( _hexNodes[iN]._intPoint->_faceIDs[iF])) - insertAndIncrement( _hexNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); - } - - maxFlag = IS_NOT_INTERNAL; - for ( TID2Nb::iterator id2nb = id2NbPoints.begin(); id2nb != id2NbPoints.end(); ++id2nb ) - { - TGeomID intFace = id2nb->first; - IsInternalFlag intFlag = ( id2nb->second >= 3 ? IS_CUT_BY_INTERNAL_FACE : IS_INTERNAL ); - if ( intFlag > maxFlag ) - maxFlag = intFlag; - - for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) - if ( _intNodes[iN].IsOnFace( intFace )) - _intNodes[iN].SetInternal( intFlag ); - - for ( size_t iN = 0; iN < 8; ++iN ) - if ( _hexNodes[iN].IsOnFace( intFace )) - _hexNodes[iN].SetInternal( intFlag ); - } - - return maxFlag; - } - - //================================================================================ - /*! - * \brief Return any FACE interfering with this Hexahedron - */ - TGeomID Hexahedron::getAnyFace() const - { - TID2Nb id2NbPoints; - id2NbPoints.reserve( 3 ); - - for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) - for ( size_t iF = 0; iF < _intNodes[iN]._intPoint->_faceIDs.size(); ++iF ) - insertAndIncrement( _intNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); - - for ( size_t iN = 0; iN < 8; ++iN ) - if ( _hexNodes[iN]._intPoint ) - for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) - insertAndIncrement( _hexNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); - - for ( unsigned int minNb = 3; minNb > 0; --minNb ) - for ( TID2Nb::iterator id2nb = id2NbPoints.begin(); id2nb != id2NbPoints.end(); ++id2nb ) - if ( id2nb->second >= minNb ) - return id2nb->first; - - return 0; - } - - //================================================================================ - /*! - * \brief Initializes IJK by Hexahedron index - */ - void Hexahedron::setIJK( size_t iCell ) - { - size_t iNbCell = _grid->_coords[0].size() - 1; - size_t jNbCell = _grid->_coords[1].size() - 1; - _i = iCell % iNbCell; - _j = ( iCell % ( iNbCell * jNbCell )) / iNbCell; - _k = iCell / iNbCell / jNbCell; - } - - //================================================================================ - /*! - * \brief Initializes its data by given grid cell (countered from zero) - */ - void Hexahedron::init( size_t iCell ) - { - setIJK( iCell ); - init( _i, _j, _k ); - } - - //================================================================================ - /*! - * \brief Initializes its data by given grid cell nodes and intersections - */ - void Hexahedron::init( size_t i, size_t j, size_t k, const Solid* solid ) - { - _i = i; _j = j; _k = k; - - bool isCompute = solid; - if ( !solid ) - solid = _grid->GetSolid(); - - // set nodes of grid to nodes of the hexahedron and - // count nodes at hexahedron corners located IN and ON geometry - _nbCornerNodes = _nbBndNodes = 0; - _origNodeInd = _grid->NodeIndex( i,j,k ); - for ( int iN = 0; iN < 8; ++iN ) - { - _hexNodes[iN]._isInternalFlags = 0; - - // Grid node - _hexNodes[iN]._node = _grid->_nodes [ _origNodeInd + _grid->_nodeShift[iN] ]; - _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ]; - - if ( _grid->_allBorderNodes[ _origNodeInd + _grid->_nodeShift[iN] ] ) - _hexNodes[iN]._boundaryCornerNode = _grid->_allBorderNodes [ _origNodeInd + _grid->_nodeShift[iN] ]; - - if ( _hexNodes[iN]._node && !solid->Contains( _hexNodes[iN]._node->GetShapeID() )) - _hexNodes[iN]._node = 0; - - if ( _hexNodes[iN]._intPoint && !solid->ContainsAny( _hexNodes[iN]._intPoint->_faceIDs )) - _hexNodes[iN]._intPoint = 0; - - _nbCornerNodes += bool( _hexNodes[iN]._node ); - _nbBndNodes += bool( _hexNodes[iN]._intPoint ); - } - _sideLength[0] = _grid->_coords[0][i+1] - _grid->_coords[0][i]; - _sideLength[1] = _grid->_coords[1][j+1] - _grid->_coords[1][j]; - _sideLength[2] = _grid->_coords[2][k+1] - _grid->_coords[2][k]; - - _intNodes.clear(); - _vIntNodes.clear(); - - if ( !isCompute ) - return; - - if ( _nbFaceIntNodes + _eIntPoints.size() > 0 && - _nbFaceIntNodes + _eIntPoints.size() + _nbCornerNodes > 3) - { - _intNodes.reserve( 3 * ( _nbBndNodes + _nbFaceIntNodes + _eIntPoints.size() )); - - // this method can be called in parallel, so use own helper - SMESH_MesherHelper helper( *_grid->_helper->GetMesh() ); - - // Create sub-links (_Link::_splits) by splitting links with _Link::_fIntPoints - // --------------------------------------------------------------- - _Link split; - for ( int iLink = 0; iLink < 12; ++iLink ) - { - _Link& link = _hexLinks[ iLink ]; - link._fIntNodes.clear(); - link._fIntNodes.reserve( link._fIntPoints.size() ); - for ( size_t i = 0; i < link._fIntPoints.size(); ++i ) - if ( solid->ContainsAny( link._fIntPoints[i]->_faceIDs )) - { - _intNodes.push_back( _Node( 0, link._fIntPoints[i] )); - link._fIntNodes.push_back( & _intNodes.back() ); - } - - link._splits.clear(); - split._nodes[ 0 ] = link._nodes[0]; - bool isOut = ( ! link._nodes[0]->Node() ); - bool checkTransition; - for ( size_t i = 0; i < link._fIntNodes.size(); ++i ) - { - const bool isGridNode = ( ! link._fIntNodes[i]->Node() ); - if ( !isGridNode ) // intersection non-coincident with a grid node - { - if ( split._nodes[ 0 ]->Node() && !isOut ) - { - split._nodes[ 1 ] = link._fIntNodes[i]; - link._splits.push_back( split ); - } - split._nodes[ 0 ] = link._fIntNodes[i]; - checkTransition = true; - } - else // FACE intersection coincident with a grid node (at link ends) - { - checkTransition = ( i == 0 && link._nodes[0]->Node() ); - } - if ( checkTransition ) - { - const vector< TGeomID >& faceIDs = link._fIntNodes[i]->_intPoint->_faceIDs; - if ( _grid->IsInternal( faceIDs.back() )) - isOut = false; - else if ( faceIDs.size() > 1 || _eIntPoints.size() > 0 ) - isOut = isOutPoint( link, i, helper, solid ); - else - { - bool okTransi = _grid->IsCorrectTransition( faceIDs[0], solid ); - switch ( link._fIntNodes[i]->FaceIntPnt()->_transition ) { - case Trans_OUT: isOut = okTransi; break; - case Trans_IN : isOut = !okTransi; break; - default: - isOut = isOutPoint( link, i, helper, solid ); - } - } - } - } - if ( link._nodes[ 1 ]->Node() && split._nodes[ 0 ]->Node() && !isOut ) - { - split._nodes[ 1 ] = link._nodes[1]; - link._splits.push_back( split ); - } - } - - // Create _Node's at intersections with EDGEs. - // -------------------------------------------- - // 1) add this->_eIntPoints to _Face::_eIntNodes - // 2) fill _intNodes and _vIntNodes - // - const double tol2 = _grid->_tol * _grid->_tol * 4; - int facets[3], nbFacets, subEntity; - - for ( int iF = 0; iF < 6; ++iF ) - _hexQuads[ iF ]._eIntNodes.clear(); - - for ( size_t iP = 0; iP < _eIntPoints.size(); ++iP ) - { - if ( !solid->ContainsAny( _eIntPoints[iP]->_faceIDs )) - continue; - nbFacets = getEntity( _eIntPoints[iP], facets, subEntity ); - _Node* equalNode = 0; - switch( nbFacets ) { - case 1: // in a _Face - { - _Face& quad = _hexQuads[ facets[0] - SMESH_Block::ID_FirstF ]; - equalNode = findEqualNode( quad._eIntNodes, _eIntPoints[ iP ], tol2 ); - if ( equalNode ) { - equalNode->Add( _eIntPoints[ iP ] ); - } - else { - _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); - quad._eIntNodes.push_back( & _intNodes.back() ); - } - break; - } - case 2: // on a _Link - { - _Link& link = _hexLinks[ subEntity - SMESH_Block::ID_FirstE ]; - if ( link._splits.size() > 0 ) - { - equalNode = findEqualNode( link._fIntNodes, _eIntPoints[ iP ], tol2 ); - if ( equalNode ) - equalNode->Add( _eIntPoints[ iP ] ); - else if ( link._splits.size() == 1 && - link._splits[0]._nodes[0] && - link._splits[0]._nodes[1] ) - link._splits.clear(); // hex edge is divided by _eIntPoints[iP] - } - //else - if ( !equalNode ) - { - _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); - bool newNodeUsed = false; - for ( int iF = 0; iF < 2; ++iF ) - { - _Face& quad = _hexQuads[ facets[iF] - SMESH_Block::ID_FirstF ]; - equalNode = findEqualNode( quad._eIntNodes, _eIntPoints[ iP ], tol2 ); - if ( equalNode ) { - equalNode->Add( _eIntPoints[ iP ] ); - } - else { - quad._eIntNodes.push_back( & _intNodes.back() ); - newNodeUsed = true; - } - } - if ( !newNodeUsed ) - _intNodes.pop_back(); - } - break; - } - case 3: // at a corner - { - _Node& node = _hexNodes[ subEntity - SMESH_Block::ID_FirstV ]; - if ( node.Node() ) - { - if ( node._intPoint ) - node._intPoint->Add( _eIntPoints[ iP ]->_faceIDs, _eIntPoints[ iP ]->_node ); - } - else - { - _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); - for ( int iF = 0; iF < 3; ++iF ) - { - _Face& quad = _hexQuads[ facets[iF] - SMESH_Block::ID_FirstF ]; - equalNode = findEqualNode( quad._eIntNodes, _eIntPoints[ iP ], tol2 ); - if ( equalNode ) { - equalNode->Add( _eIntPoints[ iP ] ); - } - else { - quad._eIntNodes.push_back( & _intNodes.back() ); - } - } - } - break; - } - } // switch( nbFacets ) - - if ( nbFacets == 0 || - _grid->ShapeType( _eIntPoints[ iP ]->_shapeID ) == TopAbs_VERTEX ) - { - equalNode = findEqualNode( _vIntNodes, _eIntPoints[ iP ], tol2 ); - if ( equalNode ) { - equalNode->Add( _eIntPoints[ iP ] ); - } - else if ( nbFacets == 0 ) { - if ( _intNodes.empty() || _intNodes.back().EdgeIntPnt() != _eIntPoints[ iP ]) - _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); - _vIntNodes.push_back( & _intNodes.back() ); - } - } - } // loop on _eIntPoints - } - - else if (( 3 < _nbCornerNodes && _nbCornerNodes < 8 ) || // _nbFaceIntNodes == 0 - ( !_grid->_geometry.IsOneSolid() )) - { - _Link split; - // create sub-links (_splits) of whole links - for ( int iLink = 0; iLink < 12; ++iLink ) - { - _Link& link = _hexLinks[ iLink ]; - link._splits.clear(); - if ( link._nodes[ 0 ]->Node() && link._nodes[ 1 ]->Node() ) - { - split._nodes[ 0 ] = link._nodes[0]; - split._nodes[ 1 ] = link._nodes[1]; - link._splits.push_back( split ); - } - } - } - return; - - } // init( _i, _j, _k ) - - //================================================================================ - /*! - * \brief Compute mesh volumes resulted from intersection of the Hexahedron - */ - void Hexahedron::computeElements( const Solid* solid, int solidIndex ) - { - if ( !solid ) - { - solid = _grid->GetSolid(); - if ( !_grid->_geometry.IsOneSolid() ) - { - TGeomID solidIDs[20] = { 0 }; - size_t nbSolids = getSolids( solidIDs ); - if ( nbSolids > 1 ) - { - for ( size_t i = 0; i < nbSolids; ++i ) - { - solid = _grid->GetSolid( solidIDs[i] ); - computeElements( solid, i ); - if ( !_volumeDefs._nodes.empty() && i < nbSolids - 1 ) - _volumeDefs.SetNext( new _volumeDef( _volumeDefs )); - } - return; - } - solid = _grid->GetSolid( solidIDs[0] ); - } - } - - init( _i, _j, _k, solid ); // get nodes and intersections from grid nodes and split links - - int nbIntersections = _nbFaceIntNodes + _eIntPoints.size(); - if ( _nbCornerNodes + nbIntersections < 4 ) - return; - - if ( _nbBndNodes == _nbCornerNodes && nbIntersections == 0 && isInHole() ) - return; // cell is in a hole - - IsInternalFlag intFlag = IS_NOT_INTERNAL; - if ( solid->HasInternalFaces() && this->isCutByInternalFace( intFlag )) - { - for ( _SplitIterator it( _hexLinks ); it.More(); it.Next() ) - { - if ( compute( solid, intFlag )) - _volumeDefs.SetNext( new _volumeDef( _volumeDefs )); - } - } - else - { - if ( solidIndex >= 0 ) - intFlag = IS_CUT_BY_INTERNAL_FACE; - - compute( solid, intFlag ); - } - } - - //================================================================================ - /*! - * \brief Compute mesh volumes resulted from intersection of the Hexahedron - */ - bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) - { - _polygons.clear(); - _polygons.reserve( 20 ); - - for ( int iN = 0; iN < 8; ++iN ) - _hexNodes[iN]._usedInFace = 0; - - if ( intFlag & IS_CUT_BY_INTERNAL_FACE && !_grid->_toAddEdges ) // Issue #19913 - preventVolumesOverlapping(); - - std::set< TGeomID > concaveFaces; // to avoid connecting nodes laying on them - - if ( solid->HasConcaveVertex() ) - { - for ( const E_IntersectPoint* ip : _eIntPoints ) - { - if ( const ConcaveFace* cf = solid->GetConcave( ip->_shapeID )) - if ( this->hasEdgesAround( cf )) - concaveFaces.insert( cf->_concaveFace ); - } - if ( concaveFaces.empty() || concaveFaces.size() * 3 < _eIntPoints.size() ) - for ( const _Node& hexNode: _hexNodes ) - { - if ( hexNode._node && hexNode._intPoint && hexNode._intPoint->_faceIDs.size() >= 3 ) - if ( const ConcaveFace* cf = solid->GetConcave( hexNode._node->GetShapeID() )) - if ( this->hasEdgesAround( cf )) - concaveFaces.insert( cf->_concaveFace ); - } - } - - // Create polygons from quadrangles - // -------------------------------- - - vector< _OrientedLink > splits; - vector<_Node*> chainNodes; - _Face* coplanarPolyg; - - const bool hasEdgeIntersections = !_eIntPoints.empty(); - const bool toCheckSideDivision = isImplementEdges() || intFlag & IS_CUT_BY_INTERNAL_FACE; - - for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron - { - _Face& quad = _hexQuads[ iF ] ; - - _polygons.resize( _polygons.size() + 1 ); - _Face* polygon = &_polygons.back(); - polygon->_polyLinks.reserve( 20 ); - polygon->_name = quad._name; - - splits.clear(); - for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle - for ( size_t iS = 0; iS < quad._links[ iE ].NbResultLinks(); ++iS ) - splits.push_back( quad._links[ iE ].ResultLink( iS )); - - if ( splits.size() == 4 && - isQuadOnFace( iF )) // check if a quad on FACE is not split - { - polygon->_links.swap( splits ); - continue; // goto the next quad - } - - // add splits of links to a polygon and add _polyLinks to make - // polygon's boundary closed - - int nbSplits = splits.size(); - if (( nbSplits == 1 ) && - ( quad._eIntNodes.empty() || - splits[0].FirstNode()->IsLinked( splits[0].LastNode()->_intPoint ))) - //( quad._eIntNodes.empty() || _nbCornerNodes + nbIntersections > 6 )) - nbSplits = 0; - - for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) - if ( quad._eIntNodes[ iP ]->IsUsedInFace( polygon )) - quad._eIntNodes[ iP ]->_usedInFace = 0; - - size_t nbUsedEdgeNodes = 0; - _Face* prevPolyg = 0; // polygon previously created from this quad - - while ( nbSplits > 0 ) - { - size_t iS = 0; - while ( !splits[ iS ] ) - ++iS; - - if ( !polygon->_links.empty() ) - { - _polygons.resize( _polygons.size() + 1 ); - polygon = &_polygons.back(); - polygon->_polyLinks.reserve( 20 ); - polygon->_name = quad._name; - } - polygon->_links.push_back( splits[ iS ] ); - splits[ iS++ ]._link = 0; - --nbSplits; - - _Node* nFirst = polygon->_links.back().FirstNode(); - _Node *n1,*n2 = polygon->_links.back().LastNode(); - for ( ; nFirst != n2 && iS < splits.size(); ++iS ) - { - _OrientedLink& split = splits[ iS ]; - if ( !split ) continue; - - n1 = split.FirstNode(); - if ( n1 == n2 && - n1->_intPoint && - (( n1->_intPoint->_faceIDs.size() > 1 && toCheckSideDivision ) || - ( n1->_isInternalFlags ))) - { - // n1 is at intersection with EDGE - if ( findChainOnEdge( splits, polygon->_links.back(), split, concaveFaces, - iS, quad, chainNodes )) - { - for ( size_t i = 1; i < chainNodes.size(); ++i ) - polygon->AddPolyLink( chainNodes[i-1], chainNodes[i], prevPolyg ); - if ( chainNodes.back() != n1 ) // not a partial cut by INTERNAL FACE - { - prevPolyg = polygon; - n2 = chainNodes.back(); - continue; - } - } - } - else if ( n1 != n2 ) - { - // try to connect to intersections with EDGEs - if ( quad._eIntNodes.size() > nbUsedEdgeNodes && - findChain( n2, n1, quad, chainNodes )) - { - for ( size_t i = 1; i < chainNodes.size(); ++i ) - { - polygon->AddPolyLink( chainNodes[i-1], chainNodes[i] ); - nbUsedEdgeNodes += ( chainNodes[i]->IsUsedInFace( polygon )); - } - if ( chainNodes.back() != n1 ) - { - n2 = chainNodes.back(); - --iS; - continue; - } - } - // try to connect to a split ending on the same FACE - else - { - _OrientedLink foundSplit; - for ( size_t i = iS; i < splits.size() && !foundSplit; ++i ) - if (( foundSplit = splits[ i ]) && - ( n2->IsLinked( foundSplit.FirstNode()->_intPoint ))) - { - iS = i - 1; - } - else - { - foundSplit._link = 0; - } - if ( foundSplit ) - { - if ( n2 != foundSplit.FirstNode() ) - { - polygon->AddPolyLink( n2, foundSplit.FirstNode() ); - n2 = foundSplit.FirstNode(); - } - continue; - } - else - { - if ( n2->IsLinked( nFirst->_intPoint )) - break; - polygon->AddPolyLink( n2, n1, prevPolyg ); - } - } - } // if ( n1 != n2 ) - - polygon->_links.push_back( split ); - split._link = 0; - --nbSplits; - n2 = polygon->_links.back().LastNode(); - - } // loop on splits - - if ( nFirst != n2 ) // close a polygon - { - if ( !findChain( n2, nFirst, quad, chainNodes )) - { - if ( !closePolygon( polygon, chainNodes )) - if ( !isImplementEdges() ) - chainNodes.push_back( nFirst ); - } - for ( size_t i = 1; i < chainNodes.size(); ++i ) - { - polygon->AddPolyLink( chainNodes[i-1], chainNodes[i], prevPolyg ); - nbUsedEdgeNodes += bool( chainNodes[i]->IsUsedInFace( polygon )); - } - } - - if ( polygon->_links.size() < 3 && nbSplits > 0 ) - { - polygon->_polyLinks.clear(); - polygon->_links.clear(); - } - } // while ( nbSplits > 0 ) - - if ( polygon->_links.size() < 3 ) - { - _polygons.pop_back(); - } - } // loop on 6 hexahedron sides - - // Create polygons closing holes in a polyhedron - // ---------------------------------------------- - - // clear _usedInFace - for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) - _intNodes[ iN ]._usedInFace = 0; - - // add polygons to their links and mark used nodes - for ( size_t iP = 0; iP < _polygons.size(); ++iP ) - { - _Face& polygon = _polygons[ iP ]; - for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) - { - polygon._links[ iL ].AddFace( &polygon ); - polygon._links[ iL ].FirstNode()->_usedInFace = &polygon; - } - } - // find free links - vector< _OrientedLink* > freeLinks; - freeLinks.reserve(20); - for ( size_t iP = 0; iP < _polygons.size(); ++iP ) - { - _Face& polygon = _polygons[ iP ]; - for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) - if ( polygon._links[ iL ].NbFaces() < 2 ) - freeLinks.push_back( & polygon._links[ iL ]); - } - int nbFreeLinks = freeLinks.size(); - if ( nbFreeLinks == 1 ) return false; - - // put not used intersection nodes to _vIntNodes - int nbVertexNodes = 0; // nb not used vertex nodes - { - for ( size_t iN = 0; iN < _vIntNodes.size(); ++iN ) - nbVertexNodes += ( !_vIntNodes[ iN ]->IsUsedInFace() ); - - const double tol = 1e-3 * Min( Min( _sideLength[0], _sideLength[1] ), _sideLength[0] ); - for ( size_t iN = _nbFaceIntNodes; iN < _intNodes.size(); ++iN ) - { - if ( _intNodes[ iN ].IsUsedInFace() ) continue; - if ( dynamic_cast< const F_IntersectPoint* >( _intNodes[ iN ]._intPoint )) continue; - _Node* equalNode = - findEqualNode( _vIntNodes, _intNodes[ iN ].EdgeIntPnt(), tol*tol ); - if ( !equalNode ) - { - _vIntNodes.push_back( &_intNodes[ iN ]); - ++nbVertexNodes; - } - } - } - - std::set usedFaceIDs; - std::vector< TGeomID > faces; - TGeomID curFace = 0; - const size_t nbQuadPolygons = _polygons.size(); - E_IntersectPoint ipTmp; - std::map< TGeomID, std::vector< const B_IntersectPoint* > > tmpAddedFace; // face added to _intPoint - - // create polygons by making closed chains of free links - size_t iPolygon = _polygons.size(); - while ( nbFreeLinks > 0 ) - { - if ( iPolygon == _polygons.size() ) - { - _polygons.resize( _polygons.size() + 1 ); - _polygons[ iPolygon ]._polyLinks.reserve( 20 ); - _polygons[ iPolygon ]._links.reserve( 20 ); - } - _Face& polygon = _polygons[ iPolygon ]; - - _OrientedLink* curLink = 0; - _Node* curNode; - if (( !hasEdgeIntersections ) || - ( nbFreeLinks < 4 && nbVertexNodes == 0 )) - { - // get a remaining link to start from - for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) - if (( curLink = freeLinks[ iL ] )) - freeLinks[ iL ] = 0; - polygon._links.push_back( *curLink ); - --nbFreeLinks; - do - { - // find all links connected to curLink - curNode = curLink->FirstNode(); - curLink = 0; - for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) - if ( freeLinks[ iL ] && freeLinks[ iL ]->LastNode() == curNode ) - { - curLink = freeLinks[ iL ]; - freeLinks[ iL ] = 0; - --nbFreeLinks; - polygon._links.push_back( *curLink ); - } - } while ( curLink ); - } - else // there are intersections with EDGEs - { - // get a remaining link to start from, one lying on minimal nb of FACEs - { - typedef pair< TGeomID, int > TFaceOfLink; - TFaceOfLink faceOfLink( -1, -1 ); - TFaceOfLink facesOfLink[3] = { faceOfLink, faceOfLink, faceOfLink }; - for ( size_t iL = 0; iL < freeLinks.size(); ++iL ) - if ( freeLinks[ iL ] ) - { - faces = freeLinks[ iL ]->GetNotUsedFace( usedFaceIDs ); - if ( faces.size() == 1 ) - { - faceOfLink = TFaceOfLink( faces[0], iL ); - if ( !freeLinks[ iL ]->HasEdgeNodes() ) - break; - facesOfLink[0] = faceOfLink; - } - else if ( facesOfLink[0].first < 0 ) - { - faceOfLink = TFaceOfLink(( faces.empty() ? -1 : faces[0]), iL ); - facesOfLink[ 1 + faces.empty() ] = faceOfLink; - } - } - for ( int i = 0; faceOfLink.first < 0 && i < 3; ++i ) - faceOfLink = facesOfLink[i]; - - if ( faceOfLink.first < 0 ) // all faces used - { - for ( size_t iL = 0; iL < freeLinks.size() && faceOfLink.first < 1; ++iL ) - if (( curLink = freeLinks[ iL ])) - { - faceOfLink.first = - curLink->FirstNode()->IsLinked( curLink->LastNode()->_intPoint ); - faceOfLink.second = iL; - } - usedFaceIDs.clear(); - } - curFace = faceOfLink.first; - curLink = freeLinks[ faceOfLink.second ]; - freeLinks[ faceOfLink.second ] = 0; - } - usedFaceIDs.insert( curFace ); - polygon._links.push_back( *curLink ); - --nbFreeLinks; - - // find all links lying on a curFace - do - { - // go forward from curLink - curNode = curLink->LastNode(); - curLink = 0; - for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) - if ( freeLinks[ iL ] && - freeLinks[ iL ]->FirstNode() == curNode && - freeLinks[ iL ]->LastNode()->IsOnFace( curFace )) - { - curLink = freeLinks[ iL ]; - freeLinks[ iL ] = 0; - polygon._links.push_back( *curLink ); - --nbFreeLinks; - } - } while ( curLink ); - - std::reverse( polygon._links.begin(), polygon._links.end() ); - - curLink = & polygon._links.back(); - do - { - // go backward from curLink - curNode = curLink->FirstNode(); - curLink = 0; - for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) - if ( freeLinks[ iL ] && - freeLinks[ iL ]->LastNode() == curNode && - freeLinks[ iL ]->FirstNode()->IsOnFace( curFace )) - { - curLink = freeLinks[ iL ]; - freeLinks[ iL ] = 0; - polygon._links.push_back( *curLink ); - --nbFreeLinks; - } - } while ( curLink ); - - curNode = polygon._links.back().FirstNode(); - - if ( polygon._links[0].LastNode() != curNode ) - { - if ( nbVertexNodes > 0 ) - { - // add links with _vIntNodes if not already used - chainNodes.clear(); - for ( size_t iN = 0; iN < _vIntNodes.size(); ++iN ) - if ( !_vIntNodes[ iN ]->IsUsedInFace() && - _vIntNodes[ iN ]->IsOnFace( curFace )) - { - _vIntNodes[ iN ]->_usedInFace = &polygon; - chainNodes.push_back( _vIntNodes[ iN ] ); - } - if ( chainNodes.size() > 1 && - curFace != _grid->PseudoIntExtFaceID() ) /////// TODO - { - sortVertexNodes( chainNodes, curNode, curFace ); - } - for ( size_t i = 0; i < chainNodes.size(); ++i ) - { - polygon.AddPolyLink( chainNodes[ i ], curNode ); - curNode = chainNodes[ i ]; - freeLinks.push_back( &polygon._links.back() ); - ++nbFreeLinks; - } - nbVertexNodes -= chainNodes.size(); - } - // if ( polygon._links.size() > 1 ) - { - polygon.AddPolyLink( polygon._links[0].LastNode(), curNode ); - freeLinks.push_back( &polygon._links.back() ); - ++nbFreeLinks; - } - } - } // if there are intersections with EDGEs - - if ( polygon._links.size() < 2 || - polygon._links[0].LastNode() != polygon._links.back().FirstNode() ) - { - _polygons.clear(); - break; // closed polygon not found -> invalid polyhedron - } - - if ( polygon._links.size() == 2 ) - { - if ( freeLinks.back() == &polygon._links.back() ) - { - freeLinks.pop_back(); - --nbFreeLinks; - } - if ( polygon._links.front().NbFaces() > 0 ) - polygon._links.back().AddFace( polygon._links.front()._link->_faces[0] ); - if ( polygon._links.back().NbFaces() > 0 ) - polygon._links.front().AddFace( polygon._links.back()._link->_faces[0] ); - - if ( iPolygon == _polygons.size()-1 ) - _polygons.pop_back(); - } - else // polygon._links.size() >= 2 - { - // add polygon to its links - for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) - { - polygon._links[ iL ].AddFace( &polygon ); - polygon._links[ iL ].Reverse(); - } - if ( /*hasEdgeIntersections &&*/ iPolygon == _polygons.size() - 1 ) - { - // check that a polygon does not lie on a hexa side - coplanarPolyg = 0; - for ( size_t iL = 0; iL < polygon._links.size() && !coplanarPolyg; ++iL ) - { - if ( polygon._links[ iL ].NbFaces() < 2 ) - continue; // it's a just added free link - // look for a polygon made on a hexa side and sharing - // two or more haxa links - size_t iL2; - coplanarPolyg = polygon._links[ iL ]._link->_faces[0]; - for ( iL2 = iL + 1; iL2 < polygon._links.size(); ++iL2 ) - if ( polygon._links[ iL2 ]._link->_faces[0] == coplanarPolyg && - !coplanarPolyg->IsPolyLink( polygon._links[ iL ]) && - !coplanarPolyg->IsPolyLink( polygon._links[ iL2 ]) && - coplanarPolyg < & _polygons[ nbQuadPolygons ]) - break; - if ( iL2 == polygon._links.size() ) - coplanarPolyg = 0; - } - if ( coplanarPolyg ) // coplanar polygon found - { - freeLinks.resize( freeLinks.size() - polygon._polyLinks.size() ); - nbFreeLinks -= polygon._polyLinks.size(); - - // an E_IntersectPoint used to mark nodes of coplanarPolyg - // as lying on curFace while they are not at intersection with geometry - ipTmp._faceIDs.resize(1); - ipTmp._faceIDs[0] = curFace; - - // fill freeLinks with links not shared by coplanarPolyg and polygon - for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) - if ( polygon._links[ iL ]._link->_faces[1] && - polygon._links[ iL ]._link->_faces[0] != coplanarPolyg ) - { - _Face* p = polygon._links[ iL ]._link->_faces[0]; - for ( size_t iL2 = 0; iL2 < p->_links.size(); ++iL2 ) - if ( p->_links[ iL2 ]._link == polygon._links[ iL ]._link ) - { - freeLinks.push_back( & p->_links[ iL2 ] ); - ++nbFreeLinks; - freeLinks.back()->RemoveFace( &polygon ); - break; - } - } - for ( size_t iL = 0; iL < coplanarPolyg->_links.size(); ++iL ) - if ( coplanarPolyg->_links[ iL ]._link->_faces[1] && - coplanarPolyg->_links[ iL ]._link->_faces[1] != &polygon ) - { - _Face* p = coplanarPolyg->_links[ iL ]._link->_faces[0]; - if ( p == coplanarPolyg ) - p = coplanarPolyg->_links[ iL ]._link->_faces[1]; - for ( size_t iL2 = 0; iL2 < p->_links.size(); ++iL2 ) - if ( p->_links[ iL2 ]._link == coplanarPolyg->_links[ iL ]._link ) - { - // set links of coplanarPolyg in place of used freeLinks - // to re-create coplanarPolyg next - size_t iL3 = 0; - for ( ; iL3 < freeLinks.size() && freeLinks[ iL3 ]; ++iL3 ); - if ( iL3 < freeLinks.size() ) - freeLinks[ iL3 ] = ( & p->_links[ iL2 ] ); - else - freeLinks.push_back( & p->_links[ iL2 ] ); - ++nbFreeLinks; - freeLinks[ iL3 ]->RemoveFace( coplanarPolyg ); - // mark nodes of coplanarPolyg as lying on curFace - for ( int iN = 0; iN < 2; ++iN ) - { - _Node* n = freeLinks[ iL3 ]->_link->_nodes[ iN ]; - bool added = false; - if ( n->_intPoint ) added = n->_intPoint->Add( ipTmp._faceIDs ); - else n->_intPoint = &ipTmp; - if ( added ) - tmpAddedFace[ ipTmp._faceIDs[0] ].push_back( n->_intPoint ); - } - break; - } - } - // set coplanarPolyg to be re-created next - for ( size_t iP = 0; iP < _polygons.size(); ++iP ) - if ( coplanarPolyg == & _polygons[ iP ] ) - { - iPolygon = iP; - _polygons[ iPolygon ]._links.clear(); - _polygons[ iPolygon ]._polyLinks.clear(); - break; - } - _polygons.pop_back(); - usedFaceIDs.erase( curFace ); - continue; - } // if ( coplanarPolyg ) - } // if ( hasEdgeIntersections ) - search for coplanarPolyg - - iPolygon = _polygons.size(); - - } // end of case ( polygon._links.size() > 2 ) - } // while ( nbFreeLinks > 0 ) - - for ( auto & face_ip : tmpAddedFace ) - { - curFace = face_ip.first; - for ( const B_IntersectPoint* ip : face_ip.second ) - { - auto it = std::find( ip->_faceIDs.begin(), ip->_faceIDs.end(), curFace ); - if ( it != ip->_faceIDs.end() ) - ip->_faceIDs.erase( it ); - } - } - - if ( _polygons.size() < 3 ) - return false; - - // check volume size - double volSize = 0; - _hasTooSmall = ! checkPolyhedronSize( intFlag & IS_CUT_BY_INTERNAL_FACE, volSize ); - - for ( size_t i = 0; i < 8; ++i ) - if ( _hexNodes[ i ]._intPoint == &ipTmp ) - _hexNodes[ i ]._intPoint = 0; - - if ( _hasTooSmall ) - return false; // too small volume - - - // Try to find out names of no-name polygons (issue # 19887) - if ( _grid->IsToRemoveExcessEntities() && _polygons.back()._name == SMESH_Block::ID_NONE ) - { - gp_XYZ uvwCenter = - 0.5 * ( _grid->_coords[0][_i] + _grid->_coords[0][_i+1] ) * _grid->_axes[0] + - 0.5 * ( _grid->_coords[1][_j] + _grid->_coords[1][_j+1] ) * _grid->_axes[1] + - 0.5 * ( _grid->_coords[2][_k] + _grid->_coords[2][_k+1] ) * _grid->_axes[2]; - for ( size_t i = _polygons.size() - 1; _polygons[i]._name == SMESH_Block::ID_NONE; --i ) - { - _Face& face = _polygons[ i ]; - Bnd_Box bb; - gp_Pnt uvw; - for ( size_t iL = 0; iL < face._links.size(); ++iL ) - { - _Node* n = face._links[ iL ].FirstNode(); - gp_XYZ p = SMESH_NodeXYZ( n->Node() ); - _grid->ComputeUVW( p, uvw.ChangeCoord().ChangeData() ); - bb.Add( uvw ); - } - gp_Pnt pMin = bb.CornerMin(); - if ( bb.IsXThin( _grid->_tol )) - face._name = pMin.X() < uvwCenter.X() ? SMESH_Block::ID_F0yz : SMESH_Block::ID_F1yz; - else if ( bb.IsYThin( _grid->_tol )) - face._name = pMin.Y() < uvwCenter.Y() ? SMESH_Block::ID_Fx0z : SMESH_Block::ID_Fx1z; - else if ( bb.IsZThin( _grid->_tol )) - face._name = pMin.Z() < uvwCenter.Z() ? SMESH_Block::ID_Fxy0 : SMESH_Block::ID_Fxy1; - } - } - - _volumeDefs._nodes.clear(); - _volumeDefs._quantities.clear(); - _volumeDefs._names.clear(); - // create a classic cell if possible - - int nbPolygons = 0; - for ( size_t iF = 0; iF < _polygons.size(); ++iF ) - nbPolygons += (_polygons[ iF ]._links.size() > 2 ); - - //const int nbNodes = _nbCornerNodes + nbIntersections; - int nbNodes = 0; - for ( size_t i = 0; i < 8; ++i ) - nbNodes += _hexNodes[ i ].IsUsedInFace(); - for ( size_t i = 0; i < _intNodes.size(); ++i ) - nbNodes += _intNodes[ i ].IsUsedInFace(); - - bool isClassicElem = false; - if ( nbNodes == 8 && nbPolygons == 6 ) isClassicElem = addHexa(); - else if ( nbNodes == 4 && nbPolygons == 4 ) isClassicElem = addTetra(); - else if ( nbNodes == 6 && nbPolygons == 5 ) isClassicElem = addPenta(); - else if ( nbNodes == 5 && nbPolygons == 5 ) isClassicElem = addPyra (); - if ( !isClassicElem ) - { - for ( size_t iF = 0; iF < _polygons.size(); ++iF ) - { - const size_t nbLinks = _polygons[ iF ]._links.size(); - if ( nbLinks < 3 ) continue; - _volumeDefs._quantities.push_back( nbLinks ); - _volumeDefs._names.push_back( _polygons[ iF ]._name ); - for ( size_t iL = 0; iL < nbLinks; ++iL ) - _volumeDefs._nodes.push_back( _polygons[ iF ]._links[ iL ].FirstNode() ); - } - } - _volumeDefs._solidID = solid->ID(); - _volumeDefs._size = volSize; - - return !_volumeDefs._nodes.empty(); - } - - template - void computeHexa(Type& hex) - { - if ( hex ) - hex->computeElements(); - } - - // Implement parallel computation of Hexa with c++ thread implementation - template - void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1) - { - const unsigned int group = ((last-first))/std::abs(nthreads); - - std::vector threads; - threads.reserve(nthreads); - Iterator it = first; - for (; it < last-group; it += group) { - // to create a thread - // Pass iterators by value and the function by reference! - auto lambda = [=,&f](){ std::for_each(it, std::min(it+group, last), f);}; - - // stack the threads - threads.push_back( std::thread( lambda ) ); - } - - std::for_each(it, last, f); // last steps while we wait for other threads - std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();}); - } - //================================================================================ - /*! - * \brief Create elements in the mesh - */ - int Hexahedron::MakeElements(SMESH_MesherHelper& helper, - const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap) - { - SMESHDS_Mesh* mesh = helper.GetMeshDS(); - - CellsAroundLink c( _grid, 0 ); - const size_t nbGridCells = c._nbCells[0] * c._nbCells[1] * c._nbCells[2]; - vector< Hexahedron* > allHexa( nbGridCells, 0 ); - int nbIntHex = 0; - - // set intersection nodes from GridLine's to links of allHexa - int i,j,k, cellIndex, iLink; - for ( int iDir = 0; iDir < 3; ++iDir ) - { - // loop on GridLine's parallel to iDir - LineIndexer lineInd = _grid->GetLineIndexer( iDir ); - CellsAroundLink fourCells( _grid, iDir ); - for ( ; lineInd.More(); ++lineInd ) - { - GridLine& line = _grid->_lines[ iDir ][ lineInd.LineIndex() ]; - multiset< F_IntersectPoint >::const_iterator ip = line._intPoints.begin(); - for ( ; ip != line._intPoints.end(); ++ip ) - { - // if ( !ip->_node ) continue; // intersection at a grid node - lineInd.SetIndexOnLine( ip->_indexOnLine ); - fourCells.Init( lineInd.I(), lineInd.J(), lineInd.K() ); - for ( int iL = 0; iL < 4; ++iL ) // loop on 4 cells sharing a link - { - if ( !fourCells.GetCell( iL, i,j,k, cellIndex, iLink )) - continue; - Hexahedron *& hex = allHexa[ cellIndex ]; - if ( !hex) - { - hex = new Hexahedron( *this, i, j, k, cellIndex ); - ++nbIntHex; - } - hex->_hexLinks[iLink]._fIntPoints.push_back( &(*ip) ); - hex->_nbFaceIntNodes += bool( ip->_node ); - } - } - } - } - - // implement geom edges into the mesh - addEdges( helper, allHexa, edge2faceIDsMap ); - - // add not split hexahedra to the mesh - int nbAdded = 0; - TGeomID solidIDs[20]; - vector< Hexahedron* > intHexa; intHexa.reserve( nbIntHex ); - vector< const SMDS_MeshElement* > boundaryVolumes; boundaryVolumes.reserve( nbIntHex * 1.1 ); - for ( size_t i = 0; i < allHexa.size(); ++i ) - { - // initialize this by not cut allHexa[ i ] - Hexahedron * & hex = allHexa[ i ]; - if ( hex ) // split hexahedron - { - intHexa.push_back( hex ); - if ( hex->_nbFaceIntNodes > 0 || - hex->_eIntPoints.size() > 0 || - hex->getSolids( solidIDs ) > 1 ) - continue; // treat intersected hex later in parallel - this->init( hex->_i, hex->_j, hex->_k ); - } - else - { - this->init( i ); // == init(i,j,k) - } - if (( _nbCornerNodes == 8 ) && - ( _nbBndNodes < _nbCornerNodes || !isInHole() )) - { - // order of _hexNodes is defined by enum SMESH_Block::TShapeID - SMDS_MeshElement* el = - mesh->AddVolume( _hexNodes[0].Node(), _hexNodes[2].Node(), - _hexNodes[3].Node(), _hexNodes[1].Node(), - _hexNodes[4].Node(), _hexNodes[6].Node(), - _hexNodes[7].Node(), _hexNodes[5].Node() ); - TGeomID solidID = 0; - if ( _nbBndNodes < _nbCornerNodes ) - { - for ( int iN = 0; iN < 8 && !solidID; ++iN ) - if ( !_hexNodes[iN]._intPoint ) // no intersection - solidID = _hexNodes[iN].Node()->GetShapeID(); - } - else - { - getSolids( solidIDs ); - solidID = solidIDs[0]; - } - mesh->SetMeshElementOnShape( el, solidID ); - ++nbAdded; - if ( hex ) - intHexa.pop_back(); - if ( _grid->_toCreateFaces && _nbBndNodes >= 3 ) - { - boundaryVolumes.push_back( el ); - el->setIsMarked( true ); - } - } - else if ( _nbCornerNodes > 3 && !hex ) - { - // all intersections of hex with geometry are at grid nodes - hex = new Hexahedron( *this, _i, _j, _k, i ); - intHexa.push_back( hex ); - } - } - - // compute definitions of volumes resulted from hexadron intersection -#ifdef WITH_TBB - auto numOfThreads = std::thread::hardware_concurrency(); - numOfThreads = (numOfThreads != 0) ? numOfThreads : 1; - parallel_for(intHexa.begin(), intHexa.end(), computeHexa, numOfThreads ); -#else - for ( size_t i = 0; i < intHexa.size(); ++i ) - if ( Hexahedron * hex = intHexa[ i ] ) - hex->computeElements(); -#endif - - // simplify polyhedrons - if ( _grid->IsToRemoveExcessEntities() ) - { - for ( size_t i = 0; i < intHexa.size(); ++i ) - if ( Hexahedron * hex = intHexa[ i ] ) - hex->removeExcessSideDivision( allHexa ); - - for ( size_t i = 0; i < intHexa.size(); ++i ) - if ( Hexahedron * hex = intHexa[ i ] ) - hex->removeExcessNodes( allHexa ); - } - - // add volumes - for ( size_t i = 0; i < intHexa.size(); ++i ) - if ( Hexahedron * hex = intHexa[ i ] ) - nbAdded += hex->addVolumes( helper ); - - // fill boundaryVolumes with volumes neighboring too small skipped volumes - if ( _grid->_toCreateFaces ) - { - for ( size_t i = 0; i < intHexa.size(); ++i ) - if ( Hexahedron * hex = intHexa[ i ] ) - hex->getBoundaryElems( boundaryVolumes ); - } - - // merge nodes on outer sub-shapes with pre-existing ones - TopTools_DataMapIteratorOfDataMapOfShapeInteger s2nIt( _grid->_geometry._shape2NbNodes ); - for ( ; s2nIt.More(); s2nIt.Next() ) - if ( s2nIt.Value() > 0 ) - if ( SMESHDS_SubMesh* sm = mesh->MeshElements( s2nIt.Key() )) - { - TIDSortedNodeSet smNodes( SMDS_MeshElement::iterator( sm->GetNodes() ), - SMDS_MeshElement::iterator() ); - SMESH_MeshEditor::TListOfListOfNodes equalNodes; - SMESH_MeshEditor editor( helper.GetMesh() ); - editor.FindCoincidentNodes( smNodes, 10 * _grid->_tol, equalNodes, - /*SeparateCornersAndMedium =*/ false); - if ((int) equalNodes.size() <= s2nIt.Value() ) - editor.MergeNodes( equalNodes ); - } - - // create boundary mesh faces - addFaces( helper, boundaryVolumes ); - - // create mesh edges - addSegments( helper, edge2faceIDsMap ); - - for ( size_t i = 0; i < allHexa.size(); ++i ) - if ( allHexa[ i ] ) - delete allHexa[ i ]; - - return nbAdded; - } - - //================================================================================ - /*! - * \brief Implements geom edges into the mesh - */ - void Hexahedron::addEdges(SMESH_MesherHelper& helper, - vector< Hexahedron* >& hexes, - const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap) - { - if ( edge2faceIDsMap.empty() ) return; - - // Prepare planes for intersecting with EDGEs - GridPlanes pln[3]; - { - for ( int iDirZ = 0; iDirZ < 3; ++iDirZ ) // iDirZ gives normal direction to planes - { - GridPlanes& planes = pln[ iDirZ ]; - int iDirX = ( iDirZ + 1 ) % 3; - int iDirY = ( iDirZ + 2 ) % 3; - planes._zNorm = ( _grid->_axes[ iDirX ] ^ _grid->_axes[ iDirY ] ).Normalized(); - planes._zProjs.resize ( _grid->_coords[ iDirZ ].size() ); - planes._zProjs [0] = 0; - const double zFactor = _grid->_axes[ iDirZ ] * planes._zNorm; - const vector< double > & u = _grid->_coords[ iDirZ ]; - for ( size_t i = 1; i < planes._zProjs.size(); ++i ) - { - planes._zProjs [i] = zFactor * ( u[i] - u[0] ); - } - } - } - const double deflection = _grid->_minCellSize / 20.; - const double tol = _grid->_tol; - E_IntersectPoint ip; - - TColStd_MapOfInteger intEdgeIDs; // IDs of not shared INTERNAL EDGES - - // Intersect EDGEs with the planes - map< TGeomID, vector< TGeomID > >::const_iterator e2fIt = edge2faceIDsMap.begin(); - for ( ; e2fIt != edge2faceIDsMap.end(); ++e2fIt ) - { - const TGeomID edgeID = e2fIt->first; - const TopoDS_Edge & E = TopoDS::Edge( _grid->Shape( edgeID )); - BRepAdaptor_Curve curve( E ); - TopoDS_Vertex v1 = helper.IthVertex( 0, E, false ); - TopoDS_Vertex v2 = helper.IthVertex( 1, E, false ); - - ip._faceIDs = e2fIt->second; - ip._shapeID = edgeID; - - bool isInternal = ( ip._faceIDs.size() == 1 && _grid->IsInternal( edgeID )); - if ( isInternal ) - { - intEdgeIDs.Add( edgeID ); - intEdgeIDs.Add( _grid->ShapeID( v1 )); - intEdgeIDs.Add( _grid->ShapeID( v2 )); - } - - // discretize the EDGE - GCPnts_UniformDeflection discret( curve, deflection, true ); - if ( !discret.IsDone() || discret.NbPoints() < 2 ) - continue; - - // perform intersection - E_IntersectPoint* eip, *vip = 0; - for ( int iDirZ = 0; iDirZ < 3; ++iDirZ ) - { - GridPlanes& planes = pln[ iDirZ ]; - int iDirX = ( iDirZ + 1 ) % 3; - int iDirY = ( iDirZ + 2 ) % 3; - double xLen = _grid->_coords[ iDirX ].back() - _grid->_coords[ iDirX ][0]; - double yLen = _grid->_coords[ iDirY ].back() - _grid->_coords[ iDirY ][0]; - double zLen = _grid->_coords[ iDirZ ].back() - _grid->_coords[ iDirZ ][0]; - int dIJK[3], d000[3] = { 0,0,0 }; - double o[3] = { _grid->_coords[0][0], - _grid->_coords[1][0], - _grid->_coords[2][0] }; - - // locate the 1st point of a segment within the grid - gp_XYZ p1 = discret.Value( 1 ).XYZ(); - double u1 = discret.Parameter( 1 ); - double zProj1 = planes._zNorm * ( p1 - _grid->_origin ); - - _grid->ComputeUVW( p1, ip._uvw ); - int iX1 = int(( ip._uvw[iDirX] - o[iDirX]) / xLen * (_grid->_coords[ iDirX ].size() - 1)); - int iY1 = int(( ip._uvw[iDirY] - o[iDirY]) / yLen * (_grid->_coords[ iDirY ].size() - 1)); - int iZ1 = int(( ip._uvw[iDirZ] - o[iDirZ]) / zLen * (_grid->_coords[ iDirZ ].size() - 1)); - locateValue( iX1, ip._uvw[iDirX], _grid->_coords[ iDirX ], dIJK[ iDirX ], tol ); - locateValue( iY1, ip._uvw[iDirY], _grid->_coords[ iDirY ], dIJK[ iDirY ], tol ); - locateValue( iZ1, ip._uvw[iDirZ], _grid->_coords[ iDirZ ], dIJK[ iDirZ ], tol ); - - int ijk[3]; // grid index where a segment intersects a plane - ijk[ iDirX ] = iX1; - ijk[ iDirY ] = iY1; - ijk[ iDirZ ] = iZ1; - - // add the 1st vertex point to a hexahedron - if ( iDirZ == 0 ) - { - ip._point = p1; - ip._shapeID = _grid->ShapeID( v1 ); - vip = _grid->Add( ip ); - _grid->UpdateFacesOfVertex( *vip, v1 ); - if ( isInternal ) - vip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - if ( !addIntersection( vip, hexes, ijk, d000 )) - _grid->Remove( vip ); - ip._shapeID = edgeID; - } - for ( int iP = 2; iP <= discret.NbPoints(); ++iP ) - { - // locate the 2nd point of a segment within the grid - gp_XYZ p2 = discret.Value( iP ).XYZ(); - double u2 = discret.Parameter( iP ); - double zProj2 = planes._zNorm * ( p2 - _grid->_origin ); - int iZ2 = iZ1; - if ( Abs( zProj2 - zProj1 ) > std::numeric_limits::min() ) - { - locateValue( iZ2, zProj2, planes._zProjs, dIJK[ iDirZ ], tol ); - - // treat intersections with planes between 2 end points of a segment - int dZ = ( iZ1 <= iZ2 ) ? +1 : -1; - int iZ = iZ1 + ( iZ1 < iZ2 ); - for ( int i = 0, nb = Abs( iZ1 - iZ2 ); i < nb; ++i, iZ += dZ ) - { - ip._point = findIntPoint( u1, zProj1, u2, zProj2, - planes._zProjs[ iZ ], - curve, planes._zNorm, _grid->_origin ); - _grid->ComputeUVW( ip._point.XYZ(), ip._uvw ); - locateValue( ijk[iDirX], ip._uvw[iDirX], _grid->_coords[iDirX], dIJK[iDirX], tol ); - locateValue( ijk[iDirY], ip._uvw[iDirY], _grid->_coords[iDirY], dIJK[iDirY], tol ); - ijk[ iDirZ ] = iZ; - - // add ip to hex "above" the plane - eip = _grid->Add( ip ); - if ( isInternal ) - eip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - dIJK[ iDirZ ] = 0; - bool added = addIntersection( eip, hexes, ijk, dIJK); - - // add ip to hex "below" the plane - ijk[ iDirZ ] = iZ-1; - if ( !addIntersection( eip, hexes, ijk, dIJK ) && - !added ) - _grid->Remove( eip ); - } - } - iZ1 = iZ2; - p1 = p2; - u1 = u2; - zProj1 = zProj2; - } - // add the 2nd vertex point to a hexahedron - if ( iDirZ == 0 ) - { - ip._point = p1; - ip._shapeID = _grid->ShapeID( v2 ); - _grid->ComputeUVW( p1, ip._uvw ); - locateValue( ijk[iDirX], ip._uvw[iDirX], _grid->_coords[iDirX], dIJK[iDirX], tol ); - locateValue( ijk[iDirY], ip._uvw[iDirY], _grid->_coords[iDirY], dIJK[iDirY], tol ); - ijk[ iDirZ ] = iZ1; - bool sameV = ( v1.IsSame( v2 )); - if ( !sameV ) - { - vip = _grid->Add( ip ); - _grid->UpdateFacesOfVertex( *vip, v2 ); - if ( isInternal ) - vip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - } - if ( !addIntersection( vip, hexes, ijk, d000 ) && !sameV ) - _grid->Remove( vip ); - ip._shapeID = edgeID; - } - } // loop on 3 grid directions - } // loop on EDGEs - - - if ( intEdgeIDs.Size() > 0 ) - cutByExtendedInternal( hexes, intEdgeIDs ); - - return; - } - - //================================================================================ - /*! - * \brief Fully cut hexes that are partially cut by INTERNAL FACE. - * Cut them by extended INTERNAL FACE. - */ - void Hexahedron::cutByExtendedInternal( std::vector< Hexahedron* >& hexes, - const TColStd_MapOfInteger& intEdgeIDs ) - { - IntAna_IntConicQuad intersection; - SMESHDS_Mesh* meshDS = _grid->_helper->GetMeshDS(); - const double tol2 = _grid->_tol * _grid->_tol; - - for ( size_t iH = 0; iH < hexes.size(); ++iH ) - { - Hexahedron* hex = hexes[ iH ]; - if ( !hex || hex->_eIntPoints.size() < 2 ) - continue; - if ( !intEdgeIDs.Contains( hex->_eIntPoints.back()->_shapeID )) - continue; - - // get 3 points on INTERNAL FACE to construct a cutting plane - gp_Pnt p1 = hex->_eIntPoints[0]->_point; - gp_Pnt p2 = hex->_eIntPoints[1]->_point; - gp_Pnt p3 = hex->mostDistantInternalPnt( iH, p1, p2 ); - - gp_Vec norm = gp_Vec( p1, p2 ) ^ gp_Vec( p1, p3 ); - gp_Pln pln; - try { - pln = gp_Pln( p1, norm ); - } - catch(...) - { - continue; - } - - TGeomID intFaceID = hex->_eIntPoints.back()->_faceIDs.front(); // FACE being "extended" - TGeomID solidID = _grid->GetSolid( intFaceID )->ID(); - - // cut links by the plane - //bool isCut = false; - for ( int iLink = 0; iLink < 12; ++iLink ) - { - _Link& link = hex->_hexLinks[ iLink ]; - if ( !link._fIntPoints.empty() ) - { - // if ( link._fIntPoints[0]->_faceIDs.back() == _grid->PseudoIntExtFaceID() ) - // isCut = true; - continue; // already cut link - } - if ( !link._nodes[0]->Node() || - !link._nodes[1]->Node() ) - continue; // outside link - - if ( link._nodes[0]->IsOnFace( intFaceID )) - { - if ( link._nodes[0]->_intPoint->_faceIDs.back() != _grid->PseudoIntExtFaceID() ) - if ( p1.SquareDistance( link._nodes[0]->Point() ) < tol2 || - p2.SquareDistance( link._nodes[0]->Point() ) < tol2 ) - link._nodes[0]->_intPoint->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - continue; // link is cut by FACE being "extended" - } - if ( link._nodes[1]->IsOnFace( intFaceID )) - { - if ( link._nodes[1]->_intPoint->_faceIDs.back() != _grid->PseudoIntExtFaceID() ) - if ( p1.SquareDistance( link._nodes[1]->Point() ) < tol2 || - p2.SquareDistance( link._nodes[1]->Point() ) < tol2 ) - link._nodes[1]->_intPoint->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - continue; // link is cut by FACE being "extended" - } - gp_Pnt p4 = link._nodes[0]->Point(); - gp_Pnt p5 = link._nodes[1]->Point(); - gp_Lin line( p4, gp_Vec( p4, p5 )); - - intersection.Perform( line, pln ); - if ( !intersection.IsDone() || - intersection.IsInQuadric() || - intersection.IsParallel() || - intersection.NbPoints() < 1 ) - continue; - - double u = intersection.ParamOnConic(1); - if ( u + _grid->_tol < 0 ) - continue; - int iDir = iLink / 4; - int index = (&hex->_i)[iDir]; - double linkLen = _grid->_coords[iDir][index+1] - _grid->_coords[iDir][index]; - if ( u - _grid->_tol > linkLen ) - continue; - - if ( u < _grid->_tol || - u > linkLen - _grid->_tol ) // intersection at grid node - { - int i = ! ( u < _grid->_tol ); // [0,1] - int iN = link._nodes[ i ] - hex->_hexNodes; // [0-7] - - const F_IntersectPoint * & ip = _grid->_gridIntP[ hex->_origNodeInd + - _grid->_nodeShift[iN] ]; - if ( !ip ) - { - ip = _grid->_extIntPool.getNew(); - ip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - //ip->_transition = Trans_INTERNAL; - } - else if ( ip->_faceIDs.back() != _grid->PseudoIntExtFaceID() ) - { - ip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - } - hex->_nbFaceIntNodes++; - //isCut = true; - } - else - { - const gp_Pnt& p = intersection.Point( 1 ); - F_IntersectPoint* ip = _grid->_extIntPool.getNew(); - ip->_node = meshDS->AddNode( p.X(), p.Y(), p.Z() ); - ip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - ip->_transition = Trans_INTERNAL; - meshDS->SetNodeInVolume( ip->_node, solidID ); - - CellsAroundLink fourCells( _grid, iDir ); - fourCells.Init( hex->_i, hex->_j, hex->_k, iLink ); - int i,j,k, cellIndex; - for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing the link - { - if ( !fourCells.GetCell( iC, i,j,k, cellIndex, iLink )) - continue; - Hexahedron * h = hexes[ cellIndex ]; - if ( !h ) - h = hexes[ cellIndex ] = new Hexahedron( *this, i, j, k, cellIndex ); - h->_hexLinks[iLink]._fIntPoints.push_back( ip ); - h->_nbFaceIntNodes++; - //isCut = true; - } - } - } - - // if ( isCut ) - // for ( size_t i = 0; i < hex->_eIntPoints.size(); ++i ) - // { - // if ( _grid->IsInternal( hex->_eIntPoints[i]->_shapeID ) && - // ! hex->_eIntPoints[i]->IsOnFace( _grid->PseudoIntExtFaceID() )) - // hex->_eIntPoints[i]->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); - // } - continue; - - } // loop on all hexes - return; - } - - //================================================================================ - /*! - * \brief Return intersection point on INTERNAL FACE most distant from given ones - */ - gp_Pnt Hexahedron::mostDistantInternalPnt( int hexIndex, const gp_Pnt& p1, const gp_Pnt& p2 ) - { - gp_Pnt resultPnt = p1; - - double maxDist2 = 0; - for ( int iLink = 0; iLink < 12; ++iLink ) // check links - { - _Link& link = _hexLinks[ iLink ]; - for ( size_t i = 0; i < link._fIntPoints.size(); ++i ) - if ( _grid->PseudoIntExtFaceID() != link._fIntPoints[i]->_faceIDs[0] && - _grid->IsInternal( link._fIntPoints[i]->_faceIDs[0] ) && - link._fIntPoints[i]->_node ) - { - gp_Pnt p = SMESH_NodeXYZ( link._fIntPoints[i]->_node ); - double d = p1.SquareDistance( p ); - if ( d > maxDist2 ) - { - resultPnt = p; - maxDist2 = d; - } - else - { - d = p2.SquareDistance( p ); - if ( d > maxDist2 ) - { - resultPnt = p; - maxDist2 = d; - } - } - } - } - setIJK( hexIndex ); - _origNodeInd = _grid->NodeIndex( _i,_j,_k ); - - for ( size_t iN = 0; iN < 8; ++iN ) // check corners - { - _hexNodes[iN]._node = _grid->_nodes [ _origNodeInd + _grid->_nodeShift[iN] ]; - _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ]; - if ( _hexNodes[iN]._intPoint ) - for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) - { - if ( _grid->IsInternal( _hexNodes[iN]._intPoint->_faceIDs[iF])) - { - gp_Pnt p = SMESH_NodeXYZ( _hexNodes[iN]._node ); - double d = p1.SquareDistance( p ); - if ( d > maxDist2 ) - { - resultPnt = p; - maxDist2 = d; - } - else - { - d = p2.SquareDistance( p ); - if ( d > maxDist2 ) - { - resultPnt = p; - maxDist2 = d; - } - } - } - } - } - if ( maxDist2 < _grid->_tol * _grid->_tol ) - return p1; - - return resultPnt; - } - - //================================================================================ - /*! - * \brief Finds intersection of a curve with a plane - * \param [in] u1 - parameter of one curve point - * \param [in] proj1 - projection of the curve point to the plane normal - * \param [in] u2 - parameter of another curve point - * \param [in] proj2 - projection of the other curve point to the plane normal - * \param [in] proj - projection of a point where the curve intersects the plane - * \param [in] curve - the curve - * \param [in] axis - the plane normal - * \param [in] origin - the plane origin - * \return gp_Pnt - the found intersection point - */ - gp_Pnt Hexahedron::findIntPoint( double u1, double proj1, - double u2, double proj2, - double proj, - BRepAdaptor_Curve& curve, - const gp_XYZ& axis, - const gp_XYZ& origin) - { - double r = (( proj - proj1 ) / ( proj2 - proj1 )); - double u = u1 * ( 1 - r ) + u2 * r; - gp_Pnt p = curve.Value( u ); - double newProj = axis * ( p.XYZ() - origin ); - if ( Abs( proj - newProj ) > _grid->_tol / 10. ) - { - if ( r > 0.5 ) - return findIntPoint( u2, proj2, u, newProj, proj, curve, axis, origin ); - else - return findIntPoint( u1, proj2, u, newProj, proj, curve, axis, origin ); - } - return p; - } - - //================================================================================ - /*! - * \brief Returns indices of a hexahedron sub-entities holding a point - * \param [in] ip - intersection point - * \param [out] facets - 0-3 facets holding a point - * \param [out] sub - index of a vertex or an edge holding a point - * \return int - number of facets holding a point - */ - int Hexahedron::getEntity( const E_IntersectPoint* ip, int* facets, int& sub ) - { - enum { X = 1, Y = 2, Z = 4 }; // == 001, 010, 100 - int nbFacets = 0; - int vertex = 0, edgeMask = 0; - - if ( Abs( _grid->_coords[0][ _i ] - ip->_uvw[0] ) < _grid->_tol ) { - facets[ nbFacets++ ] = SMESH_Block::ID_F0yz; - edgeMask |= X; - } - else if ( Abs( _grid->_coords[0][ _i+1 ] - ip->_uvw[0] ) < _grid->_tol ) { - facets[ nbFacets++ ] = SMESH_Block::ID_F1yz; - vertex |= X; - edgeMask |= X; - } - if ( Abs( _grid->_coords[1][ _j ] - ip->_uvw[1] ) < _grid->_tol ) { - facets[ nbFacets++ ] = SMESH_Block::ID_Fx0z; - edgeMask |= Y; - } - else if ( Abs( _grid->_coords[1][ _j+1 ] - ip->_uvw[1] ) < _grid->_tol ) { - facets[ nbFacets++ ] = SMESH_Block::ID_Fx1z; - vertex |= Y; - edgeMask |= Y; - } - if ( Abs( _grid->_coords[2][ _k ] - ip->_uvw[2] ) < _grid->_tol ) { - facets[ nbFacets++ ] = SMESH_Block::ID_Fxy0; - edgeMask |= Z; - } - else if ( Abs( _grid->_coords[2][ _k+1 ] - ip->_uvw[2] ) < _grid->_tol ) { - facets[ nbFacets++ ] = SMESH_Block::ID_Fxy1; - vertex |= Z; - edgeMask |= Z; - } - - switch ( nbFacets ) - { - case 0: sub = 0; break; - case 1: sub = facets[0]; break; - case 2: { - const int edge [3][8] = { - { SMESH_Block::ID_E00z, SMESH_Block::ID_E10z, - SMESH_Block::ID_E01z, SMESH_Block::ID_E11z }, - { SMESH_Block::ID_E0y0, SMESH_Block::ID_E1y0, 0, 0, - SMESH_Block::ID_E0y1, SMESH_Block::ID_E1y1 }, - { SMESH_Block::ID_Ex00, 0, SMESH_Block::ID_Ex10, 0, - SMESH_Block::ID_Ex01, 0, SMESH_Block::ID_Ex11 } - }; - switch ( edgeMask ) { - case X | Y: sub = edge[ 0 ][ vertex ]; break; - case X | Z: sub = edge[ 1 ][ vertex ]; break; - default: sub = edge[ 2 ][ vertex ]; - } - break; - } - //case 3: - default: - sub = vertex + SMESH_Block::ID_FirstV; - } - - return nbFacets; - } - //================================================================================ - /*! - * \brief Adds intersection with an EDGE - */ - bool Hexahedron::addIntersection( const E_IntersectPoint* ip, - vector< Hexahedron* >& hexes, - int ijk[], int dIJK[] ) - { - bool added = false; - - size_t hexIndex[4] = { - _grid->CellIndex( ijk[0], ijk[1], ijk[2] ), - dIJK[0] ? _grid->CellIndex( ijk[0]+dIJK[0], ijk[1], ijk[2] ) : -1, - dIJK[1] ? _grid->CellIndex( ijk[0], ijk[1]+dIJK[1], ijk[2] ) : -1, - dIJK[2] ? _grid->CellIndex( ijk[0], ijk[1], ijk[2]+dIJK[2] ) : -1 - }; - for ( int i = 0; i < 4; ++i ) - { - if ( hexIndex[i] < hexes.size() && hexes[ hexIndex[i] ] ) - { - Hexahedron* h = hexes[ hexIndex[i] ]; - h->_eIntPoints.reserve(2); - h->_eIntPoints.push_back( ip ); - added = true; - - // check if ip is really inside the hex - if (SALOME::VerbosityActivated() && h->isOutParam( ip->_uvw )) - throw SALOME_Exception("ip outside a hex"); - } - } - return added; - } - //================================================================================ - /*! - * \brief Check if a hexahedron facet lies on a FACE - * Also return true if the facet does not interfere with any FACE - */ - bool Hexahedron::isQuadOnFace( const size_t iQuad ) - { - _Face& quad = _hexQuads[ iQuad ] ; - - int nbGridNodesInt = 0; // nb FACE intersections at grid nodes - int nbNoGeomNodes = 0; - for ( int iE = 0; iE < 4; ++iE ) - { - nbNoGeomNodes = ( !quad._links[ iE ].FirstNode()->_intPoint && - quad._links[ iE ].NbResultLinks() == 1 ); - nbGridNodesInt += - ( quad._links[ iE ].FirstNode()->_intPoint && - quad._links[ iE ].NbResultLinks() == 1 && - quad._links[ iE ].ResultLink( 0 ).FirstNode() == quad._links[ iE ].FirstNode() && - quad._links[ iE ].ResultLink( 0 ).LastNode() == quad._links[ iE ].LastNode() ); - } - if ( nbNoGeomNodes == 4 ) - return true; - - if ( nbGridNodesInt == 4 ) // all quad nodes are at FACE intersection - { - size_t iEmin = 0, minNbFaces = 1000; - for ( int iE = 0; iE < 4; ++iE ) // look for a node with min nb FACEs - { - size_t nbFaces = quad._links[ iE ].FirstNode()->faces().size(); - if ( minNbFaces > nbFaces ) - { - iEmin = iE; - minNbFaces = nbFaces; - } - } - // check if there is a FACE passing through all 4 nodes - for ( const TGeomID& faceID : quad._links[ iEmin ].FirstNode()->faces() ) - { - bool allNodesAtFace = true; - for ( size_t iE = 0; iE < 4 && allNodesAtFace; ++iE ) - allNodesAtFace = ( iE == iEmin || - quad._links[ iE ].FirstNode()->IsOnFace( faceID )); - if ( allNodesAtFace ) // quad if on faceID - return true; - } - } - return false; - } - //================================================================================ - /*! - * \brief Finds nodes at a path from one node to another via intersections with EDGEs - */ - bool Hexahedron::findChain( _Node* n1, - _Node* n2, - _Face& quad, - vector<_Node*>& chn ) - { - chn.clear(); - chn.push_back( n1 ); - for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) - if ( !quad._eIntNodes[ iP ]->IsUsedInFace( &quad ) && - n1->IsLinked( quad._eIntNodes[ iP ]->_intPoint ) && - n2->IsLinked( quad._eIntNodes[ iP ]->_intPoint )) - { - chn.push_back( quad._eIntNodes[ iP ]); - chn.push_back( n2 ); - quad._eIntNodes[ iP ]->_usedInFace = &quad; - return true; - } - bool found; - do - { - found = false; - for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) - if ( !quad._eIntNodes[ iP ]->IsUsedInFace( &quad ) && - chn.back()->IsLinked( quad._eIntNodes[ iP ]->_intPoint )) - { - chn.push_back( quad._eIntNodes[ iP ]); - found = ( quad._eIntNodes[ iP ]->_usedInFace = &quad ); - break; - } - } while ( found && ! chn.back()->IsLinked( n2->_intPoint ) ); - - if ( chn.back() != n2 && chn.back()->IsLinked( n2->_intPoint )) - chn.push_back( n2 ); - - return chn.size() > 1; - } - //================================================================================ - /*! - * \brief Try to heal a polygon whose ends are not connected - */ - bool Hexahedron::closePolygon( _Face* polygon, vector<_Node*>& chainNodes ) const - { - int i = -1, nbLinks = polygon->_links.size(); - if ( nbLinks < 3 ) - return false; - vector< _OrientedLink > newLinks; - // find a node lying on the same FACE as the last one - _Node* node = polygon->_links.back().LastNode(); - TGeomID avoidFace = node->IsLinked( polygon->_links.back().FirstNode()->_intPoint ); - for ( i = nbLinks - 2; i >= 0; --i ) - if ( node->IsLinked( polygon->_links[i].FirstNode()->_intPoint, avoidFace )) - break; - if ( i >= 0 ) - { - for ( ; i < nbLinks; ++i ) - newLinks.push_back( polygon->_links[i] ); - } - else - { - // find a node lying on the same FACE as the first one - node = polygon->_links[0].FirstNode(); - avoidFace = node->IsLinked( polygon->_links[0].LastNode()->_intPoint ); - for ( i = 1; i < nbLinks; ++i ) - if ( node->IsLinked( polygon->_links[i].LastNode()->_intPoint, avoidFace )) - break; - if ( i < nbLinks ) - for ( nbLinks = i + 1, i = 0; i < nbLinks; ++i ) - newLinks.push_back( polygon->_links[i] ); - } - if ( newLinks.size() > 1 ) - { - polygon->_links.swap( newLinks ); - chainNodes.clear(); - chainNodes.push_back( polygon->_links.back().LastNode() ); - chainNodes.push_back( polygon->_links[0].FirstNode() ); - return true; - } - return false; - } - //================================================================================ - /*! - * \brief Finds nodes on the same EDGE as the first node of avoidSplit. - * - * This function is for - * 1) a case where an EDGE lies on a quad which lies on a FACE - * so that a part of quad in ON and another part is IN - * 2) INTERNAL FACE passes through the 1st node of avoidSplit - */ - bool Hexahedron::findChainOnEdge( const vector< _OrientedLink >& splits, - const _OrientedLink& prevSplit, - const _OrientedLink& avoidSplit, - const std::set< TGeomID > & concaveFaces, - size_t & iS, - _Face& quad, - vector<_Node*>& chn ) - { - _Node* pn1 = prevSplit.FirstNode(); - _Node* pn2 = prevSplit.LastNode(); // pn2 is on EDGE, if not on INTERNAL FACE - _Node* an3 = avoidSplit.LastNode(); - TGeomID avoidFace = pn1->IsLinked( pn2->_intPoint ); // FACE under the quad - if ( avoidFace < 1 && pn1->_intPoint ) - return false; - - chn.clear(); - - if ( !quad._eIntNodes.empty() ) // connect pn2 with EDGE intersections - { - chn.push_back( pn2 ); - bool found; - do - { - found = false; - for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) - if (( !quad._eIntNodes[ iP ]->IsUsedInFace( &quad )) && - ( chn.back()->IsLinked( quad._eIntNodes[ iP ]->_intPoint, avoidFace )) && - ( !avoidFace || quad._eIntNodes[ iP ]->IsOnFace( avoidFace ))) - { - chn.push_back( quad._eIntNodes[ iP ]); - found = ( quad._eIntNodes[ iP ]->_usedInFace = &quad ); - break; - } - } while ( found ); - pn2 = chn.back(); - } - - _Node* n = 0, *stopNode = avoidSplit.LastNode(); - - if ( pn2 == prevSplit.LastNode() && // pn2 is at avoidSplit.FirstNode() - !isCorner( stopNode )) // stopNode is in the middle of a _hexLinks - { - // move stopNode to a _hexNodes - for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle - for ( size_t iL = 0; iL < quad._links[ iE ].NbResultLinks(); ++iL ) - { - const _Link* sideSplit = & quad._links[ iE ]._link->_splits[ iL ]; - if ( sideSplit == avoidSplit._link ) - { - if ( quad._links[ iE ].LastNode()->Node() ) - stopNode = quad._links[ iE ].LastNode(); - iE = 4; - break; - } - } - } - - // connect pn2 (probably new, at _eIntNodes) with a split - - int i, iConn = 0; - size_t nbCommon; - TGeomID commonFaces[20]; - _Node* nPrev = nullptr; - for ( i = splits.size()-1; i >= 0; --i ) - { - if ( !splits[i] ) - continue; - - bool stop = false; - for ( int is1st = 0; is1st < 2; ++is1st ) - { - _Node* nConn = is1st ? splits[i].FirstNode() : splits[i].LastNode(); - if ( nConn == nPrev ) - { - if ( n == nConn ) - iConn = i; - continue; - } - nPrev = nConn; - if (( stop = ( nConn == stopNode ))) - break; - // find a FACE connecting nConn with pn2 but not with an3 - if (( nConn != pn1 ) && - ( nConn->_intPoint && !nConn->_intPoint->_faceIDs.empty() ) && - ( nbCommon = nConn->GetCommonFaces( pn2->_intPoint, commonFaces ))) - { - bool a3Coonect = true; - for ( size_t iF = 0; iF < nbCommon && a3Coonect; ++iF ) - a3Coonect = an3->IsOnFace( commonFaces[ iF ]) || concaveFaces.count( commonFaces[ iF ]); - if ( a3Coonect ) - continue; - - if ( !n ) - { - n = nConn; - iConn = i + !is1st; - } - if ( nbCommon > 1 ) // nConn is linked with pn2 by an EDGE - { - n = nConn; - iConn = i + !is1st; - stop = true; - break; - } - } - } - if ( stop ) - { - i = iConn; - break; - } - } - - if ( n && n != stopNode ) - { - if ( chn.empty() ) - chn.push_back( pn2 ); - chn.push_back( n ); - iS = i-1; - return true; - } - else if ( !chn.empty() && chn.back()->_isInternalFlags ) - { - // INTERNAL FACE partially cuts the quad - for ( int ip = chn.size() - 2; ip >= 0; --ip ) - chn.push_back( chn[ ip ]); - return true; - } - return false; - } - //================================================================================ - /*! - * \brief Checks transition at the ginen intersection node of a link - */ - bool Hexahedron::isOutPoint( _Link& link, int iP, - SMESH_MesherHelper& helper, const Solid* solid ) const - { - bool isOut = false; - - if ( link._fIntNodes[iP]->faces().size() == 1 && - _grid->IsInternal( link._fIntNodes[iP]->face(0) )) - return false; - - const bool moreIntPoints = ( iP+1 < (int) link._fIntNodes.size() ); - - // get 2 _Node's - _Node* n1 = link._fIntNodes[ iP ]; - if ( !n1->Node() ) - n1 = link._nodes[0]; - _Node* n2 = moreIntPoints ? link._fIntNodes[ iP+1 ] : 0; - if ( !n2 || !n2->Node() ) - n2 = link._nodes[1]; - if ( !n2->Node() ) - return true; - - // get all FACEs under n1 and n2 - set< TGeomID > faceIDs; - if ( moreIntPoints ) faceIDs.insert( link._fIntNodes[iP+1]->faces().begin(), - link._fIntNodes[iP+1]->faces().end() ); - if ( n2->_intPoint ) faceIDs.insert( n2->_intPoint->_faceIDs.begin(), - n2->_intPoint->_faceIDs.end() ); - if ( faceIDs.empty() ) - return false; // n2 is inside - if ( n1->_intPoint ) faceIDs.insert( n1->_intPoint->_faceIDs.begin(), - n1->_intPoint->_faceIDs.end() ); - faceIDs.insert( link._fIntNodes[iP]->faces().begin(), - link._fIntNodes[iP]->faces().end() ); - - // get a point between 2 nodes - gp_Pnt p1 = n1->Point(); - gp_Pnt p2 = n2->Point(); - gp_Pnt pOnLink = 0.8 * p1.XYZ() + 0.2 * p2.XYZ(); - - TopLoc_Location loc; - - set< TGeomID >::iterator faceID = faceIDs.begin(); - for ( ; faceID != faceIDs.end(); ++faceID ) - { - // project pOnLink on a FACE - if ( *faceID < 1 || !solid->Contains( *faceID )) continue; - const TopoDS_Face& face = TopoDS::Face( _grid->Shape( *faceID )); - GeomAPI_ProjectPointOnSurf& proj = helper.GetProjector( face, loc, 0.1*_grid->_tol ); - gp_Pnt testPnt = pOnLink.Transformed( loc.Transformation().Inverted() ); - proj.Perform( testPnt ); - if ( proj.IsDone() && proj.NbPoints() > 0 ) - { - Standard_Real u,v; - proj.LowerDistanceParameters( u,v ); - - if ( proj.LowerDistance() <= 0.1 * _grid->_tol ) - { - isOut = false; - } - else - { - // find isOut by normals - gp_Dir normal; - if ( GeomLib::NormEstim( BRep_Tool::Surface( face, loc ), - gp_Pnt2d( u,v ), - 0.1*_grid->_tol, - normal ) < 3 ) - { - if ( solid->Orientation( face ) == TopAbs_REVERSED ) - normal.Reverse(); - gp_Vec v( proj.NearestPoint(), testPnt ); - isOut = ( v * normal > 0 ); - } - } - if ( !isOut ) - { - // classify a projection - if ( !n1->IsOnFace( *faceID ) || !n2->IsOnFace( *faceID )) - { - BRepTopAdaptor_FClass2d cls( face, Precision::Confusion() ); - TopAbs_State state = cls.Perform( gp_Pnt2d( u,v )); - if ( state == TopAbs_OUT ) - { - isOut = true; - continue; - } - } - return false; - } - } - } - return isOut; - } - //================================================================================ - /*! - * \brief Sort nodes on a FACE - */ - void Hexahedron::sortVertexNodes(vector<_Node*>& nodes, _Node* curNode, TGeomID faceID) - { - if ( nodes.size() > 20 ) return; - - // get shapes under nodes - TGeomID nShapeIds[20], *nShapeIdsEnd = &nShapeIds[0] + nodes.size(); - for ( size_t i = 0; i < nodes.size(); ++i ) - if ( !( nShapeIds[i] = nodes[i]->ShapeID() )) - return; - - // get shapes of the FACE - const TopoDS_Face& face = TopoDS::Face( _grid->Shape( faceID )); - list< TopoDS_Edge > edges; - list< int > nbEdges; - int nbW = SMESH_Block::GetOrderedEdges (face, edges, nbEdges); - if ( nbW > 1 ) { - // select a WIRE - remove EDGEs of irrelevant WIREs from edges - list< TopoDS_Edge >::iterator e = edges.begin(), eEnd = e; - list< int >::iterator nE = nbEdges.begin(); - for ( ; nbW > 0; ++nE, --nbW ) - { - std::advance( eEnd, *nE ); - for ( ; e != eEnd; ++e ) - for ( int i = 0; i < 2; ++i ) - { - TGeomID id = i==0 ? - _grid->ShapeID( *e ) : - _grid->ShapeID( SMESH_MesherHelper::IthVertex( 0, *e )); - if (( id > 0 ) && - ( std::find( &nShapeIds[0], nShapeIdsEnd, id ) != nShapeIdsEnd )) - { - edges.erase( eEnd, edges.end() ); // remove rest wires - e = eEnd = edges.end(); - --e; - nbW = 0; - break; - } - } - if ( nbW > 0 ) - edges.erase( edges.begin(), eEnd ); // remove a current irrelevant wire - } - } - // rotate edges to have the first one at least partially out of the hexa - list< TopoDS_Edge >::iterator e = edges.begin(), eMidOut = edges.end(); - for ( ; e != edges.end(); ++e ) - { - if ( !_grid->ShapeID( *e )) - continue; - bool isOut = false; - gp_Pnt p; - double uvw[3], f,l; - for ( int i = 0; i < 2 && !isOut; ++i ) - { - if ( i == 0 ) - { - TopoDS_Vertex v = SMESH_MesherHelper::IthVertex( 0, *e ); - p = BRep_Tool::Pnt( v ); - } - else if ( eMidOut == edges.end() ) - { - TopLoc_Location loc; - Handle(Geom_Curve) c = BRep_Tool::Curve( *e, loc, f, l); - if ( c.IsNull() ) break; - p = c->Value( 0.5 * ( f + l )).Transformed( loc ); - } - else - { - continue; - } - - _grid->ComputeUVW( p.XYZ(), uvw ); - if ( isOutParam( uvw )) - { - if ( i == 0 ) - isOut = true; - else - eMidOut = e; - } - } - if ( isOut ) - break; - } - if ( e != edges.end() ) - edges.splice( edges.end(), edges, edges.begin(), e ); - else if ( eMidOut != edges.end() ) - edges.splice( edges.end(), edges, edges.begin(), eMidOut ); - - // sort nodes according to the order of edges - _Node* orderNodes [20]; - //TGeomID orderShapeIDs[20]; - size_t nbN = 0; - TGeomID id, *pID = 0; - for ( e = edges.begin(); e != edges.end(); ++e ) - { - if (( id = _grid->ShapeID( SMESH_MesherHelper::IthVertex( 0, *e ))) && - (( pID = std::find( &nShapeIds[0], nShapeIdsEnd, id )) != nShapeIdsEnd )) - { - //orderShapeIDs[ nbN ] = id; - orderNodes [ nbN++ ] = nodes[ pID - &nShapeIds[0] ]; - *pID = -1; - } - if (( id = _grid->ShapeID( *e )) && - (( pID = std::find( &nShapeIds[0], nShapeIdsEnd, id )) != nShapeIdsEnd )) - { - //orderShapeIDs[ nbN ] = id; - orderNodes [ nbN++ ] = nodes[ pID - &nShapeIds[0] ]; - *pID = -1; - } - } - if ( nbN != nodes.size() ) - return; - - bool reverse = ( orderNodes[0 ]->Point().SquareDistance( curNode->Point() ) > - orderNodes[nbN-1]->Point().SquareDistance( curNode->Point() )); - - for ( size_t i = 0; i < nodes.size(); ++i ) - nodes[ i ] = orderNodes[ reverse ? nbN-1-i : i ]; - } - - //================================================================================ - /*! - * \brief Adds computed elements to the mesh - */ - int Hexahedron::addVolumes( SMESH_MesherHelper& helper ) - { - F_IntersectPoint noIntPnt; - const bool toCheckNodePos = _grid->IsToCheckNodePos(); - const bool useQuanta = _grid->_toUseQuanta; - - int nbAdded = 0; - // add elements resulted from hexahedron intersection - for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) - { - vector< const SMDS_MeshNode* > nodes( volDef->_nodes.size() ); - for ( size_t iN = 0; iN < nodes.size(); ++iN ) - { - if ( !( nodes[iN] = volDef->_nodes[iN].Node() )) - { - if ( const E_IntersectPoint* eip = volDef->_nodes[iN].EdgeIntPnt() ) - { - nodes[iN] = volDef->_nodes[iN]._intPoint->_node = - helper.AddNode( eip->_point.X(), - eip->_point.Y(), - eip->_point.Z() ); - if ( _grid->ShapeType( eip->_shapeID ) == TopAbs_VERTEX ) - helper.GetMeshDS()->SetNodeOnVertex( nodes[iN], eip->_shapeID ); - else - helper.GetMeshDS()->SetNodeOnEdge( nodes[iN], eip->_shapeID ); - } - else - throw SALOME_Exception("Bug: no node at intersection point"); - } - else if ( volDef->_nodes[iN]._intPoint && - volDef->_nodes[iN]._intPoint->_node == volDef->_nodes[iN]._node ) - { - // Update position of node at EDGE intersection; - // see comment to _Node::Add( E_IntersectPoint ) - SMESHDS_Mesh* mesh = helper.GetMeshDS(); - TGeomID shapeID = volDef->_nodes[iN].EdgeIntPnt()->_shapeID; - mesh->UnSetNodeOnShape( nodes[iN] ); - if ( _grid->ShapeType( shapeID ) == TopAbs_VERTEX ) - mesh->SetNodeOnVertex( nodes[iN], shapeID ); - else - mesh->SetNodeOnEdge( nodes[iN], shapeID ); - } - else if ( toCheckNodePos && - !nodes[iN]->isMarked() && - _grid->ShapeType( nodes[iN]->GetShapeID() ) == TopAbs_FACE ) - { - _grid->SetOnShape( nodes[iN], noIntPnt, /*v=*/nullptr,/*unset=*/true ); - nodes[iN]->setIsMarked( true ); - } - } // loop to get nodes - - const SMDS_MeshElement* v = 0; - if ( !volDef->_quantities.empty() ) - { - if ( !useQuanta ) - { - // split polyhedrons of with disjoint volumes - std::vector> splitQuantities; - std::vector > splitNodes; - if ( checkPolyhedronValidity( volDef, splitQuantities, splitNodes ) == 1 ) - v = addPolyhedronToMesh( volDef, helper, nodes, volDef->_quantities ); - else - { - int counter = -1; - for (size_t id = 0; id < splitQuantities.size(); id++) - { - v = addPolyhedronToMesh( volDef, helper, splitNodes[ id ], splitQuantities[ id ] ); - if ( id < splitQuantities.size()-1 ) - volDef->_brotherVolume.push_back( v ); - counter++; - } - nbAdded += counter; - } - } - else - { - const double quanta = _grid->_quanta; - double polyVol = volDef->_size; - double hexaVolume = _sideLength[0] * _sideLength[1] * _sideLength[2]; - if ( hexaVolume > 0.0 && polyVol/hexaVolume >= quanta /*set the volume if the relation is satisfied*/) - v = helper.AddVolume( _hexNodes[0].BoundaryNode(), _hexNodes[2].BoundaryNode(), - _hexNodes[3].BoundaryNode(), _hexNodes[1].BoundaryNode(), - _hexNodes[4].BoundaryNode(), _hexNodes[6].BoundaryNode(), - _hexNodes[7].BoundaryNode(), _hexNodes[5].BoundaryNode() ); - - } - } - else - { - switch ( nodes.size() ) - { - case 8: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3], - nodes[4],nodes[5],nodes[6],nodes[7] ); - break; - case 4: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3] ); - break; - case 6: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3],nodes[4],nodes[5] ); - break; - case 5: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3],nodes[4] ); - break; - } - } - volDef->_volume = v; - nbAdded += bool( v ); - - } // loop on _volumeDefs chain - - // avoid creating overlapping volumes (bos #24052) - if ( nbAdded > 1 ) - { - double sumSize = 0, maxSize = 0; - _volumeDef* maxSizeDef = nullptr; - for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) - { - if ( !volDef->_volume ) - continue; - sumSize += volDef->_size; - if ( volDef->_size > maxSize ) - { - maxSize = volDef->_size; - maxSizeDef = volDef; - } - } - if ( sumSize > _sideLength[0] * _sideLength[1] * _sideLength[2] * 1.05 ) - { - for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) - if ( volDef != maxSizeDef && volDef->_volume ) - { - helper.GetMeshDS()->RemoveFreeElement( volDef->_volume, /*sm=*/nullptr, - /*fromGroups=*/false ); - volDef->_volume = nullptr; - //volDef->_nodes.clear(); - --nbAdded; - } - } - } - - for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) - { - if ( volDef->_volume ) - { - helper.GetMeshDS()->SetMeshElementOnShape( volDef->_volume, volDef->_solidID ); - for (auto broVol : volDef->_brotherVolume ) - { - helper.GetMeshDS()->SetMeshElementOnShape( broVol, volDef->_solidID ); - } - } - } - - return nbAdded; - } - //================================================================================ - /*! - * \brief Return true if the element is in a hole - * \remark consider a cell to be in a hole if all links in any direction - * comes OUT of geometry - */ - bool Hexahedron::isInHole() const - { - if ( !_vIntNodes.empty() ) - return false; - - const size_t ijk[3] = { _i, _j, _k }; - F_IntersectPoint curIntPnt; - - // consider a cell to be in a hole if all links in any direction - // comes OUT of geometry - for ( int iDir = 0; iDir < 3; ++iDir ) - { - const vector& coords = _grid->_coords[ iDir ]; - LineIndexer li = _grid->GetLineIndexer( iDir ); - li.SetIJK( _i,_j,_k ); - size_t lineIndex[4] = { li.LineIndex (), - li.LineIndex10(), - li.LineIndex01(), - li.LineIndex11() }; - bool allLinksOut = true, hasLinks = false; - for ( int iL = 0; iL < 4 && allLinksOut; ++iL ) // loop on 4 links parallel to iDir - { - const _Link& link = _hexLinks[ iL + 4*iDir ]; - // check transition of the first node of a link - const F_IntersectPoint* firstIntPnt = 0; - if ( link._nodes[0]->Node() ) // 1st node is a hexa corner - { - curIntPnt._paramOnLine = coords[ ijk[ iDir ]] - coords[0] + _grid->_tol; - const GridLine& line = _grid->_lines[ iDir ][ lineIndex[ iL ]]; - if ( !line._intPoints.empty() ) - { - multiset< F_IntersectPoint >::const_iterator ip = - line._intPoints.upper_bound( curIntPnt ); - --ip; - firstIntPnt = &(*ip); - } - } - else if ( !link._fIntPoints.empty() ) - { - firstIntPnt = link._fIntPoints[0]; - } - - if ( firstIntPnt ) - { - hasLinks = true; - allLinksOut = ( firstIntPnt->_transition == Trans_OUT && - !_grid->IsShared( firstIntPnt->_faceIDs[0] )); - } - } - if ( hasLinks && allLinksOut ) - return true; - } - return false; - } - - //================================================================================ - /*! - * \brief Check if a polyherdon has an edge lying on EDGE shared by strange FACE - * that will be meshed by other algo - */ - bool Hexahedron::hasStrangeEdge() const - { - if ( _eIntPoints.size() < 2 ) - return false; - - TopTools_MapOfShape edges; - for ( size_t i = 0; i < _eIntPoints.size(); ++i ) - { - if ( !_grid->IsStrangeEdge( _eIntPoints[i]->_shapeID )) - continue; - const TopoDS_Shape& s = _grid->Shape( _eIntPoints[i]->_shapeID ); - if ( s.ShapeType() == TopAbs_EDGE ) - { - if ( ! edges.Add( s )) - return true; // an EDGE encounters twice - } - else - { - PShapeIteratorPtr edgeIt = _grid->_helper->GetAncestors( s, - *_grid->_helper->GetMesh(), - TopAbs_EDGE ); - while ( const TopoDS_Shape* edge = edgeIt->next() ) - if ( ! edges.Add( *edge )) - return true; // an EDGE encounters twice - } - } - return false; - } - - //================================================================================ - /*! - * \brief Return true if a polyhedron passes _sizeThreshold criterion - */ - bool Hexahedron::checkPolyhedronSize( bool cutByInternalFace, double & volume) const - { - volume = 0; - - if ( cutByInternalFace && !_grid->_toUseThresholdForInternalFaces ) - { - // check if any polygon fully lies on shared/internal FACEs - for ( size_t iP = 0; iP < _polygons.size(); ++iP ) - { - const _Face& polygon = _polygons[iP]; - if ( polygon._links.empty() ) - continue; - bool allNodesInternal = true; - for ( size_t iL = 0; iL < polygon._links.size() && allNodesInternal; ++iL ) - { - _Node* n = polygon._links[ iL ].FirstNode(); - allNodesInternal = (( n->IsCutByInternal() ) || - ( n->_intPoint && _grid->IsAnyShared( n->_intPoint->_faceIDs ))); - } - if ( allNodesInternal ) - return true; - } - } - for ( size_t iP = 0; iP < _polygons.size(); ++iP ) - { - const _Face& polygon = _polygons[iP]; - if ( polygon._links.empty() ) - continue; - gp_XYZ area (0,0,0); - gp_XYZ p1 = polygon._links[ 0 ].FirstNode()->Point().XYZ(); - for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) - { - gp_XYZ p2 = polygon._links[ iL ].LastNode()->Point().XYZ(); - area += p1 ^ p2; - p1 = p2; - } - volume += p1 * area; - } - volume /= 6; - - if ( this->hasStrangeEdge() && volume > 1e-13 ) - return true; - - double initVolume = _sideLength[0] * _sideLength[1] * _sideLength[2]; - - return volume > initVolume / _grid->_sizeThreshold; - } - - //================================================================================ - /*! - * \brief Check that all faces in polyhedron are connected so a unique volume is defined. - * We test that it is possible to go from any node to all nodes in the polyhedron. - * The set of nodes that can be visit within then defines a unique element. - * In case more than one polyhedron is detected. The function return the set of quantities and nodes defining separates elements. - * Reference to issue #bos[38521][EDF] Generate polyhedron with separate volume. - */ - int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector>& splitQuantities, - std::vector>& splitNodes ) - { - int mySet = 1; - std::map numberOfSets; // define set id with the number of faces associated! - if ( !volDef->_quantities.empty() ) - { - auto connectivity = volDef->_quantities; - int accum = 0; - std::vector allFaces( connectivity.size(), false ); - std::set elementSet; - allFaces[ 0 ] = true; // the first node below to the first face - size_t connectedFaces = 1; - // Start filling the set with the nodes of the first face - splitQuantities.push_back( { connectivity[ 0 ] } ); - splitNodes.push_back( { volDef->_nodes[ 0 ].Node() } ); - elementSet.insert( volDef->_nodes[ 0 ].Node()->GetID() ); - for (int n = 1; n < connectivity[ 0 ]; n++) - { - elementSet.insert( volDef->_nodes[ n ].Node()->GetID() ); - splitNodes.back().push_back( volDef->_nodes[ n ].Node() ); - } - - numberOfSets.insert( std::pair(mySet,1) ); - while ( connectedFaces != allFaces.size() ) - { - for (size_t innerId = 1; innerId < connectivity.size(); innerId++) - { - if ( innerId == 1 ) - accum = connectivity[ 0 ]; - - if ( !allFaces[ innerId ] ) - { - int faceCounter = 0; - for (int n = 0; n < connectivity[ innerId ]; n++) - { - int nodeId = volDef->_nodes[ accum + n ].Node()->GetID(); - if ( elementSet.count( nodeId ) != 0 ) - faceCounter++; - } - if ( faceCounter >= 2 ) // found coincidences nodes - { - for (int n = 0; n < connectivity[ innerId ]; n++) - { - int nodeId = volDef->_nodes[ accum + n ].Node()->GetID(); - // insert new nodes so other faces can be identified as belowing to the element - splitNodes.back().push_back( volDef->_nodes[ accum + n ].Node() ); - elementSet.insert( nodeId ); - } - allFaces[ innerId ] = true; - splitQuantities.back().push_back( connectivity[ innerId ] ); - numberOfSets[ mySet ]++; - connectedFaces++; - innerId = 0; // to restart searching! - } - } - accum += connectivity[ innerId ]; - } - - if ( connectedFaces != allFaces.size() ) - { - // empty the set, and fill it with nodes of a unvisited face! - elementSet.clear(); - accum = connectivity[ 0 ]; - for (size_t faceId = 1; faceId < connectivity.size(); faceId++) - { - if ( !allFaces[ faceId ] ) - { - splitNodes.push_back( { volDef->_nodes[ accum ].Node() } ); - elementSet.insert( volDef->_nodes[ accum ].Node()->GetID() ); - for (int n = 1; n < connectivity[ faceId ]; n++) - { - elementSet.insert( volDef->_nodes[ accum + n ].Node()->GetID() ); - splitNodes.back().push_back( volDef->_nodes[ accum + n ].Node() ); - } - - splitQuantities.push_back( { connectivity[ faceId ] } ); - allFaces[ faceId ] = true; - connectedFaces++; - break; - } - accum += connectivity[ faceId ]; - } - mySet++; - numberOfSets.insert( std::pair(mySet,1) ); - } - } - - if ( numberOfSets.size() > 1 ) - { - bool allMoreThan2Faces = true; - for( auto k : numberOfSets ) - { - if ( k.second <= 2 ) - allMoreThan2Faces &= false; - } - - if ( allMoreThan2Faces ) - { - // The separate objects are suspect to be closed - return numberOfSets.size(); - } - else - { - // Have to index the last face nodes to the final set - // contrary case return as it were a valid polyhedron for backward compatibility - return 1; - } - } - } - return numberOfSets.size(); - } - - - //================================================================================ - /*! - * \brief add original or separated polyhedrons to the mesh - */ - const SMDS_MeshElement* Hexahedron::addPolyhedronToMesh( _volumeDef* volDef, SMESH_MesherHelper& helper, const std::vector& nodes, - const std::vector& quantities ) - { - const SMDS_MeshElement* v = helper.AddPolyhedralVolume( nodes, quantities ); - - volDef->_size = SMDS_VolumeTool( v ).GetSize(); - if ( volDef->_size < 0 ) // invalid polyhedron - { - if ( ! SMESH_MeshEditor( helper.GetMesh() ).Reorient( v ) || // try to fix - SMDS_VolumeTool( v ).GetSize() < 0 ) - { - helper.GetMeshDS()->RemoveFreeElement( v, /*sm=*/nullptr, /*fromGroups=*/false ); - v = nullptr; - //_hasTooSmall = true; - - if (SALOME::VerbosityActivated()) - { - std::cout << "Remove INVALID polyhedron, _cellID = " << _cellID - << " ijk = ( " << _i << " " << _j << " " << _k << " ) " - << " solid " << volDef->_solidID << std::endl; - } - } - } - return v; - } - - //================================================================================ - /*! - * \brief Tries to create a hexahedron - */ - bool Hexahedron::addHexa() - { - int nbQuad = 0, iQuad = -1; - for ( size_t i = 0; i < _polygons.size(); ++i ) - { - if ( _polygons[i]._links.empty() ) - continue; - if ( _polygons[i]._links.size() != 4 ) - return false; - ++nbQuad; - if ( iQuad < 0 ) - iQuad = i; - } - if ( nbQuad != 6 ) - return false; - - _Node* nodes[8]; - int nbN = 0; - for ( int iL = 0; iL < 4; ++iL ) - { - // a base node - nodes[iL] = _polygons[iQuad]._links[iL].FirstNode(); - ++nbN; - - // find a top node above the base node - _Link* link = _polygons[iQuad]._links[iL]._link; - if ( !link->_faces[0] || !link->_faces[1] ) - return debugDumpLink( link ); - // a quadrangle sharing with _polygons[iQuad] - _Face* quad = link->_faces[ bool( link->_faces[0] == & _polygons[iQuad] )]; - for ( int i = 0; i < 4; ++i ) - if ( quad->_links[i]._link == link ) - { - // 1st node of a link opposite to in - nodes[iL+4] = quad->_links[(i+2)%4].FirstNode(); - ++nbN; - break; - } - } - if ( nbN == 8 ) - _volumeDefs.Set( &nodes[0], 8 ); - - return nbN == 8; - } - //================================================================================ - /*! - * \brief Tries to create a tetrahedron - */ - bool Hexahedron::addTetra() - { - int iTria = -1; - for ( size_t i = 0; i < _polygons.size() && iTria < 0; ++i ) - if ( _polygons[i]._links.size() == 3 ) - iTria = i; - if ( iTria < 0 ) - return false; - - _Node* nodes[4]; - nodes[0] = _polygons[iTria]._links[0].FirstNode(); - nodes[1] = _polygons[iTria]._links[1].FirstNode(); - nodes[2] = _polygons[iTria]._links[2].FirstNode(); - - _Link* link = _polygons[iTria]._links[0]._link; - if ( !link->_faces[0] || !link->_faces[1] ) - return debugDumpLink( link ); - - // a triangle sharing with _polygons[0] - _Face* tria = link->_faces[ bool( link->_faces[0] == & _polygons[iTria] )]; - for ( int i = 0; i < 3; ++i ) - if ( tria->_links[i]._link == link ) - { - nodes[3] = tria->_links[(i+1)%3].LastNode(); - _volumeDefs.Set( &nodes[0], 4 ); - return true; - } - - return false; - } - //================================================================================ - /*! - * \brief Tries to create a pentahedron - */ - bool Hexahedron::addPenta() - { - // find a base triangular face - int iTri = -1; - for ( int iF = 0; iF < 5 && iTri < 0; ++iF ) - if ( _polygons[ iF ]._links.size() == 3 ) - iTri = iF; - if ( iTri < 0 ) return false; - - // find nodes - _Node* nodes[6]; - int nbN = 0; - for ( int iL = 0; iL < 3; ++iL ) - { - // a base node - nodes[iL] = _polygons[ iTri ]._links[iL].FirstNode(); - ++nbN; - - // find a top node above the base node - _Link* link = _polygons[ iTri ]._links[iL]._link; - if ( !link->_faces[0] || !link->_faces[1] ) - return debugDumpLink( link ); - // a quadrangle sharing with a base triangle - _Face* quad = link->_faces[ bool( link->_faces[0] == & _polygons[ iTri ] )]; - if ( quad->_links.size() != 4 ) return false; - for ( int i = 0; i < 4; ++i ) - if ( quad->_links[i]._link == link ) - { - // 1st node of a link opposite to in - nodes[iL+3] = quad->_links[(i+2)%4].FirstNode(); - ++nbN; - break; - } - } - if ( nbN == 6 ) - _volumeDefs.Set( &nodes[0], 6 ); - - return ( nbN == 6 ); - } - //================================================================================ - /*! - * \brief Tries to create a pyramid - */ - bool Hexahedron::addPyra() - { - // find a base quadrangle - int iQuad = -1; - for ( int iF = 0; iF < 5 && iQuad < 0; ++iF ) - if ( _polygons[ iF ]._links.size() == 4 ) - iQuad = iF; - if ( iQuad < 0 ) return false; - - // find nodes - _Node* nodes[5]; - nodes[0] = _polygons[iQuad]._links[0].FirstNode(); - nodes[1] = _polygons[iQuad]._links[1].FirstNode(); - nodes[2] = _polygons[iQuad]._links[2].FirstNode(); - nodes[3] = _polygons[iQuad]._links[3].FirstNode(); - - _Link* link = _polygons[iQuad]._links[0]._link; - if ( !link->_faces[0] || !link->_faces[1] ) - return debugDumpLink( link ); - - // a triangle sharing with a base quadrangle - _Face* tria = link->_faces[ bool( link->_faces[0] == & _polygons[ iQuad ] )]; - if ( tria->_links.size() != 3 ) return false; - for ( int i = 0; i < 3; ++i ) - if ( tria->_links[i]._link == link ) - { - nodes[4] = tria->_links[(i+1)%3].LastNode(); - _volumeDefs.Set( &nodes[0], 5 ); - return true; - } - - return false; - } - //================================================================================ - /*! - * \brief Return true if there are _eIntPoints at EDGEs forming a concave corner - */ - bool Hexahedron::hasEdgesAround( const ConcaveFace* cf ) const - { - int nbEdges = 0; - ConcaveFace foundGeomHolder; - for ( const E_IntersectPoint* ip : _eIntPoints ) - { - if ( cf->HasEdge( ip->_shapeID )) - { - if ( ++nbEdges == 2 ) - return true; - foundGeomHolder.SetEdge( ip->_shapeID ); - } - else if ( ip->_faceIDs.size() >= 3 ) - { - const TGeomID & vID = ip->_shapeID; - if ( cf->HasVertex( vID ) && !foundGeomHolder.HasVertex( vID )) - { - if ( ++nbEdges == 2 ) - return true; - foundGeomHolder.SetVertex( vID ); - } - } - } - - for ( const _Node& hexNode: _hexNodes ) - { - if ( !hexNode._node || !hexNode._intPoint ) - continue; - const B_IntersectPoint* ip = hexNode._intPoint; - if ( ip->_faceIDs.size() == 2 ) // EDGE - { - TGeomID edgeID = hexNode._node->GetShapeID(); - if ( cf->HasEdge( edgeID ) && !foundGeomHolder.HasEdge( edgeID )) - { - foundGeomHolder.SetEdge( edgeID ); - if ( ++nbEdges == 2 ) - return true; - } - } - else if ( ip->_faceIDs.size() >= 3 ) // VERTEX - { - TGeomID vID = hexNode._node->GetShapeID(); - if ( cf->HasVertex( vID ) && !foundGeomHolder.HasVertex( vID )) - { - if ( ++nbEdges == 2 ) - return true; - foundGeomHolder.SetVertex( vID ); - } - } - } - - return false; - } - //================================================================================ - /*! - * \brief Dump a link and return \c false - */ - bool Hexahedron::debugDumpLink( Hexahedron::_Link* link ) - { - if (SALOME::VerbosityActivated()) - { - gp_Pnt p1 = link->_nodes[0]->Point(), p2 = link->_nodes[1]->Point(); - cout << "BUG: not shared link. IKJ = ( "<< _i << " " << _j << " " << _k << " )" << endl - << "n1 (" << p1.X() << ", "<< p1.Y() << ", "<< p1.Z() << " )" << endl - << "n2 (" << p2.X() << ", "<< p2.Y() << ", "<< p2.Z() << " )" << endl; - } - - return false; - } - //================================================================================ - /*! - * \brief Classify a point by grid parameters - */ - bool Hexahedron::isOutParam(const double uvw[3]) const - { - return (( _grid->_coords[0][ _i ] - _grid->_tol > uvw[0] ) || - ( _grid->_coords[0][ _i+1 ] + _grid->_tol < uvw[0] ) || - ( _grid->_coords[1][ _j ] - _grid->_tol > uvw[1] ) || - ( _grid->_coords[1][ _j+1 ] + _grid->_tol < uvw[1] ) || - ( _grid->_coords[2][ _k ] - _grid->_tol > uvw[2] ) || - ( _grid->_coords[2][ _k+1 ] + _grid->_tol < uvw[2] )); - } - //================================================================================ - /*! - * \brief Find existing triangulation of a polygon - */ - int findExistingTriangulation( const SMDS_MeshElement* polygon, - //const SMDS_Mesh* mesh, - std::vector< const SMDS_MeshNode* >& nodes ) - { - int nbSplits = 0; - nodes.clear(); - std::vector twoNodes(2); - std::vector foundFaces; foundFaces.reserve(10); - std::set< const SMDS_MeshElement * > avoidFaces; avoidFaces.insert( polygon ); - - const int nbPolyNodes = polygon->NbCornerNodes(); - twoNodes[1] = polygon->GetNode( nbPolyNodes - 1 ); - for ( int iN = 0; iN < nbPolyNodes; ++iN ) // loop on border links of polygon - { - twoNodes[0] = polygon->GetNode( iN ); - - int nbFaces = SMDS_Mesh::GetElementsByNodes( twoNodes, foundFaces, SMDSAbs_Face ); - int nbOkFaces = 0; - for ( int iF = 0; iF < nbFaces; ++iF ) // keep faces lying over polygon - { - if ( avoidFaces.count( foundFaces[ iF ])) - continue; - int i, nbFaceNodes = foundFaces[ iF ]->NbCornerNodes(); - for ( i = 0; i < nbFaceNodes; ++i ) - { - const SMDS_MeshNode* n = foundFaces[ iF ]->GetNode( i ); - bool isCommonNode = ( n == twoNodes[0] || - n == twoNodes[1] || - polygon->GetNodeIndex( n ) >= 0 ); - if ( !isCommonNode ) - break; - } - if ( i == nbFaceNodes ) // all nodes of foundFaces[iF] are shared with polygon - if ( nbOkFaces++ != iF ) - foundFaces[ nbOkFaces-1 ] = foundFaces[ iF ]; - } - if ( nbOkFaces > 0 ) - { - int iFaceSelected = 0; - if ( nbOkFaces > 1 ) // select a face with minimal distance from polygon - { - double minDist = Precision::Infinite(); - for ( int iF = 0; iF < nbOkFaces; ++iF ) - { - int i, nbFaceNodes = foundFaces[ iF ]->NbCornerNodes(); - gp_XYZ gc = SMESH_NodeXYZ( foundFaces[ iF ]->GetNode( 0 )); - for ( i = 1; i < nbFaceNodes; ++i ) - gc += SMESH_NodeXYZ( foundFaces[ iF ]->GetNode( i )); - gc /= nbFaceNodes; - - double dist = SMESH_MeshAlgos::GetDistance( polygon, gc ); - if ( dist < minDist ) - { - minDist = dist; - iFaceSelected = iF; - } - } - } - if ( foundFaces[ iFaceSelected ]->NbCornerNodes() != 3 ) - return 0; - nodes.insert( nodes.end(), - foundFaces[ iFaceSelected ]->begin_nodes(), - foundFaces[ iFaceSelected ]->end_nodes()); - if ( !SMESH_MeshAlgos::IsRightOrder( foundFaces[ iFaceSelected ], - twoNodes[0], twoNodes[1] )) - { - // reverse just added nodes - std::reverse( nodes.end() - 3, nodes.end() ); - } - avoidFaces.insert( foundFaces[ iFaceSelected ]); - nbSplits++; - } - - twoNodes[1] = twoNodes[0]; - - } // loop on polygon nodes - - return nbSplits; - } - //================================================================================ - /*! - * \brief Divide a polygon into triangles and modify accordingly an adjacent polyhedron - */ - void splitPolygon( const SMDS_MeshElement* polygon, - SMDS_VolumeTool & volume, - const int facetIndex, - const TGeomID faceID, - const TGeomID solidID, - SMESH_MeshEditor::ElemFeatures& face, - SMESH_MeshEditor& editor, - const bool reinitVolume) - { - SMESH_MeshAlgos::Triangulate divider(/*optimize=*/false); - bool triangulationExist = false; - int nbTrias = findExistingTriangulation( polygon, face.myNodes ); - if ( nbTrias > 0 ) - triangulationExist = true; - else - nbTrias = divider.GetTriangles( polygon, face.myNodes ); - face.myNodes.resize( nbTrias * 3 ); - - SMESH_MeshEditor::ElemFeatures newVolumeDef; - newVolumeDef.Init( volume.Element() ); - newVolumeDef.SetID( volume.Element()->GetID() ); - - newVolumeDef.myPolyhedQuantities.reserve( volume.NbFaces() + nbTrias ); - newVolumeDef.myNodes.reserve( volume.NbNodes() + nbTrias * 3 ); - - SMESHDS_Mesh* meshDS = editor.GetMeshDS(); - SMDS_MeshElement* newTriangle; - for ( int iF = 0, nF = volume.NbFaces(); iF < nF; iF++ ) - { - if ( iF == facetIndex ) - { - newVolumeDef.myPolyhedQuantities.push_back( 3 ); - newVolumeDef.myNodes.insert( newVolumeDef.myNodes.end(), - face.myNodes.begin(), - face.myNodes.begin() + 3 ); - meshDS->RemoveFreeElement( polygon, 0, false ); - if ( !triangulationExist ) - { - newTriangle = meshDS->AddFace( face.myNodes[0], face.myNodes[1], face.myNodes[2] ); - meshDS->SetMeshElementOnShape( newTriangle, faceID ); - } - } - else - { - const SMDS_MeshNode** nn = volume.GetFaceNodes( iF ); - const size_t nbFaceNodes = volume.NbFaceNodes ( iF ); - newVolumeDef.myPolyhedQuantities.push_back( nbFaceNodes ); - newVolumeDef.myNodes.insert( newVolumeDef.myNodes.end(), nn, nn + nbFaceNodes ); - } - } - - for ( size_t iN = 3; iN < face.myNodes.size(); iN += 3 ) - { - newVolumeDef.myPolyhedQuantities.push_back( 3 ); - newVolumeDef.myNodes.insert( newVolumeDef.myNodes.end(), - face.myNodes.begin() + iN, - face.myNodes.begin() + iN + 3 ); - if ( !triangulationExist ) - { - newTriangle = meshDS->AddFace( face.myNodes[iN], face.myNodes[iN+1], face.myNodes[iN+2] ); - meshDS->SetMeshElementOnShape( newTriangle, faceID ); - } - } - - meshDS->RemoveFreeElement( volume.Element(), 0, false ); - SMDS_MeshElement* newVolume = editor.AddElement( newVolumeDef.myNodes, newVolumeDef ); - meshDS->SetMeshElementOnShape( newVolume, solidID ); - - if ( reinitVolume ) - { - volume.Set( 0 ); - volume.Set( newVolume ); - } - return; - } - //================================================================================ - /*! - * \brief Look for a FACE supporting all given nodes made on EDGEs and VERTEXes - */ - TGeomID findCommonFace( const std::vector< const SMDS_MeshNode* > & nn, - const SMESH_Mesh* mesh ) - { - TGeomID faceID = 0; - TGeomID shapeIDs[20]; - for ( size_t iN = 0; iN < nn.size(); ++iN ) - shapeIDs[ iN ] = nn[ iN ]->GetShapeID(); - - SMESH_subMesh* sm = mesh->GetSubMeshContaining( shapeIDs[ 0 ]); - for ( const SMESH_subMesh * smFace : sm->GetAncestors() ) - { - if ( smFace->GetSubShape().ShapeType() != TopAbs_FACE ) - continue; - - faceID = smFace->GetId(); - - for ( size_t iN = 1; iN < nn.size() && faceID; ++iN ) - { - if ( !smFace->DependsOn( shapeIDs[ iN ])) - faceID = 0; - } - if ( faceID > 0 ) - break; - } - return faceID; - } - //================================================================================ - /*! - * \brief Create mesh faces at free facets - */ - void Hexahedron::addFaces( SMESH_MesherHelper& helper, - const vector< const SMDS_MeshElement* > & boundaryVolumes ) - { - if ( !_grid->_toCreateFaces ) - return; - - SMDS_VolumeTool vTool; - vector bndFacets; - SMESH_MeshEditor editor( helper.GetMesh() ); - SMESH_MeshEditor::ElemFeatures face( SMDSAbs_Face ); - SMESHDS_Mesh* meshDS = helper.GetMeshDS(); - - bool isQuantaSet = _grid->_toUseQuanta; - // check if there are internal or shared FACEs - bool hasInternal = ( !_grid->_geometry.IsOneSolid() || - _grid->_geometry._soleSolid.HasInternalFaces() ); - - for ( size_t iV = 0; iV < boundaryVolumes.size(); ++iV ) - { - if ( !vTool.Set( boundaryVolumes[ iV ])) - continue; - TGeomID solidID = vTool.Element()->GetShapeID(); - Solid * solid = _grid->GetOneOfSolids( solidID ); - - // find boundary facets - bndFacets.clear(); - for ( int iF = 0, n = vTool.NbFaces(); iF < n; iF++ ) - { - const SMDS_MeshElement* otherVol; - bool isBoundary = isQuantaSet ? vTool.IsFreeFaceCheckAllNodes( iF, &otherVol ) : vTool.IsFreeFace( iF, &otherVol ); - if ( isBoundary ) - { - bndFacets.push_back( iF ); - } - else if (( hasInternal ) || - ( !_grid->IsSolid( otherVol->GetShapeID() ))) - { - // check if all nodes are on internal/shared FACEs - isBoundary = true; - const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF ); - const size_t nbFaceNodes = vTool.NbFaceNodes ( iF ); - for ( size_t iN = 0; iN < nbFaceNodes && isBoundary; ++iN ) - isBoundary = ( nn[ iN ]->GetShapeID() != solidID ); - if ( isBoundary ) - bndFacets.push_back( -( iF+1 )); // !!! minus ==> to check the FACE - } - } - if ( bndFacets.empty() ) - continue; - - // create faces - if ( !vTool.IsPoly() ) - vTool.SetExternalNormal(); - for ( size_t i = 0; i < bndFacets.size(); ++i ) // loop on boundary facets - { - const bool isBoundary = ( bndFacets[i] >= 0 ); - const int iFacet = isBoundary ? bndFacets[i] : -bndFacets[i]-1; - const SMDS_MeshNode** nn = vTool.GetFaceNodes( iFacet ); - const size_t nbFaceNodes = vTool.NbFaceNodes ( iFacet ); - face.myNodes.assign( nn, nn + nbFaceNodes ); - - TGeomID faceID = 0; - const SMDS_MeshElement* existFace = 0, *newFace = 0; - - if (( existFace = meshDS->FindElement( face.myNodes, SMDSAbs_Face ))) - { - if ( existFace->isMarked() ) - continue; // created by this method - faceID = existFace->GetShapeID(); - } - else - { - // look for a supporting FACE - for ( size_t iN = 0; iN < nbFaceNodes && !faceID; ++iN ) // look for a node on FACE - { - if ( nn[ iN ]->GetPosition()->GetDim() == 2 ) - faceID = nn[ iN ]->GetShapeID(); - } - if ( faceID == 0 && !isQuantaSet /*if quanta is set boundary nodes at boundary does not coincide with any geometrical face */ ) - faceID = findCommonFace( face.myNodes, helper.GetMesh() ); - - bool toCheckFace = faceID && (( !isBoundary ) || - ( hasInternal && _grid->_toUseThresholdForInternalFaces )); - if ( toCheckFace ) // check if all nodes are on the found FACE - { - SMESH_subMesh* faceSM = helper.GetMesh()->GetSubMeshContaining( faceID ); - for ( size_t iN = 0; iN < nbFaceNodes && faceID; ++iN ) - { - TGeomID subID = nn[ iN ]->GetShapeID(); - if ( subID != faceID && !faceSM->DependsOn( subID )) - faceID = 0; - } - // if ( !faceID && !isBoundary ) - // continue; - } - if ( !faceID && !isBoundary && !isQuantaSet ) - continue; - } - - // orient a new face according to supporting FACE orientation in shape_to_mesh - if ( !isBoundary && !solid->IsOutsideOriented( faceID )) - { - if ( existFace ) - editor.Reorient( existFace ); - else - std::reverse( face.myNodes.begin(), face.myNodes.end() ); - } - - if ( ! ( newFace = existFace )) - { - face.SetPoly( nbFaceNodes > 4 ); - newFace = editor.AddElement( face.myNodes, face ); - if ( !newFace ) - continue; - newFace->setIsMarked( true ); // to distinguish from face created in getBoundaryElems() - } - - if ( faceID && _grid->IsBoundaryFace( faceID )) // face is not shared - { - // set newFace to the found FACE provided that it fully lies on the FACE - for ( size_t iN = 0; iN < nbFaceNodes && faceID; ++iN ) - if ( nn[iN]->GetShapeID() == solidID ) - { - if ( existFace ) - meshDS->UnSetMeshElementOnShape( existFace, _grid->Shape( faceID )); - faceID = 0; - } - } - - if ( faceID && nbFaceNodes > 4 && - !_grid->IsInternal( faceID ) && - !_grid->IsShared( faceID ) && - !_grid->IsBoundaryFace( faceID )) - { - // split a polygon that will be used by other 3D algorithm - if ( !existFace ) - splitPolygon( newFace, vTool, iFacet, faceID, solidID, - face, editor, i+1 < bndFacets.size() ); - } - else - { - if ( faceID ) - meshDS->SetMeshElementOnShape( newFace, faceID ); - else - meshDS->SetMeshElementOnShape( newFace, solidID ); - } - } // loop on bndFacets - } // loop on boundaryVolumes - - - // Orient coherently mesh faces on INTERNAL FACEs - - if ( hasInternal ) - { - TopExp_Explorer faceExp( _grid->_geometry._mainShape, TopAbs_FACE ); - for ( ; faceExp.More(); faceExp.Next() ) - { - if ( faceExp.Current().Orientation() != TopAbs_INTERNAL ) - continue; - - SMESHDS_SubMesh* sm = meshDS->MeshElements( faceExp.Current() ); - if ( !sm ) continue; - - TIDSortedElemSet facesToOrient; - for ( SMDS_ElemIteratorPtr fIt = sm->GetElements(); fIt->more(); ) - facesToOrient.insert( facesToOrient.end(), fIt->next() ); - if ( facesToOrient.size() < 2 ) - continue; - - gp_Dir direction(1,0,0); - TIDSortedElemSet refFaces; - editor.Reorient2D( facesToOrient, direction, refFaces, /*allowNonManifold=*/true ); - } - } - return; - } +//STD +#include +#include - //================================================================================ - /*! - * \brief Create mesh segments. - */ - void Hexahedron::addSegments( SMESH_MesherHelper& helper, - const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap ) - { - SMESHDS_Mesh* mesh = helper.GetMeshDS(); +// BOOST +#include - std::vector nodes; - std::vector elems; - map< TGeomID, vector< TGeomID > >::const_iterator e2ff = edge2faceIDsMap.begin(); - for ( ; e2ff != edge2faceIDsMap.end(); ++e2ff ) - { - const TopoDS_Edge& edge = TopoDS::Edge( _grid->Shape( e2ff->first )); - const TopoDS_Face& face = TopoDS::Face( _grid->Shape( e2ff->second[0] )); - StdMeshers_FaceSide side( face, edge, helper.GetMesh(), /*isFwd=*/true, /*skipMed=*/true ); - nodes = side.GetOrderedNodes(); +#ifdef WITH_TBB - elems.clear(); - if ( nodes.size() == 2 ) - // check that there is an element connecting two nodes - if ( !mesh->GetElementsByNodes( nodes, elems )) - continue; +#ifdef WIN32 +// See https://docs.microsoft.com/en-gb/cpp/porting/modifying-winver-and-win32-winnt?view=vs-2019 +// Windows 10 = 0x0A00 +#define WINVER 0x0A00 +#define _WIN32_WINNT 0x0A00 +#endif - for ( size_t i = 1; i < nodes.size(); i++ ) - { - if ( mesh->FindEdge( nodes[i-1], nodes[i] )) - continue; - SMDS_MeshElement* segment = mesh->AddEdge( nodes[i-1], nodes[i] ); - mesh->SetMeshElementOnShape( segment, e2ff->first ); - } - } - return; - } +#include +//#include +#endif - //================================================================================ +using namespace std; +using namespace SMESH; +using namespace gridtools; +// using namespace facegridintersector; +namespace +{ /*! - * \brief Return created volumes and volumes that can have free facet because of - * skipped small volume. Also create mesh faces on free facets - * of adjacent not-cut volumes if the result volume is too small. + * \brief Temporary mesh to hold */ - void Hexahedron::getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryElems ) + struct TmpMesh: public SMESH_Mesh { - if ( _hasTooSmall /*|| _volumeDefs.IsEmpty()*/ ) - { - // create faces around a missing small volume - TGeomID faceID = 0; - SMESH_MeshEditor editor( _grid->_helper->GetMesh() ); - SMESH_MeshEditor::ElemFeatures polygon( SMDSAbs_Face ); - SMESHDS_Mesh* meshDS = _grid->_helper->GetMeshDS(); - std::vector adjVolumes(2); - for ( size_t iF = 0; iF < _polygons.size(); ++iF ) - { - const size_t nbLinks = _polygons[ iF ]._links.size(); - if ( nbLinks != 4 ) continue; - polygon.myNodes.resize( nbLinks ); - polygon.myNodes.back() = 0; - for ( size_t iL = 0, iN = nbLinks - 1; iL < nbLinks; ++iL, --iN ) - if ( ! ( polygon.myNodes[iN] = _polygons[ iF ]._links[ iL ].FirstNode()->Node() )) - break; - if ( !polygon.myNodes.back() ) - continue; - - meshDS->GetElementsByNodes( polygon.myNodes, adjVolumes, SMDSAbs_Volume ); - if ( adjVolumes.size() != 1 ) - continue; - if ( !adjVolumes[0]->isMarked() ) - { - boundaryElems.push_back( adjVolumes[0] ); - adjVolumes[0]->setIsMarked( true ); - } - - bool sameShape = true; - TGeomID shapeID = polygon.myNodes[0]->GetShapeID(); - for ( size_t i = 1; i < polygon.myNodes.size() && sameShape; ++i ) - sameShape = ( shapeID == polygon.myNodes[i]->GetShapeID() ); - - if ( !sameShape || !_grid->IsSolid( shapeID )) - continue; // some of shapes must be FACE - - if ( !faceID ) - { - faceID = getAnyFace(); - if ( !faceID ) - break; - if ( _grid->IsInternal( faceID ) || - _grid->IsShared( faceID ) //|| - //_grid->IsBoundaryFace( faceID ) -- commented for #19887 - ) - break; // create only if a new face will be used by other 3D algo - } - - Solid * solid = _grid->GetOneOfSolids( adjVolumes[0]->GetShapeID() ); - if ( !solid->IsOutsideOriented( faceID )) - std::reverse( polygon.myNodes.begin(), polygon.myNodes.end() ); - - //polygon.SetPoly( polygon.myNodes.size() > 4 ); - const SMDS_MeshElement* newFace = editor.AddElement( polygon.myNodes, polygon ); - meshDS->SetMeshElementOnShape( newFace, faceID ); - } - } - - // return created volumes - for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) - { - if ( volDef ->_volume && - !volDef->_volume->IsNull() && - !volDef->_volume->isMarked() ) - { - volDef->_volume->setIsMarked( true ); - boundaryElems.push_back( volDef->_volume ); - - if ( _grid->IsToCheckNodePos() ) // un-mark nodes marked in addVolumes() - for ( size_t iN = 0; iN < volDef->_nodes.size(); ++iN ) - volDef->_nodes[iN].Node()->setIsMarked( false ); - } - if ( volDef->_brotherVolume.size() > 0 ) - { - for (auto _bro : volDef->_brotherVolume ) - { - _bro->setIsMarked( true ); - boundaryElems.push_back( _bro ); - } - } + TmpMesh() { + _isShapeToMesh = (_id = 0); + _meshDS = new SMESHDS_Mesh( _id, true ); } - } - - //================================================================================ - /*! - * \brief Remove edges and nodes dividing a hexa side in the case if an adjacent - * volume also sharing the dividing edge is missing due to its small side. - * Issue #19887. - */ - //================================================================================ - - void Hexahedron::removeExcessSideDivision(const vector< Hexahedron* >& allHexa) - { - if ( ! _volumeDefs.IsPolyhedron() ) - return; // not a polyhedron - - // look for a divided side adjacent to a small hexahedron - - int di[6] = { 0, 0, 0, 0,-1, 1 }; - int dj[6] = { 0, 0,-1, 1, 0, 0 }; - int dk[6] = {-1, 1, 0, 0, 0, 0 }; - - for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron - { - size_t neighborIndex = _grid->CellIndex( _i + di[iF], - _j + dj[iF], - _k + dk[iF] ); - if ( neighborIndex >= allHexa.size() || - !allHexa[ neighborIndex ] || - !allHexa[ neighborIndex ]->_hasTooSmall ) - continue; - - // check if a side is divided into several polygons - for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) - { - int nbPolygons = 0, nbNodes = 0; - for ( size_t i = 0; i < volDef->_names.size(); ++i ) - if ( volDef->_names[ i ] == _hexQuads[ iF ]._name ) - { - ++nbPolygons; - nbNodes += volDef->_quantities[ i ]; - } - if ( nbPolygons < 2 ) - continue; - - // construct loops from polygons - typedef _volumeDef::_linkDef TLinkDef; - std::vector< TLinkDef* > loops; - std::vector< TLinkDef > links( nbNodes ); - for ( size_t i = 0, iN = 0, iLoop = 0; iLoop < volDef->_quantities.size(); ++iLoop ) - { - size_t nbLinks = volDef->_quantities[ iLoop ]; - if ( volDef->_names[ iLoop ] != _hexQuads[ iF ]._name ) - { - iN += nbLinks; - continue; - } - loops.push_back( & links[i] ); - for ( size_t n = 0; n < nbLinks-1; ++n, ++i, ++iN ) - { - links[i].init( volDef->_nodes[iN], volDef->_nodes[iN+1], iLoop ); - links[i].setNext( &links[i+1] ); - } - links[i].init( volDef->_nodes[iN], volDef->_nodes[iN-nbLinks+1], iLoop ); - links[i].setNext( &links[i-nbLinks+1] ); - ++i; ++iN; - } - - // look for equal links in different loops and join such loops - bool loopsJoined = false; - std::set< TLinkDef > linkSet; - for ( size_t iLoop = 0; iLoop < loops.size(); ++iLoop ) - { - TLinkDef* beg = 0; - for ( TLinkDef* l = loops[ iLoop ]; l != beg; l = l->_next ) // walk around the iLoop - { - std::pair< std::set< TLinkDef >::iterator, bool > it2new = linkSet.insert( *l ); - if ( !it2new.second ) // equal found, join loops - { - const TLinkDef* equal = &(*it2new.first); - if ( equal->_loopIndex == l->_loopIndex ) - continue; // error? - - loopsJoined = true; - - for ( size_t i = iLoop - 1; i < loops.size(); --i ) - if ( loops[ i ] && loops[ i ]->_loopIndex == equal->_loopIndex ) - loops[ i ] = 0; - - // exclude l and equal and join two loops - if ( l->_prev != equal ) - l->_prev->setNext( equal->_next ); - if ( equal->_prev != l ) - equal->_prev->setNext( l->_next ); - - if ( volDef->_quantities[ l->_loopIndex ] > 0 ) - volDef->_quantities[ l->_loopIndex ] *= -1; - if ( volDef->_quantities[ equal->_loopIndex ] > 0 ) - volDef->_quantities[ equal->_loopIndex ] *= -1; - - if ( loops[ iLoop ] == l ) - loops[ iLoop ] = l->_prev->_next; - } - beg = loops[ iLoop ]; - } - } - // update volDef - if ( loopsJoined ) - { - // set unchanged polygons - std::vector< int > newQuantities; - std::vector< _volumeDef::_nodeDef > newNodes; - vector< SMESH_Block::TShapeID > newNames; - newQuantities.reserve( volDef->_quantities.size() ); - newNodes.reserve ( volDef->_nodes.size() ); - newNames.reserve ( volDef->_names.size() ); - for ( size_t i = 0, iLoop = 0; iLoop < volDef->_quantities.size(); ++iLoop ) - { - if ( volDef->_quantities[ iLoop ] < 0 ) - { - i -= volDef->_quantities[ iLoop ]; - continue; - } - newQuantities.push_back( volDef->_quantities[ iLoop ]); - newNodes.insert( newNodes.end(), - volDef->_nodes.begin() + i, - volDef->_nodes.begin() + i + newQuantities.back() ); - newNames.push_back( volDef->_names[ iLoop ]); - i += volDef->_quantities[ iLoop ]; - } - - // set joined loops - for ( size_t iLoop = 0; iLoop < loops.size(); ++iLoop ) - { - if ( !loops[ iLoop ] ) - continue; - newQuantities.push_back( 0 ); - TLinkDef* beg = 0; - for ( TLinkDef* l = loops[ iLoop ]; l != beg; l = l->_next, ++newQuantities.back() ) - { - newNodes.push_back( l->_node1 ); - beg = loops[ iLoop ]; - } - newNames.push_back( _hexQuads[ iF ]._name ); - } - volDef->_quantities.swap( newQuantities ); - volDef->_nodes.swap( newNodes ); - volDef->_names.swap( newNames ); - } - } // loop on volDef's - } // loop on hex sides - - return; - } // removeExcessSideDivision() - - - //================================================================================ - /*! - * \brief Remove nodes splitting Cartesian cell edges in the case if a node - * is used in every cells only by two polygons sharing the edge - * Issue #19887. - */ - //================================================================================ - - void Hexahedron::removeExcessNodes(vector< Hexahedron* >& allHexa) - { - if ( ! _volumeDefs.IsPolyhedron() ) - return; // not a polyhedron - - typedef vector< _volumeDef::_nodeDef >::iterator TNodeIt; - vector< int > nodesInPoly[ 4 ]; // node index in _volumeDefs._nodes - vector< int > volDefInd [ 4 ]; // index of a _volumeDefs - Hexahedron* hexa [ 4 ]; - int i,j,k, cellIndex, iLink = 0, iCellLink; - for ( int iDir = 0; iDir < 3; ++iDir ) - { - CellsAroundLink fourCells( _grid, iDir ); - for ( int iL = 0; iL < 4; ++iL, ++iLink ) // 4 links in a direction - { - _Link& link = _hexLinks[ iLink ]; - fourCells.Init( _i, _j, _k, iLink ); - - for ( size_t iP = 0; iP < link._fIntPoints.size(); ++iP ) // loop on nodes on the link - { - bool nodeRemoved = true; - _volumeDef::_nodeDef node; node._intPoint = link._fIntPoints[iP]; - - for ( size_t i = 0, nb = _volumeDefs.size(); i < nb && nodeRemoved; ++i ) - if ( _volumeDef* vol = _volumeDefs.at( i )) - nodeRemoved = - ( std::find( vol->_nodes.begin(), vol->_nodes.end(), node ) == vol->_nodes.end() ); - if ( nodeRemoved ) - continue; // node already removed - - // check if a node encounters zero or two times in 4 cells sharing iLink - // if so, the node can be removed from the cells - bool nodeIsOnEdge = true; - int nbPolyhedraWithNode = 0; - for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing a link - { - nodesInPoly[ iC ].clear(); - volDefInd [ iC ].clear(); - hexa [ iC ] = 0; - if ( !fourCells.GetCell( iC, i,j,k, cellIndex, iCellLink )) - continue; - hexa[ iC ] = allHexa[ cellIndex ]; - if ( !hexa[ iC ]) - continue; - for ( size_t i = 0, nb = hexa[ iC ]->_volumeDefs.size(); i < nb; ++i ) - if ( _volumeDef* vol = hexa[ iC ]->_volumeDefs.at( i )) - { - for ( TNodeIt nIt = vol->_nodes.begin(); nIt != vol->_nodes.end(); ++nIt ) - { - nIt = std::find( nIt, vol->_nodes.end(), node ); - if ( nIt != vol->_nodes.end() ) - { - nodesInPoly[ iC ].push_back( std::distance( vol->_nodes.begin(), nIt )); - volDefInd [ iC ].push_back( i ); - } - else - break; - } - nbPolyhedraWithNode += ( !nodesInPoly[ iC ].empty() ); - } - if ( nodesInPoly[ iC ].size() != 0 && - nodesInPoly[ iC ].size() != 2 ) - { - nodeIsOnEdge = false; - break; - } - } // loop on 4 cells - - // remove nodes from polyhedra - if ( nbPolyhedraWithNode > 0 && nodeIsOnEdge ) - { - for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing the link - { - if ( nodesInPoly[ iC ].empty() ) - continue; - for ( int i = volDefInd[ iC ].size() - 1; i >= 0; --i ) - { - _volumeDef* vol = hexa[ iC ]->_volumeDefs.at( volDefInd[ iC ][ i ]); - int nIndex = nodesInPoly[ iC ][ i ]; - // decrement _quantities - for ( size_t iQ = 0; iQ < vol->_quantities.size(); ++iQ ) - if ( nIndex < vol->_quantities[ iQ ]) - { - vol->_quantities[ iQ ]--; - break; - } - else - { - nIndex -= vol->_quantities[ iQ ]; - } - vol->_nodes.erase( vol->_nodes.begin() + nodesInPoly[ iC ][ i ]); + }; +} // namespace - if ( i == 0 && - vol->_nodes.size() == 6 * 4 && - vol->_quantities.size() == 6 ) // polyhedron becomes hexahedron? - { - bool allQuads = true; - for ( size_t iQ = 0; iQ < vol->_quantities.size() && allQuads; ++iQ ) - allQuads = ( vol->_quantities[ iQ ] == 4 ); - if ( allQuads ) - { - // set side nodes as this: bottom, top, top, ... - int iTop = 0, iBot = 0; // side indices - for ( int iS = 0; iS < 6; ++iS ) - { - if ( vol->_names[ iS ] == SMESH_Block::ID_Fxy0 ) - iBot = iS; - if ( vol->_names[ iS ] == SMESH_Block::ID_Fxy1 ) - iTop = iS; - } - if ( iBot != 0 ) - { - if ( iTop == 0 ) - { - std::copy( vol->_nodes.begin(), - vol->_nodes.begin() + 4, - vol->_nodes.begin() + 4 ); - iTop = 1; - } - std::copy( vol->_nodes.begin() + 4 * iBot, - vol->_nodes.begin() + 4 * ( iBot + 1), - vol->_nodes.begin() ); - } - if ( iTop != 1 ) - std::copy( vol->_nodes.begin() + 4 * iTop, - vol->_nodes.begin() + 4 * ( iTop + 1), - vol->_nodes.begin() + 4 ); +//============================================================================= +/*! + * Constructor + */ +//============================================================================= - std::copy( vol->_nodes.begin() + 4, - vol->_nodes.begin() + 8, - vol->_nodes.begin() + 8 ); - // set up top facet nodes by comparing their uvw with bottom nodes - E_IntersectPoint ip[8]; - for ( int iN = 0; iN < 8; ++iN ) - { - SMESH_NodeXYZ p = vol->_nodes[ iN ].Node(); - _grid->ComputeUVW( p, ip[ iN ]._uvw ); - } - const double tol2 = _grid->_tol * _grid->_tol; - for ( int iN = 0; iN < 4; ++iN ) - { - gp_Pnt2d pBot( ip[ iN ]._uvw[0], ip[ iN ]._uvw[1] ); - for ( int iT = 4; iT < 8; ++iT ) - { - gp_Pnt2d pTop( ip[ iT ]._uvw[0], ip[ iT ]._uvw[1] ); - if ( pBot.SquareDistance( pTop ) < tol2 ) - { - // vol->_nodes[ iN + 4 ]._node = ip[ iT ]._node; - // vol->_nodes[ iN + 4 ]._intPoint = 0; - vol->_nodes[ iN + 4 ] = vol->_nodes[ iT + 4 ]; - break; - } - } - } - vol->_nodes.resize( 8 ); - vol->_quantities.clear(); - //vol->_names.clear(); - } - } - } // loop on _volumeDefs - } // loop on 4 cell abound a link - } // if ( nodeIsOnEdge ) - } // loop on intersection points of a link - } // loop on 4 links of a direction - } // loop on 3 directions +StdMeshers_Cartesian_3D::StdMeshers_Cartesian_3D(int hypId, SMESH_Gen * gen) + :SMESH_3D_Algo(hypId, gen) +{ + _name = "Cartesian_3D"; + _shapeType = (1 << TopAbs_SOLID); // 1 bit /shape type + _compatibleHypothesis.push_back( "CartesianParameters3D" ); + _compatibleHypothesis.push_back( StdMeshers_ViscousLayers::GetHypType() ); - return; + _onlyUnaryInput = false; // to mesh all SOLIDs at once + _requireDiscreteBoundary = false; // 2D mesh not needed + _supportSubmeshes = false; // do not use any existing mesh +} - } // removeExcessNodes() +//============================================================================= +/*! + * Check presence of a hypothesis + */ +//============================================================================= - //================================================================================ - /*! - * \brief [Issue #19913] Modify _hexLinks._splits to prevent creating overlapping volumes - */ - //================================================================================ +bool StdMeshers_Cartesian_3D::CheckHypothesis (SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + Hypothesis_Status& aStatus) +{ + aStatus = SMESH_Hypothesis::HYP_MISSING; - void Hexahedron::preventVolumesOverlapping() + const list& hyps = GetUsedHypothesis(aMesh, aShape, /*skipAux=*/false); + list ::const_iterator h = hyps.begin(); + if ( h == hyps.end()) { - // Cut off a quadrangle corner if two links sharing the corner - // are shared by same two solids, in this case each of solids gets - // a triangle for it-self. - std::vector< TGeomID > soIDs[4]; - for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron - { - _Face& quad = _hexQuads[ iF ] ; - - int iFOpposite = iF + ( iF % 2 ? -1 : 1 ); - _Face& quadOpp = _hexQuads[ iFOpposite ] ; - - int nbSides = 0, nbSidesOpp = 0; - for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle - { - nbSides += ( quad._links [ iE ].NbResultLinks() > 0 ); - nbSidesOpp += ( quadOpp._links[ iE ].NbResultLinks() > 0 ); - } - if ( nbSides < 4 || nbSidesOpp != 2 ) - continue; - - for ( int iE = 0; iE < 4; ++iE ) - { - soIDs[ iE ].clear(); - _Node* n = quad._links[ iE ].FirstNode(); - if ( n->_intPoint && n->_intPoint->_faceIDs.size() ) - soIDs[ iE ] = _grid->GetSolidIDs( n->_intPoint->_faceIDs[0] ); - } - if ((( soIDs[0].size() >= 2 ) + - ( soIDs[1].size() >= 2 ) + - ( soIDs[2].size() >= 2 ) + - ( soIDs[3].size() >= 2 ) ) < 3 ) - continue; + return false; + } - bool done = false; - for ( int i = 0; i < 4; ++i ) - { - int i1 = _grid->_helper->WrapIndex( i + 1, 4 ); - int i2 = _grid->_helper->WrapIndex( i + 2, 4 ); - int i3 = _grid->_helper->WrapIndex( i + 3, 4 ); - if ( soIDs[i1].size() == 2 && soIDs[i ] != soIDs[i1] && - soIDs[i2].size() == 2 && soIDs[i1] == soIDs[i2] && - soIDs[i3].size() == 2 && soIDs[i2] == soIDs[i3] ) - { - quad._links[ i1 ]._link->_splits.clear(); - quad._links[ i2 ]._link->_splits.clear(); - done = true; - break; - } - } - if ( done ) - break; - } - return; - } // preventVolumesOverlapping() + _hyp = nullptr; + _hypViscousLayers = nullptr; + _isComputeOffset = false; - //================================================================================ - /*! - * \brief Set to _hexLinks a next portion of splits located on one side of INTERNAL FACEs - */ - bool Hexahedron::_SplitIterator::Next() + for ( ; h != hyps.end(); ++h ) { - if ( _iterationNb > 0 ) - // count used splits - for ( size_t i = 0; i < _splits.size(); ++i ) - { - if ( _splits[i]._iCheckIteration == _iterationNb ) - { - _splits[i]._isUsed = _splits[i]._checkedSplit->_faces[1]; - _nbUsed += _splits[i]._isUsed; - } - if ( !More() ) - return false; - } - - ++_iterationNb; - - bool toTestUsed = ( _nbChecked >= _splits.size() ); - if ( toTestUsed ) + if ( !_hyp && ( _hyp = dynamic_cast( *h ))) { - // all splits are checked; find all not used splits - for ( size_t i = 0; i < _splits.size(); ++i ) - if ( !_splits[i].IsCheckedOrUsed( toTestUsed )) - _splits[i]._iCheckIteration = _iterationNb; - - _nbUsed = _splits.size(); // to stop iteration + aStatus = _hyp->IsDefined() ? HYP_OK : HYP_BAD_PARAMETER; } else { - // get any not used/checked split to start from - _freeNodes.clear(); - for ( size_t i = 0; i < _splits.size(); ++i ) - { - if ( !_splits[i].IsCheckedOrUsed( toTestUsed )) - { - _freeNodes.push_back( _splits[i]._nodes[0] ); - _freeNodes.push_back( _splits[i]._nodes[1] ); - _splits[i]._iCheckIteration = _iterationNb; - break; - } - } - // find splits connected to the start one via _freeNodes - for ( size_t iN = 0; iN < _freeNodes.size(); ++iN ) - { - for ( size_t iS = 0; iS < _splits.size(); ++iS ) - { - if ( _splits[iS].IsCheckedOrUsed( toTestUsed )) - continue; - int iN2 = -1; - if ( _freeNodes[iN] == _splits[iS]._nodes[0] ) - iN2 = 1; - else if ( _freeNodes[iN] == _splits[iS]._nodes[1] ) - iN2 = 0; - else - continue; - if ( _freeNodes[iN]->_isInternalFlags > 0 ) - { - if ( _splits[iS]._nodes[ iN2 ]->_isInternalFlags == 0 ) - continue; - if ( !_splits[iS]._nodes[ iN2 ]->IsLinked( _freeNodes[iN]->_intPoint )) - continue; - } - _splits[iS]._iCheckIteration = _iterationNb; - _freeNodes.push_back( _splits[iS]._nodes[ iN2 ]); - } - } - } - // set splits to hex links - - for ( int iL = 0; iL < 12; ++iL ) - _hexLinks[ iL ]._splits.clear(); - - _Link split; - for ( size_t i = 0; i < _splits.size(); ++i ) - { - if ( _splits[i]._iCheckIteration == _iterationNb ) - { - split._nodes[0] = _splits[i]._nodes[0]; - split._nodes[1] = _splits[i]._nodes[1]; - _Link & hexLink = _hexLinks[ _splits[i]._linkID ]; - hexLink._splits.push_back( split ); - _splits[i]._checkedSplit = & hexLink._splits.back(); - ++_nbChecked; - } + _hypViscousLayers = dynamic_cast( *h ); } - return More(); } - //================================================================================ - /*! - * \brief computes exact bounding box with axes parallel to given ones - */ - //================================================================================ - - void getExactBndBox( const vector< TopoDS_Shape >& faceVec, - const double* axesDirs, - Bnd_Box& shapeBox ) - { - BRep_Builder b; - TopoDS_Compound allFacesComp; - b.MakeCompound( allFacesComp ); - for ( size_t iF = 0; iF < faceVec.size(); ++iF ) - b.Add( allFacesComp, faceVec[ iF ] ); - - double sP[6]; // aXmin, aYmin, aZmin, aXmax, aYmax, aZmax - shapeBox.Get(sP[0],sP[1],sP[2],sP[3],sP[4],sP[5]); - double farDist = 0; - for ( int i = 0; i < 6; ++i ) - farDist = Max( farDist, 10 * sP[i] ); - - gp_XYZ axis[3] = { gp_XYZ( axesDirs[0], axesDirs[1], axesDirs[2] ), - gp_XYZ( axesDirs[3], axesDirs[4], axesDirs[5] ), - gp_XYZ( axesDirs[6], axesDirs[7], axesDirs[8] ) }; - axis[0].Normalize(); - axis[1].Normalize(); - axis[2].Normalize(); - - gp_Mat basis( axis[0], axis[1], axis[2] ); - gp_Mat bi = basis.Inverted(); - - gp_Pnt pMin, pMax; - for ( int iDir = 0; iDir < 3; ++iDir ) - { - gp_XYZ axis0 = axis[ iDir ]; - gp_XYZ axis1 = axis[ ( iDir + 1 ) % 3 ]; - gp_XYZ axis2 = axis[ ( iDir + 2 ) % 3 ]; - for ( int isMax = 0; isMax < 2; ++isMax ) - { - double shift = isMax ? farDist : -farDist; - gp_XYZ orig = shift * axis0; - gp_XYZ norm = axis1 ^ axis2; - gp_Pln pln( orig, norm ); - norm = pln.Axis().Direction().XYZ(); - BRepBuilderAPI_MakeFace plane( pln, -farDist, farDist, -farDist, farDist ); - - gp_Pnt& pAxis = isMax ? pMax : pMin; - gp_Pnt pPlane, pFaces; - double dist = GEOMUtils::GetMinDistance( plane, allFacesComp, pPlane, pFaces ); - if ( dist < 0 ) - { - Bnd_B3d bb; - gp_XYZ corner; - for ( int i = 0; i < 2; ++i ) { - corner.SetCoord( 1, sP[ i*3 ]); - for ( int j = 0; j < 2; ++j ) { - corner.SetCoord( 2, sP[ i*3 + 1 ]); - for ( int k = 0; k < 2; ++k ) - { - corner.SetCoord( 3, sP[ i*3 + 2 ]); - corner *= bi; - bb.Add( corner ); - } - } - } - corner = isMax ? bb.CornerMax() : bb.CornerMin(); - pAxis.SetCoord( iDir+1, corner.Coord( iDir+1 )); - } - else - { - gp_XYZ pf = pFaces.XYZ() * bi; - pAxis.SetCoord( iDir+1, pf.Coord( iDir+1 ) ); - } - } - } // loop on 3 axes - - shapeBox.SetVoid(); - shapeBox.Add( pMin ); - shapeBox.Add( pMax ); + return aStatus == HYP_OK; +} - return; - } -} // namespace //============================================================================= /*! @@ -6697,91 +192,22 @@ bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh & theMesh, grid._sizeThreshold = _hyp->GetSizeThreshold(); grid._toUseQuanta = _hyp->GetToUseQuanta(); grid._quanta = _hyp->GetQuanta(); + if ( _isComputeOffset ) { grid._toAddEdges = true; grid._toCreateFaces = true; } - grid.InitGeometry( theShape ); - - vector< TopoDS_Shape > faceVec; - { - TopTools_MapOfShape faceMap; - TopExp_Explorer fExp; - for ( fExp.Init( theShape, TopAbs_FACE ); fExp.More(); fExp.Next() ) - { - bool isNewFace = faceMap.Add( fExp.Current() ); - if ( !grid._toConsiderInternalFaces ) - if ( !isNewFace || fExp.Current().Orientation() == TopAbs_INTERNAL ) - // remove an internal face - faceMap.Remove( fExp.Current() ); - } - faceVec.reserve( faceMap.Extent() ); - faceVec.assign( faceMap.cbegin(), faceMap.cend() ); - } - vector facesItersectors( faceVec.size() ); - Bnd_Box shapeBox; - for ( size_t i = 0; i < faceVec.size(); ++i ) - { - facesItersectors[i]._face = TopoDS::Face( faceVec[i] ); - facesItersectors[i]._faceID = grid.ShapeID( faceVec[i] ); - facesItersectors[i]._grid = &grid; - shapeBox.Add( facesItersectors[i].GetFaceBndBox() ); - } - getExactBndBox( faceVec, _hyp->GetAxisDirs(), shapeBox ); - - - vector xCoords, yCoords, zCoords; - _hyp->GetCoordinates( xCoords, yCoords, zCoords, shapeBox ); - - grid.SetCoordinates( xCoords, yCoords, zCoords, _hyp->GetAxisDirs(), shapeBox ); - - if ( _computeCanceled ) return false; - -#ifdef WITH_TBB - { // copy partner faces and curves of not thread-safe types - set< const Standard_Transient* > tshapes; - BRepBuilderAPI_Copy copier; - for ( size_t i = 0; i < facesItersectors.size(); ++i ) - { - if ( !facesItersectors[i].IsThreadSafe( tshapes )) - { - copier.Perform( facesItersectors[i]._face ); - facesItersectors[i]._face = TopoDS::Face( copier ); - } - } - } - // Intersection of grid lines with the geometry boundary. - tbb::parallel_for ( tbb::blocked_range( 0, facesItersectors.size() ), - ParallelIntersector( facesItersectors ), - tbb::simple_partitioner()); -#else - for ( size_t i = 0; i < facesItersectors.size(); ++i ) - facesItersectors[i].Intersect(); -#endif - // put intersection points onto the GridLine's; this is done after intersection - // to avoid contention of facesItersectors for writing into the same GridLine - // in case of parallel work of facesItersectors - for ( size_t i = 0; i < facesItersectors.size(); ++i ) - facesItersectors[i].StoreIntersections(); - - if ( _computeCanceled ) return false; - - // create nodes on the geometry - grid.ComputeNodes( helper ); - - if ( _computeCanceled ) return false; - - // get EDGEs to take into account + const auto numOfThreads = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency(); map< TGeomID, vector< TGeomID > > edge2faceIDsMap; - grid.GetEdgesToImplement( edge2faceIDsMap, theShape, faceVec ); + grid.GridInitAndInterserctWithShape( theShape, edge2faceIDsMap, _hyp, numOfThreads, _computeCanceled ); // create volume elements Hexahedron hex( &grid ); - int nbAdded = hex.MakeElements( helper, edge2faceIDsMap ); - - if ( nbAdded > 0 ) + + int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, numOfThreads ); + if ( nbAdded > 0 ) { if ( !grid._toConsiderInternalFaces ) { @@ -6803,7 +229,7 @@ bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh & theMesh, } // remove free nodes - //if ( SMESHDS_SubMesh * smDS = meshDS->MeshElements( helper.GetSubShapeID() )) + if ( SMESHDS_SubMesh * smDS = meshDS->MeshElements( helper.GetSubShapeID() )) { std::vector< const SMDS_MeshNode* > nodesToRemove; // get intersection nodes @@ -6870,21 +296,6 @@ bool StdMeshers_Cartesian_3D::Evaluate(SMESH_Mesh & /*theMesh*/, const TopoDS_Shape & /*theShape*/, MapShapeNbElems& /*theResMap*/) { - // TODO -// std::vector aResVec(SMDSEntity_Last); -// for(int i=SMDSEntity_Node; i +#endif + +using namespace SMESH; +using namespace gridtools; +std::mutex _bMutex; +//============================================================================= +/* + * Remove coincident intersection points + */ +void GridLine::RemoveExcessIntPoints( const double tol ) +{ + if ( _intPoints.size() < 2 ) return; + + set< Transition > tranSet; + multiset< F_IntersectPoint >::iterator ip1, ip2 = _intPoints.begin(); + while ( ip2 != _intPoints.end() ) + { + tranSet.clear(); + ip1 = ip2++; + while ( ip2 != _intPoints.end() && ip2->_paramOnLine - ip1->_paramOnLine <= tol ) + { + tranSet.insert( ip1->_transition ); + tranSet.insert( ip2->_transition ); + ip2->Add( ip1->_faceIDs ); + _intPoints.erase( ip1 ); + ip1 = ip2++; + } + if ( tranSet.size() > 1 ) // points with different transition coincide + { + bool isIN = tranSet.count( Trans_IN ); + bool isOUT = tranSet.count( Trans_OUT ); + if ( isIN && isOUT ) + (*ip1)._transition = Trans_TANGENT; + else + (*ip1)._transition = isIN ? Trans_IN : Trans_OUT; + } + } +} +//================================================================================ +/* + * Return ID of SOLID for nodes before the given intersection point + */ +TGeomID GridLine::GetSolidIDBefore( multiset< F_IntersectPoint >::iterator ip, + const TGeomID prevID, + const Geometry& geom ) +{ + if ( ip == _intPoints.begin() ) + return 0; + + if ( geom.IsOneSolid() ) + { + bool isOut = true; + switch ( ip->_transition ) { + case Trans_IN: isOut = true; break; + case Trans_OUT: isOut = false; break; + case Trans_TANGENT: isOut = ( prevID != 0 ); break; + case Trans_APEX: + { + // singularity point (apex of a cone) + multiset< F_IntersectPoint >::iterator ipBef = ip, ipAft = ++ip; + if ( ipAft == _intPoints.end() ) + isOut = false; + else + { + --ipBef; + if ( ipBef->_transition != ipAft->_transition ) + isOut = ( ipBef->_transition == Trans_OUT ); + else + isOut = ( ipBef->_transition != Trans_OUT ); + } + break; + } + case Trans_INTERNAL: isOut = false; + default:; + } + return isOut ? 0 : geom._soleSolid.ID(); + } + + GeomIDVecHelder solids = geom.GetSolidIDsByShapeID( ip->_faceIDs ); + + --ip; + if ( ip->_transition == Trans_INTERNAL ) + return prevID; + + GeomIDVecHelder solidsBef = geom.GetSolidIDsByShapeID( ip->_faceIDs ); + + if ( ip->_transition == Trans_IN || + ip->_transition == Trans_OUT ) + { + if ( solidsBef.size() == 1 ) + { + if ( solidsBef[0] == prevID ) + return ip->_transition == Trans_OUT ? 0 : solidsBef[0]; + else + return solidsBef[0]; + } + + if ( solids.size() == 2 ) + { + if ( solids == solidsBef ) + return solids.contain( prevID ) ? solids.otherThan( prevID ) : theUndefID; // bos #29212 + } + return solids.oneCommon( solidsBef ); + } + + if ( solidsBef.size() == 1 ) + return solidsBef[0]; + + return solids.oneCommon( solidsBef ); +} + +//================================================================================ +/* + * Adds face IDs + */ +bool B_IntersectPoint::Add( const vector< TGeomID >& fIDs, + const SMDS_MeshNode* n) const +{ + const std::lock_guard lock(_bMutex); + size_t prevNbF = _faceIDs.size(); + + if ( _faceIDs.empty() ) + _faceIDs = fIDs; + else + for ( size_t i = 0; i < fIDs.size(); ++i ) + { + vector< TGeomID >::iterator it = + std::find( _faceIDs.begin(), _faceIDs.end(), fIDs[i] ); + if ( it == _faceIDs.end() ) + _faceIDs.push_back( fIDs[i] ); + } + if ( !_node && n != NULL ) + _node = n; + + return prevNbF < _faceIDs.size(); +} +//================================================================================ +/* + * Return ID of a common face if any, else zero + */ +TGeomID B_IntersectPoint::HasCommonFace( const B_IntersectPoint * other, TGeomID avoidFace ) const +{ + if ( other ) + for ( size_t i = 0; i < other->_faceIDs.size(); ++i ) + if ( avoidFace != other->_faceIDs[i] && + IsOnFace ( other->_faceIDs[i] )) + return other->_faceIDs[i]; + return 0; +} +//================================================================================ +/* + * Return faces common with other point + */ +size_t B_IntersectPoint::GetCommonFaces( const B_IntersectPoint * other, TGeomID* common ) const +{ + size_t nbComm = 0; + if ( !other ) + return nbComm; + if ( _faceIDs.size() > other->_faceIDs.size() ) + return other->GetCommonFaces( this, common ); + for ( const TGeomID& face : _faceIDs ) + if ( other->IsOnFace( face )) + common[ nbComm++ ] = face; + return nbComm; +} +//================================================================================ +/* + * Return \c true if \a faceID in in this->_faceIDs + */ +bool B_IntersectPoint::IsOnFace( TGeomID faceID ) const // returns true if faceID is found +{ + vector< TGeomID >::const_iterator it = + std::find( _faceIDs.begin(), _faceIDs.end(), faceID ); + return ( it != _faceIDs.end() ); +} + +//================================================================================ +/* + * OneOfSolids initialization + */ +void OneOfSolids::Init( const TopoDS_Shape& solid, + TopAbs_ShapeEnum subType, + const SMESHDS_Mesh* mesh ) +{ + SetID( mesh->ShapeToIndex( solid )); + + if ( subType == TopAbs_FACE ) + SetHasInternalFaces( false ); + + for ( TopExp_Explorer sub( solid, subType ); sub.More(); sub.Next() ) + { + _subIDs.Add( mesh->ShapeToIndex( sub.Current() )); + if ( subType == TopAbs_FACE ) + { + _faces.Add( sub.Current() ); + if ( sub.Current().Orientation() == TopAbs_INTERNAL ) + SetHasInternalFaces( true ); + + TGeomID faceID = mesh->ShapeToIndex( sub.Current() ); + if ( sub.Current().Orientation() == TopAbs_INTERNAL || + sub.Current().Orientation() == mesh->IndexToShape( faceID ).Orientation() ) + _outFaceIDs.Add( faceID ); + } + } +} + +//============================================================================= +/* + * Return a vector of SOLIDS sharing given shapes + */ +GeomIDVecHelder Geometry::GetSolidIDsByShapeID( const vector< TGeomID >& theShapeIDs ) const +{ + if ( theShapeIDs.size() == 1 ) + return GeomIDVecHelder( _solidIDsByShapeID[ theShapeIDs[ 0 ]], /*owner=*/false ); + + // look for an empty slot in _solidIDsByShapeID + vector< TGeomID > * resultIDs = 0; + for ( const vector< TGeomID >& vec : _solidIDsByShapeID ) + if ( vec.empty() ) + { + resultIDs = const_cast< vector< TGeomID > * >( & vec ); + break; + } + // fill in resultIDs + for ( const TGeomID& id : theShapeIDs ) + for ( const TGeomID& solid : _solidIDsByShapeID[ id ]) + { + if ( std::find( resultIDs->begin(), resultIDs->end(), solid ) == resultIDs->end() ) + resultIDs->push_back( solid ); + } + return GeomIDVecHelder( *resultIDs, /*owner=*/true ); +} + +//================================================================================ +/* + * Return an iterator on GridLine's in a given direction + */ +LineIndexer Grid::GetLineIndexer(size_t iDir) const +{ + const size_t indices[] = { 1,2,0, 0,2,1, 0,1,2 }; + const string s [] = { "X", "Y", "Z" }; + LineIndexer li( _coords[0].size(), _coords[1].size(), _coords[2].size(), + indices[iDir*3], indices[iDir*3+1], indices[iDir*3+2], + s[indices[iDir*3]], s[indices[iDir*3+1]], s[indices[iDir*3+2]]); + return li; +} +//================================================================================ +/* + * Return direction [0,1,2] of a GridLine + */ +size_t Grid::GetLineDir( const GridLine* line, size_t & index ) const +{ + for ( size_t iDir = 0; iDir < 3; ++iDir ) + if ( &_lines[ iDir ][0] <= line && line <= &_lines[ iDir ].back() ) + { + index = line - &_lines[ iDir ][0]; + return iDir; + } + return -1; +} +//============================================================================= +/* + * Creates GridLine's of the grid + */ +void Grid::SetCoordinates(const vector& xCoords, + const vector& yCoords, + const vector& zCoords, + const double* axesDirs, + const Bnd_Box& shapeBox) +{ + _coords[0] = xCoords; + _coords[1] = yCoords; + _coords[2] = zCoords; + + _axes[0].SetCoord( axesDirs[0], + axesDirs[1], + axesDirs[2]); + _axes[1].SetCoord( axesDirs[3], + axesDirs[4], + axesDirs[5]); + _axes[2].SetCoord( axesDirs[6], + axesDirs[7], + axesDirs[8]); + _axes[0].Normalize(); + _axes[1].Normalize(); + _axes[2].Normalize(); + + _invB.SetCols( _axes[0], _axes[1], _axes[2] ); + _invB.Invert(); + + // compute tolerance + _minCellSize = Precision::Infinite(); + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + for ( size_t i = 1; i < _coords[ iDir ].size(); ++i ) + { + double cellLen = _coords[ iDir ][ i ] - _coords[ iDir ][ i-1 ]; + if ( cellLen < _minCellSize ) + _minCellSize = cellLen; + } + } + if ( _minCellSize < Precision::Confusion() ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Too small cell size: ") << _minCellSize ); + _tol = _minCellSize / 1000.; + + // attune grid extremities to shape bounding box + + double sP[6]; // aXmin, aYmin, aZmin, aXmax, aYmax, aZmax + shapeBox.Get(sP[0],sP[1],sP[2],sP[3],sP[4],sP[5]); + double* cP[6] = { &_coords[0].front(), &_coords[1].front(), &_coords[2].front(), + &_coords[0].back(), &_coords[1].back(), &_coords[2].back() }; + for ( int i = 0; i < 6; ++i ) + if ( fabs( sP[i] - *cP[i] ) < _tol ) + *cP[i] = sP[i];// + _tol/1000. * ( i < 3 ? +1 : -1 ); + + for ( int iDir = 0; iDir < 3; ++iDir ) + { + if ( _coords[iDir][0] - sP[iDir] > _tol ) + { + _minCellSize = Min( _minCellSize, _coords[iDir][0] - sP[iDir] ); + _coords[iDir].insert( _coords[iDir].begin(), sP[iDir] + _tol/1000.); + } + if ( sP[iDir+3] - _coords[iDir].back() > _tol ) + { + _minCellSize = Min( _minCellSize, sP[iDir+3] - _coords[iDir].back() ); + _coords[iDir].push_back( sP[iDir+3] - _tol/1000.); + } + } + _tol = _minCellSize / 1000.; + + _origin = ( _coords[0][0] * _axes[0] + + _coords[1][0] * _axes[1] + + _coords[2][0] * _axes[2] ); + + // create lines + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + LineIndexer li = GetLineIndexer( iDir ); + _lines[iDir].resize( li.NbLines() ); + double len = _coords[ iDir ].back() - _coords[iDir].front(); + for ( ; li.More(); ++li ) + { + GridLine& gl = _lines[iDir][ li.LineIndex() ]; + gl._line.SetLocation( _coords[0][li.I()] * _axes[0] + + _coords[1][li.J()] * _axes[1] + + _coords[2][li.K()] * _axes[2] ); + gl._line.SetDirection( _axes[ iDir ]); + gl._length = len; + } + } +} +//================================================================================ +/* + * Return local ID of shape + */ +TGeomID Grid::ShapeID( const TopoDS_Shape& s ) const +{ + return _helper->GetMeshDS()->ShapeToIndex( s ); +} +//================================================================================ +/* + * Return a shape by its local ID + */ +const TopoDS_Shape& Grid::Shape( TGeomID id ) const +{ + return _helper->GetMeshDS()->IndexToShape( id ); +} +//================================================================================ +/* + * Initialize _geometry + */ +void Grid::InitGeometry( const TopoDS_Shape& theShapeToMesh ) +{ + SMESH_Mesh* mesh = _helper->GetMesh(); + + _geometry._mainShape = theShapeToMesh; + _geometry._extIntFaceID = mesh->GetMeshDS()->MaxShapeIndex() * 100; + _geometry._soleSolid.SetID( 0 ); + _geometry._soleSolid.SetHasInternalFaces( false ); + + InitClassifier( theShapeToMesh, TopAbs_VERTEX, _geometry._vertexClassifier ); + InitClassifier( theShapeToMesh, TopAbs_EDGE , _geometry._edgeClassifier ); + + TopExp_Explorer solidExp( theShapeToMesh, TopAbs_SOLID ); + + bool isSeveralSolids = false; + if ( _toConsiderInternalFaces ) // check nb SOLIDs + { + solidExp.Next(); + isSeveralSolids = solidExp.More(); + _toConsiderInternalFaces = isSeveralSolids; + solidExp.ReInit(); + + if ( !isSeveralSolids ) // look for an internal FACE + { + TopExp_Explorer fExp( theShapeToMesh, TopAbs_FACE ); + for ( ; fExp.More() && !_toConsiderInternalFaces; fExp.Next() ) + _toConsiderInternalFaces = ( fExp.Current().Orientation() == TopAbs_INTERNAL ); + + _geometry._soleSolid.SetHasInternalFaces( _toConsiderInternalFaces ); + _geometry._soleSolid.SetID( ShapeID( solidExp.Current() )); + } + else // fill Geometry::_solidByID + { + for ( ; solidExp.More(); solidExp.Next() ) + { + OneOfSolids & solid = _geometry._solidByID[ ShapeID( solidExp.Current() )]; + solid.Init( solidExp.Current(), TopAbs_FACE, mesh->GetMeshDS() ); + solid.Init( solidExp.Current(), TopAbs_EDGE, mesh->GetMeshDS() ); + solid.Init( solidExp.Current(), TopAbs_VERTEX, mesh->GetMeshDS() ); + } + } + } + else + { + _geometry._soleSolid.SetID( ShapeID( solidExp.Current() )); + } + + if ( !_toCreateFaces ) + { + int nbSolidsGlobal = _helper->Count( mesh->GetShapeToMesh(), TopAbs_SOLID, false ); + int nbSolidsLocal = _helper->Count( theShapeToMesh, TopAbs_SOLID, false ); + _toCreateFaces = ( nbSolidsLocal < nbSolidsGlobal ); + } + + TopTools_IndexedMapOfShape faces; + TopExp::MapShapes( theShapeToMesh, TopAbs_FACE, faces ); + + // find boundary FACEs on boundary of mesh->ShapeToMesh() + if ( _toCreateFaces ) + for ( int i = 1; i <= faces.Size(); ++i ) + if ( faces(i).Orientation() != TopAbs_INTERNAL && + _helper->NbAncestors( faces(i), *mesh, TopAbs_SOLID ) == 1 ) + { + _geometry._boundaryFaces.Add( ShapeID( faces(i) )); + } + + if ( isSeveralSolids ) + for ( int i = 1; i <= faces.Size(); ++i ) + { + SetSolidFather( faces(i), theShapeToMesh ); + for ( TopExp_Explorer eExp( faces(i), TopAbs_EDGE ); eExp.More(); eExp.Next() ) + { + const TopoDS_Edge& edge = TopoDS::Edge( eExp.Current() ); + SetSolidFather( edge, theShapeToMesh ); + SetSolidFather( _helper->IthVertex( 0, edge ), theShapeToMesh ); + SetSolidFather( _helper->IthVertex( 1, edge ), theShapeToMesh ); + } + } + + // fill in _geometry._shape2NbNodes == find already meshed sub-shapes + _geometry._shape2NbNodes.Clear(); + if ( mesh->NbNodes() > 0 ) + { + for ( TopAbs_ShapeEnum type : { TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX }) + for ( TopExp_Explorer exp( theShapeToMesh, type ); exp.More(); exp.Next() ) + { + if ( _geometry._shape2NbNodes.IsBound( exp.Current() )) + continue; + if ( SMESHDS_SubMesh* sm = mesh->GetMeshDS()->MeshElements( exp.Current() )) + if ( sm->NbNodes() > 0 ) + _geometry._shape2NbNodes.Bind( exp.Current(), sm->NbNodes() ); + } + } + + // fill in Solid::_concaveVertex + vector< TGeomID > soleSolidID( 1, _geometry._soleSolid.ID() ); + for ( int i = 1; i <= faces.Size(); ++i ) + { + const TopoDS_Face& F = TopoDS::Face( faces( i )); + TError error; + TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, *mesh, 0, error, + nullptr, nullptr, false ); + for ( StdMeshers_FaceSidePtr& wire : wires ) + { + const int nbEdges = wire->NbEdges(); + if ( nbEdges < 2 && SMESH_Algo::isDegenerated( wire->Edge(0))) + continue; + for ( int iE1 = 0; iE1 < nbEdges; ++iE1 ) + { + if ( SMESH_Algo::isDegenerated( wire->Edge( iE1 ))) continue; + int iE2 = ( iE1 + 1 ) % nbEdges; + while ( SMESH_Algo::isDegenerated( wire->Edge( iE2 ))) + iE2 = ( iE2 + 1 ) % nbEdges; + TopoDS_Vertex V = wire->FirstVertex( iE2 ); + double angle = _helper->GetAngle( wire->Edge( iE1 ), + wire->Edge( iE2 ), F, V ); + if ( angle < -5. * M_PI / 180. ) + { + TGeomID faceID = ShapeID( F ); + const vector< TGeomID > & solids = + _geometry.IsOneSolid() ? soleSolidID : GetSolidIDs( faceID ); + for ( const TGeomID & solidID : solids ) + { + Solid* solid = GetSolid( solidID ); + TGeomID V1 = ShapeID( wire->FirstVertex( iE1 )); + TGeomID V2 = ShapeID( wire->LastVertex ( iE2 )); + solid->SetConcave( ShapeID( V ), faceID, + wire->EdgeID( iE1 ), wire->EdgeID( iE2 ), V1, V2 ); + } + } + } + } + } + + return; +} +//================================================================================ +/* + * Store ID of SOLID as father of its child shape ID + */ +void Grid::SetSolidFather( const TopoDS_Shape& s, const TopoDS_Shape& theShapeToMesh ) +{ + if ( _geometry._solidIDsByShapeID.empty() ) + _geometry._solidIDsByShapeID.resize( _helper->GetMeshDS()->MaxShapeIndex() + 1 ); + + vector< TGeomID > & solidIDs = _geometry._solidIDsByShapeID[ ShapeID( s )]; + if ( !solidIDs.empty() ) + return; + solidIDs.reserve(2); + PShapeIteratorPtr solidIt = _helper->GetAncestors( s, + *_helper->GetMesh(), + TopAbs_SOLID, + & theShapeToMesh ); + while ( const TopoDS_Shape* solid = solidIt->next() ) + solidIDs.push_back( ShapeID( *solid )); +} +//================================================================================ +/* + * Return IDs of solids given sub-shape belongs to + */ +const vector< TGeomID > & Grid::GetSolidIDs( TGeomID subShapeID ) const +{ + return _geometry._solidIDsByShapeID[ subShapeID ]; +} +//================================================================================ +/* + * Check if a sub-shape belongs to several SOLIDs + */ +bool Grid::IsShared( TGeomID shapeID ) const +{ + return !_geometry.IsOneSolid() && ( _geometry._solidIDsByShapeID[ shapeID ].size() > 1 ); +} +//================================================================================ +/* + * Check if any of FACEs belongs to several SOLIDs + */ +bool Grid::IsAnyShared( const std::vector< TGeomID >& faceIDs ) const +{ + for ( size_t i = 0; i < faceIDs.size(); ++i ) + if ( IsShared( faceIDs[ i ])) + return true; + return false; +} +//================================================================================ +/* + * Return Solid by ID + */ +Solid* Grid::GetSolid( TGeomID solidID ) +{ + if ( !solidID || _geometry.IsOneSolid() || _geometry._solidByID.empty() ) + return & _geometry._soleSolid; + + return & _geometry._solidByID[ solidID ]; +} +//================================================================================ +/* + * Return OneOfSolids by ID + */ +Solid* Grid::GetOneOfSolids( TGeomID solidID ) +{ + map< TGeomID, OneOfSolids >::iterator is2s = _geometry._solidByID.find( solidID ); + if ( is2s != _geometry._solidByID.end() ) + return & is2s->second; + + return & _geometry._soleSolid; +} +//================================================================================ +/* + * Check if transition on given FACE is correct for a given SOLID + */ +bool Grid::IsCorrectTransition( TGeomID faceID, const Solid* solid ) +{ + if ( _geometry.IsOneSolid() ) + return true; + + const vector< TGeomID >& solidIDs = _geometry._solidIDsByShapeID[ faceID ]; + return solidIDs[0] == solid->ID(); +} + +//================================================================================ +/* + * Assign to geometry a node at FACE intersection + * Return a found supporting VERTEX + */ +void Grid::SetOnShape( const SMDS_MeshNode* n, const F_IntersectPoint& ip, TopoDS_Vertex* vertex, bool unset ) +{ + TopoDS_Shape s; + SMESHDS_Mesh* mesh = _helper->GetMeshDS(); + if ( ip._faceIDs.size() == 1 ) + { + mesh->SetNodeOnFace( n, ip._faceIDs[0], ip._u, ip._v ); + } + else if ( _geometry._vertexClassifier.IsSatisfy( n, &s )) + { + if ( unset ) mesh->UnSetNodeOnShape( n ); + mesh->SetNodeOnVertex( n, TopoDS::Vertex( s )); + if ( vertex ) + *vertex = TopoDS::Vertex( s ); + } + else if ( _geometry._edgeClassifier.IsSatisfy( n, &s )) + { + if ( unset ) mesh->UnSetNodeOnShape( n ); + mesh->SetNodeOnEdge( n, TopoDS::Edge( s )); + } + else if ( ip._faceIDs.size() > 0 ) + { + mesh->SetNodeOnFace( n, ip._faceIDs[0], ip._u, ip._v ); + } + else if ( !unset && _geometry.IsOneSolid() ) + { + mesh->SetNodeInVolume( n, _geometry._soleSolid.ID() ); + } +} +//================================================================================ +/* + * Fill in B_IntersectPoint::_faceIDs with all FACEs sharing a VERTEX + */ +void Grid::UpdateFacesOfVertex( const B_IntersectPoint& ip, const TopoDS_Vertex& vertex ) +{ + if ( vertex.IsNull() ) + return; + std::vector< int > faceID(1); + PShapeIteratorPtr fIt = _helper->GetAncestors( vertex, *_helper->GetMesh(), + TopAbs_FACE, & _geometry._mainShape ); + while ( const TopoDS_Shape* face = fIt->next() ) + { + faceID[ 0 ] = ShapeID( *face ); + ip.Add( faceID ); + } +} +//================================================================================ +/* + * Initialize a classifier + */ +void Grid::InitClassifier( const TopoDS_Shape& mainShape, + TopAbs_ShapeEnum shapeType, + Controls::ElementsOnShape& classifier ) +{ + TopTools_IndexedMapOfShape shapes; + TopExp::MapShapes( mainShape, shapeType, shapes ); + + TopoDS_Compound compound; BRep_Builder builder; + builder.MakeCompound( compound ); + for ( int i = 1; i <= shapes.Size(); ++i ) + builder.Add( compound, shapes(i) ); + + classifier.SetMesh( _helper->GetMeshDS() ); + //classifier.SetTolerance( _tol ); // _tol is not initialised + classifier.SetShape( compound, SMDSAbs_Node ); +} + +//================================================================================ +/* + * Return EDGEs with FACEs to implement into the mesh + */ +void Grid::GetEdgesToImplement( map< TGeomID, vector< TGeomID > > & edge2faceIDsMap, + const TopoDS_Shape& shape, + const vector< TopoDS_Shape >& faces ) +{ + // check if there are strange EDGEs + TopTools_IndexedMapOfShape faceMap; + TopExp::MapShapes( _helper->GetMesh()->GetShapeToMesh(), TopAbs_FACE, faceMap ); + int nbFacesGlobal = faceMap.Size(); + faceMap.Clear( false ); + TopExp::MapShapes( shape, TopAbs_FACE, faceMap ); + int nbFacesLocal = faceMap.Size(); + bool hasStrangeEdges = ( nbFacesGlobal > nbFacesLocal ); + if ( !_toAddEdges && !hasStrangeEdges ) + return; // no FACEs in contact with those meshed by other algo + + for ( size_t i = 0; i < faces.size(); ++i ) + { + _helper->SetSubShape( faces[i] ); + for ( TopExp_Explorer eExp( faces[i], TopAbs_EDGE ); eExp.More(); eExp.Next() ) + { + const TopoDS_Edge& edge = TopoDS::Edge( eExp.Current() ); + if ( hasStrangeEdges ) + { + bool hasStrangeFace = false; + PShapeIteratorPtr faceIt = _helper->GetAncestors( edge, *_helper->GetMesh(), TopAbs_FACE); + while ( const TopoDS_Shape* face = faceIt->next() ) + if (( hasStrangeFace = !faceMap.Contains( *face ))) + break; + if ( !hasStrangeFace && !_toAddEdges ) + continue; + _geometry._strangeEdges.Add( ShapeID( edge )); + _geometry._strangeEdges.Add( ShapeID( _helper->IthVertex( 0, edge ))); + _geometry._strangeEdges.Add( ShapeID( _helper->IthVertex( 1, edge ))); + } + if ( !SMESH_Algo::isDegenerated( edge ) && + !_helper->IsRealSeam( edge )) + { + edge2faceIDsMap[ ShapeID( edge )].push_back( ShapeID( faces[i] )); + } + } + } + return; +} + +//================================================================================ +/* + * Computes coordinates of a point in the grid CS + */ +void Grid::ComputeUVW(const gp_XYZ& P, double UVW[3]) +{ + gp_XYZ p = P * _invB; + p.Coord( UVW[0], UVW[1], UVW[2] ); +} +//================================================================================ +/* + * Creates all nodes + */ +void Grid::ComputeNodes(SMESH_MesherHelper& helper) +{ + // state of each node of the grid relative to the geometry + const size_t nbGridNodes = _coords[0].size() * _coords[1].size() * _coords[2].size(); + vector< TGeomID > shapeIDVec( nbGridNodes, theUndefID ); + _nodes.resize( nbGridNodes, 0 ); + _allBorderNodes.resize( nbGridNodes, 0 ); + _gridIntP.resize( nbGridNodes, NULL ); + + SMESHDS_Mesh* mesh = helper.GetMeshDS(); + + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + LineIndexer li = GetLineIndexer( iDir ); + + // find out a shift of node index while walking along a GridLine in this direction + li.SetIndexOnLine( 0 ); + size_t nIndex0 = NodeIndex( li.I(), li.J(), li.K() ); + li.SetIndexOnLine( 1 ); + const size_t nShift = NodeIndex( li.I(), li.J(), li.K() ) - nIndex0; + + const vector & coords = _coords[ iDir ]; + for ( ; li.More(); ++li ) // loop on lines in iDir + { + li.SetIndexOnLine( 0 ); + nIndex0 = NodeIndex( li.I(), li.J(), li.K() ); + + GridLine& line = _lines[ iDir ][ li.LineIndex() ]; + const gp_XYZ lineLoc = line._line.Location().XYZ(); + const gp_XYZ lineDir = line._line.Direction().XYZ(); + + line.RemoveExcessIntPoints( _tol ); + multiset< F_IntersectPoint >& intPnts = line._intPoints; + multiset< F_IntersectPoint >::iterator ip = intPnts.begin(); + + // Create mesh nodes at intersections with geometry + // and set OUT state of nodes between intersections + + TGeomID solidID = 0; + const double* nodeCoord = & coords[0]; + const double* coord0 = nodeCoord; + const double* coordEnd = coord0 + coords.size(); + double nodeParam = 0; + for ( ; ip != intPnts.end(); ++ip ) + { + solidID = line.GetSolidIDBefore( ip, solidID, _geometry ); + + // set OUT state or just skip IN nodes before ip + if ( nodeParam < ip->_paramOnLine - _tol ) + { + while ( nodeParam < ip->_paramOnLine - _tol ) + { + TGeomID & nodeShapeID = shapeIDVec[ nIndex0 + nShift * ( nodeCoord-coord0 ) ]; + nodeShapeID = Min( solidID, nodeShapeID ); + if ( ++nodeCoord < coordEnd ) + nodeParam = *nodeCoord - *coord0; + else + break; + } + if ( nodeCoord == coordEnd ) break; + } + + // create a mesh node on a GridLine at ip if it does not coincide with a grid node + if ( nodeParam > ip->_paramOnLine + _tol ) + { + gp_XYZ xyz = lineLoc + ip->_paramOnLine * lineDir; + ip->_node = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); + ip->_indexOnLine = nodeCoord-coord0-1; + TopoDS_Vertex v; + SetOnShape( ip->_node, *ip, & v ); + UpdateFacesOfVertex( *ip, v ); + } + // create a mesh node at ip coincident with a grid node + else + { + int nodeIndex = nIndex0 + nShift * ( nodeCoord-coord0 ); + if ( !_nodes[ nodeIndex ] ) + { + gp_XYZ xyz = lineLoc + nodeParam * lineDir; + _nodes [ nodeIndex ] = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); + //_gridIntP[ nodeIndex ] = & * ip; + //SetOnShape( _nodes[ nodeIndex ], *ip ); + } + if ( _gridIntP[ nodeIndex ] ) + _gridIntP[ nodeIndex ]->Add( ip->_faceIDs ); + else + _gridIntP[ nodeIndex ] = & (*ip); + // ip->_node = _nodes[ nodeIndex ]; -- to differ from ip on links + ip->_indexOnLine = nodeCoord-coord0; + if ( ++nodeCoord < coordEnd ) + nodeParam = *nodeCoord - *coord0; + } + } + // set OUT state to nodes after the last ip + for ( ; nodeCoord < coordEnd; ++nodeCoord ) + shapeIDVec[ nIndex0 + nShift * ( nodeCoord-coord0 ) ] = 0; + } + } + + // Create mesh nodes at !OUT nodes of the grid + + for ( size_t z = 0; z < _coords[2].size(); ++z ) + for ( size_t y = 0; y < _coords[1].size(); ++y ) + for ( size_t x = 0; x < _coords[0].size(); ++x ) + { + size_t nodeIndex = NodeIndex( x, y, z ); + if ( !_nodes[ nodeIndex ] && + 0 < shapeIDVec[ nodeIndex ] && shapeIDVec[ nodeIndex ] < theUndefID ) + { + gp_XYZ xyz = ( _coords[0][x] * _axes[0] + + _coords[1][y] * _axes[1] + + _coords[2][z] * _axes[2] ); + _nodes[ nodeIndex ] = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); + mesh->SetNodeInVolume( _nodes[ nodeIndex ], shapeIDVec[ nodeIndex ]); + } + else if ( _nodes[ nodeIndex ] && _gridIntP[ nodeIndex ] /*&& + !_nodes[ nodeIndex]->GetShapeID()*/ ) + { + TopoDS_Vertex v; + SetOnShape( _nodes[ nodeIndex ], *_gridIntP[ nodeIndex ], & v ); + UpdateFacesOfVertex( *_gridIntP[ nodeIndex ], v ); + } + else if ( _toUseQuanta && !_allBorderNodes[ nodeIndex ] /*add all nodes outside the body. Used to reconstruct the hexahedrals when polys are not desired!*/) + { + gp_XYZ xyz = ( _coords[0][x] * _axes[0] + + _coords[1][y] * _axes[1] + + _coords[2][z] * _axes[2] ); + _allBorderNodes[ nodeIndex ] = mesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); + mesh->SetNodeInVolume( _allBorderNodes[ nodeIndex ], shapeIDVec[ nodeIndex ]); + } + } +#ifdef _MY_DEBUG_ + // check validity of transitions + const char* trName[] = { "TANGENT", "IN", "OUT", "APEX" }; + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + LineIndexer li = GetLineIndexer( iDir ); + for ( ; li.More(); ++li ) + { + multiset< F_IntersectPoint >& intPnts = _lines[ iDir ][ li.LineIndex() ]._intPoints; + if ( intPnts.empty() ) continue; + if ( intPnts.size() == 1 ) + { + if ( intPnts.begin()->_transition != Trans_TANGENT && + intPnts.begin()->_transition != Trans_APEX ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Wrong SOLE transition of GridLine (") + << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] + << ") along " << li._nameConst + << ": " << trName[ intPnts.begin()->_transition] ); + } + else + { + if ( intPnts.begin()->_transition == Trans_OUT ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Wrong START transition of GridLine (") + << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] + << ") along " << li._nameConst + << ": " << trName[ intPnts.begin()->_transition ]); + if ( intPnts.rbegin()->_transition == Trans_IN ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Wrong END transition of GridLine (") + << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] + << ") along " << li._nameConst + << ": " << trName[ intPnts.rbegin()->_transition ]); + } + } + } +#endif + return; +} + +bool Grid::GridInitAndInterserctWithShape( const TopoDS_Shape& theShape, std::map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, + const StdMeshers_CartesianParameters3D* hyp, const int numOfThreads, bool computeCanceled ) +{ + InitGeometry( theShape ); + + vector< TopoDS_Shape > faceVec; + { + TopTools_MapOfShape faceMap; + TopExp_Explorer fExp; + for ( fExp.Init( theShape, TopAbs_FACE ); fExp.More(); fExp.Next() ) + { + bool isNewFace = faceMap.Add( fExp.Current() ); + if ( !_toConsiderInternalFaces ) + if ( !isNewFace || fExp.Current().Orientation() == TopAbs_INTERNAL ) + // remove an internal face + faceMap.Remove( fExp.Current() ); + } + faceVec.reserve( faceMap.Extent() ); + faceVec.assign( faceMap.cbegin(), faceMap.cend() ); + } + vector facesItersectors( faceVec.size() ); + Bnd_Box shapeBox; + for ( size_t i = 0; i < faceVec.size(); ++i ) + { + facesItersectors[i]._face = TopoDS::Face( faceVec[i] ); + facesItersectors[i]._faceID = ShapeID( faceVec[i] ); + facesItersectors[i]._grid = this; + shapeBox.Add( facesItersectors[i].GetFaceBndBox() ); + } + Tools::GetExactBndBox( faceVec, hyp->GetAxisDirs(), shapeBox ); + + vector xCoords, yCoords, zCoords; + hyp->GetCoordinates( xCoords, yCoords, zCoords, shapeBox ); + + SetCoordinates( xCoords, yCoords, zCoords, hyp->GetAxisDirs(), shapeBox ); + + if ( computeCanceled ) return false; + +#ifdef WITH_TBB + { // copy partner faces and curves of not thread-safe types + set< const Standard_Transient* > tshapes; + BRepBuilderAPI_Copy copier; + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + { + if ( !facesItersectors[i].IsThreadSafe( tshapes )) + { + copier.Perform( facesItersectors[i]._face ); + facesItersectors[i]._face = TopoDS::Face( copier ); + } + } + } + // Intersection of grid lines with the geometry boundary. + tbb::parallel_for ( tbb::blocked_range( 0, facesItersectors.size() ), + ParallelIntersector( facesItersectors ), + tbb::simple_partitioner()); + + // TODO SOLVE SEGFAULT WHEN USING THREAD PARALLEL FUNCTION + + // parallel_for(facesItersectors.begin(), facesItersectors.end(), computeGridIntersection, numOfThreads ); +#else + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + facesItersectors[i].Intersect(); +#endif + + // put intersection points onto the GridLine's; this is done after intersection + // to avoid contention of facesItersectors for writing into the same GridLine + // in case of parallel work of facesItersectors + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + facesItersectors[i].StoreIntersections(); + + if ( computeCanceled ) return false; + + // create nodes on the geometry + ComputeNodes( *_helper ); + + if ( computeCanceled ) return false; + + // get EDGEs to take into account + // map< TGeomID, vector< TGeomID > > edge2faceIDsMap; + GetEdgesToImplement( edge2faceIDsMap, theShape, faceVec ); + return true; +} + +void Tools::GetExactBndBox( const vector< TopoDS_Shape >& faceVec, const double* axesDirs, Bnd_Box& shapeBox ) +{ + BRep_Builder b; + TopoDS_Compound allFacesComp; + b.MakeCompound( allFacesComp ); + for ( size_t iF = 0; iF < faceVec.size(); ++iF ) + b.Add( allFacesComp, faceVec[ iF ] ); + + double sP[6]; // aXmin, aYmin, aZmin, aXmax, aYmax, aZmax + shapeBox.Get(sP[0],sP[1],sP[2],sP[3],sP[4],sP[5]); + double farDist = 0; + for ( int i = 0; i < 6; ++i ) + farDist = Max( farDist, 10 * sP[i] ); + + gp_XYZ axis[3] = { gp_XYZ( axesDirs[0], axesDirs[1], axesDirs[2] ), + gp_XYZ( axesDirs[3], axesDirs[4], axesDirs[5] ), + gp_XYZ( axesDirs[6], axesDirs[7], axesDirs[8] ) }; + axis[0].Normalize(); + axis[1].Normalize(); + axis[2].Normalize(); + + gp_Mat basis( axis[0], axis[1], axis[2] ); + gp_Mat bi = basis.Inverted(); + + gp_Pnt pMin, pMax; + for ( int iDir = 0; iDir < 3; ++iDir ) + { + gp_XYZ axis0 = axis[ iDir ]; + gp_XYZ axis1 = axis[ ( iDir + 1 ) % 3 ]; + gp_XYZ axis2 = axis[ ( iDir + 2 ) % 3 ]; + for ( int isMax = 0; isMax < 2; ++isMax ) + { + double shift = isMax ? farDist : -farDist; + gp_XYZ orig = shift * axis0; + gp_XYZ norm = axis1 ^ axis2; + gp_Pln pln( orig, norm ); + norm = pln.Axis().Direction().XYZ(); + BRepBuilderAPI_MakeFace plane( pln, -farDist, farDist, -farDist, farDist ); + + gp_Pnt& pAxis = isMax ? pMax : pMin; + gp_Pnt pPlane, pFaces; + double dist = GEOMUtils::GetMinDistance( plane, allFacesComp, pPlane, pFaces ); + if ( dist < 0 ) + { + Bnd_B3d bb; + gp_XYZ corner; + for ( int i = 0; i < 2; ++i ) { + corner.SetCoord( 1, sP[ i*3 ]); + for ( int j = 0; j < 2; ++j ) { + corner.SetCoord( 2, sP[ i*3 + 1 ]); + for ( int k = 0; k < 2; ++k ) + { + corner.SetCoord( 3, sP[ i*3 + 2 ]); + corner *= bi; + bb.Add( corner ); + } + } + } + corner = isMax ? bb.CornerMax() : bb.CornerMin(); + pAxis.SetCoord( iDir+1, corner.Coord( iDir+1 )); + } + else + { + gp_XYZ pf = pFaces.XYZ() * bi; + pAxis.SetCoord( iDir+1, pf.Coord( iDir+1 ) ); + } + } + } // loop on 3 axes + + shapeBox.SetVoid(); + shapeBox.Add( pMin ); + shapeBox.Add( pMax ); + + return; +} + diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx new file mode 100644 index 000000000..3f8cc6800 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx @@ -0,0 +1,991 @@ +// Copyright (C) 2016-2024 CEA, EDF +// +// 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_Cartesian_3D_Grid.hxx +// Module : SMESH +// Purpose: Make BodyFitting mesh algorithm more modular and testable +// + +#ifndef _SMESH_Cartesian_3D_GRID_HXX_ +#define _SMESH_Cartesian_3D_GRID_HXX_ + +#ifdef WITH_TBB +#include +#endif + +#include + +// STD +#include +#include +#include +#include +#include + + +// SMESH +#include "SMESH_StdMeshers.hxx" +#include "StdMeshers_FaceSide.hxx" +#include "StdMeshers_CartesianParameters3D.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +//OCC +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// All utility structs used in Grid and hexahedron class will be included here +// Ideally each one of this should define their own testable class +using namespace std; +using namespace SMESH; +namespace gridtools +{ + typedef int TGeomID; // IDs of sub-shapes + typedef TopTools_ShapeMapHasher TShapeHasher; // non-oriented shape hasher + typedef std::array< int, 3 > TIJK; + + const TGeomID theUndefID = 1e+9; + + //============================================================================= + // Definitions of internal utils + // -------------------------------------------------------------------------- + enum Transition { + Trans_TANGENT = IntCurveSurface_Tangent, + Trans_IN = IntCurveSurface_In, + Trans_OUT = IntCurveSurface_Out, + Trans_APEX, + Trans_INTERNAL // for INTERNAL FACE + }; + // -------------------------------------------------------------------------- + /*! + * \brief Sub-entities of a FACE neighboring its concave VERTEX. + * Help to avoid linking nodes on EDGEs that seem connected + * by the concave FACE but the link actually lies outside the FACE + */ + struct ConcaveFace + { + TGeomID _concaveFace; + TGeomID _edge1, _edge2; + TGeomID _v1, _v2; + ConcaveFace( int f=0, int e1=0, int e2=0, int v1=0, int v2=0 ) + : _concaveFace(f), _edge1(e1), _edge2(e2), _v1(v1), _v2(v2) {} + bool HasEdge( TGeomID edge ) const { return edge == _edge1 || edge == _edge2; } + bool HasVertex( TGeomID v ) const { return v == _v1 || v == _v2; } + void SetEdge( TGeomID edge ) { ( _edge1 ? _edge2 : _edge1 ) = edge; } + void SetVertex( TGeomID v ) { ( _v1 ? _v2 : _v1 ) = v; } + }; + typedef NCollection_DataMap< TGeomID, ConcaveFace > TConcaveVertex2Face; + // -------------------------------------------------------------------------- + /*! + * \brief Container of IDs of SOLID sub-shapes + */ + class Solid // sole SOLID contains all sub-shapes + { + TGeomID _id; // SOLID id + bool _hasInternalFaces; + TConcaveVertex2Face _concaveVertex; // concave VERTEX -> ConcaveFace + public: + virtual ~Solid() {} + virtual bool Contains( TGeomID /*subID*/ ) const { return true; } + virtual bool ContainsAny( const vector< TGeomID>& /*subIDs*/ ) const { return true; } + virtual TopAbs_Orientation Orientation( const TopoDS_Shape& s ) const { return s.Orientation(); } + virtual bool IsOutsideOriented( TGeomID /*faceID*/ ) const { return true; } + void SetID( TGeomID id ) { _id = id; } + TGeomID ID() const { return _id; } + void SetHasInternalFaces( bool has ) { _hasInternalFaces = has; } + bool HasInternalFaces() const { return _hasInternalFaces; } + void SetConcave( TGeomID V, TGeomID F, TGeomID E1, TGeomID E2, TGeomID V1, TGeomID V2 ) + { _concaveVertex.Bind( V, ConcaveFace{ F, E1, E2, V1, V2 }); } + bool HasConcaveVertex() const { return !_concaveVertex.IsEmpty(); } + const ConcaveFace* GetConcave( TGeomID V ) const { return _concaveVertex.Seek( V ); } + }; + // -------------------------------------------------------------------------- + class OneOfSolids : public Solid + { + TColStd_MapOfInteger _subIDs; + TopTools_MapOfShape _faces; // keep FACE orientation + TColStd_MapOfInteger _outFaceIDs; // FACEs of shape_to_mesh oriented outside the SOLID + public: + void Init( const TopoDS_Shape& solid, + TopAbs_ShapeEnum subType, + const SMESHDS_Mesh* mesh ); + virtual bool Contains( TGeomID i ) const { return i == ID() || _subIDs.Contains( i ); } + virtual bool ContainsAny( const vector< TGeomID>& subIDs ) const + { + for ( size_t i = 0; i < subIDs.size(); ++i ) if ( Contains( subIDs[ i ])) return true; + return false; + } + virtual TopAbs_Orientation Orientation( const TopoDS_Shape& face ) const + { + const TopoDS_Shape& sInMap = const_cast< OneOfSolids* >(this)->_faces.Added( face ); + return sInMap.Orientation(); + } + virtual bool IsOutsideOriented( TGeomID faceID ) const + { + return faceID == 0 || _outFaceIDs.Contains( faceID ); + } + }; + // -------------------------------------------------------------------------- + /*! + * \brief Hold a vector of TGeomID and clear it at destruction + */ + class GeomIDVecHelder + { + typedef std::vector< TGeomID > TVector; + const TVector& myVec; + bool myOwn; + + public: + GeomIDVecHelder( const TVector& idVec, bool isOwner ): myVec( idVec ), myOwn( isOwner ) {} + GeomIDVecHelder( const GeomIDVecHelder& holder ): myVec( holder.myVec ), myOwn( holder.myOwn ) + { + const_cast< bool& >( holder.myOwn ) = false; + } + ~GeomIDVecHelder() { if ( myOwn ) const_cast( myVec ).clear(); } + size_t size() const { return myVec.size(); } + TGeomID operator[]( size_t i ) const { return i < size() ? myVec[i] : theUndefID; } + bool operator==( const GeomIDVecHelder& other ) const { return myVec == other.myVec; } + bool contain( const TGeomID& id ) const { + return std::find( myVec.begin(), myVec.end(), id ) != myVec.end(); + } + TGeomID otherThan( const TGeomID& id ) const { + for ( const TGeomID& id2 : myVec ) + if ( id != id2 ) + return id2; + return theUndefID; + } + TGeomID oneCommon( const GeomIDVecHelder& other ) const { + TGeomID common = theUndefID; + for ( const TGeomID& id : myVec ) + if ( other.contain( id )) + { + if ( common != theUndefID ) + return theUndefID; + common = id; + } + return common; + } + }; + // -------------------------------------------------------------------------- + /*! + * \brief Geom data + */ + struct Geometry + { + TopoDS_Shape _mainShape; + vector< vector< TGeomID > > _solidIDsByShapeID;// V/E/F ID -> SOLID IDs + Solid _soleSolid; + map< TGeomID, OneOfSolids > _solidByID; + TColStd_MapOfInteger _boundaryFaces; // FACEs on boundary of mesh->ShapeToMesh() + TColStd_MapOfInteger _strangeEdges; // EDGEs shared by strange FACEs + TGeomID _extIntFaceID; // pseudo FACE - extension of INTERNAL FACE + + TopTools_DataMapOfShapeInteger _shape2NbNodes; // nb of pre-existing nodes on shapes + + Controls::ElementsOnShape _edgeClassifier; + Controls::ElementsOnShape _vertexClassifier; + + bool IsOneSolid() const { return _solidByID.size() < 2; } + GeomIDVecHelder GetSolidIDsByShapeID( const vector< TGeomID >& shapeIDs ) const; + }; + + // -------------------------------------------------------------------------- + /*! + * \brief Common data of any intersection between a Grid and a shape + */ + struct B_IntersectPoint + { + // This two class members are being updated in a non thread safe way. + // See Add method modify _node and _faceIDs class members dinamicaly during execution + // of Hexahedron.compute() method. + // std::mutex _mutex; + mutable const SMDS_MeshNode* _node; + mutable vector< TGeomID > _faceIDs; + + B_IntersectPoint(): _node(NULL) {} + bool Add( const vector< TGeomID >& fIDs, const SMDS_MeshNode* n=NULL ) const; + TGeomID HasCommonFace( const B_IntersectPoint * other, TGeomID avoidFace=-1 ) const; + size_t GetCommonFaces( const B_IntersectPoint * other, TGeomID * commonFaces ) const; + bool IsOnFace( TGeomID faceID ) const; + virtual ~B_IntersectPoint() {} + }; + // -------------------------------------------------------------------------- + /*! + * \brief Data of intersection between a GridLine and a TopoDS_Face + */ + struct F_IntersectPoint : public B_IntersectPoint + { + double _paramOnLine; + double _u, _v; + mutable Transition _transition; + mutable size_t _indexOnLine; + + bool operator< ( const F_IntersectPoint& o ) const { + return _paramOnLine < o._paramOnLine; + } + }; + // -------------------------------------------------------------------------- + /*! + * \brief Data of intersection between GridPlanes and a TopoDS_EDGE + */ + struct E_IntersectPoint : public B_IntersectPoint + { + gp_Pnt _point; + double _uvw[3]; + TGeomID _shapeID; // ID of EDGE or VERTEX + }; + + // -------------------------------------------------------------------------- + /*! + * \brief A line of the grid and its intersections with 2D geometry + */ + struct GridLine + { + gp_Lin _line; + double _length; // line length + multiset< F_IntersectPoint > _intPoints; + + void RemoveExcessIntPoints( const double tol ); + TGeomID GetSolidIDBefore( multiset< F_IntersectPoint >::iterator ip, + const TGeomID prevID, + const Geometry& geom); + }; + // -------------------------------------------------------------------------- + /*! + * \brief Planes of the grid used to find intersections of an EDGE with a hexahedron + */ + struct GridPlanes + { + gp_XYZ _zNorm; + vector< gp_XYZ > _origins; // origin points of all planes in one direction + vector< double > _zProjs; // projections of origins to _zNorm + }; + // -------------------------------------------------------------------------- + /*! + * \brief Iterator on the parallel grid lines of one direction + */ + struct LineIndexer + { + size_t _size [3]; + size_t _curInd[3]; + size_t _iVar1, _iVar2, _iConst; + string _name1, _name2, _nameConst; + LineIndexer() {} + LineIndexer( size_t sz1, size_t sz2, size_t sz3, + size_t iv1, size_t iv2, size_t iConst, + const string& nv1, const string& nv2, const string& nConst ) + { + _size[0] = sz1; _size[1] = sz2; _size[2] = sz3; + _curInd[0] = _curInd[1] = _curInd[2] = 0; + _iVar1 = iv1; _iVar2 = iv2; _iConst = iConst; + _name1 = nv1; _name2 = nv2; _nameConst = nConst; + } + + size_t I() const { return _curInd[0]; } + size_t J() const { return _curInd[1]; } + size_t K() const { return _curInd[2]; } + void SetIJK( size_t i, size_t j, size_t k ) + { + _curInd[0] = i; _curInd[1] = j; _curInd[2] = k; + } + void SetLineIndex(size_t i) + { + _curInd[_iVar2] = i / _size[_iVar1]; + _curInd[_iVar1] = i % _size[_iVar1]; + } + void operator++() + { + if ( ++_curInd[_iVar1] == _size[_iVar1] ) + _curInd[_iVar1] = 0, ++_curInd[_iVar2]; + } + bool More() const { return _curInd[_iVar2] < _size[_iVar2]; } + size_t LineIndex () const { return _curInd[_iVar1] + _curInd[_iVar2]* _size[_iVar1]; } + size_t LineIndex10 () const { return (_curInd[_iVar1] + 1 ) + _curInd[_iVar2]* _size[_iVar1]; } + size_t LineIndex01 () const { return _curInd[_iVar1] + (_curInd[_iVar2] + 1 )* _size[_iVar1]; } + size_t LineIndex11 () const { return (_curInd[_iVar1] + 1 ) + (_curInd[_iVar2] + 1 )* _size[_iVar1]; } + void SetIndexOnLine (size_t i) { _curInd[ _iConst ] = i; } + bool IsValidIndexOnLine (size_t i) const { return i < _size[ _iConst ]; } + size_t NbLines() const { return _size[_iVar1] * _size[_iVar2]; } + }; + + class Tools + { + public: + Tools() = delete; + + //================================================================================ + /*! + * \brief computes exact bounding box with axes parallel to given ones + */ + //================================================================================ + static void GetExactBndBox( const vector< TopoDS_Shape >& faceVec, const double* axesDirs, Bnd_Box& shapeBox ); + }; +} // end namespace gridtools + +using namespace gridtools; +class STDMESHERS_EXPORT Grid +{ + +public: + vector< double > _coords[3]; // coordinates of grid nodes + gp_XYZ _axes [3]; // axis directions + vector< GridLine > _lines [3]; // in 3 directions + double _tol, _minCellSize; + gp_XYZ _origin; + gp_Mat _invB; // inverted basis of _axes + + // index shift within _nodes of nodes of a cell from the 1st node + int _nodeShift[8]; + + vector< const SMDS_MeshNode* > _nodes; // mesh nodes at grid nodes + vector< const SMDS_MeshNode* > _allBorderNodes; // mesh nodes between the bounding box and the geometry boundary + + vector< const F_IntersectPoint* > _gridIntP; // grid node intersection with geometry + ObjectPool< E_IntersectPoint > _edgeIntPool; // intersections with EDGEs + ObjectPool< F_IntersectPoint > _extIntPool; // intersections with extended INTERNAL FACEs + //list< E_IntersectPoint > _edgeIntP; // intersections with EDGEs + + Geometry _geometry; + bool _toAddEdges; + bool _toCreateFaces; + bool _toConsiderInternalFaces; + bool _toUseThresholdForInternalFaces; + double _sizeThreshold; + bool _toUseQuanta; + double _quanta; + + SMESH_MesherHelper* _helper; + + size_t CellIndex( size_t i, size_t j, size_t k ) const + { + return i + j*(_coords[0].size()-1) + k*(_coords[0].size()-1)*(_coords[1].size()-1); + } + size_t NodeIndex( size_t i, size_t j, size_t k ) const + { + return i + j*_coords[0].size() + k*_coords[0].size()*_coords[1].size(); + } + size_t NodeIndex( const TIJK& ijk ) const + { + return NodeIndex( ijk[0], ijk[1], ijk[2] ); + } + size_t NodeIndexDX() const { return 1; } + size_t NodeIndexDY() const { return _coords[0].size(); } + size_t NodeIndexDZ() const { return _coords[0].size() * _coords[1].size(); } + + LineIndexer GetLineIndexer(size_t iDir) const; + size_t GetLineDir( const GridLine* line, size_t & index ) const; + + E_IntersectPoint* Add( const E_IntersectPoint& ip ) + { + E_IntersectPoint* eip = _edgeIntPool.getNew(); + *eip = ip; + return eip; + } + void Remove( E_IntersectPoint* eip ) { _edgeIntPool.destroy( eip ); } + + TGeomID ShapeID( const TopoDS_Shape& s ) const; + const TopoDS_Shape& Shape( TGeomID id ) const; + TopAbs_ShapeEnum ShapeType( TGeomID id ) const { return Shape(id).ShapeType(); } + void InitGeometry( const TopoDS_Shape& theShape ); + void InitClassifier( const TopoDS_Shape& mainShape, + TopAbs_ShapeEnum shapeType, + Controls::ElementsOnShape& classifier ); + void GetEdgesToImplement( map< TGeomID, vector< TGeomID > > & edge2faceMap, + const TopoDS_Shape& shape, + const vector< TopoDS_Shape >& faces ); + void SetSolidFather( const TopoDS_Shape& s, const TopoDS_Shape& theShapeToMesh ); + bool IsShared( TGeomID faceID ) const; + bool IsAnyShared( const std::vector< TGeomID >& faceIDs ) const; + bool IsInternal( TGeomID faceID ) const { + return ( faceID == PseudoIntExtFaceID() || + Shape( faceID ).Orientation() == TopAbs_INTERNAL ); } + bool IsSolid( TGeomID shapeID ) const { + if ( _geometry.IsOneSolid() ) return _geometry._soleSolid.ID() == shapeID; + else return _geometry._solidByID.count( shapeID ); } + bool IsStrangeEdge( TGeomID id ) const { return _geometry._strangeEdges.Contains( id ); } + TGeomID PseudoIntExtFaceID() const { return _geometry._extIntFaceID; } + Solid* GetSolid( TGeomID solidID = 0 ); + Solid* GetOneOfSolids( TGeomID solidID ); + const vector< TGeomID > & GetSolidIDs( TGeomID subShapeID ) const; + bool IsCorrectTransition( TGeomID faceID, const Solid* solid ); + bool IsBoundaryFace( TGeomID face ) const { return _geometry._boundaryFaces.Contains( face ); } + void SetOnShape( const SMDS_MeshNode* n, const F_IntersectPoint& ip, + TopoDS_Vertex* vertex = nullptr, bool unset = false ); + void UpdateFacesOfVertex( const B_IntersectPoint& ip, const TopoDS_Vertex& vertex ); + bool IsToCheckNodePos() const { return !_toAddEdges && _toCreateFaces; } + bool IsToRemoveExcessEntities() const { return !_toAddEdges; } + + void SetCoordinates(const vector& xCoords, + const vector& yCoords, + const vector& zCoords, + const double* axesDirs, + const Bnd_Box& bndBox ); + void ComputeUVW(const gp_XYZ& p, double uvw[3]); + void ComputeNodes(SMESH_MesherHelper& helper); + bool GridInitAndInterserctWithShape( const TopoDS_Shape& theShape, std::map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, + const StdMeshers_CartesianParameters3D* hyp, const int numOfThreads, bool computeCanceled ); +}; + +namespace +{ + + // Implement parallel computation of Hexa with c++ thread implementation + template + void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const int nthreads = 1) + { + const unsigned int group = ((last-first))/std::abs(nthreads); + + std::vector threads; + threads.reserve(nthreads); + Iterator it = first; + for (; it < last-group; it += group) { + // to create a thread + // Pass iterators by value and the function by reference! + auto lambda = [=,&f](){ std::for_each(it, std::min(it+group, last), f);}; + + // stack the threads + threads.push_back( std::thread( lambda ) ); + } + std::for_each(it, last, f); // last steps while we wait for other threads + std::for_each(threads.begin(), threads.end(), [](std::thread& x){x.join();}); + } + // -------------------------------------------------------------------------- + /*! + * \brief Intersector of TopoDS_Face with all GridLine's + */ + struct FaceGridIntersector + { + TopoDS_Face _face; + TGeomID _faceID; + Grid* _grid; + Bnd_Box _bndBox; + IntCurvesFace_Intersector* _surfaceInt; + vector< std::pair< GridLine*, F_IntersectPoint > > _intersections; + + FaceGridIntersector(): _grid(0), _surfaceInt(0) {} + void Intersect(); + + void StoreIntersections() + { + for ( size_t i = 0; i < _intersections.size(); ++i ) + { + multiset< F_IntersectPoint >::iterator ip = + _intersections[i].first->_intPoints.insert( _intersections[i].second ); + ip->_faceIDs.reserve( 1 ); + ip->_faceIDs.push_back( _faceID ); + } + } + const Bnd_Box& GetFaceBndBox() + { + GetCurveFaceIntersector(); + return _bndBox; + } + IntCurvesFace_Intersector* GetCurveFaceIntersector() + { + if ( !_surfaceInt ) + { + _surfaceInt = new IntCurvesFace_Intersector( _face, Precision::PConfusion() ); + _bndBox = _surfaceInt->Bounding(); + if ( _bndBox.IsVoid() ) + BRepBndLib::Add (_face, _bndBox); + } + return _surfaceInt; + } +#ifdef WITH_TBB + bool IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const; +#endif + }; + +#ifdef WITH_TBB + // -------------------------------------------------------------------------- + /*! + * \brief Structure intersecting certain nb of faces with GridLine's in one thread + */ + struct ParallelIntersector + { + vector< FaceGridIntersector >& _faceVec; + ParallelIntersector( vector< FaceGridIntersector >& faceVec): _faceVec(faceVec){} + void operator() ( const tbb::blocked_range& r ) const + { + for ( size_t i = r.begin(); i != r.end(); ++i ) + _faceVec[i].Intersect(); + } + }; +#endif + + template + void computeGridIntersection( Type faceGridIntersector ) + { + faceGridIntersector.Intersect(); + } + + // -------------------------------------------------------------------------- + /*! + * \brief Intersector of a surface with a GridLine + */ + struct FaceLineIntersector + { + double _tol; + double _u, _v, _w; // params on the face and the line + Transition _transition; // transition at intersection (see IntCurveSurface.cdl) + Transition _transIn, _transOut; // IN and OUT transitions depending of face orientation + + gp_Pln _plane; + gp_Cylinder _cylinder; + gp_Cone _cone; + gp_Sphere _sphere; + gp_Torus _torus; + IntCurvesFace_Intersector* _surfaceInt; + + vector< F_IntersectPoint > _intPoints; + + void IntersectWithPlane (const GridLine& gridLine); + void IntersectWithCylinder(const GridLine& gridLine); + void IntersectWithCone (const GridLine& gridLine); + void IntersectWithSphere (const GridLine& gridLine); + void IntersectWithTorus (const GridLine& gridLine); + void IntersectWithSurface (const GridLine& gridLine); + + bool UVIsOnFace() const; + void addIntPoint(const bool toClassify=true); + bool isParamOnLineOK( const double linLength ) + { + return -_tol < _w && _w < linLength + _tol; + } + FaceLineIntersector():_surfaceInt(0) {} + ~FaceLineIntersector() { if (_surfaceInt ) delete _surfaceInt; _surfaceInt = 0; } + }; + + //============================================================================= + /* + * Intersects TopoDS_Face with all GridLine's + */ + void FaceGridIntersector::Intersect() + { + FaceLineIntersector intersector; + intersector._surfaceInt = GetCurveFaceIntersector(); + intersector._tol = _grid->_tol; + intersector._transOut = _face.Orientation() == TopAbs_REVERSED ? Trans_IN : Trans_OUT; + intersector._transIn = _face.Orientation() == TopAbs_REVERSED ? Trans_OUT : Trans_IN; + + typedef void (FaceLineIntersector::* PIntFun )(const GridLine& gridLine); + PIntFun interFunction; + + bool isDirect = true; + BRepAdaptor_Surface surf( _face ); + switch ( surf.GetType() ) { + case GeomAbs_Plane: + intersector._plane = surf.Plane(); + interFunction = &FaceLineIntersector::IntersectWithPlane; + isDirect = intersector._plane.Direct(); + break; + case GeomAbs_Cylinder: + intersector._cylinder = surf.Cylinder(); + interFunction = &FaceLineIntersector::IntersectWithCylinder; + isDirect = intersector._cylinder.Direct(); + break; + case GeomAbs_Cone: + intersector._cone = surf.Cone(); + interFunction = &FaceLineIntersector::IntersectWithCone; + //isDirect = intersector._cone.Direct(); + break; + case GeomAbs_Sphere: + intersector._sphere = surf.Sphere(); + interFunction = &FaceLineIntersector::IntersectWithSphere; + isDirect = intersector._sphere.Direct(); + break; + case GeomAbs_Torus: + intersector._torus = surf.Torus(); + interFunction = &FaceLineIntersector::IntersectWithTorus; + //isDirect = intersector._torus.Direct(); + break; + default: + interFunction = &FaceLineIntersector::IntersectWithSurface; + } + if ( !isDirect ) + std::swap( intersector._transOut, intersector._transIn ); + + _intersections.clear(); + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + if ( surf.GetType() == GeomAbs_Plane ) + { + // check if all lines in this direction are parallel to a plane + if ( intersector._plane.Axis().IsNormal( _grid->_lines[iDir][0]._line.Position(), + Precision::Angular())) + continue; + // find out a transition, that is the same for all lines of a direction + gp_Dir plnNorm = intersector._plane.Axis().Direction(); + gp_Dir lineDir = _grid->_lines[iDir][0]._line.Direction(); + intersector._transition = + ( plnNorm * lineDir < 0 ) ? intersector._transIn : intersector._transOut; + } + if ( surf.GetType() == GeomAbs_Cylinder ) + { + // check if all lines in this direction are parallel to a cylinder + if ( intersector._cylinder.Axis().IsParallel( _grid->_lines[iDir][0]._line.Position(), + Precision::Angular())) + continue; + } + + // intersect the grid lines with the face + for ( size_t iL = 0; iL < _grid->_lines[iDir].size(); ++iL ) + { + GridLine& gridLine = _grid->_lines[iDir][iL]; + if ( _bndBox.IsOut( gridLine._line )) continue; + + intersector._intPoints.clear(); + (intersector.*interFunction)( gridLine ); // <- intersection with gridLine + for ( size_t i = 0; i < intersector._intPoints.size(); ++i ) + _intersections.push_back( make_pair( &gridLine, intersector._intPoints[i] )); + } + } + + if ( _face.Orientation() == TopAbs_INTERNAL ) + { + for ( size_t i = 0; i < _intersections.size(); ++i ) + if ( _intersections[i].second._transition == Trans_IN || + _intersections[i].second._transition == Trans_OUT ) + { + _intersections[i].second._transition = Trans_INTERNAL; + } + } + return; + } + //================================================================================ + /* + * Return true if (_u,_v) is on the face + */ + bool FaceLineIntersector::UVIsOnFace() const + { + TopAbs_State state = _surfaceInt->ClassifyUVPoint(gp_Pnt2d( _u,_v )); + return ( state == TopAbs_IN || state == TopAbs_ON ); + } + //================================================================================ + /* + * Store an intersection if it is IN or ON the face + */ + void FaceLineIntersector::addIntPoint(const bool toClassify) + { + if ( !toClassify || UVIsOnFace() ) + { + F_IntersectPoint p; + p._paramOnLine = _w; + p._u = _u; + p._v = _v; + p._transition = _transition; + _intPoints.push_back( p ); + } + } + //================================================================================ + /* + * Intersect a line with a plane + */ + void FaceLineIntersector::IntersectWithPlane(const GridLine& gridLine) + { + IntAna_IntConicQuad linPlane( gridLine._line, _plane, Precision::Angular()); + _w = linPlane.ParamOnConic(1); + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_plane, linPlane.Point(1) ,_u,_v); + addIntPoint(); + } + } + //================================================================================ + /* + * Intersect a line with a cylinder + */ + void FaceLineIntersector::IntersectWithCylinder(const GridLine& gridLine) + { + IntAna_IntConicQuad linCylinder( gridLine._line, _cylinder ); + if ( linCylinder.IsDone() && linCylinder.NbPoints() > 0 ) + { + _w = linCylinder.ParamOnConic(1); + if ( linCylinder.NbPoints() == 1 ) + _transition = Trans_TANGENT; + else + _transition = _w < linCylinder.ParamOnConic(2) ? _transIn : _transOut; + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_cylinder, linCylinder.Point(1) ,_u,_v); + addIntPoint(); + } + if ( linCylinder.NbPoints() > 1 ) + { + _w = linCylinder.ParamOnConic(2); + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_cylinder, linCylinder.Point(2) ,_u,_v); + _transition = ( _transition == Trans_OUT ) ? Trans_IN : Trans_OUT; + addIntPoint(); + } + } + } + } + //================================================================================ + /* + * Intersect a line with a cone + */ + void FaceLineIntersector::IntersectWithCone (const GridLine& gridLine) + { + IntAna_IntConicQuad linCone(gridLine._line,_cone); + if ( !linCone.IsDone() ) return; + gp_Pnt P; + gp_Vec du, dv, norm; + for ( int i = 1; i <= linCone.NbPoints(); ++i ) + { + _w = linCone.ParamOnConic( i ); + if ( !isParamOnLineOK( gridLine._length )) continue; + ElSLib::Parameters(_cone, linCone.Point(i) ,_u,_v); + if ( UVIsOnFace() ) + { + ElSLib::D1( _u, _v, _cone, P, du, dv ); + norm = du ^ dv; + double normSize2 = norm.SquareMagnitude(); + if ( normSize2 > Precision::Angular() * Precision::Angular() ) + { + double cos = norm.XYZ() * gridLine._line.Direction().XYZ(); + cos /= sqrt( normSize2 ); + if ( cos < -Precision::Angular() ) + _transition = _transIn; + else if ( cos > Precision::Angular() ) + _transition = _transOut; + else + _transition = Trans_TANGENT; + } + else + { + _transition = Trans_APEX; + } + addIntPoint( /*toClassify=*/false); + } + } + } + //================================================================================ + /* + * Intersect a line with a sphere + */ + void FaceLineIntersector::IntersectWithSphere (const GridLine& gridLine) + { + IntAna_IntConicQuad linSphere(gridLine._line,_sphere); + if ( linSphere.IsDone() && linSphere.NbPoints() > 0 ) + { + _w = linSphere.ParamOnConic(1); + if ( linSphere.NbPoints() == 1 ) + _transition = Trans_TANGENT; + else + _transition = _w < linSphere.ParamOnConic(2) ? _transIn : _transOut; + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_sphere, linSphere.Point(1) ,_u,_v); + addIntPoint(); + } + if ( linSphere.NbPoints() > 1 ) + { + _w = linSphere.ParamOnConic(2); + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_sphere, linSphere.Point(2) ,_u,_v); + _transition = ( _transition == Trans_OUT ) ? Trans_IN : Trans_OUT; + addIntPoint(); + } + } + } + } + //================================================================================ + /* + * Intersect a line with a torus + */ + void FaceLineIntersector::IntersectWithTorus (const GridLine& gridLine) + { + IntAna_IntLinTorus linTorus(gridLine._line,_torus); + if ( !linTorus.IsDone()) return; + gp_Pnt P; + gp_Vec du, dv, norm; + for ( int i = 1; i <= linTorus.NbPoints(); ++i ) + { + _w = linTorus.ParamOnLine( i ); + if ( !isParamOnLineOK( gridLine._length )) continue; + linTorus.ParamOnTorus( i, _u,_v ); + if ( UVIsOnFace() ) + { + ElSLib::D1( _u, _v, _torus, P, du, dv ); + norm = du ^ dv; + double normSize = norm.Magnitude(); + double cos = norm.XYZ() * gridLine._line.Direction().XYZ(); + cos /= normSize; + if ( cos < -Precision::Angular() ) + _transition = _transIn; + else if ( cos > Precision::Angular() ) + _transition = _transOut; + else + _transition = Trans_TANGENT; + addIntPoint( /*toClassify=*/false); + } + } + } + //================================================================================ + /* + * Intersect a line with a non-analytical surface + */ + void FaceLineIntersector::IntersectWithSurface (const GridLine& gridLine) + { + _surfaceInt->Perform( gridLine._line, 0.0, gridLine._length ); + if ( !_surfaceInt->IsDone() ) return; + for ( int i = 1; i <= _surfaceInt->NbPnt(); ++i ) + { + _transition = Transition( _surfaceInt->Transition( i ) ); + _w = _surfaceInt->WParameter( i ); + addIntPoint(/*toClassify=*/false); + } + } + +#ifdef WITH_TBB + //================================================================================ + /* + * check if its face can be safely intersected in a thread + */ + bool FaceGridIntersector::IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const + { + bool isSafe = true; + + // check surface + TopLoc_Location loc; + Handle(Geom_Surface) surf = BRep_Tool::Surface( _face, loc ); + Handle(Geom_RectangularTrimmedSurface) ts = + Handle(Geom_RectangularTrimmedSurface)::DownCast( surf ); + while( !ts.IsNull() ) { + surf = ts->BasisSurface(); + ts = Handle(Geom_RectangularTrimmedSurface)::DownCast(surf); + } + if ( surf->IsKind( STANDARD_TYPE(Geom_BSplineSurface )) || + surf->IsKind( STANDARD_TYPE(Geom_BezierSurface ))) + if ( !noSafeTShapes.insert( _face.TShape().get() ).second ) + isSafe = false; + + double f, l; + TopExp_Explorer exp( _face, TopAbs_EDGE ); + for ( ; exp.More(); exp.Next() ) + { + bool edgeIsSafe = true; + const TopoDS_Edge& e = TopoDS::Edge( exp.Current() ); + // check 3d curve + { + Handle(Geom_Curve) c = BRep_Tool::Curve( e, loc, f, l); + if ( !c.IsNull() ) + { + Handle(Geom_TrimmedCurve) tc = Handle(Geom_TrimmedCurve)::DownCast(c); + while( !tc.IsNull() ) { + c = tc->BasisCurve(); + tc = Handle(Geom_TrimmedCurve)::DownCast(c); + } + if ( c->IsKind( STANDARD_TYPE(Geom_BSplineCurve )) || + c->IsKind( STANDARD_TYPE(Geom_BezierCurve ))) + edgeIsSafe = false; + } + } + // check 2d curve + if ( edgeIsSafe ) + { + Handle(Geom2d_Curve) c2 = BRep_Tool::CurveOnSurface( e, surf, loc, f, l); + if ( !c2.IsNull() ) + { + Handle(Geom2d_TrimmedCurve) tc = Handle(Geom2d_TrimmedCurve)::DownCast(c2); + while( !tc.IsNull() ) { + c2 = tc->BasisCurve(); + tc = Handle(Geom2d_TrimmedCurve)::DownCast(c2); + } + if ( c2->IsKind( STANDARD_TYPE(Geom2d_BSplineCurve )) || + c2->IsKind( STANDARD_TYPE(Geom2d_BezierCurve ))) + edgeIsSafe = false; + } + } + if ( !edgeIsSafe && !noSafeTShapes.insert( e.TShape().get() ).second ) + isSafe = false; + } + return isSafe; + } +#endif +} + +#endif diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx new file mode 100644 index 000000000..f1f8f6965 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -0,0 +1,4226 @@ +// Copyright (C) 2016-2024 CEA, EDF +// +// 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_Cartesian_3D_Hexahedron.cxx +// Module : SMESH +// Purpose: Make BodyFitting mesh algorithm more modular and testable +// + +#include "StdMeshers_Cartesian_3D_Hexahedron.hxx" + +using namespace SMESH; +using namespace gridtools; + +std::mutex _eMutex; + +#ifdef WITH_TBB + // -------------------------------------------------------------------------- + /*! + * \brief Hexahedron computing volumes in one thread + */ + struct ParallelHexahedron + { + vector< Hexahedron* >& _hexVec; + ParallelHexahedron( vector< Hexahedron* >& hv ): _hexVec(hv) {} + void operator() ( const tbb::blocked_range& r ) const + { + for ( size_t i = r.begin(); i != r.end(); ++i ) + if ( Hexahedron* hex = _hexVec[ i ] ) + hex->computeElements(); + } + }; +#endif + +//============================================================================= +// Implementation of internal utils +//============================================================================= +/*! + * \brief adjust \a i to have \a val between values[i] and values[i+1] + */ +inline void locateValue( int & i, double val, const vector& values, + int& di, double tol ) +{ + //val += values[0]; // input \a val is measured from 0. + if ( i > (int) values.size()-2 ) + i = values.size()-2; + else + while ( i+2 < (int) values.size() && val > values[ i+1 ]) + ++i; + while ( i > 0 && val < values[ i ]) + --i; + + if ( i > 0 && val - values[ i ] < tol ) + di = -1; + else if ( i+2 < (int) values.size() && values[ i+1 ] - val < tol ) + di = 1; + else + di = 0; +} + +//================================================================================ +/*! + * \brief Find existing triangulation of a polygon + */ +int findExistingTriangulation( const SMDS_MeshElement* polygon, + //const SMDS_Mesh* mesh, + std::vector< const SMDS_MeshNode* >& nodes ) +{ + int nbSplits = 0; + nodes.clear(); + std::vector twoNodes(2); + std::vector foundFaces; foundFaces.reserve(10); + std::set< const SMDS_MeshElement * > avoidFaces; avoidFaces.insert( polygon ); + + const int nbPolyNodes = polygon->NbCornerNodes(); + twoNodes[1] = polygon->GetNode( nbPolyNodes - 1 ); + for ( int iN = 0; iN < nbPolyNodes; ++iN ) // loop on border links of polygon + { + twoNodes[0] = polygon->GetNode( iN ); + + int nbFaces = SMDS_Mesh::GetElementsByNodes( twoNodes, foundFaces, SMDSAbs_Face ); + int nbOkFaces = 0; + for ( int iF = 0; iF < nbFaces; ++iF ) // keep faces lying over polygon + { + if ( avoidFaces.count( foundFaces[ iF ])) + continue; + int i, nbFaceNodes = foundFaces[ iF ]->NbCornerNodes(); + for ( i = 0; i < nbFaceNodes; ++i ) + { + const SMDS_MeshNode* n = foundFaces[ iF ]->GetNode( i ); + bool isCommonNode = ( n == twoNodes[0] || + n == twoNodes[1] || + polygon->GetNodeIndex( n ) >= 0 ); + if ( !isCommonNode ) + break; + } + if ( i == nbFaceNodes ) // all nodes of foundFaces[iF] are shared with polygon + if ( nbOkFaces++ != iF ) + foundFaces[ nbOkFaces-1 ] = foundFaces[ iF ]; + } + if ( nbOkFaces > 0 ) + { + int iFaceSelected = 0; + if ( nbOkFaces > 1 ) // select a face with minimal distance from polygon + { + double minDist = Precision::Infinite(); + for ( int iF = 0; iF < nbOkFaces; ++iF ) + { + int i, nbFaceNodes = foundFaces[ iF ]->NbCornerNodes(); + gp_XYZ gc = SMESH_NodeXYZ( foundFaces[ iF ]->GetNode( 0 )); + for ( i = 1; i < nbFaceNodes; ++i ) + gc += SMESH_NodeXYZ( foundFaces[ iF ]->GetNode( i )); + gc /= nbFaceNodes; + + double dist = SMESH_MeshAlgos::GetDistance( polygon, gc ); + if ( dist < minDist ) + { + minDist = dist; + iFaceSelected = iF; + } + } + } + if ( foundFaces[ iFaceSelected ]->NbCornerNodes() != 3 ) + return 0; + nodes.insert( nodes.end(), + foundFaces[ iFaceSelected ]->begin_nodes(), + foundFaces[ iFaceSelected ]->end_nodes()); + if ( !SMESH_MeshAlgos::IsRightOrder( foundFaces[ iFaceSelected ], + twoNodes[0], twoNodes[1] )) + { + // reverse just added nodes + std::reverse( nodes.end() - 3, nodes.end() ); + } + avoidFaces.insert( foundFaces[ iFaceSelected ]); + nbSplits++; + } + + twoNodes[1] = twoNodes[0]; + + } // loop on polygon nodes + + return nbSplits; +} +//================================================================================ +/*! + * \brief Divide a polygon into triangles and modify accordingly an adjacent polyhedron + */ +void splitPolygon( const SMDS_MeshElement* polygon, + SMDS_VolumeTool & volume, + const int facetIndex, + const TGeomID faceID, + const TGeomID solidID, + SMESH_MeshEditor::ElemFeatures& face, + SMESH_MeshEditor& editor, + const bool reinitVolume) +{ + SMESH_MeshAlgos::Triangulate divider(/*optimize=*/false); + bool triangulationExist = false; + int nbTrias = findExistingTriangulation( polygon, face.myNodes ); + if ( nbTrias > 0 ) + triangulationExist = true; + else + nbTrias = divider.GetTriangles( polygon, face.myNodes ); + face.myNodes.resize( nbTrias * 3 ); + + SMESH_MeshEditor::ElemFeatures newVolumeDef; + newVolumeDef.Init( volume.Element() ); + newVolumeDef.SetID( volume.Element()->GetID() ); + + newVolumeDef.myPolyhedQuantities.reserve( volume.NbFaces() + nbTrias ); + newVolumeDef.myNodes.reserve( volume.NbNodes() + nbTrias * 3 ); + + SMESHDS_Mesh* meshDS = editor.GetMeshDS(); + SMDS_MeshElement* newTriangle; + for ( int iF = 0, nF = volume.NbFaces(); iF < nF; iF++ ) + { + if ( iF == facetIndex ) + { + newVolumeDef.myPolyhedQuantities.push_back( 3 ); + newVolumeDef.myNodes.insert( newVolumeDef.myNodes.end(), + face.myNodes.begin(), + face.myNodes.begin() + 3 ); + meshDS->RemoveFreeElement( polygon, 0, false ); + if ( !triangulationExist ) + { + newTriangle = meshDS->AddFace( face.myNodes[0], face.myNodes[1], face.myNodes[2] ); + meshDS->SetMeshElementOnShape( newTriangle, faceID ); + } + } + else + { + const SMDS_MeshNode** nn = volume.GetFaceNodes( iF ); + const size_t nbFaceNodes = volume.NbFaceNodes ( iF ); + newVolumeDef.myPolyhedQuantities.push_back( nbFaceNodes ); + newVolumeDef.myNodes.insert( newVolumeDef.myNodes.end(), nn, nn + nbFaceNodes ); + } + } + + for ( size_t iN = 3; iN < face.myNodes.size(); iN += 3 ) + { + newVolumeDef.myPolyhedQuantities.push_back( 3 ); + newVolumeDef.myNodes.insert( newVolumeDef.myNodes.end(), + face.myNodes.begin() + iN, + face.myNodes.begin() + iN + 3 ); + if ( !triangulationExist ) + { + newTriangle = meshDS->AddFace( face.myNodes[iN], face.myNodes[iN+1], face.myNodes[iN+2] ); + meshDS->SetMeshElementOnShape( newTriangle, faceID ); + } + } + + meshDS->RemoveFreeElement( volume.Element(), 0, false ); + SMDS_MeshElement* newVolume = editor.AddElement( newVolumeDef.myNodes, newVolumeDef ); + meshDS->SetMeshElementOnShape( newVolume, solidID ); + + if ( reinitVolume ) + { + volume.Set( 0 ); + volume.Set( newVolume ); + } + return; +} + + //================================================================================ +/*! + * \brief Look for a FACE supporting all given nodes made on EDGEs and VERTEXes + */ +TGeomID findCommonFace( const std::vector< const SMDS_MeshNode* > & nn, + const SMESH_Mesh* mesh ) +{ + TGeomID faceID = 0; + TGeomID shapeIDs[20]; + for ( size_t iN = 0; iN < nn.size(); ++iN ) + shapeIDs[ iN ] = nn[ iN ]->GetShapeID(); + + SMESH_subMesh* sm = mesh->GetSubMeshContaining( shapeIDs[ 0 ]); + for ( const SMESH_subMesh * smFace : sm->GetAncestors() ) + { + if ( smFace->GetSubShape().ShapeType() != TopAbs_FACE ) + continue; + + faceID = smFace->GetId(); + + for ( size_t iN = 1; iN < nn.size() && faceID; ++iN ) + { + if ( !smFace->DependsOn( shapeIDs[ iN ])) + faceID = 0; + } + if ( faceID > 0 ) + break; + } + return faceID; +} + + +//================================================================================ +/*! + * \brief Creates topology of the hexahedron + */ +Hexahedron::Hexahedron(Grid* grid) + : _grid( grid ), _nbFaceIntNodes(0), _hasTooSmall( false ) +{ + _polygons.reserve(100); // to avoid reallocation; + + //set nodes shift within grid->_nodes from the node 000 + size_t dx = _grid->NodeIndexDX(); + size_t dy = _grid->NodeIndexDY(); + size_t dz = _grid->NodeIndexDZ(); + size_t i000 = 0; + size_t i100 = i000 + dx; + size_t i010 = i000 + dy; + size_t i110 = i010 + dx; + size_t i001 = i000 + dz; + size_t i101 = i100 + dz; + size_t i011 = i010 + dz; + size_t i111 = i110 + dz; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V000 )] = i000; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V100 )] = i100; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V010 )] = i010; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V110 )] = i110; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V001 )] = i001; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V101 )] = i101; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V011 )] = i011; + grid->_nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V111 )] = i111; + + vector< int > idVec; + // set nodes to links + for ( int linkID = SMESH_Block::ID_Ex00; linkID <= SMESH_Block::ID_E11z; ++linkID ) + { + SMESH_Block::GetEdgeVertexIDs( linkID, idVec ); + _Link& link = _hexLinks[ SMESH_Block::ShapeIndex( linkID )]; + link._nodes[0] = &_hexNodes[ SMESH_Block::ShapeIndex( idVec[0] )]; + link._nodes[1] = &_hexNodes[ SMESH_Block::ShapeIndex( idVec[1] )]; + } + + // set links to faces + int interlace[4] = { 0, 3, 1, 2 }; // to walk by links around a face: { u0, 1v, u1, 0v } + for ( int faceID = SMESH_Block::ID_Fxy0; faceID <= SMESH_Block::ID_F1yz; ++faceID ) + { + _Face& quad = _hexQuads[ SMESH_Block::ShapeIndex( faceID )]; + quad._name = (SMESH_Block::TShapeID) faceID; + + SMESH_Block::GetFaceEdgesIDs( faceID, idVec ); + bool revFace = ( faceID == SMESH_Block::ID_Fxy0 || + faceID == SMESH_Block::ID_Fx1z || + faceID == SMESH_Block::ID_F0yz ); + quad._links.resize(4); + vector<_OrientedLink>::iterator frwLinkIt = quad._links.begin(); + vector<_OrientedLink>::reverse_iterator revLinkIt = quad._links.rbegin(); + for ( int i = 0; i < 4; ++i ) + { + bool revLink = revFace; + if ( i > 1 ) // reverse links u1 and v0 + revLink = !revLink; + _OrientedLink& link = revFace ? *revLinkIt++ : *frwLinkIt++; + link = _OrientedLink( & _hexLinks[ SMESH_Block::ShapeIndex( idVec[interlace[i]] )], + revLink ); + } + } +} +//================================================================================ +/*! + * \brief Copy constructor + */ +Hexahedron::Hexahedron( const Hexahedron& other, size_t i, size_t j, size_t k, int cellID ) + :_grid( other._grid ), _nbFaceIntNodes(0), _i( i ), _j( j ), _k( k ), _hasTooSmall( false ) +{ + _polygons.reserve(100); // to avoid reallocation; + + // copy topology + for ( int i = 0; i < 12; ++i ) + { + const _Link& srcLink = other._hexLinks[ i ]; + _Link& tgtLink = this->_hexLinks[ i ]; + tgtLink._nodes[0] = _hexNodes + ( srcLink._nodes[0] - other._hexNodes ); + tgtLink._nodes[1] = _hexNodes + ( srcLink._nodes[1] - other._hexNodes ); + } + + for ( int i = 0; i < 6; ++i ) + { + const _Face& srcQuad = other._hexQuads[ i ]; + _Face& tgtQuad = this->_hexQuads[ i ]; + tgtQuad._name = srcQuad._name; + tgtQuad._links.resize(4); + for ( int j = 0; j < 4; ++j ) + { + const _OrientedLink& srcLink = srcQuad._links[ j ]; + _OrientedLink& tgtLink = tgtQuad._links[ j ]; + tgtLink._reverse = srcLink._reverse; + tgtLink._link = _hexLinks + ( srcLink._link - other._hexLinks ); + } + } + + if (SALOME::VerbosityActivated()) + _cellID = cellID; +} + +void Hexahedron::_Node::Add( const E_IntersectPoint* ip ) +{ + const std::lock_guard lock(_eMutex); + // Possible cases before Add(ip): + /// 1) _node != 0 --> _Node at hex corner ( _intPoint == 0 || _intPoint._node == 0 ) + /// 2) _node == 0 && _intPoint._node != 0 --> link intersected by FACE + /// 3) _node == 0 && _intPoint._node == 0 --> _Node at EDGE intersection + // + // If ip is added in cases 1) and 2) _node position must be changed to ip._shapeID + // at creation of elements + // To recognize this case, set _intPoint._node = Node() + const SMDS_MeshNode* node = Node(); + if ( !_intPoint ) { + _intPoint = ip; + } + else { + ip->Add( _intPoint->_faceIDs ); + _intPoint = ip; + } + if ( node ) + _node = _intPoint->_node = node; +} + +//================================================================================ +/*! + * \brief Return IDs of SOLIDs interfering with this Hexahedron + */ +size_t Hexahedron::getSolids( TGeomID ids[] ) +{ + if ( _grid->_geometry.IsOneSolid() ) + { + ids[0] = _grid->GetSolid()->ID(); + return 1; + } + // count intersection points belonging to each SOLID + TID2Nb id2NbPoints; + id2NbPoints.reserve( 3 ); + + _origNodeInd = _grid->NodeIndex( _i,_j,_k ); + for ( int iN = 0; iN < 8; ++iN ) + { + _hexNodes[iN]._node = _grid->_nodes [ _origNodeInd + _grid->_nodeShift[iN] ]; + _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ]; + + if ( _hexNodes[iN]._intPoint ) // intersection with a FACE + { + for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) + { + const vector< TGeomID > & solidIDs = + _grid->GetSolidIDs( _hexNodes[iN]._intPoint->_faceIDs[iF] ); + for ( size_t i = 0; i < solidIDs.size(); ++i ) + insertAndIncrement( solidIDs[i], id2NbPoints ); + } + } + else if ( _hexNodes[iN]._node ) // node inside a SOLID + { + insertAndIncrement( _hexNodes[iN]._node->GetShapeID(), id2NbPoints ); + } + } + + for ( int iL = 0; iL < 12; ++iL ) + { + const _Link& link = _hexLinks[ iL ]; + for ( size_t iP = 0; iP < link._fIntPoints.size(); ++iP ) + { + for ( size_t iF = 0; iF < link._fIntPoints[iP]->_faceIDs.size(); ++iF ) + { + const vector< TGeomID > & solidIDs = + _grid->GetSolidIDs( link._fIntPoints[iP]->_faceIDs[iF] ); + for ( size_t i = 0; i < solidIDs.size(); ++i ) + insertAndIncrement( solidIDs[i], id2NbPoints ); + } + } + } + + for ( size_t iP = 0; iP < _eIntPoints.size(); ++iP ) + { + const vector< TGeomID > & solidIDs = _grid->GetSolidIDs( _eIntPoints[iP]->_shapeID ); + for ( size_t i = 0; i < solidIDs.size(); ++i ) + insertAndIncrement( solidIDs[i], id2NbPoints ); + } + + size_t nbSolids = 0; + for ( TID2Nb::iterator id2nb = id2NbPoints.begin(); id2nb != id2NbPoints.end(); ++id2nb ) + if ( id2nb->second >= 3 ) + ids[ nbSolids++ ] = id2nb->first; + + return nbSolids; +} + +//================================================================================ +/*! + * \brief Count cuts by INTERNAL FACEs and set _Node::_isInternalFlags + */ +bool Hexahedron::isCutByInternalFace( IsInternalFlag & maxFlag ) +{ + TID2Nb id2NbPoints; + id2NbPoints.reserve( 3 ); + + for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) + for ( size_t iF = 0; iF < _intNodes[iN]._intPoint->_faceIDs.size(); ++iF ) + { + if ( _grid->IsInternal( _intNodes[iN]._intPoint->_faceIDs[iF])) + insertAndIncrement( _intNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); + } + for ( size_t iN = 0; iN < 8; ++iN ) + if ( _hexNodes[iN]._intPoint ) + for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) + { + if ( _grid->IsInternal( _hexNodes[iN]._intPoint->_faceIDs[iF])) + insertAndIncrement( _hexNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); + } + + maxFlag = IS_NOT_INTERNAL; + for ( TID2Nb::iterator id2nb = id2NbPoints.begin(); id2nb != id2NbPoints.end(); ++id2nb ) + { + TGeomID intFace = id2nb->first; + IsInternalFlag intFlag = ( id2nb->second >= 3 ? IS_CUT_BY_INTERNAL_FACE : IS_INTERNAL ); + if ( intFlag > maxFlag ) + maxFlag = intFlag; + + for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) + if ( _intNodes[iN].IsOnFace( intFace )) + _intNodes[iN].SetInternal( intFlag ); + + for ( size_t iN = 0; iN < 8; ++iN ) + if ( _hexNodes[iN].IsOnFace( intFace )) + _hexNodes[iN].SetInternal( intFlag ); + } + + return maxFlag; +} + +//================================================================================ +/*! + * \brief Return any FACE interfering with this Hexahedron + */ +TGeomID Hexahedron::getAnyFace() const +{ + TID2Nb id2NbPoints; + id2NbPoints.reserve( 3 ); + + for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) + for ( size_t iF = 0; iF < _intNodes[iN]._intPoint->_faceIDs.size(); ++iF ) + insertAndIncrement( _intNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); + + for ( size_t iN = 0; iN < 8; ++iN ) + if ( _hexNodes[iN]._intPoint ) + for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) + insertAndIncrement( _hexNodes[iN]._intPoint->_faceIDs[iF], id2NbPoints ); + + for ( unsigned int minNb = 3; minNb > 0; --minNb ) + for ( TID2Nb::iterator id2nb = id2NbPoints.begin(); id2nb != id2NbPoints.end(); ++id2nb ) + if ( id2nb->second >= minNb ) + return id2nb->first; + + return 0; +} + +//================================================================================ +/*! + * \brief Initializes IJK by Hexahedron index + */ +void Hexahedron::setIJK( size_t iCell ) +{ + size_t iNbCell = _grid->_coords[0].size() - 1; + size_t jNbCell = _grid->_coords[1].size() - 1; + _i = iCell % iNbCell; + _j = ( iCell % ( iNbCell * jNbCell )) / iNbCell; + _k = iCell / iNbCell / jNbCell; +} + +//================================================================================ +/*! + * \brief Initializes its data by given grid cell (countered from zero) + */ +void Hexahedron::init( size_t iCell ) +{ + setIJK( iCell ); + init( _i, _j, _k ); +} + +//================================================================================ +/*! + * \brief Initializes its data by given grid cell nodes and intersections + */ +void Hexahedron::init( size_t i, size_t j, size_t k, const Solid* solid ) +{ + _i = i; _j = j; _k = k; + + bool isCompute = solid; + if ( !solid ) + solid = _grid->GetSolid(); + + // set nodes of grid to nodes of the hexahedron and + // count nodes at hexahedron corners located IN and ON geometry + _nbCornerNodes = _nbBndNodes = 0; + _origNodeInd = _grid->NodeIndex( i,j,k ); + for ( int iN = 0; iN < 8; ++iN ) + { + _hexNodes[iN]._isInternalFlags = 0; + + // Grid node + _hexNodes[iN]._node = _grid->_nodes [ _origNodeInd + _grid->_nodeShift[iN] ]; + _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ]; + + if ( _grid->_allBorderNodes[ _origNodeInd + _grid->_nodeShift[iN] ] ) + _hexNodes[iN]._boundaryCornerNode = _grid->_allBorderNodes [ _origNodeInd + _grid->_nodeShift[iN] ]; + + if ( _hexNodes[iN]._node && !solid->Contains( _hexNodes[iN]._node->GetShapeID() )) + _hexNodes[iN]._node = 0; + + if ( _hexNodes[iN]._intPoint && !solid->ContainsAny( _hexNodes[iN]._intPoint->_faceIDs )) + _hexNodes[iN]._intPoint = 0; + + _nbCornerNodes += bool( _hexNodes[iN]._node ); + _nbBndNodes += bool( _hexNodes[iN]._intPoint ); + } + _sideLength[0] = _grid->_coords[0][i+1] - _grid->_coords[0][i]; + _sideLength[1] = _grid->_coords[1][j+1] - _grid->_coords[1][j]; + _sideLength[2] = _grid->_coords[2][k+1] - _grid->_coords[2][k]; + + _intNodes.clear(); + _vIntNodes.clear(); + + if ( !isCompute ) + return; + + if ( _nbFaceIntNodes + _eIntPoints.size() > 0 && + _nbFaceIntNodes + _eIntPoints.size() + _nbCornerNodes > 3) + { + _intNodes.reserve( 3 * ( _nbBndNodes + _nbFaceIntNodes + _eIntPoints.size() )); + + // this method can be called in parallel, so use own helper + SMESH_MesherHelper helper( *_grid->_helper->GetMesh() ); + + // Create sub-links (_Link::_splits) by splitting links with _Link::_fIntPoints + // --------------------------------------------------------------- + _Link split; + for ( int iLink = 0; iLink < 12; ++iLink ) + { + _Link& link = _hexLinks[ iLink ]; + link._fIntNodes.clear(); + link._fIntNodes.reserve( link._fIntPoints.size() ); + for ( size_t i = 0; i < link._fIntPoints.size(); ++i ) + if ( solid->ContainsAny( link._fIntPoints[i]->_faceIDs )) + { + _intNodes.push_back( _Node( 0, link._fIntPoints[i] )); + link._fIntNodes.push_back( & _intNodes.back() ); + } + + link._splits.clear(); + split._nodes[ 0 ] = link._nodes[0]; + bool isOut = ( ! link._nodes[0]->Node() ); + bool checkTransition; + for ( size_t i = 0; i < link._fIntNodes.size(); ++i ) + { + const bool isGridNode = ( ! link._fIntNodes[i]->Node() ); + if ( !isGridNode ) // intersection non-coincident with a grid node + { + if ( split._nodes[ 0 ]->Node() && !isOut ) + { + split._nodes[ 1 ] = link._fIntNodes[i]; + link._splits.push_back( split ); + } + split._nodes[ 0 ] = link._fIntNodes[i]; + checkTransition = true; + } + else // FACE intersection coincident with a grid node (at link ends) + { + checkTransition = ( i == 0 && link._nodes[0]->Node() ); + } + if ( checkTransition ) + { + const vector< TGeomID >& faceIDs = link._fIntNodes[i]->_intPoint->_faceIDs; + if ( _grid->IsInternal( faceIDs.back() )) + isOut = false; + else if ( faceIDs.size() > 1 || _eIntPoints.size() > 0 ) + isOut = isOutPoint( link, i, helper, solid ); + else + { + bool okTransi = _grid->IsCorrectTransition( faceIDs[0], solid ); + switch ( link._fIntNodes[i]->FaceIntPnt()->_transition ) { + case Trans_OUT: isOut = okTransi; break; + case Trans_IN : isOut = !okTransi; break; + default: + isOut = isOutPoint( link, i, helper, solid ); + } + } + } + } + if ( link._nodes[ 1 ]->Node() && split._nodes[ 0 ]->Node() && !isOut ) + { + split._nodes[ 1 ] = link._nodes[1]; + link._splits.push_back( split ); + } + } + + // Create _Node's at intersections with EDGEs. + // -------------------------------------------- + // 1) add this->_eIntPoints to _Face::_eIntNodes + // 2) fill _intNodes and _vIntNodes + // + const double tol2 = _grid->_tol * _grid->_tol * 4; + int facets[3], nbFacets, subEntity; + + for ( int iF = 0; iF < 6; ++iF ) + _hexQuads[ iF ]._eIntNodes.clear(); + + for ( size_t iP = 0; iP < _eIntPoints.size(); ++iP ) + { + if ( !solid->ContainsAny( _eIntPoints[iP]->_faceIDs )) + continue; + nbFacets = getEntity( _eIntPoints[iP], facets, subEntity ); + _Node* equalNode = 0; + switch( nbFacets ) { + case 1: // in a _Face + { + _Face& quad = _hexQuads[ facets[0] - SMESH_Block::ID_FirstF ]; + equalNode = findEqualNode( quad._eIntNodes, _eIntPoints[ iP ], tol2 ); + if ( equalNode ) { + equalNode->Add( _eIntPoints[ iP ] ); + } + else { + _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); + quad._eIntNodes.push_back( & _intNodes.back() ); + } + break; + } + case 2: // on a _Link + { + _Link& link = _hexLinks[ subEntity - SMESH_Block::ID_FirstE ]; + if ( link._splits.size() > 0 ) + { + equalNode = findEqualNode( link._fIntNodes, _eIntPoints[ iP ], tol2 ); + if ( equalNode ) + equalNode->Add( _eIntPoints[ iP ] ); + else if ( link._splits.size() == 1 && + link._splits[0]._nodes[0] && + link._splits[0]._nodes[1] ) + link._splits.clear(); // hex edge is divided by _eIntPoints[iP] + } + //else + if ( !equalNode ) + { + _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); + bool newNodeUsed = false; + for ( int iF = 0; iF < 2; ++iF ) + { + _Face& quad = _hexQuads[ facets[iF] - SMESH_Block::ID_FirstF ]; + equalNode = findEqualNode( quad._eIntNodes, _eIntPoints[ iP ], tol2 ); + if ( equalNode ) { + equalNode->Add( _eIntPoints[ iP ] ); + } + else { + quad._eIntNodes.push_back( & _intNodes.back() ); + newNodeUsed = true; + } + } + if ( !newNodeUsed ) + _intNodes.pop_back(); + } + break; + } + case 3: // at a corner + { + _Node& node = _hexNodes[ subEntity - SMESH_Block::ID_FirstV ]; + if ( node.Node() ) + { + if ( node._intPoint ) + node._intPoint->Add( _eIntPoints[ iP ]->_faceIDs, _eIntPoints[ iP ]->_node ); + } + else + { + _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); + for ( int iF = 0; iF < 3; ++iF ) + { + _Face& quad = _hexQuads[ facets[iF] - SMESH_Block::ID_FirstF ]; + equalNode = findEqualNode( quad._eIntNodes, _eIntPoints[ iP ], tol2 ); + if ( equalNode ) { + equalNode->Add( _eIntPoints[ iP ] ); + } + else { + quad._eIntNodes.push_back( & _intNodes.back() ); + } + } + } + break; + } + } // switch( nbFacets ) + + if ( nbFacets == 0 || + _grid->ShapeType( _eIntPoints[ iP ]->_shapeID ) == TopAbs_VERTEX ) + { + equalNode = findEqualNode( _vIntNodes, _eIntPoints[ iP ], tol2 ); + if ( equalNode ) { + equalNode->Add( _eIntPoints[ iP ] ); + } + else if ( nbFacets == 0 ) { + if ( _intNodes.empty() || _intNodes.back().EdgeIntPnt() != _eIntPoints[ iP ]) + _intNodes.push_back( _Node( 0, _eIntPoints[ iP ])); + _vIntNodes.push_back( & _intNodes.back() ); + } + } + } // loop on _eIntPoints + } + + else if (( 3 < _nbCornerNodes && _nbCornerNodes < 8 ) || // _nbFaceIntNodes == 0 + ( !_grid->_geometry.IsOneSolid() )) + { + _Link split; + // create sub-links (_splits) of whole links + for ( int iLink = 0; iLink < 12; ++iLink ) + { + _Link& link = _hexLinks[ iLink ]; + link._splits.clear(); + if ( link._nodes[ 0 ]->Node() && link._nodes[ 1 ]->Node() ) + { + split._nodes[ 0 ] = link._nodes[0]; + split._nodes[ 1 ] = link._nodes[1]; + link._splits.push_back( split ); + } + } + } + return; + +} // init( _i, _j, _k ) + +//================================================================================ +/*! + * \brief Compute mesh volumes resulted from intersection of the Hexahedron + */ +void Hexahedron::computeElements( const Solid* solid, int solidIndex ) +{ + if ( !solid ) + { + solid = _grid->GetSolid(); + if ( !_grid->_geometry.IsOneSolid() ) + { + TGeomID solidIDs[20] = { 0 }; + size_t nbSolids = getSolids( solidIDs ); + if ( nbSolids > 1 ) + { + for ( size_t i = 0; i < nbSolids; ++i ) + { + solid = _grid->GetSolid( solidIDs[i] ); + computeElements( solid, i ); + if ( !_volumeDefs._nodes.empty() && i < nbSolids - 1 ) + _volumeDefs.SetNext( new _volumeDef( _volumeDefs )); + } + return; + } + solid = _grid->GetSolid( solidIDs[0] ); + } + } + + init( _i, _j, _k, solid ); // get nodes and intersections from grid nodes and split links + + int nbIntersections = _nbFaceIntNodes + _eIntPoints.size(); + if ( _nbCornerNodes + nbIntersections < 4 ) + return; + + if ( _nbBndNodes == _nbCornerNodes && nbIntersections == 0 && isInHole() ) + return; // cell is in a hole + + IsInternalFlag intFlag = IS_NOT_INTERNAL; + if ( solid->HasInternalFaces() && this->isCutByInternalFace( intFlag )) + { + for ( _SplitIterator it( _hexLinks ); it.More(); it.Next() ) + { + if ( compute( solid, intFlag )) + _volumeDefs.SetNext( new _volumeDef( _volumeDefs )); + } + } + else + { + if ( solidIndex >= 0 ) + intFlag = IS_CUT_BY_INTERNAL_FACE; + + compute( solid, intFlag ); + } +} + +//================================================================================ +/*! + * \brief Compute mesh volumes resulted from intersection of the Hexahedron + */ +void Hexahedron::defineHexahedralFaces( std::vector< _OrientedLink >& splits, std::vector<_Node*>& chainNodes, std::set< TGeomID >& concaveFaces, bool toCheckSideDivision ) +{ + for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron + { + _Face& quad = _hexQuads[ iF ] ; + + _polygons.resize( _polygons.size() + 1 ); + _Face* polygon = &_polygons.back(); + polygon->_polyLinks.reserve( 20 ); + polygon->_name = quad._name; + + splits.clear(); + for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle + for ( size_t iS = 0; iS < quad._links[ iE ].NbResultLinks(); ++iS ) + splits.push_back( quad._links[ iE ].ResultLink( iS )); + + if ( splits.size() == 4 && + isQuadOnFace( iF )) // check if a quad on FACE is not split + { + polygon->_links.swap( splits ); + continue; // goto the next quad + } + + // add splits of links to a polygon and add _polyLinks to make + // polygon's boundary closed + + int nbSplits = splits.size(); + if (( nbSplits == 1 ) && + ( quad._eIntNodes.empty() || + splits[0].FirstNode()->IsLinked( splits[0].LastNode()->_intPoint ))) + //( quad._eIntNodes.empty() || _nbCornerNodes + nbIntersections > 6 )) + nbSplits = 0; + + for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) + if ( quad._eIntNodes[ iP ]->IsUsedInFace( polygon )) + quad._eIntNodes[ iP ]->_usedInFace = 0; + + size_t nbUsedEdgeNodes = 0; + _Face* prevPolyg = 0; // polygon previously created from this quad + + while ( nbSplits > 0 ) + { + size_t iS = 0; + while ( !splits[ iS ] ) + ++iS; + + if ( !polygon->_links.empty() ) + { + _polygons.resize( _polygons.size() + 1 ); + polygon = &_polygons.back(); + polygon->_polyLinks.reserve( 20 ); + polygon->_name = quad._name; + } + polygon->_links.push_back( splits[ iS ] ); + splits[ iS++ ]._link = 0; + --nbSplits; + + _Node* nFirst = polygon->_links.back().FirstNode(); + _Node *n1,*n2 = polygon->_links.back().LastNode(); + for ( ; nFirst != n2 && iS < splits.size(); ++iS ) + { + _OrientedLink& split = splits[ iS ]; + if ( !split ) continue; + + n1 = split.FirstNode(); + if ( n1 == n2 && + n1->_intPoint && + (( n1->_intPoint->_faceIDs.size() > 1 && toCheckSideDivision ) || + ( n1->_isInternalFlags ))) + { + // n1 is at intersection with EDGE + if ( findChainOnEdge( splits, polygon->_links.back(), split, concaveFaces, + iS, quad, chainNodes )) + { + for ( size_t i = 1; i < chainNodes.size(); ++i ) + polygon->AddPolyLink( chainNodes[i-1], chainNodes[i], prevPolyg ); + if ( chainNodes.back() != n1 ) // not a partial cut by INTERNAL FACE + { + prevPolyg = polygon; + n2 = chainNodes.back(); + continue; + } + } + } + else if ( n1 != n2 ) + { + // try to connect to intersections with EDGEs + if ( quad._eIntNodes.size() > nbUsedEdgeNodes && + findChain( n2, n1, quad, chainNodes )) + { + for ( size_t i = 1; i < chainNodes.size(); ++i ) + { + polygon->AddPolyLink( chainNodes[i-1], chainNodes[i] ); + nbUsedEdgeNodes += ( chainNodes[i]->IsUsedInFace( polygon )); + } + if ( chainNodes.back() != n1 ) + { + n2 = chainNodes.back(); + --iS; + continue; + } + } + // try to connect to a split ending on the same FACE + else + { + _OrientedLink foundSplit; + for ( size_t i = iS; i < splits.size() && !foundSplit; ++i ) + if (( foundSplit = splits[ i ]) && + ( n2->IsLinked( foundSplit.FirstNode()->_intPoint ))) + { + iS = i - 1; + } + else + { + foundSplit._link = 0; + } + if ( foundSplit ) + { + if ( n2 != foundSplit.FirstNode() ) + { + polygon->AddPolyLink( n2, foundSplit.FirstNode() ); + n2 = foundSplit.FirstNode(); + } + continue; + } + else + { + if ( n2->IsLinked( nFirst->_intPoint )) + break; + polygon->AddPolyLink( n2, n1, prevPolyg ); + } + } + } // if ( n1 != n2 ) + + polygon->_links.push_back( split ); + split._link = 0; + --nbSplits; + n2 = polygon->_links.back().LastNode(); + + } // loop on splits + + if ( nFirst != n2 ) // close a polygon + { + if ( !findChain( n2, nFirst, quad, chainNodes )) + { + if ( !closePolygon( polygon, chainNodes )) + if ( !isImplementEdges() ) + chainNodes.push_back( nFirst ); + } + for ( size_t i = 1; i < chainNodes.size(); ++i ) + { + polygon->AddPolyLink( chainNodes[i-1], chainNodes[i], prevPolyg ); + nbUsedEdgeNodes += bool( chainNodes[i]->IsUsedInFace( polygon )); + } + } + + if ( polygon->_links.size() < 3 && nbSplits > 0 ) + { + polygon->_polyLinks.clear(); + polygon->_links.clear(); + } + } // while ( nbSplits > 0 ) + + if ( polygon->_links.size() < 3 ) + { + _polygons.pop_back(); + } + } // +} + +//================================================================================ +/*! + * \brief Compute mesh volumes resulted from intersection of the Hexahedron + */ +bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) +{ + _polygons.clear(); + _polygons.reserve( 20 ); + + for ( int iN = 0; iN < 8; ++iN ) + _hexNodes[iN]._usedInFace = 0; + + if ( intFlag & IS_CUT_BY_INTERNAL_FACE && !_grid->_toAddEdges ) // Issue #19913 + preventVolumesOverlapping(); + + std::set< TGeomID > concaveFaces; // to avoid connecting nodes laying on them + + if ( solid->HasConcaveVertex() ) + { + for ( const E_IntersectPoint* ip : _eIntPoints ) + { + if ( const ConcaveFace* cf = solid->GetConcave( ip->_shapeID )) + if ( this->hasEdgesAround( cf )) + concaveFaces.insert( cf->_concaveFace ); + } + if ( concaveFaces.empty() || concaveFaces.size() * 3 < _eIntPoints.size() ) + for ( const _Node& hexNode: _hexNodes ) + { + if ( hexNode._node && hexNode._intPoint && hexNode._intPoint->_faceIDs.size() >= 3 ) + if ( const ConcaveFace* cf = solid->GetConcave( hexNode._node->GetShapeID() )) + if ( this->hasEdgesAround( cf )) + concaveFaces.insert( cf->_concaveFace ); + } + } + + // Create polygons from quadrangles + // -------------------------------- + + vector< _OrientedLink > splits; + vector<_Node*> chainNodes; + _Face* coplanarPolyg; + + const bool hasEdgeIntersections = !_eIntPoints.empty(); + const bool toCheckSideDivision = isImplementEdges() || intFlag & IS_CUT_BY_INTERNAL_FACE; + defineHexahedralFaces( splits, chainNodes, concaveFaces, toCheckSideDivision ); + + // Create polygons closing holes in a polyhedron + // ---------------------------------------------- + + // clear _usedInFace + for ( size_t iN = 0; iN < _intNodes.size(); ++iN ) + _intNodes[ iN ]._usedInFace = 0; + + // add polygons to their links and mark used nodes + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + { + _Face& polygon = _polygons[ iP ]; + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + { + polygon._links[ iL ].AddFace( &polygon ); + polygon._links[ iL ].FirstNode()->_usedInFace = &polygon; + } + } + // find free links + vector< _OrientedLink* > freeLinks; + freeLinks.reserve(20); + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + { + _Face& polygon = _polygons[ iP ]; + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + if ( polygon._links[ iL ].NbFaces() < 2 ) + freeLinks.push_back( & polygon._links[ iL ]); + } + int nbFreeLinks = freeLinks.size(); + if ( nbFreeLinks == 1 ) return false; + + // put not used intersection nodes to _vIntNodes + int nbVertexNodes = 0; // nb not used vertex nodes + { + for ( size_t iN = 0; iN < _vIntNodes.size(); ++iN ) + nbVertexNodes += ( !_vIntNodes[ iN ]->IsUsedInFace() ); + + const double tol = 1e-3 * Min( Min( _sideLength[0], _sideLength[1] ), _sideLength[0] ); + for ( size_t iN = _nbFaceIntNodes; iN < _intNodes.size(); ++iN ) + { + if ( _intNodes[ iN ].IsUsedInFace() ) continue; + if ( dynamic_cast< const F_IntersectPoint* >( _intNodes[ iN ]._intPoint )) continue; + _Node* equalNode = findEqualNode( _vIntNodes, _intNodes[ iN ].EdgeIntPnt(), tol*tol ); + if ( !equalNode ) + { + _vIntNodes.push_back( &_intNodes[ iN ]); + ++nbVertexNodes; + } + } + } + + std::set usedFaceIDs; + std::vector< TGeomID > faces; + TGeomID curFace = 0; + const size_t nbQuadPolygons = _polygons.size(); + E_IntersectPoint ipTmp; + std::map< TGeomID, std::vector< const B_IntersectPoint* > > tmpAddedFace; // face added to _intPoint + // std::cout << "2\n"; + // create polygons by making closed chains of free links + size_t iPolygon = _polygons.size(); + while ( nbFreeLinks > 0 ) + { + if ( iPolygon == _polygons.size() ) + { + _polygons.resize( _polygons.size() + 1 ); + _polygons[ iPolygon ]._polyLinks.reserve( 20 ); + _polygons[ iPolygon ]._links.reserve( 20 ); + } + _Face& polygon = _polygons[ iPolygon ]; + + _OrientedLink* curLink = 0; + _Node* curNode; + if (( !hasEdgeIntersections ) || + ( nbFreeLinks < 4 && nbVertexNodes == 0 )) + { + // get a remaining link to start from + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + if (( curLink = freeLinks[ iL ] )) + freeLinks[ iL ] = 0; + polygon._links.push_back( *curLink ); + --nbFreeLinks; + do + { + // find all links connected to curLink + curNode = curLink->FirstNode(); + curLink = 0; + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + if ( freeLinks[ iL ] && freeLinks[ iL ]->LastNode() == curNode ) + { + curLink = freeLinks[ iL ]; + freeLinks[ iL ] = 0; + --nbFreeLinks; + polygon._links.push_back( *curLink ); + } + } while ( curLink ); + } + else // there are intersections with EDGEs + { + // get a remaining link to start from, one lying on minimal nb of FACEs + { + typedef pair< TGeomID, int > TFaceOfLink; + TFaceOfLink faceOfLink( -1, -1 ); + TFaceOfLink facesOfLink[3] = { faceOfLink, faceOfLink, faceOfLink }; + for ( size_t iL = 0; iL < freeLinks.size(); ++iL ) + if ( freeLinks[ iL ] ) + { + faces = freeLinks[ iL ]->GetNotUsedFace( usedFaceIDs ); + if ( faces.size() == 1 ) + { + faceOfLink = TFaceOfLink( faces[0], iL ); + if ( !freeLinks[ iL ]->HasEdgeNodes() ) + break; + facesOfLink[0] = faceOfLink; + } + else if ( facesOfLink[0].first < 0 ) + { + faceOfLink = TFaceOfLink(( faces.empty() ? -1 : faces[0]), iL ); + facesOfLink[ 1 + faces.empty() ] = faceOfLink; + } + } + for ( int i = 0; faceOfLink.first < 0 && i < 3; ++i ) + faceOfLink = facesOfLink[i]; + + if ( faceOfLink.first < 0 ) // all faces used + { + for ( size_t iL = 0; iL < freeLinks.size() && faceOfLink.first < 1; ++iL ) + if (( curLink = freeLinks[ iL ])) + { + faceOfLink.first = + curLink->FirstNode()->IsLinked( curLink->LastNode()->_intPoint ); + faceOfLink.second = iL; + } + usedFaceIDs.clear(); + } + curFace = faceOfLink.first; + curLink = freeLinks[ faceOfLink.second ]; + freeLinks[ faceOfLink.second ] = 0; + } + usedFaceIDs.insert( curFace ); + polygon._links.push_back( *curLink ); + --nbFreeLinks; + + // find all links lying on a curFace + do + { + // go forward from curLink + curNode = curLink->LastNode(); + curLink = 0; + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + if ( freeLinks[ iL ] && + freeLinks[ iL ]->FirstNode() == curNode && + freeLinks[ iL ]->LastNode()->IsOnFace( curFace )) + { + curLink = freeLinks[ iL ]; + freeLinks[ iL ] = 0; + polygon._links.push_back( *curLink ); + --nbFreeLinks; + } + } while ( curLink ); + + std::reverse( polygon._links.begin(), polygon._links.end() ); + + curLink = & polygon._links.back(); + do + { + // go backward from curLink + curNode = curLink->FirstNode(); + curLink = 0; + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + if ( freeLinks[ iL ] && + freeLinks[ iL ]->LastNode() == curNode && + freeLinks[ iL ]->FirstNode()->IsOnFace( curFace )) + { + curLink = freeLinks[ iL ]; + freeLinks[ iL ] = 0; + polygon._links.push_back( *curLink ); + --nbFreeLinks; + } + } while ( curLink ); + + curNode = polygon._links.back().FirstNode(); + + if ( polygon._links[0].LastNode() != curNode ) + { + if ( nbVertexNodes > 0 ) + { + // add links with _vIntNodes if not already used + chainNodes.clear(); + for ( size_t iN = 0; iN < _vIntNodes.size(); ++iN ) + if ( !_vIntNodes[ iN ]->IsUsedInFace() && + _vIntNodes[ iN ]->IsOnFace( curFace )) + { + _vIntNodes[ iN ]->_usedInFace = &polygon; + chainNodes.push_back( _vIntNodes[ iN ] ); + } + if ( chainNodes.size() > 1 && + curFace != _grid->PseudoIntExtFaceID() ) /////// TODO + { + sortVertexNodes( chainNodes, curNode, curFace ); + } + for ( size_t i = 0; i < chainNodes.size(); ++i ) + { + polygon.AddPolyLink( chainNodes[ i ], curNode ); + curNode = chainNodes[ i ]; + freeLinks.push_back( &polygon._links.back() ); + ++nbFreeLinks; + } + nbVertexNodes -= chainNodes.size(); + } + // if ( polygon._links.size() > 1 ) + { + polygon.AddPolyLink( polygon._links[0].LastNode(), curNode ); + freeLinks.push_back( &polygon._links.back() ); + ++nbFreeLinks; + } + } + } // if there are intersections with EDGEs + + if ( polygon._links.size() < 2 || + polygon._links[0].LastNode() != polygon._links.back().FirstNode() ) + { + _polygons.clear(); + break; // closed polygon not found -> invalid polyhedron + } + + if ( polygon._links.size() == 2 ) + { + if ( freeLinks.back() == &polygon._links.back() ) + { + freeLinks.pop_back(); + --nbFreeLinks; + } + if ( polygon._links.front().NbFaces() > 0 ) + polygon._links.back().AddFace( polygon._links.front()._link->_faces[0] ); + if ( polygon._links.back().NbFaces() > 0 ) + polygon._links.front().AddFace( polygon._links.back()._link->_faces[0] ); + + if ( iPolygon == _polygons.size()-1 ) + _polygons.pop_back(); + } + else // polygon._links.size() >= 2 + { + // add polygon to its links + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + { + polygon._links[ iL ].AddFace( &polygon ); + polygon._links[ iL ].Reverse(); + } + if ( /*hasEdgeIntersections &&*/ iPolygon == _polygons.size() - 1 ) + { + // check that a polygon does not lie on a hexa side + coplanarPolyg = 0; + for ( size_t iL = 0; iL < polygon._links.size() && !coplanarPolyg; ++iL ) + { + if ( polygon._links[ iL ].NbFaces() < 2 ) + continue; // it's a just added free link + // look for a polygon made on a hexa side and sharing + // two or more haxa links + size_t iL2; + coplanarPolyg = polygon._links[ iL ]._link->_faces[0]; + for ( iL2 = iL + 1; iL2 < polygon._links.size(); ++iL2 ) + if ( polygon._links[ iL2 ]._link->_faces[0] == coplanarPolyg && + !coplanarPolyg->IsPolyLink( polygon._links[ iL ]) && + !coplanarPolyg->IsPolyLink( polygon._links[ iL2 ]) && + coplanarPolyg < & _polygons[ nbQuadPolygons ]) + break; + if ( iL2 == polygon._links.size() ) + coplanarPolyg = 0; + } + if ( coplanarPolyg ) // coplanar polygon found + { + freeLinks.resize( freeLinks.size() - polygon._polyLinks.size() ); + nbFreeLinks -= polygon._polyLinks.size(); + + ipTmp._faceIDs.resize(1); + ipTmp._faceIDs[0] = curFace; + + // fill freeLinks with links not shared by coplanarPolyg and polygon + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + if ( polygon._links[ iL ]._link->_faces[1] && + polygon._links[ iL ]._link->_faces[0] != coplanarPolyg ) + { + _Face* p = polygon._links[ iL ]._link->_faces[0]; + for ( size_t iL2 = 0; iL2 < p->_links.size(); ++iL2 ) + if ( p->_links[ iL2 ]._link == polygon._links[ iL ]._link ) + { + freeLinks.push_back( & p->_links[ iL2 ] ); + ++nbFreeLinks; + freeLinks.back()->RemoveFace( &polygon ); + break; + } + } + for ( size_t iL = 0; iL < coplanarPolyg->_links.size(); ++iL ) + if ( coplanarPolyg->_links[ iL ]._link->_faces[1] && + coplanarPolyg->_links[ iL ]._link->_faces[1] != &polygon ) + { + _Face* p = coplanarPolyg->_links[ iL ]._link->_faces[0]; + if ( p == coplanarPolyg ) + p = coplanarPolyg->_links[ iL ]._link->_faces[1]; + for ( size_t iL2 = 0; iL2 < p->_links.size(); ++iL2 ) + if ( p->_links[ iL2 ]._link == coplanarPolyg->_links[ iL ]._link ) + { + // set links of coplanarPolyg in place of used freeLinks + // to re-create coplanarPolyg next + size_t iL3 = 0; + for ( ; iL3 < freeLinks.size() && freeLinks[ iL3 ]; ++iL3 ); + if ( iL3 < freeLinks.size() ) + freeLinks[ iL3 ] = ( & p->_links[ iL2 ] ); + else + freeLinks.push_back( & p->_links[ iL2 ] ); + ++nbFreeLinks; + freeLinks[ iL3 ]->RemoveFace( coplanarPolyg ); + // mark nodes of coplanarPolyg as lying on curFace + for ( int iN = 0; iN < 2; ++iN ) + { + _Node* n = freeLinks[ iL3 ]->_link->_nodes[ iN ]; + bool added = false; + if ( n->_intPoint ) added = n->_intPoint->Add( ipTmp._faceIDs ); + else n->_intPoint = &ipTmp; + if ( added ) + tmpAddedFace[ ipTmp._faceIDs[0] ].push_back( n->_intPoint ); + } + break; + } + } + // set coplanarPolyg to be re-created next + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + if ( coplanarPolyg == & _polygons[ iP ] ) + { + iPolygon = iP; + _polygons[ iPolygon ]._links.clear(); + _polygons[ iPolygon ]._polyLinks.clear(); + break; + } + _polygons.pop_back(); + usedFaceIDs.erase( curFace ); + continue; + } // if ( coplanarPolyg ) + } // if ( hasEdgeIntersections ) - search for coplanarPolyg + + iPolygon = _polygons.size(); + + } // end of case ( polygon._links.size() > 2 ) + } // while ( nbFreeLinks > 0 ) + // std::cout << "3\n"; + for ( auto & face_ip : tmpAddedFace ) + { + curFace = face_ip.first; + for ( const B_IntersectPoint* ip : face_ip.second ) + { + auto it = std::find( ip->_faceIDs.begin(), ip->_faceIDs.end(), curFace ); + if ( it != ip->_faceIDs.end() ) + ip->_faceIDs.erase( it ); + } + } + + if ( _polygons.size() < 3 ) + return false; + + // check volume size + double volSize = 0; + _hasTooSmall = ! checkPolyhedronSize( intFlag & IS_CUT_BY_INTERNAL_FACE, volSize ); + + for ( size_t i = 0; i < 8; ++i ) + if ( _hexNodes[ i ]._intPoint == &ipTmp ) + _hexNodes[ i ]._intPoint = 0; + + if ( _hasTooSmall ) + return false; // too small volume + + + // Try to find out names of no-name polygons (issue # 19887) + if ( _grid->IsToRemoveExcessEntities() && _polygons.back()._name == SMESH_Block::ID_NONE ) + { + gp_XYZ uvwCenter = 0.5 * ( _grid->_coords[0][_i] + _grid->_coords[0][_i+1] ) * _grid->_axes[0] + + 0.5 * ( _grid->_coords[1][_j] + _grid->_coords[1][_j+1] ) * _grid->_axes[1] + + 0.5 * ( _grid->_coords[2][_k] + _grid->_coords[2][_k+1] ) * _grid->_axes[2]; + + for ( size_t i = _polygons.size() - 1; _polygons[i]._name == SMESH_Block::ID_NONE; --i ) + { + _Face& face = _polygons[ i ]; + Bnd_Box bb; + gp_Pnt uvw; + for ( size_t iL = 0; iL < face._links.size(); ++iL ) + { + _Node* n = face._links[ iL ].FirstNode(); + if ( n ) + { + gp_XYZ p = SMESH_NodeXYZ( n->Node() ); + _grid->ComputeUVW( p, uvw.ChangeCoord().ChangeData() ); + bb.Add( uvw ); + } + } + gp_Pnt pMin = bb.CornerMin(); + if ( bb.IsXThin( _grid->_tol )) + face._name = pMin.X() < uvwCenter.X() ? SMESH_Block::ID_F0yz : SMESH_Block::ID_F1yz; + else if ( bb.IsYThin( _grid->_tol )) + face._name = pMin.Y() < uvwCenter.Y() ? SMESH_Block::ID_Fx0z : SMESH_Block::ID_Fx1z; + else if ( bb.IsZThin( _grid->_tol )) + face._name = pMin.Z() < uvwCenter.Z() ? SMESH_Block::ID_Fxy0 : SMESH_Block::ID_Fxy1; + } + } + + + /* This call is irrelevant here because _volumeDefs datas are were not filled! + or .... it is potentialy filled by other thread?? */ + _volumeDefs._nodes.clear(); + _volumeDefs._quantities.clear(); + _volumeDefs._names.clear(); + // create a classic cell if possible + + int nbPolygons = 0; + for ( size_t iF = 0; iF < _polygons.size(); ++iF ) + nbPolygons += (_polygons[ iF ]._links.size() > 2 ); + + //const int nbNodes = _nbCornerNodes + nbIntersections; + int nbNodes = 0; + for ( size_t i = 0; i < 8; ++i ) + nbNodes += _hexNodes[ i ].IsUsedInFace(); + for ( size_t i = 0; i < _intNodes.size(); ++i ) + nbNodes += _intNodes[ i ].IsUsedInFace(); + + bool isClassicElem = false; + if ( nbNodes == 8 && nbPolygons == 6 ) isClassicElem = addHexa(); + else if ( nbNodes == 4 && nbPolygons == 4 ) isClassicElem = addTetra(); + else if ( nbNodes == 6 && nbPolygons == 5 ) isClassicElem = addPenta(); + else if ( nbNodes == 5 && nbPolygons == 5 ) isClassicElem = addPyra (); + if ( !isClassicElem ) + { + for ( size_t iF = 0; iF < _polygons.size(); ++iF ) + { + const size_t nbLinks = _polygons[ iF ]._links.size(); + if ( nbLinks < 3 ) continue; + _volumeDefs._quantities.push_back( nbLinks ); + _volumeDefs._names.push_back( _polygons[ iF ]._name ); + for ( size_t iL = 0; iL < nbLinks; ++iL ) + _volumeDefs._nodes.push_back( _polygons[ iF ]._links[ iL ].FirstNode() ); + } + } + _volumeDefs._solidID = solid->ID(); + _volumeDefs._size = volSize; + + return !_volumeDefs._nodes.empty(); +} + +template +void computeHexa(Type& hex) +{ + if ( hex ) + hex->computeElements(); +} + +//================================================================================ +/*! + * \brief Create elements in the mesh + */ +int Hexahedron::MakeElements(SMESH_MesherHelper& helper, + const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, + const int numOfThreads ) +{ + SMESHDS_Mesh* mesh = helper.GetMeshDS(); + + CellsAroundLink c( _grid, 0 ); + const size_t nbGridCells = c._nbCells[0] * c._nbCells[1] * c._nbCells[2]; + vector< Hexahedron* > allHexa( nbGridCells, 0 ); + int nbIntHex = 0; + + // set intersection nodes from GridLine's to links of allHexa + int i,j,k, cellIndex, iLink; + for ( int iDir = 0; iDir < 3; ++iDir ) + { + // loop on GridLine's parallel to iDir + LineIndexer lineInd = _grid->GetLineIndexer( iDir ); + CellsAroundLink fourCells( _grid, iDir ); + for ( ; lineInd.More(); ++lineInd ) + { + GridLine& line = _grid->_lines[ iDir ][ lineInd.LineIndex() ]; + multiset< F_IntersectPoint >::const_iterator ip = line._intPoints.begin(); + for ( ; ip != line._intPoints.end(); ++ip ) + { + // if ( !ip->_node ) continue; // intersection at a grid node + lineInd.SetIndexOnLine( ip->_indexOnLine ); + fourCells.Init( lineInd.I(), lineInd.J(), lineInd.K() ); + for ( int iL = 0; iL < 4; ++iL ) // loop on 4 cells sharing a link + { + if ( !fourCells.GetCell( iL, i,j,k, cellIndex, iLink )) + continue; + Hexahedron *& hex = allHexa[ cellIndex ]; + if ( !hex) + { + hex = new Hexahedron( *this, i, j, k, cellIndex ); + ++nbIntHex; + } + hex->_hexLinks[iLink]._fIntPoints.push_back( &(*ip) ); + hex->_nbFaceIntNodes += bool( ip->_node ); + } + } + } + } + + // implement geom edges into the mesh + addEdges( helper, allHexa, edge2faceIDsMap ); + + // add not split hexahedra to the mesh + int nbAdded = 0; + TGeomID solidIDs[20]; + vector< Hexahedron* > intHexa; intHexa.reserve( nbIntHex ); + vector< const SMDS_MeshElement* > boundaryVolumes; boundaryVolumes.reserve( nbIntHex * 1.1 ); + + for ( size_t i = 0; i < allHexa.size(); ++i ) + { + // initialize this by not cut allHexa[ i ] + Hexahedron * & hex = allHexa[ i ]; + if ( hex ) // split hexahedron + { + intHexa.push_back( hex ); + if ( hex->_nbFaceIntNodes > 0 || + hex->_eIntPoints.size() > 0 || + hex->getSolids( solidIDs ) > 1 ) + continue; // treat intersected hex later in parallel + this->init( hex->_i, hex->_j, hex->_k ); + } + else + { + this->init( i ); // == init(i,j,k) + } + if (( _nbCornerNodes == 8 ) && + ( _nbBndNodes < _nbCornerNodes || !isInHole() )) + { + // order of _hexNodes is defined by enum SMESH_Block::TShapeID + SMDS_MeshElement* el = + mesh->AddVolume( _hexNodes[0].Node(), _hexNodes[2].Node(), + _hexNodes[3].Node(), _hexNodes[1].Node(), + _hexNodes[4].Node(), _hexNodes[6].Node(), + _hexNodes[7].Node(), _hexNodes[5].Node() ); + TGeomID solidID = 0; + if ( _nbBndNodes < _nbCornerNodes ) + { + for ( int iN = 0; iN < 8 && !solidID; ++iN ) + if ( !_hexNodes[iN]._intPoint ) // no intersection + solidID = _hexNodes[iN].Node()->GetShapeID(); + } + else + { + getSolids( solidIDs ); + solidID = solidIDs[0]; + } + mesh->SetMeshElementOnShape( el, solidID ); + ++nbAdded; + if ( hex ) + intHexa.pop_back(); + if ( _grid->_toCreateFaces && _nbBndNodes >= 3 ) + { + boundaryVolumes.push_back( el ); + el->setIsMarked( true ); + } + } + else if ( _nbCornerNodes > 3 && !hex ) + { + // all intersections of hex with geometry are at grid nodes + hex = new Hexahedron( *this, _i, _j, _k, i ); + intHexa.push_back( hex ); + } + } + + // compute definitions of volumes resulted from hexadron intersection +#ifdef WITH_TBB + parallel_for(intHexa.begin(), intHexa.end(), computeHexa, numOfThreads ); +#else + for ( size_t i = 0; i < intHexa.size(); ++i ) + if ( Hexahedron * hex = intHexa[ i ] ) + hex->computeElements(); +#endif + + // simplify polyhedrons + if ( _grid->IsToRemoveExcessEntities() ) + { + for ( size_t i = 0; i < intHexa.size(); ++i ) + if ( Hexahedron * hex = intHexa[ i ] ) + hex->removeExcessSideDivision( allHexa ); + + for ( size_t i = 0; i < intHexa.size(); ++i ) + if ( Hexahedron * hex = intHexa[ i ] ) + hex->removeExcessNodes( allHexa ); + } + + // add volumes + for ( size_t i = 0; i < intHexa.size(); ++i ) + if ( Hexahedron * hex = intHexa[ i ] ) + nbAdded += hex->addVolumes( helper ); + + // fill boundaryVolumes with volumes neighboring too small skipped volumes + if ( _grid->_toCreateFaces ) + { + for ( size_t i = 0; i < intHexa.size(); ++i ) + if ( Hexahedron * hex = intHexa[ i ] ) + hex->getBoundaryElems( boundaryVolumes ); + } + + // merge nodes on outer sub-shapes with pre-existing ones + TopTools_DataMapIteratorOfDataMapOfShapeInteger s2nIt( _grid->_geometry._shape2NbNodes ); + for ( ; s2nIt.More(); s2nIt.Next() ) + if ( s2nIt.Value() > 0 ) + if ( SMESHDS_SubMesh* sm = mesh->MeshElements( s2nIt.Key() )) + { + TIDSortedNodeSet smNodes( SMDS_MeshElement::iterator( sm->GetNodes() ), + SMDS_MeshElement::iterator() ); + SMESH_MeshEditor::TListOfListOfNodes equalNodes; + SMESH_MeshEditor editor( helper.GetMesh() ); + editor.FindCoincidentNodes( smNodes, 10 * _grid->_tol, equalNodes, + /*SeparateCornersAndMedium =*/ false); + if ((int) equalNodes.size() <= s2nIt.Value() ) + editor.MergeNodes( equalNodes ); + } + + // create boundary mesh faces + addFaces( helper, boundaryVolumes ); + + // create mesh edges + addSegments( helper, edge2faceIDsMap ); + + for ( size_t i = 0; i < allHexa.size(); ++i ) + if ( allHexa[ i ] ) + delete allHexa[ i ]; + + return nbAdded; +} + +//================================================================================ +/*! + * \brief Implements geom edges into the mesh + */ +void Hexahedron::addEdges(SMESH_MesherHelper& helper, + vector< Hexahedron* >& hexes, + const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap) +{ + if ( edge2faceIDsMap.empty() ) return; + + // Prepare planes for intersecting with EDGEs + GridPlanes pln[3]; + { + for ( int iDirZ = 0; iDirZ < 3; ++iDirZ ) // iDirZ gives normal direction to planes + { + GridPlanes& planes = pln[ iDirZ ]; + int iDirX = ( iDirZ + 1 ) % 3; + int iDirY = ( iDirZ + 2 ) % 3; + planes._zNorm = ( _grid->_axes[ iDirX ] ^ _grid->_axes[ iDirY ] ).Normalized(); + planes._zProjs.resize ( _grid->_coords[ iDirZ ].size() ); + planes._zProjs [0] = 0; + const double zFactor = _grid->_axes[ iDirZ ] * planes._zNorm; + const vector< double > & u = _grid->_coords[ iDirZ ]; + for ( size_t i = 1; i < planes._zProjs.size(); ++i ) + { + planes._zProjs [i] = zFactor * ( u[i] - u[0] ); + } + } + } + const double deflection = _grid->_minCellSize / 20.; + const double tol = _grid->_tol; + E_IntersectPoint ip; + + TColStd_MapOfInteger intEdgeIDs; // IDs of not shared INTERNAL EDGES + + // Intersect EDGEs with the planes + map< TGeomID, vector< TGeomID > >::const_iterator e2fIt = edge2faceIDsMap.begin(); + for ( ; e2fIt != edge2faceIDsMap.end(); ++e2fIt ) + { + const TGeomID edgeID = e2fIt->first; + const TopoDS_Edge & E = TopoDS::Edge( _grid->Shape( edgeID )); + BRepAdaptor_Curve curve( E ); + TopoDS_Vertex v1 = helper.IthVertex( 0, E, false ); + TopoDS_Vertex v2 = helper.IthVertex( 1, E, false ); + + ip._faceIDs = e2fIt->second; + ip._shapeID = edgeID; + + bool isInternal = ( ip._faceIDs.size() == 1 && _grid->IsInternal( edgeID )); + if ( isInternal ) + { + intEdgeIDs.Add( edgeID ); + intEdgeIDs.Add( _grid->ShapeID( v1 )); + intEdgeIDs.Add( _grid->ShapeID( v2 )); + } + + // discretize the EDGE + GCPnts_UniformDeflection discret( curve, deflection, true ); + if ( !discret.IsDone() || discret.NbPoints() < 2 ) + continue; + + // perform intersection + E_IntersectPoint* eip, *vip = 0; + for ( int iDirZ = 0; iDirZ < 3; ++iDirZ ) + { + GridPlanes& planes = pln[ iDirZ ]; + int iDirX = ( iDirZ + 1 ) % 3; + int iDirY = ( iDirZ + 2 ) % 3; + double xLen = _grid->_coords[ iDirX ].back() - _grid->_coords[ iDirX ][0]; + double yLen = _grid->_coords[ iDirY ].back() - _grid->_coords[ iDirY ][0]; + double zLen = _grid->_coords[ iDirZ ].back() - _grid->_coords[ iDirZ ][0]; + int dIJK[3], d000[3] = { 0,0,0 }; + double o[3] = { _grid->_coords[0][0], + _grid->_coords[1][0], + _grid->_coords[2][0] }; + + // locate the 1st point of a segment within the grid + gp_XYZ p1 = discret.Value( 1 ).XYZ(); + double u1 = discret.Parameter( 1 ); + double zProj1 = planes._zNorm * ( p1 - _grid->_origin ); + + _grid->ComputeUVW( p1, ip._uvw ); + int iX1 = int(( ip._uvw[iDirX] - o[iDirX]) / xLen * (_grid->_coords[ iDirX ].size() - 1)); + int iY1 = int(( ip._uvw[iDirY] - o[iDirY]) / yLen * (_grid->_coords[ iDirY ].size() - 1)); + int iZ1 = int(( ip._uvw[iDirZ] - o[iDirZ]) / zLen * (_grid->_coords[ iDirZ ].size() - 1)); + locateValue( iX1, ip._uvw[iDirX], _grid->_coords[ iDirX ], dIJK[ iDirX ], tol ); + locateValue( iY1, ip._uvw[iDirY], _grid->_coords[ iDirY ], dIJK[ iDirY ], tol ); + locateValue( iZ1, ip._uvw[iDirZ], _grid->_coords[ iDirZ ], dIJK[ iDirZ ], tol ); + + int ijk[3]; // grid index where a segment intersects a plane + ijk[ iDirX ] = iX1; + ijk[ iDirY ] = iY1; + ijk[ iDirZ ] = iZ1; + + // add the 1st vertex point to a hexahedron + if ( iDirZ == 0 ) + { + ip._point = p1; + ip._shapeID = _grid->ShapeID( v1 ); + vip = _grid->Add( ip ); + _grid->UpdateFacesOfVertex( *vip, v1 ); + if ( isInternal ) + vip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + if ( !addIntersection( vip, hexes, ijk, d000 )) + _grid->Remove( vip ); + ip._shapeID = edgeID; + } + for ( int iP = 2; iP <= discret.NbPoints(); ++iP ) + { + // locate the 2nd point of a segment within the grid + gp_XYZ p2 = discret.Value( iP ).XYZ(); + double u2 = discret.Parameter( iP ); + double zProj2 = planes._zNorm * ( p2 - _grid->_origin ); + int iZ2 = iZ1; + if ( Abs( zProj2 - zProj1 ) > std::numeric_limits::min() ) + { + locateValue( iZ2, zProj2, planes._zProjs, dIJK[ iDirZ ], tol ); + + // treat intersections with planes between 2 end points of a segment + int dZ = ( iZ1 <= iZ2 ) ? +1 : -1; + int iZ = iZ1 + ( iZ1 < iZ2 ); + for ( int i = 0, nb = Abs( iZ1 - iZ2 ); i < nb; ++i, iZ += dZ ) + { + ip._point = findIntPoint( u1, zProj1, u2, zProj2, + planes._zProjs[ iZ ], + curve, planes._zNorm, _grid->_origin ); + _grid->ComputeUVW( ip._point.XYZ(), ip._uvw ); + locateValue( ijk[iDirX], ip._uvw[iDirX], _grid->_coords[iDirX], dIJK[iDirX], tol ); + locateValue( ijk[iDirY], ip._uvw[iDirY], _grid->_coords[iDirY], dIJK[iDirY], tol ); + ijk[ iDirZ ] = iZ; + + // add ip to hex "above" the plane + eip = _grid->Add( ip ); + if ( isInternal ) + eip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + dIJK[ iDirZ ] = 0; + bool added = addIntersection( eip, hexes, ijk, dIJK); + + // add ip to hex "below" the plane + ijk[ iDirZ ] = iZ-1; + if ( !addIntersection( eip, hexes, ijk, dIJK ) && + !added ) + _grid->Remove( eip ); + } + } + iZ1 = iZ2; + p1 = p2; + u1 = u2; + zProj1 = zProj2; + } + // add the 2nd vertex point to a hexahedron + if ( iDirZ == 0 ) + { + ip._point = p1; + ip._shapeID = _grid->ShapeID( v2 ); + _grid->ComputeUVW( p1, ip._uvw ); + locateValue( ijk[iDirX], ip._uvw[iDirX], _grid->_coords[iDirX], dIJK[iDirX], tol ); + locateValue( ijk[iDirY], ip._uvw[iDirY], _grid->_coords[iDirY], dIJK[iDirY], tol ); + ijk[ iDirZ ] = iZ1; + bool sameV = ( v1.IsSame( v2 )); + if ( !sameV ) + { + vip = _grid->Add( ip ); + _grid->UpdateFacesOfVertex( *vip, v2 ); + if ( isInternal ) + vip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + } + if ( !addIntersection( vip, hexes, ijk, d000 ) && !sameV ) + _grid->Remove( vip ); + ip._shapeID = edgeID; + } + } // loop on 3 grid directions + } // loop on EDGEs + + + if ( intEdgeIDs.Size() > 0 ) + cutByExtendedInternal( hexes, intEdgeIDs ); + + return; +} + +//================================================================================ +/*! + * \brief Fully cut hexes that are partially cut by INTERNAL FACE. + * Cut them by extended INTERNAL FACE. + */ +void Hexahedron::cutByExtendedInternal( std::vector< Hexahedron* >& hexes, + const TColStd_MapOfInteger& intEdgeIDs ) +{ + IntAna_IntConicQuad intersection; + SMESHDS_Mesh* meshDS = _grid->_helper->GetMeshDS(); + const double tol2 = _grid->_tol * _grid->_tol; + + for ( size_t iH = 0; iH < hexes.size(); ++iH ) + { + Hexahedron* hex = hexes[ iH ]; + if ( !hex || hex->_eIntPoints.size() < 2 ) + continue; + if ( !intEdgeIDs.Contains( hex->_eIntPoints.back()->_shapeID )) + continue; + + // get 3 points on INTERNAL FACE to construct a cutting plane + gp_Pnt p1 = hex->_eIntPoints[0]->_point; + gp_Pnt p2 = hex->_eIntPoints[1]->_point; + gp_Pnt p3 = hex->mostDistantInternalPnt( iH, p1, p2 ); + + gp_Vec norm = gp_Vec( p1, p2 ) ^ gp_Vec( p1, p3 ); + gp_Pln pln; + try { + pln = gp_Pln( p1, norm ); + } + catch(...) + { + continue; + } + + TGeomID intFaceID = hex->_eIntPoints.back()->_faceIDs.front(); // FACE being "extended" + TGeomID solidID = _grid->GetSolid( intFaceID )->ID(); + + // cut links by the plane + //bool isCut = false; + for ( int iLink = 0; iLink < 12; ++iLink ) + { + _Link& link = hex->_hexLinks[ iLink ]; + if ( !link._fIntPoints.empty() ) + { + // if ( link._fIntPoints[0]->_faceIDs.back() == _grid->PseudoIntExtFaceID() ) + // isCut = true; + continue; // already cut link + } + if ( !link._nodes[0]->Node() || + !link._nodes[1]->Node() ) + continue; // outside link + + if ( link._nodes[0]->IsOnFace( intFaceID )) + { + if ( link._nodes[0]->_intPoint->_faceIDs.back() != _grid->PseudoIntExtFaceID() ) + if ( p1.SquareDistance( link._nodes[0]->Point() ) < tol2 || + p2.SquareDistance( link._nodes[0]->Point() ) < tol2 ) + link._nodes[0]->_intPoint->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + continue; // link is cut by FACE being "extended" + } + if ( link._nodes[1]->IsOnFace( intFaceID )) + { + if ( link._nodes[1]->_intPoint->_faceIDs.back() != _grid->PseudoIntExtFaceID() ) + if ( p1.SquareDistance( link._nodes[1]->Point() ) < tol2 || + p2.SquareDistance( link._nodes[1]->Point() ) < tol2 ) + link._nodes[1]->_intPoint->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + continue; // link is cut by FACE being "extended" + } + gp_Pnt p4 = link._nodes[0]->Point(); + gp_Pnt p5 = link._nodes[1]->Point(); + gp_Lin line( p4, gp_Vec( p4, p5 )); + + intersection.Perform( line, pln ); + if ( !intersection.IsDone() || + intersection.IsInQuadric() || + intersection.IsParallel() || + intersection.NbPoints() < 1 ) + continue; + + double u = intersection.ParamOnConic(1); + if ( u + _grid->_tol < 0 ) + continue; + int iDir = iLink / 4; + int index = (&hex->_i)[iDir]; + double linkLen = _grid->_coords[iDir][index+1] - _grid->_coords[iDir][index]; + if ( u - _grid->_tol > linkLen ) + continue; + + if ( u < _grid->_tol || + u > linkLen - _grid->_tol ) // intersection at grid node + { + int i = ! ( u < _grid->_tol ); // [0,1] + int iN = link._nodes[ i ] - hex->_hexNodes; // [0-7] + + const F_IntersectPoint * & ip = _grid->_gridIntP[ hex->_origNodeInd + + _grid->_nodeShift[iN] ]; + if ( !ip ) + { + ip = _grid->_extIntPool.getNew(); + ip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + //ip->_transition = Trans_INTERNAL; + } + else if ( ip->_faceIDs.back() != _grid->PseudoIntExtFaceID() ) + { + ip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + } + hex->_nbFaceIntNodes++; + //isCut = true; + } + else + { + const gp_Pnt& p = intersection.Point( 1 ); + F_IntersectPoint* ip = _grid->_extIntPool.getNew(); + ip->_node = meshDS->AddNode( p.X(), p.Y(), p.Z() ); + ip->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + ip->_transition = Trans_INTERNAL; + meshDS->SetNodeInVolume( ip->_node, solidID ); + + CellsAroundLink fourCells( _grid, iDir ); + fourCells.Init( hex->_i, hex->_j, hex->_k, iLink ); + int i,j,k, cellIndex; + for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing the link + { + if ( !fourCells.GetCell( iC, i,j,k, cellIndex, iLink )) + continue; + Hexahedron * h = hexes[ cellIndex ]; + if ( !h ) + h = hexes[ cellIndex ] = new Hexahedron( *this, i, j, k, cellIndex ); + h->_hexLinks[iLink]._fIntPoints.push_back( ip ); + h->_nbFaceIntNodes++; + //isCut = true; + } + } + } + + // if ( isCut ) + // for ( size_t i = 0; i < hex->_eIntPoints.size(); ++i ) + // { + // if ( _grid->IsInternal( hex->_eIntPoints[i]->_shapeID ) && + // ! hex->_eIntPoints[i]->IsOnFace( _grid->PseudoIntExtFaceID() )) + // hex->_eIntPoints[i]->_faceIDs.push_back( _grid->PseudoIntExtFaceID() ); + // } + continue; + + } // loop on all hexes + return; +} + +//================================================================================ +/*! + * \brief Return intersection point on INTERNAL FACE most distant from given ones + */ +gp_Pnt Hexahedron::mostDistantInternalPnt( int hexIndex, const gp_Pnt& p1, const gp_Pnt& p2 ) +{ + gp_Pnt resultPnt = p1; + + double maxDist2 = 0; + for ( int iLink = 0; iLink < 12; ++iLink ) // check links + { + _Link& link = _hexLinks[ iLink ]; + for ( size_t i = 0; i < link._fIntPoints.size(); ++i ) + if ( _grid->PseudoIntExtFaceID() != link._fIntPoints[i]->_faceIDs[0] && + _grid->IsInternal( link._fIntPoints[i]->_faceIDs[0] ) && + link._fIntPoints[i]->_node ) + { + gp_Pnt p = SMESH_NodeXYZ( link._fIntPoints[i]->_node ); + double d = p1.SquareDistance( p ); + if ( d > maxDist2 ) + { + resultPnt = p; + maxDist2 = d; + } + else + { + d = p2.SquareDistance( p ); + if ( d > maxDist2 ) + { + resultPnt = p; + maxDist2 = d; + } + } + } + } + setIJK( hexIndex ); + _origNodeInd = _grid->NodeIndex( _i,_j,_k ); + + for ( size_t iN = 0; iN < 8; ++iN ) // check corners + { + _hexNodes[iN]._node = _grid->_nodes [ _origNodeInd + _grid->_nodeShift[iN] ]; + _hexNodes[iN]._intPoint = _grid->_gridIntP[ _origNodeInd + _grid->_nodeShift[iN] ]; + if ( _hexNodes[iN]._intPoint ) + for ( size_t iF = 0; iF < _hexNodes[iN]._intPoint->_faceIDs.size(); ++iF ) + { + if ( _grid->IsInternal( _hexNodes[iN]._intPoint->_faceIDs[iF])) + { + gp_Pnt p = SMESH_NodeXYZ( _hexNodes[iN]._node ); + double d = p1.SquareDistance( p ); + if ( d > maxDist2 ) + { + resultPnt = p; + maxDist2 = d; + } + else + { + d = p2.SquareDistance( p ); + if ( d > maxDist2 ) + { + resultPnt = p; + maxDist2 = d; + } + } + } + } + } + if ( maxDist2 < _grid->_tol * _grid->_tol ) + return p1; + + return resultPnt; +} + +//================================================================================ +/*! + * \brief Finds intersection of a curve with a plane + * \param [in] u1 - parameter of one curve point + * \param [in] proj1 - projection of the curve point to the plane normal + * \param [in] u2 - parameter of another curve point + * \param [in] proj2 - projection of the other curve point to the plane normal + * \param [in] proj - projection of a point where the curve intersects the plane + * \param [in] curve - the curve + * \param [in] axis - the plane normal + * \param [in] origin - the plane origin + * \return gp_Pnt - the found intersection point + */ +gp_Pnt Hexahedron::findIntPoint( double u1, double proj1, + double u2, double proj2, + double proj, + BRepAdaptor_Curve& curve, + const gp_XYZ& axis, + const gp_XYZ& origin) +{ + double r = (( proj - proj1 ) / ( proj2 - proj1 )); + double u = u1 * ( 1 - r ) + u2 * r; + gp_Pnt p = curve.Value( u ); + double newProj = axis * ( p.XYZ() - origin ); + if ( Abs( proj - newProj ) > _grid->_tol / 10. ) + { + if ( r > 0.5 ) + return findIntPoint( u2, proj2, u, newProj, proj, curve, axis, origin ); + else + return findIntPoint( u1, proj2, u, newProj, proj, curve, axis, origin ); + } + return p; +} + +//================================================================================ +/*! + * \brief Returns indices of a hexahedron sub-entities holding a point + * \param [in] ip - intersection point + * \param [out] facets - 0-3 facets holding a point + * \param [out] sub - index of a vertex or an edge holding a point + * \return int - number of facets holding a point + */ +int Hexahedron::getEntity( const E_IntersectPoint* ip, int* facets, int& sub ) +{ + enum { X = 1, Y = 2, Z = 4 }; // == 001, 010, 100 + int nbFacets = 0; + int vertex = 0, edgeMask = 0; + + if ( Abs( _grid->_coords[0][ _i ] - ip->_uvw[0] ) < _grid->_tol ) { + facets[ nbFacets++ ] = SMESH_Block::ID_F0yz; + edgeMask |= X; + } + else if ( Abs( _grid->_coords[0][ _i+1 ] - ip->_uvw[0] ) < _grid->_tol ) { + facets[ nbFacets++ ] = SMESH_Block::ID_F1yz; + vertex |= X; + edgeMask |= X; + } + if ( Abs( _grid->_coords[1][ _j ] - ip->_uvw[1] ) < _grid->_tol ) { + facets[ nbFacets++ ] = SMESH_Block::ID_Fx0z; + edgeMask |= Y; + } + else if ( Abs( _grid->_coords[1][ _j+1 ] - ip->_uvw[1] ) < _grid->_tol ) { + facets[ nbFacets++ ] = SMESH_Block::ID_Fx1z; + vertex |= Y; + edgeMask |= Y; + } + if ( Abs( _grid->_coords[2][ _k ] - ip->_uvw[2] ) < _grid->_tol ) { + facets[ nbFacets++ ] = SMESH_Block::ID_Fxy0; + edgeMask |= Z; + } + else if ( Abs( _grid->_coords[2][ _k+1 ] - ip->_uvw[2] ) < _grid->_tol ) { + facets[ nbFacets++ ] = SMESH_Block::ID_Fxy1; + vertex |= Z; + edgeMask |= Z; + } + + switch ( nbFacets ) + { + case 0: sub = 0; break; + case 1: sub = facets[0]; break; + case 2: { + const int edge [3][8] = { + { SMESH_Block::ID_E00z, SMESH_Block::ID_E10z, + SMESH_Block::ID_E01z, SMESH_Block::ID_E11z }, + { SMESH_Block::ID_E0y0, SMESH_Block::ID_E1y0, 0, 0, + SMESH_Block::ID_E0y1, SMESH_Block::ID_E1y1 }, + { SMESH_Block::ID_Ex00, 0, SMESH_Block::ID_Ex10, 0, + SMESH_Block::ID_Ex01, 0, SMESH_Block::ID_Ex11 } + }; + switch ( edgeMask ) { + case X | Y: sub = edge[ 0 ][ vertex ]; break; + case X | Z: sub = edge[ 1 ][ vertex ]; break; + default: sub = edge[ 2 ][ vertex ]; + } + break; + } + //case 3: + default: + sub = vertex + SMESH_Block::ID_FirstV; + } + + return nbFacets; +} +//================================================================================ +/*! + * \brief Adds intersection with an EDGE + */ +bool Hexahedron::addIntersection( const E_IntersectPoint* ip, + vector< Hexahedron* >& hexes, + int ijk[], int dIJK[] ) +{ + bool added = false; + + size_t hexIndex[4] = { + _grid->CellIndex( ijk[0], ijk[1], ijk[2] ), + dIJK[0] ? _grid->CellIndex( ijk[0]+dIJK[0], ijk[1], ijk[2] ) : -1, + dIJK[1] ? _grid->CellIndex( ijk[0], ijk[1]+dIJK[1], ijk[2] ) : -1, + dIJK[2] ? _grid->CellIndex( ijk[0], ijk[1], ijk[2]+dIJK[2] ) : -1 + }; + for ( int i = 0; i < 4; ++i ) + { + if ( hexIndex[i] < hexes.size() && hexes[ hexIndex[i] ] ) + { + Hexahedron* h = hexes[ hexIndex[i] ]; + h->_eIntPoints.reserve(2); + h->_eIntPoints.push_back( ip ); + added = true; + + // check if ip is really inside the hex + if (SALOME::VerbosityActivated() && h->isOutParam( ip->_uvw )) + throw SALOME_Exception("ip outside a hex"); + } + } + return added; +} +//================================================================================ +/*! + * \brief Check if a hexahedron facet lies on a FACE + * Also return true if the facet does not interfere with any FACE + */ +bool Hexahedron::isQuadOnFace( const size_t iQuad ) +{ + _Face& quad = _hexQuads[ iQuad ] ; + + int nbGridNodesInt = 0; // nb FACE intersections at grid nodes + int nbNoGeomNodes = 0; + for ( int iE = 0; iE < 4; ++iE ) + { + nbNoGeomNodes = ( !quad._links[ iE ].FirstNode()->_intPoint && + quad._links[ iE ].NbResultLinks() == 1 ); + nbGridNodesInt += + ( quad._links[ iE ].FirstNode()->_intPoint && + quad._links[ iE ].NbResultLinks() == 1 && + quad._links[ iE ].ResultLink( 0 ).FirstNode() == quad._links[ iE ].FirstNode() && + quad._links[ iE ].ResultLink( 0 ).LastNode() == quad._links[ iE ].LastNode() ); + } + if ( nbNoGeomNodes == 4 ) + return true; + + if ( nbGridNodesInt == 4 ) // all quad nodes are at FACE intersection + { + size_t iEmin = 0, minNbFaces = 1000; + for ( int iE = 0; iE < 4; ++iE ) // look for a node with min nb FACEs + { + size_t nbFaces = quad._links[ iE ].FirstNode()->faces().size(); + if ( minNbFaces > nbFaces ) + { + iEmin = iE; + minNbFaces = nbFaces; + } + } + // check if there is a FACE passing through all 4 nodes + for ( const TGeomID& faceID : quad._links[ iEmin ].FirstNode()->faces() ) + { + bool allNodesAtFace = true; + for ( size_t iE = 0; iE < 4 && allNodesAtFace; ++iE ) + allNodesAtFace = ( iE == iEmin || + quad._links[ iE ].FirstNode()->IsOnFace( faceID )); + if ( allNodesAtFace ) // quad if on faceID + return true; + } + } + return false; +} +//================================================================================ +/*! + * \brief Finds nodes at a path from one node to another via intersections with EDGEs + */ +bool Hexahedron::findChain( _Node* n1, + _Node* n2, + _Face& quad, + vector<_Node*>& chn ) +{ + chn.clear(); + chn.push_back( n1 ); + for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) + if ( !quad._eIntNodes[ iP ]->IsUsedInFace( &quad ) && + n1->IsLinked( quad._eIntNodes[ iP ]->_intPoint ) && + n2->IsLinked( quad._eIntNodes[ iP ]->_intPoint )) + { + chn.push_back( quad._eIntNodes[ iP ]); + chn.push_back( n2 ); + quad._eIntNodes[ iP ]->_usedInFace = &quad; + return true; + } + bool found; + do + { + found = false; + for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) + if ( !quad._eIntNodes[ iP ]->IsUsedInFace( &quad ) && + chn.back()->IsLinked( quad._eIntNodes[ iP ]->_intPoint )) + { + chn.push_back( quad._eIntNodes[ iP ]); + found = ( quad._eIntNodes[ iP ]->_usedInFace = &quad ); + break; + } + } while ( found && ! chn.back()->IsLinked( n2->_intPoint ) ); + + if ( chn.back() != n2 && chn.back()->IsLinked( n2->_intPoint )) + chn.push_back( n2 ); + + return chn.size() > 1; +} +//================================================================================ +/*! + * \brief Try to heal a polygon whose ends are not connected + */ +bool Hexahedron::closePolygon( _Face* polygon, vector<_Node*>& chainNodes ) const +{ + int i = -1, nbLinks = polygon->_links.size(); + if ( nbLinks < 3 ) + return false; + vector< _OrientedLink > newLinks; + // find a node lying on the same FACE as the last one + _Node* node = polygon->_links.back().LastNode(); + TGeomID avoidFace = node->IsLinked( polygon->_links.back().FirstNode()->_intPoint ); + for ( i = nbLinks - 2; i >= 0; --i ) + if ( node->IsLinked( polygon->_links[i].FirstNode()->_intPoint, avoidFace )) + break; + if ( i >= 0 ) + { + for ( ; i < nbLinks; ++i ) + newLinks.push_back( polygon->_links[i] ); + } + else + { + // find a node lying on the same FACE as the first one + node = polygon->_links[0].FirstNode(); + avoidFace = node->IsLinked( polygon->_links[0].LastNode()->_intPoint ); + for ( i = 1; i < nbLinks; ++i ) + if ( node->IsLinked( polygon->_links[i].LastNode()->_intPoint, avoidFace )) + break; + if ( i < nbLinks ) + for ( nbLinks = i + 1, i = 0; i < nbLinks; ++i ) + newLinks.push_back( polygon->_links[i] ); + } + if ( newLinks.size() > 1 ) + { + polygon->_links.swap( newLinks ); + chainNodes.clear(); + chainNodes.push_back( polygon->_links.back().LastNode() ); + chainNodes.push_back( polygon->_links[0].FirstNode() ); + return true; + } + return false; +} +//================================================================================ +/*! + * \brief Finds nodes on the same EDGE as the first node of avoidSplit. + * + * This function is for + * 1) a case where an EDGE lies on a quad which lies on a FACE + * so that a part of quad in ON and another part is IN + * 2) INTERNAL FACE passes through the 1st node of avoidSplit + */ +bool Hexahedron::findChainOnEdge( const vector< _OrientedLink >& splits, + const _OrientedLink& prevSplit, + const _OrientedLink& avoidSplit, + const std::set< TGeomID > & concaveFaces, + size_t & iS, + _Face& quad, + vector<_Node*>& chn ) +{ + _Node* pn1 = prevSplit.FirstNode(); + _Node* pn2 = prevSplit.LastNode(); // pn2 is on EDGE, if not on INTERNAL FACE + _Node* an3 = avoidSplit.LastNode(); + TGeomID avoidFace = pn1->IsLinked( pn2->_intPoint ); // FACE under the quad + if ( avoidFace < 1 && pn1->_intPoint ) + return false; + + chn.clear(); + + if ( !quad._eIntNodes.empty() ) // connect pn2 with EDGE intersections + { + chn.push_back( pn2 ); + bool found; + do + { + found = false; + for ( size_t iP = 0; iP < quad._eIntNodes.size(); ++iP ) + if (( !quad._eIntNodes[ iP ]->IsUsedInFace( &quad )) && + ( chn.back()->IsLinked( quad._eIntNodes[ iP ]->_intPoint, avoidFace )) && + ( !avoidFace || quad._eIntNodes[ iP ]->IsOnFace( avoidFace ))) + { + chn.push_back( quad._eIntNodes[ iP ]); + found = ( quad._eIntNodes[ iP ]->_usedInFace = &quad ); + break; + } + } while ( found ); + pn2 = chn.back(); + } + + _Node* n = 0, *stopNode = avoidSplit.LastNode(); + + if ( pn2 == prevSplit.LastNode() && // pn2 is at avoidSplit.FirstNode() + !isCorner( stopNode )) // stopNode is in the middle of a _hexLinks + { + // move stopNode to a _hexNodes + for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle + for ( size_t iL = 0; iL < quad._links[ iE ].NbResultLinks(); ++iL ) + { + const _Link* sideSplit = & quad._links[ iE ]._link->_splits[ iL ]; + if ( sideSplit == avoidSplit._link ) + { + if ( quad._links[ iE ].LastNode()->Node() ) + stopNode = quad._links[ iE ].LastNode(); + iE = 4; + break; + } + } + } + + // connect pn2 (probably new, at _eIntNodes) with a split + + int i, iConn = 0; + size_t nbCommon; + TGeomID commonFaces[20]; + _Node* nPrev = nullptr; + for ( i = splits.size()-1; i >= 0; --i ) + { + if ( !splits[i] ) + continue; + + bool stop = false; + for ( int is1st = 0; is1st < 2; ++is1st ) + { + _Node* nConn = is1st ? splits[i].FirstNode() : splits[i].LastNode(); + if ( nConn == nPrev ) + { + if ( n == nConn ) + iConn = i; + continue; + } + nPrev = nConn; + if (( stop = ( nConn == stopNode ))) + break; + // find a FACE connecting nConn with pn2 but not with an3 + if (( nConn != pn1 ) && + ( nConn->_intPoint && !nConn->_intPoint->_faceIDs.empty() ) && + ( nbCommon = nConn->GetCommonFaces( pn2->_intPoint, commonFaces ))) + { + bool a3Coonect = true; + for ( size_t iF = 0; iF < nbCommon && a3Coonect; ++iF ) + a3Coonect = an3->IsOnFace( commonFaces[ iF ]) || concaveFaces.count( commonFaces[ iF ]); + if ( a3Coonect ) + continue; + + if ( !n ) + { + n = nConn; + iConn = i + !is1st; + } + if ( nbCommon > 1 ) // nConn is linked with pn2 by an EDGE + { + n = nConn; + iConn = i + !is1st; + stop = true; + break; + } + } + } + if ( stop ) + { + i = iConn; + break; + } + } + + if ( n && n != stopNode ) + { + if ( chn.empty() ) + chn.push_back( pn2 ); + chn.push_back( n ); + iS = i-1; + return true; + } + else if ( !chn.empty() && chn.back()->_isInternalFlags ) + { + // INTERNAL FACE partially cuts the quad + for ( int ip = chn.size() - 2; ip >= 0; --ip ) + chn.push_back( chn[ ip ]); + return true; + } + return false; +} +//================================================================================ +/*! + * \brief Checks transition at the ginen intersection node of a link + */ +bool Hexahedron::isOutPoint( _Link& link, int iP, + SMESH_MesherHelper& helper, const Solid* solid ) const +{ + bool isOut = false; + + if ( link._fIntNodes[iP]->faces().size() == 1 && + _grid->IsInternal( link._fIntNodes[iP]->face(0) )) + return false; + + const bool moreIntPoints = ( iP+1 < (int) link._fIntNodes.size() ); + + // get 2 _Node's + _Node* n1 = link._fIntNodes[ iP ]; + if ( !n1->Node() ) + n1 = link._nodes[0]; + _Node* n2 = moreIntPoints ? link._fIntNodes[ iP+1 ] : 0; + if ( !n2 || !n2->Node() ) + n2 = link._nodes[1]; + if ( !n2->Node() ) + return true; + + // get all FACEs under n1 and n2 + set< TGeomID > faceIDs; + if ( moreIntPoints ) faceIDs.insert( link._fIntNodes[iP+1]->faces().begin(), + link._fIntNodes[iP+1]->faces().end() ); + if ( n2->_intPoint ) faceIDs.insert( n2->_intPoint->_faceIDs.begin(), + n2->_intPoint->_faceIDs.end() ); + if ( faceIDs.empty() ) + return false; // n2 is inside + if ( n1->_intPoint ) faceIDs.insert( n1->_intPoint->_faceIDs.begin(), + n1->_intPoint->_faceIDs.end() ); + faceIDs.insert( link._fIntNodes[iP]->faces().begin(), + link._fIntNodes[iP]->faces().end() ); + + // get a point between 2 nodes + gp_Pnt p1 = n1->Point(); + gp_Pnt p2 = n2->Point(); + gp_Pnt pOnLink = 0.8 * p1.XYZ() + 0.2 * p2.XYZ(); + + TopLoc_Location loc; + + set< TGeomID >::iterator faceID = faceIDs.begin(); + for ( ; faceID != faceIDs.end(); ++faceID ) + { + // project pOnLink on a FACE + if ( *faceID < 1 || !solid->Contains( *faceID )) continue; + const TopoDS_Face& face = TopoDS::Face( _grid->Shape( *faceID )); + GeomAPI_ProjectPointOnSurf& proj = helper.GetProjector( face, loc, 0.1*_grid->_tol ); + gp_Pnt testPnt = pOnLink.Transformed( loc.Transformation().Inverted() ); + proj.Perform( testPnt ); + if ( proj.IsDone() && proj.NbPoints() > 0 ) + { + Standard_Real u,v; + proj.LowerDistanceParameters( u,v ); + + if ( proj.LowerDistance() <= 0.1 * _grid->_tol ) + { + isOut = false; + } + else + { + // find isOut by normals + gp_Dir normal; + if ( GeomLib::NormEstim( BRep_Tool::Surface( face, loc ), + gp_Pnt2d( u,v ), + 0.1*_grid->_tol, + normal ) < 3 ) + { + if ( solid->Orientation( face ) == TopAbs_REVERSED ) + normal.Reverse(); + gp_Vec v( proj.NearestPoint(), testPnt ); + isOut = ( v * normal > 0 ); + } + } + if ( !isOut ) + { + // classify a projection + if ( !n1->IsOnFace( *faceID ) || !n2->IsOnFace( *faceID )) + { + BRepTopAdaptor_FClass2d cls( face, Precision::Confusion() ); + TopAbs_State state = cls.Perform( gp_Pnt2d( u,v )); + if ( state == TopAbs_OUT ) + { + isOut = true; + continue; + } + } + return false; + } + } + } + return isOut; +} +//================================================================================ +/*! + * \brief Sort nodes on a FACE + */ +void Hexahedron::sortVertexNodes(vector<_Node*>& nodes, _Node* curNode, TGeomID faceID) +{ + if ( nodes.size() > 20 ) return; + + // get shapes under nodes + TGeomID nShapeIds[20], *nShapeIdsEnd = &nShapeIds[0] + nodes.size(); + for ( size_t i = 0; i < nodes.size(); ++i ) + if ( !( nShapeIds[i] = nodes[i]->ShapeID() )) + return; + + // get shapes of the FACE + const TopoDS_Face& face = TopoDS::Face( _grid->Shape( faceID )); + list< TopoDS_Edge > edges; + list< int > nbEdges; + int nbW = SMESH_Block::GetOrderedEdges (face, edges, nbEdges); + if ( nbW > 1 ) { + // select a WIRE - remove EDGEs of irrelevant WIREs from edges + list< TopoDS_Edge >::iterator e = edges.begin(), eEnd = e; + list< int >::iterator nE = nbEdges.begin(); + for ( ; nbW > 0; ++nE, --nbW ) + { + std::advance( eEnd, *nE ); + for ( ; e != eEnd; ++e ) + for ( int i = 0; i < 2; ++i ) + { + TGeomID id = i==0 ? + _grid->ShapeID( *e ) : + _grid->ShapeID( SMESH_MesherHelper::IthVertex( 0, *e )); + if (( id > 0 ) && + ( std::find( &nShapeIds[0], nShapeIdsEnd, id ) != nShapeIdsEnd )) + { + edges.erase( eEnd, edges.end() ); // remove rest wires + e = eEnd = edges.end(); + --e; + nbW = 0; + break; + } + } + if ( nbW > 0 ) + edges.erase( edges.begin(), eEnd ); // remove a current irrelevant wire + } + } + // rotate edges to have the first one at least partially out of the hexa + list< TopoDS_Edge >::iterator e = edges.begin(), eMidOut = edges.end(); + for ( ; e != edges.end(); ++e ) + { + if ( !_grid->ShapeID( *e )) + continue; + bool isOut = false; + gp_Pnt p; + double uvw[3], f,l; + for ( int i = 0; i < 2 && !isOut; ++i ) + { + if ( i == 0 ) + { + TopoDS_Vertex v = SMESH_MesherHelper::IthVertex( 0, *e ); + p = BRep_Tool::Pnt( v ); + } + else if ( eMidOut == edges.end() ) + { + TopLoc_Location loc; + Handle(Geom_Curve) c = BRep_Tool::Curve( *e, loc, f, l); + if ( c.IsNull() ) break; + p = c->Value( 0.5 * ( f + l )).Transformed( loc ); + } + else + { + continue; + } + + _grid->ComputeUVW( p.XYZ(), uvw ); + if ( isOutParam( uvw )) + { + if ( i == 0 ) + isOut = true; + else + eMidOut = e; + } + } + if ( isOut ) + break; + } + if ( e != edges.end() ) + edges.splice( edges.end(), edges, edges.begin(), e ); + else if ( eMidOut != edges.end() ) + edges.splice( edges.end(), edges, edges.begin(), eMidOut ); + + // sort nodes according to the order of edges + _Node* orderNodes [20]; + //TGeomID orderShapeIDs[20]; + size_t nbN = 0; + TGeomID id, *pID = 0; + for ( e = edges.begin(); e != edges.end(); ++e ) + { + if (( id = _grid->ShapeID( SMESH_MesherHelper::IthVertex( 0, *e ))) && + (( pID = std::find( &nShapeIds[0], nShapeIdsEnd, id )) != nShapeIdsEnd )) + { + //orderShapeIDs[ nbN ] = id; + orderNodes [ nbN++ ] = nodes[ pID - &nShapeIds[0] ]; + *pID = -1; + } + if (( id = _grid->ShapeID( *e )) && + (( pID = std::find( &nShapeIds[0], nShapeIdsEnd, id )) != nShapeIdsEnd )) + { + //orderShapeIDs[ nbN ] = id; + orderNodes [ nbN++ ] = nodes[ pID - &nShapeIds[0] ]; + *pID = -1; + } + } + if ( nbN != nodes.size() ) + return; + + bool reverse = ( orderNodes[0 ]->Point().SquareDistance( curNode->Point() ) > + orderNodes[nbN-1]->Point().SquareDistance( curNode->Point() )); + + for ( size_t i = 0; i < nodes.size(); ++i ) + nodes[ i ] = orderNodes[ reverse ? nbN-1-i : i ]; +} + +//================================================================================ +/*! + * \brief Adds computed elements to the mesh + */ +int Hexahedron::addVolumes( SMESH_MesherHelper& helper ) +{ + F_IntersectPoint noIntPnt; + const bool toCheckNodePos = _grid->IsToCheckNodePos(); + const bool useQuanta = _grid->_toUseQuanta; + + int nbAdded = 0; + // add elements resulted from hexahedron intersection + for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) + { + vector< const SMDS_MeshNode* > nodes( volDef->_nodes.size() ); + for ( size_t iN = 0; iN < nodes.size(); ++iN ) + { + if ( !( nodes[iN] = volDef->_nodes[iN].Node() )) + { + if ( const E_IntersectPoint* eip = volDef->_nodes[iN].EdgeIntPnt() ) + { + nodes[iN] = volDef->_nodes[iN]._intPoint->_node = + helper.AddNode( eip->_point.X(), + eip->_point.Y(), + eip->_point.Z() ); + if ( _grid->ShapeType( eip->_shapeID ) == TopAbs_VERTEX ) + helper.GetMeshDS()->SetNodeOnVertex( nodes[iN], eip->_shapeID ); + else + helper.GetMeshDS()->SetNodeOnEdge( nodes[iN], eip->_shapeID ); + } + else + throw SALOME_Exception("Bug: no node at intersection point"); + } + else if ( volDef->_nodes[iN]._intPoint && + volDef->_nodes[iN]._intPoint->_node == volDef->_nodes[iN]._node ) + { + // Update position of node at EDGE intersection; + // see comment to _Node::Add( E_IntersectPoint ) + SMESHDS_Mesh* mesh = helper.GetMeshDS(); + TGeomID shapeID = volDef->_nodes[iN].EdgeIntPnt()->_shapeID; + mesh->UnSetNodeOnShape( nodes[iN] ); + if ( _grid->ShapeType( shapeID ) == TopAbs_VERTEX ) + mesh->SetNodeOnVertex( nodes[iN], shapeID ); + else + mesh->SetNodeOnEdge( nodes[iN], shapeID ); + } + else if ( toCheckNodePos && + !nodes[iN]->isMarked() && + _grid->ShapeType( nodes[iN]->GetShapeID() ) == TopAbs_FACE ) + { + _grid->SetOnShape( nodes[iN], noIntPnt, /*v=*/nullptr,/*unset=*/true ); + nodes[iN]->setIsMarked( true ); + } + } // loop to get nodes + + const SMDS_MeshElement* v = 0; + if ( !volDef->_quantities.empty() ) + { + if ( !useQuanta ) + { + // split polyhedrons of with disjoint volumes + std::vector> splitQuantities; + std::vector > splitNodes; + if ( checkPolyhedronValidity( volDef, splitQuantities, splitNodes ) == 1 ) + v = addPolyhedronToMesh( volDef, helper, nodes, volDef->_quantities ); + else + { + int counter = -1; + for (size_t id = 0; id < splitQuantities.size(); id++) + { + v = addPolyhedronToMesh( volDef, helper, splitNodes[ id ], splitQuantities[ id ] ); + if ( id < splitQuantities.size()-1 ) + volDef->_brotherVolume.push_back( v ); + counter++; + } + nbAdded += counter; + } + } + else + { + const double quanta = _grid->_quanta; + double polyVol = volDef->_size; + double hexaVolume = _sideLength[0] * _sideLength[1] * _sideLength[2]; + if ( hexaVolume > 0.0 && polyVol/hexaVolume >= quanta /*set the volume if the relation is satisfied*/) + v = helper.AddVolume( _hexNodes[0].BoundaryNode(), _hexNodes[2].BoundaryNode(), + _hexNodes[3].BoundaryNode(), _hexNodes[1].BoundaryNode(), + _hexNodes[4].BoundaryNode(), _hexNodes[6].BoundaryNode(), + _hexNodes[7].BoundaryNode(), _hexNodes[5].BoundaryNode() ); + + } + } + else + { + switch ( nodes.size() ) + { + case 8: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3], + nodes[4],nodes[5],nodes[6],nodes[7] ); + break; + case 4: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3] ); + break; + case 6: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3],nodes[4],nodes[5] ); + break; + case 5: v = helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3],nodes[4] ); + break; + } + } + volDef->_volume = v; + nbAdded += bool( v ); + + } // loop on _volumeDefs chain + + // avoid creating overlapping volumes (bos #24052) + if ( nbAdded > 1 ) + { + double sumSize = 0, maxSize = 0; + _volumeDef* maxSizeDef = nullptr; + for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) + { + if ( !volDef->_volume ) + continue; + sumSize += volDef->_size; + if ( volDef->_size > maxSize ) + { + maxSize = volDef->_size; + maxSizeDef = volDef; + } + } + if ( sumSize > _sideLength[0] * _sideLength[1] * _sideLength[2] * 1.05 ) + { + for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) + if ( volDef != maxSizeDef && volDef->_volume ) + { + helper.GetMeshDS()->RemoveFreeElement( volDef->_volume, /*sm=*/nullptr, + /*fromGroups=*/false ); + volDef->_volume = nullptr; + //volDef->_nodes.clear(); + --nbAdded; + } + } + } + + for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) + { + if ( volDef->_volume ) + { + helper.GetMeshDS()->SetMeshElementOnShape( volDef->_volume, volDef->_solidID ); + for (auto broVol : volDef->_brotherVolume ) + { + helper.GetMeshDS()->SetMeshElementOnShape( broVol, volDef->_solidID ); + } + } + } + + return nbAdded; +} +//================================================================================ +/*! + * \brief Return true if the element is in a hole + * \remark consider a cell to be in a hole if all links in any direction + * comes OUT of geometry + */ +bool Hexahedron::isInHole() const +{ + if ( !_vIntNodes.empty() ) + return false; + + const size_t ijk[3] = { _i, _j, _k }; + F_IntersectPoint curIntPnt; + + // consider a cell to be in a hole if all links in any direction + // comes OUT of geometry + for ( int iDir = 0; iDir < 3; ++iDir ) + { + const vector& coords = _grid->_coords[ iDir ]; + LineIndexer li = _grid->GetLineIndexer( iDir ); + li.SetIJK( _i,_j,_k ); + size_t lineIndex[4] = { li.LineIndex (), + li.LineIndex10(), + li.LineIndex01(), + li.LineIndex11() }; + bool allLinksOut = true, hasLinks = false; + for ( int iL = 0; iL < 4 && allLinksOut; ++iL ) // loop on 4 links parallel to iDir + { + const _Link& link = _hexLinks[ iL + 4*iDir ]; + // check transition of the first node of a link + const F_IntersectPoint* firstIntPnt = 0; + if ( link._nodes[0]->Node() ) // 1st node is a hexa corner + { + curIntPnt._paramOnLine = coords[ ijk[ iDir ]] - coords[0] + _grid->_tol; + const GridLine& line = _grid->_lines[ iDir ][ lineIndex[ iL ]]; + if ( !line._intPoints.empty() ) + { + multiset< F_IntersectPoint >::const_iterator ip = + line._intPoints.upper_bound( curIntPnt ); + --ip; + firstIntPnt = &(*ip); + } + } + else if ( !link._fIntPoints.empty() ) + { + firstIntPnt = link._fIntPoints[0]; + } + + if ( firstIntPnt ) + { + hasLinks = true; + allLinksOut = ( firstIntPnt->_transition == Trans_OUT && + !_grid->IsShared( firstIntPnt->_faceIDs[0] )); + } + } + if ( hasLinks && allLinksOut ) + return true; + } + return false; +} + +//================================================================================ +/*! + * \brief Check if a polyherdon has an edge lying on EDGE shared by strange FACE + * that will be meshed by other algo + */ +bool Hexahedron::hasStrangeEdge() const +{ + if ( _eIntPoints.size() < 2 ) + return false; + + TopTools_MapOfShape edges; + for ( size_t i = 0; i < _eIntPoints.size(); ++i ) + { + if ( !_grid->IsStrangeEdge( _eIntPoints[i]->_shapeID )) + continue; + const TopoDS_Shape& s = _grid->Shape( _eIntPoints[i]->_shapeID ); + if ( s.ShapeType() == TopAbs_EDGE ) + { + if ( ! edges.Add( s )) + return true; // an EDGE encounters twice + } + else + { + PShapeIteratorPtr edgeIt = _grid->_helper->GetAncestors( s, + *_grid->_helper->GetMesh(), + TopAbs_EDGE ); + while ( const TopoDS_Shape* edge = edgeIt->next() ) + if ( ! edges.Add( *edge )) + return true; // an EDGE encounters twice + } + } + return false; +} + +//================================================================================ +/*! + * \brief Return true if a polyhedron passes _sizeThreshold criterion + */ +bool Hexahedron::checkPolyhedronSize( bool cutByInternalFace, double & volume) const +{ + volume = 0; + + if ( cutByInternalFace && !_grid->_toUseThresholdForInternalFaces ) + { + // check if any polygon fully lies on shared/internal FACEs + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + { + const _Face& polygon = _polygons[iP]; + if ( polygon._links.empty() ) + continue; + bool allNodesInternal = true; + for ( size_t iL = 0; iL < polygon._links.size() && allNodesInternal; ++iL ) + { + _Node* n = polygon._links[ iL ].FirstNode(); + allNodesInternal = (( n->IsCutByInternal() ) || + ( n->_intPoint && _grid->IsAnyShared( n->_intPoint->_faceIDs ))); + } + if ( allNodesInternal ) + return true; + } + } + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + { + const _Face& polygon = _polygons[iP]; + if ( polygon._links.empty() ) + continue; + gp_XYZ area (0,0,0); + gp_XYZ p1 = polygon._links[ 0 ].FirstNode()->Point().XYZ(); + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + { + gp_XYZ p2 = polygon._links[ iL ].LastNode()->Point().XYZ(); + area += p1 ^ p2; + p1 = p2; + } + volume += p1 * area; + } + volume /= 6; + + if ( this->hasStrangeEdge() && volume > 1e-13 ) + return true; + + double initVolume = _sideLength[0] * _sideLength[1] * _sideLength[2]; + + return volume > initVolume / _grid->_sizeThreshold; +} + +//================================================================================ +/*! + * \brief Check that all faces in polyhedron are connected so a unique volume is defined. + * We test that it is possible to go from any node to all nodes in the polyhedron. + * The set of nodes that can be visit within then defines a unique element. + * In case more than one polyhedron is detected. The function return the set of quantities and nodes defining separates elements. + * Reference to issue #bos[38521][EDF] Generate polyhedron with separate volume. + */ +int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector>& splitQuantities, + std::vector>& splitNodes ) +{ + int mySet = 1; + std::map numberOfSets; // define set id with the number of faces associated! + if ( !volDef->_quantities.empty() ) + { + auto connectivity = volDef->_quantities; + int accum = 0; + std::vector allFaces( connectivity.size(), false ); + std::set elementSet; + allFaces[ 0 ] = true; // the first node below to the first face + size_t connectedFaces = 1; + // Start filling the set with the nodes of the first face + splitQuantities.push_back( { connectivity[ 0 ] } ); + splitNodes.push_back( { volDef->_nodes[ 0 ].Node() } ); + elementSet.insert( volDef->_nodes[ 0 ].Node()->GetID() ); + for (int n = 1; n < connectivity[ 0 ]; n++) + { + elementSet.insert( volDef->_nodes[ n ].Node()->GetID() ); + splitNodes.back().push_back( volDef->_nodes[ n ].Node() ); + } + + numberOfSets.insert( std::pair(mySet,1) ); + while ( connectedFaces != allFaces.size() ) + { + for (size_t innerId = 1; innerId < connectivity.size(); innerId++) + { + if ( innerId == 1 ) + accum = connectivity[ 0 ]; + + if ( !allFaces[ innerId ] ) + { + int faceCounter = 0; + for (int n = 0; n < connectivity[ innerId ]; n++) + { + int nodeId = volDef->_nodes[ accum + n ].Node()->GetID(); + if ( elementSet.count( nodeId ) != 0 ) + faceCounter++; + } + if ( faceCounter >= 2 ) // found coincidences nodes + { + for (int n = 0; n < connectivity[ innerId ]; n++) + { + int nodeId = volDef->_nodes[ accum + n ].Node()->GetID(); + // insert new nodes so other faces can be identified as belowing to the element + splitNodes.back().push_back( volDef->_nodes[ accum + n ].Node() ); + elementSet.insert( nodeId ); + } + allFaces[ innerId ] = true; + splitQuantities.back().push_back( connectivity[ innerId ] ); + numberOfSets[ mySet ]++; + connectedFaces++; + innerId = 0; // to restart searching! + } + } + accum += connectivity[ innerId ]; + } + + if ( connectedFaces != allFaces.size() ) + { + // empty the set, and fill it with nodes of a unvisited face! + elementSet.clear(); + accum = connectivity[ 0 ]; + for (size_t faceId = 1; faceId < connectivity.size(); faceId++) + { + if ( !allFaces[ faceId ] ) + { + splitNodes.push_back( { volDef->_nodes[ accum ].Node() } ); + elementSet.insert( volDef->_nodes[ accum ].Node()->GetID() ); + for (int n = 1; n < connectivity[ faceId ]; n++) + { + elementSet.insert( volDef->_nodes[ accum + n ].Node()->GetID() ); + splitNodes.back().push_back( volDef->_nodes[ accum + n ].Node() ); + } + + splitQuantities.push_back( { connectivity[ faceId ] } ); + allFaces[ faceId ] = true; + connectedFaces++; + break; + } + accum += connectivity[ faceId ]; + } + mySet++; + numberOfSets.insert( std::pair(mySet,1) ); + } + } + + if ( numberOfSets.size() > 1 ) + { + bool allMoreThan2Faces = true; + for( auto k : numberOfSets ) + { + if ( k.second <= 2 ) + allMoreThan2Faces &= false; + } + + if ( allMoreThan2Faces ) + { + // The separate objects are suspect to be closed + return numberOfSets.size(); + } + else + { + // Have to index the last face nodes to the final set + // contrary case return as it were a valid polyhedron for backward compatibility + return 1; + } + } + } + return numberOfSets.size(); +} + + +//================================================================================ +/*! + * \brief add original or separated polyhedrons to the mesh + */ +const SMDS_MeshElement* Hexahedron::addPolyhedronToMesh( _volumeDef* volDef, SMESH_MesherHelper& helper, const std::vector& nodes, + const std::vector& quantities ) +{ + const SMDS_MeshElement* v = helper.AddPolyhedralVolume( nodes, quantities ); + + volDef->_size = SMDS_VolumeTool( v ).GetSize(); + if ( volDef->_size < 0 ) // invalid polyhedron + { + if ( ! SMESH_MeshEditor( helper.GetMesh() ).Reorient( v ) || // try to fix + SMDS_VolumeTool( v ).GetSize() < 0 ) + { + helper.GetMeshDS()->RemoveFreeElement( v, /*sm=*/nullptr, /*fromGroups=*/false ); + v = nullptr; + //_hasTooSmall = true; + + if (SALOME::VerbosityActivated()) + { + std::cout << "Remove INVALID polyhedron, _cellID = " << _cellID + << " ijk = ( " << _i << " " << _j << " " << _k << " ) " + << " solid " << volDef->_solidID << std::endl; + } + } + } + return v; +} + +//================================================================================ +/*! + * \brief Tries to create a hexahedron + */ +bool Hexahedron::addHexa() +{ + int nbQuad = 0, iQuad = -1; + for ( size_t i = 0; i < _polygons.size(); ++i ) + { + if ( _polygons[i]._links.empty() ) + continue; + if ( _polygons[i]._links.size() != 4 ) + return false; + ++nbQuad; + if ( iQuad < 0 ) + iQuad = i; + } + if ( nbQuad != 6 ) + return false; + + _Node* nodes[8]; + int nbN = 0; + for ( int iL = 0; iL < 4; ++iL ) + { + // a base node + nodes[iL] = _polygons[iQuad]._links[iL].FirstNode(); + ++nbN; + + // find a top node above the base node + _Link* link = _polygons[iQuad]._links[iL]._link; + if ( !link->_faces[0] || !link->_faces[1] ) + return debugDumpLink( link ); + // a quadrangle sharing with _polygons[iQuad] + _Face* quad = link->_faces[ bool( link->_faces[0] == & _polygons[iQuad] )]; + for ( int i = 0; i < 4; ++i ) + if ( quad->_links[i]._link == link ) + { + // 1st node of a link opposite to in + nodes[iL+4] = quad->_links[(i+2)%4].FirstNode(); + ++nbN; + break; + } + } + if ( nbN == 8 ) + _volumeDefs.Set( &nodes[0], 8 ); + + return nbN == 8; +} +//================================================================================ +/*! + * \brief Tries to create a tetrahedron + */ +bool Hexahedron::addTetra() +{ + int iTria = -1; + for ( size_t i = 0; i < _polygons.size() && iTria < 0; ++i ) + if ( _polygons[i]._links.size() == 3 ) + iTria = i; + if ( iTria < 0 ) + return false; + + _Node* nodes[4]; + nodes[0] = _polygons[iTria]._links[0].FirstNode(); + nodes[1] = _polygons[iTria]._links[1].FirstNode(); + nodes[2] = _polygons[iTria]._links[2].FirstNode(); + + _Link* link = _polygons[iTria]._links[0]._link; + if ( !link->_faces[0] || !link->_faces[1] ) + return debugDumpLink( link ); + + // a triangle sharing with _polygons[0] + _Face* tria = link->_faces[ bool( link->_faces[0] == & _polygons[iTria] )]; + for ( int i = 0; i < 3; ++i ) + if ( tria->_links[i]._link == link ) + { + nodes[3] = tria->_links[(i+1)%3].LastNode(); + _volumeDefs.Set( &nodes[0], 4 ); + return true; + } + + return false; +} +//================================================================================ +/*! + * \brief Tries to create a pentahedron + */ +bool Hexahedron::addPenta() +{ + // find a base triangular face + int iTri = -1; + for ( int iF = 0; iF < 5 && iTri < 0; ++iF ) + if ( _polygons[ iF ]._links.size() == 3 ) + iTri = iF; + if ( iTri < 0 ) return false; + + // find nodes + _Node* nodes[6]; + int nbN = 0; + for ( int iL = 0; iL < 3; ++iL ) + { + // a base node + nodes[iL] = _polygons[ iTri ]._links[iL].FirstNode(); + ++nbN; + + // find a top node above the base node + _Link* link = _polygons[ iTri ]._links[iL]._link; + if ( !link->_faces[0] || !link->_faces[1] ) + return debugDumpLink( link ); + // a quadrangle sharing with a base triangle + _Face* quad = link->_faces[ bool( link->_faces[0] == & _polygons[ iTri ] )]; + if ( quad->_links.size() != 4 ) return false; + for ( int i = 0; i < 4; ++i ) + if ( quad->_links[i]._link == link ) + { + // 1st node of a link opposite to in + nodes[iL+3] = quad->_links[(i+2)%4].FirstNode(); + ++nbN; + break; + } + } + if ( nbN == 6 ) + _volumeDefs.Set( &nodes[0], 6 ); + + return ( nbN == 6 ); +} +//================================================================================ +/*! + * \brief Tries to create a pyramid + */ +bool Hexahedron::addPyra() +{ + // find a base quadrangle + int iQuad = -1; + for ( int iF = 0; iF < 5 && iQuad < 0; ++iF ) + if ( _polygons[ iF ]._links.size() == 4 ) + iQuad = iF; + if ( iQuad < 0 ) return false; + + // find nodes + _Node* nodes[5]; + nodes[0] = _polygons[iQuad]._links[0].FirstNode(); + nodes[1] = _polygons[iQuad]._links[1].FirstNode(); + nodes[2] = _polygons[iQuad]._links[2].FirstNode(); + nodes[3] = _polygons[iQuad]._links[3].FirstNode(); + + _Link* link = _polygons[iQuad]._links[0]._link; + if ( !link->_faces[0] || !link->_faces[1] ) + return debugDumpLink( link ); + + // a triangle sharing with a base quadrangle + _Face* tria = link->_faces[ bool( link->_faces[0] == & _polygons[ iQuad ] )]; + if ( tria->_links.size() != 3 ) return false; + for ( int i = 0; i < 3; ++i ) + if ( tria->_links[i]._link == link ) + { + nodes[4] = tria->_links[(i+1)%3].LastNode(); + _volumeDefs.Set( &nodes[0], 5 ); + return true; + } + + return false; +} +//================================================================================ +/*! + * \brief Return true if there are _eIntPoints at EDGEs forming a concave corner + */ +bool Hexahedron::hasEdgesAround( const ConcaveFace* cf ) const +{ + int nbEdges = 0; + ConcaveFace foundGeomHolder; + for ( const E_IntersectPoint* ip : _eIntPoints ) + { + if ( cf->HasEdge( ip->_shapeID )) + { + if ( ++nbEdges == 2 ) + return true; + foundGeomHolder.SetEdge( ip->_shapeID ); + } + else if ( ip->_faceIDs.size() >= 3 ) + { + const TGeomID & vID = ip->_shapeID; + if ( cf->HasVertex( vID ) && !foundGeomHolder.HasVertex( vID )) + { + if ( ++nbEdges == 2 ) + return true; + foundGeomHolder.SetVertex( vID ); + } + } + } + + for ( const _Node& hexNode: _hexNodes ) + { + if ( !hexNode._node || !hexNode._intPoint ) + continue; + const B_IntersectPoint* ip = hexNode._intPoint; + if ( ip->_faceIDs.size() == 2 ) // EDGE + { + TGeomID edgeID = hexNode._node->GetShapeID(); + if ( cf->HasEdge( edgeID ) && !foundGeomHolder.HasEdge( edgeID )) + { + foundGeomHolder.SetEdge( edgeID ); + if ( ++nbEdges == 2 ) + return true; + } + } + else if ( ip->_faceIDs.size() >= 3 ) // VERTEX + { + TGeomID vID = hexNode._node->GetShapeID(); + if ( cf->HasVertex( vID ) && !foundGeomHolder.HasVertex( vID )) + { + if ( ++nbEdges == 2 ) + return true; + foundGeomHolder.SetVertex( vID ); + } + } + } + + return false; +} +//================================================================================ +/*! + * \brief Dump a link and return \c false + */ +bool Hexahedron::debugDumpLink( Hexahedron::_Link* link ) +{ + if (SALOME::VerbosityActivated()) + { + gp_Pnt p1 = link->_nodes[0]->Point(), p2 = link->_nodes[1]->Point(); + cout << "BUG: not shared link. IKJ = ( "<< _i << " " << _j << " " << _k << " )" << endl + << "n1 (" << p1.X() << ", "<< p1.Y() << ", "<< p1.Z() << " )" << endl + << "n2 (" << p2.X() << ", "<< p2.Y() << ", "<< p2.Z() << " )" << endl; + } + + return false; +} +//================================================================================ +/*! + * \brief Classify a point by grid parameters + */ +bool Hexahedron::isOutParam(const double uvw[3]) const +{ + return (( _grid->_coords[0][ _i ] - _grid->_tol > uvw[0] ) || + ( _grid->_coords[0][ _i+1 ] + _grid->_tol < uvw[0] ) || + ( _grid->_coords[1][ _j ] - _grid->_tol > uvw[1] ) || + ( _grid->_coords[1][ _j+1 ] + _grid->_tol < uvw[1] ) || + ( _grid->_coords[2][ _k ] - _grid->_tol > uvw[2] ) || + ( _grid->_coords[2][ _k+1 ] + _grid->_tol < uvw[2] )); +} + +//================================================================================ +/*! + * \brief Create mesh faces at free facets + */ +void Hexahedron::addFaces( SMESH_MesherHelper& helper, + const vector< const SMDS_MeshElement* > & boundaryVolumes ) +{ + if ( !_grid->_toCreateFaces ) + return; + + SMDS_VolumeTool vTool; + vector bndFacets; + SMESH_MeshEditor editor( helper.GetMesh() ); + SMESH_MeshEditor::ElemFeatures face( SMDSAbs_Face ); + SMESHDS_Mesh* meshDS = helper.GetMeshDS(); + + bool isQuantaSet = _grid->_toUseQuanta; + // check if there are internal or shared FACEs + bool hasInternal = ( !_grid->_geometry.IsOneSolid() || + _grid->_geometry._soleSolid.HasInternalFaces() ); + + for ( size_t iV = 0; iV < boundaryVolumes.size(); ++iV ) + { + if ( !vTool.Set( boundaryVolumes[ iV ])) + continue; + TGeomID solidID = vTool.Element()->GetShapeID(); + Solid * solid = _grid->GetOneOfSolids( solidID ); + + // find boundary facets + bndFacets.clear(); + for ( int iF = 0, n = vTool.NbFaces(); iF < n; iF++ ) + { + const SMDS_MeshElement* otherVol; + bool isBoundary = isQuantaSet ? vTool.IsFreeFaceCheckAllNodes( iF, &otherVol ) : vTool.IsFreeFace( iF, &otherVol ); + if ( isBoundary ) + { + bndFacets.push_back( iF ); + } + else if (( hasInternal ) || + ( !_grid->IsSolid( otherVol->GetShapeID() ))) + { + // check if all nodes are on internal/shared FACEs + isBoundary = true; + const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF ); + const size_t nbFaceNodes = vTool.NbFaceNodes ( iF ); + for ( size_t iN = 0; iN < nbFaceNodes && isBoundary; ++iN ) + isBoundary = ( nn[ iN ]->GetShapeID() != solidID ); + if ( isBoundary ) + bndFacets.push_back( -( iF+1 )); // !!! minus ==> to check the FACE + } + } + if ( bndFacets.empty() ) + continue; + + // create faces + if ( !vTool.IsPoly() ) + vTool.SetExternalNormal(); + for ( size_t i = 0; i < bndFacets.size(); ++i ) // loop on boundary facets + { + const bool isBoundary = ( bndFacets[i] >= 0 ); + const int iFacet = isBoundary ? bndFacets[i] : -bndFacets[i]-1; + const SMDS_MeshNode** nn = vTool.GetFaceNodes( iFacet ); + const size_t nbFaceNodes = vTool.NbFaceNodes ( iFacet ); + face.myNodes.assign( nn, nn + nbFaceNodes ); + + TGeomID faceID = 0; + const SMDS_MeshElement* existFace = 0, *newFace = 0; + + if (( existFace = meshDS->FindElement( face.myNodes, SMDSAbs_Face ))) + { + if ( existFace->isMarked() ) + continue; // created by this method + faceID = existFace->GetShapeID(); + } + else + { + // look for a supporting FACE + for ( size_t iN = 0; iN < nbFaceNodes && !faceID; ++iN ) // look for a node on FACE + { + if ( nn[ iN ]->GetPosition()->GetDim() == 2 ) + faceID = nn[ iN ]->GetShapeID(); + } + if ( faceID == 0 && !isQuantaSet /*if quanta is set boundary nodes at boundary does not coincide with any geometrical face */ ) + faceID = findCommonFace( face.myNodes, helper.GetMesh() ); + + bool toCheckFace = faceID && (( !isBoundary ) || + ( hasInternal && _grid->_toUseThresholdForInternalFaces )); + if ( toCheckFace ) // check if all nodes are on the found FACE + { + SMESH_subMesh* faceSM = helper.GetMesh()->GetSubMeshContaining( faceID ); + for ( size_t iN = 0; iN < nbFaceNodes && faceID; ++iN ) + { + TGeomID subID = nn[ iN ]->GetShapeID(); + if ( subID != faceID && !faceSM->DependsOn( subID )) + faceID = 0; + } + // if ( !faceID && !isBoundary ) + // continue; + } + if ( !faceID && !isBoundary && !isQuantaSet ) + continue; + } + + // orient a new face according to supporting FACE orientation in shape_to_mesh + if ( !isBoundary && !solid->IsOutsideOriented( faceID )) + { + if ( existFace ) + editor.Reorient( existFace ); + else + std::reverse( face.myNodes.begin(), face.myNodes.end() ); + } + + if ( ! ( newFace = existFace )) + { + face.SetPoly( nbFaceNodes > 4 ); + newFace = editor.AddElement( face.myNodes, face ); + if ( !newFace ) + continue; + newFace->setIsMarked( true ); // to distinguish from face created in getBoundaryElems() + } + + if ( faceID && _grid->IsBoundaryFace( faceID )) // face is not shared + { + // set newFace to the found FACE provided that it fully lies on the FACE + for ( size_t iN = 0; iN < nbFaceNodes && faceID; ++iN ) + if ( nn[iN]->GetShapeID() == solidID ) + { + if ( existFace ) + meshDS->UnSetMeshElementOnShape( existFace, _grid->Shape( faceID )); + faceID = 0; + } + } + + if ( faceID && nbFaceNodes > 4 && + !_grid->IsInternal( faceID ) && + !_grid->IsShared( faceID ) && + !_grid->IsBoundaryFace( faceID )) + { + // split a polygon that will be used by other 3D algorithm + if ( !existFace ) + splitPolygon( newFace, vTool, iFacet, faceID, solidID, + face, editor, i+1 < bndFacets.size() ); + } + else + { + if ( faceID ) + meshDS->SetMeshElementOnShape( newFace, faceID ); + else + meshDS->SetMeshElementOnShape( newFace, solidID ); + } + } // loop on bndFacets + } // loop on boundaryVolumes + + + // Orient coherently mesh faces on INTERNAL FACEs + + if ( hasInternal ) + { + TopExp_Explorer faceExp( _grid->_geometry._mainShape, TopAbs_FACE ); + for ( ; faceExp.More(); faceExp.Next() ) + { + if ( faceExp.Current().Orientation() != TopAbs_INTERNAL ) + continue; + + SMESHDS_SubMesh* sm = meshDS->MeshElements( faceExp.Current() ); + if ( !sm ) continue; + + TIDSortedElemSet facesToOrient; + for ( SMDS_ElemIteratorPtr fIt = sm->GetElements(); fIt->more(); ) + facesToOrient.insert( facesToOrient.end(), fIt->next() ); + if ( facesToOrient.size() < 2 ) + continue; + + gp_Dir direction(1,0,0); + TIDSortedElemSet refFaces; + editor.Reorient2D( facesToOrient, direction, refFaces, /*allowNonManifold=*/true ); + } + } + return; +} + +//================================================================================ +/*! + * \brief Create mesh segments. + */ +void Hexahedron::addSegments( SMESH_MesherHelper& helper, + const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap ) +{ + SMESHDS_Mesh* mesh = helper.GetMeshDS(); + + std::vector nodes; + std::vector elems; + map< TGeomID, vector< TGeomID > >::const_iterator e2ff = edge2faceIDsMap.begin(); + for ( ; e2ff != edge2faceIDsMap.end(); ++e2ff ) + { + const TopoDS_Edge& edge = TopoDS::Edge( _grid->Shape( e2ff->first )); + const TopoDS_Face& face = TopoDS::Face( _grid->Shape( e2ff->second[0] )); + StdMeshers_FaceSide side( face, edge, helper.GetMesh(), /*isFwd=*/true, /*skipMed=*/true ); + nodes = side.GetOrderedNodes(); + + elems.clear(); + if ( nodes.size() == 2 ) + // check that there is an element connecting two nodes + if ( !mesh->GetElementsByNodes( nodes, elems )) + continue; + + for ( size_t i = 1; i < nodes.size(); i++ ) + { + if ( mesh->FindEdge( nodes[i-1], nodes[i] )) + continue; + SMDS_MeshElement* segment = mesh->AddEdge( nodes[i-1], nodes[i] ); + mesh->SetMeshElementOnShape( segment, e2ff->first ); + } + } + return; +} + +//================================================================================ +/*! + * \brief Return created volumes and volumes that can have free facet because of + * skipped small volume. Also create mesh faces on free facets + * of adjacent not-cut volumes if the result volume is too small. + */ +void Hexahedron::getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryElems ) +{ + if ( _hasTooSmall /*|| _volumeDefs.IsEmpty()*/ ) + { + // create faces around a missing small volume + TGeomID faceID = 0; + SMESH_MeshEditor editor( _grid->_helper->GetMesh() ); + SMESH_MeshEditor::ElemFeatures polygon( SMDSAbs_Face ); + SMESHDS_Mesh* meshDS = _grid->_helper->GetMeshDS(); + std::vector adjVolumes(2); + for ( size_t iF = 0; iF < _polygons.size(); ++iF ) + { + const size_t nbLinks = _polygons[ iF ]._links.size(); + if ( nbLinks != 4 ) continue; + polygon.myNodes.resize( nbLinks ); + polygon.myNodes.back() = 0; + for ( size_t iL = 0, iN = nbLinks - 1; iL < nbLinks; ++iL, --iN ) + if ( ! ( polygon.myNodes[iN] = _polygons[ iF ]._links[ iL ].FirstNode()->Node() )) + break; + if ( !polygon.myNodes.back() ) + continue; + + meshDS->GetElementsByNodes( polygon.myNodes, adjVolumes, SMDSAbs_Volume ); + if ( adjVolumes.size() != 1 ) + continue; + if ( !adjVolumes[0]->isMarked() ) + { + boundaryElems.push_back( adjVolumes[0] ); + adjVolumes[0]->setIsMarked( true ); + } + + bool sameShape = true; + TGeomID shapeID = polygon.myNodes[0]->GetShapeID(); + for ( size_t i = 1; i < polygon.myNodes.size() && sameShape; ++i ) + sameShape = ( shapeID == polygon.myNodes[i]->GetShapeID() ); + + if ( !sameShape || !_grid->IsSolid( shapeID )) + continue; // some of shapes must be FACE + + if ( !faceID ) + { + faceID = getAnyFace(); + if ( !faceID ) + break; + if ( _grid->IsInternal( faceID ) || + _grid->IsShared( faceID ) //|| + //_grid->IsBoundaryFace( faceID ) -- commented for #19887 + ) + break; // create only if a new face will be used by other 3D algo + } + + Solid * solid = _grid->GetOneOfSolids( adjVolumes[0]->GetShapeID() ); + if ( !solid->IsOutsideOriented( faceID )) + std::reverse( polygon.myNodes.begin(), polygon.myNodes.end() ); + + //polygon.SetPoly( polygon.myNodes.size() > 4 ); + const SMDS_MeshElement* newFace = editor.AddElement( polygon.myNodes, polygon ); + meshDS->SetMeshElementOnShape( newFace, faceID ); + } + } + + // return created volumes + for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) + { + if ( volDef ->_volume && + !volDef->_volume->IsNull() && + !volDef->_volume->isMarked() ) + { + volDef->_volume->setIsMarked( true ); + boundaryElems.push_back( volDef->_volume ); + + if ( _grid->IsToCheckNodePos() ) // un-mark nodes marked in addVolumes() + for ( size_t iN = 0; iN < volDef->_nodes.size(); ++iN ) + volDef->_nodes[iN].Node()->setIsMarked( false ); + } + if ( volDef->_brotherVolume.size() > 0 ) + { + for (auto _bro : volDef->_brotherVolume ) + { + _bro->setIsMarked( true ); + boundaryElems.push_back( _bro ); + } + } + } +} + +//================================================================================ +/*! + * \brief Remove edges and nodes dividing a hexa side in the case if an adjacent + * volume also sharing the dividing edge is missing due to its small side. + * Issue #19887. + */ +//================================================================================ + +void Hexahedron::removeExcessSideDivision(const vector< Hexahedron* >& allHexa) +{ + if ( ! _volumeDefs.IsPolyhedron() ) + return; // not a polyhedron + + // look for a divided side adjacent to a small hexahedron + + int di[6] = { 0, 0, 0, 0,-1, 1 }; + int dj[6] = { 0, 0,-1, 1, 0, 0 }; + int dk[6] = {-1, 1, 0, 0, 0, 0 }; + + for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron + { + size_t neighborIndex = _grid->CellIndex( _i + di[iF], + _j + dj[iF], + _k + dk[iF] ); + if ( neighborIndex >= allHexa.size() || + !allHexa[ neighborIndex ] || + !allHexa[ neighborIndex ]->_hasTooSmall ) + continue; + + // check if a side is divided into several polygons + for ( _volumeDef* volDef = &_volumeDefs; volDef; volDef = volDef->_next ) + { + int nbPolygons = 0, nbNodes = 0; + for ( size_t i = 0; i < volDef->_names.size(); ++i ) + if ( volDef->_names[ i ] == _hexQuads[ iF ]._name ) + { + ++nbPolygons; + nbNodes += volDef->_quantities[ i ]; + } + if ( nbPolygons < 2 ) + continue; + + // construct loops from polygons + typedef _volumeDef::_linkDef TLinkDef; + std::vector< TLinkDef* > loops; + std::vector< TLinkDef > links( nbNodes ); + for ( size_t i = 0, iN = 0, iLoop = 0; iLoop < volDef->_quantities.size(); ++iLoop ) + { + size_t nbLinks = volDef->_quantities[ iLoop ]; + if ( volDef->_names[ iLoop ] != _hexQuads[ iF ]._name ) + { + iN += nbLinks; + continue; + } + loops.push_back( & links[i] ); + for ( size_t n = 0; n < nbLinks-1; ++n, ++i, ++iN ) + { + links[i].init( volDef->_nodes[iN], volDef->_nodes[iN+1], iLoop ); + links[i].setNext( &links[i+1] ); + } + links[i].init( volDef->_nodes[iN], volDef->_nodes[iN-nbLinks+1], iLoop ); + links[i].setNext( &links[i-nbLinks+1] ); + ++i; ++iN; + } + + // look for equal links in different loops and join such loops + bool loopsJoined = false; + std::set< TLinkDef > linkSet; + for ( size_t iLoop = 0; iLoop < loops.size(); ++iLoop ) + { + TLinkDef* beg = 0; + for ( TLinkDef* l = loops[ iLoop ]; l != beg; l = l->_next ) // walk around the iLoop + { + std::pair< std::set< TLinkDef >::iterator, bool > it2new = linkSet.insert( *l ); + if ( !it2new.second ) // equal found, join loops + { + const TLinkDef* equal = &(*it2new.first); + if ( equal->_loopIndex == l->_loopIndex ) + continue; // error? + + loopsJoined = true; + + for ( size_t i = iLoop - 1; i < loops.size(); --i ) + if ( loops[ i ] && loops[ i ]->_loopIndex == equal->_loopIndex ) + loops[ i ] = 0; + + // exclude l and equal and join two loops + if ( l->_prev != equal ) + l->_prev->setNext( equal->_next ); + if ( equal->_prev != l ) + equal->_prev->setNext( l->_next ); + + if ( volDef->_quantities[ l->_loopIndex ] > 0 ) + volDef->_quantities[ l->_loopIndex ] *= -1; + if ( volDef->_quantities[ equal->_loopIndex ] > 0 ) + volDef->_quantities[ equal->_loopIndex ] *= -1; + + if ( loops[ iLoop ] == l ) + loops[ iLoop ] = l->_prev->_next; + } + beg = loops[ iLoop ]; + } + } + // update volDef + if ( loopsJoined ) + { + // set unchanged polygons + std::vector< int > newQuantities; + std::vector< _volumeDef::_nodeDef > newNodes; + vector< SMESH_Block::TShapeID > newNames; + newQuantities.reserve( volDef->_quantities.size() ); + newNodes.reserve ( volDef->_nodes.size() ); + newNames.reserve ( volDef->_names.size() ); + for ( size_t i = 0, iLoop = 0; iLoop < volDef->_quantities.size(); ++iLoop ) + { + if ( volDef->_quantities[ iLoop ] < 0 ) + { + i -= volDef->_quantities[ iLoop ]; + continue; + } + newQuantities.push_back( volDef->_quantities[ iLoop ]); + newNodes.insert( newNodes.end(), + volDef->_nodes.begin() + i, + volDef->_nodes.begin() + i + newQuantities.back() ); + newNames.push_back( volDef->_names[ iLoop ]); + i += volDef->_quantities[ iLoop ]; + } + + // set joined loops + for ( size_t iLoop = 0; iLoop < loops.size(); ++iLoop ) + { + if ( !loops[ iLoop ] ) + continue; + newQuantities.push_back( 0 ); + TLinkDef* beg = 0; + for ( TLinkDef* l = loops[ iLoop ]; l != beg; l = l->_next, ++newQuantities.back() ) + { + newNodes.push_back( l->_node1 ); + beg = loops[ iLoop ]; + } + newNames.push_back( _hexQuads[ iF ]._name ); + } + volDef->_quantities.swap( newQuantities ); + volDef->_nodes.swap( newNodes ); + volDef->_names.swap( newNames ); + } + } // loop on volDef's + } // loop on hex sides + + return; +} // removeExcessSideDivision() + + +//================================================================================ +/*! + * \brief Remove nodes splitting Cartesian cell edges in the case if a node + * is used in every cells only by two polygons sharing the edge + * Issue #19887. + */ +//================================================================================ + +void Hexahedron::removeExcessNodes(vector< Hexahedron* >& allHexa) +{ + if ( ! _volumeDefs.IsPolyhedron() ) + return; // not a polyhedron + + typedef vector< _volumeDef::_nodeDef >::iterator TNodeIt; + vector< int > nodesInPoly[ 4 ]; // node index in _volumeDefs._nodes + vector< int > volDefInd [ 4 ]; // index of a _volumeDefs + Hexahedron* hexa [ 4 ]; + int i,j,k, cellIndex, iLink = 0, iCellLink; + for ( int iDir = 0; iDir < 3; ++iDir ) + { + CellsAroundLink fourCells( _grid, iDir ); + for ( int iL = 0; iL < 4; ++iL, ++iLink ) // 4 links in a direction + { + _Link& link = _hexLinks[ iLink ]; + fourCells.Init( _i, _j, _k, iLink ); + + for ( size_t iP = 0; iP < link._fIntPoints.size(); ++iP ) // loop on nodes on the link + { + bool nodeRemoved = true; + _volumeDef::_nodeDef node; node._intPoint = link._fIntPoints[iP]; + + for ( size_t i = 0, nb = _volumeDefs.size(); i < nb && nodeRemoved; ++i ) + if ( _volumeDef* vol = _volumeDefs.at( i )) + nodeRemoved = + ( std::find( vol->_nodes.begin(), vol->_nodes.end(), node ) == vol->_nodes.end() ); + if ( nodeRemoved ) + continue; // node already removed + + // check if a node encounters zero or two times in 4 cells sharing iLink + // if so, the node can be removed from the cells + bool nodeIsOnEdge = true; + int nbPolyhedraWithNode = 0; + for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing a link + { + nodesInPoly[ iC ].clear(); + volDefInd [ iC ].clear(); + hexa [ iC ] = 0; + if ( !fourCells.GetCell( iC, i,j,k, cellIndex, iCellLink )) + continue; + hexa[ iC ] = allHexa[ cellIndex ]; + if ( !hexa[ iC ]) + continue; + for ( size_t i = 0, nb = hexa[ iC ]->_volumeDefs.size(); i < nb; ++i ) + if ( _volumeDef* vol = hexa[ iC ]->_volumeDefs.at( i )) + { + for ( TNodeIt nIt = vol->_nodes.begin(); nIt != vol->_nodes.end(); ++nIt ) + { + nIt = std::find( nIt, vol->_nodes.end(), node ); + if ( nIt != vol->_nodes.end() ) + { + nodesInPoly[ iC ].push_back( std::distance( vol->_nodes.begin(), nIt )); + volDefInd [ iC ].push_back( i ); + } + else + break; + } + nbPolyhedraWithNode += ( !nodesInPoly[ iC ].empty() ); + } + if ( nodesInPoly[ iC ].size() != 0 && + nodesInPoly[ iC ].size() != 2 ) + { + nodeIsOnEdge = false; + break; + } + } // loop on 4 cells + + // remove nodes from polyhedra + if ( nbPolyhedraWithNode > 0 && nodeIsOnEdge ) + { + for ( int iC = 0; iC < 4; ++iC ) // loop on 4 cells sharing the link + { + if ( nodesInPoly[ iC ].empty() ) + continue; + for ( int i = volDefInd[ iC ].size() - 1; i >= 0; --i ) + { + _volumeDef* vol = hexa[ iC ]->_volumeDefs.at( volDefInd[ iC ][ i ]); + int nIndex = nodesInPoly[ iC ][ i ]; + // decrement _quantities + for ( size_t iQ = 0; iQ < vol->_quantities.size(); ++iQ ) + if ( nIndex < vol->_quantities[ iQ ]) + { + vol->_quantities[ iQ ]--; + break; + } + else + { + nIndex -= vol->_quantities[ iQ ]; + } + vol->_nodes.erase( vol->_nodes.begin() + nodesInPoly[ iC ][ i ]); + + if ( i == 0 && + vol->_nodes.size() == 6 * 4 && + vol->_quantities.size() == 6 ) // polyhedron becomes hexahedron? + { + bool allQuads = true; + for ( size_t iQ = 0; iQ < vol->_quantities.size() && allQuads; ++iQ ) + allQuads = ( vol->_quantities[ iQ ] == 4 ); + if ( allQuads ) + { + // set side nodes as this: bottom, top, top, ... + int iTop = 0, iBot = 0; // side indices + for ( int iS = 0; iS < 6; ++iS ) + { + if ( vol->_names[ iS ] == SMESH_Block::ID_Fxy0 ) + iBot = iS; + if ( vol->_names[ iS ] == SMESH_Block::ID_Fxy1 ) + iTop = iS; + } + if ( iBot != 0 ) + { + if ( iTop == 0 ) + { + std::copy( vol->_nodes.begin(), + vol->_nodes.begin() + 4, + vol->_nodes.begin() + 4 ); + iTop = 1; + } + std::copy( vol->_nodes.begin() + 4 * iBot, + vol->_nodes.begin() + 4 * ( iBot + 1), + vol->_nodes.begin() ); + } + if ( iTop != 1 ) + std::copy( vol->_nodes.begin() + 4 * iTop, + vol->_nodes.begin() + 4 * ( iTop + 1), + vol->_nodes.begin() + 4 ); + + std::copy( vol->_nodes.begin() + 4, + vol->_nodes.begin() + 8, + vol->_nodes.begin() + 8 ); + // set up top facet nodes by comparing their uvw with bottom nodes + E_IntersectPoint ip[8]; + for ( int iN = 0; iN < 8; ++iN ) + { + SMESH_NodeXYZ p = vol->_nodes[ iN ].Node(); + _grid->ComputeUVW( p, ip[ iN ]._uvw ); + } + const double tol2 = _grid->_tol * _grid->_tol; + for ( int iN = 0; iN < 4; ++iN ) + { + gp_Pnt2d pBot( ip[ iN ]._uvw[0], ip[ iN ]._uvw[1] ); + for ( int iT = 4; iT < 8; ++iT ) + { + gp_Pnt2d pTop( ip[ iT ]._uvw[0], ip[ iT ]._uvw[1] ); + if ( pBot.SquareDistance( pTop ) < tol2 ) + { + // vol->_nodes[ iN + 4 ]._node = ip[ iT ]._node; + // vol->_nodes[ iN + 4 ]._intPoint = 0; + vol->_nodes[ iN + 4 ] = vol->_nodes[ iT + 4 ]; + break; + } + } + } + vol->_nodes.resize( 8 ); + vol->_quantities.clear(); + //vol->_names.clear(); + } + } + } // loop on _volumeDefs + } // loop on 4 cell abound a link + } // if ( nodeIsOnEdge ) + } // loop on intersection points of a link + } // loop on 4 links of a direction + } // loop on 3 directions + + return; + +} // removeExcessNodes() + +//================================================================================ +/*! + * \brief [Issue #19913] Modify _hexLinks._splits to prevent creating overlapping volumes + */ +//================================================================================ + +void Hexahedron::preventVolumesOverlapping() +{ + // Cut off a quadrangle corner if two links sharing the corner + // are shared by same two solids, in this case each of solids gets + // a triangle for it-self. + std::vector< TGeomID > soIDs[4]; + for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron + { + _Face& quad = _hexQuads[ iF ] ; + + int iFOpposite = iF + ( iF % 2 ? -1 : 1 ); + _Face& quadOpp = _hexQuads[ iFOpposite ] ; + + int nbSides = 0, nbSidesOpp = 0; + for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle + { + nbSides += ( quad._links [ iE ].NbResultLinks() > 0 ); + nbSidesOpp += ( quadOpp._links[ iE ].NbResultLinks() > 0 ); + } + if ( nbSides < 4 || nbSidesOpp != 2 ) + continue; + + for ( int iE = 0; iE < 4; ++iE ) + { + soIDs[ iE ].clear(); + _Node* n = quad._links[ iE ].FirstNode(); + if ( n->_intPoint && n->_intPoint->_faceIDs.size() ) + soIDs[ iE ] = _grid->GetSolidIDs( n->_intPoint->_faceIDs[0] ); + } + if ((( soIDs[0].size() >= 2 ) + + ( soIDs[1].size() >= 2 ) + + ( soIDs[2].size() >= 2 ) + + ( soIDs[3].size() >= 2 ) ) < 3 ) + continue; + + bool done = false; + for ( int i = 0; i < 4; ++i ) + { + int i1 = _grid->_helper->WrapIndex( i + 1, 4 ); + int i2 = _grid->_helper->WrapIndex( i + 2, 4 ); + int i3 = _grid->_helper->WrapIndex( i + 3, 4 ); + if ( soIDs[i1].size() == 2 && soIDs[i ] != soIDs[i1] && + soIDs[i2].size() == 2 && soIDs[i1] == soIDs[i2] && + soIDs[i3].size() == 2 && soIDs[i2] == soIDs[i3] ) + { + quad._links[ i1 ]._link->_splits.clear(); + quad._links[ i2 ]._link->_splits.clear(); + done = true; + break; + } + } + if ( done ) + break; + } + return; +} // preventVolumesOverlapping() + +//================================================================================ +/*! + * \brief Set to _hexLinks a next portion of splits located on one side of INTERNAL FACEs + */ +bool Hexahedron::_SplitIterator::Next() +{ + if ( _iterationNb > 0 ) + // count used splits + for ( size_t i = 0; i < _splits.size(); ++i ) + { + if ( _splits[i]._iCheckIteration == _iterationNb ) + { + _splits[i]._isUsed = _splits[i]._checkedSplit->_faces[1]; + _nbUsed += _splits[i]._isUsed; + } + if ( !More() ) + return false; + } + + ++_iterationNb; + + bool toTestUsed = ( _nbChecked >= _splits.size() ); + if ( toTestUsed ) + { + // all splits are checked; find all not used splits + for ( size_t i = 0; i < _splits.size(); ++i ) + if ( !_splits[i].IsCheckedOrUsed( toTestUsed )) + _splits[i]._iCheckIteration = _iterationNb; + + _nbUsed = _splits.size(); // to stop iteration + } + else + { + // get any not used/checked split to start from + _freeNodes.clear(); + for ( size_t i = 0; i < _splits.size(); ++i ) + { + if ( !_splits[i].IsCheckedOrUsed( toTestUsed )) + { + _freeNodes.push_back( _splits[i]._nodes[0] ); + _freeNodes.push_back( _splits[i]._nodes[1] ); + _splits[i]._iCheckIteration = _iterationNb; + break; + } + } + // find splits connected to the start one via _freeNodes + for ( size_t iN = 0; iN < _freeNodes.size(); ++iN ) + { + for ( size_t iS = 0; iS < _splits.size(); ++iS ) + { + if ( _splits[iS].IsCheckedOrUsed( toTestUsed )) + continue; + int iN2 = -1; + if ( _freeNodes[iN] == _splits[iS]._nodes[0] ) + iN2 = 1; + else if ( _freeNodes[iN] == _splits[iS]._nodes[1] ) + iN2 = 0; + else + continue; + if ( _freeNodes[iN]->_isInternalFlags > 0 ) + { + if ( _splits[iS]._nodes[ iN2 ]->_isInternalFlags == 0 ) + continue; + if ( !_splits[iS]._nodes[ iN2 ]->IsLinked( _freeNodes[iN]->_intPoint )) + continue; + } + _splits[iS]._iCheckIteration = _iterationNb; + _freeNodes.push_back( _splits[iS]._nodes[ iN2 ]); + } + } + } + // set splits to hex links + + for ( int iL = 0; iL < 12; ++iL ) + _hexLinks[ iL ]._splits.clear(); + + _Link split; + for ( size_t i = 0; i < _splits.size(); ++i ) + { + if ( _splits[i]._iCheckIteration == _iterationNb ) + { + split._nodes[0] = _splits[i]._nodes[0]; + split._nodes[1] = _splits[i]._nodes[1]; + _Link & hexLink = _hexLinks[ _splits[i]._linkID ]; + hexLink._splits.push_back( split ); + _splits[i]._checkedSplit = & hexLink._splits.back(); + ++_nbChecked; + } + } + return More(); +} \ No newline at end of file diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx new file mode 100644 index 000000000..c87877b4d --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -0,0 +1,503 @@ +// Copyright (C) 2016-2024 CEA, EDF +// +// 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_Cartesian_3D_Hexahedron.hxx +// Module : SMESH +// Purpose: Make BodyFitting mesh algorithm more modular and testable +// + +#ifndef _SMESH_Cartesian_3D_HEXAHEDRON_HXX_ +#define _SMESH_Cartesian_3D_HEXAHEDRON_HXX_ + +// BOOST +#include + +// STD +#include +#include + +// SMESH +#include "SMESH_StdMeshers.hxx" +#include "StdMeshers_Cartesian_3D_Grid.hxx" + +using namespace std; +using namespace SMESH; +namespace +{ + // -------------------------------------------------------------------------- + /*! + * \brief Return cells sharing a link + */ + struct CellsAroundLink + { + int _iDir; + int _dInd[4][3]; + size_t _nbCells[3]; + int _i,_j,_k; + Grid* _grid; + + CellsAroundLink( Grid* grid, int iDir ): + _iDir( iDir ), + _dInd{ {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }, + _nbCells{ grid->_coords[0].size() - 1, + grid->_coords[1].size() - 1, + grid->_coords[2].size() - 1 }, + _grid( grid ) + { + const int iDirOther[3][2] = {{ 1,2 },{ 0,2 },{ 0,1 }}; + _dInd[1][ iDirOther[iDir][0] ] = -1; + _dInd[2][ iDirOther[iDir][1] ] = -1; + _dInd[3][ iDirOther[iDir][0] ] = -1; _dInd[3][ iDirOther[iDir][1] ] = -1; + } + void Init( int i, int j, int k, int link12 = 0 ) + { + int iL = link12 % 4; + _i = i - _dInd[iL][0]; + _j = j - _dInd[iL][1]; + _k = k - _dInd[iL][2]; + } + bool GetCell( int iL, int& i, int& j, int& k, int& cellIndex, int& linkIndex ) + { + i = _i + _dInd[iL][0]; + j = _j + _dInd[iL][1]; + k = _k + _dInd[iL][2]; + if ( i < 0 || i >= (int)_nbCells[0] || + j < 0 || j >= (int)_nbCells[1] || + k < 0 || k >= (int)_nbCells[2] ) + return false; + cellIndex = _grid->CellIndex( i,j,k ); + linkIndex = iL + _iDir * 4; + return true; + } + }; +} + +// -------------------------------------------------------------------------- +/*! + * \brief Class representing topology of the hexahedron and creating a mesh + * volume basing on analysis of hexahedron intersection with geometry + */ +class STDMESHERS_EXPORT Hexahedron +{ + // -------------------------------------------------------------------------------- + struct _Face; + struct _Link; + enum IsInternalFlag { IS_NOT_INTERNAL, IS_INTERNAL, IS_CUT_BY_INTERNAL_FACE }; + // -------------------------------------------------------------------------------- + struct _Node //!< node either at a hexahedron corner or at intersection + { + const SMDS_MeshNode* _node; // mesh node at hexahedron corner + const SMDS_MeshNode* _boundaryCornerNode; // missing mesh node due to hex truncation on the boundary + const B_IntersectPoint* _intPoint; + const _Face* _usedInFace; + char _isInternalFlags; + + _Node(const SMDS_MeshNode* n=0, const B_IntersectPoint* ip=0) + :_node(n), _intPoint(ip), _usedInFace(0), _isInternalFlags(0) {} + const SMDS_MeshNode* Node() const + { return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; } + const SMDS_MeshNode* BoundaryNode() const + { return _node ? _node : _boundaryCornerNode; } + const E_IntersectPoint* EdgeIntPnt() const + { return static_cast< const E_IntersectPoint* >( _intPoint ); } + const F_IntersectPoint* FaceIntPnt() const + { return static_cast< const F_IntersectPoint* >( _intPoint ); } + const vector< TGeomID >& faces() const { return _intPoint->_faceIDs; } + TGeomID face(size_t i) const { return _intPoint->_faceIDs[ i ]; } + void SetInternal( IsInternalFlag intFlag ) { _isInternalFlags |= intFlag; } + bool IsCutByInternal() const { return _isInternalFlags & IS_CUT_BY_INTERNAL_FACE; } + bool IsUsedInFace( const _Face* polygon = 0 ) + { + return polygon ? ( _usedInFace == polygon ) : bool( _usedInFace ); + } + TGeomID IsLinked( const B_IntersectPoint* other, + TGeomID avoidFace=-1 ) const // returns id of a common face + { + return _intPoint ? _intPoint->HasCommonFace( other, avoidFace ) : 0; + } + bool IsOnFace( TGeomID faceID ) const // returns true if faceID is found + { + return _intPoint ? _intPoint->IsOnFace( faceID ) : false; + } + size_t GetCommonFaces( const B_IntersectPoint * other, TGeomID* common ) const + { + return _intPoint && other ? _intPoint->GetCommonFaces( other, common ) : 0; + } + gp_Pnt Point() const + { + if ( const SMDS_MeshNode* n = Node() ) + return SMESH_NodeXYZ( n ); + if ( const E_IntersectPoint* eip = + dynamic_cast< const E_IntersectPoint* >( _intPoint )) + return eip->_point; + return gp_Pnt( 1e100, 0, 0 ); + } + TGeomID ShapeID() const + { + if ( const E_IntersectPoint* eip = dynamic_cast< const E_IntersectPoint* >( _intPoint )) + return eip->_shapeID; + return 0; + } + + void Add( const E_IntersectPoint* ip ); + }; + // -------------------------------------------------------------------------------- + struct _Link // link connecting two _Node's + { + _Node* _nodes[2]; + _Face* _faces[2]; // polygons sharing a link + vector< const F_IntersectPoint* > _fIntPoints; // GridLine intersections with FACEs + vector< _Node* > _fIntNodes; // _Node's at _fIntPoints + vector< _Link > _splits; + _Link(): _faces{ 0, 0 } {} + }; + // -------------------------------------------------------------------------------- + struct _OrientedLink + { + _Link* _link; + bool _reverse; + _OrientedLink( _Link* link=0, bool reverse=false ): _link(link), _reverse(reverse) {} + void Reverse() { _reverse = !_reverse; } + size_t NbResultLinks() const { return _link->_splits.size(); } + _OrientedLink ResultLink(int i) const + { + return _OrientedLink(&_link->_splits[_reverse ? NbResultLinks()-i-1 : i],_reverse); + } + _Node* FirstNode() const { return _link->_nodes[ _reverse ]; } + _Node* LastNode() const { return _link->_nodes[ !_reverse ]; } + operator bool() const { return _link; } + vector< TGeomID > GetNotUsedFace(const set& usedIDs ) const // returns supporting FACEs + { + vector< TGeomID > faces; + const B_IntersectPoint *ip0, *ip1; + if (( ip0 = _link->_nodes[0]->_intPoint ) && + ( ip1 = _link->_nodes[1]->_intPoint )) + { + for ( size_t i = 0; i < ip0->_faceIDs.size(); ++i ) + if ( ip1->IsOnFace ( ip0->_faceIDs[i] ) && + !usedIDs.count( ip0->_faceIDs[i] ) ) + faces.push_back( ip0->_faceIDs[i] ); + } + return faces; + } + bool HasEdgeNodes() const + { + return ( dynamic_cast< const E_IntersectPoint* >( _link->_nodes[0]->_intPoint ) || + dynamic_cast< const E_IntersectPoint* >( _link->_nodes[1]->_intPoint )); + } + int NbFaces() const + { + return !_link->_faces[0] ? 0 : 1 + bool( _link->_faces[1] ); + } + void AddFace( _Face* f ) + { + if ( _link->_faces[0] ) + { + _link->_faces[1] = f; + } + else + { + _link->_faces[0] = f; + _link->_faces[1] = 0; + } + } + void RemoveFace( _Face* f ) + { + if ( !_link->_faces[0] ) return; + + if ( _link->_faces[1] == f ) + { + _link->_faces[1] = 0; + } + else if ( _link->_faces[0] == f ) + { + _link->_faces[0] = 0; + if ( _link->_faces[1] ) + { + _link->_faces[0] = _link->_faces[1]; + _link->_faces[1] = 0; + } + } + } + }; + // -------------------------------------------------------------------------------- + struct _SplitIterator //! set to _hexLinks splits on one side of INTERNAL FACEs + { + struct _Split // data of a link split + { + int _linkID; // hex link ID + _Node* _nodes[2]; + int _iCheckIteration; // iteration where split is tried as Hexahedron split + _Link* _checkedSplit; // split set to hex links + bool _isUsed; // used in a volume + + _Split( _Link & split, int iLink ): + _linkID( iLink ), _nodes{ split._nodes[0], split._nodes[1] }, + _iCheckIteration( 0 ), _isUsed( false ) + {} + bool IsCheckedOrUsed( bool used ) const { return used ? _isUsed : _iCheckIteration > 0; } + }; + _Link* _hexLinks; + std::vector< _Split > _splits; + int _iterationNb; + size_t _nbChecked; + size_t _nbUsed; + std::vector< _Node* > _freeNodes; // nodes reached while composing a split set + + _SplitIterator( _Link* hexLinks ): + _hexLinks( hexLinks ), _iterationNb(0), _nbChecked(0), _nbUsed(0) + { + _freeNodes.reserve( 12 ); + _splits.reserve( 24 ); + for ( int iL = 0; iL < 12; ++iL ) + for ( size_t iS = 0; iS < _hexLinks[ iL ]._splits.size(); ++iS ) + _splits.emplace_back( _hexLinks[ iL ]._splits[ iS ], iL ); + Next(); + } + bool More() const { return _nbUsed < _splits.size(); } + bool Next(); + }; + // -------------------------------------------------------------------------------- + struct _Face + { + SMESH_Block::TShapeID _name; + vector< _OrientedLink > _links; // links on GridLine's + vector< _Link > _polyLinks; // links added to close a polygonal face + vector< _Node* > _eIntNodes; // nodes at intersection with EDGEs + + _Face():_name( SMESH_Block::ID_NONE ) + {} + bool IsPolyLink( const _OrientedLink& ol ) + { + return _polyLinks.empty() ? false : + ( &_polyLinks[0] <= ol._link && ol._link <= &_polyLinks.back() ); + } + void AddPolyLink(_Node* n0, _Node* n1, _Face* faceToFindEqual=0) + { + if ( faceToFindEqual && faceToFindEqual != this ) { + for ( size_t iL = 0; iL < faceToFindEqual->_polyLinks.size(); ++iL ) + if ( faceToFindEqual->_polyLinks[iL]._nodes[0] == n1 && + faceToFindEqual->_polyLinks[iL]._nodes[1] == n0 ) + { + _links.push_back + ( _OrientedLink( & faceToFindEqual->_polyLinks[iL], /*reverse=*/true )); + return; + } + } + _Link l; + l._nodes[0] = n0; + l._nodes[1] = n1; + _polyLinks.push_back( l ); + _links.push_back( _OrientedLink( &_polyLinks.back() )); + } + }; + // -------------------------------------------------------------------------------- + struct _volumeDef // holder of nodes of a volume mesh element + { + typedef void* _ptr; + + struct _nodeDef + { + const SMDS_MeshNode* _node; // mesh node at hexahedron corner + const B_IntersectPoint* _intPoint; + + _nodeDef(): _node(0), _intPoint(0) {} + _nodeDef( _Node* n ): _node( n->_node), _intPoint( n->_intPoint ) {} + const SMDS_MeshNode* Node() const + { return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; } + const E_IntersectPoint* EdgeIntPnt() const + { return static_cast< const E_IntersectPoint* >( _intPoint ); } + _ptr Ptr() const { return Node() ? (_ptr) Node() : (_ptr) EdgeIntPnt(); } + bool operator==(const _nodeDef& other ) const { return Ptr() == other.Ptr(); } + }; + + vector< _nodeDef > _nodes; + vector< int > _quantities; + _volumeDef* _next; // to store several _volumeDefs in a chain + TGeomID _solidID; + double _size; + const SMDS_MeshElement* _volume; // new volume + std::vector _brotherVolume; // produced due to poly split + + vector< SMESH_Block::TShapeID > _names; // name of side a polygon originates from + + _volumeDef(): _next(0), _solidID(0), _size(0), _volume(0) {} + ~_volumeDef() { delete _next; } + _volumeDef( _volumeDef& other ): + _next(0), _solidID( other._solidID ), _size( other._size ), _volume( other._volume ) + { _nodes.swap( other._nodes ); _quantities.swap( other._quantities ); other._volume = 0; + _names.swap( other._names ); } + + size_t size() const { return 1 + ( _next ? _next->size() : 0 ); } // nb _volumeDef in a chain + _volumeDef* at(int index) + { return index == 0 ? this : ( _next ? _next->at(index-1) : _next ); } + + void Set( _Node** nodes, int nb ) + { _nodes.assign( nodes, nodes + nb ); } + + void SetNext( _volumeDef* vd ) + { if ( _next ) { _next->SetNext( vd ); } else { _next = vd; }} + + bool IsEmpty() const { return (( _nodes.empty() ) && + ( !_next || _next->IsEmpty() )); } + bool IsPolyhedron() const { return ( !_quantities.empty() || + ( _next && !_next->_quantities.empty() )); } + + + struct _linkDef: public std::pair<_ptr,_ptr> // to join polygons in removeExcessSideDivision() + { + _nodeDef _node1;//, _node2; + mutable /*const */_linkDef *_prev, *_next; + size_t _loopIndex; + + _linkDef():_prev(0), _next(0) {} + + void init( const _nodeDef& n1, const _nodeDef& n2, size_t iLoop ) + { + _node1 = n1; //_node2 = n2; + _loopIndex = iLoop; + first = n1.Ptr(); + second = n2.Ptr(); + if ( first > second ) std::swap( first, second ); + } + void setNext( _linkDef* next ) + { + _next = next; + next->_prev = this; + } + }; + }; + + // topology of a hexahedron + _Node _hexNodes [8]; + _Link _hexLinks [12]; + _Face _hexQuads [6]; + + // faces resulted from hexahedron intersection + vector< _Face > _polygons; + + // intresections with EDGEs + vector< const E_IntersectPoint* > _eIntPoints; + + // additional nodes created at intersection points + vector< _Node > _intNodes; + + // nodes inside the hexahedron (at VERTEXes) refer to _intNodes + vector< _Node* > _vIntNodes; + + // computed volume elements + _volumeDef _volumeDefs; + + Grid* _grid; + double _sideLength[3]; + int _nbCornerNodes, _nbFaceIntNodes, _nbBndNodes; + int _origNodeInd; // index of _hexNodes[0] node within the _grid + size_t _i,_j,_k; + bool _hasTooSmall; + int _cellID; + +public: + Hexahedron(Grid* grid); + int MakeElements(SMESH_MesherHelper& helper, + const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, const int numOfThreads = 1 ); + void computeElements( const Solid* solid = 0, int solidIndex = -1 ); + +private: + Hexahedron(const Hexahedron& other, size_t i, size_t j, size_t k, int cellID ); + void init( size_t i, size_t j, size_t k, const Solid* solid=0 ); + void init( size_t i ); + void setIJK( size_t i ); + /*Auxiliary methods to extract operations from monolitic compute method*/ + void defineHexahedralFaces( std::vector< _OrientedLink >& splits, std::vector<_Node*>& chainNodes, std::set< TGeomID >& concaveFaces, bool toCheckSideDivision ); + bool compute( const Solid* solid, const IsInternalFlag intFlag ); + size_t getSolids( TGeomID ids[] ); + bool isCutByInternalFace( IsInternalFlag & maxFlag ); + void addEdges(SMESH_MesherHelper& helper, + vector< Hexahedron* >& intersectedHex, + const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap); + gp_Pnt findIntPoint( double u1, double proj1, double u2, double proj2, + double proj, BRepAdaptor_Curve& curve, + const gp_XYZ& axis, const gp_XYZ& origin ); + int getEntity( const E_IntersectPoint* ip, int* facets, int& sub ); + bool addIntersection( const E_IntersectPoint* ip, + vector< Hexahedron* >& hexes, + int ijk[], int dIJK[] ); + bool isQuadOnFace( const size_t iQuad ); + bool findChain( _Node* n1, _Node* n2, _Face& quad, vector<_Node*>& chainNodes ); + bool closePolygon( _Face* polygon, vector<_Node*>& chainNodes ) const; + bool findChainOnEdge( const vector< _OrientedLink >& splits, + const _OrientedLink& prevSplit, + const _OrientedLink& avoidSplit, + const std::set< TGeomID > & concaveFaces, + size_t & iS, + _Face& quad, + vector<_Node*>& chn); + int addVolumes(SMESH_MesherHelper& helper ); + void addFaces( SMESH_MesherHelper& helper, + const vector< const SMDS_MeshElement* > & boundaryVolumes ); + void addSegments( SMESH_MesherHelper& helper, + const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap ); + void getVolumes( vector< const SMDS_MeshElement* > & volumes ); + void getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryVolumes ); + void removeExcessSideDivision(const vector< Hexahedron* >& allHexa); + void removeExcessNodes(vector< Hexahedron* >& allHexa); + void preventVolumesOverlapping(); + TGeomID getAnyFace() const; + void cutByExtendedInternal( std::vector< Hexahedron* >& hexes, + const TColStd_MapOfInteger& intEdgeIDs ); + gp_Pnt mostDistantInternalPnt( int hexIndex, const gp_Pnt& p1, const gp_Pnt& p2 ); + bool isOutPoint( _Link& link, int iP, SMESH_MesherHelper& helper, const Solid* solid ) const; + void sortVertexNodes(vector<_Node*>& nodes, _Node* curNode, TGeomID face); + bool isInHole() const; + bool hasStrangeEdge() const; + bool checkPolyhedronSize( bool isCutByInternalFace, double & volSize ) const; + int checkPolyhedronValidity( _volumeDef* volDef, std::vector>& splitQuantities, + std::vector>& splitNodes ); + const SMDS_MeshElement* addPolyhedronToMesh( _volumeDef* volDef, SMESH_MesherHelper& helper, const std::vector& nodes, + const std::vector& quantities ); + bool addHexa (); + bool addTetra(); + bool addPenta(); + bool addPyra (); + bool debugDumpLink( _Link* link ); + _Node* findEqualNode( vector< _Node* >& nodes, + const E_IntersectPoint* ip, + const double tol2 ) + { + for ( size_t i = 0; i < nodes.size(); ++i ) + if ( nodes[i]->EdgeIntPnt() == ip || + nodes[i]->Point().SquareDistance( ip->_point ) <= tol2 ) + return nodes[i]; + return 0; + } + bool isCorner( const _Node* node ) const { return ( node >= &_hexNodes[0] && + node - &_hexNodes[0] < 8 ); } + bool hasEdgesAround( const ConcaveFace* cf ) const; + bool isImplementEdges() const { return _grid->_edgeIntPool.nbElements(); } + bool isOutParam(const double uvw[3]) const; + + typedef boost::container::flat_map< TGeomID, size_t > TID2Nb; + static void insertAndIncrement( TGeomID id, TID2Nb& id2nbMap ) + { + TID2Nb::value_type s0( id, 0 ); + TID2Nb::iterator id2nb = id2nbMap.insert( s0 ).first; + id2nb->second++; + } +}; // class Hexahedron + +#endif diff --git a/test/CTestTestfileInstall.cmake b/test/CTestTestfileInstall.cmake index 53d4f456b..82d75866f 100644 --- a/test/CTestTestfileInstall.cmake +++ b/test/CTestTestfileInstall.cmake @@ -41,3 +41,10 @@ FOREACH(tfile ${CPP_TESTS}) ADD_TEST(${TEST_NAME} ${BASE_NAME} ) SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES LABELS "${COMPONENT_NAME};${COMPONENT_NAME}_tests") ENDFOREACH() + +FOREACH(tfile ${UNIT_TESTS}) + GET_FILENAME_COMPONENT(BASE_NAME ${tfile} NAME_WE) + SET(TEST_NAME SMESH_${BASE_NAME}) + ADD_TEST(${TEST_NAME} ${BASE_NAME} ) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES LABELS "${COMPONENT_NAME};${COMPONENT_NAME}_tests") +ENDFOREACH() diff --git a/test/data/HexahedronTest/NRTM1.brep b/test/data/HexahedronTest/NRTM1.brep new file mode 100644 index 000000000..2508f0ca6 --- /dev/null +++ b/test/data/HexahedronTest/NRTM1.brep @@ -0,0 +1,558 @@ +DBRep_DrawableShape + +CASCADE Topology V3, (c) Open Cascade +Locations 0 +Curve2ds 32 +1 0 0 1 0 +1 0 0 1 0 +1 10 0 0 -1 +1 0 0 0 1 +1 0 -10 1 0 +1 0 0 1 0 +1 0 0 0 -1 +1 0 0 0 1 +1 0 0 0 1 +1 0 0 1 0 +1 0 0 1 0 +1 0 10 1 0 +1 10 0 0 1 +1 0 0 1 0 +1 10 0 0 1 +1 0 10 1 0 +1 10 0 0 -1 +1 10 0 0 1 +1 0 0 0 1 +1 0 10 1 0 +1 0 -10 1 0 +1 0 10 1 0 +1 0 0 0 -1 +1 10 0 0 1 +2 5 5 1 0 -0 1 1 +1 0 6 1 0 +1 6.2831853071795862 -0 0 1 +1 0 -0 0 1 +1 0 9 1 0 +2 0 0 1 0 -0 1 1 +1 0 0 1 0 +2 0 0 1 0 -0 1 1 +Curves 16 +1 -5 -5 0 0 0 1 +1 -5 -5 10 -0 1 0 +1 -5 5 0 0 0 1 +1 -5 -5 0 -0 1 0 +1 -5 -5 0 1 0 -0 +1 5 -5 0 0 0 1 +1 -5 -5 10 1 0 -0 +1 -5 5 10 1 0 -0 +1 5 -5 10 -0 1 0 +1 -5 5 0 1 0 -0 +1 5 5 0 0 0 1 +1 5 -5 0 -0 1 0 +2 0 0 0 0 0 1 1 0 -0 -0 1 0 1 +1 1 -2.4492935982947064e-16 -6 0 0 1 +2 0 0 3 0 0 1 1 0 -0 -0 1 0 1 +2 0 0 -6 0 0 1 1 0 -0 -0 1 0 1 +Polygon3D 0 +PolygonOnTriangulations 36 +2 1 2 +p 0.0640000008 1 0 10 +2 1 3 +p 0.0640000008 1 0 10 +2 2 4 +p 0.0640000008 1 0 10 +2 1 2 +p 0.0640000008 1 0 10 +2 3 4 +p 0.0640000008 1 0 10 +2 1 3 +p 0.0640000008 1 0 10 +2 1 3 +p 0.0640000008 1 0 10 +2 1 2 +p 0.0640000008 1 0 10 +2 1 2 +p 0.0640000008 1 0 10 +2 1 3 +p 0.0640000008 1 0 10 +2 2 4 +p 0.0640000008 1 0 10 +2 1 2 +p 0.0640000008 1 0 10 +2 3 4 +p 0.0640000008 1 0 10 +2 1 3 +p 0.0640000008 1 0 10 +2 2 4 +p 0.0640000008 1 0 10 +2 3 4 +p 0.0640000008 1 0 10 +2 3 4 +p 0.0640000008 1 0 10 +2 2 4 +p 0.0640000008 1 0 10 +2 1 2 +p 0.0640000008 1 0 10 +2 2 4 +p 0.0640000008 1 0 10 +2 2 4 +p 0.0640000008 1 0 10 +2 3 4 +p 0.0640000008 1 0 10 +2 3 4 +p 0.0640000008 1 0 10 +2 1 3 +p 0.0640000008 1 0 10 +37 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 1 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +37 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 5 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 1 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +2 1 2 +p 0.0640000008 1 6 9 +2 39 3 +p 0.0640000008 1 6 9 +37 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 2 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 1 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +2 38 1 +p 0.0640000008 1 0 6 +2 74 37 +p 0.0640000008 1 0 6 +37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +37 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 1 +p 0.0640000008 1 0 0.174532925199433 0.349065850398866 0.523598775598299 0.698131700797732 0.872664625997165 1.0471975511966 1.22173047639603 1.39626340159546 1.5707963267949 1.74532925199433 1.91986217719376 2.0943951023932 2.26892802759263 2.44346095279206 2.61799387799149 2.79252680319093 2.96705972839036 3.14159265358979 3.31612557878922 3.49065850398866 3.66519142918809 3.83972435438752 4.01425727958696 4.18879020478639 4.36332312998582 4.53785605518525 4.71238898038469 4.88692190558412 5.06145483078355 5.23598775598299 5.41052068118242 5.58505360638185 5.75958653158128 5.93411945678072 6.10865238198015 6.28318530717959 +Surfaces 9 +1 -5 -5 0 1 0 -0 0 0 1 0 -1 0 +1 -5 -5 0 -0 1 0 0 0 1 1 0 -0 +1 -5 -5 10 0 0 1 1 0 -0 -0 1 0 +1 -5 5 0 -0 1 0 0 0 1 1 0 -0 +1 -5 -5 0 0 0 1 1 0 -0 -0 1 0 +1 5 -5 0 1 0 -0 0 0 1 0 -1 0 +2 0 0 -6 0 0 1 1 0 -0 -0 1 0 1 +1 0 0 3 0 0 1 1 0 -0 -0 1 0 +1 0 0 -6 0 0 1 1 0 -0 -0 1 0 +Triangulations 11 +4 2 1 0 2.22044604925031e-16 +-5 -5 0 -5 -5 10 -5 5 0 -5 5 10 0 0 10 0 0 -10 10 -10 2 1 3 2 3 4 +4 2 1 0 2.22044604925031e-16 +-5 -5 0 5 -5 0 -5 -5 10 5 -5 10 0 0 0 10 10 0 10 10 4 2 1 4 1 3 +4 2 1 0 3.14018491736755e-16 +-5 -5 10 -5 5 10 5 -5 10 5 5 10 0 0 0 10 10 0 10 10 4 2 1 4 1 3 +4 2 1 0 2.22044604925031e-16 +-5 5 0 5 5 0 -5 5 10 5 5 10 0 0 0 10 10 0 10 10 4 2 1 4 1 3 +40 40 1 0 9.93013661298909e-16 +-5 -5 0 -5 5 0 5 -5 0 5 5 0 1 -2.44929359829471e-16 0 0.984807753012208 0.17364817766693 0 0.939692620785908 0.342020143325669 0 0.866025403784439 0.5 0 0.766044443118978 0.642787609686539 0 0.642787609686539 0.766044443118978 0 0.5 0.866025403784439 0 0.342020143325669 0.939692620785908 0 0.17364817766693 0.984807753012208 0 6.12323399573677e-17 1 0 -0.17364817766693 0.984807753012208 0 -0.342020143325669 0.939692620785908 0 -0.5 0.866025403784439 0 -0.642787609686539 0.766044443118978 0 -0.766044443118978 0.64278760968654 0 -0.866025403784438 0.500000000000001 0 -0.939692620785908 0.34202014332567 0 -0.984807753012208 0.173648177666932 0 -1 1.45473230946492e-15 0 -0.984807753012208 -0.173648177666929 0 -0.939692620785909 -0.342020143325667 0 -0.86602540378444 -0.499999999999998 0 -0.766044443118979 -0.642787609686538 0 -0.642787609686541 -0.766044443118977 0 -0.500000000000002 -0.866025403784437 0 -0.342020143325671 -0.939692620785908 0 -0.173648177666933 -0.984807753012208 0 -2.84823227897248e-15 -1 0 0.173648177666927 -0.984807753012209 0 0.342020143325666 -0.93969262078591 0 0.499999999999997 -0.86602540378444 0 0.642787609686536 -0.76604444311898 0 0.766044443118976 -0.642787609686542 0 0.866025403784437 -0.500000000000004 0 0.939692620785907 -0.342020143325673 0 0.984807753012207 -0.173648177666935 0 0 0 0 10 10 0 10 10 6 5 5.98480775301221 5.17364817766693 5.93969262078591 5.34202014332567 5.86602540378444 5.5 5.76604444311898 5.64278760968654 5.64278760968654 5.76604444311898 5.5 5.86602540378444 5.34202014332567 5.93969262078591 5.17364817766693 5.98480775301221 5 6 4.82635182233307 5.98480775301221 4.65797985667433 5.93969262078591 4.5 5.86602540378444 4.35721239031346 5.76604444311898 4.23395555688102 5.64278760968654 4.13397459621556 5.5 4.06030737921409 5.34202014332567 4.01519224698779 5.17364817766693 4 5 4.01519224698779 4.82635182233307 4.06030737921409 4.65797985667433 4.13397459621556 4.5 4.23395555688102 4.35721239031346 4.35721239031346 4.23395555688102 4.5 4.13397459621556 4.65797985667433 4.06030737921409 4.82635182233307 4.01519224698779 5 4 5.17364817766693 4.01519224698779 5.34202014332567 4.06030737921409 5.5 4.13397459621556 5.64278760968654 4.23395555688102 5.76604444311898 4.35721239031346 5.86602540378444 4.5 5.93969262078591 4.65797985667433 5.98480775301221 4.82635182233307 27 1 28 29 28 1 26 1 27 30 29 1 25 1 26 31 30 1 24 1 25 32 31 1 23 1 24 2 20 19 2 21 20 2 22 21 2 23 22 2 1 23 3 33 32 3 34 33 3 35 34 3 36 35 3 32 1 37 36 3 18 2 19 38 37 3 17 2 18 39 38 3 16 2 17 40 39 3 15 2 16 5 40 3 14 2 15 4 5 3 4 6 5 4 7 6 4 8 7 4 9 8 4 10 9 4 11 10 4 12 11 4 13 12 4 14 13 4 2 14 +4 2 1 0 2.22044604925031e-16 +5 -5 0 5 -5 10 5 5 0 5 5 10 0 0 10 0 0 -10 10 -10 2 1 3 2 3 4 +74 72 1 0 0.00380530190825463 +1 -2.44929359829471e-16 0 1 -2.44929359829471e-16 3 1 -2.44929359829471e-16 3 0.984807753012208 0.17364817766693 3 0.939692620785908 0.342020143325669 3 0.866025403784439 0.5 3 0.766044443118978 0.642787609686539 3 0.642787609686539 0.766044443118978 3 0.5 0.866025403784439 3 0.342020143325669 0.939692620785908 3 0.17364817766693 0.984807753012208 3 6.12323399573677e-17 1 3 -0.17364817766693 0.984807753012208 3 -0.342020143325669 0.939692620785908 3 -0.5 0.866025403784439 3 -0.642787609686539 0.766044443118978 3 -0.766044443118978 0.64278760968654 3 -0.866025403784438 0.500000000000001 3 -0.939692620785908 0.34202014332567 3 -0.984807753012208 0.173648177666932 3 -1 1.45473230946492e-15 3 -0.984807753012208 -0.173648177666929 3 -0.939692620785909 -0.342020143325667 3 -0.86602540378444 -0.499999999999998 3 -0.766044443118979 -0.642787609686538 3 -0.642787609686541 -0.766044443118977 3 -0.500000000000002 -0.866025403784437 3 -0.342020143325671 -0.939692620785908 3 -0.173648177666933 -0.984807753012208 3 -2.84823227897248e-15 -1 3 0.173648177666927 -0.984807753012209 3 0.342020143325666 -0.93969262078591 3 0.499999999999997 -0.86602540378444 3 0.642787609686536 -0.76604444311898 3 0.766044443118976 -0.642787609686542 3 0.866025403784437 -0.500000000000004 3 0.939692620785907 -0.342020143325673 3 0.984807753012207 -0.173648177666935 3 1 -2.44929359829471e-16 0 0.984807753012208 0.17364817766693 0 0.939692620785908 0.342020143325669 0 0.866025403784439 0.5 0 0.766044443118978 0.642787609686539 0 0.642787609686539 0.766044443118978 0 0.5 0.866025403784439 0 0.342020143325669 0.939692620785908 0 0.17364817766693 0.984807753012208 0 6.12323399573677e-17 1 0 -0.17364817766693 0.984807753012208 0 -0.342020143325669 0.939692620785908 0 -0.5 0.866025403784439 0 -0.642787609686539 0.766044443118978 0 -0.766044443118978 0.64278760968654 0 -0.866025403784438 0.500000000000001 0 -0.939692620785908 0.34202014332567 0 -0.984807753012208 0.173648177666932 0 -1 1.45473230946492e-15 0 -0.984807753012208 -0.173648177666929 0 -0.939692620785909 -0.342020143325667 0 -0.86602540378444 -0.499999999999998 0 -0.766044443118979 -0.642787609686538 0 -0.642787609686541 -0.766044443118977 0 -0.500000000000002 -0.866025403784437 0 -0.342020143325671 -0.939692620785908 0 -0.173648177666933 -0.984807753012208 0 -2.84823227897248e-15 -1 0 0.173648177666927 -0.984807753012209 0 0.342020143325666 -0.93969262078591 0 0.499999999999997 -0.86602540378444 0 0.642787609686536 -0.76604444311898 0 0.766044443118976 -0.642787609686542 0 0.866025403784437 -0.500000000000004 0 0.939692620785907 -0.342020143325673 0 0.984807753012207 -0.173648177666935 0 6.28318530717959 6 6.28318530717959 9 0 9 0.174532925199433 9 0.349065850398866 9 0.523598775598299 9 0.698131700797732 9 0.872664625997165 9 1.0471975511966 9 1.22173047639603 9 1.39626340159546 9 1.5707963267949 9 1.74532925199433 9 1.91986217719376 9 2.0943951023932 9 2.26892802759263 9 2.44346095279206 9 2.61799387799149 9 2.79252680319093 9 2.96705972839036 9 3.14159265358979 9 3.31612557878922 9 3.49065850398866 9 3.66519142918809 9 3.83972435438752 9 4.01425727958696 9 4.18879020478639 9 4.36332312998582 9 4.53785605518525 9 4.71238898038469 9 4.88692190558412 9 5.06145483078355 9 5.23598775598299 9 5.41052068118242 9 5.58505360638185 9 5.75958653158128 9 5.93411945678072 9 6.10865238198015 9 0 6 0.174532925199433 6 0.349065850398866 6 0.523598775598299 6 0.698131700797732 6 0.872664625997165 6 1.0471975511966 6 1.22173047639603 6 1.39626340159546 6 1.5707963267949 6 1.74532925199433 6 1.91986217719376 6 2.0943951023932 6 2.26892802759263 6 2.44346095279206 6 2.61799387799149 6 2.79252680319093 6 2.96705972839036 6 3.14159265358979 6 3.31612557878922 6 3.49065850398866 6 3.66519142918809 6 3.83972435438752 6 4.01425727958696 6 4.18879020478639 6 4.36332312998582 6 4.53785605518525 6 4.71238898038469 6 4.88692190558412 6 5.06145483078355 6 5.23598775598299 6 5.41052068118242 6 5.58505360638185 6 5.75958653158128 6 5.93411945678072 6 6.10865238198015 6 4 3 39 4 39 40 5 40 41 5 4 40 6 41 42 6 5 41 7 42 43 7 43 44 7 6 42 8 44 45 8 7 44 9 8 45 10 9 45 10 45 46 10 46 47 11 10 47 11 47 48 12 11 48 13 12 48 13 48 49 14 13 49 14 49 50 14 50 51 15 14 51 16 15 51 16 51 52 16 52 53 17 16 53 18 17 53 18 53 54 19 18 54 19 54 55 20 19 55 20 55 56 21 20 56 21 56 57 22 21 57 22 57 58 22 58 59 23 22 59 24 59 60 24 23 59 25 60 61 25 61 62 25 24 60 26 25 62 27 62 63 27 26 62 28 63 64 28 27 63 29 64 65 29 28 64 30 65 66 30 29 65 31 66 67 31 30 66 32 67 68 32 31 67 33 68 69 33 32 68 34 33 69 34 69 70 34 70 71 35 34 71 36 35 71 36 71 72 37 36 72 37 72 73 38 37 73 38 73 74 2 38 74 2 74 1 +74 72 1 0 0.00380530190825463 +1 -2.44929359829471e-16 0 0.984807753012208 0.17364817766693 0 0.939692620785908 0.342020143325669 0 0.866025403784439 0.5 0 0.766044443118978 0.642787609686539 0 0.642787609686539 0.766044443118978 0 0.5 0.866025403784439 0 0.342020143325669 0.939692620785908 0 0.17364817766693 0.984807753012208 0 6.12323399573677e-17 1 0 -0.17364817766693 0.984807753012208 0 -0.342020143325669 0.939692620785908 0 -0.5 0.866025403784439 0 -0.642787609686539 0.766044443118978 0 -0.766044443118978 0.64278760968654 0 -0.866025403784438 0.500000000000001 0 -0.939692620785908 0.34202014332567 0 -0.984807753012208 0.173648177666932 0 -1 1.45473230946492e-15 0 -0.984807753012208 -0.173648177666929 0 -0.939692620785909 -0.342020143325667 0 -0.86602540378444 -0.499999999999998 0 -0.766044443118979 -0.642787609686538 0 -0.642787609686541 -0.766044443118977 0 -0.500000000000002 -0.866025403784437 0 -0.342020143325671 -0.939692620785908 0 -0.173648177666933 -0.984807753012208 0 -2.84823227897248e-15 -1 0 0.173648177666927 -0.984807753012209 0 0.342020143325666 -0.93969262078591 0 0.499999999999997 -0.86602540378444 0 0.642787609686536 -0.76604444311898 0 0.766044443118976 -0.642787609686542 0 0.866025403784437 -0.500000000000004 0 0.939692620785907 -0.342020143325673 0 0.984807753012207 -0.173648177666935 0 1 -2.44929359829471e-16 0 1 -2.44929359829471e-16 -6 0.984807753012208 0.17364817766693 -6 0.939692620785908 0.342020143325669 -6 0.866025403784439 0.5 -6 0.766044443118978 0.642787609686539 -6 0.642787609686539 0.766044443118978 -6 0.5 0.866025403784439 -6 0.342020143325669 0.939692620785908 -6 0.17364817766693 0.984807753012208 -6 6.12323399573677e-17 1 -6 -0.17364817766693 0.984807753012208 -6 -0.342020143325669 0.939692620785908 -6 -0.5 0.866025403784439 -6 -0.642787609686539 0.766044443118978 -6 -0.766044443118978 0.64278760968654 -6 -0.866025403784438 0.500000000000001 -6 -0.939692620785908 0.34202014332567 -6 -0.984807753012208 0.173648177666932 -6 -1 1.45473230946492e-15 -6 -0.984807753012208 -0.173648177666929 -6 -0.939692620785909 -0.342020143325667 -6 -0.86602540378444 -0.499999999999998 -6 -0.766044443118979 -0.642787609686538 -6 -0.642787609686541 -0.766044443118977 -6 -0.500000000000002 -0.866025403784437 -6 -0.342020143325671 -0.939692620785908 -6 -0.173648177666933 -0.984807753012208 -6 -2.84823227897248e-15 -1 -6 0.173648177666927 -0.984807753012209 -6 0.342020143325666 -0.93969262078591 -6 0.499999999999997 -0.86602540378444 -6 0.642787609686536 -0.76604444311898 -6 0.766044443118976 -0.642787609686542 -6 0.866025403784437 -0.500000000000004 -6 0.939692620785907 -0.342020143325673 -6 0.984807753012207 -0.173648177666935 -6 1 -2.44929359829471e-16 -6 0 6 0.174532925199433 6 0.349065850398866 6 0.523598775598299 6 0.698131700797732 6 0.872664625997165 6 1.0471975511966 6 1.22173047639603 6 1.39626340159546 6 1.5707963267949 6 1.74532925199433 6 1.91986217719376 6 2.0943951023932 6 2.26892802759263 6 2.44346095279206 6 2.61799387799149 6 2.79252680319093 6 2.96705972839036 6 3.14159265358979 6 3.31612557878922 6 3.49065850398866 6 3.66519142918809 6 3.83972435438752 6 4.01425727958696 6 4.18879020478639 6 4.36332312998582 6 4.53785605518525 6 4.71238898038469 6 4.88692190558412 6 5.06145483078355 6 5.23598775598299 6 5.41052068118242 6 5.58505360638185 6 5.75958653158128 6 5.93411945678072 6 6.10865238198015 6 6.28318530717959 6 0 0 0.174532925199433 0 0.349065850398866 0 0.523598775598299 0 0.698131700797732 0 0.872664625997165 0 1.0471975511966 0 1.22173047639603 0 1.39626340159546 0 1.5707963267949 0 1.74532925199433 0 1.91986217719376 0 2.0943951023932 0 2.26892802759263 0 2.44346095279206 0 2.61799387799149 0 2.79252680319093 0 2.96705972839036 0 3.14159265358979 0 3.31612557878922 0 3.49065850398866 0 3.66519142918809 0 3.83972435438752 0 4.01425727958696 0 4.18879020478639 0 4.36332312998582 0 4.53785605518525 0 4.71238898038469 0 4.88692190558412 0 5.06145483078355 0 5.23598775598299 0 5.41052068118242 0 5.58505360638185 0 5.75958653158128 0 5.93411945678072 0 6.10865238198015 0 6.28318530717959 0 2 1 38 2 38 39 3 39 40 3 2 39 4 40 41 4 3 40 5 41 42 5 4 41 6 42 43 6 5 42 7 6 43 7 43 44 8 7 44 8 44 45 9 8 45 9 45 46 10 9 46 10 46 47 11 10 47 11 47 48 12 11 48 12 48 49 13 12 49 13 49 50 14 13 50 14 50 51 15 14 51 15 51 52 16 15 52 16 52 53 17 16 53 17 53 54 17 54 55 18 17 55 19 18 55 19 55 56 20 56 57 20 19 56 21 57 58 21 20 57 22 58 59 22 21 58 23 59 60 23 22 59 24 60 61 24 23 60 25 61 62 25 24 61 26 62 63 26 25 62 27 63 64 27 26 63 28 64 65 28 27 64 29 65 66 29 28 65 30 66 67 30 29 66 31 67 68 31 30 67 32 31 68 32 68 69 33 32 69 33 69 70 34 33 70 34 70 71 35 34 71 35 71 72 36 35 72 36 72 73 36 73 74 37 36 74 +36 34 1 0 1.38026443154486e-15 +1 -2.44929359829471e-16 0 0.984807753012208 0.17364817766693 0 0.939692620785908 0.342020143325669 0 0.866025403784439 0.5 0 0.766044443118978 0.642787609686539 0 0.642787609686539 0.766044443118978 0 0.5 0.866025403784439 0 0.342020143325669 0.939692620785908 0 0.17364817766693 0.984807753012208 0 6.12323399573677e-17 1 0 -0.17364817766693 0.984807753012208 0 -0.342020143325669 0.939692620785908 0 -0.5 0.866025403784439 0 -0.642787609686539 0.766044443118978 0 -0.766044443118978 0.64278760968654 0 -0.866025403784438 0.500000000000001 0 -0.939692620785908 0.34202014332567 0 -0.984807753012208 0.173648177666932 0 -1 1.45473230946492e-15 0 -0.984807753012208 -0.173648177666929 0 -0.939692620785909 -0.342020143325667 0 -0.86602540378444 -0.499999999999998 0 -0.766044443118979 -0.642787609686538 0 -0.642787609686541 -0.766044443118977 0 -0.500000000000002 -0.866025403784437 0 -0.342020143325671 -0.939692620785908 0 -0.173648177666933 -0.984807753012208 0 -2.84823227897248e-15 -1 0 0.173648177666927 -0.984807753012209 0 0.342020143325666 -0.93969262078591 0 0.499999999999997 -0.86602540378444 0 0.642787609686536 -0.76604444311898 0 0.766044443118976 -0.642787609686542 0 0.866025403784437 -0.500000000000004 0 0.939692620785907 -0.342020143325673 0 0.984807753012207 -0.173648177666935 0 6 5 5.98480775301221 5.17364817766693 5.93969262078591 5.34202014332567 5.86602540378444 5.5 5.76604444311898 5.64278760968654 5.64278760968654 5.76604444311898 5.5 5.86602540378444 5.34202014332567 5.93969262078591 5.17364817766693 5.98480775301221 5 6 4.82635182233307 5.98480775301221 4.65797985667433 5.93969262078591 4.5 5.86602540378444 4.35721239031346 5.76604444311898 4.23395555688102 5.64278760968654 4.13397459621556 5.5 4.06030737921409 5.34202014332567 4.01519224698779 5.17364817766693 4 5 4.01519224698779 4.82635182233307 4.06030737921409 4.65797985667433 4.13397459621556 4.5 4.23395555688102 4.35721239031346 4.35721239031346 4.23395555688102 4.5 4.13397459621556 4.65797985667433 4.06030737921409 4.82635182233307 4.01519224698779 5 4 5.17364817766693 4.01519224698779 5.34202014332567 4.06030737921409 5.5 4.13397459621556 5.64278760968654 4.23395555688102 5.76604444311898 4.35721239031346 5.86602540378444 4.5 5.93969262078591 4.65797985667433 5.98480775301221 4.82635182233307 22 23 24 27 24 25 27 25 26 27 22 24 17 18 19 31 27 28 31 28 29 31 29 30 15 16 17 33 31 32 33 27 31 13 14 15 13 19 20 13 17 19 13 15 17 35 33 34 11 12 13 8 9 10 8 10 11 8 11 13 4 1 2 4 2 3 4 35 36 4 36 1 4 33 35 6 8 13 6 4 5 6 33 4 6 7 8 6 20 21 6 21 22 6 22 27 6 27 33 6 13 20 +36 34 1 0 1.57009245868378e-16 +1 -2.44929359829471e-16 3 0.984807753012208 0.17364817766693 3 0.939692620785908 0.342020143325669 3 0.866025403784439 0.5 3 0.766044443118978 0.642787609686539 3 0.642787609686539 0.766044443118978 3 0.5 0.866025403784439 3 0.342020143325669 0.939692620785908 3 0.17364817766693 0.984807753012208 3 6.12323399573677e-17 1 3 -0.17364817766693 0.984807753012208 3 -0.342020143325669 0.939692620785908 3 -0.5 0.866025403784439 3 -0.642787609686539 0.766044443118978 3 -0.766044443118978 0.64278760968654 3 -0.866025403784438 0.500000000000001 3 -0.939692620785908 0.34202014332567 3 -0.984807753012208 0.173648177666932 3 -1 1.45473230946492e-15 3 -0.984807753012208 -0.173648177666929 3 -0.939692620785909 -0.342020143325667 3 -0.86602540378444 -0.499999999999998 3 -0.766044443118979 -0.642787609686538 3 -0.642787609686541 -0.766044443118977 3 -0.500000000000002 -0.866025403784437 3 -0.342020143325671 -0.939692620785908 3 -0.173648177666933 -0.984807753012208 3 -2.84823227897248e-15 -1 3 0.173648177666927 -0.984807753012209 3 0.342020143325666 -0.93969262078591 3 0.499999999999997 -0.86602540378444 3 0.642787609686536 -0.76604444311898 3 0.766044443118976 -0.642787609686542 3 0.866025403784437 -0.500000000000004 3 0.939692620785907 -0.342020143325673 3 0.984807753012207 -0.173648177666935 3 1 -2.22044604925031e-16 0.984807753012208 0.17364817766693 0.939692620785908 0.342020143325669 0.866025403784439 0.5 0.766044443118978 0.642787609686539 0.642787609686539 0.766044443118978 0.5 0.866025403784439 0.342020143325669 0.939692620785908 0.173648177666931 0.984807753012208 0 1 -0.17364817766693 0.984807753012208 -0.342020143325669 0.939692620785908 -0.5 0.866025403784439 -0.642787609686539 0.766044443118978 -0.766044443118978 0.64278760968654 -0.866025403784438 0.500000000000001 -0.939692620785908 0.34202014332567 -0.984807753012208 0.173648177666932 -1 1.55431223447522e-15 -0.984807753012208 -0.173648177666929 -0.939692620785909 -0.342020143325667 -0.86602540378444 -0.499999999999998 -0.766044443118979 -0.642787609686538 -0.642787609686541 -0.766044443118977 -0.500000000000002 -0.866025403784437 -0.342020143325671 -0.939692620785908 -0.173648177666933 -0.984807753012208 -2.88657986402541e-15 -1 0.173648177666927 -0.984807753012209 0.342020143325666 -0.93969262078591 0.499999999999997 -0.86602540378444 0.642787609686537 -0.76604444311898 0.766044443118976 -0.642787609686542 0.866025403784437 -0.500000000000004 0.939692620785907 -0.342020143325673 0.984807753012207 -0.173648177666935 21 22 23 27 25 26 15 16 17 33 31 32 13 14 15 1 30 31 1 33 34 1 34 35 1 35 36 1 31 33 8 9 10 8 10 11 7 8 11 4 1 2 4 2 3 6 4 5 6 11 12 6 12 13 6 17 18 6 18 19 6 19 20 6 20 21 6 23 24 6 24 25 6 27 28 6 28 29 6 29 30 6 25 27 6 21 23 6 15 17 6 13 15 6 30 1 6 1 4 6 7 11 +36 34 1 0 1.57009245868378e-16 +1 -2.44929359829471e-16 -6 0.984807753012208 0.17364817766693 -6 0.939692620785908 0.342020143325669 -6 0.866025403784439 0.5 -6 0.766044443118978 0.642787609686539 -6 0.642787609686539 0.766044443118978 -6 0.5 0.866025403784439 -6 0.342020143325669 0.939692620785908 -6 0.17364817766693 0.984807753012208 -6 6.12323399573677e-17 1 -6 -0.17364817766693 0.984807753012208 -6 -0.342020143325669 0.939692620785908 -6 -0.5 0.866025403784439 -6 -0.642787609686539 0.766044443118978 -6 -0.766044443118978 0.64278760968654 -6 -0.866025403784438 0.500000000000001 -6 -0.939692620785908 0.34202014332567 -6 -0.984807753012208 0.173648177666932 -6 -1 1.45473230946492e-15 -6 -0.984807753012208 -0.173648177666929 -6 -0.939692620785909 -0.342020143325667 -6 -0.86602540378444 -0.499999999999998 -6 -0.766044443118979 -0.642787609686538 -6 -0.642787609686541 -0.766044443118977 -6 -0.500000000000002 -0.866025403784437 -6 -0.342020143325671 -0.939692620785908 -6 -0.173648177666933 -0.984807753012208 -6 -2.84823227897248e-15 -1 -6 0.173648177666927 -0.984807753012209 -6 0.342020143325666 -0.93969262078591 -6 0.499999999999997 -0.86602540378444 -6 0.642787609686536 -0.76604444311898 -6 0.766044443118976 -0.642787609686542 -6 0.866025403784437 -0.500000000000004 -6 0.939692620785907 -0.342020143325673 -6 0.984807753012207 -0.173648177666935 -6 1 -2.22044604925031e-16 0.984807753012208 0.17364817766693 0.939692620785908 0.342020143325669 0.866025403784439 0.5 0.766044443118978 0.642787609686539 0.642787609686539 0.766044443118978 0.5 0.866025403784439 0.342020143325669 0.939692620785908 0.173648177666931 0.984807753012208 0 1 -0.17364817766693 0.984807753012208 -0.342020143325669 0.939692620785908 -0.5 0.866025403784439 -0.642787609686539 0.766044443118978 -0.766044443118978 0.64278760968654 -0.866025403784438 0.500000000000001 -0.939692620785908 0.34202014332567 -0.984807753012208 0.173648177666932 -1 1.55431223447522e-15 -0.984807753012208 -0.173648177666929 -0.939692620785909 -0.342020143325667 -0.86602540378444 -0.499999999999998 -0.766044443118979 -0.642787609686538 -0.642787609686541 -0.766044443118977 -0.500000000000002 -0.866025403784437 -0.342020143325671 -0.939692620785908 -0.173648177666933 -0.984807753012208 -2.88657986402541e-15 -1 0.173648177666927 -0.984807753012209 0.342020143325666 -0.93969262078591 0.499999999999997 -0.86602540378444 0.642787609686537 -0.76604444311898 0.766044443118976 -0.642787609686542 0.866025403784437 -0.500000000000004 0.939692620785907 -0.342020143325673 0.984807753012207 -0.173648177666935 21 22 23 27 25 26 15 16 17 33 31 32 13 14 15 1 30 31 1 33 34 1 34 35 1 35 36 1 31 33 8 9 10 8 10 11 7 8 11 4 1 2 4 2 3 6 4 5 6 11 12 6 12 13 6 17 18 6 18 19 6 19 20 6 20 21 6 23 24 6 24 25 6 27 28 6 28 29 6 29 30 6 25 27 6 21 23 6 15 17 6 13 15 6 30 1 6 1 4 6 7 11 + +TShapes 58 +Ve +1e-07 +-5 -5 10 +0 0 + +0101101 +* +Ve +1e-07 +-5 -5 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 1 0 0 10 +2 1 1 0 0 10 +2 2 2 0 0 10 +6 1 1 0 +6 2 2 0 +0 + +0101000 +-58 0 +57 0 * +Ve +1e-07 +-5 5 10 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 2 0 0 10 +2 3 1 0 0 10 +2 4 3 0 0 10 +6 3 1 0 +6 4 3 0 +0 + +0101000 +-55 0 +58 0 * +Ve +1e-07 +-5 5 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 3 0 0 10 +2 5 1 0 0 10 +2 6 4 0 0 10 +6 5 1 0 +6 6 4 0 +0 + +0101000 +-55 0 +53 0 * +Ed + 1e-07 1 1 0 +1 4 0 0 10 +2 7 1 0 0 10 +2 8 5 0 0 10 +6 7 1 0 +6 8 5 0 +0 + +0101000 +-53 0 +57 0 * +Wi + +0101100 +-56 0 -54 0 +52 0 +51 0 * +Fa +0 1e-07 1 0 +2 1 +0101000 ++50 0 * +Ve +1e-07 +5 -5 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 5 0 0 10 +2 9 2 0 0 10 +2 10 5 0 0 10 +6 9 2 0 +6 10 5 0 +0 + +0101000 +-48 0 +57 0 * +Ve +1e-07 +5 -5 10 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 6 0 0 10 +2 11 6 0 0 10 +2 12 2 0 0 10 +6 11 2 0 +6 12 6 0 +0 + +0101000 +-46 0 +48 0 * +Ed + 1e-07 1 1 0 +1 7 0 0 10 +2 13 2 0 0 10 +2 14 3 0 0 10 +6 13 2 0 +6 14 3 0 +0 + +0101000 +-46 0 +58 0 * +Wi + +0101100 +-47 0 -45 0 +44 0 +56 0 * +Fa +0 1e-07 2 0 +2 2 +0101000 ++43 0 * +Ve +1e-07 +5 5 10 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 8 0 0 10 +2 15 4 0 0 10 +2 16 3 0 0 10 +6 15 3 0 +6 16 4 0 +0 + +0101000 +-41 0 +55 0 * +Ed + 1e-07 1 1 0 +1 9 0 0 10 +2 17 6 0 0 10 +2 18 3 0 0 10 +6 17 3 0 +6 18 6 0 +0 + +0101000 +-41 0 +46 0 * +Wi + +0101100 +-54 0 -40 0 +39 0 +44 0 * +Fa +0 1e-07 3 0 +2 3 +0101000 ++38 0 * +Ve +1e-07 +5 5 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 10 0 0 10 +2 19 4 0 0 10 +2 20 5 0 0 10 +6 19 4 0 +6 20 5 0 +0 + +0101000 +-36 0 +53 0 * +Ed + 1e-07 1 1 0 +1 11 0 0 10 +2 21 6 0 0 10 +2 22 4 0 0 10 +6 21 4 0 +6 22 6 0 +0 + +0101000 +-41 0 +36 0 * +Wi + +0101100 +-35 0 -34 0 +40 0 +52 0 * +Fa +0 1e-07 4 0 +2 4 +0101000 ++33 0 * +Ed + 1e-07 1 1 0 +1 12 0 0 10 +2 23 6 0 0 10 +2 24 5 0 0 10 +6 23 5 0 +6 24 6 0 +0 + +0101000 +-36 0 +48 0 * +Wi + +0101100 +-51 0 -35 0 +47 0 +31 0 * +Ve +1.00000000244929e-07 +1 -2.44929359829471e-16 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 13 0 0 6.28318530717959 +2 25 5 0 0 6.28318530717959 +2 26 7 0 0 6.28318530717959 +6 25 7 0 +6 26 8 0 +6 27 5 0 +6 28 9 0 +0 + +0101000 ++29 0 -29 0 * +Wi + +0101100 +-28 0 * +Fa +0 1e-07 5 0 +2 5 +0101000 ++30 0 +27 0 * +Wi + +0101100 +-45 0 -39 0 +34 0 +31 0 * +Fa +0 1e-07 6 0 +2 6 +0101000 ++25 0 * +Ve +1e-07 +1 -2.44929359829471e-16 3 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 14 0 6 9 +3 27 28CN 7 0 6 9 +7 29 30 7 0 +0 + +0101000 ++29 0 -23 0 * +Ed + 1e-07 1 1 0 +1 15 0 0 6.28318530717959 +2 29 7 0 0 6.28318530717959 +2 30 8 0 0 6.28318530717959 +6 31 7 0 +6 32 10 0 +0 + +0101000 ++23 0 -23 0 * +Wi + +0101100 ++22 0 +28 0 -22 0 -21 0 * +Fa +0 1e-07 7 0 +2 7 +0101000 ++20 0 * +Wi + +0101100 ++21 0 * +Fa +0 1e-07 8 0 +2 10 +0101000 ++18 0 * +Sh + +0101100 +-49 0 -42 0 +37 0 +32 0 -26 0 +24 0 -19 0 -17 0 * +So + +0100000 ++16 0 * +Wi + +0101100 ++28 0 * +Fa +0 1e-07 5 0 +2 9 +0101000 ++14 0 * +Sh + +0101100 +-13 0 +19 0 +17 0 * +So + +0100000 ++12 0 * +Ve +1e-07 +1 -2.44929359829471e-16 -6 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 14 0 0 6 +3 27 28CN 7 0 0 6 +7 33 34 8 0 +0 + +0101000 ++10 0 -29 0 * +Ed + 1e-07 1 1 0 +1 16 0 0 6.28318530717959 +2 31 7 0 0 6.28318530717959 +2 32 9 0 0 6.28318530717959 +6 35 8 0 +6 36 11 0 +0 + +0101000 ++10 0 -10 0 * +Wi + +0101100 +-28 0 +9 0 +8 0 -9 0 * +Fa +0 1e-07 7 0 +2 8 +0101000 ++7 0 * +Wi + +0101100 +-8 0 * +Fa +0 1e-07 9 0 +2 11 +0101000 +-5 0 * +Sh + +0101100 ++6 0 +13 0 -4 0 * +So + +0100000 ++3 0 * +Co + +1100000 ++15 0 +11 0 +2 0 * + ++1 0 \ No newline at end of file diff --git a/test/data/HexahedronTest/Sphere.brep b/test/data/HexahedronTest/Sphere.brep new file mode 100644 index 000000000..e404f6d4a --- /dev/null +++ b/test/data/HexahedronTest/Sphere.brep @@ -0,0 +1,73 @@ +DBRep_DrawableShape + +CASCADE Topology V3, (c) Open Cascade +Locations 0 +Curve2ds 4 +1 0 1.5707963267948966 1 0 +1 6.2831853071795862 -6.2831853071795862 0 1 +1 0 -6.2831853071795862 0 1 +1 0 -1.5707963267948966 1 0 +Curves 1 +2 0 0 0 -2.4492935982947064e-16 -1 0 1 -2.4492935982947064e-16 0 0 0 1 100 +Polygon3D 0 +PolygonOnTriangulations 0 +Surfaces 1 +4 0 0 0 0 0 1 1 0 -0 -0 1 0 100 +Triangulations 0 + +TShapes 9 +Ve +1e-07 +6.12323399573677e-15 -1.49975978266186e-30 100 +0 0 + +0101101 +* +Ed + 1e-07 1 1 1 +2 1 1 0 0 6.28318530717959 +0 + +0101000 ++9 0 -9 0 * +Ve +1e-07 +6.12323399573677e-15 -1.49975978266186e-30 -100 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 1 0 4.71238898038469 7.85398163397448 +3 2 3CN 1 0 4.71238898038469 7.85398163397448 +0 + +0101000 +-9 0 +7 0 * +Ed + 1e-07 1 1 1 +2 4 1 0 0 6.28318530717959 +0 + +0101000 ++7 0 -7 0 * +Wi + +0101100 +-8 0 +6 0 +5 0 -6 0 * +Fa +0 1e-07 1 0 + +0111000 ++4 0 * +Sh + +0101100 ++3 0 * +So + +1100000 ++2 0 * + ++1 0 \ No newline at end of file diff --git a/test/tests.set b/test/tests.set index f1a155efc..185fe644a 100644 --- a/test/tests.set +++ b/test/tests.set @@ -127,6 +127,10 @@ SET(CPP_TESTS SMESH_RegularGridTest ) +SET(UNIT_TESTS # Any unit test add in src names space should be added here + HexahedronTest +) + # The following tests can be executed without driver, just by python. # ----------------------------------------------------------------------------