Salome HOME
Updated copyright comment
[modules/gui.git] / src / SPV3D / SPV3D_CADSelection.cxx
1 // Copyright (C) 2023-2024  CEA, EDF
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SPV3D_CADSelection.h"
21
22 #include <pqActiveObjects.h>
23 #include <pqRenderView.h>
24 #include <pqUndoStack.h>
25 #include <pqCoreUtilities.h>
26
27 #include <vtkDataObject.h>
28 #include <vtkCollection.h>
29 #include <vtkRenderWindowInteractor.h>
30 #include <vtkPVRenderView.h>
31 #include <vtkPVDataInformation.h>
32 #include <vtkPVArrayInformation.h>
33 #include <vtkPVRenderViewSettings.h>
34 #include <vtkSMRenderViewProxy.h>
35 #include <vtkSMPropertyHelper.h>
36 #include <vtkSMSessionProxyManager.h>
37 #include <vtkSMStringVectorProperty.h>
38 #include <vtkSMRepresentationProxy.h>
39 #include <vtkPVDataSetAttributesInformation.h>
40
41 //-----------------------------------------------------------------------------
42 SPV3D_CADSelection::SPV3D_CADSelection(QObject *parent,
43   pqRenderView* view, SelectionMode mode):QObject(parent)
44 {
45   this->View = view;
46   this->Mode = mode;
47   this->PreviousRenderViewMode = -1;
48   this->MousePosition[0] = 0;
49   this->MousePosition[1] = 0;
50   //this->selectionComboBox = comboBox;
51
52   for (size_t i = 0; i < sizeof(this->ObserverIds) / sizeof(this->ObserverIds[0]); ++i)
53   {
54     this->ObserverIds[i] = 0;
55   }
56
57   //QObject::connect(button, SIGNAL(toggled(bool)), this, SLOT(actionTriggered(bool)));
58
59   // if view == nullptr, we track the active view.
60   if (view == nullptr)
61   {
62     QObject::connect(
63       &pqActiveObjects::instance(), SIGNAL(viewChanged(pqView*)), this, SLOT(setView(pqView*)));
64     // this ensure that the enabled-state is set correctly.
65     this->setView(nullptr);
66   }
67
68   this->setRepresentation(nullptr);
69   QObject::connect(&pqActiveObjects::instance(),
70     SIGNAL(representationChanged(pqDataRepresentation*)), this,
71     SLOT(setRepresentation(pqDataRepresentation*)));
72
73   vtkPVRenderViewSettings::GetInstance()->SetEnableFastPreselection(true);
74
75   this->updateEnableState();
76 }
77
78 //-----------------------------------------------------------------------------
79 SPV3D_CADSelection::~SPV3D_CADSelection()
80 {
81   this->cleanupObservers();
82 }
83
84 //-----------------------------------------------------------------------------
85 void SPV3D_CADSelection::SetMode(const SPV3D_CADSelection::SelectionMode mode)
86 {
87   this->Mode = mode;
88 }
89
90 //-----------------------------------------------------------------------------
91 void SPV3D_CADSelection::updateEnableState()
92 {
93   this->endSelection();
94
95   //auto paction = this->parentAction();
96   //bool state = false;
97   if (this->Representation)
98   {
99     vtkSMProxy* proxy = this->Representation->getProxy();
100     vtkSMStringVectorProperty* prop =
101       vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("ColorArrayName"));
102     if (prop)
103     {
104       int association = std::atoi(prop->GetElement(3));
105       const char* arrayName = prop->GetElement(4);
106
107       vtkPVDataInformation* dataInfo = this->Representation->getInputDataInformation();
108
109       vtkPVDataSetAttributesInformation* info = nullptr;
110       if (association == vtkDataObject::CELL &&
111         this->Mode == SELECT_FACES)
112       {
113         info = dataInfo->GetCellDataInformation();
114       }
115       if (association == vtkDataObject::POINT &&
116         this->Mode == SELECT_VERTICES)
117       {
118         info = dataInfo->GetPointDataInformation();
119       }
120
121       if (info)
122       {
123         /*vtkPVArrayInformation* arrayInfo = */info->GetArrayInformation(arrayName);
124         //state = arrayInfo && arrayInfo->GetDataType() == VTK_ID_TYPE;
125       }
126     }
127   }
128   //paction->setEnabled(state);
129 }
130
131 //-----------------------------------------------------------------------------
132 void SPV3D_CADSelection::actionTriggered(bool val)
133 {
134   if (val)
135   {
136     this->beginSelection();
137   }
138   else
139   {
140     this->endSelection();
141   }
142 }
143
144 //-----------------------------------------------------------------------------
145 void SPV3D_CADSelection::setView(pqView* view)
146 {
147   if (this->View != view)
148   {
149     // if we were currently in selection, finish that before changing the view.
150     this->endSelection();
151   }
152
153   this->View = qobject_cast<pqRenderView*>(view);
154
155   // update enable state.
156   //this->parentAction()->setEnabled(this->View != nullptr);
157 }
158
159 //-----------------------------------------------------------------------------
160 void SPV3D_CADSelection::setRepresentation(pqDataRepresentation* representation)
161 {
162   if (this->Representation != representation)
163   {
164     // if we are currently in selection, finish that before changing the representation.
165     this->endSelection();
166
167     if (this->Representation != nullptr)
168     {
169       QObject::disconnect(this->RepresentationConnection);
170     }
171
172     this->Representation = representation;
173
174     if (this->Representation != nullptr)
175     {
176       this->RepresentationConnection = this->connect(
177         this->Representation, SIGNAL(colorArrayNameModified()), SLOT(updateEnableState()));
178     }
179
180     // update enable state.
181     this->updateEnableState();
182   }
183 }
184
185 //-----------------------------------------------------------------------------
186 void SPV3D_CADSelection::beginSelection()
187 {
188   if (!this->View)
189   {
190     return;
191   }
192
193   //QAction* actn = this->parentAction();
194   //if (actn->isCheckable())
195   {
196     //this->selectionComboBox->setEnabled(false);
197
198     vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
199     vtkSMPropertyHelper(rmp, "InteractionMode").Get(&this->PreviousRenderViewMode);
200
201     //QString currentPrimitive = this->selectionComboBox->currentText();
202     pqCoreUtilities::promptUser("SPV3D_CADSelection", QMessageBox::Information,
203       "Interactive Selection Information",
204       "You are entering interactive selection mode to highlight.\n"
205       "Move the mouse point over the dataset to interactively highlight elements.\n\n"
206       "Press Ctrl + click to add the currently highlighted element.\n"
207       "Press Shift + click to remove the element.\n\n"
208       "Click on Select button to exit this mode.",
209       QMessageBox::Ok | QMessageBox::Save);
210     this->View->setCursor(Qt::CrossCursor);
211     vtkSMPropertyHelper(rmp, "InteractionMode").Set(vtkPVRenderView::INTERACTION_MODE_SELECTION);
212
213     rmp->UpdateVTKObjects();
214
215     // Setup observer.
216     assert(this->ObserverIds[0] == 0 && this->ObservedObject == nullptr && this->ObserverIds[1] == 0);
217     this->ObservedObject = rmp->GetInteractor();
218     this->ObserverIds[0] = this->ObservedObject->AddObserver(
219       vtkCommand::MouseMoveEvent, this, &SPV3D_CADSelection::onMouseMove);
220     this->ObserverIds[1] = this->ObservedObject->AddObserver(vtkCommand::LeftButtonReleaseEvent,
221       this, &SPV3D_CADSelection::onLeftButtonRelease);
222     this->ObserverIds[2] = this->ObservedObject->AddObserver(vtkCommand::RightButtonPressEvent,
223       this, &SPV3D_CADSelection::onRightButtonPress);
224     this->ObserverIds[3] = this->ObservedObject->AddObserver(vtkCommand::RightButtonReleaseEvent,
225       this, &SPV3D_CADSelection::onRightButtonRelease);
226
227     //this->parentAction()->setChecked(true);
228   }
229 }
230
231 //-----------------------------------------------------------------------------
232 void SPV3D_CADSelection::endSelection()
233 {
234   if (!this->View)
235   {
236     return;
237   }
238
239   if (this->PreviousRenderViewMode == -1)
240   {
241     return;
242   }
243
244   //QAction* actn = this->parentAction();
245   //if (actn->isCheckable())
246   {
247     vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
248     vtkSMPropertyHelper(rmp, "InteractionMode").Set(this->PreviousRenderViewMode);
249     this->PreviousRenderViewMode = -1;
250     rmp->UpdateVTKObjects();
251     this->View->setCursor(QCursor());
252     this->cleanupObservers();
253     //this->parentAction()->setChecked(false);
254
255     if (this->CurrentRepresentation != nullptr)
256     {
257       vtkSMSessionProxyManager* pxm = rmp->GetSessionProxyManager();
258       vtkSMProxy* emptySel = pxm->NewProxy("sources", "IDSelectionSource");
259
260       this->CurrentRepresentation->UpdateVTKObjects();
261       this->CurrentRepresentation = nullptr;
262       emptySel->Delete();
263
264       rmp->StillRender();
265     }
266
267     //this->selectionComboBox->setEnabled(true);
268   }
269 }
270
271 //-----------------------------------------------------------------------------
272 void SPV3D_CADSelection::onMouseMove()
273 {
274   if (!this->DisablePreSelection && vtkPVRenderViewSettings::GetInstance()->GetEnableFastPreselection())
275   {
276     this->fastSelection(true);
277   }
278 }
279
280 //-----------------------------------------------------------------------------
281 void SPV3D_CADSelection::onLeftButtonRelease()
282 {
283   if (vtkPVRenderViewSettings::GetInstance()->GetEnableFastPreselection())
284   {
285     this->fastSelection();
286   }
287 }
288
289 //-----------------------------------------------------------------------------
290 void SPV3D_CADSelection::onRightButtonPress()
291 {
292   this->DisablePreSelection = true;
293 }
294
295 //-----------------------------------------------------------------------------
296 void SPV3D_CADSelection::onRightButtonRelease()
297 {
298   this->DisablePreSelection = false;
299 }
300
301 //-----------------------------------------------------------------------------
302 void SPV3D_CADSelection::fastSelection(bool presel)
303 {
304   vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
305   assert(rmp != nullptr);
306
307   int x = rmp->GetInteractor()->GetEventPosition()[0];
308   int y = rmp->GetInteractor()->GetEventPosition()[1];
309   this->MousePosition[0] = x;
310   this->MousePosition[1] = y;
311
312   int region[4] = { x, y, x, y };
313
314   vtkNew<vtkCollection> selectedRepresentations;
315   vtkNew<vtkCollection> selectionSources;
316   bool status = false;
317   // Do the selection on the current region
318   switch (this->Mode)
319   {
320     case SELECT_SOLIDS:
321       status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Solid id");
322       break;
323     case SELECT_FACES:
324       status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Face id");
325       break;
326     case SELECT_EDGES:
327       status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Edge id");
328       break;
329     case SELECT_VERTICES:
330       status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Vertex id");
331       break;
332     default:
333       qCritical("Invalid call to SPV3D_CADSelection::fastSelection");
334       return;
335   }
336
337   vtkSMRepresentationProxy* repr =
338     vtkSMRepresentationProxy::SafeDownCast(selectedRepresentations->GetItemAsObject(0));
339
340   if (repr && !presel)
341   {
342     repr->InvokeCommand("BeginSelect");
343   }
344
345   // If something has been preselected or selected
346   if (status)
347   {
348     vtkSMRepresentationProxy* repr =
349       vtkSMRepresentationProxy::SafeDownCast(selectedRepresentations->GetItemAsObject(0));
350
351     // If the selection occurs on a new represention, clean the selection on the
352     // current representation before continuing
353     if (this->CurrentRepresentation != nullptr && repr != this->CurrentRepresentation)
354     {
355       vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
356       vtkSMProxy* emptySel = pxm->NewProxy("sources", "IDSelectionSource");
357
358       vtkSMPropertyHelper(this->CurrentRepresentation, "Selection").Set(emptySel);
359       this->CurrentRepresentation->UpdateVTKObjects();
360       emptySel->Delete();
361     }
362
363     this->CurrentRepresentation = repr;
364
365     // Set the selection (selection source) to the current representation
366     vtkSMSourceProxy* sel = vtkSMSourceProxy::SafeDownCast(selectionSources->GetItemAsObject(0));
367     vtkSMPropertyHelper(repr, "Selection").Set(sel);
368     repr->UpdateVTKObjects();
369   }
370   // If nothing has been selected then clean current representation
371   // with an "empty" selection source
372   else if (this->CurrentRepresentation != nullptr)
373   {
374     vtkSMSessionProxyManager* pxm = rmp->GetSessionProxyManager();
375     vtkSMProxy* emptySel = pxm->NewProxy("sources", "IDSelectionSource");
376
377     vtkSMPropertyHelper(this->CurrentRepresentation, "Selection").Set(emptySel);
378     this->CurrentRepresentation->UpdateVTKObjects();
379     this->CurrentRepresentation = nullptr;
380     emptySel->Delete();
381   }
382
383   this->View->forceRender();
384   this->View->forceRender();
385   // TODO improve this to avoid double render
386
387   if (repr && !presel)
388   {
389     repr->InvokeCommand("EndSelect");
390   }
391 }
392
393 //-----------------------------------------------------------------------------
394 void SPV3D_CADSelection::selectionChanged(vtkObject*, unsigned long, void* calldata)
395 {
396   BEGIN_UNDO_EXCLUDE();
397
398   int selectionModifier = this->getSelectionModifier();
399   int* region = reinterpret_cast<int*>(calldata);
400 // Simple version check to change once 5.10 support is dropped
401   this->View->selectCellsOnSurface(region, selectionModifier);
402   //this->View->selectOnSurface(region, selectionModifier);
403   END_UNDO_EXCLUDE();
404
405   this->endSelection();
406 }
407
408 //-----------------------------------------------------------------------------
409 int SPV3D_CADSelection::getSelectionModifier()
410 {
411   int selectionModifier = pqView::PV_SELECTION_DEFAULT;//this->Superclass::getSelectionModifier();
412
413   vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
414   assert(rmp != nullptr);
415
416   bool ctrl = rmp->GetInteractor()->GetControlKey() == 1;
417   bool shift = rmp->GetInteractor()->GetShiftKey() == 1;
418   selectionModifier = pqView::PV_SELECTION_TOGGLE;
419
420   if (ctrl)
421   {
422     selectionModifier = pqView::PV_SELECTION_ADDITION;
423   }
424   else if (shift)
425   {
426     selectionModifier = pqView::PV_SELECTION_SUBTRACTION;
427   }
428   return selectionModifier;
429 }
430
431 //-----------------------------------------------------------------------------
432 void SPV3D_CADSelection::cleanupObservers()
433 {
434   for (size_t i = 0; i < sizeof(this->ObserverIds) / sizeof(this->ObserverIds[0]); ++i)
435   {
436     if (this->ObservedObject != nullptr && this->ObserverIds[i] > 0)
437     {
438       this->ObservedObject->RemoveObserver(this->ObserverIds[i]);
439     }
440     this->ObserverIds[i] = 0;
441   }
442   this->ObservedObject = nullptr;
443 }