1 // Copyright (C) 2007-2021 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESHGUI_AddNodeOnSegmentDlg.cxx
24 // Author : Edward AGAPOV, Open CASCADE S.A.S.
26 #include "SMESHGUI_AddNodeOnSegmentDlg.h"
29 #include "SMESHGUI_MeshUtils.h"
30 #include "SMESHGUI_VTKUtils.h"
31 #include "SMESHGUI_SpinBox.h"
32 #include "SMESHGUI_MeshEditPreview.h"
34 #include <SMDS_Mesh.hxx>
35 #include <SMESH_Actor.h>
36 #include <SMESH_ActorUtils.h>
37 #include <SMESH_TypeDefs.hxx>
39 #include <LightApp_SelectionMgr.h>
40 #include <SALOME_ListIO.hxx>
41 #include <SUIT_Desktop.h>
42 #include <SUIT_MessageBox.h>
43 #include <SUIT_OverrideCursor.h>
44 #include <SUIT_ResourceMgr.h>
45 #include <SVTK_ViewModel.h>
46 #include <SVTK_ViewWindow.h>
47 #include <SVTK_RenderWindowInteractor.h>
48 #include <SVTK_Renderer.h>
49 #include <SVTK_Event.h>
50 #include <SalomeApp_Tools.h>
53 #include <QApplication>
55 #include <QGridLayout>
56 #include <QHBoxLayout>
57 #include <QVBoxLayout>
59 #include <QPushButton>
61 #include <QRadioButton>
63 #include <QButtonGroup>
66 #include <vtkProperty.h>
67 #include <vtkInteractorStyle.h>
68 #include <vtkGenericRenderWindowInteractor.h>
69 #include <vtkInteractorObserver.h>
73 #include <SALOMEconfig.h>
74 #include CORBA_SERVER_HEADER(SMESH_Mesh)
75 #include CORBA_SERVER_HEADER(SMESH_MeshEditor)
80 #define SPIN_TOLERANCE 1e-3
82 //=======================================================================
84 * \brief Dialog to split a diagonal of a quadrangle formed by two adjacent triangles
86 //=======================================================================
88 SMESHGUI_AddNodeOnSegmentDlg::SMESHGUI_AddNodeOnSegmentDlg()
89 : SMESHGUI_Dialog( 0, false, true )
91 setWindowTitle(tr("CAPTION"));
93 QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame());
94 aDlgLay->setMargin(0);
95 aDlgLay->setSpacing(SPACING);
96 QWidget* mainFr = createMainFrame(mainFrame());
98 aDlgLay->addWidget( mainFr );
100 aDlgLay->setStretchFactor( mainFr, 1);
103 //=======================================================================
104 // function : createMainFrame()
105 // purpose : Create frame containing dialog's input fields
106 //=======================================================================
108 QWidget* SMESHGUI_AddNodeOnSegmentDlg::createMainFrame (QWidget* theParent)
110 QWidget* aFrame = new QWidget(theParent);
112 SUIT_ResourceMgr* rm = SMESH::GetResourceMgr( SMESHGUI::GetSMESHGUI() );
113 QPixmap iconSelect( rm->loadPixmap("SMESH", tr("ICON_SELECT")));
117 QGroupBox* segmentGrp = new QGroupBox(tr("SEGMENT_GROUP"), aFrame);
118 segmentGrp->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
119 QLabel* segmentLabel = new QLabel(tr("SEGMENT"), segmentGrp);
120 mySegmentBtn = new QPushButton(segmentGrp);
121 mySegmentBtn->setIcon(iconSelect);
122 mySegmentBtn->setCheckable(true);
123 mySegment = new QLineEdit(segmentGrp);
124 mySegment->setValidator(new QRegExpValidator(QRegExp("[\\d]*-[\\d]*"), this));
126 QGridLayout* segmentGrpLayout = new QGridLayout(segmentGrp);
127 segmentGrpLayout->setSpacing(SPACING);
128 segmentGrpLayout->setMargin(MARGIN);
130 segmentGrpLayout->addWidget( segmentLabel, 0, 0 );
131 segmentGrpLayout->addWidget( mySegmentBtn, 0, 1 );
132 segmentGrpLayout->addWidget( mySegment, 0, 2 );
134 // Position on segment
136 QGroupBox* positionGrp = new QGroupBox(tr("POSITION_GROUP"), aFrame);
137 positionGrp->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
139 myPositionBtn = new QPushButton(positionGrp);
140 myPositionBtn->setIcon(iconSelect);
141 myPositionBtn->setCheckable(true);
143 QLabel* positionLbl = new QLabel(tr("POSITION"), positionGrp);
145 myPositionSpin = new SMESHGUI_SpinBox(positionGrp);
146 myPositionSpin->setReadOnly(false);
147 myPositionSpin->RangeStepAndValidator(SPIN_TOLERANCE, 1- SPIN_TOLERANCE, 0.1, "length_precision");
149 QGridLayout* positionLayout = new QGridLayout(positionGrp);
150 positionLayout->setMargin(MARGIN);
151 positionLayout->setSpacing(SPACING);
152 positionLayout->addWidget(positionLbl, 0, 0);
153 positionLayout->addWidget(myPositionBtn, 0, 1);
154 positionLayout->addWidget(myPositionSpin, 0, 2);
155 positionLayout->setColumnStretch(2, 1);
159 myPreviewChkBox = new QCheckBox( tr("PREVIEW"), aFrame);
160 myPreviewChkBox->setChecked( true );
162 QVBoxLayout* aLay = new QVBoxLayout(aFrame);
163 aLay->addWidget(segmentGrp);
164 aLay->addWidget(positionGrp);
165 aLay->addWidget(myPreviewChkBox);
167 connect(myPositionBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool)));
168 connect(mySegmentBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool)));
170 mySegmentBtn->setChecked(true);
175 //================================================================================
177 * \brief SLOT called when any button is toggled
178 * \param bool - on or off
180 //================================================================================
182 void SMESHGUI_AddNodeOnSegmentDlg::ButtonToggled (bool on)
184 const QObject* aSender = sender();
186 if ( aSender == myPositionBtn )
188 mySegmentBtn->setChecked( !on );
190 else if ( aSender == mySegmentBtn )
192 myPositionBtn->setChecked( !on );
195 emit selTypeChanged();
198 //================================================================================
202 //================================================================================
204 SMESHGUI_AddNodeOnSegmentOp::SMESHGUI_AddNodeOnSegmentOp() :
205 SMESHGUI_InteractiveOp()
209 myDlg = new SMESHGUI_AddNodeOnSegmentDlg;
210 myHelpFileName = "add_node_on_segment.html";
214 // connect signals and slots
215 connect(myDlg->myPreviewChkBox, SIGNAL (toggled(bool)), SLOT(redisplayPreview()));
216 connect(myDlg->myPositionSpin, SIGNAL (valueChanged(double)), SLOT(redisplayPreview()));
217 connect(myDlg->myPositionSpin, SIGNAL (textChanged(const QString&)),SLOT(redisplayPreview()));
218 connect(myDlg, SIGNAL (selTypeChanged() ), SLOT(onSelTypeChange()));
219 connect(myDlg->mySegment, SIGNAL (textChanged(const QString&)),SLOT(onTextChange(const QString&)));
222 //================================================================================
224 * \brief SLOT. Called upon change of selection type
226 //================================================================================
228 void SMESHGUI_AddNodeOnSegmentOp::onSelTypeChange()
230 if ( myDlg->mySegmentBtn->isChecked() )
232 setSelectionMode( EdgeOfCellSelection );
234 else if ( myDlg->myPositionBtn->isChecked() )
236 if (SVTK_ViewWindow* svtkViewWindow = SMESH::GetViewWindow(mySMESHGUI)) {
238 SMESH::smIdType node1 = 0, node2 = 0;
239 if (isValid(msg, node1, node2)) {
240 //Disconnect selectionChanged to keep selected element
241 disconnect(selectionMgr(), SIGNAL(selectionChanged()), this, SLOT(onSelectionDone()));
242 // Set selection mode to ActorSelection to avoid element's prehighlight during interactive selection
243 setSelectionMode(ActorSelection);
244 connect(selectionMgr(), SIGNAL(selectionChanged()), SLOT(onSelectionDone()));
250 setSelectionMode( ActorSelection );
254 //=======================================================================
255 // function : startOperation()
256 // purpose : Init dialog fields, connect signals and slots, show dialog
257 //=======================================================================
259 void SMESHGUI_AddNodeOnSegmentOp::startOperation()
264 // init simulation with a current View
265 if ( mySimulation ) delete mySimulation;
266 mySMESHGUI = getSMESHGUI();
267 mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ) );
268 connect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
269 connect(mySMESHGUI, SIGNAL (SignalCloseView()), this, SLOT(onCloseView()));
270 vtkProperty* aProp = vtkProperty::New();
271 aProp->SetRepresentationToWireframe();
272 aProp->SetColor(250, 0, 250);
273 aProp->SetPointSize(5);
274 aProp->SetLineWidth( SMESH::GetFloat("SMESH:element_width",1) + 1);
275 mySimulation->GetActor()->SetProperty(aProp);
278 SMESHGUI_SelectionOp::startOperation(); // this method should be called only after filter creation
279 SMESHGUI_InteractiveOp::startOperation();
280 myDlg->mySegment->setText("");
281 myDlg->myPositionSpin->SetValue(0.5);
282 myDlg->myPositionSpin->setReadOnly(false);
288 onSelectionDone(); // init myMeshActor
291 //================================================================================
293 * \brief Stops operation
295 //================================================================================
297 void SMESHGUI_AddNodeOnSegmentOp::stopOperation()
302 mySimulation->SetVisibility(false);
309 SMESH::SetPointRepresentation( false );
310 SMESH::RepaintCurrentView();
312 disconnect(mySMESHGUI, SIGNAL (SignalActivatedViewManager()), this, SLOT(onOpenView()));
313 disconnect(mySMESHGUI, SIGNAL (SignalCloseView()), this, SLOT(onCloseView()));
314 //selectionMgr()->removeFilter( myFilter );
315 SMESHGUI_SelectionOp::stopOperation();
319 //================================================================================
321 * \brief perform it's intention action: create a node on a segment
323 //================================================================================
325 bool SMESHGUI_AddNodeOnSegmentOp::onApply()
327 if( SMESHGUI::isStudyLocked() )
331 SMESH::smIdType node1= 0, node2 = 0;
332 if ( !isValid( msg, node1, node2 ))
334 SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ),
335 msg.isEmpty() ? tr("INVALID_ID") : msg );
340 QStringList aParameters;
341 aParameters << myDlg->myPositionSpin->text();
344 SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO( myMeshActor->getIO() );
345 if (aMesh->_is_nil()) {
346 SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), tr("INVALID_MESH") );
349 SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor();
350 if (aMeshEditor->_is_nil())
353 aMesh->SetParameters( aParameters.join(":").toUtf8().constData() );
355 aMeshEditor->AddNodeOnSegment( node1, node2, myDlg->myPositionSpin->GetValue() );
357 selector()->ClearIndex();
358 selector()->ClearCompositeIndex();
360 aList.Append( myMeshActor->getIO() );
361 selectionMgr()->setSelectedObjects(aList,false);
364 SMESHGUI::Modified();
366 catch (const SALOME::SALOME_Exception& S_ex) {
367 SalomeApp_Tools::QtCatchCorbaException(S_ex);
375 //================================================================================
377 * \brief Check selected node id validity
379 //================================================================================
381 bool SMESHGUI_AddNodeOnSegmentOp::isValid( QString& msg,
382 SMESH::smIdType & node1,
383 SMESH::smIdType & node2 )
388 msg = tr("INVALID_MESH");
392 if ( SMDS_Mesh* mesh = myMeshActor->GetObject()->GetMesh() )
394 QString txt = myDlg->mySegment->text();
395 if ( txt.contains('-'))
397 QString str1 = txt.section('-', 0, 0, QString::SectionSkipEmpty);
398 QString str2 = txt.section('-', 1, 1, QString::SectionSkipEmpty);
399 node1 = str1.toLong();
400 node2 = str2.toLong();
401 const SMDS_MeshNode* n1 = mesh->FindNode( node1 );
402 const SMDS_MeshNode* n2 = mesh->FindNode( node2 );
403 std::vector<const SMDS_MeshNode *> nodes = { n1, n2 };
404 std::vector<const SMDS_MeshElement *> foundElems;
405 if ( !mesh->GetElementsByNodes( nodes, foundElems ))
406 msg = tr("NO_ELEMENTS");
409 for ( const SMDS_MeshElement * elem : foundElems )
411 if ( elem->GetGeomType() == SMDSGeom_TRIANGLE )
415 if ( elem->GetType() == SMDSAbs_Volume )
416 msg = tr("VOLUME_FOUND");
417 if ( elem->GetType() == SMDSAbs_Face )
418 msg = tr("NOT_TRIANGLE_FACE_FOUND");
421 if ( !msg.isEmpty() )
427 if ( !ok && msg.isEmpty() )
430 msg += tr("INVALID_EDGE") + "\n";
433 if ( ok && ! myDlg->myPositionSpin->isValid( msg, /*toCorrect=*/false ))
435 msg = tr("BAD_POSITION");
442 //================================================================================
444 * \brief SLOT called when selection changed
446 //================================================================================
448 void SMESHGUI_AddNodeOnSegmentOp::onSelectionDone()
450 if ( !myDlg->isVisible() || !myDlg->isEnabled() )
457 selectionMgr()->selectedObjects(aList, SVTK_Viewer::Type());
458 if (aList.Extent() != 1)
460 Handle(SALOME_InteractiveObject) anIO = aList.First();
461 if (( myMeshActor = SMESH::FindActorByEntry(anIO->getEntry()) ))
463 SVTK_IndexedMapOfVtkIds IDs;
464 selector()->GetCompositeIndex( anIO, IDs );
465 if ( IDs.Extent() == 1 && IDs(1).size() == 2 )
467 SMESH::smIdType id1 = IDs(1)[0];
468 SMESH::smIdType id2 = IDs(1)[1];
469 segmentStr = QString("%1-%2").arg( id1 ).arg( id2 );
474 myDlg->mySegment->setText( segmentStr );
480 //================================================================================
482 * \brief update preview
484 //================================================================================
486 void SMESHGUI_AddNodeOnSegmentOp::redisplayPreview()
488 if ( myNoPreview || !myDlg->myPreviewChkBox->isChecked() )
491 mySimulation->SetVisibility(false);
496 SMESH::MeshPreviewStruct_var aMeshPreviewStruct;
499 SMESH::smIdType node1, node2;
501 if ( isValid( msg, node1, node2 ))
503 SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myMeshActor->getIO());
504 if ( !aMesh->_is_nil() )
506 SMESH::SMESH_MeshEditor_var aPreviewer = aMesh->GetMeshEditPreviewer();
507 if ( !aPreviewer->_is_nil() )
509 SUIT_OverrideCursor aWaitCursor;
510 double pos = myDlg->myPositionSpin->value();
511 aPreviewer->AddNodeOnSegment( node1, node2, pos );
513 aMeshPreviewStruct = aPreviewer->GetPreviewData();
522 mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
524 if ( & aMeshPreviewStruct.in() )
526 mySimulation->SetData( aMeshPreviewStruct.in() );
530 mySimulation->SetVisibility(false);
536 //=================================================================================
538 * \brief SLOT called when the viewer opened
540 //=================================================================================
542 void SMESHGUI_AddNodeOnSegmentOp::onOpenView()
544 if ( mySimulation ) {
545 mySimulation->SetVisibility(false);
546 SMESH::SetPointRepresentation(false);
549 mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI ));
553 //=================================================================================
555 * \brief SLOT called when the viewer closed
557 //=================================================================================
559 void SMESHGUI_AddNodeOnSegmentOp::onCloseView()
565 //================================================================================
567 * \brief SLOT called when the node ids are manually changed
569 //================================================================================
571 void SMESHGUI_AddNodeOnSegmentOp::onTextChange( const QString& /*theText*/ )
574 SMESH::smIdType node1= 0, node2 = 0;
576 if (( isValid( msg, node1, node2 )) ||
577 ( node1 && node2 )) // position only can be invalid
579 // highlight entered segment
581 Handle(SALOME_InteractiveObject) anIO = myMeshActor->getIO();
583 aList.Append( anIO );
584 selectionMgr()->setSelectedObjects( aList, false );
586 SVTK_ListOfVtk newIndices = { node1, node2 };
587 selector()->AddOrRemoveCompositeIndex( anIO, newIndices, false );
588 SMESH::GetViewWindow(mySMESHGUI)->highlight( anIO, true, true );
592 //================================================================================
594 * \brief Activate Node selection
596 //================================================================================
598 void SMESHGUI_AddNodeOnSegmentOp::activateSelection()
600 selectionMgr()->clearFilters();
601 SMESH::SetPointRepresentation( false );
605 //================================================================================
609 //================================================================================
611 SMESHGUI_AddNodeOnSegmentOp::~SMESHGUI_AddNodeOnSegmentOp()
613 if ( myDlg ) delete myDlg;
614 if ( mySimulation ) delete mySimulation;
617 //================================================================================
619 * \brief Gets dialog of this operation
620 * \retval LightApp_Dialog* - pointer to dialog of this operation
622 //================================================================================
624 LightApp_Dialog* SMESHGUI_AddNodeOnSegmentOp::dlg() const
629 //================================================================================
631 * \brief Process InteractiveSelectionChanged event
633 //================================================================================
634 void SMESHGUI_AddNodeOnSegmentOp::processStyleEvents(unsigned long theEvent, void* theCallData)
638 SMESH::smIdType node1 = 0, node2 = 0;
639 if (isValid(msg, node1, node2)) {
640 if (theEvent == SVTK::InteractiveSelectionChanged) {
641 if (SMDS_Mesh* mesh = myMeshActor->GetObject()->GetMesh())
642 if(myRWInteractor && myRWInteractor->GetDevice() && myInteractorStyle) {
652 const SMDS_MeshNode* n1 = mesh->FindNode(node1);
653 const SMDS_MeshNode* n2 = mesh->FindNode(node2);
654 int xClick, yClick; // Last event (move or left button down) position
655 myRWInteractor->GetDevice()->GetEventPosition(xClick, yClick);
659 // Get 2D screen coordinates of each node
660 vtkInteractorObserver::ComputeWorldToDisplay(myRWInteractor->GetRenderer()->GetDevice(),
661 N1[0], N1[1], N1[2], N1_SC);
662 vtkInteractorObserver::ComputeWorldToDisplay(myRWInteractor->GetRenderer()->GetDevice(),
663 N2[0], N2[1], N2[2], N2_SC);
664 N1_SC[2] = N2_SC[2] = xyz[2] = 0;
665 xyz[0] = static_cast<double>(xClick);
666 xyz[1] = static_cast<double>(yClick);
667 // Parametric position of selected point on a line
668 vtkLine::DistanceToLine(xyz, N1_SC, N2_SC, pos, closest);
670 pos = SPIN_TOLERANCE;
672 pos = 1.0 - SPIN_TOLERANCE;
673 myDlg->myPositionSpin->SetValue(pos);
681 //================================================================================
683 * \brief Process LeftButtonPressEvent event: activate interactive selection
685 //================================================================================
686 void SMESHGUI_AddNodeOnSegmentOp::processInteractorEvents(unsigned long theEvent, void* theCallData)
689 if (theEvent == vtkCommand::LeftButtonPressEvent && myDlg->myPositionBtn->isChecked()) {
690 bool control = myRWInteractor->GetDevice()->GetControlKey();
691 bool shift = myRWInteractor->GetDevice()->GetControlKey();
692 SVTK_ViewWindow* svtkViewWindow = SMESH::GetViewWindow(mySMESHGUI);
693 if (svtkViewWindow && !shift && ! control) {
695 SMESH::smIdType node1 = 0, node2 = 0;
696 if (isValid(msg, node1, node2)) {
697 svtkViewWindow->activateInteractiveSelection();