From 00e720328a5a77046ade5a9f2a404cb0450be337 Mon Sep 17 00:00:00 2001 From: Thomas Galland Date: Wed, 20 Nov 2024 16:03:01 +0100 Subject: [PATCH] Do refactorings and add comments --- src/SPV3D/SPV3D_CADSelection.cxx | 1086 ++++++++++-------------------- src/SPV3D/SPV3D_CADSelection.h | 153 +++-- 2 files changed, 460 insertions(+), 779 deletions(-) diff --git a/src/SPV3D/SPV3D_CADSelection.cxx b/src/SPV3D/SPV3D_CADSelection.cxx index 30e65b6f6..5233179cf 100644 --- a/src/SPV3D/SPV3D_CADSelection.cxx +++ b/src/SPV3D/SPV3D_CADSelection.cxx @@ -18,344 +18,238 @@ // #include "SPV3D_CADSelection.h" +#include "SPV3D_Prs.h" #include #include -#include #include #include #include -#include -#include #include -#include -#include #include +#include #include #include -#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include "SPV3D_Prs.h" +#include +#include -// TO REMOVE #include #include -#include "vtkSMProxyManager.h" -#include #include -#include -namespace + +namespace CADSelection { -void ReplaceString(std::string& source, const std::string& replace, const std::string& with) +typedef std::set> 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 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 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 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 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 newSelectionNamesAP2; - for (int i = static_cast(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(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 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 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 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 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 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::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(view); } - - this->View = qobject_cast(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>& values) -{ - vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager(); - vtkSmartPointer 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 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 emptySelection; - emptySelection.TakeReference(pxm->NewProxy("sources", "ValueSelectionSource")); - emptySelection->UpdateVTKObjects(); - - vtkSMPropertyHelper(repr, "Selection").Set(emptySelection); - repr->UpdateVTKObjects(); -} - -//----------------------------------------------------------------------------- -bool GetSelectionValues(vtkSMProxy* selectionSourceProxy, std::set>& 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>& result) -{ - result.clear(); - std::set> 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>& result) -{ - result.clear(); - std::set> 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>& result) -{ - result.clear(); - std::set> 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 selectedRepresentations; vtkNew 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 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> values; - SelectionOperations::Union(currentSelection, newSelection, values); - SelectionUtilities::SetSelectionFromValues(repr, values); - break; - } - case pqView::PV_SELECTION_SUBTRACTION: - { - std::set> values; - SelectionOperations::Difference(currentSelection, newSelection, values); - SelectionUtilities::SetSelectionFromValues(repr, values); - break; - } - case pqView::PV_SELECTION_TOGGLE: - { - std::set> 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 selectedRepresentations; vtkNew 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(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 SPV3D_CADSelection::GetSelectedObEntry() { - // std::cout << "Get Selected Ob Entry" << std::endl; std::set EntryList; - // get preselected id here if (this->Representation == nullptr) { return EntryList; @@ -961,18 +610,27 @@ std::set 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 SPV3D_CADSelection::GetSelectedObEntry() } //----------------------------------------------------------------------------- -// TODO: Prefer passing parameters by const ref rather than by copy -void SPV3D_CADSelection::SetSelectionFromEntrySet(std::set EntryList) +void SPV3D_CADSelection::SetSelectionFromEntrySet(const std::set& 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 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"); - - } diff --git a/src/SPV3D/SPV3D_CADSelection.h b/src/SPV3D/SPV3D_CADSelection.h index e50d1a92a..1dbae407f 100644 --- a/src/SPV3D/SPV3D_CADSelection.h +++ b/src/SPV3D/SPV3D_CADSelection.h @@ -19,34 +19,43 @@ #pragma once +#include +#include #include -#include #include -#include #include -#include -#include -#include #include 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 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 EntryList); + void SetSelectionFromEntrySet(const std::set& 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 View = nullptr; + QPointer 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 View; - QPointer 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 ObservedObject; unsigned long ObserverIds[4]; - int MousePosition[2]; - QComboBox* selectionComboBox; - bool DisablePreSelection = false; }; -- 2.39.2