X-Git-Url: http://git.salome-platform.org/gitweb/?p=modules%2Fsmesh.git;a=blobdiff_plain;f=src%2FSMESHGUI%2FSMESHGUI_Measurements.cxx;h=852bca21e0a7d6e0906e37175b9f44af87a6cde0;hp=a5580dd65ee5461c0181ddf0f233323c57a196fa;hb=09bc0414c91ebabb67c7fe200549044a1854e199;hpb=5c95ba9078f96d3e32b6d0dc4f09da6eb6dd38e3 diff --git a/src/SMESHGUI/SMESHGUI_Measurements.cxx b/src/SMESHGUI/SMESHGUI_Measurements.cxx index a5580dd65..852bca21e 100644 --- a/src/SMESHGUI/SMESHGUI_Measurements.cxx +++ b/src/SMESHGUI/SMESHGUI_Measurements.cxx @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE // // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS @@ -6,7 +6,7 @@ // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either -// version 2.1 of the License. +// version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,15 +28,19 @@ #include "SMESHGUI.h" #include "SMESHGUI_IdValidator.h" #include "SMESHGUI_Utils.h" +#include "SMESHGUI_MeshEditPreview.h" #include "SMESHGUI_VTKUtils.h" #include +#include #include +#include +#include #include #include #include #include -#include +#include #include #include @@ -59,6 +63,8 @@ #include #include +#include + #include #include CORBA_SERVER_HEADER(SMESH_MeshEditor) #include CORBA_SERVER_HEADER(SMESH_Measurements) @@ -167,11 +173,11 @@ SMESHGUI_MinDistance::SMESHGUI_MinDistance( QWidget* parent ) aSOrigin->setChecked( true ); #ifndef MINDIST_ENABLE_ELEMENT aFElem->setEnabled( false ); // NOT AVAILABLE YET - aSElem->setEnabled( false ); // NOT AVAILABLE YET + //aSElem->setEnabled( false ); // NOT AVAILABLE YET #endif #ifndef MINDIST_ENABLE_OBJECT aFObject->setEnabled( false ); // NOT AVAILABLE YET - aSObject->setEnabled( false ); // NOT AVAILABLE YET + //aSObject->setEnabled( false ); // NOT AVAILABLE YET #endif myDX->setReadOnly( true ); myDY->setReadOnly( true ); @@ -198,6 +204,7 @@ SMESHGUI_MinDistance::SMESHGUI_MinDistance( QWidget* parent ) clear(); //setTarget( FirstTgt ); + selectionChanged(); } /*! @@ -367,6 +374,7 @@ void SMESHGUI_MinDistance::createPreview( double x1, double y1, double z1, doubl myPreview = SALOME_Actor::New(); myPreview->PickableOff(); myPreview->SetMapper( aMapper ); + myPreview->SetResolveCoincidentTopology(true); aMapper->Delete(); vtkProperty* aProp = vtkProperty::New(); aProp->SetRepresentationToWireframe(); @@ -500,13 +508,21 @@ void SMESHGUI_MinDistance::secondEdited() setTarget( SecondTgt ); if ( sender() == mySecondTgt ) clear(); + QString text = mySecondTgt->text(); + if ( !mySecondActor ) + { + selectionChanged(); + mySecondTgt->setText( text ); + } SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); if ( mySecondActor && selector ) { Handle(SALOME_InteractiveObject) IO = mySecondActor->getIO(); if ( mySecond->checkedId() == NodeTgt || mySecond->checkedId() == ElementTgt ) { - TColStd_MapOfInteger ID; - ID.Add( mySecondTgt->text().toLong() ); - selector->AddOrRemoveIndex( IO, ID, false ); + if ( !text.isEmpty() ) { + TColStd_MapOfInteger ID; + ID.Add( text.toLong() ); + selector->AddOrRemoveIndex( IO, ID, false ); + } } if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) aViewWindow->highlight( IO, true, true ); @@ -519,8 +535,8 @@ void SMESHGUI_MinDistance::secondEdited() void SMESHGUI_MinDistance::compute() { SUIT_OverrideCursor wc; - SMESH::SMESH_IDSource_wrap s1; - SMESH::SMESH_IDSource_wrap s2; + SMESH::IDSource_wrap s1; + SMESH::IDSource_wrap s2; bool isOrigin = mySecond->checkedId() == OriginTgt; // process first target @@ -579,10 +595,14 @@ void SMESHGUI_MinDistance::compute() if ( isOrigin ) { x2 = y2 = z2 = 0.; } - else { + else if ( mySecond->checkedId() == NodeTgt ) { coord = s2->GetMesh()->GetNodeXYZ( result.node2 ); x2 = coord[0]; y2 = coord[1]; z2 = coord[2]; } + else + { + x2 = result.maxX; y2 = result.maxY; z2 = result.maxZ; + } createPreview( x1, y1, z1, x2, y2, z2 ); displayPreview(); } @@ -760,7 +780,8 @@ void SMESHGUI_BoundingBox::updateSelection() sourceEdited(); - //selectionChanged(); + if ( mySource->text().isEmpty() ) + selectionChanged(); } /*! @@ -1085,6 +1106,583 @@ void SMESHGUI_BoundingBox::clear() erasePreview(); } +/*! + \class SMESHGUI_BasicProperties + \brief basic properties measurement widget. + + Widget to calculate length, area or volume for the selected object(s). +*/ + +/*! + \brief Constructor. + \param parent parent widget +*/ +SMESHGUI_BasicProperties::SMESHGUI_BasicProperties( QWidget* parent ) +: QWidget( parent ) +{ + // Property (length, area or volume) + QGroupBox* aPropertyGrp = new QGroupBox( tr( "PROPERTY" ), this ); + + QRadioButton* aLength = new QRadioButton( tr( "LENGTH" ), aPropertyGrp ); + QRadioButton* anArea = new QRadioButton( tr( "AREA" ), aPropertyGrp ); + QRadioButton* aVolume = new QRadioButton( tr( "VOLUME" ), aPropertyGrp ); + + myMode = new QButtonGroup( this ); + myMode->addButton( aLength, Length ); + myMode->addButton( anArea, Area ); + myMode->addButton( aVolume, Volume ); + + QHBoxLayout* aPropertyLayout = new QHBoxLayout; + aPropertyLayout->addWidget( aLength ); + aPropertyLayout->addWidget( anArea ); + aPropertyLayout->addWidget( aVolume ); + + aPropertyGrp->setLayout( aPropertyLayout ); + + // Source object + QGroupBox* aSourceGrp = new QGroupBox( tr( "SOURCE_MESH_SUBMESH_GROUP" ), this ); + + mySource = new QLineEdit( aSourceGrp ); + mySource->setReadOnly( true ); + + QHBoxLayout* aSourceLayout = new QHBoxLayout; + aSourceLayout->addWidget( mySource ); + + aSourceGrp->setLayout( aSourceLayout ); + + // Compute button + QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this ); + + // Result of computation (length, area or volume) + myResultGrp = new QGroupBox( this ); + + myResult = new QLineEdit; + myResult->setReadOnly( true ); + + QHBoxLayout* aResultLayout = new QHBoxLayout; + aResultLayout->addWidget( myResult ); + + myResultGrp->setLayout( aResultLayout ); + + // Layout + QGridLayout* aMainLayout = new QGridLayout( this ); + aMainLayout->setMargin( MARGIN ); + aMainLayout->setSpacing( SPACING ); + + aMainLayout->addWidget( aPropertyGrp, 0, 0, 1, 2 ); + aMainLayout->addWidget( aSourceGrp, 1, 0, 1, 2 ); + aMainLayout->addWidget( aCompute, 2, 0 ); + aMainLayout->addWidget( myResultGrp, 3, 0, 1, 2 ); + aMainLayout->setColumnStretch( 1, 5 ); + aMainLayout->setRowStretch( 4, 5 ); + + // Initial state + setMode( Length ); + + // Connections + connect( myMode, SIGNAL( buttonClicked( int ) ), this, SLOT( modeChanged( int ) ) ); + connect( aCompute, SIGNAL( clicked() ), this, SLOT( compute() ) ); + + // Selection filter + QList filters; + filters.append( new SMESH_TypeFilter( SMESH::MESHorSUBMESH ) ); + filters.append( new SMESH_TypeFilter( SMESH::GROUP ) ); + myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR ); +} + +/*! + \brief Destructor +*/ +SMESHGUI_BasicProperties::~SMESHGUI_BasicProperties() +{ +} + +/*! + \brief Sets the measurement mode. + \param theMode the mode to set (length, area or volume meausurement) +*/ +void SMESHGUI_BasicProperties::setMode( const Mode theMode ) +{ + QRadioButton* aButton = qobject_cast( myMode->button( theMode ) ); + if ( aButton ) { + aButton->setChecked( true ); + modeChanged( theMode ); + } +} + +/*! + \brief Setup the selection mode. +*/ +void SMESHGUI_BasicProperties::updateSelection() +{ + LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr(); + + disconnect( selMgr, 0, this, 0 ); + selMgr->clearFilters(); + + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) { + aViewWindow->SetSelectionMode( ActorSelection ); + } + selMgr->installFilter( myFilter ); + + connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ) ); + + if ( mySource->text().isEmpty() ) + selectionChanged(); +} + +/*! + \brief Deactivate widget +*/ +void SMESHGUI_BasicProperties::deactivate() +{ + disconnect( SMESHGUI::selectionMgr(), 0, this, 0 ); +} + +/*! + \brief Called when selection is changed +*/ +void SMESHGUI_BasicProperties::selectionChanged() +{ + SUIT_OverrideCursor wc; + + SALOME_ListIO selected; + SMESHGUI::selectionMgr()->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) IO = selected.First(); + SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface( IO ); + if ( !CORBA::is_nil( obj ) ) { + mySrc = obj; + + QString aName; + SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName ); + mySource->setText( aName ); + } + } + + clear(); +} + +/*! + \brief Called when the measurement mode selection is changed. + \param theMode the selected mode +*/ +void SMESHGUI_BasicProperties::modeChanged( int theMode ) +{ + clear(); + + if ( theMode == Length ) { + myResultGrp->setTitle( tr("LENGTH") ); + } else if ( theMode == Area ) { + myResultGrp->setTitle( tr("AREA") ); + } else if ( theMode == Volume ) { + myResultGrp->setTitle( tr("VOLUME") ); + } +} + +/*! + \brief Calculate length, area or volume for the selected object(s) +*/ +void SMESHGUI_BasicProperties::compute() +{ + SUIT_OverrideCursor wc; + + SMESH::SMESH_IDSource_var source; + + if ( !CORBA::is_nil( mySrc ) ) { + // compute + int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 ); + SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements(); + + double result = 0; + + if ( myMode->checkedId() == Length ) { + result = measure->Length( mySrc.in() ); + } else if ( myMode->checkedId() == Area ) { + result = measure->Area( mySrc.in() ); + } else if ( myMode->checkedId() == Volume ) { + result = measure->Volume( mySrc.in() ); + } + + measure->UnRegister(); + + myResult->setText( QString::number( result, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + } else { + clear(); + } +} + +/*! + \brief Reset the widget to the initial state (nullify the result field) +*/ +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( 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 @@ -1096,7 +1694,7 @@ void SMESHGUI_BoundingBox::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 ); @@ -1110,12 +1708,20 @@ SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page ) // min distance myMinDist = new SMESHGUI_MinDistance( myTabWidget ); - myTabWidget->addTab( myMinDist, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_MIN_DIST" ) ), tr( "MIN_DIST" ) ); + int aMinDistInd = myTabWidget->addTab( myMinDist, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_MIN_DIST" ) ), tr( "MIN_DIST" ) ); // bounding box myBndBox = new SMESHGUI_BoundingBox( myTabWidget ); - myTabWidget->addTab( myBndBox, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_BND_BOX" ) ), tr( "BND_BOX" ) ); + int aBndBoxInd = myTabWidget->addTab( myBndBox, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_BND_BOX" ) ), tr( "BND_BOX" ) ); + + // 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 ); @@ -1139,7 +1745,21 @@ SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page ) l->addStretch(); l->addLayout( btnLayout ); - myTabWidget->setCurrentIndex( qMax( (int)MinDistance, qMin( (int)BoundingBox, page ) ) ); + int anInd = -1; + if ( page == MinDistance ) { + 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; + } + + if ( anInd >= 0 ) { + myTabWidget->setCurrentIndex( anInd ); + } connect( okBtn, SIGNAL( clicked() ), this, SLOT( reject() ) ); connect( helpBtn, SIGNAL( clicked() ), this, SLOT( help() ) ); @@ -1200,7 +1820,12 @@ 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(); + } } /*! @@ -1208,9 +1833,18 @@ void SMESHGUI_MeasureDlg::updateSelection() */ void SMESHGUI_MeasureDlg::help() { - SMESH::ShowHelpFile( myTabWidget->currentIndex() == MinDistance ? - "measurements_page.html#min_distance_anchor" : - "measurements_page.html#bounding_box_anchor" ); + QString aHelpFile; + if ( myTabWidget->currentIndex() == MinDistance ) { + 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"; + } + + SMESH::ShowHelpFile( aHelpFile ); } /*! @@ -1229,6 +1863,7 @@ void SMESHGUI_MeasureDlg::activate() */ void SMESHGUI_MeasureDlg::deactivate() { + myBasicProps->deactivate(); myMinDist->deactivate(); myBndBox->deactivate(); myTabWidget->setEnabled( false );