Salome HOME
bos #26452 [EDF] (2021) SMESH: orientation of faces
authoreap <eap@opencascade.com>
Mon, 8 Nov 2021 16:38:38 +0000 (19:38 +0300)
committervsr <vsr@opencascade.com>
Fri, 19 Nov 2021 15:29:24 +0000 (18:29 +0300)
27 files changed:
doc/salome/examples/transforming_meshes_ex13.py
doc/salome/gui/SMESH/images/reorient_2d_refgroup.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/reorient_faces_point.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/reorient_faces_ref_groups.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/reorient_faces_volume.png [new file with mode: 0644]
doc/salome/gui/SMESH/input/modules.rst
doc/salome/gui/SMESH/input/reorient_faces.rst
idl/SMESH_MeshEditor.idl
resources/CMakeLists.txt
resources/reorient_faces_ref_groups.png [new file with mode: 0644]
src/SMDS/SMDS_ElementFactory.cxx
src/SMDS/SMDS_ElementFactory.hxx
src/SMDS/SMDS_Mesh.cxx
src/SMDS/SMDS_Mesh.hxx
src/SMESH/SMESH_MeshEditor.cxx
src/SMESH/SMESH_MeshEditor.hxx
src/SMESHFiltersSelection/SMESH_Type.h
src/SMESHGUI/SMESHGUI_ReorientFacesDlg.cxx
src/SMESHGUI/SMESHGUI_ReorientFacesDlg.h
src/SMESHGUI/SMESH_images.ts
src/SMESHGUI/SMESH_msg_en.ts
src/SMESHGUI/SMESH_msg_fr.ts
src/SMESH_I/SMESH_2smeshpy.cxx
src/SMESH_I/SMESH_MeshEditor_i.cxx
src/SMESH_I/SMESH_MeshEditor_i.hxx
src/SMESH_SWIG/smeshBuilder.py
src/StdMeshers/StdMeshers_Cartesian_3D.cxx

index 94b6f7f22d7bc2d7035151a26b925a83bc4f37f1..d4b553963d57a3d56c5a04f085b6b54a315a3cf7 100644 (file)
@@ -33,6 +33,10 @@ group = mesh.Group( faces[1] )
 
 vec = geompy.MakeVectorDXDYDZ( 1, 1, 1 )
 
+# ============
+# Reorient2D()
+# ============
+
 # Each of arguments of Reorient2D() function can be of different types:
 #
 # 2DObject    - the whole mesh
@@ -53,6 +57,19 @@ mesh.Reorient2D( group, smesh.MakeDirStruct( -10, 1, 10 ), [0,0,0])
 # FaceOrPoint - a SMESH.PointStruct structure
 mesh.Reorient2D( localAlgo.GetSubMesh().GetIDs(), [10,1,0], SMESH.PointStruct(0,0,0))
 
+# ========================
+# Reorient2DByNeighbours()
+# ========================
+
+# Use faces of 'group' as a reference to reorient equally all faces
+mesh.Reorient2DByNeighbours([mesh], [group])
+
+# Orient equally face on 'group', but not define which orientation is correct
+mesh.Reorient2DByNeighbours([group])
+
+# =================
+# Reorient2DBy3D()
+# =================
 
 # Use Reorient2DBy3D() to orient faces of 2 geom faces to have their normal pointing inside volumes
 
diff --git a/doc/salome/gui/SMESH/images/reorient_2d_refgroup.png b/doc/salome/gui/SMESH/images/reorient_2d_refgroup.png
new file mode 100644 (file)
index 0000000..f080f46
Binary files /dev/null and b/doc/salome/gui/SMESH/images/reorient_2d_refgroup.png differ
diff --git a/doc/salome/gui/SMESH/images/reorient_faces_point.png b/doc/salome/gui/SMESH/images/reorient_faces_point.png
new file mode 100644 (file)
index 0000000..63fae72
Binary files /dev/null and b/doc/salome/gui/SMESH/images/reorient_faces_point.png differ
diff --git a/doc/salome/gui/SMESH/images/reorient_faces_ref_groups.png b/doc/salome/gui/SMESH/images/reorient_faces_ref_groups.png
new file mode 100644 (file)
index 0000000..f9cf53b
Binary files /dev/null and b/doc/salome/gui/SMESH/images/reorient_faces_ref_groups.png differ
diff --git a/doc/salome/gui/SMESH/images/reorient_faces_volume.png b/doc/salome/gui/SMESH/images/reorient_faces_volume.png
new file mode 100644 (file)
index 0000000..e58bdb6
Binary files /dev/null and b/doc/salome/gui/SMESH/images/reorient_faces_volume.png differ
index 12133771b785309b880341e64b90f6548d5cae03..41915c4f131f7eca8b30d0766f9fc13645a74ca2 100644 (file)
@@ -449,6 +449,7 @@ Changing orientation of elements
    Mesh.Reorient
    Mesh.ReorientObject
    Mesh.Reorient2D
+   Mesh.Reorient2DByNeighbours
    Mesh.Reorient2DBy3D
 
 Uniting triangles
index 82982dd9a15645250adc3d7ce440d5777dc36ee4..87d510007af5685e8386492e1f3018a88e835d11 100644 (file)
@@ -6,22 +6,33 @@ Orient faces
 ************
 
 This operation allows fixing the orientation of a set of faces in the following ways:
-
+  
 * The required orientation of a set of neighboring faces can be defined by a vector giving the direction of a normal to a certain face. Since the direction of face normals in the set can be even opposite, it is necessary to specify a *control* face, the normal to which will be compared with the vector. This face can be either:
        * found by proximity to a given point, or 
        * specified explicitly. 
+* The required orientation is given by faces of specified reference groups or/and sub-meshes. The reference groups can be omitted, then orientation of an arbitrary selected face to orient defines common orientation.
 * Alternatively, the faces can be oriented relatively to the adjacent volumes.
        
 The orientation of a face is changed by reverting the order of its nodes.
 
 *To set orientation of faces:*
 
-.. |img| image:: ../images/reorient_faces_face.png
+.. |imgfac| image:: ../images/reorient_faces_face.png
+.. |imgpnt| image:: ../images/reorient_faces_point.png
+.. |imggrp| image:: ../images/reorient_faces_ref_groups.png
+.. |imgvol| image:: ../images/reorient_faces_volume.png
 
-#. In the **Modification** menu select **Reorient faces** item or click *"Reorient faces"* button |img| in the toolbar.
+#. In the **Modification** menu select **Reorient faces** item or click *"Reorient faces"* button |imgfac| in the toolbar.
 #. In the "Reorient faces" dialog box
 
-   * Select the **Object** (mesh, sub-mesh or group) containing faces to reorient, in the Object Browser or in the 3D Viewer.
+   * Select a way to define orientation:
+
+       * |imgpnt| - by specifying a point and a vector
+       * |imgfac| - by specifying a face and a vector
+       * |imggrp| - by specifying reference face groups
+       * |imgvol| - by specifying reference volume groups
+
+   * Select the **Object(s)** (mesh, groups and/or sub-meshes) containing faces to reorient, in the Object Browser or in the 3D Viewer.
    * To reorient by direction of the face normal:
     
        * Specify the coordinates of the **Point** by which the control face will be found. You can specify the **Point** by picking a node in the 3D Viewer or selecting a vertex in the Object Browser.
@@ -37,7 +48,7 @@ The orientation of a face is changed by reverting the order of its nodes.
                .. centered::
                        The orientation of adjacent faces is chosen according to a vector. The control face is found by point.
 
-   * In the second mode it is possible to pick the **Face** by mouse in the 3D Viewer or directly input the **Face** ID in the corresponding field.
+   * In the second mode it is possible to pick the **Face** by mouse in the 3D Viewer or directly enter the **Face** ID in the corresponding field.
 
        .. image:: ../images/reorient_2d_face.png 
                :align: center
@@ -46,16 +57,27 @@ The orientation of a face is changed by reverting the order of its nodes.
                The orientation of adjacent faces is chosen according to a vector. The control face is explicitly given.
 
 
-   * In the third mode, the faces can be reoriented according to volumes:
+   * In the third mode, the faces can be reoriented equally to reference faces:
+
+     * If necessary, select 2D **Reference objects** (groups or/and sub-meshes) containing the reference faces, in the Object Browser or in the 3D Viewer. This field can be left empty, then orientation of an arbitrary face will be used as a reference.
+
+
+       .. image:: ../images/reorient_2d_refgroup.png
+               :align: center
+
+       .. centered::
+               The orientation of faces is given by reference face groups and/or sub-meshes.
+
+   * In the fourth mode, the faces can be reoriented according to volumes:
 
      * Select an object (mesh, sub-mesh or group) containing reference **Volumes**, in the Object Browser or in the 3D Viewer.
      * Specify whether face normals should point outside or inside the reference volumes using **Face normal outside volume** check-box.
 
-               .. image:: ../images/reorient_2d_volume.png 
-                       :align: center
+       .. image:: ../images/reorient_2d_volume.png 
+               :align: center
 
-               .. centered::
-                       The orientation of faces is chosen relatively to adjacent volumes.
+       .. centered::
+               The orientation of faces is chosen relatively to adjacent volumes.
 
 #. Click the **Apply** or **Apply and Close** button to confirm the operation.
 
index 26e73dfafb7c152e32b7611c9b204d375b3cfc7f..69f159dc224b0782663a42c4cb2b125446765a28 100644 (file)
@@ -318,6 +318,19 @@ module SMESH
                     in DirStruct      theDirection,
                     in long           theFace,
                     in PointStruct    thePoint) raises (SALOME::SALOME_Exception);
+    /*!
+     * \brief Reorient faces contained in a list of \a objectFaces
+     *        equally to faces contained in a list of \a referenceFaces.
+     * \param objectFaces - faces to reorient in a list including either
+     *        the whole mesh or groups and/or sub-meshes.
+     * \param referenceFaces  - correctly oriented faces in a list of groups and/or sub-meshes.
+     *        It can be empty, then the 1st face in \a objectFaces is used as the reference.
+     * \return number of reoriented faces.
+     */
+    long Reorient2DByNeighbours(in SMESH::ListOfIDSources objectFaces,
+                                in SMESH::ListOfIDSources referenceFaces)
+      raises (SALOME::SALOME_Exception);
+
     /*!
      * \brief Reorient faces basing on orientation of adjacent volumes.
      * \param faces - a list of objects containing face to reorient
@@ -338,6 +351,7 @@ module SMESH
      *                     is still performed; theMaxAngle is measured in radians.
      * \return \c true in case of success, FALSE otherwise.
      */
+
     boolean TriToQuad (in smIdType_array   IDsOfElements,
                        in NumericalFunctor Criterion,
                        in double           MaxAngle) raises (SALOME::SALOME_Exception);
index 6c6c90d80c0ab71158c28fce3403e80cf7626ab7..7e0d620128926cfdcdec43980bf307d31177db49 100644 (file)
@@ -236,6 +236,7 @@ SET(SMESH_RESOURCES_FILES
   pattern_sample_3D.png
   reorient_faces_face.png
   reorient_faces_point.png
+  reorient_faces_ref_groups.png
   reorient_faces_volume.png
   scale.png
   scale_along_axes.png
diff --git a/resources/reorient_faces_ref_groups.png b/resources/reorient_faces_ref_groups.png
new file mode 100644 (file)
index 0000000..f9cf53b
Binary files /dev/null and b/resources/reorient_faces_ref_groups.png differ
index c3ab97c8c9444829f709486d0806ab1f3b60f812..611511916dd3fd57ae6767f97408eb145436f6ad 100644 (file)
@@ -230,6 +230,18 @@ smIdType SMDS_ElementFactory::FromVtkToSmds( vtkIdType vtkID )
   return vtkID + 1;
 }
 
+//================================================================================
+/*!
+ * \brief Clear marked flag of all elements
+ */
+//================================================================================
+
+void SMDS_ElementFactory::SetAllNotMarked()
+{
+  for ( SMDS_ElementChunk& chunk : myChunks )
+    chunk.SetAllNotMarked();
+}
+
 //================================================================================
 /*!
  * \brief Mark the element as non-used
@@ -727,6 +739,17 @@ void SMDS_ElementChunk::SetIsMarked( const SMDS_MeshElement* e, bool is )
   myMarkedSet[ Index( e )] = is;
 }
 
+//================================================================================
+/*!
+ * \brief Clear marked flag of all elements
+ */
+//================================================================================
+
+void SMDS_ElementChunk::SetAllNotMarked()
+{
+  clearVector( myMarkedSet );
+}
+
 //================================================================================
 /*!
  * \brief Return SMDS_Position of a node on a shape
index dcc00a307c1b71d705e2c1a6895e7543f9603baf..92f22e55a460b590ae9e1188e548e6bd66a52f1c 100644 (file)
@@ -116,6 +116,8 @@ public:
   boost::shared_ptr< ElemIterator > GetShapeIterator( int                     shapeID,
                                                       size_t                  nbElemsToReturn,
                                                       const SMDS_MeshElement* sm1stElem );
+  //! Clear marked flag of all elements
+  void SetAllNotMarked();
 
   //! Mark the element as non-used
   void Free( const SMDS_MeshElement* );
@@ -441,6 +443,7 @@ public:
 
   bool IsMarked   ( const SMDS_MeshElement* e ) const;
   void SetIsMarked( const SMDS_MeshElement* e, bool is );
+  void SetAllNotMarked();
 
   SMDS_PositionPtr GetPosition( const SMDS_MeshNode* n ) const;
   void SetPosition( const SMDS_MeshNode* n, const SMDS_PositionPtr& pos, int shapeID );
index 2e992429542479675b82af092ba4bdfb63f5987f..d26107218b83f42a71d505cf5a60bed951fc9903 100644 (file)
@@ -1052,6 +1052,26 @@ bool SMDS_Mesh::RemoveSubMesh(const SMDS_Mesh * aMesh)
   return found;
 }
 
+//=======================================================================
+//function : SetAllNodesNotMarked
+//purpose  : Clear marked flag of all nodes
+//=======================================================================
+
+void SMDS_Mesh::SetAllNodesNotMarked()
+{
+  myNodeFactory->SetAllNotMarked();
+}
+
+//=======================================================================
+//function : SetAllCellsNotMarked
+//purpose  : Clear marked flag of all cells
+//=======================================================================
+
+void SMDS_Mesh::SetAllCellsNotMarked()
+{
+  myCellFactory->SetAllNotMarked();
+}
+
 //=======================================================================
 //function : ChangePolyhedronNodes
 //purpose  :
index c76f7d2775ee184b7c7f0347752dadc3166f9050..de50492f946ed7dbf5b4c3b7e4fce68ab55a02e2 100644 (file)
@@ -618,6 +618,9 @@ public:
                              const std::vector<const SMDS_MeshNode*>& nodes,
                              const std::vector<int>&                  quantities);
 
+  void SetAllNodesNotMarked();
+  void SetAllCellsNotMarked();
+
   //virtual void Renumber (const bool isNodes, const int startID = 1, const int deltaID = 1);
   // Renumber all nodes or elements.
 
index f97e645dc67e8ef3377c411a7affeedc7fefb8ae..853d59b3e8428070ca9dc8cde48fb3a4b11db31a 100644 (file)
@@ -1161,69 +1161,88 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
 /*!
  * \brief Reorient faces.
  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
- * \param theDirection - desired direction of normal of \a theFace
- * \param theFace - one of \a theFaces that should be oriented according to
- *        \a theDirection and whose orientation defines orientation of other faces
+ * \param theDirection - desired direction of normal of \a theRefFaces.
+ *        It can be (0,0,0) in order to keep orientation of \a theRefFaces.
+ * \param theRefFaces - correctly oriented faces whose orientation defines
+ *        orientation of other faces.
  * \return number of reoriented faces.
  */
 //================================================================================
 
-int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
-                                  const gp_Dir&            theDirection,
-                                  const SMDS_MeshElement * theFace)
+int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet &  theFaces,
+                                  const gp_Vec&       theDirection,
+                                  TIDSortedElemSet &  theRefFaces,
+                                  bool                theAllowNonManifold )
 {
   int nbReori = 0;
-  if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
 
   if ( theFaces.empty() )
   {
-    SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
+    SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
     while ( fIt->more() )
       theFaces.insert( theFaces.end(), fIt->next() );
+
+    if ( theFaces.empty() )
+      return nbReori;
   }
 
-  // orient theFace according to theDirection
-  gp_XYZ normal;
-  SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
-  if ( normal * theDirection.XYZ() < 0 )
-    nbReori += Reorient( theFace );
+  // orient theRefFaces according to theDirection
+  if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
+    for ( const SMDS_MeshElement* refFace : theRefFaces )
+    {
+      gp_XYZ normal;
+      SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
+      if ( normal * theDirection.XYZ() < 0 )
+        nbReori += Reorient( refFace );
+    }
 
-  // Orient other faces
+  // mark reference faces
+  GetMeshDS()->SetAllCellsNotMarked();
+  for ( const SMDS_MeshElement* refFace : theRefFaces )
+    refFace->setIsMarked( true );
 
-  set< const SMDS_MeshElement* > startFaces, visitedFaces;
-  TIDSortedElemSet avoidSet;
-  set< SMESH_TLink > checkedLinks;
-  pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
+  // erase reference faces from theFaces
+  for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
+    if ( (*fIt)->isMarked() )
+      fIt = theFaces.erase( fIt );
+    else
+      ++fIt;
+
+  if ( theRefFaces.empty() )
+  {
+    theRefFaces.insert( *theFaces.begin() );
+    theFaces.erase( theFaces.begin() );
+  }
+
+  // Orient theFaces
 
-  if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
-    theFaces.erase( theFace );
-  startFaces.insert( theFace );
+  // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
+  //   theFaces.erase( theFace );
 
   int nodeInd1, nodeInd2;
-  const SMDS_MeshElement*           otherFace;
+  const SMDS_MeshElement*           refFace, *otherFace;
   vector< const SMDS_MeshElement* > facesNearLink;
   vector< std::pair< int, int > >   nodeIndsOfFace;
+  TIDSortedElemSet                  avoidSet, emptySet;
+  NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
 
-  set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
-  while ( !startFaces.empty() )
+  while ( !theRefFaces.empty() )
   {
-    startFace = startFaces.begin();
-    theFace = *startFace;
-    startFaces.erase( startFace );
-    if ( !visitedFaces.insert( theFace ).second )
-      continue;
+    auto refFaceIt = theRefFaces.begin();
+    refFace = *refFaceIt;
+    theRefFaces.erase( refFaceIt );
 
     avoidSet.clear();
-    avoidSet.insert(theFace);
+    avoidSet.insert( refFace );
 
-    NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
+    NLink link( refFace->GetNode( 0 ), nullptr );
 
-    const int nbNodes = theFace->NbCornerNodes();
-    for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
+    const int nbNodes = refFace->NbCornerNodes();
+    for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
     {
-      link.second = theFace->GetNode(( i+1 ) % nbNodes );
-      linkIt_isNew = checkedLinks.insert( link );
-      if ( !linkIt_isNew.second )
+      link.second = refFace->GetNode(( i+1 ) % nbNodes );
+      bool isLinkVisited = checkedLinks.Contains( link );
+      if ( isLinkVisited )
       {
         // link has already been checked and won't be encountered more
         // if the group (theFaces) is manifold
@@ -1231,28 +1250,41 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
       }
       else
       {
+        checkedLinks.Add( link );
+
         facesNearLink.clear();
         nodeIndsOfFace.clear();
+        TIDSortedElemSet::iterator objFaceIt = theFaces.end();
+
         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
-                                                             theFaces, avoidSet,
+                                                             emptySet, avoidSet,
                                                              &nodeInd1, &nodeInd2 )))
-          if ( otherFace != theFace)
+        {
+          if (( otherFace->isMarked() ) || // ref face
+              (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
           {
             facesNearLink.push_back( otherFace );
             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
-            avoidSet.insert( otherFace );
           }
+          avoidSet.insert( otherFace );
+        }
         if ( facesNearLink.size() > 1 )
         {
           // NON-MANIFOLD mesh shell !
-          // select a face most co-directed with theFace,
+          if ( !theAllowNonManifold )
+          {
+            throw SALOME_Exception("Non-manifold topology of groups");
+          }
+          // select a face most co-directed with refFace,
           // other faces won't be visited this time
           gp_XYZ NF, NOF;
-          SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
+          SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
           double proj, maxProj = -1;
-          for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
+          for ( size_t i = 0; i < facesNearLink.size(); ++i )
+          {
             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
-            if (( proj = Abs( NF * NOF )) > maxProj ) {
+            if (( proj = Abs( NF * NOF )) > maxProj )
+            {
               maxProj = proj;
               otherFace = facesNearLink[i];
               nodeInd1  = nodeIndsOfFace[i].first;
@@ -1260,9 +1292,9 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
             }
           }
           // not to visit rejected faces
-          for ( size_t i = 0; i < facesNearLink.size(); ++i )
-            if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
-              visitedFaces.insert( facesNearLink[i] );
+          // for ( size_t i = 0; i < facesNearLink.size(); ++i )
+          //   if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
+          //     visitedFaces.insert( facesNearLink[i] );
         }
         else if ( facesNearLink.size() == 1 )
         {
@@ -1270,20 +1302,36 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
           nodeInd1  = nodeIndsOfFace.back().first;
           nodeInd2  = nodeIndsOfFace.back().second;
         }
-        if ( otherFace && otherFace != theFace)
+        if ( otherFace )
         {
-          // link must be reverse in otherFace if orientation to otherFace
-          // is same as that of theFace
-          if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
+          // link must be reverse in otherFace if orientation of otherFace
+          // is same as that of refFace
+          if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
           {
+            if ( otherFace->isMarked() )
+              throw SALOME_Exception("Different orientation of reference faces");
             nbReori += Reorient( otherFace );
           }
-          startFaces.insert( otherFace );
+          if ( !otherFace->isMarked() )
+          {
+            theRefFaces.insert( otherFace );
+            if ( objFaceIt != theFaces.end() )
+              theFaces.erase( objFaceIt );
+          }
         }
       }
-      std::swap( link.first, link.second ); // reverse the link
+      link.first = link.second; // reverse the link
+
+    } // loop on links of refFace
+
+    if ( theRefFaces.empty() && !theFaces.empty() )
+    {
+      theRefFaces.insert( *theFaces.begin() );
+      theFaces.erase( theFaces.begin() );
     }
-  }
+
+  } // while ( !theRefFaces.empty() )
+
   return nbReori;
 }
 
index e9e13f7963afe8ae495c1be3e32127344db8880e..6962f2c2db4d14a56b03c023cd78cc71f36d6686 100644 (file)
@@ -158,11 +158,12 @@ public:
   bool Reorient (const SMDS_MeshElement * theElement);
   // Reverse theElement orientation
 
-  int Reorient2D (TIDSortedElemSet &       theFaces,
-                  const gp_Dir&            theDirection,
-                  const SMDS_MeshElement * theFace);
-  // Reverse theFaces whose orientation to be same as that of theFace
-  // oriented according to theDirection. Return nb of reoriented faces
+  int Reorient2D (TIDSortedElemSet &  theFaces,
+                  const gp_Vec&       theDirection,
+                  TIDSortedElemSet &  theRefFaces,
+                  bool                theAllowNonManifold);
+  // Reverse theFaces whose orientation to be same as that of theRefFaces
+  // optionally oriented according to theDirection. Return nb of reoriented faces
 
   int Reorient2DBy3D (TIDSortedElemSet & theFaces,
                       TIDSortedElemSet & theVolumes,
index 5aa2df226cb49ddfc94f12461cd5f3fcbb2923e8..2d2838c120eb1a071d888777858151bff094537c 100644 (file)
@@ -62,7 +62,8 @@ namespace SMESH {
     IDSOURCE,
     IDSOURCE_EDGE, // IDSource including edges
     IDSOURCE_FACE,
-    IDSOURCE_VOLUME
+    IDSOURCE_VOLUME,
+    NB_SMESH_TYPES
   };
 }
 #endif
index d7b0b4f880fe79865aba12e589a6367a24ff8f8f..e5c74545a3dff69e632a1ed3dad439d260f52873 100644 (file)
@@ -81,8 +81,6 @@
 #include <vtkProperty.h>
 
 // IDL includes
-#include <SALOMEconfig.h>
-#include CORBA_SERVER_HEADER(SMESH_Mesh)
 #include CORBA_SERVER_HEADER(SMESH_MeshEditor)
 
 // std
@@ -91,8 +89,8 @@
 #define SPACING 6
 #define MARGIN  11
 
-enum { CONSTRUCTOR_POINT=0, CONSTRUCTOR_FACE, CONSTRUCTOR_VOLUME,
-       EObject, EPoint, EFace, EDirection, EVolumes };
+enum { CONSTRUCTOR_POINT=0, CONSTRUCTOR_FACE, CONSTRUCTOR_FACE_GROUPS, CONSTRUCTOR_VOLUME,
+       EObject, EPoint, EFace, EDirection, ERefGroups };
 
 //=======================================================================
 /*!
@@ -130,6 +128,7 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
 
   QPixmap iconReoriPoint (resMgr()->loadPixmap("SMESH", tr("ICON_DLG_REORIENT2D_POINT")));
   QPixmap iconReoriFace  (resMgr()->loadPixmap("SMESH", tr("ICON_DLG_REORIENT2D_FACE")));
+  QPixmap iconReoriGroups(resMgr()->loadPixmap("SMESH", tr("ICON_DLG_REORIENT2D_GROUPS")));
   QPixmap iconReoriVolum (resMgr()->loadPixmap("SMESH", tr("ICON_DLG_REORIENT2D_VOLUME")));
 
   QGroupBox* aConstructorBox = new QGroupBox(tr("REORIENT_FACES"), aFrame);
@@ -149,6 +148,11 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   aConstructorGrpLayout->addWidget(aFaceBut);
   myConstructorGrp->addButton(aFaceBut, CONSTRUCTOR_FACE);
 
+  QRadioButton* aGroupBut= new QRadioButton(aConstructorBox);
+  aGroupBut->setIcon(iconReoriGroups);
+  aConstructorGrpLayout->addWidget(aGroupBut);
+  myConstructorGrp->addButton(aGroupBut, CONSTRUCTOR_FACE_GROUPS);
+
   QRadioButton* aVolBut= new QRadioButton(aConstructorBox);
   aVolBut->setIcon(iconReoriVolum);
   aConstructorGrpLayout->addWidget(aVolBut);
@@ -162,9 +166,10 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   createObject( tr("POINT")    , aFrame, EPoint );
   createObject( tr("FACE")     , aFrame, EFace );
   createObject( tr("DIRECTION"), aFrame, EDirection );
-  createObject( tr("VOLUMES"),   aFrame, EVolumes );
-  setNameIndication( EObject, OneName );
+  createObject( tr("VOLUMES"),   aFrame, ERefGroups );
+  setNameIndication( EObject, ListOfNames );
   setNameIndication( EFace, OneName );
+  setNameIndication( ERefGroups, ListOfNames );
   setReadOnly( EFace, false );
   if ( QLineEdit* le = qobject_cast<QLineEdit*>( objectWg( EFace, Control ) ))
     le->setValidator( new SMESHGUI_IdValidator( this,1 ));
@@ -174,7 +179,6 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   objectWg( EObject   , Label )->setFixedWidth( width );
   objectWg( EPoint    , Label )->setFixedWidth( width );
   objectWg( EFace     , Label )->setFixedWidth( width );
-  objectWg( EVolumes  , Label )->setFixedWidth( width );
 
   myOutsideChk = new QCheckBox( tr("OUTSIDE_VOLUME_NORMAL"), aFrame);
   myOutsideChk->setChecked( true );
@@ -246,14 +250,14 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   aFaceGrpLayout->addWidget( objectWg( EFace, Btn ) );
   aFaceGrpLayout->addWidget( objectWg( EFace, Control ) );
 
-  myVolumFrm = new QFrame(aFrame);
-  QGridLayout* aVolumGrpLayout = new QGridLayout(myVolumFrm);
-  aVolumGrpLayout->setMargin(0);
-  aVolumGrpLayout->setSpacing(SPACING);
-  aVolumGrpLayout->addWidget( objectWg( EVolumes, Label ),   0, 0 );
-  aVolumGrpLayout->addWidget( objectWg( EVolumes, Btn ),     0, 1 );
-  aVolumGrpLayout->addWidget( objectWg( EVolumes, Control ), 0, 2 );
-  aVolumGrpLayout->addWidget( myOutsideChk,                  1, 0, 1, 3 );
+  myRefGroupFrm = new QFrame(aFrame);
+  QGridLayout* aRefGrpLayout = new QGridLayout(myRefGroupFrm);
+  aRefGrpLayout->setMargin(0);
+  aRefGrpLayout->setSpacing(SPACING);
+  aRefGrpLayout->addWidget( objectWg( ERefGroups, Label ),   0, 0 );
+  aRefGrpLayout->addWidget( objectWg( ERefGroups, Btn ),     0, 1 );
+  aRefGrpLayout->addWidget( objectWg( ERefGroups, Control ), 0, 2 );
+  aRefGrpLayout->addWidget( myOutsideChk,                    1, 0, 1, 3 );
 
   myDirFrm = new QFrame(aFrame);
   QHBoxLayout* aDirectGrpLayout = new QHBoxLayout(myDirFrm);
@@ -273,7 +277,7 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   QVBoxLayout* anOrientGrpLayout = new QVBoxLayout ( anOrientGrp );
   anOrientGrpLayout->addWidget(myPointFrm);
   anOrientGrpLayout->addWidget(myFaceFrm);
-  anOrientGrpLayout->addWidget(myVolumFrm);
+  anOrientGrpLayout->addWidget(myRefGroupFrm);
   anOrientGrpLayout->addWidget(myDirFrm);
   
 
@@ -298,7 +302,7 @@ void SMESHGUI_ReorientFacesDlg::constructorChange(int id)
   if ( id == CONSTRUCTOR_FACE )
   {
     myPointFrm->hide();
-    myVolumFrm->hide();
+    myRefGroupFrm->hide();
     myFaceFrm->show();
     myDirFrm->show();
     activateObject( EFace );
@@ -306,25 +310,57 @@ void SMESHGUI_ReorientFacesDlg::constructorChange(int id)
   else if ( id == CONSTRUCTOR_POINT )
   {
     myFaceFrm->hide();
-    myVolumFrm->hide();
+    myRefGroupFrm->hide();
     myPointFrm->show();
     myDirFrm->show();
     activateObject( EPoint );
   }
-  else // CONSTRUCTOR_VOLUME
+  else // CONSTRUCTOR_VOLUME || CONSTRUCTOR_FACE_GROUPS
   {
     myFaceFrm->hide();
     myPointFrm->hide();
     myDirFrm->hide();
-    myVolumFrm->show();
-    activateObject( EVolumes );
+    myOutsideChk->setVisible( id == CONSTRUCTOR_VOLUME );
+    myRefGroupFrm->show();
+    QAbstractButton* refButton = qobject_cast<QAbstractButton*>( objectWg( ERefGroups, Btn ));
+    refButton->setChecked( false ); // force ERefGroups activation
+    activateObject( ERefGroups );
+    setLabel( ERefGroups, id == CONSTRUCTOR_VOLUME ? "VOLUMES" : "REF_GROUPS" );
+  }
+
+  // minimize width of labels
+  QFontMetrics font = objectWg( EDirection, Label )->fontMetrics();
+  int width = 0;
+  for ( int obj = EObject; obj <= ERefGroups; ++obj )
+  {
+    QLabel* label = qobject_cast< QLabel* >( objectWg( obj, Label ));
+    if ( label->isVisible() )
+      width = std::max( width, font.width( label->text() ));
   }
+
+  for ( int obj = EObject; obj <= ERefGroups; ++obj )
+  {
+    QWidget* label = objectWg( obj, Label );
+    if ( label->isVisible() )
+      label->setFixedWidth( width );
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Set object label
+ */
+//================================================================================
+
+void SMESHGUI_ReorientFacesDlg::setLabel( int object, const char* text )
+{
+  qobject_cast< QLabel* >( objectWg( object, Label ))->setText( tr( text ));
 }
 
 //================================================================================
 /*!
  * \brief Constructor
-*/
+ */
 //================================================================================
 
 SMESHGUI_ReorientFacesOp::SMESHGUI_ReorientFacesOp()
@@ -336,16 +372,16 @@ SMESHGUI_ReorientFacesOp::SMESHGUI_ReorientFacesOp()
   myDlg = new SMESHGUI_ReorientFacesDlg;
   myDlg->constructorChange( CONSTRUCTOR_POINT );
 
+  myRefGroupFilter   = new SMESH_TypeFilter( SMESH::GROUP_VOLUME );
+  myRefSubMeshFilter = new SMESH_TypeFilter( SMESH::SUBMESH_SOLID );
+  myRefMeshFilter    = new SMESH_TypeFilter( SMESH::MESH );
+
+  myObjects   = new SMESH::ListOfIDSources();
+  myRefGroups = new SMESH::ListOfIDSources();
+
   // connect signals and slots
   connect( myDlg->objectWg( EFace, LightApp_Dialog::Control ), SIGNAL(textChanged(const QString&)),
            this, SLOT(onTextChange(const QString&)));
-  // connect(myDlg->myX, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
-  // connect(myDlg->myY, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
-  // connect(myDlg->myZ, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
-  // connect(myDlg->myDX, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
-  // connect(myDlg->myDY, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
-  // connect(myDlg->myDZ, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview()));
-
 }
 
 //=======================================================================
@@ -357,23 +393,14 @@ void SMESHGUI_ReorientFacesOp::startOperation()
 {
   myObjectActor = 0;
 
-  // init simulation with a current View
-  //if ( myVectorPreview ) delete myVectorPreview;
-  // myVectorPreview = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( getSMESHGUI() ));
-  // vtkProperty* aProp = vtkProperty::New();
-  // aProp->SetRepresentationToWireframe();
-  // aProp->SetColor(250, 0, 250);
-  // aProp->SetPointSize(5);
-  // aProp->SetLineWidth( SMESH::GetFloat("SMESH:element_width",1) + 1);
-  // myVectorPreview->GetActor()->SetProperty(aProp);
-  // aProp->Delete();
-
   SMESHGUI_SelectionOp::startOperation();
 
   myDlg->show();
 
-  mySelectionMode = 0;
+  mySelectionMode = EObject;
   myDlg->activateObject( EObject );
+
+  selectionDone();
 }
 
 //================================================================================
@@ -384,8 +411,8 @@ void SMESHGUI_ReorientFacesOp::startOperation()
 
 void SMESHGUI_ReorientFacesOp::stopOperation()
 {
-  //myVectorPreview->SetVisibility(false);
-  if ( myObjectActor ) {
+  if ( myObjectActor )
+  {
     myObjectActor->SetPointRepresentation(false);
     SMESH::RepaintCurrentView();
     myObjectActor = 0;
@@ -403,31 +430,43 @@ void SMESHGUI_ReorientFacesOp::stopOperation()
 void SMESHGUI_ReorientFacesOp::onActivateObject( int what )
 {
   if ( what == mySelectionMode )
-    return;
-  mySelectionMode = what;
-  switch ( mySelectionMode )
   {
-  case EPoint:
-  case EDirection:
-    SMESH::SetPointRepresentation(true);
-    setSelectionMode( NodeSelection );
-    SMESH::SetPickable();
-    break;
-  case EObject:
-  case EVolumes:
-    SMESH::SetPointRepresentation(false);
-    setSelectionMode( ActorSelection );
-    break;
-  case EFace:
-    SMESH::SetPointRepresentation(false);
-    setSelectionMode( FaceSelection );
-    if ( myObjectActor )
-      SMESH::SetPickable( myObjectActor );
-    else
+    if ( what == ERefGroups )
+      setRefFiltersByConstructor();
+  }
+  else
+  {
+    mySelectionMode = what;
+    switch ( mySelectionMode )
+    {
+    case EPoint:
+    case EDirection:
+      SMESH::SetPointRepresentation(true);
+      setSelectionMode( NodeSelection );
       SMESH::SetPickable();
-    break;
+      break;
+    case EObject:
+      SMESH::SetPointRepresentation(false);
+      setSelectionMode( ActorSelection );
+      break;
+    case ERefGroups:
+      SMESH::SetPointRepresentation(false);
+      setSelectionMode( ActorSelection );
+      setRefFiltersByConstructor();
+      break;
+    case EFace:
+      SMESH::SetPointRepresentation(false);
+      setSelectionMode( FaceSelection );
+      if ( myObjectActor )
+        SMESH::SetPickable( myObjectActor );
+      else
+        SMESH::SetPickable();
+      break;
+    }
   }
   SMESHGUI_SelectionOp::onActivateObject( what );
+
+  myDlg->setLabel( EObject, onlyOneObjAllowed() ? "OBJECT" : "OBJECTS" );
 }
 
 //================================================================================
@@ -448,12 +487,10 @@ SUIT_SelectionFilter* SMESHGUI_ReorientFacesOp::createFilter( const int what ) c
       filters.append( new SMESH_TypeFilter( SMESH::GROUP_FACE ));
       return new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR );
     }
-  case EVolumes:
+  case ERefGroups:
     {
       QList<SUIT_SelectionFilter*> filters;
-      filters.append( new SMESH_TypeFilter( SMESH::MESH ));
-      filters.append( new SMESH_TypeFilter( SMESH::SUBMESH_SOLID ));
-      filters.append( new SMESH_TypeFilter( SMESH::GROUP_VOLUME ));
+      filters <<  myRefGroupFilter << myRefSubMeshFilter << myRefMeshFilter;
       return new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR );
     }
   case EPoint:
@@ -472,6 +509,28 @@ SUIT_SelectionFilter* SMESHGUI_ReorientFacesOp::createFilter( const int what ) c
   return NULL;
 }
 
+//================================================================================
+/*!
+ * \brief Switch between selection of faces and volumes according to the constructor
+ */
+//================================================================================
+
+void SMESHGUI_ReorientFacesOp::setRefFiltersByConstructor()
+{
+  if ( constructorID() == CONSTRUCTOR_VOLUME )
+  {
+    myRefMeshFilter   ->setType( SMESH::MESH );// SMESH::NB_SMESH_TYPES
+    myRefGroupFilter  ->setType( SMESH::GROUP_VOLUME );
+    myRefSubMeshFilter->setType( SMESH::SUBMESH_SOLID );
+  }
+  else
+  {
+    myRefMeshFilter   ->setType( SMESH::NB_SMESH_TYPES ); // mesh not allowed
+    myRefGroupFilter  ->setType( SMESH::GROUP_FACE );
+    myRefSubMeshFilter->setType( SMESH::SUBMESH_FACE );
+  }
+}
+
 //================================================================================
 /*!
  * \brief get data from selection
@@ -483,12 +542,6 @@ void SMESHGUI_ReorientFacesOp::selectionDone()
   if ( !myDlg->isVisible() || !myDlg->isEnabled() )
     return;
 
-  if ( mySelectionMode == EVolumes )
-  {
-    SMESHGUI_SelectionOp::selectionDone();
-    return;
-  }
-
   myDlg->clearSelection( mySelectionMode );
 
   SALOME_ListIO aList;
@@ -497,6 +550,15 @@ void SMESHGUI_ReorientFacesOp::selectionDone()
   if ( nbSelected == 0 )
     return;
 
+  if ( onlyOneObjAllowed() && nbSelected != 1 )
+    return;
+
+  if ( mySelectionMode == ERefGroups )
+  {
+    SMESHGUI_SelectionOp::selectionDone();
+    return;
+  }
+
   Handle(SALOME_InteractiveObject) anIO = aList.First();
 
   try
@@ -505,35 +567,27 @@ void SMESHGUI_ReorientFacesOp::selectionDone()
     {
     case EObject: { // get an actor of object
 
-      if ( nbSelected == 1 )
-      {
-        myDlg->selectObject( EObject, anIO->getName(), 0, anIO->getEntry(), true );
-        // typeById( aList.First()->getEntry(),
-        //           SMESHGUI_SelectionOp::Object ),
-        myObjectActor = SMESH::FindActorByEntry( anIO->getEntry() );
-      }
+      SMESHGUI_SelectionOp::selectionDone();
+      myObjectActor = SMESH::FindActorByEntry( anIO->getEntry() );
       break;
     }
     case EFace: {  // get a face ID
 
-      if ( nbSelected == 1 )
+      SVTK_TIndexedMapOfVtkId faceIndices;
+      selector()->GetIndex( anIO, faceIndices );
+      if ( faceIndices.Extent() == 1 )
       {
-        SVTK_TIndexedMapOfVtkId faceIndices;
-        selector()->GetIndex( anIO, faceIndices );
-        if ( faceIndices.Extent() == 1 )
-        {
-          SMESH_Actor* savedActor = myObjectActor;
-          myObjectActor = 0; // to prevent work of onTextChange()
-          myDlg->setObjectText( EFace, QString("%1").arg( faceIndices(1) ));
-          myObjectActor = savedActor;
+        SMESH_Actor* savedActor = myObjectActor;
+        myObjectActor = 0; // to prevent work of onTextChange()
+        myDlg->setObjectText( EFace, QString("%1").arg( faceIndices(1) ));
+        myObjectActor = savedActor;
 
-          if ( !myObjectActor )
-          {
-            myDlg->selectObject( EObject, anIO->getName(), 0, anIO->getEntry(), true );
-            // typeById( aList.First()->getEntry(),
-            //           SMESHGUI_SelectionOp::Object ),
-            myObjectActor = SMESH::FindActorByEntry( anIO->getEntry() );
-          }
+        if ( !myObjectActor )
+        {
+          myDlg->selectObject( EObject, anIO->getName(), 0, anIO->getEntry(), true );
+          // typeById( aList.First()->getEntry(),
+          //           SMESHGUI_SelectionOp::Object ),
+          myObjectActor = SMESH::FindActorByEntry( anIO->getEntry() );
         }
       }
       break;
@@ -541,9 +595,6 @@ void SMESHGUI_ReorientFacesOp::selectionDone()
     case EPoint:
     case EDirection: {  // set XYZ by selected nodes or vertices
 
-      if ( mySelectionMode == EPoint && aList.Extent() > 1 )
-        return;
-
       TColgp_SequenceOfXYZ points;
       for( SALOME_ListIteratorOfListIO anIt( aList ); anIt.More(); anIt.Next() )
       {
@@ -660,24 +711,30 @@ bool SMESHGUI_ReorientFacesOp::onApply()
   try {
     SUIT_OverrideCursor wc;
 
-    SMESH::SMESH_Mesh_var aMesh = myObject->GetMesh();
+    SMESH::SMESH_Mesh_var aMesh = myObjects[0]->GetMesh();
     if ( aMesh->_is_nil() ) return false;
 
     SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
     if (aMeshEditor->_is_nil()) return false;
 
     int aResult = 0;
-    if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_VOLUME )
+    switch ( constructorID() )
+    {
+    case CONSTRUCTOR_VOLUME:
     {
-      SMESH::ListOfIDSources_var faceGroups = new SMESH::ListOfIDSources;
-      faceGroups->length(1);
-      faceGroups[0] = myObject;
-
       bool outsideNormal = myDlg->myOutsideChk->isChecked();
 
-      aResult = aMeshEditor->Reorient2DBy3D( faceGroups, myVolumeObj, outsideNormal );
+      aResult = aMeshEditor->Reorient2DBy3D( myObjects, myRefGroups[0], outsideNormal );
+
+      break;
+    }
+    case CONSTRUCTOR_FACE_GROUPS:
+    {
+      aResult = aMeshEditor->Reorient2DByNeighbours( myObjects, myRefGroups );
+
+      break;
     }
-    else
+    default:
     {
       SMESH::DirStruct direction;
       direction.PS.x = myDlg->myDX->GetValue();
@@ -685,7 +742,7 @@ bool SMESHGUI_ReorientFacesOp::onApply()
       direction.PS.z = myDlg->myDZ->GetValue();
 
       long face = myDlg->objectText( EFace ).toInt();
-      if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_POINT )
+      if ( constructorID() == CONSTRUCTOR_POINT )
         face = -1;
 
       SMESH::PointStruct point;
@@ -695,7 +752,8 @@ bool SMESHGUI_ReorientFacesOp::onApply()
 
       aMesh->SetParameters( aParameters.join(":").toUtf8().constData() );
 
-      aResult = aMeshEditor->Reorient2D( myObject, direction, face, point );
+      aResult = aMeshEditor->Reorient2D( myObjects[0], direction, face, point );
+    }
     }
 
     if (aResult)
@@ -726,47 +784,71 @@ bool SMESHGUI_ReorientFacesOp::onApply()
 
 bool SMESHGUI_ReorientFacesOp::isValid( QString& msg )
 {
-  // check object
-  QString objectEntry = myDlg->selectedObject( EObject );
-  _PTR(SObject) pSObject = SMESH::getStudy()->FindObjectID( objectEntry.toUtf8().data() );
-  myObject = SMESH::SMESH_IDSource::_narrow( _CAST( SObject,pSObject )->GetObject() );
-  if ( myObject->_is_nil() )
+  // Check objects
+
+  QStringList objectEntries;
+  myDlg->selectedObject( EObject, objectEntries );
+  if ( objectEntries.size() == 0 )
   {
     msg = tr("NO_OBJECT_SELECTED");
     return false;
   }
-  bool hasFaces = false;
-  SMESH::array_of_ElementType_var types = myObject->GetTypes();
-  for ( size_t i = 0; i < types->length() && !hasFaces; ++i )
-    hasFaces = ( types[i] == SMESH::FACE );
-  if ( !hasFaces )
+  myObjects->length( objectEntries.size() );
+  int nbObj = 0;
+  for ( QString & entry : objectEntries )
+  {
+    SMESH::SMESH_IDSource_var obj = SMESH::EntryToInterface< SMESH::SMESH_IDSource >( entry );
+    if ( !obj->_is_nil() )
+    {
+      bool hasFaces = false;
+      SMESH::array_of_ElementType_var types = obj->GetTypes();
+      for ( size_t i = 0; i < types->length() && !hasFaces; ++i )
+        hasFaces = ( types[i] == SMESH::FACE );
+      if ( hasFaces )
+        myObjects[ nbObj++ ] = SMESH::SMESH_IDSource::_duplicate( obj );
+    }
+  }
+  if ( nbObj == 0 )
   {
     msg = tr("NO_FACES");
     return false;
   }
+  myObjects->length( nbObj );
+
+  // Check volume object or ref faces
 
-  // check volume object
-  if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_VOLUME )
+  int constructorType = constructorID();
+  if ( constructorType >= CONSTRUCTOR_FACE_GROUPS )
   {
-    objectEntry = myDlg->selectedObject( EVolumes );
-    _PTR(SObject) pSObject = SMESH::getStudy()->FindObjectID( objectEntry.toUtf8().data() );
-    myVolumeObj = SMESH::SObjectToInterface< SMESH::SMESH_IDSource>( pSObject );
-    if ( myVolumeObj->_is_nil() )
+    objectEntries.clear();
+    myDlg->selectedObject( ERefGroups, objectEntries );
+    myRefGroups->length( objectEntries.size() );
+    nbObj = 0;
+    for ( QString & entry : objectEntries )
     {
-      msg = tr("NO_VOLUME_OBJECT_SELECTED");
-      return false;
+      SMESH::SMESH_IDSource_var obj = SMESH::EntryToInterface< SMESH::SMESH_IDSource >( entry );
+      if ( !obj->_is_nil() )
+      {
+        bool hasElems = false;
+        SMESH::ElementType elemType =
+          ( constructorType == CONSTRUCTOR_VOLUME ? SMESH::VOLUME : SMESH::FACE );
+        SMESH::array_of_ElementType_var types = obj->GetTypes();
+        for ( size_t i = 0; i < types->length() && !hasElems; ++i )
+          hasElems = ( types[i] == elemType );
+        if ( hasElems )
+          myRefGroups[ nbObj++ ] = SMESH::SMESH_IDSource::_duplicate( obj );
+      }
     }
-    bool hasVolumes = false;
-    types = myVolumeObj->GetTypes();
-    for ( size_t i = 0; i < types->length() && !hasVolumes; ++i )
-      hasVolumes = ( types[i] == SMESH::VOLUME );
-    if ( !hasVolumes )
+    if ( nbObj == 0 && constructorType == CONSTRUCTOR_VOLUME )
     {
       msg = tr("NO_VOLUMES");
       return false;
     }
+    myRefGroups->length( nbObj );
   }
-  // check vector
+
+  // Check vector
+
   gp_Vec vec( myDlg->myDX->GetValue(),
               myDlg->myDY->GetValue(),
               myDlg->myDZ->GetValue() );
@@ -776,8 +858,9 @@ bool SMESHGUI_ReorientFacesOp::isValid( QString& msg )
     return false;
   }
 
-  // check face ID
-  if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_FACE )
+  // Check face ID
+
+  if ( constructorID() == CONSTRUCTOR_FACE )
   {
     int faceID = myDlg->objectText( EFace ).toInt();
     bool faceOK = ( faceID > 0 );
@@ -789,8 +872,8 @@ bool SMESHGUI_ReorientFacesOp::isValid( QString& msg )
       }
       else
       {
-        SMESH::SMESH_Mesh_var aMesh = myObject->GetMesh();
-        if ( !aMesh->_is_nil() ) 
+        SMESH::SMESH_Mesh_var aMesh = myObjects[0]->GetMesh();
+        if ( !aMesh->_is_nil() )
           faceOK = ( aMesh->GetElementType( faceID, true ) == SMESH::FACE );
       }
     }
@@ -807,7 +890,7 @@ bool SMESHGUI_ReorientFacesOp::isValid( QString& msg )
 //================================================================================
 /*!
  * \brief Destructor
-*/
+ */
 //================================================================================
 
 SMESHGUI_ReorientFacesOp::~SMESHGUI_ReorientFacesOp()
@@ -828,6 +911,29 @@ LightApp_Dialog* SMESHGUI_ReorientFacesOp::dlg() const
   return myDlg;
 }
 
+//================================================================================
+/*!
+ * \brief ID of a current constructor: CONSTRUCTOR_FACE, CONSTRUCTOR_POINT etc.
+ */
+//================================================================================
+
+int SMESHGUI_ReorientFacesOp::constructorID()
+{
+  return myDlg->myConstructorGrp->checkedId();
+}
+
+//================================================================================
+/*!
+ * \brief Check if selection of multiple objects allowed
+ */
+//================================================================================
+
+bool SMESHGUI_ReorientFacesOp::onlyOneObjAllowed()
+{
+  return (( constructorID() <= CONSTRUCTOR_FACE ) ||
+          ( constructorID() == CONSTRUCTOR_VOLUME && mySelectionMode == ERefGroups ));
+}
+
 //================================================================================
 /*!
  * \brief update preview
@@ -836,110 +942,4 @@ LightApp_Dialog* SMESHGUI_ReorientFacesOp::dlg() const
 
 void SMESHGUI_ReorientFacesOp::redisplayPreview()
 {
-//   SMESH::MeshPreviewStruct_var aMeshPreviewStruct;
-
-//   bool moveShown = false;
-//   if ( myObjectActor)
-//   {
-//     const bool autoSearch = myDlg->myAutoSearchChkBox->isChecked();
-//     const bool preview    = myDlg->myPreviewChkBox->isChecked();
-//     if ( autoSearch )
-//     {
-//       myDlg->myCurrentX->SetValue(0);
-//       myDlg->myCurrentY->SetValue(0);
-//       myDlg->myCurrentZ->SetValue(0);
-//       myDlg->myDX->SetValue(0);
-//       myDlg->myDY->SetValue(0);
-//       myDlg->myDZ->SetValue(0);
-//       myDlg->myId->setText("");
-//     }
-//     QString msg;
-//     if ( autoSearch || isValid( msg ) )
-//     {
-//       try {
-//         SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myObjectActor->getIO());
-//         if (!aMesh->_is_nil()) {
-//           SMESH::SMESH_MeshEditor_var aPreviewer = aMesh->GetMeshEditPreviewer();
-//           if (!aPreviewer->_is_nil())
-//           {
-//             SUIT_OverrideCursor aWaitCursor;
-
-//             int anId = 0;
-//             if ( autoSearch )
-//               anId = aPreviewer->FindNodeClosestTo(myDlg->myX->GetValue(),
-//                                                    myDlg->myY->GetValue(),
-//                                                    myDlg->myZ->GetValue());
-//             else
-//               anId = myDlg->myId->text().toInt();
-
-//             // find id and/or just compute preview
-//             aPreviewer->MoveNode(anId,
-//                                  myDlg->myX->GetValue(),
-//                                  myDlg->myY->GetValue(),
-//                                  myDlg->myZ->GetValue());
-//             if ( autoSearch ) { // set found id
-//               QString idTxt("%1");
-//               if ( anId > 0 )
-//                 idTxt = idTxt.arg( anId );
-//               else
-//                 idTxt = "";
-//               myDlg->myId->setText( idTxt );
-//             }
-
-//             SMESH::double_array* aXYZ = aMesh->GetNodeXYZ( anId );
-//             if( aXYZ && aXYZ->length() >= 3 )
-//             {
-//               double x = aXYZ->operator[](0);
-//               double y = aXYZ->operator[](1);
-//               double z = aXYZ->operator[](2);
-//               double dx = myDlg->myX->GetValue() - x;
-//               double dy = myDlg->myY->GetValue() - y;
-//               double dz = myDlg->myZ->GetValue() - z;
-//               myDlg->myCurrentX->SetValue(x);
-//               myDlg->myCurrentY->SetValue(y);
-//               myDlg->myCurrentZ->SetValue(z);
-//               myDlg->myDX->SetValue(dx);
-//               myDlg->myDY->SetValue(dy);
-//               myDlg->myDZ->SetValue(dz);
-//             }
-
-//             if ( preview ) { // fill preview data
-//               aMeshPreviewStruct = aPreviewer->GetPreviewData();
-//               moveShown = ( anId > 0 );
-//             }
-//           }
-//         }
-//       }catch (...) {
-//       }
-//     }
-//   }
-
-//   if ( !moveShown )
-//   {
-//     aMeshPreviewStruct = new SMESH::MeshPreviewStruct();
-
-//     aMeshPreviewStruct->nodesXYZ.length(1);
-//     aMeshPreviewStruct->nodesXYZ[0].x = myDlg->myX->GetValue();
-//     aMeshPreviewStruct->nodesXYZ[0].y = myDlg->myY->GetValue();
-//     aMeshPreviewStruct->nodesXYZ[0].z = myDlg->myZ->GetValue();
-
-//     aMeshPreviewStruct->elementTypes.length(1);
-//     aMeshPreviewStruct->elementTypes[0].SMDS_ElementType = SMESH::NODE;
-//     aMeshPreviewStruct->elementTypes[0].isPoly = false;
-//     aMeshPreviewStruct->elementTypes[0].nbNodesInElement = 1;
-
-//     aMeshPreviewStruct->elementConnectivities.length(1);
-//     aMeshPreviewStruct->elementConnectivities[0] = 0;
-//   }
-
-//   // display data
-//   if ( & aMeshPreviewStruct.in() )
-//   {
-//     myVectorPreview->SetData(aMeshPreviewStruct.in());
-//   }
-//   else
-//   {
-//     myVectorPreview->SetVisibility(false);
-//   }
-
 }
index e9df0563abea9ce04edd70875dca72dd7db94846..5b86db6ffbce2627ac482e4a3864c6fa7d3f1d38 100644 (file)
 #define SMESHGUI_ReorientFacesDlg_H
 
 // SMESH includes
+
 #include "SMESH_SMESHGUI.hxx"
 
 #include "SMESHGUI_Dialog.h"
 #include "SMESHGUI_SelectionOp.h"
 
+#include <SALOMEconfig.h>
+#include CORBA_SERVER_HEADER(SMESH_Mesh)
+
 class QButtonGroup;
 class QCheckBox;
 class QLineEdit;
 class SMESHGUI_SpinBox;
 class SMESHGUI_ReorientFacesDlg;
+class SMESH_TypeFilter;
 
-/*!
+/*! ================================================================================
  * \brief Operation to reorient faces according to some criterion
  */
+
 class SMESHGUI_EXPORT SMESHGUI_ReorientFacesOp: public SMESHGUI_SelectionOp
 {
   Q_OBJECT
 
-public:
+ public:
   SMESHGUI_ReorientFacesOp();
   virtual ~SMESHGUI_ReorientFacesOp();
 
   virtual LightApp_Dialog*       dlg() const;
 
-protected:
+ protected:
 
   virtual void                   startOperation();
   virtual void                   stopOperation();
@@ -60,27 +66,35 @@ protected:
   virtual void                   selectionDone();
 
   bool                           isValid( QString& );
+  void                           setRefFiltersByConstructor();
+  int                            constructorID();
+
 
-protected slots:
+ protected slots:
   virtual bool                   onApply();
 
-private slots:
+ private slots:
   virtual void                   onActivateObject( int );
   void                           redisplayPreview();
   void                           onTextChange( const QString& );
+  bool                           onlyOneObjAllowed();
 
-private:
-  SMESHGUI_ReorientFacesDlg*    myDlg;
+ private:
+
+  SMESHGUI_ReorientFacesDlg* myDlg;
+
+  SMESH_Actor*               myObjectActor;
+  int                        mySelectionMode;
 
-  //SMESHGUI_MeshEditPreview*     myVectorPreview;
-  SMESH_Actor*                  myObjectActor;
-  int                           mySelectionMode;
+  SMESH_TypeFilter*          myRefGroupFilter;
+  SMESH_TypeFilter*          myRefSubMeshFilter;
+  SMESH_TypeFilter*          myRefMeshFilter;
 
-  SMESH::SMESH_IDSource_var     myObject;
-  SMESH::SMESH_IDSource_var     myVolumeObj;
+  SMESH::ListOfIDSources_var myObjects;
+  SMESH::ListOfIDSources_var myRefGroups;
 };
 
-/*!
+/*! ================================================================================
  * \brief Dialog to reorient faces according to vector
  */
 
@@ -88,7 +102,7 @@ class SMESHGUI_EXPORT SMESHGUI_ReorientFacesDlg : public SMESHGUI_Dialog
 {
   Q_OBJECT
 
-public:
+ public:
   SMESHGUI_ReorientFacesDlg();
 
 public slots:
@@ -96,12 +110,13 @@ public slots:
 
 private:
   QWidget*                      createMainFrame( QWidget* );
+  void                          setLabel( int object, const char* text );
   
   QButtonGroup*                 myConstructorGrp;
   QFrame*                       myFaceFrm;
   QFrame*                       myPointFrm;
   QFrame*                       myDirFrm;
-  QFrame*                       myVolumFrm;
+  QFrame*                       myRefGroupFrm;
   QCheckBox*                    myOutsideChk;
   SMESHGUI_SpinBox*             myX;
   SMESHGUI_SpinBox*             myY;
index 317a769cb4d70f8611b1ad10856535ddca63b81d..74697d463cd92204d47edc35af07032a0bdc5984 100644 (file)
             <source>ICON_DLG_REORIENT2D_FACE</source>
             <translation>reorient_faces_face.png</translation>
         </message>
+        <message>
+            <source>ICON_DLG_REORIENT2D_GROUPS</source>
+            <translation>reorient_faces_ref_groups.png</translation>
+        </message>
         <message>
             <source>ICON_DLG_REORIENT2D_VOLUME</source>
             <translation>reorient_faces_volume.png</translation>
index 8c4e75870869d68660addb0bc06e81e882fbeb48..67ca49fe593308d11be425a54e8790a2223714e5 100644 (file)
@@ -8609,6 +8609,10 @@ red in the Object Browser.</translation>
         <source>OBJECT</source>
         <translation>Object</translation>
     </message>
+     <message>
+         <source>OBJECTS</source>
+         <translation>Objects</translation>
+     </message>
     <message>
         <source>POINT</source>
         <translation>Point</translation>
@@ -8629,6 +8633,10 @@ red in the Object Browser.</translation>
         <source>VOLUMES</source>
         <translation>Volumes</translation>
     </message>
+    <message>
+        <source>REF_GROUPS</source>
+        <translation>Ref. objects (optional)</translation>
+    </message>
     <message>
         <source>OUTSIDE_VOLUME_NORMAL</source>
         <translation>Face normal outside volume</translation>
index a91b39971067867279a3f9c3dde0fece349bed7c..5ff73160634d09fc439c0299cc00e3a88a2768c4 100644 (file)
     </message>
     <message>
         <source>TOP_REORIENT_2D</source>
-        <translation>Réorienter des faces selon un vecteur</translation>
+        <translation>Réorienter des faces</translation>
     </message>
     <message>
         <source>MEN_REORIENT_2D</source>
-        <translation>Réorienter des faces selon un vecteur</translation>
+        <translation>Réorienter des faces</translation>
     </message>
     <message>
         <source>STB_REORIENT_2D</source>
-        <translation>Réorienter des faces selon un vecteur</translation>
+        <translation>Réorienter des faces</translation>
     </message>
     <message>
         <source>TOP_FIND_ELEM</source>
@@ -8613,7 +8613,7 @@ en rouge dans le browser.</translation>
     <name>SMESHGUI_ReorientFacesDlg</name>
     <message>
         <source>CAPTION</source>
-        <translation>Réorienter des faces selon un vector</translation>
+        <translation>Réorienter des faces</translation>
     </message>
     <message>
         <source>REORIENT_FACES</source>
@@ -8627,6 +8627,10 @@ en rouge dans le browser.</translation>
         <source>OBJECT</source>
         <translation>Objet</translation>
     </message>
+     <message>
+         <source>OBJECTS</source>
+         <translation>Objets</translation>
+     </message>
     <message>
         <source>POINT</source>
         <translation>Point</translation>
@@ -8647,6 +8651,10 @@ en rouge dans le browser.</translation>
         <source>VOLUMES</source>
         <translation>Volumes</translation>
     </message>
+    <message>
+        <source>REF_GROUPS</source>
+        <translation>Objets de référence (optionnel)</translation>
+    </message>
     <message>
         <source>OUTSIDE_VOLUME_NORMAL</source>
         <translation>Normale de face en dehors du volume</translation>
index 5f3456bb9b90c7ef0e5fb33348d3ad0bb03d3ea9..07b3d3399823699e3d26274f3b75a5e16b942b1b 100644 (file)
@@ -2489,8 +2489,8 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand)
       "RemoveElements","RemoveNodes","RemoveOrphanNodes",
       "AddNode","Add0DElement","AddEdge","AddFace","AddPolygonalFace","AddBall",
       "AddVolume","AddPolyhedralVolume","AddPolyhedralVolumeByFaces",
-      "MoveNode", "MoveClosestNodeToPoint",
-      "InverseDiag","DeleteDiag","Reorient","ReorientObject","Reorient2DBy3D",
+      "MoveNode", "MoveClosestNodeToPoint","InverseDiag","DeleteDiag",
+      "Reorient","ReorientObject","Reorient2DBy3D","Reorient2DByNeighbours",
       "TriToQuad","TriToQuadObject", "QuadTo4Tri", "SplitQuad","SplitQuadObject",
       "BestSplit","Smooth","SmoothObject","SmoothParametric","SmoothParametricObject",
       "ConvertToQuadratic","ConvertFromQuadratic","RenumberNodes","RenumberElements",
index f11615845c1dc4769cec4336ce6b4747885c9f6e..323a6603adf24d1994d9f0635c9964bc8c757539 100644 (file)
@@ -1696,7 +1696,8 @@ CORBA::Long SMESH_MeshEditor_i::Reorient2D(SMESH::SMESH_IDSource_ptr the2Dgroup,
   if ( dirVec.Magnitude() < std::numeric_limits< double >::min() )
     THROW_SALOME_CORBA_EXCEPTION("Zero size vector", SALOME::BAD_PARAM);
 
-  int nbReori = getEditor().Reorient2D( elements, dirVec, face );
+  TIDSortedElemSet refFaces = { face };
+  int nbReori = getEditor().Reorient2D( elements, dirVec, refFaces, /*allowNonManifold=*/true );
 
   if ( nbReori ) {
     declareMeshModified( /*isReComputeSafe=*/false );
@@ -1713,6 +1714,64 @@ CORBA::Long SMESH_MeshEditor_i::Reorient2D(SMESH::SMESH_IDSource_ptr the2Dgroup,
   return 0;
 }
 
+//=======================================================================
+//function : Reorient2DByNeighbours
+//purpose  : Reorient faces contained in a list of objectFaces
+//           equally to faces contained in a list of referenceFaces.
+//=======================================================================
+
+CORBA::Long
+SMESH_MeshEditor_i::Reorient2DByNeighbours(const SMESH::ListOfIDSources& theObjectFaces,
+                                           const SMESH::ListOfIDSources& theReferenceFaces)
+{
+  SMESH_TRY;
+  initData(/*deleteSearchers=*/false);
+
+  if ( theObjectFaces.length() == 0 )
+    return 0;
+
+  // get object faces
+  TIDSortedElemSet objFaces;
+  bool invalidObjFaces = false;
+  for ( CORBA::ULong i = 0; i < theObjectFaces.length(); ++i )
+  {
+    IDSource_Error err;
+    if ( !idSourceToSet( theObjectFaces[i], getMeshDS(), objFaces, SMDSAbs_Face,
+                         /*emptyIfIsMesh=*/1, &err ) &&
+         err == IDSource_INVALID )
+      invalidObjFaces = true;
+  }
+  if ( objFaces.empty() && invalidObjFaces )
+    THROW_SALOME_CORBA_EXCEPTION("No valid faces in given groups", SALOME::BAD_PARAM);
+
+  // get reference faces
+  TIDSortedElemSet refFaces;
+  for ( CORBA::ULong i = 0; i < theReferenceFaces.length(); ++i )
+  {
+    idSourceToSet( theReferenceFaces[i], getMeshDS(), refFaces, SMDSAbs_Face, /*emptyIfIsMesh=*/1 );
+  }
+  if ( refFaces.empty() && theReferenceFaces.length() > 0 )
+    THROW_SALOME_CORBA_EXCEPTION("Reference faces are invalid", SALOME::BAD_PARAM);
+
+
+  gp_Vec zeroVec( 0,0,0 );
+
+  // reorient
+  int nbReori = getEditor().Reorient2D( objFaces, zeroVec, refFaces, /*allowNonManifold=*/false );
+
+  if ( nbReori )
+    declareMeshModified( /*isReComputeSafe=*/false );
+
+  TPythonDump() << this << ".Reorient2DByNeighbours("
+                << theObjectFaces << ", "
+                << theReferenceFaces << ")";
+
+  return nbReori;
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+  return 0;
+}
+
 //=======================================================================
 //function : Reorient2DBy3D
 //purpose  : Reorient faces basing on orientation of adjacent volumes.
index 69f62d5396054fe75dee83da0abc29db7b71fa5a..3d7072ed42a88e79c478f628dd958863266b3729 100644 (file)
@@ -192,6 +192,17 @@ public:
                          const SMESH::DirStruct&   theDirection,
                          CORBA::Long               theFace,
                          const SMESH::PointStruct& thePoint);
+  /*!
+   * \brief Reorient faces contained in a list of \a objectFaces
+   *        equally to faces contained in a list of \a referenceFaces.
+   * \param objectFaces - faces to reorient in a list including either
+   *        the whole mesh or groups and/or sub-meshes.
+   * \param referenceFaces  - correctly oriented faces in a list of groups and/or sub-meshes.
+   *        It can be empty, then the 1st face in \a objectFaces is used as the reference.
+   * \return number of reoriented faces.
+   */
+  CORBA::Long Reorient2DByNeighbours(const SMESH::ListOfIDSources& objectFaces,
+                                     const SMESH::ListOfIDSources& referenceFaces);
   /*!
    * \brief Reorient faces basing on orientation of adjacent volumes.
    * \param faces - a list of objects containing face to reorient
index 47744206c2a5880c2c8544f93de209e647630642..1d5269c19c3d0df8f324f56a9ff8c948725260c0 100644 (file)
@@ -4664,6 +4664,29 @@ class Mesh(metaclass = MeshMeta):
             theFace = -1
         return self.editor.Reorient2D( the2DObject, theDirection, theFace, thePoint )
 
+    def Reorient2DByNeighbours(self, objectFaces, referenceFaces=[]):
+        """
+        Reorient faces contained in a list of *objectFaces*
+        equally to faces contained in a list of *referenceFaces*.
+
+        Parameters:
+                 objectFaces: list of :class:`mesh, sub-mesh, group, filter <SMESH.SMESH_IDSource>` holding faces to reorient.
+                 referenceFaces: list of :class:`sub-mesh, group, filter <SMESH.SMESH_IDSource>` holding reference faces. It can be empty, then any face in *objectFaces* is used as the reference.
+
+        Returns:
+                 number of reoriented faces.
+        """
+        if not isinstance( objectFaces, list ):
+            objectFaces = [ objectFaces ]
+        for i,obj2D in enumerate( objectFaces ):
+            if isinstance( obj2D, Mesh ):
+                objectFaces[i] = obj2D.GetMesh()
+        if not isinstance( referenceFaces, list ):
+            referenceFaces = [ referenceFaces ]
+
+        return self.editor.Reorient2DByNeighbours( objectFaces, referenceFaces )
+
+
     def Reorient2DBy3D(self, the2DObject, the3DObject, theOutsideNormal=True ):
         """
         Reorient faces according to adjacent volumes.
index dc82aa09a035697663a0f87732b4048c945036b1..c847d9efb3a7fbc880b18337c5b1ae257221064d 100644 (file)
@@ -5644,8 +5644,8 @@ namespace
           continue;
 
         gp_Dir direction(1,0,0);
-        const SMDS_MeshElement* anyFace = *facesToOrient.begin();
-        editor.Reorient2D( facesToOrient, direction, anyFace );
+        TIDSortedElemSet refFaces;
+        editor.Reorient2D( facesToOrient, direction, refFaces, /*allowNonManifold=*/true );
       }
     }
     return;