From 1ce54e1c128c2e3e5caf2a57daf92489c419fd6e Mon Sep 17 00:00:00 2001 From: eap Date: Mon, 12 Oct 2020 11:47:53 +0300 Subject: [PATCH 01/10] #19982 EDF 21954 - Compute mesh fails --- src/StdMeshers/CMakeLists.txt | 1 + src/StdMeshers/StdMeshers_FaceSide.cxx | 35 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/StdMeshers/CMakeLists.txt b/src/StdMeshers/CMakeLists.txt index 2947ada60..87e4db8eb 100644 --- a/src/StdMeshers/CMakeLists.txt +++ b/src/StdMeshers/CMakeLists.txt @@ -62,6 +62,7 @@ SET(_link_LIBRARIES ${GEOM_GEOMUtils} SMESHimpl SMESHDS + SMESHUtils ${TBB_LIBS} ) diff --git a/src/StdMeshers/StdMeshers_FaceSide.cxx b/src/StdMeshers/StdMeshers_FaceSide.cxx index da593c9c3..6d0e8a97a 100644 --- a/src/StdMeshers/StdMeshers_FaceSide.cxx +++ b/src/StdMeshers/StdMeshers_FaceSide.cxx @@ -181,6 +181,41 @@ StdMeshers_FaceSide::StdMeshers_FaceSide(const TopoDS_Face& theFace, } // loop on edges + // orient seam edges (#19982) + const tol = Precision::Confusion(); + if ( NbEdges() > 1 && !myC2d[0].IsNull() ) + for ( int i = 0; i < NbEdges(); ++i ) + { + int iPrev = SMESH_MesherHelper::WrapIndex( i - 1, NbEdges() ); + if ( !BRep_Tool::IsClosed( myEdge[i], myFace ) || !myC2d[iPrev] ) + continue; + gp_Pnt2d pLastPrev = myC2d[iPrev]->Value( myLast[iPrev] ); + gp_Pnt2d pFirst = myC2d[i]->Value( myFirst[i] ); + if ( pLastPrev.IsEqual( pFirst, tol )) + continue; // OK + pFirst = myC2d[i]->Value( myLast[i] ); + if ( pLastPrev.IsEqual( pFirst, tol )) + { + std::swap( myFirst[i], myLast[i] ); + continue; + } + TopoDS_Edge E = myEdge[i]; + E.Reverse(); + Handle(Geom2d_Curve) c2dRev = BRep_Tool::CurveOnSurface( E, myFace, myFirst[i], myLast[i] ); + pFirst = c2dRev->Value( myFirst[i] ); + if ( pLastPrev.IsEqual( pFirst, tol )) + { + myC2d[i] = c2dRev; + continue; + } + pFirst = c2dRev->Value( myLast[i] ); + if ( pLastPrev.IsEqual( pFirst, tol )) + { + myC2d[i] = c2dRev; + std::swap( myFirst[i], myLast[i] ); + } + } + // count nodes and segments NbPoints( /*update=*/true ); From 50479f058d8bd84c6571beaa05c208313516f1c7 Mon Sep 17 00:00:00 2001 From: eap Date: Mon, 12 Oct 2020 12:38:29 +0300 Subject: [PATCH 02/10] #19982 EDF 21954 - Compute mesh fails -------- compilation error --- src/StdMeshers/StdMeshers_FaceSide.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StdMeshers/StdMeshers_FaceSide.cxx b/src/StdMeshers/StdMeshers_FaceSide.cxx index 6d0e8a97a..a9c254e09 100644 --- a/src/StdMeshers/StdMeshers_FaceSide.cxx +++ b/src/StdMeshers/StdMeshers_FaceSide.cxx @@ -182,7 +182,7 @@ StdMeshers_FaceSide::StdMeshers_FaceSide(const TopoDS_Face& theFace, } // loop on edges // orient seam edges (#19982) - const tol = Precision::Confusion(); + const double tol = Precision::Confusion(); if ( NbEdges() > 1 && !myC2d[0].IsNull() ) for ( int i = 0; i < NbEdges(); ++i ) { From 981e2bca16ed6b3a3cf7efc2e9d945c531b1d410 Mon Sep 17 00:00:00 2001 From: eap Date: Mon, 12 Oct 2020 17:15:51 +0300 Subject: [PATCH 03/10] bos #19961 EDF 21858 - ExportMED --- src/SMESH/SMESH_Mesh.cxx | 6 ++---- src/SMESH_I/SMESH_Mesh_i.cxx | 2 +- src/SMESH_SWIG/smeshBuilder.py | 21 +++++++++++++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/SMESH/SMESH_Mesh.cxx b/src/SMESH/SMESH_Mesh.cxx index b7d9737dd..2f8846f96 100644 --- a/src/SMESH/SMESH_Mesh.cxx +++ b/src/SMESH/SMESH_Mesh.cxx @@ -1406,10 +1406,8 @@ bool SMESH_Mesh::HasDuplicatedGroupNamesMED() * \param [in] theAutoGroups - boolean parameter for creating/not creating * the groups Group_On_All_Nodes, Group_On_All_Faces, ... ; * the typical use is auto_groups=false. - * \param [in] theMinor - define the minor version (y, where version is x.y.z) of MED file format. - * The theMinor must be between 0 and the current minor version of MED file library. - * If theMinor is equal to -1, the minor version is not changed (default). - * The major version (x, where version is x.y.z) cannot be changed. + * \param [in] theVersion - define the minor (xy, where version is x.y.z) of MED file format. + * If theVersion is equal to -1, the minor version is not changed (default). * \param [in] meshPart - mesh data to export * \param [in] theAutoDimension - if \c true, a space dimension of a MED mesh can be either * - 1D if all mesh nodes lie on OX coordinate axis, or diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index fd26d1cf0..2e9130d67 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -3776,7 +3776,7 @@ void SMESH_Mesh_i::ExportMED(const char* file, TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportMED( r'" << file << "', " << "auto_groups=" <`) to export instead of the mesh autoDimension: if *True* (default), a space dimension of a MED mesh can be either @@ -2328,7 +2331,7 @@ class Mesh(metaclass = MeshMeta): #args = [i for i in args if i not in [SMESH.MED_V2_1, SMESH.MED_V2_2]] # backward compatibility fileName = args[0] auto_groups = args[1] if len(args) > 1 else False - minor = args[2] if len(args) > 2 else -1 + version = args[2] if len(args) > 2 else -1 overwrite = args[3] if len(args) > 3 else True meshPart = args[4] if len(args) > 4 else None autoDimension = args[5] if len(args) > 5 else True @@ -2337,7 +2340,8 @@ class Mesh(metaclass = MeshMeta): z_tolerance = args[8] if len(args) > 8 else -1. # process keywords arguments auto_groups = kwargs.get("auto_groups", auto_groups) - minor = kwargs.get("minor", minor) + version = kwargs.get("version", version) + version = kwargs.get("minor", version) overwrite = kwargs.get("overwrite", overwrite) meshPart = kwargs.get("meshPart", meshPart) autoDimension = kwargs.get("autoDimension", autoDimension) @@ -2355,10 +2359,11 @@ class Mesh(metaclass = MeshMeta): z_tolerance,Parameters,hasVars = ParseParameters(z_tolerance) self.mesh.SetParameters(Parameters) - self.mesh.ExportPartToMED( meshPart, fileName, auto_groups, minor, overwrite, autoDimension, + self.mesh.ExportPartToMED( meshPart, fileName, auto_groups, + version, overwrite, autoDimension, fields, geomAssocFields, z_tolerance) else: - self.mesh.ExportMED(fileName, auto_groups, minor, overwrite, autoDimension) + self.mesh.ExportMED(fileName, auto_groups, version, overwrite, autoDimension) def ExportSAUV(self, f, auto_groups=0): """ From 34d237befeb90f2b48ddb43cfd40fd6f80d58603 Mon Sep 17 00:00:00 2001 From: eap Date: Wed, 14 Oct 2020 13:41:45 +0300 Subject: [PATCH 04/10] fix missing "break"s --- src/DriverCGNS/DriverCGNS_Read.cxx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/DriverCGNS/DriverCGNS_Read.cxx b/src/DriverCGNS/DriverCGNS_Read.cxx index 967628af2..fc1ba5120 100644 --- a/src/DriverCGNS/DriverCGNS_Read.cxx +++ b/src/DriverCGNS/DriverCGNS_Read.cxx @@ -1073,14 +1073,14 @@ Driver_Mesh::Status DriverCGNS_Read::Perform() PGetNodesFun getNodesFun = 0; if ( elemType == SMDSAbs_Face && meshDim == 3 ) switch ( axis ) { - case 0: getNodesFun = & TZoneData::IFaceNodes; - case 1: getNodesFun = & TZoneData::JFaceNodes; - case 2: getNodesFun = & TZoneData::KFaceNodes; + case 0: getNodesFun = & TZoneData::IFaceNodes; break; + case 1: getNodesFun = & TZoneData::JFaceNodes; break; + case 2: getNodesFun = & TZoneData::KFaceNodes; break; } else if ( elemType == SMDSAbs_Edge && meshDim == 2 ) switch ( axis ) { - case 0: getNodesFun = & TZoneData::IEdgeNodes; - case 1: getNodesFun = & TZoneData::JEdgeNodes; + case 0: getNodesFun = & TZoneData::IEdgeNodes; break; + case 1: getNodesFun = & TZoneData::JEdgeNodes; break; } if ( !getNodesFun ) { @@ -1103,14 +1103,14 @@ Driver_Mesh::Status DriverCGNS_Read::Perform() PGetNodesFun getNodesFun = 0; if ( elemType == SMDSAbs_Face ) switch ( axis ) { - case 0: getNodesFun = & TZoneData::IFaceNodes; - case 1: getNodesFun = & TZoneData::JFaceNodes; - case 2: getNodesFun = & TZoneData::KFaceNodes; + case 0: getNodesFun = & TZoneData::IFaceNodes; break; + case 1: getNodesFun = & TZoneData::JFaceNodes; break; + case 2: getNodesFun = & TZoneData::KFaceNodes; break; } else if ( elemType == SMDSAbs_Edge && meshDim == 2 ) switch ( axis ) { - case 0: getNodesFun = & TZoneData::IEdgeNodes; - case 1: getNodesFun = & TZoneData::JEdgeNodes; + case 0: getNodesFun = & TZoneData::IEdgeNodes; break; + case 1: getNodesFun = & TZoneData::JEdgeNodes; break; } if ( !getNodesFun ) { @@ -1129,9 +1129,9 @@ Driver_Mesh::Status DriverCGNS_Read::Perform() PAddElemFun addElemFun = 0; switch ( meshDim ) { - case 1: addElemFun = & add_BAR_2; - case 2: addElemFun = & add_QUAD_4; - case 3: addElemFun = & add_HEXA_8; + case 1: addElemFun = & add_BAR_2; break; + case 2: addElemFun = & add_QUAD_4; break; + case 3: addElemFun = & add_HEXA_8; break; } int elemID = meshInfo.NbElements(); const SMDS_MeshElement* elem = 0; From d245f797f1b69aca678fa372391275d7042faa10 Mon Sep 17 00:00:00 2001 From: eap Date: Fri, 9 Oct 2020 11:44:21 +0300 Subject: [PATCH 05/10] #19926 [CEA 19782] renumbering meshes --- doc/salome/examples/filters_ex39.py | 12 +- .../gui/SMESH/images/block_renumber_hyp.png | Bin 0 -> 27071 bytes .../gui/SMESH/input/3d_meshing_hypo.rst | 42 ++ doc/salome/gui/SMESH/input/about_hypo.rst | 4 +- .../SMESH/input/max_element_volume_hypo.rst | 14 - .../SMESH/input/tui_defining_hypotheses.rst | 14 + idl/SMESH_BasicHypothesis.idl | 19 + resources/CMakeLists.txt | 1 + resources/StdMeshers.xml.in | 7 + resources/mesh_tree_hypo_renumber.png | Bin 0 -> 250 bytes src/SMESH/SMESH_Mesh.cxx | 10 + src/SMESH/SMESH_Mesh.hxx | 4 + src/SMESH_I/SMESH_Gen_i.hxx | 8 +- src/SMESH_I/SMESH_Gen_i_1.cxx | 27 +- src/SMESH_I/SMESH_Mesh_i.cxx | 17 +- src/SMESH_SWIG/StdMeshersBuilder.py | 9 + src/SMESH_SWIG/smeshBuilder.py | 5 +- src/StdMeshers/CMakeLists.txt | 3 + src/StdMeshers/StdMeshers_BlockRenumber.cxx | 324 +++++++++++ src/StdMeshers/StdMeshers_BlockRenumber.hxx | 170 ++++++ .../StdMeshers_CompositeHexa_3D.cxx | 251 ++++++++- .../StdMeshers_CompositeHexa_3D.hxx | 9 +- src/StdMeshers/StdMeshers_Hexa_3D.cxx | 359 ++++++++++-- src/StdMeshers/StdMeshers_Hexa_3D.hxx | 2 + src/StdMeshersGUI/CMakeLists.txt | 2 + src/StdMeshersGUI/StdMeshersGUI.cxx | 7 +- .../StdMeshersGUI_BlockRenumberCreator.cxx | 515 ++++++++++++++++++ .../StdMeshersGUI_BlockRenumberCreator.h | 89 +++ src/StdMeshersGUI/StdMeshers_images.ts | 8 + src/StdMeshersGUI/StdMeshers_msg_en.ts | 59 ++ src/StdMeshers_I/CMakeLists.txt | 2 + .../StdMeshers_BlockRenumber_i.cxx | 189 +++++++ .../StdMeshers_BlockRenumber_i.hxx | 76 +++ src/StdMeshers_I/StdMeshers_i.cxx | 3 + 34 files changed, 2160 insertions(+), 101 deletions(-) create mode 100644 doc/salome/gui/SMESH/images/block_renumber_hyp.png create mode 100644 doc/salome/gui/SMESH/input/3d_meshing_hypo.rst delete mode 100644 doc/salome/gui/SMESH/input/max_element_volume_hypo.rst create mode 100644 resources/mesh_tree_hypo_renumber.png create mode 100644 src/StdMeshers/StdMeshers_BlockRenumber.cxx create mode 100644 src/StdMeshers/StdMeshers_BlockRenumber.hxx create mode 100644 src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx create mode 100644 src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.h create mode 100644 src/StdMeshers_I/StdMeshers_BlockRenumber_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_BlockRenumber_i.hxx diff --git a/doc/salome/examples/filters_ex39.py b/doc/salome/examples/filters_ex39.py index 2d3c907d6..6a2ddd507 100644 --- a/doc/salome/examples/filters_ex39.py +++ b/doc/salome/examples/filters_ex39.py @@ -1,4 +1,4 @@ -# Elements of a domain +# "Elements of a domain" filter and "Renumber" hypothesis import salome, SMESH salome.salome_init() @@ -22,7 +22,15 @@ mesh = smesh.Mesh( boxes ) mesh.Segment(box1).NumberOfSegments( 5 ) # to have different nb of elements on the boxes mesh.Segment(box2).NumberOfSegments( 10 ) mesh.Quadrangle() -mesh.Hexahedron() +ijkAlgo = mesh.Hexahedron() + +# Use Renumber hypothesis to get hexahedra and nodes numbered like in a structured mesh. +# k axis of box1 will be ( 100,100,0 ) - ( 100,100,100 ) +# k axis of box2 will be ( 0,0,0 ) - (0,0,100), by default +v000 = geompy.MakeVertex( 100,100,0, theName='v000' ) # can use box sub-vertex or standalone one +v001 = geompy.GetVertexNearPoint( box1, geompy.MakeVertex(100,100,100), theName='v001') +ijkAlgo.Renumber([ smeshBuilder.BlockCS( box1, v000, v001 ) ]) + mesh.Compute() # Create filters with FT_ConnectedElements criterion by pointing a domain in different ways: diff --git a/doc/salome/gui/SMESH/images/block_renumber_hyp.png b/doc/salome/gui/SMESH/images/block_renumber_hyp.png new file mode 100644 index 0000000000000000000000000000000000000000..4c610f7874cd11d00980ce14a670d20a89f16861 GIT binary patch literal 27071 zcmbTd1z1#H+c%1dA_ywdAtK$~jFQsQ9n#(1NVjx{AYCHe(%mvNNHYxG%~|9BdCq&T z^L+1DUU~^Ld-klo_FDJ-s~!AFS`-Zh4+RMc2~F&ykQ@@yeO@G_d!|nwgI90!JHu3zo0#x65kXjVG??B8gF1qojbj2nd}x~X8GQP zzV=JV{XTj|mW?xHrl$kHGT#K9ZPc%aaBZmWvgq1f@eXrEF7s>}ye9F(5C{^NL}uLV zR9zzs#1MEwB2>WR%9oV5w=gW_jPzVvigwr;9Q1@lJWV7eWh?O|QG3a;?K6=l;GWvy zMl7I}JJ>LsUAmZ~kn@t6{NTh@*0s*Rz?evJe6OdcZ0;f`jm2?0CAhv;k^2kr?XsR@ zWgVY7E1pkWmt4_vtm{V|cM~y}cIm9F%I%&!VoO{f04)9+q0oi#Kz!m)M4oiPU?6t5=|%f4oLi!kY$ zHTo+Jl1SLRk9@AE_?}I-NI$#veS~hHR=HsIgszd1=1)D{1ogdB&c8$BXH}!Cu7;A~ zlRKW~?j0VMYJZ)6k)Es}^g&ow!9IV=1hsZ*^^>6817xIcHOjVFYm(k=b)Q{myppY|E6Rywl(f%>4M({ zyI)e&H8E4RG@7y6TWKh`&Xw;c!KiB4@_QYI`4!|#Ulp<5E5Xua%8>1bY}jN@`&}bf z&A8jFPV=#QOKS_icH6d0G8@<6B|A9iCeBva`K&nIj8=3Y#NyOgG!y;xn-Q$snLWMU zM)_v3loe9(#UgH%+aE(<4!QW8zD7YtHumMjaKbB0`MV9R=e%`XF?+<0l=YtX5Tj+k zw)+5&RGQG!`$)pY_AKzXT`j(Rf$3iGm#A1X{p&*CP@-kCj{5_kKnymZGaGmU^w2@=o_8 zY1deiv)#UBm!5b6!|3Z`$#DJ`C%;Ie%fzne0MUicK_>DEG@c_E;4FJ}1Htel z-ubpPitC~k-7>bSaZJ#hjI>m8^eA}%c z(ooIpYgPMW*Kat_P7^jx;j?Ug85uI~Mq8rr=i}2EJCiB)JTy6Y$eoI%+P6-pj4?wk z9JFj3J8V!${PGiNJrdOA%knicb%?%Fl$pz(WGp22PV%8p@O0lNPkf0dQ71I&WI~09 zs%UE4zL55N8|h~>d$z=7OoiqAsN3Vr8O|?RpR8HvrCtz6*R8m8v#OPJvhg;ET%?5W z9mdhW?+iG*|sMU2BX?@J{I!_is-WF+53LJL0fWfabE;2MaRdU=F{ zpB*v6n8{{kZ$8vb>^1OREatYCzv$#%rnoDw_R!r6D`Mi?y`{iuPyX-%LO!mGZYD

XwxUs*_q}X7)EJ)xUNJS_var=#2L8dZot72U68 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 KR4kP@5B*=qw4%cR literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/input/3d_meshing_hypo.rst b/doc/salome/gui/SMESH/input/3d_meshing_hypo.rst new file mode 100644 index 000000000..bef317c09 --- /dev/null +++ b/doc/salome/gui/SMESH/input/3d_meshing_hypo.rst @@ -0,0 +1,42 @@ +.. _a3d_meshing_hypo_page: + +********************* +3D Meshing Hypotheses +********************* + +.. _max_element_volume_hypo: + +Max Element Volume hypothesis +############################# + +**Max Element Volume** hypothesis is applied for meshing of 3D objects composing your geometrical object. Definition of this hypothesis consists of setting the **maximum volume** of 3D meshing elements (depending on the chosen meshing algorithm it can be **hexahedrons** or **tetrahedrons**), which will compose the mesh of these 3D objects. + +.. image:: ../images/a-maxelvolume.png + :align: center + +**See Also** a sample TUI Script of a :ref:`tui_max_element_volume` operation. + +.. _block_renumber_hypo: + +Renumber hypothesis +################### + +**Renumber** hypothesis is applied for meshing of 3D objects along with **Hexahedron (i,j,k)** algorithm. + +Order and hence numbering of hexahedral elements in the structured mesh generated by **Hexahedron (ijk)** algorithm depends on the order of faces in a block geometry. Nodes are not in a structured order, since geometrical edges and faces are discretized before construction of hexahedra. **Renumber** hypothesis allows for getting hexahedra and nodes ordered like in a structured grid. + +.. image:: ../images/block_renumber_hyp.png + :align: center + +**Renumber** hypothesis allows to define direction of **i, j** and **k** axes of a block. For that you specify two vertices of the block + +* **Vertex (0,0,0)** located at the origin of the block local coordinate system. +* **Vertex (0,0,1)** located at the end of **k** axis of the block. + +Axes **i** and **j** are found automatically using the right-hand rule. + +For blocks with edges parallel to the axes of global coordinate system, their local coordinate system by default is defined as having axes parallel to the global ones. So, for such blocks specification of vertices is optional, it is enough just to assign **Renumber** hypothesis. + +**See Also** a sample TUI Script of a :ref:`tui_block_renumber` operation. + + diff --git a/doc/salome/gui/SMESH/input/about_hypo.rst b/doc/salome/gui/SMESH/input/about_hypo.rst index df935f3d7..a4609d905 100644 --- a/doc/salome/gui/SMESH/input/about_hypo.rst +++ b/doc/salome/gui/SMESH/input/about_hypo.rst @@ -16,7 +16,7 @@ In **MESH** there are: * :ref:`a1d_meshing_hypo_page` for meshing of **edges** * :ref:`a2d_meshing_hypo_page` for meshing of **faces** -* :ref:`3D Hypothesis ` for meshing of **volumes** +* :ref:`a3d_meshing_hypo_page` for meshing of **volumes** * :ref:`additional_hypo_page` **Table of Contents** @@ -26,6 +26,6 @@ In **MESH** there are: 1d_meshing_hypo.rst 2d_meshing_hypo.rst - 3D: Max Element Volume hypothesis + 3d_meshing_hypo.rst additional_hypo.rst diff --git a/doc/salome/gui/SMESH/input/max_element_volume_hypo.rst b/doc/salome/gui/SMESH/input/max_element_volume_hypo.rst deleted file mode 100644 index 5f835fc6b..000000000 --- a/doc/salome/gui/SMESH/input/max_element_volume_hypo.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _max_element_volume_hypo_page: - -***************************** -Max Element Volume hypothesis -***************************** - -**Max Element Volume** hypothesis is applied for meshing of 3D objects composing your geometrical object. Definition of this hypothesis consists of setting the **maximum volume** of 3D meshing elements (depending on the chosen meshing algorithm it can be **hexahedrons** or **tetrahedrons**), which will compose the mesh of these 3D objects. - -.. image:: ../images/a-maxelvolume.png - :align: center - -**See Also** a sample TUI Script of a :ref:`tui_max_element_volume` operation. - - diff --git a/doc/salome/gui/SMESH/input/tui_defining_hypotheses.rst b/doc/salome/gui/SMESH/input/tui_defining_hypotheses.rst index facc0fdc1..fd676a358 100644 --- a/doc/salome/gui/SMESH/input/tui_defining_hypotheses.rst +++ b/doc/salome/gui/SMESH/input/tui_defining_hypotheses.rst @@ -34,6 +34,10 @@ and hypotheses. * :ref:`tui_max_element_volume` hypothesis * :ref:`Viscous layers ` hypotheses +* Hexahedron (i,j,k) 3D algorithm + + * :ref:`tui_block_renumber` + * :ref:`tui_projection` * :ref:`Extrusion 3D ` algorithm * :ref:`Radial Prism ` algorithm @@ -136,6 +140,16 @@ Length from Edges :download:`Download this script <../../../examples/defining_hypotheses_ex07.py>` +.. _tui_block_renumber: + +Renumber 3D hypothesis +====================== + +.. literalinclude:: ../../../examples/filters_ex39.py + :language: python + +:download:`Download this script <../../../examples/filters_ex39.py>` + Defining Additional Hypotheses ############################## diff --git a/idl/SMESH_BasicHypothesis.idl b/idl/SMESH_BasicHypothesis.idl index 0f7edc5f6..a6b6882cc 100644 --- a/idl/SMESH_BasicHypothesis.idl +++ b/idl/SMESH_BasicHypothesis.idl @@ -1089,6 +1089,25 @@ module StdMeshers raises (SALOME::SALOME_Exception); }; + + /*! + * interface of "Renumber" hypothesis used by Hexahedron(ijk) algorithm + * to renumber mesh of a block to be structured-like + */ + struct BlockCS // Local coordinate system of a block + { + GEOM::GEOM_Object solid; + GEOM::GEOM_Object vertex000; + GEOM::GEOM_Object vertex001; + }; + typedef sequence blockcs_array; + + interface StdMeshers_BlockRenumber : SMESH::SMESH_Hypothesis + { + void SetBlocksOrientation( in blockcs_array blockCS ); + blockcs_array GetBlocksOrientation(); + }; + /*! * StdMeshers_SegmentAroundVertex_0D: interface of "SegmentAroundVertex" algorithm */ diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index fe0d107fb..1a5619a10 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -203,6 +203,7 @@ SET(SMESH_RESOURCES_FILES mesh_tree_hypo_projection_2d.png mesh_tree_hypo_projection_3d.png mesh_tree_hypo_quadratic.png + mesh_tree_hypo_renumber.png mesh_tree_hypo_segment.png mesh_tree_hypo_source_3d_shape.png mesh_tree_hypo_source_edge.png diff --git a/resources/StdMeshers.xml.in b/resources/StdMeshers.xml.in index d70cdd45e..8baf92e0a 100644 --- a/resources/StdMeshers.xml.in +++ b/resources/StdMeshers.xml.in @@ -226,6 +226,11 @@ icon-id ="mesh_hypo_length.png" dim ="3"/> + + @@ -390,11 +395,13 @@ input ="QUAD" output ="HEXA,PENTA" need-geom="false" + hypos ="BlockRenumber" opt-hypos="ViscousLayers" dim ="3"> Hexa_3D=Hexahedron(algo=smeshBuilder.Hexa) ViscousLayers=ViscousLayers(SetTotalThickness(),SetNumberLayers(),SetStretchFactor(),SetFaces(1),SetFaces(2),SetMethod(),SetGroupName()) + BlockRenumber=Renumber(GetBlocksOrientation()) diff --git a/resources/mesh_tree_hypo_renumber.png b/resources/mesh_tree_hypo_renumber.png new file mode 100644 index 0000000000000000000000000000000000000000..0d2714f9043afd5532f92db8d29f0ac1696d622e GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xawj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&pIwi-TW;an6V27NAhOr;B5V#`&$2cJnqE2sqzYRB<{l zvBqg~X+(?WjCR`$4^b|GT{8Ov5;VPZj;7B|y&=%Zz|-Y!bdhQEqy9rdd#w|;$TxjH z*^nja`dMy9!?TkY`7TDC6dS9lJ)uGCu+-&mYw&16rXT-qpIbhox<_c pmYm2u8SQybX7VJDWga~1!#}qf9$!ShapeToMesh() ); } +//======================================================================= +//function : GetShapeByEntry +//purpose : return TopoDS_Shape by its study entry +//======================================================================= + +TopoDS_Shape SMESH_Mesh::GetShapeByEntry(const std::string& entry) const +{ + return _callUp ? _callUp->GetShapeByEntry( entry ) : TopoDS_Shape(); +} + //============================================================================= /*! * diff --git a/src/SMESH/SMESH_Mesh.hxx b/src/SMESH/SMESH_Mesh.hxx index 27b54ed47..18c11c894 100644 --- a/src/SMESH/SMESH_Mesh.hxx +++ b/src/SMESH/SMESH_Mesh.hxx @@ -214,6 +214,9 @@ class SMESH_EXPORT SMESH_Mesh bool IsNotConformAllowed() const; bool IsMainShape(const TopoDS_Shape& theShape) const; + + TopoDS_Shape GetShapeByEntry(const std::string& entry) const; + /*! * \brief Return list of ancestors of theSubShape in the order * that lower dimension shapes come first @@ -342,6 +345,7 @@ class SMESH_EXPORT SMESH_Mesh virtual void HypothesisModified( int hypID, bool updateIcons )=0; virtual void Load()=0; virtual bool IsLoaded()=0; + virtual TopoDS_Shape GetShapeByEntry(const std::string& entry)=0; virtual ~TCallUp() {} }; void SetCallUp( TCallUp * upCaller ); diff --git a/src/SMESH_I/SMESH_Gen_i.hxx b/src/SMESH_I/SMESH_Gen_i.hxx index d6cb5b130..3c2e62f5d 100644 --- a/src/SMESH_I/SMESH_Gen_i.hxx +++ b/src/SMESH_I/SMESH_Gen_i.hxx @@ -116,13 +116,15 @@ public: // Get CORBA object corresponding to the SALOMEDS::SObject static CORBA::Object_var SObjectToObject( SALOMEDS::SObject_ptr theSObject ); // Get the SALOMEDS::SObject corresponding to a CORBA object - static SALOMEDS::SObject_ptr ObjectToSObject(CORBA::Object_ptr theObject); + static SALOMEDS::SObject_ptr ObjectToSObject( CORBA::Object_ptr theObject ); // Get the SALOMEDS::Study from naming service static SALOMEDS::Study_var getStudyServant(); // Get GEOM Object corresponding to TopoDS_Shape - GEOM::GEOM_Object_ptr ShapeToGeomObject (const TopoDS_Shape& theShape ); + static GEOM::GEOM_Object_ptr ShapeToGeomObject( const TopoDS_Shape& theShape ); // Get TopoDS_Shape corresponding to GEOM_Object - TopoDS_Shape GeomObjectToShape(GEOM::GEOM_Object_ptr theGeomObject); + static TopoDS_Shape GeomObjectToShape( GEOM::GEOM_Object_ptr theGeomObject ); + // Get GEOM Object by its study entry + static GEOM::GEOM_Object_ptr GetGeomObjectByEntry( const std::string& entry ); // Default constructor SMESH_Gen_i(); diff --git a/src/SMESH_I/SMESH_Gen_i_1.cxx b/src/SMESH_I/SMESH_Gen_i_1.cxx index 4c78e2316..d7d61bcaf 100644 --- a/src/SMESH_I/SMESH_Gen_i_1.cxx +++ b/src/SMESH_I/SMESH_Gen_i_1.cxx @@ -236,8 +236,8 @@ template static inline T* objectToServant( CORBA::Object_ptr theIOR GEOM::GEOM_Object_ptr SMESH_Gen_i::ShapeToGeomObject (const TopoDS_Shape& theShape ) { GEOM::GEOM_Object_var aShapeObj; - if ( !theShape.IsNull() ) { - GEOM_Client* aClient = GetShapeReader(); + if ( !theShape.IsNull() && mySMESHGen ) { + GEOM_Client* aClient = mySMESHGen->GetShapeReader(); TCollection_AsciiString IOR; if ( aClient && aClient->Find( theShape, IOR )) { @@ -257,9 +257,9 @@ GEOM::GEOM_Object_ptr SMESH_Gen_i::ShapeToGeomObject (const TopoDS_Shape& theSha TopoDS_Shape SMESH_Gen_i::GeomObjectToShape(GEOM::GEOM_Object_ptr theGeomObject) { TopoDS_Shape S; - if ( !theGeomObject->_is_nil() && !theGeomObject->_non_existent() ) + if ( mySMESHGen && !theGeomObject->_is_nil() && !theGeomObject->_non_existent() ) { - GEOM_Client* aClient = GetShapeReader(); + GEOM_Client* aClient = mySMESHGen->GetShapeReader(); GEOM::GEOM_Gen_var aGeomEngine = GetGeomEngine( theGeomObject ); if ( aClient && !aGeomEngine->_is_nil () ) S = aClient->GetShape( aGeomEngine, theGeomObject ); @@ -267,6 +267,25 @@ TopoDS_Shape SMESH_Gen_i::GeomObjectToShape(GEOM::GEOM_Object_ptr theGeomObject) return S; } +//================================================================================ +/*! + * \brief Get GEOM Object by its study entry + */ +//================================================================================ + +GEOM::GEOM_Object_ptr SMESH_Gen_i::GetGeomObjectByEntry( const std::string& entry ) +{ + GEOM::GEOM_Object_var go; + if ( !entry.empty() && mySMESHGen ) + { + SALOMEDS::SObject_wrap so = mySMESHGen->getStudyServant()->FindObjectID( entry.c_str() ); + CORBA::Object_var obj = SObjectToObject( so ); + go = GEOM::GEOM_Object::_narrow( obj ); + } + return go._retn(); +} + + //======================================================================= //function : publish //purpose : diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index 2e9130d67..8a4313de5 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -3435,12 +3435,17 @@ namespace { SMESH_Mesh_i* _mesh; TCallUp_i(SMESH_Mesh_i* mesh):_mesh(mesh) {} - virtual void RemoveGroup (const int theGroupID) { _mesh->removeGroup( theGroupID ); } - virtual void HypothesisModified( int hypID, - bool updIcons) { _mesh->onHypothesisModified( hypID, - updIcons ); } - virtual void Load () { _mesh->Load(); } - virtual bool IsLoaded() { return _mesh->IsLoaded(); } + void RemoveGroup (const int theGroupID) override { _mesh->removeGroup( theGroupID ); } + void HypothesisModified( int hypID, + bool updIcons) override { _mesh->onHypothesisModified( hypID, + updIcons ); } + void Load () override { _mesh->Load(); } + bool IsLoaded() override { return _mesh->IsLoaded(); } + TopoDS_Shape GetShapeByEntry(const std::string& entry) override + { + GEOM::GEOM_Object_var go = SMESH_Gen_i::GetGeomObjectByEntry( entry ); + return SMESH_Gen_i::GeomObjectToShape( go ); + } }; } diff --git a/src/SMESH_SWIG/StdMeshersBuilder.py b/src/SMESH_SWIG/StdMeshersBuilder.py index 14bf30414..0759332e9 100644 --- a/src/SMESH_SWIG/StdMeshersBuilder.py +++ b/src/SMESH_SWIG/StdMeshersBuilder.py @@ -916,8 +916,17 @@ class StdMeshersBuilder_Hexahedron(Mesh_Algorithm): """ Mesh_Algorithm.__init__(self) self.Create(mesh, geom, Hexa) + self.renumHypothesis = 0 pass + def Renumber(self, blockCSList=[] ): + if isinstance( blockCSList, StdMeshers.BlockCS ): + blockCSList = [blockCSList] + if not self.renumHypothesis: + self.renumHypothesis = self.Hypothesis("BlockRenumber", blockCSList, UseExisting=0) + self.renumHypothesis.SetBlocksOrientation( blockCSList ) + return self.renumHypothesis + pass # end of StdMeshersBuilder_Hexahedron class class StdMeshersBuilder_Projection1D(Mesh_Algorithm): diff --git a/src/SMESH_SWIG/smeshBuilder.py b/src/SMESH_SWIG/smeshBuilder.py index 21456eed4..b40bc6d1e 100644 --- a/src/SMESH_SWIG/smeshBuilder.py +++ b/src/SMESH_SWIG/smeshBuilder.py @@ -38,8 +38,9 @@ SMESH.MED_MINOR_7 = 27 # back compatibility SMESH.MED_MINOR_8 = 28 # back compatibility SMESH.MED_MINOR_9 = 29 # back compatibility -from SMESH import * -from salome.smesh.smesh_algorithm import Mesh_Algorithm +from SMESH import * +from salome.smesh.smesh_algorithm import Mesh_Algorithm +from StdMeshers import BlockCS import SALOME import SALOMEDS diff --git a/src/StdMeshers/CMakeLists.txt b/src/StdMeshers/CMakeLists.txt index 87e4db8eb..3f7dc1af7 100644 --- a/src/StdMeshers/CMakeLists.txt +++ b/src/StdMeshers/CMakeLists.txt @@ -64,6 +64,7 @@ SET(_link_LIBRARIES SMESHDS SMESHUtils ${TBB_LIBS} + ${Boost_LIBRARIES} ) IF(SALOME_SMESH_ENABLE_MEFISTO) @@ -131,6 +132,7 @@ SET(StdMeshers_HEADERS StdMeshers_QuadFromMedialAxis_1D2D.hxx StdMeshers_PolygonPerFace_2D.hxx StdMeshers_PolyhedronPerSolid_3D.hxx + StdMeshers_BlockRenumber.hxx ) IF(SALOME_SMESH_ENABLE_MEFISTO) @@ -197,6 +199,7 @@ SET(StdMeshers_SOURCES StdMeshers_QuadFromMedialAxis_1D2D.cxx StdMeshers_PolygonPerFace_2D.cxx StdMeshers_PolyhedronPerSolid_3D.cxx + StdMeshers_BlockRenumber.cxx ) IF(SALOME_SMESH_ENABLE_MEFISTO) diff --git a/src/StdMeshers/StdMeshers_BlockRenumber.cxx b/src/StdMeshers/StdMeshers_BlockRenumber.cxx new file mode 100644 index 000000000..bc2e63a43 --- /dev/null +++ b/src/StdMeshers/StdMeshers_BlockRenumber.cxx @@ -0,0 +1,324 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// File : StdMeshers_BlockRenumber.cxx +// Author : Edward AGAPOV, OCC +// Module : SMESH + +#include "StdMeshers_BlockRenumber.hxx" + +#include "SMDS_EdgePosition.hxx" +#include "SMDS_FacePosition.hxx" +#include "SMESHDS_Mesh.hxx" +#include "SMESHDS_SubMesh.hxx" +#include "SMESH_Algo.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_TryCatch.hxx" + +#include +#include +#include +#include + +#include +#include + +//============================================================================= +/*! + * Constructor + */ +//============================================================================= + +StdMeshers_BlockRenumber::StdMeshers_BlockRenumber(int hypId, SMESH_Gen * gen) + :SMESH_Hypothesis(hypId, gen) +{ + _name = "BlockRenumber"; + _param_algo_dim = 3; // is used by StdMeshers_Hexa_3D and StdMeshers_CompositeHexa_3D +} + +//================================================================================ +/*! + * \brief Set local CS of blocks + */ +//================================================================================ + +void StdMeshers_BlockRenumber::SetBlocksOrientation( std::vector< StdMeshers_BlockCS > & blockCS ) +{ + if ( _blockCS != blockCS ) + { + NotifySubMeshesHypothesisModification(); + _blockCS.swap( blockCS ); + _solids2vertices.Clear(); + } +} + +//================================================================================ +/* + * Return true and vertices if block orientation is defined for a given solid + */ +//================================================================================ + +bool StdMeshers_BlockRenumber::IsSolidIncluded( SMESH_Mesh& mesh, + const TopoDS_Shape& solid, + TopoDS_Vertex& vertex000, + TopoDS_Vertex& vertex001 ) const +{ + bool result = false; + vertex000.Nullify(); + vertex001.Nullify(); + + if ( _solids2vertices.IsEmpty() ) + { + StdMeshers_BlockRenumber* me = const_cast(this); + for ( StdMeshers_BlockCS& bcs : me->_blockCS ) + { + TopoDS_Shape so = mesh.GetShapeByEntry( bcs._solid ); + TopoDS_Shape s000 = mesh.GetShapeByEntry( bcs._vertex000 ); + TopoDS_Shape s001 = mesh.GetShapeByEntry( bcs._vertex001 ); + TopoDS_Vertex v000 = StdMeshers_RenumberHelper::GetVertexAtPoint( so, s000 ); + TopoDS_Vertex v001 = StdMeshers_RenumberHelper::GetVertexAtPoint( so, s001 ); + if ( !v000.IsNull() && !v001.IsNull() ) + { + me->_solids2vertices.Bind( so, std::make_pair( v000, v001 )); + if ( so.IsSame( solid )) + { + result = true; + vertex000 = v000; + vertex001 = v001; + } + } + } + } + else if ( !solid.IsNull() ) + { + if (( result = _solids2vertices.IsBound( solid ))) + { + auto vvPairPtr = _solids2vertices.Seek( solid ); + vertex000 = vvPairPtr->first; + vertex001 = vvPairPtr->second; + } + } + return result; +} + +//======================================================================= +//function : CheckHypothesis +//purpose : +//======================================================================= + +SMESH_ComputeErrorPtr StdMeshers_BlockRenumber::CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape) const +{ + SMESH_Comment errorTxt; + for ( size_t i = 0; i < _blockCS.size() && errorTxt.empty(); ++i ) + { + TopoDS_Shape solid = aMesh.GetShapeByEntry( _blockCS[i]._solid ); + TopoDS_Shape v000 = aMesh.GetShapeByEntry( _blockCS[i]._vertex000 ); + TopoDS_Shape v001 = aMesh.GetShapeByEntry( _blockCS[i]._vertex001 ); + v000 = StdMeshers_RenumberHelper::GetVertexAtPoint( solid, v000 ); + v001 = StdMeshers_RenumberHelper::GetVertexAtPoint( solid, v001 ); + + if ( solid.IsNull() || solid.ShapeType() != TopAbs_SOLID ) + errorTxt << "Can't find a SOLID by entry '" << _blockCS[i]._solid << "'"; + else if ( v000.IsNull() || v000.ShapeType() != TopAbs_VERTEX ) + errorTxt << "Can't find a VERTEX by entry '" << _blockCS[i]._vertex000 << "'"; + else if ( v001.IsNull() || v001.ShapeType() != TopAbs_VERTEX ) + errorTxt << "Can't find a VERTEX by entry '" << _blockCS[i]._vertex001 << "'"; + else if ( !SMESH_MesherHelper::IsSubShape( v000, solid )) + errorTxt << "VERTEX '" << _blockCS[i]._vertex001 << "' does not belong to SOLID '" + << _blockCS[i]._solid << "'"; + else if ( !SMESH_MesherHelper::IsSubShape( v001, solid )) + errorTxt << "VERTEX '" << _blockCS[i]._vertex001 << "' does not belong to SOLID '" + << _blockCS[i]._solid << "'"; + else if ( SMESH_MesherHelper::Count( solid, TopAbs_VERTEX, true ) == 8 && + SMESH_MesherHelper::GetCommonAncestor( v000, v001, aMesh, TopAbs_EDGE ).IsNull() ) + errorTxt << "Vertices '" << _blockCS[i]._vertex000 << "' and '" << _blockCS[i]._vertex001 + << "' are not connected by an edge"; + } + + SMESH_ComputeErrorPtr error; + if ( !errorTxt.empty() ) + { + error = SMESH_ComputeError::New( COMPERR_BAD_PARMETERS, + SMESH_Comment("Renumber hypothesis: ") << errorTxt ); + } + return error; +} + +//======================================================================= +//function : StdMeshers_RenumberHelper +//purpose : constructor +//======================================================================= + +StdMeshers_RenumberHelper::StdMeshers_RenumberHelper( SMESH_Mesh& mesh, + const StdMeshers_BlockRenumber* hyp) + : _mesh( &mesh ), _hyp( hyp ), _newOldNodes( 2, nullptr ) +{ +} + +//======================================================================= +//function : GetVertex000 +//purpose : Find default vertex at (0,0,0) local position +//======================================================================= + +TopoDS_Vertex StdMeshers_RenumberHelper::GetVertex000( const TopTools_MapOfShape& cornerVertices ) +{ + TopoDS_Vertex v000; + if ( cornerVertices.Extent() < 8 ) + return TopoDS_Vertex(); + + double minVal = DBL_MAX, minX = DBL_MAX, val; + for ( auto it = cornerVertices.cbegin(); it != cornerVertices.cend(); ++it ) + { + gp_Pnt P = BRep_Tool::Pnt( TopoDS::Vertex( *it )); + val = P.X() + P.Y() + P.Z(); + if ( val < minVal || ( val == minVal && P.X() < minX )) + { + v000 = TopoDS::Vertex( *it ); + minVal = val; + minX = P.X(); + } + } + return v000; +} + +//======================================================================= +//function : GetVertex000 +//purpose : Find default vertex at (0,0,0) local position +//======================================================================= + +TopoDS_Vertex StdMeshers_RenumberHelper::GetVertexAtPoint( const TopoDS_Shape& solid, + const TopoDS_Shape& point ) +{ + if ( !solid.IsNull() && !point.IsNull() && point.ShapeType() == TopAbs_VERTEX ) + { + gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( point )); + double tol = Precision::Confusion(); + for ( TopExp_Explorer exp( solid, TopAbs_VERTEX ); exp.More(); exp.Next() ) + { + const TopoDS_Vertex& v = TopoDS::Vertex( exp.Current() ); + if ( v.IsSame( point ) || p.IsEqual( BRep_Tool::Pnt( v ), tol )) + return v; + } + } + return TopoDS_Vertex(); +} + +//================================================================================ +/* + * Create a copy of an old node and remember this couple of nodes for replacement + */ +//================================================================================ + +void StdMeshers_RenumberHelper::AddReplacingNode( const SMDS_MeshNode* & oldNode ) +{ + SMESHDS_Mesh* mesh = _mesh->GetMeshDS(); + SMESH_NodeXYZ oldXYZ = oldNode; + SMDS_MeshNode* newNode = mesh->AddNode( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() ); + _newOldNodes.front() = newNode; + _newOldNodes.back() = oldNode; + _nodesToMerge.push_back( _newOldNodes ); + oldNode = newNode; + + int shapeID = oldXYZ->GetShapeID(); + const TopoDS_Shape& shape = mesh->IndexToShape( shapeID ); + if ( !shape.IsNull() ) + switch ( shape.ShapeType() ) + { + case TopAbs_FACE: + if ( SMDS_FacePositionPtr pos = oldXYZ->GetPosition() ) + mesh->SetNodeOnFace( newNode, shapeID, pos->GetUParameter(), pos->GetVParameter() ); + break; + case TopAbs_EDGE: + if ( SMDS_EdgePositionPtr pos = oldXYZ->GetPosition() ) + mesh->SetNodeOnEdge( newNode, shapeID, pos->GetUParameter() ); + break; + case TopAbs_VERTEX: + mesh->SetNodeOnVertex( newNode, shapeID ); + break; + default: + mesh->SetNodeInVolume( newNode, shapeID ); + } +} + +//================================================================================ +/* + * Replace old nodes by new ones + */ +//================================================================================ + +void StdMeshers_RenumberHelper::DoReplaceNodes() +{ + SMESH_MeshEditor( _mesh ).MergeNodes( _nodesToMerge ); +} + +//============================================================================= +/*! + * Persistence + */ +//============================================================================= + +ostream & StdMeshers_BlockRenumber::SaveTo(ostream & save) +{ + boost::archive::text_oarchive archive( save ); + archive << *this; + + return save; +} + +//============================================================================= +/*! + * Persistence + */ +//============================================================================= + +istream & StdMeshers_BlockRenumber::LoadFrom(istream & load) +{ + SMESH_TRY; + + boost::archive::text_iarchive archive( load ); + archive >> *this; + + SMESH_CATCH( SMESH::doNothing ); + + return load; +} + +namespace boost { + namespace serialization { + + //======================================================================= + //function : serialize + //purpose : serialize StdMeshers_BlockCS + //======================================================================= + + template + void serialize(Archive & ar, StdMeshers_BlockCS & blockCS, const unsigned int version) + { + ar & blockCS._solid; + ar & blockCS._vertex000; + ar & blockCS._vertex001; + } + + } // namespace serialization +} // namespace boost diff --git a/src/StdMeshers/StdMeshers_BlockRenumber.hxx b/src/StdMeshers/StdMeshers_BlockRenumber.hxx new file mode 100644 index 000000000..f68b17ecd --- /dev/null +++ b/src/StdMeshers/StdMeshers_BlockRenumber.hxx @@ -0,0 +1,170 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// SMESH SMESH : implementation of SMESH idl descriptions +// File : StdMeshers_BlockRenumber.hxx +// Author : Edward AGAPOV, OCC +// Module : SMESH +// +#ifndef _SMESH_BlockRenumber_HXX_ +#define _SMESH_BlockRenumber_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_ComputeError.hxx" +#include "SMESH_Hypothesis.hxx" +#include "SMESH_MeshEditor.hxx" + +#include +#include +#include + +#include +#include + +class SMESH_Mesh; +class TopoDS_Shape; +class TopoDS_TShape; +class TopoDS_Vertex; + +// ========================================================= +struct StdMeshers_BlockCS // Local coordinate system of a block +{ + std::string _solid; + std::string _vertex000; + std::string _vertex001; + + bool operator==( const StdMeshers_BlockCS& other ) const + { + return ( _solid == other._solid && + _vertex000 == other._vertex000 && + _vertex001 == other._vertex001 ); + } +}; + +// ========================================================= +/*! + * \class 3D Hypothesis used by Hexahedron(ijk) algorithm + * to renumber mesh of a block to be structured-like + */ +// ========================================================= + +class STDMESHERS_EXPORT StdMeshers_BlockRenumber : public SMESH_Hypothesis +{ +public: + StdMeshers_BlockRenumber(int hypId, SMESH_Gen * gen); + + void SetBlocksOrientation( std::vector< StdMeshers_BlockCS > & blockCS ); + + const std::vector< StdMeshers_BlockCS > & GetBlocksOrientation() const { return _blockCS; } + + virtual std::ostream & SaveTo(std::ostream & save) override; + virtual std::istream & LoadFrom(std::istream & load) override; + + /*! + * \brief Initialize Fineness by the mesh built on the geometry + * \param theMesh - the built mesh + * \param theShape - the geometry of interest + * \retval bool - true if parameter values have been successfully defined + */ + bool SetParametersByMesh(const SMESH_Mesh* theMesh, const TopoDS_Shape& theShape) override + { return false; } + + /*! + * \brief Initialize my parameter values by default parameters. + * \retval bool - true if parameter values have been successfully defined + */ + bool SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh=0) override + { return false; } + + public: + + /*! + * \brief Check validity of parameter + */ + SMESH_ComputeErrorPtr CheckHypothesis(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape) const; + + /*! + * \brief Return true and vertices if block orientation is defined for a given solid + */ + bool IsSolidIncluded( SMESH_Mesh& mesh, + const TopoDS_Shape& solid, + TopoDS_Vertex& vertex000, + TopoDS_Vertex& vertex001 ) const; + + private: + + // Persistence: define both input and output at once + friend class boost::serialization::access; + template void serialize( Archive & ar, const unsigned int version ) + { + ar & _blockCS; + } + + protected: + + std::vector< StdMeshers_BlockCS > _blockCS; + + typedef NCollection_DataMap< TopoDS_Shape, std::pair< TopoDS_Vertex, TopoDS_Vertex > > TSolid2VV; + TSolid2VV _solids2vertices; // shapes defined by _blockCS, non-persistent +}; + +// ========================================================= +/*! + * \brief Help in using StdMeshers_BlockRenumber + */ +class StdMeshers_RenumberHelper +{ +public: + + StdMeshers_RenumberHelper( SMESH_Mesh& mesh, + const StdMeshers_BlockRenumber* hyp); + /*! + * \brief Find default vertex at (0,0,0) local position + */ + static TopoDS_Vertex GetVertex000( const TopTools_MapOfShape& cornerVertices ); + + /*! + * \brief Find a vertex of a solid located at the given point + */ + static TopoDS_Vertex GetVertexAtPoint( const TopoDS_Shape& solid, const TopoDS_Shape& point ); + + /*! + * \brief Create a copy of an old node and remember this couple of nodes for replacement + */ + void AddReplacingNode( const SMDS_MeshNode* & oldNode ); + + /*! + * \brief Replace old nodes by new ones + */ + void DoReplaceNodes(); + +private: + + SMESH_Mesh* _mesh; + const StdMeshers_BlockRenumber* _hyp; + + SMESH_MeshEditor::TListOfListOfNodes _nodesToMerge; + std::list< const SMDS_MeshNode* > _newOldNodes; +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx b/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx index 8f624c777..2e697303a 100644 --- a/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx @@ -38,6 +38,8 @@ #include "SMESH_MeshAlgos.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" +#include "StdMeshers_BlockRenumber.hxx" +#include "StdMeshers_FaceSide.hxx" #include "StdMeshers_ViscousLayers.hxx" #include @@ -61,6 +63,7 @@ #include #include #include +#include using namespace std; @@ -180,6 +183,8 @@ public: //** Methods to find and orient faces of 6 sides of the box **// TChildIterator GetChildren() const { return TChildIterator( myChildren.begin(), myChildren.end()); } + bool Contain( const TopoDS_Vertex& vertex ) const { return mySides.Contain( vertex ); } + public: //** Loading and access to mesh **// //!< Load nodes of a mesh @@ -271,7 +276,7 @@ private: //================================================================================ StdMeshers_CompositeHexa_3D::StdMeshers_CompositeHexa_3D(int hypId, SMESH_Gen* gen) - :SMESH_3D_Algo(hypId, gen) + :SMESH_3D_Algo(hypId, gen), _blockRenumberHyp( nullptr ) { _name = "CompositeHexa_3D"; _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit /shape type @@ -287,6 +292,7 @@ bool StdMeshers_CompositeHexa_3D::CheckHypothesis(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape, Hypothesis_Status& aStatus) { + _blockRenumberHyp = nullptr; aStatus = HYP_OK; return true; } @@ -545,6 +551,163 @@ namespace return faceFound; } + //================================================================================ + /*! + * \brief Rearrange block sides according to StdMeshers_BlockRenumber hypothesis + */ + //================================================================================ + + bool arrangeForRenumber( list< _QuadFaceGrid >& blockSides, + const TopTools_MapOfShape& cornerVertices, + SMESH_Mesh* mesh, + TopoDS_Vertex& v000, + TopoDS_Vertex& v001 ) + { + if ( v000.IsNull() ) + { + // block CS is not defined; + // renumber only if the block has an edge parallel to an axis of global CS + + v000 = StdMeshers_RenumberHelper::GetVertex000( cornerVertices ); + } + + Bnd_B3d bbox; + for ( auto it = cornerVertices.cbegin(); it != cornerVertices.cend(); ++it ) + bbox.Add( BRep_Tool::Pnt( TopoDS::Vertex( *it ))); + double tol = 1e-5 * Sqrt( bbox.SquareExtent() ); + + // get block edges starting at v000 + + std::vector< const _FaceSide* > edgesAtV000; + std::vector< gp_Vec > edgeDir; + std::vector< int > iParallel; // 0 - none, 1 - X, 2 - Y, 3 - Z + TopTools_MapOfShape lastVertices; + for ( _QuadFaceGrid & quad: blockSides ) + { + for ( int iS = 0; iS < 4 && edgesAtV000.size() < 3; ++iS ) + { + const _FaceSide* side = & quad.GetSide( iS ); + TopoDS_Vertex v1 = side->FirstVertex(), v2 = side->LastVertex(); + if (( v1.IsSame( v000 ) && !lastVertices.Contains( v2 )) || + ( v2.IsSame( v000 ) && !lastVertices.Contains( v1 ))) + { + bool reverse = v2.IsSame( v000 ); + if ( reverse ) + std::swap( v1, v2 ); + lastVertices.Add( v2 ); + + edgesAtV000.push_back( side ); + + gp_Pnt pf = BRep_Tool::Pnt( v1 ); + gp_Pnt pl = BRep_Tool::Pnt( v2 ); + gp_Vec vec( pf, pl ); + edgeDir.push_back( vec ); + + iParallel.push_back( 0 ); + if ( !v001.IsNull() ) + { + if ( quad.IsComplex() ) + for ( _QuadFaceGrid::TChildIterator chIt = quad.GetChildren(); chIt.more(); ) + { + const _QuadFaceGrid& child = chIt.next(); + if ( child.GetSide( iS ).Contain( v001 )) + { + iParallel.back() = 3; + break; + } + } + else if ( side->Contain( v001 )) + iParallel.back() = 3; + } + else + { + bool isStraight = true; + std::list< TopoDS_Edge > edges; + for ( int iE = 0; true; ++iE ) + { + TopoDS_Edge edge = side->Edge( iE ); + if ( edge.IsNull() ) + break; + edges.push_back( edge ); + if ( isStraight ) + isStraight = SMESH_Algo::IsStraight( edge ); + } + // is parallel to a GCS axis? + if ( isStraight ) + { + int nbDiff = (( Abs( vec.X() ) > tol ) + + ( Abs( vec.Y() ) > tol ) + + ( Abs( vec.Z() ) > tol ) ); + if ( nbDiff == 1 ) + iParallel.back() = ( Abs( vec.X() ) > tol ) ? 1 : ( Abs( vec.Y() ) > tol ) ? 2 : 3; + } + else + { + TopoDS_Face nullFace; + StdMeshers_FaceSide fSide( nullFace, edges, mesh, true, true ); + edgeDir.back() = gp_Vec( pf, fSide.Value3d( reverse ? 0.99 : 0.01 )); + } + } + } + } + } + if ( std::accumulate( iParallel.begin(), iParallel.end(), 0 ) == 0 ) + return false; + + // find edge OZ and edge OX + const _FaceSide* edgeOZ = nullptr, *edgeOY = nullptr, *edgeOX = nullptr; + auto iZIt = std::find( iParallel.begin(), iParallel.end(), 3 ); + if ( iZIt != iParallel.end() ) + { + int i = std::distance( iParallel.begin(), iZIt ); + edgeOZ = edgesAtV000[ i ]; + int iE1 = SMESH_MesherHelper::WrapIndex( i + 1, edgesAtV000.size() ); + int iE2 = SMESH_MesherHelper::WrapIndex( i + 2, edgesAtV000.size() ); + if (( edgeDir[ iE1 ] ^ edgeDir[ iE2 ] ) * edgeDir[ i ] < 0 ) + std::swap( iE1, iE2 ); + edgeOX = edgesAtV000[ iE1 ]; + edgeOY = edgesAtV000[ iE2 ]; + } + else + { + for ( size_t i = 0; i < edgesAtV000.size(); ++i ) + { + if ( !iParallel[ i ] ) + continue; + int iE1 = SMESH_MesherHelper::WrapIndex( i + 1, edgesAtV000.size() ); + int iE2 = SMESH_MesherHelper::WrapIndex( i + 2, edgesAtV000.size() ); + if (( edgeDir[ iE1 ] ^ edgeDir[ iE2 ] ) * edgeDir[ i ] < 0 ) + std::swap( iE1, iE2 ); + edgeOZ = edgesAtV000[ iParallel[i] == 1 ? iE2 : iE1 ]; + edgeOX = edgesAtV000[ iParallel[i] == 1 ? i : iE1 ]; + edgeOY = edgesAtV000[ iParallel[i] == 1 ? iE1 : i ]; + break; + } + } + + if ( !edgeOZ || !edgeOX || !edgeOY ) + return false; + + TopoDS_Vertex v100 = edgeOX->LastVertex(); + if ( v100.IsSame( v000 )) + v100 = edgeOX->FirstVertex(); + + // Find the left quad, one including v000 but not v100 + + for ( auto quad = blockSides.begin(); quad != blockSides.end(); ++quad ) + { + if ( quad->Contain( v000 ) && !quad->Contain( v100 )) // it's a left quad + { + if ( quad != blockSides.begin() ) + blockSides.splice( blockSides.begin(), blockSides, quad ); + blockSides.front().SetBottomSide( *edgeOZ ); // edgeOY + + return true; + } + } + return false; + } + } // namespace //================================================================================ @@ -557,6 +720,7 @@ bool StdMeshers_CompositeHexa_3D::findBoxFaces( const TopoDS_Shape& shape, list< _QuadFaceGrid >& boxFaces, SMESH_Mesh& mesh, SMESH_ProxyMesh& proxyMesh, + bool& toRenumber, _QuadFaceGrid * & fBottom, _QuadFaceGrid * & fTop, _QuadFaceGrid * & fFront, @@ -609,6 +773,22 @@ bool StdMeshers_CompositeHexa_3D::findBoxFaces( const TopoDS_Shape& shape, for ( exp.Init( shape, TopAbs_FACE); exp.More(); exp.Next(), ++boxFace ) boxFace->Init( TopoDS::Face( exp.Current() ), proxyMesh ); } + + toRenumber = _blockRenumberHyp; + if ( toRenumber ) + { + TopoDS_Vertex v000, v001; + _blockRenumberHyp->IsSolidIncluded( mesh, shape, v000, v001 ); + + toRenumber = arrangeForRenumber( boxFaces, cornerVertices, &mesh, v000, v001 ); + + if ( toRenumber ) + { + mesh.GetMeshDS()->Modified(); + mesh.GetMeshDS()->CompactMesh(); // remove numbering holes + } + } + // ---------------------------------------- // Find out position of faces within a box // ---------------------------------------- @@ -686,8 +866,9 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, // Try to find 6 side faces // ------------------------- list< _QuadFaceGrid > boxFaceContainer; + bool toRenumber = false; _QuadFaceGrid *fBottom, *fTop, *fFront, *fBack, *fLeft, *fRight; - if ( ! findBoxFaces( theShape, boxFaceContainer, theMesh, *proxyMesh, + if ( ! findBoxFaces( theShape, boxFaceContainer, theMesh, *proxyMesh, toRenumber, fBottom, fTop, fFront, fBack, fLeft, fRight)) return false; @@ -711,6 +892,8 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, fRight ->ComputeIJK( COO_Y, COO_Z, /*x=*/1. ); fTop ->ComputeIJK( COO_X, COO_Y, /*z=*/1. ); + StdMeshers_RenumberHelper renumHelper( theMesh, _blockRenumberHyp ); + int x, xSize = fBottom->GetNbHoriSegments(*proxyMesh) + 1, X = xSize - 1; int y, ySize = fBottom->GetNbVertSegments(*proxyMesh) + 1, Y = ySize - 1; int z, zSize = fFront ->GetNbVertSegments(*proxyMesh) + 1, Z = zSize - 1; @@ -768,8 +951,23 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, gp_XYZ params; // normalized parameters of an internal node within the unit box - for ( x = 1; x < xSize-1; ++x ) + if ( toRenumber ) + for ( y = 0; y < ySize; ++y ) + { + vector< const SMDS_MeshNode* >& columnXy = columns[ colIndex( X, y )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( columnXy[ z ] ); + } + + for ( x = X-1; x > 0; --x ) { + if ( toRenumber ) + { + vector< const SMDS_MeshNode* >& columnX0 = columns[ colIndex( x, 0 )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( columnX0[ z ] ); + } + const double rX = x / double(X); for ( y = 1; y < ySize-1; ++y ) { @@ -788,6 +986,10 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, // points projections on horizontal faces pointsOnShapes[ SMESH_Block::ID_Fxy0 ] = fBottom->GetXYZ( x, y ); pointsOnShapes[ SMESH_Block::ID_Fxy1 ] = fTop ->GetXYZ( x, y ); + + if ( toRenumber ) + renumHelper.AddReplacingNode( column[ 0 ] ); + for ( z = 1; z < zSize-1; ++z ) // z loop { // compute normalized parameters of an internal node within the unit box @@ -832,16 +1034,35 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, //cout << "Params: ( "<< params.X()<<", "<& columnXY = columns[ colIndex( x, Y )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( columnXY[ z ] ); } - } + } // for ( x = X-1; x > 0; --x ) + + if ( toRenumber ) + for ( y = 0; y < ySize; ++y ) + { + vector< const SMDS_MeshNode* >& column0Y = columns[ colIndex( 0, y )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( column0Y[ z ] ); + } + + // faces no more needed, free memory boxFaceContainer.clear(); // ---------------- // Add hexahedrons // ---------------- - for ( x = 0; x < xSize-1; ++x ) { + for ( x = xSize-2; true; --x ) { for ( y = 0; y < ySize-1; ++y ) { vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x, y )]; vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x+1, y )]; @@ -850,11 +1071,22 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, for ( z = 0; z < zSize-1; ++z ) { // bottom face normal of a hexa mush point outside the volume - helper.AddVolume(col00[z], col01[z], col11[z], col10[z], - col00[z+1], col01[z+1], col11[z+1], col10[z+1]); + helper.AddVolume(col10[z], col11[z], col11[z+1], col10[z+1], + col00[z], col01[z], col01[z+1], col00[z+1]); } } + if ( x == 0) + break; + } + if ( toRenumber ) + renumHelper.DoReplaceNodes(); + + if ( _blockRenumberHyp ) + { + return error( _blockRenumberHyp->CheckHypothesis( theMesh, theShape )); + } + return true; } @@ -875,7 +1107,8 @@ bool StdMeshers_CompositeHexa_3D::Evaluate(SMESH_Mesh& theMesh, // ------------------------- list< _QuadFaceGrid > boxFaceContainer; _QuadFaceGrid *fBottom, *fTop, *fFront, *fBack, *fLeft, *fRight; - if ( ! findBoxFaces( theShape, boxFaceContainer, theMesh, *proxyMesh, + bool toRenumber = false; + if ( ! findBoxFaces( theShape, boxFaceContainer, theMesh, *proxyMesh, toRenumber, fBottom, fTop, fFront, fBack, fLeft, fRight)) return false; diff --git a/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx b/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx index 695624fe5..9a17e2b81 100644 --- a/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx +++ b/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx @@ -29,6 +29,7 @@ class SMESH_Mesh; class SMESH_ProxyMesh; +class StdMeshers_BlockRenumber; class StdMeshers_FaceSide; class TopoDS_Edge; class TopoDS_Face; @@ -44,7 +45,8 @@ class STDMESHERS_EXPORT StdMeshers_CompositeHexa_3D: public SMESH_3D_Algo { public: StdMeshers_CompositeHexa_3D(int hypId, SMESH_Gen* gen); - //virtual ~StdMeshers_CompositeHexa_3D(); + + void SetHypothesis( const StdMeshers_BlockRenumber* hyp ) { _blockRenumberHyp = hyp; } virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); @@ -56,18 +58,21 @@ public: const TopoDS_Shape& aShape, Hypothesis_Status& aStatus); -private: + private: bool findBoxFaces( const TopoDS_Shape& shape, std::list< _QuadFaceGrid >& boxFaceContainer, SMESH_Mesh& mesh, SMESH_ProxyMesh& proxyMesh, + bool& toRenumber, _QuadFaceGrid * & fBottom, _QuadFaceGrid * & fTop, _QuadFaceGrid * & fFront, _QuadFaceGrid * & fBack, _QuadFaceGrid * & fLeft, _QuadFaceGrid * & fRight); + + const StdMeshers_BlockRenumber* _blockRenumberHyp; }; #endif diff --git a/src/StdMeshers/StdMeshers_Hexa_3D.cxx b/src/StdMeshers/StdMeshers_Hexa_3D.cxx index 53877e2ab..26b22cae1 100644 --- a/src/StdMeshers/StdMeshers_Hexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_Hexa_3D.cxx @@ -28,6 +28,13 @@ // #include "StdMeshers_Hexa_3D.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMESH_Comment.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_subMesh.hxx" +#include "StdMeshers_BlockRenumber.hxx" #include "StdMeshers_CompositeHexa_3D.hxx" #include "StdMeshers_FaceSide.hxx" #include "StdMeshers_HexaFromSkin_3D.hxx" @@ -36,23 +43,19 @@ #include "StdMeshers_Quadrangle_2D.hxx" #include "StdMeshers_ViscousLayers.hxx" -#include "SMESH_Comment.hxx" -#include "SMESH_Gen.hxx" -#include "SMESH_Mesh.hxx" -#include "SMESH_MesherHelper.hxx" -#include "SMESH_subMesh.hxx" - -#include "SMDS_MeshNode.hxx" - +#include +#include #include #include -#include #include +#include #include #include "utilities.h" #include "Utils_ExceptHandlers.hxx" +#include + typedef SMESH_Comment TComm; using namespace std; @@ -77,6 +80,7 @@ StdMeshers_Hexa_3D::StdMeshers_Hexa_3D(int hypId, SMESH_Gen * gen) _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit /shape type _requireShape = false; _compatibleHypothesis.push_back("ViscousLayers"); + _compatibleHypothesis.push_back("BlockRenumber"); _quadAlgo = new StdMeshers_Quadrangle_2D( gen->GetANewId(), _gen ); } @@ -114,7 +118,8 @@ bool StdMeshers_Hexa_3D::CheckHypothesis return false; */ - _viscousLayersHyp = NULL; + _viscousLayersHyp = nullptr; + _blockRenumberHyp = nullptr; const list& hyps = GetUsedHypothesis(aMesh, aShape, /*ignoreAuxiliary=*/false); @@ -129,13 +134,25 @@ bool StdMeshers_Hexa_3D::CheckHypothesis aStatus = HYP_OK; for ( ; h != hyps.end(); ++h ) { - if ( !(_viscousLayersHyp = dynamic_cast< const StdMeshers_ViscousLayers*> ( *h ))) - break; + if ( !_viscousLayersHyp && + (_viscousLayersHyp = dynamic_cast< const StdMeshers_ViscousLayers*> ( *h ))) + continue; + if ( !_blockRenumberHyp && + (_blockRenumberHyp = dynamic_cast< const StdMeshers_BlockRenumber*> ( *h ))) + continue; + break; } - if ( !_viscousLayersHyp ) + if ((int) hyps.size() != (bool)_viscousLayersHyp + (bool)_blockRenumberHyp ) aStatus = HYP_INCOMPATIBLE; else - error( _viscousLayersHyp->CheckHypothesis( aMesh, aShape, aStatus )); + { + if ( _viscousLayersHyp ) + if ( !error( _viscousLayersHyp->CheckHypothesis( aMesh, aShape, aStatus ))) + aStatus = HYP_BAD_PARAMETER; + + if ( _blockRenumberHyp && aStatus == HYP_OK ) + error( _blockRenumberHyp->CheckHypothesis( aMesh, aShape )); + } return aStatus == HYP_OK; } @@ -202,26 +219,6 @@ namespace int operator()(const int x, const int y) const { return y * _xSize + x; } }; - //================================================================================ - /*! - * \brief Appends a range of node columns from a map to another map - */ - template< class TMapIterator > - void append( TParam2ColumnMap& toMap, TMapIterator from, TMapIterator to ) - { - const SMDS_MeshNode* lastNode = toMap.rbegin()->second[0]; - const SMDS_MeshNode* firstNode = from->second[0]; - if ( lastNode == firstNode ) - from++; - double u = toMap.rbegin()->first; - for (; from != to; ++from ) - { - u += 1; - TParam2ColumnMap::iterator u2nn = toMap.insert( toMap.end(), make_pair ( u, TNodeColumn())); - u2nn->second.swap( from->second ); - } - } - //================================================================================ /*! * \brief Finds FaceQuadStruct having a side equal to a given one and rearranges @@ -260,6 +257,204 @@ namespace } return foundQuad; } + + //================================================================================ + /*! + * \brief Put quads to aCubeSide in the order of enum EBoxSides + */ + //================================================================================ + + bool arrangeQuads( FaceQuadStructPtr quad[ 6 ], _FaceGrid aCubeSide[ 6 ], bool reverseBottom ) + { + swap( aCubeSide[B_BOTTOM]._quad, quad[0] ); + if ( reverseBottom ) + swap( aCubeSide[B_BOTTOM]._quad->side[ Q_RIGHT],// direct the bottom normal inside cube + aCubeSide[B_BOTTOM]._quad->side[ Q_LEFT ] ); + + aCubeSide[B_FRONT]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_BOTTOM], quad ); + aCubeSide[B_RIGHT]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_RIGHT ], quad ); + aCubeSide[B_BACK ]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_TOP ], quad ); + aCubeSide[B_LEFT ]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_LEFT ], quad ); + if ( aCubeSide[B_FRONT ]._quad ) + aCubeSide[B_TOP]._quad = getQuadWithBottom( aCubeSide[B_FRONT ]._quad->side[Q_TOP ], quad ); + + for ( int i = 1; i < 6; ++i ) + if ( !aCubeSide[i]._quad ) + return false; + return true; + } + + //================================================================================ + /*! + * \brief Rearrange block sides according to StdMeshers_BlockRenumber hypothesis + */ + //================================================================================ + + bool arrangeForRenumber( _FaceGrid blockSide[ 6 ], + TopoDS_Vertex& v000, + TopoDS_Vertex& v001 ) + { + std::swap( blockSide[B_BOTTOM]._quad->side[ Q_RIGHT],// restore after arrangeQuads() + blockSide[B_BOTTOM]._quad->side[ Q_LEFT ] ); + + // find v000 + TopTools_MapOfShape cornerVertices; + cornerVertices.Add( blockSide[B_BOTTOM]._quad->side[Q_BOTTOM].grid->LastVertex() ); + cornerVertices.Add( blockSide[B_BOTTOM]._quad->side[Q_BOTTOM].grid->FirstVertex() ); + cornerVertices.Add( blockSide[B_BOTTOM]._quad->side[Q_TOP ].grid->LastVertex() ); + cornerVertices.Add( blockSide[B_BOTTOM]._quad->side[Q_TOP ].grid->FirstVertex() ); + cornerVertices.Add( blockSide[B_TOP ]._quad->side[Q_BOTTOM].grid->FirstVertex() ); + cornerVertices.Add( blockSide[B_TOP ]._quad->side[Q_BOTTOM].grid->LastVertex() ); + cornerVertices.Add( blockSide[B_TOP ]._quad->side[Q_TOP ].grid->FirstVertex() ); + cornerVertices.Add( blockSide[B_TOP ]._quad->side[Q_TOP ].grid->LastVertex() ); + + if ( v000.IsNull() ) + { + // block CS is not defined; + // renumber only if the block has an edge parallel to an axis of global CS + + v000 = StdMeshers_RenumberHelper::GetVertex000( cornerVertices ); + } + + Bnd_B3d bbox; + for ( auto it = cornerVertices.cbegin(); it != cornerVertices.cend(); ++it ) + bbox.Add( BRep_Tool::Pnt( TopoDS::Vertex( *it ))); + double tol = 1e-5 * Sqrt( bbox.SquareExtent() ); + + // get block edges starting at v000 + + std::vector< StdMeshers_FaceSidePtr > edgesAtV000; + std::vector< gp_Vec > edgeDir; + std::vector< int > iParallel; // 0 - none, 1 - X, 2 - Y, 3 - Z + TopTools_MapOfShape lastVertices; + for ( int iQ = 0; iQ < 6; ++iQ ) + { + FaceQuadStructPtr quad = blockSide[iQ]._quad; + for ( size_t iS = 0; iS < quad->side.size() && edgesAtV000.size() < 3; ++iS ) + { + StdMeshers_FaceSidePtr edge = quad->side[iS]; + TopoDS_Vertex v1 = edge->FirstVertex(), v2 = edge->LastVertex(); + if (( v1.IsSame( v000 ) && !lastVertices.Contains( v2 )) || + ( v2.IsSame( v000 ) && !lastVertices.Contains( v1 ))) + { + bool reverse = v2.IsSame( v000 ); + if ( reverse ) + std::swap( v1, v2 ); + lastVertices.Add( v2 ); + + edgesAtV000.push_back( edge ); + + gp_Pnt pf = BRep_Tool::Pnt( v1 ); + gp_Pnt pl = BRep_Tool::Pnt( v2 ); + gp_Vec vec( pf, pl ); + edgeDir.push_back( vec ); + + iParallel.push_back( 0 ); + if ( !v001.IsNull() ) + { + if ( v001.IsSame( v2 )) + iParallel.back() = 3; + } + else + { + bool isStraight = true; + for ( int iE = 0; iE < edge->NbEdges() && isStraight; ++iE ) + isStraight = SMESH_Algo::IsStraight( edge->Edge( iE )); + + // is parallel to a GCS axis? + if ( isStraight ) + { + int nbDiff = (( Abs( vec.X() ) > tol ) + + ( Abs( vec.Y() ) > tol ) + + ( Abs( vec.Z() ) > tol ) ); + if ( nbDiff == 1 ) + iParallel.back() = ( Abs( vec.X() ) > tol ) ? 1 : ( Abs( vec.Y() ) > tol ) ? 2 : 3; + } + else + { + edgeDir.back() = gp_Vec( pf, edge->Value3d( reverse ? 0.99 : 0.01 )); + } + } + } + } + } + if ( std::accumulate( iParallel.begin(), iParallel.end(), 0 ) == 0 ) + return false; + + // find edge OZ and edge OX + StdMeshers_FaceSidePtr edgeOZ, edgeOX; + auto iZIt = std::find( iParallel.begin(), iParallel.end(), 3 ); + if ( iZIt != iParallel.end() ) + { + int i = std::distance( iParallel.begin(), iZIt ); + edgeOZ = edgesAtV000[ i ]; + int iE1 = SMESH_MesherHelper::WrapIndex( i + 1, edgesAtV000.size() ); + int iE2 = SMESH_MesherHelper::WrapIndex( i + 2, edgesAtV000.size() ); + if (( edgeDir[ iE1 ] ^ edgeDir[ iE2 ] ) * edgeDir[ i ] < 0 ) + std::swap( iE1, iE2 ); + edgeOX = edgesAtV000[ iE1 ]; + } + else + { + for ( size_t i = 0; i < edgesAtV000.size(); ++i ) + { + if ( !iParallel[ i ] ) + continue; + int iE1 = SMESH_MesherHelper::WrapIndex( i + 1, edgesAtV000.size() ); + int iE2 = SMESH_MesherHelper::WrapIndex( i + 2, edgesAtV000.size() ); + if (( edgeDir[ iE1 ] ^ edgeDir[ iE2 ] ) * edgeDir[ i ] < 0 ) + std::swap( iE1, iE2 ); + edgeOZ = edgesAtV000[ iParallel[i] == 1 ? iE2 : iE1 ]; + edgeOX = edgesAtV000[ iParallel[i] == 1 ? i : iE1 ]; + break; + } + } + + if ( !edgeOZ || !edgeOX ) + return false; + + TopoDS_Vertex v100 = edgeOX->LastVertex(); + if ( v100.IsSame( v000 )) + v100 = edgeOX->FirstVertex(); + + // Find the left quad, one including v000 but not v100 + + for ( int iQ = 0; iQ < 6; ++iQ ) + { + FaceQuadStructPtr quad = blockSide[iQ]._quad; + bool hasV000 = false, hasV100 = false; + for ( size_t iS = 0; iS < quad->side.size(); ++iS ) + { + StdMeshers_FaceSidePtr edge = quad->side[iS]; + if ( edge->FirstVertex().IsSame( v000 ) || edge->LastVertex().IsSame( v000 )) + hasV000 = true; + if ( edge->FirstVertex().IsSame( v100 ) || edge->LastVertex().IsSame( v100 )) + hasV100 = true; + } + if ( hasV000 && !hasV100 ) + { + // orient the left quad + for ( int i = 0; i < 4; ++i ) + { + if ( quad->side[Q_BOTTOM].grid->Edge(0).IsSame( edgeOZ->Edge(0) )) + break; + quad->shift( 1, true ); + } + + FaceQuadStructPtr quads[ 6 ]; + quads[0].swap( blockSide[iQ]._quad ); + for ( int i = 1, j = 0; i < 6; ++i, ++j ) + if ( blockSide[ j ]._quad ) + quads[ i ].swap( blockSide[ j ]._quad ); + else + --i; + + return arrangeQuads( quads, blockSide, false/* true*/ ); + } + } + return false; + } + //================================================================================ /*! * \brief Returns true if the 1st base node of sideGrid1 belongs to sideGrid2 @@ -371,9 +566,11 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, if ( FF.Extent() != 6) { static StdMeshers_CompositeHexa_3D compositeHexa(_gen->GetANewId(), _gen); + compositeHexa.SetHypothesis( _blockRenumberHyp ); if ( !compositeHexa.Compute( aMesh, aShape )) return error( compositeHexa.GetComputeError() ); - return true; + + return _blockRenumberHyp ? error( _blockRenumberHyp->CheckHypothesis( aMesh, aShape )) : true; } // Find sides of a cube @@ -399,22 +596,11 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, return error( COMPERR_BAD_SHAPE, "Not a quadrangular box side" ); } + // put quads in a proper order _FaceGrid aCubeSide[ 6 ]; + if ( !arrangeQuads( quad, aCubeSide, true )) + return error( COMPERR_BAD_SHAPE ); - swap( aCubeSide[B_BOTTOM]._quad, quad[0] ); - swap( aCubeSide[B_BOTTOM]._quad->side[ Q_RIGHT],// direct the normal of bottom quad inside cube - aCubeSide[B_BOTTOM]._quad->side[ Q_LEFT ] ); - - aCubeSide[B_FRONT]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_BOTTOM], quad ); - aCubeSide[B_RIGHT]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_RIGHT ], quad ); - aCubeSide[B_BACK ]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_TOP ], quad ); - aCubeSide[B_LEFT ]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_LEFT ], quad ); - if ( aCubeSide[B_FRONT ]._quad ) - aCubeSide[B_TOP]._quad = getQuadWithBottom( aCubeSide[B_FRONT ]._quad->side[Q_TOP ], quad ); - - for ( int i = 1; i < 6; ++i ) - if ( !aCubeSide[i]._quad ) - return error( COMPERR_BAD_SHAPE ); // Make viscous layers // -------------------- @@ -446,6 +632,22 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, } } + // Arrange sides according to _blockRenumberHyp + bool toRenumber = _blockRenumberHyp; + if ( toRenumber ) + { + TopoDS_Vertex v000, v001; + _blockRenumberHyp->IsSolidIncluded( aMesh, aShape, v000, v001 ); + + toRenumber = arrangeForRenumber( aCubeSide, v000, v001 ); + + if ( toRenumber ) + { + meshDS->Modified(); + meshDS->CompactMesh(); // remove numbering holes + } + } + // Check presence of regular grid mesh on FACEs of the cube // ------------------------------------------------------------ @@ -605,6 +807,8 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, computeIJK( *fFront, COO_X, COO_Z, /*y=*/0. ); computeIJK( *fBack, COO_X, COO_Z, /*y=*/1. ); + StdMeshers_RenumberHelper renumHelper( aMesh, _blockRenumberHyp ); + // projection points of the internal node on cube sub-shapes by which // coordinates of the internal node are computed vector pointsOnShapes( SMESH_Block::ID_Shell ); @@ -620,8 +824,25 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, pointsOnShapes[ SMESH_Block::ID_V111 ] = fTop->GetXYZ( X, Y ); gp_XYZ params; // normalized parameters of an internal node within the unit box - for ( x = 1; x < xSize-1; ++x ) + for ( x = 0; x < xSize; ++x ) { + if ( toRenumber ) + { + vector< const SMDS_MeshNode* >& columnX0 = columns[ colIndex( x, 0 )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( columnX0[ z ] ); + if ( x == 0 || x == X ) + { + for ( y = 1; y < ySize; ++y ) + { + vector< const SMDS_MeshNode* >& column0Y = columns[ colIndex( x, y )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( column0Y[ z ] ); + } + continue; + } + } + const double rX = x / double(X); for ( y = 1; y < ySize-1; ++y ) { @@ -641,6 +862,10 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, // projection points on horizontal faces pointsOnShapes[ SMESH_Block::ID_Fxy0 ] = fBottom->GetXYZ( x, y ); pointsOnShapes[ SMESH_Block::ID_Fxy1 ] = fTop ->GetXYZ( x, y ); + + if ( toRenumber ) + renumHelper.AddReplacingNode( column[ 0 ] ); + for ( z = 1; z < zSize-1; ++z ) // z loop { const double rZ = z / double(Z); @@ -673,13 +898,23 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, gp_XYZ coords; SMESH_Block::ShellPoint( params, pointsOnShapes, coords ); column[ z ] = helper.AddNode( coords.X(), coords.Y(), coords.Z() ); - } + + } // z loop + if ( toRenumber ) + renumHelper.AddReplacingNode( column[ Z ] ); + + } // y loop + if ( toRenumber ) + { + vector< const SMDS_MeshNode* >& columnX0 = columns[ colIndex( x, Y )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( columnX0[ z ] ); } - } + } // x loop // side data no more needed, free memory for ( int i = 0; i < 6; ++i ) - aCubeSide[i]._columns.clear(); + SMESHUtils::FreeVector( aCubeSide[i]._columns ); // 5) Create hexahedrons // --------------------- @@ -693,11 +928,25 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, for ( z = 0; z < zSize-1; ++z ) { // bottom face normal of a hexa mush point outside the volume - helper.AddVolume(col00[z], col01[z], col11[z], col10[z], - col00[z+1], col01[z+1], col11[z+1], col10[z+1]); + if ( toRenumber ) + helper.AddVolume(col00[z], col01[z], col01[z+1], col00[z+1], + col10[z], col11[z], col11[z+1], col10[z+1]); + else + helper.AddVolume(col00[z], col01[z], col11[z], col10[z], + col00[z+1], col01[z+1], col11[z+1], col10[z+1]); } } } + + if ( toRenumber ) + renumHelper.DoReplaceNodes(); + + + if ( _blockRenumberHyp ) + { + return error( _blockRenumberHyp->CheckHypothesis( aMesh, aShape )); + } + return true; } diff --git a/src/StdMeshers/StdMeshers_Hexa_3D.hxx b/src/StdMeshers/StdMeshers_Hexa_3D.hxx index a743d4754..16315e0d7 100644 --- a/src/StdMeshers/StdMeshers_Hexa_3D.hxx +++ b/src/StdMeshers/StdMeshers_Hexa_3D.hxx @@ -37,6 +37,7 @@ class SMESH_MesherHelper; class StdMeshers_Quadrangle_2D; class StdMeshers_ViscousLayers; +class StdMeshers_BlockRenumber; class STDMESHERS_EXPORT StdMeshers_Hexa_3D : public SMESH_3D_Algo { @@ -64,6 +65,7 @@ public: protected: const StdMeshers_ViscousLayers* _viscousLayersHyp; + const StdMeshers_BlockRenumber* _blockRenumberHyp; StdMeshers_Quadrangle_2D* _quadAlgo; }; diff --git a/src/StdMeshersGUI/CMakeLists.txt b/src/StdMeshersGUI/CMakeLists.txt index c0f1c390f..d423dd2a3 100644 --- a/src/StdMeshersGUI/CMakeLists.txt +++ b/src/StdMeshersGUI/CMakeLists.txt @@ -81,6 +81,7 @@ SET(_moc_HEADERS StdMeshersGUI_RadioButtonsGrpWdg.h StdMeshersGUI_PropagationHelperWdg.h StdMeshersGUI_NameCheckableGrpWdg.h + StdMeshersGUI_BlockRenumberCreator.h ) IF(SALOME_USE_PLOT2DVIEWER) @@ -117,6 +118,7 @@ SET(_other_SOURCES StdMeshersGUI_RadioButtonsGrpWdg.cxx StdMeshersGUI_PropagationHelperWdg.cxx StdMeshersGUI_NameCheckableGrpWdg.cxx + StdMeshersGUI_BlockRenumberCreator.cxx ) IF(SALOME_USE_PLOT2DVIEWER) diff --git a/src/StdMeshersGUI/StdMeshersGUI.cxx b/src/StdMeshersGUI/StdMeshersGUI.cxx index 511874583..406c14de9 100644 --- a/src/StdMeshersGUI/StdMeshersGUI.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI.cxx @@ -24,10 +24,11 @@ // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. // SMESH includes // -#include "StdMeshersGUI_StdHypothesisCreator.h" -#include "StdMeshersGUI_NbSegmentsCreator.h" +#include "StdMeshersGUI_BlockRenumberCreator.h" #include "StdMeshersGUI_CartesianParamCreator.h" +#include "StdMeshersGUI_NbSegmentsCreator.h" #include "StdMeshersGUI_QuadrangleParamWdg.h" +#include "StdMeshersGUI_StdHypothesisCreator.h" //============================================================================= /*! GetHypothesisCreator @@ -45,6 +46,8 @@ extern "C" return new StdMeshersGUI_CartesianParamCreator( aHypType ); else if ( aHypType=="QuadrangleParams" ) return new StdMeshersGUI_QuadrangleParamCreator( aHypType ); + else if ( aHypType=="BlockRenumber") + return new StdMeshersGUI_BlockRenumberCreator( aHypType ); else return new StdMeshersGUI_StdHypothesisCreator( aHypType ); } diff --git a/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx b/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx new file mode 100644 index 000000000..9aacd166d --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx @@ -0,0 +1,515 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : StdMeshersGUI_BlockRenumberCreator.cxx +// Author : Open CASCADE S.A.S. + +#include "StdMeshersGUI_BlockRenumberCreator.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_HypothesesUtils.h" + +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPACING 6 +#define MARGIN 11 + +namespace { + + //================================================================================ + /*! + * \brief Textual representation of GEOM_Object in QTreeWidget + */ + //================================================================================ + + QString toText( const GEOM::GEOM_Object_var& theGO, + const QString & /*theLabel*/, + QString& theEntry ) + { + QString txt; + if ( !theGO->_is_nil() ) + { + CORBA::String_var name = theGO->GetName(); + CORBA::String_var entry = theGO->GetStudyEntry(); + theEntry = entry.in(); + + txt = name.in(); + //txt = theLabel + name.in() + " [" + theEntry + "]"; + //txt += " ";//" ("; + //txt += entry.in(); + //txt += ")"; + } + return txt; + } + + //================================================================================ + /*! + * \brief Find GEOM_Object by study entry + */ + //================================================================================ + + GEOM::GEOM_Object_var toGeom( const QString& entry ) + { + return SMESH::EntryToInterface( entry ); + } + + + //================================================================================ + /*! + * \brief Find GEOM_Object in the tree + */ + //================================================================================ + + QTreeWidgetItem* findSolidInTree( const GEOM::GEOM_Object_var& go, QTreeWidget* tree ) + { + if ( go->_is_nil() || !tree ) + return nullptr; + + for ( int i = 0; i < tree->topLevelItemCount(); ++i ) + { + QTreeWidgetItem* item = tree->topLevelItem( i ); + if ( item->data( 1, Qt::UserRole ).toString() == SMESH::toQStr( go->GetStudyEntry() )) + return item; + } + return nullptr; + } +} + +//================================================================================ +/*! + * \brief StdMeshersGUI_BlockRenumberCreator constructor + */ +//================================================================================ + +StdMeshersGUI_BlockRenumberCreator::StdMeshersGUI_BlockRenumberCreator(const QString& aHypType) + : StdMeshersGUI_StdHypothesisCreator( aHypType ), + mySolidFilter( "", TopAbs_SOLID, 1, TopAbs_SOLID ), + myVertexFilter( "", TopAbs_VERTEX, 1, TopAbs_VERTEX ) +{ +} + +//================================================================================ +/*! + * \brief StdMeshersGUI_BlockRenumberCreator destructor + */ +//================================================================================ + +StdMeshersGUI_BlockRenumberCreator::~StdMeshersGUI_BlockRenumberCreator() +{ + SMESHGUI::selectionMgr()->clearFilters(); +} + +//================================================================================ +/*! + * \brief Create widgets + */ +//================================================================================ + +QFrame* StdMeshersGUI_BlockRenumberCreator::buildFrame() +{ + QFrame* fr = new QFrame(); + //fr->setMinimumWidth(460); + + QGridLayout* frLayout = new QGridLayout( fr ); + frLayout->setSpacing( SPACING ); + frLayout->setMargin( MARGIN ); + + // name + myName = 0; + int row = 0; + if( isCreation() ) + { + myName = new QLineEdit( fr ); + frLayout->addWidget( new QLabel( tr( "SMESH_NAME" ), fr ), row, 0 ); + frLayout->addWidget( myName, row, 1 ); + row++; + } + + QGroupBox* groupBox = new QGroupBox( tr( "BLOCK_CS_GROUPBOX" ), fr ); + frLayout->addWidget( groupBox, row, 0, 1, 2 ); + + QGridLayout* layout = new QGridLayout( groupBox ); + layout->setSpacing( SPACING ); + layout->setMargin( MARGIN ); + layout->setColumnStretch( 0, 1 ); + + // tree + row = 0; + myBlockTree = new QTreeWidget( groupBox ); + myBlockTree->setColumnCount( 2 ); + myBlockTree->header()->hide(); + // myBlockTree->setHeaderLabels( QStringList() + // << tr("COLUMN_BLOCK") << tr("COLUMN_V000") << tr("COLUMN_V001")); + layout->addWidget( myBlockTree, row, 0, row + 7, 1 ); + + // selection widgets + myButGroup = new QButtonGroup( groupBox ); + myButGroup->setExclusive( true ); + QLabel* label[3] = { new QLabel( tr( "SOLID" ), groupBox ), + new QLabel( tr( "V000" ), groupBox ), + new QLabel( tr( "V001" ), groupBox ) }; + QIcon icon( SMESHGUI::resourceMgr()->loadPixmap("SMESH", tr("ICON_SELECT"))); + for ( int i = 0; i < 3; ++i ) + { + myShapeSelectBut[i] = new QPushButton( icon, "", groupBox ); + myLineEdit [i] = new QLineEdit( groupBox ); + layout->addWidget( label[i], row, 1 ); + layout->addWidget( myShapeSelectBut[i], row, 2 ); + layout->addWidget( myLineEdit[i], row, 3 ); + myShapeSelectBut[i]->setCheckable( true ); + myButGroup->addButton( myShapeSelectBut[i], i ); + row++; + } + + // buttons + myAddBut = new QPushButton( tr("ADD"), groupBox ); + myModifBut = new QPushButton( tr("MODIFY"), groupBox ); + myRemoveBut = new QPushButton( tr("SMESH_REMOVE"), groupBox ); + layout->addWidget( myAddBut, row, 1, 1, 3 ); + row++; + layout->addWidget( myModifBut, row, 1, 1, 3 ); + row++; + layout->addWidget( myRemoveBut, row, 1, 1, 3 ); + row++; + layout->setRowStretch( row, 1 ); + + LightApp_SelectionMgr* selMgr = SMESH::GetSelectionMgr( SMESHGUI::GetSMESHGUI() ); + + connect( selMgr, SIGNAL( currentSelectionChanged()), SLOT( onSelectionChange())); + connect( myBlockTree, SIGNAL( itemSelectionChanged() ), SLOT( onTreeSelectionChange())); + connect( myShapeSelectBut[0], SIGNAL( clicked(bool)), SLOT( onSelectBtnClick() )); + connect( myShapeSelectBut[1], SIGNAL( clicked(bool)), SLOT( onSelectBtnClick() )); + connect( myShapeSelectBut[2], SIGNAL( clicked(bool)), SLOT( onSelectBtnClick() )); + connect( myAddBut, SIGNAL( clicked() ), SLOT( onAddBtnClick() )); + connect( myModifBut, SIGNAL( clicked() ), SLOT( onModifBtnClick() )); + connect( myRemoveBut, SIGNAL( clicked() ), SLOT( onRemoveBtnClick() )); + + return fr; +} + +//================================================================================ +/*! + * \brief Set myGO's to the tree + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::setBlockToTree(QTreeWidgetItem* solidItem) +{ + if ( !myGO[0]->_is_nil() && !myGO[1]->_is_nil() && !myGO[2]->_is_nil() ) + { + if ( !solidItem ) solidItem = new QTreeWidgetItem( myBlockTree ); + solidItem->setExpanded( true ); + QTreeWidgetItem* item = solidItem; + + QFont boldFont = item->font( 0 ); + boldFont.setBold( true ); + + QString entry, label[3] = { tr("BLOCK_LABEL"), tr("V000_LABEL"), tr("V001_LABEL") }; + for ( int i = 0; i < 3; ++i ) + { + if ( i > 0 && ! (item = solidItem->child( i - 1 ))) + item = new QTreeWidgetItem( solidItem ); + + item->setText( 0, label[i] ); + item->setText( 1, toText( myGO[i], label[i], entry )); + item->setData( 1, Qt::UserRole, entry ); + item->setFont( 1, boldFont ); + item->setToolTip( 1, entry ); + } + myBlockTree->resizeColumnToContents( 0 ); + myBlockTree->resizeColumnToContents( 1 ); + } + for ( int i = 0; i < 3; ++i ) + { + myGO[i] = GEOM::GEOM_Object::_nil(); + myLineEdit[i]->setText(""); + } +} + +//================================================================================ +/*! + * \brief Transfer parameters from hypothesis to widgets + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::retrieveParams() const +{ + StdMeshersGUI_BlockRenumberCreator* me = const_cast( this ); + + StdMeshers::StdMeshers_BlockRenumber_var h = + StdMeshers::StdMeshers_BlockRenumber::_narrow( initParamsHypothesis() ); + + if( myName ) + me->myName->setText( hypName() ); + + me->myBlockTree->clear(); + + StdMeshers::blockcs_array_var blkArray = h->GetBlocksOrientation(); + for ( CORBA::ULong i = 0; i < blkArray->length(); ++i ) + { + me->myGO[0] = GEOM::GEOM_Object::_duplicate( blkArray[i].solid.in() ); + me->myGO[1] = GEOM::GEOM_Object::_duplicate( blkArray[i].vertex000.in() ); + me->myGO[2] = GEOM::GEOM_Object::_duplicate( blkArray[i].vertex001.in() ); + me->setBlockToTree(); + } + + me->myShapeSelectBut[0]->click(); + + me->updateButtons(); +} + +//================================================================================ +/*! + * \brief Transfer parameters from widgets to hypothesis + */ +//================================================================================ + +QString StdMeshersGUI_BlockRenumberCreator::storeParams() const +{ + StdMeshers::StdMeshers_BlockRenumber_var h = + StdMeshers::StdMeshers_BlockRenumber::_narrow( hypothesis() ); + + try + { + if( isCreation() ) + SMESH::SetName( SMESH::FindSObject( h ), myName->text().toUtf8().constData() ); + + StdMeshers::blockcs_array_var array = new StdMeshers::blockcs_array(); + array->length( myBlockTree->topLevelItemCount() ); + + for ( int i = 0; i < myBlockTree->topLevelItemCount(); ++i ) + { + StdMeshers::BlockCS& bcs = array[i]; + QTreeWidgetItem* item0 = myBlockTree->topLevelItem( i ); + QTreeWidgetItem* item1 = item0->child( 0 ); + QTreeWidgetItem* item2 = item0->child( 1 ); + + bcs.solid = toGeom( item0->data( 1, Qt::UserRole ).toString() )._retn(); + bcs.vertex000 = toGeom( item1->data( 1, Qt::UserRole ).toString() )._retn(); + bcs.vertex001 = toGeom( item2->data( 1, Qt::UserRole ).toString() )._retn(); + } + h->SetBlocksOrientation( array ); + + } + catch(const SALOME::SALOME_Exception& ex) + { + SalomeApp_Tools::QtCatchCorbaException(ex); + } + return ""; +} + +//================================================================================ +/*! + * \brief Take selected object + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::onSelectionChange() +{ + SALOME_ListIO list; + SMESHGUI::GetSMESHGUI()->selectionMgr()->selectedObjects( list ); + + int shapeID = myButGroup->checkedId(); + if ( shapeID < 0 || shapeID > 2 ) + shapeID = 0; + + myGO[ shapeID ] = GEOM::GEOM_Object::_nil(); + myLineEdit[ shapeID ]->clear(); + + if ( list.IsEmpty() ) + return; + + Handle(SALOME_InteractiveObject) io = list.First(); + if ( !io->hasEntry() ) + return; + + myGO[ shapeID ] = SMESH::IObjectToInterface( io ); + + if ( !myGO[ shapeID ]->_is_nil() ) + myLineEdit[ shapeID ]->setText( SMESH::toQStr( myGO[ shapeID ]->GetName() )); + + updateButtons(); +} + +//================================================================================ +/*! + * \brief Display selected block CS in myLineEdit's + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::onTreeSelectionChange() +{ + QList items = myBlockTree->selectedItems(); + for ( QTreeWidgetItem* item : items ) + { + if ( item->parent() ) + item = item->parent(); + + QTreeWidgetItem* items[3] = { item, item->child( 0 ), item->child( 1 ) }; + if ( items[1] && items[2] ) + for ( int i = 0; i < 3; ++i ) + { + myGO[i] = toGeom( items[i]->data( 1, Qt::UserRole ).toString() ); + myLineEdit[i]->setText( items[i]->text( 1 )); + } + + break; + } + + updateButtons(); +} + +//================================================================================ +/*! + * \brief Activate/deactivate buttons + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::updateButtons() +{ + bool isSolidInTree = findSolidInTree( myGO[0], myBlockTree ); + myAddBut ->setEnabled( !isSolidInTree && + !myGO[0]->_is_nil() && !myGO[1]->_is_nil() && !myGO[2]->_is_nil() ); + myModifBut ->setEnabled( isSolidInTree ); + myRemoveBut->setEnabled( !myBlockTree->selectedItems().isEmpty() || + myBlockTree->topLevelItemCount() == 1 ); +} + +//================================================================================ +/*! + * \brief Install filter upon activation of another object selection + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::onSelectBtnClick() +{ + int shapeID = myButGroup->checkedId(); + LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr(); + selMgr->clearFilters(); + selMgr->installFilter( shapeID > 0 ? &myVertexFilter : &mySolidFilter ); +} + +//================================================================================ +/*! + * \brief Add shapes to the tree + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::onAddBtnClick() +{ + setBlockToTree(); + updateButtons(); +} + +//================================================================================ +/*! + * \brief Modify a current block + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::onModifBtnClick() +{ + if ( QTreeWidgetItem* item = findSolidInTree( myGO[0], myBlockTree )) + { + setBlockToTree( item ); + } + updateButtons(); +} + +//================================================================================ +/*! + * \brief Remove selected block from the tree + */ +//================================================================================ + +void StdMeshersGUI_BlockRenumberCreator::onRemoveBtnClick() +{ + QList items = myBlockTree->selectedItems(); + if ( items.isEmpty() && myBlockTree->topLevelItemCount() == 1 ) + items.push_back( myBlockTree->topLevelItem( 0 )); + + for ( QTreeWidgetItem* item : items ) + { + if ( item->parent() ) + item = item->parent(); + delete item; + } + + updateButtons(); +} + + +//================================================================================ +/*! + * \brief Validate parameters + */ +//================================================================================ + +bool StdMeshersGUI_BlockRenumberCreator::checkParams( QString& msg ) const +{ + return true; +} + +//================================================================================ +/*! + * \brief Returns a name of help page + */ +//================================================================================ + +QString StdMeshersGUI_BlockRenumberCreator::helpPage() const +{ + return "block_renumber.html"; +} + +//================================================================================ +/*! + * \brief Type name + */ +//================================================================================ + +QString StdMeshersGUI_BlockRenumberCreator::hypTypeName( const QString& ) const +{ + return "BLOCK_RENUMBER"; +} diff --git a/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.h b/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.h new file mode 100644 index 000000000..f3fe31d4f --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.h @@ -0,0 +1,89 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// File : StdMeshersGUI_BlockRenumberCreator.h +// Author : Open CASCADE S.A.S. +// +#ifndef STDMESHERSGUI_BlockRenumberCreator_H +#define STDMESHERSGUI_BlockRenumberCreator_H + +#include "SMESH_NumberFilter.hxx" +#include "SMESH_StdMeshersGUI.hxx" +#include "StdMeshersGUI_StdHypothesisCreator.h" + +#include +#include CORBA_SERVER_HEADER(GEOM_Gen) +//#include CORBA_SERVER_HEADER(SMESH_Hypothesis) + +class QButtonGroup; +class QLineEdit; +class QString; +class QTreeWidget; +class QTreeWidgetItem; + +class STDMESHERSGUI_EXPORT StdMeshersGUI_BlockRenumberCreator : public StdMeshersGUI_StdHypothesisCreator +{ + Q_OBJECT + +public: + StdMeshersGUI_BlockRenumberCreator( const QString& aHypType ); + ~StdMeshersGUI_BlockRenumberCreator(); + + virtual bool checkParams( QString& ) const override; + virtual QString helpPage() const override; + virtual QString hypTypeName( const QString& ) const override; + +protected: + virtual QFrame* buildFrame() override; + virtual void retrieveParams() const override; + virtual QString storeParams() const override; + +private slots: + void onSelectionChange(); + void onTreeSelectionChange(); + void updateButtons(); + void onSelectBtnClick(); + void onAddBtnClick(); + void onModifBtnClick(); + void onRemoveBtnClick(); + +private: + + void setBlockToTree( QTreeWidgetItem* item = 0 ); + + QLineEdit* myName; + QTreeWidget* myBlockTree; + + QPushButton* myShapeSelectBut[3]; + QButtonGroup* myButGroup; + QLineEdit* myLineEdit[3]; + + QPushButton* myAddBut; + QPushButton* myModifBut; + QPushButton* myRemoveBut; + + GEOM::GEOM_Object_var myGO[3]; + + SMESH_NumberFilter mySolidFilter, myVertexFilter; +}; + +#endif // STDMESHERSGUI_BlockRenumberCreator_H diff --git a/src/StdMeshersGUI/StdMeshers_images.ts b/src/StdMeshersGUI/StdMeshers_images.ts index 681bf7bed..3b41151ac 100644 --- a/src/StdMeshersGUI/StdMeshers_images.ts +++ b/src/StdMeshersGUI/StdMeshers_images.ts @@ -99,6 +99,10 @@ ICON_DLG_START_END_LENGTH mesh_hypo_length.png + + ICON_DLG_BLOCK_RENUMBER + mesh_tree_hypo_renumber.png + ICON_SELECT select1.png @@ -291,6 +295,10 @@ ICON_SMESH_TREE_HYPO_QuadraticMesh mesh_tree_hypo_quadratic.png + + ICON_SMESH_TREE_HYPO_BlockRenumber + mesh_tree_hypo_renumber.png + ICON_SMESH_TREE_HYPO_SegmentLengthAroundVertex mesh_tree_hypo_length.png diff --git a/src/StdMeshersGUI/StdMeshers_msg_en.ts b/src/StdMeshersGUI/StdMeshers_msg_en.ts index 0c0509f19..1af404162 100644 --- a/src/StdMeshersGUI/StdMeshers_msg_en.ts +++ b/src/StdMeshersGUI/StdMeshers_msg_en.ts @@ -508,6 +508,14 @@ this one for this mesh/sub-mesh. SMESH_QUAD_TYPE Type + + SMESH_BLOCK_RENUMBER_TITLE + Hypothesis Construction + + + SMESH_BLOCK_RENUMBER_HYPOTHESIS + Renumber + StdMeshersGUI_QuadrangleParamWdg @@ -679,4 +687,55 @@ this one for this mesh/sub-mesh. Chain %1 (%2 edges) + + StdMeshersGUI_BlockRenumberCreator + + BLOCK_CS_GROUPBOX + Block coordinate system + + + SOLID + Solid + + + V000 + Vertex (0,0,0) + + + V001 + Vertex (0,0,1) + + + ADD + Add + + + MODIFY + Modify + + + COLUMN_BLOCK + Block + + + COLUMN_V000 + Vertex (0,0,0) + + + COLUMN_V001 + Vertex (0,0,1) + + + BLOCK_LABEL + Block: + + + V000_LABEL + Vertex (0,0,0): + + + V001_LABEL + Vertex (0,0,1): + + diff --git a/src/StdMeshers_I/CMakeLists.txt b/src/StdMeshers_I/CMakeLists.txt index e4e0f27f4..8dbfc245d 100644 --- a/src/StdMeshers_I/CMakeLists.txt +++ b/src/StdMeshers_I/CMakeLists.txt @@ -119,6 +119,7 @@ SET(StdMeshersEngine_HEADERS StdMeshers_Cartesian_3D_i.hxx StdMeshers_PolygonPerFace_2D_i.hxx StdMeshers_PolyhedronPerSolid_3D_i.hxx + StdMeshers_BlockRenumber_i.hxx ) IF(SALOME_SMESH_ENABLE_MEFISTO) SET(StdMeshersEngine_HEADERS ${StdMeshersEngine_HEADERS} StdMeshers_MEFISTO_2D_i.hxx) @@ -175,6 +176,7 @@ SET(StdMeshersEngine_SOURCES StdMeshers_Adaptive1D_i.cxx StdMeshers_PolygonPerFace_2D_i.cxx StdMeshers_PolyhedronPerSolid_3D_i.cxx + StdMeshers_BlockRenumber_i.cxx ) IF(SALOME_SMESH_ENABLE_MEFISTO) diff --git a/src/StdMeshers_I/StdMeshers_BlockRenumber_i.cxx b/src/StdMeshers_I/StdMeshers_BlockRenumber_i.cxx new file mode 100644 index 000000000..7c517cab6 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_BlockRenumber_i.cxx @@ -0,0 +1,189 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes +// File : StdMeshers_BlockRenumber_i.cxx +// Author : Edward AGAPOV +// Module : SMESH +// $Header$ +// +#include "StdMeshers_ObjRefUlils.hxx" +#include "StdMeshers_BlockRenumber_i.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_PythonDump.hxx" + +#include "Utils_CorbaException.hxx" + +//============================================================================= +/*! + * StdMeshers_BlockRenumber_i::StdMeshers_BlockRenumber_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_BlockRenumber_i::StdMeshers_BlockRenumber_i( PortableServer::POA_ptr thePOA, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ) +{ + myBaseImpl = new ::StdMeshers_BlockRenumber( theGenImpl->GetANewId(), + theGenImpl ); +} + +//================================================================================ +/*! + * \brief Set orientation of blocks + */ +//================================================================================ + +void StdMeshers_BlockRenumber_i::SetBlocksOrientation( const StdMeshers::blockcs_array& blockCS ) +{ + try { + SMESH_Comment dump; + CORBA::String_var entry; + std::vector< StdMeshers_BlockCS > bcsVec( blockCS.length() ); + for ( size_t i = 0; i < bcsVec.size(); i++ ) + { + StdMeshers_BlockCS& bcs = bcsVec[i]; + if ( !CORBA::is_nil( blockCS[i].solid ) && + !CORBA::is_nil( blockCS[i].vertex000 )&& + !CORBA::is_nil( blockCS[i].vertex001 )) + { + entry = blockCS[i].solid->GetStudyEntry(); + bcs._solid = entry.in(); + entry = blockCS[i].vertex000->GetStudyEntry(); + bcs._vertex000 = entry.in(); + entry = blockCS[i].vertex001->GetStudyEntry(); + bcs._vertex001 = entry.in(); + + if ( !dump.empty() ) dump << ",\n "; + dump << "StdMeshers.BlockCS( " + << bcs._solid << ", " << bcs._vertex000 << ", " << bcs._vertex001 + << " )"; + } + } + this->GetImpl()->SetBlocksOrientation( bcsVec ); + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetBlocksOrientation([ " << dump << " ])"; + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } +} + +//================================================================================ +/*! + * \brief Return orientation of blocks + */ +//================================================================================ + +StdMeshers::blockcs_array* StdMeshers_BlockRenumber_i::GetBlocksOrientation() +{ + const std::vector< StdMeshers_BlockCS >& bcsVec = this->GetImpl()->GetBlocksOrientation(); + StdMeshers::blockcs_array_var bcsArray = new StdMeshers::blockcs_array(); + bcsArray->length( bcsVec.size() ); + TopoDS_Shape nullShape; + for ( size_t i = 0; i < bcsVec.size(); ++i ) + { + bcsArray[i].solid = + StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject( bcsVec[i]._solid, nullShape ); + bcsArray[i].vertex000 = + StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject( bcsVec[i]._vertex000, nullShape ); + bcsArray[i].vertex001 = + StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject( bcsVec[i]._vertex001, nullShape ); + } + return bcsArray._retn(); +} + +//================================================================================ +/*! + * \brief Return geom entries + */ +//================================================================================ + +bool StdMeshers_BlockRenumber_i::getObjectsDependOn( std::vector< std::string > & entryArray, + std::vector< int > & subIDArray ) const +{ + const std::vector< StdMeshers_BlockCS >& bcsVec = + const_cast(this)->GetImpl()->GetBlocksOrientation(); + entryArray.reserve( entryArray.capacity() + 3 * bcsVec.size()); + for ( size_t i = 0; i < bcsVec.size(); ++i ) + { + entryArray.push_back( bcsVec[i]._solid ); + entryArray.push_back( bcsVec[i]._vertex000 ); + entryArray.push_back( bcsVec[i]._vertex001 ); + } + return !bcsVec.empty(); +} + +//================================================================================ +/*! + * \brief Update geom entries for a new geometry + */ +//================================================================================ + +bool StdMeshers_BlockRenumber_i::setObjectsDependOn( std::vector< std::string > & entryArray, + std::vector< int > & subIDArray ) +{ + std::vector< StdMeshers_BlockCS > bcsVec( entryArray.size() / 3 ); + for ( size_t i = 0; i + 2 < entryArray.size(); i += 3 ) + { + StdMeshers_BlockCS& bcs = bcsVec[i]; + bcs._solid = entryArray[ i ]; + bcs._vertex000 = entryArray[ i + 1 ]; + bcs._vertex001 = entryArray[ i + 2 ]; + } + this->GetImpl()->SetBlocksOrientation( bcsVec ); + return true; +} + + +//============================================================================= +/*! + * StdMeshers_BlockRenumber_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_BlockRenumber* StdMeshers_BlockRenumber_i::GetImpl() +{ + return ( ::StdMeshers_BlockRenumber* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_BlockRenumber_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_3D; +} + diff --git a/src/StdMeshers_I/StdMeshers_BlockRenumber_i.hxx b/src/StdMeshers_I/StdMeshers_BlockRenumber_i.hxx new file mode 100644 index 000000000..42291fd04 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_BlockRenumber_i.hxx @@ -0,0 +1,76 @@ +// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// File : StdMeshers_BlockRenumber_i.hxx +// Author : Edward AGAPOV +// Module : SMESH +// +#ifndef _SMESH_BlockRenumber_I_HXX_ +#define _SMESH_BlockRenumber_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_Hypothesis_i.hxx" +#include "StdMeshers_BlockRenumber.hxx" + +class SMESH_Gen; + +// ========================================================= +/*! + * \class 3D Hypothesis used by Hexahedron(ijk) algorithm + * to renumber mesh of a block to be structured-like + */ +// ========================================================= + +class STDMESHERS_I_EXPORT StdMeshers_BlockRenumber_i: + public virtual POA_StdMeshers::StdMeshers_BlockRenumber, + public virtual SMESH_Hypothesis_i +{ +public: + + StdMeshers_BlockRenumber_i( PortableServer::POA_ptr thePOA, + ::SMESH_Gen* theGenImpl ); + + // Set orientation of blocks + void SetBlocksOrientation( const StdMeshers::blockcs_array& blockCS ) override; + + // Return orientation of blocks + StdMeshers::blockcs_array* GetBlocksOrientation() override; + + + // Get implementation + ::StdMeshers_BlockRenumber* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ) override; + + // Methods for copying mesh definition to other geometry + bool getObjectsDependOn( std::vector< std::string > & entryArray, + std::vector< int > & subIDArray ) const override; + bool setObjectsDependOn( std::vector< std::string > & entryArray, + std::vector< int > & subIDArray ) override; +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_i.cxx b/src/StdMeshers_I/StdMeshers_i.cxx index 2bd9e4eda..9038d2f58 100644 --- a/src/StdMeshers_I/StdMeshers_i.cxx +++ b/src/StdMeshers_I/StdMeshers_i.cxx @@ -34,6 +34,7 @@ #include "StdMeshers_Adaptive1D_i.hxx" #include "StdMeshers_Arithmetic1D_i.hxx" #include "StdMeshers_AutomaticLength_i.hxx" +#include "StdMeshers_BlockRenumber_i.hxx" #include "StdMeshers_CartesianParameters3D_i.hxx" #include "StdMeshers_Cartesian_3D_i.hxx" #include "StdMeshers_CompositeSegment_1D_i.hxx" @@ -207,6 +208,8 @@ STDMESHERS_I_EXPORT aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "CartesianParameters3D") == 0) aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "BlockRenumber") == 0) + aCreator = new StdHypothesisCreator_i; // Algorithms else if (strcmp(aHypName, "Regular_1D") == 0) From abc50b5f498c96d0a252a1dda7d85b8b2de6c3e4 Mon Sep 17 00:00:00 2001 From: eap Date: Mon, 12 Oct 2020 11:59:18 +0300 Subject: [PATCH 06/10] #19926 [CEA 19782] renumbering meshes --- src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx b/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx index 9aacd166d..2f946d9a1 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_BlockRenumberCreator.cxx @@ -500,7 +500,7 @@ bool StdMeshersGUI_BlockRenumberCreator::checkParams( QString& msg ) const QString StdMeshersGUI_BlockRenumberCreator::helpPage() const { - return "block_renumber.html"; + return "3d_meshing_hypo.html#renumber-hypothesis"; } //================================================================================ From 3afca7a3ff82fd4350bd662acf130a72a4e3fc9a Mon Sep 17 00:00:00 2001 From: eap Date: Thu, 15 Oct 2020 18:02:26 +0300 Subject: [PATCH 07/10] #19926 [CEA 19782] renumbering meshes \\ fix excess nodes --- src/SMESHGUI/SMESHGUI_Hypotheses.cxx | 2 +- src/StdMeshers/StdMeshers_Hexa_3D.cxx | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/SMESHGUI/SMESHGUI_Hypotheses.cxx b/src/SMESHGUI/SMESHGUI_Hypotheses.cxx index a228b87e0..8256f8883 100644 --- a/src/SMESHGUI/SMESHGUI_Hypotheses.cxx +++ b/src/SMESHGUI/SMESHGUI_Hypotheses.cxx @@ -605,7 +605,7 @@ QString SMESHGUI_GenericHypothesisCreator::helpPage() const else if ( aHypType == "MaxElementArea") aHelpFileName = "2d_meshing_hypo.html#max-element-area-anchor"; else if ( aHypType == "MaxElementVolume") - aHelpFileName = "max_element_volume_hypo.html"; + aHelpFileName = "3d_meshing_hypo.html#max-element-volume-hypothesis"; else if ( aHypType == "StartEndLength") aHelpFileName = "1d_meshing_hypo.html#start-and-end-length-anchor"; else if ( aHypType == "Deflection1D") diff --git a/src/StdMeshers/StdMeshers_Hexa_3D.cxx b/src/StdMeshers/StdMeshers_Hexa_3D.cxx index 26b22cae1..cde18b142 100644 --- a/src/StdMeshers/StdMeshers_Hexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_Hexa_3D.cxx @@ -824,7 +824,16 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, pointsOnShapes[ SMESH_Block::ID_V111 ] = fTop->GetXYZ( X, Y ); gp_XYZ params; // normalized parameters of an internal node within the unit box - for ( x = 0; x < xSize; ++x ) + + if ( toRenumber ) + for ( y = 0; y < ySize; ++y ) + { + vector< const SMDS_MeshNode* >& column0y = columns[ colIndex( 0, y )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( column0y[ z ] ); + } + + for ( x = 1; x < xSize-1; ++x ) { if ( toRenumber ) { @@ -912,6 +921,14 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, } } // x loop + if ( toRenumber ) + for ( y = 0; y < ySize; ++y ) + { + vector< const SMDS_MeshNode* >& columnXy = columns[ colIndex( X, y )]; + for ( z = 0; z < zSize; ++z ) + renumHelper.AddReplacingNode( columnXy[ z ] ); + } + // side data no more needed, free memory for ( int i = 0; i < 6; ++i ) SMESHUtils::FreeVector( aCubeSide[i]._columns ); From 6bc94c22113c7133067ebd33f85ccb437b3c3fe0 Mon Sep 17 00:00:00 2001 From: eap Date: Fri, 16 Oct 2020 01:16:47 +0300 Subject: [PATCH 08/10] bos #20144 [CEA 20142] Import1D - Error: Algorithm failed --- src/SMESHUtils/SMESH_MAT2d.cxx | 5 + src/SMESHUtils/SMESH_MeshAlgos.cxx | 2 +- src/SMESHUtils/SMESH_Octree.cxx | 10 +- src/SMESHUtils/SMESH_Tree.hxx | 5 +- src/StdMeshers/StdMeshers_Hexa_3D.cxx | 10 - src/StdMeshers/StdMeshers_Import_1D.cxx | 389 +++++++++++++++++++--- src/StdMeshers/StdMeshers_Import_1D2D.cxx | 18 +- 7 files changed, 371 insertions(+), 68 deletions(-) diff --git a/src/SMESHUtils/SMESH_MAT2d.cxx b/src/SMESHUtils/SMESH_MAT2d.cxx index 601fdd84d..fbbb0553f 100644 --- a/src/SMESHUtils/SMESH_MAT2d.cxx +++ b/src/SMESHUtils/SMESH_MAT2d.cxx @@ -1077,6 +1077,11 @@ namespace } } + else // 2D_mesh_QuadranglePreference_00/A1, bos20144.brep + { + continue; // bndSegs.size() == 1 + } + bndSegs[i].setBranch( branchID, bndSegsPerEdge ); // set to i-th and to the opposite bndSeg if ( bndSegs[i].hasOppositeEdge() ) branchEdges[ bndSegs[i].branchID() ].push_back( bndSegs[i]._edge ); diff --git a/src/SMESHUtils/SMESH_MeshAlgos.cxx b/src/SMESHUtils/SMESH_MeshAlgos.cxx index 14c781836..db6f5e690 100644 --- a/src/SMESHUtils/SMESH_MeshAlgos.cxx +++ b/src/SMESHUtils/SMESH_MeshAlgos.cxx @@ -317,7 +317,7 @@ namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() //================================================================================ /*! - * \brief Redistrubute element boxes among children + * \brief Redistribute element boxes among children */ //================================================================================ diff --git a/src/SMESHUtils/SMESH_Octree.cxx b/src/SMESHUtils/SMESH_Octree.cxx index 19473e226..85df6d93b 100644 --- a/src/SMESHUtils/SMESH_Octree.cxx +++ b/src/SMESHUtils/SMESH_Octree.cxx @@ -28,6 +28,9 @@ // #include "SMESH_Octree.hxx" +#include +#include + //=========================================================================== /*! * Constructor. limit must be provided at tree root construction. @@ -90,7 +93,12 @@ void SMESH_Octree::enlargeByFactor( Bnd_B3d* box, double factor ) const { gp_XYZ halfSize = 0.5 * ( box->CornerMax() - box->CornerMin() ); for ( int iDim = 1; iDim <= 3; ++iDim ) - halfSize.SetCoord( iDim, factor * halfSize.Coord( iDim )); + { + double newHSize = factor * halfSize.Coord( iDim ); + if ( newHSize < std::numeric_limits::min() ) + newHSize = Precision::Confusion(); // 1.e-7 + halfSize.SetCoord( iDim, newHSize ); + } box->SetHSize( halfSize ); } } diff --git a/src/SMESHUtils/SMESH_Tree.hxx b/src/SMESHUtils/SMESH_Tree.hxx index 37e92a18c..7be283943 100644 --- a/src/SMESHUtils/SMESH_Tree.hxx +++ b/src/SMESHUtils/SMESH_Tree.hxx @@ -31,6 +31,8 @@ #include "SMESH_Utils.hxx" +const double theEnlargeFactor = 1. + 1e-10; + //================================================================================ // Data limiting the tree height struct SMESH_TreeLimit { @@ -160,6 +162,7 @@ void SMESH_Tree::compute() { if ( !myLimit ) myLimit = new SMESH_TreeLimit(); myBox = buildRootBox(); + enlargeByFactor( myBox, theEnlargeFactor ); if ( myLimit->myMinBoxSize > 0. && maxSize() <= myLimit->myMinBoxSize ) myIsLeaf = true; else @@ -227,7 +230,7 @@ void SMESH_Tree::buildChildren() myChildren[i]->myLimit = myLimit; myChildren[i]->myLevel = myLevel + 1; myChildren[i]->myBox = newChildBox( i ); - enlargeByFactor( myChildren[i]->myBox, 1. + 1e-10 ); + enlargeByFactor( myChildren[i]->myBox, theEnlargeFactor ); if ( myLimit->myMinBoxSize > 0. && myChildren[i]->maxSize() <= myLimit->myMinBoxSize ) myChildren[i]->myIsLeaf = true; } diff --git a/src/StdMeshers/StdMeshers_Hexa_3D.cxx b/src/StdMeshers/StdMeshers_Hexa_3D.cxx index cde18b142..db2f66880 100644 --- a/src/StdMeshers/StdMeshers_Hexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_Hexa_3D.cxx @@ -840,16 +840,6 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, vector< const SMDS_MeshNode* >& columnX0 = columns[ colIndex( x, 0 )]; for ( z = 0; z < zSize; ++z ) renumHelper.AddReplacingNode( columnX0[ z ] ); - if ( x == 0 || x == X ) - { - for ( y = 1; y < ySize; ++y ) - { - vector< const SMDS_MeshNode* >& column0Y = columns[ colIndex( x, y )]; - for ( z = 0; z < zSize; ++z ) - renumHelper.AddReplacingNode( column0Y[ z ] ); - } - continue; - } } const double rX = x / double(X); diff --git a/src/StdMeshers/StdMeshers_Import_1D.cxx b/src/StdMeshers/StdMeshers_Import_1D.cxx index 179dd52e4..e3d59a79c 100644 --- a/src/StdMeshers/StdMeshers_Import_1D.cxx +++ b/src/StdMeshers/StdMeshers_Import_1D.cxx @@ -38,14 +38,19 @@ #include "SMESH_Mesh.hxx" #include "SMESH_MeshEditor.hxx" #include "SMESH_MesherHelper.hxx" +#include "SMESH_Octree.hxx" #include "SMESH_subMesh.hxx" #include "SMESH_subMeshEventListener.hxx" #include "Utils_SALOME_Exception.hxx" #include "utilities.h" +#include #include #include +#include +#include +#include #include #include #include @@ -55,25 +60,251 @@ using namespace std; -//============================================================================= -/*! - * Creates StdMeshers_Import_1D - */ -//============================================================================= - -StdMeshers_Import_1D::StdMeshers_Import_1D(int hypId, SMESH_Gen * gen) - :SMESH_1D_Algo(hypId, gen), _sourceHyp(0) -{ - _name = "Import_1D"; - _shapeType = (1 << TopAbs_EDGE); - - _compatibleHypothesis.push_back("ImportSource1D"); -} - //================================================================================ namespace // INTERNAL STUFF //================================================================================ { + /*! + * \brief Compute point position on a curve. Use octree to fast reject far points + */ + class CurveProjector : public SMESH_Octree + { + public: + CurveProjector( const TopoDS_Edge& edge, double enlarge ); + + bool IsOnCurve( const gp_XYZ& point, double & distance2, double & u ); + + bool IsOut( const gp_XYZ& point ) const { return getBox()->IsOut( point ); } + + protected: + CurveProjector() {} + SMESH_Octree* newChild() const { return new CurveProjector; } + void buildChildrenData(); + Bnd_B3d* buildRootBox(); + + private: + struct CurveSegment : public Bnd_B3d + { + double _chord, _chord2, _length2; + gp_Pnt _pFirst, _pLast; + gp_Lin _line; + Handle(Geom_Curve) _curve; + + CurveSegment() {} + void Init( const gp_Pnt& pf, const gp_Pnt& pl, + double uf, double ul, double tol, Handle(Geom_Curve)& curve ); + bool IsOn( const gp_XYZ& point, double & distance2, double & u ); + bool IsInContact( const Bnd_B3d& bb ); + }; + std::vector< CurveSegment > _segments; + }; + + //=============================================================================== + /*! + * \brief Create an octree of curve segments + */ + //================================================================================ + + CurveProjector::CurveProjector( const TopoDS_Edge& edge, double enlarge ) + :SMESH_Octree( 0 ) + { + double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, f, l ); + double curDeflect = 0.3; // Curvature deflection + double angDeflect = 1e+100; // Angular deflection - don't control chordal error + GCPnts_TangentialDeflection div( BRepAdaptor_Curve( edge ), angDeflect, curDeflect ); + _segments.resize( div.NbPoints() - 1 ); + for ( int i = 1; i < div.NbPoints(); ++i ) + try { + _segments[ i - 1 ].Init( div.Value( i ), div.Value( i+1 ), + div.Parameter( i ), div.Parameter( i+1 ), + enlarge, curve ); + } + catch ( Standard_Failure ) { + _segments.resize( _segments.size() - 1 ); + --i; + } + if ( _segments.size() < 3 ) + myIsLeaf = true; + + compute(); + + if ( _segments.size() == 1 ) + myBox->Enlarge( enlarge ); + } + + //================================================================================ + /*! + * \brief Return the maximal box + */ + //================================================================================ + + Bnd_B3d* CurveProjector::buildRootBox() + { + Bnd_B3d* box = new Bnd_B3d; + for ( size_t i = 0; i < _segments.size(); ++i ) + box->Add( _segments[i] ); + return box; + } + + //================================================================================ + /*! + * \brief Redistribute segments among children + */ + //================================================================================ + + void CurveProjector::buildChildrenData() + { + bool allIn = true; + for ( size_t i = 0; i < _segments.size(); ++i ) + { + for (int j = 0; j < 8; j++) + { + if ( _segments[i].IsInContact( *myChildren[j]->getBox() )) + ((CurveProjector*)myChildren[j])->_segments.push_back( _segments[i]); + else + allIn = false; + } + } + if ( allIn && _segments.size() < 3 ) + { + myIsLeaf = true; + for (int j = 0; j < 8; j++) + static_cast( myChildren[j])->myIsLeaf = true; + } + else + { + SMESHUtils::FreeVector( _segments ); // = _segments.clear() + free memory + + for (int j = 0; j < 8; j++) + { + CurveProjector* child = static_cast( myChildren[j]); + if ( child->_segments.size() < 3 ) + child->myIsLeaf = true; + } + } + } + + //================================================================================ + /*! + * \brief Return true if a point is close to the curve + * \param [in] point - the point + * \param [out] distance2 - distance to the curve + * \param [out] u - parameter on the curve + * \return bool - is the point is close to the curve + */ + //================================================================================ + + bool CurveProjector::IsOnCurve( const gp_XYZ& point, double & distance2, double & u ) + { + if ( getBox()->IsOut( point )) + return false; + + if ( isLeaf() ) + { + for ( size_t i = 0; i < _segments.size(); ++i ) + if ( !_segments[i].IsOut( point ) && + _segments[i].IsOn( point, distance2, u )) + return true; + } + else + { + for (int i = 0; i < 8; i++) + if (((CurveProjector*) myChildren[i])->IsOnCurve( point, distance2, u )) + return true; + } + return false; + } + + //================================================================================ + /*! + * \brief Initialize + */ + //================================================================================ + + void CurveProjector::CurveSegment::Init(const gp_Pnt& pf, + const gp_Pnt& pl, + const double uf, + const double ul, + const double tol, + Handle(Geom_Curve)& curve ) + { + _pFirst = pf; + _pLast = pl; + _curve = curve; + _length2 = pf.SquareDistance( pl ); + _chord2 = Max( _line. SquareDistance( curve->Value( uf + 0.25 * ( ul - uf ))), + Max( _line.SquareDistance( curve->Value( uf + 0.5 * ( ul - uf ))), + _line.SquareDistance( curve->Value( uf + 0.75 * ( ul - uf ))))); + _chord2 = Max( tol, _chord2 ); + _chord = Sqrt( _chord2 ); + _line.SetLocation( pf ); + _line.SetDirection( gp_Vec( pf, pl )); + + Bnd_Box bb; + BndLib_Add3dCurve::Add( GeomAdaptor_Curve( curve, uf, ul ), tol, bb ); + Add( bb.CornerMin() ); + Add( bb.CornerMax() ); + } + + //================================================================================ + /*! + * \brief Return true if a point is close to the curve segment + * \param [in] point - the point + * \param [out] distance2 - distance to the curve + * \param [out] u - parameter on the curve + * \return bool - is the point is close to the curve segment + */ + //================================================================================ + + bool CurveProjector::CurveSegment::IsOn( const gp_XYZ& point, double & distance2, double & u ) + { + distance2 = _line.SquareDistance( point ); + if ( distance2 > _chord2 ) + return false; + + // check if the point projection falls into the segment range + { + gp_Vec edge( _pFirst, _pLast ); + gp_Vec n1p ( _pFirst, point ); + u = ( edge * n1p ) / _length2; // param [0,1] on the edge + if ( u < 0 ) + { + if ( _pFirst.SquareDistance( point ) > _chord2 ) + return false; + } + else if ( u > _chord ) + { + if ( _pLast.SquareDistance( point ) > _chord2 ) + return false; + } + } + gp_Pnt proj; + distance2 = ShapeAnalysis_Curve().Project( _curve, point, Precision::Confusion(), + proj, u, false ); + distance2 *= distance2; + return true; + } + + //================================================================================ + /*! + * \brief Check if the segment is in contact with a box + */ + //================================================================================ + + bool CurveProjector::CurveSegment::IsInContact( const Bnd_B3d& bb ) + { + if ( bb.IsOut( _line.Position(), /*isRay=*/true, _chord )) + return false; + + gp_Ax1 axRev = _line.Position().Reversed(); + axRev.SetLocation( _pLast ); + return !bb.IsOut( axRev, /*isRay=*/true, _chord ); + } + + //================================================================================ + //================================================================================ + int getSubmeshIDForCopiedMesh(const SMESHDS_Mesh* srcMeshDS, SMESH_Mesh* tgtMesh); enum _ListenerDataType @@ -590,8 +821,50 @@ namespace // INTERNAL STUFF return 0; } + //================================================================================ + /*! + * \brief Return minimal square length of edges of 1D and 2D elements sharing the node + */ + //================================================================================ + + double getMinEdgeLength2( const SMDS_MeshNode* n ) + { + SMESH_NodeXYZ p = n; + double minLen2 = Precision::Infinite(); + for ( SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator(); eIt->more(); ) + { + const SMDS_MeshElement* e = eIt->next(); + const SMDSAbs_ElementType type = e->GetType(); + if ( type != SMDSAbs_Edge && type != SMDSAbs_Face ) + continue; + int i = e->GetNodeIndex( n ); + int iNext = SMESH_MesherHelper::WrapIndex( i + 1, e->NbCornerNodes() ); + minLen2 = Min( minLen2, p.SquareDistance( e->GetNode( iNext ))); + if ( type != SMDSAbs_Face ) + continue; + int iPrev = SMESH_MesherHelper::WrapIndex( i - 1, e->NbCornerNodes() ); + minLen2 = Min( minLen2, p.SquareDistance( e->GetNode( iPrev ))); + } + return minLen2; + } + } // namespace +//============================================================================= +/*! + * Creates StdMeshers_Import_1D + */ +//============================================================================= + +StdMeshers_Import_1D::StdMeshers_Import_1D(int hypId, SMESH_Gen * gen) + :SMESH_1D_Algo(hypId, gen), _sourceHyp(0) +{ + _name = "Import_1D"; + _shapeType = (1 << TopAbs_EDGE); + + _compatibleHypothesis.push_back("ImportSource1D"); +} + //============================================================================= /*! * Check presence of a hypothesis @@ -658,17 +931,31 @@ bool StdMeshers_Import_1D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & th const double edgeTol = BRep_Tool::Tolerance( geomEdge ); const int shapeID = tgtMesh->ShapeToIndex( geomEdge ); - set subShapeIDs; - subShapeIDs.insert( shapeID ); + + double geomTol = Precision::Confusion(); + for ( size_t iG = 0; iG < srcGroups.size(); ++iG ) + { + const SMESHDS_GroupBase* srcGroup = srcGroups[iG]->GetGroupDS(); + for ( SMDS_ElemIteratorPtr srcElems = srcGroup->GetElements(); srcElems->more(); ) + { + const SMDS_MeshElement* edge = srcElems->next(); + geomTol = Sqrt( 0.5 * ( getMinEdgeLength2( edge->GetNode(0) ) + + getMinEdgeLength2( edge->GetNode(1) ))) / 25; + iG = srcGroups.size(); + break; + } + } + CurveProjector curveProjector( geomEdge, geomTol ); // get nodes on vertices + set vertexIDs; list < SMESH_TNodeXYZ > vertexNodes; list < SMESH_TNodeXYZ >::iterator vNIt; TopExp_Explorer vExp( theShape, TopAbs_VERTEX ); for ( ; vExp.More(); vExp.Next() ) { const TopoDS_Vertex& v = TopoDS::Vertex( vExp.Current() ); - if ( !subShapeIDs.insert( tgtMesh->ShapeToIndex( v )).second ) + if ( !vertexIDs.insert( tgtMesh->ShapeToIndex( v )).second ) continue; // closed edge const SMDS_MeshNode* n = SMESH_Algo::VertexNode( v, tgtMesh ); if ( !n ) @@ -696,56 +983,61 @@ bool StdMeshers_Import_1D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & th SMDS_ElemIteratorPtr srcElems = srcGroup->GetElements(); vector newNodes; - SMDS_MeshNode *tmpNode = helper.AddNode(0,0,0); - double u = 0.314159; // "random" value between 0 and 1, avoid 0 and 1, false detection possible on edge restrictions while ( srcElems->more() ) // loop on group contents { const SMDS_MeshElement* edge = srcElems->next(); + gp_XYZ middle = 0.5 * ( SMESH_NodeXYZ( edge->GetNode(0)) + + SMESH_NodeXYZ( edge->GetNode(1))); + if ( curveProjector.IsOut( middle )) + continue; + // find or create nodes of a new edge newNodes.resize( edge->NbNodes() ); - //MESSAGE("edge->NbNodes " << edge->NbNodes()); newNodes.back() = 0; + int nbNodesOnVertex = 0; SMDS_MeshElement::iterator node = edge->begin_nodes(); - SMESH_TNodeXYZ a(edge->GetNode(0)); - // --- define a tolerance relative to the length of an edge - double mytol = a.Distance(edge->GetNode(edge->NbNodes()-1))/25; - //mytol = max(1.E-5, 10*edgeTol); // too strict and not necessary - //MESSAGE("mytol = " << mytol); for ( size_t i = 0; i < newNodes.size(); ++i, ++node ) { - TNodeNodeMap::iterator n2nIt = n2n->insert( make_pair( *node, (SMDS_MeshNode*)0 )).first; + TNodeNodeMap::iterator n2nIt = n2n->insert( make_pair( *node, nullptr )).first; if ( n2nIt->second ) { - if ( !subShapeIDs.count( n2nIt->second->getshapeId() )) - break; + int sId = n2nIt->second->getshapeId(); + if ( sId != shapeID ) + { + if ( vertexIDs.count( sId )) + ++nbNodesOnVertex; + else + break; + } } - else + else if ( !vertexNodes.empty() ) { // find an existing vertex node double checktol = max(1.E-10, 10*edgeTol*edgeTol); for ( vNIt = vertexNodes.begin(); vNIt != vertexNodes.end(); ++vNIt) if ( vNIt->SquareDistance( *node ) < checktol) { - //MESSAGE("SquareDistance " << vNIt->SquareDistance( *node ) << " checktol " << checktol <<" "<X()<<" "<Y()<<" "<Z()); (*n2nIt).second = vNIt->_node; vertexNodes.erase( vNIt ); + ++nbNodesOnVertex; break; } - else if ( vNIt->SquareDistance( *node ) < 10*checktol) - MESSAGE("SquareDistance missed" << vNIt->SquareDistance( *node ) << " checktol " << checktol <<" "<X()<<" "<Y()<<" "<Z()); } if ( !n2nIt->second ) { - // find out if node lies on theShape - //double dxyz[4]; - tmpNode->setXYZ( (*node)->X(), (*node)->Y(), (*node)->Z()); - if ( helper.CheckNodeU( geomEdge, tmpNode, u, mytol, /*force=*/true)) // , dxyz )) // dxyz used for debug purposes + // find out if the node lies on theShape + SMESH_NodeXYZ xyz = *node; + double dist2, u; + if ( curveProjector.IsOnCurve( xyz, dist2, u )) { - SMDS_MeshNode* newNode = tgtMesh->AddNode( (*node)->X(), (*node)->Y(), (*node)->Z()); - n2nIt->second = newNode; - tgtMesh->SetNodeOnEdge( newNode, shapeID, u ); - //MESSAGE("u=" << u << " " << newNode->X()<< " " << newNode->Y()<< " " << newNode->Z()); - //MESSAGE("d=" << dxyz[0] << " " << dxyz[1] << " " << dxyz[2] << " " << dxyz[3]); + // tolerance relative to the length of surrounding edges + double mytol2 = getMinEdgeLength2( *node ) / 25 / 25; + if ( dist2 < mytol2 ) + { + SMDS_MeshNode* newNode = tgtMesh->AddNode( xyz.X(), xyz.Y(), xyz.Z() ); + n2nIt->second = newNode; + tgtMesh->SetNodeOnEdge( newNode, shapeID, u ); + } } } if ( !(newNodes[i] = n2nIt->second )) @@ -763,12 +1055,17 @@ bool StdMeshers_Import_1D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & th newEdge = tgtMesh->AddEdge( newNodes[0], newNodes[1], newNodes[2] ); else newEdge = tgtMesh->AddEdge( newNodes[0], newNodes[1]); - //MESSAGE("add Edge " << newNodes[0]->GetID() << " " << newNodes[1]->GetID()); tgtMesh->SetMeshElementOnShape( newEdge, shapeID ); e2e->insert( make_pair( edge, newEdge )); - } - helper.GetMeshDS()->RemoveNode(tmpNode); - } + + if ( nbNodesOnVertex >= 2 ) // EDGE is meshed by a sole segment + { + iG = srcGroups.size(); // stop looingp on groups + break; + } + } // loop on group contents + } // loop on groups + if ( n2n->empty()) return error("Empty source groups"); diff --git a/src/StdMeshers/StdMeshers_Import_1D2D.cxx b/src/StdMeshers/StdMeshers_Import_1D2D.cxx index 04d903508..30f3d163c 100644 --- a/src/StdMeshers/StdMeshers_Import_1D2D.cxx +++ b/src/StdMeshers/StdMeshers_Import_1D2D.cxx @@ -324,8 +324,7 @@ bool StdMeshers_Import_1D2D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & { const SMDS_MeshElement* face = srcElems->next(); - SMDS_MeshElement::iterator node = face->begin_nodes(); - if ( bndBox3d.IsOut( SMESH_TNodeXYZ( *node ))) + if ( bndBox3d.IsOut( SMESH_NodeXYZ( face->GetNode(0) ))) continue; // find or create nodes of a new face @@ -334,13 +333,14 @@ bool StdMeshers_Import_1D2D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & newNodes.back() = 0; int nbCreatedNodes = 0; bool isOut = false, isIn = false; // if at least one node isIn - do not classify other nodes - for ( size_t i = 0; i < newNodes.size(); ++i, ++node ) + for ( size_t i = 0; i < newNodes.size(); ++i ) { - SMESH_TNodeXYZ nXYZ = *node; + const SMDS_MeshNode* node = face->GetNode( i ); + SMESH_NodeXYZ nXYZ = node; nodeState[ i ] = TopAbs_UNKNOWN; newNodes [ i ] = 0; - it_isnew = n2n->insert( make_pair( *node, (SMDS_MeshNode*)0 )); + it_isnew = n2n->insert( make_pair( node, nullptr )); n2nIt = it_isnew.first; const SMDS_MeshNode* & newNode = n2nIt->second; @@ -354,7 +354,7 @@ bool StdMeshers_Import_1D2D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & if ( newNode->GetID() < (int) isNodeIn.size() && isNodeIn[ newNode->GetID() ]) isIn = true; - if ( !isIn && bndNodes.count( *node )) + if ( !isIn && bndNodes.count( node )) nodeState[ i ] = TopAbs_ON; } else @@ -373,7 +373,7 @@ bool StdMeshers_Import_1D2D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & { // find out if node lies on the surface of theShape gp_XY uv( Precision::Infinite(), 0 ); - isOut = ( !helper.CheckNodeUV( geomFace, *node, uv, groupTol, /*force=*/true ) || + isOut = ( !helper.CheckNodeUV( geomFace, node, uv, groupTol, /*force=*/true ) || bndBox2d.IsOut( uv )); //int iCoo; if ( !isOut && !isIn ) // classify @@ -381,7 +381,7 @@ bool StdMeshers_Import_1D2D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & nodeState[i] = classifier.Perform( uv ); //classifier.Perform( geomFace, uv, clsfTol ); //nodeState[i] = classifier.State(); isOut = ( nodeState[i] == TopAbs_OUT ); - if ( isOut && helper.IsOnSeam( uv ) && onEdgeClassifier.IsSatisfy( (*node)->GetID() )) + if ( isOut && helper.IsOnSeam( uv ) && onEdgeClassifier.IsSatisfy( node->GetID() )) { // uv.SetCoord( iCoo, helper.GetOtherParam( uv.Coord( iCoo ))); // classifier.Perform( geomFace, uv, clsfTol ); @@ -402,7 +402,7 @@ bool StdMeshers_Import_1D2D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & isNodeIn.resize( newNode->GetID() + 1, false ); } if ( nodeState[i] == TopAbs_ON ) - bndNodes.insert( *node ); + bndNodes.insert( node ); else if ( nodeState[i] != TopAbs_UNKNOWN ) isNodeIn[ newNode->GetID() ] = isIn = true; } From 9d0765db5d66008669b55c3388966a8de3755c92 Mon Sep 17 00:00:00 2001 From: rnv Date: Fri, 16 Oct 2020 12:20:46 +0300 Subject: [PATCH 09/10] Fix compilation errors under Windows: VS-2017 --- src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx | 1 + src/StdMeshers/StdMeshers_Hexa_3D.cxx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx b/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx index 2e697303a..b3e420619 100644 --- a/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx @@ -63,6 +63,7 @@ #include #include #include +#include #include using namespace std; diff --git a/src/StdMeshers/StdMeshers_Hexa_3D.cxx b/src/StdMeshers/StdMeshers_Hexa_3D.cxx index db2f66880..eeb6ad40e 100644 --- a/src/StdMeshers/StdMeshers_Hexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_Hexa_3D.cxx @@ -56,6 +56,8 @@ #include +#include + typedef SMESH_Comment TComm; using namespace std; From 4a2c77515c262f201d74f2bf49a36afb47161edf Mon Sep 17 00:00:00 2001 From: jfa Date: Fri, 16 Oct 2020 13:41:08 +0300 Subject: [PATCH 10/10] bos #20087: [CEA 20080] spacing value constraint not taken into account in Body fitting hypothesis construction. --- doc/salome/gui/SMESH/input/cartesian_algo.rst | 2 ++ src/SMESH_SWIG/StdMeshersBuilder.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/doc/salome/gui/SMESH/input/cartesian_algo.rst b/doc/salome/gui/SMESH/input/cartesian_algo.rst index 345872871..76b150e7e 100644 --- a/doc/salome/gui/SMESH/input/cartesian_algo.rst +++ b/doc/salome/gui/SMESH/input/cartesian_algo.rst @@ -59,6 +59,8 @@ This dialog allows to define * You can specify the **Coordinates** of grid nodes. **Insert** button inserts a node at **Step** distance (negative or positive) from the selected node. **Delete** button removes the selected node. Double click on a coordinate in the list enables its edition. **Note** that node coordinates are measured along directions of axes that can differ from the directions of the Global Coordinate System. * You can define the **Spacing** of a grid as an algebraic formula *f(t)* where *t* is a position along a grid axis normalized at [0.0,1.0]. *f(t)* must be non-negative at 0. <= *t* <= 1. The whole extent of geometry can be divided into ranges with their own spacing formulas to apply; a t varies between 0.0 and 1.0 within each **Range**. **Insert** button divides a selected range into two. **Delete** button adds the selected sub-range to the previous one. Double click on a range in the list enables edition of its right boundary. Double click on a function in the list enables its edition. + +.. note:: The actual grid spacing can slightly differ from the defined one. This is done for the best fitting of polyhedrons and for a better mesh quality on the interval boundaries. For example, if a constant **Spacing** is defined along an axis, the actual grid will fill the shape's dimension L along this axis with round number of equal cells: Spacing_actual = L / round( L / Spacing_defined ). * **Fixed Point** group allows defining an exact location of a grid node in the direction defined by spacing. The following cases are possible: diff --git a/src/SMESH_SWIG/StdMeshersBuilder.py b/src/SMESH_SWIG/StdMeshersBuilder.py index 0759332e9..c2f0ed0aa 100644 --- a/src/SMESH_SWIG/StdMeshersBuilder.py +++ b/src/SMESH_SWIG/StdMeshersBuilder.py @@ -1900,6 +1900,12 @@ class StdMeshersBuilder_Cartesian_3D(Mesh_Algorithm): points dividing the whole shape into ranges where the functions apply; points coordinates should vary within (0.0, 1.0) range. Parameter *t* of the spacing function f(t) varies from 0.0 to 1.0 within a shape range. + Note: + The actual grid spacing can slightly differ from the defined one. This is done for the + best fitting of polyhedrons and for a better mesh quality on the interval boundaries. + For example, if a constant **Spacing** is defined along an axis, the actual grid will + fill the shape's dimension L along this axis with round number of equal cells: + Spacing_actual = L / round( L / Spacing_defined ). Examples: "10.5" - defines a grid with a constant spacing