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/OBJECT/SMESH_DeviceActor.cxx b/src/OBJECT/SMESH_DeviceActor.cxx index be17fe3c3..f01910963 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,8 +254,10 @@ SMESH_DeviceActor anId++; // 3 myGeomFilter->SetInputConnection( myPassFilter[ anId ]->GetOutputPort() ); + myTriangleFilter->SetInputConnection(myGeomFilter->GetOutputPort()); anId++; // 4 + // myPassFilter[ anId ]->SetInputConnection( myTriangleFilter->GetOutputPort() ); myPassFilter[ anId ]->SetInputConnection( myGeomFilter->GetOutputPort() ); myPassFilter[ anId + 1 ]->SetInputConnection( myPassFilter[ anId ]->GetOutputPort() ); 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/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/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/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); diff --git a/src/StdMeshers.test/CMakeLists.txt b/src/StdMeshers.test/CMakeLists.txt new file mode 100644 index 000000000..bda437fc7 --- /dev/null +++ b/src/StdMeshers.test/CMakeLists.txt @@ -0,0 +1,70 @@ +# 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() + +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..8f770e980 --- /dev/null +++ b/src/StdMeshers.test/HexahedronCanonicalShapesTest.cxx @@ -0,0 +1,245 @@ +// 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 + +using namespace StdMeshers::Cartesian3D; + +// Helper functions! +// Build Grid +// Require building mesh +// Require building shape. + +/*! + * \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, + TEdge2faceIDsMap& edge2faceIDsMap, + const int theNumOfThreads) +{ + // Canonical axes(i,j,k) + double axisDirs[9] = {1.,0.,0.,0.,1.,0.,0.,0.,1.}; + std::vector grdSpace = { std::to_string(gridSpacing) }; + std::vector intPnts; + + 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.GridInitAndInterserctWithShape(theShape, edge2faceIDsMap, aHypo.get(), theNumOfThreads, false); +} + +/*! + * \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; + + TEdge2faceIDsMap edge2faceIDsMap; + 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) { + 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; + + 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)) + 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 new file mode 100644 index 000000000..8931e1a08 --- /dev/null +++ b/src/StdMeshers.test/HexahedronTest.cxx @@ -0,0 +1,203 @@ +// 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 +#include +#include + +#include +#include + +using namespace StdMeshers::Cartesian3D; + +// Helper functions! +// Build Grid +// Require building mesh +// Require building shape. + +/*! + * \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); +} + +/*! + * \brief Initialize the grid and intesersectors of grid with the geometry + */ +void GridInitAndIntersectWithShape (Grid& grid, + double gridSpacing, + double theSizeThreshold, + const TopoDS_Shape theShape, + TEdge2faceIDsMap& edge2faceIDsMap, + const int theNumOfThreads) +{ + // Canonical axes(i,j,k) + double axisDirs[9] = {1.,0.,0., 0.,1.,0., 0.,0.,1.}; + std::vector grdSpace = { std::to_string(gridSpacing) }; + std::vector intPnts; + + 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.GridInitAndInterserctWithShape(theShape, edge2faceIDsMap, aHypo.get(), theNumOfThreads, false); +} + +/*! + * \brief Reproduce conditions of TBPERF_GRIDS_PERF_SMESH_M1 test to detect and solve segfault in unit test. + */ +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); + + for (auto nThreads : numberOfThreads ) + { + for (size_t i = 0; i < 10 /*trials*/; i++) + { + 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._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; +} + +/*! + * \brief Reproduce conditions of TBPERF_GRIDS_PERF_SMESH_J4 test to detect and solve segfault in unit test. + */ +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); + + // Test with face creation + for (auto nThreads : numberOfThreads ) + { + for (size_t i = 0; i < 10 /*trials*/; i++) + { + 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; + double testThreshold = 1.000001; + grid._toCreateFaces = true; + grid._sizeThreshold = testThreshold; + + TEdge2faceIDsMap edge2faceIDsMap; + 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; +} + +// Entry point for test +int main() +{ + bool isOK = testNRTM1(); + if (!testNRTJ4()) + isOK = false; + + return isOK ? 0 : 1; +} diff --git a/src/StdMeshers.test/tests.set b/src/StdMeshers.test/tests.set new file mode 100644 index 000000000..df1ddaa09 --- /dev/null +++ b/src/StdMeshers.test/tests.set @@ -0,0 +1,28 @@ +# 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 + HexahedronCanonicalShapesTest + ) \ 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_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.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx index c392deeea..c20c5a2a0 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,29 @@ #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 StdMeshers::Cartesian3D; +// 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 +126,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 +193,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 +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 @@ -6870,21 +297,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 std; +using namespace SMESH; +using namespace StdMeshers::Cartesian3D; + +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, + TEdge2faceIDsMap& 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; +} + +//============================================================================= +/* + * 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 new file mode 100644 index 000000000..7177bbd37 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Grid.hxx @@ -0,0 +1,702 @@ +// 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 +#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 +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; + + //============================================================================= + // 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 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; } + 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 std::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; + 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 + + SMESH::Controls::ElementsOnShape _edgeClassifier; + SMESH::Controls::ElementsOnShape _vertexClassifier; + + bool IsOneSolid() const { return _solidByID.size() < 2; } + GeomIDVecHelder GetSolidIDsByShapeID( const std::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 std::vector< TGeomID > _faceIDs; + + B_IntersectPoint(): _node(NULL) {} + 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 + */ + 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 + std::multiset< F_IntersectPoint > _intPoints; + + void RemoveExcessIntPoints( const double tol ); + TGeomID GetSolidIDBefore( std::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; + std::vector< gp_XYZ > _origins; // origin points of all planes in one direction + std::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; + 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 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; + _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 std::vector< TopoDS_Shape >& faceVec, const double* axesDirs, Bnd_Box& shapeBox ); + }; + + 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]; + + 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 + + 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; + 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, + SMESH::Controls::ElementsOnShape& classifier ); + void GetEdgesToImplement( std::map< TGeomID, std::vector< TGeomID > > & edge2faceMap, + const TopoDS_Shape& shape, + 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; + 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 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, + 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 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, std::vector< TGeomID > >& edge2faceIDsMap, + const StdMeshers_CartesianParameters3D* hyp, + const int numOfThreads, + bool computeCanceled ); + }; + + // Implement parallel computation of Hexa with c++ thread implementation + template + void parallel_for(const Iterator& first, const Iterator& last, Function&& f, const unsigned int nthreads = 1) + { + MESSAGE("Start parallel computation of Hexa with c++ threads..."); + + assert(nthreads > 0); + + 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)); + } + + // This line for debug in sequential mode + // std::for_each(it, last, f); + } + 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"); + } + + // -------------------------------------------------------------------------- + /*! + * \brief Intersector of TopoDS_Face with all GridLine's + */ + struct FaceGridIntersector + { + TopoDS_Face _face; + TGeomID _faceID; + Grid* _grid; + Bnd_Box _bndBox; + IntCurvesFace_Intersector* _surfaceInt; + std::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 ) + { + std::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(std::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 + { + 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 ) + _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; + + std::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); + + /* + * 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 ) + { + return -_tol < _w && _w < linLength + _tol; + } + FaceLineIntersector():_surfaceInt(0) {} + ~FaceLineIntersector() { if (_surfaceInt ) delete _surfaceInt; _surfaceInt = 0; } + }; + +} // 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 new file mode 100644 index 000000000..dae114fba --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.cxx @@ -0,0 +1,4896 @@ +// 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 std; +using namespace SMESH; +using namespace StdMeshers::Cartesian3D; + +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; +} + +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 + */ +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() ); + + // clearNodesLinkedToNull(solid, helper); + + // 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 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 + */ +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( 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 ] ; + + _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 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 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 + */ +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(); + + const bool hasEdgeIntersections = !_eIntPoints.empty(); + + // Create polygons from quadrangles + defineHexahedralFaces( solid, intFlag ); + + // 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; + 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 ) { + curLink = freeLinks[ iL ]; + if ( curLink ) + 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 + TFaceOfLink faceOfLink = findStartLink(freeLinks, usedFaceIDs); + 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 + vector< _Node* > chainNodes; + //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 ) { + iPolygon = findCoplanarPolygon(polygon, nbQuadPolygons, freeLinks, nbFreeLinks, + ipTmp, usedFaceIDs, tmpAddedFace, curFace); + } + else { + 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 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(); +} + +#ifdef WITH_TBB +template +void computeHexa(Type& hex) +{ + if ( hex ) + hex->computeElements(); +} +#endif + +//================================================================================ +/*! + * \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 ) + { + // Try to find and fix geometry issues + const bool hasDividedPolygons = volDef->divideOverlappingPolygons(); + const bool hasFixedOpenEdges = volDef->fixOpenEdgesPolygons(); + + // Update nodes if the geometry was changed + if (hasDividedPolygons || hasFixedOpenEdges) + { + 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; + 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 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 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 + * 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; + } + } + + if (nodesIndexesToErase.empty()) + return false; + + // 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()); + + // 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(); + 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() && (lastIdx - firstIdx) > 1; ++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() && 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; +} + +//================================================================================ +/*! + * \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(const std::vector>>& edgesByPolygon) +{ + // 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. + * 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 ) { + polygon.myNodes[iN] = _polygons[ iF ]._links[ iL ].FirstNode()->Node(); + if ( ! polygon.myNodes[iN] ) + 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(); +} diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx new file mode 100644 index 000000000..0a38e023a --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D_Hexahedron.hxx @@ -0,0 +1,653 @@ +// 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" + +namespace StdMeshers +{ +namespace Cartesian3D +{ + // -------------------------------------------------------------------------- + /*! + * \brief Return cells sharing a link + */ + struct CellsAroundLink + { + int _iDir; + int _dInd[4][3]; + size_t _nbCells[3]; + int _i,_j,_k; + StdMeshers::Cartesian3D::Grid* _grid; + + 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( 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 StdMeshers::Cartesian3D::B_IntersectPoint* _intPoint; + const _Face* _usedInFace; + char _isInternalFlags; + + _Node(const SMDS_MeshNode* n=0, const StdMeshers::Cartesian3D::B_IntersectPoint* ip=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 + { 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 ) + { + return polygon ? ( _usedInFace == polygon ) : bool( _usedInFace ); + } + StdMeshers::Cartesian3D::TGeomID IsLinked( const StdMeshers::Cartesian3D::B_IntersectPoint* other, + StdMeshers::Cartesian3D::TGeomID avoidFace=-1 ) const // returns id of a common face + { + 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 ); + 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; + } + }; + + // -------------------------------------------------------------------------------- + struct _Link // link connecting two _Node's + { + 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(); + + 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; + } + }; + + // -------------------------------------------------------------------------------- + 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 )) + { + 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; + } + } + void RemoveFace( const _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; + } + } + } + + 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; + } + }; + + // -------------------------------------------------------------------------------- + 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() )); + } + + 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; + } + }; + + // -------------------------------------------------------------------------------- + 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(); } + + 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; + 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() )); } + + std::vector>> getPolygonsEdges() const; + std::vector>> findOpenEdges() const; + int getStartNodeIndex(const int polygon) const; + std::map> findOverlappingPolygons() const; + bool divideOverlappingPolygons(); + 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() + { + _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; + } + + 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; + } + }; + }; + + // topology of a hexahedron + 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; + + // 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 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 ); + 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); + 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 ); + 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; + } + 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< StdMeshers::Cartesian3D::TGeomID, size_t > TID2Nb; + static void insertAndIncrement( StdMeshers::Cartesian3D::TGeomID id, TID2Nb& id2nbMap ) + { + 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/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..1807a694d --- /dev/null +++ b/test/data/HexahedronTest/NRTM1.brep @@ -0,0 +1,441 @@ +DBRep_DrawableShape + +CASCADE Topology V1, (c) Matra-Datavision +Locations 0 +Curve2ds 36 +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 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 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 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 17 +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 +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 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 +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 -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 0 + +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 +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 +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 +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 +0 + +0101000 +-53 0 +57 0 * +Wi + +0101100 +-56 0 -54 0 +52 0 +51 0 * +Fa +0 1e-07 1 0 + +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 +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 2 0 0 10 +2 12 6 0 0 10 +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 +0 + +0101000 +-46 0 +58 0 * +Wi + +0101100 +-47 0 -45 0 +44 0 +56 0 * +Fa +0 1e-07 2 0 + +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 3 0 0 10 +2 16 4 0 0 10 +0 + +0101000 +-41 0 +55 0 * +Ed + 1e-07 1 1 0 +1 9 0 0 10 +2 17 3 0 0 10 +2 18 6 0 0 10 +0 + +0101000 +-41 0 +46 0 * +Wi + +0101100 +-54 0 -40 0 +39 0 +44 0 * +Fa +0 1e-07 3 0 + +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 +0 + +0101000 +-36 0 +53 0 * +Ed + 1e-07 1 1 0 +1 11 0 0 10 +2 21 4 0 0 10 +2 22 6 0 0 10 +0 + +0101000 +-41 0 +36 0 * +Wi + +0101100 +-35 0 -34 0 +40 0 +52 0 * +Fa +0 1e-07 4 0 + +0101000 ++33 0 * +Ed + 1e-07 1 1 0 +1 12 0 0 10 +2 23 5 0 0 10 +2 24 6 0 0 10 +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 +2 27 8 0 0 6.28318530717959 +2 28 9 0 0 6.28318530717959 +0 + +0101000 ++29 0 -29 0 * +Wi + +0101100 +-28 0 * +Fa +0 1e-07 5 0 + +0101000 ++30 0 +27 0 * +Wi + +0101100 +-45 0 -39 0 +34 0 +31 0 * +Fa +0 1e-07 6 0 + +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 29 30CN 7 0 6 9 +0 + +0101000 ++29 0 -23 0 * +Ed + 1e-07 1 1 0 +1 15 0 0 6.28318530717959 +2 31 7 0 0 6.28318530717959 +2 32 10 0 0 6.28318530717959 +0 + +0101000 ++23 0 -23 0 * +Wi + +0101100 ++22 0 +28 0 -22 0 -21 0 * +Fa +0 1e-07 7 0 + +0101000 ++20 0 * +Wi + +0101100 ++21 0 * +Fa +0 1e-07 10 0 + +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 8 0 + +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 16 0 0 6 +3 33 34CN 9 0 0 6 +0 + +0101000 ++10 0 -29 0 * +Ed + 1e-07 1 1 0 +1 17 0 0 6.28318530717959 +2 35 9 0 0 6.28318530717959 +2 36 11 0 0 6.28318530717959 +0 + +0101000 ++10 0 -10 0 * +Wi + +0101100 +-28 0 +9 0 +8 0 -9 0 * +Fa +0 1e-07 9 0 + +0101000 ++7 0 * +Wi + +0101100 +-8 0 * +Fa +0 1e-07 11 0 + +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 +0 + diff --git a/test/data/HexahedronTest/NRTMJ4.brep b/test/data/HexahedronTest/NRTMJ4.brep new file mode 100644 index 000000000..7f6412ea5 --- /dev/null +++ b/test/data/HexahedronTest/NRTMJ4.brep @@ -0,0 +1,598 @@ +DBRep_DrawableShape + +CASCADE Topology V1, (c) Matra-Datavision +Locations 0 +Curve2ds 54 +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 60 1 0 +1 0 0 1 0 +1 16.899999999999999 50 0 1 +1 0 0 1 0 +1 16.899999999999999 50 1 0 +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 0 50 1 0 +1 100 0 0 1 +1 60 0 0 1 +1 100 0 0 -1 +1 0 60 1 0 +1 0 -50 1 0 +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 60 0 0 1 +1 0 0 0 -1 +2 25 25 -1 0 0 1 10 +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 59.899999999999999 0 0 -1 +1 0 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 27 +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 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 +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 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 +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 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 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 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 -50 0 0 -1 -1 0 -0 0 1 0 +Triangulations 0 + +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 +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 +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 +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 +0 + +0101000 +-67 0 +71 0 * +Wi + +0101100 +-70 0 -68 0 +66 0 +65 0 * +Fa +0 1e-07 1 0 + +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 2 0 0 16.9 +2 10 6 0 0 16.9 +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 2 0 0 10 +2 12 7 0 0 10 +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 13 2 0 0 59.9 +2 14 8 0 0 59.9 +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 15 2 0 0 10 +2 16 9 0 0 10 +0 + +0101000 +-55 0 +57 0 * +Ve +1e-07 +60 0 100 +0 0 + +0101101 +* +Ed + 1e-07 1 1 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 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 11 0 0 60 +2 21 2 0 0 60 +2 22 5 0 0 60 +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 + +0101000 ++49 0 * +Ve +1e-07 +60 50 100 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 12 0 0 60 +2 23 3 0 0 60 +2 24 4 0 0 60 +0 + +0101000 +-47 0 +69 0 * +Ed + 1e-07 1 1 0 +1 13 0 0 50 +2 25 3 0 0 50 +2 26 10 0 0 50 +0 + +0101000 +-47 0 +53 0 * +Wi + +0101100 +-68 0 -46 0 +45 0 +51 0 * +Fa +0 1e-07 3 0 + +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 14 0 0 16.9 +2 27 4 0 0 16.9 +2 28 6 0 0 16.9 +0 + +0101000 ++42 0 -41 0 * +Ve +1e-07 +50 50 16.9 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 15 0 0 10 +2 29 4 0 0 10 +2 30 7 0 0 10 +0 + +0101000 +-41 0 +39 0 * +Ve +1e-07 +50 50 76.8 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 16 0 0 59.9 +2 31 4 0 0 59.9 +2 32 8 0 0 59.9 +0 + +0101000 +-37 0 +39 0 * +Ve +1e-07 +60 50 76.8 +0 0 + +0101101 +* +Ed + 1e-07 1 1 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 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 19 0 0 60 +2 37 4 0 0 60 +2 38 5 0 0 60 +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 + +0101000 ++31 0 * +Ed + 1e-07 1 1 0 +1 20 0 0 50 +2 39 5 0 0 50 +2 40 6 0 0 50 +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 21 0 0 6.28318530717959 +2 41 5 0 0 6.28318530717959 +2 42 11 0 0 6.28318530717959 +0 + +0101000 ++27 0 -27 0 * +Wi + +0101100 ++26 0 * +Fa +0 1e-07 5 0 + +0101000 ++28 0 +25 0 * +Ed + 1e-07 1 1 0 +1 22 0 0 50 +2 43 6 0 0 50 +2 44 7 0 0 50 +0 + +0101000 +-41 0 +61 0 * +Wi + +0101100 +-60 0 -23 0 +40 0 +29 0 * +Fa +0 1e-07 6 0 + +0101000 ++22 0 * +Ed + 1e-07 1 1 0 +1 23 0 0 50 +2 45 7 0 0 50 +2 46 8 0 0 50 +0 + +0101000 +-39 0 +59 0 * +Wi + +0101100 +-20 0 -38 0 +23 0 +58 0 * +Fa +0 1e-07 7 0 + +0101000 ++19 0 * +Ed + 1e-07 1 1 0 +1 24 0 0 50 +2 47 8 0 0 50 +2 48 9 0 0 50 +0 + +0101000 +-37 0 +57 0 * +Wi + +0101100 +-56 0 -17 0 +36 0 +20 0 * +Fa +0 1e-07 8 0 + +0101000 ++16 0 * +Ed + 1e-07 1 1 0 +1 25 0 0 50 +2 49 9 0 0 50 +2 50 10 0 0 50 +0 + +0101000 +-35 0 +55 0 * +Wi + +0101100 +-17 0 -34 0 +14 0 +54 0 * +Fa +0 1e-07 9 0 + +0101000 ++13 0 * +Wi + +0101100 +-52 0 -45 0 +33 0 +14 0 * +Fa +0 1e-07 10 0 + +0101000 ++11 0 * +Ve +1e-07 +15 25 -50 +0 0 + +0101101 +* +Ed + 1e-07 1 1 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 27 0 0 50 +3 53 54CN 11 0 0 50 +0 + +0101000 +-9 0 +27 0 * +Wi + +0101100 +-8 0 +7 0 +26 0 -7 0 * +Fa +0 1e-07 11 0 + +0101000 ++6 0 * +Wi + +0101100 ++8 0 * +Fa +0 1e-07 12 0 + +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 + +1100000 ++2 0 * + ++1 0 +0 + diff --git a/test/tests.set b/test/tests.set index f1a155efc..a3af40ace 100644 --- a/test/tests.set +++ b/test/tests.set @@ -127,6 +127,11 @@ SET(CPP_TESTS SMESH_RegularGridTest ) +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. # ----------------------------------------------------------------------------