IMP 23612: EDF 14143 - Compute angle from 3 points

This commit is contained in:
eap 2018-11-29 17:31:43 +03:00
parent 7b4c10fd0e
commit 48b83422af
26 changed files with 595 additions and 13 deletions

View File

@ -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 )

View File

@ -115,6 +115,8 @@ SET(GOOD_TESTS
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -2,7 +2,7 @@
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:
@ -11,7 +11,7 @@ The operation dialog looks as follows:
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.

View 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.
* :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.

View 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.
.. _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`.

View File

@ -168,6 +168,7 @@ Creating groups
Mesh.MakeGroupByCriterion
Mesh.MakeGroupByCriteria
Mesh.MakeGroupByFilter
Mesh.FaceGroupsSeparatedByEdges
Mesh.GetGroups
Mesh.NbGroups
Mesh.GetGroupNames
@ -305,6 +306,7 @@ Measurements
smeshBuilder.GetLength
smeshBuilder.GetArea
smeshBuilder.GetVolume
smeshBuilder.GetAngle
Mesh.GetFreeBorders
Mesh.MinDistance
Mesh.GetMinDistance
@ -315,6 +317,7 @@ Measurements
Mesh.GetLength
Mesh.GetArea
Mesh.GetVolume
Mesh.GetAngle
****************
Modifying meshes

View File

@ -33,3 +33,11 @@ Basic Properties
: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>`

View File

@ -74,6 +74,11 @@ module SMESH
* 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 );
};
};

View File

@ -517,7 +517,7 @@ module SMESH
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.

View File

@ -232,6 +232,7 @@ SET(SMESH_RESOURCES_FILES
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})

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

View File

@ -3719,6 +3719,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID )
case SMESHOp::OpPropertiesVolume:
case SMESHOp::OpMinimumDistance:
case SMESHOp::OpBoundingBox:
case SMESHOp::OpAngle:
{
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;
else if ( theCommandID == SMESHOp::OpAngle )
page = SMESHGUI_MeasureDlg::Angle;
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::OpAngle, "MEASURE_ANGLE", "ICON_MEASURE_ANGLE" );
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::OpAngle, measureId, -1 );
createMenu( SMESHOp::OpPropertiesLength, basicPropId, -1 );
createMenu( SMESHOp::OpPropertiesArea, basicPropId, -1 );
createMenu( SMESHOp::OpPropertiesVolume, basicPropId, -1 );

View File

@ -28,9 +28,13 @@
#include "SMESHGUI.h"
#include "SMESHGUI_IdValidator.h"
#include "SMESHGUI_Utils.h"
#include "SMESHGUI_MeshEditPreview.h"
#include "SMESHGUI_VTKUtils.h"
#include <SMESH_TypeFilter.hxx>
#include <SMESH_MeshAlgos.hxx>
#include <SMESH_LogicalFilter.hxx>
#include <SMDS_Mesh.hxx>
#include <SMDS_MeshNode.hxx>
#include <LightApp_SelectionMgr.h>
#include <SUIT_OverrideCursor.h>
@ -59,6 +63,8 @@
#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)
@ -1312,6 +1318,367 @@ void SMESHGUI_BasicProperties::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
@ -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 )
: QDialog( parent )
: QDialog( parent )
{
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" ) );
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 );
@ -1376,6 +1746,8 @@ SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page )
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;
@ -1444,6 +1816,8 @@ void SMESHGUI_MeasureDlg::updateSelection()
myMinDist->updateSelection();
else if ( myTabWidget->currentIndex() == BoundingBox )
myBndBox->updateSelection();
else if ( myTabWidget->currentWidget() == myAngle )
myAngle->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";
} else if ( myTabWidget->currentWidget() == myAngle ) {
aHelpFile = "measurements.html#angle-anchor";
} else {
aHelpFile = "measurements.html#basic-properties-anchor";
}

View File

@ -37,10 +37,13 @@ class SUIT_SelectionFilter;
class SALOME_Actor;
class SMESH_Actor;
class SMESHGUI_IdValidator;
class SMESHGUI_MeshEditPreview;
#include <SALOMEconfig.h>
#include CORBA_SERVER_HEADER(SMESH_Mesh)
#include <gp_Vec.hxx>
class SMESHGUI_EXPORT SMESHGUI_MinDistance : public QWidget
{
Q_OBJECT;
@ -167,6 +170,40 @@ private:
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;
@ -180,7 +217,8 @@ public:
BoundingBox, //!< bounding box
Length, //!< length
Area, //!< area
Volume //!< volume
Volume, //!< volume
Angle
};
SMESHGUI_MeasureDlg( QWidget* = 0, int = MinDistance );
@ -203,6 +241,7 @@ private:
SMESHGUI_MinDistance* myMinDist;
SMESHGUI_BoundingBox* myBndBox;
SMESHGUI_BasicProperties* myBasicProps;
SMESHGUI_Angle* myAngle;
};
#endif // SMESHGUI_MEASUREMENTS_H

View 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
OpAngle = 5005, // MENU MEASUREMENTS - ANGLE
// Hypothesis ---------------------//--------------------------------
OpEditHypothesis = 6000, // POPUP MENU - EDIT HYPOTHESIS
OpUnassign = 6001, // POPUP MENU - UNASSIGN

View File

@ -923,7 +923,7 @@ namespace SMESH
aCollection->InitTraversal();
while ( vtkActor *anAct = aCollection->GetNextActor() ) {
if ( SMESH_Actor *anActor = dynamic_cast<SMESH_Actor*>(anAct) ) {
anActor->UpdateSelectionProps();
anActor->UpdateSelectionProps();
}
}
}

View File

@ -651,6 +651,10 @@
<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>

View File

@ -848,6 +848,18 @@
<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>
@ -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>
<source>STB_UNDERLYING_ELEMS</source>
<source>STB_FACE_GROUPS_BY_EDGES</source>
<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>
<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>
@ -8164,6 +8191,10 @@ with red in the Object Browser.</translation>
<source>BASIC_PROPERTIES</source>
<translation>Basic Properties</translation>
</message>
<message>
<source>ANGLE</source>
<translation>Angle</translation>
</message>
</context>
<context>
<name>SMESHGUI_BoundingBox</name>

View File

@ -2478,7 +2478,7 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand)
"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",

View File

@ -346,3 +346,30 @@ SMESH::PointStruct Measurements_i::GravityCenter(SMESH::SMESH_IDSource_ptr theSo
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;
}

View File

@ -78,6 +78,13 @@ namespace SMESH
* 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 );
};
}

View File

@ -1443,6 +1443,28 @@ class smeshBuilder( SMESH._objref_SMESH_Gen, object ):
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
@ -2887,7 +2909,7 @@ class Mesh(metaclass = MeshMeta):
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:
@ -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:
ListOfGroups - the created groups
ListOfGroups - the created :class:`groups <SMESH.SMESH_Group>`
"""
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
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.