Salome HOME
IMP 23612: EDF 14143 - Compute angle from 3 points
authoreap <eap@opencascade.com>
Thu, 29 Nov 2018 14:31:43 +0000 (17:31 +0300)
committereap <eap@opencascade.com>
Thu, 29 Nov 2018 14:31:43 +0000 (17:31 +0300)
26 files changed:
doc/salome/examples/measurements_ex04.py [new file with mode: 0644]
doc/salome/examples/tests.set
doc/salome/gui/SMESH/images/angle_measure.png [new file with mode: 0644]
doc/salome/gui/SMESH/images/basic_props.png
doc/salome/gui/SMESH/images/bnd_box.png
doc/salome/gui/SMESH/images/min_distance.png
doc/salome/gui/SMESH/input/face_groups_by_sharp_edges.rst
doc/salome/gui/SMESH/input/grouping_elements.rst
doc/salome/gui/SMESH/input/measurements.rst
doc/salome/gui/SMESH/input/modules.rst
doc/salome/gui/SMESH/input/tui_measurements.rst
idl/SMESH_Measurements.idl
idl/SMESH_Mesh.idl
resources/CMakeLists.txt
resources/mesh_angle_measure.png [new file with mode: 0644]
src/SMESHGUI/SMESHGUI.cxx
src/SMESHGUI/SMESHGUI_Measurements.cxx
src/SMESHGUI/SMESHGUI_Measurements.h
src/SMESHGUI/SMESHGUI_Operations.h
src/SMESHGUI/SMESHGUI_VTKUtils.cxx
src/SMESHGUI/SMESH_images.ts
src/SMESHGUI/SMESH_msg_en.ts
src/SMESH_I/SMESH_2smeshpy.cxx
src/SMESH_I/SMESH_Measurements_i.cxx
src/SMESH_I/SMESH_Measurements_i.hxx
src/SMESH_SWIG/smeshBuilder.py

diff --git a/doc/salome/examples/measurements_ex04.py b/doc/salome/examples/measurements_ex04.py
new file mode 100644 (file)
index 0000000..6524ac9
--- /dev/null
@@ -0,0 +1,26 @@
+# Angle measurement
+
+
+import salome
+salome.salome_init()
+from salome.smesh import smeshBuilder
+smesh =  smeshBuilder.New()
+
+# use smeshBuilder.GetAngle() to compute angle between 3 arbitrary points
+
+p0 = [1,0,0]
+p1 = [0,0,0]
+p2 = [0,1,0]
+
+a1 = smesh.GetAngle(p0, p1, p2)
+print("Right angle measure", a1 )
+
+# use Mesh.GetAngle() to compute angle between 3 nodes of a mesh
+
+mesh = smesh.Mesh()
+n0 = mesh.AddNode( *p0 )
+n1 = mesh.AddNode( *p1 )
+n2 = mesh.AddNode( *p2 )
+
+a2 = mesh.GetAngle( n0,n1,n2 )
+
index f8940611d3192e5b64689917eb5878d21f52e6d2..29910eda8917f9ce92ed945eedbf855f559399ed 100644 (file)
@@ -115,6 +115,8 @@ SET(GOOD_TESTS
   grouping_elements_ex09.py
   measurements_ex01.py
   measurements_ex02.py
   grouping_elements_ex09.py
   measurements_ex01.py
   measurements_ex02.py
+  measurements_ex03.py
+  measurements_ex04.py
   modifying_meshes_ex01.py
   modifying_meshes_ex02.py
   modifying_meshes_ex03.py
   modifying_meshes_ex01.py
   modifying_meshes_ex02.py
   modifying_meshes_ex03.py
diff --git a/doc/salome/gui/SMESH/images/angle_measure.png b/doc/salome/gui/SMESH/images/angle_measure.png
new file mode 100644 (file)
index 0000000..80a2116
Binary files /dev/null and b/doc/salome/gui/SMESH/images/angle_measure.png differ
index bba200d09384ef4c28feff548144436b2f118132..abf3aa32c3223a28aee7aa6f1716d2cc03c0ba0f 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/basic_props.png and b/doc/salome/gui/SMESH/images/basic_props.png differ
index d079eaaa4b38a970146d8aa085201a8ade3eec78..4c0bbd3e67b057c8956ad386a57daa83838279b5 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/bnd_box.png and b/doc/salome/gui/SMESH/images/bnd_box.png differ
index 486dab376e386fffbaba4798aa49939daef52d5e..294178f2a2526c05df261acd2a2597bbb1e99cab 100644 (file)
Binary files a/doc/salome/gui/SMESH/images/min_distance.png and b/doc/salome/gui/SMESH/images/min_distance.png differ
index 5fdee17c5049bec16614a8663487da5102eb2569..eed6996b4f546e8677858011d3ea1ce61497df72 100644 (file)
@@ -2,7 +2,7 @@
 Face Groups Separated By Sharp Edges
 ************************************
 
 Face Groups Separated By Sharp Edges
 ************************************
 
-**Face groups separated by sharp edges** operation distributes all faces of the mesh between groups using sharp edges and optionally existing 1D elements as group boundaries. Edges where more than two faces meet are always considered as a group boundary. The operation is available in **Mesh** menu.
+**Face groups separated by sharp edges** operation distributes all faces of the mesh among groups using sharp edges and optionally existing 1D elements as group boundaries. Edges where more than two faces meet are always considered as a group boundary. The operation is available in **Mesh** menu.
 
 The operation dialog looks as follows:
 
 
 The operation dialog looks as follows:
 
@@ -11,7 +11,7 @@ The operation dialog looks as follows:
 
 In this dialog box specify
 
 
 In this dialog box specify
 
-       * **Mesh** including the faces to distribute between groups.
+       * **Mesh** including the faces to distribute among groups.
         * **Sharp angle** in degrees, by which edges used as group boundaries are detected. An edge is considered as a group boundary if an angle between normals of adjacent faces is more than this angle.
        * Activate **Create edges** option if you wish that 1D elements to be created (if not yet exist) on the edges that served as group boundaries.
        * Activate **Use existing edges** option if you wish that existing 1D elements to be used as group boundaries.
         * **Sharp angle** in degrees, by which edges used as group boundaries are detected. An edge is considered as a group boundary if an angle between normals of adjacent faces is more than this angle.
        * Activate **Create edges** option if you wish that 1D elements to be created (if not yet exist) on the edges that served as group boundaries.
        * Activate **Use existing edges** option if you wish that existing 1D elements to be used as group boundaries.
index 57dcfdb8cea8c56b46da9300d701c8458dc2dda6..2846fa94b3d18b37ddeddb15d280aafd084d19fc 100644 (file)
@@ -27,7 +27,7 @@ The following ways of group creation are possible:
 
 * :ref:`Create group <creating_groups_page>` dialog allows creation of a group of any type: :ref:`Standalone group<standalone_group>`, :ref:`Group on geometry <group_on_geom>` and :ref:`Group on filter <group_on_filter>` using dedicated tabs.
 * :ref:`Create Groups from Geometry <create_groups_from_geometry_page>` dialog allows creation of several groups on geometry at once.
 
 * :ref:`Create group <creating_groups_page>` dialog allows creation of a group of any type: :ref:`Standalone group<standalone_group>`, :ref:`Group on geometry <group_on_geom>` and :ref:`Group on filter <group_on_filter>` using dedicated tabs.
 * :ref:`Create Groups from Geometry <create_groups_from_geometry_page>` dialog allows creation of several groups on geometry at once.
-* :doc:`face_groups_by_sharp_edges` operation distributes all faces of the mesh between groups using sharp edges and/or existing 1D elements as group boundaries.
+* :doc:`face_groups_by_sharp_edges` operation distributes all faces of the mesh among groups using sharp edges and/or existing 1D elements as group boundaries.
 * Standalone groups of all nodes and elements of the chosen sub-mesh (type of elements depends on dimension of sub-mesh geometry) can be created using **Mesh -> Construct Group** menu item (available from the context menu as well).
 * Standalone groups of any element type can be created basing on nodes of other groups - using :ref:`Group based on nodes of other groups <group_of_underlying_elements_page>` dialog.
 * Standalone groups can be created by applying :ref:`Boolean operations <using_operations_on_groups_page>` to other groups.
 * Standalone groups of all nodes and elements of the chosen sub-mesh (type of elements depends on dimension of sub-mesh geometry) can be created using **Mesh -> Construct Group** menu item (available from the context menu as well).
 * Standalone groups of any element type can be created basing on nodes of other groups - using :ref:`Group based on nodes of other groups <group_of_underlying_elements_page>` dialog.
 * Standalone groups can be created by applying :ref:`Boolean operations <using_operations_on_groups_page>` to other groups.
index ed1426a0ae727d9b0a38a844c9d64aa8350d1906..248ea4f02a9a7841789d18317151d64467030316 100644 (file)
@@ -84,6 +84,17 @@ The result of calculation will be shown in the bottom area of the dialog.
        * As calculation result is a sum of lengths, areas and volumes of all mesh elements, the duplication is not taken into account; i.e. all duplicated elements (elements built on the same set of nodes) will be included into the result.
        * Similarly, intersection of elements is not taken into account.
 
        * As calculation result is a sum of lengths, areas and volumes of all mesh elements, the duplication is not taken into account; i.e. all duplicated elements (elements built on the same set of nodes) will be included into the result.
        * Similarly, intersection of elements is not taken into account.
 
-**See Also** a sample TUI Script of :ref:`tui_measurements_page`.
+.. _angle_anchor:
+
+Angle
+#####
 
 
+This operation measures angle defined by three nodes. The second of the specified nodes is a vertex of angle. 
 
 
+You can specify nodes either by clicking them in the Viewer or by typing their IDs in **Tree nodes** field. If the nodes are correctly specified, upon pressing **Compute** button the angle measure will be displayed and the angle will be shown in the Viewer.
+
+.. image:: ../images/angle_measure.png
+       :align: center
+
+
+**See Also** a sample TUI Script of :ref:`tui_measurements_page`.
index 45494b2a5c09d3d1941bc82ba3a184338cb68fe2..6c673f6dbfb4513273465e79ca5e765cffa01194 100644 (file)
@@ -168,6 +168,7 @@ Creating groups
    Mesh.MakeGroupByCriterion
    Mesh.MakeGroupByCriteria
    Mesh.MakeGroupByFilter
    Mesh.MakeGroupByCriterion
    Mesh.MakeGroupByCriteria
    Mesh.MakeGroupByFilter
+   Mesh.FaceGroupsSeparatedByEdges
    Mesh.GetGroups
    Mesh.NbGroups
    Mesh.GetGroupNames
    Mesh.GetGroups
    Mesh.NbGroups
    Mesh.GetGroupNames
@@ -305,6 +306,7 @@ Measurements
    smeshBuilder.GetLength
    smeshBuilder.GetArea
    smeshBuilder.GetVolume
    smeshBuilder.GetLength
    smeshBuilder.GetArea
    smeshBuilder.GetVolume
+   smeshBuilder.GetAngle
    Mesh.GetFreeBorders
    Mesh.MinDistance
    Mesh.GetMinDistance
    Mesh.GetFreeBorders
    Mesh.MinDistance
    Mesh.GetMinDistance
@@ -315,6 +317,7 @@ Measurements
    Mesh.GetLength
    Mesh.GetArea
    Mesh.GetVolume
    Mesh.GetLength
    Mesh.GetArea
    Mesh.GetVolume
+   Mesh.GetAngle
 
 ****************
 Modifying meshes
 
 ****************
 Modifying meshes
index b3f5964509e71cf5f6b01459561bd69add74bf4a..a4e056531e0ed3aff3b0555d4c1338b00f5931a7 100644 (file)
@@ -33,3 +33,11 @@ Basic Properties
     :language: python
 
 :download:`Download this script <../../../examples/measurements_ex03.py>`
     :language: python
 
 :download:`Download this script <../../../examples/measurements_ex03.py>`
+
+Angle
+=====
+
+.. literalinclude:: ../../../examples/measurements_ex04.py
+    :language: python
+
+:download:`Download this script <../../../examples/measurements_ex04.py>`
index d3a9c77f450df1aa7798b093e419df296c01c5b5..bf8dc2152282abf0be6053d42efd8e6426a1f5de 100644 (file)
@@ -74,6 +74,11 @@ module SMESH
      * gravity center of the source
      */
     PointStruct GravityCenter(in SMESH_IDSource source);
      * gravity center of the source
      */
     PointStruct GravityCenter(in SMESH_IDSource source);
+
+    /*!
+     * angle in radians defined by 3 points <(p1,p2,p3)
+     */
+    double Angle(in PointStruct p1, in PointStruct p2, in PointStruct p3 );
   };
 };
 
   };
 };
 
index cb25bb18318c4bf2003ac7919b238939b666eb90..c8c7bd786c839c2981fc82a4f108291feff927ec 100644 (file)
@@ -517,7 +517,7 @@ module SMESH
       raises (SALOME::SALOME_Exception);
 
     /*!
       raises (SALOME::SALOME_Exception);
 
     /*!
-     * Distribute all faces of the mesh between groups using sharp edges and optionally
+     * Distribute all faces of the mesh among groups using sharp edges and optionally
      * existing 1D elements as group boundaries.
      *  \param [in] sharpAngle - edge is considered sharp if an angle between normals of
      *              adjacent faces is more than \a sharpAngle in degrees.
      * existing 1D elements as group boundaries.
      *  \param [in] sharpAngle - edge is considered sharp if an angle between normals of
      *              adjacent faces is more than \a sharpAngle in degrees.
index 2196b1b8c0ee9d8616f7325077460e6490412030..638e99c3027cd8410615a22c3134326f0bc9f718 100755 (executable)
@@ -232,6 +232,7 @@ SET(SMESH_RESOURCES_FILES
   mesh_deflection.png
   mesh_offset.png
   mesh_face_groups_by_edges.png
   mesh_deflection.png
   mesh_offset.png
   mesh_face_groups_by_edges.png
+  mesh_angle_measure.png
 )
 
 INSTALL(FILES ${SMESH_RESOURCES_FILES} DESTINATION ${SALOME_SMESH_INSTALL_RES_DATA})
 )
 
 INSTALL(FILES ${SMESH_RESOURCES_FILES} DESTINATION ${SALOME_SMESH_INSTALL_RES_DATA})
diff --git a/resources/mesh_angle_measure.png b/resources/mesh_angle_measure.png
new file mode 100644 (file)
index 0000000..d77b745
Binary files /dev/null and b/resources/mesh_angle_measure.png differ
index 6f4e1d7cb2047f3b2a225b02f1535664edcc0236..b4a75cb4dd97d6b84bf532bf65ed155e95736149 100644 (file)
@@ -3719,6 +3719,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
   case SMESHOp::OpPropertiesVolume:
   case SMESHOp::OpMinimumDistance:
   case SMESHOp::OpBoundingBox:
   case SMESHOp::OpPropertiesVolume:
   case SMESHOp::OpMinimumDistance:
   case SMESHOp::OpBoundingBox:
+  case SMESHOp::OpAngle:
     {
       int page = SMESHGUI_MeasureDlg::MinDistance;
       if ( theCommandID == SMESHOp::OpBoundingBox )
     {
       int page = SMESHGUI_MeasureDlg::MinDistance;
       if ( theCommandID == SMESHOp::OpBoundingBox )
@@ -3729,6 +3730,8 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
         page = SMESHGUI_MeasureDlg::Area;
       else if ( theCommandID == SMESHOp::OpPropertiesVolume )
         page = SMESHGUI_MeasureDlg::Volume;
         page = SMESHGUI_MeasureDlg::Area;
       else if ( theCommandID == SMESHOp::OpPropertiesVolume )
         page = SMESHGUI_MeasureDlg::Volume;
+      else if ( theCommandID == SMESHOp::OpAngle )
+        page = SMESHGUI_MeasureDlg::Angle;
 
       EmitSignalDeactivateDialog();
       SMESHGUI_MeasureDlg* dlg = new SMESHGUI_MeasureDlg( SMESHGUI::desktop(), page );
 
       EmitSignalDeactivateDialog();
       SMESHGUI_MeasureDlg* dlg = new SMESHGUI_MeasureDlg( SMESHGUI::desktop(), page );
@@ -4063,6 +4066,7 @@ void SMESHGUI::initialize( CAM_Application* app )
   createSMESHAction( SMESHOp::OpPropertiesLength, "MEASURE_LENGTH",   "ICON_MEASURE_LENGTH" );
   createSMESHAction( SMESHOp::OpPropertiesArea,   "MEASURE_AREA",     "ICON_MEASURE_AREA" );
   createSMESHAction( SMESHOp::OpPropertiesVolume, "MEASURE_VOLUME",   "ICON_MEASURE_VOLUME" );
   createSMESHAction( SMESHOp::OpPropertiesLength, "MEASURE_LENGTH",   "ICON_MEASURE_LENGTH" );
   createSMESHAction( SMESHOp::OpPropertiesArea,   "MEASURE_AREA",     "ICON_MEASURE_AREA" );
   createSMESHAction( SMESHOp::OpPropertiesVolume, "MEASURE_VOLUME",   "ICON_MEASURE_VOLUME" );
+  createSMESHAction( SMESHOp::OpAngle,            "MEASURE_ANGLE",    "ICON_MEASURE_ANGLE" );
 
   createSMESHAction( SMESHOp::OpHide,     "HIDE", "ICON_HIDE" );
   createSMESHAction( SMESHOp::OpShow,     "SHOW", "ICON_SHOW" );
 
   createSMESHAction( SMESHOp::OpHide,     "HIDE", "ICON_HIDE" );
   createSMESHAction( SMESHOp::OpShow,     "SHOW", "ICON_SHOW" );
@@ -4270,6 +4274,7 @@ void SMESHGUI::initialize( CAM_Application* app )
 
   createMenu( SMESHOp::OpMinimumDistance,  measureId,   -1 );
   createMenu( SMESHOp::OpBoundingBox,      measureId,   -1 );
 
   createMenu( SMESHOp::OpMinimumDistance,  measureId,   -1 );
   createMenu( SMESHOp::OpBoundingBox,      measureId,   -1 );
+  createMenu( SMESHOp::OpAngle,            measureId,   -1 );
   createMenu( SMESHOp::OpPropertiesLength, basicPropId, -1 );
   createMenu( SMESHOp::OpPropertiesArea,   basicPropId, -1 );
   createMenu( SMESHOp::OpPropertiesVolume, basicPropId, -1 );
   createMenu( SMESHOp::OpPropertiesLength, basicPropId, -1 );
   createMenu( SMESHOp::OpPropertiesArea,   basicPropId, -1 );
   createMenu( SMESHOp::OpPropertiesVolume, basicPropId, -1 );
index 1f1d5aa8a0315d1db7057afd6f17ddc92c6f5dcb..8e19cddcaa8afff01f0893db9d55cfd7885da656 100644 (file)
 #include "SMESHGUI.h"
 #include "SMESHGUI_IdValidator.h"
 #include "SMESHGUI_Utils.h"
 #include "SMESHGUI.h"
 #include "SMESHGUI_IdValidator.h"
 #include "SMESHGUI_Utils.h"
+#include "SMESHGUI_MeshEditPreview.h"
 #include "SMESHGUI_VTKUtils.h"
 #include <SMESH_TypeFilter.hxx>
 #include "SMESHGUI_VTKUtils.h"
 #include <SMESH_TypeFilter.hxx>
+#include <SMESH_MeshAlgos.hxx>
 #include <SMESH_LogicalFilter.hxx>
 #include <SMESH_LogicalFilter.hxx>
+#include <SMDS_Mesh.hxx>
+#include <SMDS_MeshNode.hxx>
 
 #include <LightApp_SelectionMgr.h>
 #include <SUIT_OverrideCursor.h>
 
 #include <LightApp_SelectionMgr.h>
 #include <SUIT_OverrideCursor.h>
@@ -59,6 +63,8 @@
 #include <VTKViewer_CellLocationsArray.h>
 #include <vtkProperty.h>
 
 #include <VTKViewer_CellLocationsArray.h>
 #include <vtkProperty.h>
 
+#include <ElCLib.hxx>
+
 #include <SALOMEconfig.h>
 #include CORBA_SERVER_HEADER(SMESH_MeshEditor)
 #include CORBA_SERVER_HEADER(SMESH_Measurements)
 #include <SALOMEconfig.h>
 #include CORBA_SERVER_HEADER(SMESH_MeshEditor)
 #include CORBA_SERVER_HEADER(SMESH_Measurements)
@@ -1312,6 +1318,367 @@ void SMESHGUI_BasicProperties::clear()
   myResult->clear();
 }
 
   myResult->clear();
 }
 
+/*!
+  \class SMESHGUI_Angle
+  \brief Angle measurement widget.
+  
+  Widget to calculate angle between 3 nodes.
+*/
+
+/*!
+  \brief Constructor.
+  \param parent parent widget
+*/
+SMESHGUI_Angle::SMESHGUI_Angle( QWidget* parent )
+  : QWidget( parent )
+{
+  // 3 nodes
+
+  QGroupBox* aNodesGrp = new QGroupBox( tr( "NODES_GROUP" ), this );
+  myNodes = new QLineEdit( aNodesGrp );
+  myNodes->setValidator( new SMESHGUI_IdValidator( this, 3 ));
+  QHBoxLayout* aNodesLayout = new QHBoxLayout( aNodesGrp );
+  aNodesLayout->addWidget( myNodes );
+  
+  // Compute button
+  QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this );
+
+  // Angle
+
+  QGroupBox* aResultGrp = new QGroupBox( tr( "RESULT" ), this );
+
+  myResult = new QLineEdit;
+  myResult->setReadOnly( true );
+
+  QHBoxLayout* aResultLayout = new QHBoxLayout( aResultGrp );
+  aResultLayout->addWidget( myResult );
+  
+  // Layout
+
+  QGridLayout* aMainLayout = new QGridLayout( this );
+  aMainLayout->setMargin( MARGIN );
+  aMainLayout->setSpacing( SPACING );
+
+  aMainLayout->addWidget( aNodesGrp,   0, 0, 1, 2 );
+  aMainLayout->addWidget( aCompute,    1, 0 );
+  aMainLayout->addWidget( aResultGrp, 2, 0, 1, 2 );
+  aMainLayout->setColumnStretch( 1, 5 );
+  aMainLayout->setRowStretch( 3, 5 );
+
+  // Connections
+  connect( aCompute, SIGNAL( clicked() ), this, SLOT( compute() ));
+
+  // preview
+  myPreview = 0;
+  if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
+  {
+    myPreview = new SMESHGUI_MeshEditPreview( aViewWindow );
+    if ( myPreview && myPreview->GetActor() )
+      myPreview->GetActor()->GetProperty()->SetLineWidth( 5 );
+  }
+  myActor = 0;
+}
+
+SMESHGUI_Angle::~SMESHGUI_Angle()
+{
+  if ( myPreview )
+    delete myPreview;
+}
+
+/*!
+  \brief Setup selection mode
+*/
+void SMESHGUI_Angle::updateSelection()
+{
+  LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr();
+
+  disconnect( selMgr, 0, this, 0 );
+  selMgr->clearFilters();
+
+  SMESH::SetPointRepresentation( true );
+  if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
+    aViewWindow->SetSelectionMode( NodeSelection );
+
+  connect( selMgr,  SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ));
+  connect( myNodes, SIGNAL( textEdited( QString ) ),     this, SLOT( nodesEdited() ));
+
+  if ( myPoints.empty() )
+    selectionChanged();
+}
+
+/*!
+  \brief Called when selection is changed
+*/
+void SMESHGUI_Angle::selectionChanged()
+{
+  clear();
+  QString nodesString;
+
+  TColStd_IndexedMapOfInteger idsMap;
+  SALOME_ListIO selected;
+  SMESHGUI::selectionMgr()->selectedObjects( selected );
+  selected.Reverse(); // to keep order of selection
+
+  SALOME_ListIteratorOfListIO ioIterator( selected );
+  for ( ; ioIterator.More(); ioIterator.Next() )
+  {
+    Handle(SALOME_InteractiveObject) IO = ioIterator.Value();
+
+    idsMap.Clear();
+    if ( SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector() )
+      selector->GetIndex( IO, idsMap );
+
+    if ( SMESH_Actor* actor = SMESH::FindActorByEntry( IO->getEntry() ))
+    {
+      myActor = actor;
+      for ( int i = 1; i <= idsMap.Extent() && myPoints.size() < 3; ++i )
+        if ( addPointByActor( idsMap(i) ))
+          nodesString += QString(" %1").arg( idsMap(i) );
+      idsMap.Clear();
+    }
+    SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface<SMESH::SMESH_IDSource>( IO );
+    if ( !CORBA::is_nil( obj ) )
+    {
+      myIDSrc = obj;
+      for ( int i = 1; i <= idsMap.Extent() && myPoints.size() < 3; ++i )
+        if ( addPointByIDSource( idsMap(i) ))
+          nodesString += QString(" %1").arg( idsMap(i) );
+    }
+  }
+
+  myNodes->setText( nodesString );
+}
+
+//=======================================================================
+//function : clear
+//purpose  : Erase preview and result
+//=======================================================================
+
+void SMESHGUI_Angle::clear()
+{
+  myPoints.clear();
+  myResult->clear();
+  if ( myPreview && myPreview->GetActor())
+  {
+    myPreview->GetActor()->SetVisibility( false );
+    if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
+      aViewWindow->Repaint();
+  }
+}
+
+//=======================================================================
+//function : addPointByActor
+//purpose  : append to myPoints XYZ got from myActor
+//=======================================================================
+
+bool SMESHGUI_Angle::addPointByActor( int id )
+{
+  size_t nbP = myPoints.size();
+
+  if ( myActor )
+  {
+    TVisualObjPtr obj = myActor->GetObject();
+    if ( SMDS_Mesh* mesh = obj->GetMesh() )
+      if ( const SMDS_MeshNode* node = mesh->FindNode( id ))
+      {
+        SMESH::PointStruct p = { node->X(), node->Y(), node->Z() };
+        myPoints.push_back( p );
+      }
+  }
+  return nbP < myPoints.size();
+}
+
+//=======================================================================
+//function : addPointByIDSource
+//purpose  : append to myPoints XYZ got from myIDSrc
+//=======================================================================
+
+bool SMESHGUI_Angle::addPointByIDSource( int id )
+{
+  size_t nbP = myPoints.size();
+
+  if ( !myIDSrc->_is_nil() )
+  {
+    SMESH::SMESH_Mesh_var mesh = myIDSrc->GetMesh();
+    if ( !mesh->_is_nil() )
+    {
+      SMESH::double_array_var xyz = mesh->GetNodeXYZ( id );
+      if ( xyz->length() == 3 )
+      {
+        SMESH::PointStruct p = { xyz[0], xyz[1], xyz[2] };
+        myPoints.push_back( p );
+      }
+    }
+  }
+  return nbP < myPoints.size();
+}
+
+//=======================================================================
+//function : nodesEdited
+//purpose  : SLOT called when the user types node IDs
+//=======================================================================
+
+void SMESHGUI_Angle::nodesEdited()
+{
+  clear();
+
+  TColStd_MapOfInteger ID;
+  QStringList ids = myNodes->text().split( " ", QString::SkipEmptyParts );
+  foreach ( QString idStr, ids )
+  {
+    int id = idStr.trimmed().toLong();
+    if (( !ID.Contains( id )) &&
+        ( addPointByActor( id ) || addPointByIDSource( id )))
+      ID.Add( id );
+  }
+
+  SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector();
+  if ( myActor && selector )
+  {
+    Handle(SALOME_InteractiveObject) IO = myActor->getIO();
+    selector->AddOrRemoveIndex( IO, ID, false );
+    if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() )
+      aViewWindow->highlight( IO, true, true );
+  }
+}
+
+//=======================================================================
+//function : compute
+//purpose  : SLOT. Compute angle and show preview
+//=======================================================================
+
+void SMESHGUI_Angle::compute()
+{
+  if ( myPoints.size() != 3 )
+    return;
+
+  // --------------
+  // compute angle
+  // --------------
+
+  SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements();
+  double radians = measure->Angle( myPoints[0], myPoints[1], myPoints[2] );
+  measure->UnRegister();
+  if ( radians < 0 )
+    return;
+
+  int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 );
+  myResult->setText( QString::number( radians * 180 / M_PI,
+                                      precision > 0 ? 'f' : 'g', qAbs( precision )));
+
+  // -------------
+  // show preview
+  // -------------
+
+  if ( !myPreview || !myPreview->GetActor() )
+    return;
+
+  SMESH::MeshPreviewStruct preveiwData;
+
+  const double     anglePerSeg = 5 * M_PI/180; // angle per an arc segment
+  const double arcRadiusFactor = 0.5;          // arc position, from p1
+
+  gp_Pnt p0 ( myPoints[0].x, myPoints[0].y, myPoints[0].z );
+  gp_Pnt p1 ( myPoints[1].x, myPoints[1].y, myPoints[1].z );
+  gp_Pnt p2 ( myPoints[2].x, myPoints[2].y, myPoints[2].z );
+  gp_Vec vec10( p1, p0 ), vec12( p1, p2 ), norm( vec10 ^ vec12 );
+
+  if ( norm.Magnitude() <= gp::Resolution() ) // 180 degrees
+    norm = getNormal( vec10 );
+
+  double     len10 = vec10.Magnitude();
+  double     len12 = vec12.Magnitude();
+  double    lenMax = Max( len10, len12 );
+  double arcRadius = arcRadiusFactor * lenMax;
+
+  p0 = p1.Translated( lenMax * vec10.Normalized() );
+  p2 = p1.Translated( lenMax * vec12.Normalized() );
+
+  gp_Circ arc( gp_Ax2( p1, norm, vec10 ), arcRadius );
+
+  int nbRadialSegmensts = ceil( radians / anglePerSeg ) + 1;
+  int           nbNodes = 3 + ( nbRadialSegmensts + 1 );
+
+  // coordinates
+  preveiwData.nodesXYZ.length( nbNodes );
+  int iP = 0;
+  for ( ; iP < nbRadialSegmensts + 1; ++iP )
+  {
+    double u = double( iP ) / nbRadialSegmensts * radians;
+    gp_Pnt p = ElCLib::Value( u, arc );
+    preveiwData.nodesXYZ[ iP ].x = p.X();
+    preveiwData.nodesXYZ[ iP ].y = p.Y();
+    preveiwData.nodesXYZ[ iP ].z = p.Z();
+  }
+  int iP0 = iP;
+  preveiwData.nodesXYZ[ iP ].x = p0.X();
+  preveiwData.nodesXYZ[ iP ].y = p0.Y();
+  preveiwData.nodesXYZ[ iP ].z = p0.Z();
+  int iP1 = ++iP;
+  preveiwData.nodesXYZ[ iP ].x = p1.X();
+  preveiwData.nodesXYZ[ iP ].y = p1.Y();
+  preveiwData.nodesXYZ[ iP ].z = p1.Z();
+  int iP2 = ++iP;
+  preveiwData.nodesXYZ[ iP ].x = p2.X();
+  preveiwData.nodesXYZ[ iP ].y = p2.Y();
+  preveiwData.nodesXYZ[ iP ].z = p2.Z();
+
+  // connectivity
+  preveiwData.elementConnectivities.length( 2 * ( 2 + nbRadialSegmensts ));
+  for ( int iSeg = 0; iSeg < nbRadialSegmensts; ++iSeg )
+  {
+    preveiwData.elementConnectivities[ iSeg * 2 + 0 ] = iSeg;
+    preveiwData.elementConnectivities[ iSeg * 2 + 1 ] = iSeg + 1;
+  }
+  int iSeg = nbRadialSegmensts;
+  preveiwData.elementConnectivities[ iSeg * 2 + 0 ] = iP0;
+  preveiwData.elementConnectivities[ iSeg * 2 + 1 ] = iP1;
+  ++iSeg;
+  preveiwData.elementConnectivities[ iSeg * 2 + 0 ] = iP1;
+  preveiwData.elementConnectivities[ iSeg * 2 + 1 ] = iP2;
+
+  // types
+  preveiwData.elementTypes.length( 2 + nbRadialSegmensts );
+  SMESH::ElementSubType type = { SMESH::EDGE, /*isPoly=*/false, /*nbNodesInElement=*/2 };
+  for ( CORBA::ULong i = 0; i < preveiwData.elementTypes.length(); ++i )
+    preveiwData.elementTypes[ i ] = type;
+
+  myPreview->SetData( preveiwData );
+}
+
+//================================================================================
+/*!
+ * \brief Return normal to a plane of drawing in the case of 180 degrees angle
+ */
+//================================================================================
+
+gp_Vec SMESHGUI_Angle::getNormal(const gp_Vec& vec10 )
+{
+  gp_XYZ norm;
+
+  // try to get normal by a face at the 2nd node
+  if ( myActor && myActor->GetObject()->GetMesh() )
+  {
+    QStringList ids = myNodes->text().split( " ", QString::SkipEmptyParts );
+    SMDS_Mesh* mesh = myActor->GetObject()->GetMesh();
+    if ( const SMDS_MeshNode* n = mesh->FindNode( ids[1].trimmed().toLong() ))
+    {
+      SMDS_ElemIteratorPtr faceIt = n->GetInverseElementIterator( SMDSAbs_Face );
+      while ( faceIt->more() )
+        if ( SMESH_MeshAlgos::FaceNormal( faceIt->next(), norm ))
+          return norm;
+    }
+  }
+  int iMinCoord = 1;
+  if ( vec10.Coord( iMinCoord ) > vec10.Y() ) iMinCoord = 2;
+  if ( vec10.Coord( iMinCoord ) > vec10.Z() ) iMinCoord = 3;
+
+  gp_Vec vec = vec10;
+  vec.SetCoord( iMinCoord, vec10.Coord( iMinCoord ) + 1. );
+
+  return vec ^ vec10;
+}
+
 /*!
   \class SMESHGUI_MeshInfoDlg
   \brief Centralized dialog box for the measurements
 /*!
   \class SMESHGUI_MeshInfoDlg
   \brief Centralized dialog box for the measurements
@@ -1323,7 +1690,7 @@ void SMESHGUI_BasicProperties::clear()
   \param page specifies the dialog page to be shown at the start-up
 */
 SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page )
   \param page specifies the dialog page to be shown at the start-up
 */
 SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page )
-: QDialog( parent )
+  : QDialog( parent )
 {
   setModal( false );
   setAttribute( Qt::WA_DeleteOnClose, true );
 {
   setModal( false );
   setAttribute( Qt::WA_DeleteOnClose, true );
@@ -1349,6 +1716,9 @@ SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page )
   myBasicProps = new SMESHGUI_BasicProperties( myTabWidget );
   int aBasicPropInd = myTabWidget->addTab( myBasicProps, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_BASIC_PROPS" ) ), tr( "BASIC_PROPERTIES" ) );
 
   myBasicProps = new SMESHGUI_BasicProperties( myTabWidget );
   int aBasicPropInd = myTabWidget->addTab( myBasicProps, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_BASIC_PROPS" ) ), tr( "BASIC_PROPERTIES" ) );
 
+  myAngle = new SMESHGUI_Angle( myTabWidget );
+  int aAngleInd = myTabWidget->addTab( myAngle, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_ANGLE" ) ), tr( "ANGLE" ) );
+
   // buttons
   QPushButton* okBtn = new QPushButton( tr( "SMESH_BUT_OK" ), this );
   okBtn->setAutoDefault( true );
   // buttons
   QPushButton* okBtn = new QPushButton( tr( "SMESH_BUT_OK" ), this );
   okBtn->setAutoDefault( true );
@@ -1376,6 +1746,8 @@ SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page )
     anInd = aMinDistInd;
   } else if ( page == BoundingBox ) {
     anInd = aBndBoxInd;
     anInd = aMinDistInd;
   } else if ( page == BoundingBox ) {
     anInd = aBndBoxInd;
+  } else if ( page == Angle ) {
+    anInd = aAngleInd;
   } else if ( page == Length || page == Area || page == Volume ) {
     myBasicProps->setMode( (SMESHGUI_BasicProperties::Mode)(page - Length) );
     anInd = aBasicPropInd;
   } else if ( page == Length || page == Area || page == Volume ) {
     myBasicProps->setMode( (SMESHGUI_BasicProperties::Mode)(page - Length) );
     anInd = aBasicPropInd;
@@ -1444,6 +1816,8 @@ void SMESHGUI_MeasureDlg::updateSelection()
     myMinDist->updateSelection();
   else if ( myTabWidget->currentIndex() == BoundingBox )
     myBndBox->updateSelection();
     myMinDist->updateSelection();
   else if ( myTabWidget->currentIndex() == BoundingBox )
     myBndBox->updateSelection();
+  else if ( myTabWidget->currentWidget() == myAngle )
+    myAngle->updateSelection();
   else {
     myBndBox->erasePreview();
     myBasicProps->updateSelection();
   else {
     myBndBox->erasePreview();
     myBasicProps->updateSelection();
@@ -1460,6 +1834,8 @@ void SMESHGUI_MeasureDlg::help()
     aHelpFile = "measurements.html#min-distance-anchor";
   } else if ( myTabWidget->currentIndex() == BoundingBox ) {
     aHelpFile = "measurements.html#bounding-box-anchor";
     aHelpFile = "measurements.html#min-distance-anchor";
   } else if ( myTabWidget->currentIndex() == BoundingBox ) {
     aHelpFile = "measurements.html#bounding-box-anchor";
+  } else if ( myTabWidget->currentWidget() == myAngle ) {
+    aHelpFile = "measurements.html#angle-anchor";
   } else {
     aHelpFile = "measurements.html#basic-properties-anchor";
   }
   } else {
     aHelpFile = "measurements.html#basic-properties-anchor";
   }
index cadcf1f557dcb2317025fe5f021954c585ad0ed5..3d583f06a55e4995a3bfe5cac5fa020907166d2b 100644 (file)
@@ -37,10 +37,13 @@ class SUIT_SelectionFilter;
 class SALOME_Actor;
 class SMESH_Actor;
 class SMESHGUI_IdValidator;
 class SALOME_Actor;
 class SMESH_Actor;
 class SMESHGUI_IdValidator;
+class SMESHGUI_MeshEditPreview;
 
 #include <SALOMEconfig.h>
 #include CORBA_SERVER_HEADER(SMESH_Mesh)
 
 
 #include <SALOMEconfig.h>
 #include CORBA_SERVER_HEADER(SMESH_Mesh)
 
+#include <gp_Vec.hxx>
+
 class SMESHGUI_EXPORT SMESHGUI_MinDistance : public QWidget
 {
   Q_OBJECT;
 class SMESHGUI_EXPORT SMESHGUI_MinDistance : public QWidget
 {
   Q_OBJECT;
@@ -167,6 +170,40 @@ private:
   SUIT_SelectionFilter*     myFilter;
 };
 
   SUIT_SelectionFilter*     myFilter;
 };
 
+class SMESHGUI_EXPORT SMESHGUI_Angle : public QWidget
+{
+  Q_OBJECT;
+  
+public:
+
+  SMESHGUI_Angle( QWidget* = 0 );
+  ~SMESHGUI_Angle();
+
+  void deactivate();
+  void updateSelection();
+
+private slots:
+  void selectionChanged();
+  void nodesEdited();
+  void compute();
+  void clear();
+
+private:
+
+  bool addPointByActor( int id );
+  bool addPointByIDSource( int id );
+  gp_Vec getNormal(const gp_Vec& vec10 );
+
+  QLineEdit* myNodes;
+  QLineEdit* myResult;
+
+  SMESH::SMESH_IDSource_var myIDSrc;
+  SMESH_Actor*              myActor;
+
+  std::vector< SMESH::PointStruct > myPoints;
+  SMESHGUI_MeshEditPreview*         myPreview;
+};
+
 class SMESHGUI_EXPORT SMESHGUI_MeasureDlg : public QDialog
 { 
   Q_OBJECT;
 class SMESHGUI_EXPORT SMESHGUI_MeasureDlg : public QDialog
 { 
   Q_OBJECT;
@@ -180,7 +217,8 @@ public:
     BoundingBox,   //!< bounding box
     Length,        //!< length
     Area,          //!< area
     BoundingBox,   //!< bounding box
     Length,        //!< length
     Area,          //!< area
-    Volume         //!< volume
+    Volume,        //!< volume
+    Angle
   };
 
   SMESHGUI_MeasureDlg( QWidget* = 0, int = MinDistance );
   };
 
   SMESHGUI_MeasureDlg( QWidget* = 0, int = MinDistance );
@@ -203,6 +241,7 @@ private:
   SMESHGUI_MinDistance* myMinDist;   
   SMESHGUI_BoundingBox* myBndBox;
   SMESHGUI_BasicProperties* myBasicProps;
   SMESHGUI_MinDistance* myMinDist;   
   SMESHGUI_BoundingBox* myBndBox;
   SMESHGUI_BasicProperties* myBasicProps;
+  SMESHGUI_Angle*       myAngle;
 };
 
 #endif // SMESHGUI_MEASUREMENTS_H
 };
 
 #endif // SMESHGUI_MEASUREMENTS_H
index b4ccb9d02f16b011bf3cf0d8e43503b7ec49b9f9..97f79382e665e0297a2886d530813271db3a26a8 100644 (file)
@@ -190,6 +190,7 @@ namespace SMESHOp {
     OpPropertiesVolume       = 5002,   // MENU MEASUREMENTS - BASIC PROPERTIES - VOLUME
     OpMinimumDistance        = 5003,   // MENU MEASUREMENTS - MINIMUM DISTANCE
     OpBoundingBox            = 5004,   // MENU MEASUREMENTS - BOUNDING BOX
     OpPropertiesVolume       = 5002,   // MENU MEASUREMENTS - BASIC PROPERTIES - VOLUME
     OpMinimumDistance        = 5003,   // MENU MEASUREMENTS - MINIMUM DISTANCE
     OpBoundingBox            = 5004,   // MENU MEASUREMENTS - BOUNDING BOX
+    OpAngle                  = 5005,   // MENU MEASUREMENTS - ANGLE
     // Hypothesis ---------------------//--------------------------------
     OpEditHypothesis         = 6000,   // POPUP MENU - EDIT HYPOTHESIS
     OpUnassign               = 6001,   // POPUP MENU - UNASSIGN
     // Hypothesis ---------------------//--------------------------------
     OpEditHypothesis         = 6000,   // POPUP MENU - EDIT HYPOTHESIS
     OpUnassign               = 6001,   // POPUP MENU - UNASSIGN
index b74d29e50b73237b8359e46843b9b364a6356a8d..cf0e475d49403d4f000d17825c648e4d73b85b06 100644 (file)
@@ -923,7 +923,7 @@ namespace SMESH
         aCollection->InitTraversal();
         while ( vtkActor *anAct = aCollection->GetNextActor() ) {
           if ( SMESH_Actor *anActor = dynamic_cast<SMESH_Actor*>(anAct) ) {
         aCollection->InitTraversal();
         while ( vtkActor *anAct = aCollection->GetNextActor() ) {
           if ( SMESH_Actor *anActor = dynamic_cast<SMESH_Actor*>(anAct) ) {
-           anActor->UpdateSelectionProps();
+            anActor->UpdateSelectionProps();
           }
         }
       }
           }
         }
       }
index 50b33888fd4f48ed85c59a0089a259c1601c2a0a..0a09a43ecdc43ed1c70082ca9b3e25d4fbe2d652 100644 (file)
             <source>ICON_MEASURE_BND_BOX</source>
             <translation>mesh_bounding_box.png</translation>
         </message>
             <source>ICON_MEASURE_BND_BOX</source>
             <translation>mesh_bounding_box.png</translation>
         </message>
+        <message>
+            <source>ICON_MEASURE_ANGLE</source>
+            <translation>mesh_angle_measure.png</translation>
+        </message>
         <message>
             <source>ICON_SHOW</source>
             <translation>mesh_show.png</translation>
         <message>
             <source>ICON_SHOW</source>
             <translation>mesh_show.png</translation>
index b6f1fa6196c7094538c0844b4182dc4ea3c23e02..209a6bd8856c55ad027273bef7c4978952415768 100644 (file)
         <source>TOP_MEASURE_VOLUME</source>
         <translation>Volume</translation>
     </message>
         <source>TOP_MEASURE_VOLUME</source>
         <translation>Volume</translation>
     </message>
+    <message>
+        <source>MEN_MEASURE_ANGLE</source>
+        <translation>Angle</translation>
+    </message>
+    <message>
+        <source>STB_MEASURE_ANGLE</source>
+        <translation>Measure angle defined by three nodes</translation>
+    </message>
+    <message>
+        <source>TOP_MEASURE_ANGLE</source>
+        <translation>Angle</translation>
+    </message>
     <message>
         <source>MEN_MOVE</source>
         <translation>Move Node</translation>
     <message>
         <source>MEN_MOVE</source>
         <translation>Move Node</translation>
@@ -3617,7 +3629,7 @@ Use Display Entity menu command to show them.
         <translation>Create groups of entities basing on nodes of other groups</translation>
     </message>
     <message>
         <translation>Create groups of entities basing on nodes of other groups</translation>
     </message>
     <message>
-        <source>STB_UNDERLYING_ELEMS</source>
+        <source>STB_FACE_GROUPS_BY_EDGES</source>
         <translation>Create face groups separated by sharp edges</translation>
     </message>
     <message>
         <translation>Create face groups separated by sharp edges</translation>
     </message>
     <message>
@@ -8108,6 +8120,21 @@ as they are of improper type:
         <translation>Compute</translation>
     </message>
 </context>
         <translation>Compute</translation>
     </message>
 </context>
+<context>
+    <name>SMESHGUI_Angle</name>
+    <message>
+        <source>NODES_GROUP</source>
+        <translation>Three nodes</translation>
+    </message>
+    <message>
+        <source>RESULT</source>
+        <translation>Angle in degrees</translation>
+    </message>
+    <message>
+        <source>COMPUTE</source>
+        <translation>Compute</translation>
+    </message>
+</context>
 <context>
     <name>SMESHGUI_CopyMeshDlg</name>
     <message>
 <context>
     <name>SMESHGUI_CopyMeshDlg</name>
     <message>
@@ -8164,6 +8191,10 @@ with red in the Object Browser.</translation>
         <source>BASIC_PROPERTIES</source>
         <translation>Basic Properties</translation>
     </message>
         <source>BASIC_PROPERTIES</source>
         <translation>Basic Properties</translation>
     </message>
+    <message>
+        <source>ANGLE</source>
+        <translation>Angle</translation>
+    </message>
 </context>
 <context>
     <name>SMESHGUI_BoundingBox</name>
 </context>
 <context>
     <name>SMESHGUI_BoundingBox</name>
index cef6517ce435f1c1da81d5ff84e9eda198e69580..e76d1b34348c834654574b015197fd239b7fbb3b 100644 (file)
@@ -2478,7 +2478,7 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand)
       "MergeElements","MergeEqualElements","SewFreeBorders","SewConformFreeBorders",
       "FindCoincidentFreeBorders", "SewCoincidentFreeBorders",
       "SewBorderToSide","SewSideElements","ChangeElemNodes","GetLastCreatedNodes",
       "MergeElements","MergeEqualElements","SewFreeBorders","SewConformFreeBorders",
       "FindCoincidentFreeBorders", "SewCoincidentFreeBorders",
       "SewBorderToSide","SewSideElements","ChangeElemNodes","GetLastCreatedNodes",
-      "GetLastCreatedElems",
+      "GetLastCreatedElems", "FaceGroupsSeparatedByEdges",
       "MirrorMakeMesh","MirrorObjectMakeMesh","TranslateMakeMesh","TranslateObjectMakeMesh",
       "Scale","ScaleMakeMesh","RotateMakeMesh","RotateObjectMakeMesh","MakeBoundaryMesh",
       "MakeBoundaryElements", "SplitVolumesIntoTetra","SplitHexahedraIntoPrisms",
       "MirrorMakeMesh","MirrorObjectMakeMesh","TranslateMakeMesh","TranslateObjectMakeMesh",
       "Scale","ScaleMakeMesh","RotateMakeMesh","RotateObjectMakeMesh","MakeBoundaryMesh",
       "MakeBoundaryElements", "SplitVolumesIntoTetra","SplitHexahedraIntoPrisms",
index 2fcac5a7f9122551b4efb276f0f3c77fd0daf3a0..45f5f0b7fd3c60f11988d47d950e25fcd003221b 100644 (file)
@@ -346,3 +346,30 @@ SMESH::PointStruct Measurements_i::GravityCenter(SMESH::SMESH_IDSource_ptr theSo
 
   return grCenter;
 }
 
   return grCenter;
 }
+
+//=======================================================================
+//function : Angle
+//purpose  : Return angle in radians defined by 3 points <(p1,p2,p3)
+//=======================================================================
+
+CORBA::Double Measurements_i::Angle(const SMESH::PointStruct& p1,
+                                    const SMESH::PointStruct& p2,
+                                    const SMESH::PointStruct& p3 )
+{
+  gp_Vec v1( p1.x - p2.x, p1.y - p2.y, p1.z - p2.z );
+  gp_Vec v2( p3.x - p2.x, p3.y - p2.y, p3.z - p2.z );
+
+  double angle = -1;
+
+  try
+  {
+    angle = v1.Angle( v2 );
+  }
+  catch(...)
+  {
+  }
+  if ( isnan( angle ))
+    angle = -1;
+
+  return angle;
+}
index bb696eb92421822a1fdb858a6349b1cb3a60a88a..3ba642ca62434912f99e5a61e3ce2ef591675993 100644 (file)
@@ -78,6 +78,13 @@ namespace SMESH
      * gravity center of the source
      */
     SMESH::PointStruct GravityCenter(SMESH::SMESH_IDSource_ptr  theSource);
      * gravity center of the source
      */
     SMESH::PointStruct GravityCenter(SMESH::SMESH_IDSource_ptr  theSource);
+
+    /*!
+     * angle in radians defined by 3 points <(p1,p2,p3)
+     */
+    CORBA::Double Angle(const SMESH::PointStruct& p1,
+                        const SMESH::PointStruct& p2,
+                        const SMESH::PointStruct& p3 );
   };
 }
 
   };
 }
 
index 7037fd2eb8f984d87fc178934803f577f37f0e74..35198fe5982de2249dd3d1d675afb30775877e74 100755 (executable)
@@ -1443,6 +1443,28 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
         aMeasurements.UnRegister()
         return pointStruct.x, pointStruct.y, pointStruct.z
 
         aMeasurements.UnRegister()
         return pointStruct.x, pointStruct.y, pointStruct.z
 
+    def GetAngle(self, p1, p2, p3 ):
+        """
+        Computes a radian measure of an angle defined by 3 points: <(p1,p2,p3)
+
+        Parameters:            
+                p1,p2,p3: coordinates of 3 points defined by either SMESH.PointStruct 
+                          or list [x,y,z]
+
+        Returns:        
+            Angle in radians
+        """
+        if isinstance( p1, list ): p1 = PointStruct(*p1)
+        if isinstance( p2, list ): p2 = PointStruct(*p2)
+        if isinstance( p3, list ): p3 = PointStruct(*p3)
+
+        aMeasurements = self.CreateMeasurements()
+        angle = aMeasurements.Angle(p1,p2,p3)
+        aMeasurements.UnRegister()
+
+        return angle
+
+
     pass # end of class smeshBuilder
 
 import omniORB
     pass # end of class smeshBuilder
 
 import omniORB
@@ -2887,7 +2909,7 @@ class Mesh(metaclass = MeshMeta):
 
     def FaceGroupsSeparatedByEdges( self, sharpAngle, createEdges=False, useExistingEdges=False ):
         """
 
     def FaceGroupsSeparatedByEdges( self, sharpAngle, createEdges=False, useExistingEdges=False ):
         """
-        Distribute all faces of the mesh between groups using sharp edges and optionally
+        Distribute all faces of the mesh among groups using sharp edges and optionally
         existing 1D elements as group boundaries.
 
         Parameters:
         existing 1D elements as group boundaries.
 
         Parameters:
@@ -2896,7 +2918,7 @@ class Mesh(metaclass = MeshMeta):
                 createEdges (boolean): to create 1D elements for detected sharp edges.
                 useExistingEdges (boolean): to use existing edges as group boundaries
         Returns:
                 createEdges (boolean): to create 1D elements for detected sharp edges.
                 useExistingEdges (boolean): to use existing edges as group boundaries
         Returns:
-                ListOfGroups - the created groups
+                ListOfGroups - the created :class:`groups <SMESH.SMESH_Group>`
         """
         sharpAngle,Parameters,hasVars = ParseParameters( sharpAngle )
         self.mesh.SetParameters(Parameters)
         """
         sharpAngle,Parameters,hasVars = ParseParameters( sharpAngle )
         self.mesh.SetParameters(Parameters)
@@ -6826,6 +6848,20 @@ class Mesh(metaclass = MeshMeta):
             volume = self.FunctorValue(SMESH.FT_Volume3D, elemId)
         return volume
 
             volume = self.FunctorValue(SMESH.FT_Volume3D, elemId)
         return volume
 
+    def GetAngle(self, node1, node2, node3 ):
+        """
+        Computes a radian measure of an angle defined by 3 nodes: <(node1,node2,node3)
+
+        Parameters:            
+                node1,node2,node3: IDs of the three nodes
+
+        Returns:        
+            Angle in radians
+        """
+        return self.smeshpyD.GetAngle( self.GetNodeXYZ( node1 ),
+                                       self.GetNodeXYZ( node2 ),
+                                       self.GetNodeXYZ( node3 ))
+
     def GetMaxElementLength(self, elemId):
         """
         Get maximum element length.
     def GetMaxElementLength(self, elemId):
         """
         Get maximum element length.