]> SALOME platform Git repositories - modules/gui.git/commitdiff
Salome HOME
Do refactorings and add comments
authorThomas Galland <thomas.galland@kitware.com>
Wed, 20 Nov 2024 15:03:01 +0000 (16:03 +0100)
committerThomas Galland <thomas.galland@kitware.com>
Fri, 22 Nov 2024 14:15:48 +0000 (15:15 +0100)
src/SPV3D/SPV3D_CADSelection.cxx
src/SPV3D/SPV3D_CADSelection.h

index 30e65b6f66a8bdba2d5fef18a1d91a51ad1c7fc6..5233179cf8b15091562c1dcec47463b6d085d057 100644 (file)
 //
 
 #include "SPV3D_CADSelection.h"
+#include "SPV3D_Prs.h"
 
 #include <pqActiveObjects.h>
 #include <pqRenderView.h>
-#include <pqUndoStack.h>
 #include <pqCoreUtilities.h>
 
 #include <vtkDataObject.h>
 #include <vtkCollection.h>
-#include <vtkIdTypeArray.h>
-#include <vtkRenderWindowInteractor.h>
 #include <vtkPVRenderView.h>
-#include <vtkPVDataInformation.h>
-#include <vtkPVArrayInformation.h>
 #include <vtkPVRenderViewSettings.h>
+#include <vtkRenderWindowInteractor.h>
 #include <vtkSMRenderViewProxy.h>
 #include <vtkSMPropertyHelper.h>
-#include <vtkSMSelectionHelper.h>
 #include <vtkSMSessionProxyManager.h>
-#include <vtkSMStringVectorProperty.h>
 #include <vtkSMRepresentationProxy.h>
-#include <vtkPVDataSetAttributesInformation.h>
-#include <vtkSMSelectionHelper.h>
-#include <vtkSMOutputPort.h>
-#include <QDebug>
-#include <vtkSelectionSource.h>
-#include <vtkSelectionNode.h>
-#include <pqPVApplicationCore.h>
-#include <pqSelectionManager.h>
-#include <pqServerManagerModel.h>
-#include <vtkPVEncodeSelectionForServer.h>
-#include <vtkInformation.h>
+#include <vtkSmartPointer.h>
 
-#include "SPV3D_Prs.h"
+#include <QCursor>
+#include <QDebug>
 
-// TO REMOVE
 #include <algorithm>
 #include <iterator>
-#include "vtkSMProxyManager.h"
-#include <regex>
 #include <set>
-#include <unordered_set>
-namespace
+
+namespace CADSelection
 {
-void ReplaceString(std::string& source, const std::string& replace, const std::string& with)
+typedef std::set<std::pair<vtkIdType, vtkIdType>> SelectionValues;
+
+namespace Utilities
 {
-  const std::regex reg("[a-zA-Z0-9]+");
-  for (auto match = std::sregex_iterator(source.begin(), source.end(), reg);
-       match != std::sregex_iterator(); ++match)
-  {
-    if (match->str() == replace)
+//-----------------------------------------------------------------------------
+/**
+ * Create a new selection source for given selection values (process ID + array value).
+ * The values correspond to the IDs of objects being selected (solid, faces, edges, vertices).
+ * The type of object is determined by the array name (the array containing the values).
+ */
+bool SetSelectionFromValues(vtkSMRepresentationProxy* repr, const SelectionValues& values, const std::string& arrayName)
+{
+  if (repr)
+  {
+    vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
+    vtkSmartPointer<vtkSMProxy> newSelectionSource;
+    newSelectionSource.TakeReference(pxm->NewProxy("sources", "ValueSelectionSource"));
+    vtkSMPropertyHelper(newSelectionSource, "ArrayName").Set(arrayName.c_str());
+    vtkSMPropertyHelper(newSelectionSource, "FieldType").Set("CELL");
+    vtkSMPropertyHelper(newSelectionSource, "Values").SetNumberOfElements(values.size());
+    vtkSMPropertyHelper(newSelectionSource, "Values").RemoveAllValues();
+    for (const auto& pair: values)
     {
-      source.replace(match->position(), match->length(), with);
+      std::array<vtkIdType, 2> pairValues = {pair.first, pair.second};
+      vtkSMPropertyHelper(newSelectionSource, "Values").Append(pairValues.data(), 2);
     }
+    newSelectionSource->UpdateVTKObjects();
+
+    vtkSMPropertyHelper(repr, "Selection").Set(newSelectionSource);
+    repr->UpdateVTKObjects();
+    return true;
   }
+  return false;
 }
-enum class CombineOperation
-{
-  DEFAULT = 0,
-  ADDITION = 1,
-  SUBTRACTION = 2,
-  TOGGLE = 3
-};
-const std::string SubSelectionBaseName = "s";
-bool CombineSelection(vtkSMSourceProxy* appendSelections1,
-  vtkSMSourceProxy* appendSelections2, CombineOperation combineOperation, bool deepCopy)
+
+//-----------------------------------------------------------------------------
+// Get the selection values (process ID + array value) "contained" in the given selection source.
+bool GetValuesFromSelection(vtkSMProxy* selectionSourceProxy, SelectionValues& values)
 {
-  if (!appendSelections1 || !appendSelections2)
-  {
-    return false;
-  }
-  if (vtkSMPropertyHelper(appendSelections2, "Input").GetNumberOfElements() == 0)
-  {
-    return false;
-  }
-  if (deepCopy)
-  {
-    if (vtkSMPropertyHelper(appendSelections1, "Input").GetNumberOfElements() == 0)
-    {
-      // create a combined appendSelections which is deep copy of appendSelections2
-      vtkSMSessionProxyManager* pxm = vtkSMProxyManager::GetProxyManager()->GetSessionProxyManager(
-        appendSelections2->GetSession());
-      vtkSmartPointer<vtkSMSourceProxy> combinedAppendSelections;
-      combinedAppendSelections.TakeReference(
-        vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("filters", "AppendSelections")));
-
-      vtkSMPropertyHelper(combinedAppendSelections, "Expression")
-        .Set(vtkSMPropertyHelper(appendSelections2, "Expression").GetAsString());
-      vtkSMPropertyHelper(combinedAppendSelections, "InsideOut")
-        .Set(vtkSMPropertyHelper(appendSelections2, "InsideOut").GetAsInt());
-
-      // add selection input and names of appendSelections2
-      unsigned int numInputs =
-        vtkSMPropertyHelper(appendSelections2, "Input").GetNumberOfElements();
-      for (unsigned int i = 0; i < numInputs; ++i)
-      {
-        auto selectionSource = vtkSMPropertyHelper(appendSelections2, "Input").GetAsProxy(i);
-        vtkSmartPointer<vtkSMSourceProxy> selectionSourceCopy;
-        selectionSourceCopy.TakeReference(
-          vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("sources", selectionSource->GetXMLName())));
-        selectionSourceCopy->Copy(selectionSource);
-        selectionSourceCopy->UpdateVTKObjects();
-
-        vtkSMPropertyHelper(combinedAppendSelections, "Input").Add(selectionSourceCopy);
-        vtkSMPropertyHelper(combinedAppendSelections, "SelectionNames")
-          .Set(i, vtkSMPropertyHelper(appendSelections2, "SelectionNames").GetAsString(i));
-      }
-      appendSelections2->Copy(combinedAppendSelections);
-      appendSelections2->UpdateVTKObjects();
-      return true;
-    }
-  }
-  else
+  if (selectionSourceProxy)
   {
-    if (vtkSMPropertyHelper(appendSelections1, "Input").GetNumberOfElements() == 0)
+    auto allValues = vtkSMPropertyHelper(selectionSourceProxy, "Values").GetIdTypeArray();
+    for (std::size_t i = 0; i < allValues.size(); i+=2)
     {
-      return true;
+      values.insert(std::make_pair(allValues[i], allValues[i+1]));
     }
-  }
-  if (combineOperation == CombineOperation::DEFAULT)
-  {
     return true;
   }
-  // appendSelections1 serves as input1 and appendSelections2 serves as input2.
-  // The result of this function will be appended into appendSelections2.
-
-  // The appendSelections1 is combine-able with the appendSelections2 if they have the same
-  // FieldType/ElementType and if they have the same ContainingCells
-  // Checking only one of the inputs is sufficient.
-  vtkSMProxy* firstSelectionSourceAP1 =
-    vtkSMPropertyHelper(appendSelections1, "Input").GetAsProxy(0);
-  vtkSMProxy* firstSelectionSourceAP2 =
-    vtkSMPropertyHelper(appendSelections2, "Input").GetAsProxy(0);
-
-  // SelectionQuerySource has element type and not field type
-  int fieldTypeOfFirstSelectionSourceAP1 = firstSelectionSourceAP1->GetProperty("FieldType")
-    ? vtkSMPropertyHelper(firstSelectionSourceAP1, "FieldType").GetAsInt()
-    : vtkSelectionNode::ConvertAttributeTypeToSelectionField(
-        vtkSMPropertyHelper(firstSelectionSourceAP1, "ElementType").GetAsInt());
-  int fieldTypeOfFirstSelectionSourceAP2 = firstSelectionSourceAP2->GetProperty("FieldType")
-    ? vtkSMPropertyHelper(firstSelectionSourceAP2, "FieldType").GetAsInt()
-    : vtkSelectionNode::ConvertAttributeTypeToSelectionField(
-        vtkSMPropertyHelper(firstSelectionSourceAP2, "ElementType").GetAsInt());
-  if (fieldTypeOfFirstSelectionSourceAP1 != fieldTypeOfFirstSelectionSourceAP2)
-  {
-    return false;
-  }
-
-  if (vtkSMPropertyHelper(firstSelectionSourceAP1, "ContainingCells", true).GetAsInt() !=
-    vtkSMPropertyHelper(firstSelectionSourceAP2, "ContainingCells", true).GetAsInt())
-  {
-    return false;
-  }
+  return false;
+}
 
-  unsigned int numInputsAP1 = vtkSMPropertyHelper(appendSelections1, "Input").GetNumberOfElements();
-  // find the largest selection name id of the appendSelections2
-  int maxId = -1;
-  for (unsigned int i = 0; i < numInputsAP1; ++i)
+//-----------------------------------------------------------------------------
+// Set the selection (described by the selection source) to the representation.
+void SetSelection(vtkSMRepresentationProxy* repr, vtkSMSourceProxy* selectionSource)
+{
+  if (repr && selectionSource)
   {
-    // get the selection name
-    std::string selectionName =
-      vtkSMPropertyHelper(appendSelections1, "SelectionNames").GetAsString(i);
-    // remove the S prefix
-    selectionName.erase(0, SubSelectionBaseName.size());
-    // get the id
-    maxId = std::max(maxId, std::stoi(selectionName));
+    selectionSource->UpdateVTKObjects();
+    vtkSMPropertyHelper(repr, "Selection").Set(selectionSource);
+    repr->UpdateVTKObjects();
   }
+}
 
-  // create new expression and selection names from appendSelections2's selections sources
-  std::string newExpressionAP2 = vtkSMPropertyHelper(appendSelections2, "Expression").GetAsString();
-  unsigned int numInputsAP2 = vtkSMPropertyHelper(appendSelections2, "Input").GetNumberOfElements();
-  std::list<std::string> newSelectionNamesAP2;
-  for (int i = static_cast<int>(numInputsAP2) - 1; i >= 0; --i)
+//-----------------------------------------------------------------------------
+// Set the preselection (described by the selection source) to the representation.
+void SetPreSelection(vtkSMRepresentationProxy* repr, vtkSMSourceProxy* selectionSource)
+{
+  if (repr && selectionSource)
   {
-    const std::string oldSelectionName = vtkSMPropertyHelper(appendSelections2, "SelectionNames")
-                                           .GetAsString(static_cast<unsigned int>(i));
-    std::string newSelectionName = oldSelectionName;
-    // remove the S prefix
-    newSelectionName.erase(0, SubSelectionBaseName.size());
-    // compute new selection name id
-    int selectionNameId = std::atoi(newSelectionName.c_str()) + maxId + 1;
-    newSelectionName = SubSelectionBaseName + std::to_string(selectionNameId);
-    // save new selection name
-    newSelectionNamesAP2.push_front(newSelectionName);
-    // update the expression
-    ReplaceString(newExpressionAP2, oldSelectionName, newSelectionName);
+    selectionSource->UpdateVTKObjects();
+    vtkSMPropertyHelper(repr, "PreSelection").Set(selectionSource);
+    repr->UpdateVTKObjects();
   }
+}
 
-  // create a combined appendSelections
-  vtkSMSessionProxyManager* pxm =
-    vtkSMProxyManager::GetProxyManager()->GetSessionProxyManager(appendSelections2->GetSession());
-  vtkSmartPointer<vtkSMSourceProxy> combinedAppendSelections;
-  combinedAppendSelections.TakeReference(
-    vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("filters", "AppendSelections")));
-  // add selection input and names of appendSelections1 to appendSelections2
-  for (unsigned int i = 0; i < numInputsAP1; ++i)
-  {
-    auto selectionSource = vtkSMPropertyHelper(appendSelections1, "Input").GetAsProxy(i);
-    if (deepCopy)
-    {
-      vtkSmartPointer<vtkSMSourceProxy> selectionSourceCopy;
-      selectionSourceCopy.TakeReference(
-        vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("sources", selectionSource->GetXMLName())));
-      selectionSourceCopy->Copy(selectionSource);
-      selectionSourceCopy->UpdateVTKObjects();
-      vtkSMPropertyHelper(combinedAppendSelections, "Input").Add(selectionSourceCopy);
-    }
-    else
-    {
-      vtkSMPropertyHelper(combinedAppendSelections, "Input").Add(selectionSource);
-    }
-    vtkSMPropertyHelper(combinedAppendSelections, "SelectionNames")
-      .Set(i, vtkSMPropertyHelper(appendSelections1, "SelectionNames").GetAsString(i));
-  }
-  auto iter = newSelectionNamesAP2.begin();
-  for (unsigned int i = 0; i < numInputsAP2; ++i, ++iter)
+//-----------------------------------------------------------------------------
+// Retrieve the selection (selection source) from the representation.
+vtkSMSourceProxy* GetSelection(vtkSMRepresentationProxy* repr)
+{
+  if (repr)
   {
-    auto selectionSource = vtkSMPropertyHelper(appendSelections2, "Input").GetAsProxy(i);
-    if (deepCopy)
-    {
-      vtkSmartPointer<vtkSMSourceProxy> selectionSourceCopy;
-      selectionSourceCopy.TakeReference(
-        vtkSMSourceProxy::SafeDownCast(pxm->NewProxy("sources", selectionSource->GetXMLName())));
-      selectionSourceCopy->Copy(selectionSource);
-      selectionSourceCopy->UpdateVTKObjects();
-      vtkSMPropertyHelper(combinedAppendSelections, "Input").Add(selectionSourceCopy);
-    }
-    else
-    {
-      vtkSMPropertyHelper(combinedAppendSelections, "Input").Add(selectionSource);
-    }
-    vtkSMPropertyHelper(combinedAppendSelections, "SelectionNames")
-      .Set(numInputsAP1 + i, iter->c_str());
+    return vtkSMSourceProxy::SafeDownCast(vtkSMPropertyHelper(repr, "Selection").GetAsProxy());
   }
+  return nullptr;
+}
 
-  // add inside out qualifier to the expressions
-  const int insideOutAP1 = vtkSMPropertyHelper(appendSelections1, "InsideOut").GetAsInt();
-  std::string newExpressionAP1 = vtkSMPropertyHelper(appendSelections1, "Expression").GetAsString();
-  if (numInputsAP1 > 1)
+//-----------------------------------------------------------------------------
+/**
+ * Remove the selection (desctrbed by the selection source) from the representation.
+ * Under the hood, this method creates an empty selection and sets it to the representation.
+ */
+void RemoveSelection(vtkSMRepresentationProxy* repr)
+{
+  if (repr)
   {
-    newExpressionAP1 = '(' + newExpressionAP1 + ')';
-  }
-  newExpressionAP1 = (insideOutAP1 ? "!" : "") + newExpressionAP1;
+    /**
+     * Under the hood, we simply add an empty selection source
+     * XXX: avoid creating a new proxy each time, and look for pre/post initialize
+     */ 
+    vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
+    vtkSmartPointer<vtkSMProxy> emptySelection;
+    emptySelection.TakeReference(pxm->NewProxy("sources", "ValueSelectionSource"));
+    emptySelection->UpdateVTKObjects();
 
-  const int insideOutAP2 = vtkSMPropertyHelper(appendSelections2, "InsideOut").GetAsInt();
-  if (numInputsAP2 > 1)
-  {
-    newExpressionAP2 = '(' + newExpressionAP2 + ')';
+    vtkSMPropertyHelper(repr, "Selection").Set(emptySelection);
+    repr->UpdateVTKObjects();
   }
-  newExpressionAP2 = (insideOutAP2 ? "!" : "") + newExpressionAP2;
+}
 
-  // combine appendSelections1 and appendSelections2 expressions
-  std::string newCombinedExpression;
-  switch (combineOperation)
-  {
-    case CombineOperation::ADDITION:
-    {
-      newCombinedExpression = newExpressionAP1 + '|' + newExpressionAP2;
-      break;
-    }
-    case CombineOperation::SUBTRACTION:
-    {
-      newCombinedExpression = newExpressionAP1 + "&!" + newExpressionAP2;
-      break;
-    }
-    case CombineOperation::TOGGLE:
-    default:
-    {
-      newCombinedExpression = newExpressionAP1 + '^' + newExpressionAP2;
-      break;
-    }
+//-----------------------------------------------------------------------------
+/**
+ * Remove the preselection (described by the selection source) from the representation.
+ * Under the hood, this method creates an empty selection and sets it to the representation.
+ */
+void RemovePreSelection(vtkSMRepresentationProxy* repr)
+{
+  if (repr)
+  {
+    /**
+     * Under the hood, we simply add an empty selection source
+     * XXX: avoid creating a new proxy each time, and look for pre/post initialize
+     */ 
+    vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
+    vtkSmartPointer<vtkSMProxy> emptySelection;
+    emptySelection.TakeReference(pxm->NewProxy("sources", "ValueSelectionSource"));
+    emptySelection->UpdateVTKObjects();
+
+    vtkSMPropertyHelper(repr, "PreSelection").Set(emptySelection);
+    repr->UpdateVTKObjects();
   }
-  vtkSMPropertyHelper(combinedAppendSelections, "Expression").Set(newCombinedExpression.c_str());
-  appendSelections2->Copy(combinedAppendSelections);
-  appendSelections2->UpdateVTKObjects();
-  // cout << "Expression : " << newCombinedExpression << endl;
-  return true;
 }
-bool IgnoreSelection(
-  vtkSMSourceProxy* appendSelections1, vtkSMSourceProxy* appendSelections2, bool deepCopy = false)
+} // namespace Utilities
+
+namespace Operations
 {
-  return CombineSelection(
-    appendSelections1, appendSelections2, CombineOperation::DEFAULT, deepCopy);
-}
-bool AddSelection(
-  vtkSMSourceProxy* appendSelections1, vtkSMSourceProxy* appendSelections2, bool deepCopy = false)
+//-----------------------------------------------------------------------------
+/** 
+ * Return the selection values corresponding of the union of the selected values "contained" 
+ * in the two selection sources. Selected values will be unique (no dupplicates).
+ */
+void Union(vtkSMProxy* selectionSourceProxy1, vtkSMProxy* selectionSourceProxy2, SelectionValues& result)
 {
-  return CombineSelection(
-    appendSelections1, appendSelections2, CombineOperation::ADDITION, deepCopy);
+  result.clear();
+  SelectionValues values1, values2;
+  CADSelection::Utilities::GetValuesFromSelection(selectionSourceProxy1, values1);
+  CADSelection::Utilities::GetValuesFromSelection(selectionSourceProxy2, values2);
+
+  std::set_union(values1.begin(), values1.end(), values2.begin(), values2.end(), std::inserter(result, result.begin()));
 }
-bool SubtractSelection(
-  vtkSMSourceProxy* appendSelections1, vtkSMSourceProxy* appendSelections2, bool deepCopy = false)
+
+//-----------------------------------------------------------------------------
+/** 
+ * Return the selection values corresponding of the difference of the selected values "contained" 
+ * in the two selection sources. Selected values will be unique (no dupplicates).
+ */
+void Difference(vtkSMProxy* selectionSourceProxy1, vtkSMProxy* selectionSourceProxy2, SelectionValues& result)
 {
-  return CombineSelection(
-    appendSelections1, appendSelections2, CombineOperation::SUBTRACTION, deepCopy);
+  result.clear();
+  SelectionValues values1, values2;
+  CADSelection::Utilities::GetValuesFromSelection(selectionSourceProxy1, values1);
+  CADSelection::Utilities::GetValuesFromSelection(selectionSourceProxy2, values2);
+
+  std::set_difference(values1.begin(), values1.end(), values2.begin(), values2.end(), std::inserter(result, result.begin()));
 }
-bool ToggleSelection(
-  vtkSMSourceProxy* appendSelections1, vtkSMSourceProxy* appendSelections2, bool deepCopy = false)
+
+//-----------------------------------------------------------------------------
+/** 
+ * Return the selection values corresponding of the symmetric difference of the selected values 
+ * "contained" in the two selection sources. Selected values will be unique (no dupplicates).
+ */
+void SymmetricDifference(vtkSMProxy* selectionSourceProxy1, vtkSMProxy* selectionSourceProxy2, SelectionValues& result)
 {
-  return CombineSelection(
-    appendSelections1, appendSelections2, CombineOperation::TOGGLE, deepCopy);
-}
+  result.clear();
+  SelectionValues values1, values2;
+  CADSelection::Utilities::GetValuesFromSelection(selectionSourceProxy1, values1);
+  CADSelection::Utilities::GetValuesFromSelection(selectionSourceProxy2, values2);
+
+  std::set_symmetric_difference(values1.begin(), values1.end(), values2.begin(), values2.end(), std::inserter(result, result.begin()));
 }
+} // namespace Operations
+} // namespace CADSelection
 
 //-----------------------------------------------------------------------------
-SPV3D_CADSelection::SPV3D_CADSelection(QObject *parent,
-  pqRenderView* view, SelectionMode mode):QObject(parent)
+SPV3D_CADSelection::SPV3D_CADSelection(QObject *parent, pqRenderView* view, SelectionMode mode)
+  : QObject(parent), View(view)
 {
-  this->View = view;
-  this->Mode = mode;
-  this->PreviousRenderViewMode = -1;
-  this->MousePosition[0] = 0;
-  this->MousePosition[1] = 0;
-  //this->selectionComboBox = comboBox;
+  // Initialize the selection mode and the observers
+  this->SetMode(mode);
 
   for (size_t i = 0; i < sizeof(this->ObserverIds) / sizeof(this->ObserverIds[0]); ++i)
   {
     this->ObserverIds[i] = 0;
   }
 
-  //QObject::connect(button, SIGNAL(toggled(bool)), this, SLOT(actionTriggered(bool)));
-
   // if view == nullptr, we track the active view.
   if (view == nullptr)
   {
     QObject::connect(
-      &pqActiveObjects::instance(), SIGNAL(viewChanged(pqView*)), this, SLOT(setView(pqView*)));
-    // this ensure that the enabled-state is set correctly.
-    this->setView(nullptr);
+      &pqActiveObjects::instance(), &pqActiveObjects::viewChanged, this, &SPV3D_CADSelection::setView);
   }
 
-  this->setRepresentation(nullptr);
   QObject::connect(&pqActiveObjects::instance(),
-    SIGNAL(representationChanged(pqDataRepresentation*)), this,
-    SLOT(setRepresentation(pqDataRepresentation*)));
+    QOverload<pqDataRepresentation*>::of(&pqActiveObjects::representationChanged), this, &SPV3D_CADSelection::setRepresentation);
 
+  // This info may be useful in vtkSMRenderViewProxy::MarkDirty
   vtkPVRenderViewSettings::GetInstance()->SetEnableFastPreselection(true);
-
-  this->updateEnableState();
 }
 
 //-----------------------------------------------------------------------------
@@ -367,48 +261,23 @@ SPV3D_CADSelection::~SPV3D_CADSelection()
 //-----------------------------------------------------------------------------
 void SPV3D_CADSelection::SetMode(const SPV3D_CADSelection::SelectionMode mode)
 {
-  this->Mode = mode;
-}
-
-//-----------------------------------------------------------------------------
-void SPV3D_CADSelection::updateEnableState()
-{
-  this->endSelection();
-
-  //auto paction = this->parentAction();
-  //bool state = false;
-  if (this->Representation)
+  // Set the array containing the values describing the entities (solid, faces, etc.).
+  switch (mode)
   {
-    vtkSMProxy* proxy = this->Representation->getProxy();
-    vtkSMStringVectorProperty* prop =
-      vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("ColorArrayName"));
-    if (prop)
-    {
-      int association = std::atoi(prop->GetElement(3));
-      const char* arrayName = prop->GetElement(4);
-
-      vtkPVDataInformation* dataInfo = this->Representation->getInputDataInformation();
-
-      vtkPVDataSetAttributesInformation* info = nullptr;
-      if (association == vtkDataObject::CELL &&
-        this->Mode == SELECT_FACES)
-      {
-        info = dataInfo->GetCellDataInformation();
-      }
-      if (association == vtkDataObject::POINT &&
-        this->Mode == SELECT_VERTICES)
-      {
-        info = dataInfo->GetPointDataInformation();
-      }
-
-      if (info)
-      {
-        /*vtkPVArrayInformation* arrayInfo = */info->GetArrayInformation(arrayName);
-        //state = arrayInfo && arrayInfo->GetDataType() == VTK_ID_TYPE;
-      }
-    }
+  case SELECT_VERTICES:
+    this->SelectionArrayName = "Vertex id";
+    break;
+  case SELECT_EDGES:
+    this->SelectionArrayName = "Edge id";
+    break;
+  case SELECT_FACES:
+    this->SelectionArrayName = "Face id";
+    break;
+  case SELECT_SOLIDS:
+  default:
+    this->SelectionArrayName = "Solid id";
+    break;
   }
-  //paction->setEnabled(state);
 }
 
 //-----------------------------------------------------------------------------
@@ -431,12 +300,8 @@ void SPV3D_CADSelection::setView(pqView* view)
   {
     // if we were currently in selection, finish that before changing the view.
     this->endSelection();
+    this->View = qobject_cast<pqRenderView*>(view);
   }
-
-  this->View = qobject_cast<pqRenderView*>(view);
-
-  // update enable state.
-  //this->parentAction()->setEnabled(this->View != nullptr);
 }
 
 //-----------------------------------------------------------------------------
@@ -446,22 +311,7 @@ void SPV3D_CADSelection::setRepresentation(pqDataRepresentation* representation)
   {
     // if we are currently in selection, finish that before changing the representation.
     this->endSelection();
-
-    if (this->Representation != nullptr)
-    {
-      QObject::disconnect(this->RepresentationConnection);
-    }
-
     this->Representation = representation;
-
-    if (this->Representation != nullptr)
-    {
-      this->RepresentationConnection = this->connect(
-        this->Representation, SIGNAL(colorArrayNameModified()), SLOT(updateEnableState()));
-    }
-
-    // update enable state.
-    this->updateEnableState();
   }
 }
 
@@ -473,128 +323,71 @@ void SPV3D_CADSelection::beginSelection()
     return;
   }
 
-  //QAction* actn = this->parentAction();
-  //if (actn->isCheckable())
-  {
-    //this->selectionComboBox->setEnabled(false);
-
-    vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
-    vtkSMPropertyHelper(rmp, "InteractionMode").Get(&this->PreviousRenderViewMode);
-
-    //QString currentPrimitive = this->selectionComboBox->currentText();
-    pqCoreUtilities::promptUser("SPV3D_CADSelection", QMessageBox::Information,
-      "Interactive Selection Information",
-      "You are entering interactive selection mode to highlight.\n"
-      "Move the mouse point over the dataset to interactively highlight elements.\n\n"
-      "Press Ctrl + click to add the currently highlighted element.\n"
-      "Press Shift + click to remove the element.\n\n"
-      "Click on Select button to exit this mode.",
-      QMessageBox::Ok | QMessageBox::Save);
-    this->View->setCursor(Qt::CrossCursor);
-    vtkSMPropertyHelper(rmp, "InteractionMode").Set(vtkPVRenderView::INTERACTION_MODE_SELECTION);
-
-    rmp->UpdateVTKObjects();
-
-    // Setup observer.
-    assert(this->ObserverIds[0] == 0 && this->ObservedObject == nullptr && this->ObserverIds[1] == 0);
-    this->ObservedObject = rmp->GetInteractor();
-    this->ObserverIds[0] = this->ObservedObject->AddObserver(
-      vtkCommand::MouseMoveEvent, this, &SPV3D_CADSelection::onMouseMove);
-    this->ObserverIds[1] = this->ObservedObject->AddObserver(vtkCommand::LeftButtonReleaseEvent,
-      this, &SPV3D_CADSelection::onLeftButtonRelease);
-    this->ObserverIds[2] = this->ObservedObject->AddObserver(vtkCommand::RightButtonPressEvent,
-      this, &SPV3D_CADSelection::onRightButtonPress);
-    this->ObserverIds[3] = this->ObservedObject->AddObserver(vtkCommand::RightButtonReleaseEvent,
-      this, &SPV3D_CADSelection::onRightButtonRelease);
-
-    //this->parentAction()->setChecked(true);
-  }
+  // Save the current interaction mode to restore it when selection ends
+  vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
+  vtkSMPropertyHelper(rmp, "InteractionMode").Get(&this->PreviousInteractionMode);
+
+  pqCoreUtilities::promptUser("SPV3D_CADSelection", QMessageBox::Information,
+    "Interactive Selection Information",
+    "You are entering interactive selection mode to highlight.\n"
+    "Move the mouse point over the dataset to interactively highlight elements.\n\n"
+    "Press Ctrl + click to add the currently highlighted element.\n"
+    "Press Shift + click to remove the element.\n\n"
+    "Click on Select button to exit this mode.",
+    QMessageBox::Ok | QMessageBox::Save);
+  this->View->setCursor(Qt::CrossCursor);
+  vtkSMPropertyHelper(rmp, "InteractionMode").Set(vtkPVRenderView::INTERACTION_MODE_SELECTION);
+
+  rmp->UpdateVTKObjects();
+
+  // Setup observers.
+  assert(this->ObserverIds[0] == 0 && this->ObservedObject == nullptr && this->ObserverIds[1] == 0);
+  this->ObservedObject = rmp->GetInteractor();
+  this->ObserverIds[0] = this->ObservedObject->AddObserver(
+    vtkCommand::MouseMoveEvent, this, &SPV3D_CADSelection::onMouseMove);
+  this->ObserverIds[1] = this->ObservedObject->AddObserver(vtkCommand::LeftButtonReleaseEvent,
+    this, &SPV3D_CADSelection::onLeftButtonRelease);
+  this->ObserverIds[2] = this->ObservedObject->AddObserver(vtkCommand::RightButtonPressEvent,
+    this, &SPV3D_CADSelection::onRightButtonPress);
+  this->ObserverIds[3] = this->ObservedObject->AddObserver(vtkCommand::RightButtonReleaseEvent,
+    this, &SPV3D_CADSelection::onRightButtonRelease);
 }
 
 //-----------------------------------------------------------------------------
 void SPV3D_CADSelection::endSelection()
 {
-  cout << "End Selection " << endl;
   if (!this->View)
   {
     return;
   }
 
-  if (this->PreviousRenderViewMode == -1)
+  if (this->PreviousInteractionMode == -1)
   {
     return;
   }
 
-  //QAction* actn = this->parentAction();
-  //if (actn->isCheckable())
-  {
-    vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
-    vtkSMPropertyHelper(rmp, "InteractionMode").Set(this->PreviousRenderViewMode);
-    this->PreviousRenderViewMode = -1;
-    rmp->UpdateVTKObjects();
-    this->View->setCursor(QCursor());
-    this->cleanupObservers();
-    //this->parentAction()->setChecked(false);
-
-    if (this->CurrentRepresentation != nullptr)
-    {
-      cout << "???" << endl;
-      vtkSMSessionProxyManager* pxm = rmp->GetSessionProxyManager();
-      vtkSMProxy* emptySel = pxm->NewProxy("sources", "IDSelectionSource");
-
-      this->CurrentRepresentation->UpdateVTKObjects();
-      this->CurrentRepresentation = nullptr;
-      emptySel->Delete();
-
-      rmp->StillRender();
-    }
-
-    //this->selectionComboBox->setEnabled(true);
-  }
+  // Restore the previous interaction mode and cursor
+  vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
+  vtkSMPropertyHelper(rmp, "InteractionMode").Set(this->PreviousInteractionMode);
+  this->PreviousInteractionMode = -1;
+  rmp->UpdateVTKObjects();
+  this->View->setCursor(QCursor());
+  this->cleanupObservers();
 }
 
 //-----------------------------------------------------------------------------
 void SPV3D_CADSelection::onMouseMove()
 {
-  if (!this->DisablePreSelection && vtkPVRenderViewSettings::GetInstance()->GetEnableFastPreselection())
+  if (!this->DisablePreSelection)
   {
-    this->fastPreSelection();
-  }
-
-  // get preselected id here
-  vtkSMProxy* proxyRepresentation = this->Representation->getProxy();
-  if (!proxyRepresentation)
-  {
-    qWarning()<< "There is no representation in the active view for the Geometry Source.";
-    return;
-  }
-
-
-  // Retrieve the wanted information property
-  vtkSMProperty* PreselectedIDProperty =
-    proxyRepresentation->GetProperty("PreSelectedID");
-  if (!PreselectedIDProperty)
-  {
-    qWarning()<< "The representation named '" << proxyRepresentation->GetXMLName()<< "' didn't have a property named 'PreSelectedID'.";
-    return;
+    this->preSelect();
   }
-
-  // Force to update the information property
-  proxyRepresentation->UpdatePropertyInformation(PreselectedIDProperty);
-
-  vtkIdType PreSelectedID =
-    vtkSMPropertyHelper(proxyRepresentation,"PreSelectedID").GetAsInt(0);
-  qInfo() << "entry from client: "<< SPV3D_Prs::FromVtkIdToEntry(PreSelectedID).c_str();
-  
 }
 
 //-----------------------------------------------------------------------------
 void SPV3D_CADSelection::onLeftButtonRelease()
 {
-  if (vtkPVRenderViewSettings::GetInstance()->GetEnableFastPreselection())
-  {
-    this->fastSelection();
-  }
+  this->select();
   emit selectionChanged();
 }
 
@@ -610,107 +403,26 @@ void SPV3D_CADSelection::onRightButtonRelease()
   this->DisablePreSelection = false;
 }
 
-namespace SelectionUtilities
-{
-//-----------------------------------------------------------------------------
-bool SetSelectionFromValues(vtkSMRepresentationProxy* repr, const std::set<std::pair<vtkIdType, vtkIdType>>& values)
-{
-  vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
-  vtkSmartPointer<vtkSMProxy> newSelectionSource;
-  newSelectionSource.TakeReference(pxm->NewProxy("sources", "ValueSelectionSource"));
-  vtkSMPropertyHelper(newSelectionSource, "ArrayName").Set("Solid id");
-  vtkSMPropertyHelper(newSelectionSource, "FieldType").Set("CELL");
-  vtkSMPropertyHelper(newSelectionSource, "Values").SetNumberOfElements(values.size());
-  vtkSMPropertyHelper(newSelectionSource, "Values").RemoveAllValues();
-  for (const auto& pair: values)
-  {
-    std::array<vtkIdType, 2> pairValues = {pair.first, pair.second};
-    vtkSMPropertyHelper(newSelectionSource, "Values").Append(pairValues.data(), 2);
-  }
-  newSelectionSource->UpdateVTKObjects();
-
-  vtkSMPropertyHelper(repr, "Selection").Set(newSelectionSource);
-  repr->UpdateVTKObjects();
-  return true;
-}
-
 //-----------------------------------------------------------------------------
-void SetSelection(vtkSMRepresentationProxy* repr, vtkSMSourceProxy* selectionSource)
-{
-  selectionSource->UpdateVTKObjects();
-
-  vtkSMPropertyHelper(repr, "Selection").Set(selectionSource);
-  repr->UpdateVTKObjects();
-}
-
-//-----------------------------------------------------------------------------
-void RemoveSelection(vtkSMRepresentationProxy* repr)
-{
-  // Under the hood, we simply add an empty selection source
-  vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
-  vtkSmartPointer<vtkSMProxy> emptySelection;
-  emptySelection.TakeReference(pxm->NewProxy("sources", "ValueSelectionSource"));
-  emptySelection->UpdateVTKObjects();
-
-  vtkSMPropertyHelper(repr, "Selection").Set(emptySelection);
-  repr->UpdateVTKObjects();
-}
-
-//-----------------------------------------------------------------------------
-bool GetSelectionValues(vtkSMProxy* selectionSourceProxy, std::set<std::pair<vtkIdType, vtkIdType>>& values)
-{
-  if (selectionSourceProxy)
-  {
-    auto allValues = vtkSMPropertyHelper(selectionSourceProxy, "Values").GetIdTypeArray();
-    for (int i = 0; i < allValues.size(); i+=2) // skip process ids
-    {
-      values.insert(std::make_pair(allValues[i], allValues[i+1]));
-    }
-    return true;
-  }
-  return false;
-}
-}
-
-namespace SelectionOperations
-{
-//-----------------------------------------------------------------------------
-void Union(vtkSMProxy* selectionSourceProxy1, vtkSMProxy* selectionSourceProxy2, std::set<std::pair<vtkIdType, vtkIdType>>& result)
-{
-  result.clear();
-  std::set<std::pair<vtkIdType, vtkIdType>> values1, values2;
-  SelectionUtilities::GetSelectionValues(selectionSourceProxy1, values1);
-  SelectionUtilities::GetSelectionValues(selectionSourceProxy2, values2);
-
-  std::set_union(values1.begin(), values1.end(), values2.begin(), values2.end(), std::inserter(result, result.begin()));
-}
-
-//-----------------------------------------------------------------------------
-void Difference(vtkSMProxy* selectionSourceProxy1, vtkSMProxy* selectionSourceProxy2, std::set<std::pair<vtkIdType, vtkIdType>>& result)
-{
-  result.clear();
-  std::set<std::pair<vtkIdType, vtkIdType>> values1, values2;
-  SelectionUtilities::GetSelectionValues(selectionSourceProxy1, values1);
-  SelectionUtilities::GetSelectionValues(selectionSourceProxy2, values2);
-
-  std::set_difference(values1.begin(), values1.end(), values2.begin(), values2.end(), std::inserter(result, result.begin()));
-}
-
-//-----------------------------------------------------------------------------
-void SymmetricDifference(vtkSMProxy* selectionSourceProxy1, vtkSMProxy* selectionSourceProxy2, std::set<std::pair<vtkIdType, vtkIdType>>& result)
-{
-  result.clear();
-  std::set<std::pair<vtkIdType, vtkIdType>> values1, values2;
-  SelectionUtilities::GetSelectionValues(selectionSourceProxy1, values1);
-  SelectionUtilities::GetSelectionValues(selectionSourceProxy2, values2);
-
-  std::set_symmetric_difference(values1.begin(), values1.end(), values2.begin(), values2.end(), std::inserter(result, result.begin()));
-}
-}
-
-//-----------------------------------------------------------------------------
-void SPV3D_CADSelection::fastSelection()
-{
+bool SPV3D_CADSelection::hardwareSelect(vtkCollection* selectedRepresentations, vtkCollection* selectionSources)
+{
+  /**
+   * Perform the hardware selection, i.e. the first step in the selection mechanism of ParaView.
+   * Under the hood, a dedicated render pass will be executed, displaying the values contained in the
+   * selection array (identified by this->SelectionArrayName) at each cell. Then, the value of the cell
+   * under the cursor will be retrieved. This value will be used to retrieve all the cells sharing it
+   * in the CADMapper (second step of the selection mechanism).
+   * 
+   * The selectedRepresentation will contain the selected representation proxy (i.e. representation proxy
+   * of the selected vtk object) and a selection source proxy. The selection source is a vtk source used to
+   * generate a vtkSelection. In the SPV3D_CADSelection, we only manipulate vtkPVSelectionSource proxies to 
+   * keep track of the selection on the server side. This proxy notably allow to directly access the selected
+   * value(s) through the "Values" property (see ValueSelectionSource in extraction_filter.xml).
+   * 
+   * For now, since we do not support yet the selection by rubber band or frustum, only one cell will be 
+   * hardware selected at once. This means only one representation and selection source (containing an 
+   * unique value) will be returned each time hardwareSelect is called.
+   */
   vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
   assert(rmp != nullptr);
 
@@ -722,206 +434,145 @@ void SPV3D_CADSelection::fastSelection()
   int region[4] = { x, y, x, y };
 
   // Do the selection on the current region (client side)
+  return rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, this->SelectionArrayName.c_str());
+}
+
+//-----------------------------------------------------------------------------
+void SPV3D_CADSelection::select()
+{
+  // Do hardware selection:
+  // - selectionSources will contain the selection source containing the selection values
+  // - selectionRepresentations will contain the representation of the vtk data object being selected 
   vtkNew<vtkCollection> selectedRepresentations;
   vtkNew<vtkCollection> selectionSources;
-  bool status = false;
-  switch (this->Mode)
-  {
-    case SELECT_SOLIDS:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Solid id");
-      break;
-    case SELECT_FACES:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Face id");
-      break;
-    case SELECT_EDGES:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Edge id");
-      break;
-    case SELECT_VERTICES:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Vertex id");
-      break;
-    default:
-      qCritical("Invalid call to SPV3D_CADSelection::fastSelection");
-      return;
-  }
-
-  vtkSMRepresentationProxy* repr =
-    vtkSMRepresentationProxy::SafeDownCast(selectedRepresentations->GetItemAsObject(0));
+  bool isSomethingSelected = this->hardwareSelect(selectedRepresentations, selectionSources);
 
-  // If something has been preselected or selected
-  if (status)
+  if (isSomethingSelected)
   {
-    // If the selection occurs on a new represention, clean the selection on the
-    // current representation before continuing
-    if (this->CurrentRepresentation != nullptr && repr != this->CurrentRepresentation)
+    // We only select one object at once, so we always have only one representation and one selection source
+    vtkSMRepresentationProxy* repr =
+      vtkSMRepresentationProxy::SafeDownCast(selectedRepresentations->GetItemAsObject(0));
+    vtkSMSourceProxy* selection = vtkSMSourceProxy::SafeDownCast(selectionSources->GetItemAsObject(0));
+
+    /**
+     * If the selection occurs on a new represention (new vtk data object), clean the selection on the
+     * current representation before continuing
+     */
+    if (repr != this->CurrentRepresentation)
     {
-      SelectionUtilities::RemoveSelection(this->CurrentRepresentation);
+      CADSelection::Utilities::RemoveSelection(this->CurrentRepresentation);
+      this->CurrentRepresentation = repr;
     }
 
-    this->CurrentRepresentation = repr;
-
-    // Set the selection (selection source) to the current representation
-    vtkSMSourceProxy* currentSelection = vtkSMSourceProxy::SafeDownCast(vtkSMPropertyHelper(repr, "Selection").GetAsProxy());
-
-    vtkSMSourceProxy* newSelection = vtkSMSourceProxy::SafeDownCast(selectionSources->GetItemAsObject(0));
-    // vtkSmartPointer<vtkSMSourceProxy> newAppendSelections;
-    // newAppendSelections.TakeReference(vtkSMSourceProxy::SafeDownCast(
-    //   vtkSMSelectionHelper::NewAppendSelectionsFromSelectionSource(selectionSource)));
-
+    // Get the current selection source attached to the representation (if any)
+    vtkSMSourceProxy* currentSelection = CADSelection::Utilities::GetSelection(repr);
+
+    /**
+     * Combine the new selection with the current one, depending on the selection modifier
+     * 
+     * XXX: If the approach of combining selection values directly shows limitations in
+     * the future, we can considerate using a vtkAppendSelection filter to do it instead 
+     * (see pqRenderView::collectSelectionPorts method). However, since in our case we 
+     * don't do selection extraction but display it directly with the vtkCADMapper, we 
+     * need to add support for selection expressions in this mapper.
+     */
     switch (this->getSelectionModifier())
     {
-      case pqView::PV_SELECTION_ADDITION:
-      {
-        std::set<std::pair<vtkIdType, vtkIdType>> values;
-        SelectionOperations::Union(currentSelection, newSelection, values);
-        SelectionUtilities::SetSelectionFromValues(repr, values);
-        break;
-      }
-      case pqView::PV_SELECTION_SUBTRACTION:
-      {
-        std::set<std::pair<vtkIdType, vtkIdType>> values;
-        SelectionOperations::Difference(currentSelection, newSelection, values);
-        SelectionUtilities::SetSelectionFromValues(repr, values);
-        break;     
-      }
-      case pqView::PV_SELECTION_TOGGLE:
-      {
-        std::set<std::pair<vtkIdType, vtkIdType>> values;
-        SelectionOperations::SymmetricDifference(currentSelection, newSelection, values);
-        SelectionUtilities::SetSelectionFromValues(repr, values);
-        break;
-      }
-      case pqView::PV_SELECTION_DEFAULT:
-      default:
-        // Just keep the current selection
-        SelectionUtilities::SetSelection(repr, newSelection);
-        break;
+    case pqView::PV_SELECTION_ADDITION:
+    {
+      CADSelection::SelectionValues values;
+      CADSelection::Operations::Union(currentSelection, selection, values);
+      CADSelection::Utilities::SetSelectionFromValues(repr, values, this->SelectionArrayName);
+      break;
+    }
+    case pqView::PV_SELECTION_SUBTRACTION:
+    {
+      CADSelection::SelectionValues values;
+      CADSelection::Operations::Difference(currentSelection, selection, values);
+      CADSelection::Utilities::SetSelectionFromValues(repr, values, this->SelectionArrayName);
+      break;     
+    }
+    case pqView::PV_SELECTION_TOGGLE:
+    {
+      CADSelection::SelectionValues values;
+      CADSelection::Operations::SymmetricDifference(currentSelection, selection, values);
+      CADSelection::Utilities::SetSelectionFromValues(repr, values, this->SelectionArrayName);
+      break;
+    }
+    case pqView::PV_SELECTION_DEFAULT:
+    default:
+      // No modifier, so just keep the current selection
+      CADSelection::Utilities::SetSelection(repr, selection);
+      break;
     }
-    // vtkSMPropertyHelper(repr, "Selection").Set(newAppendSelections);
-    // repr->UpdateVTKObjects();
   }
   else if (this->CurrentRepresentation != nullptr)
   {
     // If nothing has been selected then clean current representation
-    // with an "empty" selection source
-    SelectionUtilities::RemoveSelection(repr);
+    CADSelection::Utilities::RemoveSelection(this->CurrentRepresentation);
   }
 
+  // XXX: improve this to avoid double render
   this->View->forceRender();
   this->View->forceRender();
-  // TODO improve this to avoid double render
 }
 
 //-----------------------------------------------------------------------------
-void SPV3D_CADSelection::fastPreSelection()
+void SPV3D_CADSelection::preSelect()
 {
-  vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
-  assert(rmp != nullptr);
-
-  int x = rmp->GetInteractor()->GetEventPosition()[0];
-  int y = rmp->GetInteractor()->GetEventPosition()[1];
-  this->MousePosition[0] = x;
-  this->MousePosition[1] = y;
-
-  int region[4] = { x, y, x, y };
-
-  // Do the selection on the current region (client side)
+  // Do hardware selection:
+  // - selectionSources will contain the selection source containing the selection values
+  // - selectionRepresentations will contain the representation of the entity being selected 
   vtkNew<vtkCollection> selectedRepresentations;
   vtkNew<vtkCollection> selectionSources;
-  bool status = false;
-  switch (this->Mode)
-  {
-    case SELECT_SOLIDS:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Solid id");
-      break;
-    case SELECT_FACES:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Face id");
-      break;
-    case SELECT_EDGES:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Edge id");
-      break;
-    case SELECT_VERTICES:
-      status = rmp->SelectSurfaceCells(region, selectedRepresentations, selectionSources, false, 0, false, "Vertex id");
-      break;
-    default:
-      qCritical("Invalid call to SPV3D_CADSelection::fastSelection");
-      return;
-  }
-
-  vtkSMRepresentationProxy* repr =
-    vtkSMRepresentationProxy::SafeDownCast(selectedRepresentations->GetItemAsObject(0));
+  bool isSomethingSelected = this->hardwareSelect(selectedRepresentations, selectionSources);
 
-  // If something has been preselected or selected
-  if (status)
+  if (isSomethingSelected)
   {
+    // We only select one object at once, so we always have only one representation and one selection source
+    vtkSMRepresentationProxy* repr =
+      vtkSMRepresentationProxy::SafeDownCast(selectedRepresentations->GetItemAsObject(0));
+    vtkSMSourceProxy* selection = vtkSMSourceProxy::SafeDownCast(selectionSources->GetItemAsObject(0));
+
     // If the selection occurs on a new represention, clean the selection on the
     // current representation before continuing
-    if (this->CurrentRepresentation != nullptr && repr != this->CurrentRepresentation)
+    if (repr != this->CurrentRepresentation)
     {
-      vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
-      vtkSMProxy* emptySel = pxm->NewProxy("sources", "IDSelectionSource");
-
-      vtkSMPropertyHelper(this->CurrentRepresentation, "PreSelection").Set(emptySel);
-      this->CurrentRepresentation->UpdateVTKObjects();
-      emptySel->Delete();
+      CADSelection::Utilities::RemovePreSelection(this->CurrentRepresentation);
+      this->CurrentRepresentation = repr;
     }
 
-    this->CurrentRepresentation = repr;
-
-    // Set the selection (selection source) to the current representation
-    // TODO: There should be some cases where we append instead. Which ones ? When a modifier is pressed ? (i.e. the Control Key)
-    vtkSMSourceProxy* sel = vtkSMSourceProxy::SafeDownCast(selectionSources->GetItemAsObject(0));
-    vtkSMPropertyHelper(repr, "PreSelection").Set(sel);
-    repr->UpdateVTKObjects();
+    // Set the preselection
+    CADSelection::Utilities::SetPreSelection(repr, selection);
   }
-
-  // If nothing has been selected then clean current representation
-  // with an "empty" selection source
   else if (this->CurrentRepresentation != nullptr)
   {
-    vtkSMSessionProxyManager* pxm = rmp->GetSessionProxyManager();
-    vtkSMProxy* emptySel = pxm->NewProxy("sources", "IDSelectionSource");
-
-    vtkSMPropertyHelper(this->CurrentRepresentation, "PreSelection").Set(emptySel);
-    this->CurrentRepresentation->UpdateVTKObjects();
-    this->CurrentRepresentation = nullptr;
-    emptySel->Delete();
+    // If nothing has been selected then clean current representation
+    CADSelection::Utilities::RemovePreSelection(this->CurrentRepresentation);
   }
 
+  // XXX: improve this to avoid double render
   this->View->forceRender();
   this->View->forceRender();
-  // TODO improve this to avoid double render
-}
-
-//-----------------------------------------------------------------------------
-void SPV3D_CADSelection::selectionChanged(vtkObject*, unsigned long, void* calldata)
-{
-  BEGIN_UNDO_EXCLUDE();
-
-  cout << "Selection changed: should not enter there !" << endl;
-
-  int selectionModifier = this->getSelectionModifier();
-  int* region = reinterpret_cast<int*>(calldata);
-// Simple version check to change once 5.10 support is dropped
-  this->View->selectCellsOnSurface(region, selectionModifier);
-  //this->View->selectOnSurface(region, selectionModifier);
-  END_UNDO_EXCLUDE();
-
-  // this->endSelection();
 }
 
 //-----------------------------------------------------------------------------
 int SPV3D_CADSelection::getSelectionModifier()
 {
-  int selectionModifier = pqView::PV_SELECTION_DEFAULT;//this->Superclass::getSelectionModifier();
+  // Similar to pqRenderViewSelectionReaction::getSelectionModifier()
+  int selectionModifier = pqView::PV_SELECTION_DEFAULT;
 
   vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
   assert(rmp != nullptr);
 
-  bool ctrl = rmp->GetInteractor()->GetControlKey() == 1;
-  bool shift = rmp->GetInteractor()->GetShiftKey() == 1;
-  // selectionModifier = pqView::PV_SELECTION_TOGGLE;
+  const bool ctrl = rmp->GetInteractor()->GetControlKey() == 1;
+  const bool shift = rmp->GetInteractor()->GetShiftKey() == 1;
 
-  if (ctrl)
+  if (ctrl && shift)
+  {
+    selectionModifier = pqView::PV_SELECTION_TOGGLE;
+  }
+  else if (ctrl)
   {
     selectionModifier = pqView::PV_SELECTION_ADDITION;
   }
@@ -949,10 +600,8 @@ void SPV3D_CADSelection::cleanupObservers()
 //-----------------------------------------------------------------------------
 std::set<std::string> SPV3D_CADSelection::GetSelectedObEntry()
 {
-  // std::cout << "Get Selected Ob Entry" << std::endl;
   std::set<std::string> EntryList;
 
-  // get preselected id here
   if (this->Representation == nullptr)
   {
     return EntryList;
@@ -961,18 +610,27 @@ std::set<std::string> SPV3D_CADSelection::GetSelectedObEntry()
   vtkSMProxy* proxyRepresentation = this->Representation->getProxy();
   if (!proxyRepresentation)
   {
-    qWarning()<< "There is no representation in the active view for the Geometry Source.";
+    qWarning() << "There is no representation in the active view for the Geometry Source.";
     return EntryList;
   }
 
-  vtkSMProxy* selInfo = vtkSMPropertyHelper(proxyRepresentation, "Selection").GetAsProxy(0);
-  if (selInfo)
+  /**
+   * Retrieve the selection source currently attached to the active representation, and 
+   * retrieve all the selected values (process ID + value) from it.
+   * 
+   * XXX: since the EntryList cannot store the process IDs (at least for now), we need
+   * to drop them here. In order to eventually support the multi-process selection in the
+   * future, we will need to rework this to store this info.
+   */
+  vtkSMProxy* selection = vtkSMPropertyHelper(proxyRepresentation, "Selection").GetAsProxy(0);
+  if (selection)
   {
-    auto ids = vtkSMPropertyHelper(selInfo, "Values").GetIdTypeArray();
-    for (const auto id : ids)
+    auto ids = vtkSMPropertyHelper(selection, "Values").GetIdTypeArray();
+
+    // Values came by pair of (process ID, value); drop the process ID.
+    for (std::size_t i = 1; i < ids.size(); i+=2)
     {
-      // cout << "Values : " << id << endl;
-      EntryList.insert(SPV3D_Prs::FromVtkIdToEntry(id));
+      EntryList.insert(SPV3D_Prs::FromVtkIdToEntry(ids[i]));
     }
   }
 
@@ -980,64 +638,40 @@ std::set<std::string> SPV3D_CADSelection::GetSelectedObEntry()
 }
 
 //-----------------------------------------------------------------------------
-// TODO: Prefer passing parameters by const ref rather than by copy
-void SPV3D_CADSelection::SetSelectionFromEntrySet(std::set<std::string> EntryList)
+void SPV3D_CADSelection::SetSelectionFromEntrySet(const std::set<std::string>& EntryList)
 {
-  if (this->Representation == nullptr) {
+  if (this->Representation == nullptr) 
+  {
     qWarning() << "There is no pqDataRepresentation";
     return;
   }
 
-  vtkSMProxy* proxyRepresentation = this->Representation->getProxy();
+  vtkSMRepresentationProxy* proxyRepresentation = vtkSMRepresentationProxy::SafeDownCast(this->Representation->getProxy());
   if (!proxyRepresentation) 
   {
     qWarning() << "There is no representation";
     return;
   }
-  // this->View->forceRender();
-
-  // proxyRepresentation->InvokeCommand("BeginSelect");
-
-  // Create a new selection source on the server side and its proxy on the client side
-  vtkSMRenderViewProxy* rmp = this->View->getRenderViewProxy();
-  vtkSMSessionProxyManager* pxm = rmp->GetSessionProxyManager();
-  vtkSMProxy* selectionSource = pxm->NewProxy("sources", "ValueSelectionSource");
-  vtkSMPropertyHelper(selectionSource, "ArrayName").Set("Solid id");
-  vtkSMPropertyHelper(selectionSource, "FieldType").Set("CELL");
-  vtkSMPropertyHelper(selectionSource, "Values").SetNumberOfElements(EntryList.size());
-
-  // TODO: How do i get that properly ?
-  vtkIdType processNumber = 0;
-
-  // Retrieve entry ids and put them in a vector
-  // std::cout << "(Client) Sending selected entries to server:" << std::endl;
   
-  // TODO: Make sure this works for multiple selections
+  /**
+   * From the selection values (process ID + value) given by the EntryList, generates a new 
+   * selection source and set it to the representation.
+   * 
+   * XXX: since the EntryList cannot store the process IDs (at least for now), we assume that
+   * the process ID is always 0. In order to eventually support the multi-process selection 
+   * in the future, we will need to rework this to store this info.
+   */
+  CADSelection::SelectionValues selectionValues;
   for (const std::string& entryName: EntryList)
   {
-    vtkIdType solidId = SPV3D_Prs::FromEntryToVtkId(entryName.c_str());
-    // std::cout << "(Client) " << solidId << std::endl;
-
-    // This takes pairs of values as (process number, value).
-    std::array<vtkIdType, 2> values = {solidId, processNumber};
-    vtkSMPropertyHelper(selectionSource, "Values").Append(values.data(), 2);
+    vtkIdType processNumber = 0;
+    vtkIdType value = SPV3D_Prs::FromEntryToVtkId(entryName.c_str());
+    selectionValues.emplace(processNumber, value);
   }
 
-  selectionSource->UpdateVTKObjects();
-  // cout << "SetSelectionFromEntrySet" << endl;
-  // vtkSMPropertyHelper(proxyRepresentation, "Selection").Set(selectionSource);
-  proxyRepresentation->UpdateVTKObjects();
+  CADSelection::Utilities::SetSelectionFromValues(proxyRepresentation, selectionValues, this->SelectionArrayName);
   
-  selectionSource->Delete();
-  
-  // TODO: Which render calls are necessary to display the selection right away ?
-  // TODO: Before, after or during the slection ?
-  // Force render to make sure the selection appears
+  // XXX: improve this to avoid double render
   this->View->forceRender();
   this->View->forceRender();
-  rmp->StillRender();
-
-  // proxyRepresentation->InvokeCommand("EndSelect");
-  
-
 }
index e50d1a92a7ed5e62d7e4e53eed204210b386ef3b..1dbae407f76c492ac09b41ff22340c7b436ed14e 100644 (file)
 
 #pragma once
 
+#include <pqDataRepresentation.h>
+#include <pqRenderView.h>
 #include <pqSelectionReaction.h>
 
-#include <vtkSmartPointer.h>
 #include <vtkWeakPointer.h>
 
-#include <QCursor>
 #include <QPointer>
-#include <QTimer>
-#include <QPushButton>
-#include <QComboBox>
 
 #include <set>
 
 class vtkObject;
 class pqView;
-class pqRenderView;
 class vtkIntArray;
-class pqDataRepresentation;
 class vtkSMRepresentationProxy;
 class vtkSMSourceProxy;
 
 /**
- * SPV3D_CADSelection handles various selection modes available on
- * RenderViews. Simply create multiple instances of
- * SPV3D_CADSelection to handle selection modes for that RenderView.
- * SPV3D_CADSelection uses internal static members to ensure that
- * at most 1 view (and 1 type of selection) is in selection-mode at any given
- * time.
+ * @class SPV3D_CADSelection 
+ * @brief Class handling the selection for LightApp_PV3DSelector & SPV3D_ViewModel
+ *
+ * This class handles the selection in the context of the PV3D Viewer.
+ * It is responsible to:
+ * - Retrieve & display the elements selected in the view and transfer the result to the LightApp_PV3DSelector
+ * in order to update them in the pipeline
+ * - Retrieve the elements given by the LightApp_PV3DSelector (selected though the pipeline) in order
+ * to display them in the view.
+ * 
+ * The specificity of this selection mechanism is that it avoids extracting selected cells for both
+ * selection and preselection (like the "fastPreselection" of ParaView). This significantly speed-up the
+ * selection and save memory in the case of selection containing a lot of cells.
+ * 
+ * Current limitations of this selection mode:
+ * - This selection mechanism only allows to select one object (solid, face, etc.) at the time
+ * (multi-selection is exclusively done through modifiers)
+ * - This mechanism do not support selection on data stored in parallel on the server side.
+ *
+ * @sa pqRenderViewSelectionReaction
  */
 class SPV3D_CADSelection : public QObject
 {
@@ -66,40 +75,47 @@ public:
    * pqActiveObjects.
    */
   SPV3D_CADSelection(QObject *parent, pqRenderView* view, SelectionMode mode);
-  
+
   ~SPV3D_CADSelection() override;
 
   /**
-   * Set the selectionMode
+   * Set the selectionMode. This mode is used to determine which entity
+   * will be selected: solids, faces, edges or vertices.
+   * Only one element at the time can be selected. If the selection
+   * mode changes, the previous selection will be deleted.
    */
   void SetMode(const SPV3D_CADSelection::SelectionMode mode);
   
   /**
-   * Get Selected entry list from server
+   * Called when the selection is modified through the view.
+   * Get Selected entry list from server. This correspond to the list
+   * of objects (solids, faces, etc.) selected from the view.
    */
   std::set<std::string> GetSelectedObEntry();
 
   /**
-   * Called when the active representation changes.
+   * Called when the selection is modified through the pipeline.
+   * Update the selection in the view based of the given list of
+   * entities (solids, faces, etc.).
    */
-  void SetSelectionFromEntrySet(std::set<std::string> EntryList);
+  void SetSelectionFromEntrySet(const std::set<std::string>& EntryList);
 
 signals:
+  /**
+   * Fired whenever the selection done through the view changes.
+   */
   void selectionChanged();
 
 public Q_SLOTS:
   /**
    * For checkable actions, this calls this->beginSelection() or
-   * this->endSelection() is val is true or false, respectively. For
-   * non-checkable actions, this call this->beginSelection() and
-   * this->endSelection() in that order.
+   * this->endSelection() if val is true or false, respectively.
    */
   virtual void actionTriggered(bool val);
   
 private Q_SLOTS:
   /**
-   * Called when this object was created with nullptr as the view and the active
-   * view changes.
+   * Called when the active view changes.
    */
   void setView(pqView* view);
 
@@ -109,72 +125,103 @@ private Q_SLOTS:
   void setRepresentation(pqDataRepresentation* representation);
 
   /**
-   * starts the selection i.e. setup render view in selection mode.
+   * Set the render view in selection mode.
    */
   void beginSelection();
 
   /**
-   * finishes the selection. Doesn't cause the selection, just returns the
-   * render view to previous interaction mode.
+   * Restore the render view to interaction mode.
    */
   void endSelection();
 
-  void updateEnableState();
+  /**
+   * In selection mode, triggers the preselection mechanism
+   */
+  void onMouseMove();
+
+  /**
+   * In selection mode, triggers the selection mechanism
+   */
+  void onLeftButtonRelease();
 
   ///@{
   /**
-   * Disable preselection during rotation using the right button.
+   * Used to disable the preselection during rotation using the 
+   * right mouse button.
    */
   void onRightButtonPress();
   void onRightButtonRelease();
   ///@}
 
 private:
+  Q_DISABLE_COPY(SPV3D_CADSelection)
 
   /**
-   * callback called when the vtkPVRenderView is done with selection.
+   * Get the current state of selection modifier:
+   * - Ctrl : addition
+   * - Shift : substraction
+   * - Ctrl + Shift : toggle
    */
-  void selectionChanged(vtkObject*, unsigned long, void* calldata);
+  int getSelectionModifier();
 
   /**
-   * callback called for mouse move events when in 'interactive selection'
-   * modes.
+   * Perform the selection from the view.
+   * The selected entities will be combined depending on the selection modifier.
    */
-  void onMouseMove();
+  void select();
 
   /**
-   * callback called for click events when in 'interactive selection' modes.
+   * Perform the preselection from the view.
    */
-  void onLeftButtonRelease();
+  void preSelect();
 
-  // Get the current state of selection modifier
-  int getSelectionModifier();
+  /**
+   * Do the hardware selection.
+   * - selectionSources will contain the selection source containing the selection values
+   * - selectionRepresentations will contain the representation of the entity being selected 
+   */
+  bool hardwareSelect(vtkCollection* selectedRepresentations, vtkCollection* selectedSources);
 
   /**
-   * makes fast selection.
+   * Clean up observers.
+   * Only called upon deletion.
    */
-  void fastSelection();
+  void cleanupObservers();
 
   /**
-   * makes fast selection.
+   * Keep track of the active objects.
+   * XXX: May be desynchronization between Representation & CurrentRepresentation,
+   * look for keeping only one of them.
    */
-  void fastPreSelection();
+  QPointer<pqRenderView> View = nullptr;
+  QPointer<pqDataRepresentation> Representation = nullptr;
+  vtkSMRepresentationProxy* CurrentRepresentation = nullptr;
 
   /**
-   * cleans up observers.
+   * Current array name used to retrieve selected elements IDs.
+   * Depends on the selection mode used.
    */
-  void cleanupObservers();
+  std::string SelectionArrayName;
 
-  Q_DISABLE_COPY(SPV3D_CADSelection)
-  QPointer<pqRenderView> View;
-  QPointer<pqDataRepresentation> Representation;
-  QMetaObject::Connection RepresentationConnection;
-  vtkSMRepresentationProxy* CurrentRepresentation = nullptr;
-  SelectionMode Mode;
-  int PreviousRenderViewMode;
+  /**
+   * Keep track of the mouse position, will be useful later for
+   * tooltip implementation.
+   */
+  int MousePosition[2] = { 0, 0 };
+
+  /**
+   * Keep track of interaction mode to restore it each time
+   * we exit the selection mode
+   */
+  int PreviousInteractionMode = -1;
+
+  /**
+   * Used to avoid doing preselection when doing right-click
+   * rotation in selection mode.
+   */
+  bool DisablePreSelection = false;
+
+  // Keep track of observed objects
   vtkWeakPointer<vtkObject> ObservedObject;
   unsigned long ObserverIds[4];
-  int MousePosition[2];
-  QComboBox* selectionComboBox;
-  bool DisablePreSelection = false;
 };