IMP 23612: EDF 14143 - Compute angle from 3 points
26
doc/salome/examples/measurements_ex04.py
Normal 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 )
|
||||
|
@ -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
|
||||
|
BIN
doc/salome/gui/SMESH/images/angle_measure.png
Normal file
After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 27 KiB |
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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`.
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>`
|
||||
|
@ -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 );
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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})
|
||||
|
BIN
resources/mesh_angle_measure.png
Normal file
After Width: | Height: | Size: 360 B |
@ -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 );
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 );
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|