From 9f7d4a55e29a362d5518b00ccb6cc05001d62d59 Mon Sep 17 00:00:00 2001 From: cconopoima Date: Sun, 26 May 2024 13:04:12 +0100 Subject: [PATCH 01/15] [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. --- .vscode/settings.json | 73 + src/CMakeLists.txt | 1 + src/SMESH/SMESH_Hypothesis.cxx | 43 +- src/StdMeshers.test/CMakeLists.txt | 73 + src/StdMeshers.test/HexahedronTest.cxx | 163 + src/StdMeshers.test/tests.set | 27 + src/StdMeshers/CMakeLists.txt | 4 + src/StdMeshers/StdMeshers_Cartesian_3D.cxx | 6645 +---------------- .../StdMeshers_Cartesian_3D_Grid.cxx | 1090 +++ .../StdMeshers_Cartesian_3D_Grid.hxx | 991 +++ .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 4226 +++++++++++ .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 503 ++ test/CTestTestfileInstall.cmake | 7 + test/data/HexahedronTest/NRTM1.brep | 558 ++ test/data/HexahedronTest/Sphere.brep | 73 + test/tests.set | 4 + 16 files changed, 7847 insertions(+), 6634 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/StdMeshers.test/CMakeLists.txt create mode 100644 src/StdMeshers.test/HexahedronTest.cxx create mode 100644 src/StdMeshers.test/tests.set create mode 100644 src/StdMeshers/StdMeshers_Cartesian_3D_Grid.cxx create mode 100644 src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx create mode 100644 src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx create mode 100644 src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx create mode 100644 test/data/HexahedronTest/NRTM1.brep create mode 100644 test/data/HexahedronTest/Sphere.brep 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,91 +25,18 @@ #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 - -#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 +// StdMeshersAlgos +#include"StdMeshers_Cartesian_3D_Hexahedron.hxx" //STD #include -#include #include +// BOOST #include -#ifdef _DEBUG_ -// #define _MY_DEBUG_ -// #undef WITH_TBB -#endif - #ifdef WITH_TBB #ifdef WIN32 @@ -118,14 +45,28 @@ #define WINVER 0x0A00 #define _WIN32_WINNT 0x0A00 #endif -#include + #include +//#include #endif using namespace std; using namespace SMESH; -std::mutex _eMutex; -std::mutex _bMutex; +using namespace gridtools; +// using namespace facegridintersector; +namespace +{ + /*! + * \brief Temporary mesh to hold + */ + struct TmpMesh: public SMESH_Mesh + { + TmpMesh() { + _isShapeToMesh = (_id = 0); + _meshDS = new SMESHDS_Mesh( _id, true ); + } + }; +} // namespace //============================================================================= /*! @@ -184,6453 +125,7 @@ bool StdMeshers_Cartesian_3D::CheckHypothesis (SMESH_Mesh& aMesh, 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; - } - - //================================================================================ - /*! - * \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(); - } - - //================================================================================ - /*! - * \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; - } - -} // 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. # ---------------------------------------------------------------------------- From f7e94c2a07fa701bd95d80b2615c954cec7f5f01 Mon Sep 17 00:00:00 2001 From: jfa Date: Thu, 11 Jul 2024 16:46:22 +0100 Subject: [PATCH 02/15] Add some unit tests --- src/StdMeshers.test/HexahedronTest.cxx | 170 ++++++++++++++++-- test/data/HexahedronTest/NRTM1.brep | 229 ++++++------------------- 2 files changed, 216 insertions(+), 183 deletions(-) diff --git a/src/StdMeshers.test/HexahedronTest.cxx b/src/StdMeshers.test/HexahedronTest.cxx index 8467ac857..e5037897f 100644 --- a/src/StdMeshers.test/HexahedronTest.cxx +++ b/src/StdMeshers.test/HexahedronTest.cxx @@ -29,10 +29,14 @@ #include // OCC -#include -#include +#include +#include +#include +#include +#include +#include -#include +#include #include // Helper functions! @@ -72,8 +76,12 @@ void loadBrepShape( std::string shapeName, TopoDS_Shape & shape ) } // 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 ) +void GridInitAndIntersectWithShape( Grid& grid, + double gridSpacing, + double theSizeThreshold, + const TopoDS_Shape theShape, + std::map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, + const int /*numOfThreads*/ ) { std::vector< TopoDS_Shape > faceVec; TopTools_MapOfShape faceMap; @@ -110,7 +118,7 @@ void GridInitAndInterserctWithShape( Grid& grid, double gridSpacing, TopoDS_Shap 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->SetSizeThreshold(theSizeThreshold); // set threshold myHypo->GetCoordinates(xCoords, yCoords, zCoords, shapeBox); grid.SetCoordinates( xCoords, yCoords, zCoords, axisDirs, shapeBox ); @@ -146,7 +154,7 @@ bool testNRTM1() grid.InitGeometry( myShape ); std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; - GridInitAndInterserctWithShape( grid, 1.0, myShape, edge2faceIDsMap, numOfThreads ); + GridInitAndIntersectWithShape( grid, 1.0, 4.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 ); @@ -155,9 +163,151 @@ bool testNRTM1() return true; } +// Test fitting of the given shape +bool testShape (const TopoDS_Shape theShape, + const bool toAddEdges, + const bool toCreateFaces, + const double theGridSpacing, + const double theSizeThreshold, + const int theNbCreatedExpected) +{ + std::unique_ptr aMesh( new SMESH_Mesh_Test() ); + aMesh->ShapeToMesh( theShape ); + SMESH_MesherHelper helper( *aMesh ); + Grid grid; + grid._helper = &helper; + grid._toAddEdges = toAddEdges; + grid._toCreateFaces = toCreateFaces; + grid._toConsiderInternalFaces = false; + grid._toUseThresholdForInternalFaces = false; + grid._toUseQuanta = false; + grid._sizeThreshold = theSizeThreshold; + grid.InitGeometry( theShape ); + + std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; + GridInitAndIntersectWithShape( grid, theGridSpacing, theSizeThreshold, + theShape, edge2faceIDsMap, 1 ); + Hexahedron hex( &grid ); + int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, 1 ); + if (nbAdded != theNbCreatedExpected) { + std::stringstream buffer; + buffer << "Number of computed elements does not match: obtained " << nbAdded << " != expected " << theNbCreatedExpected; + //CPPUNIT_ASSERT_MESSAGE(buffer.str().c_str(), nbAdded == theNbCreatedExpected ); + //MESSAGE(buffer.str().c_str()); + //CppUnitTestFramework::Logger::WriteMessage(buffer.str().c_str()); + std::cerr << buffer.str() << std::endl; + return false; + } + + return true; +} + +// Test some primitive shapes +bool testPrimitives() +{ + bool isOK = true; + + // Test fitting of a box + BRepPrimAPI_MakeBox aMakeBox (10, 20, 30); + aMakeBox.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the box!", aMakeBox.IsDone() ); + TopoDS_Shape aShape = aMakeBox.Shape(); + + // Test exact fitting of a box + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/true, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) + isOK = false; + + // TODO: debug this case + //if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/true, + // /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/8)) + // isOK = false; + + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/5, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48)) + isOK = false; + + // Test not exact fitting of a box + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/7, /*theSizeThreshold*/4, /*theNbCreatedExpected*/12)) + isOK = false; + + // Test fitting of a cylinder + gp_Ax2 anAxes (gp::Origin(), gp::DZ()); + BRepPrimAPI_MakeCylinder aMakeCyl (anAxes, 20., 30.); + aMakeCyl.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cylinder!", aMakeCyl.IsDone() ); + aShape = aMakeCyl.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/2, /*theNbCreatedExpected*/36)) + isOK = false; + + // Test fitting of a sphere + BRepPrimAPI_MakeSphere aMakeSph (anAxes, 30.); + aMakeSph.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the sphere!", aMakeSph.IsDone() ); + aShape = aMakeSph.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/136)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/2, /*theNbCreatedExpected*/88)) + isOK = false; + + // Test fitting of a cone + BRepPrimAPI_MakeCone aMakeCon (anAxes, 30., 0., 40.); + aMakeCon.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); + aShape = aMakeCon.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/100, /*theNbCreatedExpected*/72)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/40)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/1.5, /*theNbCreatedExpected*/32)) + isOK = false; + + // truncated cone + aMakeCon = BRepPrimAPI_MakeCone(anAxes, 30., 15., 20.); + aMakeCon.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); + aShape = aMakeCon.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/100, /*theNbCreatedExpected*/56)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/36)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/1.5, /*theNbCreatedExpected*/28)) + isOK = false; + + return isOK; +} + // Entry point for test int main() { - auto t0 = testNRTM1(); - return 0; -} \ No newline at end of file + bool isOK = testNRTM1(); + + if (!testPrimitives()) + isOK = false; + + return isOK ? 0 : 1; +} diff --git a/test/data/HexahedronTest/NRTM1.brep b/test/data/HexahedronTest/NRTM1.brep index 2508f0ca6..1807a694d 100644 --- a/test/data/HexahedronTest/NRTM1.brep +++ b/test/data/HexahedronTest/NRTM1.brep @@ -1,8 +1,8 @@ DBRep_DrawableShape -CASCADE Topology V3, (c) Open Cascade +CASCADE Topology V1, (c) Matra-Datavision Locations 0 -Curve2ds 32 +Curve2ds 36 1 0 0 1 0 1 0 0 1 0 1 10 0 0 -1 @@ -13,29 +13,33 @@ Curve2ds 32 1 0 0 0 1 1 0 0 0 1 1 0 0 1 0 +1 0 10 1 0 +1 0 0 1 0 +1 10 0 0 1 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 0 -10 1 0 1 10 0 0 1 +1 0 0 0 -1 +2 5 5 1 0 -0 1 1 +1 0 6 1 0 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 6.2831853071795862 -0 0 1 +1 0 -0 0 1 1 0 0 1 0 2 0 0 1 0 -0 1 1 -Curves 16 +Curves 17 1 -5 -5 0 0 0 1 1 -5 -5 10 -0 1 0 1 -5 5 0 0 0 1 @@ -51,82 +55,11 @@ Curves 16 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 +1 1 -2.4492935982947064e-16 -6 0 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 +PolygonOnTriangulations 0 +Surfaces 11 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 @@ -134,31 +67,11 @@ Surfaces 9 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 -5 -5 0 0 0 1 1 0 -0 -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 +Triangulations 0 TShapes 58 Ve @@ -180,8 +93,6 @@ Ed 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 @@ -198,8 +109,6 @@ Ed 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 @@ -216,8 +125,6 @@ Ed 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 @@ -227,8 +134,6 @@ Ed 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 @@ -239,7 +144,7 @@ Wi -56 0 -54 0 +52 0 +51 0 * Fa 0 1e-07 1 0 -2 1 + 0101000 +50 0 * Ve @@ -254,8 +159,6 @@ Ed 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 @@ -270,10 +173,8 @@ Ve 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 +2 11 2 0 0 10 +2 12 6 0 0 10 0 0101000 @@ -283,8 +184,6 @@ Ed 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 @@ -295,7 +194,7 @@ Wi -47 0 -45 0 +44 0 +56 0 * Fa 0 1e-07 2 0 -2 2 + 0101000 +43 0 * Ve @@ -308,10 +207,8 @@ Ve 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 +2 15 3 0 0 10 +2 16 4 0 0 10 0 0101000 @@ -319,10 +216,8 @@ Ed 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 +2 17 3 0 0 10 +2 18 6 0 0 10 0 0101000 @@ -333,7 +228,7 @@ Wi -54 0 -40 0 +39 0 +44 0 * Fa 0 1e-07 3 0 -2 3 + 0101000 +38 0 * Ve @@ -348,8 +243,6 @@ Ed 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 @@ -357,10 +250,8 @@ Ed 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 +2 21 4 0 0 10 +2 22 6 0 0 10 0 0101000 @@ -371,16 +262,14 @@ Wi -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 +2 23 5 0 0 10 +2 24 6 0 0 10 0 0101000 @@ -401,10 +290,8 @@ Ed 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 +2 27 8 0 0 6.28318530717959 +2 28 9 0 0 6.28318530717959 0 0101000 @@ -415,7 +302,7 @@ Wi -28 0 * Fa 0 1e-07 5 0 -2 5 + 0101000 +30 0 +27 0 * Wi @@ -424,7 +311,7 @@ Wi -45 0 -39 0 +34 0 +31 0 * Fa 0 1e-07 6 0 -2 6 + 0101000 +25 0 * Ve @@ -437,8 +324,7 @@ Ve Ed 1e-07 1 1 0 1 14 0 6 9 -3 27 28CN 7 0 6 9 -7 29 30 7 0 +3 29 30CN 7 0 6 9 0 0101000 @@ -446,10 +332,8 @@ Ed 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 +2 31 7 0 0 6.28318530717959 +2 32 10 0 0 6.28318530717959 0 0101000 @@ -460,7 +344,7 @@ Wi +22 0 +28 0 -22 0 -21 0 * Fa 0 1e-07 7 0 -2 7 + 0101000 +20 0 * Wi @@ -468,8 +352,8 @@ Wi 0101100 +21 0 * Fa -0 1e-07 8 0 -2 10 +0 1e-07 10 0 + 0101000 +18 0 * Sh @@ -485,8 +369,8 @@ Wi 0101100 +28 0 * Fa -0 1e-07 5 0 -2 9 +0 1e-07 8 0 + 0101000 +14 0 * Sh @@ -506,20 +390,17 @@ Ve * Ed 1e-07 1 1 0 -1 14 0 0 6 -3 27 28CN 7 0 0 6 -7 33 34 8 0 +1 16 0 0 6 +3 33 34CN 9 0 0 6 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 +1 17 0 0 6.28318530717959 +2 35 9 0 0 6.28318530717959 +2 36 11 0 0 6.28318530717959 0 0101000 @@ -529,8 +410,8 @@ Wi 0101100 -28 0 +9 0 +8 0 -9 0 * Fa -0 1e-07 7 0 -2 8 +0 1e-07 9 0 + 0101000 +7 0 * Wi @@ -538,8 +419,8 @@ Wi 0101100 -8 0 * Fa -0 1e-07 9 0 -2 11 +0 1e-07 11 0 + 0101000 -5 0 * Sh @@ -555,4 +436,6 @@ Co 1100000 +15 0 +11 0 +2 0 * -+1 0 \ No newline at end of file ++1 0 +0 + From 135a1513d49fa6b85e8d0ac98c43c6d0502f0f16 Mon Sep 17 00:00:00 2001 From: cconopoima Date: Fri, 12 Jul 2024 13:29:04 +0100 Subject: [PATCH 03/15] Separating unit test for imported geometries and canonical shapes. Small modif on the numofthreads vector to avoid slow test run on machines with few cores. Erasing sporious files. --- .vscode/settings.json | 73 -- src/StdMeshers.test/CMakeLists.txt | 21 +- .../HexahedronCanonicalShapesTest.cxx | 276 ++++++ src/StdMeshers.test/HexahedronTest.cxx | 194 ++--- src/StdMeshers.test/tests.set | 1 + test/data/HexahedronTest/NRTMJ4.brep | 796 ++++++++++++++++++ test/data/HexahedronTest/Sphere.brep | 73 -- test/tests.set | 1 + 8 files changed, 1131 insertions(+), 304 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx create mode 100644 test/data/HexahedronTest/NRTMJ4.brep delete mode 100644 test/data/HexahedronTest/Sphere.brep diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d373bc121..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "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/StdMeshers.test/CMakeLists.txt b/src/StdMeshers.test/CMakeLists.txt index ee9e01335..bda437fc7 100644 --- a/src/StdMeshers.test/CMakeLists.txt +++ b/src/StdMeshers.test/CMakeLists.txt @@ -58,16 +58,13 @@ FOREACH(_test ${UNIT_TESTS}) 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) - +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 ${UNIT_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/HexahedronCanonicalShapesTest.cxx b/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx new file mode 100644 index 000000000..fe3ab4c95 --- /dev/null +++ b/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx @@ -0,0 +1,276 @@ +// 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 : HexahedronCanonicalShapesTest.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 +#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 Initialize the grid and intesersectors of grid with the geometry + */ +void GridInitAndIntersectWithShape( Grid& grid, + double gridSpacing, + double theSizeThreshold, + const 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(theSizeThreshold); // 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 ); +} + + +/*! + * \brief Test runner + */ +bool testShape (const TopoDS_Shape theShape, + const bool toAddEdges, + const bool toCreateFaces, + const double theGridSpacing, + const double theSizeThreshold, + const int theNbCreatedExpected) +{ + std::unique_ptr aMesh( new SMESH_Mesh_Test() ); + aMesh->ShapeToMesh( theShape ); + SMESH_MesherHelper helper( *aMesh ); + Grid grid; + grid._helper = &helper; + grid._toAddEdges = toAddEdges; + grid._toCreateFaces = toCreateFaces; + grid._toConsiderInternalFaces = false; + grid._toUseThresholdForInternalFaces = false; + grid._toUseQuanta = false; + grid._sizeThreshold = theSizeThreshold; + grid.InitGeometry( theShape ); + + std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; + GridInitAndIntersectWithShape( grid, theGridSpacing, theSizeThreshold, + theShape, edge2faceIDsMap, 1 ); + Hexahedron hex( &grid ); + int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, 1 ); + if (nbAdded != theNbCreatedExpected) { + std::stringstream buffer; + buffer << "Number of computed elements does not match: obtained " << nbAdded << " != expected " << theNbCreatedExpected; + //CPPUNIT_ASSERT_MESSAGE(buffer.str().c_str(), nbAdded == theNbCreatedExpected ); + //MESSAGE(buffer.str().c_str()); + //CppUnitTestFramework::Logger::WriteMessage(buffer.str().c_str()); + std::cerr << buffer.str() << std::endl; + return false; + } + + return true; +} + +/*! + * \brief Test some primitive shapes + */ +bool testPrimitives() +{ + bool isOK = true; + + // Test fitting of a box + BRepPrimAPI_MakeBox aMakeBox (10, 20, 30); + aMakeBox.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the box!", aMakeBox.IsDone() ); + TopoDS_Shape aShape = aMakeBox.Shape(); + + // Test exact fitting of a box + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/true, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) + isOK = false; + + // TODO: debug this case + //if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/true, + // /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/8)) + // isOK = false; + + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/5, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48)) + isOK = false; + + // Test not exact fitting of a box + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/7, /*theSizeThreshold*/4, /*theNbCreatedExpected*/12)) + isOK = false; + + // Test fitting of a cylinder + gp_Ax2 anAxes (gp::Origin(), gp::DZ()); + BRepPrimAPI_MakeCylinder aMakeCyl (anAxes, 20., 30.); + aMakeCyl.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cylinder!", aMakeCyl.IsDone() ); + aShape = aMakeCyl.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/2, /*theNbCreatedExpected*/36)) + isOK = false; + + // Test fitting of a sphere + BRepPrimAPI_MakeSphere aMakeSph (anAxes, 30.); + aMakeSph.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the sphere!", aMakeSph.IsDone() ); + aShape = aMakeSph.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/136)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/2, /*theNbCreatedExpected*/88)) + isOK = false; + + // Test fitting of a cone + BRepPrimAPI_MakeCone aMakeCon (anAxes, 30., 0., 40.); + aMakeCon.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); + aShape = aMakeCon.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/100, /*theNbCreatedExpected*/72)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/40)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/1.5, /*theNbCreatedExpected*/32)) + isOK = false; + + // truncated cone + aMakeCon = BRepPrimAPI_MakeCone(anAxes, 30., 15., 20.); + aMakeCon.Build(); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); + aShape = aMakeCon.Shape(); + + // test for different threshold values + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/100, /*theNbCreatedExpected*/56)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/36)) + isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, + /*gridSpacing*/10, /*theSizeThreshold*/1.5, /*theNbCreatedExpected*/28)) + isOK = false; + + return isOK; +} + +// Entry point for test +int main() +{ + bool isOK = testPrimitives(); + return isOK ? 0 : 1; +} diff --git a/src/StdMeshers.test/HexahedronTest.cxx b/src/StdMeshers.test/HexahedronTest.cxx index e5037897f..e993a96f5 100644 --- a/src/StdMeshers.test/HexahedronTest.cxx +++ b/src/StdMeshers.test/HexahedronTest.cxx @@ -75,7 +75,9 @@ void loadBrepShape( std::string shapeName, TopoDS_Shape & shape ) BRepTools::Read(shape, shapeName.c_str(), b); } -// Initialize the grid and intesersectors of grid with the geometry +/*! + * \brief Initialize the grid and intesersectors of grid with the geometry + */ void GridInitAndIntersectWithShape( Grid& grid, double gridSpacing, double theSizeThreshold, @@ -132,14 +134,18 @@ void GridInitAndIntersectWithShape( Grid& grid, 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. +/*! + * \brief 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} ) + const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency(); + std::vector numberOfThreads(numOfCores); + std::iota (std::begin(numberOfThreads), std::end(numberOfThreads), 1); + + for (auto nThreads : numberOfThreads ) { - for (size_t i = 0; i < 10; i++) + for (size_t i = 0; i < 10 /*trials*/; i++) { TopoDS_Shape myShape; loadBrepShape( "data/HexahedronTest/NRTM1.brep", myShape ); @@ -154,160 +160,56 @@ bool testNRTM1() grid.InitGeometry( myShape ); std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; - GridInitAndIntersectWithShape( grid, 1.0, 4.0, myShape, edge2faceIDsMap, numOfThreads ); + GridInitAndIntersectWithShape( grid, 1.0, 4.0, myShape, edge2faceIDsMap, nThreads ); Hexahedron hex( &grid ); - int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, numOfThreads ); + int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads ); CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 1024 ); } } return true; } -// Test fitting of the given shape -bool testShape (const TopoDS_Shape theShape, - const bool toAddEdges, - const bool toCreateFaces, - const double theGridSpacing, - const double theSizeThreshold, - const int theNbCreatedExpected) +/*! + * \brief Reproduce conditions of TBPERF_GRIDS_PERF_SMESH_J4 test to detect and solve segfault in unit test. + */ +bool testNRTJ4() { - std::unique_ptr aMesh( new SMESH_Mesh_Test() ); - aMesh->ShapeToMesh( theShape ); - SMESH_MesherHelper helper( *aMesh ); - Grid grid; - grid._helper = &helper; - grid._toAddEdges = toAddEdges; - grid._toCreateFaces = toCreateFaces; - grid._toConsiderInternalFaces = false; - grid._toUseThresholdForInternalFaces = false; - grid._toUseQuanta = false; - grid._sizeThreshold = theSizeThreshold; - grid.InitGeometry( theShape ); - - std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; - GridInitAndIntersectWithShape( grid, theGridSpacing, theSizeThreshold, - theShape, edge2faceIDsMap, 1 ); - Hexahedron hex( &grid ); - int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, 1 ); - if (nbAdded != theNbCreatedExpected) { - std::stringstream buffer; - buffer << "Number of computed elements does not match: obtained " << nbAdded << " != expected " << theNbCreatedExpected; - //CPPUNIT_ASSERT_MESSAGE(buffer.str().c_str(), nbAdded == theNbCreatedExpected ); - //MESSAGE(buffer.str().c_str()); - //CppUnitTestFramework::Logger::WriteMessage(buffer.str().c_str()); - std::cerr << buffer.str() << std::endl; - return false; - } + const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency()/2; + std::vector numberOfThreads(numOfCores); + std::iota (std::begin(numberOfThreads), std::end(numberOfThreads), 1); + // Test with face creation + for (auto nThreads : numberOfThreads ) + { + for (size_t i = 0; i < 10 /*trials*/; i++) + { + TopoDS_Shape myShape; + loadBrepShape( "data/HexahedronTest/NRTMJ4.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._toConsiderInternalFaces = false; grid._toUseThresholdForInternalFaces = false; grid._toUseQuanta = false; + double testThreshold = 1.000001; + grid._toCreateFaces = true; + grid._sizeThreshold = testThreshold; + grid.InitGeometry( myShape ); + + std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; + GridInitAndIntersectWithShape( grid, 2.0, testThreshold, myShape, edge2faceIDsMap, nThreads ); + Hexahedron hex( &grid ); + int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads ); + CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 35150 ); + } + } return true; } -// Test some primitive shapes -bool testPrimitives() -{ - bool isOK = true; - - // Test fitting of a box - BRepPrimAPI_MakeBox aMakeBox (10, 20, 30); - aMakeBox.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the box!", aMakeBox.IsDone() ); - TopoDS_Shape aShape = aMakeBox.Shape(); - - // Test exact fitting of a box - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) - isOK = false; - if (!testShape (aShape, /*toAddEdges*/true, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) - isOK = false; - - // TODO: debug this case - //if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/true, - // /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/8)) - // isOK = false; - - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/5, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48)) - isOK = false; - - // Test not exact fitting of a box - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/7, /*theSizeThreshold*/4, /*theNbCreatedExpected*/12)) - isOK = false; - - // Test fitting of a cylinder - gp_Ax2 anAxes (gp::Origin(), gp::DZ()); - BRepPrimAPI_MakeCylinder aMakeCyl (anAxes, 20., 30.); - aMakeCyl.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the cylinder!", aMakeCyl.IsDone() ); - aShape = aMakeCyl.Shape(); - - // test for different threshold values - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48)) - isOK = false; - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/2, /*theNbCreatedExpected*/36)) - isOK = false; - - // Test fitting of a sphere - BRepPrimAPI_MakeSphere aMakeSph (anAxes, 30.); - aMakeSph.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the sphere!", aMakeSph.IsDone() ); - aShape = aMakeSph.Shape(); - - // test for different threshold values - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/136)) - isOK = false; - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/2, /*theNbCreatedExpected*/88)) - isOK = false; - - // Test fitting of a cone - BRepPrimAPI_MakeCone aMakeCon (anAxes, 30., 0., 40.); - aMakeCon.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); - aShape = aMakeCon.Shape(); - - // test for different threshold values - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/100, /*theNbCreatedExpected*/72)) - isOK = false; - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/40)) - isOK = false; - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/1.5, /*theNbCreatedExpected*/32)) - isOK = false; - - // truncated cone - aMakeCon = BRepPrimAPI_MakeCone(anAxes, 30., 15., 20.); - aMakeCon.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); - aShape = aMakeCon.Shape(); - - // test for different threshold values - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/100, /*theNbCreatedExpected*/56)) - isOK = false; - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/36)) - isOK = false; - if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, - /*gridSpacing*/10, /*theSizeThreshold*/1.5, /*theNbCreatedExpected*/28)) - isOK = false; - - return isOK; -} - // Entry point for test int main() { - bool isOK = testNRTM1(); - - if (!testPrimitives()) - isOK = false; - + bool isOK = testNRTM1() && testNRTJ4(); return isOK ? 0 : 1; } diff --git a/src/StdMeshers.test/tests.set b/src/StdMeshers.test/tests.set index 26e79035d..df1ddaa09 100644 --- a/src/StdMeshers.test/tests.set +++ b/src/StdMeshers.test/tests.set @@ -24,4 +24,5 @@ SET(UNIT_TESTS HexahedronTest + HexahedronCanonicalShapesTest ) \ No newline at end of file diff --git a/test/data/HexahedronTest/NRTMJ4.brep b/test/data/HexahedronTest/NRTMJ4.brep new file mode 100644 index 000000000..3ed6ccc59 --- /dev/null +++ b/test/data/HexahedronTest/NRTMJ4.brep @@ -0,0 +1,796 @@ +DBRep_DrawableShape + +CASCADE Topology V3, (c) Open Cascade +Locations 0 +Curve2ds 59 +1 0 0 1 0 +1 0 0 1 0 +1 100 0 0 -1 +1 0 0 0 1 +1 0 -50 1 0 +1 0 0 1 0 +1 0 0 0 -1 +1 0 0 0 1 +1 0 0 1 0 +1 0 60 1 0 +1 0 0 0 1 +1 0 0 1 0 +1 16.899999999999999 50 0 1 +1 0 0 1 0 +1 0 0 1 0 +1 16.899999999999999 50 1 0 +1 59.899999999999999 0 0 1 +1 0 0 1 0 +1 76.799999999999997 50 0 1 +1 100 0 0 1 +1 0 0 1 0 +1 0 0 0 1 +1 0 0 1 0 +1 100 0 0 1 +1 0 50 1 0 +1 100 0 0 -1 +1 60 0 0 1 +1 0 -50 1 0 +1 0 60 1 0 +1 0 0 0 1 +1 0 50 1 0 +1 16.899999999999999 50 0 1 +1 0 -50 1 0 +1 0 0 1 0 +1 16.899999999999999 50 1 0 +1 59.899999999999999 0 0 1 +1 0 50 1 0 +1 76.799999999999997 50 0 1 +1 0 0 0 1 +1 0 50 1 0 +1 0 0 0 -1 +1 60 0 0 1 +1 0 0 1 0 +2 0 0 1 0 -0 1 10 +2 25 25 -1 0 0 1 10 +1 0 0 0 -1 +1 10 0 0 1 +1 16.899999999999999 0 0 -1 +1 0 0 0 -1 +1 0 0 0 1 +1 59.899999999999999 0 0 -1 +1 0 0 0 1 +1 59.899999999999999 0 0 -1 +1 10 0 0 1 +1 76.799999999999997 0 0 -1 +1 0 50 1 0 +2 0 0 1 0 -0 1 10 +1 6.2831853071795862 -0 0 1 +1 0 -0 0 1 +Curves 25 +1 0 0 0 0 0 1 +1 0 0 100 -0 1 0 +1 0 50 0 0 0 1 +1 0 0 0 -0 1 0 +1 60 0 0 0 0 1 +1 50 0 16.899999999999999 1 0 -0 +1 50 0 16.899999999999999 0 0 1 +1 50 0 76.799999999999997 1 0 -0 +1 0 0 100 1 0 -0 +1 0 0 0 1 0 -0 +1 0 50 100 1 0 -0 +1 60 0 100 -0 1 0 +1 60 50 0 0 0 1 +1 50 50 16.899999999999999 1 0 -0 +1 50 50 16.899999999999999 0 0 1 +1 50 50 76.799999999999997 1 0 -0 +1 0 50 0 1 0 -0 +1 60 0 0 -0 1 0 +2 25 25 0 0 0 -1 -1 0 -0 0 1 0 10 +1 60 0 16.899999999999999 -0 1 0 +1 50 0 16.899999999999999 -0 1 0 +1 50 0 76.799999999999997 -0 1 0 +1 60 0 76.799999999999997 -0 1 0 +2 25 25 -50 0 0 -1 -1 0 -0 0 1 0 10 +1 15.000000000000002 24.999999999999996 0 0 0 -1 +Polygon3D 0 +PolygonOnTriangulations 54 +2 1 2 +p 0.6000000008 1 0 100 +2 3 4 +p 0.6000000008 1 0 100 +2 2 4 +p 0.6000000008 1 0 50 +2 1 2 +p 0.6000000008 1 0 50 +2 3 4 +p 0.6000000008 1 0 100 +2 3 4 +p 0.6000000008 1 0 100 +2 1 3 +p 0.6000000008 1 0 50 +2 1 2 +p 0.6000000008 1 0 50 +2 1 2 +p 0.6000000008 1 0 16.9 +2 1 2 +p 0.6000000008 1 0 16.9 +2 8 2 +p 0.6000000008 1 0 10 +2 1 3 +p 0.6000000008 1 0 10 +2 8 7 +p 0.6000000008 1 0 59.9 +2 1 2 +p 0.6000000008 1 0 59.9 +2 7 6 +p 0.6000000008 1 0 10 +2 1 3 +p 0.6000000008 1 0 10 +2 6 5 +p 0.6000000008 1 76.8 100 +2 1 2 +p 0.6000000008 1 76.8 100 +2 4 5 +p 0.6000000008 1 0 60 +2 1 3 +p 0.6000000008 1 0 60 +2 3 1 +p 0.6000000008 1 0 60 +2 1 3 +p 0.6000000008 1 0 60 +2 2 4 +p 0.6000000008 1 0 60 +2 4 5 +p 0.6000000008 1 0 60 +2 3 4 +p 0.6000000008 1 0 50 +2 2 4 +p 0.6000000008 1 0 50 +2 1 2 +p 0.6000000008 1 0 16.9 +2 3 4 +p 0.6000000008 1 0 16.9 +2 8 2 +p 0.6000000008 1 0 10 +2 2 4 +p 0.6000000008 1 0 10 +2 8 7 +p 0.6000000008 1 0 59.9 +2 3 4 +p 0.6000000008 1 0 59.9 +2 7 6 +p 0.6000000008 1 0 10 +2 2 4 +p 0.6000000008 1 0 10 +2 6 5 +p 0.6000000008 1 76.8 100 +2 3 4 +p 0.6000000008 1 76.8 100 +2 3 1 +p 0.6000000008 1 0 60 +2 2 4 +p 0.6000000008 1 0 60 +2 3 4 +p 0.6000000008 1 0 50 +2 1 3 +p 0.6000000008 1 0 50 +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.6000000008 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.6000000008 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 2 4 +p 0.6000000008 1 0 50 +2 3 4 +p 0.6000000008 1 0 50 +2 1 2 +p 0.6000000008 1 0 50 +2 1 3 +p 0.6000000008 1 0 50 +2 2 4 +p 0.6000000008 1 0 50 +2 1 2 +p 0.6000000008 1 0 50 +2 3 4 +p 0.6000000008 1 0 50 +2 1 3 +p 0.6000000008 1 0 50 +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.6000000008 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.6000000008 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.6000000008 1 0 50 +2 74 37 +p 0.6000000008 1 0 50 +Surfaces 15 +1 0 0 0 1 0 -0 0 0 1 0 -1 0 +1 0 0 0 -0 1 0 0 0 1 1 0 -0 +1 0 0 100 0 0 1 1 0 -0 -0 1 0 +1 0 50 0 -0 1 0 0 0 1 1 0 -0 +1 0 0 0 0 0 1 1 0 -0 -0 1 0 +1 60 0 0 1 0 -0 0 0 1 0 -1 0 +1 50 0 16.899999999999999 -0 1 0 0 0 1 1 0 -0 +1 50 0 16.899999999999999 0 0 1 1 0 -0 -0 1 0 +1 50 0 16.899999999999999 1 0 -0 0 0 1 0 -1 0 +1 50 0 76.799999999999997 0 0 1 1 0 -0 -0 1 0 +1 50 50 16.899999999999999 -0 1 0 0 0 1 1 0 -0 +2 25 25 0 0 0 -1 -1 0 -0 0 1 0 10 +1 25 25 0 0 0 -1 -1 0 -0 0 1 0 +1 60 0 16.899999999999999 1 0 -0 0 0 1 0 -1 0 +1 25 25 -50 0 0 -1 -1 0 -0 0 1 0 +Triangulations 12 +4 2 1 0 0 +0 0 0 0 0 100 0 50 0 0 50 100 0 0 100 0 0 -50 100 -50 2 1 3 2 3 4 +8 6 1 0 0 +60 0 0 60 0 16.9 0 0 0 0 0 100 60 0 100 60 0 76.8 50 0 76.8 50 0 16.9 0 60 16.9 60 0 0 100 0 100 60 76.8 60 76.8 50 16.9 50 8 1 3 2 1 8 7 3 4 7 8 3 5 6 7 5 7 4 +4 2 1 0 0 +0 0 100 0 50 100 60 0 100 60 50 100 0 0 0 50 60 0 60 50 3 2 1 4 2 3 +8 6 1 0 0 +60 50 0 60 50 16.9 0 50 0 0 50 100 60 50 100 60 50 76.8 50 50 76.8 50 50 16.9 0 60 16.9 60 0 0 100 0 100 60 76.8 60 76.8 50 16.9 50 8 1 3 2 1 8 7 3 4 7 8 3 5 6 7 5 7 4 +40 40 1 0 1.77635683940025e-15 +0 0 0 0 50 0 60 0 0 60 50 0 15 25 0 15.1519224698779 26.7364817766693 0 15.6030737921409 28.4202014332567 0 16.3397459621556 30 0 17.3395555688102 31.4278760968654 0 18.5721239031346 32.6604444311898 0 20 33.6602540378444 0 21.5797985667433 34.3969262078591 0 23.2635182233307 34.8480775301221 0 25 35 0 26.7364817766693 34.8480775301221 0 28.4202014332567 34.3969262078591 0 30 33.6602540378444 0 31.4278760968654 32.6604444311898 0 32.6604444311898 31.4278760968654 0 33.6602540378444 30 0 34.3969262078591 28.4202014332567 0 34.8480775301221 26.7364817766693 0 35 25 0 34.8480775301221 23.2635182233307 0 34.3969262078591 21.5797985667433 0 33.6602540378444 20 0 32.6604444311898 18.5721239031346 0 31.4278760968654 17.3395555688102 0 30 16.3397459621556 0 28.4202014332567 15.6030737921409 0 26.7364817766693 15.1519224698779 0 25 15 0 23.2635182233307 15.1519224698779 0 21.5797985667433 15.6030737921409 0 20 16.3397459621556 0 18.5721239031346 17.3395555688102 0 17.3395555688102 18.5721239031346 0 16.3397459621556 20 0 15.6030737921409 21.5797985667433 0 15.1519224698779 23.2635182233307 0 0 0 0 50 60 0 60 50 15 25 15.1519224698779 26.7364817766693 15.6030737921409 28.4202014332567 16.3397459621556 30 17.3395555688102 31.4278760968654 18.5721239031346 32.6604444311898 20 33.6602540378444 21.5797985667433 34.3969262078591 23.2635182233307 34.8480775301221 25 35 26.7364817766693 34.8480775301221 28.4202014332567 34.3969262078591 30 33.6602540378444 31.4278760968654 32.6604444311898 32.6604444311898 31.4278760968654 33.6602540378444 30 34.3969262078591 28.4202014332567 34.8480775301221 26.7364817766693 35 25 34.8480775301221 23.2635182233307 34.3969262078591 21.5797985667433 33.6602540378444 20 32.6604444311898 18.5721239031346 31.4278760968654 17.3395555688102 30 16.3397459621556 28.4202014332567 15.6030737921409 26.7364817766693 15.1519224698779 25 15 23.2635182233307 15.1519224698779 21.5797985667433 15.6030737921409 20 16.3397459621556 18.5721239031346 17.3395555688102 17.3395555688102 18.5721239031346 16.3397459621556 20 15.6030737921409 21.5797985667433 15.1519224698779 23.2635182233307 36 37 1 38 1 37 35 36 1 39 1 38 34 35 1 40 1 39 33 34 1 5 1 40 32 33 1 31 32 1 2 5 6 2 6 7 2 7 8 2 8 9 2 1 5 10 2 9 11 2 10 12 2 11 13 2 12 3 24 25 3 25 26 3 26 27 3 27 28 3 28 29 3 29 30 3 30 31 3 31 1 14 2 13 23 24 3 15 2 14 4 2 15 4 15 16 4 16 17 4 17 18 4 18 19 4 19 20 4 20 21 4 21 22 4 22 23 4 23 3 +4 2 1 0 0 +60 0 0 60 0 16.9 60 50 0 60 50 16.9 0 0 16.9 0 0 -50 16.9 -50 1 3 4 2 1 4 +4 2 1 0 0 +50 0 16.9 50 50 16.9 60 0 16.9 60 50 16.9 0 0 0 50 10 0 10 50 4 2 1 4 1 3 +4 2 1 0 7.105427357601e-15 +50 0 16.9 50 0 76.8 50 50 16.9 50 50 76.8 0 0 59.9 0 0 -50 59.9 -50 2 1 3 2 3 4 +4 2 1 0 0 +50 0 76.8 50 50 76.8 60 0 76.8 60 50 76.8 0 0 0 50 10 0 10 50 4 2 1 4 1 3 +4 2 1 0 0 +60 0 76.8 60 0 100 60 50 76.8 60 50 100 76.8 0 100 0 76.8 -50 100 -50 1 3 4 2 1 4 +74 72 1 0 0.0380530190825497 +15 25 -50 15.1519224698779 26.7364817766693 -50 15.6030737921409 28.4202014332567 -50 16.3397459621556 30 -50 17.3395555688102 31.4278760968654 -50 18.5721239031346 32.6604444311898 -50 20 33.6602540378444 -50 21.5797985667433 34.3969262078591 -50 23.2635182233307 34.8480775301221 -50 25 35 -50 26.7364817766693 34.8480775301221 -50 28.4202014332567 34.3969262078591 -50 30 33.6602540378444 -50 31.4278760968654 32.6604444311898 -50 32.6604444311898 31.4278760968654 -50 33.6602540378444 30 -50 34.3969262078591 28.4202014332567 -50 34.8480775301221 26.7364817766693 -50 35 25 -50 34.8480775301221 23.2635182233307 -50 34.3969262078591 21.5797985667433 -50 33.6602540378444 20 -50 32.6604444311898 18.5721239031346 -50 31.4278760968654 17.3395555688102 -50 30 16.3397459621556 -50 28.4202014332567 15.6030737921409 -50 26.7364817766693 15.1519224698779 -50 25 15 -50 23.2635182233307 15.1519224698779 -50 21.5797985667433 15.6030737921409 -50 20 16.3397459621556 -50 18.5721239031346 17.3395555688102 -50 17.3395555688102 18.5721239031346 -50 16.3397459621556 20 -50 15.6030737921409 21.5797985667433 -50 15.1519224698779 23.2635182233307 -50 15 25 -50 15 25 0 15.1519224698779 26.7364817766693 0 15.6030737921409 28.4202014332567 0 16.3397459621556 30 0 17.3395555688102 31.4278760968654 0 18.5721239031346 32.6604444311898 0 20 33.6602540378444 0 21.5797985667433 34.3969262078591 0 23.2635182233307 34.8480775301221 0 25 35 0 26.7364817766693 34.8480775301221 0 28.4202014332567 34.3969262078591 0 30 33.6602540378444 0 31.4278760968654 32.6604444311898 0 32.6604444311898 31.4278760968654 0 33.6602540378444 30 0 34.3969262078591 28.4202014332567 0 34.8480775301221 26.7364817766693 0 35 25 0 34.8480775301221 23.2635182233307 0 34.3969262078591 21.5797985667433 0 33.6602540378444 20 0 32.6604444311898 18.5721239031346 0 31.4278760968654 17.3395555688102 0 30 16.3397459621556 0 28.4202014332567 15.6030737921409 0 26.7364817766693 15.1519224698779 0 25 15 0 23.2635182233307 15.1519224698779 0 21.5797985667433 15.6030737921409 0 20 16.3397459621556 0 18.5721239031346 17.3395555688102 0 17.3395555688102 18.5721239031346 0 16.3397459621556 20 0 15.6030737921409 21.5797985667433 0 15.1519224698779 23.2635182233307 0 15 25 0 0 50 0.174532925199433 50 0.349065850398866 50 0.523598775598299 50 0.698131700797732 50 0.872664625997165 50 1.0471975511966 50 1.22173047639603 50 1.39626340159546 50 1.5707963267949 50 1.74532925199433 50 1.91986217719376 50 2.0943951023932 50 2.26892802759263 50 2.44346095279206 50 2.61799387799149 50 2.79252680319093 50 2.96705972839036 50 3.14159265358979 50 3.31612557878922 50 3.49065850398866 50 3.66519142918809 50 3.83972435438752 50 4.01425727958696 50 4.18879020478639 50 4.36332312998582 50 4.53785605518525 50 4.71238898038469 50 4.88692190558412 50 5.06145483078355 50 5.23598775598299 50 5.41052068118242 50 5.58505360638185 50 5.75958653158128 50 5.93411945678072 50 6.10865238198015 50 6.28318530717959 50 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 42 43 5 4 41 6 43 44 6 5 43 7 6 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 18 17 54 18 54 55 18 55 56 19 18 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 64 65 27 26 63 28 27 65 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 37 36 73 37 73 74 +36 34 1 0 7.94410929039127e-15 +15 25 -50 15.1519224698779 26.7364817766693 -50 15.6030737921409 28.4202014332567 -50 16.3397459621556 30 -50 17.3395555688102 31.4278760968654 -50 18.5721239031346 32.6604444311898 -50 20 33.6602540378444 -50 21.5797985667433 34.3969262078591 -50 23.2635182233307 34.8480775301221 -50 25 35 -50 26.7364817766693 34.8480775301221 -50 28.4202014332567 34.3969262078591 -50 30 33.6602540378444 -50 31.4278760968654 32.6604444311898 -50 32.6604444311898 31.4278760968654 -50 33.6602540378444 30 -50 34.3969262078591 28.4202014332567 -50 34.8480775301221 26.7364817766693 -50 35 25 -50 34.8480775301221 23.2635182233307 -50 34.3969262078591 21.5797985667433 -50 33.6602540378444 20 -50 32.6604444311898 18.5721239031346 -50 31.4278760968654 17.3395555688102 -50 30 16.3397459621556 -50 28.4202014332567 15.6030737921409 -50 26.7364817766693 15.1519224698779 -50 25 15 -50 23.2635182233307 15.1519224698779 -50 21.5797985667433 15.6030737921409 -50 20 16.3397459621556 -50 18.5721239031346 17.3395555688102 -50 17.3395555688102 18.5721239031346 -50 16.3397459621556 20 -50 15.6030737921409 21.5797985667433 -50 15.1519224698779 23.2635182233307 -50 10 -1.77635683940025e-15 9.84807753012208 1.7364817766693 9.39692620785909 3.42020143325669 8.66025403784439 5 7.66044443118978 6.42787609686539 6.42787609686539 7.66044443118978 5 8.66025403784439 3.42020143325669 9.39692620785908 1.7364817766693 9.84807753012208 0 10 -1.7364817766693 9.84807753012208 -3.42020143325669 9.39692620785909 -5 8.66025403784439 -6.42787609686539 7.66044443118978 -7.66044443118978 6.4278760968654 -8.66025403784438 5.00000000000001 -9.39692620785908 3.4202014332567 -9.84807753012208 1.73648177666932 -10 1.4210854715202e-14 -9.84807753012208 -1.73648177666929 -9.39692620785909 -3.42020143325667 -8.6602540378444 -4.99999999999998 -7.6604444311898 -6.42787609686538 -6.42787609686541 -7.66044443118977 -5.00000000000002 -8.66025403784437 -3.42020143325671 -9.39692620785907 -1.73648177666933 -9.84807753012208 -2.8421709430404e-14 -10 1.73648177666927 -9.84807753012209 3.42020143325666 -9.3969262078591 4.99999999999997 -8.6602540378444 6.42787609686536 -7.6604444311898 7.66044443118976 -6.42787609686542 8.66025403784437 -5.00000000000004 9.39692620785907 -3.42020143325673 9.84807753012207 -1.73648177666935 22 23 24 20 21 22 28 26 27 18 19 20 18 24 25 18 22 24 18 20 22 30 28 29 31 25 26 31 26 28 31 28 30 33 31 32 13 14 15 13 15 16 13 16 17 13 17 18 35 33 34 35 31 33 36 18 25 36 25 31 36 31 35 11 12 13 11 13 18 9 10 11 2 36 1 8 9 11 8 11 18 3 36 2 4 36 3 4 7 8 4 18 36 4 8 18 6 7 4 6 4 5 + +TShapes 72 +Ve +1e-07 +0 0 100 +0 0 + +0101101 +* +Ve +1e-07 +0 0 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 1 0 0 100 +2 1 1 0 0 100 +2 2 2 0 0 100 +6 1 1 0 +6 2 2 0 +0 + +0101000 +-72 0 +71 0 * +Ve +1e-07 +0 50 100 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 2 0 0 50 +2 3 1 0 0 50 +2 4 3 0 0 50 +6 3 1 0 +6 4 3 0 +0 + +0101000 +-69 0 +72 0 * +Ve +1e-07 +0 50 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 3 0 0 100 +2 5 1 0 0 100 +2 6 4 0 0 100 +6 5 1 0 +6 6 4 0 +0 + +0101000 +-69 0 +67 0 * +Ed + 1e-07 1 1 0 +1 4 0 0 50 +2 7 1 0 0 50 +2 8 5 0 0 50 +6 7 1 0 +6 8 5 0 +0 + +0101000 +-67 0 +71 0 * +Wi + +0101100 +-70 0 -68 0 +66 0 +65 0 * +Fa +0 1e-07 1 0 +2 1 +0101000 ++64 0 * +Ve +1e-07 +60 0 0 +0 0 + +0101101 +* +Ve +1e-07 +60 0 16.9 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 5 0 0 16.9 +2 9 6 0 0 16.9 +2 10 2 0 0 16.9 +6 9 2 0 +6 10 6 0 +0 + +0101000 ++62 0 -61 0 * +Ve +1e-07 +50 0 16.9 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 6 0 0 10 +2 11 7 0 0 10 +2 12 8 0 0 10 +2 13 2 0 0 10 +6 11 2 0 +6 12 7 0 +0 + +0101000 +-61 0 +59 0 * +Ve +1e-07 +50 0 76.8 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 7 0 0 59.9 +2 14 9 0 0 59.9 +2 15 7 0 0 59.9 +2 16 2 0 0 59.9 +6 13 2 0 +6 14 8 0 +0 + +0101000 +-57 0 +59 0 * +Ve +1e-07 +60 0 76.8 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 8 0 0 10 +2 17 7 0 0 10 +2 18 10 0 0 10 +2 19 2 0 0 10 +6 15 2 0 +6 16 9 0 +0 + +0101000 +-55 0 +57 0 * +Ve +1e-07 +60 0 100 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 5 0 76.8 100 +2 9 6 0 76.8 100 +2 10 2 0 76.8 100 +6 17 2 0 +6 18 10 0 +0 + +0101000 ++55 0 -53 0 * +Ed + 1e-07 1 1 0 +1 9 0 0 60 +2 20 2 0 0 60 +2 21 3 0 0 60 +6 19 2 0 +6 20 3 0 +0 + +0101000 +-53 0 +72 0 * +Ed + 1e-07 1 1 0 +1 10 0 0 60 +2 22 2 0 0 60 +2 23 5 0 0 60 +6 21 2 0 +6 22 5 0 +0 + +0101000 +-62 0 +71 0 * +Wi + +0101100 +-60 0 +58 0 -56 0 -54 0 -52 0 +51 0 +70 0 -50 0 * +Fa +0 1e-07 2 0 +2 2 +0101000 ++49 0 * +Ve +1e-07 +60 50 100 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 11 0 0 60 +2 24 4 0 0 60 +2 25 3 0 0 60 +6 23 3 0 +6 24 4 0 +0 + +0101000 +-47 0 +69 0 * +Ed + 1e-07 1 1 0 +1 12 0 0 50 +2 26 6 0 0 50 +2 27 3 0 0 50 +6 25 3 0 +6 26 10 0 +0 + +0101000 +-47 0 +53 0 * +Wi + +0101100 +-68 0 -46 0 +45 0 +51 0 * +Fa +0 1e-07 3 0 +2 3 +0101000 ++44 0 * +Ve +1e-07 +60 50 0 +0 0 + +0101101 +* +Ve +1e-07 +60 50 16.9 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 13 0 0 16.9 +2 28 6 0 0 16.9 +2 29 4 0 0 16.9 +6 27 4 0 +6 28 6 0 +0 + +0101000 ++42 0 -41 0 * +Ve +1e-07 +50 50 16.9 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 14 0 0 10 +2 30 11 0 0 10 +2 31 8 0 0 10 +2 32 4 0 0 10 +6 29 4 0 +6 30 7 0 +0 + +0101000 +-41 0 +39 0 * +Ve +1e-07 +50 50 76.8 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 15 0 0 59.9 +2 33 9 0 0 59.9 +2 34 11 0 0 59.9 +2 35 4 0 0 59.9 +6 31 4 0 +6 32 8 0 +0 + +0101000 +-37 0 +39 0 * +Ve +1e-07 +60 50 76.8 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 16 0 0 10 +2 36 11 0 0 10 +2 37 10 0 0 10 +2 38 4 0 0 10 +6 33 4 0 +6 34 9 0 +0 + +0101000 +-35 0 +37 0 * +Ed + 1e-07 1 1 0 +1 13 0 76.8 100 +2 28 6 0 76.8 100 +2 29 4 0 76.8 100 +6 35 4 0 +6 36 10 0 +0 + +0101000 ++35 0 -47 0 * +Ed + 1e-07 1 1 0 +1 17 0 0 60 +2 39 4 0 0 60 +2 40 5 0 0 60 +6 37 4 0 +6 38 5 0 +0 + +0101000 +-42 0 +67 0 * +Wi + +0101100 +-40 0 +38 0 -36 0 -34 0 -33 0 +46 0 +66 0 -32 0 * +Fa +0 1e-07 4 0 +2 4 +0101000 ++31 0 * +Ed + 1e-07 1 1 0 +1 18 0 0 50 +2 41 6 0 0 50 +2 42 5 0 0 50 +6 39 5 0 +6 40 6 0 +0 + +0101000 +-42 0 +62 0 * +Wi + +0101100 +-65 0 -32 0 +50 0 +29 0 * +Ve +1e-07 +15 25 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 19 0 0 6.28318530717959 +2 43 12 0 0 6.28318530717959 +2 44 13 0 0 6.28318530717959 +2 45 5 0 0 6.28318530717959 +6 41 11 0 +6 42 5 0 +0 + +0101000 ++27 0 -27 0 * +Wi + +0101100 ++26 0 * +Fa +0 1e-07 5 0 +2 5 +0101000 ++28 0 +25 0 * +Ed + 1e-07 1 1 0 +1 20 0 0 50 +2 46 14 0 0 50 +2 47 8 0 0 50 +2 48 6 0 0 50 +6 43 6 0 +6 44 7 0 +0 + +0101000 +-41 0 +61 0 * +Wi + +0101100 +-60 0 -23 0 +40 0 +29 0 * +Fa +0 1e-07 6 0 +2 6 +0101000 ++22 0 * +Ed + 1e-07 1 1 0 +1 21 0 0 50 +2 49 9 0 0 50 +2 50 8 0 0 50 +6 45 7 0 +6 46 8 0 +0 + +0101000 +-39 0 +59 0 * +Wi + +0101100 +-20 0 -38 0 +23 0 +58 0 * +Fa +0 1e-07 8 0 +2 7 +0101000 ++19 0 * +Ed + 1e-07 1 1 0 +1 22 0 0 50 +2 51 9 0 0 50 +2 52 10 0 0 50 +6 47 8 0 +6 48 9 0 +0 + +0101000 +-37 0 +57 0 * +Wi + +0101100 +-56 0 -17 0 +36 0 +20 0 * +Fa +0 1e-07 9 0 +2 8 +0101000 ++16 0 * +Ed + 1e-07 1 1 0 +1 23 0 0 50 +2 53 14 0 0 50 +2 54 10 0 0 50 +2 55 6 0 0 50 +6 49 9 0 +6 50 10 0 +0 + +0101000 +-35 0 +55 0 * +Wi + +0101100 +-17 0 -34 0 +14 0 +54 0 * +Fa +0 1e-07 10 0 +2 9 +0101000 ++13 0 * +Wi + +0101100 +-52 0 -45 0 +33 0 +14 0 * +Fa +0 1e-07 6 0 +2 10 +0101000 ++11 0 * +Ve +1e-07 +15 25 -50 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 24 0 0 6.28318530717959 +2 56 12 0 0 6.28318530717959 +2 57 15 0 0 6.28318530717959 +6 51 11 0 +6 52 12 0 +0 + +0101000 ++9 0 -9 0 * +Ed + 1e-07 1 1 0 +1 25 0 0 50 +3 58 59CN 12 0 0 50 +7 53 54 11 0 +0 + +0101000 +-9 0 +27 0 * +Wi + +0101100 +-8 0 +7 0 +26 0 -7 0 * +Fa +0 1e-07 12 0 +2 11 +0101000 ++6 0 * +Wi + +0101100 ++8 0 * +Fa +0 1e-07 15 0 +2 12 +0101000 ++4 0 * +Sh + +0101100 +-63 0 -48 0 +43 0 +30 0 -24 0 +21 0 +18 0 +15 0 -12 0 +10 0 ++5 0 +3 0 * +So + +0100000 ++2 0 * + ++1 0 \ No newline at end of file diff --git a/test/data/HexahedronTest/Sphere.brep b/test/data/HexahedronTest/Sphere.brep deleted file mode 100644 index e404f6d4a..000000000 --- a/test/data/HexahedronTest/Sphere.brep +++ /dev/null @@ -1,73 +0,0 @@ -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 185fe644a..a3af40ace 100644 --- a/test/tests.set +++ b/test/tests.set @@ -129,6 +129,7 @@ SET(CPP_TESTS SET(UNIT_TESTS # Any unit test add in src names space should be added here HexahedronTest + HexahedronCanonicalShapesTest ) # The following tests can be executed without driver, just by python. From b6e0752eacaf3a3905d3a9013417802b175d27e7 Mon Sep 17 00:00:00 2001 From: jfa Date: Fri, 19 Jul 2024 12:53:33 +0100 Subject: [PATCH 04/15] Avoid anonymous namespaces and 'using' instructions in headers. Simplify tests code. Continue refactoring of Hexahedron::compute() method. --- .../HexahedronCanonicalShapesTest.cxx | 65 +- src/StdMeshers.test/HexahedronTest.cxx | 116 +-- src/StdMeshers/StdMeshers_Cartesian_3D.cxx | 7 +- .../StdMeshers_Cartesian_3D_Grid.cxx | 359 +++++++- .../StdMeshers_Cartesian_3D_Grid.hxx | 489 ++--------- .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 229 ++--- .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 822 +++++++++--------- test/data/HexahedronTest/NRTMJ4.brep | 414 +++------ 8 files changed, 1149 insertions(+), 1352 deletions(-) diff --git a/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx b/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx index fe3ab4c95..3f4b16f56 100644 --- a/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx +++ b/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx @@ -39,11 +39,12 @@ #include #include +using namespace StdMeshers::Cartesian3D; + // Helper functions! // Build Grid // Require building mesh -// Require building shape. For test load shapes from memory in .brep files seems the simplest -// +// Require building shape. /*! * \brief Mock mesh @@ -69,63 +70,28 @@ struct CartesianHypo: public StdMeshers_CartesianParameters3D /*! * \brief Initialize the grid and intesersectors of grid with the geometry */ -void GridInitAndIntersectWithShape( Grid& grid, +void GridInitAndIntersectWithShape (Grid& grid, double gridSpacing, double theSizeThreshold, const TopoDS_Shape theShape, - std::map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, - const int /*numOfThreads*/ ) + TEdge2faceIDsMap& edge2faceIDsMap, + const int theNumOfThreads) { - 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(theSizeThreshold); // 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(); + std::unique_ptr aHypo ( new CartesianHypo() ); + aHypo->SetGridSpacing(grdSpace, intPnts, 0 ); // Spacing in dir 0 + aHypo->SetGridSpacing(grdSpace, intPnts, 1 ); // Spacing in dir 1 + aHypo->SetGridSpacing(grdSpace, intPnts, 2 ); // Spacing in dir 2 + aHypo->SetSizeThreshold(theSizeThreshold); // set threshold + aHypo->SetAxisDirs(axisDirs); - grid.ComputeNodes( *grid._helper ); - grid.GetEdgesToImplement( edge2faceIDsMap, theShape, faceVec ); + grid.GridInitAndInterserctWithShape(theShape, edge2faceIDsMap, aHypo.get(), theNumOfThreads, false); } - /*! * \brief Test runner */ @@ -139,6 +105,7 @@ bool testShape (const TopoDS_Shape theShape, std::unique_ptr aMesh( new SMESH_Mesh_Test() ); aMesh->ShapeToMesh( theShape ); SMESH_MesherHelper helper( *aMesh ); + Grid grid; grid._helper = &helper; grid._toAddEdges = toAddEdges; @@ -147,11 +114,11 @@ bool testShape (const TopoDS_Shape theShape, grid._toUseThresholdForInternalFaces = false; grid._toUseQuanta = false; grid._sizeThreshold = theSizeThreshold; - grid.InitGeometry( theShape ); - std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; + TEdge2faceIDsMap edge2faceIDsMap; GridInitAndIntersectWithShape( grid, theGridSpacing, theSizeThreshold, theShape, edge2faceIDsMap, 1 ); + Hexahedron hex( &grid ); int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, 1 ); if (nbAdded != theNbCreatedExpected) { diff --git a/src/StdMeshers.test/HexahedronTest.cxx b/src/StdMeshers.test/HexahedronTest.cxx index e993a96f5..dd78c796e 100644 --- a/src/StdMeshers.test/HexahedronTest.cxx +++ b/src/StdMeshers.test/HexahedronTest.cxx @@ -39,11 +39,12 @@ #include #include +using namespace StdMeshers::Cartesian3D; + // Helper functions! // Build Grid // Require building mesh -// Require building shape. For test load shapes from memory in .brep files seems the simplest -// +// Require building shape. /*! * \brief Mock mesh @@ -78,60 +79,26 @@ void loadBrepShape( std::string shapeName, TopoDS_Shape & shape ) /*! * \brief Initialize the grid and intesersectors of grid with the geometry */ -void GridInitAndIntersectWithShape( Grid& grid, +void GridInitAndIntersectWithShape (Grid& grid, double gridSpacing, double theSizeThreshold, const TopoDS_Shape theShape, - std::map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, - const int /*numOfThreads*/ ) + TEdge2faceIDsMap& edge2faceIDsMap, + const int theNumOfThreads) { - 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() ); + double axisDirs[9] = {1.,0.,0., 0.,1.,0., 0.,0.,1.}; 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(theSizeThreshold); // 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(); + std::unique_ptr aHypo ( new CartesianHypo() ); + aHypo->SetAxisDirs(axisDirs); + aHypo->SetGridSpacing(grdSpace, intPnts, 0 ); // Spacing in dir 0 + aHypo->SetGridSpacing(grdSpace, intPnts, 1 ); // Spacing in dir 1 + aHypo->SetGridSpacing(grdSpace, intPnts, 2 ); // Spacing in dir 2 + aHypo->SetSizeThreshold(theSizeThreshold); // set threshold - grid.ComputeNodes( *grid._helper ); - grid.GetEdgesToImplement( edge2faceIDsMap, theShape, faceVec ); + grid.GridInitAndInterserctWithShape(theShape, edge2faceIDsMap, aHypo.get(), theNumOfThreads, false); } /*! @@ -139,6 +106,10 @@ void GridInitAndIntersectWithShape( Grid& grid, */ bool testNRTM1() { + TopoDS_Shape aShape; + loadBrepShape( "data/HexahedronTest/NRTM1.brep", aShape ); + CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() ); + const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency(); std::vector numberOfThreads(numOfCores); std::iota (std::begin(numberOfThreads), std::end(numberOfThreads), 1); @@ -147,20 +118,22 @@ bool testNRTM1() { for (size_t i = 0; i < 10 /*trials*/; 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 ); + std::unique_ptr aMesh( new SMESH_Mesh_Test() ); + aMesh->ShapeToMesh( aShape ); + SMESH_MesherHelper helper( *aMesh ); + Grid grid; grid._helper = &helper; - grid._toAddEdges = false; grid._toCreateFaces = false; grid._toConsiderInternalFaces = false; grid._toUseThresholdForInternalFaces = false; grid._toUseQuanta = false; + 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; - GridInitAndIntersectWithShape( grid, 1.0, 4.0, myShape, edge2faceIDsMap, nThreads ); + TEdge2faceIDsMap edge2faceIDsMap; + GridInitAndIntersectWithShape( grid, 1.0, 4.0, aShape, edge2faceIDsMap, nThreads ); + Hexahedron hex( &grid ); int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads ); CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 1024 ); @@ -174,6 +147,10 @@ bool testNRTM1() */ bool testNRTJ4() { + TopoDS_Shape aShape; + loadBrepShape( "data/HexahedronTest/NRTMJ4.brep", aShape ); + CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() ); + const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency()/2; std::vector numberOfThreads(numOfCores); std::iota (std::begin(numberOfThreads), std::end(numberOfThreads), 1); @@ -183,22 +160,22 @@ bool testNRTJ4() { for (size_t i = 0; i < 10 /*trials*/; i++) { - TopoDS_Shape myShape; - loadBrepShape( "data/HexahedronTest/NRTMJ4.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 ); + std::unique_ptr aMesh( new SMESH_Mesh_Test() ); + aMesh->ShapeToMesh( aShape ); + SMESH_MesherHelper helper( *aMesh ); + Grid grid; grid._helper = &helper; - grid._toAddEdges = false; grid._toConsiderInternalFaces = false; grid._toUseThresholdForInternalFaces = false; grid._toUseQuanta = false; + grid._toAddEdges = false; + grid._toConsiderInternalFaces = false; + grid._toUseThresholdForInternalFaces = false; + grid._toUseQuanta = false; double testThreshold = 1.000001; grid._toCreateFaces = true; grid._sizeThreshold = testThreshold; - grid.InitGeometry( myShape ); - std::map< TGeomID, vector< TGeomID > > edge2faceIDsMap; - GridInitAndIntersectWithShape( grid, 2.0, testThreshold, myShape, edge2faceIDsMap, nThreads ); + TEdge2faceIDsMap edge2faceIDsMap; + GridInitAndIntersectWithShape( grid, 2.0, testThreshold, aShape, edge2faceIDsMap, nThreads ); Hexahedron hex( &grid ); int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads ); CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 35150 ); @@ -210,6 +187,9 @@ bool testNRTJ4() // Entry point for test int main() { - bool isOK = testNRTM1() && testNRTJ4(); + bool isOK = testNRTM1(); + if (!testNRTJ4()) + isOK = false; + return isOK ? 0 : 1; } diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx index 98733b703..c20c5a2a0 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx @@ -52,8 +52,9 @@ using namespace std; using namespace SMESH; -using namespace gridtools; +using namespace StdMeshers::Cartesian3D; // using namespace facegridintersector; + namespace { /*! @@ -229,7 +230,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 @@ -391,4 +392,4 @@ void StdMeshers_Cartesian_3D::setSubmeshesComputed(SMESH_Mesh& theMesh, { for ( TopExp_Explorer soExp( theShape, TopAbs_SOLID ); soExp.More(); soExp.Next() ) _EventListener::setAlwaysComputed( true, theMesh.GetSubMesh( soExp.Current() )); -} \ No newline at end of file +} diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.cxx index 0d02a9f73..273b51c19 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.cxx @@ -35,9 +35,12 @@ #include #endif +using namespace std; using namespace SMESH; -using namespace gridtools; +using namespace StdMeshers::Cartesian3D; + std::mutex _bMutex; + //============================================================================= /* * Remove coincident intersection points @@ -929,8 +932,10 @@ void Grid::ComputeNodes(SMESH_MesherHelper& helper) return; } -bool Grid::GridInitAndInterserctWithShape( const TopoDS_Shape& theShape, std::map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, - const StdMeshers_CartesianParameters3D* hyp, const int numOfThreads, bool computeCanceled ) +bool Grid::GridInitAndInterserctWithShape( const TopoDS_Shape& theShape, + TEdge2faceIDsMap& edge2faceIDsMap, + const StdMeshers_CartesianParameters3D* hyp, + const int /*numOfThreads*/, bool computeCanceled ) { InitGeometry( theShape ); @@ -1088,3 +1093,351 @@ void Tools::GetExactBndBox( const vector< TopoDS_Shape >& faceVec, const double* 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( std::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; +} + +#ifdef WITH_TBB +//================================================================================ +/* + * check if its face can be safely intersected in a thread + */ +bool FaceGridIntersector::IsThreadSafe(std::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 + +//================================================================================ +/* + * 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); + } +} diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx index 3f8cc6800..3cf439aa3 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx @@ -32,12 +32,12 @@ // STD #include +#include #include #include #include #include - // SMESH #include "SMESH_StdMeshers.hxx" #include "StdMeshers_FaceSide.hxx" @@ -116,14 +116,16 @@ // 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 +namespace StdMeshers +{ +namespace Cartesian3D { typedef int TGeomID; // IDs of sub-shapes typedef TopTools_ShapeMapHasher TShapeHasher; // non-oriented shape hasher typedef std::array< int, 3 > TIJK; + typedef std::map< TGeomID, std::vector< TGeomID > > TEdge2faceIDsMap; + const TGeomID theUndefID = 1e+9; //============================================================================= @@ -136,6 +138,7 @@ namespace gridtools Trans_APEX, Trans_INTERNAL // for INTERNAL FACE }; + // -------------------------------------------------------------------------- /*! * \brief Sub-entities of a FACE neighboring its concave VERTEX. @@ -155,6 +158,7 @@ namespace gridtools void SetVertex( TGeomID v ) { ( _v1 ? _v2 : _v1 ) = v; } }; typedef NCollection_DataMap< TGeomID, ConcaveFace > TConcaveVertex2Face; + // -------------------------------------------------------------------------- /*! * \brief Container of IDs of SOLID sub-shapes @@ -167,7 +171,7 @@ namespace gridtools public: virtual ~Solid() {} virtual bool Contains( TGeomID /*subID*/ ) const { return true; } - virtual bool ContainsAny( const vector< TGeomID>& /*subIDs*/ ) const { return true; } + virtual bool ContainsAny( const std::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; } @@ -179,6 +183,7 @@ namespace gridtools bool HasConcaveVertex() const { return !_concaveVertex.IsEmpty(); } const ConcaveFace* GetConcave( TGeomID V ) const { return _concaveVertex.Seek( V ); } }; + // -------------------------------------------------------------------------- class OneOfSolids : public Solid { @@ -190,7 +195,7 @@ namespace gridtools 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 + virtual bool ContainsAny( const std::vector< TGeomID>& subIDs ) const { for ( size_t i = 0; i < subIDs.size(); ++i ) if ( Contains( subIDs[ i ])) return true; return false; @@ -205,6 +210,7 @@ namespace gridtools return faceID == 0 || _outFaceIDs.Contains( faceID ); } }; + // -------------------------------------------------------------------------- /*! * \brief Hold a vector of TGeomID and clear it at destruction @@ -246,6 +252,7 @@ namespace gridtools return common; } }; + // -------------------------------------------------------------------------- /*! * \brief Geom data @@ -253,20 +260,20 @@ namespace gridtools struct Geometry { TopoDS_Shape _mainShape; - vector< vector< TGeomID > > _solidIDsByShapeID;// V/E/F ID -> SOLID IDs - Solid _soleSolid; - map< TGeomID, OneOfSolids > _solidByID; + std::vector< std::vector< TGeomID > > _solidIDsByShapeID;// V/E/F ID -> SOLID IDs + Solid _soleSolid; + std::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; + SMESH::Controls::ElementsOnShape _edgeClassifier; + SMESH::Controls::ElementsOnShape _vertexClassifier; bool IsOneSolid() const { return _solidByID.size() < 2; } - GeomIDVecHelder GetSolidIDsByShapeID( const vector< TGeomID >& shapeIDs ) const; + GeomIDVecHelder GetSolidIDsByShapeID( const std::vector< TGeomID >& shapeIDs ) const; }; // -------------------------------------------------------------------------- @@ -279,16 +286,17 @@ namespace gridtools // 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; + mutable const SMDS_MeshNode* _node; + mutable std::vector< TGeomID > _faceIDs; B_IntersectPoint(): _node(NULL) {} - bool Add( const vector< TGeomID >& fIDs, const SMDS_MeshNode* n=NULL ) const; + bool Add( const std::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 @@ -304,6 +312,7 @@ namespace gridtools return _paramOnLine < o._paramOnLine; } }; + // -------------------------------------------------------------------------- /*! * \brief Data of intersection between GridPlanes and a TopoDS_EDGE @@ -323,10 +332,10 @@ namespace gridtools { gp_Lin _line; double _length; // line length - multiset< F_IntersectPoint > _intPoints; + std::multiset< F_IntersectPoint > _intPoints; void RemoveExcessIntPoints( const double tol ); - TGeomID GetSolidIDBefore( multiset< F_IntersectPoint >::iterator ip, + TGeomID GetSolidIDBefore( std::multiset< F_IntersectPoint >::iterator ip, const TGeomID prevID, const Geometry& geom); }; @@ -336,9 +345,9 @@ namespace gridtools */ 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 + gp_XYZ _zNorm; + std::vector< gp_XYZ > _origins; // origin points of all planes in one direction + std::vector< double > _zProjs; // projections of origins to _zNorm }; // -------------------------------------------------------------------------- /*! @@ -349,11 +358,11 @@ namespace gridtools size_t _size [3]; size_t _curInd[3]; size_t _iVar1, _iVar2, _iConst; - string _name1, _name2, _nameConst; + std::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 ) + const std::string& nv1, const std::string& nv2, const std::string& nConst ) { _size[0] = sz1; _size[1] = sz2; _size[2] = sz3; _curInd[0] = _curInd[1] = _curInd[2] = 0; @@ -398,31 +407,28 @@ namespace gridtools * \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 ); + static void GetExactBndBox( const std::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 + class STDMESHERS_EXPORT Grid + { + public: + std::vector< double > _coords[3]; // coordinates of grid nodes + gp_XYZ _axes [3]; // axis directions + std::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]; + 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 + std::vector< const SMDS_MeshNode* > _nodes; // mesh nodes at grid nodes + std::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 + std::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; @@ -469,10 +475,10 @@ public: 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, + SMESH::Controls::ElementsOnShape& classifier ); + void GetEdgesToImplement( std::map< TGeomID, std::vector< TGeomID > > & edge2faceMap, const TopoDS_Shape& shape, - const vector< TopoDS_Shape >& faces ); + const std::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; @@ -486,7 +492,7 @@ public: TGeomID PseudoIntExtFaceID() const { return _geometry._extIntFaceID; } Solid* GetSolid( TGeomID solidID = 0 ); Solid* GetOneOfSolids( TGeomID solidID ); - const vector< TGeomID > & GetSolidIDs( TGeomID subShapeID ) const; + const std::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, @@ -495,19 +501,19 @@ public: bool IsToCheckNodePos() const { return !_toAddEdges && _toCreateFaces; } bool IsToRemoveExcessEntities() const { return !_toAddEdges; } - void SetCoordinates(const vector& xCoords, - const vector& yCoords, - const vector& zCoords, + void SetCoordinates(const std::vector& xCoords, + const std::vector& yCoords, + const std::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 -{ + bool GridInitAndInterserctWithShape( const TopoDS_Shape& theShape, + std::map< TGeomID, std::vector< TGeomID > >& edge2faceIDsMap, + const StdMeshers_CartesianParameters3D* hyp, + const int numOfThreads, + bool computeCanceled ); + }; // Implement parallel computation of Hexa with c++ thread implementation template @@ -529,6 +535,7 @@ namespace 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 @@ -540,7 +547,7 @@ namespace Grid* _grid; Bnd_Box _bndBox; IntCurvesFace_Intersector* _surfaceInt; - vector< std::pair< GridLine*, F_IntersectPoint > > _intersections; + std::vector< std::pair< GridLine*, F_IntersectPoint > > _intersections; FaceGridIntersector(): _grid(0), _surfaceInt(0) {} void Intersect(); @@ -549,7 +556,7 @@ namespace { for ( size_t i = 0; i < _intersections.size(); ++i ) { - multiset< F_IntersectPoint >::iterator ip = + std::multiset< F_IntersectPoint >::iterator ip = _intersections[i].first->_intPoints.insert( _intersections[i].second ); ip->_faceIDs.reserve( 1 ); ip->_faceIDs.push_back( _faceID ); @@ -572,7 +579,7 @@ namespace return _surfaceInt; } #ifdef WITH_TBB - bool IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const; + bool IsThreadSafe(std::set< const Standard_Transient* >& noSafeTShapes) const; #endif }; @@ -583,8 +590,8 @@ namespace */ struct ParallelIntersector { - vector< FaceGridIntersector >& _faceVec; - ParallelIntersector( vector< FaceGridIntersector >& faceVec): _faceVec(faceVec){} + std::vector< FaceGridIntersector >& _faceVec; + ParallelIntersector( std::vector< FaceGridIntersector >& faceVec): _faceVec(faceVec){} void operator() ( const tbb::blocked_range& r ) const { for ( size_t i = r.begin(); i != r.end(); ++i ) @@ -617,7 +624,7 @@ namespace gp_Torus _torus; IntCurvesFace_Intersector* _surfaceInt; - vector< F_IntersectPoint > _intPoints; + std::vector< F_IntersectPoint > _intPoints; void IntersectWithPlane (const GridLine& gridLine); void IntersectWithCylinder(const GridLine& gridLine); @@ -626,7 +633,14 @@ namespace void IntersectWithTorus (const GridLine& gridLine); void IntersectWithSurface (const GridLine& gridLine); - bool UVIsOnFace() const; + /* + * Return true if (_u,_v) is on the face + */ + bool UVIsOnFace() const + { + TopAbs_State state = _surfaceInt->ClassifyUVPoint(gp_Pnt2d( _u,_v )); + return ( state == TopAbs_IN || state == TopAbs_ON ); + } void addIntPoint(const bool toClassify=true); bool isParamOnLineOK( const double linLength ) { @@ -636,356 +650,7 @@ namespace ~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 -} +} // end namespace Cartesian3D +} // end namespace StdMeshers #endif diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index f1f8f6965..8a2b10300 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -23,8 +23,9 @@ #include "StdMeshers_Cartesian_3D_Hexahedron.hxx" +using namespace std; using namespace SMESH; -using namespace gridtools; +using namespace StdMeshers::Cartesian3D; std::mutex _eMutex; @@ -856,8 +857,31 @@ void Hexahedron::computeElements( const Solid* solid, int solidIndex ) /*! * \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 ) +void Hexahedron::defineHexahedralFaces( const Solid* solid, const IsInternalFlag intFlag ) { + 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 ); + } + } + } + + const bool toCheckSideDivision = isImplementEdges() || intFlag & IS_CUT_BY_INTERNAL_FACE; + + std::vector< _OrientedLink > splits; + std::vector< _Node* > chainNodes; for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron { _Face& quad = _hexQuads[ iF ] ; @@ -1026,6 +1050,47 @@ void Hexahedron::defineHexahedralFaces( std::vector< _OrientedLink >& splits, st } // } +//================================================================================ +/*! + * \brief get a remaining link to start from, one lying on minimal nb of FACEs + */ +Hexahedron::TFaceOfLink Hexahedron::findStartLink(const vector<_OrientedLink*>& freeLinks, + std::set& usedFaceIDs) +{ + TFaceOfLink faceOfLink( -1, -1 ); + TFaceOfLink facesOfLink[3] = { faceOfLink, faceOfLink, faceOfLink }; + for ( size_t iL = 0; iL < freeLinks.size(); ++iL ) { + if ( freeLinks[ iL ] ) { + std::vector< TGeomID > faces = freeLinks[ iL ]->GetNotUsedFaces( 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 ) { + _OrientedLink* curLinkLocal = freeLinks[ iL ]; + if ( curLinkLocal ) { + faceOfLink.first = curLinkLocal->FirstNode()->IsLinked( curLinkLocal->LastNode()->_intPoint ); + faceOfLink.second = iL; + } + } + usedFaceIDs.clear(); + } + return faceOfLink; +} + //================================================================================ /*! * \brief Compute mesh volumes resulted from intersection of the Hexahedron @@ -1041,36 +1106,10 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) 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 ); - } - } + const bool hasEdgeIntersections = !_eIntPoints.empty(); // 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 ); + defineHexahedralFaces( solid, intFlag ); // Create polygons closing holes in a polyhedron // ---------------------------------------------- @@ -1080,24 +1119,24 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) _intNodes[ iN ]._usedInFace = 0; // add polygons to their links and mark used nodes - for ( size_t iP = 0; iP < _polygons.size(); ++iP ) - { + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) { _Face& polygon = _polygons[ iP ]; - for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) - { + 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 ) + 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; @@ -1123,11 +1162,11 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) } 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(); @@ -1147,9 +1186,11 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) ( 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 ] )) + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) { + curLink = freeLinks[ iL ]; + if ( curLink ) freeLinks[ iL ] = 0; + } polygon._links.push_back( *curLink ); --nbFreeLinks; do @@ -1170,109 +1211,70 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) 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]; + TFaceOfLink faceOfLink = findStartLink(freeLinks, usedFaceIDs); + curFace = faceOfLink.first; + curLink = freeLinks[ faceOfLink.second ]; + freeLinks[ faceOfLink.second ] = 0; - 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 - { + do { // go forward from curLink curNode = curLink->LastNode(); curLink = 0; - for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) { if ( freeLinks[ iL ] && - freeLinks[ iL ]->FirstNode() == curNode && - freeLinks[ iL ]->LastNode()->IsOnFace( curFace )) - { + 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 - { + do { // go backward from curLink curNode = curLink->FirstNode(); curLink = 0; - for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) { if ( freeLinks[ iL ] && - freeLinks[ iL ]->LastNode() == curNode && - freeLinks[ iL ]->FirstNode()->IsOnFace( curFace )) - { + 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 ) - { + 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 ) + vector< _Node* > chainNodes; + //chainNodes.clear(); + for ( size_t iN = 0; iN < _vIntNodes.size(); ++iN ) { if ( !_vIntNodes[ iN ]->IsUsedInFace() && - _vIntNodes[ iN ]->IsOnFace( curFace )) - { + _vIntNodes[ iN ]->IsOnFace( curFace )) { _vIntNodes[ iN ]->_usedInFace = &polygon; chainNodes.push_back( _vIntNodes[ iN ] ); } + } if ( chainNodes.size() > 1 && - curFace != _grid->PseudoIntExtFaceID() ) /////// TODO - { + curFace != _grid->PseudoIntExtFaceID() ) { /////// TODO sortVertexNodes( chainNodes, curNode, curFace ); } - for ( size_t i = 0; i < chainNodes.size(); ++i ) - { + for ( size_t i = 0; i < chainNodes.size(); ++i ) { polygon.AddPolyLink( chainNodes[ i ], curNode ); curNode = chainNodes[ i ]; freeLinks.push_back( &polygon._links.back() ); @@ -1290,7 +1292,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) } // if there are intersections with EDGEs if ( polygon._links.size() < 2 || - polygon._links[0].LastNode() != polygon._links.back().FirstNode() ) + polygon._links[0].LastNode() != polygon._links.back().FirstNode() ) { _polygons.clear(); break; // closed polygon not found -> invalid polyhedron @@ -1311,7 +1313,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) if ( iPolygon == _polygons.size()-1 ) _polygons.pop_back(); } - else // polygon._links.size() >= 2 + else // polygon._links.size() > 2 { // add polygon to its links for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) @@ -1322,7 +1324,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) if ( /*hasEdgeIntersections &&*/ iPolygon == _polygons.size() - 1 ) { // check that a polygon does not lie on a hexa side - coplanarPolyg = 0; + _Face* coplanarPolyg = 0; for ( size_t iL = 0; iL < polygon._links.size() && !coplanarPolyg; ++iL ) { if ( polygon._links[ iL ].NbFaces() < 2 ) @@ -1332,11 +1334,13 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) 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; } @@ -1350,11 +1354,13 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) // 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 ] ); @@ -1362,8 +1368,11 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) 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 ) { @@ -1371,6 +1380,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) 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 @@ -1395,9 +1405,12 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) } break; } + } } + } // set coplanarPolyg to be re-created next for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + { if ( coplanarPolyg == & _polygons[ iP ] ) { iPolygon = iP; @@ -1405,6 +1418,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) _polygons[ iPolygon ]._polyLinks.clear(); break; } + } _polygons.pop_back(); usedFaceIDs.erase( curFace ); continue; @@ -1415,6 +1429,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) } // end of case ( polygon._links.size() > 2 ) } // while ( nbFreeLinks > 0 ) + // std::cout << "3\n"; for ( auto & face_ip : tmpAddedFace ) { @@ -1435,13 +1450,14 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) _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 ) { @@ -1474,8 +1490,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) } } - - /* This call is irrelevant here because _volumeDefs datas are were not filled! + /* This call is irrelevant here because _volumeDefs datas were not filled! or .... it is potentialy filled by other thread?? */ _volumeDefs._nodes.clear(); _volumeDefs._quantities.clear(); @@ -4223,4 +4238,4 @@ bool Hexahedron::_SplitIterator::Next() } } 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 index c87877b4d..ec1e1d0fe 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -35,9 +35,9 @@ #include "SMESH_StdMeshers.hxx" #include "StdMeshers_Cartesian_3D_Grid.hxx" -using namespace std; -using namespace SMESH; -namespace +namespace StdMeshers +{ +namespace Cartesian3D { // -------------------------------------------------------------------------- /*! @@ -49,14 +49,14 @@ namespace int _dInd[4][3]; size_t _nbCells[3]; int _i,_j,_k; - Grid* _grid; + StdMeshers::Cartesian3D::Grid* _grid; - CellsAroundLink( Grid* grid, int iDir ): + CellsAroundLink( StdMeshers::Cartesian3D::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->_coords[1].size() - 1, + grid->_coords[2].size() - 1 }, _grid( grid ) { const int iDirOther[3][2] = {{ 1,2 },{ 0,2 },{ 0,1 }}; @@ -85,419 +85,433 @@ namespace 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 + // -------------------------------------------------------------------------- + /*! + * \brief Class representing topology of the hexahedron and creating a mesh + * volume basing on analysis of hexahedron intersection with geometry + */ + class STDMESHERS_EXPORT Hexahedron { - _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 + // -------------------------------------------------------------------------------- + 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 { - 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; + const SMDS_MeshNode* _node; // mesh node at hexahedron corner + const SMDS_MeshNode* _boundaryCornerNode; // missing mesh node due to hex truncation on the boundary + const StdMeshers::Cartesian3D::B_IntersectPoint* _intPoint; + const _Face* _usedInFace; + char _isInternalFlags; - if ( _link->_faces[1] == f ) + _Node(const SMDS_MeshNode* n=0, const StdMeshers::Cartesian3D::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 StdMeshers::Cartesian3D::E_IntersectPoint* EdgeIntPnt() const + { return static_cast< const StdMeshers::Cartesian3D::E_IntersectPoint* >( _intPoint ); } + const StdMeshers::Cartesian3D::F_IntersectPoint* FaceIntPnt() const + { return static_cast< const StdMeshers::Cartesian3D::F_IntersectPoint* >( _intPoint ); } + const std::vector< StdMeshers::Cartesian3D::TGeomID >& faces() const { return _intPoint->_faceIDs; } + StdMeshers::Cartesian3D::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 ) { - _link->_faces[1] = 0; + return polygon ? ( _usedInFace == polygon ) : bool( _usedInFace ); } - else if ( _link->_faces[0] == f ) + StdMeshers::Cartesian3D::TGeomID IsLinked( const StdMeshers::Cartesian3D::B_IntersectPoint* other, + StdMeshers::Cartesian3D::TGeomID avoidFace=-1 ) const // returns id of a common face { - _link->_faces[0] = 0; - if ( _link->_faces[1] ) + return _intPoint ? _intPoint->HasCommonFace( other, avoidFace ) : 0; + } + bool IsOnFace( StdMeshers::Cartesian3D::TGeomID faceID ) const // returns true if faceID is found + { + return _intPoint ? _intPoint->IsOnFace( faceID ) : false; + } + size_t GetCommonFaces( const StdMeshers::Cartesian3D::B_IntersectPoint * other, + StdMeshers::Cartesian3D::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 StdMeshers::Cartesian3D::E_IntersectPoint* eip = + dynamic_cast< const StdMeshers::Cartesian3D::E_IntersectPoint* >( _intPoint )) + return eip->_point; + return gp_Pnt( 1e100, 0, 0 ); + } + StdMeshers::Cartesian3D::TGeomID ShapeID() const + { + if ( const StdMeshers::Cartesian3D::E_IntersectPoint* eip = + dynamic_cast< const StdMeshers::Cartesian3D::E_IntersectPoint* >( _intPoint )) + return eip->_shapeID; + return 0; + } + + void Add( const StdMeshers::Cartesian3D::E_IntersectPoint* ip ); + }; + + // -------------------------------------------------------------------------------- + struct _Link // link connecting two _Node's + { + _Node* _nodes[2]; + _Face* _faces[2]; // polygons sharing a link + std::vector< const StdMeshers::Cartesian3D::F_IntersectPoint* > _fIntPoints; // GridLine intersections with FACEs + std::vector< _Node* > _fIntNodes; // _Node's at _fIntPoints + std::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; } + + // returns supporting FACEs + std::vector< StdMeshers::Cartesian3D::TGeomID > GetNotUsedFaces + (const std::set& usedIDs ) const + { + std::vector< StdMeshers::Cartesian3D::TGeomID > faces; + const StdMeshers::Cartesian3D::B_IntersectPoint *ip0, *ip1; + if (( ip0 = _link->_nodes[0]->_intPoint ) && + ( ip1 = _link->_nodes[1]->_intPoint )) { - _link->_faces[0] = _link->_faces[1]; + 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 StdMeshers::Cartesian3D::E_IntersectPoint* >( _link->_nodes[0]->_intPoint ) || + dynamic_cast< const StdMeshers::Cartesian3D::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; } } - } - }; - // -------------------------------------------------------------------------------- - 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 + void RemoveFace( const _Face* f ) + { + if ( !_link->_faces[0] ) return; - _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; + 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; } + } } - _Link l; - l._nodes[0] = n0; - l._nodes[1] = n1; - _polyLinks.push_back( l ); - _links.push_back( _OrientedLink( &_polyLinks.back() )); + }; + + // -------------------------------------------------------------------------------- + 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; + std::vector< _OrientedLink > _links; // links on GridLine's + std::vector< _Link > _polyLinks; // links added to close a polygonal face + std::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 StdMeshers::Cartesian3D::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 StdMeshers::Cartesian3D::E_IntersectPoint* EdgeIntPnt() const + { return static_cast< const StdMeshers::Cartesian3D::E_IntersectPoint* >( _intPoint ); } + _ptr Ptr() const { return Node() ? (_ptr) Node() : (_ptr) EdgeIntPnt(); } + bool operator==(const _nodeDef& other ) const { return Ptr() == other.Ptr(); } + }; + + std::vector< _nodeDef > _nodes; + std::vector< int > _quantities; + _volumeDef* _next; // to store several _volumeDefs in a chain + StdMeshers::Cartesian3D::TGeomID _solidID; + double _size; + const SMDS_MeshElement* _volume; // new volume + std::vector _brotherVolume; // produced due to poly split + std::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 + std::vector< _Face > _polygons; + + // intresections with EDGEs + std::vector< const StdMeshers::Cartesian3D::E_IntersectPoint* > _eIntPoints; + + // additional nodes created at intersection points + std::vector< _Node > _intNodes; + + // nodes inside the hexahedron (at VERTEXes) refer to _intNodes + std::vector< _Node* > _vIntNodes; + + // computed volume elements + _volumeDef _volumeDefs; + + StdMeshers::Cartesian3D::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(StdMeshers::Cartesian3D::Grid* grid); + int MakeElements(SMESH_MesherHelper& helper, + const TEdge2faceIDsMap& 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( const Solid* solid, const IsInternalFlag intFlag ); + bool compute( const Solid* solid, const IsInternalFlag intFlag ); + size_t getSolids( StdMeshers::Cartesian3D::TGeomID ids[] ); + bool isCutByInternalFace( IsInternalFlag & maxFlag ); + void addEdges(SMESH_MesherHelper& helper, + std::vector< Hexahedron* >& intersectedHex, + const TEdge2faceIDsMap& 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 StdMeshers::Cartesian3D::E_IntersectPoint* ip, int* facets, int& sub ); + bool addIntersection( const StdMeshers::Cartesian3D::E_IntersectPoint* ip, + std::vector< Hexahedron* >& hexes, + int ijk[], int dIJK[] ); + bool isQuadOnFace( const size_t iQuad ); + bool findChain( _Node* n1, _Node* n2, _Face& quad, std::vector<_Node*>& chainNodes ); + bool closePolygon( _Face* polygon, std::vector<_Node*>& chainNodes ) const; + bool findChainOnEdge( const std::vector< _OrientedLink >& splits, + const _OrientedLink& prevSplit, + const _OrientedLink& avoidSplit, + const std::set< StdMeshers::Cartesian3D::TGeomID > & concaveFaces, + size_t & iS, + _Face& quad, + std::vector<_Node*>& chn); + typedef std::pair< StdMeshers::Cartesian3D::TGeomID, int > TFaceOfLink; // (face, link) + static TFaceOfLink findStartLink(const std::vector< _OrientedLink* >& freeLinks, + std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs); + int addVolumes( SMESH_MesherHelper& helper ); + void addFaces( SMESH_MesherHelper& helper, + const std::vector< const SMDS_MeshElement* > & boundaryVolumes ); + void addSegments( SMESH_MesherHelper& helper, + const TEdge2faceIDsMap& edge2faceIDsMap ); + void getVolumes( std::vector< const SMDS_MeshElement* > & volumes ); + void getBoundaryElems( std::vector< const SMDS_MeshElement* > & boundaryVolumes ); + void removeExcessSideDivision(const std::vector< Hexahedron* >& allHexa); + void removeExcessNodes(std::vector< Hexahedron* >& allHexa); + void preventVolumesOverlapping(); + StdMeshers::Cartesian3D::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(std::vector<_Node*>& nodes, + _Node* curNode, + StdMeshers::Cartesian3D::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( std::vector< _Node* >& nodes, + const StdMeshers::Cartesian3D::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; } - }; - // -------------------------------------------------------------------------------- - struct _volumeDef // holder of nodes of a volume mesh element - { - typedef void* _ptr; + 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; - struct _nodeDef + typedef boost::container::flat_map< StdMeshers::Cartesian3D::TGeomID, size_t > TID2Nb; + static void insertAndIncrement( StdMeshers::Cartesian3D::TGeomID id, TID2Nb& id2nbMap ) { - 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 + TID2Nb::value_type s0( id, 0 ); + TID2Nb::iterator id2nb = id2nbMap.insert( s0 ).first; + id2nb->second++; + } + }; // class Hexahedron +} // end namespace Cartesian3D +} // end namespace StdMeshers #endif diff --git a/test/data/HexahedronTest/NRTMJ4.brep b/test/data/HexahedronTest/NRTMJ4.brep index 3ed6ccc59..7f6412ea5 100644 --- a/test/data/HexahedronTest/NRTMJ4.brep +++ b/test/data/HexahedronTest/NRTMJ4.brep @@ -1,8 +1,8 @@ DBRep_DrawableShape -CASCADE Topology V3, (c) Open Cascade +CASCADE Topology V1, (c) Matra-Datavision Locations 0 -Curve2ds 59 +Curve2ds 54 1 0 0 1 0 1 0 0 1 0 1 100 0 0 -1 @@ -11,58 +11,53 @@ Curve2ds 59 1 0 0 1 0 1 0 0 0 -1 1 0 0 0 1 -1 0 0 1 0 1 0 60 1 0 -1 0 0 0 1 1 0 0 1 0 1 16.899999999999999 50 0 1 1 0 0 1 0 -1 0 0 1 0 1 16.899999999999999 50 1 0 -1 59.899999999999999 0 0 1 1 0 0 1 0 1 76.799999999999997 50 0 1 +1 0 0 1 0 +1 0 60 1 0 +1 0 0 1 0 1 100 0 0 1 1 0 0 1 0 1 0 0 0 1 1 0 0 1 0 -1 100 0 0 1 1 0 50 1 0 +1 100 0 0 1 +1 60 0 0 1 1 100 0 0 -1 -1 60 0 0 1 -1 0 -50 1 0 1 0 60 1 0 -1 0 0 0 1 -1 0 50 1 0 -1 16.899999999999999 50 0 1 1 0 -50 1 0 -1 0 0 1 0 -1 16.899999999999999 50 1 0 -1 59.899999999999999 0 0 1 +1 16.899999999999999 50 0 1 1 0 50 1 0 +1 16.899999999999999 50 1 0 +1 0 -50 1 0 1 76.799999999999997 50 0 1 +1 0 50 1 0 +1 0 60 1 0 +1 0 -50 1 0 1 0 0 0 1 1 0 50 1 0 -1 0 0 0 -1 1 60 0 0 1 -1 0 0 1 0 -2 0 0 1 0 -0 1 10 +1 0 0 0 -1 2 25 25 -1 0 0 1 10 -1 0 0 0 -1 -1 10 0 0 1 +1 0 0 1 0 1 16.899999999999999 0 0 -1 +1 10 0 0 1 +1 0 0 0 1 1 0 0 0 -1 -1 0 0 0 1 1 59.899999999999999 0 0 -1 1 0 0 0 1 -1 59.899999999999999 0 0 -1 1 10 0 0 1 1 76.799999999999997 0 0 -1 1 0 50 1 0 2 0 0 1 0 -0 1 10 1 6.2831853071795862 -0 0 1 1 0 -0 0 1 -Curves 25 +Curves 27 1 0 0 0 0 0 1 1 0 0 100 -0 1 0 1 0 50 0 0 0 1 @@ -71,6 +66,7 @@ Curves 25 1 50 0 16.899999999999999 1 0 -0 1 50 0 16.899999999999999 0 0 1 1 50 0 76.799999999999997 1 0 -0 +1 60 0 0 0 0 1 1 0 0 100 1 0 -0 1 0 0 0 1 0 -0 1 0 50 100 1 0 -0 @@ -79,6 +75,7 @@ Curves 25 1 50 50 16.899999999999999 1 0 -0 1 50 50 16.899999999999999 0 0 1 1 50 50 76.799999999999997 1 0 -0 +1 60 50 0 0 0 1 1 0 50 0 1 0 -0 1 60 0 0 -0 1 0 2 25 25 0 0 0 -1 -1 0 -0 0 1 0 10 @@ -89,156 +86,21 @@ Curves 25 2 25 25 -50 0 0 -1 -1 0 -0 0 1 0 10 1 15.000000000000002 24.999999999999996 0 0 0 -1 Polygon3D 0 -PolygonOnTriangulations 54 -2 1 2 -p 0.6000000008 1 0 100 -2 3 4 -p 0.6000000008 1 0 100 -2 2 4 -p 0.6000000008 1 0 50 -2 1 2 -p 0.6000000008 1 0 50 -2 3 4 -p 0.6000000008 1 0 100 -2 3 4 -p 0.6000000008 1 0 100 -2 1 3 -p 0.6000000008 1 0 50 -2 1 2 -p 0.6000000008 1 0 50 -2 1 2 -p 0.6000000008 1 0 16.9 -2 1 2 -p 0.6000000008 1 0 16.9 -2 8 2 -p 0.6000000008 1 0 10 -2 1 3 -p 0.6000000008 1 0 10 -2 8 7 -p 0.6000000008 1 0 59.9 -2 1 2 -p 0.6000000008 1 0 59.9 -2 7 6 -p 0.6000000008 1 0 10 -2 1 3 -p 0.6000000008 1 0 10 -2 6 5 -p 0.6000000008 1 76.8 100 -2 1 2 -p 0.6000000008 1 76.8 100 -2 4 5 -p 0.6000000008 1 0 60 -2 1 3 -p 0.6000000008 1 0 60 -2 3 1 -p 0.6000000008 1 0 60 -2 1 3 -p 0.6000000008 1 0 60 -2 2 4 -p 0.6000000008 1 0 60 -2 4 5 -p 0.6000000008 1 0 60 -2 3 4 -p 0.6000000008 1 0 50 -2 2 4 -p 0.6000000008 1 0 50 -2 1 2 -p 0.6000000008 1 0 16.9 -2 3 4 -p 0.6000000008 1 0 16.9 -2 8 2 -p 0.6000000008 1 0 10 -2 2 4 -p 0.6000000008 1 0 10 -2 8 7 -p 0.6000000008 1 0 59.9 -2 3 4 -p 0.6000000008 1 0 59.9 -2 7 6 -p 0.6000000008 1 0 10 -2 2 4 -p 0.6000000008 1 0 10 -2 6 5 -p 0.6000000008 1 76.8 100 -2 3 4 -p 0.6000000008 1 76.8 100 -2 3 1 -p 0.6000000008 1 0 60 -2 2 4 -p 0.6000000008 1 0 60 -2 3 4 -p 0.6000000008 1 0 50 -2 1 3 -p 0.6000000008 1 0 50 -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.6000000008 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.6000000008 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 2 4 -p 0.6000000008 1 0 50 -2 3 4 -p 0.6000000008 1 0 50 -2 1 2 -p 0.6000000008 1 0 50 -2 1 3 -p 0.6000000008 1 0 50 -2 2 4 -p 0.6000000008 1 0 50 -2 1 2 -p 0.6000000008 1 0 50 -2 3 4 -p 0.6000000008 1 0 50 -2 1 3 -p 0.6000000008 1 0 50 -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.6000000008 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.6000000008 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.6000000008 1 0 50 -2 74 37 -p 0.6000000008 1 0 50 -Surfaces 15 +PolygonOnTriangulations 0 +Surfaces 12 1 0 0 0 1 0 -0 0 0 1 0 -1 0 1 0 0 0 -0 1 0 0 0 1 1 0 -0 1 0 0 100 0 0 1 1 0 -0 -0 1 0 1 0 50 0 -0 1 0 0 0 1 1 0 -0 1 0 0 0 0 0 1 1 0 -0 -0 1 0 1 60 0 0 1 0 -0 0 0 1 0 -1 0 -1 50 0 16.899999999999999 -0 1 0 0 0 1 1 0 -0 1 50 0 16.899999999999999 0 0 1 1 0 -0 -0 1 0 1 50 0 16.899999999999999 1 0 -0 0 0 1 0 -1 0 1 50 0 76.799999999999997 0 0 1 1 0 -0 -0 1 0 -1 50 50 16.899999999999999 -0 1 0 0 0 1 1 0 -0 +1 60 0 0 1 0 -0 0 0 1 0 -1 0 2 25 25 0 0 0 -1 -1 0 -0 0 1 0 10 -1 25 25 0 0 0 -1 -1 0 -0 0 1 0 -1 60 0 16.899999999999999 1 0 -0 0 0 1 0 -1 0 1 25 25 -50 0 0 -1 -1 0 -0 0 1 0 -Triangulations 12 -4 2 1 0 0 -0 0 0 0 0 100 0 50 0 0 50 100 0 0 100 0 0 -50 100 -50 2 1 3 2 3 4 -8 6 1 0 0 -60 0 0 60 0 16.9 0 0 0 0 0 100 60 0 100 60 0 76.8 50 0 76.8 50 0 16.9 0 60 16.9 60 0 0 100 0 100 60 76.8 60 76.8 50 16.9 50 8 1 3 2 1 8 7 3 4 7 8 3 5 6 7 5 7 4 -4 2 1 0 0 -0 0 100 0 50 100 60 0 100 60 50 100 0 0 0 50 60 0 60 50 3 2 1 4 2 3 -8 6 1 0 0 -60 50 0 60 50 16.9 0 50 0 0 50 100 60 50 100 60 50 76.8 50 50 76.8 50 50 16.9 0 60 16.9 60 0 0 100 0 100 60 76.8 60 76.8 50 16.9 50 8 1 3 2 1 8 7 3 4 7 8 3 5 6 7 5 7 4 -40 40 1 0 1.77635683940025e-15 -0 0 0 0 50 0 60 0 0 60 50 0 15 25 0 15.1519224698779 26.7364817766693 0 15.6030737921409 28.4202014332567 0 16.3397459621556 30 0 17.3395555688102 31.4278760968654 0 18.5721239031346 32.6604444311898 0 20 33.6602540378444 0 21.5797985667433 34.3969262078591 0 23.2635182233307 34.8480775301221 0 25 35 0 26.7364817766693 34.8480775301221 0 28.4202014332567 34.3969262078591 0 30 33.6602540378444 0 31.4278760968654 32.6604444311898 0 32.6604444311898 31.4278760968654 0 33.6602540378444 30 0 34.3969262078591 28.4202014332567 0 34.8480775301221 26.7364817766693 0 35 25 0 34.8480775301221 23.2635182233307 0 34.3969262078591 21.5797985667433 0 33.6602540378444 20 0 32.6604444311898 18.5721239031346 0 31.4278760968654 17.3395555688102 0 30 16.3397459621556 0 28.4202014332567 15.6030737921409 0 26.7364817766693 15.1519224698779 0 25 15 0 23.2635182233307 15.1519224698779 0 21.5797985667433 15.6030737921409 0 20 16.3397459621556 0 18.5721239031346 17.3395555688102 0 17.3395555688102 18.5721239031346 0 16.3397459621556 20 0 15.6030737921409 21.5797985667433 0 15.1519224698779 23.2635182233307 0 0 0 0 50 60 0 60 50 15 25 15.1519224698779 26.7364817766693 15.6030737921409 28.4202014332567 16.3397459621556 30 17.3395555688102 31.4278760968654 18.5721239031346 32.6604444311898 20 33.6602540378444 21.5797985667433 34.3969262078591 23.2635182233307 34.8480775301221 25 35 26.7364817766693 34.8480775301221 28.4202014332567 34.3969262078591 30 33.6602540378444 31.4278760968654 32.6604444311898 32.6604444311898 31.4278760968654 33.6602540378444 30 34.3969262078591 28.4202014332567 34.8480775301221 26.7364817766693 35 25 34.8480775301221 23.2635182233307 34.3969262078591 21.5797985667433 33.6602540378444 20 32.6604444311898 18.5721239031346 31.4278760968654 17.3395555688102 30 16.3397459621556 28.4202014332567 15.6030737921409 26.7364817766693 15.1519224698779 25 15 23.2635182233307 15.1519224698779 21.5797985667433 15.6030737921409 20 16.3397459621556 18.5721239031346 17.3395555688102 17.3395555688102 18.5721239031346 16.3397459621556 20 15.6030737921409 21.5797985667433 15.1519224698779 23.2635182233307 36 37 1 38 1 37 35 36 1 39 1 38 34 35 1 40 1 39 33 34 1 5 1 40 32 33 1 31 32 1 2 5 6 2 6 7 2 7 8 2 8 9 2 1 5 10 2 9 11 2 10 12 2 11 13 2 12 3 24 25 3 25 26 3 26 27 3 27 28 3 28 29 3 29 30 3 30 31 3 31 1 14 2 13 23 24 3 15 2 14 4 2 15 4 15 16 4 16 17 4 17 18 4 18 19 4 19 20 4 20 21 4 21 22 4 22 23 4 23 3 -4 2 1 0 0 -60 0 0 60 0 16.9 60 50 0 60 50 16.9 0 0 16.9 0 0 -50 16.9 -50 1 3 4 2 1 4 -4 2 1 0 0 -50 0 16.9 50 50 16.9 60 0 16.9 60 50 16.9 0 0 0 50 10 0 10 50 4 2 1 4 1 3 -4 2 1 0 7.105427357601e-15 -50 0 16.9 50 0 76.8 50 50 16.9 50 50 76.8 0 0 59.9 0 0 -50 59.9 -50 2 1 3 2 3 4 -4 2 1 0 0 -50 0 76.8 50 50 76.8 60 0 76.8 60 50 76.8 0 0 0 50 10 0 10 50 4 2 1 4 1 3 -4 2 1 0 0 -60 0 76.8 60 0 100 60 50 76.8 60 50 100 76.8 0 100 0 76.8 -50 100 -50 1 3 4 2 1 4 -74 72 1 0 0.0380530190825497 -15 25 -50 15.1519224698779 26.7364817766693 -50 15.6030737921409 28.4202014332567 -50 16.3397459621556 30 -50 17.3395555688102 31.4278760968654 -50 18.5721239031346 32.6604444311898 -50 20 33.6602540378444 -50 21.5797985667433 34.3969262078591 -50 23.2635182233307 34.8480775301221 -50 25 35 -50 26.7364817766693 34.8480775301221 -50 28.4202014332567 34.3969262078591 -50 30 33.6602540378444 -50 31.4278760968654 32.6604444311898 -50 32.6604444311898 31.4278760968654 -50 33.6602540378444 30 -50 34.3969262078591 28.4202014332567 -50 34.8480775301221 26.7364817766693 -50 35 25 -50 34.8480775301221 23.2635182233307 -50 34.3969262078591 21.5797985667433 -50 33.6602540378444 20 -50 32.6604444311898 18.5721239031346 -50 31.4278760968654 17.3395555688102 -50 30 16.3397459621556 -50 28.4202014332567 15.6030737921409 -50 26.7364817766693 15.1519224698779 -50 25 15 -50 23.2635182233307 15.1519224698779 -50 21.5797985667433 15.6030737921409 -50 20 16.3397459621556 -50 18.5721239031346 17.3395555688102 -50 17.3395555688102 18.5721239031346 -50 16.3397459621556 20 -50 15.6030737921409 21.5797985667433 -50 15.1519224698779 23.2635182233307 -50 15 25 -50 15 25 0 15.1519224698779 26.7364817766693 0 15.6030737921409 28.4202014332567 0 16.3397459621556 30 0 17.3395555688102 31.4278760968654 0 18.5721239031346 32.6604444311898 0 20 33.6602540378444 0 21.5797985667433 34.3969262078591 0 23.2635182233307 34.8480775301221 0 25 35 0 26.7364817766693 34.8480775301221 0 28.4202014332567 34.3969262078591 0 30 33.6602540378444 0 31.4278760968654 32.6604444311898 0 32.6604444311898 31.4278760968654 0 33.6602540378444 30 0 34.3969262078591 28.4202014332567 0 34.8480775301221 26.7364817766693 0 35 25 0 34.8480775301221 23.2635182233307 0 34.3969262078591 21.5797985667433 0 33.6602540378444 20 0 32.6604444311898 18.5721239031346 0 31.4278760968654 17.3395555688102 0 30 16.3397459621556 0 28.4202014332567 15.6030737921409 0 26.7364817766693 15.1519224698779 0 25 15 0 23.2635182233307 15.1519224698779 0 21.5797985667433 15.6030737921409 0 20 16.3397459621556 0 18.5721239031346 17.3395555688102 0 17.3395555688102 18.5721239031346 0 16.3397459621556 20 0 15.6030737921409 21.5797985667433 0 15.1519224698779 23.2635182233307 0 15 25 0 0 50 0.174532925199433 50 0.349065850398866 50 0.523598775598299 50 0.698131700797732 50 0.872664625997165 50 1.0471975511966 50 1.22173047639603 50 1.39626340159546 50 1.5707963267949 50 1.74532925199433 50 1.91986217719376 50 2.0943951023932 50 2.26892802759263 50 2.44346095279206 50 2.61799387799149 50 2.79252680319093 50 2.96705972839036 50 3.14159265358979 50 3.31612557878922 50 3.49065850398866 50 3.66519142918809 50 3.83972435438752 50 4.01425727958696 50 4.18879020478639 50 4.36332312998582 50 4.53785605518525 50 4.71238898038469 50 4.88692190558412 50 5.06145483078355 50 5.23598775598299 50 5.41052068118242 50 5.58505360638185 50 5.75958653158128 50 5.93411945678072 50 6.10865238198015 50 6.28318530717959 50 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 42 43 5 4 41 6 43 44 6 5 43 7 6 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 18 17 54 18 54 55 18 55 56 19 18 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 64 65 27 26 63 28 27 65 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 37 36 73 37 73 74 -36 34 1 0 7.94410929039127e-15 -15 25 -50 15.1519224698779 26.7364817766693 -50 15.6030737921409 28.4202014332567 -50 16.3397459621556 30 -50 17.3395555688102 31.4278760968654 -50 18.5721239031346 32.6604444311898 -50 20 33.6602540378444 -50 21.5797985667433 34.3969262078591 -50 23.2635182233307 34.8480775301221 -50 25 35 -50 26.7364817766693 34.8480775301221 -50 28.4202014332567 34.3969262078591 -50 30 33.6602540378444 -50 31.4278760968654 32.6604444311898 -50 32.6604444311898 31.4278760968654 -50 33.6602540378444 30 -50 34.3969262078591 28.4202014332567 -50 34.8480775301221 26.7364817766693 -50 35 25 -50 34.8480775301221 23.2635182233307 -50 34.3969262078591 21.5797985667433 -50 33.6602540378444 20 -50 32.6604444311898 18.5721239031346 -50 31.4278760968654 17.3395555688102 -50 30 16.3397459621556 -50 28.4202014332567 15.6030737921409 -50 26.7364817766693 15.1519224698779 -50 25 15 -50 23.2635182233307 15.1519224698779 -50 21.5797985667433 15.6030737921409 -50 20 16.3397459621556 -50 18.5721239031346 17.3395555688102 -50 17.3395555688102 18.5721239031346 -50 16.3397459621556 20 -50 15.6030737921409 21.5797985667433 -50 15.1519224698779 23.2635182233307 -50 10 -1.77635683940025e-15 9.84807753012208 1.7364817766693 9.39692620785909 3.42020143325669 8.66025403784439 5 7.66044443118978 6.42787609686539 6.42787609686539 7.66044443118978 5 8.66025403784439 3.42020143325669 9.39692620785908 1.7364817766693 9.84807753012208 0 10 -1.7364817766693 9.84807753012208 -3.42020143325669 9.39692620785909 -5 8.66025403784439 -6.42787609686539 7.66044443118978 -7.66044443118978 6.4278760968654 -8.66025403784438 5.00000000000001 -9.39692620785908 3.4202014332567 -9.84807753012208 1.73648177666932 -10 1.4210854715202e-14 -9.84807753012208 -1.73648177666929 -9.39692620785909 -3.42020143325667 -8.6602540378444 -4.99999999999998 -7.6604444311898 -6.42787609686538 -6.42787609686541 -7.66044443118977 -5.00000000000002 -8.66025403784437 -3.42020143325671 -9.39692620785907 -1.73648177666933 -9.84807753012208 -2.8421709430404e-14 -10 1.73648177666927 -9.84807753012209 3.42020143325666 -9.3969262078591 4.99999999999997 -8.6602540378444 6.42787609686536 -7.6604444311898 7.66044443118976 -6.42787609686542 8.66025403784437 -5.00000000000004 9.39692620785907 -3.42020143325673 9.84807753012207 -1.73648177666935 22 23 24 20 21 22 28 26 27 18 19 20 18 24 25 18 22 24 18 20 22 30 28 29 31 25 26 31 26 28 31 28 30 33 31 32 13 14 15 13 15 16 13 16 17 13 17 18 35 33 34 35 31 33 36 18 25 36 25 31 36 31 35 11 12 13 11 13 18 9 10 11 2 36 1 8 9 11 8 11 18 3 36 2 4 36 3 4 7 8 4 18 36 4 8 18 6 7 4 6 4 5 +Triangulations 0 TShapes 72 Ve @@ -260,8 +122,6 @@ Ed 1 1 0 0 100 2 1 1 0 0 100 2 2 2 0 0 100 -6 1 1 0 -6 2 2 0 0 0101000 @@ -278,8 +138,6 @@ Ed 1 2 0 0 50 2 3 1 0 0 50 2 4 3 0 0 50 -6 3 1 0 -6 4 3 0 0 0101000 @@ -296,8 +154,6 @@ Ed 1 3 0 0 100 2 5 1 0 0 100 2 6 4 0 0 100 -6 5 1 0 -6 6 4 0 0 0101000 @@ -307,8 +163,6 @@ Ed 1 4 0 0 50 2 7 1 0 0 50 2 8 5 0 0 50 -6 7 1 0 -6 8 5 0 0 0101000 @@ -319,7 +173,7 @@ Wi -70 0 -68 0 +66 0 +65 0 * Fa 0 1e-07 1 0 -2 1 + 0101000 +64 0 * Ve @@ -339,10 +193,8 @@ Ve Ed 1e-07 1 1 0 1 5 0 0 16.9 -2 9 6 0 0 16.9 -2 10 2 0 0 16.9 -6 9 2 0 -6 10 6 0 +2 9 2 0 0 16.9 +2 10 6 0 0 16.9 0 0101000 @@ -357,11 +209,8 @@ Ve Ed 1e-07 1 1 0 1 6 0 0 10 -2 11 7 0 0 10 -2 12 8 0 0 10 -2 13 2 0 0 10 -6 11 2 0 -6 12 7 0 +2 11 2 0 0 10 +2 12 7 0 0 10 0 0101000 @@ -376,11 +225,8 @@ Ve Ed 1e-07 1 1 0 1 7 0 0 59.9 -2 14 9 0 0 59.9 -2 15 7 0 0 59.9 -2 16 2 0 0 59.9 -6 13 2 0 -6 14 8 0 +2 13 2 0 0 59.9 +2 14 8 0 0 59.9 0 0101000 @@ -395,11 +241,8 @@ Ve Ed 1e-07 1 1 0 1 8 0 0 10 -2 17 7 0 0 10 -2 18 10 0 0 10 -2 19 2 0 0 10 -6 15 2 0 -6 16 9 0 +2 15 2 0 0 10 +2 16 9 0 0 10 0 0101000 @@ -413,33 +256,27 @@ Ve * Ed 1e-07 1 1 0 -1 5 0 76.8 100 -2 9 6 0 76.8 100 -2 10 2 0 76.8 100 -6 17 2 0 -6 18 10 0 +1 9 0 76.8 100 +2 17 2 0 76.8 100 +2 18 10 0 76.8 100 0 0101000 +55 0 -53 0 * Ed 1e-07 1 1 0 -1 9 0 0 60 -2 20 2 0 0 60 -2 21 3 0 0 60 -6 19 2 0 -6 20 3 0 +1 10 0 0 60 +2 19 2 0 0 60 +2 20 3 0 0 60 0 0101000 -53 0 +72 0 * Ed 1e-07 1 1 0 -1 10 0 0 60 -2 22 2 0 0 60 -2 23 5 0 0 60 -6 21 2 0 -6 22 5 0 +1 11 0 0 60 +2 21 2 0 0 60 +2 22 5 0 0 60 0 0101000 @@ -450,7 +287,7 @@ Wi -60 0 +58 0 -56 0 -54 0 -52 0 +51 0 +70 0 -50 0 * Fa 0 1e-07 2 0 -2 2 + 0101000 +49 0 * Ve @@ -462,22 +299,18 @@ Ve * Ed 1e-07 1 1 0 -1 11 0 0 60 +1 12 0 0 60 +2 23 3 0 0 60 2 24 4 0 0 60 -2 25 3 0 0 60 -6 23 3 0 -6 24 4 0 0 0101000 -47 0 +69 0 * Ed 1e-07 1 1 0 -1 12 0 0 50 -2 26 6 0 0 50 -2 27 3 0 0 50 -6 25 3 0 -6 26 10 0 +1 13 0 0 50 +2 25 3 0 0 50 +2 26 10 0 0 50 0 0101000 @@ -488,7 +321,7 @@ Wi -68 0 -46 0 +45 0 +51 0 * Fa 0 1e-07 3 0 -2 3 + 0101000 +44 0 * Ve @@ -507,11 +340,9 @@ Ve * Ed 1e-07 1 1 0 -1 13 0 0 16.9 +1 14 0 0 16.9 +2 27 4 0 0 16.9 2 28 6 0 0 16.9 -2 29 4 0 0 16.9 -6 27 4 0 -6 28 6 0 0 0101000 @@ -525,12 +356,9 @@ Ve * Ed 1e-07 1 1 0 -1 14 0 0 10 -2 30 11 0 0 10 -2 31 8 0 0 10 -2 32 4 0 0 10 -6 29 4 0 -6 30 7 0 +1 15 0 0 10 +2 29 4 0 0 10 +2 30 7 0 0 10 0 0101000 @@ -544,12 +372,9 @@ Ve * Ed 1e-07 1 1 0 -1 15 0 0 59.9 -2 33 9 0 0 59.9 -2 34 11 0 0 59.9 -2 35 4 0 0 59.9 -6 31 4 0 -6 32 8 0 +1 16 0 0 59.9 +2 31 4 0 0 59.9 +2 32 8 0 0 59.9 0 0101000 @@ -563,34 +388,27 @@ Ve * Ed 1e-07 1 1 0 -1 16 0 0 10 -2 36 11 0 0 10 -2 37 10 0 0 10 -2 38 4 0 0 10 -6 33 4 0 -6 34 9 0 +1 17 0 0 10 +2 33 4 0 0 10 +2 34 9 0 0 10 0 0101000 -35 0 +37 0 * Ed 1e-07 1 1 0 -1 13 0 76.8 100 -2 28 6 0 76.8 100 -2 29 4 0 76.8 100 -6 35 4 0 -6 36 10 0 +1 18 0 76.8 100 +2 35 4 0 76.8 100 +2 36 10 0 76.8 100 0 0101000 +35 0 -47 0 * Ed 1e-07 1 1 0 -1 17 0 0 60 -2 39 4 0 0 60 -2 40 5 0 0 60 -6 37 4 0 -6 38 5 0 +1 19 0 0 60 +2 37 4 0 0 60 +2 38 5 0 0 60 0 0101000 @@ -601,16 +419,14 @@ Wi -40 0 +38 0 -36 0 -34 0 -33 0 +46 0 +66 0 -32 0 * Fa 0 1e-07 4 0 -2 4 + 0101000 +31 0 * Ed 1e-07 1 1 0 -1 18 0 0 50 -2 41 6 0 0 50 -2 42 5 0 0 50 -6 39 5 0 -6 40 6 0 +1 20 0 0 50 +2 39 5 0 0 50 +2 40 6 0 0 50 0 0101000 @@ -628,12 +444,9 @@ Ve * Ed 1e-07 1 1 0 -1 19 0 0 6.28318530717959 -2 43 12 0 0 6.28318530717959 -2 44 13 0 0 6.28318530717959 -2 45 5 0 0 6.28318530717959 -6 41 11 0 -6 42 5 0 +1 21 0 0 6.28318530717959 +2 41 5 0 0 6.28318530717959 +2 42 11 0 0 6.28318530717959 0 0101000 @@ -644,17 +457,14 @@ Wi +26 0 * Fa 0 1e-07 5 0 -2 5 + 0101000 +28 0 +25 0 * Ed 1e-07 1 1 0 -1 20 0 0 50 -2 46 14 0 0 50 -2 47 8 0 0 50 -2 48 6 0 0 50 -6 43 6 0 -6 44 7 0 +1 22 0 0 50 +2 43 6 0 0 50 +2 44 7 0 0 50 0 0101000 @@ -665,16 +475,14 @@ Wi -60 0 -23 0 +40 0 +29 0 * Fa 0 1e-07 6 0 -2 6 + 0101000 +22 0 * Ed 1e-07 1 1 0 -1 21 0 0 50 -2 49 9 0 0 50 -2 50 8 0 0 50 -6 45 7 0 -6 46 8 0 +1 23 0 0 50 +2 45 7 0 0 50 +2 46 8 0 0 50 0 0101000 @@ -684,17 +492,15 @@ Wi 0101100 -20 0 -38 0 +23 0 +58 0 * Fa -0 1e-07 8 0 -2 7 +0 1e-07 7 0 + 0101000 +19 0 * Ed 1e-07 1 1 0 -1 22 0 0 50 -2 51 9 0 0 50 -2 52 10 0 0 50 -6 47 8 0 -6 48 9 0 +1 24 0 0 50 +2 47 8 0 0 50 +2 48 9 0 0 50 0 0101000 @@ -704,18 +510,15 @@ Wi 0101100 -56 0 -17 0 +36 0 +20 0 * Fa -0 1e-07 9 0 -2 8 +0 1e-07 8 0 + 0101000 +16 0 * Ed 1e-07 1 1 0 -1 23 0 0 50 -2 53 14 0 0 50 -2 54 10 0 0 50 -2 55 6 0 0 50 -6 49 9 0 -6 50 10 0 +1 25 0 0 50 +2 49 9 0 0 50 +2 50 10 0 0 50 0 0101000 @@ -725,8 +528,8 @@ Wi 0101100 -17 0 -34 0 +14 0 +54 0 * Fa -0 1e-07 10 0 -2 9 +0 1e-07 9 0 + 0101000 +13 0 * Wi @@ -734,8 +537,8 @@ Wi 0101100 -52 0 -45 0 +33 0 +14 0 * Fa -0 1e-07 6 0 -2 10 +0 1e-07 10 0 + 0101000 +11 0 * Ve @@ -747,20 +550,17 @@ Ve * Ed 1e-07 1 1 0 -1 24 0 0 6.28318530717959 -2 56 12 0 0 6.28318530717959 -2 57 15 0 0 6.28318530717959 -6 51 11 0 -6 52 12 0 +1 26 0 0 6.28318530717959 +2 51 11 0 0 6.28318530717959 +2 52 12 0 0 6.28318530717959 0 0101000 +9 0 -9 0 * Ed 1e-07 1 1 0 -1 25 0 0 50 -3 58 59CN 12 0 0 50 -7 53 54 11 0 +1 27 0 0 50 +3 53 54CN 11 0 0 50 0 0101000 @@ -770,8 +570,8 @@ Wi 0101100 -8 0 +7 0 +26 0 -7 0 * Fa -0 1e-07 12 0 -2 11 +0 1e-07 11 0 + 0101000 +6 0 * Wi @@ -779,8 +579,8 @@ Wi 0101100 +8 0 * Fa -0 1e-07 15 0 -2 12 +0 1e-07 12 0 + 0101000 +4 0 * Sh @@ -790,7 +590,9 @@ Sh +5 0 +3 0 * So -0100000 +1100000 +2 0 * -+1 0 \ No newline at end of file ++1 0 +0 + From 2a2216e5d2f6ea2791f1f2353bc863cf715f9c36 Mon Sep 17 00:00:00 2001 From: jfa Date: Fri, 19 Jul 2024 15:19:47 +0100 Subject: [PATCH 05/15] [bos #42002] BodyFitting refactoring: NRTJ4 test fails after moving a part of code into a separate method --- .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 230 +++++++++--------- .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 9 + 2 files changed, 130 insertions(+), 109 deletions(-) diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index 8a2b10300..a7ea4cb1b 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -1091,6 +1091,113 @@ Hexahedron::TFaceOfLink Hexahedron::findStartLink(const vector<_OrientedLink*>& return faceOfLink; } +//================================================================================ +/*! + * \brief find coplanar polygon, if any, and set it to be re-created next + */ +size_t Hexahedron::findCoplanarPolygon + (const _Face& thePolygon, + const size_t nbQuadPolygons, + std::vector< _OrientedLink* >& freeLinks, + int& nbFreeLinks, + const E_IntersectPoint ipTmp, + std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs, + std::map< StdMeshers::Cartesian3D::TGeomID, std::vector< const B_IntersectPoint* > >& tmpAddedFace, + const StdMeshers::Cartesian3D::TGeomID& curFace) +{ + size_t iPolygon = _polygons.size() - 1; + // check that a polygon does not lie on a hexa side + _Face* coplanarPolyg = 0; + for ( size_t iL = 0; iL < thePolygon._links.size() && !coplanarPolyg; ++iL ) { + if ( thePolygon._links[ iL ].NbFaces() >= 2 ) { // if it's not 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 = thePolygon._links[ iL ]._link->_faces[0]; + for ( iL2 = iL + 1; iL2 < thePolygon._links.size(); ++iL2 ) { + if ( thePolygon._links[ iL2 ]._link->_faces[0] == coplanarPolyg && + !coplanarPolyg->IsPolyLink( thePolygon._links[ iL ]) && + !coplanarPolyg->IsPolyLink( thePolygon._links[ iL2 ]) && + coplanarPolyg < & _polygons[ nbQuadPolygons ]) + break; + } + if ( iL2 == thePolygon._links.size() ) + coplanarPolyg = 0; + } + } + + if ( coplanarPolyg ) { // coplanar polygon found + freeLinks.resize( freeLinks.size() - thePolygon._polyLinks.size() ); + nbFreeLinks -= thePolygon._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 < thePolygon._links.size(); ++iL ) { + if ( thePolygon._links[ iL ]._link->_faces[1] && + thePolygon._links[ iL ]._link->_faces[0] != coplanarPolyg ) { + _Face* p = thePolygon._links[ iL ]._link->_faces[0]; + for ( size_t iL2 = 0; iL2 < p->_links.size(); ++iL2 ) { + if ( p->_links[ iL2 ]._link == thePolygon._links[ iL ]._link ) { + freeLinks.push_back( & p->_links[ iL2 ] ); + ++nbFreeLinks; + freeLinks.back()->RemoveFace( &thePolygon ); + break; + } + } + } + } + for ( size_t iL = 0; iL < coplanarPolyg->_links.size(); ++iL ) { + if ( coplanarPolyg->_links[ iL ]._link->_faces[1] && + coplanarPolyg->_links[ iL ]._link->_faces[1] != &thePolygon ) { + _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 ); + } // if ( coplanarPolyg ) + else { + iPolygon = _polygons.size(); + } + return iPolygon; +} + //================================================================================ /*! * \brief Compute mesh volumes resulted from intersection of the Hexahedron @@ -1321,112 +1428,13 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) 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 - _Face* 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(); - + if ( /*hasEdgeIntersections &&*/ iPolygon == _polygons.size() - 1 ) { + iPolygon = findCoplanarPolygon(polygon, nbQuadPolygons, freeLinks, nbFreeLinks, + ipTmp, usedFaceIDs, tmpAddedFace, curFace); + } + else { + iPolygon = _polygons.size(); + } } // end of case ( polygon._links.size() > 2 ) } // while ( nbFreeLinks > 0 ) @@ -1531,12 +1539,14 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) return !_volumeDefs._nodes.empty(); } +#ifdef WITH_TBB template void computeHexa(Type& hex) { - if ( hex ) + if ( hex ) hex->computeElements(); } +#endif //================================================================================ /*! @@ -3684,9 +3694,11 @@ void Hexahedron::getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryE 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() )) + for ( size_t iL = 0, iN = nbLinks - 1; iL < nbLinks; ++iL, --iN ) { + polygon.myNodes[iN] = _polygons[ iF ]._links[ iL ].FirstNode()->Node(); + if ( ! polygon.myNodes[iN] ) break; + } if ( !polygon.myNodes.back() ) continue; diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx index ec1e1d0fe..75d69ce0c 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -455,6 +455,15 @@ namespace Cartesian3D typedef std::pair< StdMeshers::Cartesian3D::TGeomID, int > TFaceOfLink; // (face, link) static TFaceOfLink findStartLink(const std::vector< _OrientedLink* >& freeLinks, std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs); + size_t findCoplanarPolygon + (const _Face& thePolygon, + const size_t nbQuadPolygons, + std::vector< _OrientedLink* >& freeLinks, + int& nbFreeLinks, + const E_IntersectPoint ipTmp, + std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs, + std::map< StdMeshers::Cartesian3D::TGeomID, std::vector< const B_IntersectPoint* > >& tmpAddedFace, + const StdMeshers::Cartesian3D::TGeomID& curFace); int addVolumes( SMESH_MesherHelper& helper ); void addFaces( SMESH_MesherHelper& helper, const std::vector< const SMDS_MeshElement* > & boundaryVolumes ); From 02ad02e21151da4e05a887ce0938ddfd24b9900a Mon Sep 17 00:00:00 2001 From: jfa Date: Fri, 26 Jul 2024 13:46:11 +0100 Subject: [PATCH 06/15] Some fixes --- .../HexahedronCanonicalShapesTest.cxx | 32 ++++----- src/StdMeshers.test/HexahedronTest.cxx | 36 ++++++---- .../StdMeshers_CartesianParameters3D.cxx | 8 ++- .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 67 +++++++++---------- .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 12 ++-- 5 files changed, 84 insertions(+), 71 deletions(-) diff --git a/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx b/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx index 3f4b16f56..8f770e980 100644 --- a/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx +++ b/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx @@ -19,7 +19,7 @@ // File : HexahedronCanonicalShapesTest.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. +// 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" @@ -28,7 +28,7 @@ // CPP TEST #include -// OCC +// OCC #include #include #include @@ -62,10 +62,10 @@ struct SMESH_Mesh_Test: public SMESH_Mesh */ struct CartesianHypo: public StdMeshers_CartesianParameters3D { - CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/) + CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/) { } -}; +}; /*! * \brief Initialize the grid and intesersectors of grid with the geometry @@ -73,7 +73,7 @@ struct CartesianHypo: public StdMeshers_CartesianParameters3D void GridInitAndIntersectWithShape (Grid& grid, double gridSpacing, double theSizeThreshold, - const TopoDS_Shape theShape, + const TopoDS_Shape theShape, TEdge2faceIDsMap& edge2faceIDsMap, const int theNumOfThreads) { @@ -82,7 +82,7 @@ void GridInitAndIntersectWithShape (Grid& grid, std::vector grdSpace = { std::to_string(gridSpacing) }; std::vector intPnts; - std::unique_ptr aHypo ( new CartesianHypo() ); + std::unique_ptr aHypo ( new CartesianHypo() ); aHypo->SetGridSpacing(grdSpace, intPnts, 0 ); // Spacing in dir 0 aHypo->SetGridSpacing(grdSpace, intPnts, 1 ); // Spacing in dir 1 aHypo->SetGridSpacing(grdSpace, intPnts, 2 ); // Spacing in dir 2 @@ -119,6 +119,9 @@ bool testShape (const TopoDS_Shape theShape, GridInitAndIntersectWithShape( grid, theGridSpacing, theSizeThreshold, theShape, edge2faceIDsMap, 1 ); + SMESH_subMesh * aSubMesh = aMesh->GetSubMesh(theShape); + aSubMesh->DependsOn(); // init sub-meshes + Hexahedron hex( &grid ); int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, 1 ); if (nbAdded != theNbCreatedExpected) { @@ -144,7 +147,7 @@ bool testPrimitives() // Test fitting of a box BRepPrimAPI_MakeBox aMakeBox (10, 20, 30); aMakeBox.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the box!", aMakeBox.IsDone() ); + CPPUNIT_ASSERT_MESSAGE( "Could not create the box!", aMakeBox.IsDone() ); TopoDS_Shape aShape = aMakeBox.Shape(); // Test exact fitting of a box @@ -155,10 +158,9 @@ bool testPrimitives() /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) isOK = false; - // TODO: debug this case - //if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/true, - // /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/8)) - // isOK = false; + if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/true, + /*gridSpacing*/10, /*theSizeThreshold*/4, /*theNbCreatedExpected*/6)) + isOK = false; if (!testShape (aShape, /*toAddEdges*/false, /*toCreateFaces*/false, /*gridSpacing*/5, /*theSizeThreshold*/4, /*theNbCreatedExpected*/48)) @@ -173,7 +175,7 @@ bool testPrimitives() gp_Ax2 anAxes (gp::Origin(), gp::DZ()); BRepPrimAPI_MakeCylinder aMakeCyl (anAxes, 20., 30.); aMakeCyl.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the cylinder!", aMakeCyl.IsDone() ); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cylinder!", aMakeCyl.IsDone() ); aShape = aMakeCyl.Shape(); // test for different threshold values @@ -187,7 +189,7 @@ bool testPrimitives() // Test fitting of a sphere BRepPrimAPI_MakeSphere aMakeSph (anAxes, 30.); aMakeSph.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the sphere!", aMakeSph.IsDone() ); + CPPUNIT_ASSERT_MESSAGE( "Could not create the sphere!", aMakeSph.IsDone() ); aShape = aMakeSph.Shape(); // test for different threshold values @@ -201,7 +203,7 @@ bool testPrimitives() // Test fitting of a cone BRepPrimAPI_MakeCone aMakeCon (anAxes, 30., 0., 40.); aMakeCon.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); aShape = aMakeCon.Shape(); // test for different threshold values @@ -218,7 +220,7 @@ bool testPrimitives() // truncated cone aMakeCon = BRepPrimAPI_MakeCone(anAxes, 30., 15., 20.); aMakeCon.Build(); - CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); + CPPUNIT_ASSERT_MESSAGE( "Could not create the cone!", aMakeCon.IsDone() ); aShape = aMakeCon.Shape(); // test for different threshold values diff --git a/src/StdMeshers.test/HexahedronTest.cxx b/src/StdMeshers.test/HexahedronTest.cxx index dd78c796e..8931e1a08 100644 --- a/src/StdMeshers.test/HexahedronTest.cxx +++ b/src/StdMeshers.test/HexahedronTest.cxx @@ -19,7 +19,7 @@ // 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. +// 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" @@ -28,7 +28,7 @@ // CPP TEST #include -// OCC +// OCC #include #include #include @@ -62,10 +62,10 @@ struct SMESH_Mesh_Test: public SMESH_Mesh */ struct CartesianHypo: public StdMeshers_CartesianParameters3D { - CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/) + CartesianHypo() : StdMeshers_CartesianParameters3D(0/*zero hypoId*/, nullptr/*NULL generator*/) { } -}; +}; /*! * \brief Shape loader @@ -73,7 +73,7 @@ struct CartesianHypo: public StdMeshers_CartesianParameters3D void loadBrepShape( std::string shapeName, TopoDS_Shape & shape ) { BRep_Builder b; - BRepTools::Read(shape, shapeName.c_str(), b); + BRepTools::Read(shape, shapeName.c_str(), b); } /*! @@ -82,7 +82,7 @@ void loadBrepShape( std::string shapeName, TopoDS_Shape & shape ) void GridInitAndIntersectWithShape (Grid& grid, double gridSpacing, double theSizeThreshold, - const TopoDS_Shape theShape, + const TopoDS_Shape theShape, TEdge2faceIDsMap& edge2faceIDsMap, const int theNumOfThreads) { @@ -91,7 +91,7 @@ void GridInitAndIntersectWithShape (Grid& grid, std::vector grdSpace = { std::to_string(gridSpacing) }; std::vector intPnts; - std::unique_ptr aHypo ( new CartesianHypo() ); + std::unique_ptr aHypo ( new CartesianHypo() ); aHypo->SetAxisDirs(axisDirs); aHypo->SetGridSpacing(grdSpace, intPnts, 0 ); // Spacing in dir 0 aHypo->SetGridSpacing(grdSpace, intPnts, 1 ); // Spacing in dir 1 @@ -108,7 +108,7 @@ bool testNRTM1() { TopoDS_Shape aShape; loadBrepShape( "data/HexahedronTest/NRTM1.brep", aShape ); - CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() ); + CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() ); const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency(); std::vector numberOfThreads(numOfCores); @@ -130,15 +130,18 @@ bool testNRTM1() grid._toUseThresholdForInternalFaces = false; grid._toUseQuanta = false; grid._sizeThreshold = 4.0; - + TEdge2faceIDsMap edge2faceIDsMap; GridInitAndIntersectWithShape( grid, 1.0, 4.0, aShape, edge2faceIDsMap, nThreads ); + SMESH_subMesh * aSubMesh = aMesh->GetSubMesh(aShape); + aSubMesh->DependsOn(); // init sub-meshes + Hexahedron hex( &grid ); int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads ); CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 1024 ); } - } + } return true; } @@ -149,7 +152,7 @@ bool testNRTJ4() { TopoDS_Shape aShape; loadBrepShape( "data/HexahedronTest/NRTMJ4.brep", aShape ); - CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() ); + CPPUNIT_ASSERT_MESSAGE( "Could not load the brep shape!", !aShape.IsNull() ); const auto numOfCores = std::thread::hardware_concurrency() == 0 ? 1 : std::thread::hardware_concurrency()/2; std::vector numberOfThreads(numOfCores); @@ -173,14 +176,19 @@ bool testNRTJ4() double testThreshold = 1.000001; grid._toCreateFaces = true; grid._sizeThreshold = testThreshold; - + TEdge2faceIDsMap edge2faceIDsMap; - GridInitAndIntersectWithShape( grid, 2.0, testThreshold, aShape, edge2faceIDsMap, nThreads ); + GridInitAndIntersectWithShape( grid, 2.0, testThreshold, + aShape, edge2faceIDsMap, nThreads ); + + SMESH_subMesh * aSubMesh = aMesh->GetSubMesh(aShape); + aSubMesh->DependsOn(); // init sub-meshes + Hexahedron hex( &grid ); int nbAdded = hex.MakeElements( helper, edge2faceIDsMap, nThreads ); CPPUNIT_ASSERT_MESSAGE( "Number of computed elements does not match", nbAdded == 35150 ); } - } + } return true; } diff --git a/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx b/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx index 66256788b..bcc37a070 100644 --- a/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx +++ b/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx @@ -361,9 +361,13 @@ void StdMeshers_CartesianParameters3D::ComputeCoordinates(const double x0, ++iCell; } } - const double lastCellLen = coords.back() - coords[ coords.size() - 2 ]; - if ( fabs( coords.back() - p1 ) > 0.5 * lastCellLen ) + if (coords.size() < 2) coords.push_back ( p1 ); + else { + const double lastCellLen = coords.back() - coords[ coords.size() - 2 ]; + if ( fabs( coords.back() - p1 ) > 0.5 * lastCellLen ) + coords.push_back ( p1 ); + } } // correct coords if a forced point is too close to a neighbor node diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index a7ea4cb1b..677dd6761 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -277,7 +277,7 @@ Hexahedron::Hexahedron(Grid* grid) { _polygons.reserve(100); // to avoid reallocation; - //set nodes shift within grid->_nodes from the node 000 + //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(); @@ -365,7 +365,7 @@ Hexahedron::Hexahedron( const Hexahedron& other, size_t i, size_t j, size_t k, i tgtLink._link = _hexLinks + ( srcLink._link - other._hexLinks ); } } - + if (SALOME::VerbosityActivated()) _cellID = cellID; } @@ -572,13 +572,13 @@ void Hexahedron::init( size_t i, size_t j, size_t k, const Solid* solid ) { _hexNodes[iN]._isInternalFlags = 0; - // Grid node + // 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] ] ) + 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; @@ -1100,7 +1100,7 @@ size_t Hexahedron::findCoplanarPolygon const size_t nbQuadPolygons, std::vector< _OrientedLink* >& freeLinks, int& nbFreeLinks, - const E_IntersectPoint ipTmp, + const E_IntersectPoint& ipTmp, std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs, std::map< StdMeshers::Cartesian3D::TGeomID, std::vector< const B_IntersectPoint* > >& tmpAddedFace, const StdMeshers::Cartesian3D::TGeomID& curFace) @@ -1472,7 +1472,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) 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 ]; @@ -1485,7 +1485,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) { gp_XYZ p = SMESH_NodeXYZ( n->Node() ); _grid->ComputeUVW( p, uvw.ChangeCoord().ChangeData() ); - bb.Add( uvw ); + bb.Add( uvw ); } } gp_Pnt pMin = bb.CornerMin(); @@ -1498,7 +1498,7 @@ bool Hexahedron::compute( const Solid* solid, const IsInternalFlag intFlag ) } } - /* This call is irrelevant here because _volumeDefs datas were not filled! + /* This call is irrelevant here because _volumeDefs datas were not filled! or .... it is potentialy filled by other thread?? */ _volumeDefs._nodes.clear(); _volumeDefs._quantities.clear(); @@ -1553,8 +1553,8 @@ void computeHexa(Type& hex) * \brief Create elements in the mesh */ int Hexahedron::MakeElements(SMESH_MesherHelper& helper, - const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, - const int numOfThreads ) + const map< TGeomID, vector< TGeomID > >& edge2faceIDsMap, + const int numOfThreads ) { SMESHDS_Mesh* mesh = helper.GetMeshDS(); @@ -1604,7 +1604,7 @@ int Hexahedron::MakeElements(SMESH_MesherHelper& helper, 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 ] @@ -1660,10 +1660,10 @@ int Hexahedron::MakeElements(SMESH_MesherHelper& helper, intHexa.push_back( hex ); } } - + // compute definitions of volumes resulted from hexadron intersection #ifdef WITH_TBB - parallel_for(intHexa.begin(), intHexa.end(), computeHexa, numOfThreads ); + parallel_for(intHexa.begin(), intHexa.end(), computeHexa, numOfThreads ); #else for ( size_t i = 0; i < intHexa.size(); ++i ) if ( Hexahedron * hex = intHexa[ i ] ) @@ -1686,7 +1686,7 @@ int Hexahedron::MakeElements(SMESH_MesherHelper& helper, 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 ) { @@ -2586,7 +2586,7 @@ bool Hexahedron::isOutPoint( _Link& link, int iP, 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 ) + if ( proj.IsDone() && proj.NbPoints() > 0 ) { Standard_Real u,v; proj.LowerDistanceParameters( u,v ); @@ -2807,9 +2807,9 @@ int Hexahedron::addVolumes( SMESH_MesherHelper& helper ) } } // loop to get nodes - const SMDS_MeshElement* v = 0; + const SMDS_MeshElement* v = 0; if ( !volDef->_quantities.empty() ) - { + { if ( !useQuanta ) { // split polyhedrons of with disjoint volumes @@ -2834,13 +2834,12 @@ int Hexahedron::addVolumes( SMESH_MesherHelper& helper ) { const double quanta = _grid->_quanta; double polyVol = volDef->_size; - double hexaVolume = _sideLength[0] * _sideLength[1] * _sideLength[2]; + 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 @@ -3062,9 +3061,9 @@ bool Hexahedron::checkPolyhedronSize( bool cutByInternalFace, double & volume) c * 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, +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() ) @@ -3084,7 +3083,7 @@ int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector_nodes[ n ].Node()->GetID() ); splitNodes.back().push_back( volDef->_nodes[ n ].Node() ); } - + numberOfSets.insert( std::pair(mySet,1) ); while ( connectedFaces != allFaces.size() ) { @@ -3099,7 +3098,7 @@ int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector_nodes[ accum + n ].Node()->GetID(); - if ( elementSet.count( nodeId ) != 0 ) + if ( elementSet.count( nodeId ) != 0 ) faceCounter++; } if ( faceCounter >= 2 ) // found coincidences nodes @@ -3158,17 +3157,17 @@ int Hexahedron::checkPolyhedronValidity( _volumeDef* volDef, std::vector& nodes, +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 ); @@ -3471,10 +3470,10 @@ void Hexahedron::addFaces( SMESH_MesherHelper& helper, SMESH_MeshEditor::ElemFeatures face( SMDSAbs_Face ); SMESHDS_Mesh* meshDS = helper.GetMeshDS(); - bool isQuantaSet = _grid->_toUseQuanta; + bool isQuantaSet = _grid->_toUseQuanta; // check if there are internal or shared FACEs bool hasInternal = ( !_grid->_geometry.IsOneSolid() || - _grid->_geometry._soleSolid.HasInternalFaces() ); + _grid->_geometry._soleSolid.HasInternalFaces() ); for ( size_t iV = 0; iV < boundaryVolumes.size(); ++iV ) { @@ -3727,7 +3726,7 @@ void Hexahedron::getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryE 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 } @@ -3761,7 +3760,7 @@ void Hexahedron::getBoundaryElems( vector< const SMDS_MeshElement* > & boundaryE { _bro->setIsMarked( true ); boundaryElems.push_back( _bro ); - } + } } } } @@ -3778,7 +3777,7 @@ 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 }; diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx index 75d69ce0c..5676ac856 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -24,7 +24,7 @@ #ifndef _SMESH_Cartesian_3D_HEXAHEDRON_HXX_ #define _SMESH_Cartesian_3D_HEXAHEDRON_HXX_ -// BOOST +// BOOST #include // STD @@ -107,7 +107,7 @@ namespace Cartesian3D char _isInternalFlags; _Node(const SMDS_MeshNode* n=0, const StdMeshers::Cartesian3D::B_IntersectPoint* ip=0) - :_node(n), _intPoint(ip), _usedInFace(0), _isInternalFlags(0) {} + :_node(n), _boundaryCornerNode(0), _intPoint(ip), _usedInFace(0), _isInternalFlags(0) {} const SMDS_MeshNode* Node() const { return ( _intPoint && _intPoint->_node ) ? _intPoint->_node : _node; } const SMDS_MeshNode* BoundaryNode() const @@ -166,7 +166,7 @@ namespace Cartesian3D std::vector< const StdMeshers::Cartesian3D::F_IntersectPoint* > _fIntPoints; // GridLine intersections with FACEs std::vector< _Node* > _fIntNodes; // _Node's at _fIntPoints std::vector< _Link > _splits; - _Link(): _faces{ 0, 0 } {} + _Link(): _nodes{ 0, 0 }, _faces{ 0, 0 } {} }; // -------------------------------------------------------------------------------- @@ -337,7 +337,7 @@ namespace Cartesian3D StdMeshers::Cartesian3D::TGeomID _solidID; double _size; const SMDS_MeshElement* _volume; // new volume - std::vector _brotherVolume; // produced due to poly split + std::vector _brotherVolume; // produced due to poly split std::vector< SMESH_Block::TShapeID > _names; // name of side a polygon originates from _volumeDef(): _next(0), _solidID(0), _size(0), _volume(0) {} @@ -460,7 +460,7 @@ namespace Cartesian3D const size_t nbQuadPolygons, std::vector< _OrientedLink* >& freeLinks, int& nbFreeLinks, - const E_IntersectPoint ipTmp, + const E_IntersectPoint& ipTmp, std::set< StdMeshers::Cartesian3D::TGeomID >& usedFaceIDs, std::map< StdMeshers::Cartesian3D::TGeomID, std::vector< const B_IntersectPoint* > >& tmpAddedFace, const StdMeshers::Cartesian3D::TGeomID& curFace); @@ -485,7 +485,7 @@ namespace Cartesian3D bool isInHole() const; bool hasStrangeEdge() const; bool checkPolyhedronSize( bool isCutByInternalFace, double & volSize ) const; - int checkPolyhedronValidity( _volumeDef* volDef, std::vector>& splitQuantities, + int checkPolyhedronValidity( _volumeDef* volDef, std::vector>& splitQuantities, std::vector>& splitNodes ); const SMDS_MeshElement* addPolyhedronToMesh( _volumeDef* volDef, SMESH_MesherHelper& helper, From 05136f0f59f2a73c1979c85b3f3d85e04996c66c Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Mon, 5 Aug 2024 12:57:49 +0100 Subject: [PATCH 07/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Fixed endless loop of creating threads and crash for an edge case when num of threads greater than num of hexaedrons to compute. --- .../StdMeshers_Cartesian_3D_Grid.hxx | 69 +++++++++++++++---- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx index 3cf439aa3..8ac347c52 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx @@ -517,23 +517,66 @@ namespace Cartesian3D // 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) + void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const unsigned int nthreads = 1) { - const unsigned int group = ((last-first))/std::abs(nthreads); + MESSAGE("Start parallel computation of Hexa with c++ threads..."); - 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);}; + assert(nthreads > 0); - // stack the threads - threads.push_back( std::thread( lambda ) ); + const unsigned int numTasksTotal = last - first; + std::vector threads; + Iterator it = first; + + MESSAGE("Number of elements to compute: " << numTasksTotal << "; num of threads: " << nthreads); + + // Distribute tasks among threads + if (numTasksTotal <= nthreads) + { + // A simple case - just one task executed in one thread. + // TODO: check if it's faster to do it sequentially + threads.reserve(numTasksTotal); + for (; it < last; ++it) + { + threads.emplace_back(f, std::ref(*it)); } - 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();}); + } + else + { + // Calculate how to distribute elements among threads evenly + const unsigned int numTasksInThread = numTasksTotal / nthreads; + MESSAGE("Number of tasks in thread: " << numTasksInThread); + + // Store the numbers of tasks per thread + std::vector distTasksInThreads(nthreads, numTasksInThread); + + // Distribute a remainder among saved numbers + const unsigned int remainder = numTasksTotal % nthreads; + MESSAGE("Remainder of tasks " << remainder << " will be evenly distributed among threads"); + for (unsigned int i = 0; i < remainder; ++i) + { + ++distTasksInThreads[i]; + } + + // Create threads for each number of tasks + threads.reserve(nthreads); + for (const auto i : distTasksInThreads) + { + Iterator curLast = it + i; + + // Pass iterators by value and the function by reference! + auto lambda = [=,&f](){ std::for_each(it, curLast, f); }; + + // Create a thread + threads.emplace_back(lambda); + + // Advance iterator to the next step + it = curLast; + } + } + + std::for_each(threads.begin(), threads.end(), [](std::thread& x){ x.join(); }); + + MESSAGE("Parallel computation was finished successfully"); } // -------------------------------------------------------------------------- From 312549ec6417686ba197fad16e93da6fa0a1775d Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Mon, 5 Aug 2024 13:44:07 +0100 Subject: [PATCH 08/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Nodes linked to a null nodes without any possible splits now being cleared befor splits initialization. This prevents building non-planar faces in cases when the source geometry surface coincident with an intermediate grid plane. --- .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 122 ++++++++++++++++++ .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 20 ++- 2 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index 677dd6761..66409b01a 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -393,6 +393,28 @@ void Hexahedron::_Node::Add( const E_IntersectPoint* ip ) _node = _intPoint->_node = node; } +void Hexahedron::_Node::clear() +{ + _node = nullptr; + _boundaryCornerNode = nullptr; + _intPoint = nullptr; + _usedInFace = nullptr; + _isInternalFlags = IS_NOT_INTERNAL; +} + +void Hexahedron::_Link::clear() +{ + for (std::size_t i = 0; i < nodesNum; ++i) + { + if (_nodes[i]) + _nodes[i]->clear(); + } + + _fIntPoints.clear(); + _fIntNodes.clear(); + _splits.clear(); +} + //================================================================================ /*! * \brief Return IDs of SOLIDs interfering with this Hexahedron @@ -606,6 +628,8 @@ void Hexahedron::init( size_t i, size_t j, size_t k, const Solid* solid ) // this method can be called in parallel, so use own helper SMESH_MesherHelper helper( *_grid->_helper->GetMesh() ); + clearNodesLinkedToNull(solid, helper); + // Create sub-links (_Link::_splits) by splitting links with _Link::_fIntPoints // --------------------------------------------------------------- _Link split; @@ -798,6 +822,104 @@ void Hexahedron::init( size_t i, size_t j, size_t k, const Solid* solid ) } // init( _i, _j, _k ) +//================================================================================ +/*! + * \brief Clear nodes linked to null by not splitted links. + * Exclude from computation all nodes those linked to null without any splits in links. + * This prevents building non-planar faces in cases when the source geometry surface + * coincident with an intermediate grid plane. + */ +void Hexahedron::clearNodesLinkedToNull(const Solid* solid, SMESH_MesherHelper& helper) +{ + for (std::size_t i = 0; i < HEX_LINKS_NUM; ++i) + { + _Link& link = _hexLinks[i]; + + if (isSplittedLink(solid, helper, link)) + continue; + + // Links where both nodes are valid should have itself as a split. + // Check if we have at least one null node here for a chance if we have + // some unexpected edge case. + _Node* n1 = link._nodes[0]; + _Node* n2 = link._nodes[1]; + + if (!n1->_node || !n2->_node) + { + link.clear(); + } + } +} + +//================================================================================ +/*! + * \brief Returns true if the link will be splitted on the Hexaedron initialization + */ +bool Hexahedron::isSplittedLink(const Solid* solid, SMESH_MesherHelper& helper, const Hexahedron::_Link& linkIn) const +{ + _Link split; + + _Link link = linkIn; + link._fIntNodes.clear(); + link._fIntNodes.reserve( link._fIntPoints.size() ); + + std::vector<_Node> intNodes; + intNodes.reserve(link._fIntPoints.size()); + + for ( size_t i = 0; i < link._fIntPoints.size(); ++i ) + if ( solid->ContainsAny( link._fIntPoints[i]->_faceIDs )) + { + intNodes.emplace_back(nullptr, 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 = false; + 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 ) + { + return true; + } + 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 ) + { + return true; + } + + return false; +} + //================================================================================ /*! * \brief Compute mesh volumes resulted from intersection of the Hexahedron diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx index 5676ac856..ade329d8d 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -156,17 +156,22 @@ namespace Cartesian3D } void Add( const StdMeshers::Cartesian3D::E_IntersectPoint* ip ); + void clear(); }; // -------------------------------------------------------------------------------- struct _Link // link connecting two _Node's { - _Node* _nodes[2]; - _Face* _faces[2]; // polygons sharing a link + static const std::size_t nodesNum = 2; + + _Node* _nodes[nodesNum]; + _Face* _faces[nodesNum]; // polygons sharing a link std::vector< const StdMeshers::Cartesian3D::F_IntersectPoint* > _fIntPoints; // GridLine intersections with FACEs std::vector< _Node* > _fIntNodes; // _Node's at _fIntPoints std::vector< _Link > _splits; _Link(): _nodes{ 0, 0 }, _faces{ 0, 0 } {} + + void clear(); }; // -------------------------------------------------------------------------------- @@ -388,9 +393,12 @@ namespace Cartesian3D }; // topology of a hexahedron - _Node _hexNodes [8]; - _Link _hexLinks [12]; - _Face _hexQuads [6]; + static const std::size_t HEX_NODES_NUM = 8; + static const std::size_t HEX_LINKS_NUM = 12; + static const std::size_t HEX_QUADS_NUM = 6; + _Node _hexNodes [HEX_NODES_NUM]; + _Link _hexLinks [HEX_LINKS_NUM]; + _Face _hexQuads [HEX_QUADS_NUM]; // faces resulted from hexahedron intersection std::vector< _Face > _polygons; @@ -426,6 +434,8 @@ namespace Cartesian3D 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 clearNodesLinkedToNull(const Solid* solid, SMESH_MesherHelper& helper); + bool isSplittedLink(const Solid* solid, SMESH_MesherHelper& helper, const Hexahedron::_Link& linkIn) const; void setIJK( size_t i ); /*Auxiliary methods to extract operations from monolitic compute method*/ void defineHexahedralFaces( const Solid* solid, const IsInternalFlag intFlag ); From dda7e974696462f366b60e5bd45e4215d25afdb5 Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Tue, 6 Aug 2024 12:24:46 +0100 Subject: [PATCH 09/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Added operators << for nodes and links debug output. --- src/SMDS/SMDS_FaceOfNodes.hxx | 2 +- src/SMDS/SMDS_MeshElement.hxx | 2 +- src/SMDS/SMDS_MeshNode.hxx | 2 +- src/SMDS/SMDS_PolygonalFaceOfNodes.hxx | 2 +- src/SMDS/SMDS_VolumeOfNodes.hxx | 2 +- .../StdMeshers_Cartesian_3D_Grid.hxx | 3 + .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 75 +++++++++++++++++++ 7 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/SMDS/SMDS_FaceOfNodes.hxx b/src/SMDS/SMDS_FaceOfNodes.hxx index 1ccd14f6a..225e620ab 100644 --- a/src/SMDS/SMDS_FaceOfNodes.hxx +++ b/src/SMDS/SMDS_FaceOfNodes.hxx @@ -32,7 +32,7 @@ class SMDS_EXPORT SMDS_FaceOfNodes: public SMDS_CellOfNodes { public: - void Print(std::ostream & OS) const; + virtual void Print(std::ostream & OS) const override; SMDS_FaceOfNodes(const SMDS_MeshNode* node1, const SMDS_MeshNode* node2, const SMDS_MeshNode* node3); diff --git a/src/SMDS/SMDS_MeshElement.hxx b/src/SMDS/SMDS_MeshElement.hxx index eeec4a1f7..1d44ecf75 100644 --- a/src/SMDS/SMDS_MeshElement.hxx +++ b/src/SMDS/SMDS_MeshElement.hxx @@ -142,7 +142,7 @@ public: SMDS_Mesh* GetMesh() const; - void Print(std::ostream & OS) const; + virtual void Print(std::ostream & OS) const; friend SMDS_EXPORT std::ostream & operator <<(std::ostream & OS, const SMDS_MeshElement *); friend class SMDS_ElementFactory; diff --git a/src/SMDS/SMDS_MeshNode.hxx b/src/SMDS/SMDS_MeshNode.hxx index 99549e937..e2aa11270 100644 --- a/src/SMDS/SMDS_MeshNode.hxx +++ b/src/SMDS/SMDS_MeshNode.hxx @@ -65,7 +65,7 @@ class SMDS_EXPORT SMDS_MeshNode: public SMDS_MeshElement virtual bool IsMediumNode(const SMDS_MeshNode* /*node*/) const { return false; } virtual int NbCornerNodes() const { return 1; } - void Print(std::ostream & OS) const; + virtual void Print(std::ostream & OS) const override; private: diff --git a/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx b/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx index 7dbff1211..8b3d4fa5d 100644 --- a/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx +++ b/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx @@ -47,7 +47,7 @@ class SMDS_EXPORT SMDS_PolygonalFaceOfNodes : public SMDS_CellOfNodes virtual int NbEdges() const; virtual int NbFaces() const; - virtual void Print (std::ostream & OS) const; + virtual void Print (std::ostream & OS) const override; virtual const SMDS_MeshNode* GetNode(const int ind) const; diff --git a/src/SMDS/SMDS_VolumeOfNodes.hxx b/src/SMDS/SMDS_VolumeOfNodes.hxx index 36a515c5d..7098cd623 100644 --- a/src/SMDS/SMDS_VolumeOfNodes.hxx +++ b/src/SMDS/SMDS_VolumeOfNodes.hxx @@ -62,7 +62,7 @@ class SMDS_EXPORT SMDS_VolumeOfNodes: public SMDS_CellOfNodes const int nbNodes); ~SMDS_VolumeOfNodes(); - void Print(std::ostream & OS) const; + virtual void Print(std::ostream & OS) const override; int NbFaces() const; int NbNodes() const; int NbEdges() const; diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx index 8ac347c52..7177bbd37 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx @@ -539,6 +539,9 @@ namespace Cartesian3D { threads.emplace_back(f, std::ref(*it)); } + + // This line for debug in sequential mode + // std::for_each(it, last, f); } else { diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx index ade329d8d..dbaa7cf52 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -157,6 +157,24 @@ namespace Cartesian3D void Add( const StdMeshers::Cartesian3D::E_IntersectPoint* ip ); void clear(); + + friend std::ostream& operator<<(std::ostream& os, const _Node& node) + { + if (node._node) + { + os << "Node at hexahedron corner: "; + node._node->Print(os); + } + else if (node._intPoint && node._intPoint->_node) + { + os << "Node at intersection point: "; + node._intPoint->_node->Print(os); // intersection point + } + else + os << "mesh node is null\n"; + + return os; + } }; // -------------------------------------------------------------------------------- @@ -172,6 +190,25 @@ namespace Cartesian3D _Link(): _nodes{ 0, 0 }, _faces{ 0, 0 } {} void clear(); + + friend std::ostream& operator<<(std::ostream& os, const _Link& link) + { + os << "Link:\n"; + + for (std::size_t i = 0; i < nodesNum; ++i) + { + if (link._nodes[i]) + os << *link._nodes[i]; + else + os << "link node with index " << i << " is null\n"; + } + + os << "_fIntPoints: " << link._fIntPoints.size() << '\n'; + os << "_fIntNodes: " << link._fIntNodes.size() << '\n'; + os << "_splits: " << link._splits.size() << '\n'; + + return os; + } }; // -------------------------------------------------------------------------------- @@ -241,6 +278,16 @@ namespace Cartesian3D } } } + + friend std::ostream& operator<<(std::ostream& os, const _OrientedLink& link) + { + if (link._link) + os << "Oriented " << *link._link; + else + os << "Oriented link is null\n"; + + return os; + } }; // -------------------------------------------------------------------------------- @@ -314,6 +361,34 @@ namespace Cartesian3D _polyLinks.push_back( l ); _links.push_back( _OrientedLink( &_polyLinks.back() )); } + + friend std::ostream& operator<<(std::ostream& os, const _Face& face) + { + os << "Face " << face._name << '\n'; + + os << "Links on GridLines: \n"; + for (const auto& link : face._links) + { + os << link; + } + + os << "Links added to close a polygonal face: \n"; + for (const auto& link : face._polyLinks) + { + os << link; + } + + os << "Nodes at intersection with EDGEs: \n"; + for (const auto node : face._eIntNodes) + { + if (node) + { + os << *node; + } + } + + return os; + } }; // -------------------------------------------------------------------------------- From a8ab49d0b35b94d81513cd41029a610512d34405 Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Mon, 12 Aug 2024 12:42:04 +0100 Subject: [PATCH 10/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Added vtkTriangleFilter to prevent concave polygons render artifacts. --- src/OBJECT/SMESH_DeviceActor.cxx | 6 +++++- src/OBJECT/SMESH_DeviceActor.h | 2 ++ src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx | 17 +++++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/OBJECT/SMESH_DeviceActor.cxx b/src/OBJECT/SMESH_DeviceActor.cxx index be17fe3c3..fafd40aef 100644 --- a/src/OBJECT/SMESH_DeviceActor.cxx +++ b/src/OBJECT/SMESH_DeviceActor.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,7 @@ SMESH_DeviceActor myMergeFilter = vtkMergeFilter::New(); myGeomFilter = VTKViewer_GeometryFilter::New(); + myTriangleFilter = vtkTriangleFilter::New(); myTransformFilter = VTKViewer_TransformFilter::New(); @@ -153,6 +155,7 @@ SMESH_DeviceActor myFaceOrientation->Delete(); myGeomFilter->Delete(); + myTriangleFilter->Delete(); myTransformFilter->Delete(); @@ -251,9 +254,10 @@ SMESH_DeviceActor anId++; // 3 myGeomFilter->SetInputConnection( myPassFilter[ anId ]->GetOutputPort() ); + myTriangleFilter->SetInputConnection(myGeomFilter->GetOutputPort()); anId++; // 4 - myPassFilter[ anId ]->SetInputConnection( myGeomFilter->GetOutputPort() ); + myPassFilter[ anId ]->SetInputConnection( myTriangleFilter->GetOutputPort() ); myPassFilter[ anId + 1 ]->SetInputConnection( myPassFilter[ anId ]->GetOutputPort() ); anId++; // 5 diff --git a/src/OBJECT/SMESH_DeviceActor.h b/src/OBJECT/SMESH_DeviceActor.h index c8f17d254..f9a131f70 100644 --- a/src/OBJECT/SMESH_DeviceActor.h +++ b/src/OBJECT/SMESH_DeviceActor.h @@ -46,6 +46,7 @@ class vtkLookupTable; class vtkImplicitBoolean; class vtkPassThrough; class vtkPlaneCollection; +class vtkTriangleFilter; class VTKViewer_Transform; class VTKViewer_TransformFilter; @@ -182,6 +183,7 @@ class SMESHOBJECT_EXPORT SMESH_DeviceActor: public vtkLODActor{ bool myStoreClippingMapping; VTKViewer_GeometryFilter *myGeomFilter; + vtkTriangleFilter* myTriangleFilter = nullptr; VTKViewer_TransformFilter *myTransformFilter; std::vector myPassFilter; diff --git a/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx b/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx index 7d38aa510..a1af7d572 100644 --- a/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx @@ -70,6 +70,8 @@ #include #include #include +#include +#include // Qt includes #include @@ -108,6 +110,8 @@ namespace SMESH SALOME_Actor* myFaceOrientation; vtkPolyDataMapper* myFaceOrientationDataMapper; SMESH_FaceOrientationFilter* myFaceOrientationFilter; + vtkSmartPointer myGeometryFilter = nullptr; + vtkSmartPointer myTriangleFilter = nullptr; public: TElementSimulation (SalomeApp_Application* theApplication) @@ -120,9 +124,15 @@ namespace SMESH myGrid = vtkUnstructuredGrid::New(); + myGeometryFilter = vtkSmartPointer::New(); + myGeometryFilter->SetInputData(myGrid); + + myTriangleFilter = vtkSmartPointer::New(); + myTriangleFilter->SetInputConnection(myGeometryFilter->GetOutputPort()); + // Create and display actor myMapper = vtkDataSetMapper::New(); - myMapper->SetInputData(myGrid); + myMapper->SetInputConnection(myTriangleFilter->GetOutputPort()); myPreviewActor = SALOME_Actor::New(); myPreviewActor->PickableOff(); @@ -148,7 +158,7 @@ namespace SMESH // Orientation of faces myFaceOrientationFilter = SMESH_FaceOrientationFilter::New(); - myFaceOrientationFilter->SetInputData(myGrid); + myFaceOrientationFilter->SetInputConnection(myTriangleFilter->GetOutputPort()); myFaceOrientationDataMapper = vtkPolyDataMapper::New(); myFaceOrientationDataMapper->SetInputConnection(myFaceOrientationFilter->GetOutputPort()); @@ -209,6 +219,9 @@ namespace SMESH myGrid->InsertNextCell(theType,anIds); anIds->Delete(); + myGeometryFilter->Update(); + myTriangleFilter->Update(); + myGrid->Modified(); SetVisibility(true, theActor->GetFacesOriented(), false); From dd7f711c079ac0b84cc1b02257c1c39c1387d89c Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Wed, 21 Aug 2024 17:48:29 +0100 Subject: [PATCH 11/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Added subdivision for overlapped polygons and removing of polygons with edges those are not connected with any other polygon in a volume. --- .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 339 ++++++++++++++++++ .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 39 ++ 2 files changed, 378 insertions(+) diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index 66409b01a..5a235b743 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -2934,6 +2934,21 @@ int Hexahedron::addVolumes( SMESH_MesherHelper& helper ) { if ( !useQuanta ) { + // Try to find and fix geometry issues + const bool isDivided = volDef->divideOverlappingPolygons(); + const bool isRemoved = volDef->removeOpenEdgesPolygons(); + + // Update nodes if the geometry was changed + if (isDivided || isRemoved) + { + nodes.clear(); + nodes.resize(volDef->_nodes.size()); + for (size_t i = 0; i < nodes.size(); ++i) + { + nodes[i] = volDef->_nodes[i].Node(); + } + } + // split polyhedrons of with disjoint volumes std::vector> splitQuantities; std::vector > splitNodes; @@ -3175,6 +3190,330 @@ bool Hexahedron::checkPolyhedronSize( bool cutByInternalFace, double & volume) c return volume > initVolume / _grid->_sizeThreshold; } +//================================================================================ +/*! + * \brief Returns an index of the first node of the given polygon in the _nodes container. + */ + int Hexahedron::_volumeDef::getStartNodeIndex(const int polygon) const + { + return std::accumulate(_quantities.begin(), _quantities.begin() + polygon, 0); + } + +//================================================================================ +/*! + * \brief Returns a vector of sets of edges like {{ nodeId1, nodeId2 }, { nodeId2, nodeId3 }} + * where an index of element in the vector is an index of polygon in the volume. + * Nodes ids in the pairs are sorted, so index of a first node is always less than second. + */ +std::vector>> Hexahedron::_volumeDef::getPolygonsEdges() const +{ + if (_quantities.empty()) + return {}; + + const int numOfPolygons = _quantities.size(); + std::vector>> polygonsEdges(numOfPolygons); + + auto storeEdge = [&](const int polygon, const int idx1, const int idx2) -> void + { + const int firstNode = _nodes[idx1].Node()->GetID(); + const int secondNode = _nodes[idx2].Node()->GetID(); + + polygonsEdges[polygon].emplace( + std::min(firstNode, secondNode), std::max(firstNode, secondNode)); + }; + + // Go over all polygons in this volume + int numOfNodesCounter = 0; + for (int i = 0; i < numOfPolygons; ++i) + { + const int numOfNodesInPolygon = _quantities[i]; + + // Iterate pairs of nodes for each edge + const int lastIndex = numOfNodesCounter + numOfNodesInPolygon - 1; + for (int j = numOfNodesCounter; j < lastIndex; ++j) + { + storeEdge(i, j, j + 1); + } + + // Store an edge connected last and first nodes to close the polygon + storeEdge(i, lastIndex, numOfNodesCounter); + + numOfNodesCounter += numOfNodesInPolygon; + } + + // Debug output + // MESSAGE("Collected edges for " << numOfPolygons << " polygons: "); + // for (size_t i = 0; i < polygonsEdges.size(); ++i) + // { + // MESSAGE("Polygon " << i << ": "); + // int j = 0; + // for (const auto& edge : polygonsEdges[i]) + // { + // MESSAGE("Edge " << j++ << ": " << edge.first << ", " << edge.second); + // } + // } + + return polygonsEdges; +} + +//================================================================================ +/*! + * \brief Finds in the volume a polygon with such a set of edges that is a subset + * of edges of other polygon. Returns a map where a key is a larger polygon, + * the value - is a smaller polygon that is a subset of larger, followed by + * two nodes those we need to connect in a process of cutting of the first polygon. + * If the second poly is a complete clone of the first one, both nodes will be -1. + */ +std::map> Hexahedron::_volumeDef::findOverlappingPolygons() const +{ + std::vector>> polygonsEdges = getPolygonsEdges(); + if (polygonsEdges.empty()) + return {}; + + auto isOverlappedSet = [](const std::set>& set1, + const std::set>& set2, + std::set>& diff) -> bool + { + diff.clear(); + + if (set1.size() > set2.size()) + std::set_difference(set2.begin(), set2.end(), set1.begin(), set1.end(), std::inserter(diff, diff.begin())); + else + std::set_difference(set1.begin(), set1.end(), set2.begin(), set2.end(), std::inserter(diff, diff.begin())); + + return diff.size() <= 1; + }; + + // The key is a larger polygon, the value - is a smaller polygon that is a subset of larger, + // followed by two nodes those we need to connect in a process of cutting of the first polygon. + std::map> overlappedPolygons; + + // Check all the polygons one by one + const int numOfPolygons = polygonsEdges.size(); + for (int i = 0; i < numOfPolygons - 1; ++i) + { + const auto& edges = polygonsEdges[i]; + + bool isOverlapped = false; + int largerPolygon = i; + int smallerPolygon = -1; + std::set> diff; + + for (int j = i + 1; j < numOfPolygons; ++j) + { + const auto& otherEdges = polygonsEdges[j]; + + if (isOverlappedSet(edges, otherEdges, diff)) + { + isOverlapped = true; + smallerPolygon = j; + + if (edges.size() < otherEdges.size()) + { + largerPolygon = j; + smallerPolygon = i; + } + + // At this moment don't expect more than two overlapped polygons. + // TODO: check if we need to consider the case of multiple polygons. + break; + } + } + + // Store results + if (isOverlapped) + { + int node1 = diff.empty() ? -1 : diff.begin()->first; + int node2 = diff.empty() ? -1 : diff.begin()->second; + + overlappedPolygons.emplace(largerPolygon, std::vector { smallerPolygon, node1, node2 }); + } + } + + return overlappedPolygons; +} + +//================================================================================ +/*! + * \brief Finds in the volume a polygon with such a set of edges that is a subset + * of edges of other polygon. Then we can remove these edges from a larger + * polygon and have as a result two connected polygons those are + * next to each other instead of being overlapped. + * We can't just delete the smaller polygon, because it could be a part of + * separated volume that can be revealed later in checkPolyhedronValidity(). + * Returns true if at least one polygon was changed. + */ +bool Hexahedron::_volumeDef::divideOverlappingPolygons() +{ + // The key is a larger polygon, the value - is a smaller polygon that is a subset of larger, + // followed by two nodes those we need to connect in a process of cutting of the first polygon. + const std::map> overlappedPolygons = findOverlappingPolygons(); + if (overlappedPolygons.empty()) + return false; + + // Here we have at least one pair of overlapped polygons. + // The smaller one should stay intact, but the larger need to be reduced by nodes + // of the smaller one except nodes for the close edge. + std::set nodesIndexesToErase; + std::map quantsUpdatedValues; + for (auto polygon = overlappedPolygons.rbegin(); polygon != overlappedPolygons.rend(); ++polygon) + { + const int largerPolygon = polygon->first; + const int smallerPolygon = polygon->second[0]; + const int edgeNode1 = polygon->second[1]; + const int startNodeIndex = getStartNodeIndex(largerPolygon); + + MESSAGE("Overlapped: " << largerPolygon << ", " << smallerPolygon << ". Edge node: " << edgeNode1 << ", " << polygon->second[2]); + + // We expect to have valid ids here, exclude the case when both polygons are the same (-1 node). + if (edgeNode1 != -1) + { + // Remove redundant nodes from the larger polygon + const int otherStartNodeIndex = getStartNodeIndex(smallerPolygon); + const int edgeNode2 = polygon->second[2]; + + quantsUpdatedValues[largerPolygon] = _quantities[largerPolygon]; + SCRUTE(quantsUpdatedValues[largerPolygon]); + + for (int i = startNodeIndex; i < startNodeIndex + _quantities[largerPolygon]; ++i) + { + const int thisNodeId = _nodes[i].Node()->GetID(); + MESSAGE("thisNodeId " << thisNodeId << " at index " << i); + + // Skip nodes from the close edge + if (thisNodeId == edgeNode1 || thisNodeId == edgeNode2) + continue; + + for (int j = otherStartNodeIndex; j < otherStartNodeIndex + _quantities[smallerPolygon]; ++j) + { + const int otherNodeId = _nodes[j].Node()->GetID(); + MESSAGE("otherNodeId " << otherNodeId << " at index " << j); + + if (thisNodeId == otherNodeId) + { + // Add to be erased later + nodesIndexesToErase.insert(i); + --quantsUpdatedValues[largerPolygon]; + MESSAGE("Set to erase node " << thisNodeId << " at index " << i); + + break; + } + } + } + } + else + { + MESSAGE("Polygons are identical!!!"); + + // Don't erase a polygon right now to prevent a mess with polygons and nodes ids + const int endNodeIndex = startNodeIndex + _quantities[largerPolygon]; + for (int i = startNodeIndex; i < endNodeIndex; ++i) + { + nodesIndexesToErase.insert(i); + } + quantsUpdatedValues[largerPolygon] = 0; + } + } + + // Erase all nodes from collected indexes in reverse order + for (auto nodeIdx = nodesIndexesToErase.rbegin(); nodeIdx != nodesIndexesToErase.rend(); ++nodeIdx) + { + MESSAGE("Erased node on index " << *nodeIdx); + _nodes.erase(_nodes.begin() + *nodeIdx); + } + + // Update quantities + for (const auto& quantUpdate : quantsUpdatedValues) + { + MESSAGE("Quantity for a polygon " << quantUpdate.first << " updated from " << _quantities[quantUpdate.first] << " to " << quantUpdate.second); + _quantities[quantUpdate.first] = quantUpdate.second; + } + + // Now check if we have completely deleted polygons here and erase them + _quantities.erase(std::remove(_quantities.begin(), _quantities.end(), 0), _quantities.end()); + + return true; +} + +//================================================================================ +/*! + * \brief Finds polygons with at least one edge not connected to any other polygon, + * and removes all of them. Returns true if at least polygon one was removed. + */ +bool Hexahedron::_volumeDef::removeOpenEdgesPolygons() +{ + if (_quantities.empty()) + { + MESSAGE("Volume doesn't have any polygons"); + return false; + } + + // Check every polygon if it has at least one edge not connected to other polygon + const int numOfPolygons = _quantities.size(); + std::vector>> edgesByPolygon = getPolygonsEdges(); + + // Check if nodes pairs presented in other polygons + for (int i = 0; i < numOfPolygons - 1; ++i) + { + auto& edges = edgesByPolygon[i]; + + for (auto edge = edges.begin(); edge != edges.end(); /* ++edge */) + { + // Iterate other polygons to find matching pairs + bool edgeFound = false; + for (int j = i + 1; j < numOfPolygons; ++j) + { + auto& otherEdges = edgesByPolygon[j]; + auto foundEdge = otherEdges.find(*edge); + if (foundEdge != otherEdges.end()) + { + // We can have more than two the same edges in the volume. + // For example, when two parts of the volume connected by one polygon. + // So, we can't break here and must continue to search. + otherEdges.erase(foundEdge); + edgeFound = true; + } + } + + if (edgeFound) + { + auto curEdge = edge; + ++edge; + edges.erase(curEdge); + } + else + ++edge; + } + } + + // At this point for correct polyhedron all pairs of edges must be erased. + // Check if we have some edges without pairs and remove related polygons. + bool isRemoved = false; + for (int i = edgesByPolygon.size() - 1; i >= 0; --i) + { + if (edgesByPolygon[i].empty()) + continue; + + MESSAGE("Found not connected edges for polygon " << i << " :"); + for (auto& edge : edgesByPolygon[i]) + { + MESSAGE("Edge: " << edge.first << ", " << edge.second); + } + + // Erase redundant nodes + auto first = _nodes.begin() + getStartNodeIndex(i); + auto last = first + _quantities[i]; + _nodes.erase(first, last); + + // Erase quantities + _quantities.erase(_quantities.begin() + i); + + isRemoved = true; + } + + return isRemoved; +} + //================================================================================ /*! * \brief Check that all faces in polyhedron are connected so a unique volume is defined. diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx index dbaa7cf52..99a8f4b88 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -409,6 +409,24 @@ namespace Cartesian3D { return static_cast< const StdMeshers::Cartesian3D::E_IntersectPoint* >( _intPoint ); } _ptr Ptr() const { return Node() ? (_ptr) Node() : (_ptr) EdgeIntPnt(); } bool operator==(const _nodeDef& other ) const { return Ptr() == other.Ptr(); } + + friend std::ostream& operator<<(std::ostream& os, const _nodeDef& node) + { + if (node._node) + { + os << "Node at hexahedron corner: "; + node._node->Print(os); + } + else if (node._intPoint && node._intPoint->_node) + { + os << "Node at intersection point: "; + node._intPoint->_node->Print(os); // intersection point + } + else + os << "mesh node is null\n"; + + return os; + } }; std::vector< _nodeDef > _nodes; @@ -442,6 +460,11 @@ namespace Cartesian3D bool IsPolyhedron() const { return ( !_quantities.empty() || ( _next && !_next->_quantities.empty() )); } + std::vector>> getPolygonsEdges() const; + int getStartNodeIndex(const int polygon) const; + std::map> findOverlappingPolygons() const; + bool divideOverlappingPolygons(); + bool removeOpenEdgesPolygons(); struct _linkDef: public std::pair<_ptr,_ptr> // to join polygons in removeExcessSideDivision() { @@ -464,6 +487,22 @@ namespace Cartesian3D _next = next; next->_prev = this; } + + friend std::ostream& operator<<(std::ostream& os, const _linkDef& link) + { + os << "Link def:\n"; + + os << link._node1; + if (link.first) + os << "first: " << link.first; + + if (link.second) + os << "second: " << link.second; + + os << "_loopIndex: " << link._loopIndex << '\n'; + + return os; + } }; }; From c4bd5657c454bf6af8a3201d642a27bcb2a43701 Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Fri, 23 Aug 2024 21:00:14 +0100 Subject: [PATCH 12/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Updated to fix some tests issues from previous removing of polygons implementation. --- .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 281 +++++++++++++++--- .../StdMeshers_Cartesian_3D_Hexahedron.hxx | 5 +- 2 files changed, 236 insertions(+), 50 deletions(-) diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index 5a235b743..2fd65a3f3 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -628,7 +628,7 @@ void Hexahedron::init( size_t i, size_t j, size_t k, const Solid* solid ) // this method can be called in parallel, so use own helper SMESH_MesherHelper helper( *_grid->_helper->GetMesh() ); - clearNodesLinkedToNull(solid, helper); + // clearNodesLinkedToNull(solid, helper); // Create sub-links (_Link::_splits) by splitting links with _Link::_fIntPoints // --------------------------------------------------------------- @@ -2935,11 +2935,11 @@ int Hexahedron::addVolumes( SMESH_MesherHelper& helper ) if ( !useQuanta ) { // Try to find and fix geometry issues - const bool isDivided = volDef->divideOverlappingPolygons(); - const bool isRemoved = volDef->removeOpenEdgesPolygons(); + const bool hasDividedPolygons = volDef->divideOverlappingPolygons(); + const bool hasFixedOpenEdges = volDef->fixOpenEdgesPolygons(); // Update nodes if the geometry was changed - if (isDivided || isRemoved) + if (hasDividedPolygons || hasFixedOpenEdges) { nodes.clear(); nodes.resize(volDef->_nodes.size()); @@ -3256,6 +3256,62 @@ std::vector>> Hexahedron::_volumeDef::getPolygonsEd return polygonsEdges; } +//================================================================================ +/*! + * \brief Finds polygons with at least one edge not connected to any other polygon. + * Returns a vector of sets of edges like {{ nodeId1, nodeId2 }, { nodeId2, nodeId3 }} + * where an index of element in the vector is an index of polygon in the volume. + * Nodes ids in the pairs are sorted, so index of a first node is always less than second. + */ +std::vector>> Hexahedron::_volumeDef::findOpenEdges() const +{ + if (_quantities.empty()) + { + MESSAGE("Volume doesn't have any polygons"); + return {}; + } + + // Check every polygon if it has at least one edge not connected to other polygon + const int numOfPolygons = _quantities.size(); + std::vector>> edgesByPolygon = getPolygonsEdges(); + + // Check if nodes pairs presented in other polygons + for (int i = 0; i < numOfPolygons - 1; ++i) + { + auto& edges = edgesByPolygon[i]; + + for (auto edge = edges.begin(); edge != edges.end(); /* ++edge */) + { + // Iterate other polygons to find matching pairs + bool edgeFound = false; + for (int j = i + 1; j < numOfPolygons; ++j) + { + auto& otherEdges = edgesByPolygon[j]; + auto foundEdge = otherEdges.find(*edge); + if (foundEdge != otherEdges.end()) + { + // We can have more than two the same edges in the volume. + // For example, when two parts of the volume connected by one polygon. + // So, we can't break here and must continue to search. + otherEdges.erase(foundEdge); + edgeFound = true; + } + } + + if (edgeFound) + { + auto curEdge = edge; + ++edge; + edges.erase(curEdge); + } + else + ++edge; + } + } + + return edgesByPolygon; +} + //================================================================================ /*! * \brief Finds in the volume a polygon with such a set of edges that is a subset @@ -3415,6 +3471,9 @@ bool Hexahedron::_volumeDef::divideOverlappingPolygons() } } + if (nodesIndexesToErase.empty()) + return false; + // Erase all nodes from collected indexes in reverse order for (auto nodeIdx = nodesIndexesToErase.rbegin(); nodeIdx != nodesIndexesToErase.rend(); ++nodeIdx) { @@ -3432,6 +3491,174 @@ bool Hexahedron::_volumeDef::divideOverlappingPolygons() // Now check if we have completely deleted polygons here and erase them _quantities.erase(std::remove(_quantities.begin(), _quantities.end(), 0), _quantities.end()); + // Make sure we didn't create some new mess + divideOverlappingPolygons(); + + return true; +} + +//================================================================================ +/*! + * \brief Finds polygons with at least one edge not connected to any other polygon, + * and tries to cap them with new polygons or just remove as invalid. + * Returns true if any of this changes were made. + */ +bool Hexahedron::_volumeDef::fixOpenEdgesPolygons() +{ + std::vector>> edgesByPolygon = findOpenEdges(); + if (edgesByPolygon.empty()) + return false; + + const bool wasCapped = capOpenEdgesPolygons(edgesByPolygon); + if (wasCapped) + { + // The volume's geometry was changed, we need to collect edges again + edgesByPolygon = findOpenEdges(); + } + + const bool wasRemoved = removeOpenEdgesPolygons(edgesByPolygon); + if (!wasCapped && !wasRemoved) + { + return false; + } + + // Make sure we didn't create some new mess + fixOpenEdgesPolygons(); + + return true; +} + +//================================================================================ +/*! + * \brief Finds polygons with at least one edge not connected to any other polygon, + * and tries to cap them with new polygons. Returns true if at least one polygon + * was created. + */ +bool Hexahedron::_volumeDef::capOpenEdgesPolygons(const std::vector>>& edgesByPolygon) +{ + // Check if we can cap this edges with a new polygon. + // At this moment we don't interesting in relations of edges + // with other polygons, just if it's possible to connect them. + std::set> freeEdges; + for (int i = edgesByPolygon.size() - 1; i >= 0; --i) + { + if (edgesByPolygon[i].empty()) + continue; + + for (auto& edge : edgesByPolygon[i]) + { + freeEdges.insert(edge); + } + } + + // If we don't have free edges, then nothing to do here + if (freeEdges.empty()) + { + return false; + } + + // Consider a simple case where all the edges could form a closed polygon. + // First edge will form the first and the last node in the vector. + const size_t freeNodesNum = freeEdges.size() * 2 - 1; + std::vector nodes(freeNodesNum); + + auto findNode = [&](const std::pair& edge, const int node, const int index) -> bool + { + if (edge.first == node) + { + nodes[index] = edge.second; + return true; + } + else if (edge.second == node) + { + nodes[index] = edge.first; + return true; + } + + return false; + }; + + size_t firstIdx = 0; + size_t lastIdx = freeNodesNum - 1; + for (auto curEdge = freeEdges.begin(); curEdge != freeEdges.end(); ++curEdge) + { + // Store current nodes + int firstNode = curEdge->first; + int lastNode = curEdge->second; + nodes[firstIdx++] = firstNode; + nodes[lastIdx--] = lastNode; + + // Try to find connected nodes in other edges + auto otherEdge = curEdge; + ++otherEdge; + bool firstFound = false; + bool lastFound = false; + + while (otherEdge != freeEdges.end()) + { + if (!firstFound) + { + firstFound = findNode(*otherEdge, firstNode, firstIdx); + if (firstFound) + { + auto edgeToRemove = otherEdge; + ++otherEdge; + freeEdges.erase(edgeToRemove); + + continue; + } + } + + if (!lastFound) + { + lastFound = findNode(*otherEdge, lastNode, lastIdx); + if (lastFound) + { + auto edgeToRemove = otherEdge; + ++otherEdge; + freeEdges.erase(edgeToRemove); + + continue; + } + } + + if (firstFound && lastFound) + break; + + ++otherEdge; + } + + // If we didn't find a node to connect for one edge, + // then we can't build a polygon here. + // TODO: add ability to create a polygon from part of the given edge? + if (!firstFound || !lastFound) + return false; + + ++firstIdx; + --lastIdx; + } + + // Now we collected all the nodes for a new polygon + // and need to add quantities and nodes to the volume. + // TODO: add a check if this polygon is planar? + _quantities.push_back(freeNodesNum); + + // Update storage + const size_t newNodesNum = _nodes.size() + freeNodesNum; + if (_nodes.capacity() < newNodesNum) + _nodes.reserve(newNodesNum); + + // Find nodes by ids and store them + for (size_t i = 0; i < freeNodesNum; ++i) + { + auto nodeIt = std::find_if(_nodes.begin(), _nodes.end(), + [&](const _nodeDef& node) { return node.Node()->GetID() == nodes[i]; }); + + _nodes.push_back(*nodeIt); + } + + MESSAGE("New polygon to cap open edges at index " << _quantities.size() - 1 << ", quant: " << _quantities.back()); + return true; } @@ -3440,52 +3667,8 @@ bool Hexahedron::_volumeDef::divideOverlappingPolygons() * \brief Finds polygons with at least one edge not connected to any other polygon, * and removes all of them. Returns true if at least polygon one was removed. */ -bool Hexahedron::_volumeDef::removeOpenEdgesPolygons() +bool Hexahedron::_volumeDef::removeOpenEdgesPolygons(const std::vector>>& edgesByPolygon) { - if (_quantities.empty()) - { - MESSAGE("Volume doesn't have any polygons"); - return false; - } - - // Check every polygon if it has at least one edge not connected to other polygon - const int numOfPolygons = _quantities.size(); - std::vector>> edgesByPolygon = getPolygonsEdges(); - - // Check if nodes pairs presented in other polygons - for (int i = 0; i < numOfPolygons - 1; ++i) - { - auto& edges = edgesByPolygon[i]; - - for (auto edge = edges.begin(); edge != edges.end(); /* ++edge */) - { - // Iterate other polygons to find matching pairs - bool edgeFound = false; - for (int j = i + 1; j < numOfPolygons; ++j) - { - auto& otherEdges = edgesByPolygon[j]; - auto foundEdge = otherEdges.find(*edge); - if (foundEdge != otherEdges.end()) - { - // We can have more than two the same edges in the volume. - // For example, when two parts of the volume connected by one polygon. - // So, we can't break here and must continue to search. - otherEdges.erase(foundEdge); - edgeFound = true; - } - } - - if (edgeFound) - { - auto curEdge = edge; - ++edge; - edges.erase(curEdge); - } - else - ++edge; - } - } - // At this point for correct polyhedron all pairs of edges must be erased. // Check if we have some edges without pairs and remove related polygons. bool isRemoved = false; diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx index 99a8f4b88..0a38e023a 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -461,10 +461,13 @@ namespace Cartesian3D ( _next && !_next->_quantities.empty() )); } std::vector>> getPolygonsEdges() const; + std::vector>> findOpenEdges() const; int getStartNodeIndex(const int polygon) const; std::map> findOverlappingPolygons() const; bool divideOverlappingPolygons(); - bool removeOpenEdgesPolygons(); + bool fixOpenEdgesPolygons(); + bool capOpenEdgesPolygons(const std::vector>>& edgesByPolygon); + bool removeOpenEdgesPolygons(const std::vector>>& edgesByPolygon); struct _linkDef: public std::pair<_ptr,_ptr> // to join polygons in removeExcessSideDivision() { From a605c12d81d1c257900c8c3872b500b539b63c48 Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Mon, 26 Aug 2024 16:04:30 +0100 Subject: [PATCH 13/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Fixed wrong nodes num in open edges capping. --- src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index 2fd65a3f3..716f923dd 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -3559,7 +3559,7 @@ bool Hexahedron::_volumeDef::capOpenEdgesPolygons(const std::vector nodes(freeNodesNum); auto findNode = [&](const std::pair& edge, const int node, const int index) -> bool @@ -3580,7 +3580,7 @@ bool Hexahedron::_volumeDef::capOpenEdgesPolygons(const std::vector 1; ++curEdge) { // Store current nodes int firstNode = curEdge->first; @@ -3652,7 +3652,7 @@ bool Hexahedron::_volumeDef::capOpenEdgesPolygons(const std::vectorGetID() == nodes[i]; }); + [&](const _nodeDef& node) { return node.Node() && node.Node()->GetID() == nodes[i]; }); _nodes.push_back(*nodeIt); } From 37de8d765a384fccf5ad1a767b69a4f24025f4bd Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Mon, 26 Aug 2024 16:26:42 +0100 Subject: [PATCH 14/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Commented open edges capping because of producing null nodes. --- .../StdMeshers_Cartesian_3D_Hexahedron.cxx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx index 716f923dd..dae114fba 100644 --- a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -3509,15 +3509,15 @@ bool Hexahedron::_volumeDef::fixOpenEdgesPolygons() if (edgesByPolygon.empty()) return false; - const bool wasCapped = capOpenEdgesPolygons(edgesByPolygon); - if (wasCapped) - { - // The volume's geometry was changed, we need to collect edges again - edgesByPolygon = findOpenEdges(); - } + // const bool wasCapped = capOpenEdgesPolygons(edgesByPolygon); + // if (wasCapped) + // { + // // The volume's geometry was changed, we need to collect edges again + // edgesByPolygon = findOpenEdges(); + // } const bool wasRemoved = removeOpenEdgesPolygons(edgesByPolygon); - if (!wasCapped && !wasRemoved) + if (/* !wasCapped && */!wasRemoved) { return false; } From 77dc3d3a8cc40f1c9dc7705bab785649daf8a29b Mon Sep 17 00:00:00 2001 From: Konstantin Leontev Date: Mon, 26 Aug 2024 16:29:37 +0100 Subject: [PATCH 15/15] [bos #42217][EDF 28921] Horseshoe with bodyfitting. Triangle filter switched off because of producing more new edges than we have concave polygons rendering artifacts. --- src/OBJECT/SMESH_DeviceActor.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OBJECT/SMESH_DeviceActor.cxx b/src/OBJECT/SMESH_DeviceActor.cxx index fafd40aef..f01910963 100644 --- a/src/OBJECT/SMESH_DeviceActor.cxx +++ b/src/OBJECT/SMESH_DeviceActor.cxx @@ -257,7 +257,8 @@ SMESH_DeviceActor myTriangleFilter->SetInputConnection(myGeomFilter->GetOutputPort()); anId++; // 4 - myPassFilter[ anId ]->SetInputConnection( myTriangleFilter->GetOutputPort() ); + // myPassFilter[ anId ]->SetInputConnection( myTriangleFilter->GetOutputPort() ); + myPassFilter[ anId ]->SetInputConnection( myGeomFilter->GetOutputPort() ); myPassFilter[ anId + 1 ]->SetInputConnection( myPassFilter[ anId ]->GetOutputPort() ); anId++; // 5