Salome HOME
022484: EDF 2304 SMESH: Reorient a group of faces regarding to a volume
authoreap <eap@opencascade.com>
Mon, 11 Aug 2014 13:30:26 +0000 (17:30 +0400)
committereap <eap@opencascade.com>
Mon, 11 Aug 2014 13:30:26 +0000 (17:30 +0400)
18 files changed:
doc/salome/examples/transforming_meshes_ex13.py
doc/salome/gui/SMESH/images/reorient_2d_face.png
doc/salome/gui/SMESH/images/reorient_2d_point.png
doc/salome/gui/SMESH/images/reorient_2d_volume.png [new file with mode: 0644]
doc/salome/gui/SMESH/input/reorient_faces.doc
idl/SMESH_MeshEditor.idl
resources/CMakeLists.txt
resources/reorient_faces_volume.png [new file with mode: 0644]
src/SMESH/SMESH_MeshEditor.cxx
src/SMESH/SMESH_MeshEditor.hxx
src/SMESHGUI/SMESHGUI_ReorientFacesDlg.cxx
src/SMESHGUI/SMESHGUI_ReorientFacesDlg.h
src/SMESHGUI/SMESH_images.ts
src/SMESHGUI/SMESH_msg_en.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

index fb56273..37a3f5e 100644 (file)
@@ -1,4 +1,4 @@
-# Reorient faces by vector
+# Reorient faces
 
 
 import salome
@@ -52,3 +52,20 @@ 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))
+
+
+# Use Reorient2DBy3D() to orient faces of 2 geom faces to have their normal pointing inside volumes
+
+mesh3D = smesh.Mesh( box, '3D mesh')
+mesh3D.AutomaticHexahedralization(0.5)
+group0 = mesh3D.Group( faces[0] )
+group1 = mesh3D.Group( faces[1] )
+
+# pass group0 and ids of faces of group1 to inverse
+nbRev = mesh3D.Reorient2DBy3D([ group0, group1.GetIDs() ], mesh3D, theOutsideNormal=False)
+print "Nb reoriented faces:", nbRev
+
+# orient the reversed faces back
+nbRev = mesh3D.Reorient2DBy3D( mesh3D, mesh3D, theOutsideNormal=True)
+print "Nb re-reoriented faces:", nbRev
+
index b143ac4..0407faf 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/reorient_2d_face.png and b/doc/salome/gui/SMESH/images/reorient_2d_face.png differ
index 844ac09..8440c91 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/reorient_2d_point.png and b/doc/salome/gui/SMESH/images/reorient_2d_point.png differ
diff --git a/doc/salome/gui/SMESH/images/reorient_2d_volume.png b/doc/salome/gui/SMESH/images/reorient_2d_volume.png
new file mode 100644 (file)
index 0000000..1d04de0
Binary files /dev/null and b/doc/salome/gui/SMESH/images/reorient_2d_volume.png differ
index db408ef..519da7e 100644 (file)
@@ -1,49 +1,66 @@
 /*!
 
-\page reorient_faces_page Reorient faces by vector
+\page reorient_faces_page Reorient faces
 
-\n This operation allows changing orientation of a set of neighboring
-faces. The desired orientation is defined by a vector. Since the direction
-of face normals in the set can be even opposite, it is necessary to
-specify a control face whose normal will be compared with the vector. This
-face can be either specified explicitly or found by proximity to
-a given point.
+\n This operation allows changing orientation of faces two ways.
+<ol>
+<li> To reorient a set of neighboring faces by defining the desired
+  orientation by a vector. <br> Since the direction of face normals in
+  the set can be even opposite, it is necessary to specify a control
+  face whose normal will be compared with the vector. This face can be
+  either <ul>
+    <li> found by proximity to a given point or </li> 
+    <li> specified explicitly. </li> 
+</ul> </li>
+<li> To reorient faces with relation to adjacent volumes. </li>
+</ol>
 
 Orientation of a face is changed by reverting the order of its nodes.
 
 <em>To change orientation of faces:</em>
 <ol>
-<li>In the \b Modification menu select <b>Reorient faces by
-    vector</b> item or click <em>Reorient faces by
-    vector</em> button in the toolbar.
+<li>In the \b Modification menu select <b>Reorient faces</b>
+  item or click <em>Reorient faces</em> button in the toolbar.
 
 <center>
 \image html reorient_faces_face.png
-<em>"Reorient faces by vector" button</em>
+<em>"Reorient faces" button</em>
 </center>
 
 The following dialog box will appear:
 
 <center>
-\image html reorient_2d_point.png
-\image html reorient_2d_face.png
+\image html reorient_2d_point.png "First mode: to reorient adjacent faces according to a vector. The control face is found by point."
+<br>
+\image html reorient_2d_face.png "Second mode: to reorient adjacent faces according to a vector. The control face is explicitly given."
+<br>
+\image html reorient_2d_volume.png "Third mode: to reorient faces with relation to adjacent volumes."
 </center>
 
 <li>In this dialog
 <ul>
-<li>Specify the way of selection of the control face: by point or
-  explicitly.</li>
-<li>Select the \b Object (mesh, sub-mesh or a group of faces) containing faces to reorient in the Object Browser or in the 3D Viewer.</li>
-<li>Specify the coordinates of the \b Point by which the control face
-  will be found or of the control \b Face itself. You can easy specify the \b
-  Point by either picking a node in the 3D Viewer or selecting a vertex
-  in the Object Browser. It is possible to pick the  \b Face by mouse in
-  the 3D Viewer or enter its ID.</li>
-<li>Set up the \b Direction vector to be compared with the normal of the
-  control face. If you pick a node in the 3D Viewer then the \b Direction
-  vector will go from the coordinate system origin to the selected node.
-  If you pick two nodes (holding Shift button) then the \b Direction vector
-  will go from the first to the second node.</li>
+<li>Specify either of the tree operation modes.</li>
+<li>Select the \b Object (mesh, sub-mesh or group)
+  containing faces to reorient, in the Object Browser or in the 3D
+  Viewer.</li>
+<li>To reorient according to vector: <ul>
+    <li>Specify the coordinates of the \b Point by which the control face
+      will be found or the control \b Face itself. You can easy specify the \b
+      Point by either picking a node in the 3D Viewer or selecting a vertex
+      in the Object Browser. It is possible to pick the \b Face by mouse in
+      the 3D Viewer or enter its ID.</li>
+    <li>Set up the \b Direction vector to be compared with the normal of the
+      control face. If you pick a node in the 3D Viewer then the \b Direction
+      vector will go from the coordinate system origin to the selected node.
+      If you pick two nodes (holding Shift button) then the \b Direction vector
+      will go from the first to the second node.</li> </ul> </li>
+<li>To reorient according to volumes: <ul>
+    <li>Select an object (mesh, sub-mesh or group) containing
+      reference \b Volumes, in the Object Browser or in the 3D
+      Viewer.</li>
+    <li>Specify whether face normals should point outside or inside
+      the reference volumes using <b>Face normal outside volume</b>
+      check-box.</li></ul> </li>
 </ul>
 </li>
 
@@ -51,6 +68,6 @@ The following dialog box will appear:
 </ol>
 
 <br><b>See Also</b> a sample TUI Script of a
-\ref tui_reorient_faces "Reorient faces by vector" operation. 
+\ref tui_reorient_faces "Reorient faces" operation. 
 
 */
index 397efff..293c8c8 100644 (file)
@@ -238,12 +238,23 @@ module SMESH
      * \param theFace - ID of face whose orientation is checked.
      *        It can be < 1 then \a thePoint is used to find a face.
      * \param thePoint - is used to find a face if \a theFace < 1.
-     * \return number of reoriented elements.
+     * \return number of reoriented faces.
      */
     long Reorient2D(in SMESH_IDSource the2Dgroup,
                     in DirStruct      theDirection,
                     in long           theFace,
                     in PointStruct    thePoint) raises (SALOME::SALOME_Exception);
+    /*!
+     * \brief Reorient faces basing on orientation of adjacent volumes.
+     * \param faces - a list of objects containing face to reorient
+     * \param volumes - an object containing volumes.
+     * \param outsideNormal - to orient faces to have their normal 
+     *        pointing either \a outside or \a inside the adjacent volumes.
+     * \return number of reoriented faces.
+     */
+    long Reorient2DBy3D(in ListOfIDSources faces,
+                        in SMESH_IDSource  volumes,
+                        in boolean         outsideNormal) raises (SALOME::SALOME_Exception);
 
     /*!
      * \brief Fuse neighbour triangles into quadrangles.
index f55e54a..6bab9ef 100755 (executable)
@@ -205,6 +205,7 @@ SET(SMESH_RESOURCES_FILES
   mesh_min_dist.png
   reorient_faces_point.png
   reorient_faces_face.png
+  reorient_faces_volume.png
   mesh_ball.png
   mesh_measure_basic_props.png
   mesh_measure_length.png
diff --git a/resources/reorient_faces_volume.png b/resources/reorient_faces_volume.png
new file mode 100644 (file)
index 0000000..e58bdb6
Binary files /dev/null and b/resources/reorient_faces_volume.png differ
index 6bdd80c..e216b2e 100644 (file)
@@ -1243,6 +1243,92 @@ int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
   return nbReori;
 }
 
+//================================================================================
+/*!
+ * \brief Reorient faces basing on orientation of adjacent volumes.
+ * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
+ * \param theVolumes - reference volumes.
+ * \param theOutsideNormal - to orient faces to have their normal
+ *        pointing either \a outside or \a inside the adjacent volumes.
+ * \return number of reoriented faces.
+ */
+//================================================================================
+
+int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
+                                      TIDSortedElemSet & theVolumes,
+                                      const bool         theOutsideNormal)
+{
+  int nbReori = 0;
+
+  SMDS_ElemIteratorPtr faceIt;
+  if ( theFaces.empty() )
+    faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
+  else
+    faceIt = elemSetIterator( theFaces );
+
+  vector< const SMDS_MeshNode* > faceNodes;
+  TIDSortedElemSet checkedVolumes;
+  set< const SMDS_MeshNode* > faceNodesSet;
+  SMDS_VolumeTool volumeTool;
+
+  while ( faceIt->more() ) // loop on given faces
+  {
+    const SMDS_MeshElement* face = faceIt->next();
+    if ( face->GetType() != SMDSAbs_Face )
+      continue;
+
+    const int nbCornersNodes = face->NbCornerNodes();
+    faceNodes.assign( face->begin_nodes(), face->end_nodes() );
+
+    checkedVolumes.clear();
+    SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
+    while ( vIt->more() )
+    {
+      const SMDS_MeshElement* volume = vIt->next();
+
+      if ( !checkedVolumes.insert( volume ).second )
+        continue;
+      if ( !theVolumes.empty() && !theVolumes.count( volume ))
+        continue;
+
+      // is volume adjacent?
+      bool allNodesCommon = true;
+      for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
+        allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
+      if ( !allNodesCommon )
+        continue;
+
+      // get nodes of a corresponding volume facet
+      faceNodesSet.clear();
+      faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
+      volumeTool.Set( volume );
+      int facetID = volumeTool.GetFaceIndex( faceNodesSet );
+      if ( facetID < 0 ) continue;
+      volumeTool.SetExternalNormal();
+      const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
+
+      // compare order of faceNodes and facetNodes
+      const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
+      int iNN[2];
+      for ( int i = 0; i < 2; ++i )
+      {
+        const SMDS_MeshNode* n = facetNodes[ i*iQ ];
+        for ( int iN = 0; iN < nbCornersNodes; ++iN )
+          if ( faceNodes[ iN ] == n )
+          {
+            iNN[ i ] = iN;
+            break;
+          }
+      }
+      bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
+      if ( isOutside != theOutsideNormal )
+        nbReori += Reorient( face );
+    }
+  }  // loop on given faces
+
+  return nbReori;
+}
+
 //=======================================================================
 //function : getBadRate
 //purpose  :
index c8f821f..5adb43a 100644 (file)
@@ -126,6 +126,12 @@ public:
   // Reverse theFaces whose orientation to be same as that of theFace
   // oriented according to theDirection. Return nb of reoriented faces
 
+  int Reorient2DBy3D (TIDSortedElemSet & theFaces,
+                      TIDSortedElemSet & theVolumes,
+                      const bool         theOutsideNormal);
+  // Reorient faces basing on orientation of adjacent volumes.
+  // Return nb of reoriented faces
+
   /*!
    * \brief Fuse neighbour triangles into quadrangles.
    * \param theElems     - The triangles to be fused.
index aee6f62..41e341c 100644 (file)
@@ -92,8 +92,8 @@
 #define SPACING 6
 #define MARGIN  11
 
-enum { CONSTRUCTOR_POINT=0, CONSTRUCTOR_FACE,
-       EObject, EPoint, EFace, EDirection };
+enum { CONSTRUCTOR_POINT=0, CONSTRUCTOR_FACE, CONSTRUCTOR_VOLUME,
+       EObject, EPoint, EFace, EDirection, EVolumes };
 
 //=======================================================================
 /*!
@@ -131,6 +131,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 iconReoriVolum (resMgr()->loadPixmap("SMESH", tr("ICON_DLG_REORIENT2D_VOLUME")));
 
   QGroupBox* aConstructorBox = new QGroupBox(tr("REORIENT_FACES"), aFrame);
   myConstructorGrp = new QButtonGroup(aConstructorBox);
@@ -149,6 +150,11 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   aConstructorGrpLayout->addWidget(aFaceBut);
   myConstructorGrp->addButton(aFaceBut, CONSTRUCTOR_FACE);
 
+  QRadioButton* aVolBut= new QRadioButton(aConstructorBox);
+  aVolBut->setIcon(iconReoriVolum);
+  aConstructorGrpLayout->addWidget(aVolBut);
+  myConstructorGrp->addButton(aVolBut, CONSTRUCTOR_VOLUME);
+
   // Create other controls
 
   setObjectPixmap( "SMESH", tr( "ICON_SELECT" ) );
@@ -157,17 +163,22 @@ 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 );
   setNameIndication( EFace, OneName );
   setReadOnly( EFace, false );
   if ( QLineEdit* le = qobject_cast<QLineEdit*>( objectWg( EFace, Control ) ))
     le->setValidator( new SMESHGUI_IdValidator( this,1 ));
 
-  const int width = aFaceBut->fontMetrics().width( tr("DIRECTION"));
+  int width = aFaceBut->fontMetrics().width( tr("DIRECTION"));
   objectWg( EDirection, Label )->setFixedWidth( width );
   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 );
 
   QLabel* aXLabel = new QLabel(tr("SMESH_X"), aFrame);
   myX = new SMESHGUI_SpinBox(aFrame);
@@ -197,6 +208,15 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   myDY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision");
   myDZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision");
 
+  width = Max( aFaceBut->fontMetrics().width( tr("SMESH_X")),
+               aFaceBut->fontMetrics().width( tr("SMESH_DX")));
+  aXLabel->setFixedWidth( width );
+  aYLabel->setFixedWidth( width );
+  aZLabel->setFixedWidth( width );
+  aDXLabel->setFixedWidth( width );
+  aDYLabel->setFixedWidth( width );
+  aDZLabel->setFixedWidth( width );
+
   // Layouting
 
   QGroupBox* anObjectGrp = new QGroupBox(tr("FACES"), aFrame);
@@ -213,12 +233,12 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   objectWg( EPoint, Control )->hide();
   aPointGrpLayout->addWidget( objectWg( EPoint, Label ) );
   aPointGrpLayout->addWidget( objectWg( EPoint, Btn ) );
-  aPointGrpLayout->addWidget( aXLabel );
-  aPointGrpLayout->addWidget( myX     );
-  aPointGrpLayout->addWidget( aYLabel );
-  aPointGrpLayout->addWidget( myY     );
-  aPointGrpLayout->addWidget( aZLabel );
-  aPointGrpLayout->addWidget( myZ     );
+  aPointGrpLayout->addWidget( aXLabel, 0 );
+  aPointGrpLayout->addWidget( myX,     1 );
+  aPointGrpLayout->addWidget( aYLabel, 0 );
+  aPointGrpLayout->addWidget( myY,     1 );
+  aPointGrpLayout->addWidget( aZLabel, 0 );
+  aPointGrpLayout->addWidget( myZ,     1 );
 
   myFaceFrm = new QFrame(aFrame);
   QHBoxLayout* aFaceGrpLayout = new QHBoxLayout(myFaceFrm);
@@ -227,25 +247,35 @@ QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent)
   aFaceGrpLayout->addWidget( objectWg( EFace, Btn ) );
   aFaceGrpLayout->addWidget( objectWg( EFace, Control ) );
 
-  QFrame* aDirectFrm = new QFrame(aFrame);
-  QHBoxLayout* aDirectGrpLayout = new QHBoxLayout(aDirectFrm);
+  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 );
+
+  myDirFrm = new QFrame(aFrame);
+  QHBoxLayout* aDirectGrpLayout = new QHBoxLayout(myDirFrm);
   aDirectGrpLayout->setMargin(0);
   objectWg( EDirection, Control )->hide();
   aDirectGrpLayout->addWidget( objectWg( EDirection, Label ) );
   aDirectGrpLayout->addWidget( objectWg( EDirection, Btn ) );
-  aDirectGrpLayout->addWidget(aDXLabel );
-  aDirectGrpLayout->addWidget(myDX );
-  aDirectGrpLayout->addWidget(aDYLabel );
-  aDirectGrpLayout->addWidget(myDY );
-  aDirectGrpLayout->addWidget(aDZLabel );
-  aDirectGrpLayout->addWidget(myDZ );
+  aDirectGrpLayout->addWidget( aDXLabel, 0 );
+  aDirectGrpLayout->addWidget( myDX,     1 );
+  aDirectGrpLayout->addWidget( aDYLabel, 0 );
+  aDirectGrpLayout->addWidget( myDY,     1 );
+  aDirectGrpLayout->addWidget( aDZLabel, 0 );
+  aDirectGrpLayout->addWidget( myDZ,     1 );
   
 
   QGroupBox* anOrientGrp = new QGroupBox(tr("ORIENTATION"), aFrame);
   QVBoxLayout* anOrientGrpLayout = new QVBoxLayout ( anOrientGrp );
   anOrientGrpLayout->addWidget(myPointFrm);
   anOrientGrpLayout->addWidget(myFaceFrm);
-  anOrientGrpLayout->addWidget(aDirectFrm);
+  anOrientGrpLayout->addWidget(myVolumFrm);
+  anOrientGrpLayout->addWidget(myDirFrm);
   
 
   QVBoxLayout* aLay = new QVBoxLayout(aFrame);
@@ -269,15 +299,27 @@ void SMESHGUI_ReorientFacesDlg::constructorChange(int id)
   if ( id == CONSTRUCTOR_FACE )
   {
     myPointFrm->hide();
+    myVolumFrm->hide();
     myFaceFrm->show();
+    myDirFrm->show();
     activateObject( EFace );
   }
-  else
+  else if ( id == CONSTRUCTOR_POINT )
   {
     myFaceFrm->hide();
+    myVolumFrm->hide();
     myPointFrm->show();
+    myDirFrm->show();
     activateObject( EPoint );
   }
+  else // CONSTRUCTOR_VOLUME
+  {
+    myFaceFrm->hide();
+    myPointFrm->hide();
+    myDirFrm->hide();
+    myVolumFrm->show();
+    activateObject( EVolumes );
+  }
 }
 
 //================================================================================
@@ -373,6 +415,7 @@ void SMESHGUI_ReorientFacesOp::onActivateObject( int what )
     SMESH::SetPickable();
     break;
   case EObject:
+  case EVolumes:
     SMESH::SetPointRepresentation(false);
     setSelectionMode( ActorSelection );
     break;
@@ -406,6 +449,14 @@ 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:
+    {
+      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 ));
+      return new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR );
+    }
   case EPoint:
     {
       QList<SUIT_SelectionFilter*> filters;
@@ -433,6 +484,12 @@ void SMESHGUI_ReorientFacesOp::selectionDone()
   if ( !myDlg->isVisible() || !myDlg->isEnabled() )
     return;
 
+  if ( mySelectionMode == EVolumes )
+  {
+    SMESHGUI_SelectionOp::selectionDone();
+    return;
+  }
+
   myDlg->clearSelection( mySelectionMode );
 
   SALOME_ListIO aList;
@@ -603,29 +660,45 @@ bool SMESHGUI_ReorientFacesOp::onApply()
 
   try {
     SUIT_OverrideCursor wc;
+
     SMESH::SMESH_Mesh_var aMesh = myObject->GetMesh();
     if ( aMesh->_is_nil() ) return false;
 
-    SMESH::DirStruct direction;
-    direction.PS.x = myDlg->myDX->GetValue();
-    direction.PS.y = myDlg->myDY->GetValue();
-    direction.PS.z = myDlg->myDZ->GetValue();
+    SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
+    if (aMeshEditor->_is_nil()) return false;
 
-    long face = myDlg->objectText( EFace ).toInt();
-    if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_POINT )
-      face = -1;
+    int aResult = 0;
+    if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_VOLUME )
+    {
+      SMESH::ListOfIDSources_var faceGroups = new SMESH::ListOfIDSources;
+      faceGroups->length(1);
+      faceGroups[0] = myObject;
 
-    SMESH::PointStruct point;
-    point.x = myDlg->myX->GetValue();
-    point.y = myDlg->myY->GetValue();
-    point.z = myDlg->myZ->GetValue();
+      bool outsideNormal = myDlg->myOutsideChk->isChecked();
 
-    SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
-    if (aMeshEditor->_is_nil()) return false;
+      aResult = aMeshEditor->Reorient2DBy3D( faceGroups, myVolumeObj, outsideNormal );
+    }
+    else
+    {
+      SMESH::DirStruct direction;
+      direction.PS.x = myDlg->myDX->GetValue();
+      direction.PS.y = myDlg->myDY->GetValue();
+      direction.PS.z = myDlg->myDZ->GetValue();
+
+      long face = myDlg->objectText( EFace ).toInt();
+      if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_POINT )
+        face = -1;
+
+      SMESH::PointStruct point;
+      point.x = myDlg->myX->GetValue();
+      point.y = myDlg->myY->GetValue();
+      point.z = myDlg->myZ->GetValue();
+
+      aMesh->SetParameters( aParameters.join(":").toLatin1().constData() );
 
-    aMesh->SetParameters( aParameters.join(":").toLatin1().constData() );
+      aResult = aMeshEditor->Reorient2D( myObject, direction, face, point );
+    }
 
-    int aResult = aMeshEditor->Reorient2D( myObject, direction, face, point );
     if (aResult)
     {
       SALOME_ListIO aList;
@@ -673,6 +746,27 @@ bool SMESHGUI_ReorientFacesOp::isValid( QString& msg )
     return false;
   }
 
+  // check volume object
+  if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_VOLUME )
+  {
+    objectEntry = myDlg->selectedObject( EVolumes );
+    _PTR(SObject) pSObject = studyDS()->FindObjectID( objectEntry.toLatin1().data() );
+    myVolumeObj = SMESH::SObjectToInterface< SMESH::SMESH_IDSource>( pSObject );
+    if ( myVolumeObj->_is_nil() )
+    {
+      msg = tr("NO_VOLUME_OBJECT_SELECTED");
+      return false;
+    }
+    bool hasVolumes = false;
+    types = myVolumeObj->GetTypes();
+    for ( size_t i = 0; i < types->length() && !hasVolumes; ++i )
+      hasVolumes = ( types[i] == SMESH::VOLUME );
+    if ( !hasVolumes )
+    {
+      msg = tr("NO_VOLUMES");
+      return false;
+    }
+  }
   // check vector
   gp_Vec vec( myDlg->myDX->GetValue(),
               myDlg->myDY->GetValue(),
index 400850b..e090817 100644 (file)
 #include "SMESHGUI_SelectionOp.h"
 
 class QButtonGroup;
+class QCheckBox;
 class QLineEdit;
 class SMESHGUI_SpinBox;
 class SMESHGUI_ReorientFacesDlg;
 
 /*!
- * \brief Operation to reorient faces acoording to vector
+ * \brief Operation to reorient faces acoording to some criterion
  */
 class SMESHGUI_EXPORT SMESHGUI_ReorientFacesOp: public SMESHGUI_SelectionOp
 {
@@ -76,6 +77,7 @@ private:
   int                           mySelectionMode;
 
   SMESH::SMESH_IDSource_var     myObject;
+  SMESH::SMESH_IDSource_var     myVolumeObj;
 };
 
 /*!
@@ -98,6 +100,9 @@ private:
   QButtonGroup*                 myConstructorGrp;
   QFrame*                       myFaceFrm;
   QFrame*                       myPointFrm;
+  QFrame*                       myDirFrm;
+  QFrame*                       myVolumFrm;
+  QCheckBox*                    myOutsideChk;
   SMESHGUI_SpinBox*             myX;
   SMESHGUI_SpinBox*             myY;
   SMESHGUI_SpinBox*             myZ;
index b27b101..63187d1 100644 (file)
             <translation>reorient_faces_face.png</translation>
         </message>
         <message>
+            <source>ICON_DLG_REORIENT2D_VOLUME</source>
+            <translation>reorient_faces_volume.png</translation>
+        </message>
+        <message>
             <source>ICON_REORIENT_2D</source>
             <translation>reorient_faces_face.png</translation>
         </message>
index 7e1bb3d..fcb3774 100644 (file)
     </message>
     <message>
         <source>TOP_REORIENT_2D</source>
-        <translation>Reorient faces by vector</translation>
+        <translation>Reorient faces</translation>
     </message>
     <message>
         <source>MEN_REORIENT_2D</source>
-        <translation>Reorient faces by vector</translation>
+        <translation>Reorient faces</translation>
     </message>
     <message>
         <source>STB_REORIENT_2D</source>
-        <translation>Reorient faces by vector</translation>
+        <translation>Reorient faces</translation>
     </message>
     <message>
         <source>TOP_FIND_ELEM</source>
@@ -7656,7 +7656,7 @@ as they are of improper type:
     <name>SMESHGUI_ReorientFacesDlg</name>
     <message>
         <source>CAPTION</source>
-        <translation>Reorient faces by vector</translation>
+        <translation>Reorient faces</translation>
     </message>
     <message>
         <source>REORIENT_FACES</source>
@@ -7686,6 +7686,14 @@ as they are of improper type:
         <source>ORIENTATION</source>
         <translation>Orientation</translation>
     </message>
+    <message>
+        <source>VOLUMES</source>
+        <translation>Volumes</translation>
+    </message>
+    <message>
+        <source>OUTSIDE_VOLUME_NORMAL</source>
+        <translation>Face normal outside volume</translation>
+    </message>
 </context>
 <context>
     <name>SMESHGUI_ReorientFacesOp</name>
@@ -7694,10 +7702,18 @@ as they are of improper type:
         <translation>No object selected</translation>
     </message>
     <message>
+        <source>NO_VOLUME_OBJECT_SELECTED</source>
+        <translation>No volume object selected</translation>
+    </message>
+    <message>
         <source>NO_FACES</source>
         <translation>Object includes no faces</translation>
     </message>
     <message>
+        <source>NO_VOLUMES</source>
+        <translation>Volume object includes no volumes</translation>
+    </message>
+    <message>
         <source>ZERO_SIZE_VECTOR</source>
         <translation>Zero size vector</translation>
     </message>
index 9a7550d..5646327 100644 (file)
@@ -2369,7 +2369,7 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand)
       "AddNode","Add0DElement","AddEdge","AddFace","AddPolygonalFace","AddBall",
       "AddVolume","AddPolyhedralVolume","AddPolyhedralVolumeByFaces",
       "MoveNode", "MoveClosestNodeToPoint",
-      "InverseDiag","DeleteDiag","Reorient","ReorientObject",
+      "InverseDiag","DeleteDiag","Reorient","ReorientObject","Reorient2DBy3D",
       "TriToQuad","TriToQuadObject", "QuadTo4Tri", "SplitQuad","SplitQuadObject",
       "BestSplit","Smooth","SmoothObject","SmoothParametric","SmoothParametricObject",
       "ConvertToQuadratic","ConvertFromQuadratic","RenumberNodes","RenumberElements",
index 9de57c9..fd69f00 100644 (file)
@@ -1645,7 +1645,7 @@ CORBA::Long SMESH_MeshEditor_i::Reorient2D(SMESH::SMESH_IDSource_ptr the2Dgroup,
   TIDSortedElemSet elements;
   prepareIdSource( the2Dgroup );
   if ( !idSourceToSet( the2Dgroup, getMeshDS(), elements, SMDSAbs_Face, /*emptyIfIsMesh=*/1))
-    THROW_SALOME_CORBA_EXCEPTION("No faces in given group", SALOME::BAD_PARAM);
+    return 0;//THROW_SALOME_CORBA_EXCEPTION("No faces in given group", SALOME::BAD_PARAM);
 
 
   const SMDS_MeshElement* face = 0;
@@ -1710,6 +1710,55 @@ CORBA::Long SMESH_MeshEditor_i::Reorient2D(SMESH::SMESH_IDSource_ptr the2Dgroup,
   return 0;
 }
 
+//=======================================================================
+//function : Reorient2DBy3D
+//purpose  : Reorient faces basing on orientation of adjacent volumes.
+//=======================================================================
+
+CORBA::Long SMESH_MeshEditor_i::Reorient2DBy3D(const SMESH::ListOfIDSources& faceGroups,
+                                               SMESH::SMESH_IDSource_ptr     volumeGroup,
+                                               CORBA::Boolean                outsideNormal)
+  throw (SALOME::SALOME_Exception)
+{
+  SMESH_TRY;
+  initData();
+
+  TIDSortedElemSet volumes;
+  prepareIdSource( volumeGroup );
+  if ( !idSourceToSet( volumeGroup, getMeshDS(), volumes, SMDSAbs_Volume, /*emptyIfIsMesh=*/1))
+      THROW_SALOME_CORBA_EXCEPTION("No volumes in a given object", SALOME::BAD_PARAM);
+
+  int nbReori = 0;
+  for ( size_t i = 0; i < faceGroups.length(); ++i )
+  {
+    SMESH::SMESH_IDSource_ptr faceGrp = faceGroups[i].in();
+    prepareIdSource( faceGrp );
+
+    TIDSortedElemSet faces;
+    if ( !idSourceToSet( faceGrp, getMeshDS(), faces, SMDSAbs_Face, /*emptyIfIsMesh=*/1) &&
+         faceGroups.length() == 1 )
+      ; //THROW_SALOME_CORBA_EXCEPTION("No faces in a given object", SALOME::BAD_PARAM);
+
+    nbReori += getEditor().Reorient2DBy3D( faces, volumes, outsideNormal );
+
+    if ( faces.empty() ) // all faces in the mesh treated
+      break;
+  }
+
+  if ( nbReori ) {
+    declareMeshModified( /*isReComputeSafe=*/false );
+  }
+  TPythonDump() << this << ".Reorient2DBy3D( "
+                << faceGroups << ", "
+                << volumeGroup << ", "
+                << outsideNormal << " )";
+
+  return nbReori;
+
+  SMESH_CATCH( SMESH::throwCorbaException );
+  return 0;
+}
+
 //=============================================================================
 /*!
  * \brief Fuse neighbour triangles into quadrangles.
index ead3048..bce7c4c 100644 (file)
@@ -203,6 +203,18 @@ public:
                          const SMESH::DirStruct&   theDirection,
                          CORBA::Long               theFace,
                          const SMESH::PointStruct& thePoint) throw (SALOME::SALOME_Exception);
+  /*!
+   * \brief Reorient faces basing on orientation of adjacent volumes.
+   * \param faces - a list of objects containing face to reorient
+   * \param volumes - an object containing volumes.
+   * \param outsideNormal - to orient faces to have their normal
+   *        pointing either \a outside or \a inside the adjacent volumes.
+   * \return number of reoriented faces.
+   */
+  CORBA::Long Reorient2DBy3D(const SMESH::ListOfIDSources & faces,
+                             SMESH::SMESH_IDSource_ptr      volumes,
+                             CORBA::Boolean                 outsideNormal)
+    throw (SALOME::SALOME_Exception);
 
   // Split/Join faces
   CORBA::Boolean TriToQuad       (const SMESH::long_array &   IDsOfElements,
index 767a5ef..a51e40c 100644 (file)
@@ -2964,6 +2964,37 @@ class Mesh:
             theFace = -1
         return self.editor.Reorient2D( the2DObject, theDirection, theFace, thePoint )
 
+    ## Reorient faces according to adjacent volumes.
+    #  @param the2DObject is a mesh, sub-mesh, group or list of
+    #         either IDs of faces or face groups.
+    #  @param the3DObject is a mesh, sub-mesh, group or list of IDs of volumes.
+    #  @param theOutsideNormal to orient faces to have their normals
+    #         pointing either \a outside or \a inside the adjacent volumes.
+    #  @return number of reoriented faces.
+    #  @ingroup l2_modif_changori
+    def Reorient2DBy3D(self, the2DObject, the3DObject, theOutsideNormal=True ):
+        unRegister = genObjUnRegister()
+        # check the2DObject
+        if not isinstance( the2DObject, list ):
+            the2DObject = [ the2DObject ]
+        elif the2DObject and isinstance( the2DObject[0], int ):
+            the2DObject = self.GetIDSource( the2DObject, SMESH.FACE )
+            unRegister.set( the2DObject )
+            the2DObject = [ the2DObject ]
+        for i,obj2D in enumerate( the2DObject ):
+            if isinstance( obj2D, Mesh ):
+                the2DObject[i] = obj2D.GetMesh()
+            if isinstance( obj2D, list ):
+                the2DObject[i] = self.GetIDSource( obj2D, SMESH.FACE )
+                unRegister.set( the2DObject[i] )
+        # check the3DObject
+        if isinstance( the3DObject, Mesh ):
+            the3DObject = the3DObject.GetMesh()
+        if isinstance( the3DObject, list ):
+            the3DObject = self.GetIDSource( the3DObject, SMESH.VOLUME )
+            unRegister.set( the3DObject )
+        return self.editor.Reorient2DBy3D( the2DObject, the3DObject, theOutsideNormal )
+
     ## Fuses the neighbouring triangles into quadrangles.
     #  @param IDsOfElements The triangles to be fused,
     #  @param theCriterion  is a numerical functor, in terms of enum SMESH.FunctorType, used to